Compare commits
347 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
756db8103a | ||
|
20c0054460 | ||
|
ae78d012ac | ||
|
ef900789bc | ||
|
d78f555254 | ||
|
c2f70829d9 | ||
|
b280c387c8 | ||
|
b75f13927e | ||
|
22cb286ad7 | ||
|
8f4b7c1820 | ||
|
2e112e2406 | ||
|
812fe90eca | ||
|
6c1122a1d9 | ||
|
d3be2b582a | ||
|
419226d1f6 | ||
|
f554807563 | ||
|
d972845ff6 | ||
|
2c405aed55 | ||
|
da0a18a318 | ||
|
8ed3fa1693 | ||
|
60fe9983ee | ||
|
724fc3cbdf | ||
|
de475cf8d3 | ||
|
b369fc2de4 | ||
|
8c5eaf7ae9 | ||
|
7eb8b2efad | ||
|
b6f6152e26 | ||
|
f8ee136c29 | ||
|
f1ab70649b | ||
|
1548695c83 | ||
|
3da521a586 | ||
|
d22cec81fb | ||
|
d2e0edd721 | ||
|
3002a89419 | ||
|
17ba662004 | ||
|
db4119f971 | ||
|
4a3db71692 | ||
|
dc559d6b7a | ||
|
595e060347 | ||
|
8e4fc5d5d2 | ||
|
b8b7b506a2 | ||
|
550863198c | ||
|
198ae3e366 | ||
|
6e4c7d6211 | ||
|
d2542dcec0 | ||
|
f18a6c2cf2 | ||
|
25e5aa645d | ||
|
620d0d8029 | ||
|
8ec8410651 | ||
|
4cc8ddabe5 | ||
|
07e875972a | ||
|
79ef8b3653 | ||
|
b11c4326d2 | ||
|
390a2a8ab9 | ||
|
cf6f67997e | ||
|
4d1ce3c7ad | ||
|
76449df903 | ||
|
226c9836e4 | ||
|
05008f3930 | ||
|
59ceeae8ea | ||
|
b397f69633 | ||
|
5b3c7572ca | ||
|
e89e4355eb | ||
|
abe0d9421f | ||
|
7c1f3f8163 | ||
|
eab93992d1 | ||
|
0d59d7c680 | ||
|
1efda1c453 | ||
|
a51c8074df | ||
|
3722f90865 | ||
|
9bddb946f0 | ||
|
bbaac89eb0 | ||
|
0dfffb6dcb | ||
|
1b0a5658f1 | ||
|
682b68438e | ||
|
09ec6e504b | ||
|
a2a2af244c | ||
|
cb50ecdb07 | ||
|
1379124682 | ||
|
0b34ade66b | ||
|
191696ab30 | ||
|
af706583bd | ||
|
85c9496340 | ||
|
6ee3a10f17 | ||
|
d0dd9eb5b5 | ||
|
a588358f41 | ||
|
0a110d07b6 | ||
|
5f727f9068 | ||
|
72c8562cc9 | ||
|
882e4f5322 | ||
|
bc1a91f4cd | ||
|
aeb90b7c4a | ||
|
fb87e847bc | ||
|
657496b5a9 | ||
|
fd03a3d957 | ||
|
4bd0488a77 | ||
|
1b17da6ed9 | ||
|
dc5b746f42 | ||
|
89210781cb | ||
|
e9810cbad6 | ||
|
1027556614 | ||
|
3dcb5fa28f | ||
|
1d5dcfcd46 | ||
|
66ff9ed34e | ||
|
66328adf83 | ||
|
b65950bb2e | ||
|
0d70fe2659 | ||
|
a1fc2cfa09 | ||
|
b535966ab5 | ||
|
02412429ab | ||
|
5abdc77c80 | ||
|
b5a9c6b3d2 | ||
|
60f3230a05 | ||
|
0cb4b9205c | ||
|
43d754eb42 | ||
|
2cc0d56652 | ||
|
e0c3ed29d8 | ||
|
2991a7cfe6 | ||
|
44a3584e2d | ||
|
831386977e | ||
|
68035966fb | ||
|
62a98a3f0e | ||
|
888864ad5a | ||
|
654f4f62ed | ||
|
58bcd50f7f | ||
|
60ecfb87ae | ||
|
d0ef318eaa | ||
|
65f9db73b0 | ||
|
a822f7a05a | ||
|
a2c8da0185 | ||
|
88fd5cb688 | ||
|
c78e8c01a3 | ||
|
0ec77c5b3e | ||
|
2d000e9c4e | ||
|
b913746752 | ||
|
9cd3a6836b | ||
|
53c2274d48 | ||
|
7ff84cb07e | ||
|
e6fbf0334f | ||
|
72698bc3b4 | ||
|
65027657ec | ||
|
08949cca41 | ||
|
a231f915a0 | ||
|
c3ef5d5414 | ||
|
57a3d71c90 | ||
|
43db2cf5e7 | ||
|
cc9a6a710f | ||
|
2fba4196ef | ||
|
fd66f7cdc0 | ||
|
d142544159 | ||
|
7ac092513c | ||
|
2db53526c9 | ||
|
1f28d40c78 | ||
|
e2491680e6 | ||
|
3a38322a54 | ||
|
29d8313b28 | ||
|
682507bc3c | ||
|
441d6dc734 | ||
|
d5cabfe5c6 | ||
|
af6a84da14 | ||
|
08e94d1b19 | ||
|
2fba94b36e | ||
|
8c9116dc98 | ||
|
42eb841dc2 | ||
|
584b45530c | ||
|
f5cdea5122 | ||
|
f36a791227 | ||
|
ef226a6f22 | ||
|
7c249dfd88 | ||
|
5bea42412e | ||
|
04166c4a35 | ||
|
fed585e3f4 | ||
|
406229d927 | ||
|
7a7d12d27f | ||
|
cd830a2fab | ||
|
aef554d553 | ||
|
01c4c29b3a | ||
|
459bbfa4b2 | ||
|
7140def5c9 | ||
|
b85dec2b97 | ||
|
cbd673601c | ||
|
66a3979cba | ||
|
9de254c46e | ||
|
388e70b881 | ||
|
8c9aff0bef | ||
|
48594b18e6 | ||
|
b18504adfe | ||
|
bba537a7be | ||
|
0291b73de7 | ||
|
28e674bc6a | ||
|
9d84dda213 | ||
|
d63c291f86 | ||
|
6ad19036e3 | ||
|
3bdcf5d8f0 | ||
|
5c1f70b5c5 | ||
|
c7848f54ff | ||
|
267ed3d74b | ||
|
d3704fdb09 | ||
|
ca05bfaac7 | ||
|
e4b84c7ba5 | ||
|
983593ddf4 | ||
|
f14df43435 | ||
|
f000673599 | ||
|
5b6c2a1e72 | ||
|
d372068620 | ||
|
139d183485 | ||
|
d7c17c32af | ||
|
ee1486a7de | ||
|
b8ba719f73 | ||
|
ada8a6cb77 | ||
|
6c678b7472 | ||
|
bfbfaf9f9f | ||
|
df81bc4a97 | ||
|
87588fa894 | ||
|
74036a2c9d | ||
|
05b72368ed | ||
|
6f71cfeff9 | ||
|
59ca634b89 | ||
|
8009366231 | ||
|
bd71327180 | ||
|
67b7d3d3b6 | ||
|
6358a169fd | ||
|
99b9a0e5de | ||
|
aa235318fc | ||
|
a0b1951791 | ||
|
2d45794956 | ||
|
453fb84c9c | ||
|
59804abc3d | ||
|
496f466d73 | ||
|
fa033c4d5f | ||
|
b8e166894b | ||
|
1f15a15621 | ||
|
fd1e29c3f8 | ||
|
553e13144f | ||
|
494945ff4f | ||
|
7c0cd2597a | ||
|
37caf0b36e | ||
|
cf0b753209 | ||
|
ddc34feb58 | ||
|
3f5b994ff0 | ||
|
dacdfec973 | ||
|
72c30f8393 | ||
|
4e05751346 | ||
|
ee3e0a93f4 | ||
|
d1290fbd8f | ||
|
484c9709b6 | ||
|
d08f1112d5 | ||
|
bcfd9a2f8e | ||
|
886176f854 | ||
|
d397d0d681 | ||
|
20c37ed0f9 | ||
|
9501a87704 | ||
|
8f0f4a861a | ||
|
8c9ea9b849 | ||
|
4d22d03fab | ||
|
81584779cb | ||
|
61c33652ad | ||
|
f9d398e8fb | ||
|
74c8ca699c | ||
|
eddb95b012 | ||
|
84eb425f38 | ||
|
a50a87457e | ||
|
566e0a772d | ||
|
11077af52f | ||
|
0fc73a6e47 | ||
|
2bd132d458 | ||
|
91ddd345f2 | ||
|
75bd141e22 | ||
|
0cdcf32865 | ||
|
629a4d0fca | ||
|
e95983f5df | ||
|
e37e84d210 | ||
|
e57e6f509d | ||
|
bea117a4b6 | ||
|
908b96a370 | ||
|
13c16b4e95 | ||
|
4fcc0d5ac9 | ||
|
3b51581f1b | ||
|
db92eec876 | ||
|
44969307c7 | ||
|
4babdff72f | ||
|
c997091166 | ||
|
005f1fd360 | ||
|
8d4e7504b1 | ||
|
aa6a26a2d5 | ||
|
d91ba3c8d0 | ||
|
bafd22ecf4 | ||
|
dd9d57300b | ||
|
8c5ad23b24 | ||
|
53384b0ffe | ||
|
1c469ca98b | ||
|
e61ecf4091 | ||
|
90c00f075a | ||
|
38473f0aa0 | ||
|
24a5d13d60 | ||
|
383c0b7802 | ||
|
bf8031e984 | ||
|
ab307b816b | ||
|
40562fd266 | ||
|
5f9cb48882 | ||
|
2ab7dc9a55 | ||
|
2b9bc9c154 | ||
|
f5bf5ebb82 | ||
|
26bc591572 | ||
|
268dd32d76 | ||
|
bea97ea766 | ||
|
03f3223d72 | ||
|
7880671f35 | ||
|
b5eec34230 | ||
|
2128682162 | ||
|
e68c0ce5f6 | ||
|
54dddfe9b8 | ||
|
aea3aff4e4 | ||
|
46943b64c6 | ||
|
302c0d2046 | ||
|
22b1a70274 | ||
|
6f75c8451d | ||
|
b9b78549f3 | ||
|
438ce5809f | ||
|
f485fa31f3 | ||
|
34ae4cf511 | ||
|
298796cc7b | ||
|
a4859446ab | ||
|
7bffd16024 | ||
|
2bd46f442d | ||
|
11b706acdf | ||
|
33b9e8d461 | ||
|
f025cc6782 | ||
|
3988f2dade | ||
|
1899cf5f04 | ||
|
5259319cf5 | ||
|
b83bc0ae64 | ||
|
282427cdd9 | ||
|
c67d3c990b | ||
|
2e47fe3e1a | ||
|
e12bb39c20 | ||
|
5caa727e7e | ||
|
0a46201a66 | ||
|
3f248dcaae | ||
|
baa43e40a0 | ||
|
a6788662b0 | ||
|
4a5f73c8ae | ||
|
fdcf884cf7 | ||
|
964035b118 | ||
|
5135d609b7 | ||
|
f48cb3eb17 | ||
|
8325866c61 |
63
.babelrc
63
.babelrc
@@ -1,7 +1,62 @@
|
||||
{
|
||||
"presets": ["es2015", "react"],
|
||||
"presets": [
|
||||
"react",
|
||||
[
|
||||
"env",
|
||||
{
|
||||
"loose": true,
|
||||
"modules": false,
|
||||
"targets": {
|
||||
"browsers": ["last 2 versions", "IE >= 11", "iOS >= 9"]
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
"plugins": [
|
||||
"transform-decorators-legacy",
|
||||
"transform-object-rest-spread"
|
||||
]
|
||||
"syntax-dynamic-import",
|
||||
"transform-object-rest-spread",
|
||||
"transform-class-properties",
|
||||
[
|
||||
"react-intl",
|
||||
{
|
||||
"messagesDir": "./build/messages"
|
||||
}
|
||||
]
|
||||
],
|
||||
"env": {
|
||||
"development": {
|
||||
"plugins": [
|
||||
"transform-react-jsx-source",
|
||||
"transform-react-jsx-self"
|
||||
]
|
||||
},
|
||||
"production": {
|
||||
"plugins": [
|
||||
"lodash",
|
||||
[
|
||||
"transform-react-remove-prop-types",
|
||||
{
|
||||
"mode": "remove",
|
||||
"removeImport": true,
|
||||
"additionalLibraries": [
|
||||
"react-immutable-proptypes"
|
||||
]
|
||||
}
|
||||
],
|
||||
[
|
||||
"transform-runtime",
|
||||
{
|
||||
"helpers": true,
|
||||
"polyfill": false,
|
||||
"regenerator": false
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"test": {
|
||||
"plugins": [
|
||||
"transform-es2015-modules-commonjs"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,2 +1,3 @@
|
||||
https://github.com/heroku/heroku-buildpack-apt
|
||||
https://github.com/Scalingo/nodejs-buildpack
|
||||
https://github.com/Scalingo/ruby-buildpack
|
||||
|
@@ -1,14 +1,21 @@
|
||||
engines:
|
||||
duplication:
|
||||
enabled: false
|
||||
rubocop:
|
||||
enabled: true
|
||||
eslint:
|
||||
enabled: true
|
||||
brakeman:
|
||||
enabled: true
|
||||
bundler-audit:
|
||||
enabled: true
|
||||
duplication:
|
||||
enabled: false
|
||||
eslint:
|
||||
enabled: true
|
||||
rubocop:
|
||||
enabled: true
|
||||
scss-lint:
|
||||
enabled: true
|
||||
ratings:
|
||||
paths:
|
||||
- "**.rb"
|
||||
- "**.js"
|
||||
paths:
|
||||
- "**.rb"
|
||||
- "**.js"
|
||||
- "**.scss"
|
||||
exclude_paths:
|
||||
- spec/
|
||||
- vendor/asset
|
||||
|
@@ -9,3 +9,5 @@ vendor/bundle
|
||||
.DS_Store
|
||||
*.swp
|
||||
*~
|
||||
postgres
|
||||
redis
|
||||
|
@@ -1,7 +1,8 @@
|
||||
# Service dependencies
|
||||
# You may set REDIS_URL instead for more advanced options
|
||||
REDIS_HOST=redis
|
||||
REDIS_PORT=6379
|
||||
# REDIS_DB=0
|
||||
# You may set DATABASE_URL instead for more advanced options
|
||||
DB_HOST=db
|
||||
DB_USER=postgres
|
||||
DB_NAME=postgres
|
||||
@@ -9,11 +10,14 @@ DB_PASS=
|
||||
DB_PORT=5432
|
||||
|
||||
# Federation
|
||||
LOCAL_DOMAIN=example.com
|
||||
# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects.
|
||||
# LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com.
|
||||
LOCAL_DOMAIN=example.com
|
||||
LOCAL_HTTPS=true
|
||||
|
||||
# Use this only if you need to run mastodon on a different domain than the one used for federation.
|
||||
# Do not use this unless you know exactly what you are doing.
|
||||
# You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md
|
||||
# DO *NOT* USE THIS UNLESS YOU KNOW *EXACTLY* WHAT YOU ARE DOING.
|
||||
# WEB_DOMAIN=mastodon.example.com
|
||||
|
||||
# Application secrets
|
||||
@@ -36,8 +40,8 @@ OTP_SECRET=
|
||||
# E-mail configuration
|
||||
# Note: Mailgun and SparkPost (https://sparkpo.st/smtp) each have good free tiers
|
||||
# If you want to use an SMTP server without authentication (e.g local Postfix relay)
|
||||
# then set SMTP_AUTH_METHOD to 'none' and *comment* SMTP_LOGIN and SMTP_PASSWORD.
|
||||
# Leaving them blank is not enough for authentication method 'none'.
|
||||
# then set SMTP_AUTH_METHOD and SMTP_OPENSSL_VERIFY_MODE to 'none' and
|
||||
# *comment* SMTP_LOGIN and SMTP_PASSWORD (leaving them blank is not enough).
|
||||
SMTP_SERVER=smtp.mailgun.org
|
||||
SMTP_PORT=587
|
||||
SMTP_LOGIN=
|
||||
@@ -46,6 +50,7 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
||||
#SMTP_DOMAIN= # defaults to LOCAL_DOMAIN
|
||||
#SMTP_DELIVERY_METHOD=smtp # delivery method can also be sendmail
|
||||
#SMTP_AUTH_METHOD=plain
|
||||
#SMTP_CA_FILE=/etc/ssl/certs/ca-certificates.crt
|
||||
#SMTP_OPENSSL_VERIFY_MODE=peer
|
||||
#SMTP_ENABLE_STARTTLS_AUTO=true
|
||||
|
||||
@@ -90,3 +95,8 @@ SMTP_FROM_ADDRESS=notifications@example.com
|
||||
# Cluster number setting for streaming API server.
|
||||
# If you comment out following line, cluster number will be `numOfCpuCores - 1`.
|
||||
STREAMING_CLUSTER_NUM=1
|
||||
|
||||
# Docker mastodon user
|
||||
# If you use Docker, you may want to assign UID/GID manually.
|
||||
# UID=1000
|
||||
# GID=1000
|
||||
|
@@ -1,79 +0,0 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"node": false,
|
||||
"es6": true
|
||||
},
|
||||
|
||||
"parser": "babel-eslint",
|
||||
|
||||
"plugins": [
|
||||
"react",
|
||||
"jsx-a11y"
|
||||
],
|
||||
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
|
||||
"ecmaFeatures": {
|
||||
"arrowFunctions": true,
|
||||
"jsx": true,
|
||||
"destructuring": true,
|
||||
"modules": true,
|
||||
"spread": true
|
||||
}
|
||||
},
|
||||
|
||||
"rules": {
|
||||
"no-cond-assign": 2,
|
||||
"no-console": 1,
|
||||
"no-irregular-whitespace": 2,
|
||||
"no-unreachable": 2,
|
||||
"valid-typeof": 2,
|
||||
"consistent-return": 2,
|
||||
"dot-notation": 2,
|
||||
"eqeqeq": 2,
|
||||
"no-fallthrough": 2,
|
||||
"no-unused-expressions": 2,
|
||||
"strict": 0,
|
||||
"no-catch-shadow": 2,
|
||||
"indent": [1, 2],
|
||||
"brace-style": 1,
|
||||
"comma-spacing": [1, {"before": false, "after": true}],
|
||||
"comma-style": [1, "last"],
|
||||
"no-mixed-spaces-and-tabs": 1,
|
||||
"no-nested-ternary": 1,
|
||||
"no-trailing-spaces": 1,
|
||||
|
||||
"react/jsx-wrap-multilines": 2,
|
||||
"react/self-closing-comp": 2,
|
||||
"react/prop-types": 2,
|
||||
"react/no-multi-comp": 0,
|
||||
|
||||
"jsx-a11y/accessible-emoji": 1,
|
||||
"jsx-a11y/anchor-has-content": 1,
|
||||
"jsx-a11y/aria-activedescendant-has-tabindex": 1,
|
||||
"jsx-a11y/aria-props": 1,
|
||||
"jsx-a11y/aria-proptypes": 1,
|
||||
"jsx-a11y/aria-role": 1,
|
||||
"jsx-a11y/aria-unsupported-elements": 1,
|
||||
"jsx-a11y/heading-has-content": 1,
|
||||
"jsx-a11y/href-no-hash": 1,
|
||||
"jsx-a11y/html-has-lang": 1,
|
||||
"jsx-a11y/iframe-has-title": 1,
|
||||
"jsx-a11y/img-has-alt": 1,
|
||||
"jsx-a11y/img-redundant-alt": 1,
|
||||
"jsx-a11y/label-has-for": 1,
|
||||
"jsx-a11y/mouse-events-have-key-events": 1,
|
||||
"jsx-a11y/no-access-key": 1,
|
||||
"jsx-a11y/no-distracting-elements": 1,
|
||||
"jsx-a11y/no-onchange": 1,
|
||||
"jsx-a11y/no-redundant-roles": 1,
|
||||
"jsx-a11y/onclick-has-focus": 1,
|
||||
"jsx-a11y/onclick-has-role": 1,
|
||||
"jsx-a11y/role-has-required-aria-props": 1,
|
||||
"jsx-a11y/role-supports-aria-props": 1,
|
||||
"jsx-a11y/scope": 1,
|
||||
"jsx-a11y/tabindex-no-positive": 1
|
||||
}
|
||||
}
|
88
.eslintrc.yml
Normal file
88
.eslintrc.yml
Normal file
@@ -0,0 +1,88 @@
|
||||
---
|
||||
env:
|
||||
browser: true
|
||||
node: false
|
||||
es6: true
|
||||
|
||||
parser: babel-eslint
|
||||
|
||||
plugins:
|
||||
- react
|
||||
- jsx-a11y
|
||||
|
||||
parserOptions:
|
||||
sourceType: module
|
||||
ecmaFeatures:
|
||||
arrowFunctions: true
|
||||
jsx: true
|
||||
destructuring: true
|
||||
modules: true
|
||||
spread: true
|
||||
|
||||
rules:
|
||||
|
||||
no-cond-assign: error
|
||||
no-console: warn
|
||||
no-irregular-whitespace: error
|
||||
no-unreachable: error
|
||||
valid-typeof: error
|
||||
consistent-return: error
|
||||
dot-notation: error
|
||||
eqeqeq: error
|
||||
no-fallthrough: error
|
||||
no-unused-expressions: error
|
||||
strict: off
|
||||
no-catch-shadow: error
|
||||
indent:
|
||||
- warn
|
||||
- 2
|
||||
brace-style: warn
|
||||
comma-spacing:
|
||||
- warn
|
||||
- before: false
|
||||
after: true
|
||||
comma-style:
|
||||
- warn
|
||||
- last
|
||||
no-mixed-spaces-and-tabs: warn
|
||||
no-nested-ternary: warn
|
||||
no-trailing-spaces: warn
|
||||
semi: error
|
||||
padded-blocks:
|
||||
- error
|
||||
- classes: always
|
||||
comma-dangle:
|
||||
- error
|
||||
- always-multiline
|
||||
|
||||
react/jsx-wrap-multilines: error
|
||||
react/jsx-no-bind: error
|
||||
react/self-closing-comp: error
|
||||
react/prop-types: error
|
||||
react/no-multi-comp: off
|
||||
|
||||
jsx-a11y/accessible-emoji: warn
|
||||
jsx-a11y/anchor-has-content: warn
|
||||
jsx-a11y/aria-activedescendant-has-tabindex: warn
|
||||
jsx-a11y/aria-props: warn
|
||||
jsx-a11y/aria-proptypes: warn
|
||||
jsx-a11y/aria-role: warn
|
||||
jsx-a11y/aria-unsupported-elements: warn
|
||||
jsx-a11y/heading-has-content: warn
|
||||
jsx-a11y/href-no-hash: warn
|
||||
jsx-a11y/html-has-lang: warn
|
||||
jsx-a11y/iframe-has-title: warn
|
||||
jsx-a11y/img-has-alt: warn
|
||||
jsx-a11y/img-redundant-alt: warn
|
||||
jsx-a11y/label-has-for: warn
|
||||
jsx-a11y/mouse-events-have-key-events: warn
|
||||
jsx-a11y/no-access-key: warn
|
||||
jsx-a11y/no-distracting-elements: warn
|
||||
jsx-a11y/no-onchange: warn
|
||||
jsx-a11y/no-redundant-roles: warn
|
||||
jsx-a11y/onclick-has-focus: warn
|
||||
jsx-a11y/onclick-has-role: warn
|
||||
jsx-a11y/role-has-required-aria-props: warn
|
||||
jsx-a11y/role-supports-aria-props: warn
|
||||
jsx-a11y/scope: warn
|
||||
jsx-a11y/tabindex-no-positive: warn
|
16
.gitignore
vendored
16
.gitignore
vendored
@@ -22,7 +22,7 @@ public/assets
|
||||
.env
|
||||
.env.production
|
||||
node_modules/
|
||||
neo4j/
|
||||
build/
|
||||
|
||||
# Ignore Vagrant files
|
||||
.vagrant/
|
||||
@@ -43,3 +43,17 @@ redis
|
||||
# Ignore vim files
|
||||
*~
|
||||
*.swp
|
||||
/public/packs
|
||||
/node_modules
|
||||
|
||||
|
||||
# Ignore npm debug log
|
||||
npm-debug.log
|
||||
|
||||
# Ignore yarn log files
|
||||
yarn-error.log
|
||||
yarn-debug.log
|
||||
|
||||
# Ignore Docker option files
|
||||
docker-compose.override.yml
|
||||
|
||||
|
108
.haml-lint.yml
Normal file
108
.haml-lint.yml
Normal file
@@ -0,0 +1,108 @@
|
||||
# Whether to ignore frontmatter at the beginning of HAML documents for
|
||||
# frameworks such as Jekyll/Middleman
|
||||
skip_frontmatter: false
|
||||
|
||||
exclude:
|
||||
- 'vendor/**/*'
|
||||
- 'spec/**/*'
|
||||
- 'lib/templates/**/*'
|
||||
- 'app/views/kaminari/**/*'
|
||||
|
||||
linters:
|
||||
AltText:
|
||||
enabled: false
|
||||
|
||||
ClassAttributeWithStaticValue:
|
||||
enabled: true
|
||||
|
||||
ClassesBeforeIds:
|
||||
enabled: true
|
||||
|
||||
ConsecutiveComments:
|
||||
enabled: true
|
||||
|
||||
ConsecutiveSilentScripts:
|
||||
enabled: true
|
||||
max_consecutive: 2
|
||||
|
||||
EmptyObjectReference:
|
||||
enabled: true
|
||||
|
||||
EmptyScript:
|
||||
enabled: true
|
||||
|
||||
FinalNewline:
|
||||
enabled: true
|
||||
present: true
|
||||
|
||||
HtmlAttributes:
|
||||
enabled: true
|
||||
|
||||
ImplicitDiv:
|
||||
enabled: true
|
||||
|
||||
LeadingCommentSpace:
|
||||
enabled: true
|
||||
|
||||
LineLength:
|
||||
enabled: false
|
||||
max: 80
|
||||
|
||||
MultilinePipe:
|
||||
enabled: true
|
||||
|
||||
MultilineScript:
|
||||
enabled: true
|
||||
|
||||
ObjectReferenceAttributes:
|
||||
enabled: true
|
||||
|
||||
RuboCop:
|
||||
enabled: true
|
||||
# These cops are incredibly noisy when it comes to HAML templates, so we
|
||||
# ignore them.
|
||||
ignored_cops:
|
||||
- Lint/BlockAlignment
|
||||
- Lint/EndAlignment
|
||||
- Lint/Void
|
||||
- Metrics/BlockLength
|
||||
- Metrics/LineLength
|
||||
- Style/AlignParameters
|
||||
- Style/BlockNesting
|
||||
- Style/ElseAlignment
|
||||
- Style/EndOfLine
|
||||
- Style/FileName
|
||||
- Style/FinalNewline
|
||||
- Style/FrozenStringLiteralComment
|
||||
- Style/IfUnlessModifier
|
||||
- Style/IndentationWidth
|
||||
- Style/Next
|
||||
- Style/TrailingBlankLines
|
||||
- Style/TrailingWhitespace
|
||||
- Style/WhileUntilModifier
|
||||
|
||||
RubyComments:
|
||||
enabled: true
|
||||
|
||||
SpaceBeforeScript:
|
||||
enabled: true
|
||||
|
||||
SpaceInsideHashAttributes:
|
||||
enabled: true
|
||||
style: space
|
||||
|
||||
Indentation:
|
||||
enabled: true
|
||||
character: space # or tab
|
||||
|
||||
TagName:
|
||||
enabled: true
|
||||
|
||||
TrailingWhitespace:
|
||||
enabled: true
|
||||
|
||||
UnnecessaryInterpolation:
|
||||
enabled: true
|
||||
|
||||
UnnecessaryStringOutput:
|
||||
enabled: true
|
8
.postcssrc.yml
Normal file
8
.postcssrc.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
plugins:
|
||||
postcss-smart-import: {}
|
||||
precss: {}
|
||||
autoprefixer:
|
||||
browsers:
|
||||
- last 2 versions
|
||||
- IE >= 11
|
||||
- iOS >= 9
|
@@ -77,6 +77,9 @@ Style/Lambda:
|
||||
Rails/HasAndBelongsToMany:
|
||||
Enabled: false
|
||||
|
||||
Bundler/OrderedGems:
|
||||
Enabled: false
|
||||
|
||||
AllCops:
|
||||
TargetRubyVersion: 2.3
|
||||
Exclude:
|
||||
@@ -88,3 +91,4 @@ AllCops:
|
||||
- 'Rakefile'
|
||||
- 'node_modules/**/*'
|
||||
- 'Vagrantfile'
|
||||
- 'vendor/**/*'
|
||||
|
264
.scss-lint.yml
Normal file
264
.scss-lint.yml
Normal file
@@ -0,0 +1,264 @@
|
||||
# Linter Documentation:
|
||||
# https://github.com/brigade/scss-lint/blob/v0.42.2/lib/scss_lint/linter/README.md
|
||||
|
||||
scss_files: 'app/javascript/styles/**/*.scss'
|
||||
|
||||
exclude:
|
||||
- app/javascript/styles/reset.scss
|
||||
|
||||
linters:
|
||||
# Reports when you use improper spacing around ! (the "bang") in !default,
|
||||
# !global, !important, and !optional flags.
|
||||
BangFormat:
|
||||
enabled: false
|
||||
|
||||
# Whether or not to prefer `border: 0` over `border: none`.
|
||||
BorderZero:
|
||||
enabled: false
|
||||
|
||||
# Reports when you define a rule set using a selector with chained classes
|
||||
# (a.k.a. adjoining classes).
|
||||
ChainedClasses:
|
||||
enabled: false
|
||||
|
||||
# Prefer hexadecimal color codes over color keywords.
|
||||
# (e.g. `color: green` is a color keyword)
|
||||
ColorKeyword:
|
||||
enabled: false
|
||||
|
||||
# Prefer color literals (keywords or hexadecimal codes) to be used only in
|
||||
# variable declarations. They should be referred to via variables everywhere
|
||||
# else.
|
||||
ColorVariable:
|
||||
enabled: true
|
||||
|
||||
# Which form of comments to prefer in CSS.
|
||||
Comment:
|
||||
enabled: false
|
||||
|
||||
# Reports @debug statements (which you probably left behind accidentally).
|
||||
DebugStatement:
|
||||
enabled: false
|
||||
|
||||
# Rule sets should be ordered as follows:
|
||||
# - @extend declarations
|
||||
# - @include declarations without inner @content
|
||||
# - properties, @include declarations with inner @content
|
||||
# - nested rule sets.
|
||||
DeclarationOrder:
|
||||
enabled: false
|
||||
|
||||
# `scss-lint:disable` control comments should be preceded by a comment
|
||||
# explaining why these linters are being disabled for this file.
|
||||
# See https://github.com/brigade/scss-lint#disabling-linters-via-source for
|
||||
# more information.
|
||||
DisableLinterReason:
|
||||
enabled: true
|
||||
|
||||
# Reports when you define the same property twice in a single rule set.
|
||||
DuplicateProperty:
|
||||
enabled: false
|
||||
|
||||
# Separate rule, function, and mixin declarations with empty lines.
|
||||
EmptyLineBetweenBlocks:
|
||||
enabled: true
|
||||
|
||||
# Reports when you have an empty rule set.
|
||||
EmptyRule:
|
||||
enabled: true
|
||||
|
||||
# Reports when you have an @extend directive.
|
||||
ExtendDirective:
|
||||
enabled: false
|
||||
|
||||
# Files should always have a final newline. This results in better diffs
|
||||
# when adding lines to the file, since SCM systems such as git won't
|
||||
# think that you touched the last line.
|
||||
FinalNewline:
|
||||
enabled: false
|
||||
|
||||
# HEX colors should use three-character values where possible.
|
||||
HexLength:
|
||||
enabled: false
|
||||
|
||||
# HEX color values should use lower-case colors to differentiate between
|
||||
# letters and numbers, e.g. `#E3E3E3` vs. `#e3e3e3`.
|
||||
HexNotation:
|
||||
enabled: true
|
||||
|
||||
# Avoid using ID selectors.
|
||||
IdSelector:
|
||||
enabled: false
|
||||
|
||||
# The basenames of @imported SCSS partials should not begin with an
|
||||
# underscore and should not include the filename extension.
|
||||
ImportPath:
|
||||
enabled: false
|
||||
|
||||
# Avoid using !important in properties. It is usually indicative of a
|
||||
# misunderstanding of CSS specificity and can lead to brittle code.
|
||||
ImportantRule:
|
||||
enabled: false
|
||||
|
||||
# Indentation should always be done in increments of 2 spaces.
|
||||
Indentation:
|
||||
enabled: true
|
||||
width: 2
|
||||
|
||||
# Don't write leading zeros for numeric values with a decimal point.
|
||||
LeadingZero:
|
||||
enabled: false
|
||||
|
||||
# Reports when you define the same selector twice in a single sheet.
|
||||
MergeableSelector:
|
||||
enabled: false
|
||||
|
||||
# Functions, mixins, variables, and placeholders should be declared
|
||||
# with all lowercase letters and hyphens instead of underscores.
|
||||
NameFormat:
|
||||
enabled: false
|
||||
|
||||
# Avoid nesting selectors too deeply.
|
||||
NestingDepth:
|
||||
enabled: false
|
||||
|
||||
# Always use placeholder selectors in @extend.
|
||||
PlaceholderInExtend:
|
||||
enabled: false
|
||||
|
||||
# Sort properties in a strict order.
|
||||
PropertySortOrder:
|
||||
enabled: false
|
||||
|
||||
# Reports when you use an unknown or disabled CSS property
|
||||
# (ignoring vendor-prefixed properties).
|
||||
PropertySpelling:
|
||||
enabled: false
|
||||
|
||||
# Configure which units are allowed for property values.
|
||||
PropertyUnits:
|
||||
enabled: false
|
||||
|
||||
# Pseudo-elements, like ::before, and ::first-letter, should be declared
|
||||
# with two colons. Pseudo-classes, like :hover and :first-child, should
|
||||
# be declared with one colon.
|
||||
PseudoElement:
|
||||
enabled: true
|
||||
|
||||
# Avoid qualifying elements in selectors (also known as "tag-qualifying").
|
||||
QualifyingElement:
|
||||
enabled: false
|
||||
|
||||
# Don't write selectors with a depth of applicability greater than 3.
|
||||
SelectorDepth:
|
||||
enabled: false
|
||||
|
||||
# Selectors should always use hyphenated-lowercase, rather than camelCase or
|
||||
# snake_case.
|
||||
SelectorFormat:
|
||||
enabled: false
|
||||
convention: hyphenated_lowercase
|
||||
|
||||
# Prefer the shortest shorthand form possible for properties that support it.
|
||||
Shorthand:
|
||||
enabled: true
|
||||
|
||||
# Each property should have its own line, except in the special case of
|
||||
# single line rulesets.
|
||||
SingleLinePerProperty:
|
||||
enabled: true
|
||||
allow_single_line_rule_sets: true
|
||||
|
||||
# Split selectors onto separate lines after each comma, and have each
|
||||
# individual selector occupy a single line.
|
||||
SingleLinePerSelector:
|
||||
enabled: true
|
||||
|
||||
# Commas in lists should be followed by a space.
|
||||
SpaceAfterComma:
|
||||
enabled: false
|
||||
|
||||
# Properties should be formatted with a single space separating the colon
|
||||
# from the property's value.
|
||||
SpaceAfterPropertyColon:
|
||||
enabled: true
|
||||
|
||||
# Properties should be formatted with no space between the name and the
|
||||
# colon.
|
||||
SpaceAfterPropertyName:
|
||||
enabled: true
|
||||
|
||||
# Variables should be formatted with a single space separating the colon
|
||||
# from the variable's value.
|
||||
SpaceAfterVariableColon:
|
||||
enabled: true
|
||||
|
||||
# Variables should be formatted with no space between the name and the
|
||||
# colon.
|
||||
SpaceAfterVariableName:
|
||||
enabled: false
|
||||
|
||||
# Operators should be formatted with a single space on both sides of an
|
||||
# infix operator.
|
||||
SpaceAroundOperator:
|
||||
enabled: true
|
||||
|
||||
# Opening braces should be preceded by a single space.
|
||||
SpaceBeforeBrace:
|
||||
enabled: true
|
||||
|
||||
# Parentheses should not be padded with spaces.
|
||||
SpaceBetweenParens:
|
||||
enabled: false
|
||||
|
||||
# Enforces that string literals should be written with a consistent form
|
||||
# of quotes (single or double).
|
||||
StringQuotes:
|
||||
enabled: false
|
||||
|
||||
# Property values, @extend, @include, and @import directives, and variable
|
||||
# declarations should always end with a semicolon.
|
||||
TrailingSemicolon:
|
||||
enabled: true
|
||||
|
||||
# Reports lines containing trailing whitespace.
|
||||
TrailingWhitespace:
|
||||
enabled: true
|
||||
|
||||
# Don't write trailing zeros for numeric values with a decimal point.
|
||||
TrailingZero:
|
||||
enabled: false
|
||||
|
||||
# Don't use the `all` keyword to specify transition properties.
|
||||
TransitionAll:
|
||||
enabled: false
|
||||
|
||||
# Numeric values should not contain unnecessary fractional portions.
|
||||
UnnecessaryMantissa:
|
||||
enabled: false
|
||||
|
||||
# Do not use parent selector references (&) when they would otherwise
|
||||
# be unnecessary.
|
||||
UnnecessaryParentReference:
|
||||
enabled: false
|
||||
|
||||
# URLs should be valid and not contain protocols or domain names.
|
||||
UrlFormat:
|
||||
enabled: true
|
||||
|
||||
# URLs should always be enclosed within quotes.
|
||||
UrlQuotes:
|
||||
enabled: true
|
||||
|
||||
# Properties, like color and font, are easier to read and maintain
|
||||
# when defined using variables rather than literals.
|
||||
VariableForProperty:
|
||||
enabled: false
|
||||
|
||||
# Avoid vendor prefixes. Or rather: don't write them yourself.
|
||||
VendorPrefix:
|
||||
enabled: false
|
||||
|
||||
# Omit length units on zero values, e.g. `0px` vs. `0`.
|
||||
ZeroUnit:
|
||||
enabled: true
|
23
.travis.yml
23
.travis.yml
@@ -3,7 +3,7 @@ cache:
|
||||
bundler: true
|
||||
yarn: true
|
||||
directories:
|
||||
- node_modules
|
||||
- node_modules
|
||||
dist: trusty
|
||||
sudo: false
|
||||
|
||||
@@ -15,7 +15,10 @@ env:
|
||||
- LOCAL_DOMAIN=cb6e6126.ngrok.io
|
||||
- LOCAL_HTTPS=true
|
||||
- RAILS_ENV=test
|
||||
- CXX=g++-4.8
|
||||
- NOKOGIRI_USE_SYSTEM_LIBRARIES=true
|
||||
- PARALLEL_TEST_PROCESSORS=2
|
||||
- "PATH=$HOME:$PATH"
|
||||
|
||||
addons:
|
||||
postgresql: 9.4
|
||||
apt:
|
||||
@@ -23,8 +26,10 @@ addons:
|
||||
- ubuntu-toolchain-r-test
|
||||
- trusty-media
|
||||
packages:
|
||||
- g++-4.8
|
||||
- ffmpeg
|
||||
- g++-6
|
||||
- libprotobuf-dev
|
||||
- protobuf-compiler
|
||||
|
||||
rvm:
|
||||
- 2.3.4
|
||||
@@ -33,18 +38,18 @@ rvm:
|
||||
services:
|
||||
- redis-server
|
||||
|
||||
bundler_args: --without development production --retry=3 --jobs=3
|
||||
|
||||
install:
|
||||
- nvm install
|
||||
- npm install -g yarn
|
||||
- bundle install
|
||||
- bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16
|
||||
- yarn install
|
||||
|
||||
before_script:
|
||||
- bundle exec rails db:create db:migrate
|
||||
- bundle exec rake parallel:create parallel:load_schema parallel:prepare
|
||||
- bundle exec rails assets:precompile
|
||||
- ln -s /usr/bin/x86_64-linux-gnu-g++-6 "$HOME/g++"
|
||||
|
||||
script:
|
||||
- bundle exec rspec
|
||||
- bundle exec parallel_test spec/ --group-by filesize --type rspec
|
||||
- npm test
|
||||
- i18n-tasks unused
|
||||
- bundle exec i18n-tasks unused
|
||||
|
2
Capfile
2
Capfile
@@ -1,3 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
require 'capistrano/setup'
|
||||
require 'capistrano/deploy'
|
||||
require 'capistrano/scm/git'
|
||||
@@ -8,7 +9,6 @@ require 'capistrano/rbenv'
|
||||
require 'capistrano/bundler'
|
||||
require 'capistrano/yarn'
|
||||
require 'capistrano/rails/assets'
|
||||
require 'capistrano/faster_assets'
|
||||
require 'capistrano/rails/migrations'
|
||||
|
||||
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
|
||||
|
52
Dockerfile
52
Dockerfile
@@ -3,42 +3,52 @@ FROM ruby:2.4.1-alpine
|
||||
LABEL maintainer="https://github.com/tootsuite/mastodon" \
|
||||
description="A GNU Social-compatible microblogging server"
|
||||
|
||||
ENV RAILS_ENV=production \
|
||||
NODE_ENV=production
|
||||
ENV UID=991 GID=991 \
|
||||
RAILS_SERVE_STATIC_FILES=true \
|
||||
RAILS_ENV=production NODE_ENV=production
|
||||
|
||||
EXPOSE 3000 4000
|
||||
|
||||
WORKDIR /mastodon
|
||||
|
||||
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
|
||||
|
||||
RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
|
||||
&& BUILD_DEPS=" \
|
||||
postgresql-dev \
|
||||
&& apk -U upgrade \
|
||||
&& apk add -t build-dependencies \
|
||||
build-base \
|
||||
libxml2-dev \
|
||||
libxslt-dev \
|
||||
postgresql-dev \
|
||||
protobuf-dev \
|
||||
python \
|
||||
build-base" \
|
||||
&& apk -U upgrade && apk add \
|
||||
$BUILD_DEPS \
|
||||
nodejs@edge \
|
||||
nodejs-npm@edge \
|
||||
&& apk add \
|
||||
ca-certificates \
|
||||
ffmpeg \
|
||||
file \
|
||||
git \
|
||||
imagemagick@edge \
|
||||
libpq \
|
||||
libxml2 \
|
||||
libxslt \
|
||||
ffmpeg \
|
||||
file \
|
||||
imagemagick@edge \
|
||||
ca-certificates \
|
||||
nodejs-npm@edge \
|
||||
nodejs@edge \
|
||||
protobuf \
|
||||
su-exec \
|
||||
tini \
|
||||
&& npm install -g npm@3 && npm install -g yarn \
|
||||
&& bundle install --deployment --without test development \
|
||||
&& yarn --ignore-optional \
|
||||
&& yarn cache clean \
|
||||
&& npm -g cache clean \
|
||||
&& update-ca-certificates \
|
||||
&& apk del $BUILD_DEPS \
|
||||
&& rm -rf /tmp/* /var/cache/apk/*
|
||||
|
||||
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
|
||||
|
||||
RUN bundle install --deployment --without test development \
|
||||
&& yarn --ignore-optional --pure-lockfile
|
||||
|
||||
COPY . /mastodon
|
||||
|
||||
VOLUME /mastodon/public/system /mastodon/public/assets
|
||||
COPY docker_entrypoint.sh /usr/local/bin/run
|
||||
|
||||
RUN chmod +x /usr/local/bin/run
|
||||
|
||||
VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/run"]
|
||||
|
162
Gemfile
162
Gemfile
@@ -3,103 +3,101 @@
|
||||
source 'https://rubygems.org'
|
||||
ruby '>= 2.3.0', '< 2.5.0'
|
||||
|
||||
gem 'pkg-config'
|
||||
gem 'pkg-config', '~> 1.2'
|
||||
|
||||
gem 'rails', '~> 5.0.2'
|
||||
gem 'sass-rails', '~> 5.0'
|
||||
gem 'uglifier', '>= 1.3.0'
|
||||
gem 'jquery-rails'
|
||||
gem 'puma'
|
||||
gem 'puma', '~> 3.8'
|
||||
gem 'rails', '~> 5.0'
|
||||
gem 'uglifier', '~> 3.2'
|
||||
|
||||
gem 'hamlit-rails'
|
||||
gem 'pg'
|
||||
gem 'pghero'
|
||||
gem 'dotenv-rails'
|
||||
gem 'font-awesome-rails'
|
||||
gem 'best_in_place', '~> 3.0.1'
|
||||
gem 'hamlit-rails', '~> 0.2'
|
||||
gem 'pg', '~> 0.20'
|
||||
gem 'pghero', '~> 1.7'
|
||||
gem 'dotenv-rails', '~> 2.2'
|
||||
|
||||
gem 'aws-sdk', '~> 2.9'
|
||||
gem 'paperclip', '~> 5.1'
|
||||
gem 'paperclip-av-transcoder'
|
||||
gem 'aws-sdk', '>= 2.0'
|
||||
gem 'paperclip-av-transcoder', '~> 0.6'
|
||||
|
||||
gem 'addressable'
|
||||
gem 'devise'
|
||||
gem 'devise-two-factor'
|
||||
gem 'doorkeeper'
|
||||
gem 'fast_blank'
|
||||
gem 'goldfinger'
|
||||
gem 'hiredis'
|
||||
gem 'htmlentities'
|
||||
gem 'http'
|
||||
gem 'http_accept_language'
|
||||
gem 'httplog'
|
||||
gem 'kaminari'
|
||||
gem 'link_header'
|
||||
gem 'local_time'
|
||||
gem 'nokogiri'
|
||||
gem 'oj'
|
||||
gem 'ostatus2', '~> 1.1'
|
||||
gem 'ox'
|
||||
gem 'rabl'
|
||||
gem 'rack-attack'
|
||||
gem 'rack-cors', require: 'rack/cors'
|
||||
gem 'rack-timeout'
|
||||
gem 'rails-i18n'
|
||||
gem 'rails-settings-cached'
|
||||
gem 'redis', '~>3.2', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'rqrcode'
|
||||
gem 'ruby-oembed', require: 'oembed'
|
||||
gem 'sanitize'
|
||||
gem 'sidekiq'
|
||||
gem 'sidekiq-unique-jobs'
|
||||
gem 'simple-navigation'
|
||||
gem 'simple_form'
|
||||
gem 'sprockets-rails', require: 'sprockets/railtie'
|
||||
gem 'statsd-instrument'
|
||||
gem 'twitter-text'
|
||||
gem 'tzinfo-data'
|
||||
gem 'whatlanguage'
|
||||
|
||||
gem 'react-rails'
|
||||
gem 'browserify-rails'
|
||||
gem 'autoprefixer-rails'
|
||||
gem 'addressable', '~> 2.5'
|
||||
gem 'bootsnap'
|
||||
gem 'cld3', '~> 3.1'
|
||||
gem 'devise', '~> 4.2'
|
||||
gem 'devise-two-factor', '~> 3.0'
|
||||
gem 'doorkeeper', '~> 4.2'
|
||||
gem 'fast_blank', '~> 1.0'
|
||||
gem 'goldfinger', '~> 1.2'
|
||||
gem 'hiredis', '~> 0.6'
|
||||
gem 'redis-namespace', '~> 1.5'
|
||||
gem 'htmlentities', '~> 4.3'
|
||||
gem 'http', '~> 2.2'
|
||||
gem 'http_accept_language', '~> 2.1'
|
||||
gem 'httplog', '~> 0.99'
|
||||
gem 'kaminari', '~> 1.0'
|
||||
gem 'link_header', '~> 0.0'
|
||||
gem 'nokogiri', '~> 1.7'
|
||||
gem 'oj', '~> 3.0'
|
||||
gem 'ostatus2', '~> 2.0'
|
||||
gem 'ox', '~> 2.5'
|
||||
gem 'rabl', '~> 0.13'
|
||||
gem 'rack-attack', '~> 5.0'
|
||||
gem 'rack-cors', '~> 0.4', require: 'rack/cors'
|
||||
gem 'rack-timeout', '~> 0.4'
|
||||
gem 'rails-i18n', '~> 5.0'
|
||||
gem 'rails-settings-cached', '~> 0.6'
|
||||
gem 'redis', '~> 3.3', require: ['redis', 'redis/connection/hiredis']
|
||||
gem 'rqrcode', '~> 0.10'
|
||||
gem 'ruby-oembed', '~> 0.12', require: 'oembed'
|
||||
gem 'sanitize', '~> 4.4'
|
||||
gem 'sidekiq', '~> 5.0'
|
||||
gem 'sidekiq-scheduler', '~> 2.1'
|
||||
gem 'sidekiq-unique-jobs', '~> 5.0'
|
||||
gem 'simple-navigation', '~> 4.0'
|
||||
gem 'simple_form', '~> 3.4'
|
||||
gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie'
|
||||
gem 'statsd-instrument', '~> 2.1'
|
||||
gem 'twitter-text', '~> 1.14'
|
||||
gem 'tzinfo-data', '~> 1.2017'
|
||||
gem 'webpacker', '~> 1.2'
|
||||
|
||||
group :development, :test do
|
||||
gem 'rspec-rails'
|
||||
gem 'pry-rails'
|
||||
gem 'fuubar'
|
||||
gem 'fabrication'
|
||||
gem 'i18n-tasks', '~> 0.9.6'
|
||||
gem 'fabrication', '~> 2.16'
|
||||
gem 'fuubar', '~> 2.2'
|
||||
gem 'i18n-tasks', '~> 0.9', require: false
|
||||
gem 'pry-rails', '~> 0.3'
|
||||
gem 'rspec-rails', '~> 3.6'
|
||||
end
|
||||
|
||||
group :test do
|
||||
gem 'capybara'
|
||||
gem 'faker'
|
||||
gem 'microformats2'
|
||||
gem 'rails-controller-testing'
|
||||
gem 'rspec-sidekiq'
|
||||
gem 'simplecov', require: false
|
||||
gem 'webmock'
|
||||
gem 'capybara', '~> 2.14'
|
||||
gem 'faker', '~> 1.7'
|
||||
gem 'microformats2', '~> 3.0'
|
||||
gem 'rails-controller-testing', '~> 1.0'
|
||||
gem 'rspec-sidekiq', '~> 3.0'
|
||||
gem 'simplecov', '~> 0.14', require: false
|
||||
gem 'webmock', '~> 3.0'
|
||||
gem 'parallel_tests', '~> 2.14'
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem 'rubocop', require: false
|
||||
gem 'better_errors'
|
||||
gem 'binding_of_caller'
|
||||
gem 'letter_opener'
|
||||
gem 'letter_opener_web'
|
||||
gem 'bullet'
|
||||
gem 'active_record_query_trace'
|
||||
gem 'active_record_query_trace', '~> 1.5'
|
||||
gem 'annotate', '~> 2.7'
|
||||
gem 'better_errors', '~> 2.1'
|
||||
gem 'binding_of_caller', '~> 0.7'
|
||||
gem 'bullet', '~> 5.5'
|
||||
gem 'letter_opener', '~> 1.4'
|
||||
gem 'letter_opener_web', '~> 1.3'
|
||||
gem 'rubocop', '~> 0.48', require: false
|
||||
gem 'brakeman', '~> 3.6', require: false
|
||||
gem 'bundler-audit', '~> 0.5', require: false
|
||||
gem 'scss_lint', '~> 0.53', require: false
|
||||
|
||||
gem 'capistrano', '3.8.0'
|
||||
gem 'capistrano-rails'
|
||||
gem 'capistrano-rbenv'
|
||||
gem 'capistrano-yarn'
|
||||
gem 'capistrano-faster-assets', '~> 1.0'
|
||||
gem 'capistrano', '~> 3.8'
|
||||
gem 'capistrano-rails', '~> 1.2'
|
||||
gem 'capistrano-rbenv', '~> 2.1'
|
||||
gem 'capistrano-yarn', '~> 2.0'
|
||||
end
|
||||
|
||||
group :production do
|
||||
gem 'rails_12factor'
|
||||
gem 'redis-rails'
|
||||
gem 'lograge'
|
||||
gem 'lograge', '~> 0.5'
|
||||
gem 'redis-rails', '~> 5.0'
|
||||
end
|
||||
|
446
Gemfile.lock
446
Gemfile.lock
@@ -1,40 +1,40 @@
|
||||
GEM
|
||||
remote: https://rubygems.org/
|
||||
specs:
|
||||
actioncable (5.0.2)
|
||||
actionpack (= 5.0.2)
|
||||
actioncable (5.0.3)
|
||||
actionpack (= 5.0.3)
|
||||
nio4r (>= 1.2, < 3.0)
|
||||
websocket-driver (~> 0.6.1)
|
||||
actionmailer (5.0.2)
|
||||
actionpack (= 5.0.2)
|
||||
actionview (= 5.0.2)
|
||||
activejob (= 5.0.2)
|
||||
actionmailer (5.0.3)
|
||||
actionpack (= 5.0.3)
|
||||
actionview (= 5.0.3)
|
||||
activejob (= 5.0.3)
|
||||
mail (~> 2.5, >= 2.5.4)
|
||||
rails-dom-testing (~> 2.0)
|
||||
actionpack (5.0.2)
|
||||
actionview (= 5.0.2)
|
||||
activesupport (= 5.0.2)
|
||||
actionpack (5.0.3)
|
||||
actionview (= 5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
rack (~> 2.0)
|
||||
rack-test (~> 0.6.3)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||
actionview (5.0.2)
|
||||
activesupport (= 5.0.2)
|
||||
actionview (5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
builder (~> 3.1)
|
||||
erubis (~> 2.7.0)
|
||||
rails-dom-testing (~> 2.0)
|
||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||
active_record_query_trace (1.5.4)
|
||||
activejob (5.0.2)
|
||||
activesupport (= 5.0.2)
|
||||
activejob (5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
globalid (>= 0.3.6)
|
||||
activemodel (5.0.2)
|
||||
activesupport (= 5.0.2)
|
||||
activerecord (5.0.2)
|
||||
activemodel (= 5.0.2)
|
||||
activesupport (= 5.0.2)
|
||||
activemodel (5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
activerecord (5.0.3)
|
||||
activemodel (= 5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
arel (~> 7.0)
|
||||
activesupport (5.0.2)
|
||||
activesupport (5.0.3)
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
i18n (~> 0.7)
|
||||
minitest (~> 5.1)
|
||||
@@ -43,45 +43,41 @@ GEM
|
||||
public_suffix (~> 2.0, >= 2.0.2)
|
||||
airbrussh (1.2.0)
|
||||
sshkit (>= 1.6.1, != 1.7.0)
|
||||
annotate (2.7.1)
|
||||
activerecord (>= 3.2, < 6.0)
|
||||
rake (>= 10.4, < 12.0)
|
||||
arel (7.1.4)
|
||||
ast (2.3.0)
|
||||
attr_encrypted (3.0.3)
|
||||
encryptor (~> 3.0.0)
|
||||
autoprefixer-rails (6.7.7.2)
|
||||
execjs
|
||||
av (0.9.0)
|
||||
cocaine (~> 0.5.3)
|
||||
aws-sdk (2.9.12)
|
||||
aws-sdk-resources (= 2.9.12)
|
||||
aws-sdk-core (2.9.12)
|
||||
aws-sdk (2.9.21)
|
||||
aws-sdk-resources (= 2.9.21)
|
||||
aws-sdk-core (2.9.21)
|
||||
aws-sigv4 (~> 1.0)
|
||||
jmespath (~> 1.0)
|
||||
aws-sdk-resources (2.9.12)
|
||||
aws-sdk-core (= 2.9.12)
|
||||
aws-sdk-resources (2.9.21)
|
||||
aws-sdk-core (= 2.9.21)
|
||||
aws-sigv4 (1.0.0)
|
||||
babel-source (5.8.35)
|
||||
babel-transpiler (0.7.0)
|
||||
babel-source (>= 4.0, < 6)
|
||||
execjs (~> 2.0)
|
||||
bcrypt (3.1.11)
|
||||
best_in_place (3.0.3)
|
||||
actionpack (>= 3.2)
|
||||
railties (>= 3.2)
|
||||
better_errors (2.1.1)
|
||||
coderay (>= 1.0.0)
|
||||
erubis (>= 2.6.6)
|
||||
rack (>= 0.9.0)
|
||||
binding_of_caller (0.7.2)
|
||||
debug_inspector (>= 0.0.1)
|
||||
browserify-rails (4.1.0)
|
||||
addressable (>= 2.4.0)
|
||||
railties (>= 4.0.0, < 5.1)
|
||||
sprockets (>= 3.6.0)
|
||||
bootsnap (0.2.14)
|
||||
msgpack (~> 1.0)
|
||||
brakeman (3.6.1)
|
||||
builder (3.2.3)
|
||||
bullet (5.5.1)
|
||||
activesupport (>= 3.0.0)
|
||||
uniform_notifier (~> 1.10.0)
|
||||
capistrano (3.8.0)
|
||||
bundler-audit (0.5.0)
|
||||
bundler (~> 1.2)
|
||||
thor (~> 0.18)
|
||||
capistrano (3.8.1)
|
||||
airbrussh (>= 1.0.0)
|
||||
i18n
|
||||
rake (>= 10.0.0)
|
||||
@@ -89,8 +85,6 @@ GEM
|
||||
capistrano-bundler (1.2.0)
|
||||
capistrano (~> 3.1)
|
||||
sshkit (~> 1.2)
|
||||
capistrano-faster-assets (1.0.2)
|
||||
capistrano (>= 3.1)
|
||||
capistrano-rails (1.2.3)
|
||||
capistrano (~> 3.1)
|
||||
capistrano-bundler (~> 1.1)
|
||||
@@ -99,7 +93,7 @@ GEM
|
||||
sshkit (~> 1.3)
|
||||
capistrano-yarn (2.0.2)
|
||||
capistrano (~> 3.0)
|
||||
capybara (2.13.0)
|
||||
capybara (2.14.0)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
@@ -107,28 +101,23 @@ GEM
|
||||
rack-test (>= 0.5.4)
|
||||
xpath (~> 2.0)
|
||||
chunky_png (1.3.8)
|
||||
climate_control (0.1.0)
|
||||
cld3 (3.1.2)
|
||||
ffi (>= 1.1.0, < 1.10.0)
|
||||
climate_control (0.2.0)
|
||||
cocaine (0.5.8)
|
||||
climate_control (>= 0.0.3, < 1.0)
|
||||
coderay (1.1.1)
|
||||
coffee-rails (4.2.1)
|
||||
coffee-script (>= 2.2.0)
|
||||
railties (>= 4.0.0, < 5.2.x)
|
||||
coffee-script (2.4.1)
|
||||
coffee-script-source
|
||||
execjs
|
||||
coffee-script-source (1.12.2)
|
||||
colorize (0.8.1)
|
||||
concurrent-ruby (1.0.5)
|
||||
connection_pool (2.2.1)
|
||||
crack (0.4.3)
|
||||
safe_yaml (~> 1.0.0)
|
||||
crass (1.0.2)
|
||||
debug_inspector (0.0.2)
|
||||
devise (4.2.1)
|
||||
debug_inspector (0.0.3)
|
||||
devise (4.3.0)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
railties (>= 4.1.0, < 5.1)
|
||||
railties (>= 4.1.0, < 5.2)
|
||||
responders
|
||||
warden (~> 1.2.3)
|
||||
devise-two-factor (3.0.0)
|
||||
@@ -143,23 +132,24 @@ GEM
|
||||
unf (>= 0.0.5, < 1.0.0)
|
||||
doorkeeper (4.2.5)
|
||||
railties (>= 4.2)
|
||||
dotenv (2.2.0)
|
||||
dotenv-rails (2.2.0)
|
||||
dotenv (= 2.2.0)
|
||||
railties (>= 3.2, < 5.1)
|
||||
dotenv (2.2.1)
|
||||
dotenv-rails (2.2.1)
|
||||
dotenv (= 2.2.1)
|
||||
railties (>= 3.2, < 5.2)
|
||||
easy_translate (0.5.0)
|
||||
json
|
||||
thread
|
||||
thread_safe
|
||||
encryptor (3.0.0)
|
||||
erubis (2.7.0)
|
||||
et-orbi (1.0.4)
|
||||
tzinfo
|
||||
execjs (2.7.0)
|
||||
fabrication (2.16.1)
|
||||
faker (1.7.3)
|
||||
i18n (~> 0.5)
|
||||
fast_blank (1.0.0)
|
||||
font-awesome-rails (4.7.0.1)
|
||||
railties (>= 3.2, < 5.1)
|
||||
ffi (1.9.18)
|
||||
fuubar (2.2.0)
|
||||
rspec-core (~> 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
@@ -178,7 +168,7 @@ GEM
|
||||
activesupport (>= 4.0.1)
|
||||
hamlit (>= 1.2.0)
|
||||
railties (>= 4.0.1)
|
||||
hashdiff (0.3.2)
|
||||
hashdiff (0.3.4)
|
||||
highline (1.7.8)
|
||||
hiredis (0.6.1)
|
||||
htmlentities (4.3.4)
|
||||
@@ -189,14 +179,14 @@ GEM
|
||||
http_parser.rb (~> 0.6.0)
|
||||
http-cookie (1.0.3)
|
||||
domain_name (~> 0.5)
|
||||
http-form_data (1.0.1)
|
||||
http-form_data (1.0.3)
|
||||
http_accept_language (2.1.0)
|
||||
http_parser.rb (0.6.0)
|
||||
httplog (0.99.3)
|
||||
colorize
|
||||
rack
|
||||
i18n (0.8.1)
|
||||
i18n-tasks (0.9.13)
|
||||
i18n-tasks (0.9.15)
|
||||
activesupport (>= 4.0.2)
|
||||
ast (>= 2.1.0)
|
||||
easy_translate (>= 0.5.0)
|
||||
@@ -207,10 +197,6 @@ GEM
|
||||
rainbow (~> 2.2)
|
||||
terminal-table (>= 1.5.1)
|
||||
jmespath (1.3.1)
|
||||
jquery-rails (4.3.1)
|
||||
rails-dom-testing (>= 1, < 3)
|
||||
railties (>= 4.2.0)
|
||||
thor (>= 0.14, < 2.0)
|
||||
json (2.1.0)
|
||||
kaminari (1.0.1)
|
||||
activesupport (>= 4.1.0)
|
||||
@@ -233,19 +219,16 @@ GEM
|
||||
letter_opener (~> 1.0)
|
||||
railties (>= 3.2)
|
||||
link_header (0.0.8)
|
||||
local_time (1.0.3)
|
||||
coffee-rails
|
||||
lograge (0.4.1)
|
||||
actionpack (>= 4, < 5.1)
|
||||
activesupport (>= 4, < 5.1)
|
||||
railties (>= 4, < 5.1)
|
||||
lograge (0.5.1)
|
||||
actionpack (>= 4, < 5.2)
|
||||
activesupport (>= 4, < 5.2)
|
||||
railties (>= 4, < 5.2)
|
||||
loofah (2.0.3)
|
||||
nokogiri (>= 1.5.9)
|
||||
mail (2.6.5)
|
||||
mime-types (>= 1.16, < 4)
|
||||
method_source (0.8.2)
|
||||
microformats2 (2.1.0)
|
||||
activesupport
|
||||
microformats2 (3.1.0)
|
||||
json
|
||||
nokogiri
|
||||
mime-types (3.1)
|
||||
@@ -253,24 +236,26 @@ GEM
|
||||
mime-types-data (3.2016.0521)
|
||||
mimemagic (0.3.2)
|
||||
mini_portile2 (2.1.0)
|
||||
minitest (5.10.1)
|
||||
minitest (5.10.2)
|
||||
msgpack (1.1.0)
|
||||
multi_json (1.12.1)
|
||||
net-scp (1.2.1)
|
||||
net-ssh (>= 2.6.5)
|
||||
net-ssh (4.1.0)
|
||||
nio4r (2.0.0)
|
||||
nokogiri (1.7.1)
|
||||
nokogiri (1.7.2)
|
||||
mini_portile2 (~> 2.1.0)
|
||||
nokogumbo (1.4.10)
|
||||
nokogumbo (1.4.11)
|
||||
nokogiri
|
||||
oj (3.0.2)
|
||||
oj (3.0.9)
|
||||
openssl (2.0.3)
|
||||
orm_adapter (0.5.0)
|
||||
ostatus2 (1.1.0)
|
||||
ostatus2 (2.0.0)
|
||||
addressable (~> 2.4)
|
||||
http (~> 2.0)
|
||||
nokogiri (~> 1.6)
|
||||
openssl (~> 2.0)
|
||||
ox (2.4.13)
|
||||
ox (2.5.0)
|
||||
paperclip (5.1.0)
|
||||
activemodel (>= 4.2.0)
|
||||
activesupport (>= 4.2.0)
|
||||
@@ -280,10 +265,13 @@ GEM
|
||||
paperclip-av-transcoder (0.6.4)
|
||||
av (~> 0.9.0)
|
||||
paperclip (>= 2.5.2)
|
||||
parallel (1.11.2)
|
||||
parallel_tests (2.14.1)
|
||||
parallel
|
||||
parser (2.4.0.0)
|
||||
ast (~> 2.2)
|
||||
pg (0.20.0)
|
||||
pghero (1.6.5)
|
||||
pghero (1.7.0)
|
||||
activerecord
|
||||
pkg-config (1.2.0)
|
||||
powerpack (0.1.1)
|
||||
@@ -297,61 +285,50 @@ GEM
|
||||
puma (3.8.2)
|
||||
rabl (0.13.1)
|
||||
activesupport (>= 2.3.14)
|
||||
rack (2.0.1)
|
||||
rack (2.0.3)
|
||||
rack-attack (5.0.1)
|
||||
rack
|
||||
rack-cors (0.4.1)
|
||||
rack-protection (1.5.3)
|
||||
rack-protection (2.0.0)
|
||||
rack
|
||||
rack-test (0.6.3)
|
||||
rack (>= 1.0)
|
||||
rack-timeout (0.4.2)
|
||||
rails (5.0.2)
|
||||
actioncable (= 5.0.2)
|
||||
actionmailer (= 5.0.2)
|
||||
actionpack (= 5.0.2)
|
||||
actionview (= 5.0.2)
|
||||
activejob (= 5.0.2)
|
||||
activemodel (= 5.0.2)
|
||||
activerecord (= 5.0.2)
|
||||
activesupport (= 5.0.2)
|
||||
rails (5.0.3)
|
||||
actioncable (= 5.0.3)
|
||||
actionmailer (= 5.0.3)
|
||||
actionpack (= 5.0.3)
|
||||
actionview (= 5.0.3)
|
||||
activejob (= 5.0.3)
|
||||
activemodel (= 5.0.3)
|
||||
activerecord (= 5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
bundler (>= 1.3.0, < 2.0)
|
||||
railties (= 5.0.2)
|
||||
railties (= 5.0.3)
|
||||
sprockets-rails (>= 2.0.0)
|
||||
rails-controller-testing (1.0.1)
|
||||
actionpack (~> 5.x)
|
||||
actionview (~> 5.x)
|
||||
rails-controller-testing (1.0.2)
|
||||
actionpack (~> 5.x, >= 5.0.1)
|
||||
actionview (~> 5.x, >= 5.0.1)
|
||||
activesupport (~> 5.x)
|
||||
rails-dom-testing (2.0.2)
|
||||
activesupport (>= 4.2.0, < 6.0)
|
||||
nokogiri (~> 1.6)
|
||||
rails-dom-testing (2.0.3)
|
||||
activesupport (>= 4.2.0)
|
||||
nokogiri (>= 1.6)
|
||||
rails-html-sanitizer (1.0.3)
|
||||
loofah (~> 2.0)
|
||||
rails-i18n (5.0.3)
|
||||
rails-i18n (5.0.4)
|
||||
i18n (~> 0.7)
|
||||
railties (~> 5.0)
|
||||
rails-settings-cached (0.6.5)
|
||||
rails (>= 4.2.0)
|
||||
rails_12factor (0.0.3)
|
||||
rails_serve_static_assets
|
||||
rails_stdout_logging
|
||||
rails_serve_static_assets (0.0.5)
|
||||
rails_stdout_logging (0.0.5)
|
||||
railties (5.0.2)
|
||||
actionpack (= 5.0.2)
|
||||
activesupport (= 5.0.2)
|
||||
railties (5.0.3)
|
||||
actionpack (= 5.0.3)
|
||||
activesupport (= 5.0.3)
|
||||
method_source
|
||||
rake (>= 0.8.7)
|
||||
thor (>= 0.18.1, < 2.0)
|
||||
rainbow (2.2.2)
|
||||
rake
|
||||
rake (12.0.0)
|
||||
react-rails (1.11.0)
|
||||
babel-transpiler (>= 0.7.0)
|
||||
connection_pool
|
||||
execjs
|
||||
railties (>= 3.2)
|
||||
tilt
|
||||
rake (11.3.0)
|
||||
redis (3.3.3)
|
||||
redis-actionpack (5.0.1)
|
||||
actionpack (>= 4.0, < 6)
|
||||
@@ -360,6 +337,8 @@ GEM
|
||||
redis-activesupport (5.0.2)
|
||||
activesupport (>= 3, < 6)
|
||||
redis-store (~> 1.3.0)
|
||||
redis-namespace (1.5.3)
|
||||
redis (~> 3.0, >= 3.0.4)
|
||||
redis-rack (2.0.2)
|
||||
rack (>= 1.5, < 3)
|
||||
redis-store (>= 1.2, < 1.4)
|
||||
@@ -369,31 +348,32 @@ GEM
|
||||
redis-store (>= 1.2, < 2)
|
||||
redis-store (1.3.0)
|
||||
redis (>= 2.2)
|
||||
responders (2.3.0)
|
||||
railties (>= 4.2.0, < 5.1)
|
||||
responders (2.4.0)
|
||||
actionpack (>= 4.2.0, < 5.3)
|
||||
railties (>= 4.2.0, < 5.3)
|
||||
rotp (2.1.2)
|
||||
rqrcode (0.10.1)
|
||||
chunky_png (~> 1.0)
|
||||
rspec-core (3.5.4)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-expectations (3.5.0)
|
||||
rspec-core (3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-expectations (3.6.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-mocks (3.5.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-mocks (3.6.0)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-rails (3.5.2)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-rails (3.6.0)
|
||||
actionpack (>= 3.0)
|
||||
activesupport (>= 3.0)
|
||||
railties (>= 3.0)
|
||||
rspec-core (~> 3.5.0)
|
||||
rspec-expectations (~> 3.5.0)
|
||||
rspec-mocks (~> 3.5.0)
|
||||
rspec-support (~> 3.5.0)
|
||||
rspec-sidekiq (3.0.0)
|
||||
rspec-core (~> 3.6.0)
|
||||
rspec-expectations (~> 3.6.0)
|
||||
rspec-mocks (~> 3.6.0)
|
||||
rspec-support (~> 3.6.0)
|
||||
rspec-sidekiq (3.0.1)
|
||||
rspec-core (~> 3.0, >= 3.0.0)
|
||||
sidekiq (>= 2.4.0)
|
||||
rspec-support (3.5.0)
|
||||
rspec-support (3.6.0)
|
||||
rubocop (0.48.1)
|
||||
parser (>= 2.3.3.1, < 3.0)
|
||||
powerpack (~> 0.1)
|
||||
@@ -402,36 +382,40 @@ GEM
|
||||
unicode-display_width (~> 1.0, >= 1.0.1)
|
||||
ruby-oembed (0.12.0)
|
||||
ruby-progressbar (1.8.1)
|
||||
rufus-scheduler (3.4.0)
|
||||
et-orbi (~> 1.0)
|
||||
safe_yaml (1.0.4)
|
||||
sanitize (4.4.0)
|
||||
crass (~> 1.0.2)
|
||||
nokogiri (>= 1.4.4)
|
||||
nokogumbo (~> 1.4.1)
|
||||
sass (3.4.23)
|
||||
sass-rails (5.0.6)
|
||||
railties (>= 4.0.0, < 6)
|
||||
sass (~> 3.1)
|
||||
sprockets (>= 2.8, < 4.0)
|
||||
sprockets-rails (>= 2.0, < 4.0)
|
||||
tilt (>= 1.1, < 3)
|
||||
sidekiq (4.2.10)
|
||||
sass (3.4.24)
|
||||
scss_lint (0.53.0)
|
||||
rake (>= 0.9, < 13)
|
||||
sass (~> 3.4.20)
|
||||
sidekiq (5.0.0)
|
||||
concurrent-ruby (~> 1.0)
|
||||
connection_pool (~> 2.2, >= 2.2.0)
|
||||
rack-protection (>= 1.5.0)
|
||||
redis (~> 3.2, >= 3.2.1)
|
||||
sidekiq-unique-jobs (5.0.0)
|
||||
sidekiq (>= 4.0)
|
||||
thor
|
||||
redis (~> 3.3, >= 3.3.3)
|
||||
sidekiq-scheduler (2.1.4)
|
||||
redis (~> 3)
|
||||
rufus-scheduler (~> 3.2)
|
||||
sidekiq (>= 3)
|
||||
tilt (>= 1.4.0)
|
||||
sidekiq-unique-jobs (5.0.8)
|
||||
sidekiq (>= 4.0, <= 6.0)
|
||||
thor (~> 0)
|
||||
simple-navigation (4.0.5)
|
||||
activesupport (>= 2.3.2)
|
||||
simple_form (3.4.0)
|
||||
actionpack (> 4, < 5.1)
|
||||
activemodel (> 4, < 5.1)
|
||||
simple_form (3.5.0)
|
||||
actionpack (> 4, < 5.2)
|
||||
activemodel (> 4, < 5.2)
|
||||
simplecov (0.14.1)
|
||||
docile (~> 1.1.0)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
simplecov-html (0.10.0)
|
||||
simplecov-html (0.10.1)
|
||||
slop (3.6.0)
|
||||
sprockets (3.7.1)
|
||||
concurrent-ruby (~> 1.0)
|
||||
@@ -445,8 +429,8 @@ GEM
|
||||
net-ssh (>= 2.8.0)
|
||||
statsd-instrument (2.1.2)
|
||||
temple (0.8.0)
|
||||
terminal-table (1.7.3)
|
||||
unicode-display_width (~> 1.1.1)
|
||||
terminal-table (1.8.0)
|
||||
unicode-display_width (~> 1.1, >= 1.1.1)
|
||||
thor (0.19.4)
|
||||
thread (0.2.2)
|
||||
thread_safe (0.3.6)
|
||||
@@ -462,7 +446,7 @@ GEM
|
||||
unf (0.1.4)
|
||||
unf_ext
|
||||
unf_ext (0.0.7.4)
|
||||
unicode-display_width (1.1.3)
|
||||
unicode-display_width (1.2.1)
|
||||
uniform_notifier (1.10.0)
|
||||
warden (1.2.7)
|
||||
rack (>= 1.0)
|
||||
@@ -470,10 +454,13 @@ GEM
|
||||
addressable (>= 2.3.6)
|
||||
crack (>= 0.3.2)
|
||||
hashdiff
|
||||
webpacker (1.2)
|
||||
activesupport (>= 4.2)
|
||||
multi_json (~> 1.2)
|
||||
railties (>= 4.2)
|
||||
websocket-driver (0.6.5)
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.2)
|
||||
whatlanguage (1.0.6)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
|
||||
@@ -481,88 +468,87 @@ PLATFORMS
|
||||
ruby
|
||||
|
||||
DEPENDENCIES
|
||||
active_record_query_trace
|
||||
addressable
|
||||
autoprefixer-rails
|
||||
aws-sdk (>= 2.0)
|
||||
best_in_place (~> 3.0.1)
|
||||
better_errors
|
||||
binding_of_caller
|
||||
browserify-rails
|
||||
bullet
|
||||
capistrano (= 3.8.0)
|
||||
capistrano-faster-assets (~> 1.0)
|
||||
capistrano-rails
|
||||
capistrano-rbenv
|
||||
capistrano-yarn
|
||||
capybara
|
||||
devise
|
||||
devise-two-factor
|
||||
doorkeeper
|
||||
dotenv-rails
|
||||
fabrication
|
||||
faker
|
||||
fast_blank
|
||||
font-awesome-rails
|
||||
fuubar
|
||||
goldfinger
|
||||
hamlit-rails
|
||||
hiredis
|
||||
htmlentities
|
||||
http
|
||||
http_accept_language
|
||||
httplog
|
||||
i18n-tasks (~> 0.9.6)
|
||||
jquery-rails
|
||||
kaminari
|
||||
letter_opener
|
||||
letter_opener_web
|
||||
link_header
|
||||
local_time
|
||||
lograge
|
||||
microformats2
|
||||
nokogiri
|
||||
oj
|
||||
ostatus2 (~> 1.1)
|
||||
ox
|
||||
active_record_query_trace (~> 1.5)
|
||||
addressable (~> 2.5)
|
||||
annotate (~> 2.7)
|
||||
aws-sdk (~> 2.9)
|
||||
better_errors (~> 2.1)
|
||||
binding_of_caller (~> 0.7)
|
||||
bootsnap
|
||||
brakeman (~> 3.6)
|
||||
bullet (~> 5.5)
|
||||
bundler-audit (~> 0.5)
|
||||
capistrano (~> 3.8)
|
||||
capistrano-rails (~> 1.2)
|
||||
capistrano-rbenv (~> 2.1)
|
||||
capistrano-yarn (~> 2.0)
|
||||
capybara (~> 2.14)
|
||||
cld3 (~> 3.1)
|
||||
devise (~> 4.2)
|
||||
devise-two-factor (~> 3.0)
|
||||
doorkeeper (~> 4.2)
|
||||
dotenv-rails (~> 2.2)
|
||||
fabrication (~> 2.16)
|
||||
faker (~> 1.7)
|
||||
fast_blank (~> 1.0)
|
||||
fuubar (~> 2.2)
|
||||
goldfinger (~> 1.2)
|
||||
hamlit-rails (~> 0.2)
|
||||
hiredis (~> 0.6)
|
||||
htmlentities (~> 4.3)
|
||||
http (~> 2.2)
|
||||
http_accept_language (~> 2.1)
|
||||
httplog (~> 0.99)
|
||||
i18n-tasks (~> 0.9)
|
||||
kaminari (~> 1.0)
|
||||
letter_opener (~> 1.4)
|
||||
letter_opener_web (~> 1.3)
|
||||
link_header (~> 0.0)
|
||||
lograge (~> 0.5)
|
||||
microformats2 (~> 3.0)
|
||||
nokogiri (~> 1.7)
|
||||
oj (~> 3.0)
|
||||
ostatus2 (~> 2.0)
|
||||
ox (~> 2.5)
|
||||
paperclip (~> 5.1)
|
||||
paperclip-av-transcoder
|
||||
pg
|
||||
pghero
|
||||
pkg-config
|
||||
pry-rails
|
||||
puma
|
||||
rabl
|
||||
rack-attack
|
||||
rack-cors
|
||||
rack-timeout
|
||||
rails (~> 5.0.2)
|
||||
rails-controller-testing
|
||||
rails-i18n
|
||||
rails-settings-cached
|
||||
rails_12factor
|
||||
react-rails
|
||||
redis (~> 3.2)
|
||||
redis-rails
|
||||
rqrcode
|
||||
rspec-rails
|
||||
rspec-sidekiq
|
||||
rubocop
|
||||
ruby-oembed
|
||||
sanitize
|
||||
sass-rails (~> 5.0)
|
||||
sidekiq
|
||||
sidekiq-unique-jobs
|
||||
simple-navigation
|
||||
simple_form
|
||||
simplecov
|
||||
sprockets-rails
|
||||
statsd-instrument
|
||||
twitter-text
|
||||
tzinfo-data
|
||||
uglifier (>= 1.3.0)
|
||||
webmock
|
||||
whatlanguage
|
||||
paperclip-av-transcoder (~> 0.6)
|
||||
parallel_tests (~> 2.14)
|
||||
pg (~> 0.20)
|
||||
pghero (~> 1.7)
|
||||
pkg-config (~> 1.2)
|
||||
pry-rails (~> 0.3)
|
||||
puma (~> 3.8)
|
||||
rabl (~> 0.13)
|
||||
rack-attack (~> 5.0)
|
||||
rack-cors (~> 0.4)
|
||||
rack-timeout (~> 0.4)
|
||||
rails (~> 5.0)
|
||||
rails-controller-testing (~> 1.0)
|
||||
rails-i18n (~> 5.0)
|
||||
rails-settings-cached (~> 0.6)
|
||||
redis (~> 3.3)
|
||||
redis-namespace (~> 1.5)
|
||||
redis-rails (~> 5.0)
|
||||
rqrcode (~> 0.10)
|
||||
rspec-rails (~> 3.6)
|
||||
rspec-sidekiq (~> 3.0)
|
||||
rubocop (~> 0.48)
|
||||
ruby-oembed (~> 0.12)
|
||||
sanitize (~> 4.4)
|
||||
scss_lint (~> 0.53)
|
||||
sidekiq (~> 5.0)
|
||||
sidekiq-scheduler (~> 2.1)
|
||||
sidekiq-unique-jobs (~> 5.0)
|
||||
simple-navigation (~> 4.0)
|
||||
simple_form (~> 3.4)
|
||||
simplecov (~> 0.14)
|
||||
sprockets-rails (~> 3.2)
|
||||
statsd-instrument (~> 2.1)
|
||||
twitter-text (~> 1.14)
|
||||
tzinfo-data (~> 1.2017)
|
||||
uglifier (~> 3.2)
|
||||
webmock (~> 3.0)
|
||||
webpacker (~> 1.2)
|
||||
|
||||
RUBY VERSION
|
||||
ruby 2.4.1p111
|
||||
|
2
Procfile
2
Procfile
@@ -1,2 +1,2 @@
|
||||
web: bundle exec puma -C config/puma.rb
|
||||
worker: bundle exec sidekiq -q default -q push -q pull -q mailers
|
||||
worker: bundle exec sidekiq
|
||||
|
4
Procfile.dev
Normal file
4
Procfile.dev
Normal file
@@ -0,0 +1,4 @@
|
||||
web: PORT=3000 bundle exec puma -C config/puma.rb
|
||||
sidekiq: PORT=3000 bundle exec sidekiq
|
||||
stream: PORT=4000 yarn run start
|
||||
webpack: ./bin/webpack-dev-server --host 0.0.0.0
|
@@ -13,7 +13,7 @@ An alternative implementation of the GNU social project. Based on [ActivityStrea
|
||||
|
||||
Click on the screenshot to watch a demo of the UI:
|
||||
|
||||
[][youtube_demo]
|
||||
[][youtube_demo]
|
||||
|
||||
[youtube_demo]: https://www.youtube.com/watch?v=YO1jQ8_rAMU
|
||||
|
||||
@@ -47,6 +47,10 @@ If you would like, you can [support the development of this project on Patreon][
|
||||
Mastodon tries to be as fast and responsive as possible, so all long-running tasks that can be delegated to background processing, are
|
||||
- **Deployable via Docker**
|
||||
You don't need to mess with dependencies and configuration if you want to try Mastodon, if you have Docker and Docker Compose the deployment is extremely easy
|
||||
|
||||
## Development
|
||||
|
||||
Please follow the [development guide](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Development-guide.md) from the documentation repository.
|
||||
|
||||
## Deployment
|
||||
|
||||
|
30
Vagrantfile
vendored
30
Vagrantfile
vendored
@@ -1,6 +1,8 @@
|
||||
# -*- mode: ruby -*-
|
||||
# vi: set ft=ruby :
|
||||
|
||||
ENV["PORT"] ||= "3000"
|
||||
|
||||
$provision = <<SCRIPT
|
||||
|
||||
cd /vagrant # This is where the host folder/repo is mounted
|
||||
@@ -10,10 +12,10 @@ curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||
sudo apt-add-repository 'deb https://dl.yarnpkg.com/debian/ stable main'
|
||||
|
||||
# Add repo for NodeJS
|
||||
curl -sL https://deb.nodesource.com/setup_4.x | sudo bash -
|
||||
curl -sL https://deb.nodesource.com/setup_6.x | sudo bash -
|
||||
|
||||
# Add firewall rule to redirect 80 to 3000 and save
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port 3000
|
||||
# Add firewall rule to redirect 80 to PORT and save
|
||||
sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j REDIRECT --to-port #{ENV["PORT"]}
|
||||
echo iptables-persistent iptables-persistent/autosave_v4 boolean true | sudo debconf-set-selections
|
||||
echo iptables-persistent iptables-persistent/autosave_v6 boolean true | sudo debconf-set-selections
|
||||
sudo apt-get install iptables-persistent -y
|
||||
@@ -31,12 +33,13 @@ sudo apt-get install \
|
||||
redis-tools \
|
||||
postgresql \
|
||||
postgresql-contrib \
|
||||
protobuf-compiler \
|
||||
yarn \
|
||||
libprotobuf-dev \
|
||||
libreadline-dev \
|
||||
-y
|
||||
|
||||
# Install rvm
|
||||
cd /vagrant
|
||||
read RUBY_VERSION < .ruby-version
|
||||
gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
|
||||
curl -sSL https://get.rvm.io | bash -s stable --ruby=$RUBY_VERSION
|
||||
@@ -47,22 +50,23 @@ sudo -u postgres createuser -U postgres vagrant -s
|
||||
sudo -u postgres createdb -U postgres mastodon_development
|
||||
|
||||
# Install gems and node modules
|
||||
gem install bundler
|
||||
gem install bundler foreman
|
||||
bundle install
|
||||
yarn install
|
||||
|
||||
# Build Mastodon
|
||||
export $(cat ".env.vagrant" | xargs)
|
||||
bundle exec rails db:setup
|
||||
bundle exec rails assets:precompile
|
||||
|
||||
# Configure automatic loading of environment variable
|
||||
echo 'export $(cat "/vagrant/.env.vagrant" | xargs)' >> ~/.bash_profile
|
||||
|
||||
SCRIPT
|
||||
|
||||
$start = <<SCRIPT
|
||||
|
||||
cd /vagrant
|
||||
export $(cat ".env.vagrant" | xargs)
|
||||
rails s -d -b 0.0.0.0
|
||||
echo 'To start server'
|
||||
echo ' $ vagrant ssh -c "cd /vagrant && foreman start"'
|
||||
|
||||
SCRIPT
|
||||
|
||||
@@ -74,7 +78,7 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
|
||||
config.vm.provider :virtualbox do |vb|
|
||||
vb.name = "mastodon"
|
||||
vb.customize ["modifyvm", :id, "--memory", "1024"]
|
||||
vb.customize ["modifyvm", :id, "--memory", "2048"]
|
||||
|
||||
# Disable VirtualBox DNS proxy to skip long-delay IPv6 resolutions.
|
||||
# https://github.com/mitchellh/vagrant/issues/1172
|
||||
@@ -104,8 +108,10 @@ Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
|
||||
config.vm.synced_folder ".", "/vagrant"
|
||||
end
|
||||
|
||||
# Otherwise, you can access the site at http://localhost:3000
|
||||
config.vm.network :forwarded_port, guest: 80, host: 3000
|
||||
# Otherwise, you can access the site at http://localhost:3000 and http://localhost:4000 , http://localhost:8080
|
||||
config.vm.network :forwarded_port, guest: 3000, host: 3000
|
||||
config.vm.network :forwarded_port, guest: 4000, host: 4000
|
||||
config.vm.network :forwarded_port, guest: 8080, host: 8080
|
||||
|
||||
# Full provisioning script, only runs on first 'vagrant up' or with 'vagrant provision'
|
||||
config.vm.provision :shell, inline: $provision, privileged: false
|
||||
|
3
app.json
3
app.json
@@ -94,6 +94,9 @@
|
||||
}
|
||||
},
|
||||
"buildpacks": [
|
||||
{
|
||||
"url": "https://github.com/heroku/heroku-buildpack-apt"
|
||||
},
|
||||
{
|
||||
"url": "heroku/nodejs"
|
||||
},
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 244 KiB |
@@ -1,15 +0,0 @@
|
||||
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
||||
// listed below.
|
||||
//
|
||||
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
||||
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
||||
//
|
||||
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||
// compiled file.
|
||||
//
|
||||
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
||||
// about supported directives.
|
||||
//
|
||||
//= require jquery2
|
||||
//= require jquery_ujs
|
||||
//= require components
|
@@ -1,9 +0,0 @@
|
||||
//= require jquery2
|
||||
//= require jquery_ujs
|
||||
//= require extras
|
||||
//= require best_in_place
|
||||
//= require local_time
|
||||
|
||||
$(function () {
|
||||
$(".best_in_place").best_in_place();
|
||||
});
|
@@ -1,15 +0,0 @@
|
||||
//= require_self
|
||||
//= require react_ujs
|
||||
|
||||
window.React = require('react');
|
||||
window.ReactDOM = require('react-dom');
|
||||
window.Perf = require('react-addons-perf');
|
||||
|
||||
if (!window.Intl) {
|
||||
require('intl');
|
||||
require('intl/locale-data/jsonp/en.js');
|
||||
}
|
||||
|
||||
//= require_tree ./components
|
||||
|
||||
window.Mastodon = require('./components/containers/mastodon');
|
@@ -1,63 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class Avatar extends React.PureComponent {
|
||||
|
||||
constructor (props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
hovering: false
|
||||
};
|
||||
this.handleMouseEnter = this.handleMouseEnter.bind(this);
|
||||
this.handleMouseLeave = this.handleMouseLeave.bind(this);
|
||||
}
|
||||
|
||||
handleMouseEnter () {
|
||||
this.setState({ hovering: true });
|
||||
}
|
||||
|
||||
handleMouseLeave () {
|
||||
this.setState({ hovering: false });
|
||||
}
|
||||
|
||||
render () {
|
||||
const { src, size, staticSrc, animate } = this.props;
|
||||
const { hovering } = this.state;
|
||||
|
||||
const style = {
|
||||
...this.props.style,
|
||||
width: `${size}px`,
|
||||
height: `${size}px`,
|
||||
backgroundSize: `${size}px ${size}px`
|
||||
};
|
||||
|
||||
if (hovering || animate) {
|
||||
style.backgroundImage = `url(${src})`;
|
||||
} else {
|
||||
style.backgroundImage = `url(${staticSrc})`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className='account__avatar'
|
||||
onMouseEnter={this.handleMouseEnter}
|
||||
onMouseLeave={this.handleMouseLeave}
|
||||
style={style}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Avatar.propTypes = {
|
||||
src: PropTypes.string.isRequired,
|
||||
staticSrc: PropTypes.string,
|
||||
size: PropTypes.number.isRequired,
|
||||
style: PropTypes.object,
|
||||
animate: PropTypes.bool
|
||||
};
|
||||
|
||||
Avatar.defaultProps = {
|
||||
animate: false
|
||||
};
|
||||
|
||||
export default Avatar;
|
@@ -1,49 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class Button extends React.PureComponent {
|
||||
|
||||
constructor (props, context) {
|
||||
super(props, context);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
handleClick (e) {
|
||||
if (!this.props.disabled) {
|
||||
this.props.onClick();
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const style = {
|
||||
display: this.props.block ? 'block' : 'inline-block',
|
||||
width: this.props.block ? '100%' : 'auto',
|
||||
padding: `0 ${this.props.size / 2.25}px`,
|
||||
height: `${this.props.size}px`,
|
||||
lineHeight: `${this.props.size}px`
|
||||
};
|
||||
|
||||
return (
|
||||
<button className={`button ${this.props.secondary ? 'button-secondary' : ''}`} disabled={this.props.disabled} onClick={this.handleClick} style={{ ...style, ...this.props.style }}>
|
||||
{this.props.text || this.props.children}
|
||||
</button>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Button.propTypes = {
|
||||
text: PropTypes.node,
|
||||
onClick: PropTypes.func,
|
||||
disabled: PropTypes.bool,
|
||||
block: PropTypes.bool,
|
||||
secondary: PropTypes.bool,
|
||||
size: PropTypes.number,
|
||||
style: PropTypes.object,
|
||||
children: PropTypes.node
|
||||
};
|
||||
|
||||
Button.defaultProps = {
|
||||
size: 36
|
||||
};
|
||||
|
||||
export default Button;
|
@@ -1,56 +0,0 @@
|
||||
import { Motion, spring } from 'react-motion';
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class ColumnCollapsable extends React.PureComponent {
|
||||
|
||||
constructor (props, context) {
|
||||
super(props, context);
|
||||
this.state = {
|
||||
collapsed: true
|
||||
};
|
||||
|
||||
this.handleToggleCollapsed = this.handleToggleCollapsed.bind(this);
|
||||
}
|
||||
|
||||
handleToggleCollapsed () {
|
||||
const currentState = this.state.collapsed;
|
||||
|
||||
this.setState({ collapsed: !currentState });
|
||||
|
||||
if (!currentState && this.props.onCollapse) {
|
||||
this.props.onCollapse();
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { icon, title, fullHeight, children } = this.props;
|
||||
const { collapsed } = this.state;
|
||||
const collapsedClassName = collapsed ? 'collapsable-collapsed' : 'collapsable';
|
||||
|
||||
return (
|
||||
<div className='column-collapsable'>
|
||||
<div role='button' tabIndex='0' title={`${title}`} 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 }) =>
|
||||
<div style={{ overflow: height === fullHeight ? 'auto' : 'hidden', height: `${height}px`, opacity: opacity / 100, maxHeight: '70vh' }}>
|
||||
{children}
|
||||
</div>
|
||||
}
|
||||
</Motion>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
ColumnCollapsable.propTypes = {
|
||||
icon: PropTypes.string.isRequired,
|
||||
title: PropTypes.string,
|
||||
fullHeight: PropTypes.number.isRequired,
|
||||
children: PropTypes.node,
|
||||
onCollapse: PropTypes.func
|
||||
};
|
||||
|
||||
export default ColumnCollapsable;
|
@@ -1,36 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
class Permalink extends React.Component {
|
||||
|
||||
constructor (props, context) {
|
||||
super(props, context);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
handleClick (e) {
|
||||
if (e.button === 0) {
|
||||
e.preventDefault();
|
||||
this.context.router.push(this.props.to);
|
||||
}
|
||||
}
|
||||
|
||||
render () {
|
||||
const { href, children, className, ...other } = this.props;
|
||||
|
||||
return <a href={href} onClick={this.handleClick} {...other} className={'permalink ' + className}>{children}</a>;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Permalink.contextTypes = {
|
||||
router: PropTypes.object
|
||||
};
|
||||
|
||||
Permalink.propTypes = {
|
||||
className: PropTypes.string,
|
||||
href: PropTypes.string.isRequired,
|
||||
to: PropTypes.string.isRequired,
|
||||
children: PropTypes.node
|
||||
};
|
||||
|
||||
export default Permalink;
|
@@ -1,81 +0,0 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import PropTypes from 'prop-types';
|
||||
import InnerHeader from '../../account/components/header';
|
||||
import ActionBar from '../../account/components/action_bar';
|
||||
import MissingIndicator from '../../../components/missing_indicator';
|
||||
|
||||
class Header extends React.PureComponent {
|
||||
|
||||
constructor (props, context) {
|
||||
super(props, context);
|
||||
this.handleFollow = this.handleFollow.bind(this);
|
||||
this.handleBlock = this.handleBlock.bind(this);
|
||||
this.handleMention = this.handleMention.bind(this);
|
||||
this.handleReport = this.handleReport.bind(this);
|
||||
this.handleMute = this.handleMute.bind(this);
|
||||
}
|
||||
|
||||
handleFollow () {
|
||||
this.props.onFollow(this.props.account);
|
||||
}
|
||||
|
||||
handleBlock () {
|
||||
this.props.onBlock(this.props.account);
|
||||
}
|
||||
|
||||
handleMention () {
|
||||
this.props.onMention(this.props.account, this.context.router);
|
||||
}
|
||||
|
||||
handleReport () {
|
||||
this.props.onReport(this.props.account);
|
||||
this.context.router.push('/report');
|
||||
}
|
||||
|
||||
handleMute() {
|
||||
this.props.onMute(this.props.account);
|
||||
}
|
||||
|
||||
render () {
|
||||
const { account, me } = this.props;
|
||||
|
||||
if (account === null) {
|
||||
return <MissingIndicator />;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className='account-timeline__header'>
|
||||
<InnerHeader
|
||||
account={account}
|
||||
me={me}
|
||||
onFollow={this.handleFollow}
|
||||
/>
|
||||
|
||||
<ActionBar
|
||||
account={account}
|
||||
me={me}
|
||||
onBlock={this.handleBlock}
|
||||
onMention={this.handleMention}
|
||||
onReport={this.handleReport}
|
||||
onMute={this.handleMute}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Header.propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
me: PropTypes.number.isRequired,
|
||||
onFollow: PropTypes.func.isRequired,
|
||||
onBlock: PropTypes.func.isRequired,
|
||||
onMention: PropTypes.func.isRequired,
|
||||
onReport: PropTypes.func.isRequired,
|
||||
onMute: PropTypes.func.isRequired
|
||||
};
|
||||
|
||||
Header.contextTypes = {
|
||||
router: PropTypes.object
|
||||
};
|
||||
|
||||
export default Header;
|
@@ -1,16 +0,0 @@
|
||||
import Avatar from '../../../components/avatar';
|
||||
import DisplayName from '../../../components/display_name';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
const AutosuggestAccount = ({ account }) => (
|
||||
<div className='autosuggest-account'>
|
||||
<div className='autosuggest-account-icon'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={18} /></div>
|
||||
<DisplayName account={account} />
|
||||
</div>
|
||||
);
|
||||
|
||||
AutosuggestAccount.propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired
|
||||
};
|
||||
|
||||
export default AutosuggestAccount;
|
@@ -1,15 +0,0 @@
|
||||
import { FormattedMessage } from 'react-intl';
|
||||
import DisplayName from '../../../components/display_name';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
const AutosuggestStatus = ({ status }) => (
|
||||
<div className='autosuggest-status'>
|
||||
<FormattedMessage id='search.status_by' defaultMessage='Status by {name}' values={{ name: <strong>@{status.getIn(['account', 'acct'])}</strong> }} />
|
||||
</div>
|
||||
);
|
||||
|
||||
AutosuggestStatus.propTypes = {
|
||||
status: ImmutablePropTypes.map.isRequired
|
||||
};
|
||||
|
||||
export default AutosuggestStatus;
|
@@ -1,60 +0,0 @@
|
||||
import IconButton from '../../../components/icon_button';
|
||||
import PropTypes from 'prop-types';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
upload: { id: 'upload_button.label', defaultMessage: 'Add media' }
|
||||
});
|
||||
|
||||
|
||||
const iconStyle = {
|
||||
height: null,
|
||||
lineHeight: '27px'
|
||||
}
|
||||
|
||||
class UploadButton extends React.PureComponent {
|
||||
|
||||
constructor (props, context) {
|
||||
super(props, context);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
this.setRef = this.setRef.bind(this);
|
||||
}
|
||||
|
||||
handleChange (e) {
|
||||
if (e.target.files.length > 0) {
|
||||
this.props.onSelectFile(e.target.files);
|
||||
}
|
||||
}
|
||||
|
||||
handleClick () {
|
||||
this.fileElement.click();
|
||||
}
|
||||
|
||||
setRef (c) {
|
||||
this.fileElement = c;
|
||||
}
|
||||
|
||||
render () {
|
||||
|
||||
const { intl, resetFileKey, disabled } = this.props;
|
||||
|
||||
return (
|
||||
<div className='compose-form__upload-button'>
|
||||
<IconButton icon='camera' title={intl.formatMessage(messages.upload)} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle}/>
|
||||
<input key={resetFileKey} ref={this.setRef} type='file' multiple={false} onChange={this.handleChange} disabled={disabled} style={{ display: 'none' }} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
UploadButton.propTypes = {
|
||||
disabled: PropTypes.bool,
|
||||
onSelectFile: PropTypes.func.isRequired,
|
||||
style: PropTypes.object,
|
||||
resetFileKey: PropTypes.number,
|
||||
intl: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(UploadButton);
|
@@ -1,44 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Permalink from '../../../components/permalink';
|
||||
import Avatar from '../../../components/avatar';
|
||||
import DisplayName from '../../../components/display_name';
|
||||
import emojify from '../../../emoji';
|
||||
import IconButton from '../../../components/icon_button';
|
||||
import { defineMessages, injectIntl } from 'react-intl';
|
||||
|
||||
const messages = defineMessages({
|
||||
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
|
||||
reject: { id: 'follow_request.reject', defaultMessage: 'Reject' }
|
||||
});
|
||||
|
||||
const AccountAuthorize = ({ intl, account, onAuthorize, onReject }) => {
|
||||
const content = { __html: emojify(account.get('note')) };
|
||||
|
||||
return (
|
||||
<div className='account-authorize__wrapper'>
|
||||
<div className='account-authorize'>
|
||||
<Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
|
||||
<div className='account-authorize__avatar'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={48} /></div>
|
||||
<DisplayName account={account} />
|
||||
</Permalink>
|
||||
|
||||
<div className='account__header__content' dangerouslySetInnerHTML={content} />
|
||||
</div>
|
||||
|
||||
<div className='account--panel'>
|
||||
<div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div>
|
||||
<div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
};
|
||||
|
||||
AccountAuthorize.propTypes = {
|
||||
account: ImmutablePropTypes.map.isRequired,
|
||||
onAuthorize: PropTypes.func.isRequired,
|
||||
onReject: PropTypes.func.isRequired,
|
||||
intl: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(AccountAuthorize);
|
@@ -1,66 +0,0 @@
|
||||
import Column from '../ui/components/column';
|
||||
import ColumnLink from '../ui/components/column_link';
|
||||
import ColumnSubheading from '../ui/components/column_subheading';
|
||||
import { Link } from 'react-router';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
|
||||
const messages = defineMessages({
|
||||
heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
|
||||
public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
|
||||
navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation'},
|
||||
settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings'},
|
||||
community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
|
||||
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
|
||||
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
|
||||
sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
|
||||
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
|
||||
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
|
||||
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
|
||||
info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
me: state.getIn(['accounts', state.getIn(['meta', 'me'])])
|
||||
});
|
||||
|
||||
const GettingStarted = ({ intl, me }) => {
|
||||
let followRequests = '';
|
||||
|
||||
if (me.get('locked')) {
|
||||
followRequests = <ColumnLink icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />;
|
||||
}
|
||||
|
||||
return (
|
||||
<Column icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile={true}>
|
||||
<div className='getting-started__wrapper'>
|
||||
<ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)}/>
|
||||
<ColumnLink icon='users' hideOnMobile={true} text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />
|
||||
<ColumnLink icon='globe' hideOnMobile={true} text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />
|
||||
<ColumnLink icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />
|
||||
{followRequests}
|
||||
<ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
|
||||
<ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
|
||||
<ColumnSubheading text={intl.formatMessage(messages.settings_subheading)}/>
|
||||
<ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
|
||||
<ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />
|
||||
<ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
|
||||
</div>
|
||||
|
||||
<div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
|
||||
<div className='static-content getting-started'>
|
||||
<p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
|
||||
</div>
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
};
|
||||
|
||||
GettingStarted.propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
me: ImmutablePropTypes.map.isRequired
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(GettingStarted));
|
@@ -1,37 +0,0 @@
|
||||
import { connect } from 'react-redux';
|
||||
import PropTypes from 'prop-types';
|
||||
import StatusListContainer from '../ui/containers/status_list_container';
|
||||
import Column from '../ui/components/column';
|
||||
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
|
||||
import ColumnSettingsContainer from './containers/column_settings_container';
|
||||
import { Link } from 'react-router';
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'column.home', defaultMessage: 'Home' }
|
||||
});
|
||||
|
||||
const mapStateToProps = state => ({
|
||||
hasUnread: state.getIn(['timelines', 'home', 'unread']) > 0
|
||||
});
|
||||
|
||||
class HomeTimeline extends React.PureComponent {
|
||||
|
||||
render () {
|
||||
const { intl, hasUnread } = this.props;
|
||||
|
||||
return (
|
||||
<Column icon='home' active={hasUnread} heading={intl.formatMessage(messages.title)}>
|
||||
<ColumnSettingsContainer />
|
||||
<StatusListContainer {...this.props} scrollKey='home_timeline' type='home' emptyMessage={<FormattedMessage id='empty_column.home' defaultMessage="You aren't following anyone yet. Visit {public} or use search to get started and meet other users." values={{ public: <Link to='/timelines/public'><FormattedMessage id='empty_column.home.public_timeline' defaultMessage='the public timeline' /></Link> }} />} />
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
HomeTimeline.propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
hasUnread: PropTypes.bool
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(injectIntl(HomeTimeline));
|
@@ -1,20 +0,0 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes';
|
||||
import Toggle from 'react-toggle';
|
||||
|
||||
const SettingToggle = ({ settings, settingKey, label, onChange, htmlFor = '' }) => (
|
||||
<label htmlFor={htmlFor} className='setting-toggle__label'>
|
||||
<Toggle checked={settings.getIn(settingKey)} onChange={(e) => onChange(settingKey, e.target.checked)} />
|
||||
<span className='setting-toggle'>{label}</span>
|
||||
</label>
|
||||
);
|
||||
|
||||
SettingToggle.propTypes = {
|
||||
settings: ImmutablePropTypes.map.isRequired,
|
||||
settingKey: PropTypes.array.isRequired,
|
||||
label: PropTypes.node.isRequired,
|
||||
onChange: PropTypes.func.isRequired,
|
||||
htmlFor: PropTypes.string
|
||||
};
|
||||
|
||||
export default SettingToggle;
|
@@ -1,33 +0,0 @@
|
||||
import Link from 'http-link-header';
|
||||
import querystring from 'querystring';
|
||||
|
||||
Link.parseAttrs = (link, parts) => {
|
||||
let match = null
|
||||
let attr = ''
|
||||
let value = ''
|
||||
let attrs = ''
|
||||
|
||||
let uriAttrs = /<(.*)>;\s*(.*)/gi.exec(parts)
|
||||
|
||||
if(uriAttrs) {
|
||||
attrs = uriAttrs[2]
|
||||
link = Link.parseParams(link, uriAttrs[1])
|
||||
}
|
||||
|
||||
while(match = Link.attrPattern.exec(attrs)) { // eslint-disable-line no-cond-assign
|
||||
attr = match[1].toLowerCase()
|
||||
value = match[4] || match[3] || match[2]
|
||||
|
||||
if( /\*$/.test(attr)) {
|
||||
Link.setAttr(link, attr, Link.parseExtendedValue(value))
|
||||
} else if(/%/.test(value)) {
|
||||
Link.setAttr(link, attr, querystring.decode(value))
|
||||
} else {
|
||||
Link.setAttr(link, attr, value)
|
||||
}
|
||||
}
|
||||
|
||||
return link
|
||||
};
|
||||
|
||||
export default Link;
|
@@ -1,120 +0,0 @@
|
||||
/**
|
||||
* ملاحظة للمساهمين و المساهمات :
|
||||
* لجعل مهمة المساهمين الآخرين أسهل، رجاءا تذكر :
|
||||
* 1. إضافة سلسلة جديدة هنا؛ و
|
||||
* 2. لإزالة السلاسل القديمة التي لم تعد هناك حاجة إليها. و
|
||||
* 3. لفرز السلاسل تبعا للأبجدية
|
||||
* شكر!
|
||||
*/
|
||||
const ar = {
|
||||
"account.block": "حظر @{name}",
|
||||
"account.disclaimer": "هذا المستخدم من مثيل خادم آخر. قد يكون هذا الرقم أكبر.",
|
||||
"account.edit_profile": "تعديل الملف الشخصي",
|
||||
"account.follow": "إتبع",
|
||||
"account.followers": "المتابعون",
|
||||
"account.follows_you": "يتابعك",
|
||||
"account.follows": "يتبع",
|
||||
"account.mention": "أُذكُر @{name}",
|
||||
"account.mute": "أكتم @{name}",
|
||||
"account.posts": "المشاركات",
|
||||
"account.report": "أبلغ عن @{name}",
|
||||
"account.requested": "في انتظار الموافقة",
|
||||
"account.unblock": "إلغاء الحظر عن @{name}",
|
||||
"account.unfollow": "إلغاء المتابعة",
|
||||
"account.unmute": "إلغاء الكتم عن @{name}",
|
||||
"boost_modal.combo": "يمكنك الضغط على {combo} لتخطي هذا مرة أخرى",
|
||||
"column_back_button.label": "العودة",
|
||||
"column.blocks": "الحسابات المحجوبة",
|
||||
"column.community": "الخيط العام المحلي",
|
||||
"column.favourites": "المفضلة",
|
||||
"column.follow_requests": "طلبات المتابعة",
|
||||
"column.home": "الرئيسية",
|
||||
"column.mutes": "الحسابات المكتومة",
|
||||
"column.notifications": "الإشعارات",
|
||||
"column.public": "الخيط العام الموحد",
|
||||
"compose_form.placeholder": "ماذا يدور في ذهنك ؟",
|
||||
"compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.",
|
||||
"compose_form.publish": "بَوِّق",
|
||||
"compose_form.sensitive": "ضع علامة حساس على الوسائط",
|
||||
"compose_form.spoiler_placeholder": "تنبيه عن المحتوى",
|
||||
"compose_form.spoiler": "إخفاء النص وراء التحذير",
|
||||
"emoji_button.label": "إيموجي",
|
||||
"emoji_button.search": "بحث ...",
|
||||
"emoji_button.people": "أشخاص",
|
||||
"emoji_button.nature": "طبيعة",
|
||||
"emoji_button.food": "أكل و شرب",
|
||||
"emoji_button.activity": "أنشطة",
|
||||
"emoji_button.travel": "أماكن و أسفار",
|
||||
"emoji_button.objects": "أشياء",
|
||||
"emoji_button.symbols": "رموز",
|
||||
"emoji_button.flags": "أعلام",
|
||||
"empty_column.community": "الخيط العام المحلي فارغ. قم بتحرير شيء ما كبداية.",
|
||||
"empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
|
||||
"empty_column.home.public_timeline": "الخيط العام",
|
||||
"empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.",
|
||||
"empty_column.notifications": "لم تتلق أي إشعار بعدُ. تفاعل مع المستخدمين الآخرين لإنشاء محادثة.",
|
||||
"empty_column.public": "لا يوجد شيء هنا ! قم بتحرير شيء ما بشكل عام، أو اتبع مستخدمين آخرين في الخوادم المثيلة الأخرى لملء خيط المحادثات العام.",
|
||||
"follow_request.authorize": "ترخيص",
|
||||
"follow_request.reject": "رفض",
|
||||
"getting_started.apps": "عدة تطبيقات مختلفة متوفرة",
|
||||
"getting_started.heading": "إستعدّ للبدء",
|
||||
"getting_started.about_addressing": "يمكنك متابعة الأشخاص إذا كنت تعرف اسم المستخدم الخاص بهم والنطاق الذي هم عليه عن طريق إدخال عنوان شبيه بالبريد الإلكتروني في الحقل المخصص للبحث.",
|
||||
"getting_started.about_shortcuts": "إذا كان المستخدم المستهدف في نفس النطاق الذي تستخدمه، فإسم المستخدم وحده يكفي. وتنطبق نفس القاعدة على ذكر الأشخاص في المنشورات و التبويقات.",
|
||||
"getting_started.open_source_notice": "ماستدون برنامج مفتوح المصدر. يمكنك المساهمة، أو الإبلاغ عن تقارير الأخطاء، على GitHub {github}. {apps}.",
|
||||
"home.column_settings.advanced": "متقدمة",
|
||||
"home.column_settings.basic": "أساسية",
|
||||
"home.column_settings.filter_regex": "تصفية حسب التعبيرات العادية",
|
||||
"home.column_settings.show_reblogs": "عرض الترقيات",
|
||||
"home.column_settings.show_replies": "عرض الردود",
|
||||
"home.settings": "إعدادات العمود",
|
||||
"lightbox.close": "إغلاق",
|
||||
"loading_indicator.label": "تحميل ...",
|
||||
"media_gallery.toggle_visible": "Toggle visibility",
|
||||
"missing_indicator.label": "تعذر العثور عليه",
|
||||
"navigation_bar.blocks": "الحسابات المحجوبة",
|
||||
"navigation_bar.community_timeline": "الخيط العام المحلي",
|
||||
"navigation_bar.edit_profile": "تعديل الملف الشخصي",
|
||||
"navigation_bar.preferences": "التفضيلات",
|
||||
"navigation_bar.community_timeline": "الخيط العام المحلي",
|
||||
"navigation_bar.public_timeline": "الخيط العام الموحد",
|
||||
"navigation_bar.logout": "خروج",
|
||||
"reply_indicator.cancel": "إلغاء",
|
||||
"search.placeholder": "ابحث",
|
||||
"search.account": "حساب",
|
||||
"search.hashtag": "وسم",
|
||||
"status.mention": "أذكُر @{name}",
|
||||
"status.delete": "إحذف",
|
||||
"status.reply": "ردّ",
|
||||
"status.reblog": "رَقِّي",
|
||||
"status.favourite": "أضف إلى المفضلة",
|
||||
"status.reblogged_by": "{name} رقى",
|
||||
"status.sensitive_warning": "محتوى حساس",
|
||||
"status.sensitive_toggle": "اضغط للعرض",
|
||||
"status.show_more": "أظهر المزيد",
|
||||
"status.show_less": "إعرض أقلّ",
|
||||
"status.open": "وسع هذه المشاركة",
|
||||
"status.report": "إبلِغ عن @{name}",
|
||||
"tabs_bar.compose": "تحرير",
|
||||
"tabs_bar.home": "الرئيسية",
|
||||
"tabs_bar.mentions": "الإشارات",
|
||||
"tabs_bar.public": "الخيط العام الموحد",
|
||||
"tabs_bar.notifications": "الإشعارات",
|
||||
"upload_button.label": "إضافة وسائط",
|
||||
"upload_form.undo": "إلغاء",
|
||||
"notification.follow": "{name} يتبعك",
|
||||
"notification.favourite": "{name} أعجب بمنشورك",
|
||||
"notification.reblog": "{name} قام بترقية تبويقك",
|
||||
"notification.mention": "{name} ذكرك",
|
||||
"notifications.column_settings.alert": "إشعارات سطح المكتب",
|
||||
"notifications.column_settings.show": "إعرِضها في عمود",
|
||||
"notifications.column_settings.follow": "متابعُون جُدُد :",
|
||||
"notifications.column_settings.favourite": "المُفَضَّلة :",
|
||||
"notifications.column_settings.mention": "الإشارات :",
|
||||
"notifications.column_settings.reblog": "الترقيّات:",
|
||||
"video_player.toggle_sound": "تبديل الصوت",
|
||||
"video_player.toggle_visible": "إظهار / إخفاء الفيديو",
|
||||
"video_player.expand": "توسيع الفيديو",
|
||||
"video_player.video_error": "تعذر تشغيل الفيديو",
|
||||
};
|
||||
|
||||
export default ar;
|
@@ -1,68 +0,0 @@
|
||||
const bg = {
|
||||
"column_back_button.label": "Назад",
|
||||
"lightbox.close": "Затвори",
|
||||
"loading_indicator.label": "Зареждане...",
|
||||
"status.mention": "Споменаване",
|
||||
"status.delete": "Изтриване",
|
||||
"status.reply": "Отговор",
|
||||
"status.reblog": "Споделяне",
|
||||
"status.favourite": "Предпочитани",
|
||||
"status.reblogged_by": "{name} сподели",
|
||||
"status.sensitive_warning": "Деликатно съдържание",
|
||||
"status.sensitive_toggle": "Покажи",
|
||||
"video_player.toggle_sound": "Звук",
|
||||
"account.mention": "Споменаване",
|
||||
"account.edit_profile": "Редактирай профила си",
|
||||
"account.unblock": "Не блокирай",
|
||||
"account.unfollow": "Не следвай",
|
||||
"account.block": "Блокирай",
|
||||
"account.follow": "Последвай",
|
||||
"account.posts": "Публикации",
|
||||
"account.follows": "Следвам",
|
||||
"account.followers": "Последователи",
|
||||
"account.follows_you": "Твой последовател",
|
||||
"account.requested": "В очакване на одобрение",
|
||||
"getting_started.heading": "Първи стъпки",
|
||||
"getting_started.about_addressing": "Можеш да последваш потребител, ако знаеш потребителското му име и домейна, на който се намира, като в полето за търсене ги въведеш по този начин: име@домейн",
|
||||
"getting_started.about_shortcuts": "Ако с търсения потребител се намирате на един и същ домейн, достатъчно е да въведеш само името. Същото важи и за споменаване на хора в публикации.",
|
||||
"getting_started.about_developer": "Можеш да потърсиш разработчика на този проект като: Gargron@mastodon.social",
|
||||
"getting_started.open_source_notice": "Mastodon е софтуер с отворен код. Можеш да помогнеш или да докладваш за проблеми в Github: {github}.",
|
||||
"column.home": "Начало",
|
||||
"column.mentions": "Споменавания",
|
||||
"column.public": "Публичен канал",
|
||||
"column.notifications": "Известия",
|
||||
"tabs_bar.compose": "Съставяне",
|
||||
"tabs_bar.home": "Начало",
|
||||
"tabs_bar.mentions": "Споменавания",
|
||||
"tabs_bar.public": "Публичен канал",
|
||||
"tabs_bar.notifications": "Известия",
|
||||
"compose_form.placeholder": "Какво си мислиш?",
|
||||
"compose_form.publish": "Раздумай",
|
||||
"compose_form.sensitive": "Отбележи съдържанието като деликатно",
|
||||
"compose_form.spoiler": "Скрий текста зад предупреждение",
|
||||
"compose_form.private": "Отбележи като поверително",
|
||||
"compose_form.privacy_disclaimer": "Поверителни публикации ще бъдат изпратени до споменатите потребители на {domains}. Доверяваш ли се на {domainsCount, plural, one {that server} other {those servers}}, че няма да издаде твоята публикация?",
|
||||
"compose_form.unlisted": "Не показвай в публичния канал",
|
||||
"navigation_bar.edit_profile": "Редактирай профил",
|
||||
"navigation_bar.preferences": "Предпочитания",
|
||||
"navigation_bar.public_timeline": "Публичен канал",
|
||||
"navigation_bar.logout": "Излизане",
|
||||
"reply_indicator.cancel": "Отказ",
|
||||
"search.placeholder": "Търсене",
|
||||
"search.account": "Акаунт",
|
||||
"search.hashtag": "Хаштаг",
|
||||
"upload_button.label": "Добави медия",
|
||||
"upload_form.undo": "Отмяна",
|
||||
"notification.follow": "{name} те последва",
|
||||
"notification.favourite": "{name} хареса твоята публикация",
|
||||
"notification.reblog": "{name} сподели твоята публикация",
|
||||
"notification.mention": "{name} те спомена",
|
||||
"notifications.column_settings.alert": "Десктоп известия",
|
||||
"notifications.column_settings.show": "Покажи в колона",
|
||||
"notifications.column_settings.follow": "Нови последователи:",
|
||||
"notifications.column_settings.favourite": "Предпочитани:",
|
||||
"notifications.column_settings.mention": "Споменавания:",
|
||||
"notifications.column_settings.reblog": "Споделяния:",
|
||||
};
|
||||
|
||||
export default bg;
|
@@ -1,68 +0,0 @@
|
||||
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;
|
@@ -1,93 +0,0 @@
|
||||
const es = {
|
||||
"column_back_button.label": "Atrás",
|
||||
"lightbox.close": "Cerrar",
|
||||
"loading_indicator.label": "Cargando...",
|
||||
"status.mention": "Mencionar",
|
||||
"status.delete": "Borrar",
|
||||
"status.reply": "Responder",
|
||||
"status.reblog": "Retoot",
|
||||
"status.favourite": "Favorito",
|
||||
"status.reblogged_by": "Retooteado por {name}",
|
||||
"status.sensitive_warning": "Contenido sensible",
|
||||
"status.sensitive_toggle": "Click para ver",
|
||||
"status.show_more": "Mostrar más",
|
||||
"status.show_less": "Mostrar menos",
|
||||
"status.open": "Expandir estado",
|
||||
"status.report": "Reportar",
|
||||
"video_player.toggle_sound": "Act/Desac. sonido",
|
||||
"account.mention": "Mencionar",
|
||||
"account.edit_profile": "Editar perfil",
|
||||
"account.unblock": "Desbloquear",
|
||||
"account.unfollow": "Dejar de seguir",
|
||||
"account.mute": "Silenciar",
|
||||
"account.block": "Bloquear",
|
||||
"account.follow": "Seguir",
|
||||
"account.posts": "Publicaciones",
|
||||
"account.follows": "Seguir",
|
||||
"account.followers": "Seguidores",
|
||||
"account.follows_you": "Te sigue",
|
||||
"account.requested": "Esperando aprobación",
|
||||
"getting_started.heading": "Primeros pasos",
|
||||
"getting_started.about_addressing": "Puedes seguir a gente si conoces su nombre de usuario y el dominio en el que están registrados, introduciendo algo similar a una dirección de correo electrónico en el formulario en la parte superior de la barra lateral.",
|
||||
"getting_started.about_shortcuts": "Si el usuario que buscas está en el mismo dominio que tú, simplemente funcionará introduciendo el nombre de usuario. La misma regla se aplica para mencionar a usuarios.",
|
||||
"getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}. {apps}.",
|
||||
"column.home": "Inicio",
|
||||
"column.community": "Historia local",
|
||||
"column.public": "Historia federada",
|
||||
"column.notifications": "Notificaciones",
|
||||
"column.blocks": "Usuarios bloqueados",
|
||||
"column.favourites": "Favoritos",
|
||||
"column.follow_requests": "Solicitudes para seguirte",
|
||||
"column.mutes": "Usuarios silenciados",
|
||||
"tabs_bar.compose": "Redactar",
|
||||
"tabs_bar.home": "Inicio",
|
||||
"tabs_bar.mentions": "Menciones",
|
||||
"tabs_bar.public": "Público",
|
||||
"tabs_bar.notifications": "Notificaciones",
|
||||
"compose_form.placeholder": "¿En qué estás pensando?",
|
||||
"compose_form.publish": "Tootear",
|
||||
"compose_form.sensitive": "Marcar contenido como sensible",
|
||||
"compose_form.spoiler": "Ocultar texto tras advertencia",
|
||||
"compose_form.spoiler_placeholder": "Advertencia de contenido",
|
||||
"composer_form.private": "Marcar como privado",
|
||||
"composer_form.privacy_disclaimer": "Tu estado se mostrará a los usuarios mencionados en {domains}. Tu estado podrá ser visto en otras instancias, quizás no quieras que tu estado sea visto por otros usuarios.",
|
||||
"compose_form.unlisted": "No mostrar en la historia federada",
|
||||
"navigation_bar.edit_profile": "Editar perfil",
|
||||
"navigation_bar.preferences": "Preferencias",
|
||||
"navigation_bar.community_timeline": "Historia local",
|
||||
"navigation_bar.public_timeline": "Historia federada",
|
||||
"navigation_bar.favourites": "Favoritos",
|
||||
"navigation_bar.blocks": "Usuarios bloqueados",
|
||||
"navigation_bar.info": "Información adicional",
|
||||
"navigation_bar.logout": "Cerrar sesión",
|
||||
"navigation_bar.follow_requests": "Solicitudes para seguirte",
|
||||
"navigation_bar.mutes": "Usuarios silenciados",
|
||||
"reply_indicator.cancel": "Cancelar",
|
||||
"search.placeholder": "Buscar",
|
||||
"search.account": "Cuenta",
|
||||
"search.hashtag": "Etiqueta",
|
||||
"upload_button.label": "Subir multimedia",
|
||||
"upload_form.undo": "Deshacer",
|
||||
"notification.follow": "{name} te empezó a seguir",
|
||||
"notification.favourite": "{name} marcó tu estado como favorito",
|
||||
"notification.reblog": "{name} ha retooteado tu estado",
|
||||
"notification.mention": "{name} te ha mencionado",
|
||||
"notifications.column_settings.alert": "Notificaciones de escritorio",
|
||||
"notifications.column_settings.show": "Mostrar en columna",
|
||||
"notifications.column_settings.follow": "Nuevos seguidores:",
|
||||
"notifications.column_settings.favourite": "Favoritos:",
|
||||
"notifications.column_settings.mention": "Menciones:",
|
||||
"notifications.column_settings.reblog": "Retoots:",
|
||||
"emoji_button.label": "Insertar emoji",
|
||||
"privacy.public.short": "Público",
|
||||
"privacy.public.long": "Mostrar en la historia federada",
|
||||
"privacy.unlisted.short": "Sin federar",
|
||||
"privacy.unlisted.long": "No mostrar en la historia federada",
|
||||
"privacy.private.short": "Privado",
|
||||
"privacy.private.long": "Sólo mostrar a seguidores",
|
||||
"privacy.direct.short": "Directo",
|
||||
"privacy.direct.long": "Sólo mostrar a los usuarios mencionados",
|
||||
"privacy.change": "Ajustar privacidad"
|
||||
};
|
||||
|
||||
export default es;
|
@@ -1,68 +0,0 @@
|
||||
const fi = {
|
||||
"column_back_button.label": "Takaisin",
|
||||
"lightbox.close": "Sulje",
|
||||
"loading_indicator.label": "Ladataan...",
|
||||
"status.mention": "Mainitse @{name}",
|
||||
"status.delete": "Poista",
|
||||
"status.reply": "Vastaa",
|
||||
"status.reblog": "Buustaa",
|
||||
"status.favourite": "Tykkää",
|
||||
"status.reblogged_by": "{name} buustasi",
|
||||
"status.sensitive_warning": "Arkaluontoista sisältöä",
|
||||
"status.sensitive_toggle": "Klikkaa nähdäksesi",
|
||||
"video_player.toggle_sound": "Äänet päälle/pois",
|
||||
"account.mention": "Mainitse @{name}",
|
||||
"account.edit_profile": "Muokkaa",
|
||||
"account.unblock": "Salli @{name}",
|
||||
"account.unfollow": "Lopeta seuraaminen",
|
||||
"account.block": "Estä @{name}",
|
||||
"account.follow": "Seuraa",
|
||||
"account.posts": "Postit",
|
||||
"account.follows": "Seuraa",
|
||||
"account.followers": "Seuraajia",
|
||||
"account.follows_you": "Seuraa sinua",
|
||||
"account.requested": "Odottaa hyväksyntää",
|
||||
"getting_started.heading": "Aloitus",
|
||||
"getting_started.about_addressing": "Voit seurata ihmisiä jos tiedät heidän käyttäjänimensä ja domainin missä he ovat syöttämällä e-mail-esque osoitteen Etsi kenttään.",
|
||||
"getting_started.about_shortcuts": "Jos etsimäsi henkilö on samassa domainissa kuin sinä, pelkkä käyttäjänimi kelpaa. Sama pätee kun mainitset ihmisiä statuksessasi",
|
||||
"getting_started.open_source_notice": "Mastodon Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}. {apps}.",
|
||||
"column.home": "Koti",
|
||||
"column.community": "Paikallinen aikajana",
|
||||
"column.public": "Yleinen aikajana",
|
||||
"column.notifications": "Ilmoitukset",
|
||||
"tabs_bar.compose": "Luo",
|
||||
"tabs_bar.home": "Koti",
|
||||
"tabs_bar.mentions": "Maininnat",
|
||||
"tabs_bar.public": "Yleinen aikajana",
|
||||
"tabs_bar.notifications": "Ilmoitukset",
|
||||
"compose_form.placeholder": "Mitä sinulla on mielessä?",
|
||||
"compose_form.publish": "Toot",
|
||||
"compose_form.sensitive": "Merkitse media herkäksi",
|
||||
"compose_form.spoiler": "Piiloita teksti varoituksen taakse",
|
||||
"compose_form.private": "Merkitse yksityiseksi",
|
||||
"compose_form.privacy_disclaimer": "Sinun yksityinen status toimitetaan mainitsemallesi käyttäjille domaineissa {domains}. Luotatko {domainsCount, plural, one {tähän palvelimeen} other {näihin palvelimiin}}? Postauksen yksityisyys toimii van Mastodon palvelimilla. Jos {domains} {domainsCount, plural, one {ei ole Mastodon palvelin} other {eivät ole Mastodon palvelin}}, viestiin ei tule Yksityinen-merkintää, ja sitä voidaan boostata tai muuten tehdä näkyväksi muille vastaanottajille.",
|
||||
"compose_form.unlisted": "Älä näytä yleisillä aikajanoilla",
|
||||
"navigation_bar.edit_profile": "Muokkaa profiilia",
|
||||
"navigation_bar.preferences": "Ominaisuudet",
|
||||
"navigation_bar.community_timeline": "Paikallinen aikajana",
|
||||
"navigation_bar.public_timeline": "Yleinen aikajana",
|
||||
"navigation_bar.logout": "Kirjaudu ulos",
|
||||
"reply_indicator.cancel": "Peruuta",
|
||||
"search.placeholder": "Hae",
|
||||
"search.account": "Tili",
|
||||
"search.hashtag": "Hashtag",
|
||||
"upload_button.label": "Lisää mediaa",
|
||||
"upload_form.undo": "Peru",
|
||||
"notification.follow": "{name} seurasi sinua",
|
||||
"notification.favourite": "{name} tykkäsi statuksestasi",
|
||||
"notification.reblog": "{name} buustasi statustasi",
|
||||
"notification.mention": "{name} mainitsi sinut",
|
||||
"notifications.column_settings.alert": "Työpöytä ilmoitukset",
|
||||
"notifications.column_settings.show": "Näytä sarakkeessa",
|
||||
"notifications.column_settings.follow": "Uusia seuraajia:",
|
||||
"notifications.column_settings.favourite": "Tykkäyksiä:",
|
||||
"notifications.column_settings.mention": "Mainintoja:",
|
||||
"notifications.column_settings.reblog": "Buusteja:",
|
||||
};
|
||||
|
||||
export default fi;
|
@@ -1,57 +0,0 @@
|
||||
const hu = {
|
||||
"column_back_button.label": "Vissza",
|
||||
"lightbox.close": "Bezárás",
|
||||
"loading_indicator.label": "Betöltés...",
|
||||
"status.mention": "Említés",
|
||||
"status.delete": "Törlés",
|
||||
"status.reply": "Válasz",
|
||||
"status.reblog": "Reblog",
|
||||
"status.favourite": "Kedvenc",
|
||||
"status.reblogged_by": "{name} reblogolta",
|
||||
"status.sensitive_warning": "Érzékeny tartalom",
|
||||
"status.sensitive_toggle": "Katt a megtekintéshez",
|
||||
"video_player.toggle_sound": "Hang kapcsolása",
|
||||
"account.mention": "Említés",
|
||||
"account.edit_profile": "Profil szerkesztése",
|
||||
"account.unblock": "Blokkolás levétele",
|
||||
"account.unfollow": "Követés abbahagyása",
|
||||
"account.block": "Blokkolás",
|
||||
"account.follow": "Követés",
|
||||
"account.posts": "Posts",
|
||||
"account.follows": "Követve",
|
||||
"account.followers": "Követők",
|
||||
"account.follows_you": "Követnek téged",
|
||||
"getting_started.heading": "Első lépések",
|
||||
"getting_started.about_addressing": "Követhetsz embereket felhasználónevük és a doménjük ismeretében, amennyiben megadod ezt az e-mail-szerű címet az oldalsáv tetején lévő rubrikában.",
|
||||
"getting_started.about_shortcuts": "Ha a célzott személy azonos doménen tartózkodik, a felhasználónév elegendő. Ugyanez érvényes mikor személyeket említesz az állapotokban.",
|
||||
"getting_started.about_developer": "A projekt fejlesztője követhető, mint Gargron@mastodon.social",
|
||||
"column.home": "Kezdőlap",
|
||||
"column.mentions": "Említések",
|
||||
"column.public": "Nyilvános",
|
||||
"column.notifications": "Értesítések",
|
||||
"tabs_bar.compose": "Összeállítás",
|
||||
"tabs_bar.home": "Kezdőlap",
|
||||
"tabs_bar.mentions": "Említések",
|
||||
"tabs_bar.public": "Nyilvános",
|
||||
"tabs_bar.notifications": "Notifications",
|
||||
"compose_form.placeholder": "Mire gondolsz?",
|
||||
"compose_form.publish": "Tülk!",
|
||||
"compose_form.sensitive": "Tartalom érzékenynek jelölése",
|
||||
"compose_form.unlisted": "Listázatlan mód",
|
||||
"navigation_bar.edit_profile": "Profil szerkesztése",
|
||||
"navigation_bar.preferences": "Beállítások",
|
||||
"navigation_bar.public_timeline": "Nyilvános időfolyam",
|
||||
"navigation_bar.logout": "Kijelentkezés",
|
||||
"reply_indicator.cancel": "Mégsem",
|
||||
"search.placeholder": "Keresés",
|
||||
"search.account": "Fiók",
|
||||
"search.hashtag": "Hashtag",
|
||||
"upload_button.label": "Média hozzáadása",
|
||||
"upload_form.undo": "Mégsem",
|
||||
"notification.follow": "{name} követ téged",
|
||||
"notification.favourite": "{name} kedvencnek jelölte az állapotod",
|
||||
"notification.reblog": "{name} reblogolta az állapotod",
|
||||
"notification.mention": "{name} megemlített"
|
||||
};
|
||||
|
||||
export default hu;
|
@@ -1,55 +0,0 @@
|
||||
import ar from './ar';
|
||||
import en from './en';
|
||||
import de from './de';
|
||||
import es from './es';
|
||||
import fa from './fa';
|
||||
import hr from './hr';
|
||||
import hu from './hu';
|
||||
import io from './io';
|
||||
import it from './it';
|
||||
import fr from './fr';
|
||||
import nl from './nl';
|
||||
import no from './no';
|
||||
import oc from './oc';
|
||||
import pt from './pt';
|
||||
import pt_br from './pt-br';
|
||||
import uk from './uk';
|
||||
import fi from './fi';
|
||||
import eo from './eo';
|
||||
import ru from './ru';
|
||||
import ja from './ja';
|
||||
import zh_hk from './zh-hk';
|
||||
import zh_cn from './zh-cn';
|
||||
import bg from './bg';
|
||||
import id from './id';
|
||||
|
||||
const locales = {
|
||||
ar,
|
||||
en,
|
||||
de,
|
||||
es,
|
||||
fa,
|
||||
hr,
|
||||
hu,
|
||||
io,
|
||||
it,
|
||||
fr,
|
||||
nl,
|
||||
no,
|
||||
oc,
|
||||
pt,
|
||||
'pt-BR': pt_br,
|
||||
uk,
|
||||
fi,
|
||||
eo,
|
||||
ru,
|
||||
ja,
|
||||
'zh-HK': zh_hk,
|
||||
'zh-CN': zh_cn,
|
||||
bg,
|
||||
id,
|
||||
};
|
||||
|
||||
export default function getMessagesForLocale (locale) {
|
||||
return locales[locale];
|
||||
};
|
@@ -1,57 +0,0 @@
|
||||
const uk = {
|
||||
"column_back_button.label": "Назад",
|
||||
"lightbox.close": "Закрити",
|
||||
"loading_indicator.label": "Завантаження...",
|
||||
"status.mention": "Згадати",
|
||||
"status.delete": "Видалити",
|
||||
"status.reply": "Відповісти",
|
||||
"status.reblog": "Передмухнути",
|
||||
"status.favourite": "Подобається",
|
||||
"status.reblogged_by": "{name} передмухнув(-ла)",
|
||||
"status.sensitive_warning": "Непристойний зміст",
|
||||
"status.sensitive_toggle": "Натисніть, щоб подивитися",
|
||||
"video_player.toggle_sound": "Увімкнути/вимкнути звук",
|
||||
"account.mention": "Згадати",
|
||||
"account.edit_profile": "Налаштування профілю",
|
||||
"account.unblock": "Розблокувати",
|
||||
"account.unfollow": "Відписатися",
|
||||
"account.block": "Заблокувати",
|
||||
"account.follow": "Підписатися",
|
||||
"account.posts": "Пости",
|
||||
"account.follows": "Підписки",
|
||||
"account.followers": "Підписники",
|
||||
"account.follows_you": "Підписаний",
|
||||
"getting_started.heading": "Ласкаво просимо",
|
||||
"getting_started.about_addressing": "Ви можете підписуватись на людей, якщо ви знаєте їх ім'я користувача чи домен, шляхом введення email-подібної адреси у верхньому рядку бокової панелі.",
|
||||
"getting_started.about_shortcuts": "Якщо користувач, якого ви шукаєте, знаходиться на тому ж домені, що й ви, можна просто ввести ім'я користувача. Це правило стосується й згадування людей у статусах.",
|
||||
"getting_started.about_developer": "Розробник проекту знаходиться за адресою Gargron@mastodon.social",
|
||||
"column.home": "Головна",
|
||||
"column.mentions": "Згадування",
|
||||
"column.public": "Стіна",
|
||||
"column.notifications": "Сповіщення",
|
||||
"tabs_bar.compose": "Написати",
|
||||
"tabs_bar.home": "Головна",
|
||||
"tabs_bar.mentions": "Згадування",
|
||||
"tabs_bar.public": "Стіна",
|
||||
"tabs_bar.notifications": "Сповіщення",
|
||||
"compose_form.placeholder": "Що у Вас на думці?",
|
||||
"compose_form.publish": "Дмухнути",
|
||||
"compose_form.sensitive": "Непристойний зміст",
|
||||
"compose_form.unlisted": "Таємний режим",
|
||||
"navigation_bar.edit_profile": "Редагувати профіль",
|
||||
"navigation_bar.preferences": "Налаштування",
|
||||
"navigation_bar.public_timeline": "Публічна стіна",
|
||||
"navigation_bar.logout": "Вийти",
|
||||
"reply_indicator.cancel": "Відмінити",
|
||||
"search.placeholder": "Пошук",
|
||||
"search.account": "Аккаунт",
|
||||
"search.hashtag": "Хештеґ",
|
||||
"upload_button.label": "Додати медіа",
|
||||
"upload_form.undo": "Відмінити",
|
||||
"notification.follow": "{name} підписався(-лась) на Вас",
|
||||
"notification.favourite": "{name} сподобався ваш допис",
|
||||
"notification.reblog": "{name} передмухнув(-ла) Ваш статус",
|
||||
"notification.mention": "{name} згадав(-ла) Вас"
|
||||
};
|
||||
|
||||
export default uk;
|
@@ -1,22 +0,0 @@
|
||||
const play = audio => {
|
||||
if (!audio.paused) {
|
||||
audio.pause();
|
||||
audio.fastSeek(0);
|
||||
}
|
||||
|
||||
audio.play();
|
||||
};
|
||||
|
||||
export default function soundsMiddleware() {
|
||||
const soundCache = {
|
||||
boop: new Audio(['/sounds/boop.mp3'])
|
||||
};
|
||||
|
||||
return ({ dispatch }) => next => (action) => {
|
||||
if (action.meta && action.meta.sound && soundCache[action.meta.sound]) {
|
||||
play(soundCache[action.meta.sound]);
|
||||
}
|
||||
|
||||
return next(action);
|
||||
};
|
||||
};
|
@@ -1,49 +0,0 @@
|
||||
import emojify from './components/emoji';
|
||||
import { length } from 'stringz';
|
||||
|
||||
$(() => {
|
||||
$.each($('.emojify'), (_, content) => {
|
||||
const $content = $(content);
|
||||
$content.html(emojify($content.html()));
|
||||
});
|
||||
|
||||
$('.video-player video').on('click', e => {
|
||||
if (e.target.paused) {
|
||||
e.target.play();
|
||||
} else {
|
||||
e.target.pause();
|
||||
}
|
||||
});
|
||||
|
||||
$('.media-spoiler').on('click', e => {
|
||||
$(e.target).hide();
|
||||
});
|
||||
|
||||
$('.webapp-btn').on('click', e => {
|
||||
if (e.button === 0) {
|
||||
e.preventDefault();
|
||||
window.location.href = $(e.target).attr('href');
|
||||
}
|
||||
});
|
||||
|
||||
$('.status__content__spoiler-link').on('click', e => {
|
||||
e.preventDefault();
|
||||
const contentEl = $(e.target).parent().parent().find('div');
|
||||
|
||||
if (contentEl.is(':visible')) {
|
||||
contentEl.hide();
|
||||
$(e.target).parent().attr('style', 'margin-bottom: 0');
|
||||
} else {
|
||||
contentEl.show();
|
||||
$(e.target).parent().attr('style', null);
|
||||
}
|
||||
});
|
||||
|
||||
// used on /settings/profile
|
||||
$('.account_display_name').on('input', e => {
|
||||
$('.name-counter').text(30 - length($(e.target).val()));
|
||||
});
|
||||
$('.account_note').on('input', e => {
|
||||
$('.note-counter').text(160 - length($(e.target).val()));
|
||||
});
|
||||
});
|
File diff suppressed because one or more lines are too long
@@ -1,11 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Montserrat';
|
||||
src: local('Montserrat');
|
||||
src: font-url('montserrat/Montserrat-Regular.eot');
|
||||
src: font-url('montserrat/Montserrat-Regular.eot?#iefix') format('embedded-opentype'),
|
||||
font-url('montserrat/Montserrat-Regular.woff2') format('woff2'),
|
||||
font-url('montserrat/Montserrat-Regular.woff') format('woff'),
|
||||
font-url('montserrat/Montserrat-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Roboto Mono';
|
||||
src: local('Roboto Mono');
|
||||
src: font-url('roboto-mono/robotomono-regular-webfont.eot');
|
||||
src: font-url('roboto-mono/robotomono-regular-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
font-url('roboto-mono/robotomono-regular-webfont.woff2') format('woff2'),
|
||||
font-url('roboto-mono/robotomono-regular-webfont.woff') format('woff'),
|
||||
font-url('roboto-mono/robotomono-regular-webfont.ttf') format('truetype'),
|
||||
font-url('roboto-mono/robotomono-regular-webfont.svg#roboto_monoregular') format('svg');
|
||||
font-weight: 400;
|
||||
font-style: normal;
|
||||
}
|
@@ -1,52 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: local('Roboto');
|
||||
src: font-url('roboto/roboto-italic-webfont.eot');
|
||||
src: font-url('roboto/roboto-italic-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
font-url('roboto/roboto-italic-webfont.woff2') format('woff2'),
|
||||
font-url('roboto/roboto-italic-webfont.woff') format('woff'),
|
||||
font-url('roboto/roboto-italic-webfont.ttf') format('truetype'),
|
||||
font-url('roboto/roboto-italic-webfont.svg#roboto-italic-webfont') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: local('Roboto');
|
||||
src: font-url('roboto/roboto-bold-webfont.eot');
|
||||
src: local('Roboto bold'), local('roboto-bold'),
|
||||
font-url('roboto/roboto-bold-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
font-url('roboto/roboto-bold-webfont.woff2') format('woff2'),
|
||||
font-url('roboto/roboto-bold-webfont.woff') format('woff'),
|
||||
font-url('roboto/roboto-bold-webfont.ttf') format('truetype'),
|
||||
font-url('roboto/roboto-bold-webfont.svg#roboto-bold-webfont') format('svg');
|
||||
font-weight: bold;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: local('Roboto');
|
||||
src: font-url('roboto/roboto-medium-webfont.eot');
|
||||
src: font-url('roboto/roboto-medium-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
font-url('roboto/roboto-medium-webfont.woff2') format('woff2'),
|
||||
font-url('roboto/roboto-medium-webfont.woff') format('woff'),
|
||||
font-url('roboto/roboto-medium-webfont.ttf') format('truetype'),
|
||||
font-url('roboto/roboto-medium-webfont.svg#roboto-medium-webfont') format('svg');
|
||||
font-weight: 500;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'Roboto';
|
||||
src: local('Roboto');
|
||||
src: font-url('roboto/roboto-regular-webfont.eot');
|
||||
src: font-url('roboto/roboto-regular-webfont.eot?#iefix') format('embedded-opentype'),
|
||||
font-url('roboto/roboto-regular-webfont.woff2') format('woff2'),
|
||||
font-url('roboto/roboto-regular-webfont.woff') format('woff'),
|
||||
font-url('roboto/roboto-regular-webfont.ttf') format('truetype'),
|
||||
font-url('roboto/roboto-regular-webfont.svg#roboto-regular-webfont') format('svg');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
@@ -1,8 +0,0 @@
|
||||
$color1: #282c37 !default; // darkest
|
||||
$color2: #d9e1e8 !default; // lightest
|
||||
$color3: #9baec8 !default; // lighter
|
||||
$color4: #2b90d9 !default; // vibrant
|
||||
$color5: #ffffff !default; // white
|
||||
$color6: #df405a !default; // error red
|
||||
$color7: #79bd9a !default; // succ green
|
||||
$color8: #000000 !default; // black
|
@@ -6,12 +6,12 @@ class AccountsController < ApplicationController
|
||||
def show
|
||||
respond_to do |format|
|
||||
format.html do
|
||||
@statuses = @account.statuses.permitted_for(@account, current_account).order('id desc').paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@statuses = @account.statuses.permitted_for(@account, current_account).order(id: :desc).paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@statuses = cache_collection(@statuses, Status)
|
||||
end
|
||||
|
||||
format.atom do
|
||||
@entries = @account.stream_entries.order('id desc').where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
@entries = @account.stream_entries.order(id: :desc).where(hidden: false).with_includes.paginate_by_max_id(20, params[:max_id], params[:since_id])
|
||||
render xml: AtomSerializer.render(AtomSerializer.new.feed(@account, @entries.to_a))
|
||||
end
|
||||
|
||||
|
@@ -23,7 +23,11 @@ module Admin
|
||||
:by_domain,
|
||||
:silenced,
|
||||
:recent,
|
||||
:suspended
|
||||
:suspended,
|
||||
:username,
|
||||
:display_name,
|
||||
:email,
|
||||
:ip
|
||||
)
|
||||
end
|
||||
end
|
||||
|
@@ -2,17 +2,15 @@
|
||||
|
||||
module Admin
|
||||
class ConfirmationsController < BaseController
|
||||
before_action :set_account
|
||||
|
||||
def create
|
||||
@account.user.confirm
|
||||
account_user.confirm
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_account
|
||||
@account = Account.find(params[:account_id])
|
||||
def account_user
|
||||
Account.find(params[:account_id]).user || raise(ActiveRecord::RecordNotFound)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -2,8 +2,10 @@
|
||||
|
||||
module Admin
|
||||
class DomainBlocksController < BaseController
|
||||
before_action :set_domain_block, only: [:show, :destroy]
|
||||
|
||||
def index
|
||||
@blocks = DomainBlock.page(params[:page])
|
||||
@domain_blocks = DomainBlock.page(params[:page])
|
||||
end
|
||||
|
||||
def new
|
||||
@@ -21,20 +23,25 @@ module Admin
|
||||
end
|
||||
end
|
||||
|
||||
def show
|
||||
@domain_block = DomainBlock.find(params[:id])
|
||||
end
|
||||
def show; end
|
||||
|
||||
def destroy
|
||||
@domain_block = DomainBlock.find(params[:id])
|
||||
UnblockDomainService.new.call(@domain_block, resource_params[:retroactive])
|
||||
UnblockDomainService.new.call(@domain_block, retroactive_unblock?)
|
||||
redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_blocks.destroyed_msg')
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_domain_block
|
||||
@domain_block = DomainBlock.find(params[:id])
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive)
|
||||
end
|
||||
|
||||
def retroactive_unblock?
|
||||
ActiveRecord::Type.lookup(:boolean).cast(resource_params[:retroactive])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -3,7 +3,7 @@
|
||||
module Admin
|
||||
class PubsubhubbubController < BaseController
|
||||
def index
|
||||
@subscriptions = Subscription.order('id desc').includes(:account).page(params[:page])
|
||||
@subscriptions = Subscription.order(id: :desc).includes(:account).page(params[:page])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -49,7 +49,7 @@ module Admin
|
||||
end
|
||||
|
||||
def filtered_reports
|
||||
ReportFilter.new(filter_params).results.order('id desc').includes(
|
||||
ReportFilter.new(filter_params).results.order(id: :desc).includes(
|
||||
:account,
|
||||
:target_account
|
||||
)
|
||||
|
@@ -2,38 +2,43 @@
|
||||
|
||||
module Admin
|
||||
class SettingsController < BaseController
|
||||
ADMIN_SETTINGS = %w(
|
||||
site_contact_username
|
||||
site_contact_email
|
||||
site_title
|
||||
site_description
|
||||
site_extended_description
|
||||
open_registrations
|
||||
closed_registrations_message
|
||||
).freeze
|
||||
BOOLEAN_SETTINGS = %w(open_registrations).freeze
|
||||
|
||||
def index
|
||||
def edit
|
||||
@settings = Setting.all_as_records
|
||||
end
|
||||
|
||||
def update
|
||||
@setting = Setting.where(var: params[:id]).first_or_initialize(var: params[:id])
|
||||
@setting.update(value: value_for_update)
|
||||
|
||||
respond_to do |format|
|
||||
format.html { redirect_to admin_settings_path }
|
||||
format.json { respond_with_bip(@setting) }
|
||||
settings_params.each do |key, value|
|
||||
setting = Setting.where(var: key).first_or_initialize(var: key)
|
||||
setting.update(value: value_for_update(key, value))
|
||||
end
|
||||
|
||||
flash[:notice] = 'Success!'
|
||||
redirect_to edit_admin_settings_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def settings_params
|
||||
params.require(:setting).permit(:value)
|
||||
params.permit(ADMIN_SETTINGS)
|
||||
end
|
||||
|
||||
def value_for_update
|
||||
if updating_boolean_setting?
|
||||
settings_params[:value] == 'true'
|
||||
def value_for_update(key, value)
|
||||
if BOOLEAN_SETTINGS.include?(key)
|
||||
value == 'true'
|
||||
else
|
||||
settings_params[:value]
|
||||
value
|
||||
end
|
||||
end
|
||||
|
||||
def updating_boolean_setting?
|
||||
BOOLEAN_SETTINGS.include?(params[:id])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -0,0 +1,18 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin
|
||||
class TwoFactorAuthenticationsController < BaseController
|
||||
before_action :set_user
|
||||
|
||||
def destroy
|
||||
@user.disable_two_factor!
|
||||
redirect_to admin_accounts_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_user
|
||||
@user = User.find(params[:user_id])
|
||||
end
|
||||
end
|
||||
end
|
@@ -6,7 +6,7 @@ class Api::OEmbedController < ApiController
|
||||
def show
|
||||
@stream_entry = stream_entry_from_url(params[:url])
|
||||
@width = params[:maxwidth].present? ? params[:maxwidth].to_i : 400
|
||||
@height = params[:maxheight].present? ? params[:maxheight].to_i : 600
|
||||
@height = params[:maxheight].present? ? params[:maxheight].to_i : nil
|
||||
end
|
||||
|
||||
private
|
||||
|
@@ -2,36 +2,66 @@
|
||||
|
||||
class Api::PushController < ApiController
|
||||
def update
|
||||
mode = params['hub.mode']
|
||||
topic = params['hub.topic']
|
||||
callback = params['hub.callback']
|
||||
lease_seconds = params['hub.lease_seconds']
|
||||
secret = params['hub.secret']
|
||||
|
||||
case mode
|
||||
when 'subscribe'
|
||||
response, status = Pubsubhubbub::SubscribeService.new.call(topic_to_account(topic), callback, secret, lease_seconds)
|
||||
when 'unsubscribe'
|
||||
response, status = Pubsubhubbub::UnsubscribeService.new.call(topic_to_account(topic), callback)
|
||||
else
|
||||
response = "Unknown mode: #{mode}"
|
||||
status = 422
|
||||
end
|
||||
|
||||
response, status = process_push_request
|
||||
render plain: response, status: status
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def topic_to_account(topic_url)
|
||||
return if topic_url.blank?
|
||||
def process_push_request
|
||||
case hub_mode
|
||||
when 'subscribe'
|
||||
Pubsubhubbub::SubscribeService.new.call(account_from_topic, hub_callback, hub_secret, hub_lease_seconds)
|
||||
when 'unsubscribe'
|
||||
Pubsubhubbub::UnsubscribeService.new.call(account_from_topic, hub_callback)
|
||||
else
|
||||
["Unknown mode: #{hub_mode}", 422]
|
||||
end
|
||||
end
|
||||
|
||||
uri = Addressable::URI.parse(topic_url).normalize
|
||||
params = Rails.application.routes.recognize_path(uri.path)
|
||||
domain = uri.host + (uri.port ? ":#{uri.port}" : '')
|
||||
def hub_mode
|
||||
params['hub.mode']
|
||||
end
|
||||
|
||||
return unless TagManager.instance.web_domain?(domain) && params[:controller] == 'accounts' && params[:action] == 'show' && params[:format] == 'atom'
|
||||
def hub_topic
|
||||
params['hub.topic']
|
||||
end
|
||||
|
||||
Account.find_local(params[:username])
|
||||
def hub_callback
|
||||
params['hub.callback']
|
||||
end
|
||||
|
||||
def hub_lease_seconds
|
||||
params['hub.lease_seconds']
|
||||
end
|
||||
|
||||
def hub_secret
|
||||
params['hub.secret']
|
||||
end
|
||||
|
||||
def account_from_topic
|
||||
if hub_topic.present? && local_domain? && account_feed_path?
|
||||
Account.find_local(hub_topic_params[:username])
|
||||
end
|
||||
end
|
||||
|
||||
def hub_topic_params
|
||||
@_hub_topic_params ||= Rails.application.routes.recognize_path(hub_topic_uri.path)
|
||||
end
|
||||
|
||||
def hub_topic_uri
|
||||
@_hub_topic_uri ||= Addressable::URI.parse(hub_topic).normalize
|
||||
end
|
||||
|
||||
def local_domain?
|
||||
TagManager.instance.web_domain?(hub_topic_domain)
|
||||
end
|
||||
|
||||
def hub_topic_domain
|
||||
hub_topic_uri.host + (hub_topic_uri.port ? ":#{hub_topic_uri.port}" : '')
|
||||
end
|
||||
|
||||
def account_feed_path?
|
||||
hub_topic_params[:controller] == 'accounts' && hub_topic_params[:action] == 'show' && hub_topic_params[:format] == 'atom'
|
||||
end
|
||||
end
|
||||
|
@@ -5,13 +5,13 @@ class Api::SalmonController < ApiController
|
||||
respond_to :txt
|
||||
|
||||
def update
|
||||
body = request.body.read
|
||||
payload = request.body.read
|
||||
|
||||
if body.nil?
|
||||
head 200
|
||||
else
|
||||
SalmonWorker.perform_async(@account.id, body.force_encoding('UTF-8'))
|
||||
if !payload.nil? && verify?(payload)
|
||||
SalmonWorker.perform_async(@account.id, payload.force_encoding('UTF-8'))
|
||||
head 201
|
||||
else
|
||||
head 202
|
||||
end
|
||||
end
|
||||
|
||||
@@ -20,4 +20,8 @@ class Api::SalmonController < ApiController
|
||||
def set_account
|
||||
@account = Account.find(params[:id])
|
||||
end
|
||||
|
||||
def verify?(payload)
|
||||
VerifySalmonService.new.call(payload)
|
||||
end
|
||||
end
|
||||
|
@@ -19,10 +19,9 @@ class Api::SubscriptionsController < ApiController
|
||||
|
||||
if subscription.verify(body, request.headers['HTTP_X_HUB_SIGNATURE'])
|
||||
ProcessingWorker.perform_async(@account.id, body.force_encoding('UTF-8'))
|
||||
head 201
|
||||
else
|
||||
head 202
|
||||
end
|
||||
|
||||
head 200
|
||||
end
|
||||
|
||||
private
|
||||
|
@@ -23,12 +23,14 @@ class Api::V1::AccountsController < ApiController
|
||||
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] }
|
||||
@accounts = Account.includes(:followers)
|
||||
.references(:followers)
|
||||
.merge(Follow.where(account: @account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
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?
|
||||
next_path = following_api_v1_account_url(pagination_params(max_id: @accounts.last.followers.last.id)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = following_api_v1_account_url(pagination_params(since_id: @accounts.first.followers.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
|
||||
@@ -36,12 +38,16 @@ class Api::V1::AccountsController < ApiController
|
||||
end
|
||||
|
||||
def followers
|
||||
results = Follow.where(target_account: @account).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
|
||||
@accounts = results.map { |f| accounts[f.account_id] }
|
||||
@accounts = Account.includes(:following)
|
||||
.references(:following)
|
||||
.merge(Follow.where(target_account: @account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT),
|
||||
params[:max_id],
|
||||
params[:since_id]))
|
||||
.to_a
|
||||
|
||||
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?
|
||||
next_path = followers_api_v1_account_url(pagination_params(max_id: @accounts.last.following.last.id)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = followers_api_v1_account_url(pagination_params(since_id: @accounts.first.following.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
|
||||
@@ -71,11 +77,12 @@ class Api::V1::AccountsController < ApiController
|
||||
def block
|
||||
BlockService.new.call(current_user.account, @account)
|
||||
|
||||
@following = { @account.id => false }
|
||||
@followed_by = { @account.id => false }
|
||||
@blocking = { @account.id => true }
|
||||
@requested = { @account.id => false }
|
||||
@muting = { @account.id => current_user.account.muting?(@account.id) }
|
||||
@following = { @account.id => false }
|
||||
@followed_by = { @account.id => false }
|
||||
@blocking = { @account.id => true }
|
||||
@requested = { @account.id => false }
|
||||
@muting = { @account.id => current_account.muting?(@account.id) }
|
||||
@domain_blocking = { @account.id => current_account.domain_blocking?(@account.domain) }
|
||||
|
||||
render :relationship
|
||||
end
|
||||
@@ -107,12 +114,13 @@ class Api::V1::AccountsController < ApiController
|
||||
def relationships
|
||||
ids = params[:id].is_a?(Enumerable) ? params[:id].map(&:to_i) : [params[:id].to_i]
|
||||
|
||||
@accounts = Account.where(id: ids).select('id')
|
||||
@following = Account.following_map(ids, current_user.account_id)
|
||||
@followed_by = Account.followed_by_map(ids, current_user.account_id)
|
||||
@blocking = Account.blocking_map(ids, current_user.account_id)
|
||||
@muting = Account.muting_map(ids, current_user.account_id)
|
||||
@requested = Account.requested_map(ids, current_user.account_id)
|
||||
@accounts = Account.where(id: ids).select('id')
|
||||
@following = Account.following_map(ids, current_user.account_id)
|
||||
@followed_by = Account.followed_by_map(ids, current_user.account_id)
|
||||
@blocking = Account.blocking_map(ids, current_user.account_id)
|
||||
@muting = Account.muting_map(ids, current_user.account_id)
|
||||
@requested = Account.requested_map(ids, current_user.account_id)
|
||||
@domain_blocking = Account.domain_blocking_map(ids, current_user.account_id)
|
||||
end
|
||||
|
||||
def search
|
||||
@@ -128,11 +136,12 @@ class Api::V1::AccountsController < ApiController
|
||||
end
|
||||
|
||||
def set_relationship
|
||||
@following = Account.following_map([@account.id], current_user.account_id)
|
||||
@followed_by = Account.followed_by_map([@account.id], current_user.account_id)
|
||||
@blocking = Account.blocking_map([@account.id], current_user.account_id)
|
||||
@muting = Account.muting_map([@account.id], current_user.account_id)
|
||||
@requested = Account.requested_map([@account.id], current_user.account_id)
|
||||
@following = Account.following_map([@account.id], current_user.account_id)
|
||||
@followed_by = Account.followed_by_map([@account.id], current_user.account_id)
|
||||
@blocking = Account.blocking_map([@account.id], current_user.account_id)
|
||||
@muting = Account.muting_map([@account.id], current_user.account_id)
|
||||
@requested = Account.requested_map([@account.id], current_user.account_id)
|
||||
@domain_blocking = Account.domain_blocking_map([@account.id], current_user.account_id)
|
||||
end
|
||||
|
||||
def pagination_params(core_params)
|
||||
|
@@ -7,12 +7,14 @@ class Api::V1::BlocksController < ApiController
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
results = Block.where(account: current_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] }.compact
|
||||
@accounts = Account.includes(:blocked_by)
|
||||
.references(:blocked_by)
|
||||
.merge(Block.where(account: current_account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
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?
|
||||
next_path = api_v1_blocks_url(pagination_params(max_id: @accounts.last.blocked_by_ids.last)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = api_v1_blocks_url(pagination_params(since_id: @accounts.first.blocked_by_ids.first)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
38
app/controllers/api/v1/domain_blocks_controller.rb
Normal file
38
app/controllers/api/v1/domain_blocks_controller.rb
Normal file
@@ -0,0 +1,38 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::DomainBlocksController < ApiController
|
||||
before_action -> { doorkeeper_authorize! :follow }
|
||||
before_action :require_user!
|
||||
|
||||
respond_to :json
|
||||
|
||||
def show
|
||||
@blocks = AccountDomainBlock.where(account: current_account).paginate_by_max_id(limit_param(100), params[:max_id], params[:since_id])
|
||||
|
||||
next_path = api_v1_domain_blocks_url(pagination_params(max_id: @blocks.last.id)) if @blocks.size == limit_param(100)
|
||||
prev_path = api_v1_domain_blocks_url(pagination_params(since_id: @blocks.first.id)) unless @blocks.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
render json: @blocks.map(&:domain)
|
||||
end
|
||||
|
||||
def create
|
||||
BlockDomainFromAccountService.new.call(current_account, domain_block_params[:domain])
|
||||
render_empty
|
||||
end
|
||||
|
||||
def destroy
|
||||
current_account.unblock_domain!(domain_block_params[:domain])
|
||||
render_empty
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def pagination_params(core_params)
|
||||
params.permit(:limit).merge(core_params)
|
||||
end
|
||||
|
||||
def domain_block_params
|
||||
params.permit(:domain)
|
||||
end
|
||||
end
|
@@ -5,12 +5,14 @@ class Api::V1::FollowRequestsController < ApiController
|
||||
before_action :require_user!
|
||||
|
||||
def index
|
||||
results = FollowRequest.where(target_account: current_account).paginate_by_max_id(DEFAULT_ACCOUNTS_LIMIT, params[:max_id], params[:since_id])
|
||||
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
|
||||
@accounts = results.map { |f| accounts[f.account_id] }
|
||||
@accounts = Account.includes(:follow_requests)
|
||||
.references(:follow_requests)
|
||||
.merge(FollowRequest.where(target_account: current_account)
|
||||
.paginate_by_max_id(DEFAULT_ACCOUNTS_LIMIT, params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
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?
|
||||
next_path = api_v1_follow_requests_url(pagination_params(max_id: @accounts.last.follow_requests.last.id)) if @accounts.size == DEFAULT_ACCOUNTS_LIMIT
|
||||
prev_path = api_v1_follow_requests_url(pagination_params(since_id: @accounts.first.follow_requests.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
@@ -7,12 +7,14 @@ class Api::V1::MutesController < ApiController
|
||||
respond_to :json
|
||||
|
||||
def index
|
||||
results = Mute.where(account: current_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] }
|
||||
@accounts = Account.includes(:muting)
|
||||
.references(:muting)
|
||||
.merge(Mute.where(account: current_account)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
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?
|
||||
next_path = api_v1_mutes_url(pagination_params(max_id: @accounts.last.mutings_accounts.last.id)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = api_v1_mutes_url(pagination_params(since_id: @accounts.first.mutings_accounts.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
end
|
||||
|
@@ -1,10 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Api::V1::StatusesController < ApiController
|
||||
before_action :authorize_if_got_token, except: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite]
|
||||
before_action -> { doorkeeper_authorize! :write }, only: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite]
|
||||
before_action :require_user!, except: [:show, :context, :card, :reblogged_by, :favourited_by]
|
||||
before_action :set_status, only: [:show, :context, :card, :reblogged_by, :favourited_by]
|
||||
before_action :authorize_if_got_token, except: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite, :mute, :unmute]
|
||||
before_action -> { doorkeeper_authorize! :write }, only: [:create, :destroy, :reblog, :unreblog, :favourite, :unfavourite, :mute, :unmute]
|
||||
before_action :require_user!, except: [:show, :context, :card, :reblogged_by, :favourited_by]
|
||||
before_action :set_status, only: [:show, :context, :card, :reblogged_by, :favourited_by, :mute, :unmute]
|
||||
before_action :set_conversation, only: [:mute, :unmute]
|
||||
|
||||
respond_to :json
|
||||
|
||||
@@ -31,12 +32,14 @@ class Api::V1::StatusesController < ApiController
|
||||
end
|
||||
|
||||
def reblogged_by
|
||||
results = @status.reblogs.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
|
||||
@accounts = results.map { |r| accounts[r.account_id] }
|
||||
@accounts = Account.includes(:statuses)
|
||||
.references(:statuses)
|
||||
.merge(Status.where(reblog_of_id: @status.id)
|
||||
.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
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?
|
||||
next_path = reblogged_by_api_v1_status_url(pagination_params(max_id: @accounts.last.statuses.last.id)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = reblogged_by_api_v1_status_url(pagination_params(since_id: @accounts.first.statuses.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
|
||||
@@ -44,12 +47,14 @@ class Api::V1::StatusesController < ApiController
|
||||
end
|
||||
|
||||
def favourited_by
|
||||
results = @status.favourites.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
accounts = Account.where(id: results.map(&:account_id)).map { |a| [a.id, a] }.to_h
|
||||
@accounts = results.map { |f| accounts[f.account_id] }
|
||||
@accounts = Account.includes(statuses: :favourites)
|
||||
.references(statuses: :favourites)
|
||||
.where(statuses: { id: @status.id })
|
||||
.merge(@status.favourites.paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id]))
|
||||
.to_a
|
||||
|
||||
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?
|
||||
next_path = favourited_by_api_v1_status_url(pagination_params(max_id: @accounts.last.statuses.last.favourites.last.id)) if @accounts.size == limit_param(DEFAULT_ACCOUNTS_LIMIT)
|
||||
prev_path = favourited_by_api_v1_status_url(pagination_params(since_id: @accounts.first.statuses.first.favourites.first.id)) unless @accounts.empty?
|
||||
|
||||
set_pagination_headers(next_path, prev_path)
|
||||
|
||||
@@ -105,6 +110,22 @@ class Api::V1::StatusesController < ApiController
|
||||
render :show
|
||||
end
|
||||
|
||||
def mute
|
||||
current_account.mute_conversation!(@conversation)
|
||||
|
||||
@mutes_map = { @conversation.id => true }
|
||||
|
||||
render :show
|
||||
end
|
||||
|
||||
def unmute
|
||||
current_account.unmute_conversation!(@conversation)
|
||||
|
||||
@mutes_map = { @conversation.id => false }
|
||||
|
||||
render :show
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_status
|
||||
@@ -112,6 +133,11 @@ class Api::V1::StatusesController < ApiController
|
||||
raise ActiveRecord::RecordNotFound unless @status.permitted?(current_account)
|
||||
end
|
||||
|
||||
def set_conversation
|
||||
@conversation = @status.conversation
|
||||
raise Mastodon::ValidationError if @conversation.nil?
|
||||
end
|
||||
|
||||
def status_params
|
||||
params.permit(:status, :in_reply_to_id, :sensitive, :spoiler_text, :visibility, media_ids: [])
|
||||
end
|
||||
|
@@ -93,11 +93,14 @@ class ApiController < ApplicationController
|
||||
if current_account.nil?
|
||||
@reblogs_map = {}
|
||||
@favourites_map = {}
|
||||
@mutes_map = {}
|
||||
return
|
||||
end
|
||||
|
||||
status_ids = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq
|
||||
@reblogs_map = Status.reblogs_map(status_ids, current_account)
|
||||
@favourites_map = Status.favourites_map(status_ids, current_account)
|
||||
status_ids = statuses.compact.flat_map { |s| [s.id, s.reblog_of_id] }.uniq
|
||||
conversation_ids = statuses.compact.map(&:conversation_id).compact.uniq
|
||||
@reblogs_map = Status.reblogs_map(status_ids, current_account)
|
||||
@favourites_map = Status.favourites_map(status_ids, current_account)
|
||||
@mutes_map = Status.mutes_map(conversation_ids, current_account)
|
||||
end
|
||||
end
|
||||
|
@@ -8,6 +8,7 @@ class ApplicationController < ActionController::Base
|
||||
force_ssl if: :https_enabled?
|
||||
|
||||
include Localized
|
||||
include UserTrackingConcern
|
||||
|
||||
helper_method :current_account
|
||||
helper_method :single_user_mode?
|
||||
@@ -17,7 +18,6 @@ class ApplicationController < ActionController::Base
|
||||
rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity
|
||||
|
||||
before_action :store_current_location, except: :raise_not_found, unless: :devise_controller?
|
||||
before_action :set_user_activity
|
||||
before_action :check_suspension, if: :user_signed_in?
|
||||
|
||||
def raise_not_found
|
||||
@@ -38,48 +38,26 @@ class ApplicationController < ActionController::Base
|
||||
redirect_to root_path unless current_user&.admin?
|
||||
end
|
||||
|
||||
def set_user_activity
|
||||
return unless !current_user.nil? && (current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < 24.hours.ago)
|
||||
|
||||
# Mark user as signed-in today
|
||||
current_user.update_tracked_fields(request)
|
||||
|
||||
# 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
|
||||
end
|
||||
|
||||
def check_suspension
|
||||
head 403 if current_user.account.suspended?
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def forbidden
|
||||
respond_with_error(403)
|
||||
end
|
||||
|
||||
def not_found
|
||||
respond_to do |format|
|
||||
format.any { head 404 }
|
||||
format.html { respond_with_error(404) }
|
||||
end
|
||||
respond_with_error(404)
|
||||
end
|
||||
|
||||
def gone
|
||||
respond_to do |format|
|
||||
format.any { head 410 }
|
||||
format.html { respond_with_error(410) }
|
||||
end
|
||||
end
|
||||
|
||||
def forbidden
|
||||
respond_to do |format|
|
||||
format.any { head 403 }
|
||||
format.html { render 'errors/403', layout: 'error', status: 403 }
|
||||
end
|
||||
respond_with_error(410)
|
||||
end
|
||||
|
||||
def unprocessable_entity
|
||||
respond_to do |format|
|
||||
format.any { head 422 }
|
||||
format.html { respond_with_error(422) }
|
||||
end
|
||||
respond_with_error(422)
|
||||
end
|
||||
|
||||
def single_user_mode?
|
||||
@@ -115,7 +93,12 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
|
||||
def respond_with_error(code)
|
||||
set_locale
|
||||
render "errors/#{code}", layout: 'error', status: code
|
||||
respond_to do |format|
|
||||
format.any { head code }
|
||||
format.html do
|
||||
set_locale
|
||||
render "errors/#{code}", layout: 'error', status: code
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@@ -6,6 +6,7 @@ class Auth::SessionsController < Devise::SessionsController
|
||||
layout 'auth'
|
||||
|
||||
skip_before_action :require_no_authentication, only: [:create]
|
||||
skip_before_action :check_suspension, only: [:destroy]
|
||||
prepend_before_action :authenticate_with_two_factor, if: :two_factor_enabled?, only: [:create]
|
||||
|
||||
def create
|
||||
@@ -51,7 +52,7 @@ class Auth::SessionsController < Devise::SessionsController
|
||||
def valid_otp_attempt?(user)
|
||||
user.validate_and_consume_otp!(user_params[:otp_attempt]) ||
|
||||
user.invalidate_otp_backup_code!(user_params[:otp_attempt])
|
||||
rescue OpenSSL::Cipher::CipherError => error
|
||||
rescue OpenSSL::Cipher::CipherError => _error
|
||||
false
|
||||
end
|
||||
|
||||
|
@@ -44,7 +44,7 @@ class AuthorizeFollowsController < ApplicationController
|
||||
end
|
||||
|
||||
def acct_param_is_url?
|
||||
parsed_uri.path && %w[http https].include?(parsed_uri.scheme)
|
||||
parsed_uri.path && %w(http https).include?(parsed_uri.scheme)
|
||||
end
|
||||
|
||||
def parsed_uri
|
||||
|
@@ -17,9 +17,9 @@ module Localized
|
||||
end
|
||||
|
||||
def default_locale
|
||||
ENV.fetch('DEFAULT_LOCALE') {
|
||||
ENV.fetch('DEFAULT_LOCALE') do
|
||||
user_supplied_locale || I18n.default_locale
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def user_supplied_locale
|
||||
|
30
app/controllers/concerns/user_tracking_concern.rb
Normal file
30
app/controllers/concerns/user_tracking_concern.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module UserTrackingConcern
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
REGENERATE_FEED_DAYS = 14
|
||||
UPDATE_SIGN_IN_HOURS = 24
|
||||
|
||||
included do
|
||||
before_action :set_user_activity, if: %i(user_signed_in? user_needs_sign_in_update?)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def set_user_activity
|
||||
# Mark as signed-in today
|
||||
current_user.update_tracked_fields!(request)
|
||||
|
||||
# Regenerate feed if needed
|
||||
RegenerationWorker.perform_async(current_user.account_id) if user_needs_feed_update?
|
||||
end
|
||||
|
||||
def user_needs_sign_in_update?
|
||||
current_user.current_sign_in_at.nil? || current_user.current_sign_in_at < UPDATE_SIGN_IN_HOURS.hours.ago
|
||||
end
|
||||
|
||||
def user_needs_feed_update?
|
||||
current_user.last_sign_in_at < REGENERATE_FEED_DAYS.days.ago
|
||||
end
|
||||
end
|
@@ -4,6 +4,6 @@ class FollowerAccountsController < ApplicationController
|
||||
include AccountControllerConcern
|
||||
|
||||
def index
|
||||
@accounts = @account.followers.page(params[:page]).per(FOLLOW_PER_PAGE)
|
||||
@follows = Follow.where(target_account: @account).order(id: :desc).page(params[:page]).per(FOLLOW_PER_PAGE).preload(:account)
|
||||
end
|
||||
end
|
||||
|
@@ -4,6 +4,6 @@ class FollowingAccountsController < ApplicationController
|
||||
include AccountControllerConcern
|
||||
|
||||
def index
|
||||
@accounts = @account.following.page(params[:page]).per(FOLLOW_PER_PAGE)
|
||||
@follows = Follow.where(account: @account).order(id: :desc).page(params[:page]).per(FOLLOW_PER_PAGE).preload(:target_account)
|
||||
end
|
||||
end
|
||||
|
@@ -4,34 +4,21 @@ class RemoteFollowController < ApplicationController
|
||||
layout 'public'
|
||||
|
||||
before_action :set_account
|
||||
before_action :check_account_suspension
|
||||
before_action :gone, if: :suspended_account?
|
||||
|
||||
def new
|
||||
@remote_follow = RemoteFollow.new
|
||||
@remote_follow.acct = session[:remote_follow] if session.key?(:remote_follow)
|
||||
@remote_follow = RemoteFollow.new(session_params)
|
||||
end
|
||||
|
||||
def create
|
||||
@remote_follow = RemoteFollow.new(resource_params)
|
||||
|
||||
if @remote_follow.valid?
|
||||
resource = Goldfinger.finger("acct:#{@remote_follow.acct}")
|
||||
redirect_url_link = resource&.link('http://ostatus.org/schema/1.0/subscribe')
|
||||
|
||||
if redirect_url_link.nil? || redirect_url_link.template.nil?
|
||||
@remote_follow.errors.add(:acct, I18n.t('remote_follow.missing_resource'))
|
||||
render(:new) && return
|
||||
end
|
||||
|
||||
session[:remote_follow] = @remote_follow.acct
|
||||
|
||||
redirect_to Addressable::Template.new(redirect_url_link.template).expand(uri: @account.to_webfinger_s).to_s
|
||||
redirect_to @remote_follow.subscribe_address_for(@account)
|
||||
else
|
||||
render :new
|
||||
end
|
||||
rescue Goldfinger::Error
|
||||
@remote_follow.errors.add(:acct, I18n.t('remote_follow.missing_resource'))
|
||||
render :new
|
||||
end
|
||||
|
||||
private
|
||||
@@ -40,11 +27,15 @@ class RemoteFollowController < ApplicationController
|
||||
params.require(:remote_follow).permit(:acct)
|
||||
end
|
||||
|
||||
def session_params
|
||||
{ acct: session[:remote_follow] }
|
||||
end
|
||||
|
||||
def set_account
|
||||
@account = Account.find_local!(params[:account_username])
|
||||
end
|
||||
|
||||
def check_account_suspension
|
||||
head 410 if @account.suspended?
|
||||
def suspended_account?
|
||||
@account.suspended?
|
||||
end
|
||||
end
|
||||
|
@@ -25,7 +25,8 @@ class Settings::PreferencesController < ApplicationController
|
||||
|
||||
def user_params
|
||||
params.require(:user).permit(
|
||||
:locale
|
||||
:locale,
|
||||
filtered_languages: []
|
||||
)
|
||||
end
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
# frozen_string_literal: true
|
||||
# frozen_string_literal: true
|
||||
|
||||
module WellKnown
|
||||
class HostMetaController < ApplicationController
|
||||
|
@@ -1,8 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Admin::FilterHelper
|
||||
ACCOUNT_FILTERS = %i[local remote by_domain silenced suspended recent].freeze
|
||||
REPORT_FILTERS = %i[resolved account_id target_account_id].freeze
|
||||
ACCOUNT_FILTERS = %i(local remote by_domain silenced suspended recent username display_name email ip).freeze
|
||||
REPORT_FILTERS = %i(resolved account_id target_account_id).freeze
|
||||
|
||||
FILTERS = ACCOUNT_FILTERS + REPORT_FILTERS
|
||||
|
||||
@@ -18,14 +18,18 @@ module Admin::FilterHelper
|
||||
private
|
||||
|
||||
def filter_params(more_params)
|
||||
params.permit(FILTERS).merge(more_params)
|
||||
controller_request_params.merge(more_params)
|
||||
end
|
||||
|
||||
def filter_link_class(new_url)
|
||||
filtered_url_for(params) == new_url ? 'selected' : ''
|
||||
filtered_url_for(controller_request_params) == new_url ? 'selected' : ''
|
||||
end
|
||||
|
||||
def filtered_url_for(params)
|
||||
url_for filter_params(params)
|
||||
def filtered_url_for(url_params)
|
||||
url_for filter_params(url_params)
|
||||
end
|
||||
|
||||
def controller_request_params
|
||||
params.permit(FILTERS)
|
||||
end
|
||||
end
|
||||
|
@@ -9,17 +9,25 @@ module ApplicationHelper
|
||||
!user_signed_in? && !single_user_mode?
|
||||
end
|
||||
|
||||
def open_registrations?
|
||||
Setting.open_registrations
|
||||
end
|
||||
|
||||
def add_rtl_body_class(other_classes)
|
||||
other_classes = "#{other_classes} rtl" if [:ar, :fa].include?(I18n.locale)
|
||||
other_classes = "#{other_classes} rtl" if [:ar, :fa, :he].include?(I18n.locale)
|
||||
other_classes
|
||||
end
|
||||
|
||||
def favicon_path
|
||||
env_suffix = Rails.env.production? ? '' : '-dev'
|
||||
asset_path "favicon#{env_suffix}.ico"
|
||||
"/favicon#{env_suffix}.ico"
|
||||
end
|
||||
|
||||
def title
|
||||
Rails.env.production? ? site_title : "#{site_title} (Dev)"
|
||||
end
|
||||
|
||||
def fa_icon(icon)
|
||||
content_tag(:i, nil, class: 'fa ' + icon.split(' ').map { |cl| "fa-#{cl}" }.join(' '))
|
||||
end
|
||||
end
|
||||
|
@@ -1,13 +1,17 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module HttpHelper
|
||||
USER_AGENT = "#{HTTP::Request::USER_AGENT} (Mastodon/#{Mastodon::Version}; +http://#{Rails.configuration.x.local_domain}/)"
|
||||
|
||||
def http_client(options = {})
|
||||
timeout = { write: 10, connect: 10, read: 10 }.merge(options)
|
||||
|
||||
HTTP.headers(user_agent: USER_AGENT)
|
||||
HTTP.headers(user_agent: user_agent)
|
||||
.timeout(:per_operation, timeout)
|
||||
.follow
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def user_agent
|
||||
@user_agent ||= "#{HTTP::Request::USER_AGENT} (Mastodon/#{Mastodon::Version}; +http://#{Rails.configuration.x.local_domain}/)"
|
||||
end
|
||||
end
|
||||
|
@@ -12,6 +12,6 @@ module RoutingHelper
|
||||
end
|
||||
|
||||
def full_asset_url(source)
|
||||
Rails.configuration.x.use_s3 ? source : File.join(root_url, ActionController::Base.helpers.asset_url(source))
|
||||
Rails.configuration.x.use_s3 ? source : URI.join(root_url, ActionController::Base.helpers.asset_url(source)).to_s
|
||||
end
|
||||
end
|
||||
|
@@ -5,12 +5,14 @@ module SettingsHelper
|
||||
en: 'English',
|
||||
ar: 'العربية',
|
||||
bg: 'Български',
|
||||
ca: 'Català',
|
||||
de: 'Deutsch',
|
||||
eo: 'Esperanto',
|
||||
es: 'Español',
|
||||
fa: 'فارسی',
|
||||
fi: 'Suomi',
|
||||
fr: 'Français',
|
||||
he: 'עברית',
|
||||
hr: 'Hrvatski',
|
||||
hu: 'Magyar',
|
||||
id: 'Bahasa Indonesia',
|
||||
@@ -24,6 +26,8 @@ module SettingsHelper
|
||||
pt: 'Português',
|
||||
'pt-BR': 'Português do Brasil',
|
||||
ru: 'Русский',
|
||||
th: 'ภาษาไทย',
|
||||
tr: 'Türkçe',
|
||||
uk: 'Українська',
|
||||
'zh-CN': '简体中文',
|
||||
'zh-HK': '繁體中文(香港)',
|
||||
|
@@ -1,19 +0,0 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module StyleHelper
|
||||
def stylesheet_for_layout
|
||||
if asset_exist? 'custom.css'
|
||||
'custom'
|
||||
else
|
||||
'application'
|
||||
end
|
||||
end
|
||||
|
||||
def asset_exist?(path)
|
||||
if Rails.configuration.assets.compile
|
||||
Rails.application.precompiled_assets.include? path
|
||||
else
|
||||
Rails.application.assets_manifest.assets[path].present?
|
||||
end
|
||||
end
|
||||
end
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user