Compare commits

..

219 Commits
v1.1 ... v1.1.1

Author SHA1 Message Date
Eugen
93db265be7 Do not store last visited URL from API controllers (#1330)
Sign-in redirects you back to last visited URL, but in case of API requests,
this sometimes redirected users to an API URL that, of course, greeted them
with an {"error":"The access token is invalid"}
2017-04-09 22:21:52 +02:00
Eugen
c172919745 Fix #1339 - better Atom titles (#1343) 2017-04-09 20:55:54 +02:00
Eugen
15d442cf9d Fix /api/v1/accounts/update_credentials tests (#1357) 2017-04-09 20:23:14 +02:00
Ornithologist Coder
43f955e31f Minor change to Entity on API.md (Relationship) (#1356) 2017-04-09 19:10:38 +02:00
Hugo Gameiro
4ea4ef9d0f update portuguese translation (#1280)
added the missing fields and improved the translation
2017-04-09 18:49:26 +02:00
ThibG
d19ed18388 Get handle from atom feed's author/email field instead of guessing from URL (#1344)
The goal of this change is to enhance Mastodon's handling of remote domains
for which the APIs reside on a different host (see issue #1032).

Indeed, when a remote user unknown to Mastodon is mentionned, only its profile
URL (e.g. https://social.example.org/users/User) is known, and Mastodon has to
build a @username@domain handle for it. To do so, Mastodon fetches the user's
atom feed (e.g., https://social.example.org/users/User.atom) and uses its
content to get the username part of the handle, and the URL's host part to
build the domain (e.g., @User@social.example.org). This handle is then used
for a Webfinger request.

In the case where example.org serves the Webfinger info for @User@example.org
and all feeds and APIs are hosted at social.example.org, Mastodon will still
build @User@social.example.org and fail at resolving the account's details
through Webfinger.

This patch changes this behaviour by using the author's email address from
the atom feed to build the handle. In Mastodon-generated atom feeds, the
email address is always the handle it expects for federation.
2017-04-09 18:43:48 +02:00
David Authier
f0bd439486 Use HTTP Accept-Language to detect locale (#1166)
* Use HTTP Accept-Language to detect locale

* Fix gem order to comply with codeclimate

* Sort gem to comply with rubocop

* I18n.default_locale fallback when there is no accept-language header
2017-04-09 18:40:24 +02:00
Ornithologist Coder
b16fbd52b2 Minor API.md changes (#1351) 2017-04-09 18:36:03 +02:00
Ornithologist Coder
3b34c28bee Minor change on API.md (#1352) 2017-04-09 18:35:51 +02:00
R Tucker
8bfdbf0aa6 Add comment to settings.yml to nudge admins towards editing values via Web UI (#1289)
* Put a useful message for new admins on /about/more

I totally failed to realize this file was just defaults.  I think
this message would be a good default for people like me.

* Revert default site description, expand comment

This will keep setup-related stuff from leaking into public views,
while still hopefully keeping over-eager admins from editing this
file unnecessarily before RTFMing.  (e.g., me)
2017-04-09 18:34:29 +02:00
David Celis
d4fe6cd2bf Allow users to update their Account in the API (#1179)
* Allow users to update their Account in the API

It would be nice for API clients to be able to allow users to update
their accounts without having to wrap Mastodon in a web view. This patch
adds an API endpoint to let users submit a PATCH for their account.

Signed-off-by: David Celis <me@davidcel.is>

* Add /api/v1/accounts/update_credentials to the API docs

Signed-off-by: David Celis <me@davidcel.is>
2017-04-09 18:33:40 +02:00
Matt Jankowski
ea6c930c04 Helper cleanup (#1348)
* Remove unused helper files

* Add coverage for application helper

* Add coverage for StreamEntriesHelper #display_name
2017-04-09 17:11:37 +02:00
StefOfficiel
12e29c9660 Update fr.jsx (#1329)
* Update fr.jsx

* Remove duplicate translation
2017-04-09 14:58:08 +02:00
Olivier Humbert
082bef3027 French translation update (#1271)
* Update confirmation_instructions.fr.html.erb

consistency across the French translation

* Update 

consistency across the French translation

* Update fr.yml

a bunch of consistency across the French translation + a few typos

* Update doorkeeper.fr.yml

consistency across the French translation (punctuation)
2017-04-09 14:55:58 +02:00
spf
e6b48a7048 French typo (#1257)
* French typo

* Datetime french translation
2017-04-09 14:54:47 +02:00
Jonathan Klee
ba2aea3a80 add empty notifications french translation (#1111) 2017-04-09 14:54:02 +02:00
Matt Jankowski
e5282e4ec0 Clean up about page (#1282)
* Add InstancePresenter to expose site details

* Clean up about controller, use instance presenter
2017-04-09 14:47:25 +02:00
Brian Mock
53eb31f124 Fixes #1311 margin shouldn't stay fixed (#1312) 2017-04-09 14:45:26 +02:00
Matt Jankowski
388ec0d5b6 Search cleanup (#1333)
* Clean up SQL output in Tag and Account search methods

* Add basic coverage for Tag.search_for

* Add coverage for Account.search_for

* Add coverage for Account.advanced_search_for
2017-04-09 14:45:01 +02:00
Matt Jankowski
71706f21c2 Ignore implied formats for catch all route requests (#1340)
A request to `/test` would show the custom 404 page, but a request to
`/test.test` would return a 404 with an empty body.

This change ignores the format on incoming catch all route requests, so that the
html 404 page is returned on these requests.
2017-04-09 14:39:41 +02:00
Rachel H
b1881a3d48 Fix nonworking clear notices button (#1316) 2017-04-09 11:35:23 +02:00
Eugen
d5a675099a Add env variable to disable prepared statements (#1293) 2017-04-09 05:46:32 +02:00
Ash Furrow
c3e7bac1cc Allows setting log level in env variable (#1290)
* Allows setting log level in env variable.

* Made changes based on feedback in #1290.
2017-04-09 01:42:13 +02:00
Ash Furrow
6e3925521d Adds user confirmation rake task (#1300)
* Adds task to confirm user by email.

* Adds documentation for manual confirmation.
2017-04-09 01:09:46 +02:00
Eugen
b89f007862 Make public timelines API not require user context/app credentials (#1291)
* Make /api/v1/timelines/public and /api/v1/timelines/tag/:id public
Fix #1156 - respect query params when generating pagination links in API

* Apply pagination fix to more APIs
2017-04-08 23:39:31 +02:00
Eugen
9acdb166e8 Fix #795, fix #704, fix #835 - 2FA requires confirmation to be enabled (#1278)
* Fix #795, fix #704, fix #835 - 2FA requires confirmation to be enabled
TOTP secret is not shown again after 2FA is enabled

* Clean up
2017-04-08 22:20:08 +02:00
Pavel Djundik
470eb0042e Improve responsiveness of registration form and closed banner (#1265) 2017-04-08 18:31:50 +02:00
Pavel Djundik
fc146a19cc Improve about page responsiveness (#1252) 2017-04-08 15:28:23 +02:00
Elizabeth Myers
941a593ea8 Add mst3k.interlinked.me to list of instances (#1235) 2017-04-08 21:48:16 +10:00
Eugen
982fef811e Fix #1141, fix #1126 - Avatar/profile info fetching (#1215)
* Fix #1141, fix #1126 - Work through UpdateRemoteProfileService for both <feed> and <entry> top-level tags

* Improve code quality, remove line unrelated to fix
2017-04-08 13:26:03 +02:00
Thomas Citharel
41f8fde83e Update french translation (#1148)
Add french translation for emails sent

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

Add non-breaking spaces

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

changes and fixes to the nbsps

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

French update

a few fixes

Signed-off-by: Thomas Citharel <tcit@tcit.fr>

fixes

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
2017-04-08 13:11:16 +02:00
Markus Amalthea Magnuson
157f0a2aa7 Add titles to more icons, and change clear notifications icon. (#1101) 2017-04-08 13:07:55 +02:00
kadiix
49043f644d Update mastodon.brussels instance name on List of Instances
Correct mastodon.brussels.fr to mastodon.brussels
2017-04-08 21:05:49 +10:00
Nicolai von Neudeck
c803f5b440 Updated German translation (#1248)
Fixed various spelling and grammar mistakes.
Used more gender-neutral language.
2017-04-08 13:04:58 +02:00
Sebastian Hübner
f860eb7d71 Fixed two translation errors (#1139)
Changed followers from Follower to Folger and following from Gefolgt to Folgt, to make it identical with the translation in de.jsx
2017-04-08 13:03:11 +02:00
Korbinian
8f9a11b642 Update simple_form.de.yml (#971) 2017-04-08 13:02:07 +02:00
Thibaut (Eychics)
c6b0707cf6 Update mastodon.nuzgo.net (#1242)
Add ipv6 support
2017-04-08 20:34:53 +10:00
James Smith
b79c80b620 Add OTP_SECRET to Heroku app.json (#1246) 2017-04-08 20:31:33 +10:00
Eugen
211920b622 Revert "add persistance to Postresql container" (#1251) 2017-04-08 12:25:23 +02:00
Jantso Porali
01d8003867 Updated about page in Finnish language (#1170)
* update faq with default language

* update translation for about page

* update Minio config

Thanks to @Gargon for helping me. I hope this will help others as well

* update import and export translation

* translate emails to finnish

* add finnish translation for emails

* add finnish translation for emails

* add finnish translation

* add missing dot

* update finnish language to emails

* add finnish translation for emails

* add dot and fix typo

* updated some minor typos

* remove language change due breaking emails

And by dev request

* updated minio config by dev request

* updated about page translation
2017-04-08 12:17:34 +02:00
Olivier Humbert
ed3dfd0bee French translation update (#1188)
* Update fr.yml

one typo fix

* Update simple_form.fr.yml

one translation consistency
2017-04-08 12:16:49 +02:00
Alda Marteau-Hardi
35eff3f2d0 Add some missing strings to prevent some React warning in the console (#1230) 2017-04-08 12:16:16 +02:00
Matt Jankowski
ca44c13455 Use Setting.site_title value for og:site_name occurrences (#1194)
* Add helper method to return Setting.site_title

* Use site_title helper in application layout

* Use site_title value for og:site_name
2017-04-08 12:15:40 +02:00
Eugen
a345eb44fc Merge pull request #1239 from ineffyble/patch-2
Fix my URL
2017-04-08 11:40:43 +02:00
Eugen
7b814d5bcb Merge pull request #1238 from milmazz/hunter-client
Add reference to Elixir client for Mastodon API
2017-04-08 11:40:20 +02:00
Eugen
9f9f4b248e Merge pull request #887 from valentin2105/master
add persistance to Postresql container
2017-04-08 11:39:25 +02:00
Eugen
3660f01f60 Merge pull request #1115 from vmincev/patch-1
Update Production-guide.md
2017-04-08 11:38:54 +02:00
Eugen
a2ec54a20b Merge pull request #1198 from huertanix/patch-1
Update Heroku-guide.md
2017-04-08 11:38:33 +02:00
Eugen
e33dcb79c6 Merge pull request #1221 from rbaumert/patch-1
fixed a sentence in readme
2017-04-08 11:37:51 +02:00
Eugen
7d5ea5c170 Merge pull request #1224 from chrisheninger/patch-1
Add SVG version of logo to repo
2017-04-08 11:37:37 +02:00
Eugen
33849acfa7 Merge pull request #1218 from R0ckweb/patch-2
Fix #1141 on remote follow
2017-04-08 11:37:13 +02:00
Eugen
c141f0a886 Merge pull request #1216 from tootsuite/fix-default-locale-regression
Fix #1165 - Default locale no longer breaks form submissions
2017-04-08 11:36:43 +02:00
Eugen
55d03da303 Merge pull request #1213 from tootsuite/fix-accounts-initial-case
Fix #801 - Respect webfinger's canonical response of username/domain
2017-04-08 11:36:35 +02:00
Eugen
2c3a730eae Merge pull request #1225 from tootsuite/yiskah-patch-1
Close instance list to additions
2017-04-08 11:35:40 +02:00
Milton Mazzarri
b04cbb9f5d Add reference to Elixir client for Mastodon API 2017-04-08 02:48:52 -05:00
Effy Elden
75aade3de2 Fix my URL
Update my URL since toot.zone was shut down.
2017-04-08 16:22:07 +10:00
rbaumert
ac0b84534e Merge branch 'master' into patch-1 2017-04-07 21:39:24 -07:00
Valentin Ouvrard
3c48c9ac2e Merge branch 'master' into master 2017-04-08 15:11:20 +11:00
Shel R
ecf0320a78 Close instance list to additions 2017-04-07 23:51:30 -04:00
Kurtis Rainbolt-Greene
40703b96fa Merge branch 'master' into fix-default-locale-regression 2017-04-07 20:50:21 -07:00
Kurtis Rainbolt-Greene
1e4453405b Merge branch 'master' into patch-2 2017-04-07 20:48:27 -07:00
Chris Heninger
0ad694f96b Add SVG version of logo to repo 2017-04-07 20:40:18 -07:00
Shel R
541c538f9b Merge pull request #1137 from Noiwex/patch-1
Update List-of-Mastodon-instances.md
2017-04-07 23:16:46 -04:00
Shel R
50910d1543 Merge branch 'master' into patch-1 2017-04-07 23:15:17 -04:00
Shel R
4ff8653d6c Merge pull request #1197 from blakebarnett/add_indigo_zone_instance
Add indigo.zone to list of instances
2017-04-07 23:10:25 -04:00
Shel R
17690f51a2 Merge branch 'master' into add_indigo_zone_instance 2017-04-07 23:09:50 -04:00
Shel R
cf80cb5e8b Merge pull request #1196 from mouse-reeve/list-oulipo-social
Adds Oulipo.social to Mastodons list
2017-04-07 23:09:09 -04:00
Shel R
315ff648c8 Merge branch 'master' into list-oulipo-social 2017-04-07 23:08:41 -04:00
Shel R
41923d1c6b Merge pull request #1186 from Aguay-val/patch-2
Add mastodon.fun
2017-04-07 23:07:50 -04:00
Shel R
63686fd36f Merge branch 'master' into patch-2 2017-04-07 23:05:08 -04:00
Shel R
ae9d2f4a32 Merge branch 'master' into patch-1 2017-04-07 23:03:07 -04:00
Shel R
741bbba6ff Merge branch 'master' into patch-1 2017-04-07 22:59:07 -04:00
Shel R
a25a384af3 Merge pull request #1107 from YDrogen/patch-1
Added masto.razrnet.fr
2017-04-07 22:54:44 -04:00
Shel R
971c4de18c Merge branch 'master' into patch-1 2017-04-07 22:54:25 -04:00
Shel R
394c8ef680 Merge pull request #1090 from gled-rs/master
Added mastodon.host in the list of instances
2017-04-07 22:54:14 -04:00
Shel R
3c5b0c55cb Merge branch 'master' into master 2017-04-07 22:53:42 -04:00
Shel R
0dcf3c6abe Merge pull request #1085 from isati/patch-2
Add manx.social instance
2017-04-07 22:49:34 -04:00
Shel R
b67b60fec3 Merge branch 'master' into patch-2 2017-04-07 22:49:14 -04:00
rbaumert
2c0ef75f58 fixed a sentence in readme 2017-04-07 19:48:46 -07:00
Shel R
cc16fa7513 Merge pull request #1084 from genesixx/patch-4
Update List-of-Mastodon-instances.md
2017-04-07 22:48:02 -04:00
Shel R
d6827e38a6 Merge branch 'master' into patch-4 2017-04-07 22:47:42 -04:00
Shel R
29ed448445 Merge pull request #1074 from kadiix/patch-1
Add an instance.
2017-04-07 22:47:00 -04:00
Shel R
e7d7a99fbc Merge branch 'master' into patch-1 2017-04-07 22:46:36 -04:00
Shel R
8d27de32b3 Merge pull request #1069 from Awea/master
Add an instance
2017-04-07 22:45:15 -04:00
Shel R
abab82ec1e Merge branch 'master' into master 2017-04-07 22:44:40 -04:00
Shel R
f0797bf8ce Merge pull request #1066 from jack-michaud/master
Add Northeastern University Mastodon
2017-04-07 22:43:46 -04:00
Shel R
38c5130930 Merge branch 'master' into master 2017-04-07 22:43:13 -04:00
Shel R
ee8af9083a Merge pull request #1051 from Tiwy57/patch-1
Add an instance
2017-04-07 22:42:34 -04:00
Shel R
c9f15f7991 Merge branch 'master' into patch-1 2017-04-07 22:42:14 -04:00
Shel R
91afe1f8fd Merge pull request #1050 from lfuelling/patch-1
add my instance
2017-04-07 22:41:37 -04:00
Shel R
9edee2e64f Merge branch 'master' into patch-1 2017-04-07 22:41:07 -04:00
Shel R
d7e1a282fe Merge pull request #1046 from raymestalez/patch-2
Added an instance to the list
2017-04-07 22:40:29 -04:00
Shel R
c890b86ef6 Merge branch 'master' into patch-2 2017-04-07 22:39:59 -04:00
Valentin Ouvrard
5b571fc434 Merge branch 'master' into master 2017-04-08 13:39:31 +11:00
Valentin Ouvrard
3e4eb9c95f Merge branch 'master' into master 2017-04-08 13:39:09 +11:00
Shel R
ceba26d527 Merge pull request #1041 from R0ckweb/patch-1
Update List-of-Mastodon-instances.md
2017-04-07 22:39:05 -04:00
Shel R
7826b5e93d Merge branch 'master' into patch-1 2017-04-07 22:38:37 -04:00
Shel R
fc7b830719 Merge pull request #1036 from wirehack7/patch-1
Update List-of-Mastodon-instances.md
2017-04-07 22:37:51 -04:00
Shel R
c945f29e96 Merge branch 'master' into patch-1 2017-04-07 22:35:45 -04:00
Shel R
0d4d42dce6 Merge pull request #1035 from gfaivre/add-elao-com-instance
Add mastodon.elao.com instance
2017-04-07 22:35:08 -04:00
Shel R
cf03634e74 Merge branch 'master' into add-elao-com-instance 2017-04-07 22:34:45 -04:00
Shel R
698d74a15f Merge pull request #1030 from derekcecillewis/add-infinimatix.net-instance
Add infinimatix.net to instance list
2017-04-07 22:34:10 -04:00
Shel R
d7d165db5b Merge branch 'master' into add-infinimatix.net-instance 2017-04-07 22:33:40 -04:00
Shel R
d7f4300ee3 Merge pull request #1009 from ngerakines/patch-1
Added off-the-clock.us to the list of instances
2017-04-07 22:25:45 -04:00
Shel R
7632178300 Merge branch 'master' into patch-1 2017-04-07 22:23:50 -04:00
Shel R
e4e948a21b Merge pull request #797 from AndreLewin/master
Added Esperanto translation (eo)
2017-04-07 22:21:48 -04:00
Shel R
fef478781d Merge branch 'master' into master 2017-04-07 22:21:21 -04:00
Shel R
c1a553d2c2 Merge pull request #1006 from Ninetailed/master
Description in instance list for mastodon.ninetailed.uk
2017-04-07 22:18:11 -04:00
Shel R
3e8c1a1c36 Merge branch 'master' into master 2017-04-07 22:17:53 -04:00
Shel R
9fd8bbe15c Merge pull request #992 from Eychics/master
Add mastodon.nuzgo.net in instance list
2017-04-07 22:16:49 -04:00
Shel R
1b42f717f2 Merge branch 'master' into master 2017-04-07 22:15:55 -04:00
Shel R
7004c69204 Merge pull request #991 from vladooku/patch-1
Update List-of-Mastodon-instances.md
2017-04-07 22:15:12 -04:00
Shel R
9981972844 Merge branch 'master' into patch-1 2017-04-07 22:14:08 -04:00
Shel R
5f61ef2417 Merge pull request #989 from MTNDevelopment/master
Updates instance list
2017-04-07 22:13:34 -04:00
Shel R
cf13c97cb2 Merge branch 'master' into master 2017-04-07 22:13:06 -04:00
Shel R
90a408f592 Merge pull request #986 from estuans/patch-2
Update List-of-Mastodon-instances.md
2017-04-07 22:11:55 -04:00
Shel R
451b7431c9 Merge branch 'master' into patch-2 2017-04-07 22:11:24 -04:00
Shel R
530725ba3c Merge pull request #985 from Motoma/patch-1
Add Rich.GOP
2017-04-07 22:10:22 -04:00
Shel R
6efaee30b1 Merge branch 'master' into patch-1 2017-04-07 22:09:55 -04:00
Shel R
698fe3686a Merge pull request #982 from foxiehkins/master
Adding good-dragon.com instance to list
2017-04-07 22:09:15 -04:00
Shel R
05be34a94b Merge branch 'master' into master 2017-04-07 22:08:54 -04:00
Shel R
cb58694a81 Merge pull request #976 from shug0/patch-1
Adding masto.raildecake.fr, french instance 🌻🐘
2017-04-07 22:06:06 -04:00
Shel R
e51b6bba94 Merge branch 'master' into patch-1 2017-04-07 22:05:38 -04:00
Shel R
7f393a0b68 Merge pull request #975 from tomfhowe/patch-4
Use autohiding scrollbars in Microsoft Edge
2017-04-07 22:03:35 -04:00
Shel R
9f43e3b428 Merge branch 'master' into patch-4 2017-04-07 22:02:26 -04:00
Shel R
4a40b40324 Merge pull request #973 from ZiiX/patch-1
added instance
2017-04-07 21:59:51 -04:00
Shel R
4c05f0e630 Merge branch 'master' into patch-1 2017-04-07 21:59:21 -04:00
Shel R
d438eab673 Merge pull request #972 from amandavisconti/master
Added digitalhumanities.club instance
2017-04-07 21:56:57 -04:00
Shel R
7b7bff04df Merge branch 'master' into master 2017-04-07 21:54:32 -04:00
Shel R
e659608797 Merge pull request #970 from estuans/patch-1
Update Production-guide.md
2017-04-07 21:49:57 -04:00
Shel R
ed332693fe Merge branch 'master' into patch-1 2017-04-07 21:49:18 -04:00
Shel R
881e4277fd Merge pull request #969 from Fortyseven/patch-1
Add social.bytestemplar.com to instances list
2017-04-07 21:48:58 -04:00
Shel R
5dfc9854f1 Merge branch 'master' into patch-1 2017-04-07 21:48:19 -04:00
Shel R
b300bb3b4e Merge pull request #959 from mkody/patch-1
Added im-in.space
2017-04-07 21:45:06 -04:00
Shel R
6d519e6fd1 Merge branch 'master' into patch-1 2017-04-07 21:44:16 -04:00
Shel R
d2c9cc31de Merge pull request #953 from Technowix/patch-1
Add niu.moe, cuz it's kawaii
2017-04-07 21:43:20 -04:00
Shel R
065defefac Merge branch 'master' into patch-1 2017-04-07 21:42:10 -04:00
Shel R
6bfe068904 Merge pull request #944 from VirtuBox/patch-1
Update List-of-Mastodon-instances.md
2017-04-07 21:35:34 -04:00
Shel R
bc237d17a7 Merge branch 'master' into patch-1 2017-04-07 21:34:41 -04:00
Yann GUERN
485d75a805 #1141 on remote follow
The async action is send before persist, account.id not yet generated

Pull queue receive 'nil' so no profile update.
2017-04-08 03:24:35 +02:00
Eugen Rochko
4b621188ad Fix #1165 - before_action was called before protect_from_forgery 2017-04-08 02:30:50 +02:00
David Huerta
4fb24a70d3 Merge branch 'master' into patch-1 2017-04-07 19:46:07 -04:00
Eugen Rochko
b2a7218ab7 Fix #801 - Respect webfinger's canonical response of username/domain 2017-04-08 01:07:42 +02:00
Eugen
a872f2f4c6 Merge pull request #1204 from benklop/patch-1
Update Administration-guide.md
2017-04-08 01:00:32 +02:00
Eugen
32a6f0884c Merge pull request #1200 from huertanix/patch-2
Update Heroku-guide.md
2017-04-08 01:00:06 +02:00
Eugen
a54af44975 Merge pull request #1210 from raymestalez/patch-3
Add HackerNewsBot
2017-04-08 00:59:02 +02:00
Ray Alez
f113af5350 Add HackerNewsBot
I have created a bot that will post Hacker News stories with 100+ points. Adding it to the list.
2017-04-07 15:56:02 -07:00
benklop
f578cf8331 Update Administration-guide.md
the syntax for running the rake task wasn't correct.
2017-04-07 16:31:50 -06:00
Eugen
3cb13bdd84 Merge pull request #1125 from jasonrhodes/patch-1
Email service options :P
2017-04-08 00:24:38 +02:00
Eugen
0bf31f5436 Merge pull request #1147 from fmauNeko/docker_smaller_image
Fix npm/yarn cache cleaning
2017-04-07 23:45:48 +02:00
David Huerta
a7ab2204d4 Update Heroku-guide.md
Cleaning up the heroku admin command bit to match the form used in Administration-guide.md and clarify the wording a bit.
2017-04-07 17:44:32 -04:00
David Huerta
36a83cc4f9 Update Heroku-guide.md
Removing some of the confusion around what format S3 bucket names and regions should be entered as well as providing an example of an S3 policy that follows best security practices for this sort of thing.
2017-04-07 17:29:21 -04:00
Blake
30903d5f02 Add indigo.zone to list of instances 2017-04-07 14:10:37 -07:00
Mouse Reeve
94536af96d Adds Oulipo.social to Mastodons list
This is a Mastodon with a particular constraint about
what symbols you can post.
2017-04-07 14:06:49 -07:00
Eugen
0b32b5108e Merge pull request #1191 from d3vgru/master
change suggested cipher for nginx
2017-04-07 23:02:49 +02:00
Florian Maunier
12f1cdeed1 Fix npm/yarn cache cleaning 2017-04-07 22:51:52 +02:00
Eugen
e2f024147c Merge pull request #1172 from mjankowski/mj-heroku-docs
Update heroku instructions
2017-04-07 22:50:31 +02:00
Eugen
1961825ff9 Merge pull request #1183 from thoughtbot/cp-post-status-service-specs
Add specs for PostStatusService
2017-04-07 22:30:28 +02:00
Eugen
32748c0f71 Merge pull request #1184 from thoughtbot/extract-proper-status
DRY up reblog vs original status check
2017-04-07 22:28:15 +02:00
Eugen
37a36b0bec Merge pull request #1192 from seekr/patch-2
typo
2017-04-07 22:25:48 +02:00
Eugen
c7d9b81d41 Merge pull request #1193 from thoughtbot/status-specs
Implement pending specs on Status
2017-04-07 22:25:10 +02:00
Joël Quenneville
4fdeac21f4 Implement pending specs on Status
Implement the two pending specs on `Status`: `reblogs_count` and
`favourites_count`.
2017-04-07 15:36:06 -04:00
seekr
131f505fd0 typo 2017-04-07 16:33:13 -03:00
Ed Knutson
27012aaeb6 change suggested cipher for nginx 2017-04-07 14:10:39 -05:00
Aguay-val
4c751d25e5 Add mastodon.fun 2017-04-07 20:33:52 +02:00
Chad Pytel
ad5ddd5e95 Use I18n for media attachment validation errors
These are currently user facing errors, but are not localized. This adds the
ability for these messages to be localized.
2017-04-07 14:23:18 -04:00
Chad Pytel
13c0077003 Add specs for PostStatusService
This implements all pending specs, and adds additional coverage for the
following functionality:

* Normal status creation
* Creating a reply status
* Creating a sensitive status
* Creating a status with spoiler text
* A status with no spoiler text gets an empty string for spoiler text
* Creating a status with custom visibility
* Creating a status for an application
* Processing mentions
* Processing Hashtags
* Pinging PuSH hubs
* Crawling links
* Attaching media
2017-04-07 14:21:16 -04:00
Joël Quenneville
d4c94fa004 DRY up reblog vs original status check
Checking reblog vs original status was happening in multiple places
across the app. For views, this logic was encapsulated in a helper
method named `proper_status` but in the other layers of the app, the
logic was duplicated.

Because the logic is used at all layers of the app, we extracted it into
a `Status#proper` method on the model and changed all uses of the logic
to use this method. There is now a single source of truth for this
condition.

We added test coverage to untested methods that got refactored.
2017-04-07 14:18:30 -04:00
Chad Pytel
38bec79811 Add specs for media attachment validations
There are currently not specs for the two media validations that are performed
by `PostStatusService`. This adds specs for the validations that ensure that you
cannot attach more than four files, and that a status cannot have both image and
video attachments.
2017-04-07 12:50:43 -04:00
Matt Jankowski
0f4fa59812 Update heroku instructions 2017-04-07 12:50:29 -04:00
foxiehkins
41396de7a9 Merge branch 'master' into master 2017-04-07 14:01:20 +01:00
Jason Rhodes
2ac8a590cd Moved into a comment per feedback 2017-04-07 07:43:44 -04:00
Erwan Leboucher
0c4e9fdda0 Merge branch 'master' into patch-4 2017-04-07 12:43:56 +02:00
Sergei Č
8e7d0bda40 Update List-of-Mastodon-instances.md 2017-04-07 11:54:41 +03:00
Jason Rhodes
5d43a9cae2 Email service options :P
Small addition in case people want email service options, sparkpost.com gives you 100k/mo free
2017-04-06 22:48:17 -04:00
Tristan Mahé
8a4ff30ceb updated instance hostname 2017-04-06 17:18:40 -07:00
Vladimir Mincev
56f4a94e7b Update Production-guide.md
Under ## General dependencies:
apt-get needs sudo and install was typed wrongly.
2017-04-07 01:05:32 +02:00
YDrogen
4b08b7c502 Added masto.razrnet.fr 2017-04-06 23:59:56 +02:00
Tristan Mahé
cfbd90cf44 Added pericles.world in the list of instances 2017-04-06 12:05:37 -07:00
isati
acf6436a99 Add manx.social instance 2017-04-06 19:31:25 +01:00
Erwan Leboucher
a0f1f9c664 Update List-of-Mastodon-instances.md 2017-04-06 20:18:56 +02:00
kadiix
f0d1107c53 Merge branch 'master' into patch-1 2017-04-06 19:33:36 +02:00
kadiix
02c1ad5347 Update List-of-Mastodon-instances.md 2017-04-06 19:33:17 +02:00
kadiix
a736b28646 Merge branch 'master' into patch-1 2017-04-06 19:32:31 +02:00
kadiix
0d5d3c7abe Add an instance. 2017-04-06 19:15:57 +02:00
VirtuBox
c441208e29 Merge branch 'master' into patch-1 2017-04-06 19:11:39 +02:00
awea
07495cc13f Update List-of-Mastodon-instances.md 2017-04-06 18:07:15 +02:00
Julien
6a88151eda Add meow.social 2017-04-06 14:51:42 +02:00
Lukas Fülling
30619a6716 add my instance 2017-04-06 14:22:01 +02:00
Ray Alez
eadac4e7f4 Added an instance to the list
Just launched https://hackertribe.io/, added it to the list.
2017-04-06 03:12:34 -07:00
Yann GUERN
0209b7d1b5 Update List-of-Mastodon-instances.md
Add mastodon.land instance
2017-04-06 11:07:33 +02:00
Markus R
c62696bc46 Update List-of-Mastodon-instances.md 2017-04-06 08:52:15 +02:00
Guewen FAIVRE
83530f0eef Add mastodon.elao.com instance 2017-04-06 08:46:16 +02:00
Derek Lewis
73b8e67f4b Add infinimatix.net to instance list 2017-04-06 01:15:16 -04:00
Jack Michaud
b5d87500d2 Add Northeastern University Mastodon 2017-04-05 20:58:21 -04:00
Nick Gerakines
d025c5e593 Added off-the-clock.us to the list of instances 2017-04-05 19:19:44 -04:00
Ninetailed
7aede8e720 Description in instance list for mastodon.ninetailed.uk 2017-04-05 22:35:35 +01:00
Thibaut (Eychics)
eb98c99924 Add mastodon.nuzgo.net 2017-04-05 20:45:23 +02:00
ava
6b41fb2e6f Update List-of-Mastodon-instances.md 2017-04-05 21:44:13 +03:00
André Lewin
bf7cefa516 Merge branch 'master' into master 2017-04-05 20:28:58 +02:00
Brad Janke
65b3a2a5a6 Adds mtndevelopment 2017-04-05 13:14:27 -05:00
Ben Field
8fa8004a2b Update List-of-Mastodon-instances.md 2017-04-05 18:20:08 +01:00
Christopher Gilbert
e5566ac6a6 Add Rich.GOP 2017-04-05 13:05:27 -04:00
foxiehkins
ac1989d2c0 Update description on good-dragon.com 2017-04-05 17:49:04 +01:00
foxiehkins
32d4b51939 Add good-dragon.com 2017-04-05 17:47:52 +01:00
Thomas Alberola
b38bd58921 Adding masto.raildecake.fr, french instance 🌻🐘 2017-04-05 18:15:39 +02:00
tom
8989569dd4 Update components.scss
Use nicer scrollbars in MS edge
2017-04-05 12:10:25 -04:00
ZiiX
96812a6c79 added instance 2017-04-05 09:05:57 -07:00
Amanda Visconti
a31f5765af Added digitalhumanities.club instance 2017-04-05 12:04:21 -04:00
Ben Field
41a78be25e Update Production-guide.md
Corrected spelling error for "install"
2017-04-05 16:56:51 +01:00
Toby Deshane
9cf0b5b255 Add social.bytestemplar.com to instances list 2017-04-05 11:49:36 -04:00
Kody
4de3182dc8 Added im-in.space 2017-04-05 17:00:03 +02:00
Technowix
9a534d1df6 Add niu.moe, cuz it's kawaii 2017-04-05 16:18:52 +02:00
VirtuBox
28fb01c71a Update List-of-Mastodon-instances.md 2017-04-05 16:02:47 +02:00
Valentin_NC
93e53a3311 add mastodon.cloud to List of instances 2017-04-05 20:36:21 +11:00
Valentin_NC
b73cee9774 add volume for redis container 2017-04-05 14:13:22 +11:00
Valentin_NC
4512fde181 add persistance to Postresql container 2017-04-05 09:06:08 +11:00
axolotl
8736ef50ad Added Esperanto translation inside the javascripts folder 2017-04-04 20:54:42 +02:00
axolotl
0700521ef3 added Esperanto (eo) 2017-04-03 20:22:50 +02:00
119 changed files with 1771 additions and 401 deletions

View File

@@ -29,6 +29,7 @@ OTP_SECRET=
# DEFAULT_LOCALE=de
# E-mail configuration
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
SMTP_SERVER=smtp.mailgun.org
SMTP_PORT=587
SMTP_LOGIN=
@@ -62,3 +63,7 @@ SMTP_FROM_ADDRESS=notifications@example.com
# Streaming API integration
# STREAMING_API_BASE_URL=
# Advanced settings
# If you need to use pgBouncer, you need to disable prepared statements:
# PREPARED_STATEMENTS=false

View File

@@ -29,7 +29,8 @@ RUN BUILD_DEPS=" \
&& npm install -g npm@3 && npm install -g yarn \
&& bundle install --deployment --without test development \
&& yarn \
&& npm cache clean \
&& yarn cache clean \
&& npm -g cache clean \
&& apk del $BUILD_DEPS \
&& rm -rf /tmp/* /var/cache/apk/*

37
Gemfile
View File

@@ -21,37 +21,38 @@ gem 'paperclip', '~> 5.1'
gem 'paperclip-av-transcoder'
gem 'aws-sdk', '>= 2.0'
gem 'http'
gem 'httplog'
gem 'addressable'
gem 'nokogiri'
gem 'link_header'
gem 'ostatus2'
gem 'goldfinger'
gem 'devise'
gem 'devise-two-factor'
gem 'doorkeeper'
gem 'rabl'
gem 'rqrcode'
gem 'twitter-text'
gem 'ox'
gem 'oj'
gem 'hiredis'
gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis']
gem 'fast_blank'
gem 'goldfinger'
gem 'hiredis'
gem 'htmlentities'
gem 'simple_form'
gem 'will_paginate'
gem 'http'
gem 'http_accept_language'
gem 'httplog'
gem 'link_header'
gem 'nokogiri'
gem 'oj'
gem 'ostatus2'
gem 'ox'
gem 'rabl'
gem 'rack-attack'
gem 'rack-cors', require: 'rack/cors'
gem 'rack-timeout'
gem 'rails-settings-cached'
gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis']
gem 'rqrcode'
gem 'ruby-oembed', require: 'oembed'
gem 'sidekiq'
gem 'sidekiq-unique-jobs'
gem 'rails-settings-cached'
gem 'simple-navigation'
gem 'simple_form'
gem 'statsd-instrument'
gem 'ruby-oembed', require: 'oembed'
gem 'rack-timeout'
gem 'twitter-text'
gem 'tzinfo-data'
gem 'will_paginate'
gem 'react-rails'
gem 'browserify-rails'

View File

@@ -184,6 +184,7 @@ GEM
http-cookie (1.0.3)
domain_name (~> 0.5)
http-form_data (1.0.1)
http_accept_language (2.1.0)
http_parser.rb (0.6.0)
httplog (0.3.2)
colorize
@@ -473,6 +474,7 @@ DEPENDENCIES
hiredis
htmlentities
http
http_accept_language
httplog
i18n-tasks (~> 0.9.6)
jquery-rails
@@ -522,4 +524,4 @@ RUBY VERSION
ruby 2.3.1p112
BUNDLED WITH
1.14.3
1.14.5

View File

@@ -17,7 +17,7 @@ Click on the screenshot to watch a demo of the UI:
[youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU
Focus of the project on a clean REST API and a good user interface. Ruby on Rails is used for the back-end, while React.js and Redux are used for the dynamic front-end. A static front-end for public resources (profiles and statuses) is also provided.
The project focus is a clean REST API and a good user interface. Ruby on Rails is used for the back-end, while React.js and Redux are used for the dynamic front-end. A static front-end for public resources (profiles and statuses) is also provided.
If you would like, you can [support the development of this project on Patreon][patreon]. Alternatively, you can donate to this BTC address: `17j2g7vpgHhLuXhN4bueZFCvdxxieyRVWd`

View File

@@ -26,6 +26,10 @@
"description": "The secret key base",
"generator": "secret"
},
"OTP_SECRET": {
"description": "One-time password secret",
"generator": "secret"
},
"SINGLE_USER_MODE": {
"description": "Should the instance run in single user mode? (Disable registrations, redirect to front page)",
"value": "false",

View File

@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 1000 1000">
<path d="M527.194 543.7a28.362 28.362 0 0 0-56.723 0 25.73 25.73 0 0 0 2.67 11.674 26.42 26.42 0 0 0 5.672 8.34 28.2 28.2 0 0 0 40.04 0 31.87 31.87 0 0 0 6.006-8.34 28.8 28.8 0 0 0 2.336-11.674m-48.382-113.413a28.308 28.308 0 1 0 40.04 40.027 37.2 37.2 0 0 0 4.67-5.67 28.092 28.092 0 0 0 3.67-14.343 27.29 27.29 0 0 0-8.34-20.012 28.24 28.24 0 0 0-5.006-4 26.958 26.958 0 0 0-15.015-4.336 27.31 27.31 0 0 0-20.02 8.34m20.02-101.735a28.476 28.476 0 1 0 20.02 8.34 27.31 27.31 0 0 0-20.02-8.34M231.9 573.717a28.18 28.18 0 1 0 8.342 20.012 27.308 27.308 0 0 0-8.342-20.014m-40.04-93.4a28.352 28.352 0 0 0 20.02 48.366 26.958 26.958 0 0 0 15.015-4.336 28.255 28.255 0 0 0 5.005-4 27.29 27.29 0 0 0 8.342-20.013 28.09 28.09 0 0 0-3.67-14.343 37.21 37.21 0 0 0-4.67-5.67 28.2 28.2 0 0 0-40.04 0m40.04-93.4a28.2 28.2 0 0 0-40.04 0 26.425 26.425 0 0 0-5.673 8.34 25.73 25.73 0 0 0-2.67 11.673 28.315 28.315 0 0 0 48.38 20.018 27.29 27.29 0 0 0 8.342-20.012 28.8 28.8 0 0 0-2.336-11.674 31.87 31.87 0 0 0-6.006-8.34m550.55 178.453a28.476 28.476 0 1 0 20.02 8.34 27.31 27.31 0 0 0-20.02-8.34m20.02-85.057a28.2 28.2 0 0 0-40.04 0 37.2 37.2 0 0 0-4.672 5.67 28.092 28.092 0 0 0-3.67 14.343 27.29 27.29 0 0 0 8.342 20.013 28.248 28.248 0 0 0 5.005 4 26.96 26.96 0 0 0 15.015 4.336 28.3 28.3 0 0 0 20.02-48.366m-46.046-85.057a28.8 28.8 0 0 0-2.336 11.673 28.362 28.362 0 0 0 56.723 0 25.73 25.73 0 0 0-2.668-11.674 26.427 26.427 0 0 0-5.672-8.34 28.2 28.2 0 0 0-40.04 0 31.86 31.86 0 0 0-6.007 8.343z" fill="#2b90d9"/>
<path d="M853.52 146.764Q707.04 0 499.833 0 292.96 0 146.48 146.764 0 293.2 0 500q0 207.138 146.48 353.57T499.833 1000q207.207 0 353.687-146.43T1000 500q0-206.8-146.48-353.236zM213.547 708.806h-3.337q-43.043 0-73.407-30.02-30.03-30.354-30.03-73.382V395.93v-.666q1.335-41.027 30.03-69.713 30.364-30.35 73.407-30.35t73.073 30.354q29.363 29.02 30.364 70.38V615.41q2.336 55.037 46.713 93.4zM600.6 554.7q-1 41.36-30.364 70.38-30.03 30.353-73.073 30.354t-73.407-30.354q-28.7-28.686-30.03-69.713V345.23q0-43.03 30.03-73.382 30.364-30.02 73.407-30.02h150.15q-44.378 38.36-46.713 93.4zm286.954 50.7q0 43.03-30.03 73.382-30.364 30.02-73.407 30.02h-150.15q44.378-38.36 46.713-93.4v-219.47q1-41.362 30.364-70.38 30.03-30.355 73.073-30.355t73.407 30.354q28.7 28.687 30.03 69.714V605.4z" fill="#2b90d9"/>
</svg>

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -15,6 +15,7 @@ const ColumnCollapsable = React.createClass({
propTypes: {
icon: React.PropTypes.string.isRequired,
title: React.PropTypes.string,
fullHeight: React.PropTypes.number.isRequired,
children: React.PropTypes.node,
onCollapse: React.PropTypes.func
@@ -39,13 +40,13 @@ const ColumnCollapsable = React.createClass({
},
render () {
const { icon, fullHeight, children } = this.props;
const { icon, title, fullHeight, children } = this.props;
const { collapsed } = this.state;
const collapsedClassName = collapsed ? 'collapsable-collapsed' : 'collapsable';
return (
<div style={{ position: 'relative' }}>
<div style={{...iconStyle }} className={`column-icon ${collapsedClassName}`} onClick={this.handleToggleCollapsed}><i className={`fa fa-${icon}`} /></div>
<div title={`${title}`} style={{...iconStyle }} className={`column-icon ${collapsedClassName}`} onClick={this.handleToggleCollapsed}><i className={`fa fa-${icon}`} /></div>
<Motion defaultStyle={{ opacity: 0, height: 0 }} style={{ opacity: spring(collapsed ? 0 : 100), height: spring(collapsed ? 0 : fullHeight, collapsed ? undefined : { stiffness: 150, damping: 9 }) }}>
{({ opacity, height }) =>

View File

@@ -47,6 +47,7 @@ import pt from 'react-intl/locale-data/pt';
import hu from 'react-intl/locale-data/hu';
import uk from 'react-intl/locale-data/uk';
import fi from 'react-intl/locale-data/fi';
import eo from 'react-intl/locale-data/eo';
import getMessagesForLocale from '../locales';
import { hydrateStore } from '../actions/store';
import createStream from '../stream';
@@ -59,7 +60,7 @@ const browserHistory = useRouterHistory(createBrowserHistory)({
basename: '/web'
});
addLocaleData([...en, ...de, ...es, ...fr, ...pt, ...hu, ...uk, ...fi]);
addLocaleData([...en, ...de, ...es, ...fr, ...pt, ...hu, ...uk, ...fi, ...eo]);
const Mastodon = React.createClass({

View File

@@ -6,7 +6,8 @@ import SettingToggle from '../../notifications/components/setting_toggle';
import SettingText from './setting_text';
const messages = defineMessages({
filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' }
filter_regex: { id: 'home.column_settings.filter_regex', defaultMessage: 'Filter out by regular expressions' },
settings: { id: 'home.settings', defaultMessage: 'Column settings' }
});
const outerStyle = {
@@ -39,7 +40,7 @@ const ColumnSettings = React.createClass({
const { settings, onChange, onSave, intl } = this.props;
return (
<ColumnCollapsable icon='sliders' fullHeight={209} onCollapse={onSave}>
<ColumnCollapsable icon='sliders' title={intl.formatMessage(messages.settings)} fullHeight={209} onCollapse={onSave}>
<div className='column-settings--outer' style={outerStyle}>
<span className='column-settings--section' style={sectionStyle}><FormattedMessage id='home.column_settings.basic' defaultMessage='Basic' /></span>

View File

@@ -1,3 +1,9 @@
import { defineMessages, injectIntl } from 'react-intl';
const messages = defineMessages({
clear: { id: 'notifications.clear', defaultMessage: 'Clear notifications' }
});
const iconStyle = {
fontSize: '16px',
padding: '15px',
@@ -8,14 +14,22 @@ const iconStyle = {
zIndex: '2'
};
const ClearColumnButton = ({ onClick }) => (
<div className='column-icon' tabindex='0' style={iconStyle} onClick={onClick}>
<i className='fa fa-trash' />
</div>
);
const ClearColumnButton = React.createClass({
ClearColumnButton.propTypes = {
onClick: React.PropTypes.func.isRequired
};
propTypes: {
onClick: React.PropTypes.func.isRequired,
intl: React.PropTypes.object.isRequired
},
export default ClearColumnButton;
render () {
const { intl } = this.props;
return (
<div title={intl.formatMessage(messages.clear)} className='column-icon' tabIndex='0' style={iconStyle} onClick={this.props.onClick}>
<i className='fa fa-eraser' />
</div>
);
}
})
export default injectIntl(ClearColumnButton);

View File

@@ -1,9 +1,13 @@
import PureRenderMixin from 'react-addons-pure-render-mixin';
import ImmutablePropTypes from 'react-immutable-proptypes';
import { FormattedMessage } from 'react-intl';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import ColumnCollapsable from '../../../components/column_collapsable';
import SettingToggle from './setting_toggle';
const messages = defineMessages({
settings: { id: 'notifications.settings', defaultMessage: 'Column settings' }
});
const outerStyle = {
padding: '15px'
};
@@ -30,14 +34,14 @@ const ColumnSettings = React.createClass({
mixins: [PureRenderMixin],
render () {
const { settings, onChange, onSave } = this.props;
const { settings, intl, onChange, onSave } = this.props;
const alertStr = <FormattedMessage id='notifications.column_settings.alert' defaultMessage='Desktop notifications' />;
const showStr = <FormattedMessage id='notifications.column_settings.show' defaultMessage='Show in column' />;
const soundStr = <FormattedMessage id='notifications.column_settings.sound' defaultMessage='Play sound' />;
return (
<ColumnCollapsable icon='sliders' fullHeight={616} onCollapse={onSave}>
<ColumnCollapsable icon='sliders' title={intl.formatMessage(messages.settings)} fullHeight={616} onCollapse={onSave}>
<div className='column-settings--outer' style={outerStyle}>
<span className='column-settings--section' style={sectionStyle}><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
@@ -77,4 +81,4 @@ const ColumnSettings = React.createClass({
});
export default ColumnSettings;
export default injectIntl(ColumnSettings);

View File

@@ -1,15 +1,15 @@
const en = {
"column_back_button.label": "Zurück",
"lightbox.close": "Schließen",
"loading_indicator.label": "Lade...",
"loading_indicator.label": "Lade",
"status.mention": "Erwähnen",
"status.delete": "Löschen",
"status.reply": "Antworten",
"status.reblog": "Teilen",
"status.favourite": "Favorisieren",
"status.reblogged_by": "{name} teilte",
"status.sensitive_warning": "Sensible Inhalte",
"status.sensitive_toggle": "Klicken um zu zeigen",
"status.sensitive_warning": "Heikle Inhalte",
"status.sensitive_toggle": "Klicke, um sie zu sehen",
"status.open": "Öffnen",
"video_player.toggle_sound": "Ton umschalten",
"account.mention": "Erwähnen",
@@ -20,17 +20,17 @@ const en = {
"account.follow": "Folgen",
"account.posts": "Beiträge",
"account.follows": "Folgt",
"account.followers": "Folger",
"account.followers": "Folgende",
"account.follows_you": "Folgt dir",
"account.requested": "Warte auf Erlaubnis",
"getting_started.heading": "Erste Schritte",
"getting_started.about_addressing": "Du kannst Leuten folgen, falls du ihren Nutzernamen und ihre Domain kennst, in dem du eine e-mail-artige Addresse in das Suchfeld oben an der Seite eingibst.",
"getting_started.about_shortcuts": "Falls der Zielnutzer an derselben Domain ist wie du, funktioniert der Nutzername auch alleine. Das gilt auch für Erwähnungen in Beiträgen.",
"getting_started.about_addressing": "Du kannst Leuten folgen, falls du ihren Nutzernamen und ihre Domain kennst, in dem du eine e-mail-artige Addresse in das Suchfeld oben auf der Seite eingibst.",
"getting_started.about_shortcuts": "Falls die Person auf derselben Domain ist wie du, reicht auch ihr Nutzername alleine. Das gilt auch für Erwähnungen in Beiträgen.",
"getting_started.about_developer": "Der Entwickler des Projekts kann unter Gargron@mastodon.social gefunden werden",
"getting_started.open_source_notice": "Mastodon ist quelloffene Software. Du kannst auf {github} dazu beitragen oder Probleme melden.",
"column.home": "Home",
"column.mentions": "Erwähnungen",
"column.public": "Gesamtes Bekanntes Netz",
"column.public": "Gesamtes bekanntes Netz",
"column.notifications": "Mitteilungen",
"column.follow_requests": "Folgeanfragen",
"tabs_bar.compose": "Schreiben",
@@ -38,11 +38,11 @@ const en = {
"tabs_bar.mentions": "Erwähnungen",
"tabs_bar.public": "Gesamtes Netz",
"tabs_bar.notifications": "Mitteilungen",
"compose_form.placeholder": "Worüber möchstest du schreiben?",
"compose_form.placeholder": "Worüber möchtest du schreiben?",
"compose_form.publish": "Tröt",
"compose_form.sensitive": "Medien als sensitiv markieren",
"compose_form.unlisted": "Öffentlich nicht auflisten",
"compose_form.sensitive": "Medien als heikel markieren",
"compose_form.private": "Als privat markieren",
"compose_form.unlisted": "Nicht öffentlich auflisten",
"navigation_bar.edit_profile": "Profil bearbeiten",
"navigation_bar.preferences": "Einstellungen",
"navigation_bar.public_timeline": "Öffentlich",
@@ -52,15 +52,15 @@ const en = {
"search.placeholder": "Suche",
"search.account": "Konto",
"search.hashtag": "Hashtag",
"upload_button.label": "Media-Datei anfügen",
"upload_button.label": "Mediendatei hinzufügen",
"upload_form.undo": "Entfernen",
"notification.follow": "{name} folgt dir",
"notification.favourite": "{name} favorisierte deinen Status",
"notification.reblog": "{name} teilte deinen Status",
"notification.mention": "{name} erwähnte dich",
"notifications.column_settings.alert": "Desktop-Benachrichtigunen",
"notifications.column_settings.alert": "Desktop-Benachrichtigungen",
"notifications.column_settings.show": "In der Spalte anzeigen",
"notifications.column_settings.follow": "Neue Folger:",
"notifications.column_settings.follow": "Neue Folgende:",
"notifications.column_settings.favourite": "Favorisierungen:",
"notifications.column_settings.mention": "Erwähnungen:",
"notifications.column_settings.reblog": "Geteilte Beiträge:",

View File

@@ -10,6 +10,10 @@ const en = {
"status.reblogged_by": "{name} boosted",
"status.sensitive_warning": "Sensitive content",
"status.sensitive_toggle": "Click to view",
"status.show_more": "Show more",
"status.show_less": "Show less",
"status.open": "Expand this status",
"status.report": "Report @{name}",
"video_player.toggle_sound": "Toggle sound",
"account.mention": "Mention @{name}",
"account.edit_profile": "Edit profile",

View File

@@ -0,0 +1,68 @@
const eo = {
"column_back_button.label": "Reveni",
"lightbox.close": "Fermi",
"loading_indicator.label": "Ŝarĝanta...",
"status.mention": "Mencii @{name}",
"status.delete": "Forigi",
"status.reply": "Respondi",
"status.reblog": "Diskonigi",
"status.favourite": "Favori",
"status.reblogged_by": "{name} diskonigita",
"status.sensitive_warning": "Tikla enhavo",
"status.sensitive_toggle": "Alklaki por vidi",
"video_player.toggle_sound": "Aktivigi sonojn",
"account.mention": "Mencii @{name}",
"account.edit_profile": "Redakti la profilon",
"account.unblock": "Malbloki @{name}",
"account.unfollow": "Malsekvi",
"account.block": "Bloki @{name}",
"account.follow": "Sekvi",
"account.posts": "Mesaĝoj",
"account.follows": "Sekvatoj",
"account.followers": "Sekvantoj",
"account.follows_you": "Sekvas vin",
"account.requested": "Atendas aprobon",
"getting_started.heading": "Por komenci",
"getting_started.about_addressing": "Vi povas sekvi homojn se vi konas la uzantnomon kaj domajnon tajpinte retpoŝtecan adreson en la serĉilon.",
"getting_started.about_shortcuts": "Se la celita uzanto troviĝas en la sama domajno de vi, uzi nur la uzantnomon sufiĉos. La sama regulo validas por mencii aliajn uzantojn en mesaĝo.",
"getting_started.open_source_notice": "Mastodon estas malfermitkoda programo. Vi povas kontribui aŭ raporti problemojn en github je {github}. {apps}.",
"column.home": "Hejmo",
"column.community": "Loka tempolinio",
"column.public": "Fratara tempolinio",
"column.notifications": "Sciigoj",
"tabs_bar.compose": "Ekskribi",
"tabs_bar.home": "Hejmo",
"tabs_bar.mentions": "Sciigoj",
"tabs_bar.public": "Fratara tempolinio",
"tabs_bar.notifications": "Sciigoj",
"compose_form.placeholder": "Pri kio vi pensas?",
"compose_form.publish": "Hup",
"compose_form.sensitive": "Marki ke la enhavo estas tikla",
"compose_form.spoiler": "Kaŝi la tekston malantaŭ averto",
"compose_form.private": "Marki ke la enhavo estas privata",
"compose_form.privacy_disclaimer": "Via privata mesaĝo estos sendita nur al menciitaj uzantoj en {domains}. Ĉu vi fidas {domainsCount, plural, one {tiun servilon} other {tiujn servilojn}}? Mesaĝa privateco funkcias nur en aperaĵoj de Mastodon. Se {domains} {domainsCount, plural, one {ne estas aperaĵo de Mastodon} other {ne estas aperaĵoj de Mastodon}}, estos neniu indiko ke via mesaĝo estas privata, kaj ĝi povus esti diskonigita aŭ videbligita al necelitaj ricevantoj.",
"compose_form.unlisted": "Ne afiŝi en publikaj tempolinioj",
"navigation_bar.edit_profile": "Redakti la profilon",
"navigation_bar.preferences": "Preferoj",
"navigation_bar.community_timeline": "Loka tempolinio",
"navigation_bar.public_timeline": "Fratara tempolinio",
"navigation_bar.logout": "Elsaluti",
"reply_indicator.cancel": "Rezigni",
"search.placeholder": "Serĉi",
"search.account": "Konto",
"search.hashtag": "Kradvorto",
"upload_button.label": "Aldoni enhavaĵon",
"upload_form.undo": "Malfari",
"notification.follow": "{name} sekvis vin",
"notification.favourite": "{name} favoris vian mesaĝon",
"notification.reblog": "{name} diskonigis vian mesaĝon",
"notification.mention": "{name} menciis vin",
"notifications.column_settings.alert": "Retumilaj atentigoj",
"notifications.column_settings.show": "Montri en kolono",
"notifications.column_settings.follow": "Novaj sekvantoj:",
"notifications.column_settings.favourite": "Favoroj:",
"notifications.column_settings.mention": "Mencioj:",
"notifications.column_settings.reblog": "Diskonigoj:",
};
export default eo;

View File

@@ -10,6 +10,11 @@ const fr = {
"status.reblogged_by": "{name} a partagé :",
"status.sensitive_warning": "Contenu délicat",
"status.sensitive_toggle": "Cliquer pour dévoiler",
"status.show_more": "Déplier",
"status.show_less": "Replier",
"status.open": "Déplier ce status",
"status.report": "Signaler @{name}",
"status.load_more": "Charger plus",
"video_player.toggle_sound": "Mettre/Couper le son",
"account.mention": "Mentionner",
"account.edit_profile": "Modifier le profil",
@@ -35,18 +40,18 @@ const fr = {
"column.community": "Fil public local",
"column.public": "Fil public global",
"column.notifications": "Notifications",
"column.public": "Fil public",
"column.blocks": "Utilisateurs bloqués",
"column.favourites": "Favoris",
"empty_column.notifications": "Vous navez pas encore de notification. Interagissez avec dautres utilisateurs⋅trices pour débuter la conversation.",
"tabs_bar.compose": "Composer",
"tabs_bar.home": "Accueil",
"tabs_bar.mentions": "Mentions",
"tabs_bar.public": "Fil public global",
"tabs_bar.notifications": "Notifications",
"compose_form.placeholder": "Quavez-vous en tête ?",
"compose_form.publish": "Pouet ",
"compose_form.publish": "Pouet",
"compose_form.sensitive": "Marquer le média comme délicat",
"compose_form.spoiler": "Masquer le texte par un avertissement",
"compose_form.spoiler": "Masquer le texte derrière un avertissement",
"compose_form.private": "Rendre privé",
"compose_form.privacy_disclaimer": "Votre statut privé va être transmis aux personnes mentionnées sur {domains}. Avez-vous confiance en {domainsCount, plural, one {ce serveur} other {ces serveurs}} pour ne pas divulguer votre statut ? Les statuts privés ne fonctionnent que sur les instances de Mastodons. Si {domains} {domainsCount, plural, one {n'est pas une instance de Mastodon} other {ne sont pas des instances de Mastodon}}, il n'y aura aucune indication que votre statut est privé, et il pourrait être partagé ou rendu visible d'une autre manière à d'autres personnes imprévues",
"compose_form.unlisted": "Ne pas afficher dans les fils publics",
@@ -58,7 +63,6 @@ const fr = {
"navigation_bar.blocks": "Utilisateurs bloqués",
"navigation_bar.favourites": "Favoris",
"navigation_bar.info": "Plus d'informations",
"notification.favourite": "{name} a ajouté à ses favoris :",
"navigation_bar.logout": "Déconnexion",
"reply_indicator.cancel": "Annuler",
"search.placeholder": "Chercher",

View File

@@ -6,6 +6,7 @@ import fr from './fr';
import pt from './pt';
import uk from './uk';
import fi from './fi';
import eo from './eo';
const locales = {
en,
@@ -15,7 +16,8 @@ const locales = {
fr,
pt,
uk,
fi
fi,
eo
};
export default function getMessagesForLocale (locale) {

View File

@@ -2,54 +2,71 @@ const pt = {
"column_back_button.label": "Voltar",
"lightbox.close": "Fechar",
"loading_indicator.label": "Carregando...",
"status.mention": "Menção",
"status.delete": "Deletar",
"status.mention": "Mencionar @{name}",
"status.delete": "Eliminar",
"status.reply": "Responder",
"status.reblog": "Reblogar",
"status.favourite": "Favoritar",
"status.reblogged_by": "{name} reblogou",
"video_player.toggle_sound": "Alterar som",
"account.mention": "Menção",
"status.reblog": "Partilhar",
"status.favourite": "Adicionar aos favoritos",
"status.reblogged_by": "{name} partilhou",
"status.sensitive_warning": "Conteúdo sensível",
"status.sensitive_toggle": "Clique para ver",
"status.show_more": "Mostrar mais",
"status.show_less": "Mostrar menos",
"status.open": "Expandir",
"status.report": "Reportar @{name}",
"video_player.toggle_sound": "Ligar/Desligar som",
"account.mention": "Mencionar @{name}",
"account.edit_profile": "Editar perfil",
"account.unblock": "Desbloquear",
"account.unfollow": "Unfollow",
"account.block": "Bloquear",
"account.unblock": "Não bloquear @{name}",
"account.unfollow": "Não seguir",
"account.block": "Bloquear @{name}",
"account.follow": "Seguir",
"account.block": "Bloquear",
"account.posts": "Posts",
"account.follows": "Segue",
"account.followers": "Seguidores",
"account.follows_you": "Segue você",
"account.follows_you": "É teu seguidor",
"account.requested": "A aguardar aprovação",
"getting_started.heading": "Primeiros passos",
"getting_started.about_addressing": "Podes seguir pessoas se sabes o nome de usuário deles e o domínio em que estão entrando um endereço similar a e-mail no campo no topo da barra lateral.",
"getting_started.about_addressing": "Podes seguir pessoas se sabes o nome de usuário deles e o domínio em que estão colocando um endereço similar a e-mail no campo no topo da barra lateral.",
"getting_started.about_shortcuts": "Se o usuário alvo está no mesmo domínio, só o nome funcionará. A mesma regra se aplica a mencionar pessoas nas postagens.",
"getting_started.about_developer": "O desenvolvedor desse projeto pode ser seguido em Gargron@mastodon.social",
"getting_started.open_source_notice": "Mastodon é software de fonte aberta. Podes contribuir ou repostar problemas no GitHub do projecto: {github}. {apps}.",
"column.home": "Home",
"column.mentions": "Menções",
"column.community": "Local",
"column.public": "Público",
"tabs_bar.compose": "Compôr",
"column.notifications": "Notificações",
"tabs_bar.compose": "Criar",
"tabs_bar.home": "Home",
"tabs_bar.mentions": "Menções",
"tabs_bar.public": "Público",
"tabs_bar.notifications": "Notificações",
"compose_form.placeholder": "Que estás pensando?",
"compose_form.placeholder": "Em que estás a pensar?",
"compose_form.publish": "Publicar",
"compose_form.sensitive": "Marcar conteúdo como sensível",
"compose_form.unlisted": "Modo não-listado",
"compose_form.sensitive": "Media com conteúdo sensível",
"compose_form.spoiler": "Esconder texto com aviso",
"compose_form.private": "Tornar privado",
"compose_form.privacy_disclaimer": "O teu conteúdo privado vai ser partilhado com os utilizadores do {domains}. Confias {domainsCount, plural, one {neste servidor} other {nestes servidores}}? A privacidade só funciona em instâncias do Mastodon. Se {domains} {domainsCount, plural, one {não é uma instância} other {não são instâncias}}, não existem indicadores da privacidade da tua partilha, e podem ser partilhados com outros.",
"compose_form.unlisted": "Não mostrar na listagem pública",
"navigation_bar.edit_profile": "Editar perfil",
"navigation_bar.preferences": "Preferências",
"navigation_bar.public_timeline": "Timeline Pública",
"navigation_bar.logout": "Logout",
"navigation_bar.community_timeline": "Local",
"navigation_bar.public_timeline": "Público",
"navigation_bar.logout": "Sair",
"reply_indicator.cancel": "Cancelar",
"search.placeholder": "Busca",
"search.placeholder": "Pesquisar",
"search.account": "Conta",
"search.hashtag": "Hashtag",
"upload_button.label": "Adicionar media",
"upload_form.undo": "Desfazer",
"notification.follow": "{name} seguiu você",
"notification.favourite": "{name} favoritou seu post",
"notification.reblog": "{name} reblogou o seu post",
"notification.mention": "{name} mecionou você"
"upload_form.undo": "Anular",
"notification.follow": "{name} seguiu-te",
"notification.favourite": "{name} adicionou o teu post aos favoritos",
"notification.reblog": "{name} partilhou o teu post",
"notification.mention": "{name} mencionou-te",
"notifications.column_settings.alert": "Notificações no computador",
"notifications.column_settings.show": "Mostrar nas colunas",
"notifications.column_settings.follow": "Novos seguidores:",
"notifications.column_settings.favourite": "Favoritos:",
"notifications.column_settings.mention": "Menções:",
"notifications.column_settings.reblog": "Partilhas:",
};
export default pt;

View File

@@ -114,10 +114,6 @@
padding: 20px;
}
.screenshot-with-signup .mascot {
display: none;
}
.features-list {
display: block;
}
@@ -158,6 +154,14 @@
color: $color5;
}
}
@media screen and (max-width: 500px) {
flex-direction: column;
.section {
text-align: left;
}
}
}
.owner {
@@ -281,6 +285,15 @@
}
}
}
@media screen and (max-width: 625px) {
flex-direction: column;
.sidebar {
border: 1px solid lighten($color1, 10%);
width: auto;
}
}
}
.features-list {
@@ -339,6 +352,16 @@
}
}
}
@media screen and (max-width: 625px) {
.mascot {
display: none;
}
.simple_form, .closed-registrations-message {
flex: auto;
}
}
}
.closed-registrations-message {

View File

@@ -1,5 +1,9 @@
@import 'variables';
.app-body{
-ms-overflow-style: -ms-autohiding-scrollbar;
}
.button {
background-color: darken($color4, 3%);
font-family: inherit;
@@ -710,7 +714,15 @@ a.status__content__spoiler-link {
@media screen and (min-width: 360px) {
.columns-area {
margin: 10px;
margin: 0;
}
.column:first-child, .drawer:first-child {
margin-left: 0;
}
.column:last-child, .drawer:last-child {
margin-right: 0;
}
}
@@ -812,6 +824,7 @@ a.status__content__spoiler-link {
}
.column, .drawer {
margin: 10px;
margin-left: 5px;
margin-right: 5px;
flex: 0 0 auto;
@@ -819,11 +832,11 @@ a.status__content__spoiler-link {
}
.column:first-child, .drawer:first-child {
margin-left: 0;
margin-left: 10px;
}
.column:last-child, .drawer:last-child {
margin-right: 0;
margin-right: 10px;
}
@media screen and (max-width: 1024px) {
@@ -881,6 +894,10 @@ a.status__content__spoiler-link {
}
@media screen and (min-width: 360px) {
.columns-area {
margin: 10px;
}
.tabs-bar {
margin: 10px;
margin-bottom: 0;
@@ -891,6 +908,12 @@ a.status__content__spoiler-link {
}
}
@media screen and (min-width: 1024px) {
.columns-area {
margin: 0;
}
}
@media screen and (min-width: 600px) {
.tabs-bar__link {
.fa {

View File

@@ -25,6 +25,10 @@ code {
margin-bottom: 15px;
}
strong {
font-weight: 500;
}
.label_input {
display: flex;
@@ -224,7 +228,12 @@ code {
}
}
.qr-wrapper {
display: flex;
}
.qr-code {
flex: 0 0 auto;
background: #fff;
padding: 4px;
margin-bottom: 20px;
@@ -236,3 +245,13 @@ code {
margin: 0;
}
}
.qr-alternative {
margin-left: 10px;
color: $color3;
samp {
display: block;
font-size: 14px;
}
}

View File

@@ -2,30 +2,25 @@
class AboutController < ApplicationController
before_action :set_body_classes
before_action :set_instance_presenter, only: [:show, :more]
def index
@description = Setting.site_description
@open_registrations = Setting.open_registrations
@closed_registrations_message = Setting.closed_registrations_message
def show; end
@user = User.new
@user.build_account
end
def more
@description = Setting.site_description
@extended_description = Setting.site_extended_description
@contact_account = Account.find_local(Setting.site_contact_username)
@contact_email = Setting.site_contact_email
@user_count = Rails.cache.fetch('user_count') { User.count }
@status_count = Rails.cache.fetch('local_status_count') { Status.local.count }
@domain_count = Rails.cache.fetch('distinct_domain_count') { Account.distinct.count(:domain) }
end
def more; end
def terms; end
private
def new_user
User.new.tap(&:build_account)
end
helper_method :new_user
def set_instance_presenter
@instance_presenter = InstancePresenter.new
end
def set_body_classes
@body_classes = 'about-body'
end

View File

@@ -1,10 +1,11 @@
# frozen_string_literal: true
class Api::V1::AccountsController < ApiController
before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
before_action -> { doorkeeper_authorize! :read }, except: [:follow, :unfollow, :block, :unblock, :mute, :unmute, :update_credentials]
before_action -> { doorkeeper_authorize! :follow }, only: [:follow, :unfollow, :block, :unblock, :mute, :unmute]
before_action -> { doorkeeper_authorize! :write }, only: [:update_credentials]
before_action :require_user!, except: [:show, :following, :followers, :statuses]
before_action :set_account, except: [:verify_credentials, :suggestions, :search]
before_action :set_account, except: [:verify_credentials, :update_credentials, :suggestions, :search]
respond_to :json
@@ -15,15 +16,19 @@ class Api::V1::AccountsController < ApiController
render action: :show
end
def update_credentials
current_account.update!(account_params)
@account = current_account
render action: :show
end
def following
results = Follow.where(account: @account).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
accounts = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.target_account_id] }
# set_account_counters_maps(@accounts)
next_path = following_api_v1_account_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = following_api_v1_account_url(since_id: results.first.id) unless results.empty?
next_path = following_api_v1_account_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = following_api_v1_account_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
@@ -35,10 +40,8 @@ class Api::V1::AccountsController < ApiController
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.account_id] }
# set_account_counters_maps(@accounts)
next_path = followers_api_v1_account_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = followers_api_v1_account_url(since_id: results.first.id) unless results.empty?
next_path = followers_api_v1_account_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = followers_api_v1_account_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
@@ -52,11 +55,9 @@ class Api::V1::AccountsController < ApiController
@statuses = cache_collection(@statuses, Status)
set_maps(@statuses)
# set_counters_maps(@statuses)
# set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
next_path = statuses_api_v1_account_url(max_id: @statuses.last.id) unless @statuses.empty?
prev_path = statuses_api_v1_account_url(since_id: @statuses.first.id) unless @statuses.empty?
next_path = statuses_api_v1_account_url(statuses_pagination_params(max_id: @statuses.last.id)) unless @statuses.empty?
prev_path = statuses_api_v1_account_url(statuses_pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
set_pagination_headers(next_path, prev_path)
end
@@ -117,8 +118,6 @@ class Api::V1::AccountsController < ApiController
def search
@accounts = AccountSearchService.new.call(params[:q], limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:resolve] == 'true', current_account)
# set_account_counters_maps(@accounts) unless @accounts.nil?
render action: :index
end
@@ -135,4 +134,16 @@ class Api::V1::AccountsController < ApiController
@muting = Account.muting_map([@account.id], current_user.account_id)
@requested = Account.requested_map([@account.id], current_user.account_id)
end
def pagination_params(core_params)
params.permit(:limit).merge(core_params)
end
def statuses_pagination_params(core_params)
params.permit(:limit, :only_media, :exclude_replies).merge(core_params)
end
def account_params
params.permit(:display_name, :note, :avatar, :header)
end
end

View File

@@ -11,11 +11,15 @@ class Api::V1::BlocksController < ApiController
accounts = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.target_account_id] }.compact
# set_account_counters_maps(@accounts)
next_path = api_v1_blocks_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = api_v1_blocks_url(since_id: results.first.id) unless results.empty?
next_path = api_v1_blocks_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = api_v1_blocks_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
end
private
def pagination_params(core_params)
params.permit(:limit).merge(core_params)
end
end

View File

@@ -11,11 +11,16 @@ class Api::V1::FavouritesController < ApiController
@statuses = cache_collection(Status.where(id: results.map(&:status_id)), Status)
set_maps(@statuses)
# set_counters_maps(@statuses)
next_path = api_v1_favourites_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_STATUSES_LIMIT)
prev_path = api_v1_favourites_url(since_id: results.first.id) unless results.empty?
next_path = api_v1_favourites_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_STATUSES_LIMIT)
prev_path = api_v1_favourites_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
end
private
def pagination_params(core_params)
params.permit(:limit).merge(core_params)
end
end

View File

@@ -9,10 +9,8 @@ class Api::V1::FollowRequestsController < ApiController
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.account_id] }
# set_account_counters_maps(@accounts)
next_path = api_v1_follow_requests_url(max_id: results.last.id) if results.size == DEFAULT_ACCOUNTS_LIMIT
prev_path = api_v1_follow_requests_url(since_id: results.first.id) unless results.empty?
next_path = api_v1_follow_requests_url(pagination_params(max_id: results.last.id)) if results.size == DEFAULT_ACCOUNTS_LIMIT
prev_path = api_v1_follow_requests_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
end
@@ -26,4 +24,10 @@ class Api::V1::FollowRequestsController < ApiController
RejectFollowService.new.call(Account.find(params[:id]), current_account)
render_empty
end
private
def pagination_params(core_params)
params.permit(:limit).merge(core_params)
end
end

View File

@@ -11,11 +11,15 @@ class Api::V1::MutesController < ApiController
accounts = Account.where(id: results.map(&:target_account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.target_account_id] }
# set_account_counters_maps(@accounts)
next_path = api_v1_mutes_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = api_v1_mutes_url(since_id: results.first.id) unless results.empty?
next_path = api_v1_mutes_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = api_v1_mutes_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
end
private
def pagination_params(core_params)
params.permit(:limit).merge(core_params)
end
end

View File

@@ -14,11 +14,9 @@ class Api::V1::NotificationsController < ApiController
statuses = @notifications.select { |n| !n.target_status.nil? }.map(&:target_status)
set_maps(statuses)
# set_counters_maps(statuses)
# set_account_counters_maps(@notifications.map(&:from_account))
next_path = api_v1_notifications_url(max_id: @notifications.last.id) unless @notifications.empty?
prev_path = api_v1_notifications_url(since_id: @notifications.first.id) unless @notifications.empty?
next_path = api_v1_notifications_url(pagination_params(max_id: @notifications.last.id)) unless @notifications.empty?
prev_path = api_v1_notifications_url(pagination_params(since_id: @notifications.first.id)) unless @notifications.empty?
set_pagination_headers(next_path, prev_path)
end
@@ -31,4 +29,10 @@ class Api::V1::NotificationsController < ApiController
Notification.where(account: current_account).delete_all
render_empty
end
private
def pagination_params(core_params)
params.permit(:limit).merge(core_params)
end
end

View File

@@ -23,7 +23,6 @@ class Api::V1::StatusesController < ApiController
statuses = [@status] + @context[:ancestors] + @context[:descendants]
set_maps(statuses)
# set_counters_maps(statuses)
end
def card
@@ -36,10 +35,8 @@ class Api::V1::StatusesController < ApiController
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |r| accounts[r.account_id] }
# set_account_counters_maps(@accounts)
next_path = reblogged_by_api_v1_status_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = reblogged_by_api_v1_status_url(since_id: results.first.id) unless results.empty?
next_path = reblogged_by_api_v1_status_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = reblogged_by_api_v1_status_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
@@ -51,10 +48,8 @@ class Api::V1::StatusesController < ApiController
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
@accounts = results.map { |f| accounts[f.account_id] }
# set_account_counters_maps(@accounts)
next_path = favourited_by_api_v1_status_url(max_id: results.last.id) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = favourited_by_api_v1_status_url(since_id: results.first.id) unless results.empty?
next_path = favourited_by_api_v1_status_url(pagination_params(max_id: results.last.id)) if results.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
prev_path = favourited_by_api_v1_status_url(pagination_params(since_id: results.first.id)) unless results.empty?
set_pagination_headers(next_path, prev_path)
@@ -115,4 +110,8 @@ class Api::V1::StatusesController < ApiController
def status_params
params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: [])
end
def pagination_params(core_params)
params.permit(:limit).merge(core_params)
end
end

View File

@@ -1,8 +1,8 @@
# frozen_string_literal: true
class Api::V1::TimelinesController < ApiController
before_action -> { doorkeeper_authorize! :read }
before_action :require_user!, only: [:home, :mentions]
before_action -> { doorkeeper_authorize! :read }, only: [:home]
before_action :require_user!, only: [:home]
respond_to :json
@@ -11,11 +11,9 @@ class Api::V1::TimelinesController < ApiController
@statuses = cache_collection(@statuses)
set_maps(@statuses)
# set_counters_maps(@statuses)
# set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
next_path = api_v1_home_timeline_url(max_id: @statuses.last.id) unless @statuses.empty?
prev_path = api_v1_home_timeline_url(since_id: @statuses.first.id) unless @statuses.empty?
next_path = api_v1_home_timeline_url(pagination_params(max_id: @statuses.last.id)) unless @statuses.empty?
prev_path = api_v1_home_timeline_url(pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
set_pagination_headers(next_path, prev_path)
@@ -27,11 +25,9 @@ class Api::V1::TimelinesController < ApiController
@statuses = cache_collection(@statuses)
set_maps(@statuses)
# set_counters_maps(@statuses)
# set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
next_path = api_v1_public_timeline_url(max_id: @statuses.last.id) unless @statuses.empty?
prev_path = api_v1_public_timeline_url(since_id: @statuses.first.id) unless @statuses.empty?
next_path = api_v1_public_timeline_url(pagination_params(max_id: @statuses.last.id)) unless @statuses.empty?
prev_path = api_v1_public_timeline_url(pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
set_pagination_headers(next_path, prev_path)
@@ -44,11 +40,9 @@ class Api::V1::TimelinesController < ApiController
@statuses = cache_collection(@statuses)
set_maps(@statuses)
# set_counters_maps(@statuses)
# set_account_counters_maps(@statuses.flat_map { |s| [s.account, s.reblog? ? s.reblog.account : nil] }.compact.uniq)
next_path = api_v1_hashtag_timeline_url(params[:id], max_id: @statuses.last.id) unless @statuses.empty?
prev_path = api_v1_hashtag_timeline_url(params[:id], since_id: @statuses.first.id) unless @statuses.empty?
next_path = api_v1_hashtag_timeline_url(params[:id], pagination_params(max_id: @statuses.last.id)) unless @statuses.empty?
prev_path = api_v1_hashtag_timeline_url(params[:id], pagination_params(since_id: @statuses.first.id)) unless @statuses.empty?
set_pagination_headers(next_path, prev_path)
@@ -60,4 +54,8 @@ class Api::V1::TimelinesController < ApiController
def cache_collection(raw)
super(raw, Status)
end
def pagination_params(core_params)
params.permit(:local, :limit).merge(core_params)
end
end

View File

@@ -7,6 +7,7 @@ class ApiController < ApplicationController
protect_from_forgery with: :null_session
skip_before_action :verify_authenticity_token
skip_before_action :store_current_location
before_action :set_rate_limit_headers

View File

@@ -1,14 +1,13 @@
# frozen_string_literal: true
class ApplicationController < ActionController::Base
include Localized
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
force_ssl if: "Rails.env.production? && ENV['LOCAL_HTTPS'] == 'true'"
include Localized
helper_method :current_account
rescue_from ActionController::RoutingError, with: :not_found
@@ -41,7 +40,6 @@ class ApplicationController < ActionController::Base
# If the sign in is after a two week break, we need to regenerate their feed
RegenerationWorker.perform_async(current_user.account_id) if current_user.last_sign_in_at < 14.days.ago
return
end
def check_suspension

View File

@@ -4,16 +4,30 @@ module Localized
extend ActiveSupport::Concern
included do
before_action :set_locale
around_action :set_locale
end
private
def set_locale
I18n.locale = current_user.try(:locale) || default_locale
rescue I18n::InvalidLocale
I18n.locale = default_locale
locale = default_locale
if user_signed_in?
begin
locale = current_user.try(:locale) || default_locale
rescue I18n::InvalidLocale
locale = default_locale
end
end
I18n.with_locale(locale) do
yield
end
end
def default_locale
ENV.fetch('DEFAULT_LOCALE') { I18n.default_locale }
ENV.fetch('DEFAULT_LOCALE') {
http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
}
end
end

View File

@@ -1,13 +1,13 @@
# frozen_string_literal: true
class Oauth::AuthorizationsController < Doorkeeper::AuthorizationsController
include Localized
skip_before_action :authenticate_resource_owner!
before_action :store_current_location
before_action :authenticate_resource_owner!
include Localized
private
def store_current_location

View File

@@ -1,13 +1,13 @@
# frozen_string_literal: true
class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicationsController
include Localized
skip_before_action :authenticate_resource_owner!
before_action :store_current_location
before_action :authenticate_resource_owner!
include Localized
private
def store_current_location

View File

@@ -5,19 +5,29 @@ class Settings::TwoFactorAuthsController < ApplicationController
before_action :authenticate_user!
def show
return unless current_user.otp_required_for_login
def show; end
@provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain)
@qrcode = RQRCode::QRCode.new(@provision_url)
def new
redirect_to settings_two_factor_auth_path if current_user.otp_required_for_login
@confirmation = Form::TwoFactorConfirmation.new
current_user.otp_secret = User.generate_otp_secret(32)
current_user.save!
set_qr_code
end
def enable
current_user.otp_required_for_login = true
current_user.otp_secret = User.generate_otp_secret
current_user.save!
def create
if current_user.validate_and_consume_otp!(confirmation_params[:code])
current_user.otp_required_for_login = true
current_user.save!
redirect_to settings_two_factor_auth_path
redirect_to settings_two_factor_auth_path, notice: I18n.t('two_factor_auth.enabled_success')
else
@confirmation = Form::TwoFactorConfirmation.new
set_qr_code
flash.now[:alert] = I18n.t('two_factor_auth.wrong_code')
render action: :new
end
end
def disable
@@ -26,4 +36,15 @@ class Settings::TwoFactorAuthsController < ApplicationController
redirect_to settings_two_factor_auth_path
end
private
def set_qr_code
@provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain)
@qrcode = RQRCode::QRCode.new(@provision_url)
end
def confirmation_params
params.require(:form_two_factor_confirmation).permit(:code)
end
end

View File

@@ -1,4 +0,0 @@
# frozen_string_literal: true
module AboutHelper
end

View File

@@ -1,4 +0,0 @@
# frozen_string_literal: true
module Admin::DomainBlocksHelper
end

View File

@@ -1,4 +0,0 @@
# frozen_string_literal: true
module Admin::PubsubhubbubHelper
end

View File

@@ -1,4 +0,0 @@
# frozen_string_literal: true
module AuthorizeFollowHelper
end

View File

@@ -11,6 +11,7 @@ module SettingsHelper
uk: 'Українська',
'zh-CN': '简体中文',
fi: 'Suomi',
eo: 'Esperanto',
}.freeze
def human_locale(locale)

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
module SiteTitleHelper
def site_title
Setting.site_title.to_s
end
end

View File

@@ -34,10 +34,6 @@ module StreamEntriesHelper
user_signed_in? && @favourited.key?(status.id) ? 'favourited' : ''
end
def proper_status(status)
status.reblog? ? status.reblog : status
end
def rtl?(text)
return false if text.empty?

View File

@@ -1,4 +0,0 @@
# frozen_string_literal: true
module TagsHelper
end

View File

@@ -1,4 +0,0 @@
# frozen_string_literal: true
module XrdHelper
end

View File

@@ -67,7 +67,7 @@ class AtomSerializer
append_element(entry, 'id', TagManager.instance.unique_tag(stream_entry.created_at, stream_entry.activity_id, stream_entry.activity_type))
append_element(entry, 'published', stream_entry.created_at.iso8601)
append_element(entry, 'updated', stream_entry.updated_at.iso8601)
append_element(entry, 'title', stream_entry&.status&.title)
append_element(entry, 'title', stream_entry&.status&.title || 'Delete')
entry << author(stream_entry.account) if root
@@ -328,7 +328,7 @@ class AtomSerializer
def serialize_status_attributes(entry, status)
append_element(entry, 'summary', status.spoiler_text) unless status.spoiler_text.blank?
append_element(entry, 'content', Formatter.instance.format(status.reblog? ? status.reblog : status).to_str, type: 'html')
append_element(entry, 'content', Formatter.instance.format(status.proper).to_str, type: 'html')
status.mentions.each do |mentioned|
append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': TagManager::TYPES[:person], href: TagManager.instance.uri_for(mentioned.account))

View File

@@ -125,11 +125,11 @@ class Account < ApplicationRecord
end
def favourited?(status)
(status.reblog? ? status.reblog : status).favourites.where(account: self).count.positive?
status.proper.favourites.where(account: self).count.positive?
end
def reblogged?(status)
(status.reblog? ? status.reblog : status).reblogs.where(account: self).count.positive?
status.proper.reblogs.where(account: self).count.positive?
end
def keypair
@@ -203,7 +203,7 @@ class Account < ApplicationRecord
end
def triadic_closures(account, limit = 5)
sql = <<SQL
sql = <<-SQL.squish
WITH first_degree AS (
SELECT target_account_id
FROM follows
@@ -216,7 +216,7 @@ class Account < ApplicationRecord
GROUP BY target_account_id, accounts.id
ORDER BY count(account_id) DESC
LIMIT ?
SQL
SQL
Account.find_by_sql([sql, account.id, account.id, limit])
end
@@ -226,7 +226,7 @@ SQL
textsearch = '(setweight(to_tsvector(\'simple\', accounts.display_name), \'A\') || setweight(to_tsvector(\'simple\', accounts.username), \'B\') || setweight(to_tsvector(\'simple\', coalesce(accounts.domain, \'\')), \'C\'))'
query = 'to_tsquery(\'simple\', \'\'\' \' || ' + terms + ' || \' \'\'\' || \':*\')'
sql = <<SQL
sql = <<-SQL.squish
SELECT
accounts.*,
ts_rank_cd(#{textsearch}, #{query}, 32) AS rank
@@ -234,7 +234,7 @@ SQL
WHERE #{query} @@ #{textsearch}
ORDER BY rank DESC
LIMIT ?
SQL
SQL
Account.find_by_sql([sql, limit])
end
@@ -244,7 +244,7 @@ SQL
textsearch = '(setweight(to_tsvector(\'simple\', accounts.display_name), \'A\') || setweight(to_tsvector(\'simple\', accounts.username), \'B\') || setweight(to_tsvector(\'simple\', coalesce(accounts.domain, \'\')), \'C\'))'
query = 'to_tsquery(\'simple\', \'\'\' \' || ' + terms + ' || \' \'\'\' || \':*\')'
sql = <<SQL
sql = <<-SQL.squish
SELECT
accounts.*,
(count(f.id) + 1) * ts_rank_cd(#{textsearch}, #{query}, 32) AS rank
@@ -254,7 +254,7 @@ SQL
GROUP BY accounts.id
ORDER BY rank DESC
LIMIT ?
SQL
SQL
Account.find_by_sql([sql, account.id, account.id, limit])
end

View File

@@ -0,0 +1,7 @@
# frozen_string_literal: true
class Form::TwoFactorConfirmation
include ActiveModel::Model
attr_accessor :code
end

View File

@@ -62,8 +62,12 @@ class Status < ApplicationRecord
reply? ? :comment : :note
end
def proper
reblog? ? reblog : self
end
def content
reblog? ? reblog.text : text
proper.text
end
def target
@@ -71,7 +75,7 @@ class Status < ApplicationRecord
end
def title
content
reblog? ? "#{account.acct} shared a status by #{reblog.account.acct}" : "New status by #{account.acct}"
end
def hidden?

View File

@@ -17,7 +17,7 @@ class Tag < ApplicationRecord
textsearch = 'to_tsvector(\'simple\', tags.name)'
query = 'to_tsquery(\'simple\', \'\'\' \' || ' + terms + ' || \' \'\'\' || \':*\')'
sql = <<SQL
sql = <<-SQL.squish
SELECT
tags.*,
ts_rank_cd(#{textsearch}, #{query}) AS rank
@@ -25,7 +25,7 @@ class Tag < ApplicationRecord
WHERE #{query} @@ #{textsearch}
ORDER BY rank DESC
LIMIT ?
SQL
SQL
Tag.find_by_sql([sql, limit])
end

View File

@@ -0,0 +1,28 @@
# frozen_string_literal: true
class InstancePresenter
delegate(
:closed_registrations_message,
:contact_email,
:open_registrations,
:site_description,
:site_extended_description,
to: Setting
)
def contact_account
Account.find_local(Setting.site_contact_username)
end
def user_count
Rails.cache.fetch('user_count') { User.count }
end
def status_count
Rails.cache.fetch('local_status_count') { Status.local.count }
end
def domain_count
Rails.cache.fetch('distinct_domain_count') { Account.distinct.count(:domain) }
end
end

View File

@@ -19,11 +19,16 @@ class FetchRemoteAccountService < BaseService
xml = Nokogiri::XML(body)
xml.encoding = 'utf-8'
url_parts = Addressable::URI.parse(url)
username = xml.at_xpath('//xmlns:author/xmlns:name').try(:content)
domain = url_parts.host
email = xml.at_xpath('//xmlns:author/xmlns:email').try(:content)
if email.nil?
url_parts = Addressable::URI.parse(url)
username = xml.at_xpath('//xmlns:author/xmlns:name').try(:content)
domain = url_parts.host
else
username, domain = email.split('@')
end
return nil if username.nil?
return nil if username.nil? || domain.nil?
Rails.logger.debug "Going to webfinger #{username}@#{domain}"

View File

@@ -20,8 +20,6 @@ class FollowRemoteAccountService < BaseService
Rails.logger.debug "Looking up webfinger for #{uri}"
account = Account.new(username: username, domain: domain)
data = Goldfinger.finger("acct:#{uri}")
raise Goldfinger::Error, 'Missing resource links' if data.link('http://schemas.google.com/g/2010#updates-from').nil? || data.link('salmon').nil? || data.link('http://webfinger.net/rel/profile-page').nil? || data.link('magic-public-key').nil?
@@ -37,6 +35,7 @@ class FollowRemoteAccountService < BaseService
domain_block = DomainBlock.find_by(domain: domain)
account = Account.new(username: confirmed_username, domain: confirmed_domain)
account.remote_url = data.link('http://schemas.google.com/g/2010#updates-from').href
account.salmon_url = data.link('salmon').href
account.url = data.link('http://webfinger.net/rel/profile-page').href
@@ -51,8 +50,8 @@ class FollowRemoteAccountService < BaseService
account.uri = get_account_uri(xml)
account.hub_url = hubs.first.attribute('href').value
get_profile(body, account)
account.save!
get_profile(body, account)
account
end

View File

@@ -37,11 +37,11 @@ class PostStatusService < BaseService
def validate_media!(media_ids)
return if media_ids.nil? || !media_ids.is_a?(Enumerable)
raise Mastodon::ValidationError, 'Cannot attach more than 4 files' if media_ids.size > 4
raise Mastodon::ValidationError, I18n.t('media_attachments.validations.too_many') if media_ids.size > 4
media = MediaAttachment.where(status_id: nil).where(id: media_ids.take(4).map(&:to_i))
raise Mastodon::ValidationError, 'Cannot attach a video to a toot that already contains images' if media.size > 1 && media.find(&:video?)
raise Mastodon::ValidationError, I18n.t('media_attachments.validations.images_and_video') if media.size > 1 && media.find(&:video?)
media
end

View File

@@ -5,14 +5,13 @@ class ProcessFeedService < BaseService
xml = Nokogiri::XML(body)
xml.encoding = 'utf-8'
update_author(body, xml, account)
update_author(body, account)
process_entries(xml, account)
end
private
def update_author(body, xml, account)
return if xml.at_xpath('/xmlns:feed', xmlns: TagManager::XMLNS).nil?
def update_author(body, account)
RemoteProfileUpdateWorker.perform_async(account.id, body.force_encoding('UTF-8'), true)
end

View File

@@ -1,7 +1,12 @@
# frozen_string_literal: true
class UpdateRemoteProfileService < BaseService
def call(xml, account, resubscribe = false)
def call(body, account, resubscribe = false)
xml = Nokogiri::XML(body)
xml.encoding = 'utf-8'
xml = xml.at_xpath('/xmlns:feed', xmlns: TagManager::XMLNS) || xml.at_xpath('/xmlns:entry', xmlns: TagManager::XMLNS)
return if xml.nil?
author_xml = xml.at_xpath('./xmlns:author', xmlns: TagManager::XMLNS) || xml.at_xpath('./dfrn:owner', dfrn: TagManager::DFRN_XMLNS)
@@ -12,9 +17,9 @@ class UpdateRemoteProfileService < BaseService
account.note = author_xml.at_xpath('./poco:note', poco: TagManager::POCO_XMLNS).content unless author_xml.at_xpath('./poco:note', poco: TagManager::POCO_XMLNS).nil?
account.locked = author_xml.at_xpath('./mastodon:scope', mastodon: TagManager::MTDN_XMLNS)&.content == 'private'
unless account.suspended? || DomainBlock.find_by(domain: account.domain)&.reject_media?
account.avatar_remote_url = author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS)['href'] unless author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS).nil? || author_xml.at_xpath('./xmlns:link[@rel="avatar"]', xmlns: TagManager::XMLNS)['href'].blank?
account.header_remote_url = author_xml.at_xpath('./xmlns:link[@rel="header"]', xmlns: TagManager::XMLNS)['href'] unless author_xml.at_xpath('./xmlns:link[@rel="header"]', xmlns: TagManager::XMLNS).nil? || author_xml.at_xpath('./xmlns:link[@rel="header"]', xmlns: TagManager::XMLNS)['href'].blank?
if !account.suspended? && !DomainBlock.find_by(domain: account.domain)&.reject_media?
account.avatar_remote_url = link_href_from_xml(author_xml, 'avatar') if link_has_href?(author_xml, 'avatar')
account.header_remote_url = link_href_from_xml(author_xml, 'header') if link_has_href?(author_xml, 'header')
end
end
@@ -25,4 +30,14 @@ class UpdateRemoteProfileService < BaseService
SubscribeService.new.call(account) if resubscribe && (account.hub_url != old_hub_url)
end
private
def link_href_from_xml(xml, type)
xml.at_xpath('./xmlns:link[@rel="' + type + '"]', xmlns: TagManager::XMLNS)['href']
end
def link_has_href?(xml, type)
!(xml.at_xpath('./xmlns:link[@rel="' + type + '"]', xmlns: TagManager::XMLNS).nil? || xml.at_xpath('./xmlns:link[@rel="' + type + '"]', xmlns: TagManager::XMLNS)['href'].blank?)
end
end

View File

@@ -0,0 +1,30 @@
= simple_form_for(new_user, url: user_registration_path) do |f|
= f.simple_fields_for :account do |account_fields|
= account_fields.input :username,
autofocus: true,
placeholder: t('simple_form.labels.defaults.username'),
required: true,
input_html: { 'aria-label' => t('simple_form.labels.defaults.username') }
= f.input :email,
placeholder: t('simple_form.labels.defaults.email'),
required: true,
input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
= f.input :password,
autocomplete: "off",
placeholder: t('simple_form.labels.defaults.password'),
required: true,
input_html: { 'aria-label' => t('simple_form.labels.defaults.password') }
= f.input :password_confirmation,
autocomplete: "off",
placeholder: t('simple_form.labels.defaults.confirm_password'),
required: true,
input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password') }
.actions
= f.button :button, t('about.get_started'), type: :submit
.info
= link_to t('auth.login'), new_user_session_path, class: 'webapp-btn'
·
= link_to t('about.about_this'), about_more_path

View File

@@ -7,42 +7,42 @@
.panel
%h2= Rails.configuration.x.local_domain
- unless @description.blank?
%p= @description.html_safe
- unless @instance_presenter.site_description.blank?
%p= @instance_presenter.site_description.html_safe
.information-board
.section
%span= t 'about.user_count_before'
%strong= number_with_delimiter @user_count
%strong= number_with_delimiter @instance_presenter.user_count
%span= t 'about.user_count_after'
.section
%span= t 'about.status_count_before'
%strong= number_with_delimiter @status_count
%strong= number_with_delimiter @instance_presenter.status_count
%span= t 'about.status_count_after'
.section
%span= t 'about.domain_count_before'
%strong= number_with_delimiter @domain_count
%strong= number_with_delimiter @instance_presenter.domain_count
%span= t 'about.domain_count_after'
- unless @extended_description.blank?
.panel= @extended_description.html_safe
- unless @instance_presenter.site_extended_description.blank?
.panel= @instance_presenter.site_extended_description.html_safe
.sidebar
.panel
.panel-header= t 'about.contact'
.panel-body
- if @contact_account
- if @instance_presenter.contact_account
.owner
.avatar= image_tag @contact_account.avatar.url
.avatar= image_tag @instance_presenter.contact_account.avatar.url
.name
= link_to TagManager.instance.url_for(@contact_account) do
%span.display_name.emojify= display_name(@contact_account)
%span.username= "@#{@contact_account.acct}"
= link_to TagManager.instance.url_for(@instance_presenter.contact_account) do
%span.display_name.emojify= display_name(@instance_presenter.contact_account)
%span.username= "@#{@instance_presenter.contact_account.acct}"
- unless @contact_email.blank?
- unless @instance_presenter.contact_email.blank?
.contact-email
= t 'about.business_email'
%strong= @contact_email
%strong= @instance_presenter.contact_email
.panel
.panel-header= t 'about.links'
.panel-list

View File

@@ -5,10 +5,10 @@
= Rails.configuration.x.local_domain
- content_for :header_tags do
%meta{ property: 'og:site_name', content: 'Mastodon' }/
%meta{ property: 'og:site_name', content: site_title }/
%meta{ property: 'og:type', content: 'website' }/
%meta{ property: 'og:title', content: Rails.configuration.x.local_domain }/
%meta{ property: 'og:description', content: @description.blank? ? "Mastodon is a free, open-source social network server. A decentralized alternative to commercial platforms, it avoids the risks of a single company monopolizing your communication. Anyone can run Mastodon and participate in the social network seamlessly" : strip_tags(@description) }/
%meta{ property: 'og:description', content: strip_tags(@instance_presenter.site_description.blank? ? t('about.about_mastodon') : @instance_presenter.site_description) }/
%meta{ property: 'og:image', content: asset_url('mastodon_small.jpg') }/
%meta{ property: 'og:image:width', content: '400' }/
%meta{ property: 'og:image:height', content: '400' }/
@@ -24,28 +24,14 @@
.screenshot-with-signup
.mascot= image_tag 'fluffy-elephant-friend.png'
- if @open_registrations
= simple_form_for(@user, url: user_registration_path) do |f|
= f.simple_fields_for :account do |ff|
= ff.input :username, autofocus: true, placeholder: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username') }
= f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email') }
= f.input :password, autocomplete: "off", placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password') }
= f.input :password_confirmation, autocomplete: "off", placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password') }
.actions
= f.button :button, t('about.get_started'), type: :submit
.info
= link_to t('auth.login'), new_user_session_path, class: 'webapp-btn'
·
= link_to t('about.about_this'), about_more_path
- if @instance_presenter.open_registrations
= render 'registration'
- else
.closed-registrations-message
- if @closed_registrations_message.blank?
- if @instance_presenter.closed_registrations_message.blank?
%p= t('about.closed_registrations')
- else
= @closed_registrations_message.html_safe
= @instance_presenter.closed_registrations_message.html_safe
.info
= link_to t('auth.login'), new_user_session_path, class: 'webapp-btn'
·
@@ -85,9 +71,9 @@
= fa_icon('li check-square')
= t 'about.features.api'
- unless @description.blank?
- unless @instance_presenter.site_description.blank?
%h3= t('about.description_headline', domain: Rails.configuration.x.local_domain)
%p= @description.html_safe
%p= @instance_presenter.site_description.html_safe
.actions
.info

View File

@@ -5,7 +5,7 @@
%link{ rel: 'salmon', href: api_salmon_url(@account.id) }/
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_url(@account, format: 'atom') }/
%meta{ property: 'og:site_name', content: 'Mastodon' }/
%meta{ property: 'og:site_name', content: site_title }/
%meta{ property: 'og:type', content: 'profile' }/
%meta{ property: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/
%meta{ property: 'og:description', content: @account.note }/

View File

@@ -15,7 +15,7 @@
- if content_for?(:page_title)
= yield(:page_title)
= ' - '
= Setting.site_title
= site_title
= stylesheet_link_tag 'application', media: 'all'
= csrf_meta_tags

View File

@@ -0,0 +1,17 @@
- content_for :page_title do
= t('settings.two_factor_auth')
= simple_form_for @confirmation, url: settings_two_factor_auth_path, method: :post do |f|
%p.hint= t('two_factor_auth.instructions_html')
.qr-wrapper
.qr-code= raw @qrcode.as_svg(padding: 0, module_size: 4)
.qr-alternative
%p.hint= t('two_factor_auth.manual_instructions')
%samp.qr-alternative__code= current_user.otp_secret.scan(/.{4}/).join(' ')
= f.input :code, hint: t('two_factor_auth.code_hint'), placeholder: t('simple_form.labels.defaults.otp_attempt')
.actions
= f.button :button, t('two_factor_auth.enable'), type: :submit

View File

@@ -2,16 +2,9 @@
= t('settings.two_factor_auth')
.simple_form
%p.hint= t('two_factor_auth.description_html')
- if current_user.otp_required_for_login
%p.hint= t('two_factor_auth.instructions_html')
.qr-code= raw @qrcode.as_svg(padding: 0, module_size: 5)
%p.hint= t('two_factor_auth.plaintext_secret_html', secret: current_user.otp_secret)
%p.hint= t('two_factor_auth.warning')
= link_to t('two_factor_auth.disable'), disable_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button'
- else
%p.hint= t('two_factor_auth.description_html')
= link_to t('two_factor_auth.enable'), enable_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button'
= link_to t('two_factor_auth.setup'), new_settings_two_factor_auth_path, class: 'block-button'

View File

@@ -16,7 +16,7 @@
%strong= display_name(status.account)
= t('stream_entries.reblogged')
= render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: proper_status(status) }
= render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: status.proper }
- if include_threads
= render partial: 'stream_entries/status', collection: @descendants, as: :status, locals: { is_successor: true }

View File

@@ -2,7 +2,7 @@
%link{ rel: 'alternate', type: 'application/atom+xml', href: account_stream_entry_url(@account, @stream_entry, format: 'atom') }/
%link{ rel: 'alternate', type: 'application/json+oembed', href: api_oembed_url(url: account_stream_entry_url(@account, @stream_entry), format: 'json') }/
%meta{ property: 'og:site_name', content: 'Mastodon' }/
%meta{ property: 'og:site_name', content: site_title }/
%meta{ property: 'og:type', content: 'article' }/
%meta{ property: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/

View File

@@ -0,0 +1,5 @@
<p>Bienvenue <%= @resource.email %>&nbsp;!</p>
<p>Vous pouvez confirmer le courriel de votre compte Mastodon en cliquant sur le lien ci-dessous&nbsp;:</p>
<p><%= link_to 'Confirmer mon compte', confirmation_url(@resource, confirmation_token: @token) %></p>

View File

@@ -0,0 +1,5 @@
Bienvenue <%= @resource.email %> !
Vous pouvez confirmer le courriel de votre compte Mastodon en cliquant sur le lien ci-dessous :
<%= confirmation_url(@resource, confirmation_token: @token) %>

View File

@@ -0,0 +1,3 @@
<p>Bonjour <%= @resource.email %>&nbsp;!</p>
<p>Nous vous contactons pour vous informer que votre mot de passe sur Mastodon a bien été modifié.</p>

View File

@@ -0,0 +1,3 @@
Bonjour <%= @resource.email %> !
Nous vous contactons pour vous informer que votre mot de passe sur Mastodon a bien été modifié.

View File

@@ -0,0 +1,8 @@
<p>Bonjour <%= @resource.email %>&nbsp;!</p>
<p>Quelqu'un a demandé à réinitialiser votre mot de passe sur Mastodon. Vous pouvez effectuer la réinitialisation en cliquant sur le lien ci-dessous.</p>
<p><%= link_to 'Modifier mon mot de passe', edit_password_url(@resource, reset_password_token: @token) %></p>
<p>Si vous n'êtes pas à l'origine de cette demande, vous pouvez ignorer ce message.</p>
<p>Votre mot de passe ne sera pas modifié tant que vous n'accéderez pas au lien ci-dessus et n'en choisirez pas un nouveau.</p>

View File

@@ -0,0 +1,8 @@
Bonjour <%= @resource.email %> !
Quelqu'un a demandé à réinitialiser votre mot de passe sur Mastodon. Vous pouvez effectuer la réinitialisation en cliquant sur le lien ci-dessous.
<%= edit_password_url(@resource, reset_password_token: @token) %>
Si vous n'êtes pas à l'origine de cette demande, vous pouvez ignorer ce message.
Votre mot de passe ne sera pas modifié tant que vous n'accéderez pas au lien ci-dessus et n'en choisirez pas un nouveau.

View File

@@ -6,14 +6,7 @@ class RemoteProfileUpdateWorker
sidekiq_options queue: 'pull'
def perform(account_id, body, resubscribe)
account = Account.find(account_id)
xml = Nokogiri::XML(body)
xml.encoding = 'utf-8'
author_container = xml.at_xpath('/xmlns:feed', xmlns: TagManager::XMLNS) || xml.at_xpath('/xmlns:entry', xmlns: TagManager::XMLNS)
UpdateRemoteProfileService.new.call(author_container, account, resubscribe)
UpdateRemoteProfileService.new.call(body, Account.find(account_id), resubscribe)
rescue ActiveRecord::RecordNotFound
true
end

View File

@@ -24,7 +24,7 @@ module Mastodon
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
config.i18n.available_locales = [:en, :de, :es, :pt, :fr, :hu, :uk, 'zh-CN', :fi]
config.i18n.available_locales = [:en, :de, :es, :pt, :fr, :hu, :uk, 'zh-CN', :fi, :eo]
config.i18n.default_locale = :en
# config.paths.add File.join('app', 'api'), glob: File.join('**', '*.rb')

View File

@@ -22,3 +22,4 @@ production:
password: <%= ENV['DB_PASS'] || '' %>
host: <%= ENV['DB_HOST'] || 'localhost' %>
port: <%= ENV['DB_PORT'] || 5432 %>
prepared_statements: <%= ENV['PREPARED_STATEMENTS'] || 'true' %>

View File

@@ -38,9 +38,9 @@ Rails.application.configure do
# Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
config.force_ssl = false
# Use the lowest log level to ensure availability of diagnostic information
# By default, use the lowest log level to ensure availability of diagnostic information
# when problems arise.
config.log_level = :debug
config.log_level = ENV.fetch('RAILS_LOG_LEVEL', 'debug').to_sym
# Prepend all log lines with the following tags.
config.log_tags = [:request_id]

View File

@@ -7,8 +7,8 @@ de:
terms: AGB
accounts:
follow: Folgen
followers: Follower
following: Gefolgt
followers: Folger
following: Folgt
nothing_here: Hier gibt es nichts!
people_followed_by: Nutzer, denen %{name} folgt
people_who_follow: Nutzer, die %{name} folgen

View File

@@ -0,0 +1,61 @@
---
eo:
devise:
confirmations:
confirmed: Via konto estas konfirmita.
send_instructions: Vi ricevos instrukciojn por konfirmi vian konton post kelkaj minutoj.
send_paranoid_instructions: Se via retpoŝt-adreso ekzistas en nia datumbazo, vi baldaŭ ricevos retpoŝt-mesaĝon, kiu enhavas la instrukciojn por konfirmi vian konton.
failure:
already_authenticated: Vi jam estas ensalutita.
inactive: Via konto ankoraŭ ne estas konfirmita.
invalid: Malĝusta retpoŝt-adreso aŭ pasvorto.
last_attempt: Vi ankoraŭ povas provi unufoje antaŭ ol via konto estos ŝlosita.
locked: Via konto estas ŝlosita.
not_found_in_database: Malĝusta retpoŝt-adreso aŭ pasvorto.
timeout: Via sesio eksiĝis. Bonvolu reensaluti por daŭrigi.
unauthenticated: Vi devas ensaluti aŭ membriĝi por daŭrigi.
unconfirmed: Vi devas konfirmi vian konton por daŭrigi.
mailer:
confirmation_instructions:
subject: Instrukcioj por konfirmi
password_change:
subject: Via pasvorto estis ŝanĝita senprobleme.
reset_password_instructions:
subject: Instrukcioj por ŝanĝi la pasvorton
unlock_instructions:
subject: Instrukcioj por malŝlosi la konton
omniauth_callbacks:
failure: 'Ni ne povis aŭtentigi vin per %{kind}: ''%{reason}''.'
success: Aŭtentigita senprobleme per %{kind}.
passwords:
no_token: Vi ne povas iri al tiu paĝo per alia vojo ol retpoŝt-mesaĝo por ŝanĝi pasvorton. Se vi venas de tia retpoŝt-mesaĝo, kontrolu ke vi uzis la tutan URL.
send_instructions: Vi ricevos retpoŝt-mesaĝon kun instrukcioj por ŝanĝi vian pasvorton post kelkaj minutoj.
send_paranoid_instructions: Se via retpoŝt-adreso ekzistas en nia datumbazo, vi ricevos ligilon por ŝanĝi vian pasvorton per retpoŝt-mesaĝo.
updated: Via pasvorto estis redaktita senprobleme, vi nun estas ensalutita.
updated_not_active: Via pasvorto estis redaktita senprobleme.
registrations:
destroyed: Ĝis! Via konto estis forigita senprobleme. Ni esperas revidi vin baldaŭ.
signed_up: Bonvenon! Vi membriĝis senprobleme.
signed_up_but_inactive: Vi bone membriĝis, sed vi ankoraŭ ne povas ensaluti ĉar via konto ne estis konfirmita.
signed_up_but_locked: Vi bone membriĝis, sed vi ne povas ensaluti ĉar via konto estas ŝlosita.
signed_up_but_unconfirmed: Retpoŝt-mesaĝo kun via ligilo por konfirmi vian konton estis sendita al via retpoŝt-adreso. Bonvolu uzi tiun ligilon por konfirmi vian konton.
update_needs_confirmation: Vi bone aktualigis vian konton, sed ni bezonas kontroli vian novan retpoŝt-adreson. Bonvolu kontroli viajn retpoŝt-mesaĝojn kaj uzi la ligilon por konfirmi vian novan retpoŝt-adreson.
updated: Via konto estis aktualigita senprobleme.
sessions:
already_signed_out: Elsalutita.
signed_in: Ensalutita.
signed_out: Elsalutita.
unlocks:
send_instructions: Vi ricevos retpoŝt-mesaĝon kun instrukcioj por malŝlosi vian konton post kelkaj minutoj.
send_paranoid_instructions: Se via retpoŝt-adreso ekzistas en nia datumbazo, vi ricevos ligilon por malŝlosi vian konton per retpoŝt-mesaĝo.
unlocked: Via konto estis malŝlosita senprobleme, vi nun estas ensalutita.
errors:
messages:
already_confirmed: jam estis konfirmita, bonvolu provi ensaluti
confirmation_period_expired: devas esti konfirmita en %{period}, bonvolu repeti
expired: eksiĝis, bonvolu repeti
not_found: ne estis trovita
not_locked: ne estis ŝlosita
not_saved:
one: '1 eraro malpermesis al tiu %{resource} esti konservita:'
other: '%{count} eraroj malpermesis al tiu %{resource} esti konservita:'

View File

@@ -58,4 +58,4 @@ fr:
not_locked: n'était pas verrouillé(e)
not_saved:
one: '1 erreur a empêché ce(tte) %{resource} d''être sauvegardé(e) :'
other: '%{count} erreurs ont empêché ce(tte) %{resource} d''être sauvegardé(e): '
other: '%{count} erreurs ont empêché ce(tte) %{resource} d''être sauvegardé(e) : '

View File

@@ -0,0 +1,113 @@
---
eo:
activerecord:
attributes:
doorkeeper/application:
name: Nomo
redirect_uri: URI de plusendo
errors:
models:
doorkeeper/application:
attributes:
redirect_uri:
fragment_present: ne povas enhavi eron.
invalid_uri: devas esti valida URI.
relative_uri: devas esti absoluta URI.
secured_uri: devas esti HTTPS/SSL-a URI.
doorkeeper:
applications:
buttons:
authorize: Rajtigi
cancel: Rezigni
destroy: Detrui
edit: Redakti
submit: Sendi
confirmations:
destroy: Ĉu vi certas?
edit:
title: Redakti aplikaĵon
form:
error: Ups! Kontrolu vian formularon ĉu estas eraroj
help:
native_redirect_uri: Uzu %{native_redirect_uri} por lokaj provoj
redirect_uri: Uzu unu linion por ĉiu URI
scopes: Apartigu ampleksojn per spacetoj. Lasu malplena por uzi la senŝanĝajn ampleksojn.
index:
callback_url: URL vokita per referenco
name: Nomo
new: Nova Aplikaĵo
title: Viaj aplikaĵoj
new:
title: Nova aplikaĵo
show:
actions: Agoj
application_id: Identigo de la aplikaĵo
callback_urls: URL-j vokitaj per referenco
scopes: Ampleksoj
secret: Sekreto
title: 'Aplikaĵo: %{name}'
authorizations:
buttons:
authorize: Rajtigi
deny: Rifuzi
error:
title: Eraro okazis
new:
able_to: Povos
prompt: La aplikaĵo %{client_name} petas aliron al via konto
title: Rajtigo bezonata
show:
title: Rajtiga kodo
authorized_applications:
buttons:
revoke: Malrajtigi
confirmations:
revoke: Ĉu vi certas?
index:
application: Aplikaĵo
created_at: Rajtigita
date_format: "%Y-%m-%d %H:%M:%S"
scopes: Ampleksoj
title: Viaj rajtigitaj aplikaĵoj
errors:
messages:
access_denied: La posedanto de la rimedo aŭ la rajtiga servilo rifuzis vian peton.
credential_flow_not_configured: La sendado de la identigiloj de la posedanto de la rimedo malsukcesis ĉar Doorkeeper.configure.resource_owner_from_credentials ne estis agordita.
invalid_client: La aŭtentigo de la kliento malsukcesis ĉar la kliento estas nekonata, aŭ mankis peto aŭtentigi, aŭ la aŭtentig-metodo ne estas subtenata.
invalid_grant: La rajtiga konsento ne estas valida, ne plu estas valida, estis forigita, ne kongruas kun la plusenda URI uzita en la aŭtentiga peto, aŭ estis sendita al alia kliento.
invalid_redirect_uri: La plusenda URI uzita en estas valida.
invalid_request: Mankis al la peto nepra parametro, enhavas nesubtenatan parametran valoron, aŭ la peto simple estas misformita.
invalid_resource_owner: La donitaj identigiloj pri la posedanto de la rimedo ne estas validaj, aŭ tiu ne povas esti trovita.
invalid_scope: La petita amplekso ne estas valida, estas nekonata, aŭ estas misformita.
invalid_token:
expired: La atingoĵetono eskiĝis.
revoked: La atingoĵetono estis rifuzita.
unknown: La atingoĵetono ne estas valida.
resource_owner_authenticator_not_configured: La posedanto de la rimedo ne povis esti trovita ĉar Doorkeeper.configure.resource_owner_authenticator ne estas agordita.
server_error: La rajtiga servilo rimarkis neatenditan kondiĉon, kiu malpermesis al ĝi plenumi la peton.
temporarily_unavailable: La rajtiga servilo ne povas nun plenumi la peton pro dumtempa superŝarĝo aŭ prizorgado de la servilo.
unauthorized_client: La kliento ne rajtas fari tian peton uzante tiun metodon.
unsupported_grant_type: La tipo de la rajtiga konsento ne estas subtenata de la rajtiga servilo.
unsupported_response_type: La rajtiga servilo ne subtenas tian respondon.
flash:
applications:
create:
notice: Aplikaĵo kreita.
destroy:
notice: Aplikaĵo forigita.
update:
notice: Aplikaĵo aktualigita.
authorized_applications:
destroy:
notice: Aplikaĵo malrajtigita.
layouts:
admin:
nav:
applications: Aplikaĵoj
oauth2_provider: OAuth2-provizanto
application:
title: OAuth-a rajtigo bezonata
scopes:
follow: sekvi, bloki, malbloki kaj malsekvi kontojn
read: legi la datumojn de via konto
write: mesaĝi kiel vi

View File

@@ -23,11 +23,11 @@ fr:
edit: Modifier
submit: Envoyer
confirmations:
destroy: Êtes-vous certain?
destroy: Êtes-vous certain ?
edit:
title: Modifier l'application
form:
error: Oups! Vérifier votre formulaire pour des erreurs possibles
error: Oups ! Vérifier votre formulaire pour des erreurs possibles
help:
native_redirect_uri: Utiliser %{native_redirect_uri} pour les tests locaux
redirect_uri: Utiliser une ligne par URL
@@ -54,7 +54,7 @@ fr:
title: Une erreur est survenue
new:
able_to: Cette application pourra
prompt: Autoriser %{client_name} à utiliser votre compte?
prompt: Autoriser %{client_name} à utiliser votre compte ?
title: Autorisation requise
show:
title: Code d'autorisation
@@ -109,5 +109,5 @@ fr:
title: Autorisation OAuth requise
scopes:
follow: sabonner, se désabonner, bloquer, et débloquer des comptes
read: lire les données de votre compte
read: lire les données de votre compte
write: poster en tant que vous

View File

@@ -94,6 +94,10 @@ en:
following: Following list
upload: Upload
landing_strip_html: <strong>%{name}</strong> is a user on <strong>%{domain}</strong>. You can follow them or interact with them if you have an account anywhere in the fediverse. If you don't, you can <a href="%{sign_up_path}">sign up here</a>.
media_attachments:
validations:
images_and_video: Cannot attach a video to a status that already contains images
too_many: Cannot attach more than 4 files
notification_mailer:
digest:
body: 'Here is a brief summary of what you missed on %{instance} since your last visit on %{since}:'
@@ -152,12 +156,16 @@ en:
formats:
default: "%b %d, %Y, %H:%M"
two_factor_auth:
code_hint: Enter the code generated by your authenticator app to confirm
description_html: If you enable <strong>two-factor authentication</strong>, logging in will require you to be in possession of your phone, which will generate tokens for you to enter.
disable: Disable
enable: Enable
instructions_html: "<strong>Scan this QR code into Google Authenticator or a similiar app on your phone</strong>. From now on, that app will generate tokens that you will have to enter when logging in."
plaintext_secret_html: 'Plain-text secret: <samp>%{secret}</samp>'
enabled_success: Two-factor authentication successfully enabled
instructions_html: "<strong>Scan this QR code into Google Authenticator or a similiar TOTP app on your phone</strong>. From now on, that app will generate tokens that you will have to enter when logging in."
manual_instructions: 'If you can''t scan the QR code and need to enter it manually, here is the plain-text secret:'
setup: Set up
warning: If you cannot configure an authenticator app right now, you should click "disable" or you won't be able to login.
wrong_code: The entered code was invalid! Are server time and device time correct?
users:
invalid_email: The e-mail address is invalid
invalid_otp_token: Invalid two-factor code

164
config/locales/eo.yml Normal file
View File

@@ -0,0 +1,164 @@
---
eo:
about:
about_mastodon: Mastodon estas <em>senpaga, malfermitkoda</em> socia reto. Ĝi estas <em>sencentra</em> alia eblo al komercaj servoj. Ĝi evitigas, ke unusola firmao regu vian tutan komunikadon. Elektu servilon, kiun vi fidas. Kiu ajn estas via elekto, vi povas interagi kun ĉiuj aliaj uzantoj. Iu ajn povas krei sian propran aperaĵon de Mastodon en sia servilo, kaj partopreni en la <em>socia reto</em> tute glate.
about_this: Pri tiu aperaĵo
apps: Aplikaĵoj
business_email: 'Profesia retpoŝt-adreso:'
contact: Kontakti
description_headline: Kio estas %{domain}?
domain_count_after: aliaj aperaĵoj
domain_count_before: Konektita al
features:
api: Malfermita API por aplikaĵoj kaj servoj
blocks: Kompletaj iloj por bloki kaj kaŝi
characters: Po 500 signoj por ĉiu mesaĝo
chronology: Tempolinioj laŭtempaj
ethics: 'Etike kreita: neniu reklamo, neniu ŝpurado'
gifv: Eblo diskonigi etajn videojn kaj GIFV
privacy: Videbleco agordita laŭ la mesaĝo
public: Publikaj tempolinioj
features_headline: Kiel Mastodon estas malsimila
get_started: Komenci
links: Ligiloj
other_instances: Aliaj aperaĵoj
source_code: Fontkodo
status_count_after: mesaĝoj
status_count_before: Kiu publikigis
terms: Terms
user_count_after: uzantoj
user_count_before: Hejmo de
accounts:
follow: Sekvi
followers: Sekvantoj
following: Sekvatoj
nothing_here: Estas nenio ĉi tie!
people_followed_by: Sekvatoj de %{name}
people_who_follow: Sekvantoj de %{name}
posts: Mesaĝoj
remote_follow: Fore sekvi
unfollow: Malsekvi
application_mailer:
settings: 'Ŝanĝi la retpoŝt-mesaĝajn preferojn: %{link}'
signature: Sciigoj de Mastodon el %{instance}
view: 'Vidi:'
applications:
invalid_url: La URL donita ne estas valida
auth:
change_password: Ŝanĝi pasvorton
didnt_get_confirmation: Ĉu vi ne ricevis la instrukciojn por konfirmi?
forgot_password: Pasvorto forgesita?
login: Ensaluti
logout: Elsaluti
register: Membriĝi
resend_confirmation: Resendi la instrukciojn por konfirmi
reset_password: Ŝanĝi la pasvorton
set_new_password: Elekti novan pasvorton
authorize_follow:
error: Bedaŭrinde, okazis eraro provante konsulti la foran konton
follow: Sekvi
prompt_html: 'Vi (<strong>%{self}</strong>) petis sekvi:'
title: Sekvi %{acct}
datetime:
distance_in_words:
about_x_hours: "%{count}h"
about_x_months: "%{count}mo"
about_x_years: "%{count}j"
almost_x_years: "%{count}j"
half_a_minute: Ĵus
less_than_x_minutes: "%{count}m"
less_than_x_seconds: Ĵus
over_x_years: "%{count}j"
x_days: "%{count}t"
x_minutes: "%{count}m"
x_months: "%{count}mo"
x_seconds: "%{count}s"
exports:
blocks: Vi blokas
csv: CSV
follows: Vi sekvas
storage: Mediaĵa konservado
generic:
changes_saved_msg: Ŝanĝoj senprobleme konservitaj!
powered_by: povigita de %{link}
save_changes: Konservi la ŝanĝojn
validation_errors:
one: Io ne okazis senprobleme! Bonvolu konsulti la suban erar-raporton.
other: Io ne okazis senprobleme! Bonvolu konsulti la subajn %{count} erar-raportojn.
imports:
preface: Vi povas alporti kelkajn datumojn, kiel listojn de ĉiuj homoj kiujn vi sekvas aŭ blokas, al via konto de ĉi tiu aperaĵo, per dosiero elportita de alia aperaĵo.
success: Viaj datumoj estis senprobleme alportitaj kaj estos traktitaj kiel planite.
types:
blocking: Listo de blokitoj
following: Listo de sekvatoj
upload: Alporti
landing_strip_html: <strong>%{name}</strong> estas uzanto en <strong>%{domain}</strong>. Vi povas sekvi tiun aŭ interagi kun tiu, se vi havas konton ie ajn en la Fediverse. Se vi ne havas, vi povas <a href="%{sign_up_path}">membriĝi ĉi tie.</a>.
notification_mailer:
digest:
body: 'Jen eta resumo de tio, kio okazis en %{instance}, ekde kiam vi laste vizitis en %{since}:'
mention: "%{name} menciis vin en:"
new_followers_summary:
one: Vi ekhavis novan sekvanton! Jej!
other: Vi ekhavis %{count} novajn sekvantojn! Mirinde!
subject:
one: "1 nova sciigo ekde via lasta vizito \U0001F418"
other: "%{count} novaj sciigoj ekde via lasta vizito \U0001F418"
favourite:
body: '%{name} favoris vian mesaĝon:'
subject: "%{name} favoris vian mesaĝon"
follow:
body: "%{name} eksekvis vin:"
subject: "%{name} eksekvis vin"
follow_request:
body: "%{name} petis sekvi vin:"
subject: '%{name} petis sekvi vin'
mention:
body: '%{name} menciis vin en:'
subject: '%{name} menciis vin'
reblog:
body: '%{name} diskonigis vian mesaĝon:'
subject: "%{name} diskonigis vian mesaĝon"
pagination:
next: Sekva
prev: Malsekva
remote_follow:
acct: Enmetu vian uzantnomo@aperaĵo de kie vi volas sekvi tiun uzanton
missing_resource: La URL de plusendado ne povis esti trovita
proceed: Daŭrigi por plusendi
prompt: 'Vi eksekvos:'
settings:
authorized_apps: Rajtigitaj aplikaĵoj
back: Reveni al Mastodon
edit_profile: Redakti la profilon
export: Elporti datumojn
import: Alporti
preferences: Preferoj
settings: Agordoj
two_factor_auth: Dufaktora aŭtentigo
statuses:
open_in_web: Malfermi retumile
over_character_limit: limo de %{max} signoj trapasita
show_more: Montri pli
visibilities:
private: Montri nur al sekvantoj
public: Publika
unlisted: Publika, sed ne aperos en publikaj tempolinioj
stream_entries:
click_to_show: Alklaki por montri
reblogged: diskonigita
sensitive_content: Tikla enhavo
time:
formats:
default: "%b %d, %Y, %H:%M"
two_factor_auth:
description_html: Se vi ebligas <strong>dufaktoran aŭtentigon</strong>, vi bezonos vian poŝtelefonon por ensaluti, ĉar ĝi kreos nombrojn, kiujn vi devos entajpi.
disable: Malebligi
enable: Ebligi
instructions_html: "<strong>Skanu tiun QR-kodon per Google Authenticator aŭ per simila aplikaĵo de via poŝtelefono</strong>. De tiam, la aplikaĵo kreos nombrojn, kiujn vi devos entajpi."
plaintext_secret_html: 'Rekte legebla sekreta kodo: <samp>%{secret}</samp>'
warning: Se vi ne povas agordi aŭtentigan aplikaĵon nun, elektu "malebligi" aŭ vi ne plu povos ensaluti.
users:
invalid_email: La retpoŝt-adreso ne estas valida
invalid_otp_token: La dufaktora aŭtentigila kodo ne estas valida
will_paginate:
page_gap: "&hellip;"

View File

@@ -7,7 +7,7 @@ fi:
business_email: 'Business e-mail:'
contact: Ota yhteyttä
description_headline: Mikä on %{domain}?
domain_count_after: muut palvelimet
domain_count_after: muuhun palvelimeen
domain_count_before: Yhdistyneenä
features:
api: Avoin API ohjelmille ja palveluille

View File

@@ -4,14 +4,15 @@ fr:
about_mastodon: Mastodon est un serveur <em>libre</em> de réseautage social. Alternative <em>décentralisée</em> aux plateformes commerciales, la monopolisation de vos communications par une entreprise unique est évitée. Tout un chacun peut faire tourner Mastodon et participer au <em>réseau social</em> de manière transparente.
about_this: À propos de cette instance
apps: Applications
business_email: E-mail professionnel
closed_registrations: Les inscriptions sont actuellement fermées sur cette instance. .
business_email: Courriel professionnel
closed_registrations: Les inscriptions sont actuellement fermées sur cette instance.
contact: Contact
description_headline: Qu'est-ce que %{domain} ?
domain_count_after: autres instances
domain_count_before: Connectés à
features:
api: API ouverte aux apps et services
blocking: Outils complets de bloquage et masquage
blocks: Outils complets de bloquage et masquage
characters: 500 caractères par post
chronology: Fil chronologique
ethics: 'Pas de pubs, pas de pistage'
@@ -21,6 +22,7 @@ fr:
features_headline: Ce qui rend Mastodon différent
get_started: Rejoindre le réseau
links: Liens
other_instances: Autres instances
source_code: Code source
status_count_after: posts
status_count_before: Ayant publié
@@ -38,9 +40,9 @@ fr:
remote_follow: Suivre à distance
unfollow: Ne plus suivre
application_mailer:
settings: 'Changer les préférences e-mail: ${link}'
settings: 'Changer les préférences courriel : ${link}'
signature: Notifications de Mastodon depuis %{instance}
view: 'Voir:'
view: 'Voir :'
applications:
invalid_url: L'URL fournie est invalide
auth:
@@ -54,9 +56,30 @@ fr:
reset_password: Réinitialiser le mot de passe
set_new_password: Définir le nouveau mot de passe
authorize_follow:
error: Malheureusement, il y a eu une erreur en cherchant les détails du compte distant
follow: Suivre
prompt_html: 'Vous (<strong>%{self}</strong>) avez demandé à suivre:'
prompt_html: 'Vous (<strong>%{self}</strong>) avez demandé à suivre :'
title: Suivre %{acct}
datetime:
distance_in_words:
about_x_hours: "%{count}h"
about_x_months: "%{count}mois"
about_x_years:
one: un an
other: "%{count} ans"
almost_x_years:
one: un an
other: "%{count} ans"
half_a_minute: A l'instant
less_than_x_minutes: "%{count}min"
less_than_x_seconds: A l'instant
over_x_years:
one: un an
other: "%{count} ans"
x_days: "%{count}j"
x_minutes: "%{count}min"
x_months: "%{count}mois"
x_seconds: "%{count}s"
exports:
blocks: Vous bloquez
csv: CSV
@@ -79,7 +102,7 @@ fr:
landing_strip_html: <strong>%{name}</strong> utilise <strong>%{domain}</strong>. Vous pouvez le/la suivre et interagir si vous possédez un compte quelque part dans le "fediverse". Si ce n'est pas le cas, vous pouvez <a href="%{sign_up_path}">en créer un ici</a>.
notification_mailer:
digest:
body: 'Voici ce que vous avez raté sur ${instance} depuis votre dernière visite (%{}):'
body: 'Voici ce que vous avez raté sur ${instance} depuis votre dernière visite (%{}) :'
mention: '%{name} vous a mentionné⋅e'
new_followers_summary:
one: Vous avez un⋅e nouvel⋅le abonné⋅e ! Youpi !
@@ -93,6 +116,9 @@ fr:
follow:
body: "%{name} vous suit !"
subject: "%{name} vous suit"
follow_request:
body: "%{name} a demandé à vous suivre"
subject: 'Abonné⋅es en attente : %{name}'
mention:
body: "%{name} vous a mentionné⋅e dans :"
subject: "%{name} vous a mentionné⋅e"
@@ -132,14 +158,14 @@ fr:
formats:
default: '%d %b %Y, %H:%M'
two_factor_auth:
description_html: Si vous activez <strong>l'identification à deux facteurs</strong> vous devrez être en posession de votre téléphone afin de générer un code de connexion.
description_html: Si vous activez <strong>l'identification à deux facteurs</strong>, vous devrez être en possession de votre téléphone afin de générer un code de connexion.
disable: Désactiver
enable: Activer
instructions_html: "<strong>Scannez ce QR code grâce à Google Authenticator, Authy ou une application similaire sur votre téléphone</strong>. Désormais, cette application générera des jetons que vous devrez saisir à chaque connexion."
plaintext_secret_html: 'Code secret en clair: <samp>%{secret}</samp>'
plaintext_secret_html: 'Code secret en clair : <samp>%{secret}</samp>'
warning: Si vous ne pouvez pas configurer une application d'authentification maintenant, vous devriez cliquer sur "Désactiver" pour ne pas bloquer l'accès à votre compte.
users:
invalid_email: L'adresse e-mail est invalide
invalid_email: L'adresse courriel est invalide
invalid_otp_token: Le code d'authentification à deux facteurs est invalide
will_paginate:
page_gap: "&hellip;"

View File

@@ -3,7 +3,7 @@ de:
simple_form:
hints:
defaults:
locked: Erlaubt dir, Folger zu überprüfen, bevor sie dir folgen können
locked: Erlaubt dir, Nutzer zu überprüfen, bevor sie dir folgen können
labels:
defaults:
avatar: Avatar
@@ -11,16 +11,16 @@ de:
confirm_password: Passwort bestätigen
current_password: Derzeitiges Passwort
display_name: Anzeigename
email: E-mail-Addresse
email: E-Mail-Addresse
header: Kopfbild
locale: Sprache
locked: Gesperrter Profil
locked: Gesperrtes Profil
new_password: Neues Passwort
note: Über mich
password: Passwort
username: Nutzername
interactions:
must_be_follower: Benachrichtigungen von nicht-Folgern blockieren
must_be_follower: Benachrichtigungen von Nicht-Folgern blockieren
must_be_following: Benachrichtigungen von Nutzern blockieren, denen ich nicht folge
notification_emails:
favourite: E-mail senden, wenn jemand meinen Beitrag favorisiert

View File

@@ -0,0 +1,46 @@
---
eo:
simple_form:
hints:
defaults:
avatar: En la formato PNG, GIF aŭ JPG. Ĝis 2Mo. Estos malgrandigita al 120x120px
display_name: 30 signoj pleje
header: En la formato PNG, GIF aŭ JPG. Ĝis 2Mo. Estos malgrandigita al 700x335px
locked: Vi devos aprobi ĉiun peton de sekvado, kaj viaj mesaĝoj estos senŝanĝe nur por viaj sekvantoj.
note: 160 signoj pleje
imports:
data: Dosiero CSV el alia aperaĵo de Mastodon
labels:
defaults:
avatar: Profilbildo
confirm_new_password: Konfirmi novan pasvorton
confirm_password: Konfirmi la pasvorton
current_password: Nuna pasvorto
data: Datumoj
display_name: Publika nomo
email: Retpoŝt-adreso
header: Kapbildo
locale: Lingvo
locked: Privatigi la konton
new_password: Nova pasvorto
note: Sinprezento
otp_attempt: Dufaktora identigilo
password: Pasvorto
setting_default_privacy: Videbleco de la mesaĝoj
type: Tipo de alportado
username: Uzantnomo
interactions:
must_be_follower: Kaŝi la sciigojn de homoj, kiuj ne sekvas vin
must_be_following: Kaŝi la sciigojn de homoj, kiujn vi ne sekas
notification_emails:
digest: Sendi resumajn retpoŝt-mesaĝojn
favourite: Sendi retpoŝt-mesaĝon, kiam iu favoras mesaĝon de vi
follow: Sendi retpoŝt-mesaĝon, kiam iu eksekvas vin
follow_request: Sendi retpoŝt-mesaĝon, kiam iu petas sekvi vin
mention: Sendi retpoŝt-mesaĝon, kiam iu mencias vin
reblog: Sendi retpoŝt-mesaĝon, kiam iu diskonigas mesaĝon de vi
'no': 'Ne'
required:
mark: "*"
text: bezonata
'yes': 'Jes'

View File

@@ -33,7 +33,7 @@ fr:
must_be_follower: Masquer les notifications des personnes qui ne vous suivent pas
must_be_following: Masquer les notifications des personnes que vous ne suivez pas
notification_emails:
digest: Envoyer des emails récapitulatifs
digest: Envoyer des courriels récapitulatifs
favourite: Envoyer un courriel lorsque quelquun ajoute mes statuts à ses favoris
follow: Envoyer un courriel lorsque quelquun me suit
follow_request: Envoyer un courriel lorsque quelqu'un demande à me suivre

View File

@@ -8,7 +8,7 @@ SimpleNavigation::Configuration.run do |navigation|
settings.item :profile, safe_join([fa_icon('user fw'), t('settings.edit_profile')]), settings_profile_url
settings.item :preferences, safe_join([fa_icon('sliders fw'), t('settings.preferences')]), settings_preferences_url
settings.item :password, safe_join([fa_icon('cog fw'), t('auth.change_password')]), edit_user_registration_url
settings.item :two_factor_auth, safe_join([fa_icon('mobile fw'), t('settings.two_factor_auth')]), settings_two_factor_auth_url
settings.item :two_factor_auth, safe_join([fa_icon('mobile fw'), t('settings.two_factor_auth')]), settings_two_factor_auth_url, highlights_on: %r{/settings/two_factor_auth}
settings.item :import, safe_join([fa_icon('cloud-upload fw'), t('settings.import')]), settings_import_url
settings.item :export, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url
settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url

View File

@@ -60,9 +60,8 @@ Rails.application.routes.draw do
end
end
resource :two_factor_auth, only: [:show] do
resource :two_factor_auth, only: [:show, :new, :create] do
member do
post :enable
post :disable
end
end
@@ -164,6 +163,7 @@ Rails.application.routes.draw do
collection do
get :relationships
get :verify_credentials
patch :update_credentials
get :search
end
@@ -189,11 +189,14 @@ Rails.application.routes.draw do
get '/web/(*any)', to: 'home#index', as: :web
get '/about', to: 'about#index'
get '/about', to: 'about#show'
get '/about/more', to: 'about#more'
get '/terms', to: 'about#terms'
root 'home#index'
match '*unmatched_route', via: :all, to: 'application#raise_not_found'
match '*unmatched_route',
via: :all,
to: 'application#raise_not_found',
format: false
end

View File

@@ -1,4 +1,11 @@
# config/app.yml for rails-settings-cached
#
# This file contains default values, and does not need to be edited
# when configuring an instance. These settings may be changed by an
# Administrator using the Web UI.
#
# For more information, see docs/Running-Mastodon/Administration-guide.md
#
defaults: &defaults
site_title: 'Mastodon'
site_description: ''

View File

@@ -10,7 +10,7 @@ These people make the development of Mastodon possible through [Patreon](https:/
- [Kurtis Rainbolt-Greene](https://mastodon.social/users/krainboltgreene)
- [Kit Redgrave](https://socially.constructed.space/users/KitRedgrave)
- [Zeipher](https://mastodon.social/users/Zeipher)
- [Effy Elden](https://toot.zone/users/effy)
- [Effy Elden](https://mastodon.social/users/effy)
- [Zoë Quinn](https://mastodon.social/users/zoequinn)
**Thank you to the following people**

View File

@@ -7,7 +7,7 @@ So, you have a working Mastodon instance... now what?
The following rake task:
rake mastodon:make_admin USERNAME=alice
RAILS_ENV=production bundle exec rails mastodon:make_admin USERNAME=alice
Would turn the local user "alice" into an admin.
@@ -35,3 +35,11 @@ You are able to set the following settings:
- Site extended description
You may wish to use the extended description (shown at https://yourmastodon.instance/about/more ) to display content guidelines or a user agreement (see https://mastodon.social/about/more for an example).
## Confirming Users Manually
The following rake task:
RAILS_ENV=production bundle exec rails mastodon:confirm_email USER_EMAIL=alice@alice.com
Will confirm a user manually, in case they don't have access to their confirmation email for whatever reason.

View File

@@ -3,13 +3,84 @@ Heroku guide
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://dashboard.heroku.com/new?button-url=https://github.com/tootsuite/mastodon&template=https://github.com/tootsuite/mastodon)
Mastodon can theoretically run indefinitely on a free [Heroku](https://heroku.com) app. It should be noted this has limited testing and could have unpredictable results.
Mastodon can be run on a free [Heroku](https://heroku.com) app. It should be
noted this has limited testing and could have unpredictable results.
1. Click the above button.
2. Fill in the options requested.
* You can use a .herokuapp.com domain, which will be simple to set up, or you can use a custom domain. If you want a custom domain and HTTPS, you will need to upgrade to a paid plan (to use Heroku's SSL features), or set up [CloudFlare](https://cloudflare.com) who offer free "Flexible SSL" (note: CloudFlare have some undefined limits on WebSockets. So far, no one has reported hitting concurrent connection limits).
* You will want Amazon S3 for file storage. The only exception is for development purposes, where you may not care if files are not saved. Follow a guide online for creating a free Amazon S3 bucket and Access Key, then enter the details.
* If you want your Mastodon to be able to send emails, configure SMTP settings here (or later). Consider using [Mailgun](https://mailgun.com) or similar, who offer free plans that should suit your interests.
3. Deploy! The app should be set up, with a working web interface and database. You can change settings and manage versions from the Heroku dashboard.
## Basic setup
You may need to use the `heroku` CLI application to run `USERNAME=yourUsername rails mastodon:make_admin` to make yourself an admin.
Click the button above to start creating a Heroku app with the Mastodon repo as
the source. This tells Heroku to use the `app.json` file which does things like
prompt for config variables, set up the right buildpacks, run a postdeploy task,
and add the appropriate addons.
If you don't use the deploy button and app.json approach, you will need to do
some of that manually.
## Domain names and SSL
You can add your domain name to the Heroku app's setting, and then also use
Heroku's (free) auto renewal program for Lets Encrypt certificates, by
requesting a cert from the settings screen. You'll have to point your hostname
DNS at Heroku using the values heroku gives you on this screen, using whatever
method is appropriate for your DNS setup.
You should set the Heroku config vars of `LOCAL_DOMAIN` to your hostname, and
`LOCAL_HTTPS` to "true" as well.
## Email
Consider using [Mailgun](https://mailgun.com) or similar, who offer free plans
that should suit your interests. Look in `production.rb` to see which config
variables need to be set on Heroku for outgoing email to work.
## File storage
You will want Amazon S3 for file storage. The only exception is for development
purposes, where you may not care if files are not saved. Follow a guide online
for creating a free Amazon S3 bucket and Access Key, then enter the details.
If you deploy from the web, the format for all the S3 bits use Paperclip conventions:
S3 Bucket is just the name of the bucket, e.g. `bucketname` not the full ARN.
S3 Region is the AWS code for the region e.g. `ap-northeast-1` not the name of the city displayed on the AWS Dashboard.
To protect the privacy of the users of the your instance, you should have permissons on the your S3 bucket set to no-read and no-write for the public and non-application-specific AWS users, with only one authorized IAM user or group set up to be able to upload or display content. This is an example of an IAM policy used for the S3 bucket used Mastadon instance hentai.loan:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:ListAllMyBuckets"
],
"Resource": [
"arn:aws:s3:::*"
]
},
{
"Effect": "Allow",
"Action": [
"s3:*"
],
"Resource": [
"arn:aws:s3:::hentailoan”,
"arn:aws:s3:::hentailoan/*"
]
}
]
}
## Deployment
You can deploy from the Heroku web interface or from the command line. Run:
`heroku run rails db:migrate`
after you first deploy to set up the first database.
To make yourself an admin, you may need to use the `heroku` CLI application after creating an account online:
`heroku rake mastodon:make_admin USERNAME=yourUsername`

View File

@@ -24,7 +24,7 @@ server {
ssl_protocols TLSv1.2;
ssl_ciphers EECDH+AESGCM:EECDH+AES;
ssl_ecdh_curve secp384r1;
ssl_ecdh_curve prime256v1;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
@@ -90,7 +90,9 @@ It is recommended to create a special user for mastodon on the server (you could
sudo apt-get install imagemagick ffmpeg libpq-dev libxml2-dev libxslt1-dev nodejs file git curl
curl -sL https://deb.nodesource.com/setup_4.x | sudo bash -
apt-get intall nodejs
sudo apt-get install nodejs
sudo npm install -g yarn
## Redis

View File

@@ -13,5 +13,6 @@ Some people have started working on apps for the Mastodon API. Here is a list of
|Albatross|iOS||[@goldie_ice@mastodon.social](https://mastodon.social/users/goldie_ice)|
|Tooter|Chrome|<https://github.com/ineffyble/tooter>|[@effy@mastodon.social](https://mastodon.social/users/effy)|
|tootstream|CLI|<https://github.com/magicalraccoon/tootstream>|[@Raccoon@mastodon.social](https://mastodon.social/users/Raccoon)|
|HackerNewsBot|CLI|<https://github.com/raymestalez/mastodon-hnbot>|[@rayalez@hackertribe.io](https://hackertribe.io/users/rayalez)|
If you have a project like this, let me know so I can add it to the list!

View File

@@ -7,6 +7,7 @@ There is also a list at [instances.mastodon.xyz](https://instances.mastodon.xyz)
| -------------|-------------|---|---|
| [mastodon.social](https://mastodon.social) |Flagship, quick updates|No|No|
| [securitymastod.one](https://securitymastod.one/) |Information security enthusiasts and pros|Yes|Yes|
| [mastodon.nuzgo.net](https://mastodon.nuzgo.net/) |Mastodon instance hosted in Paris |Yes|Yes|
| [mastodon.cx](https://mastodon.cx/) |Alternative Mastodon instance hosted in France|Yes|Yes|
| [mastodon.network](https://mastodon.network) |N/A|Yes|Yes|
| [awoo.space](https://awoo.space) |Intentionally moderated, only federates with mastodon.social|Yes|No|
@@ -17,9 +18,11 @@ There is also a list at [instances.mastodon.xyz](https://instances.mastodon.xyz)
| [gay.crime.team](https://gay.crime.team) |the place for doin' gay crime online (please don't actually do crime here)|No|No|
| [icosahedron.website](https://icosahedron.website/) |Icosahedron-themed (well, visually), open registration.|Yes|No|
| [memetastic.space](https://memetastic.space) |Memes|Yes|No|
| [masto.razrnet.fr](https://masto.razrnet.fr) |Instance Française pour tout le monde ! Développeurs, gamers, etc...|Yes|No|
| [social.diskseven.com](https://social.diskseven.com) |Single user|No|Yes|
| [social.gestaltzerfall.net](https://social.gestaltzerfall.net) |Single user|No|No|
| [mastodon.xyz](https://mastodon.xyz) |N/A|Yes|Yes|
| [mastodon.land](https://mastodon.land) |N/A|Yes|Yes|
| [mastodon.partipirate.org](https://mastodon.partipirate.org) |French Pirate Party Instance - Politics and stuff|Yes|No|
| [social.targaryen.house](https://social.targaryen.house) |Federates everywhere, quick updates.|Yes|Yes|
| [masto.themimitoof.fr](https://masto.themimitoof.fr) |N/A|Yes|Yes|
@@ -33,8 +36,8 @@ There is also a list at [instances.mastodon.xyz](https://instances.mastodon.xyz)
| [social.alex73630.xyz](https://social.alex73630.xyz) |Francophones|Yes|Yes|
| [oc.todon.fr](https://oc.todon.fr) |Modérée et principalement francophone, pas de tolérances pour misogynie/LGBTphobies/validisme/etc.|Yes|Yes|
| [maly.io](https://maly.io) |N/A|Yes|No|
| [social.lou.lt](https://social.lou.lt) |N/A|Yes|No|
| [mastodon.ninetailed.uk](https://mastodon.ninetailed.uk) |N/A|Yes|No|
| [social.lou.lt](https://social.lou.lt) |Francophones|Yes|No|
| [mastodon.ninetailed.uk](https://mastodon.ninetailed.uk) |Open registrations, furry-friendly, UK-based|Yes|No|
| [soc.louiz.org](https://soc.louiz.org) |"Coucou"|Yes|No|
| [7nw.eu](https://7nw.eu) |N/A|Yes|No|
| [mastodon.gougere.fr](https://mastodon.gougere.fr)|N/A|Yes|No|
@@ -47,6 +50,33 @@ There is also a list at [instances.mastodon.xyz](https://instances.mastodon.xyz)
| [status.dissidence.ovh](https://status.dissidence.ovh)|N/A|Yes|Yes|
| [mastodon.cc](https://mastodon.cc)|Art|Yes|No|
| [mastodon.technology](https://mastodon.technology)|Open registrations, federates everywhere, for tech folks|Yes|No|
| [mastodon.systemlab.fr](https://mastodon.systemlab.fr/)|Le mastodon Français, informatique, jeux-vidéos, gaming et hébergement.|Yes|No|
| [mastodon.systemlab.fr](https://mastodon.systemlab.fr/)|Le mastodon Français, informatique, jeux-vidéos, gaming et hébergement.|Yes|
| [mastodon.top](https://mastodon.top) |N/A|Yes|Yes|
| [niu.moe](https://niu.moe/)|:dolls: The most cutest node ever, FR/EN, anime and computer :balloon:|Yes|Yes|
| [im-in.space](https://im-in.space/)|SPAAAAACE! Probably with a lot of French people. (Invite-only, might randomly open registrations)|No|Yes|
| [social.bytestemplar.com](https://social.bytestemplar.com)|N/A|Yes|No|
| [digitalhumanities.club](http://www.digitalhumanities.club)|[Digital humanities](http://whatisdigitalhumanities.com) community; invitations will open once code of conduct drafted.|No|No
| [design.vu](https://design.vu)|— what's your design view‽|Yes|No|
| [masto.raildecake.fr](https://masto.raildecake.fr)|Hebergé chez un FAI associatif dans le sud de la france, grillons & pins en options|Yes|No|
| [good-dragon.com](https://good-dragon.com/)|Quick updates, Relaxed Moderation, Federates Everywhere, Furries|Yes|No|
| [rich.gop](https://rich.gop/)|Federates everywhere, Open registration, Privacy respected|Yes|Yes|
| [social.nowa.re](https://social.nowa.re)|Open Registration|Yes|No|
| [mastodon.ml](http://mastodon.ml) |A chill place to hangout and chat about anime, programming and movies.|Yes|Yes|
| [off-the-clock.us](https://off-the-clock.us/)|The work day is over.|Yes|No|
| [infinimatix.net](https://infinimatix.net)|Informatics|Yes|Yes|
| [social.0day.agency](https://social.0day.agency)|Infosec, Hacking, Fun (only protonmail)|Yes|Yes|
| [kagrumez.lerk.io](https://kagrumez.lerk.io)|Open registration. German end english.|Yes|No|
| [meow.social](https://meow.social)|A furry fandom focused instance|Yes|No|
| [neumastodon.com](https://neumastodon.com/)|Northeastern University Mastodon |Yes|No|
| [dancingbanana.party](https://dancingbanana.party)|La banane qui danse.|Yes|No|
| [mastodon.brussels](https://mastodon.brussels/)|Le mastodon pour les belges, si vous aimez la bonne ambiance venez nous rejoindre !|Yes|Yes|
| [mastodon.llamasweet.tech](https://mastodon.llamasweet.tech/)|Mastodon about Android developement|Yes|No|
| [manx.social](https://manx.social/)|Instance for the Isle of Man|Yes|Yes|
| [mastodon.host](https://mastodon.host/)|Lightly moderated, federates everywhere and has a follow bot ( Huge federated timeline )|Yes|No|
| [mastodon.fun](https://mastodon.fun/)|Mastodon for everyone ! |Yes|Yes|
| [oulipo.social](https://oulipo.social/)|An Oulipo Mastodon in which that fifth symbol in Latin script is taboo|Yes|No|
| [indigo.zone](https://indigo.zone)|Open Registrations, General Purpose|Yes|No|
| [mst3k.interlinked.me](https://mst3k.interlinked.me)|Open registrations, general purpose|Yes|Yes|
Let me know if you start running one so I can add it to the list! (Alternatively, add it yourself as a pull request).
We are no longer maintaining this list as instances are popping up too quickly for using GitHub to be a tenable system for tracking them. Please standby while we work on another solution

View File

@@ -30,7 +30,7 @@ API overview
- [Instance](#instance)
- [Mention](#mention)
- [Notification](#notification)
- [Relationships](#relationships)
- [Relationship](#relationship)
- [Results](#results)
- [Status](#status)
- [Tag](#tag)
@@ -43,6 +43,7 @@ ___
- [For Python](https://github.com/halcy/Mastodon.py)
- [For JavaScript](https://github.com/Zatnosk/libodonjs)
- [For JavaScript (Node.js)](https://github.com/jessicahayley/node-mastodon)
- [For Elixir](https://github.com/milmazz/hunter)
___
@@ -84,6 +85,17 @@ Returns an [Account](#account).
Returns the authenticated user's [Account](#account).
#### Updating the current user:
PATCH /api/v1/accounts/update_credentials
Form data:
- `display_name`: The name to display in the user's profile
- `note`: A new biography for the user
- `avatar`: A base64 encoded image to display as the user's avatar (e.g. `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUoAAADrCAYAAAA...`)
- `header`: A base64 encoded image to display as the user's header image (e.g. `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUoAAADrCAYAAAA...`)
#### Getting an account's followers:
GET /api/v1/accounts/:id/followers
@@ -455,7 +467,7 @@ ___
| `acct` | Equals `username` for local users, includes `@domain` for remote ones |
| `id` | Account ID |
### Notifications
### Notification
| Attribute | Description |
| ------------------------ | ----------- |
@@ -463,9 +475,9 @@ ___
| `type` | One of: "mention", "reblog", "favourite", "follow" |
| `created_at` | The time the notification was created |
| `account` | The [Account](#account) sending the notification to the user |
| `status` | The [Status](#status) associated with the notification, if applicible |
| `status` | The [Status](#status) associated with the notification, if applicable |
### Relationships
### Relationship
| Attribute | Description |
| ------------------------ | ----------- |
@@ -515,7 +527,7 @@ ___
| `tags` | An array of [Tags](#tag) |
| `application` | [Application](#application) from which the status was posted |
### Tags
### Tag
| Attribute | Description |
| ------------------------ | ----------- |

View File

@@ -10,6 +10,15 @@ namespace :mastodon do
puts "Congrats! #{user.account.username} is now an admin. \\o/\nNavigate to #{admin_settings_url} to get started"
end
desc 'Manually confirms a user with associated user email address stored in USER_EMAIL environment variable.'
task confirm_email: :environment do
email = ENV.fetch('USER_EMAIL')
user = User.where(email: email)
user.update(confirmed_at: Time.now.utc)
puts "User #{email} confirmed."
end
namespace :media do
desc 'Removes media attachments that have not been assigned to any status for longer than a day'
task clear: :environment do

View File

@@ -3,9 +3,16 @@ require 'rails_helper'
RSpec.describe AboutController, type: :controller do
render_views
describe 'GET #index' do
describe 'GET #show' do
it 'returns http success' do
get :index
get :show
expect(response).to have_http_status(:success)
end
end
describe 'GET #more' do
it 'returns http success' do
get :more
expect(response).to have_http_status(:success)
end
end

View File

@@ -24,6 +24,45 @@ RSpec.describe Api::V1::AccountsController, type: :controller do
end
end
describe 'PATCH #update_credentials' do
describe 'with valid data' do
before do
avatar = File.read(Rails.root.join('app', 'assets', 'images', 'logo.png'))
header = File.read(Rails.root.join('app', 'assets', 'images', 'mastodon-getting-started.png'))
patch :update_credentials, params: {
display_name: "Alice Isn't Dead",
note: "Hi!\n\nToot toot!",
avatar: "data:image/png;base64,#{Base64.encode64(avatar)}",
header: "data:image/png;base64,#{Base64.encode64(header)}",
}
end
it 'returns http success' do
expect(response).to have_http_status(:success)
end
it 'updates account info' do
user.account.reload
expect(user.account.display_name).to eq("Alice Isn't Dead")
expect(user.account.note).to eq("Hi!\n\nToot toot!")
expect(user.account.avatar).to exist
expect(user.account.header).to exist
end
end
describe 'with invalid data' do
before do
patch :update_credentials, params: { note: 'This is too long. ' * 10 }
end
it 'returns http unprocessable entity' do
expect(response).to have_http_status(:unprocessable_entity)
end
end
end
describe 'GET #statuses' do
it 'returns http success' do
get :statuses, params: { id: user.account.id }

Some files were not shown because too many files have changed in this diff Show More