Compare commits

..

40 Commits

Author SHA1 Message Date
Eugen Rochko
6cc432bbc4 Bump version to 2.3.2 2018-03-22 14:13:46 +01:00
Eugen Rochko
dafae9818d Bump version to 2.3.2rc5 2018-03-22 11:31:52 +01:00
Eugen Rochko
9fe1619db9 Do not re-query mentions from serializers (#6858)
Fix performance regression from #6836
2018-03-22 11:31:17 +01:00
Eugen Rochko
da70aca28e Restore username validation to disallow dots, for now (#6863)
Usernames with dots in them do not work with routes, because the dot usually separates the desired page format (e.g. json). I don't want to mess with changing route constraints for this patch release.
2018-03-22 11:30:22 +01:00
ThibG
6f531d140b Fix MENTION_RE to not match nil usernames (#6862) 2018-03-22 10:45:48 +01:00
Eugen Rochko
f66a786029 Hide floating action button on thread views (#6859) 2018-03-22 09:33:14 +01:00
Patrick Figel
d97903a358 Update sanitize and loofah (#6855)
Fixes CVE-2018-8048 and CVE-2018-3740, two medium-severity XSS
vulnerabilities present in these gems when built against
libxml2 >= 2.9.2.
2018-03-21 17:43:28 +01:00
Eugen Rochko
93897134ca Permit dots in usernames with conditions (#6844)
* Permit dots in usernames with conditions

- Dot cannot be the start or end of username
- a.lice and al.ice are considered the same during sign-up

* Fix regex mixin flags
2018-03-21 10:26:53 +01:00
Akihiko Odaki
a6b59cd1a3 Remove debug option from Babel preset env (#6852) 2018-03-21 10:26:15 +01:00
Eugen Rochko
f64af6473f Bump version to 2.3.2rc4 2018-03-20 23:49:24 +01:00
Eugen Rochko
ac49c7932d Add LDAP_TLS_NO_VERIFY option, don't require LDAP_ENABLED outside .env (#6845)
Fix #6816, fix #6790
2018-03-20 19:41:51 +01:00
Akihiko Odaki
61dcb686a8 Fix i18n fallback configuration conflicts with environment configurations (#6843) 2018-03-20 16:36:20 +01:00
Eugen Rochko
9381a7d9d5 Use username/domain to match existing accounts in ActivityPub (#6842)
See also: #6837, #6667
2018-03-20 14:57:46 +01:00
ThibG
a5c6c748e0 Cancel outdated pending compose suggestions (#6838) 2018-03-20 12:40:12 +01:00
Rey Tucker
36b5703796 request: in the event of failure, try other IPs (#6761) (#6813)
* request: in the event of failure, try other IPs (#6761)

In the case where a name has multiple A/AAAA records, we should
try subsequent records instead of immediately failing when we have a
failure on the first IP address.

This significantly improves delivery success when there are network
connectivity problems affecting only IPv4 or IPv6.

* fix method call style

* request_spec: adjust test case to use Addrinfo

* request: Request/open: move private addr check to within begin/rescue

* request_spec: add case to test failover, fix exception check

* Double Addrinfo.foreach so that it correctly yields instances
2018-03-20 09:06:08 +01:00
ThibG
ff6b8a6443 Serialize mentions in the order they are added (#6836)
Up until now, the order seemed to be in the *opposite* order,
which caused the WebUI to populate mentions in reversed order
when replying to toots local to one's instance.
2018-03-19 20:19:35 +01:00
ThibG
6b76a6212d Display content warning in mail notification emails (#6832) 2018-03-19 20:12:20 +01:00
Alexander
33ee347c99 rename pam email environment variable to something more understandable and default to LOCAL_DOMAIN (better fallback) (#6833) 2018-03-19 20:09:26 +01:00
Alexander
0306e3e9be bugfixes and gem update (#6831)
* update to new version of devise_pam_authenticatable2

* fix behaviour if suffix is nil, fix environment loading, fix user email creation

* code cleanup/fix linter warning
2018-03-19 20:08:56 +01:00
ThibG
357f9298bd Fix e-mail changed notification (fixes #6778) (#6835)
In Devise::Mailer#email_changed, the new email might be in the email attr.
See: https://github.com/plataformatec/devise/blob/master/app/views/devise/mailer/email_changed.html.erb
2018-03-19 20:07:47 +01:00
Renato "Lond" Cerqueira
f7c46fc113 Weblate translations 20180319 (#6827)
* Translated using Weblate (Portuguese (Brazil))

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/

* Translated using Weblate (Spanish)

Currently translated at 99.6% (579 of 581 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/es/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (58 of 58 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/es/

* Translated using Weblate (Japanese)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ja/

* Translated using Weblate (French)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fr/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (58 of 58 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/es/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/id/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/es/

* Translated using Weblate (Indonesian)

Currently translated at 94.6% (71 of 75 strings)

Translation: Mastodon/Doorkeeper
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/id/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/id/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (62 of 62 strings)

Translation: Mastodon/Devise
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/ar/

* Translated using Weblate (Indonesian)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/id/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Arabic)

Currently translated at 75.5% (439 of 581 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/

* Translated using Weblate (Arabic)

Currently translated at 76.2% (443 of 581 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/

* Translated using Weblate (Russian)

Currently translated at 95.8% (557 of 581 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ru/

* Translated using Weblate (Finnish)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fi/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/sk/

* Translated using Weblate (Slovak)

Currently translated at 91.7% (533 of 581 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/

* Translated using Weblate (Dutch)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/

* Translated using Weblate (Spanish)

Currently translated at 99.8% (580 of 581 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/es/

* Translated using Weblate (Finnish)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fi/

* Translated using Weblate (Slovak)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/sk/

* Translated using Weblate (Spanish)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/es/

* Translated using Weblate (Swedish)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/sv/

* Translated using Weblate (Finnish)

Currently translated at 93.1% (54 of 58 strings)

Translation: Mastodon/Preferences
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/fi/

* Translated using Weblate (Arabic)

Currently translated at 76.7% (446 of 581 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/

* Translated using Weblate (Slovak)

Currently translated at 93.2% (542 of 581 strings)

Translation: Mastodon/Backend
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (280 of 280 strings)

Translation: Mastodon/React
Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/

* Normalize translations
Ran i18n-tasks normalize && yarn manage:translations
2018-03-19 15:12:06 +01:00
Eugen Rochko
74c39fada0 Bump version to 2.3.2rc3 2018-03-19 12:20:57 +01:00
Eugen Rochko
f02411da40 Ignore media validation when attaching to status during processing (#6822)
Fix #6821
2018-03-19 01:51:19 +01:00
Eugen Rochko
a568e3ca8e Revert #6479, hide sensitive text/images from OpenGraph previews (#6818)
Display summary of attachments in description, and mark up content
warning if present, e.g.:

    Attached: 3 images · Content warning: Dota 2

When text is not supposed to be hidden, it looks more like:

    Attached: 3 images

    Here is the text of the toot

With #6817, multilinguagility should be assured...
2018-03-18 20:33:07 +01:00
Eugen Rochko
3b440bd5af Fix elephant graphic being draggable and selectable (#6819) 2018-03-18 20:32:44 +01:00
Eugen Rochko
39f27b6cf3 If DEFAULT_LOCALE is set, enforce it instead of HTTP request locale (#6817)
Fix #6784
2018-03-18 16:57:04 +01:00
Akihiko Odaki
721234230c Synchronize HTML page cache with sessions (#6815) 2018-03-18 15:14:38 +01:00
nightpool
566ace2d64 Add entropy to download filenames (#6811)
pretty quick fix, and with the 1 week expiration i don't think we need to be too worried about the existing files

closes #6798
2018-03-17 17:39:28 +01:00
Eugen Rochko
092f1df9d0 Bump version to 2.3.2rc2 2018-03-17 15:28:52 +01:00
Eugen Rochko
844616e950 Re-add git and nodejs-npm to Dockerfile (#6810)
Fix #6809

I don't know why, either
2018-03-17 15:28:09 +01:00
Eugen Rochko
40871caa4b Revert "Upgrade Paperclip to version 6.0.0" (#6807)
* Revert "Bump version to 2.3.2rc1"

This reverts commit cdf8b92fea.

* Revert "Downgrade Dockerfile to Ruby 2.4.3 on Alpine 3.6 (#6806)"

This reverts commit 0074cad44f.

* Revert "Handle Mastodon::HostValidationError when pulling remoteable assets (#6782)"

This reverts commit 4a0a19fe54.

* Revert "Correct the reference to user's password in mastodon:add_user task (#6800)"

This reverts commit 338bff8b93.

* Revert "Upgrade Paperclip to version 6.0.0 (#6754)"

This reverts commit b88fcd53f7.
2018-03-17 14:20:35 +01:00
Eugen Rochko
cdf8b92fea Bump version to 2.3.2rc1 2018-03-17 14:07:00 +01:00
Eugen Rochko
0074cad44f Downgrade Dockerfile to Ruby 2.4.3 on Alpine 3.6 (#6806)
Fix 6734
2018-03-17 14:06:25 +01:00
Daniel Hunsaker
4a0a19fe54 Handle Mastodon::HostValidationError when pulling remoteable assets (#6782)
This will prevent, for example, `rake mastodon:redownload_avatars` from crashing when an instance is no longer responding to connection attempts, instead silently continuing as expected.
2018-03-17 13:27:50 +01:00
Akihiko Odaki
338bff8b93 Correct the reference to user's password in mastodon:add_user task (#6800) 2018-03-17 13:27:19 +01:00
Yamagishi Kazutoshi
b88fcd53f7 Upgrade Paperclip to version 6.0.0 (#6754) 2018-03-17 12:37:58 +01:00
trwnh
ca7e6a6d2e Properly center .nothing-here (#6787) (#6788)
Apply "margin: 0 auto;" at line 443 to fix issue #6787
2018-03-17 12:35:35 +01:00
Akihiko Odaki
f0cd957c7a Cache HTML page with Service Worker (#6802)
This is the first step to make Mastodon work offline. It is also required
by Chromium to trigger Web Manifest automated install prompt.
2018-03-17 12:35:13 +01:00
Daniel Hunsaker
64fc8d2b07 [Nanobox] Stream backups to the warehouse (#6799)
The `curl` docs are terrible. Use `-X POST -T` instead of `--data-binary`, to avoid loading entire backups into memory _before_ transferring to the warehouse, and just stream the data across as it comes in.
2018-03-17 08:39:14 +01:00
Marcin Mikołajczak
fd385e256d i18n: Update Polish translation (#6780)
Signed-off-by: Marcin Mikołajczak <me@m4sk.in>
2018-03-14 02:17:48 +09:00
53 changed files with 360 additions and 157 deletions

View File

@@ -4,6 +4,7 @@
[ [
"env", "env",
{ {
"exclude": ["transform-async-to-generator", "transform-regenerator"],
"loose": true, "loose": true,
"modules": false, "modules": false,
"targets": { "targets": {

View File

@@ -155,8 +155,8 @@ STREAMING_CLUSTER_NUM=1
# The pam environment variable "email" is provided by: # The pam environment variable "email" is provided by:
# https://github.com/devkral/pam_email_extractor # https://github.com/devkral/pam_email_extractor
# PAM_ENABLED=true # PAM_ENABLED=true
# Fallback Suffix for email address generation (nil by default) # Fallback email domain for email address generation (LOCAL_DOMAIN by default)
# PAM_DEFAULT_SUFFIX=pam # PAM_EMAIL_DOMAIN=example.com
# Name of the pam service (pam "auth" section is evaluated) # Name of the pam service (pam "auth" section is evaluated)
# PAM_DEFAULT_SERVICE=rpam # PAM_DEFAULT_SERVICE=rpam
# Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default) # Name of the pam service used for checking if an user can register (pam "account" section is evaluated) (nil (disabled) by default)

View File

@@ -1,4 +1,4 @@
FROM ruby:2.5.0-alpine3.7 FROM ruby:2.4.3-alpine3.6
LABEL maintainer="https://github.com/tootsuite/mastodon" \ LABEL maintainer="https://github.com/tootsuite/mastodon" \
description="Your self-hosted, globally interconnected microblogging community" description="Your self-hosted, globally interconnected microblogging community"
@@ -9,6 +9,8 @@ ARG GID=991
ENV RAILS_SERVE_STATIC_FILES=true \ ENV RAILS_SERVE_STATIC_FILES=true \
RAILS_ENV=production NODE_ENV=production RAILS_ENV=production NODE_ENV=production
ARG YARN_VERSION=1.3.2
ARG YARN_DOWNLOAD_SHA256=6cfe82e530ef0837212f13e45c1565ba53f5199eec2527b85ecbcd88bf26821d
ARG LIBICONV_VERSION=1.15 ARG LIBICONV_VERSION=1.15
ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178 ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
@@ -30,17 +32,24 @@ RUN apk -U upgrade \
ca-certificates \ ca-certificates \
ffmpeg \ ffmpeg \
file \ file \
git \
icu-libs \ icu-libs \
imagemagick \ imagemagick \
libidn \ libidn \
libpq \ libpq \
nodejs \ nodejs \
nodejs-npm \
protobuf \ protobuf \
tini \ tini \
tzdata \ tzdata \
yarn \
&& update-ca-certificates \ && update-ca-certificates \
&& mkdir -p /tmp/src \ && mkdir -p /tmp/src /opt \
&& wget -O yarn.tar.gz "https://github.com/yarnpkg/yarn/releases/download/v$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \
&& echo "$YARN_DOWNLOAD_SHA256 *yarn.tar.gz" | sha256sum -c - \
&& tar -xzf yarn.tar.gz -C /tmp/src \
&& rm yarn.tar.gz \
&& mv /tmp/src/yarn-v$YARN_VERSION /opt/yarn \
&& ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
&& wget -O libiconv.tar.gz "https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$LIBICONV_VERSION.tar.gz" \ && wget -O libiconv.tar.gz "https://ftp.gnu.org/pub/gnu/libiconv/libiconv-$LIBICONV_VERSION.tar.gz" \
&& echo "$LIBICONV_DOWNLOAD_SHA256 *libiconv.tar.gz" | sha256sum -c - \ && echo "$LIBICONV_DOWNLOAD_SHA256 *libiconv.tar.gz" | sha256sum -c - \
&& tar -xzf libiconv.tar.gz -C /tmp/src \ && tar -xzf libiconv.tar.gz -C /tmp/src \

View File

@@ -32,7 +32,9 @@ gem 'cld3', '~> 3.2.0'
gem 'devise', '~> 4.4' gem 'devise', '~> 4.4'
gem 'devise-two-factor', '~> 3.0' gem 'devise-two-factor', '~> 3.0'
gem 'devise_pam_authenticatable2', '~> 8.0', install_if: -> { ENV['PAM_ENABLED'] == 'true' } group :pam_authentication, optional: true do
gem 'devise_pam_authenticatable2', '~> 9.0'
end
gem 'net-ldap', '~> 0.10' gem 'net-ldap', '~> 0.10'
gem 'omniauth-cas', '~> 1.1' gem 'omniauth-cas', '~> 1.1'
gem 'omniauth-saml', '~> 1.10' gem 'omniauth-saml', '~> 1.10'
@@ -69,7 +71,7 @@ gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock'
gem 'rqrcode', '~> 0.10' gem 'rqrcode', '~> 0.10'
gem 'ruby-oembed', '~> 0.12', require: 'oembed' gem 'ruby-oembed', '~> 0.12', require: 'oembed'
gem 'ruby-progressbar', '~> 1.4' gem 'ruby-progressbar', '~> 1.4'
gem 'sanitize', '~> 4.4' gem 'sanitize', '~> 4.6.4'
gem 'sidekiq', '~> 5.0' gem 'sidekiq', '~> 5.0'
gem 'sidekiq-scheduler', '~> 2.1' gem 'sidekiq-scheduler', '~> 2.1'
gem 'sidekiq-unique-jobs', '~> 5.0' gem 'sidekiq-unique-jobs', '~> 5.0'

View File

@@ -141,7 +141,7 @@ GEM
devise (~> 4.0) devise (~> 4.0)
railties (< 5.2) railties (< 5.2)
rotp (~> 2.0) rotp (~> 2.0)
devise_pam_authenticatable2 (8.0.1) devise_pam_authenticatable2 (9.0.0)
devise (>= 4.0.0) devise (>= 4.0.0)
rpam2 (~> 3.0) rpam2 (~> 3.0)
diff-lcs (1.3) diff-lcs (1.3)
@@ -288,7 +288,7 @@ GEM
activesupport (>= 4, < 5.2) activesupport (>= 4, < 5.2)
railties (>= 4, < 5.2) railties (>= 4, < 5.2)
request_store (~> 1.0) request_store (~> 1.0)
loofah (2.1.1) loofah (2.2.1)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.5.9) nokogiri (>= 1.5.9)
mail (2.7.0) mail (2.7.0)
@@ -316,9 +316,9 @@ GEM
net-ssh (>= 2.6.5) net-ssh (>= 2.6.5)
net-ssh (4.2.0) net-ssh (4.2.0)
nio4r (2.1.0) nio4r (2.1.0)
nokogiri (1.8.1) nokogiri (1.8.2)
mini_portile2 (~> 2.3.0) mini_portile2 (~> 2.3.0)
nokogumbo (1.4.13) nokogumbo (1.5.0)
nokogiri nokogiri
nsa (0.2.4) nsa (0.2.4)
activesupport (>= 4.2, < 6) activesupport (>= 4.2, < 6)
@@ -496,10 +496,10 @@ GEM
rufus-scheduler (3.4.2) rufus-scheduler (3.4.2)
et-orbi (~> 1.0) et-orbi (~> 1.0)
safe_yaml (1.0.4) safe_yaml (1.0.4)
sanitize (4.5.0) sanitize (4.6.4)
crass (~> 1.0.2) crass (~> 1.0.2)
nokogiri (>= 1.4.4) nokogiri (>= 1.4.4)
nokogumbo (~> 1.4.1) nokogumbo (~> 1.4)
sass (3.5.3) sass (3.5.3)
sass-listen (~> 4.0.0) sass-listen (~> 4.0.0)
sass-listen (4.0.0) sass-listen (4.0.0)
@@ -631,7 +631,7 @@ DEPENDENCIES
climate_control (~> 0.2) climate_control (~> 0.2)
devise (~> 4.4) devise (~> 4.4)
devise-two-factor (~> 3.0) devise-two-factor (~> 3.0)
devise_pam_authenticatable2 (~> 8.0) devise_pam_authenticatable2 (~> 9.0)
doorkeeper (~> 4.2) doorkeeper (~> 4.2)
dotenv-rails (~> 2.2) dotenv-rails (~> 2.2)
fabrication (~> 2.18) fabrication (~> 2.18)
@@ -699,7 +699,7 @@ DEPENDENCIES
rubocop rubocop
ruby-oembed (~> 0.12) ruby-oembed (~> 0.12)
ruby-progressbar (~> 1.4) ruby-progressbar (~> 1.4)
sanitize (~> 4.4) sanitize (~> 4.6.4)
scss_lint (~> 0.55) scss_lint (~> 0.55)
sidekiq (~> 5.0) sidekiq (~> 5.0)
sidekiq-bulk (~> 0.1.1) sidekiq-bulk (~> 0.1.1)

View File

@@ -17,7 +17,11 @@ module Localized
end end
def default_locale def default_locale
request_locale || I18n.default_locale if ENV['DEFAULT_LOCALE'].present?
I18n.default_locale
else
request_locale || I18n.default_locale
end
end end
def request_locale def request_locale

View File

@@ -29,6 +29,35 @@ module StreamEntriesHelper
[prepend_str, account.note].join(' · ') [prepend_str, account.note].join(' · ')
end end
def media_summary(status)
attachments = { image: 0, video: 0 }
status.media_attachments.each do |media|
if media.video?
attachments[:video] += 1
else
attachments[:image] += 1
end
end
text = attachments.to_a.reject { |_, value| value.zero? }.map { |key, value| t("statuses.attached.#{key}", count: value) }.join(' · ')
return if text.blank?
t('statuses.attached.description', attached: text)
end
def status_text_summary(status)
return if status.spoiler_text.blank?
t('statuses.content_warning', warning: status.spoiler_text)
end
def status_description(status)
components = [[media_summary(status), status_text_summary(status)].reject(&:blank?).join(' · ')]
components << status.text if status.spoiler_text.blank?
components.reject(&:blank?).join("\n\n")
end
def stream_link_target def stream_link_target
embedded_view? ? '_blank' : nil embedded_view? ? '_blank' : nil
end end

View File

@@ -1,4 +1,5 @@
import api from '../api'; import api from '../api';
import { CancelToken } from 'axios';
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light'; import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light';
import { tagHistory } from '../settings'; import { tagHistory } from '../settings';
@@ -11,6 +12,8 @@ import {
refreshPublicTimeline, refreshPublicTimeline,
} from './timelines'; } from './timelines';
let cancelFetchComposeSuggestionsAccounts;
export const COMPOSE_CHANGE = 'COMPOSE_CHANGE'; export const COMPOSE_CHANGE = 'COMPOSE_CHANGE';
export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST'; export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST';
export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS'; export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS';
@@ -257,13 +260,22 @@ export function undoUploadCompose(media_id) {
}; };
export function clearComposeSuggestions() { export function clearComposeSuggestions() {
if (cancelFetchComposeSuggestionsAccounts) {
cancelFetchComposeSuggestionsAccounts();
}
return { return {
type: COMPOSE_SUGGESTIONS_CLEAR, type: COMPOSE_SUGGESTIONS_CLEAR,
}; };
}; };
const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => { const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => {
if (cancelFetchComposeSuggestionsAccounts) {
cancelFetchComposeSuggestionsAccounts();
}
api(getState).get('/api/v1/accounts/search', { api(getState).get('/api/v1/accounts/search', {
cancelToken: new CancelToken(cancel => {
cancelFetchComposeSuggestionsAccounts = cancel;
}),
params: { params: {
q: token.slice(1), q: token.slice(1),
resolve: false, resolve: false,

View File

@@ -97,7 +97,7 @@ export default class Compose extends React.PureComponent {
<ComposeFormContainer /> <ComposeFormContainer />
{multiColumn && ( {multiColumn && (
<div className='drawer__inner__mastodon'> <div className='drawer__inner__mastodon'>
<img alt='' src={elephantUIPlane} /> <img alt='' draggable='false' src={elephantUIPlane} />
</div> </div>
)} )}
</div> </div>

View File

@@ -28,6 +28,8 @@ const componentMap = {
'LIST': ListTimeline, 'LIST': ListTimeline,
}; };
const shouldHideFAB = path => path.match(/^\/statuses\//);
@component => injectIntl(component, { withRef: true }) @component => injectIntl(component, { withRef: true })
export default class ColumnsArea extends ImmutablePureComponent { export default class ColumnsArea extends ImmutablePureComponent {
@@ -153,7 +155,7 @@ export default class ColumnsArea extends ImmutablePureComponent {
this.pendingIndex = null; this.pendingIndex = null;
if (singleColumn) { if (singleColumn) {
const floatingActionButton = this.context.router.history.location.pathname === '/statuses/new' ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button'><i className='fa fa-pencil' /></Link>; const floatingActionButton = shouldHideFAB(this.context.router.history.location.pathname) ? null : <Link key='floating-action-button' to='/statuses/new' className='floating-action-button'><i className='fa fa-pencil' /></Link>;
return columnIndex !== -1 ? [ return columnIndex !== -1 ? [
<ReactSwipeableViews key='content' index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }}> <ReactSwipeableViews key='content' index={columnIndex} onChangeIndex={this.handleSwipe} onTransitionEnd={this.handleAnimationEnd} animateTransitions={shouldAnimate} springConfig={{ duration: '400ms', delay: '0s', easeFunction: 'ease' }} style={{ height: '100%' }}>

View File

@@ -60,10 +60,10 @@
"compose_form.placeholder": "فيمَ تفكّر؟", "compose_form.placeholder": "فيمَ تفكّر؟",
"compose_form.publish": "بوّق", "compose_form.publish": "بوّق",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.marked": "Media is marked as sensitive", "compose_form.sensitive.marked": "لقد تم تحديد هذه الصورة كحساسة",
"compose_form.sensitive.unmarked": "Media is not marked as sensitive", "compose_form.sensitive.unmarked": "Media is not marked as sensitive",
"compose_form.spoiler.marked": "Text is hidden behind warning", "compose_form.spoiler.marked": "إنّ النص مخفي وراء تحذير",
"compose_form.spoiler.unmarked": "Text is not hidden", "compose_form.spoiler.unmarked": "النص غير مخفي",
"compose_form.spoiler_placeholder": "تنبيه عن المحتوى", "compose_form.spoiler_placeholder": "تنبيه عن المحتوى",
"confirmation_modal.cancel": "إلغاء", "confirmation_modal.cancel": "إلغاء",
"confirmations.block.confirm": "حجب", "confirmations.block.confirm": "حجب",
@@ -254,9 +254,9 @@
"status.sensitive_warning": "محتوى حساس", "status.sensitive_warning": "محتوى حساس",
"status.share": "مشاركة", "status.share": "مشاركة",
"status.show_less": "إعرض أقلّ", "status.show_less": "إعرض أقلّ",
"status.show_less_all": "Show less for all", "status.show_less_all": "طي الكل",
"status.show_more": "أظهر المزيد", "status.show_more": "أظهر المزيد",
"status.show_more_all": "Show more for all", "status.show_more_all": "توسيع الكل",
"status.unmute_conversation": "فك الكتم عن المحادثة", "status.unmute_conversation": "فك الكتم عن المحادثة",
"status.unpin": "فك التدبيس من الملف الشخصي", "status.unpin": "فك التدبيس من الملف الشخصي",
"tabs_bar.federated_timeline": "الموحَّد", "tabs_bar.federated_timeline": "الموحَّد",

View File

@@ -3,7 +3,7 @@
"account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}", "account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}",
"account.blocked": "Estetty", "account.blocked": "Estetty",
"account.disclaimer_full": "Alla olevat käyttäjän profiilitiedot saattavat olla epätäydellisiä.", "account.disclaimer_full": "Alla olevat käyttäjän profiilitiedot saattavat olla epätäydellisiä.",
"account.domain_blocked": "Domain hidden", "account.domain_blocked": "Verkko-osoite piilotettu",
"account.edit_profile": "Muokkaa", "account.edit_profile": "Muokkaa",
"account.follow": "Seuraa", "account.follow": "Seuraa",
"account.followers": "Seuraajia", "account.followers": "Seuraajia",
@@ -60,10 +60,10 @@
"compose_form.placeholder": "Mitä sinulla on mielessä?", "compose_form.placeholder": "Mitä sinulla on mielessä?",
"compose_form.publish": "Toot", "compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.marked": "Media is marked as sensitive", "compose_form.sensitive.marked": "Media on merkitty arkaluontoiseksi",
"compose_form.sensitive.unmarked": "Media is not marked as sensitive", "compose_form.sensitive.unmarked": "Mediaa ei ole merkitty arkaluontoiseksi",
"compose_form.spoiler.marked": "Text is hidden behind warning", "compose_form.spoiler.marked": "Teksti on piilotettu varoituksen taakse",
"compose_form.spoiler.unmarked": "Text is not hidden", "compose_form.spoiler.unmarked": "Teksti ei ole piilotettu",
"compose_form.spoiler_placeholder": "Content warning", "compose_form.spoiler_placeholder": "Content warning",
"confirmation_modal.cancel": "Peruuta", "confirmation_modal.cancel": "Peruuta",
"confirmations.block.confirm": "Estä", "confirmations.block.confirm": "Estä",
@@ -182,13 +182,13 @@
"onboarding.page_four.notifications": "Ilmoitukset-sarake näyttää sinulle, kun joku on viestii kanssasi.", "onboarding.page_four.notifications": "Ilmoitukset-sarake näyttää sinulle, kun joku on viestii kanssasi.",
"onboarding.page_one.federation": "Mastodon on yhteisöpalvelu, joka toimii monen itsenäisen palvelimen muodostamassa verkossa. Me kutsumme näitä palvelimia instansseiksi.", "onboarding.page_one.federation": "Mastodon on yhteisöpalvelu, joka toimii monen itsenäisen palvelimen muodostamassa verkossa. Me kutsumme näitä palvelimia instansseiksi.",
"onboarding.page_one.full_handle": "Koko käyttäjänimesi", "onboarding.page_one.full_handle": "Koko käyttäjänimesi",
"onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.", "onboarding.page_one.handle_hint": "Tämä on se, mitä voisit ehdottaa ystäviäsi etsimään.",
"onboarding.page_one.welcome": "Tervetuloa Mastodoniin!", "onboarding.page_one.welcome": "Tervetuloa Mastodoniin!",
"onboarding.page_six.admin": "Instanssisi ylläpitäjä on {admin}.", "onboarding.page_six.admin": "Instanssisi ylläpitäjä on {admin}.",
"onboarding.page_six.almost_done": "Melkein valmista...", "onboarding.page_six.almost_done": "Melkein valmista...",
"onboarding.page_six.appetoot": "Bon Appetööt!", "onboarding.page_six.appetoot": "Bon Appetööt!",
"onboarding.page_six.apps_available": "{apps} on saatavilla iOS:lle, Androidille ja muille alustoille.", "onboarding.page_six.apps_available": "{apps} on saatavilla iOS:lle, Androidille ja muille alustoille.",
"onboarding.page_six.github": "Mastodon is free open-source software. You can report bugs, request features, or contribute to the code on {github}.", "onboarding.page_six.github": "Mastodon on ilmainen, vapaan lähdekoodin ohjelma. Voit raportoida bugeja, pyytää ominaisuuksia tai osallistua kehittämiseen GitHub-palvelussa: {github}.",
"onboarding.page_six.guidelines": "yhteisön säännöt", "onboarding.page_six.guidelines": "yhteisön säännöt",
"onboarding.page_six.read_guidelines": "Ole hyvä ja lue {domain}:n {guidelines}!", "onboarding.page_six.read_guidelines": "Ole hyvä ja lue {domain}:n {guidelines}!",
"onboarding.page_six.various_app": "mobiilisovellukset", "onboarding.page_six.various_app": "mobiilisovellukset",
@@ -254,12 +254,12 @@
"status.sensitive_warning": "Arkaluontoista sisältöä", "status.sensitive_warning": "Arkaluontoista sisältöä",
"status.share": "Jaa", "status.share": "Jaa",
"status.show_less": "Näytä vähemmän", "status.show_less": "Näytä vähemmän",
"status.show_less_all": "Show less for all", "status.show_less_all": "Näytä vähemmän kaikista",
"status.show_more": "Näytä lisää", "status.show_more": "Näytä lisää",
"status.show_more_all": "Show more for all", "status.show_more_all": "Näytä enemmän kaikista",
"status.unmute_conversation": "Poista mykistys keskustelulta", "status.unmute_conversation": "Poista mykistys keskustelulta",
"status.unpin": "Irrota profiilista", "status.unpin": "Irrota profiilista",
"tabs_bar.federated_timeline": "Federated", "tabs_bar.federated_timeline": "Yleinen",
"tabs_bar.home": "Koti", "tabs_bar.home": "Koti",
"tabs_bar.local_timeline": "Paikallinen", "tabs_bar.local_timeline": "Paikallinen",
"tabs_bar.notifications": "Ilmoitukset", "tabs_bar.notifications": "Ilmoitukset",

View File

@@ -60,10 +60,10 @@
"compose_form.placeholder": "Co Ci chodzi po głowie?", "compose_form.placeholder": "Co Ci chodzi po głowie?",
"compose_form.publish": "Wyślij", "compose_form.publish": "Wyślij",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.marked": "Media is marked as sensitive", "compose_form.sensitive.marked": "Zawartość multimedia jest oznaczona jako wrażliwa",
"compose_form.sensitive.unmarked": "Media is not marked as sensitive", "compose_form.sensitive.unmarked": "Zawartość multimedialna nie jest oznaczona jako wrażliwa",
"compose_form.spoiler.marked": "Text is hidden behind warning", "compose_form.spoiler.marked": "Tekst jest ukryty za ostrzeżeniem",
"compose_form.spoiler.unmarked": "Text is not hidden", "compose_form.spoiler.unmarked": "Tekst nie jest ukryty",
"compose_form.spoiler_placeholder": "Wprowadź swoje ostrzeżenie o zawartości", "compose_form.spoiler_placeholder": "Wprowadź swoje ostrzeżenie o zawartości",
"confirmation_modal.cancel": "Anuluj", "confirmation_modal.cancel": "Anuluj",
"confirmations.block.confirm": "Zablokuj", "confirmations.block.confirm": "Zablokuj",

View File

@@ -18,7 +18,7 @@
"account.muted": "Utíšený/á", "account.muted": "Utíšený/á",
"account.posts": "Hlášky", "account.posts": "Hlášky",
"account.posts_with_replies": "Príspevky s odpoveďami", "account.posts_with_replies": "Príspevky s odpoveďami",
"account.report": "Nahlás @{name}", "account.report": "Nahlás @{name}",
"account.requested": "Čaká na schválenie. Kliknite pre zrušenie žiadosti", "account.requested": "Čaká na schválenie. Kliknite pre zrušenie žiadosti",
"account.share": "Zdieľať @{name} profil", "account.share": "Zdieľať @{name} profil",
"account.show_reblogs": "Zobraziť povýšenia od @{name}", "account.show_reblogs": "Zobraziť povýšenia od @{name}",
@@ -35,13 +35,13 @@
"bundle_modal_error.close": "Zatvoriť", "bundle_modal_error.close": "Zatvoriť",
"bundle_modal_error.message": "Nastala chyba pri načítaní tohto komponentu.", "bundle_modal_error.message": "Nastala chyba pri načítaní tohto komponentu.",
"bundle_modal_error.retry": "Skúsiť znova", "bundle_modal_error.retry": "Skúsiť znova",
"column.blocks": "Blokovaní používatelia", "column.blocks": "Blokovaní užívatelia",
"column.community": "Lokálna časová os", "column.community": "Lokálna časová os",
"column.favourites": "Obľúbené", "column.favourites": "Obľúbené",
"column.follow_requests": "Žiadosti o sledovaní", "column.follow_requests": "Žiadosti o sledovaní",
"column.home": "Domov", "column.home": "Domov",
"column.lists": "Zoznamy", "column.lists": "Zoznamy",
"column.mutes": "Ignorovaní používatelia", "column.mutes": "Ignorovaní užívatelia",
"column.notifications": "Notifikácie", "column.notifications": "Notifikácie",
"column.pins": "Pripnuté toots", "column.pins": "Pripnuté toots",
"column.public": "Federovaná časová os", "column.public": "Federovaná časová os",
@@ -50,20 +50,20 @@
"column_header.moveLeft_settings": "Presunúť stĺpec doľava", "column_header.moveLeft_settings": "Presunúť stĺpec doľava",
"column_header.moveRight_settings": "Presunúť stĺpec doprava", "column_header.moveRight_settings": "Presunúť stĺpec doprava",
"column_header.pin": "Pripnúť", "column_header.pin": "Pripnúť",
"column_header.show_settings": "Ukázať nastavenia", "column_header.show_settings": "Ukáž nastavenia",
"column_header.unpin": "Odopnúť", "column_header.unpin": "Odopnúť",
"column_subheading.navigation": "Navigácia", "column_subheading.navigation": "Navigácia",
"column_subheading.settings": "Nastavenia", "column_subheading.settings": "Nastavenia",
"compose_form.hashtag_warning": "Tento toot nebude zobrazený pod žiadným haštagom lebo nieje listovaný. Iba verejné toots môžu byť nájdené podľa haštagu.", "compose_form.hashtag_warning": "Tento toot nebude zobrazený pod žiadným haštagom lebo nieje listovaný. Iba verejné tooty môžu byť nájdené podľa haštagu.",
"compose_form.lock_disclaimer": "Váš účet nie je zamknutý. Ktokoľvek ťa môže nasledovať a vidieť tvoje správy pre sledujúcich.", "compose_form.lock_disclaimer": "Váš účet nie je zamknutý. Ktokoľvek ťa môže nasledovať a vidieť tvoje správy pre sledujúcich.",
"compose_form.lock_disclaimer.lock": "zamknutý", "compose_form.lock_disclaimer.lock": "zamknutý",
"compose_form.placeholder": "Na čo myslíš?", "compose_form.placeholder": "Na čo myslíš?",
"compose_form.publish": "Toot", "compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.marked": "Media is marked as sensitive", "compose_form.sensitive.marked": "Médiálny obsah je označený ako chúlostivý",
"compose_form.sensitive.unmarked": "Media is not marked as sensitive", "compose_form.sensitive.unmarked": "Médiálny obsah nieje označený ako chúlostivý",
"compose_form.spoiler.marked": "Text is hidden behind warning", "compose_form.spoiler.marked": "Text je ukrytý za varovaním",
"compose_form.spoiler.unmarked": "Text is not hidden", "compose_form.spoiler.unmarked": "Text nieje ukrytý",
"compose_form.spoiler_placeholder": "Sem napíšte vaše varovanie", "compose_form.spoiler_placeholder": "Sem napíšte vaše varovanie",
"confirmation_modal.cancel": "Zrušiť", "confirmation_modal.cancel": "Zrušiť",
"confirmations.block.confirm": "Blokovať", "confirmations.block.confirm": "Blokovať",
@@ -101,14 +101,14 @@
"empty_column.list": "Tento zoznam je ešte prázdny. Keď ale členovia tohoto zoznamu napíšu nové správy, tak tie sa objavia priamo tu.", "empty_column.list": "Tento zoznam je ešte prázdny. Keď ale členovia tohoto zoznamu napíšu nové správy, tak tie sa objavia priamo tu.",
"empty_column.notifications": "Nemáte ešte žiadne notifikácie. Napíšte niekomu, následujte niekoho a komunikujte s ostatnými aby diskusia mohla začať.", "empty_column.notifications": "Nemáte ešte žiadne notifikácie. Napíšte niekomu, následujte niekoho a komunikujte s ostatnými aby diskusia mohla začať.",
"empty_column.public": "Ešte tu nič nie je. Napíšte niečo verejne alebo začnite sledovať používateľov z iných Mastodon serverov aby tu niečo pribudlo", "empty_column.public": "Ešte tu nič nie je. Napíšte niečo verejne alebo začnite sledovať používateľov z iných Mastodon serverov aby tu niečo pribudlo",
"follow_request.authorize": "Povoliť prístup", "follow_request.authorize": "Povoľ prístup",
"follow_request.reject": "Odmietnúť", "follow_request.reject": "Odmietni",
"getting_started.appsshort": "Aplikácie", "getting_started.appsshort": "Aplikácie",
"getting_started.faq": "FAQ", "getting_started.faq": "Časté otázky",
"getting_started.heading": "Začíname", "getting_started.heading": "Začni tu",
"getting_started.open_source_notice": "Mastodon má otvorený kód. Nahlásiť chyby, alebo prispievať vlastným kódom môžete na GitHube v {github}.", "getting_started.open_source_notice": "Mastodon má otvorený kód. Nahlásiť chyby, alebo prispievať vlastným kódom môžete na GitHube v {github}.",
"getting_started.userguide": "Používateľská príručka", "getting_started.userguide": "Používateľská príručka",
"home.column_settings.advanced": "Rozšírené", "home.column_settings.advanced": "Pokročilé",
"home.column_settings.basic": "Základné", "home.column_settings.basic": "Základné",
"home.column_settings.filter_regex": "Filtrovať použitím regulárnych výrazov", "home.column_settings.filter_regex": "Filtrovať použitím regulárnych výrazov",
"home.column_settings.show_reblogs": "Zobraziť povýšené", "home.column_settings.show_reblogs": "Zobraziť povýšené",
@@ -147,7 +147,7 @@
"missing_indicator.label": "Nenájdené", "missing_indicator.label": "Nenájdené",
"missing_indicator.sublabel": "Tento zdroj sa nepodarilo nájsť", "missing_indicator.sublabel": "Tento zdroj sa nepodarilo nájsť",
"mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?", "mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?",
"navigation_bar.blocks": "Blokovaní používatelia", "navigation_bar.blocks": "Blokovaní užívatelia",
"navigation_bar.community_timeline": "Lokálna časová os", "navigation_bar.community_timeline": "Lokálna časová os",
"navigation_bar.edit_profile": "Upraviť profil", "navigation_bar.edit_profile": "Upraviť profil",
"navigation_bar.favourites": "Obľúbené", "navigation_bar.favourites": "Obľúbené",
@@ -156,9 +156,9 @@
"navigation_bar.keyboard_shortcuts": "Klávesové skratky", "navigation_bar.keyboard_shortcuts": "Klávesové skratky",
"navigation_bar.lists": "Zoznamy", "navigation_bar.lists": "Zoznamy",
"navigation_bar.logout": "Odhlásiť", "navigation_bar.logout": "Odhlásiť",
"navigation_bar.mutes": "Ignorovaní používatelia", "navigation_bar.mutes": "Ignorovaní užívatelia",
"navigation_bar.pins": "Pripnuté toots", "navigation_bar.pins": "Pripnuté toots",
"navigation_bar.preferences": "Možnosti", "navigation_bar.preferences": "Voľby",
"navigation_bar.public_timeline": "Federovaná časová os", "navigation_bar.public_timeline": "Federovaná časová os",
"notification.favourite": "{name} sa páči tvoj status", "notification.favourite": "{name} sa páči tvoj status",
"notification.follow": "{name} ťa začal/a následovať", "notification.follow": "{name} ťa začal/a následovať",
@@ -254,9 +254,9 @@
"status.sensitive_warning": "Chúlostivý obsah", "status.sensitive_warning": "Chúlostivý obsah",
"status.share": "Zdieľať", "status.share": "Zdieľať",
"status.show_less": "Zobraz menej", "status.show_less": "Zobraz menej",
"status.show_less_all": "Show less for all", "status.show_less_all": "Všetkým ukáž menej",
"status.show_more": "Zobraz viac", "status.show_more": "Zobraz viac",
"status.show_more_all": "Show more for all", "status.show_more_all": "Všetkým ukáž viac",
"status.unmute_conversation": "Prestať ignorovať konverzáciu", "status.unmute_conversation": "Prestať ignorovať konverzáciu",
"status.unpin": "Odopnúť z profilu", "status.unpin": "Odopnúť z profilu",
"tabs_bar.federated_timeline": "Federovaná", "tabs_bar.federated_timeline": "Federovaná",

View File

@@ -60,10 +60,10 @@
"compose_form.placeholder": "Vad funderar du på?", "compose_form.placeholder": "Vad funderar du på?",
"compose_form.publish": "Toot", "compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!", "compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.marked": "Media is marked as sensitive", "compose_form.sensitive.marked": "Media har markerats som känsligt",
"compose_form.sensitive.unmarked": "Media is not marked as sensitive", "compose_form.sensitive.unmarked": "Media har inte markerats som känsligt",
"compose_form.spoiler.marked": "Text is hidden behind warning", "compose_form.spoiler.marked": "Texten har dolts bakom en varning",
"compose_form.spoiler.unmarked": "Text is not hidden", "compose_form.spoiler.unmarked": "Texten är inte dold",
"compose_form.spoiler_placeholder": "Skriv din varning här", "compose_form.spoiler_placeholder": "Skriv din varning här",
"confirmation_modal.cancel": "Ångra", "confirmation_modal.cancel": "Ångra",
"confirmations.block.confirm": "Blockera", "confirmations.block.confirm": "Blockera",
@@ -254,9 +254,9 @@
"status.sensitive_warning": "Känsligt innehåll", "status.sensitive_warning": "Känsligt innehåll",
"status.share": "Dela", "status.share": "Dela",
"status.show_less": "Visa mindre", "status.show_less": "Visa mindre",
"status.show_less_all": "Show less for all", "status.show_less_all": "Visa mindre för alla",
"status.show_more": "Visa mer", "status.show_more": "Visa mer",
"status.show_more_all": "Show more for all", "status.show_more_all": "Visa mer för alla",
"status.unmute_conversation": "Öppna konversation", "status.unmute_conversation": "Öppna konversation",
"status.unpin": "Ångra fäst i profil", "status.unpin": "Ångra fäst i profil",
"tabs_bar.federated_timeline": "Förenad", "tabs_bar.federated_timeline": "Förenad",

View File

@@ -1,10 +1,48 @@
import './web_push_notifications'; import './web_push_notifications';
function openCache() {
return caches.open('mastodon-web');
}
function fetchRoot() {
return fetch('/', { credentials: 'include' });
}
// Cause a new version of a registered Service Worker to replace an existing one // Cause a new version of a registered Service Worker to replace an existing one
// that is already installed, and replace the currently active worker on open pages. // that is already installed, and replace the currently active worker on open pages.
self.addEventListener('install', function(event) { self.addEventListener('install', function(event) {
event.waitUntil(self.skipWaiting()); event.waitUntil(Promise.all([openCache(), fetchRoot()]).then(([cache, root]) => cache.put('/', root)));
}); });
self.addEventListener('activate', function(event) { self.addEventListener('activate', function(event) {
event.waitUntil(self.clients.claim()); event.waitUntil(self.clients.claim());
}); });
self.addEventListener('fetch', function(event) {
const url = new URL(event.request.url);
if (url.pathname.startsWith('/web/')) {
const asyncResponse = fetchRoot();
const asyncCache = openCache();
event.respondWith(asyncResponse.then(async response => {
if (response.ok) {
const cache = await asyncCache;
await cache.put('/', response);
return response.clone();
}
throw null;
}).catch(() => caches.match('/')));
} else if (url.pathname === '/auth/sign_out') {
const asyncResponse = fetch(event.request);
const asyncCache = openCache();
event.respondWith(asyncResponse.then(async response => {
if (response.ok || response.type === 'opaqueredirect') {
const cache = await asyncCache;
await cache.delete('/');
}
return response;
}));
}
});

View File

@@ -440,6 +440,7 @@
text-align: center; text-align: center;
padding: 60px 0; padding: 60px 0;
padding-top: 55px; padding-top: 55px;
margin: 0 auto;
cursor: default; cursor: default;
} }

View File

@@ -1842,6 +1842,9 @@
object-position: bottom left; object-position: bottom left;
width: 100%; width: 100%;
height: 100%; height: 100%;
pointer-events: none;
user-drag: none;
user-select: none;
} }
} }

View File

@@ -53,7 +53,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
visibility: visibility_from_audience, visibility: visibility_from_audience,
thread: replied_to_status, thread: replied_to_status,
conversation: conversation_from_uri(@object['conversation']), conversation: conversation_from_uri(@object['conversation']),
media_attachments: process_attachments.take(4), media_attachment_ids: process_attachments.take(4).map(&:id),
} }
end end

View File

@@ -45,7 +45,7 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
visibility: visibility_scope, visibility: visibility_scope,
conversation: find_or_create_conversation, conversation: find_or_create_conversation,
thread: thread? ? find_status(thread.first) || find_activitypub_status(thread.first, thread.second) : nil, thread: thread? ? find_status(thread.first) || find_activitypub_status(thread.first, thread.second) : nil,
media_attachments: media_attachments media_attachment_ids: media_attachments.map(&:id)
) )
save_mentions(status) save_mentions(status)

View File

@@ -351,7 +351,7 @@ class OStatus::AtomSerializer
append_element(entry, 'summary', status.spoiler_text, 'xml:lang': status.language) if status.spoiler_text? append_element(entry, 'summary', status.spoiler_text, 'xml:lang': status.language) if status.spoiler_text?
append_element(entry, 'content', Formatter.instance.format(status).to_str, type: 'html', 'xml:lang': status.language) append_element(entry, 'content', Formatter.instance.format(status).to_str, type: 'html', 'xml:lang': status.language)
status.mentions.each do |mentioned| status.mentions.order(:id).each do |mentioned|
append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': OStatus::TagManager::TYPES[:person], href: OStatus::TagManager.instance.uri_for(mentioned.account)) append_element(entry, 'link', nil, rel: :mentioned, 'ostatus:object-type': OStatus::TagManager::TYPES[:person], href: OStatus::TagManager.instance.uri_for(mentioned.account))
end end

View File

@@ -94,9 +94,16 @@ class Request
class Socket < TCPSocket class Socket < TCPSocket
class << self class << self
def open(host, *args) def open(host, *args)
address = IPSocket.getaddress(host) outer_e = nil
raise Mastodon::HostValidationError if PrivateAddressCheck.private_address? IPAddr.new(address) Addrinfo.foreach(host, nil, nil, :SOCK_STREAM) do |address|
super address, *args begin
raise Mastodon::HostValidationError if PrivateAddressCheck.private_address? IPAddr.new(address.ip_address)
return super address.ip_address, *args
rescue => e
outer_e = e
end
end
raise outer_e if outer_e
end end
alias new open alias new open

View File

@@ -47,7 +47,8 @@
# #
class Account < ApplicationRecord class Account < ApplicationRecord
MENTION_RE = /(?<=^|[^\/[:word:]])@(([a-z0-9_]+)(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.]+[a-z0-9_]+)?/i
MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE})(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i
include AccountAvatar include AccountAvatar
include AccountFinderConcern include AccountFinderConcern
@@ -68,7 +69,8 @@ class Account < ApplicationRecord
validates :username, uniqueness: { scope: :domain, case_sensitive: true }, if: -> { !local? && will_save_change_to_username? } validates :username, uniqueness: { scope: :domain, case_sensitive: true }, if: -> { !local? && will_save_change_to_username? }
# Local user validations # Local user validations
validates :username, format: { with: /\A[a-z0-9_]+\z/i }, uniqueness: { scope: :domain, case_sensitive: false }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? } validates :username, format: { with: /\A[a-z0-9_]+\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? }
validates_with UniqueUsernameValidator, if: -> { local? && will_save_change_to_username? }
validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? } validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? }
validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? } validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? }
validates :note, length: { maximum: 160 }, if: -> { local? && will_save_change_to_note? } validates :note, length: { maximum: 160 }, if: -> { local? && will_save_change_to_note? }

View File

@@ -38,7 +38,7 @@ module Remotable
send("#{attachment_name}_file_name=", basename + extname) send("#{attachment_name}_file_name=", basename + extname)
self[attribute_name] = url if has_attribute?(attribute_name) self[attribute_name] = url if has_attribute?(attribute_name)
rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError => e rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError => e
Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}" Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}"
nil nil
end end

View File

@@ -52,6 +52,8 @@ class User < ApplicationRecord
devise :registerable, :recoverable, :rememberable, :trackable, :validatable, devise :registerable, :recoverable, :rememberable, :trackable, :validatable,
:confirmable :confirmable
devise :pam_authenticatable if ENV['PAM_ENABLED'] == 'true'
devise :omniauthable devise :omniauthable
belongs_to :account, inverse_of: :user belongs_to :account, inverse_of: :user
@@ -96,7 +98,7 @@ class User < ApplicationRecord
def pam_conflict? def pam_conflict?
return false unless Devise.pam_authentication return false unless Devise.pam_authentication
encrypted_password.present? && is_pam_account? encrypted_password.present? && pam_managed_user?
end end
def pam_get_name def pam_get_name
@@ -267,22 +269,22 @@ class User < ApplicationRecord
end end
def self.pam_get_user(attributes = {}) def self.pam_get_user(attributes = {})
if attributes[:email] return nil unless attributes[:email]
resource = resource =
if Devise.check_at_sign && !attributes[:email].index('@') if Devise.check_at_sign && !attributes[:email].index('@')
joins(:account).find_by(accounts: { username: attributes[:email] }) joins(:account).find_by(accounts: { username: attributes[:email] })
else else
find_by(email: attributes[:email]) find_by(email: attributes[:email])
end end
if resource.blank? if resource.blank?
resource = new(email: attributes[:email]) resource = new(email: attributes[:email])
if Devise.check_at_sign && !resource[:email].index('@') if Devise.check_at_sign && !resource[:email].index('@')
resource[:email] = "#{attributes[:email]}@#{resource.find_pam_suffix}" resource[:email] = Rpam2.getenv(resource.find_pam_service, attributes[:email], attributes[:password], 'email', false)
end resource[:email] = "#{attributes[:email]}@#{resource.find_pam_suffix}" unless resource[:email]
end end
resource
end end
resource
end end
def self.ldap_get_user(attributes = {}) def self.ldap_get_user(attributes = {})

View File

@@ -57,7 +57,7 @@ class ActivityPub::NoteSerializer < ActiveModel::Serializer
end end
def virtual_tags def virtual_tags
object.mentions + object.tags + object.emojis object.mentions.to_a.sort_by(&:id) + object.tags + object.emojis
end end
def atom_uri def atom_uri

View File

@@ -15,7 +15,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
belongs_to :account, serializer: REST::AccountSerializer belongs_to :account, serializer: REST::AccountSerializer
has_many :media_attachments, serializer: REST::MediaAttachmentSerializer has_many :media_attachments, serializer: REST::MediaAttachmentSerializer
has_many :mentions has_many :ordered_mentions, key: :mentions
has_many :tags has_many :tags
has_many :emojis, serializer: REST::CustomEmojiSerializer has_many :emojis, serializer: REST::CustomEmojiSerializer
@@ -86,6 +86,10 @@ class REST::StatusSerializer < ActiveModel::Serializer
%w(public unlisted).include?(object.visibility) %w(public unlisted).include?(object.visibility)
end end
def ordered_mentions
object.mentions.to_a.sort_by(&:id)
end
class ApplicationSerializer < ActiveModel::Serializer class ApplicationSerializer < ActiveModel::Serializer
attributes :name, :website attributes :name, :website
end end

View File

@@ -16,7 +16,7 @@ class ActivityPub::ProcessAccountService < BaseService
RedisLock.acquire(lock_options) do |lock| RedisLock.acquire(lock_options) do |lock|
if lock.acquired? if lock.acquired?
@account = Account.find_by(uri: @uri) @account = Account.find_remote(@username, @domain)
@old_public_key = @account&.public_key @old_public_key = @account&.public_key
@old_protocol = @account&.protocol @old_protocol = @account&.protocol

View File

@@ -49,7 +49,7 @@ class BackupService < BaseService
end end
end end
archive_filename = ['archive', Time.now.utc.strftime('%Y%m%d%H%M%S'), SecureRandom.hex(2)].join('-') + '.tar.gz' archive_filename = ['archive', Time.now.utc.strftime('%Y%m%d%H%M%S'), SecureRandom.hex(16)].join('-') + '.tar.gz'
@backup.dump = ActionDispatch::Http::UploadedFile.new(tempfile: tmp_file, filename: archive_filename) @backup.dump = ActionDispatch::Http::UploadedFile.new(tempfile: tmp_file, filename: archive_filename)
@backup.processed = true @backup.processed = true

View File

@@ -0,0 +1,14 @@
# frozen_string_literal: true
class UniqueUsernameValidator < ActiveModel::Validator
def validate(account)
return if account.username.nil?
normalized_username = account.username.downcase.delete('.')
scope = Account.where(domain: nil, username: normalized_username)
scope = scope.where.not(id: account.id) if account.persisted?
account.errors.add(:username, :taken) if scope.exists?
end
end

View File

@@ -1,6 +1,6 @@
= opengraph 'og:url', url = opengraph 'og:url', url
= opengraph 'og:site_name', site_title = opengraph 'og:site_name', site_title
= opengraph 'og:title', [yield(:page_title).strip.presence, site_title].compact.join(' - ') = opengraph 'og:title', yield(:page_title).strip
= opengraph 'og:description', account_description(account) = opengraph 'og:description', account_description(account)
= opengraph 'og:image', full_asset_url(account.avatar.url(:original)) = opengraph 'og:image', full_asset_url(account.avatar.url(:original))
= opengraph 'og:image:width', '120' = opengraph 'og:image:width', '120'

View File

@@ -1,5 +1,5 @@
- content_for :page_title do - content_for :page_title do
= "#{display_name(@account)} (@#{@account.username})" = "#{display_name(@account)} (@#{@account.local_username_and_domain})"
- content_for :header_tags do - content_for :header_tags do
%meta{ name: 'description', content: account_description(@account) }/ %meta{ name: 'description', content: account_description(@account) }/

View File

@@ -24,6 +24,11 @@
%bdi= display_name(status.account) %bdi= display_name(status.account)
= "@#{status.account.acct}" = "@#{status.account.acct}"
- if status.spoiler_text?
%div{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
%p
= Formatter.instance.format_spoiler(status)
%div{ dir: rtl_status?(status) ? 'rtl' : 'ltr' } %div{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
= Formatter.instance.format(status) = Formatter.instance.format(status)

View File

@@ -1,3 +1,8 @@
<% if status.spoiler_text? %>
<%= raw status.spoiler_text %>
----
<% end %>
<%= raw Formatter.instance.plaintext(status) %> <%= raw Formatter.instance.plaintext(status) %>
<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{status.id}") %> <%= raw t('application_mailer.view')%> <%= web_url("statuses/#{status.id}") %>

View File

@@ -1 +1 @@
= opengraph 'og:description', [activity.spoiler_text, activity.text].reject(&:blank?).join("\n\n") = opengraph 'og:description', status_description(activity)

View File

@@ -1,4 +1,4 @@
- if activity.is_a?(Status) && activity.media_attachments.any? - if activity.is_a?(Status) && activity.non_sensitive_with_media?
- player_card = false - player_card = false
- activity.media_attachments.each do |media| - activity.media_attachments.each do |media|
- if media.image? - if media.image?

View File

@@ -11,8 +11,8 @@
= opengraph 'og:site_name', site_title = opengraph 'og:site_name', site_title
= opengraph 'og:type', 'article' = opengraph 'og:type', 'article'
= opengraph 'og:title', "#{@account.display_name.presence || @account.username} on #{site_hostname}" = opengraph 'og:title', "#{display_name(@account)} (@#{@account.local_username_and_domain})"
= opengraph 'og:url', account_stream_entry_url(@account, @stream_entry) = opengraph 'og:url', short_account_status_url(@account, @stream_entry)
= render 'stream_entries/og_description', activity: @stream_entry.activity = render 'stream_entries/og_description', activity: @stream_entry.activity
= render 'stream_entries/og_image', activity: @stream_entry.activity, account: @account = render 'stream_entries/og_image', activity: @stream_entry.activity, account: @account

View File

@@ -38,7 +38,7 @@
%table.input{ align: 'center', cellspacing: 0, cellpadding: 0 } %table.input{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody %tbody
%tr %tr
%td= @resource.unconfirmed_email %td= @resource.try(:unconfirmed_email) ? @resource.unconfirmed_email : @resource.email
%table.email-table{ cellspacing: 0, cellpadding: 0 } %table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody %tbody

View File

@@ -4,6 +4,6 @@
<%= t 'devise.mailer.email_changed.explanation' %> <%= t 'devise.mailer.email_changed.explanation' %>
<%= @resource.unconfirmed_email %> <%= @resource.try(:unconfirmed_email) ? @resource.unconfirmed_email : @resource.email %>
<%= t 'devise.mailer.email_changed.extra' %> <%= t 'devise.mailer.email_changed.extra' %>

View File

@@ -195,7 +195,7 @@ data.db:
command: | command: |
PGPASSWORD=${DATA_DB_PASS} pg_dump -U ${DATA_DB_USER} -w -Fc -O gonano | PGPASSWORD=${DATA_DB_PASS} pg_dump -U ${DATA_DB_USER} -w -Fc -O gonano |
gzip | gzip |
curl -k -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/backup-${HOSTNAME}-$(date -u +%Y-%m-%d.%H-%M-%S).sql.gz --data-binary @- && curl -k -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/backup-${HOSTNAME}-$(date -u +%Y-%m-%d.%H-%M-%S).sql.gz -X POST -T - >&2
curl -k -s -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/ | curl -k -s -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/ |
sed 's/,/\n/g' | sed 's/,/\n/g' |
grep ${HOSTNAME} | grep ${HOSTNAME} |
@@ -215,7 +215,7 @@ data.redis:
- id: backup - id: backup
schedule: '0 3 * * *' schedule: '0 3 * * *'
command: | command: |
curl -k -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/backup-${HOSTNAME}-$(date -u +%Y-%m-%d.%H-%M-%S).rdb --data-binary @/data/var/db/redis/dump.rdb && curl -k -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/backup-${HOSTNAME}-$(date -u +%Y-%m-%d.%H-%M-%S).rdb -X POST -T /data/var/db/redis/dump.rdb >&2
curl -k -s -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/ | curl -k -s -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/ |
sed 's/,/\n/g' | sed 's/,/\n/g' |
grep ${HOSTNAME} | grep ${HOSTNAME} |
@@ -236,7 +236,7 @@ data.storage:
schedule: '0 3 * * *' schedule: '0 3 * * *'
command: | command: |
tar cz -C /data/var/db/unfs/ . | tar cz -C /data/var/db/unfs/ . |
curl -k -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/backup-${HOSTNAME}-$(date -u +%Y-%m-%d.%H-%M-%S).tgz --data-binary @- && curl -k -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/backup-${HOSTNAME}-$(date -u +%Y-%m-%d.%H-%M-%S).tgz -X POST -T - >&2
curl -k -s -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/ | curl -k -s -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/ |
sed 's/,/\n/g' | sed 's/,/\n/g' |
grep ${HOSTNAME} | grep ${HOSTNAME} |

View File

@@ -16,6 +16,8 @@ require_relative '../lib/devise/ldap_authenticatable'
Dotenv::Railtie.load Dotenv::Railtie.load
Bundler.require(:pam_authentication) if ENV['PAM_ENABLED'] == 'true'
require_relative '../lib/mastodon/redis_config' require_relative '../lib/mastodon/redis_config'
module Mastodon module Mastodon
@@ -74,9 +76,7 @@ module Mastodon
] ]
config.i18n.default_locale = ENV['DEFAULT_LOCALE']&.to_sym config.i18n.default_locale = ENV['DEFAULT_LOCALE']&.to_sym
if config.i18n.available_locales.include?(config.i18n.default_locale) unless config.i18n.available_locales.include?(config.i18n.default_locale)
config.i18n.fallbacks = [:en]
else
config.i18n.default_locale = :en config.i18n.default_locale = :en
end end

View File

@@ -55,8 +55,8 @@ Rails.application.configure do
# config.action_mailer.raise_delivery_errors = false # config.action_mailer.raise_delivery_errors = false
# Enable locale fallbacks for I18n (makes lookups for any locale fall back to # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
# the I18n.default_locale when a translation cannot be found). # English when a translation cannot be found).
config.i18n.fallbacks = true config.i18n.fallbacks = [:en]
# Send deprecation notices to registered listeners. # Send deprecation notices to registered listeners.
config.active_support.deprecation = :notify config.active_support.deprecation = :notify

View File

@@ -62,3 +62,4 @@ ignore_unused:
- 'errors.429' - 'errors.429'
- 'admin.accounts.roles.*' - 'admin.accounts.roles.*'
- 'admin.action_logs.actions.*' - 'admin.action_logs.actions.*'
- 'statuses.attached.*'

View File

@@ -55,6 +55,8 @@ module Devise
@@ldap_bind_dn = nil @@ldap_bind_dn = nil
mattr_accessor :ldap_password mattr_accessor :ldap_password
@@ldap_password = nil @@ldap_password = nil
mattr_accessor :ldap_tls_no_verify
@@ldap_tls_no_verify = false
class Strategies::PamAuthenticatable class Strategies::PamAuthenticatable
def valid? def valid?
@@ -342,7 +344,7 @@ Devise.setup do |config|
config.usernamefield = nil config.usernamefield = nil
config.emailfield = 'email' config.emailfield = 'email'
config.check_at_sign = true config.check_at_sign = true
config.pam_default_suffix = ENV.fetch('PAM_DEFAULT_SUFFIX') { nil } config.pam_default_suffix = ENV.fetch('PAM_EMAIL_DOMAIN') { ENV['LOCAL_DOMAIN'] }
config.pam_default_service = ENV.fetch('PAM_DEFAULT_SERVICE') { 'rpam' } config.pam_default_service = ENV.fetch('PAM_DEFAULT_SERVICE') { 'rpam' }
config.pam_controlled_service = ENV.fetch('PAM_CONTROLLED_SERVICE') { nil } config.pam_controlled_service = ENV.fetch('PAM_CONTROLLED_SERVICE') { nil }
end end
@@ -357,5 +359,6 @@ Devise.setup do |config|
config.ldap_bind_dn = ENV.fetch('LDAP_BIND_DN') config.ldap_bind_dn = ENV.fetch('LDAP_BIND_DN')
config.ldap_password = ENV.fetch('LDAP_PASSWORD') config.ldap_password = ENV.fetch('LDAP_PASSWORD')
config.ldap_uid = ENV.fetch('LDAP_UID', 'cn') config.ldap_uid = ENV.fetch('LDAP_UID', 'cn')
config.ldap_tls_no_verify = ENV['LDAP_TLS_NO_VERIFY'] == 'true'
end end
end end

View File

@@ -513,6 +513,8 @@ ar:
over_character_limit: تم تجاوز حد الـ %{max} حرف المسموح بها over_character_limit: تم تجاوز حد الـ %{max} حرف المسموح بها
pin_errors: pin_errors:
ownership: لا يمكن تدبيس تبويق نشره شخص آخر ownership: لا يمكن تدبيس تبويق نشره شخص آخر
private: لا يمكن تثبيت تبويق لم يُنشر للعامة
reblog: لا يمكن تثبيت ترقية
show_more: أظهر المزيد show_more: أظهر المزيد
title: '%{name} : "%{quote}"' title: '%{name} : "%{quote}"'
visibilities: visibilities:
@@ -524,6 +526,7 @@ ar:
unlisted_long: يُمكن لأيٍ كان رُؤيتَه و لكن لن يُعرَض على الخيوط العامة unlisted_long: يُمكن لأيٍ كان رُؤيتَه و لكن لن يُعرَض على الخيوط العامة
stream_entries: stream_entries:
click_to_show: إضغط للعرض click_to_show: إضغط للعرض
pinned: تبويق مثبّت
reblogged: رقى reblogged: رقى
sensitive_content: محتوى حساس sensitive_content: محتوى حساس
terms: terms:

View File

@@ -634,6 +634,15 @@ en:
two_factor_authentication: Two-factor Auth two_factor_authentication: Two-factor Auth
your_apps: Your applications your_apps: Your applications
statuses: statuses:
attached:
description: 'Attached: %{attached}'
image:
one: "%{count} image"
other: "%{count} images"
video:
one: "%{count} video"
other: "%{count} videos"
content_warning: 'Content warning: %{warning}'
open_in_web: Open in web open_in_web: Open in web
over_character_limit: character limit of %{max} exceeded over_character_limit: character limit of %{max} exceeded
pin_errors: pin_errors:

View File

@@ -275,6 +275,7 @@ es:
username: Nombre de usuario username: Nombre de usuario
hero: hero:
desc_html: Mostrado en la página principal. Recomendable al menos 600x100px. Por defecto se establece a la miniatura de la instancia desc_html: Mostrado en la página principal. Recomendable al menos 600x100px. Por defecto se establece a la miniatura de la instancia
title: Imagen de portada
peers_api_enabled: peers_api_enabled:
desc_html: Nombres de dominio que esta instancia ha encontrado en el fediverso desc_html: Nombres de dominio que esta instancia ha encontrado en el fediverso
title: Publicar lista de instancias descubiertas title: Publicar lista de instancias descubiertas

View File

@@ -4,13 +4,19 @@ fi:
hints: hints:
defaults: defaults:
avatar: PNG, GIF tai JPG. Korkeintaan 2MB. Skaalataan kokoon 400x400px avatar: PNG, GIF tai JPG. Korkeintaan 2MB. Skaalataan kokoon 400x400px
digest: Lähetetään vain pitkän poissaolon jälkeen, ja vain jos olet vastaanottanut yksityisviestejä poissaolosi aikana. digest: Lähetetään vain pitkän poissaolon jälkeen, ja vain jos olet vastaanottanut yksityisviestejä poissaolosi aikana
display_name: Korkeintaan 30 merkkiä display_name: Korkeintaan 30 merkkiä
header: PNG, GIF tai JPG. Korkeintaan 2MB. Skaalataan kokoon 700x335px header: PNG, GIF tai JPG. Korkeintaan 2MB. Skaalataan kokoon 700x335px
locked: Vaatii sinun manuaalisesti hyväksymään seuraajat, ja asettaa julkaisujen yksityisyyden vain seuraajille locked: Vaatii sinua manuaalisesti hyväksymään seuraajat
note: Korkeintaan 160 merkkiä note: Korkeintaan 160 merkkiä
setting_noindex: Vaikuttaa julkiseen profiiliisi ja statuspäivityksiisi
setting_theme: Vaikuttaa siihen, miltä Mastodon näyttää kun olet kirjautuneena milllä tahansa laitteella.
imports: imports:
data: CSV tiedosto tuotu toiselta Mastodon palvelimelta data: CSV tiedosto, joka on tuotu toiselta Mastodon-palvelimelta
sessions:
otp: Syötä kaksivaiheisen tunnistuksen koodi puhelimestasi tai käytä yhtä palautuskoodeistasi.
user:
filtered_languages: Valitut kielet suodatetaan julkisilta aikajanoilta
labels: labels:
defaults: defaults:
avatar: Profiilikuva avatar: Profiilikuva
@@ -18,22 +24,37 @@ fi:
confirm_password: Varmista salasana confirm_password: Varmista salasana
current_password: Nykyinen salasana current_password: Nykyinen salasana
data: Data data: Data
display_name: Näykyvä nimi display_name: Nimimerkki
email: Sähköpostiosoite email: Sähköpostiosoite
header: Otsake expires_in: Vanhentuu
filtered_languages: Suodatetut kielet
header: Otsakekuva
locale: Kieli locale: Kieli
locked: Tee tilistä yksityinen locked: Tee tilistä yksityinen
max_uses: Max käyttökerrat max_uses: Max käyttökerrat
new_password: Uusi salasana new_password: Uusi salasana
note: Bio note: Kuvaus
otp_attempt: Kaksivaiheinen koodi otp_attempt: Kaksivaiheinen koodi
password: Salasana password: Salasana
setting_auto_play_gif: Animoitujen GIFfien automaattitoisto
setting_boost_modal: Näytä vahvistusikkuna ennen boostausta
setting_default_privacy: Julkaisun yksityisyys setting_default_privacy: Julkaisun yksityisyys
setting_default_sensitive: Merkitse media aina arkaluontoiseksi
setting_delete_modal: Näytä vahvistusikkuna ennen töötin poistamista
setting_display_sensitive_media: Näytä aina arkaluontoiseksi merkitty media
setting_noindex: Jättäydy pois hakukoneindeksoinnista
setting_reduce_motion: Vähennä liikettä animaatioissa
setting_system_font_ui: Käytä käyttöjärjestelmän oletusfonttia
setting_theme: Sivuston teema
setting_unfollow_modal: Näytä vahvistusikkuna ennen seuraamisen lopettamista
severity: Vakavuusaste
type: Tuontityyppi type: Tuontityyppi
username: Käyttäjänimi username: Käyttäjänimi
username_or_email: Käyttäjänimi tai sähköposti
interactions: interactions:
must_be_follower: Estä ilmoitukset käyttäjiltä jotka eivät seuraa sinua must_be_follower: Estä ilmoitukset käyttäjiltä, jotka eivät seuraa sinua
must_be_following: Estä ilmoitukset käyttäjiltä joita et seuraa must_be_following: Estä ilmoitukset käyttäjiltä, joita et seuraa
must_be_following_dm: Estä suorat viestit ihmisiltä, joita et seuraa
notification_emails: notification_emails:
digest: Lähetä koosteviestejä sähköpostilla digest: Lähetä koosteviestejä sähköpostilla
favourite: Lähetä sähköposti, kun joku tykkää statuksestasi favourite: Lähetä sähköposti, kun joku tykkää statuksestasi
@@ -44,5 +65,5 @@ fi:
'no': Ei 'no': Ei
required: required:
mark: "*" mark: "*"
text: vaaditaan text: pakollinen tieto
'yes': Kyllä 'yes': Kyllä

View File

@@ -593,7 +593,7 @@ sk:
title: Sezóna title: Sezóna
settings: settings:
authorized_apps: Autorizované aplikácie authorized_apps: Autorizované aplikácie
back: Naspäť na stránku back: Späť do Mastodonu
delete: Zmazanie účtu delete: Zmazanie účtu
development: Vývoj development: Vývoj
edit_profile: Upraviť profil edit_profile: Upraviť profil
@@ -630,8 +630,15 @@ sk:
title: Podmienky užívania, a pravidlá o súkromí pre %{instance} title: Podmienky užívania, a pravidlá o súkromí pre %{instance}
two_factor_authentication: two_factor_authentication:
enable: Povoliť enable: Povoliť
enabled: Dvoj-faktorové overovanie je povolené
enabled_success: Dvoj-faktorové overovanie bolo úspešne povolené
generate_recovery_codes: Vygeneruj zálohové kódy generate_recovery_codes: Vygeneruj zálohové kódy
lost_recovery_codes: Zálohové kódy ti umožnia dostať sa k svojmu účtu ak stratíš telefón. Pokiaľ si stratila svoje zálohové kódy, môžeš si ich tu znovu vygenerovať. Tvoje staré zálohové kódy budú zneplatnené.
manual_instructions: 'Pokiaľ nemôžeš oskenovať daný QR kód, a potrebuješ ho zadať ručne, tu je tajomstvo v textovom formáte:'
recovery_codes: Zálohuj kódy pre obnovu
recovery_codes_regenerated: Zálohové kódy boli úspešne zvova vygenerované
setup: Nastavenie setup: Nastavenie
wrong_code: Zadaný kód bol neplatný. Je serverový čas a čas na zariadení správny?
user_mailer: user_mailer:
backup_ready: backup_ready:
explanation: Vyžiadal/a si si úplnú zálohu tvojho Mastodon účtu. Táto záloha je teraz pripravená na stiahnutie! explanation: Vyžiadal/a si si úplnú zálohu tvojho Mastodon účtu. Táto záloha je teraz pripravená na stiahnutie!
@@ -639,12 +646,17 @@ sk:
title: Odber archívu title: Odber archívu
welcome: welcome:
edit_profile_action: Nastav profil edit_profile_action: Nastav profil
edit_profile_step: Profil si môžeš prispôsobiť nahratím portrétu a hlavičky, môžeš upraviť svoje meno a viac. Pokiaľ chceš preverovať nových následovateľov predtým než ťa budú môcť sledovať, môžeš uzamknúť svoj účet.
explanation: Tu nájdeš nejaké tipy do začiatku explanation: Tu nájdeš nejaké tipy do začiatku
final_action: Začni prispievať final_action: Začni prispievať
final_step: 'Začnite písať! Aj bez následovníkov budú vaše verejné správy videné ostatnými, napríklad na lokálnej osi a pod haštagmi. Môžete sa ostatným predstaviť pod haštagom #introductions.' final_step: 'Začnite písať! Aj bez následovníkov budú vaše verejné správy videné ostatnými, napríklad na lokálnej osi a pod haštagmi. Môžete sa ostatným predstaviť pod haštagom #introductions.'
full_handle: Adresa tvojho profilu v celom formáte full_handle: Adresa tvojho profilu v celom formáte
full_handle_hint: Toto je čo musíš dať vedieť svojím priateľom aby ti mohli posielať správy, alebo ťa následovať z inej instancie.
review_preferences_action: Zmeniť nastavenia review_preferences_action: Zmeniť nastavenia
subject: Vitaj na Mastodone subject: Vitaj na Mastodone
tip_bridge_html: Ak prichádzaš z Twitteru, môžeš svojích priateľov nájsť na Mastodone pomocou tzv. <a href="%{bridge_url}">mostíkovej aplikácie</a>. Ale tá funguje iba ak ju aj oni niekedy použili!
tip_federated_timeline: Federovaná os zobrazuje sieť Mastodonu až po jej hranice. Ale zahŕňa iba ľúdí ktorých ostatní okolo teba sledujú, takže predsa nieje úplne celistvá.
tip_following: Správcu servera následuješ automaticky. Môžeš ale nájsť mnoho iných zaujímavých ľudí ak prezrieš tak lokálnu, ako aj globálne federovanú os.
tip_local_timeline: Lokálna os je celkový pohľad na aktivitu užívateľov %{instance}. Toto sú tvoji najbližší susedia! tip_local_timeline: Lokálna os je celkový pohľad na aktivitu užívateľov %{instance}. Toto sú tvoji najbližší susedia!
tip_mobile_webapp: Pokiaľ ti prehliadač ponúkne možnosť pridať Mastodon na tvoju obrazovku, môžeš potom dostávať notifikácie skoro ako z natívnej aplikácie! tip_mobile_webapp: Pokiaľ ti prehliadač ponúkne možnosť pridať Mastodon na tvoju obrazovku, môžeš potom dostávať notifikácie skoro ako z natívnej aplikácie!
tips: Tipy tips: Tipy

View File

@@ -1,49 +1,53 @@
# frozen_string_literal: true # frozen_string_literal: true
if ENV['LDAP_ENABLED'] == 'true' require 'net/ldap'
require 'net/ldap' require 'devise/strategies/authenticatable'
require 'devise/strategies/authenticatable'
module Devise module Devise
module Strategies module Strategies
class LdapAuthenticatable < Authenticatable class LdapAuthenticatable < Authenticatable
def authenticate! def authenticate!
if params[:user] if params[:user]
ldap = Net::LDAP.new( ldap = Net::LDAP.new(
host: Devise.ldap_host, host: Devise.ldap_host,
port: Devise.ldap_port, port: Devise.ldap_port,
base: Devise.ldap_base, base: Devise.ldap_base,
encryption: { encryption: {
method: Devise.ldap_method, method: Devise.ldap_method,
tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS, tls_options: tls_options,
}, },
auth: { auth: {
method: :simple, method: :simple,
username: Devise.ldap_bind_dn, username: Devise.ldap_bind_dn,
password: Devise.ldap_password, password: Devise.ldap_password,
}, },
connect_timeout: 10 connect_timeout: 10
) )
if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: "(#{Devise.ldap_uid}=#{email})", password: password)) if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: "(#{Devise.ldap_uid}=#{email})", password: password))
user = User.ldap_get_user(user_info.first) user = User.ldap_get_user(user_info.first)
success!(user) success!(user)
else else
return fail(:invalid_login) return fail(:invalid_login)
end
end end
end end
end
def email def email
params[:user][:email] params[:user][:email]
end end
def password def password
params[:user][:password] params[:user][:password]
end
def tls_options
OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.tap do |options|
options[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if Devise.ldap_tls_no_verify
end end
end end
end end
end end
Warden::Strategies.add(:ldap_authenticatable, Devise::Strategies::LdapAuthenticatable)
end end
Warden::Strategies.add(:ldap_authenticatable, Devise::Strategies::LdapAuthenticatable)

View File

@@ -13,7 +13,7 @@ module Mastodon
end end
def patch def patch
1 2
end end
def pre def pre

View File

@@ -472,7 +472,7 @@ namespace :mastodon do
if user.save if user.save
prompt.ok 'User created and confirmation mail sent to the user\'s email address.' prompt.ok 'User created and confirmation mail sent to the user\'s email address.'
prompt.ok "Here is the random password generated for the user: #{password}" prompt.ok "Here is the random password generated for the user: #{user.password}"
else else
prompt.warn 'User was not created because of the following errors:' prompt.warn 'User was not created because of the following errors:'

View File

@@ -48,6 +48,13 @@ describe Request do
expect(a_request(:get, 'http://example.com')).to have_been_made.once expect(a_request(:get, 'http://example.com')).to have_been_made.once
end end
it 'executes a HTTP request when the first address is private' do
allow(Addrinfo).to receive(:foreach).with('example.com', nil, nil, :SOCK_STREAM)
.and_yield(Addrinfo.new(["AF_INET", 0, "example.com", "0.0.0.0"], :PF_INET, :SOCK_STREAM))
.and_yield(Addrinfo.new(["AF_INET6", 0, "example.com", "2001:4860:4860::8844"], :PF_INET6, :SOCK_STREAM))
expect(a_request(:get, 'http://example.com')).to have_been_made.once
end
it 'sets headers' do it 'sets headers' do
expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made
end end
@@ -61,7 +68,9 @@ describe Request do
end end
it 'raises Mastodon::ValidationError' do it 'raises Mastodon::ValidationError' do
allow(IPSocket).to receive(:getaddress).with('example.com').and_return('0.0.0.0') allow(Addrinfo).to receive(:foreach).with('example.com', nil, nil, :SOCK_STREAM)
.and_yield(Addrinfo.new(["AF_INET", 0, "example.com", "0.0.0.0"], :PF_INET, :SOCK_STREAM))
.and_yield(Addrinfo.new(["AF_INET6", 0, "example.com", "2001:db8::face"], :PF_INET6, :SOCK_STREAM))
expect{ subject.perform }.to raise_error Mastodon::ValidationError expect{ subject.perform }.to raise_error Mastodon::ValidationError
end end
end end