Merge branch 'master' into glitch-soc/merge-upstream

Conflicts:
	Dockerfile
	app/javascript/packs/common.js
	config/webpack/loaders/sass.js
	config/webpack/shared.js
	db/schema.rb
	package.json
	yarn.lock

A lot of the conflicts come from updating webpack.

Even though upstream deleted app/javascript/packs/common.js, I kept
glitch-soc's version as it unifies JS/CSS packs behavior across flavours.

Ported glitch changes to webpack 4.x
main
Thibaut Girka 2018-07-15 18:17:37 +02:00
commit f26f1145ac
79 changed files with 3212 additions and 1803 deletions

2
.nvmrc
View File

@ -1 +1 @@
6
8

View File

@ -5,6 +5,8 @@ libidn11
libidn11-dev
libpq-dev
libprotobuf-dev
libssl-dev
libxdamage1
libxfixes3
protobuf-compiler
zlib1g-dev

View File

@ -1,3 +1,4 @@
FROM node:8.11.3-alpine as node
FROM ruby:2.4.4-alpine3.6
LABEL maintainer="https://github.com/tootsuite/mastodon" \
@ -11,8 +12,6 @@ ENV PATH=/mastodon/bin:$PATH \
RAILS_ENV=production \
NODE_ENV=production
ARG YARN_VERSION=1.3.2
ARG YARN_DOWNLOAD_SHA256=6cfe82e530ef0837212f13e45c1565ba53f5199eec2527b85ecbcd88bf26821d
ARG LIBICONV_VERSION=1.15
ARG LIBICONV_DOWNLOAD_SHA256=ccf536620a45458d26ba83887a983b96827001e92a13847b45e4925cc8913178
@ -20,6 +19,11 @@ EXPOSE 3000 4000
WORKDIR /mastodon
COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /usr/local/lib/node_modules /usr/local/lib/node_modules
COPY --from=node /usr/local/bin/npm /usr/local/bin/npm
COPY --from=node /opt/yarn-* /opt/yarn
RUN apk -U upgrade \
&& apk add -t build-dependencies \
build-base \
@ -39,20 +43,13 @@ RUN apk -U upgrade \
imagemagick \
libidn \
libpq \
nodejs \
nodejs-npm \
protobuf \
tini \
tzdata \
&& update-ca-certificates \
&& 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 \
&& ln -s /opt/yarn/bin/yarnpkg /usr/local/bin/yarnpkg \
&& mkdir -p /tmp/src /opt \
&& 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 - \
&& tar -xzf libiconv.tar.gz -C /tmp/src \
@ -72,7 +69,7 @@ RUN rm /lib/stack-fix.c
RUN bundle config build.nokogiri --with-iconv-lib=/usr/local/lib --with-iconv-include=/usr/local/include \
&& bundle install -j$(getconf _NPROCESSORS_ONLN) --deployment --without test development \
&& yarn --pure-lockfile \
&& yarn install --pure-lockfile --ignore-engines \
&& yarn cache clean
RUN addgroup -g ${GID} mastodon && adduser -h /mastodon -s /bin/sh -D -G mastodon -u ${UID} mastodon \
@ -83,9 +80,11 @@ COPY . /mastodon
RUN chown -R mastodon:mastodon /mastodon
VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs
VOLUME /mastodon/public/system
USER mastodon
ENV LD_PRELOAD=/lib/stack-fix.so
RUN OTP_SECRET=precompile_placeholder SECRET_KEY_BASE=precompile_placeholder bundle exec rails assets:precompile
ENTRYPOINT ["/sbin/tini", "--"]

View File

@ -0,0 +1,58 @@
# frozen_string_literal: true
module Admin
class RelaysController < BaseController
before_action :set_relay, except: [:index, :new, :create]
def index
authorize :relay, :update?
@relays = Relay.all
end
def new
authorize :relay, :update?
@relay = Relay.new(inbox_url: Relay::PRESET_RELAY)
end
def create
authorize :relay, :update?
@relay = Relay.new(resource_params)
if @relay.save
@relay.enable!
redirect_to admin_relays_path
else
render action: :new
end
end
def destroy
authorize :relay, :update?
@relay.destroy
redirect_to admin_relays_path
end
def enable
authorize :relay, :update?
@relay.enable!
redirect_to admin_relays_path
end
def disable
authorize :relay, :update?
@relay.disable!
redirect_to admin_relays_path
end
private
def set_relay
@relay = Relay.find(params[:id])
end
def resource_params
params.require(:relay).permit(:inbox_url)
end
end
end

View File

@ -67,7 +67,7 @@ module StreamEntriesHelper
end
def acct(account)
if embedded_view? && account.local?
if account.local?
"@#{account.acct}@#{Rails.configuration.x.local_domain}"
else
"@#{account.acct}"

View File

@ -1,6 +1,9 @@
// This file will be loaded on admin pages, regardless of theme.
import { delegate } from 'rails-ujs';
import { start } from '../mastodon/common';
start();
function handleDeleteStatus(event) {
const [data] = event.detail;

View File

@ -128,7 +128,7 @@ export function expandDomainBlocks() {
return (dispatch, getState) => {
const url = getState().getIn(['domain_lists', 'blocks', 'next']);
if (url === null) {
if (!url) {
return;
}

View File

@ -0,0 +1,8 @@
import Rails from 'rails-ujs';
export function start() {
require('font-awesome/css/font-awesome.css');
require.context('../images/', true);
Rails.start();
};

View File

@ -7,7 +7,7 @@ import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { me, invitesEnabled } from '../../initial_state';
import { me, invitesEnabled, version } from '../../initial_state';
import { fetchFollowRequests } from '../../actions/accounts';
import { List as ImmutableList } from 'immutable';
import { Link } from 'react-router-dom';
@ -149,7 +149,7 @@ export default class GettingStarted extends ImmutablePureComponent {
<FormattedMessage
id='getting_started.open_source_notice'
defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}.'
values={{ github: <a href='https://github.com/tootsuite/mastodon' rel='noopener' target='_blank'>tootsuite/mastodon</a> }}
values={{ github: <span><a href='https://github.com/tootsuite/mastodon' rel='noopener' target='_blank'>tootsuite/mastodon</a> (v{version})</span> }}
/>
</p>
</div>

View File

@ -1,6 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { LoadingBar } from 'react-redux-loading-bar';
import ZoomableImage from './zoomable_image';
export default class ImageLoader extends React.PureComponent {
@ -23,6 +24,7 @@ export default class ImageLoader extends React.PureComponent {
state = {
loading: true,
error: false,
width: null,
}
removers = [];
@ -122,6 +124,7 @@ export default class ImageLoader extends React.PureComponent {
setCanvasRef = c => {
this.canvas = c;
if (c) this.setState({ width: c.offsetWidth });
}
render () {
@ -135,6 +138,7 @@ export default class ImageLoader extends React.PureComponent {
return (
<div className={className}>
<LoadingBar loading={loading ? 1 : 0} className='loading-bar' style={{ width: this.state.width || width }} />
{loading ? (
<canvas
className='image-loader__preview-canvas'

View File

@ -13,5 +13,6 @@ export const me = getMeta('me');
export const searchEnabled = getMeta('search_enabled');
export const maxChars = getMeta('max_toot_chars') || 500;
export const invitesEnabled = getMeta('invites_enabled');
export const version = getMeta('version');
export default initialState;

View File

@ -6,10 +6,10 @@
"account.direct": "Direct message @{name}",
"account.disclaimer_full": "Information below may reflect the user's profile incompletely.",
"account.domain_blocked": "Domain hidden",
"account.edit_profile": "Edit profile",
"account.follow": "Follow",
"account.followers": "Followers",
"account.follows": "Follows",
"account.edit_profile": "Uprav profil",
"account.follow": "Sleduj",
"account.followers": "Sledovatelé",
"account.follows": "Sleduje",
"account.follows_you": "Follows you",
"account.hide_reblogs": "Hide boosts from @{name}",
"account.media": "Media",

View File

@ -65,7 +65,7 @@
"compose_form.hashtag_warning": "Αυτό το τουτ δεν θα εμφανίζεται κάτω από κανένα hashtag καθώς είναι αφανές. Μόνο τα δημόσια τουτ μπορούν να αναζητηθούν ανά hashtag.",
"compose_form.lock_disclaimer": "Ο λογαριασμός σου δεν είναι {locked}. Οποιοσδήποτε μπορεί να σε ακολουθήσει για να δει τις δημοσιεύσεις σας προς τους ακολούθους σας.",
"compose_form.lock_disclaimer.lock": "κλειδωμένος",
"compose_form.placeholder": "Τι έχεις στο μυαλό σου;",
"compose_form.placeholder": "Τι σκέφτεσαι;",
"compose_form.publish": "Τουτ",
"compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.marked": "Το πολυμέσο έχει σημειωθεί ως ευαίσθητο",

View File

@ -166,7 +166,7 @@
"navigation_bar.domain_blocks": "دامین‌های پنهان‌شده",
"navigation_bar.edit_profile": "ویرایش نمایه",
"navigation_bar.favourites": "پسندیده‌ها",
"navigation_bar.filters": "Muted words",
"navigation_bar.filters": "واژگان بی‌صداشده",
"navigation_bar.follow_requests": "درخواست‌های پیگیری",
"navigation_bar.info": "اطلاعات تکمیلی",
"navigation_bar.keyboard_shortcuts": "میان‌برهای صفحه‌کلید",

View File

@ -170,7 +170,7 @@
"navigation_bar.domain_blocks": "非表示にしたドメイン",
"navigation_bar.edit_profile": "プロフィールを編集",
"navigation_bar.favourites": "お気に入り",
"navigation_bar.filters": "Muted words",
"navigation_bar.filters": "フィルター設定",
"navigation_bar.follow_requests": "フォローリクエスト",
"navigation_bar.info": "このインスタンスについて",
"navigation_bar.keyboard_shortcuts": "ホットキー",

View File

@ -166,7 +166,7 @@
"navigation_bar.domain_blocks": "Verborgen domeinen",
"navigation_bar.edit_profile": "Profiel bewerken",
"navigation_bar.favourites": "Favorieten",
"navigation_bar.filters": "Muted words",
"navigation_bar.filters": "Genegeerde woorden",
"navigation_bar.follow_requests": "Volgverzoeken",
"navigation_bar.info": "Over deze server",
"navigation_bar.keyboard_shortcuts": "Sneltoetsen",

View File

@ -166,7 +166,7 @@
"navigation_bar.domain_blocks": "Domenis resconduts",
"navigation_bar.edit_profile": "Modificar lo perfil",
"navigation_bar.favourites": "Favorits",
"navigation_bar.filters": "Muted words",
"navigation_bar.filters": "Mots ignorats",
"navigation_bar.follow_requests": "Demandas dabonament",
"navigation_bar.info": "Mai informacions",
"navigation_bar.keyboard_shortcuts": "Acorchis clavièr",

View File

@ -166,7 +166,7 @@
"navigation_bar.domain_blocks": "Domínios escondidos",
"navigation_bar.edit_profile": "Editar perfil",
"navigation_bar.favourites": "Favoritos",
"navigation_bar.filters": "Muted words",
"navigation_bar.filters": "Palavras silenciadas",
"navigation_bar.follow_requests": "Seguidores pendentes",
"navigation_bar.info": "Mais informações",
"navigation_bar.keyboard_shortcuts": "Atalhos de teclado",

View File

@ -7,7 +7,7 @@
"account.disclaimer_full": "Inofrmácie uvedené nižšie nemusia byť úplným odrazom uživateľovho účtu.",
"account.domain_blocked": "Doména ukrytá",
"account.edit_profile": "Upraviť profil",
"account.follow": "Následovať",
"account.follow": "Následuj",
"account.followers": "Sledujúci",
"account.follows": "Následuje",
"account.follows_you": "Následuje ťa",

View File

@ -1,4 +1,7 @@
import loadPolyfills from '../mastodon/load_polyfills';
import { start } from '../mastodon/common';
start();
function loaded() {
const TimelineContainer = require('../mastodon/containers/timeline_container').default;

View File

@ -1,4 +1,7 @@
import loadPolyfills from '../mastodon/load_polyfills';
import { start } from '../mastodon/common';
start();
loadPolyfills().then(() => {
require('../mastodon/main').default();

View File

@ -1,5 +1,8 @@
import loadPolyfills from '../mastodon/load_polyfills';
import ready from '../mastodon/ready';
import { start } from '../mastodon/common';
start();
function main() {
const IntlRelativeFormat = require('intl-relativeformat').default;

View File

@ -1,4 +1,7 @@
import loadPolyfills from '../mastodon/load_polyfills';
import { start } from '../mastodon/common';
start();
function loaded() {
const ComposeContainer = require('../mastodon/containers/compose_container').default;

View File

@ -165,6 +165,11 @@
color: $valid-value-color;
font-weight: 500;
}
.negative-hint {
color: $error-value-color;
font-weight: 500;
}
}
.simple_form {

View File

@ -1478,6 +1478,7 @@ a.account__display-name {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.image-loader__preview-canvas {
max-width: $media-modal-media-max-width;
@ -1486,8 +1487,8 @@ a.account__display-name {
object-fit: contain;
}
&.image-loader--loading .image-loader__preview-canvas {
filter: blur(2px);
.loading-bar {
position: relative;
}
&.image-loader--amorphous .image-loader__preview-canvas {

View File

@ -3,12 +3,16 @@
class LanguageDetector
include Singleton
CHARACTER_THRESHOLD = 140
def initialize
@identifier = CLD3::NNetLanguageIdentifier.new(1, 2048)
end
def detect(text, account)
detect_language_code(text) || default_locale(account)
input_text = prepare_text(text)
return if input_text.blank?
detect_language_code(input_text) || default_locale(account)
end
def language_names
@ -23,8 +27,13 @@ class LanguageDetector
simplify_text(text).strip
end
def unreliable_input?(text)
text.size < CHARACTER_THRESHOLD
end
def detect_language_code(text)
result = @identifier.find_language(prepare_text(text))
return if unreliable_input?(text)
result = @identifier.find_language(text)
iso6391(result.language.to_s).to_sym if result.reliable?
end
@ -66,6 +75,6 @@ class LanguageDetector
end
def default_locale(account)
account.user_locale&.to_sym
account.user_locale&.to_sym || I18n.default_locale
end
end

View File

@ -12,6 +12,8 @@ class PotentialFriendshipTracker
class << self
def record(account_id, target_account_id, action)
return if account_id == target_account_id
key = "interactions:#{account_id}"
weight = WEIGHTS[action]

74
app/models/relay.rb Normal file
View File

@ -0,0 +1,74 @@
# frozen_string_literal: true
# == Schema Information
#
# Table name: relays
#
# id :bigint(8) not null, primary key
# inbox_url :string default(""), not null
# enabled :boolean default(FALSE), not null
# follow_activity_id :string
# created_at :datetime not null
# updated_at :datetime not null
#
class Relay < ApplicationRecord
PRESET_RELAY = 'https://relay.joinmastodon.org/inbox'
validates :inbox_url, presence: true, uniqueness: true, url: true, if: :will_save_change_to_inbox_url?
scope :enabled, -> { where(enabled: true) }
before_destroy :ensure_disabled
def enable!
activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil)
payload = Oj.dump(follow_activity(activity_id))
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
update(enabled: true, follow_activity_id: activity_id)
end
def disable!
activity_id = ActivityPub::TagManager.instance.generate_uri_for(nil)
payload = Oj.dump(unfollow_activity(activity_id))
ActivityPub::DeliveryWorker.perform_async(payload, some_local_account.id, inbox_url)
update(enabled: false, follow_activity_id: nil)
end
private
def follow_activity(activity_id)
{
'@context': ActivityPub::TagManager::CONTEXT,
id: activity_id,
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(some_local_account),
object: ActivityPub::TagManager::COLLECTIONS[:public],
}
end
def unfollow_activity(activity_id)
{
'@context': ActivityPub::TagManager::CONTEXT,
id: activity_id,
type: 'Undo',
actor: ActivityPub::TagManager.instance.uri_for(some_local_account),
object: {
id: follow_activity_id,
type: 'Follow',
actor: ActivityPub::TagManager.instance.uri_for(some_local_account),
object: ActivityPub::TagManager::COLLECTIONS[:public],
},
}
end
def some_local_account
@some_local_account ||= Account.local.find_by(suspended: false)
end
def ensure_disabled
return unless enabled?
disable!
end
end

View File

@ -0,0 +1,7 @@
# frozen_string_literal: true
class RelayPolicy < ApplicationPolicy
def update?
admin?
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class ActivityPub::DeleteActorSerializer < ActiveModel::Serializer
attributes :id, :type, :actor
attributes :id, :type, :actor, :to
attribute :virtual_object, key: :object
def id
@ -19,4 +19,8 @@ class ActivityPub::DeleteActorSerializer < ActiveModel::Serializer
def virtual_object
actor
end
def to
[ActivityPub::TagManager::COLLECTIONS[:public]]
end
end

View File

@ -17,7 +17,7 @@ class ActivityPub::DeleteSerializer < ActiveModel::Serializer
end
end
attributes :id, :type, :actor
attributes :id, :type, :actor, :to
has_one :object, serializer: TombstoneSerializer
@ -32,4 +32,8 @@ class ActivityPub::DeleteSerializer < ActiveModel::Serializer
def actor
ActivityPub::TagManager.instance.uri_for(object.account)
end
def to
[ActivityPub::TagManager::COLLECTIONS[:public]]
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class ActivityPub::UndoAnnounceSerializer < ActiveModel::Serializer
attributes :id, :type, :actor
attributes :id, :type, :actor, :to
has_one :object, serializer: ActivityPub::ActivitySerializer
@ -16,4 +16,8 @@ class ActivityPub::UndoAnnounceSerializer < ActiveModel::Serializer
def actor
ActivityPub::TagManager.instance.uri_for(object.account)
end
def to
[ActivityPub::TagManager::COLLECTIONS[:public]]
end
end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class ActivityPub::UpdateSerializer < ActiveModel::Serializer
attributes :id, :type, :actor
attributes :id, :type, :actor, :to
has_one :object, serializer: ActivityPub::ActorSerializer
@ -16,4 +16,8 @@ class ActivityPub::UpdateSerializer < ActiveModel::Serializer
def actor
ActivityPub::TagManager.instance.uri_for(object)
end
def to
[ActivityPub::TagManager::COLLECTIONS[:public]]
end
end

View File

@ -19,6 +19,7 @@ class InitialStateSerializer < ActiveModel::Serializer
domain: Rails.configuration.x.local_domain,
admin: object.admin&.id&.to_s,
search_enabled: Chewy.enabled?,
version: Mastodon::Version.to_s,
invites_enabled: Setting.min_invite_role == 'user',
}

View File

@ -90,6 +90,18 @@ class RemoveStatusService < BaseService
ActivityPub::DeliveryWorker.push_bulk(@account.followers.inboxes) do |inbox_url|
[signed_activity_json, @account.id, inbox_url]
end
relay! if relayable?
end
def relayable?
@status.public_visibility?
end
def relay!
ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
[signed_activity_json, @account.id, inbox_url]
end
end
def salmon_xml

View File

@ -22,7 +22,13 @@ class SuspendAccountService < BaseService
end
def purge_content!
ActivityPub::RawDistributionWorker.perform_async(delete_actor_json, @account.id) if @account.local?
if @account.local?
ActivityPub::RawDistributionWorker.perform_async(delete_actor_json, @account.id)
ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
[delete_actor_json, @account.id, inbox_url]
end
end
@account.statuses.reorder(nil).find_in_batches do |statuses|
BatchedRemoveStatusService.new.call(statuses)
@ -59,12 +65,14 @@ class SuspendAccountService < BaseService
end
def delete_actor_json
return @delete_actor_json if defined?(@delete_actor_json)
payload = ActiveModelSerializers::SerializableResource.new(
@account,
serializer: ActivityPub::DeleteActorSerializer,
adapter: ActivityPub::Adapter
).as_json
Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(@account))
@delete_actor_json = Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(@account))
end
end

View File

@ -0,0 +1,21 @@
%tr
%td
%samp= relay.inbox_url
%td
- if relay.enabled?
%span.positive-hint
= fa_icon('check')
= ' '
= t 'admin.relays.enabled'
- else
%span.negative-hint
= fa_icon('times')
= ' '
= t 'admin.relays.disabled'
%td
- if relay.enabled?
= table_link_to 'power-off', t('admin.relays.disable'), disable_admin_relay_path(relay), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }
- else
= table_link_to 'power-off', t('admin.relays.enable'), enable_admin_relay_path(relay), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }
= table_link_to 'times', t('admin.relays.delete'), admin_relay_path(relay), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') }

View File

@ -0,0 +1,20 @@
- content_for :page_title do
= t('admin.relays.title')
.simple_form
%p.hint= t('admin.relays.description_html')
= link_to @relays.empty? ? t('admin.relays.setup') : t('admin.relays.add_new'), new_admin_relay_path, class: 'block-button'
- unless @relays.empty?
%hr.spacer
.table-wrapper
%table.table
%thead
%tr
%th= t('admin.relays.inbox_url')
%th= t('admin.relays.status')
%th
%tbody
= render @relays

View File

@ -0,0 +1,13 @@
- content_for :page_title do
= t('admin.relays.add_new')
= simple_form_for @relay, url: admin_relays_path do |f|
= render 'shared/error_messages', object: @relay
.field-group
= f.input :inbox_url, as: :string, wrapper: :with_block_label
.actions
= f.button :button, t('admin.relays.save_and_enable'), type: :submit
%p.hint.subtle-hint= t('admin.relays.enable_hint')

View File

@ -14,6 +14,8 @@ class ActivityPub::DistributionWorker
ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
[signed_payload, @account.id, inbox_url]
end
relay! if relayable?
rescue ActiveRecord::RecordNotFound
true
end
@ -24,6 +26,10 @@ class ActivityPub::DistributionWorker
@status.direct_visibility?
end
def relayable?
@status.public_visibility?
end
def inboxes
@inboxes ||= @account.followers.inboxes
end
@ -39,4 +45,10 @@ class ActivityPub::DistributionWorker
adapter: ActivityPub::Adapter
).as_json
end
def relay!
ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
[signed_payload, @account.id, inbox_url]
end
end
end

View File

@ -9,7 +9,11 @@ class ActivityPub::UpdateDistributionWorker
@account = Account.find(account_id)
ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url|
[payload, @account.id, inbox_url]
[signed_payload, @account.id, inbox_url]
end
ActivityPub::DeliveryWorker.push_bulk(Relay.enabled.pluck(:inbox_url)) do |inbox_url|
[signed_payload, @account.id, inbox_url]
end
rescue ActiveRecord::RecordNotFound
true
@ -21,6 +25,10 @@ class ActivityPub::UpdateDistributionWorker
@inboxes ||= @account.followers.inboxes
end
def signed_payload
@signed_payload ||= Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(@account))
end
def payload
@payload ||= ActiveModelSerializers::SerializableResource.new(
@account,

View File

@ -1,7 +1,7 @@
---
cs:
about:
about_hashtag_html: Toto jsou veřejné příspěvky typu označené jako <strong>#%{hashtag}</strong>. Pokud máte účet kdekoliv na fediverse, můžete s nimi interagovat.
about_hashtag_html: Toto jsou veřejné tooty označené jako <strong>#%{hashtag}</strong>. Pokud máte účet kdekoliv na fediverse, můžete s nimi interagovat.
about_mastodon_html: Mastodon je sociální síť založená na otevřených webových protokolech a svobodném, otevřeném softwaru. Je decentrovalizovaná jako e-mail.
about_this: O této instanci
administered_by: 'Server spravuje:'
@ -18,7 +18,7 @@ cs:
features:
humane_approach_body: Mastodon, poučen z chyb jiných sociálních sítí, se snaží bojovat se zneužíváním sociálních sítí vytvářením etických možností.
humane_approach_title: Lidštější přístup
not_a_product_title: Jste osoba, ne produkt.
not_a_product_title: Jste osoba, ne produkt
generic_description: "%{domain} je jedním serverem v síti"
learn_more: Zjistit více
source_code: Zdrojový kód

View File

@ -10,7 +10,7 @@ da:
contact_missing: Ikke sat
contact_unavailable: Ikke tilgængeligt
description_headline: Hvad er %{domain}?
domain_count_after: andre instancer
domain_count_after: andre instanser
domain_count_before: Forbundet til
extended_description_html: |
<h3>Et godt sted for regler</h3>
@ -27,7 +27,7 @@ da:
generic_description: "%{domain} er en server i netværket"
hosted_on: Mostodon hostet på %{domain}
learn_more: Lær mere
other_instances: Liste over instancer
other_instances: Liste over instanser
source_code: Kildekode
status_count_after: statusser
status_count_before: Som har skrevet
@ -46,7 +46,7 @@ da:
people_who_follow: Folk der følger %{name}
posts: Dyt
posts_with_replies: Toots og svar
remote_follow: Følg fra andre instancer
remote_follow: Følg fra andre instanser
reserved_username: Brugernavnet er reserveret
roles:
admin: Administrator
@ -256,6 +256,10 @@ da:
expired: Udløbet
title: Filtre
title: Invitationer
relays:
setup: Opsæt en videresendelses forbindelse
status: Status
title: Videresendelser
reports:
account:
note: notat
@ -297,7 +301,11 @@ da:
username: Kontakt brugernavn
hero:
title: Billede af helt
peers_api_enabled:
title: Udgiv liste over opdagede instanser
registrations:
closed_message:
title: Besked for lukkede registreringer
deletion:
desc_html: Tillad alle at slette deres konto
title: Åben konto sletning
@ -338,6 +346,7 @@ da:
new_report:
body: "%{reporter} har anmeldt %{target}"
body_remote: Nogen fra %{domain} har anmeldt %{target}
subject: Ny anmeldelse for %{instance} (#%{id})
application_mailer:
notification_preferences: Ændre email indstillinger
salutation: "%{name},"
@ -359,6 +368,9 @@ da:
migrate_account: Flyt til en anden konto
or: eller
or_log_in_with: Eller log in med
providers:
cas: CAS
saml: SAML
register: Opret dig
register_elsewhere: Opret dig på en anden server
reset_password: Nulstil kodeord
@ -378,8 +390,15 @@ da:
distance_in_words:
about_x_hours: "%{count}t"
about_x_months: "%{count} måneder"
about_x_years: "%{count}år"
almost_x_years: "%{count}år"
half_a_minute: Lige nu
less_than_x_minutes: "%{count}m"
less_than_x_seconds: Lige nu
over_x_years: "%{count}år"
x_days: "%{count}d"
x_minutes: "%{count}m"
x_months: "%{count}md"
x_seconds: "%{count}s"
deletes:
bad_password_msg: Godt forsøg, hackere! Forkert kodeord
@ -401,6 +420,7 @@ da:
download: Hent dit arkiv
size: Størrelse
blocks: Du blokerer
csv: CSV
follows: Du følger
mutes: Du dæmper
filters:
@ -445,9 +465,13 @@ da:
expires_in_prompt: Aldrig
generate: Generer
invited_by: 'Du er blevet inviteret af:'
max_uses:
one: 1 benyttelse
other: "%{count} benyttelser"
max_uses_prompt: Ubegrænset
table:
expires_at: Udløber
uses: Benyttelser
title: Inviter folk
media_attachments:
validations:

View File

@ -133,7 +133,7 @@ ja:
write:blocks: ユーザーのブロックやドメインの非表示
write:favourites: トゥートのお気に入り登録
write:filters: フィルターの変更
write:follows: フォローの変更
write:follows: あなたの代わりにフォロー、アンフォロー
write:lists: リストの変更
write:media: メディアのアップロード
write:mutes: アカウントや会話のミュート

View File

@ -114,7 +114,29 @@ pt-BR:
application:
title: Autorização OAuth obrigatória
scopes:
follow: seguir, bloquear, desbloquear e deixar de seguir outras contas
push: receber notificações push na sua conta
read: ler os dados da sua conta
write: postar em seu nome
follow: modificar as relações com outras contas
push: receber suas notificações push
read: ler todos os dados da sua conta
read:accounts: ver as informações da conta
read:blocks: ver seus bloqueios
read:favourites: ver seus favoritos
read:filters: ver seus filtros
read:follows: ver quem você segue
read:lists: ver suas listas
read:mutes: ver seus usuários silenciados
read:notifications: ver suas notificações
read:reports: ver suas denúncias
read:search: buscar em seu nome
read:statuses: ver todos os status
write: modificar todos os dados da sua conta
write:accounts: modificar seu perfil
write:blocks: bloquear contas e domínios
write:favourites: status favoritos
write:filters: criar filtros
write:follows: seguir pessoas
write:lists: criar listas
write:media: enviar arquivos de mídia
write:mutes: silenciar pessoas e conversas
write:notifications: limpar suas notificações
write:reports: reportar outras pessoas
write:statuses: publicar status

View File

@ -261,6 +261,14 @@ el:
expired: Ληγμένες
title: Φίλτρο
title: Προσκλήσεις
relays:
add_new: Πρόσθεσε νέο ανταποκριτή (relay)
description_html: Ο <strong>ομοσπονδιακός ανταποκριτής</strong> είναι ένας ενδιάμεσος εξυπηρετητής (server) που ανταλλάσσει μεγάλους όγκους δημόσιων τουτ μεταξύ εξυπηρετητών που εγγράφονται και δημοσιεύουν σε αυτόν. <strong>Βοηθάει μικρούς και μεσαίους εξυπηρετητές να ανακαλύψουν περιεχόμενο στο fediverse</strong>, που υπό άλλες συνθήκες θα χρειαζόταν κάποιους τοπικούς χρήστες που να ακολουθούν χρήστες σε απομακρυσμένους εξυπηρετητές.
enable_hint: Μόλις ενεργοποιηθεί, ο εξυπηρετητής (server) σου θα εγγραφεί σε όλα τα δημόσια τουτ αυτού του ανταποκριτή (relay) και θα αρχίσει να προωθεί τα δικά του δημόσια τουτ σε αυτόν.
inbox_url: URL ανταποκριτή
setup: Όρισε μια σύνδεση ανταπόκρισης
status: Κατάσταση
title: Ανταποκριτές
report_notes:
created_msg: Επιτυχής δημιουργία σημείωσης καταγγελίας!
destroyed_msg: Επιτυχής διαγραφή σημείωσης καταγγελίας!
@ -316,6 +324,9 @@ el:
peers_api_enabled:
desc_html: Ονόματα τομέων που αυτός ο κόμβος έχει ήδη συναντήσει στο fediverse
title: Δημοσίευση λίστας κόμβων που έχουν ανακαλυφθεί
preview_sensitive_media:
desc_html: Οι προεπισκοπήσεις συνδέσμων σε τρίτους ιστότοπους θα είναι ορατές ακόμα κι όταν το πολυμέσο έχει σημειωθεί ως ευαίσθητο
title: Εμφάνιση ευαίσθητων πολυμέσων στις προεπισκοπήσεις OpenGraph
registrations:
closed_message:
desc_html: Εμφανίζεται στην εισαγωγική σελίδα όταν οι εγγραφές είναι κλειστές. Μπορείς να χρησιμοποιήσεις HTML tags

View File

@ -261,6 +261,14 @@ en:
expired: Expired
title: Filter
title: Invites
relays:
add_new: Add new relay
description_html: A <strong>federation relay</strong> is an intermediary server that exchanges large volumes of public toots between servers that subscribe and publish to it. <strong>It can help small and medium servers discover content from the fediverse</strong>, which would otherwise require local users manually following other people on remote servers.
enable_hint: Once enabled, your server will subscribe to all public toots from this relay, and will begin sending this server's public toots to it.
inbox_url: Relay URL
setup: Setup a relay connection
status: Status
title: Relays
report_notes:
created_msg: Report note successfully created!
destroyed_msg: Report note successfully deleted!

View File

@ -261,6 +261,14 @@ gl:
expired: Cadudado
title: Filtro
title: Convida
relays:
add_new: Engadir un novo repetidor
description_html: Un <strong>repetidor da federación</strong> é un servidor intermedio que intercambia grandes volumes de toots públicos entre servidores que se suscriban e publiquen nel. <strong>Pode axudar a servidores pequenos e medios a descubrir contido no fediverso</strong>, o que de outro xeito precisaría que as usuarias locais seguisen a outra xente en servidores remotos.
enable_hint: Unha vez activado, o seu servidor suscribirase a todos os toots públicos de este servidor, e tamén comezará a eviar a el os toots públicos do servidor.
inbox_url: URL do repetidor
setup: Configurar a conexión ao repetidor
status: Estado
title: Repetidores
report_notes:
created_msg: Creouse correctamente a nota do informe!
destroyed_msg: Nota do informe eliminouse con éxito!

View File

@ -261,6 +261,11 @@ ja:
expired: 期限切れ
title: フィルター
title: 招待
relays:
add_new: リレーを追加
inbox_url: Relay URL
status: ステータス
title: リレー
report_notes:
created_msg: レポートメモを書き込みました!
destroyed_msg: レポートメモを削除しました!
@ -706,7 +711,7 @@ ja:
disallowed_hashtags:
one: '許可されていないハッシュタグが含まれています: %{tags}'
other: '許可されていないハッシュタグが含まれています: %{tags}'
language_detection: 自動的に言語を検出する
language_detection: 自動検出
open_in_web: Webで開く
over_character_limit: 上限は %{max}文字までです
pin_errors:

View File

@ -261,6 +261,14 @@ nl:
expired: Verlopen
title: Filter
title: Uitnodigingen
relays:
add_new: Nieuwe relayserver toevoegen
description_html: Een <strong>federatie-relay</strong> is een tussenliggende server die grote hoeveelheden openbare toots uitwisselt tussen servers die zich hierop hebben geabonneerd. <strong>Het kan kleine en middelgrote servers helpen om content uit de fediverse te ontdekken</strong>, waarvoor anders lokale gebruikers handmatig mensen van externe servers moeten volgen.
enable_hint: Eenmaal ingeschakeld gaat jouw server zich op alle openbare toots van deze relayserver abonneren en stuurt het de openbare toots van jouw server naar de relayserver.
inbox_url: Relay-URL
setup: Een verbinding met een relayserver maken
status: Status
title: Relayservers
report_notes:
created_msg: Opmerking bij gerapporteerde toot succesvol aangemaakt!
destroyed_msg: Opmerking bij gerapporteerde toot succesvol verwijderd!
@ -492,7 +500,7 @@ nl:
delete: Verwijderen
title: Filters
new:
title: Nieuwe filter toevoegen
title: Nieuw filter toevoegen
followers:
domain: Domein
explanation_html: Wanneer je de privacy van jouw toots wilt garanderen, moet je goed weten wie jouw volgers zijn. <strong>Toots die alleen aan jouw volgers zijn gericht, worden aan de Mastodonservers van jouw volgers afgeleverd.</strong> Daarom wil je ze misschien controleren en desnoods volgers verwijderen die zich op een Mastodonserver bevinden die jij niet vertrouwd. Bijvoorbeeld omdat de beheerder(s) of de software van zo'n server jouw privacy niet respecteert.

View File

@ -25,6 +25,7 @@ da:
setting_hide_network: Hvem du følger og hvem der følger dig vil ikke blive vist på din profil
setting_noindex: Påvirker din offentlige profil og status sider
setting_theme: Påvirker hvordan Mastodon ser ud nåt du er logget ind via en hvilken som helst enhed.
whole_word: Når nøgle ordet eller udtrykket kun er alfabetisk, vil det kun blive brugt hvis det passer hele ordet
imports:
data: CSV fil eksporteret fra en anden Mastodon instans
sessions:
@ -77,6 +78,7 @@ da:
type: Importtype
username: Brugernavn
username_or_email: Brugernavn eller Email
whole_word: Helt ord
interactions:
must_be_follower: Bloker notifikationer fra folk der ikke følger dig
must_be_following: Bloker notifikationer fra folk du ikke følger

View File

@ -48,7 +48,7 @@ de:
data: Daten
display_name: Anzeigename
email: E-Mail-Adresse
expires_in: Gültig bis
expires_in: Läuft ab
fields: Profil-Metadaten
header: Kopfbild
irreversible: Fallen lassen anstatt es zu verstecken

View File

@ -13,6 +13,7 @@ el:
other: απομένουν <span class="name-counter">%{count}</span> χαρακτήρες
fields: Μπορείς να έχεις έως 4 σημειώσεις σε μορφή πίνακα στο προφίλ σου
header: PNG, GIF ή JPG. Έως 2MB. Θα μειωθεί σε διάσταση 700x335px
inbox_url: Αντέγραψε το URL της αρχικής σελίδας του ανταποκριτή (relay) που θέλεις να χρησιμοποιήσεις
irreversible: Τα φιλτραρισμένα τουτ θα εξαφανιστούν αμετάκλητα, ακόμα και αν το φίλτρο αργότερα αφαιρεθεί
locale: Η γλώσσα του περιβάλλοντος χρήσης, των email και των ειδοποιήσεων ώθησης
locked: Απαιτεί να εγκρίνεις χειροκίνητα τους ακόλουθούς σου
@ -25,6 +26,7 @@ el:
setting_hide_network: Το ποιους ακολουθείς και το ποιοι σε ακολουθούν δε θα φαίνεται στο προφίλ σου
setting_noindex: Επηρεάζει το δημόσιο προφίλ και τις δημοσιεύσεις σου
setting_theme: Επηρεάζει την εμφάνιση του Mastodon όταν συνδέεται από οποιαδήποτε συσκευή.
whole_word: Όταν η λέξη ή η φράση κλειδί είναι μόνο αλφαριθμητική, θα εφαρμοστεί μόνο αν ταιριάζει με ολόκληρη τη λέξη
imports:
data: Αρχείο CSV που έχει εξαχθεί από διαφορετικό κόμβο Mastodon
sessions:
@ -51,6 +53,7 @@ el:
expires_in: Λήξη μετά από
fields: Μετεδεδομένα προφίλ
header: Επικεφαλίδα
inbox_url: Το URL του inbox του ανταποκριτή (relay)
irreversible: Απόρριψη αντί για κρύψιμο
locale: Γλώσσα περιβάλλοντος
locked: Κλείδωμα λογαριασμού
@ -77,6 +80,7 @@ el:
type: Τύπος εισαγωγής
username: Όνομα χρηστη
username_or_email: Όνομα ή διεύθυνση email χρήστη
whole_word: Ολόκληρη λέξη
interactions:
must_be_follower: Μπλόκαρε τις ειδοποιήσεις από όσους δεν ακολουθείς
must_be_following: Μπλόκαρε τις ειδοποιήσεις που προέρχονται από άτομα που δεν τα ακολουθείς

View File

@ -13,6 +13,7 @@ en:
other: <span class="name-counter">%{count}</span> characters left
fields: You can have up to 4 items displayed as a table on your profile
header: PNG, GIF or JPG. At most 2MB. Will be downscaled to 700x335px
inbox_url: Copy the URL from the frontpage of the relay you want to use
irreversible: Filtered toots will disappear irreversibly, even if filter is later removed
locale: The language of the user interface, e-mails and push notifications
locked: Requires you to manually approve followers
@ -52,6 +53,7 @@ en:
expires_in: Expire after
fields: Profile metadata
header: Header
inbox_url: URL of the relay inbox
irreversible: Drop instead of hide
locale: Interface language
locked: Lock account

View File

@ -13,6 +13,7 @@ fa:
other: <span class="name-counter">%{count}</span> حرف باقی مانده
fields: شما می‌توانید تا چهار مورد را در یک جدول در نمایهٔ خود نمایش دهید
header: یکی از قالب‌های PNG یا GIF یا JPG. بیشترین اندازه ۲ مگابایت. تصویر به اندازهٔ ۳۳۵×۷۰۰ پیکسل تبدیل خواهد شد
inbox_url: نشانی صفحهٔ اصلی رله‌ای را که می‌خواهید به کار ببرید کپی کنید
irreversible: بوق‌های فیلترشده به طور برگشت‌ناپذیری ناپدید می‌شوند، حتی اگر فیلتر را بعداً بردارید
locale: زبان محیط کاربری، ایمیل‌ها، و اعلان‌ها
locked: باید پیگیران تازه را خودتان تأیید کنید
@ -20,10 +21,12 @@ fa:
one: <span class="note-counter">1</span> حرف باقی مانده
other: <span class="note-counter">%{count}</span> حرف باقی مانده
phrase: مستقل از کوچکی و بزرگی حروف، با متن اصلی یا هشدار محتوای بوق‌ها مقایسه می‌شود
scopes: واسط‌های برنامه‌نویسی که این برنامه به آن دسترسی دارد. اگر بالاترین سطح دسترسی را انتخاب کنید، دیگر نیازی به انتخاب سطح‌های پایینی ندارید.
setting_default_language: زبان نوشته‌های شما به طور خودکار تشخیص داده می‌شود، ولی این تشخصی همیشه دقیق نیست
setting_hide_network: فهرست پیگیران شما و فهرست کسانی که شما پی می‌گیرید روی نمایهٔ شما دیده نخواهد شد
setting_noindex: روی نمایهٔ عمومی و صفحهٔ نوشته‌های شما تأثیر می‌گذارد
setting_theme: ظاهر ماستدون را وقتی که از هر دستگاهی به آن وارد می‌شوید تعیین می‌کند.
whole_word: اگر کلیدواژه فقط دارای حروف و اعداد باشد، تنها وقتی پیدا می‌شود که با کل یک واژه در متن منطبق باشد، نه با بخشی از یک واژه
imports:
data: پروندهٔ CSV که از سرور ماستدون دیگری برون‌سپاری شده
sessions:
@ -50,6 +53,7 @@ fa:
expires_in: تاریخ انقضا
fields: اطلاعات تکمیلی نمایه
header: تصویر زمینه
inbox_url: نشانی صندوق ورودی رله
irreversible: به جای پنهان‌سازی، حذف کن
locale: زبان محیط کاربری
locked: خصوصی‌کردن حساب
@ -76,6 +80,7 @@ fa:
type: نوع درون‌ریزی
username: نام کاربری (تنها حروف انگلیسی)
username_or_email: نام کاربری یا ایمیل
whole_word: تطابق واژهٔ کامل
interactions:
must_be_follower: مسدودکردن اعلان‌های همه به جز پیگیران
must_be_following: مسدودکردن اعلان‌های کسانی که شما پی نمی‌گیرید

View File

@ -13,6 +13,7 @@ gl:
other: <span class="name-counter">%{count}</span> caracteres restantes
fields: Pode ter ate 4 elementos no seu perfil mostrados como unha táboa
header: PNG, GIF ou JPG. Máximo 2MB. Será reducida a 700x335px
inbox_url: Copiar o URL desde a páxina de inicio do repetidor que quere utilizar
irreversible: Os toots filtrados desaparecerán de xeito irreversible, incluso si despois se elimina o filtro
locale: O idioma da interface de usuaria, correos e notificacións
locked: Require que vostede acepte as seguidoras de xeito manual
@ -25,6 +26,7 @@ gl:
setting_hide_network: Non se mostrará no seu perfil quen a segue e quen a está a seguir
setting_noindex: Afecta ao seu perfil público e páxinas de estado
setting_theme: Afecta ao aspecto de Mastodon en calquer dispositivo cando está conectada.
whole_word: Si a chave ou frase de paso é só alfanumérica, só se aplicará si concorda a palabra completa
imports:
data: Ficheiro CSV exportado desde outra instancia Mastodon
sessions:
@ -51,6 +53,7 @@ gl:
expires_in: Caducidade despois de
fields: Metadatos do perfil
header: Cabeceira
inbox_url: URL da caixa de entrada do repetidor
irreversible: Soltar en lugar de agochar
locale: Idioma da interface
locked: Protexer conta
@ -77,6 +80,7 @@ gl:
type: Tipo de importación
username: Nome de usuaria
username_or_email: Nome de usuaria ou Correo-e
whole_word: Palabra completa
interactions:
must_be_follower: Bloquear as notificacións de non-seguidoras
must_be_following: Bloquea as notificacións de personas que non segue

View File

@ -40,7 +40,7 @@ ja:
chosen_languages: 表示する言語
confirm_new_password: 新しいパスワード(確認用)
confirm_password: パスワード(確認用)
context: フィルター対象
context: 除外対象
current_password: 現在のパスワード
data: データ
display_name: 表示名
@ -48,6 +48,7 @@ ja:
expires_in: 有効期限
fields: プロフィール補足情報
header: ヘッダー
inbox_url: URL of the relay inbox
irreversible: 隠すのではなく除外する
locale: 言語
locked: 承認制アカウントにする
@ -56,7 +57,7 @@ ja:
note: プロフィール
otp_attempt: 二段階認証コード
password: パスワード
phrase: 単語または語句
phrase: キーワードまたはフレーズ
setting_auto_play_gif: アニメーションGIFを自動再生する
setting_boost_modal: ブーストする前に確認ダイアログを表示する
setting_default_language: 投稿する言語

View File

@ -20,10 +20,12 @@ ko:
one: <span class="note-counter">1</span> 글자 남음
other: <span class="note-counter">%{count}</span> 글자 남음
phrase: 툿 내용이나 CW 내용 안에서 대소문자 구분 없이 매칭 됩니다
scopes: 애플리케이션에 허용할 API들입니다. 최상위 스코프를 선택하면 개별적인 것은 선택하지 않아도 됩니다.
setting_default_language: 작성한 툿의 언어는 자동으로 인식할 수 있지만, 언제나 정확한 건 아닙니다
setting_hide_network: 나를 팔로우 하는 사람들과 내가 팔로우 하는 사람들이 내 프로필에 표시되지 않게 합니다
setting_noindex: 공개 프로필 및 각 툿페이지에 영향을 미칩니다
setting_theme: 로그인중인 모든 디바이스에 적용되는 디자인입니다.
whole_word: 키워드가 영문과 숫자로만 이루어 진 경우, 단어 전체에 매칭 되었을 때에만 작동하게 합니다
imports:
data: 다른 마스토돈 인스턴스에서 추출된 CSV 파일
sessions:
@ -76,6 +78,7 @@ ko:
type: 불러오기 종류
username: 유저 이름
username_or_email: 유저네임 또는 이메일
whole_word: 단어 전체에 매칭
interactions:
must_be_follower: 나를 팔로우 하지 않는 사람에게서 온 알림을 차단
must_be_following: 내가 팔로우 하지 않는 사람에게서 온 알림을 차단

View File

@ -13,6 +13,7 @@ nl:
other: <span class="name-counter">%{count}</span> tekens over
fields: Je kan maximaal 4 items als een tabel op je profiel weergeven
header: PNG, GIF of JPG. Maximaal 2MB. Wordt teruggeschaald naar 700x335px
inbox_url: Kopieer de URL van de voorpagina van de relayserver die je wil gebruiken
irreversible: Gefilterde toots verdwijnen onomkeerbaar, zelfs als de filter later wordt verwijderd
locale: De taal van de gebruikersomgeving, e-mails en pushmeldingen
locked: Vereist dat je handmatig volgers moet accepteren
@ -25,6 +26,7 @@ nl:
setting_hide_network: Wie jij volgt en wie jou volgen wordt niet op jouw profiel getoond
setting_noindex: Heeft invloed op jouw openbare profiel en toots
setting_theme: Heeft invloed op hoe de webapp van Mastodon eruitziet (op elk apparaat waarmee je inlogt).
whole_word: Wanneer het trefwoord of zinsdeel alfanumeriek is, wordt het alleen gefilterd wanneer het hele woord overeenkomt
imports:
data: CSV-bestand dat op een andere Mastodonserver werd geëxporteerd
sessions:
@ -51,6 +53,7 @@ nl:
expires_in: Vervalt na
fields: Metadata profiel
header: Omslagfoto
inbox_url: Inbox-URL van de relayserver
irreversible: Verwijderen in plaats van verbergen
locale: Taal van de gebruikersomgeving
locked: Maak account besloten
@ -77,6 +80,7 @@ nl:
type: Importtype
username: Gebruikersnaam
username_or_email: Gebruikersnaam of e-mailadres
whole_word: Heel woord
interactions:
must_be_follower: Meldingen van mensen die jou niet volgen blokkeren
must_be_following: Meldingen van mensen die jij niet volgt blokkeren

View File

@ -27,6 +27,7 @@ sk:
setting_hide_network: Koho následuješ, a kto následuje teba nebude zobrazené na tvojom profile
setting_noindex: Ovplyvňuje verejný profil a statusy
setting_theme: Toto ovplyvňuje ako Mastodon vyzerá pri prihlásení z hociakého zariadenia.
whole_word: Ak je kľúčové slovo, alebo fráza poskladaná iba s písmen a čísel, bude použité iba ak sa zhoduje s celým výrazom
imports:
data: CSV súbor vyexportovaný z inej Mastodon inštancie
sessions:
@ -53,6 +54,7 @@ sk:
expires_in: Expirovať po
fields: Metadáta profilu
header: Obrázok v hlavičke
inbox_url: URL adresa prechodnej schránky
irreversible: Zahoď, namiesto skritia
locale: Jazyk rozhrania
locked: Zamknúť účet
@ -79,6 +81,7 @@ sk:
type: Typ importu
username: Prezývka
username_or_email: Prezívka, alebo email
whole_word: Celé slovo
interactions:
must_be_follower: Blokovať oznámenia od užívateľov, ktorí ťa nesledujú
must_be_following: Blokovať oboznámenia ohľadom ľudí ktorých nesleduješ

View File

@ -259,9 +259,12 @@ sk:
filter:
all: Všetky
available: Dostupné
expired: Expirované
expired: Vypršalo
title: Filtrovať
title: Pozvánky
relays:
add_new: Pridaj novú priechodnú oporu
status: Stav
report_notes:
created_msg: Poznámka o nahlásení úspešne vytvorená!
destroyed_msg: Poznámka o nahlásení úspešne vymazaná!
@ -423,11 +426,11 @@ sk:
authorize_follow:
already_following: Tento účet už následuješ
error: Naneštastie nastala chyba pri hľadaní vzdialeného účtu
follow: Následovať
follow_request: 'Poslali ste požiadavku následovať užívateľa:'
following: 'Podarilo sa! Teraz už následujete užívateľa:'
follow: Následuj
follow_request: 'Poslal/a si žiadosť následovať užívateľa:'
following: 'Podarilo sa! Teraz už následuješ užívateľa:'
post_follow:
close: Alebo môžete iba zatvoriť toto okno.
close: Alebo môžeš iba zatvoriť toto okno.
return: Ukáž užívateľov profil
web: Prejdi do siete
title: Následuj %{acct}
@ -635,6 +638,7 @@ sk:
browser: Prehliadač
browsers:
alipay: Alipay
blackberry: RIM Blackberry
chrome: Google Chrome
edge: Microsoft Edge
electron: Electron

View File

@ -42,6 +42,7 @@ SimpleNavigation::Configuration.run do |navigation|
primary.item :admin, safe_join([fa_icon('cogs fw'), t('admin.title')]), proc { current_user.admin? ? edit_admin_settings_url : admin_custom_emojis_url }, if: proc { current_user.staff? } do |admin|
admin.item :settings, safe_join([fa_icon('cogs fw'), t('admin.settings.title')]), edit_admin_settings_url, if: -> { current_user.admin? }
admin.item :custom_emojis, safe_join([fa_icon('smile-o fw'), t('admin.custom_emojis.title')]), admin_custom_emojis_url, highlights_on: %r{/admin/custom_emojis}
admin.item :relays, safe_join([fa_icon('exchange fw'), t('admin.relays.title')]), admin_relays_url, if: -> { current_user.admin? }, highlights_on: %r{/admin/relays}
admin.item :subscriptions, safe_join([fa_icon('paper-plane-o fw'), t('admin.subscriptions.title')]), admin_subscriptions_url, if: -> { current_user.admin? }
admin.item :sidekiq, safe_join([fa_icon('diamond fw'), 'Sidekiq']), sidekiq_url, link_html: { target: 'sidekiq' }, if: -> { current_user.admin? }
admin.item :pghero, safe_join([fa_icon('database fw'), 'PgHero']), pghero_url, link_html: { target: 'pghero' }, if: -> { current_user.admin? }

View File

@ -134,6 +134,13 @@ Rails.application.routes.draw do
resource :settings, only: [:edit, :update]
resources :invites, only: [:index, :create, :destroy]
resources :relays, only: [:index, :new, :create, :destroy] do
member do
post :enable
post :disable
end
end
resources :instances, only: [:index] do
collection do
post :resubscribe

View File

@ -16,6 +16,8 @@ if (process.env.VAGRANT) {
}
module.exports = merge(sharedConfig, {
mode: 'development',
devtool: 'cheap-module-eval-source-map',
stats: {

View File

@ -1,15 +1,27 @@
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const { env } = require('../configuration.js');
module.exports = {
test: /\.(scss|sass|css)$/i,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{ loader: 'css-loader', options: { minimize: env.NODE_ENV === 'production' } },
{ loader: 'postcss-loader', options: { sourceMap: true } },
'resolve-url-loader',
{ loader: 'sass-loader', options: { includePaths: ['app/javascript'] } },
],
}),
test: /\.s?css$/i,
use: [
MiniCssExtractPlugin.loader,
{
loader: 'css-loader',
options: {
minimize: env.NODE_ENV === 'production',
},
},
{
loader: 'postcss-loader',
options: {
sourceMap: true,
},
},
{
loader: 'sass-loader',
options: {
includePaths: ['app/javascript'],
},
},
],
};

View File

@ -1,7 +1,7 @@
// Note: You must restart bin/webpack-dev-server for changes to take effect
const webpack = require('webpack');
const merge = require('webpack-merge');
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const sharedConfig = require('./shared.js');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
@ -36,6 +36,8 @@ if (process.env.S3_ENABLED === 'true') {
}
module.exports = merge(sharedConfig, {
mode: 'production',
output: {
filename: '[name]-[chunkhash].js',
chunkFilename: '[name]-[chunkhash].js',
@ -44,19 +46,28 @@ module.exports = merge(sharedConfig, {
devtool: 'source-map', // separate sourcemap file, suitable for production
stats: 'normal',
optimization: {
minimize: true,
minimizer: [
new UglifyJsPlugin({
sourceMap: true,
uglifyOptions: {
mangle: true,
compress: {
warnings: false,
},
output: {
comments: false,
},
},
}),
],
},
plugins: [
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
mangle: true,
compress: {
warnings: false,
},
output: {
comments: false,
},
}),
new CompressionPlugin({
asset: '[path].gz[query]',
algorithm: compressionAlgorithm,

View File

@ -1,9 +1,9 @@
// Note: You must restart bin/webpack-dev-server for changes to take effect
const webpack = require('webpack');
const { join, resolve } = require('path');
const { basename, dirname, join, relative, resolve } = require('path');
const { sync } = require('glob');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const ManifestPlugin = require('webpack-manifest-plugin');
const { env, settings, core, flavours, output, loadersDir } = require('./configuration.js');
const localePacks = require('./generateLocalePacks');
@ -59,6 +59,25 @@ module.exports = {
publicPath: output.publicPath,
},
optimization: {
runtimeChunk: {
name: 'locales',
},
splitChunks: {
cacheGroups: {
default: false,
vendors: false,
locales: {
name: 'locales',
chunks: 'all',
minChunks: Infinity,
minSize: 0,
},
},
},
occurrenceOrder: true,
},
module: {
rules: sync(join(loadersDir, '*.js')).map(loader => require(loader)),
},
@ -72,17 +91,13 @@ module.exports = {
resource.request = resource.request.replace(/^history/, 'history/es');
}
),
new ExtractTextPlugin({
new MiniCssExtractPlugin({
filename: env.NODE_ENV === 'production' ? '[name]-[contenthash].css' : '[name].css',
allChunks: true,
}),
new ManifestPlugin({
publicPath: output.publicPath,
writeToFileEmit: true,
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'locales',
minChunks: Infinity, // It doesn't make sense to use common chunks with multiple frontend support.
filter: file => !file.isAsset || file.isModuleAsset,
}),
],

View File

@ -3,4 +3,6 @@
const merge = require('webpack-merge');
const sharedConfig = require('./shared.js');
module.exports = merge(sharedConfig, {});
module.exports = merge(sharedConfig, {
mode: 'development',
});

View File

@ -5,7 +5,7 @@ class RevertIndexChangeOnStatusesForApiV1AccountsAccountIdStatuses < ActiveRecor
def change
safety_assured do
add_index :statuses, [:account_id, :id, :visibility, :updated_at], order: { id: :desc }, algorithm: :concurrently, name: :index_statuses_20180106 unless index_exists?(:statuses, name: "index_statuses_20180106")
add_index :statuses, [:account_id, :id, :visibility, :updated_at], order: { id: :desc }, algorithm: :concurrently, name: :index_statuses_20180106 unless index_name_exists?(:statuses, "index_statuses_20180106")
end
# These index may not exists (see migration 20180514130000)

View File

@ -0,0 +1,12 @@
class CreateRelays < ActiveRecord::Migration[5.2]
def change
create_table :relays do |t|
t.string :inbox_url, default: '', null: false
t.boolean :enabled, default: false, null: false, index: true
t.string :follow_activity_id
t.timestamps
end
end
end

View File

@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2018_07_07_193142) do
ActiveRecord::Schema.define(version: 2018_07_11_152640) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
@ -381,6 +381,15 @@ ActiveRecord::Schema.define(version: 2018_07_07_193142) do
t.index ["status_id", "preview_card_id"], name: "index_preview_cards_statuses_on_status_id_and_preview_card_id"
end
create_table "relays", force: :cascade do |t|
t.string "inbox_url", default: "", null: false
t.boolean "enabled", default: false, null: false
t.string "follow_activity_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["enabled"], name: "index_relays_on_enabled"
end
create_table "report_notes", force: :cascade do |t|
t.text "content", null: false
t.bigint "report_id", null: false

View File

@ -5,14 +5,7 @@ module Paperclip
# to convert animated gifs to webm
class GifTranscoder < Paperclip::Processor
def make
num_frames = identify('-format %n :file', file: file.path).to_i
unless options[:style] == :original && num_frames > 1
tmp_file = Paperclip::TempfileFactory.new.generate(attachment.instance.file_file_name)
tmp_file << file.read
tmp_file.flush
return tmp_file
end
return File.open(@file.path) unless needs_convert?
final_file = Paperclip::Transcoder.make(file, options, attachment)
@ -22,5 +15,12 @@ module Paperclip
final_file
end
private
def needs_convert?
num_frames = identify('-format %n :file', file: file.path).to_i
options[:style] == :original && num_frames > 1
end
end
end

View File

@ -22,29 +22,29 @@
"dependencies": {
"array-includes": "^3.0.3",
"atrament": "^0.2.3",
"autoprefixer": "^7.1.6",
"autoprefixer": "^8.6.5",
"axios": "~0.16.2",
"babel-core": "^6.25.0",
"babel-loader": "^7.1.1",
"babel-plugin-lodash": "^3.3.2",
"babel-core": "^6.26.3",
"babel-loader": "^7.1.5",
"babel-plugin-lodash": "^3.3.4",
"babel-plugin-preval": "^1.6.1",
"babel-plugin-react-intl": "^2.3.1",
"babel-plugin-react-intl": "^2.4.0",
"babel-plugin-syntax-dynamic-import": "^6.18.0",
"babel-plugin-transform-class-properties": "^6.24.1",
"babel-plugin-transform-decorators-legacy": "^1.3.4",
"babel-plugin-transform-es2015-modules-commonjs": "^6.24.1",
"babel-plugin-transform-decorators-legacy": "^1.3.5",
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
"babel-plugin-transform-object-rest-spread": "^6.23.0",
"babel-plugin-transform-react-inline-elements": "^6.22.0",
"babel-plugin-transform-react-jsx-self": "^6.22.0",
"babel-plugin-transform-react-jsx-source": "^6.22.0",
"babel-plugin-transform-react-remove-prop-types": "^0.4.10",
"babel-plugin-transform-react-remove-prop-types": "^0.4.13",
"babel-plugin-transform-runtime": "^6.23.0",
"babel-preset-env": "^1.6.1",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"classnames": "^2.2.5",
"compression-webpack-plugin": "^1.0.1",
"cross-env": "^5.1.1",
"css-loader": "^0.28.4",
"compression-webpack-plugin": "^1.1.11",
"cross-env": "^5.1.4",
"css-loader": "^0.28.11",
"detect-passive-events": "^1.0.2",
"dotenv": "^4.0.0",
"emoji-mart": "Gargron/emoji-mart#build",
@ -52,8 +52,7 @@
"escape-html": "^1.0.3",
"exif-js": "^2.3.0",
"express": "^4.16.2",
"extract-text-webpack-plugin": "^3.0.2",
"file-loader": "^0.11.2",
"file-loader": "^1.1.11",
"font-awesome": "^4.7.0",
"glob": "^7.1.1",
"http-link-header": "^0.8.0",
@ -64,27 +63,28 @@
"intl-messageformat": "^2.2.0",
"intl-relativeformat": "^2.1.0",
"is-nan": "^1.2.1",
"js-yaml": "^3.9.0",
"lodash": "^4.17.4",
"js-yaml": "^3.11.0",
"lodash": "^4.17.5",
"mark-loader": "^0.1.6",
"marky": "^1.2.0",
"mini-css-extract-plugin": "^0.4.1",
"mkdirp": "^0.5.1",
"node-sass": "^4.7.2",
"node-sass": "^4.9.2",
"npm-run-all": "^4.1.2",
"npmlog": "^4.1.2",
"object-assign": "^4.1.1",
"object-fit-images": "^3.2.3",
"object.values": "^1.0.4",
"offline-plugin": "^4.8.3",
"path-complete-extname": "^0.1.0",
"offline-plugin": "^5.0.5",
"path-complete-extname": "^1.0.0",
"pg": "^6.4.0",
"postcss-loader": "^2.0.9",
"postcss-loader": "^2.1.6",
"postcss-object-fit-images": "^1.1.2",
"postcss-smart-import": "^0.7.5",
"precss": "^2.0.0",
"postcss-smart-import": "^0.7.6",
"precss": "^3.1.2",
"prop-types": "^15.5.10",
"punycode": "^2.1.0",
"rails-ujs": "^5.1.2",
"rails-ujs": "^5.2.0",
"react": "^16.3.0",
"react-dom": "^16.3.0",
"react-hotkeys": "^0.10.0",
@ -99,7 +99,7 @@
"react-router-dom": "^4.1.1",
"react-router-scroll-4": "^1.0.0-beta.1",
"react-sparklines": "^1.7.0",
"react-swipeable-views": "^0.12.3",
"react-swipeable-views": "0.12.13",
"react-textarea-autosize": "^5.2.1",
"react-toggle": "^4.0.1",
"redis": "^2.7.1",
@ -108,25 +108,26 @@
"redux-thunk": "^2.2.0",
"requestidlecallback": "^0.3.0",
"reselect": "^3.0.1",
"resolve-url-loader": "^2.2.0",
"rimraf": "^2.6.1",
"sass-loader": "^6.0.6",
"sass-loader": "^7.0.3",
"stringz": "^0.3.0",
"style-loader": "^0.19.0",
"style-loader": "^0.21.0",
"substring-trie": "^1.0.2",
"throng": "^4.0.0",
"tiny-queue": "^0.2.1",
"uglifyjs-webpack-plugin": "^1.2.7",
"uuid": "^3.1.0",
"uws": "^8.14.0",
"webpack": "^3.9.1",
"webpack-bundle-analyzer": "^2.9.1",
"webpack-manifest-plugin": "^1.2.1",
"webpack-merge": "^4.1.1",
"uws": "10.148.0",
"webpack": "^4.16.0",
"webpack-bundle-analyzer": "^2.13.1",
"webpack-cli": "^3.0.8",
"webpack-manifest-plugin": "^2.0.3",
"webpack-merge": "^4.1.3",
"websocket.js": "^0.1.12",
"whatwg-url": "^6.4.1"
},
"devDependencies": {
"babel-eslint": "^8.2.3",
"babel-eslint": "^8.2.6",
"enzyme": "^3.2.0",
"enzyme-adapter-react-16": "^1.1.0",
"eslint": "^4.19.1",
@ -136,9 +137,9 @@
"eslint-plugin-react": "^7.8.2",
"jest": "^21.2.1",
"raf": "^3.4.0",
"react-intl-translations-manager": "^5.0.0",
"react-intl-translations-manager": "^5.0.3",
"react-test-renderer": "^16.2.0",
"webpack-dev-server": "^2.9.5",
"webpack-dev-server": "^3.1.4",
"yargs": "^8.0.2"
},
"optionalDependencies": {

View File

@ -0,0 +1,4 @@
Fabricator(:relay) do
inbox_url "https://example.com/inbox"
enabled true
end

View File

@ -58,13 +58,14 @@ RSpec.describe StreamEntriesHelper, type: :helper do
expect(acct).to eq '@user@foreign_server.com'
end
it 'is the shortname for non embedded local accounts' do
it 'is fully qualified for non embedded local accounts' do
allow(Rails.configuration.x).to receive(:local_domain).and_return('local_domain')
set_not_embedded_view
account = Account.new(domain: nil, username: 'user')
acct = helper.acct(account)
expect(acct).to eq '@user'
expect(acct).to eq '@user@local_domain'
end
end

View File

@ -57,7 +57,7 @@ describe LanguageDetector do
end
it 'detects spanish language' do
string = 'Obtener un Hola y bienvenidos a Mastodon'
string = 'Obtener un Hola y bienvenidos a Mastodon. Obtener un Hola y bienvenidos a Mastodon. Obtener un Hola y bienvenidos a Mastodon. Obtener un Hola y bienvenidos a Mastodon'
result = described_class.instance.detect(string, account_without_user_locale)
expect(result).to eq :es
@ -86,7 +86,7 @@ describe LanguageDetector do
account = double(user_locale: 'fr')
result = described_class.instance.detect('', account)
expect(result).to eq :fr
expect(result).to eq nil
end
it 'uses nil when account is present but has no locale' do

View File

@ -0,0 +1,4 @@
require 'rails_helper'
RSpec.describe Relay, type: :model do
end

View File

@ -192,7 +192,7 @@ const startWorker = (workerId) => {
return;
}
client.query('SELECT oauth_access_tokens.resource_owner_id, users.account_id, users.filtered_languages FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL LIMIT 1', [token], (err, result) => {
client.query('SELECT oauth_access_tokens.resource_owner_id, users.account_id, users.chosen_languages FROM oauth_access_tokens INNER JOIN users ON oauth_access_tokens.resource_owner_id = users.id WHERE oauth_access_tokens.token = $1 AND oauth_access_tokens.revoked_at IS NULL LIMIT 1', [token], (err, result) => {
done();
if (err) {
@ -209,7 +209,7 @@ const startWorker = (workerId) => {
}
req.accountId = result.rows[0].account_id;
req.filteredLanguages = result.rows[0].filtered_languages;
req.chosenLanguages = result.rows[0].chosen_languages;
next();
});
@ -340,7 +340,7 @@ const startWorker = (workerId) => {
const targetAccountIds = [unpackedPayload.account.id].concat(unpackedPayload.mentions.map(item => item.id));
const accountDomain = unpackedPayload.account.acct.split('@')[1];
if (Array.isArray(req.filteredLanguages) && req.filteredLanguages.indexOf(unpackedPayload.language) !== -1) {
if (Array.isArray(req.chosenLanguages) && unpackedPayload.language !== null && req.chosenLanguages.indexOf(unpackedPayload.language) === -1) {
log.silly(req.requestId, `Message ${unpackedPayload.id} filtered by language (${unpackedPayload.language})`);
return;
}

4230
yarn.lock

File diff suppressed because it is too large Load Diff