From a1d091558555c8a58f7f63c4a9c3002421ea9041 Mon Sep 17 00:00:00 2001 From: unarist Date: Tue, 27 Mar 2018 12:22:58 +0900 Subject: [PATCH 001/442] Add a spec for UniqueUsernameValidator (#6927) Note that this spec has a pending test about dots in the username, because allowing it has been reverted for now. --- .../unique_username_validator_spec.rb | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 spec/validators/unique_username_validator_spec.rb diff --git a/spec/validators/unique_username_validator_spec.rb b/spec/validators/unique_username_validator_spec.rb new file mode 100644 index 000000000..b9d773bed --- /dev/null +++ b/spec/validators/unique_username_validator_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe UniqueUsernameValidator do + describe '#validate' do + it 'does not add errors if username is nil' do + account = double(username: nil, persisted?: false, errors: double(add: nil)) + subject.validate(account) + expect(account.errors).to_not have_received(:add) + end + + it 'does not add errors when existing one is subject itself' do + account = Fabricate(:account, username: 'abcdef') + expect(account).to be_valid + end + + it 'adds an error when the username is already used with ignoring dots' do + pending 'allowing dots in username is still in development' + Fabricate(:account, username: 'abcd.ef') + account = double(username: 'ab.cdef', persisted?: false, errors: double(add: nil)) + subject.validate(account) + expect(account.errors).to have_received(:add) + end + + it 'adds an error when the username is already used with ignoring cases' do + Fabricate(:account, username: 'ABCdef') + account = double(username: 'abcDEF', persisted?: false, errors: double(add: nil)) + subject.validate(account) + expect(account.errors).to have_received(:add) + end + end +end From 31e7b7308489ecc8b43f83b78ec0a288c4195d5b Mon Sep 17 00:00:00 2001 From: Yuto Tokunaga Date: Tue, 27 Mar 2018 19:30:28 +0900 Subject: [PATCH 002/442] fix #6846 (#6914) --- app/javascript/styles/mastodon/components.scss | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 1fb1fa851..2b13b80a7 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -3436,6 +3436,19 @@ a.status-card { width: 100%; height: 100%; position: relative; + + .extended-video-player { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + + video { + max-width: $media-modal-media-max-width; + max-height: $media-modal-media-max-height; + } + } } .media-modal__closer { @@ -4411,8 +4424,8 @@ a.status-card { border-radius: 4px; video { - height: 100%; - width: 100%; + max-width: 100vw; + max-height: 80vh; z-index: 1; } From ca42f9b0ebfa1f4e8e86745a79af138b5865daee Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 27 Mar 2018 19:32:30 +0900 Subject: [PATCH 003/442] Cache media (#6902) --- app/javascript/mastodon/actions/accounts.js | 2 +- .../mastodon/actions/importer/index.js | 2 +- app/javascript/mastodon/actions/statuses.js | 4 +- app/javascript/mastodon/db/modifier.js | 93 ----------- .../mastodon/service_worker/entry.js | 30 +++- .../mastodon/{db/async.js => storage/db.js} | 0 app/javascript/mastodon/storage/modifier.js | 151 ++++++++++++++++++ config/webpack/production.js | 2 +- package.json | 1 + yarn.lock | 7 + 10 files changed, 189 insertions(+), 103 deletions(-) delete mode 100644 app/javascript/mastodon/db/modifier.js rename app/javascript/mastodon/{db/async.js => storage/db.js} (100%) create mode 100644 app/javascript/mastodon/storage/modifier.js diff --git a/app/javascript/mastodon/actions/accounts.js b/app/javascript/mastodon/actions/accounts.js index 1d1947aca..7cacff909 100644 --- a/app/javascript/mastodon/actions/accounts.js +++ b/app/javascript/mastodon/actions/accounts.js @@ -1,5 +1,5 @@ import api, { getLinks } from '../api'; -import asyncDB from '../db/async'; +import asyncDB from '../storage/db'; import { importAccount, importFetchedAccount, importFetchedAccounts } from './importer'; export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'; diff --git a/app/javascript/mastodon/actions/importer/index.js b/app/javascript/mastodon/actions/importer/index.js index a97f4d173..e671d417c 100644 --- a/app/javascript/mastodon/actions/importer/index.js +++ b/app/javascript/mastodon/actions/importer/index.js @@ -1,4 +1,4 @@ -import { putAccounts, putStatuses } from '../../db/modifier'; +import { putAccounts, putStatuses } from '../../storage/modifier'; import { normalizeAccount, normalizeStatus } from './normalizer'; export const ACCOUNT_IMPORT = 'ACCOUNT_IMPORT'; diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index dcd813dd9..d28aef880 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -1,6 +1,6 @@ import api from '../api'; -import asyncDB from '../db/async'; -import { evictStatus } from '../db/modifier'; +import asyncDB from '../storage/db'; +import { evictStatus } from '../storage/modifier'; import { deleteFromTimelines } from './timelines'; import { fetchStatusCard } from './cards'; diff --git a/app/javascript/mastodon/db/modifier.js b/app/javascript/mastodon/db/modifier.js deleted file mode 100644 index eb951905a..000000000 --- a/app/javascript/mastodon/db/modifier.js +++ /dev/null @@ -1,93 +0,0 @@ -import asyncDB from './async'; - -const limit = 1024; - -function put(name, objects, callback) { - asyncDB.then(db => { - const putTransaction = db.transaction(name, 'readwrite'); - const putStore = putTransaction.objectStore(name); - const putIndex = putStore.index('id'); - - objects.forEach(object => { - function add() { - putStore.add(object); - } - - putIndex.getKey(object.id).onsuccess = retrieval => { - if (retrieval.target.result) { - putStore.delete(retrieval.target.result).onsuccess = add; - } else { - add(); - } - }; - }); - - putTransaction.oncomplete = () => { - const readTransaction = db.transaction(name, 'readonly'); - const readStore = readTransaction.objectStore(name); - - readStore.count().onsuccess = count => { - const excess = count.target.result - limit; - - if (excess > 0) { - readStore.getAll(null, excess).onsuccess = - retrieval => callback(retrieval.target.result.map(({ id }) => id)); - } - }; - }; - }); -} - -export function evictAccounts(ids) { - asyncDB.then(db => { - const transaction = db.transaction(['accounts', 'statuses'], 'readwrite'); - const accounts = transaction.objectStore('accounts'); - const accountsIdIndex = accounts.index('id'); - const accountsMovedIndex = accounts.index('moved'); - const statuses = transaction.objectStore('statuses'); - const statusesIndex = statuses.index('account'); - - function evict(toEvict) { - toEvict.forEach(id => { - accountsMovedIndex.getAllKeys(id).onsuccess = - ({ target }) => evict(target.result); - - statusesIndex.getAll(id).onsuccess = - ({ target }) => evictStatuses(target.result.map(({ id }) => id)); - - accountsIdIndex.getKey(id).onsuccess = - ({ target }) => target.result && accounts.delete(target.result); - }); - } - - evict(ids); - }); -} - -export function evictStatus(id) { - return evictStatuses([id]); -} - -export function evictStatuses(ids) { - asyncDB.then(db => { - const store = db.transaction('statuses', 'readwrite').objectStore('statuses'); - const idIndex = store.index('id'); - const reblogIndex = store.index('reblog'); - - ids.forEach(id => { - reblogIndex.getAllKeys(id).onsuccess = - ({ target }) => target.result.forEach(reblogKey => store.delete(reblogKey)); - - idIndex.getKey(id).onsuccess = - ({ target }) => target.result && store.delete(target.result); - }); - }); -} - -export function putAccounts(records) { - put('accounts', records, evictAccounts); -} - -export function putStatuses(records) { - put('statuses', records, evictStatuses); -} diff --git a/app/javascript/mastodon/service_worker/entry.js b/app/javascript/mastodon/service_worker/entry.js index 8b65f27a3..b9cf06ef9 100644 --- a/app/javascript/mastodon/service_worker/entry.js +++ b/app/javascript/mastodon/service_worker/entry.js @@ -1,6 +1,10 @@ import './web_push_notifications'; -function openCache() { +function openSystemCache() { + return caches.open('mastodon-system'); +} + +function openWebCache() { return caches.open('mastodon-web'); } @@ -11,7 +15,7 @@ function fetchRoot() { // 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. self.addEventListener('install', function(event) { - event.waitUntil(Promise.all([openCache(), fetchRoot()]).then(([cache, root]) => cache.put('/', root))); + event.waitUntil(Promise.all([openWebCache(), fetchRoot()]).then(([cache, root]) => cache.put('/', root))); }); self.addEventListener('activate', function(event) { event.waitUntil(self.clients.claim()); @@ -21,7 +25,7 @@ self.addEventListener('fetch', function(event) { if (url.pathname.startsWith('/web/')) { const asyncResponse = fetchRoot(); - const asyncCache = openCache(); + const asyncCache = openWebCache(); event.respondWith(asyncResponse.then(async response => { if (response.ok) { @@ -31,10 +35,10 @@ self.addEventListener('fetch', function(event) { } throw null; - }).catch(() => caches.match('/'))); + }).catch(() => asyncCache.then(cache => cache.match('/')))); } else if (url.pathname === '/auth/sign_out') { const asyncResponse = fetch(event.request); - const asyncCache = openCache(); + const asyncCache = openWebCache(); event.respondWith(asyncResponse.then(async response => { if (response.ok || response.type === 'opaqueredirect') { @@ -44,5 +48,21 @@ self.addEventListener('fetch', function(event) { return response; })); + } else if (process.env.CDN_HOST ? url.host === process.env.CDN_HOST : url.pathname.startsWith('/system/')) { + event.respondWith(openSystemCache().then(async cache => { + const cached = await cache.match(event.request.url); + + if (cached === undefined) { + const fetched = await fetch(event.request); + + if (fetched.ok) { + await cache.put(event.request.url, fetched); + } + + return fetched.clone(); + } + + return cached; + })); } }); diff --git a/app/javascript/mastodon/db/async.js b/app/javascript/mastodon/storage/db.js similarity index 100% rename from app/javascript/mastodon/db/async.js rename to app/javascript/mastodon/storage/db.js diff --git a/app/javascript/mastodon/storage/modifier.js b/app/javascript/mastodon/storage/modifier.js new file mode 100644 index 000000000..63e49fe6e --- /dev/null +++ b/app/javascript/mastodon/storage/modifier.js @@ -0,0 +1,151 @@ +import asyncDB from './db'; +import { autoPlayGif } from '../initial_state'; + +const accountAssetKeys = ['avatar', 'avatar_static', 'header', 'header_static']; +const avatarKey = autoPlayGif ? 'avatar' : 'avatar_static'; +const limit = 1024; +const asyncCache = caches.open('mastodon-system'); + +function put(name, objects, onupdate, oncreate) { + return asyncDB.then(db => new Promise((resolve, reject) => { + const putTransaction = db.transaction(name, 'readwrite'); + const putStore = putTransaction.objectStore(name); + const putIndex = putStore.index('id'); + + objects.forEach(object => { + putIndex.getKey(object.id).onsuccess = retrieval => { + function addObject() { + putStore.add(object); + } + + function deleteObject() { + putStore.delete(retrieval.target.result).onsuccess = addObject; + } + + if (retrieval.target.result) { + if (onupdate) { + onupdate(object, retrieval.target.result, putStore, deleteObject); + } else { + deleteObject(); + } + } else { + if (oncreate) { + oncreate(object, addObject); + } else { + addObject(); + } + } + }; + }); + + putTransaction.oncomplete = () => { + const readTransaction = db.transaction(name, 'readonly'); + const readStore = readTransaction.objectStore(name); + const count = readStore.count(); + + count.onsuccess = () => { + const excess = count.result - limit; + + if (excess > 0) { + const retrieval = readStore.getAll(null, excess); + + retrieval.onsuccess = () => resolve(retrieval.result); + retrieval.onerror = reject; + } else { + resolve([]); + } + }; + + count.onerror = reject; + }; + + putTransaction.onerror = reject; + })); +} + +function evictAccountsByRecords(records) { + asyncDB.then(db => { + const transaction = db.transaction(['accounts', 'statuses'], 'readwrite'); + const accounts = transaction.objectStore('accounts'); + const accountsIdIndex = accounts.index('id'); + const accountsMovedIndex = accounts.index('moved'); + const statuses = transaction.objectStore('statuses'); + const statusesIndex = statuses.index('account'); + + function evict(toEvict) { + toEvict.forEach(record => { + asyncCache.then(cache => accountAssetKeys.forEach(key => cache.delete(records[key]))); + + accountsMovedIndex.getAll(record.id).onsuccess = ({ target }) => evict(target.result); + + statusesIndex.getAll(record.id).onsuccess = + ({ target }) => evictStatusesByRecords(target.result); + + accountsIdIndex.getKey(record.id).onsuccess = + ({ target }) => target.result && accounts.delete(target.result); + }); + } + + evict(records); + }); +} + +export function evictStatus(id) { + return evictStatuses([id]); +} + +export function evictStatuses(ids) { + asyncDB.then(db => { + const store = db.transaction('statuses', 'readwrite').objectStore('statuses'); + const idIndex = store.index('id'); + const reblogIndex = store.index('reblog'); + + ids.forEach(id => { + reblogIndex.getAllKeys(id).onsuccess = + ({ target }) => target.result.forEach(reblogKey => store.delete(reblogKey)); + + idIndex.getKey(id).onsuccess = + ({ target }) => target.result && store.delete(target.result); + }); + }); +} + +function evictStatusesByRecords(records) { + evictStatuses(records.map(({ id }) => id)); +} + +export function putAccounts(records) { + const newURLs = []; + + put('accounts', records, (newRecord, oldKey, store, oncomplete) => { + store.get(oldKey).onsuccess = ({ target }) => { + accountAssetKeys.forEach(key => { + const newURL = newRecord[key]; + const oldURL = target.result[key]; + + if (newURL !== oldURL) { + asyncCache.then(cache => cache.delete(oldURL)); + } + }); + + const newURL = newRecord[avatarKey]; + const oldURL = target.result[avatarKey]; + + if (newURL !== oldURL) { + newURLs.push(newURL); + } + + oncomplete(); + }; + }, (newRecord, oncomplete) => { + newURLs.push(newRecord[avatarKey]); + oncomplete(); + }).then(records => { + evictAccountsByRecords(records); + asyncCache.then(cache => cache.addAll(newURLs)); + }); +} + +export function putStatuses(records) { + put('statuses', records).then(evictStatusesByRecords); +} diff --git a/config/webpack/production.js b/config/webpack/production.js index e2d7f11dc..e1c681232 100644 --- a/config/webpack/production.js +++ b/config/webpack/production.js @@ -90,7 +90,7 @@ module.exports = merge(sharedConfig, { '**/*.woff', ], ServiceWorker: { - entry: path.join(__dirname, '../../app/javascript/mastodon/service_worker/entry.js'), + entry: `imports-loader?process.env=>${encodeURIComponent(JSON.stringify(process.env))}!${encodeURI(path.join(__dirname, '../../app/javascript/mastodon/service_worker/entry.js'))}`, cacheName: 'mastodon', output: '../assets/sw.js', publicPath: '/sw.js', diff --git a/package.json b/package.json index 33853516b..76f665dba 100644 --- a/package.json +++ b/package.json @@ -56,6 +56,7 @@ "glob": "^7.1.1", "http-link-header": "^0.8.0", "immutable": "^3.8.2", + "imports-loader": "^0.8.0", "intersection-observer": "^0.5.0", "intl": "^1.2.5", "intl-messageformat": "^2.2.0", diff --git a/yarn.lock b/yarn.lock index fbce624be..a1dd4c694 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3364,6 +3364,13 @@ import-local@^0.1.1: pkg-dir "^2.0.0" resolve-cwd "^2.0.0" +imports-loader@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/imports-loader/-/imports-loader-0.8.0.tgz#030ea51b8ca05977c40a3abfd9b4088fe0be9a69" + dependencies: + loader-utils "^1.0.2" + source-map "^0.6.1" + imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" From 2f3ac14a434c773577771b74292aa313d57db3c9 Mon Sep 17 00:00:00 2001 From: unarist Date: Tue, 27 Mar 2018 20:05:59 +0900 Subject: [PATCH 004/442] Add missing null handling in notification reducer (#6930) This patch adds null item (i.e. gap) handling on below functions to avoid TypeError. * `filterNotifications` called on user mute/block * `deleteByStatus` called on status deletion --- app/javascript/mastodon/reducers/notifications.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js index f023984b8..1ac7eb706 100644 --- a/app/javascript/mastodon/reducers/notifications.js +++ b/app/javascript/mastodon/reducers/notifications.js @@ -82,7 +82,7 @@ const expandNormalizedNotifications = (state, notifications, next) => { }; const filterNotifications = (state, relationship) => { - return state.update('items', list => list.filterNot(item => item.get('account') === relationship.id)); + return state.update('items', list => list.filterNot(item => item !== null && item.get('account') === relationship.id)); }; const updateTop = (state, top) => { @@ -94,7 +94,7 @@ const updateTop = (state, top) => { }; const deleteByStatus = (state, statusId) => { - return state.update('items', list => list.filterNot(item => item.get('status') === statusId)); + return state.update('items', list => list.filterNot(item => item !== null && item.get('status') === statusId)); }; export default function notifications(state = initialState, action) { From f5ed5f386020a08e8a659f4a6d25d2b875852be8 Mon Sep 17 00:00:00 2001 From: unarist Date: Tue, 27 Mar 2018 22:18:35 +0900 Subject: [PATCH 005/442] Clone response before put it to the cache (#6932) `Response.prototype.clone()` must be called before the response used. This fixes an error from ServiceWorker and failing to load image when the image is not cached. --- app/javascript/mastodon/service_worker/entry.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/service_worker/entry.js b/app/javascript/mastodon/service_worker/entry.js index b9cf06ef9..160c3fbf2 100644 --- a/app/javascript/mastodon/service_worker/entry.js +++ b/app/javascript/mastodon/service_worker/entry.js @@ -56,10 +56,10 @@ self.addEventListener('fetch', function(event) { const fetched = await fetch(event.request); if (fetched.ok) { - await cache.put(event.request.url, fetched); + await cache.put(event.request.url, fetched.clone()); } - return fetched.clone(); + return fetched; } return cached; From 3523aa440ba3f52bf28fe1e9707506d327c4431f Mon Sep 17 00:00:00 2001 From: unarist Date: Tue, 27 Mar 2018 23:53:52 +0900 Subject: [PATCH 006/442] Fix LoadMore on account media gallery (#6933) max_id in the fetch request should be a status id, but media attachment id was used. --- app/javascript/mastodon/features/account_gallery/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/account_gallery/index.js b/app/javascript/mastodon/features/account_gallery/index.js index 9a40d139c..5f564d3a9 100644 --- a/app/javascript/mastodon/features/account_gallery/index.js +++ b/app/javascript/mastodon/features/account_gallery/index.js @@ -67,7 +67,7 @@ export default class AccountGallery extends ImmutablePureComponent { handleScrollToBottom = () => { if (this.props.hasMore) { - this.handleLoadMore(this.props.medias.last().get('id')); + this.handleLoadMore(this.props.medias.last().getIn(['status', 'id'])); } } From 4f9136d2d55e1547c84fc394c0e5e1bb259d58d2 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Thu, 29 Mar 2018 03:40:18 +0900 Subject: [PATCH 007/442] Document CORS requirement for asset host (#6941) --- .env.production.sample | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.env.production.sample b/.env.production.sample index 1e5ed9f3d..9de2c0650 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -81,6 +81,10 @@ SMTP_FROM_ADDRESS=notifications@example.com # PAPERCLIP_ROOT_URL=/system # Optional asset host for multi-server setups +# The asset host must allow cross origin request from WEB_DOMAIN or LOCAL_DOMAIN +# if WEB_DOMAIN is not set. For example, the server may have the +# following header field: +# Access-Control-Allow-Origin: https://example.com/ # CDN_HOST=https://assets.example.com # S3 (optional) From 5021c4e9ca78881f5379a18185a46e580b8f2c34 Mon Sep 17 00:00:00 2001 From: Yuto Tokunaga Date: Thu, 29 Mar 2018 03:40:51 +0900 Subject: [PATCH 008/442] Add double-tap zoom functionary to `ZoomableImage` (#6944) add to keep margin of the image on zooming move setting `scrollLeft` and `scrollTop` of container from callback of `setState` to `componentDidUpdate` add 'hammerjs' package for touch gesture detection rewrite `ZoomableImage` using 'hammerjs' --- .../features/ui/components/zoomable_image.js | 175 ++++++++++-------- .../styles/mastodon/components.scss | 3 - package.json | 1 + yarn.lock | 4 + 4 files changed, 103 insertions(+), 80 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/zoomable_image.js b/app/javascript/mastodon/features/ui/components/zoomable_image.js index 0a0a4d41a..0cae0862d 100644 --- a/app/javascript/mastodon/features/ui/components/zoomable_image.js +++ b/app/javascript/mastodon/features/ui/components/zoomable_image.js @@ -1,16 +1,10 @@ import React from 'react'; import PropTypes from 'prop-types'; +import Hammer from 'hammerjs'; const MIN_SCALE = 1; const MAX_SCALE = 4; - -const getMidpoint = (p1, p2) => ({ - x: (p1.clientX + p2.clientX) / 2, - y: (p1.clientY + p2.clientY) / 2, -}); - -const getDistance = (p1, p2) => - Math.sqrt(Math.pow(p1.clientX - p2.clientX, 2) + Math.pow(p1.clientY - p2.clientY, 2)); +const DOUBLE_TAP_SCALE = 2; const clamp = (min, max, value) => Math.min(max, Math.max(min, value)); @@ -37,83 +31,97 @@ export default class ZoomableImage extends React.PureComponent { removers = []; container = null; image = null; - lastTouchEndTime = 0; - lastDistance = 0; + lastScale = null; + zoomCenter = null; componentDidMount () { - let handler = this.handleTouchStart; - this.container.addEventListener('touchstart', handler); - this.removers.push(() => this.container.removeEventListener('touchstart', handler)); - handler = this.handleTouchMove; - // on Chrome 56+, touch event listeners will default to passive - // https://www.chromestatus.com/features/5093566007214080 - this.container.addEventListener('touchmove', handler, { passive: false }); - this.removers.push(() => this.container.removeEventListener('touchend', handler)); + // register pinch event handlers to the container + let hammer = new Hammer.Manager(this.container, { + // required to make container scrollable by touch + touchAction: 'pan-x pan-y', + }); + hammer.add(new Hammer.Pinch()); + hammer.on('pinchstart', this.handlePinchStart); + hammer.on('pinchmove', this.handlePinchMove); + this.removers.push(() => hammer.off('pinchstart pinchmove')); + + // register tap event handlers + hammer = new Hammer.Manager(this.image); + // NOTE the order of adding is also the order of gesture recognition + hammer.add(new Hammer.Tap({ event: 'doubletap', taps: 2 })); + hammer.add(new Hammer.Tap()); + // prevent the 'tap' event handler be fired on double tap + hammer.get('tap').requireFailure('doubletap'); + // NOTE 'tap' and 'doubletap' events are fired by touch and *mouse* + hammer.on('tap', this.handleTap); + hammer.on('doubletap', this.handleDoubleTap); + this.removers.push(() => hammer.off('tap doubletap')); } componentWillUnmount () { this.removeEventListeners(); } + componentDidUpdate (prevProps, prevState) { + if (!this.zoomCenter) return; + + const { x: cx, y: cy } = this.zoomCenter; + const { scale: prevScale } = prevState; + const { scale: nextScale } = this.state; + const { scrollLeft, scrollTop } = this.container; + + // math memo: + // x = (scrollLeft + cx) / scrollWidth + // x' = (nextScrollLeft + cx) / nextScrollWidth + // scrollWidth = clientWidth * prevScale + // scrollWidth' = clientWidth * nextScale + // Solve x = x' for nextScrollLeft + const nextScrollLeft = (scrollLeft + cx) * nextScale / prevScale - cx; + const nextScrollTop = (scrollTop + cy) * nextScale / prevScale - cy; + + this.container.scrollLeft = nextScrollLeft; + this.container.scrollTop = nextScrollTop; + } + removeEventListeners () { this.removers.forEach(listeners => listeners()); this.removers = []; } - handleTouchStart = e => { - if (e.touches.length !== 2) return; - - this.lastDistance = getDistance(...e.touches); - } - - handleTouchMove = e => { - const { scrollTop, scrollHeight, clientHeight } = this.container; - if (e.touches.length === 1 && scrollTop !== scrollHeight - clientHeight) { - // prevent propagating event to MediaModal - e.stopPropagation(); - return; - } - if (e.touches.length !== 2) return; - - e.preventDefault(); - e.stopPropagation(); - - const distance = getDistance(...e.touches); - const midpoint = getMidpoint(...e.touches); - const scale = clamp(MIN_SCALE, MAX_SCALE, this.state.scale * distance / this.lastDistance); - - this.zoom(scale, midpoint); - - this.lastMidpoint = midpoint; - this.lastDistance = distance; - } - - zoom(nextScale, midpoint) { - const { scale } = this.state; - const { scrollLeft, scrollTop } = this.container; - - // math memo: - // x = (scrollLeft + midpoint.x) / scrollWidth - // x' = (nextScrollLeft + midpoint.x) / nextScrollWidth - // scrollWidth = clientWidth * scale - // scrollWidth' = clientWidth * nextScale - // Solve x = x' for nextScrollLeft - const nextScrollLeft = (scrollLeft + midpoint.x) * nextScale / scale - midpoint.x; - const nextScrollTop = (scrollTop + midpoint.y) * nextScale / scale - midpoint.y; - - this.setState({ scale: nextScale }, () => { - this.container.scrollLeft = nextScrollLeft; - this.container.scrollTop = nextScrollTop; - }); - } - handleClick = e => { - // don't propagate event to MediaModal + // prevent the click event propagated to parent e.stopPropagation(); + + // the tap event handler is executed at the same time by touch and mouse, + // so we don't need to execute the onClick handler here + } + + handlePinchStart = () => { + this.lastScale = this.state.scale; + } + + handlePinchMove = e => { + const scale = clamp(MIN_SCALE, MAX_SCALE, this.lastScale * e.scale); + this.zoom(scale, e.center); + } + + handleTap = () => { const handler = this.props.onClick; if (handler) handler(); } + handleDoubleTap = e => { + if (this.state.scale === MIN_SCALE) + this.zoom(DOUBLE_TAP_SCALE, e.center); + else + this.zoom(MIN_SCALE, e.center); + } + + zoom (scale, center) { + this.zoomCenter = center; + this.setState({ scale }); + } + setContainerRef = c => { this.container = c; } @@ -126,6 +134,18 @@ export default class ZoomableImage extends React.PureComponent { const { alt, src } = this.props; const { scale } = this.state; const overflow = scale === 1 ? 'hidden' : 'scroll'; + const marginStyle = { + position: 'absolute', + top: 0, + bottom: 0, + left: 0, + right: 0, + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + transform: `scale(${scale})`, + transformOrigin: '0 0', + }; return (
- {alt} +
+ {alt} +
); } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 2b13b80a7..447e6bc8e 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1459,9 +1459,6 @@ position: relative; width: 100%; height: 100%; - display: flex; - align-items: center; - justify-content: center; img { max-width: $media-modal-media-max-width; diff --git a/package.json b/package.json index 76f665dba..dfba49afc 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "file-loader": "^0.11.2", "font-awesome": "^4.7.0", "glob": "^7.1.1", + "hammerjs": "^2.0.8", "http-link-header": "^0.8.0", "immutable": "^3.8.2", "imports-loader": "^0.8.0", diff --git a/yarn.lock b/yarn.lock index a1dd4c694..a306ebf55 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3092,6 +3092,10 @@ gzip-size@^3.0.0: dependencies: duplexer "^0.1.1" +hammerjs@^2.0.8: + version "2.0.8" + resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" + handle-thing@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" From 9ed5eebd7ce8381af77dd2918678202a0776af4a Mon Sep 17 00:00:00 2001 From: ThibG Date: Thu, 29 Mar 2018 00:52:24 +0200 Subject: [PATCH 009/442] Do not ignore unknown media attachments, only skip them (#6948) That way, they are displayed in a list below the corresponding toot. --- app/lib/activitypub/activity/create.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 676e885c0..afee8a268 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -113,13 +113,13 @@ class ActivityPub::Activity::Create < ActivityPub::Activity media_attachments = [] as_array(@object['attachment']).each do |attachment| - next if unsupported_media_type?(attachment['mediaType']) || attachment['url'].blank? + next if attachment['url'].blank? href = Addressable::URI.parse(attachment['url']).normalize.to_s media_attachment = MediaAttachment.create(account: @account, remote_url: href, description: attachment['name'].presence, focus: attachment['focalPoint']) media_attachments << media_attachment - next if skip_download? + next if unsupported_media_type?(attachment['mediaType']) || skip_download? media_attachment.file_remote_url = href media_attachment.save From 41452e83028148b3ef6e5888e9bd3b16fd608ec7 Mon Sep 17 00:00:00 2001 From: unarist Date: Thu, 29 Mar 2018 19:59:12 +0900 Subject: [PATCH 010/442] Fix TypeError on follow notification (#6950) `notification.status` may not be present, e.g. follow notification. --- app/javascript/mastodon/actions/notifications.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index 7267b85bd..da77afbe0 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -43,7 +43,9 @@ export function updateNotifications(notification, intlMessages, intlLocale) { const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true); dispatch(importFetchedAccount(notification.account)); - dispatch(importFetchedStatus(notification.status)); + if (notification.status) { + dispatch(importFetchedStatus(notification.status)); + } dispatch({ type: NOTIFICATIONS_UPDATE, From f1f846045f26518525d5484ed9e782b73086ebe4 Mon Sep 17 00:00:00 2001 From: unarist Date: Thu, 29 Mar 2018 21:57:02 +0900 Subject: [PATCH 011/442] Fix ReferenceError when Cache API is missing (#6953) Cache API is not supported on Safari 11.0 / iOS 11. Since those caching is optional, this patch simply ignores it. --- app/javascript/mastodon/storage/modifier.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/storage/modifier.js b/app/javascript/mastodon/storage/modifier.js index 63e49fe6e..1bec04d0f 100644 --- a/app/javascript/mastodon/storage/modifier.js +++ b/app/javascript/mastodon/storage/modifier.js @@ -4,7 +4,10 @@ import { autoPlayGif } from '../initial_state'; const accountAssetKeys = ['avatar', 'avatar_static', 'header', 'header_static']; const avatarKey = autoPlayGif ? 'avatar' : 'avatar_static'; const limit = 1024; -const asyncCache = caches.open('mastodon-system'); + +// ServiceWorker and Cache API is not available on iOS 11 +// https://webkit.org/status/#specification-service-workers +const asyncCache = window.caches ? caches.open('mastodon-system') : Promise.reject(); function put(name, objects, onupdate, oncreate) { return asyncDB.then(db => new Promise((resolve, reject) => { From d1f34151aee564bb1e60ee48107797681c869a81 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Thu, 29 Mar 2018 19:08:34 +0200 Subject: [PATCH 012/442] Feature: Direct message from menu (#6956) * Implement ability to send direct messages from the user menu * Implement message warning users that direct messages are visible to all mentioned users * Update locales --- app/javascript/mastodon/actions/compose.js | 14 ++++++++++++++ .../features/account/components/action_bar.js | 3 +++ .../features/account_timeline/components/header.js | 6 ++++++ .../containers/header_container.js | 9 ++++++++- .../compose/containers/warning_container.js | 7 ++++++- app/javascript/mastodon/locales/ar.json | 2 ++ app/javascript/mastodon/locales/bg.json | 2 ++ app/javascript/mastodon/locales/ca.json | 2 ++ app/javascript/mastodon/locales/de.json | 2 ++ .../mastodon/locales/defaultMessages.json | 8 ++++++++ app/javascript/mastodon/locales/en.json | 2 ++ app/javascript/mastodon/locales/eo.json | 2 ++ app/javascript/mastodon/locales/es.json | 2 ++ app/javascript/mastodon/locales/fa.json | 2 ++ app/javascript/mastodon/locales/fi.json | 2 ++ app/javascript/mastodon/locales/fr.json | 2 ++ app/javascript/mastodon/locales/gl.json | 2 ++ app/javascript/mastodon/locales/he.json | 2 ++ app/javascript/mastodon/locales/hr.json | 2 ++ app/javascript/mastodon/locales/hu.json | 2 ++ app/javascript/mastodon/locales/hy.json | 2 ++ app/javascript/mastodon/locales/id.json | 2 ++ app/javascript/mastodon/locales/io.json | 2 ++ app/javascript/mastodon/locales/it.json | 2 ++ app/javascript/mastodon/locales/ja.json | 2 ++ app/javascript/mastodon/locales/ko.json | 2 ++ app/javascript/mastodon/locales/nl.json | 2 ++ app/javascript/mastodon/locales/no.json | 2 ++ app/javascript/mastodon/locales/oc.json | 2 ++ app/javascript/mastodon/locales/pl.json | 2 ++ app/javascript/mastodon/locales/pt-BR.json | 2 ++ app/javascript/mastodon/locales/pt.json | 2 ++ app/javascript/mastodon/locales/ru.json | 2 ++ app/javascript/mastodon/locales/sk.json | 2 ++ app/javascript/mastodon/locales/sr-Latn.json | 2 ++ app/javascript/mastodon/locales/sr.json | 2 ++ app/javascript/mastodon/locales/sv.json | 2 ++ app/javascript/mastodon/locales/th.json | 2 ++ app/javascript/mastodon/locales/tr.json | 2 ++ app/javascript/mastodon/locales/uk.json | 2 ++ app/javascript/mastodon/locales/zh-CN.json | 2 ++ app/javascript/mastodon/locales/zh-HK.json | 2 ++ app/javascript/mastodon/locales/zh-TW.json | 2 ++ app/javascript/mastodon/reducers/compose.js | 7 +++++++ 44 files changed, 126 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 5e7cdd270..2138f9426 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -15,6 +15,7 @@ export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS'; export const COMPOSE_SUBMIT_FAIL = 'COMPOSE_SUBMIT_FAIL'; export const COMPOSE_REPLY = 'COMPOSE_REPLY'; export const COMPOSE_REPLY_CANCEL = 'COMPOSE_REPLY_CANCEL'; +export const COMPOSE_DIRECT = 'COMPOSE_DIRECT'; export const COMPOSE_MENTION = 'COMPOSE_MENTION'; export const COMPOSE_RESET = 'COMPOSE_RESET'; export const COMPOSE_UPLOAD_REQUEST = 'COMPOSE_UPLOAD_REQUEST'; @@ -91,6 +92,19 @@ export function mentionCompose(account, router) { }; }; +export function directCompose(account, router) { + return (dispatch, getState) => { + dispatch({ + type: COMPOSE_DIRECT, + account: account, + }); + + if (!getState().getIn(['compose', 'mounted'])) { + router.push('/statuses/new'); + } + }; +}; + export function submitCompose() { return function (dispatch, getState) { const status = getState().getIn(['compose', 'text'], ''); diff --git a/app/javascript/mastodon/features/account/components/action_bar.js b/app/javascript/mastodon/features/account/components/action_bar.js index b538fa5fc..23dbf32bc 100644 --- a/app/javascript/mastodon/features/account/components/action_bar.js +++ b/app/javascript/mastodon/features/account/components/action_bar.js @@ -8,6 +8,7 @@ import { me } from '../../../initial_state'; const messages = defineMessages({ mention: { id: 'account.mention', defaultMessage: 'Mention @{name}' }, + direct: { id: 'account.direct', defaultMessage: 'Direct message @{name}' }, edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' }, unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' }, unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' }, @@ -32,6 +33,7 @@ export default class ActionBar extends React.PureComponent { onFollow: PropTypes.func, onBlock: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, + onDirect: PropTypes.func.isRequired, onReblogToggle: PropTypes.func.isRequired, onReport: PropTypes.func.isRequired, onMute: PropTypes.func.isRequired, @@ -53,6 +55,7 @@ export default class ActionBar extends React.PureComponent { let extraInfo = ''; menu.push({ text: intl.formatMessage(messages.mention, { name: account.get('username') }), action: this.props.onMention }); + menu.push({ text: intl.formatMessage(messages.direct, { name: account.get('username') }), action: this.props.onDirect }); if ('share' in navigator) { menu.push({ text: intl.formatMessage(messages.share, { name: account.get('username') }), action: this.handleShare }); diff --git a/app/javascript/mastodon/features/account_timeline/components/header.js b/app/javascript/mastodon/features/account_timeline/components/header.js index 6b88a7a0c..789999dce 100644 --- a/app/javascript/mastodon/features/account_timeline/components/header.js +++ b/app/javascript/mastodon/features/account_timeline/components/header.js @@ -16,6 +16,7 @@ export default class Header extends ImmutablePureComponent { onFollow: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, + onDirect: PropTypes.func.isRequired, onReblogToggle: PropTypes.func.isRequired, onReport: PropTypes.func.isRequired, onMute: PropTypes.func.isRequired, @@ -40,6 +41,10 @@ export default class Header extends ImmutablePureComponent { this.props.onMention(this.props.account, this.context.router.history); } + handleDirect = () => { + this.props.onDirect(this.props.account, this.context.router.history); + } + handleReport = () => { this.props.onReport(this.props.account); } @@ -89,6 +94,7 @@ export default class Header extends ImmutablePureComponent { account={account} onBlock={this.handleBlock} onMention={this.handleMention} + onDirect={this.handleDirect} onReblogToggle={this.handleReblogToggle} onReport={this.handleReport} onMute={this.handleMute} diff --git a/app/javascript/mastodon/features/account_timeline/containers/header_container.js b/app/javascript/mastodon/features/account_timeline/containers/header_container.js index b5e0e9a3f..214441b8a 100644 --- a/app/javascript/mastodon/features/account_timeline/containers/header_container.js +++ b/app/javascript/mastodon/features/account_timeline/containers/header_container.js @@ -9,7 +9,10 @@ import { unblockAccount, unmuteAccount, } from '../../../actions/accounts'; -import { mentionCompose } from '../../../actions/compose'; +import { + mentionCompose, + directCompose, +} from '../../../actions/compose'; import { initMuteModal } from '../../../actions/mutes'; import { initReport } from '../../../actions/reports'; import { openModal } from '../../../actions/modal'; @@ -67,6 +70,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ dispatch(mentionCompose(account, router)); }, + onDirect (account, router) { + dispatch(directCompose(account, router)); + }, + onReblogToggle (account) { if (account.getIn(['relationship', 'showing_reblogs'])) { dispatch(followAccount(account.get('id'), false)); diff --git a/app/javascript/mastodon/features/compose/containers/warning_container.js b/app/javascript/mastodon/features/compose/containers/warning_container.js index 8ee8ea190..efaa02e9e 100644 --- a/app/javascript/mastodon/features/compose/containers/warning_container.js +++ b/app/javascript/mastodon/features/compose/containers/warning_container.js @@ -10,15 +10,19 @@ const APPROX_HASHTAG_RE = /(?:^|[^\/\)\w])#(\w*[a-zA-Z·]\w*)/i; const mapStateToProps = state => ({ needsLockWarning: state.getIn(['compose', 'privacy']) === 'private' && !state.getIn(['accounts', me, 'locked']), hashtagWarning: state.getIn(['compose', 'privacy']) !== 'public' && APPROX_HASHTAG_RE.test(state.getIn(['compose', 'text'])), + directMessageWarning: state.getIn(['compose', 'privacy']) === 'direct', }); -const WarningWrapper = ({ needsLockWarning, hashtagWarning }) => { +const WarningWrapper = ({ needsLockWarning, hashtagWarning, directMessageWarning }) => { if (needsLockWarning) { return }} />} />; } if (hashtagWarning) { return } />; } + if (directMessageWarning) { + return } />; + } return null; }; @@ -26,6 +30,7 @@ const WarningWrapper = ({ needsLockWarning, hashtagWarning }) => { WarningWrapper.propTypes = { needsLockWarning: PropTypes.bool, hashtagWarning: PropTypes.bool, + directMessageWarning: PropTypes.bool, }; export default connect(mapStateToProps)(WarningWrapper); diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index 3d9620793..f9af062d0 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -2,6 +2,7 @@ "account.block": "حظر @{name}", "account.block_domain": "إخفاء كل شيئ قادم من إسم النطاق {domain}", "account.blocked": "محظور", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "قد لا تعكس المعلومات أدناه الملف الشخصي الكامل للمستخدم.", "account.domain_blocked": "النطاق مخفي", "account.edit_profile": "تعديل الملف الشخصي", @@ -56,6 +57,7 @@ "column_header.unpin": "فك التدبيس", "column_subheading.navigation": "التصفح", "column_subheading.settings": "الإعدادات", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "هذا التبويق لن يُدرَج تحت أي وسم كان بما أنه غير مُدرَج. لا يُسمح بالبحث إلّا عن التبويقات العمومية عن طريق الوسوم.", "compose_form.lock_disclaimer": "حسابك ليس {locked}. يمكن لأي شخص متابعتك و عرض المنشورات.", "compose_form.lock_disclaimer.lock": "مقفل", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 39eb05f2a..58795ca37 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -2,6 +2,7 @@ "account.block": "Блокирай", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "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": "Редактирай профила си", @@ -56,6 +57,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 33545d86f..b0ce34c6b 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -2,6 +2,7 @@ "account.block": "Bloca @{name}", "account.block_domain": "Amaga-ho tot de {domain}", "account.blocked": "Bloquejat", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "La informació següent pot reflectir incompleta el perfil de l'usuari.", "account.domain_blocked": "Domini ocult", "account.edit_profile": "Edita el perfil", @@ -56,6 +57,7 @@ "column_header.unpin": "No fixis", "column_subheading.navigation": "Navegació", "column_subheading.settings": "Configuració", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Aquest toot no es mostrarà en cap etiqueta ja que no està llistat. Només els toots públics poden ser cercats per etiqueta.", "compose_form.lock_disclaimer": "El teu compte no està bloquejat {locked}. Tothom pot seguir-te i veure els teus missatges a seguidors.", "compose_form.lock_disclaimer.lock": "blocat", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 7bdb6a3c6..eb0c5056a 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -2,6 +2,7 @@ "account.block": "@{name} blocken", "account.block_domain": "Alles von {domain} verstecken", "account.blocked": "Blockiert", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Das Profil wird möglicherweise unvollständig wiedergegeben.", "account.domain_blocked": "Domain versteckt", "account.edit_profile": "Profil bearbeiten", @@ -56,6 +57,7 @@ "column_header.unpin": "Lösen", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Einstellungen", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Dieser Beitrag wird nicht unter einen dieser Hashtags sichtbar sein, solange er ungelistet ist. Bei einer Suche kann er nicht gefunden werden.", "compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Wer dir folgen will, kann das jederzeit tun und dann auch deine privaten Beiträge sehen.", "compose_form.lock_disclaimer.lock": "gesperrt", diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 76b302f3a..1fe6861c9 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -380,6 +380,10 @@ "defaultMessage": "Mention @{name}", "id": "account.mention" }, + { + "defaultMessage": "Direct message @{name}", + "id": "account.direct" + }, { "defaultMessage": "Edit profile", "id": "account.edit_profile" @@ -804,6 +808,10 @@ { "defaultMessage": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "id": "compose_form.hashtag_warning" + }, + { + "defaultMessage": "This toot will only be visible to all the mentioned users.", + "id": "compose_form.direct_message_warning" } ], "path": "app/javascript/mastodon/features/compose/containers/warning_container.json" diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index de44bd0db..d481596e0 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -2,6 +2,7 @@ "account.block": "Block @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "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", @@ -56,6 +57,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 35d9edf2b..9b00edb00 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -2,6 +2,7 @@ "account.block": "Bloki @{name}", "account.block_domain": "Kaŝi ĉion de {domain}", "account.blocked": "Blokita", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Subaj informoj povas reflekti la profilon de la uzanto nekomplete.", "account.domain_blocked": "Domajno kaŝita", "account.edit_profile": "Redakti profilon", @@ -56,6 +57,7 @@ "column_header.unpin": "Depingli", "column_subheading.navigation": "Navigado", "column_subheading.settings": "Agordado", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Ĉi tiu mesaĝo ne estos listigita per ajna kradvorto. Nur publikaj mesaĝoj estas serĉeblaj per kradvortoj.", "compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaĝojn nur por sekvantoj.", "compose_form.lock_disclaimer.lock": "ŝlosita", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index e69938b0f..9f03b31c1 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -2,6 +2,7 @@ "account.block": "Bloquear", "account.block_domain": "Ocultar todo de {domain}", "account.blocked": "Bloqueado", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "La siguiente información del usuario puede estar incompleta.", "account.domain_blocked": "Dominio oculto", "account.edit_profile": "Editar perfil", @@ -56,6 +57,7 @@ "column_header.unpin": "Dejar de fijar", "column_subheading.navigation": "Navegación", "column_subheading.settings": "Ajustes", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Este toot no se mostrará bajo hashtags porque no es público. Sólo los toots públicos se pueden buscar por hashtag.", "compose_form.lock_disclaimer": "Tu cuenta no está bloqueada. Todos pueden seguirte para ver tus toots solo para seguidores.", "compose_form.lock_disclaimer.lock": "bloqueado", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index c9695d0a4..9421746b1 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -2,6 +2,7 @@ "account.block": "مسدودسازی @{name}", "account.block_domain": "پنهان‌سازی همه چیز از سرور {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "اطلاعات زیر ممکن است نمایهٔ این کاربر را به تمامی نشان ندهد.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "ویرایش نمایه", @@ -56,6 +57,7 @@ "column_header.unpin": "رهاکردن", "column_subheading.navigation": "گشت و گذار", "column_subheading.settings": "تنظیمات", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "حساب شما {locked} نیست. هر کسی می‌تواند پیگیر شما شود و نوشته‌های ویژهٔ پیگیران شما را ببیند.", "compose_form.lock_disclaimer.lock": "قفل", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index cbdffec10..fce441df4 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -2,6 +2,7 @@ "account.block": "Estä @{name}", "account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}", "account.blocked": "Estetty", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Alla olevat käyttäjän profiilitiedot saattavat olla epätäydellisiä.", "account.domain_blocked": "Verkko-osoite piilotettu", "account.edit_profile": "Muokkaa", @@ -56,6 +57,7 @@ "column_header.unpin": "Poista kiinnitys", "column_subheading.navigation": "Navigaatio", "column_subheading.settings": "Asetukset", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Tämä töötti ei tule näkymään hashtag-hauissa, koska se ei näy julkisilla aikajanoilla. Vain julkisia tööttejä voi hakea hashtageilla.", "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille -postauksesi.", "compose_form.lock_disclaimer.lock": "lukittu", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 8c56a7558..6eb34e644 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -2,6 +2,7 @@ "account.block": "Bloquer @{name}", "account.block_domain": "Tout masquer venant de {domain}", "account.blocked": "Bloqué", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Les données ci-dessous peuvent ne pas refléter ce profil dans sa totalité.", "account.domain_blocked": "Domaine caché", "account.edit_profile": "Modifier le profil", @@ -56,6 +57,7 @@ "column_header.unpin": "Retirer", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Paramètres", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Ce pouet ne sera pas listé dans les recherches par hashtag car sa visibilité est réglée sur \"non-listé\". Seuls les pouets avec une visibilité \"publique\" peuvent être recherchés par hashtag.", "compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre et voir vos pouets privés.", "compose_form.lock_disclaimer.lock": "verrouillé", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index c5cedd60a..a0823b93f 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -2,6 +2,7 @@ "account.block": "Bloquear @{name}", "account.block_domain": "Ocultar calquer contido de {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "A información inferior podería mostrar un perfil incompleto da usuaria.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Editar perfil", @@ -56,6 +57,7 @@ "column_header.unpin": "Soltar", "column_subheading.navigation": "Navegación", "column_subheading.settings": "Axustes", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Esta mensaxe non será listada baixo ningunha etiqueta xa que está marcada como non listada. Só os toots públicos poden buscarse por etiquetas.", "compose_form.lock_disclaimer": "A súa conta non está {locked}. Calquera pode seguila para ver as súas mensaxes só-para-seguidoras.", "compose_form.lock_disclaimer.lock": "bloqueado", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index fe6f9bbb1..0e2ee8da4 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -2,6 +2,7 @@ "account.block": "חסימת @{name}", "account.block_domain": "להסתיר הכל מהקהילה {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "המידע להלן עשוי להיות לא עדכני או לא שלם.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "עריכת פרופיל", @@ -56,6 +57,7 @@ "column_header.unpin": "שחרור קיבוע", "column_subheading.navigation": "ניווט", "column_subheading.settings": "אפשרויות", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "חשבונך אינו {locked}. כל אחד יוכל לעקוב אחריך כדי לקרוא את הודעותיך המיועדות לעוקבים בלבד.", "compose_form.lock_disclaimer.lock": "נעול", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index 11cd1bff2..1e8ce8e29 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -2,6 +2,7 @@ "account.block": "Blokiraj @{name}", "account.block_domain": "Sakrij sve sa {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Ovaj korisnik je sa druge instance. Ovaj broj bi mogao biti veći.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Uredi profil", @@ -56,6 +57,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigacija", "column_subheading.settings": "Postavke", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Tvoj račun nije {locked}. Svatko te može slijediti kako bi vidio postove namijenjene samo tvojim sljedbenicima.", "compose_form.lock_disclaimer.lock": "zaključan", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 1ea65768a..deb17c6f4 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -2,6 +2,7 @@ "account.block": "@{name} letiltása", "account.block_domain": "Minden elrejtése innen: {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Az alul található információk hiányosan mutathatják be a felhasználót.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Profil szerkesztése", @@ -56,6 +57,7 @@ "column_header.unpin": "Kitűzés eltávolítása", "column_subheading.navigation": "Navigáció", "column_subheading.settings": "Beállítások", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Ezen tülkölés nem fog megjelenni semmilyen hashtag alatt mivel listázatlan. Csak a publikus tülkölések kereshetőek hashtag-el.", "compose_form.lock_disclaimer": "Az ön fiókja nincs {locked}. Bárki követni tud, hogy megtekintse a kizárt követőknek szánt üzeneteid.", "compose_form.lock_disclaimer.lock": "lezárva", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index e9638bf96..ee2055397 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -2,6 +2,7 @@ "account.block": "Արգելափակել @{name}֊ին", "account.block_domain": "Թաքցնել ամենը հետեւյալ տիրույթից՝ {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Ներքոհիշյալը կարող է ոչ ամբողջությամբ արտացոլել օգտատիրոջ էջի տվյալները։", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Խմբագրել անձնական էջը", @@ -56,6 +57,7 @@ "column_header.unpin": "Հանել", "column_subheading.navigation": "Նավարկություն", "column_subheading.settings": "Կարգավորումներ", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Այս թութը չի հաշվառվի որեւէ պիտակի տակ, քանզի այն ծածուկ է։ Միայն հրապարակային թթերը հնարավոր է որոնել պիտակներով։", "compose_form.lock_disclaimer": "Քո հաշիվը {locked} չէ։ Յուրաքանչյուր ոք կարող է հետեւել քեզ եւ տեսնել միայն հետեւողների համար նախատեսված գրառումները։", "compose_form.lock_disclaimer.lock": "փակ", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index c8d8ebe76..cae3211ee 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -2,6 +2,7 @@ "account.block": "Blokir @{name}", "account.block_domain": "Sembunyikan segalanya dari {domain}", "account.blocked": "Terblokir", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Informasi di bawah mungkin tidak mencerminkan profil user secara lengkap.", "account.domain_blocked": "Domain disembunyikan", "account.edit_profile": "Ubah profil", @@ -56,6 +57,7 @@ "column_header.unpin": "Lepaskan", "column_subheading.navigation": "Navigasi", "column_subheading.settings": "Pengaturan", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Toot ini tidak akan ada dalam daftar tagar manapun karena telah di set sebagai tidak terdaftar. Hanya postingan publik yang bisa dicari dengan tagar.", "compose_form.lock_disclaimer": "Akun anda tidak {locked}. Semua orang dapat mengikuti anda untuk melihat postingan khusus untuk pengikut anda.", "compose_form.lock_disclaimer.lock": "terkunci", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index a2e9af8ef..121d745ca 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -2,6 +2,7 @@ "account.block": "Blokusar @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "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": "Modifikar profilo", @@ -56,6 +57,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 40ea9b26d..5e57143a9 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -2,6 +2,7 @@ "account.block": "Blocca @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "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": "Modifica profilo", @@ -56,6 +57,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 08f5e7962..c14d6191f 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -2,6 +2,7 @@ "account.block": "@{name}さんをブロック", "account.block_domain": "{domain}全体を非表示", "account.blocked": "ブロック済み", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "以下の情報は不正確な可能性があります。", "account.domain_blocked": "ドメイン非表示中", "account.edit_profile": "プロフィールを編集", @@ -56,6 +57,7 @@ "column_header.unpin": "ピン留めを外す", "column_subheading.navigation": "ナビゲーション", "column_subheading.settings": "設定", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "このトゥートは未収載なのでハッシュタグの一覧に表示されません。公開トゥートだけがハッシュタグで検索できます。", "compose_form.lock_disclaimer": "あなたのアカウントは{locked}になっていません。誰でもあなたをフォローすることができ、フォロワー限定の投稿を見ることができます。", "compose_form.lock_disclaimer.lock": "非公開", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index bde4397f3..fa15214c9 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -2,6 +2,7 @@ "account.block": "@{name}을 차단", "account.block_domain": "{domain} 전체를 숨김", "account.blocked": "차단 됨", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "여기 있는 정보는 유저의 프로파일을 정확히 반영하지 못 할 수도 있습니다.", "account.domain_blocked": "도메인 숨겨짐", "account.edit_profile": "프로필 편집", @@ -56,6 +57,7 @@ "column_header.unpin": "고정 해제", "column_subheading.navigation": "내비게이션", "column_subheading.settings": "설정", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "이 툿은 어떤 해시태그로도 검색 되지 않습니다. 전체공개로 게시 된 툿만이 해시태그로 검색 될 수 있습니다.", "compose_form.lock_disclaimer": "이 계정은 {locked}로 설정 되어 있지 않습니다. 누구나 이 계정을 팔로우 할 수 있으며, 팔로워 공개의 포스팅을 볼 수 있습니다.", "compose_form.lock_disclaimer.lock": "비공개", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 140be0dca..ff827991d 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -2,6 +2,7 @@ "account.block": "Blokkeer @{name}", "account.block_domain": "Negeer alles van {domain}", "account.blocked": "Geblokkeerd", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "De informatie hieronder kan mogelijk een incompleet beeld geven van dit gebruikersprofiel.", "account.domain_blocked": "Domein verborgen", "account.edit_profile": "Profiel bewerken", @@ -56,6 +57,7 @@ "column_header.unpin": "Losmaken", "column_subheading.navigation": "Navigatie", "column_subheading.settings": "Instellingen", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Deze toot valt niet onder een hashtag te bekijken, omdat deze niet op openbare tijdlijnen wordt getoond. Alleen openbare toots kunnen via hashtags gevonden worden.", "compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en toots zien die je alleen aan volgers hebt gericht.", "compose_form.lock_disclaimer.lock": "besloten", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 4d6ac133e..d3bc75708 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -2,6 +2,7 @@ "account.block": "Blokkér @{name}", "account.block_domain": "Skjul alt fra {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Informasjonen nedenfor kan gi et ufullstendig bilde av brukerens profil.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Rediger profil", @@ -56,6 +57,7 @@ "column_header.unpin": "Løsne", "column_subheading.navigation": "Navigasjon", "column_subheading.settings": "Innstillinger", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Denne tuten blir ikke listet under noen emneknagger da den er ulistet. Kun offentlige tuter kan søktes etter med emneknagg.", "compose_form.lock_disclaimer": "Din konto er ikke {locked}. Hvem som helst kan følge deg og se dine private poster.", "compose_form.lock_disclaimer.lock": "låst", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 24dfa9375..39ba31de3 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -2,6 +2,7 @@ "account.block": "Blocar @{name}", "account.block_domain": "Tot amagar del domeni {domain}", "account.blocked": "Blocat", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Aquelas informacions de perfil pòdon èsser incomplètas.", "account.domain_blocked": "Domeni amagat", "account.edit_profile": "Modificar lo perfil", @@ -56,6 +57,7 @@ "column_header.unpin": "Despenjar", "column_subheading.navigation": "Navigacion", "column_subheading.settings": "Paramètres", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Aqueste tut serà pas ligat a cap etiqueta estant qu’es pas listat. Òm pas cercar que los tuts publics per etiqueta.", "compose_form.lock_disclaimer": "Vòstre compte es pas {locked}. Tot lo mond pòt vos sègre e veire los estatuts reservats als seguidors.", "compose_form.lock_disclaimer.lock": "clavat", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 7262ce76b..d9490c518 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -2,6 +2,7 @@ "account.block": "Blokuj @{name}", "account.block_domain": "Blokuj wszystko z {domain}", "account.blocked": "Zablokowany", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Poniższe informacje mogą nie odwzorowywać bezbłędnie profilu użytkownika.", "account.domain_blocked": "Ukryto domenę", "account.edit_profile": "Edytuj profil", @@ -56,6 +57,7 @@ "column_header.unpin": "Cofnij przypięcie", "column_subheading.navigation": "Nawigacja", "column_subheading.settings": "Ustawienia", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Ten wpis nie będzie widoczny pod podanymi hashtagami, ponieważ jest oznaczony jako niewidoczny. Tylko publiczne wpisy mogą zostać znalezione z użyciem hashtagów.", "compose_form.lock_disclaimer": "Twoje konto nie jest {locked}. Każdy, kto Cię śledzi, może wyświetlać Twoje wpisy przeznaczone tylko dla śledzących.", "compose_form.lock_disclaimer.lock": "zablokowane", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index dcaeaced9..3d42eedb3 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -2,6 +2,7 @@ "account.block": "Bloquear @{name}", "account.block_domain": "Esconder tudo de {domain}", "account.blocked": "Bloqueado", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "As informações abaixo podem refletir o perfil do usuário de maneira incompleta.", "account.domain_blocked": "Domínio escondido", "account.edit_profile": "Editar perfil", @@ -56,6 +57,7 @@ "column_header.unpin": "Desafixar", "column_subheading.navigation": "Navegação", "column_subheading.settings": "Configurações", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Esse toot não será listado em nenhuma hashtag por ser não listado. Somente toots públicos podem ser pesquisados por hashtag.", "compose_form.lock_disclaimer": "A sua conta não está {locked}. Qualquer pessoa pode te seguir e visualizar postagens direcionadas a apenas seguidores.", "compose_form.lock_disclaimer.lock": "trancada", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index 4725a82da..5c93614a9 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -2,6 +2,7 @@ "account.block": "Bloquear @{name}", "account.block_domain": "Esconder tudo do domínio {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "As informações abaixo podem refletir o perfil do usuário de forma incompleta.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Editar perfil", @@ -56,6 +57,7 @@ "column_header.unpin": "Desafixar", "column_subheading.navigation": "Navegação", "column_subheading.settings": "Preferências", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Esta pulbicacção não será listada em nenhuma hashtag por ser não listada. Somente publicações públicas podem ser pesquisadas por hashtag.", "compose_form.lock_disclaimer": "A tua conta não está {locked}. Qualquer pessoa pode seguir-te e ver as publicações direcionadas apenas a seguidores.", "compose_form.lock_disclaimer.lock": "bloqueada", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 8e7d36659..7dffbb210 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -2,6 +2,7 @@ "account.block": "Блокировать", "account.block_domain": "Блокировать все с {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Нижеуказанная информация может не полностью отражать профиль пользователя.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Изменить профиль", @@ -56,6 +57,7 @@ "column_header.unpin": "Открепить", "column_subheading.navigation": "Навигация", "column_subheading.settings": "Настройки", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Этот пост не будет показывается в поиске по хэштегу, т.к. он непубличный. Только публичные посты можно найти в поиске по хэштегу.", "compose_form.lock_disclaimer": "Ваш аккаунт не {locked}. Любой человек может подписаться на Вас и просматривать посты для подписчиков.", "compose_form.lock_disclaimer.lock": "закрыт", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index e3b323943..0a248d261 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -2,6 +2,7 @@ "account.block": "Blokovať @{name}", "account.block_domain": "Ukryť všetko z {domain}", "account.blocked": "Blokovaný/á", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Inofrmácie nižšie nemusia byť úplným odrazom uživateľovho účtu.", "account.domain_blocked": "Doména ukrytá", "account.edit_profile": "Upraviť profil", @@ -56,6 +57,7 @@ "column_header.unpin": "Odopnúť", "column_subheading.navigation": "Navigácia", "column_subheading.settings": "Nastavenia", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "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.lock": "zamknutý", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index d38e8e3af..b9effce96 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -2,6 +2,7 @@ "account.block": "Blokiraj korisnika @{name}", "account.block_domain": "Sakrij sve sa domena {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Navedene informacije možda ne odslikavaju korisnički profil u potpunosti.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Izmeni profil", @@ -56,6 +57,7 @@ "column_header.unpin": "Otkači", "column_subheading.navigation": "Navigacija", "column_subheading.settings": "Postavke", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Vaš nalog nije {locked}. Svako može da Vas zaprati i da vidi objave namenjene samo Vašim pratiocima.", "compose_form.lock_disclaimer.lock": "zaključan", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index 3be0c89ee..a6c5f220e 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -2,6 +2,7 @@ "account.block": "Блокирај корисника @{name}", "account.block_domain": "Сакриј све са домена {domain}", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Наведене информације можда не одсликавају кориснички профил у потпуности.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Измени профил", @@ -56,6 +57,7 @@ "column_header.unpin": "Откачи", "column_subheading.navigation": "Навигација", "column_subheading.settings": "Поставке", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Ваш налог није {locked}. Свако може да Вас запрати и да види објаве намењене само Вашим пратиоцима.", "compose_form.lock_disclaimer.lock": "закључан", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index a13ba9847..6dc3d7a98 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -2,6 +2,7 @@ "account.block": "Blockera @{name}", "account.block_domain": "Dölj allt från {domain}", "account.blocked": "Blockerad", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "Informationen nedan kan spegla användarens profil ofullständigt.", "account.domain_blocked": "Domän gömd", "account.edit_profile": "Redigera profil", @@ -56,6 +57,7 @@ "column_header.unpin": "Ångra fäst", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Inställningar", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Denna toot kommer inte att listas under någon hashtag eftersom den är onoterad. Endast offentliga toots kan sökas med hashtag.", "compose_form.lock_disclaimer": "Ditt konto är inte {locked}. Vemsomhelst kan följa dig och även se dina inlägg skrivna för endast dina följare.", "compose_form.lock_disclaimer.lock": "låst", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 59ff10b46..4de354007 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -2,6 +2,7 @@ "account.block": "Block @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "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", @@ -56,6 +57,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index e83af319e..9d0affea4 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -2,6 +2,7 @@ "account.block": "Engelle @{name}", "account.block_domain": "Hide everything from {domain}", "account.blocked": "Blocked", + "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": "Profili düzenle", @@ -56,6 +57,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigasyon", "column_subheading.settings": "Ayarlar", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Hesabınız {locked} değil. Sadece takipçilerle paylaştığınız gönderileri görebilmek için sizi herhangi bir kullanıcı takip edebilir.", "compose_form.lock_disclaimer.lock": "kilitli", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index accc2d027..c49d3c7ae 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -2,6 +2,7 @@ "account.block": "Заблокувати", "account.block_domain": "Заглушити {domain}", "account.blocked": "Blocked", + "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": "Налаштування профілю", @@ -56,6 +57,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Навігація", "column_subheading.settings": "Налаштування", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Ваш акаунт не {locked}. Кожен може підписатися на Вас та бачити Ваші приватні пости.", "compose_form.lock_disclaimer.lock": "приватний", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index b9a912fb0..e95cf81f4 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -2,6 +2,7 @@ "account.block": "屏蔽 @{name}", "account.block_domain": "隐藏来自 {domain} 的内容", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "此处显示的信息可能不是全部内容。", "account.domain_blocked": "Domain hidden", "account.edit_profile": "修改个人资料", @@ -56,6 +57,7 @@ "column_header.unpin": "取消固定", "column_subheading.navigation": "导航", "column_subheading.settings": "设置", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "这条嘟文被设置为“不公开”,因此它不会出现在任何话题标签的列表下。只有公开的嘟文才能通过话题标签进行搜索。", "compose_form.lock_disclaimer": "你的帐户没有{locked}。任何人都可以在关注你后立即查看仅关注者可见的嘟文。", "compose_form.lock_disclaimer.lock": "开启保护", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 91b1d00af..1801c838d 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -2,6 +2,7 @@ "account.block": "封鎖 @{name}", "account.block_domain": "隱藏來自 {domain} 的一切文章", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "下列資料不一定完整。", "account.domain_blocked": "Domain hidden", "account.edit_profile": "修改個人資料", @@ -56,6 +57,7 @@ "column_header.unpin": "取下", "column_subheading.navigation": "瀏覽", "column_subheading.settings": "設定", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "你的用戶狀態為「{locked}」,任何人都能立即關注你,然後看到「只有關注者能看」的文章。", "compose_form.lock_disclaimer.lock": "公共", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 7e845c650..acbe6eb8e 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -2,6 +2,7 @@ "account.block": "封鎖 @{name}", "account.block_domain": "隱藏來自 {domain} 的一切貼文", "account.blocked": "Blocked", + "account.direct": "Direct Message @{name}", "account.disclaimer_full": "下列資料不一定完整。", "account.domain_blocked": "Domain hidden", "account.edit_profile": "編輯用者資訊", @@ -56,6 +57,7 @@ "column_header.unpin": "取下", "column_subheading.navigation": "瀏覽", "column_subheading.settings": "設定", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "你的帳號沒有{locked}。任何人都可以關注你,看到發給關注者的貼文。", "compose_form.lock_disclaimer.lock": "上鎖", diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 5eadebb81..a48c46941 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -4,6 +4,7 @@ import { COMPOSE_CHANGE, COMPOSE_REPLY, COMPOSE_REPLY_CANCEL, + COMPOSE_DIRECT, COMPOSE_MENTION, COMPOSE_SUBMIT_REQUEST, COMPOSE_SUBMIT_SUCCESS, @@ -262,6 +263,12 @@ export default function compose(state = initialState, action) { .update('text', text => `${text}@${action.account.get('acct')} `) .set('focusDate', new Date()) .set('idempotencyKey', uuid()); + case COMPOSE_DIRECT: + return state + .update('text', text => `${text}@${action.account.get('acct')} `) + .set('privacy', 'direct') + .set('focusDate', new Date()) + .set('idempotencyKey', uuid()); case COMPOSE_SUGGESTIONS_CLEAR: return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null); case COMPOSE_SUGGESTIONS_READY: From 947eedcab2c263e92900b53d64b184c1af856d24 Mon Sep 17 00:00:00 2001 From: takayamaki Date: Fri, 30 Mar 2018 10:04:04 +0900 Subject: [PATCH 013/442] update ja locale (#6965) related https://github.com/tootsuite/mastodon/pull/6956 --- app/javascript/mastodon/locales/ja.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index c14d6191f..b03906df0 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -2,7 +2,7 @@ "account.block": "@{name}さんをブロック", "account.block_domain": "{domain}全体を非表示", "account.blocked": "ブロック済み", - "account.direct": "Direct Message @{name}", + "account.direct": "@{name}さんにダイレクトメッセージ", "account.disclaimer_full": "以下の情報は不正確な可能性があります。", "account.domain_blocked": "ドメイン非表示中", "account.edit_profile": "プロフィールを編集", @@ -57,7 +57,7 @@ "column_header.unpin": "ピン留めを外す", "column_subheading.navigation": "ナビゲーション", "column_subheading.settings": "設定", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "このトゥートはメンションされた人だけが見ることができます。", "compose_form.hashtag_warning": "このトゥートは未収載なのでハッシュタグの一覧に表示されません。公開トゥートだけがハッシュタグで検索できます。", "compose_form.lock_disclaimer": "あなたのアカウントは{locked}になっていません。誰でもあなたをフォローすることができ、フォロワー限定の投稿を見ることができます。", "compose_form.lock_disclaimer.lock": "非公開", From 47cee7cc8e47471b372630cd28d50c6284aad8b3 Mon Sep 17 00:00:00 2001 From: Pierre-Morgan Gate Date: Fri, 30 Mar 2018 00:52:44 -0700 Subject: [PATCH 014/442] Upgrade charlock_holmes to version 0.7.6 (#6966) This version fixes compilation errors when trying to build its native extension with ICU 61. --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 29fa9cde7..9e644e7ae 100644 --- a/Gemfile +++ b/Gemfile @@ -25,7 +25,7 @@ gem 'active_model_serializers', '~> 0.10' gem 'addressable', '~> 2.5' gem 'bootsnap' gem 'browser' -gem 'charlock_holmes', '~> 0.7.5' +gem 'charlock_holmes', '~> 0.7.6' gem 'iso-639' gem 'chewy', '~> 5.0' gem 'cld3', '~> 3.2.0' diff --git a/Gemfile.lock b/Gemfile.lock index f68419d8e..a185a602e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -113,7 +113,7 @@ GEM xpath (~> 2.0) case_transform (0.2) activesupport - charlock_holmes (0.7.5) + charlock_holmes (0.7.6) chewy (5.0.0) activesupport (>= 4.0) elasticsearch (>= 2.0.0) @@ -632,7 +632,7 @@ DEPENDENCIES capistrano-rbenv (~> 2.1) capistrano-yarn (~> 2.0) capybara (~> 2.15) - charlock_holmes (~> 0.7.5) + charlock_holmes (~> 0.7.6) chewy (~> 5.0) cld3 (~> 3.2.0) climate_control (~> 0.2) From a6c129ddbdaaa84bc631d85eb248fb5a9fa7eb96 Mon Sep 17 00:00:00 2001 From: ThibG Date: Fri, 30 Mar 2018 12:38:00 +0200 Subject: [PATCH 015/442] Add some UI for user-defined domain blocks (#6628) * Keep list of blocked domains Might be overkill, but I'm trying to follow the same logic as for blocked users * Add basic domain block UI * Add the domain blocks UI to Getting Started * Fix undefined URL in `fetchDomainBlocks` * Update all known users' domain_blocking relationship instead of just one's --- .../mastodon/actions/domain_blocks.js | 66 ++++++++++++++++--- app/javascript/mastodon/components/domain.js | 42 ++++++++++++ .../mastodon/containers/domain_container.js | 33 ++++++++++ .../account_timeline/components/header.js | 4 +- .../containers/header_container.js | 8 +-- .../mastodon/features/domain_blocks/index.js | 66 +++++++++++++++++++ .../features/getting_started/index.js | 2 + app/javascript/mastodon/features/ui/index.js | 2 + .../features/ui/util/async-components.js | 4 ++ .../mastodon/reducers/domain_lists.js | 23 +++++++ app/javascript/mastodon/reducers/index.js | 2 + .../mastodon/reducers/relationships.js | 12 +++- .../styles/mastodon/components.scss | 24 +++++++ 13 files changed, 271 insertions(+), 17 deletions(-) create mode 100644 app/javascript/mastodon/components/domain.js create mode 100644 app/javascript/mastodon/containers/domain_container.js create mode 100644 app/javascript/mastodon/features/domain_blocks/index.js create mode 100644 app/javascript/mastodon/reducers/domain_lists.js diff --git a/app/javascript/mastodon/actions/domain_blocks.js b/app/javascript/mastodon/actions/domain_blocks.js index 44363697a..47e2df76b 100644 --- a/app/javascript/mastodon/actions/domain_blocks.js +++ b/app/javascript/mastodon/actions/domain_blocks.js @@ -12,12 +12,18 @@ export const DOMAIN_BLOCKS_FETCH_REQUEST = 'DOMAIN_BLOCKS_FETCH_REQUEST'; export const DOMAIN_BLOCKS_FETCH_SUCCESS = 'DOMAIN_BLOCKS_FETCH_SUCCESS'; export const DOMAIN_BLOCKS_FETCH_FAIL = 'DOMAIN_BLOCKS_FETCH_FAIL'; -export function blockDomain(domain, accountId) { +export const DOMAIN_BLOCKS_EXPAND_REQUEST = 'DOMAIN_BLOCKS_EXPAND_REQUEST'; +export const DOMAIN_BLOCKS_EXPAND_SUCCESS = 'DOMAIN_BLOCKS_EXPAND_SUCCESS'; +export const DOMAIN_BLOCKS_EXPAND_FAIL = 'DOMAIN_BLOCKS_EXPAND_FAIL'; + +export function blockDomain(domain) { return (dispatch, getState) => { dispatch(blockDomainRequest(domain)); api(getState).post('/api/v1/domain_blocks', { domain }).then(() => { - dispatch(blockDomainSuccess(domain, accountId)); + const at_domain = '@' + domain; + const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id')); + dispatch(blockDomainSuccess(domain, accounts)); }).catch(err => { dispatch(blockDomainFail(domain, err)); }); @@ -31,11 +37,11 @@ export function blockDomainRequest(domain) { }; }; -export function blockDomainSuccess(domain, accountId) { +export function blockDomainSuccess(domain, accounts) { return { type: DOMAIN_BLOCK_SUCCESS, domain, - accountId, + accounts, }; }; @@ -47,12 +53,14 @@ export function blockDomainFail(domain, error) { }; }; -export function unblockDomain(domain, accountId) { +export function unblockDomain(domain) { return (dispatch, getState) => { dispatch(unblockDomainRequest(domain)); api(getState).delete('/api/v1/domain_blocks', { params: { domain } }).then(() => { - dispatch(unblockDomainSuccess(domain, accountId)); + const at_domain = '@' + domain; + const accounts = getState().get('accounts').filter(item => item.get('acct').endsWith(at_domain)).valueSeq().map(item => item.get('id')); + dispatch(unblockDomainSuccess(domain, accounts)); }).catch(err => { dispatch(unblockDomainFail(domain, err)); }); @@ -66,11 +74,11 @@ export function unblockDomainRequest(domain) { }; }; -export function unblockDomainSuccess(domain, accountId) { +export function unblockDomainSuccess(domain, accounts) { return { type: DOMAIN_UNBLOCK_SUCCESS, domain, - accountId, + accounts, }; }; @@ -86,7 +94,7 @@ export function fetchDomainBlocks() { return (dispatch, getState) => { dispatch(fetchDomainBlocksRequest()); - api(getState).get().then(response => { + api(getState).get('/api/v1/domain_blocks').then(response => { const next = getLinks(response).refs.find(link => link.rel === 'next'); dispatch(fetchDomainBlocksSuccess(response.data, next ? next.uri : null)); }).catch(err => { @@ -115,3 +123,43 @@ export function fetchDomainBlocksFail(error) { error, }; }; + +export function expandDomainBlocks() { + return (dispatch, getState) => { + const url = getState().getIn(['domain_lists', 'blocks', 'next']); + + if (url === null) { + return; + } + + dispatch(expandDomainBlocksRequest()); + + api(getState).get(url).then(response => { + const next = getLinks(response).refs.find(link => link.rel === 'next'); + dispatch(expandDomainBlocksSuccess(response.data, next ? next.uri : null)); + }).catch(err => { + dispatch(expandDomainBlocksFail(err)); + }); + }; +}; + +export function expandDomainBlocksRequest() { + return { + type: DOMAIN_BLOCKS_EXPAND_REQUEST, + }; +}; + +export function expandDomainBlocksSuccess(domains, next) { + return { + type: DOMAIN_BLOCKS_EXPAND_SUCCESS, + domains, + next, + }; +}; + +export function expandDomainBlocksFail(error) { + return { + type: DOMAIN_BLOCKS_EXPAND_FAIL, + error, + }; +}; diff --git a/app/javascript/mastodon/components/domain.js b/app/javascript/mastodon/components/domain.js new file mode 100644 index 000000000..f657cb8d2 --- /dev/null +++ b/app/javascript/mastodon/components/domain.js @@ -0,0 +1,42 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import IconButton from './icon_button'; +import { defineMessages, injectIntl } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; + +const messages = defineMessages({ + unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' }, +}); + +@injectIntl +export default class Account extends ImmutablePureComponent { + + static propTypes = { + domain: PropTypes.string, + onUnblockDomain: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + handleDomainUnblock = () => { + this.props.onUnblockDomain(this.props.domain); + } + + render () { + const { domain, intl } = this.props; + + return ( +
+
+ + {domain} + + +
+ +
+
+
+ ); + } + +} diff --git a/app/javascript/mastodon/containers/domain_container.js b/app/javascript/mastodon/containers/domain_container.js new file mode 100644 index 000000000..52d5c1613 --- /dev/null +++ b/app/javascript/mastodon/containers/domain_container.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import { blockDomain, unblockDomain } from '../actions/domain_blocks'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import Domain from '../components/domain'; +import { openModal } from '../actions/modal'; + +const messages = defineMessages({ + blockDomainConfirm: { id: 'confirmations.domain_block.confirm', defaultMessage: 'Hide entire domain' }, +}); + +const makeMapStateToProps = () => { + const mapStateToProps = (state, { }) => ({ + }); + + return mapStateToProps; +}; + +const mapDispatchToProps = (dispatch, { intl }) => ({ + onBlockDomain (domain) { + dispatch(openModal('CONFIRM', { + message: {domain} }} />, + confirm: intl.formatMessage(messages.blockDomainConfirm), + onConfirm: () => dispatch(blockDomain(domain)), + })); + }, + + onUnblockDomain (domain) { + dispatch(unblockDomain(domain)); + }, +}); + +export default injectIntl(connect(makeMapStateToProps, mapDispatchToProps)(Domain)); diff --git a/app/javascript/mastodon/features/account_timeline/components/header.js b/app/javascript/mastodon/features/account_timeline/components/header.js index 789999dce..1ae5126e6 100644 --- a/app/javascript/mastodon/features/account_timeline/components/header.js +++ b/app/javascript/mastodon/features/account_timeline/components/header.js @@ -62,7 +62,7 @@ export default class Header extends ImmutablePureComponent { if (!domain) return; - this.props.onBlockDomain(domain, this.props.account.get('id')); + this.props.onBlockDomain(domain); } handleUnblockDomain = () => { @@ -70,7 +70,7 @@ export default class Header extends ImmutablePureComponent { if (!domain) return; - this.props.onUnblockDomain(domain, this.props.account.get('id')); + this.props.onUnblockDomain(domain); } render () { diff --git a/app/javascript/mastodon/features/account_timeline/containers/header_container.js b/app/javascript/mastodon/features/account_timeline/containers/header_container.js index 214441b8a..4d5308219 100644 --- a/app/javascript/mastodon/features/account_timeline/containers/header_container.js +++ b/app/javascript/mastodon/features/account_timeline/containers/header_container.js @@ -94,16 +94,16 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ } }, - onBlockDomain (domain, accountId) { + onBlockDomain (domain) { dispatch(openModal('CONFIRM', { message: {domain} }} />, confirm: intl.formatMessage(messages.blockDomainConfirm), - onConfirm: () => dispatch(blockDomain(domain, accountId)), + onConfirm: () => dispatch(blockDomain(domain)), })); }, - onUnblockDomain (domain, accountId) { - dispatch(unblockDomain(domain, accountId)); + onUnblockDomain (domain) { + dispatch(unblockDomain(domain)); }, }); diff --git a/app/javascript/mastodon/features/domain_blocks/index.js b/app/javascript/mastodon/features/domain_blocks/index.js new file mode 100644 index 000000000..b17c47e91 --- /dev/null +++ b/app/javascript/mastodon/features/domain_blocks/index.js @@ -0,0 +1,66 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import ImmutablePropTypes from 'react-immutable-proptypes'; +import PropTypes from 'prop-types'; +import LoadingIndicator from '../../components/loading_indicator'; +import Column from '../ui/components/column'; +import ColumnBackButtonSlim from '../../components/column_back_button_slim'; +import DomainContainer from '../../containers/domain_container'; +import { fetchDomainBlocks, expandDomainBlocks } from '../../actions/domain_blocks'; +import { defineMessages, injectIntl } from 'react-intl'; +import ImmutablePureComponent from 'react-immutable-pure-component'; +import { debounce } from 'lodash'; +import ScrollableList from '../../components/scrollable_list'; + +const messages = defineMessages({ + heading: { id: 'column.domain_blocks', defaultMessage: 'Hidden domains' }, + unblockDomain: { id: 'account.unblock_domain', defaultMessage: 'Unhide {domain}' }, +}); + +const mapStateToProps = state => ({ + domains: state.getIn(['domain_lists', 'blocks', 'items']), +}); + +@connect(mapStateToProps) +@injectIntl +export default class Blocks extends ImmutablePureComponent { + + static propTypes = { + params: PropTypes.object.isRequired, + dispatch: PropTypes.func.isRequired, + domains: ImmutablePropTypes.list, + intl: PropTypes.object.isRequired, + }; + + componentWillMount () { + this.props.dispatch(fetchDomainBlocks()); + } + + handleLoadMore = debounce(() => { + this.props.dispatch(expandDomainBlocks()); + }, 300, { leading: true }); + + render () { + const { intl, domains } = this.props; + + if (!domains) { + return ( + + + + ); + } + + return ( + + + + {domains.map(domain => + + )} + + + ); + } + +} diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index 3a875169e..1a71cff94 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -24,6 +24,7 @@ const messages = defineMessages({ sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, + domain_blocks: { id: 'navigation_bar.domain_blocks', defaultMessage: 'Hidden domains' }, mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' }, info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }, pins: { id: 'navigation_bar.pins', defaultMessage: 'Pinned toots' }, @@ -121,6 +122,7 @@ export default class GettingStarted extends ImmutablePureComponent { + diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index b6a2a6cfc..8894eb4e6 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -37,6 +37,7 @@ import { FavouritedStatuses, ListTimeline, Blocks, + DomainBlocks, Mutes, PinnedStatuses, Lists, @@ -158,6 +159,7 @@ class SwitchingColumnsArea extends React.PureComponent { + diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index d6586680b..19957208f 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -90,6 +90,10 @@ export function Blocks () { return import(/* webpackChunkName: "features/blocks" */'../../blocks'); } +export function DomainBlocks () { + return import(/* webpackChunkName: "features/domain_blocks" */'../../domain_blocks'); +} + export function Mutes () { return import(/* webpackChunkName: "features/mutes" */'../../mutes'); } diff --git a/app/javascript/mastodon/reducers/domain_lists.js b/app/javascript/mastodon/reducers/domain_lists.js new file mode 100644 index 000000000..a9e3519f3 --- /dev/null +++ b/app/javascript/mastodon/reducers/domain_lists.js @@ -0,0 +1,23 @@ +import { + DOMAIN_BLOCKS_FETCH_SUCCESS, + DOMAIN_BLOCKS_EXPAND_SUCCESS, + DOMAIN_UNBLOCK_SUCCESS, +} from '../actions/domain_blocks'; +import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable'; + +const initialState = ImmutableMap({ + blocks: ImmutableMap(), +}); + +export default function domainLists(state = initialState, action) { + switch(action.type) { + case DOMAIN_BLOCKS_FETCH_SUCCESS: + return state.setIn(['blocks', 'items'], ImmutableOrderedSet(action.domains)).setIn(['blocks', 'next'], action.next); + case DOMAIN_BLOCKS_EXPAND_SUCCESS: + return state.updateIn(['blocks', 'items'], set => set.union(action.domains)).setIn(['blocks', 'next'], action.next); + case DOMAIN_UNBLOCK_SUCCESS: + return state.updateIn(['blocks', 'items'], set => set.delete(action.domain)); + default: + return state; + } +}; diff --git a/app/javascript/mastodon/reducers/index.js b/app/javascript/mastodon/reducers/index.js index b84b2d18a..3d9a6a132 100644 --- a/app/javascript/mastodon/reducers/index.js +++ b/app/javascript/mastodon/reducers/index.js @@ -6,6 +6,7 @@ import alerts from './alerts'; import { loadingBarReducer } from 'react-redux-loading-bar'; import modal from './modal'; import user_lists from './user_lists'; +import domain_lists from './domain_lists'; import accounts from './accounts'; import accounts_counters from './accounts_counters'; import statuses from './statuses'; @@ -34,6 +35,7 @@ const reducers = { loadingBar: loadingBarReducer, modal, user_lists, + domain_lists, status_lists, accounts, accounts_counters, diff --git a/app/javascript/mastodon/reducers/relationships.js b/app/javascript/mastodon/reducers/relationships.js index c7b04a668..d1caabc1c 100644 --- a/app/javascript/mastodon/reducers/relationships.js +++ b/app/javascript/mastodon/reducers/relationships.js @@ -23,6 +23,14 @@ const normalizeRelationships = (state, relationships) => { return state; }; +const setDomainBlocking = (state, accounts, blocking) => { + return state.withMutations(map => { + accounts.forEach(id => { + map.setIn([id, 'domain_blocking'], blocking); + }); + }); +}; + const initialState = ImmutableMap(); export default function relationships(state = initialState, action) { @@ -37,9 +45,9 @@ export default function relationships(state = initialState, action) { case RELATIONSHIPS_FETCH_SUCCESS: return normalizeRelationships(state, action.relationships); case DOMAIN_BLOCK_SUCCESS: - return state.setIn([action.accountId, 'domain_blocking'], true); + return setDomainBlocking(state, action.accounts, true); case DOMAIN_UNBLOCK_SUCCESS: - return state.setIn([action.accountId, 'domain_blocking'], false); + return setDomainBlocking(state, action.accounts, false); default: return state; } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 447e6bc8e..6a83be452 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1001,6 +1001,30 @@ } } +.domain { + padding: 10px; + border-bottom: 1px solid lighten($ui-base-color, 8%); + + .domain__domain-name { + flex: 1 1 auto; + display: block; + color: $primary-text-color; + text-decoration: none; + font-size: 14px; + font-weight: 500; + } +} + +.domain__wrapper { + display: flex; +} + +.domain_buttons { + height: 18px; + padding: 10px; + white-space: nowrap; +} + .account { padding: 10px; border-bottom: 1px solid lighten($ui-base-color, 8%); From e573bb0990ece4b1a521ccf8a4c7bec5972d3538 Mon Sep 17 00:00:00 2001 From: ThibG Date: Fri, 30 Mar 2018 15:44:54 +0200 Subject: [PATCH 016/442] Fix compatibility with PeerTube (#6968) * Support fetching objects of convertible types by URL (fixes #6924) * Ignore invalid hashtags --- app/lib/activitypub/activity/create.rb | 2 ++ app/services/fetch_atom_service.rb | 6 +++++- app/services/resolve_url_service.rb | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index afee8a268..45c0e91cb 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -79,6 +79,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity hashtag = Tag.where(name: hashtag).first_or_initialize(name: hashtag) status.tags << hashtag + rescue ActiveRecord::RecordInvalid + nil end def process_mention(tag, status) diff --git a/app/services/fetch_atom_service.rb b/app/services/fetch_atom_service.rb index 62dea8298..87076cc07 100644 --- a/app/services/fetch_atom_service.rb +++ b/app/services/fetch_atom_service.rb @@ -44,7 +44,7 @@ class FetchAtomService < BaseService json = body_to_json(body) if supported_context?(json) && json['type'] == 'Person' && json['inbox'].present? [json['id'], { prefetched_body: body, id: true }, :activitypub] - elsif supported_context?(json) && json['type'] == 'Note' + elsif supported_context?(json) && expected_type?(json) [json['id'], { prefetched_body: body, id: true }, :activitypub] else @unsupported_activity = true @@ -61,6 +61,10 @@ class FetchAtomService < BaseService end end + def expected_type?(json) + (ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES).include? json['type'] + end + def process_html(response) page = Nokogiri::HTML(response.body_with_limit) diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb index 1f2b24524..9499dc286 100644 --- a/app/services/resolve_url_service.rb +++ b/app/services/resolve_url_service.rb @@ -19,7 +19,7 @@ class ResolveURLService < BaseService case type when 'Person' FetchRemoteAccountService.new.call(atom_url, body, protocol) - when 'Note' + when 'Note', 'Article', 'Image', 'Video' FetchRemoteStatusService.new.call(atom_url, body, protocol) end end From fb3dc00ddab3f8c1af42ebc0520f6108cc40a1fc Mon Sep 17 00:00:00 2001 From: unarist Date: Sat, 31 Mar 2018 20:16:38 +0900 Subject: [PATCH 017/442] Ignore AbortError when cancelled sharing (#6978) `navigator.share()` rejects Promise if user cancelled sharing, and it may print it as an error on JavaScript console. This patch ignores it and prints other errors on the console. --- app/javascript/mastodon/components/status_action_bar.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index cd59c7845..e036dc1da 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -67,6 +67,8 @@ export default class StatusActionBar extends ImmutablePureComponent { navigator.share({ text: this.props.status.get('search_index'), url: this.props.status.get('url'), + }).catch((e) => { + if (e.name !== 'AbortError') console.error(e); }); } From 3886bfb5eb9e08bb5de61b1314c6cae1be3e5530 Mon Sep 17 00:00:00 2001 From: Daniel Hunsaker Date: Sat, 31 Mar 2018 05:17:25 -0600 Subject: [PATCH 018/442] [Nanobox] Enable ElasticSearch support by default (#6977) Admins can still disable the feature by adding `ES_ENABLED=false` to their environment, if they prefer not to use it. Be sure to set the variable before you deploy! --- .env.nanobox | 6 +++--- boxfile.yml | 31 +++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/.env.nanobox b/.env.nanobox index 0d14f8a00..8e0af6a8a 100644 --- a/.env.nanobox +++ b/.env.nanobox @@ -14,9 +14,9 @@ DB_PORT=5432 DATABASE_URL=postgresql://$DATA_DB_USER:$DATA_DB_PASS@$DATA_DB_HOST/gonano # Optional ElasticSearch configuration -# ES_ENABLED=true -# ES_HOST=localhost -# ES_PORT=9200 +ES_ENABLED=true +ES_HOST=$DATA_ELASTIC_HOST +ES_PORT=9200 # Optimizations LD_PRELOAD=/data/lib/libjemalloc.so diff --git a/boxfile.yml b/boxfile.yml index aa2003a1b..9368a7d9d 100644 --- a/boxfile.yml +++ b/boxfile.yml @@ -61,6 +61,11 @@ deploy.config: before_live: web.web: - bundle exec rake db:migrate:setup + - |- + if [[ "${ES_ENABLED}" != "false" ]] + then + bundle exec rake chewy:deploy + fi web.web: @@ -208,6 +213,32 @@ data.db: done +data.elastic: + image: nanobox/elasticsearch:5 + + cron: + - id: backup + schedule: '0 3 * * *' + command: | + id=$(cat /proc/sys/kernel/random/uuid) + curl -X PUT -H "Content-Type: application/json" "127.0.0.1:9200/_snapshot/${id}" -d "{\"type\": \"fs\",\"settings\": {\"location\": \"/var/tmp/${id}\",\"compress\": true}}" + curl -X PUT -H "Content-Type: application/json" "127.0.0.1:9200/_snapshot/${id}/backup?wait_for_completion=true&pretty" + tar -cz -C "/var/tmp/${id}" . | + 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 -X DELETE -H "Content-Type: application/json" "127.0.0.1:9200/_snapshot/${id}" + rm -rf "/var/tmp/${id}" + curl -k -s -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/ | + sed 's/,/\n/g' | + grep ${HOSTNAME} | + sort | + head -n-${BACKUP_COUNT:-1} | + sed 's/.*: \?"\(.*\)".*/\1/' | + while read file + do + curl -k -H "X-AUTH-TOKEN: ${WAREHOUSE_DATA_HOARDER_TOKEN}" https://${WAREHOUSE_DATA_HOARDER_HOST}:7410/blobs/${file} -X DELETE + done + + data.redis: image: nanobox/redis:4.0 From b7d633c1bbe8a250d10c517810b700042bf2f40e Mon Sep 17 00:00:00 2001 From: mayaeh Date: Sat, 31 Mar 2018 21:09:39 +0900 Subject: [PATCH 019/442] i18n: Add Japanese translations for domain blocks (#6979) * run yarn manage:translations * Update Japanese translations for domain blocks. --- app/javascript/mastodon/locales/ar.json | 2 + app/javascript/mastodon/locales/bg.json | 2 + app/javascript/mastodon/locales/ca.json | 2 + app/javascript/mastodon/locales/de.json | 2 + .../mastodon/locales/defaultMessages.json | 39 +++++++++++++++++++ app/javascript/mastodon/locales/en.json | 2 + app/javascript/mastodon/locales/eo.json | 2 + app/javascript/mastodon/locales/es.json | 2 + app/javascript/mastodon/locales/fa.json | 2 + app/javascript/mastodon/locales/fi.json | 2 + app/javascript/mastodon/locales/fr.json | 2 + app/javascript/mastodon/locales/gl.json | 2 + app/javascript/mastodon/locales/he.json | 2 + app/javascript/mastodon/locales/hr.json | 2 + app/javascript/mastodon/locales/hu.json | 2 + app/javascript/mastodon/locales/hy.json | 2 + app/javascript/mastodon/locales/id.json | 2 + app/javascript/mastodon/locales/io.json | 2 + app/javascript/mastodon/locales/it.json | 2 + app/javascript/mastodon/locales/ja.json | 2 + app/javascript/mastodon/locales/ko.json | 2 + app/javascript/mastodon/locales/nl.json | 2 + app/javascript/mastodon/locales/no.json | 2 + app/javascript/mastodon/locales/oc.json | 2 + app/javascript/mastodon/locales/pl.json | 2 + app/javascript/mastodon/locales/pt-BR.json | 2 + app/javascript/mastodon/locales/pt.json | 2 + app/javascript/mastodon/locales/ru.json | 2 + app/javascript/mastodon/locales/sk.json | 2 + app/javascript/mastodon/locales/sr-Latn.json | 2 + app/javascript/mastodon/locales/sr.json | 2 + app/javascript/mastodon/locales/sv.json | 2 + app/javascript/mastodon/locales/th.json | 2 + app/javascript/mastodon/locales/tr.json | 2 + app/javascript/mastodon/locales/uk.json | 2 + app/javascript/mastodon/locales/zh-CN.json | 2 + app/javascript/mastodon/locales/zh-HK.json | 2 + app/javascript/mastodon/locales/zh-TW.json | 2 + 38 files changed, 113 insertions(+) diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index f9af062d0..3078b5b8c 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "إعادة المحاولة", "column.blocks": "الحسابات المحجوبة", "column.community": "الخيط العام المحلي", + "column.domain_blocks": "Hidden domains", "column.favourites": "المفضلة", "column.follow_requests": "طلبات المتابعة", "column.home": "الرئيسية", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "هل تود إخفاء الإخطارات القادمة من هذا المستخدم ؟", "navigation_bar.blocks": "الحسابات المحجوبة", "navigation_bar.community_timeline": "الخيط العام المحلي", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "تعديل الملف الشخصي", "navigation_bar.favourites": "المفضلة", "navigation_bar.follow_requests": "طلبات المتابعة", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 58795ca37..9aaff0ddf 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Local timeline", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", "column.home": "Начало", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Blocked users", "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Редактирай профил", "navigation_bar.favourites": "Favourites", "navigation_bar.follow_requests": "Follow requests", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index b0ce34c6b..ec5a8a1d6 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Torna-ho a provar", "column.blocks": "Usuaris blocats", "column.community": "Línia de temps local", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favorits", "column.follow_requests": "Peticions per seguir-te", "column.home": "Inici", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Amagar notificacions d'aquest usuari?", "navigation_bar.blocks": "Usuaris bloquejats", "navigation_bar.community_timeline": "Línia de temps Local", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favorits", "navigation_bar.follow_requests": "Sol·licituds de seguiment", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index eb0c5056a..a618b853e 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Erneut versuchen", "column.blocks": "Blockierte Profile", "column.community": "Lokale Zeitleiste", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favoriten", "column.follow_requests": "Folgeanfragen", "column.home": "Startseite", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Benachrichtigungen von diesem Account verbergen?", "navigation_bar.blocks": "Blockierte Profile", "navigation_bar.community_timeline": "Lokale Zeitleiste", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Profil bearbeiten", "navigation_bar.favourites": "Favoriten", "navigation_bar.follow_requests": "Folgeanfragen", diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 1fe6861c9..4c9401deb 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -92,6 +92,15 @@ ], "path": "app/javascript/mastodon/components/column_header.json" }, + { + "descriptors": [ + { + "defaultMessage": "Unhide {domain}", + "id": "account.unblock_domain" + } + ], + "path": "app/javascript/mastodon/components/domain.json" + }, { "descriptors": [ { @@ -298,6 +307,19 @@ ], "path": "app/javascript/mastodon/containers/account_container.json" }, + { + "descriptors": [ + { + "defaultMessage": "Hide entire domain", + "id": "confirmations.domain_block.confirm" + }, + { + "defaultMessage": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", + "id": "confirmations.domain_block.message" + } + ], + "path": "app/javascript/mastodon/containers/domain_container.json" + }, { "descriptors": [ { @@ -849,6 +871,19 @@ ], "path": "app/javascript/mastodon/features/compose/index.json" }, + { + "descriptors": [ + { + "defaultMessage": "Hidden domains", + "id": "column.domain_blocks" + }, + { + "defaultMessage": "Unhide {domain}", + "id": "account.unblock_domain" + } + ], + "path": "app/javascript/mastodon/features/domain_blocks/index.json" + }, { "descriptors": [ { @@ -930,6 +965,10 @@ "defaultMessage": "Blocked users", "id": "navigation_bar.blocks" }, + { + "defaultMessage": "Hidden domains", + "id": "navigation_bar.domain_blocks" + }, { "defaultMessage": "Muted users", "id": "navigation_bar.mutes" diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index d481596e0..23040f7e4 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Local timeline", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", "column.home": "Home", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Blocked users", "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Edit profile", "navigation_bar.favourites": "Favourites", "navigation_bar.follow_requests": "Follow requests", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 9b00edb00..82b749417 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Bonvolu reprovi", "column.blocks": "Blokitaj uzantoj", "column.community": "Loka tempolinio", + "column.domain_blocks": "Hidden domains", "column.favourites": "Stelumoj", "column.follow_requests": "Petoj de sekvado", "column.home": "Hejmo", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Ĉu kaŝi sciigojn el ĉi tiu uzanto?", "navigation_bar.blocks": "Blokitaj uzantoj", "navigation_bar.community_timeline": "Loka tempolinio", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Redakti profilon", "navigation_bar.favourites": "Stelumoj", "navigation_bar.follow_requests": "Petoj de sekvado", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 9f03b31c1..6f9c06c5f 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Inténtalo de nuevo", "column.blocks": "Usuarios bloqueados", "column.community": "Línea de tiempo local", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favoritos", "column.follow_requests": "Solicitudes de seguimiento", "column.home": "Inicio", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Ocultar notificaciones de este usuario?", "navigation_bar.blocks": "Usuarios bloqueados", "navigation_bar.community_timeline": "Historia local", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favoritos", "navigation_bar.follow_requests": "Solicitudes para seguirte", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 9421746b1..4b64ca353 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "تلاش دوباره", "column.blocks": "کاربران مسدودشده", "column.community": "نوشته‌های محلی", + "column.domain_blocks": "Hidden domains", "column.favourites": "پسندیده‌ها", "column.follow_requests": "درخواست‌های پیگیری", "column.home": "خانه", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "اعلان‌های این کاربر پنهان شود؟", "navigation_bar.blocks": "کاربران مسدودشده", "navigation_bar.community_timeline": "نوشته‌های محلی", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "ویرایش نمایه", "navigation_bar.favourites": "پسندیده‌ها", "navigation_bar.follow_requests": "درخواست‌های پیگیری", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index fce441df4..f4be80514 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Yritä uudestaan", "column.blocks": "Estetyt käyttäjät", "column.community": "Paikallinen aikajana", + "column.domain_blocks": "Hidden domains", "column.favourites": "Suosikit", "column.follow_requests": "Seurauspyynnöt", "column.home": "Koti", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Piilota ilmoitukset tältä käyttäjältä?", "navigation_bar.blocks": "Estetyt käyttäjät", "navigation_bar.community_timeline": "Paikallinen aikajana", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Muokkaa profiilia", "navigation_bar.favourites": "Suosikit", "navigation_bar.follow_requests": "Seurauspyynnöt", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 6eb34e644..58e6ad54d 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Réessayer", "column.blocks": "Comptes bloqués", "column.community": "Fil public local", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favoris", "column.follow_requests": "Demandes de suivi", "column.home": "Accueil", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Masquer les notifications de cet utilisateur ?", "navigation_bar.blocks": "Comptes bloqués", "navigation_bar.community_timeline": "Fil public local", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Modifier le profil", "navigation_bar.favourites": "Favoris", "navigation_bar.follow_requests": "Demandes de suivi", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index a0823b93f..8d586404d 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Inténteo de novo", "column.blocks": "Usuarias bloqueadas", "column.community": "Liña temporal local", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favoritas", "column.follow_requests": "Peticións de seguimento", "column.home": "Inicio", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Esconder notificacións deste usuario?", "navigation_bar.blocks": "Usuarias bloqueadas", "navigation_bar.community_timeline": "Liña temporal local", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favoritas", "navigation_bar.follow_requests": "Peticións de seguimento", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 0e2ee8da4..6bec26fd8 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "לנסות שוב", "column.blocks": "חסימות", "column.community": "ציר זמן מקומי", + "column.domain_blocks": "Hidden domains", "column.favourites": "חיבובים", "column.follow_requests": "בקשות מעקב", "column.home": "בבית", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "להסתיר הודעות מחשבון זה?", "navigation_bar.blocks": "חסימות", "navigation_bar.community_timeline": "ציר זמן מקומי", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "עריכת פרופיל", "navigation_bar.favourites": "חיבובים", "navigation_bar.follow_requests": "בקשות מעקב", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index 1e8ce8e29..f7a5d0a3f 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Blokirani korisnici", "column.community": "Lokalni timeline", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favoriti", "column.follow_requests": "Zahtjevi za slijeđenje", "column.home": "Dom", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Blokirani korisnici", "navigation_bar.community_timeline": "Lokalni timeline", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Uredi profil", "navigation_bar.favourites": "Favoriti", "navigation_bar.follow_requests": "Zahtjevi za slijeđenje", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index deb17c6f4..8b9c14993 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Próbálja újra", "column.blocks": "Letiltott felhasználók", "column.community": "Helyi idővonal", + "column.domain_blocks": "Hidden domains", "column.favourites": "Kedvencek", "column.follow_requests": "Követési kérések", "column.home": "Kezdőlap", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Értesítések elrejtése ezen felhasználótól?", "navigation_bar.blocks": "Tiltott felhasználók", "navigation_bar.community_timeline": "Helyi idővonal", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Profil szerkesztése", "navigation_bar.favourites": "Kedvencek", "navigation_bar.follow_requests": "Követési kérések", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index ee2055397..22ba89a43 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Կրկին փորձել", "column.blocks": "Արգելափակված օգտատերեր", "column.community": "Տեղական հոսք", + "column.domain_blocks": "Hidden domains", "column.favourites": "Հավանածներ", "column.follow_requests": "Հետեւելու հայցեր", "column.home": "Հիմնական", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Թաքցնե՞լ ցանուցումներն այս օգտատիրոջից։", "navigation_bar.blocks": "Արգելափակված օգտատերեր", "navigation_bar.community_timeline": "Տեղական հոսք", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Խմբագրել անձնական էջը", "navigation_bar.favourites": "Հավանածներ", "navigation_bar.follow_requests": "Հետեւելու հայցեր", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index cae3211ee..1ef610fba 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Coba lagi", "column.blocks": "Pengguna diblokir", "column.community": "Linimasa Lokal", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favorit", "column.follow_requests": "Permintaan mengikuti", "column.home": "Beranda", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Pengguna diblokir", "navigation_bar.community_timeline": "Linimasa lokal", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Ubah profil", "navigation_bar.favourites": "Favorit", "navigation_bar.follow_requests": "Permintaan mengikuti", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 121d745ca..1435178a8 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Blokusita uzeri", "column.community": "Lokala tempolineo", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favorati", "column.follow_requests": "Demandi di sequado", "column.home": "Hemo", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Blokusita uzeri", "navigation_bar.community_timeline": "Lokala tempolineo", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Modifikar profilo", "navigation_bar.favourites": "Favorati", "navigation_bar.follow_requests": "Demandi di sequado", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 5e57143a9..226127e6b 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Utenti bloccati", "column.community": "Timeline locale", + "column.domain_blocks": "Hidden domains", "column.favourites": "Apprezzati", "column.follow_requests": "Richieste di amicizia", "column.home": "Home", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Utenti bloccati", "navigation_bar.community_timeline": "Timeline locale", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Modifica profilo", "navigation_bar.favourites": "Apprezzati", "navigation_bar.follow_requests": "Richieste di amicizia", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index b03906df0..80a13a9bf 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "再試行", "column.blocks": "ブロックしたユーザー", "column.community": "ローカルタイムライン", + "column.domain_blocks": "非表示にしたドメイン", "column.favourites": "お気に入り", "column.follow_requests": "フォローリクエスト", "column.home": "ホーム", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "このユーザーからの通知を隠しますか?", "navigation_bar.blocks": "ブロックしたユーザー", "navigation_bar.community_timeline": "ローカルタイムライン", + "navigation_bar.domain_blocks": "非表示にしたドメイン", "navigation_bar.edit_profile": "プロフィールを編集", "navigation_bar.favourites": "お気に入り", "navigation_bar.follow_requests": "フォローリクエスト", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index fa15214c9..449df42b8 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "다시 시도", "column.blocks": "차단 중인 사용자", "column.community": "로컬 타임라인", + "column.domain_blocks": "Hidden domains", "column.favourites": "즐겨찾기", "column.follow_requests": "팔로우 요청", "column.home": "홈", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "이 사용자로부터의 알림을 뮤트하시겠습니까?", "navigation_bar.blocks": "차단한 사용자", "navigation_bar.community_timeline": "로컬 타임라인", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "프로필 편집", "navigation_bar.favourites": "즐겨찾기", "navigation_bar.follow_requests": "팔로우 요청", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index ff827991d..7bfb74a5c 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Opnieuw proberen", "column.blocks": "Geblokkeerde gebruikers", "column.community": "Lokale tijdlijn", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favorieten", "column.follow_requests": "Volgverzoeken", "column.home": "Start", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Verberg meldingen van deze persoon?", "navigation_bar.blocks": "Geblokkeerde gebruikers", "navigation_bar.community_timeline": "Lokale tijdlijn", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Profiel bewerken", "navigation_bar.favourites": "Favorieten", "navigation_bar.follow_requests": "Volgverzoeken", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index d3bc75708..b79277ce3 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Prøv igjen", "column.blocks": "Blokkerte brukere", "column.community": "Lokal tidslinje", + "column.domain_blocks": "Hidden domains", "column.favourites": "Likt", "column.follow_requests": "Følgeforespørsler", "column.home": "Hjem", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Skjul varslinger fra denne brukeren?", "navigation_bar.blocks": "Blokkerte brukere", "navigation_bar.community_timeline": "Lokal tidslinje", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Rediger profil", "navigation_bar.favourites": "Favoritter", "navigation_bar.follow_requests": "Følgeforespørsler", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 39ba31de3..5b12f8811 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Tornar ensajar", "column.blocks": "Personas blocadas", "column.community": "Flux public local", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favorits", "column.follow_requests": "Demandas d’abonament", "column.home": "Acuèlh", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Rescondre las notificacions d’aquesta persona ?", "navigation_bar.blocks": "Personas blocadas", "navigation_bar.community_timeline": "Flux public local", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Modificar lo perfil", "navigation_bar.favourites": "Favorits", "navigation_bar.follow_requests": "Demandas d’abonament", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index d9490c518..30ef51b19 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Spróbuj ponownie", "column.blocks": "Zablokowani użytkownicy", "column.community": "Lokalna oś czasu", + "column.domain_blocks": "Hidden domains", "column.favourites": "Ulubione", "column.follow_requests": "Prośby o śledzenie", "column.home": "Strona główna", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?", "navigation_bar.blocks": "Zablokowani użytkownicy", "navigation_bar.community_timeline": "Lokalna oś czasu", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Edytuj profil", "navigation_bar.favourites": "Ulubione", "navigation_bar.follow_requests": "Prośby o śledzenie", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 3d42eedb3..b056ec8bd 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Tente novamente", "column.blocks": "Usuários bloqueados", "column.community": "Local", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favoritos", "column.follow_requests": "Seguidores pendentes", "column.home": "Página inicial", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Esconder notificações deste usuário?", "navigation_bar.blocks": "Usuários bloqueados", "navigation_bar.community_timeline": "Local", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favoritos", "navigation_bar.follow_requests": "Seguidores pendentes", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index 5c93614a9..65983000c 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Tente de novo", "column.blocks": "Utilizadores Bloqueados", "column.community": "Local", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favoritos", "column.follow_requests": "Seguidores Pendentes", "column.home": "Início", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Esconder notificações deste utilizador?", "navigation_bar.blocks": "Utilizadores bloqueados", "navigation_bar.community_timeline": "Local", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favoritos", "navigation_bar.follow_requests": "Seguidores pendentes", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 7dffbb210..15959092c 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Попробовать снова", "column.blocks": "Список блокировки", "column.community": "Локальная лента", + "column.domain_blocks": "Hidden domains", "column.favourites": "Понравившееся", "column.follow_requests": "Запросы на подписку", "column.home": "Главная", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Убрать уведомления от этого пользователя?", "navigation_bar.blocks": "Список блокировки", "navigation_bar.community_timeline": "Локальная лента", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Изменить профиль", "navigation_bar.favourites": "Понравившееся", "navigation_bar.follow_requests": "Запросы на подписку", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 0a248d261..7bfae0302 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Skúsiť znova", "column.blocks": "Blokovaní užívatelia", "column.community": "Lokálna časová os", + "column.domain_blocks": "Hidden domains", "column.favourites": "Obľúbené", "column.follow_requests": "Žiadosti o sledovaní", "column.home": "Domov", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?", "navigation_bar.blocks": "Blokovaní užívatelia", "navigation_bar.community_timeline": "Lokálna časová os", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Upraviť profil", "navigation_bar.favourites": "Obľúbené", "navigation_bar.follow_requests": "Žiadosti o sledovanie", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index b9effce96..8fae49a03 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Pokušajte ponovo", "column.blocks": "Blokirani korisnici", "column.community": "Lokalna lajna", + "column.domain_blocks": "Hidden domains", "column.favourites": "Omiljeni", "column.follow_requests": "Zahtevi za praćenje", "column.home": "Početna", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Sakrij obaveštenja od ovog korisnika?", "navigation_bar.blocks": "Blokirani korisnici", "navigation_bar.community_timeline": "Lokalna lajna", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Izmeni profil", "navigation_bar.favourites": "Omiljeni", "navigation_bar.follow_requests": "Zahtevi za praćenje", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index a6c5f220e..a39fea582 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Покушајте поново", "column.blocks": "Блокирани корисници", "column.community": "Локална лајна", + "column.domain_blocks": "Hidden domains", "column.favourites": "Омиљени", "column.follow_requests": "Захтеви за праћење", "column.home": "Почетна", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Сакриј обавештења од овог корисника?", "navigation_bar.blocks": "Блокирани корисници", "navigation_bar.community_timeline": "Локална лајна", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Измени профил", "navigation_bar.favourites": "Омиљени", "navigation_bar.follow_requests": "Захтеви за праћење", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 6dc3d7a98..014492e19 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Försök igen", "column.blocks": "Blockerade användare", "column.community": "Lokal tidslinje", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favoriter", "column.follow_requests": "Följ förfrågningar", "column.home": "Hem", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Dölj notifikationer från denna användare?", "navigation_bar.blocks": "Blockerade användare", "navigation_bar.community_timeline": "Lokal tidslinje", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Redigera profil", "navigation_bar.favourites": "Favoriter", "navigation_bar.follow_requests": "Följförfrågningar", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 4de354007..ecfe7c9b5 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Local timeline", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", "column.home": "Home", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Blocked users", "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Edit profile", "navigation_bar.favourites": "Favourites", "navigation_bar.follow_requests": "Follow requests", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 9d0affea4..58d0e5785 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Engellenen kullanıcılar", "column.community": "Yerel zaman tüneli", + "column.domain_blocks": "Hidden domains", "column.favourites": "Favoriler", "column.follow_requests": "Takip istekleri", "column.home": "Anasayfa", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Engellenen kullanıcılar", "navigation_bar.community_timeline": "Yerel zaman tüneli", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Profili düzenle", "navigation_bar.favourites": "Favoriler", "navigation_bar.follow_requests": "Takip istekleri", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index c49d3c7ae..63866d339 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Заблоковані користувачі", "column.community": "Локальна стрічка", + "column.domain_blocks": "Hidden domains", "column.favourites": "Вподобане", "column.follow_requests": "Запити на підписку", "column.home": "Головна", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Заблоковані користувачі", "navigation_bar.community_timeline": "Локальна стрічка", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Редагувати профіль", "navigation_bar.favourites": "Вподобане", "navigation_bar.follow_requests": "Запити на підписку", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index e95cf81f4..f7cb49632 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "重试", "column.blocks": "屏蔽用户", "column.community": "本站时间轴", + "column.domain_blocks": "Hidden domains", "column.favourites": "收藏过的嘟文", "column.follow_requests": "关注请求", "column.home": "主页", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知", "navigation_bar.blocks": "被屏蔽的用户", "navigation_bar.community_timeline": "本站时间轴", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "修改个人资料", "navigation_bar.favourites": "收藏的内容", "navigation_bar.follow_requests": "关注请求", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 1801c838d..0504a8c7a 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "重試", "column.blocks": "封鎖用戶", "column.community": "本站時間軸", + "column.domain_blocks": "Hidden domains", "column.favourites": "最愛的文章", "column.follow_requests": "關注請求", "column.home": "主頁", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "被你封鎖的用戶", "navigation_bar.community_timeline": "本站時間軸", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "修改個人資料", "navigation_bar.favourites": "最愛的內容", "navigation_bar.follow_requests": "關注請求", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index acbe6eb8e..fab7ecf06 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "重試", "column.blocks": "封鎖的使用者", "column.community": "本地時間軸", + "column.domain_blocks": "Hidden domains", "column.favourites": "最愛", "column.follow_requests": "關注請求", "column.home": "家", @@ -153,6 +154,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "封鎖的使用者", "navigation_bar.community_timeline": "本地時間軸", + "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "編輯用者資訊", "navigation_bar.favourites": "最愛", "navigation_bar.follow_requests": "關注請求", From 929f58f1809e69d34f72aea3cea5ca7a8b01bc61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Sun, 1 Apr 2018 10:31:38 +0200 Subject: [PATCH 020/442] i18n: Update Polish translation (#6985) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- app/javascript/mastodon/locales/pl.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 30ef51b19..ff7180df3 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -18,7 +18,7 @@ "account.mute_notifications": "Wycisz powiadomienia o @{name}", "account.muted": "Wyciszony", "account.posts": "Wpisy", - "account.posts_with_replies": "Wpisy z odpowiedziami", + "account.posts_with_replies": "Wpisy i odpowiedzi", "account.report": "Zgłoś @{name}", "account.requested": "Oczekująca prośba, kliknij aby anulować", "account.share": "Udostępnij profil @{name}", @@ -40,7 +40,7 @@ "bundle_modal_error.retry": "Spróbuj ponownie", "column.blocks": "Zablokowani użytkownicy", "column.community": "Lokalna oś czasu", - "column.domain_blocks": "Hidden domains", + "column.domain_blocks": "Ukryte domeny", "column.favourites": "Ulubione", "column.follow_requests": "Prośby o śledzenie", "column.home": "Strona główna", @@ -58,7 +58,7 @@ "column_header.unpin": "Cofnij przypięcie", "column_subheading.navigation": "Nawigacja", "column_subheading.settings": "Ustawienia", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "Ten wpis będzie widoczny tylko dla wszystkich wspomnianych użytkowników.", "compose_form.hashtag_warning": "Ten wpis nie będzie widoczny pod podanymi hashtagami, ponieważ jest oznaczony jako niewidoczny. Tylko publiczne wpisy mogą zostać znalezione z użyciem hashtagów.", "compose_form.lock_disclaimer": "Twoje konto nie jest {locked}. Każdy, kto Cię śledzi, może wyświetlać Twoje wpisy przeznaczone tylko dla śledzących.", "compose_form.lock_disclaimer.lock": "zablokowane", @@ -154,7 +154,7 @@ "mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?", "navigation_bar.blocks": "Zablokowani użytkownicy", "navigation_bar.community_timeline": "Lokalna oś czasu", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.domain_blocks": "Ukryte domeny", "navigation_bar.edit_profile": "Edytuj profil", "navigation_bar.favourites": "Ulubione", "navigation_bar.follow_requests": "Prośby o śledzenie", From 993ce0e5a7d86330d1ac7a9123735d8a218652b3 Mon Sep 17 00:00:00 2001 From: luzi82 Date: Sun, 1 Apr 2018 20:57:26 +0800 Subject: [PATCH 021/442] improve zh-HK localization by referring zh-TW and zh-CN (#6988) --- config/locales/activerecord.zh-HK.yml | 13 + config/locales/devise.zh-HK.yml | 21 ++ config/locales/doorkeeper.zh-HK.yml | 6 + config/locales/simple_form.zh-HK.yml | 18 +- config/locales/zh-HK.yml | 351 ++++++++++++++++++++++++++ 5 files changed, 408 insertions(+), 1 deletion(-) create mode 100644 config/locales/activerecord.zh-HK.yml diff --git a/config/locales/activerecord.zh-HK.yml b/config/locales/activerecord.zh-HK.yml new file mode 100644 index 000000000..2ebf9586c --- /dev/null +++ b/config/locales/activerecord.zh-HK.yml @@ -0,0 +1,13 @@ +--- +zh-HK: + activerecord: + errors: + models: + account: + attributes: + username: + invalid: 只能使用字母、數字和下劃線 + status: + attributes: + reblog: + taken: 已經被轉推過 diff --git a/config/locales/devise.zh-HK.yml b/config/locales/devise.zh-HK.yml index a3e5980d5..12d6e3c2c 100644 --- a/config/locales/devise.zh-HK.yml +++ b/config/locales/devise.zh-HK.yml @@ -17,11 +17,32 @@ zh-HK: unconfirmed: 你必須先確認電郵地址,繼續使用。 mailer: confirmation_instructions: + action: 驗證電子郵件地址 + explanation: 你在 %{host} 上使用這個電子郵件地址建立了一個帳戶。只需點擊下面的連結,即可啟用帳戶。如果你並沒有建立過帳戶,請忽略此郵件。 + extra_html: 請記得閱讀本服務站的相關規定使用條款。 subject: 'Mastodon: 確認電郵地址' + title: 驗證電子郵件地址 + email_changed: + explanation: 你的帳戶的電子郵件地址即將變更為: + extra: 如果你沒有請求更改你的電子郵件地址,則他人很有可能已經入侵你的帳戶。請立即更改你的密碼;如果你已經無法訪問你的帳戶,請聯繫服務站的管理員請求協助。 + subject: Mastodon:電子郵件地址已被更改 + title: 新電子郵件地址 password_change: + explanation: 你的帳戶的密碼已被更改。 + extra: 如果你沒有請求更改你的密碼,則他人很有可能已經入侵你的帳戶。請立即更改你的密碼;如果你已經無法訪問你的帳戶,請聯繫服務站的管理員請求協助。 subject: 'Mastodon: 更改密碼' + title: 密碼已被重設 + reconfirmation_instructions: + explanation: 點擊下面的連結來確認你的新電子郵件地址。 + extra: 如果你沒有請求本次變更,請忽略此郵件。 Mastodon 帳戶的電子郵件地址只有在你點擊上面的連結後才會更改。 + subject: Mastodon:確認 %{instance} 電子郵件地址 + title: 驗證電子郵件地址 reset_password_instructions: + action: 更改密碼 + explanation: 點擊下面的連結來更改帳戶的密碼。 + extra: 如果你沒有請求本次變更,請忽略此郵件。你的密碼只有在你點擊上面的連結並輸入新密碼後才會更改。 subject: 'Mastodon: 重設密碼' + title: 重設密碼 unlock_instructions: subject: 'Mastodon: 解除用戶鎖定' omniauth_callbacks: diff --git a/config/locales/doorkeeper.zh-HK.yml b/config/locales/doorkeeper.zh-HK.yml index c8edc2b72..747108762 100644 --- a/config/locales/doorkeeper.zh-HK.yml +++ b/config/locales/doorkeeper.zh-HK.yml @@ -5,6 +5,8 @@ zh-HK: doorkeeper/application: name: 名稱 redirect_uri: 轉接 URI + scopes: 權限範圍 + website: 應用網站 errors: models: doorkeeper/application: @@ -33,9 +35,13 @@ zh-HK: redirect_uri: 每行輸入一個 URI scopes: 請用半形空格分開權限範圍 (scope)。留空表示使用預設的權限範圍 index: + application: 應用 callback_url: 回傳網址 + delete: 刪除 name: 名稱 new: 新增應用程式 + scopes: 權限範圍 + show: 顯示 title: 你的應用程式 new: title: 新增應用程式 diff --git a/config/locales/simple_form.zh-HK.yml b/config/locales/simple_form.zh-HK.yml index 1e545da88..01ba61fdf 100644 --- a/config/locales/simple_form.zh-HK.yml +++ b/config/locales/simple_form.zh-HK.yml @@ -4,12 +4,17 @@ zh-HK: hints: defaults: avatar: 支援 PNG, GIF 或 JPG 圖片,檔案最大為 2MB,會縮裁成 400x400px + digest: 僅在你長時間未登錄,且收到了私信時發送 display_name: 最多 30 個字元 header: 支援 PNG, GIF 或 JPG 圖片,檔案最大為 2MB,會縮裁成 700x335px locked: 你必須人手核准每個用戶對你的關注請求,而你的文章私隱會被預設為「只有關注你的人能看」 note: 最多 160 個字元 + setting_noindex: 此設定會影響到你的公開個人資料以及文章頁面 + setting_theme: 此設置會影響到你從任意設備登入時 Mastodon 的顯示樣式 imports: data: 自其他服務站匯出的 CSV 檔案 + sessions: + otp: 輸入你手機上生成的雙重認證碼,或者任意一個恢復代碼。 user: filtered_languages: 下面被選擇的語言的文章將不會出現在你的公共時間軸上。 labels: @@ -21,22 +26,33 @@ zh-HK: data: 資料 display_name: 顯示名稱 email: 電郵地址 - filtered_languages: 封鎖下面语言的文章 + expires_in: 失效時間 + filtered_languages: 封鎖下面語言的文章 header: 個人頁面頂部 locale: 語言 locked: 將用戶轉為「私人」 + max_uses: 最大使用次數 new_password: 新密碼 note: 簡介 otp_attempt: 雙重認證碼 password: 密碼 + setting_auto_play_gif: 自動播放 GIF setting_boost_modal: 在轉推前詢問我 setting_default_privacy: 文章預設為 + setting_default_sensitive: 預設我的內容為敏感內容 + setting_delete_modal: 刪推前詢問我 + setting_noindex: 阻止搜尋引擎檢索 + setting_reduce_motion: 減低動畫效果 + setting_system_font_ui: 使用系統預設字型 + setting_theme: 網站主題 + setting_unfollow_modal: 取消關注前跳出詢問我 severity: 等級 type: 匯入資料類型 username: 用戶名稱 interactions: must_be_follower: 隱藏沒有關注你的用戶的通知 must_be_following: 隱藏你不關注的用戶的通知 + must_be_following_dm: 隱藏你不關注的用戶的私信 notification_emails: digest: 定期電郵摘要 favourite: 當有用戶喜歡你的文章時,發電郵通知 diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index e7ab347a1..cc1cade1e 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -1,53 +1,98 @@ --- zh-HK: about: + about_hashtag_html: 這些是包含「#%{hashtag}」標籤的公開文章。只要你有任何 Mastodon 服務站、或者聯盟網站的用戶,便可以與他們互動。 about_mastodon_html: Mastodon(萬象)是自由、開源的社交網絡。服務站各自獨立而互連,避免單一商業機構壟斷。找你所信任的服務站,建立帳號,你即可與任何服務站上的用戶溝通,享受無縫的網絡交流。 about_this: 關於本服務站 closed_registrations: 本服務站暫時停止接受登記。 contact: 聯絡 + contact_missing: 未設定 + contact_unavailable: 未公開 description_headline: 關於 %{domain} domain_count_after: 個其他服務站 domain_count_before: 已連接至 + extended_description_html: | +

這裡可以寫一些網站規則

+

本站未有詳細介紹

+ features: + humane_approach_body: Mastodon 從其他網絡的失敗經驗中汲取教訓,以合乎道德的設計對抗社交媒體的濫用問題。 + humane_approach_title: 以人為本 + not_a_product_body: Mastodon 不是商業網絡。沒有廣告,沒有數據挖掘,也沒有中央機構操控。平台完全開放。 + not_a_product_title: 你是用戶,不是商品 + real_conversation_body: Mastodon 的字數限制高達 500 字,並支援仔細的媒體警告選項,令你暢所欲言。 + real_conversation_title: 為真正的交流而生 + within_reach_body: 簡易的 API 系統,令用戶可以透過不同的 iOS、Android 及其他平台的應用軟件,與朋友保持聯繫。 + within_reach_title: 無處不在 + generic_description: "%{domain} 是 Mastodon 網絡中其中一個服務站" + hosted_on: 在 %{domain} 運作的 Mastodon 服務站 + learn_more: 了解更多 other_instances: 其他服務站 source_code: 源代碼 status_count_after: 篇文章 status_count_before: 他們共發佈了 user_count_after: 位使用者 user_count_before: 這裏共註冊有 + what_is_mastodon: Mastodon 是甚麼? accounts: follow: 關注 followers: 關注者 following: 正在關注 + media: 媒體 + moved_html: "%{name} 已經轉移到 %{new_profile_link}:" nothing_here: 暫時未有內容可以顯示 people_followed_by: "%{name} 關注的人" people_who_follow: 關注 %{name} 的人 posts: 文章 + posts_with_replies: 文章和回覆 remote_follow: 跨站關注 + reserved_username: 此用戶名已被保留 + roles: + admin: 管理員 + moderator: 監察员 unfollow: 取消關注 admin: + account_moderation_notes: + account: 管理員 + create: 新增 + created_at: 日期 + created_msg: 管理記錄已新增 + delete: 刪除 + destroyed_msg: 管理記錄已被刪除 accounts: are_you_sure: 你確定嗎? + by_domain: 域名 confirm: 確定 confirmed: 已確定 + demote: 降任 + disable: 停用 disable_two_factor_authentication: 停用雙重認證 + disabled: 已停用 display_name: 顯示名稱 domain: 域名 edit: 編輯 email: 電郵地址 + enable: 啟用 + enabled: 已啟用 feed_url: Feed URL followers: 關注者 + followers_url: 關注者(Followers)URL follows: 正在關注 + inbox_url: 收件箱(Inbox)URL + ip: IP 位域 location: all: 全部 local: 本地 remote: 遠端 title: 地點 + login_status: 登入狀態 media_attachments: 媒體檔案 + memorialize: 設定為追悼帳戶 moderation: all: 全部 silenced: 被靜音的 suspended: 被停權的 title: 管理操作 + moderation_notes: 管理記錄 most_recent_activity: 最新活動 most_recent_ip: 最新 IP 位域 not_subscribed: 未訂閱 @@ -55,23 +100,90 @@ zh-HK: alphabetic: 按字母 most_recent: 按時間 title: 排序 + outbox_url: 寄件箱(Outbox)URL perform_full_suspension: 實行完全暫停 profile_url: 個人檔案 URL + promote: 升任 + protocol: 協議 public: 公共 push_subscription_expires: PuSH 訂閱過期 + redownload: 更新頭像 + reset: 重設 reset_password: 重設密碼 + resubscribe: 重新訂閱 + role: 身份 + roles: + admin: 管理員 + moderator: 監察员 + staff: 管理人員 + user: 普通用戶 salmon_url: Salmon 反饋 URL + search: 搜索 + shared_inbox_url: 公共收件箱(Shared Inbox)URL show: created_reports: 此用戶所提舉報的紀錄 report: 舉報 targeted_reports: 此用戶被舉報的紀錄 silence: 靜音 statuses: 文章 + subscribe: 訂閱 title: 用戶 undo_silenced: 解除靜音 undo_suspension: 解除停權 + unsubscribe: 取消訂閱 username: 用戶名稱 web: 用戶頁面 + action_logs: + actions: + confirm_user: "%{name} 確認了用戶 %{target} 的電郵地址" + create_custom_emoji: "%{name} 加入自訂表情符號 %{target}" + create_domain_block: "%{name} 阻隔了網域 %{target}" + create_email_domain_block: "%{name} 阻隔了電郵網域 %{target}" + demote_user: "%{name} 把用戶 %{target} 降任" + destroy_domain_block: "%{name} 取消了對網域 %{target} 的阻隔" + destroy_email_domain_block: "%{name} 取消了對電郵網域 %{target} 的阻隔" + destroy_status: "%{name} 刪除了 %{target} 的文章" + disable_2fa_user: "%{name} 停用了用戶 %{target} 的雙重認證" + disable_custom_emoji: "%{name} 停用了自訂表情符號 %{target}" + disable_user: "%{name} 把用戶 %{target} 設定為禁止登入" + enable_custom_emoji: "%{name} 啟用了自訂表情符號 %{target}" + enable_user: "%{name} 把用戶 %{target} 設定為允許登入" + memorialize_account: "%{name} 把 %{target} 設定為追悼帳戶" + promote_user: "%{name} 對用戶 %{target} 进行了升任操作" + reset_password_user: "%{name} 重設了用戶 %{target} 的密碼" + resolve_report: "%{name} 處理了 %{target} 的舉報" + silence_account: "%{name} 靜音了用戶 %{target}" + suspend_account: "%{name} 停權了用戶 %{target}" + unsilence_account: "%{name} 取消了用戶 %{target} 的靜音狀態" + unsuspend_account: "%{name} 取消了用戶 %{target} 的停權狀態" + update_custom_emoji: "%{name} 更新了自訂表情符號 %{target}" + update_status: "%{name} 刷新了 %{target} 的文章" + title: 營運日誌 + custom_emojis: + by_domain: 網域 + copied_msg: 成功將表情複製到本地 + copy: 複製 + copy_failed_msg: 無法將表情複製到本地 + created_msg: 已新增表情符號 + delete: 刪除 + destroyed_msg: 已刪除表情符號 + disable: 停用 + disabled_msg: 已停用表情符號 + emoji: emoji + enable: 啟用 + enabled_msg: 已啟用表情符號 + image_hint: PNG 格式,最大 50KB + listed: 已顯示 + new: + title: 加入新的自訂表情符號 + overwrite: 覆蓋 + shortcode: 短代碼 + shortcode_hint: 至少 2 個字元,只能使用字母、數字和下劃線 + title: 自訂 emoji + unlisted: 已隱藏 + update_failed_msg: 無法更新表情符號 + updated_msg: 已更新表情符號 + upload: 上傳新的表情符號 domain_blocks: add_new: 新增 created_msg: 正處理域名阻隔 @@ -82,12 +194,14 @@ zh-HK: hint: "「域名阻隔」不會隔絕該域名用戶的用戶進入本站資料庫,而是會在時候自動套用特定的審批操作。" severity: desc_html: "「自動靜音」令該域名用戶的文章,設為只對關注者顯示,沒有關注的人會看不到。 「自動刪除」會自動將該域名用戶的文章、媒體檔案、個人資料自本服務站刪除。" + noop: 無 silence: 自動靜音 suspend: 自動刪除 title: 新增域名阻隔 reject_media: 拒絕媒體檔案 reject_media_hint: 刪除本地緩存的媒體檔案,再也不在未來下載這個站點的檔案。和自動刪除無關。 severities: + noop: 無 silence: 自動靜音 suspend: 自動刪除 severity: 阻隔分級 @@ -101,17 +215,41 @@ zh-HK: undo: 撤銷 title: 域名阻隔 undo: 撤銷 + email_domain_blocks: + add_new: 加入新項目 + created_msg: 已新增電郵網域阻隔 + delete: 刪除 + destroyed_msg: 已刪除電郵網域阻隔 + domain: 網域 + new: + create: 新增網域 + title: 新增電郵網域阻隔 + title: 電郵網域阻隔 instances: account_count: 已知帳號 domain_name: 域名 + reset: 重設 + search: 搜索 title: 已知服務站 + invites: + filter: + all: 全部 + available: 可用 + expired: 已失效 + title: 篩選 + title: 邀請用戶 reports: + action_taken_by: 操作執行者 + are_you_sure: 你確認嗎? comment: label: 詳細解釋 none: 沒有 delete: 刪除 id: ID mark_as_resolved: 標示為「已處理」 + nsfw: + 'false': 取消 NSFW 標記 + 'true': 添加 NSFW 標記 report: '舉報 #%{id}' report_contents: 舉報內容 reported_account: 舉報用戶 @@ -125,23 +263,66 @@ zh-HK: unresolved: 未處理 view: 檢視 settings: + activity_api_enabled: + desc_html: 本站用戶發佈的文章,以及本站的活躍用戶和一週內新用戶數 + title: 公開用戶活躍度的統計數據 + bootstrap_timeline_accounts: + desc_html: 以半形逗號分隔多個用戶名。只能加入來自本站且未開啟保護的帳號。如果留空,則默認關注本站所有管理員。 + title: 新用戶默認關注 contact_information: email: 輸入一個公開的電郵地址 username: 輸入用戶名稱 + peers_api_enabled: + desc_html: 現時本服務站在網絡中已發現的域名 + title: 公開已知服務站的列表 registrations: closed_message: desc_html: 當本站暫停接受註冊時,會顯示這個訊息。
可使用 HTML title: 暫停註冊訊息 + deletion: + desc_html: 允許所有人刪除自己的帳戶 + title: 開放刪除帳戶的權限 + min_invite_role: + disabled: 沒有人 + title: 允許發送邀請的身份 open: + desc_html: 允許所有人建立帳戶 title: 開放註冊 + show_staff_badge: + desc_html: 在個人資料頁上顯示管理人員標誌 + title: 顯示管理人員標誌 site_description: desc_html: 在首頁顯示,及在 meta 標籤使用作網站介紹。
你可以在此使用 <a><em> 等 HTML 標籤。 title: 本站介紹 site_description_extended: desc_html: 本站詳細資訊頁的內文
你可以在此使用 HTML title: 本站詳細資訊 + site_terms: + desc_html: 可以填寫自己的隱私權政策、使用條款或其他法律文本。可以使用 HTML 標籤 + title: 自訂使用條款 site_title: 本站名稱 + thumbnail: + desc_html: 用於在 OpenGraph 和 API 中顯示預覽圖。推薦大小 1200×630px + title: 本站縮圖 + timeline_preview: + desc_html: 在主頁顯示本站時間軸 + title: 時間軸預覽 title: 網站設定 + statuses: + back_to_account: 返回帳戶信息頁 + batch: + delete: 刪除 + nsfw_off: 取消 NSFW 標記 + nsfw_on: 添加 NSFW 標記 + execute: 執行 + failed_to_execute: 執行失敗 + media: + hide: 隱藏媒體檔案 + show: 顯示媒體檔案 + title: 媒體檔案 + no_media: 不含媒體檔案 + title: 帳戶文章 + with_media: 含有媒體檔案 subscriptions: callback_url: 回傳 URL confirmed: 確定 @@ -150,16 +331,36 @@ zh-HK: title: PuSH 訂閱 topic: 所訂閱資源 title: 管理 + admin_mailer: + new_report: + body: "%{reporter} 舉報了用戶 %{target}。" + subject: 來自 %{instance} 的用戶舉報(#%{id}) application_mailer: + notification_preferences: 更改電郵首選項 + salutation: "%{name}:" settings: 修改電郵設定︰%{link} view: 進入瀏覽︰ + view_profile: 檢視個人資料頁 + view_status: 檢視文章 applications: + created: 已建立應用 + destroyed: 已刪除應用 invalid_url: 所提供的網址不正確 + regenerate_token: 重設 token + token_regenerated: 已重設 token + warning: 警告,不要把它分享給任何人! + your_token: token auth: + agreement_html: 登記即表示你同意遵守本服務站的規則使用條款。 + delete_account: 刪除帳戶 + delete_account_html: 如果你想刪除你的帳戶,請點擊這裡繼續。你需要確認你的操作。 didnt_get_confirmation: 沒有收到確認指示電郵? forgot_password: 忘記了密碼? + invalid_reset_password_token: 密碼重置 token 無效或已過期。請重新重設密碼。 login: 登入 logout: 登出 + migrate_account: 轉移到另一個帳號 + migrate_account_html: 想要將這個帳號指向另一個帳號可到這裡設定。 register: 登記 resend_confirmation: 重發確認指示電郵 reset_password: 重設密碼 @@ -168,6 +369,12 @@ zh-HK: authorize_follow: error: 對不起,尋找這個跨站用戶的過程發生錯誤 follow: 關注 + follow_request: 關注請求已發送给: + following: 成功!你正在關注: + post_follow: + close: 你也可以直接關閉這個頁面 + return: 返回至個人資料頁 + web: 返回本站 title: 關注 %{acct} datetime: distance_in_words: @@ -183,6 +390,14 @@ zh-HK: x_minutes: "%{count}分鐘" x_months: "%{count}個月" x_seconds: "%{count}秒" + deletes: + bad_password_msg: 想得美,黑客!密碼輸入錯誤 + confirm_password: 輸入你現在的密碼來驗證身份 + description_html: 繼續操作將會永久地、不可還原地刪除帳戶中的所有內容,然後凍結帳戶。你的用戶名將會被保留,以防有人冒用你的身份。 + proceed: 刪除帳戶 + success_msg: 你的帳戶已經成功刪除 + warning_html: 我們只能保證本服務站上的內容將會被徹底刪除。對於已經被廣泛傳播的內容,它們在本服務站以外的某些地方可能仍然可見。此外,失去連接的服務站以及停止接收訂閱的服務站所存儲的數據亦無法刪除。 + warning_title: 關於已傳播的內容的警告 errors: '403': 你沒有觀看本頁的權限 '404': 找不到內容 @@ -191,6 +406,10 @@ zh-HK: content: 無法確認登入資訊。會不會你阻擋了本站使用 Cookies 的權限? title: 無法確認登入資訊 '429': 伺服器繁忙 + '500': + content: 抱歉,我們的後台出錯了。 + title: 這個頁面有問題 + noscript_html: 使用 Mastodon 網頁版應用需要啟用 JavaScript。你也可以選擇適用於你的平台的 Mastodon 應用。 exports: blocks: 被你封鎖的用戶 csv: CSV @@ -224,14 +443,44 @@ zh-HK: following: 你所關注的用戶名單 muting: 靜音名單 upload: 上載 + in_memoriam_html: 謹此悼念。 + invites: + delete: 停用 + expired: 已失效 + expires_in: + '1800': 30 分鐘後 + '21600': 6 小時後 + '3600': 1 小時後 + '43200': 12 小時後 + '86400': 1 天後 + expires_in_prompt: 永不過期 + generate: 生成邀請連結 + max_uses: "%{count} 次" + max_uses_prompt: 無限制 + prompt: 生成分享連結,邀請他人在本服務站註冊 + table: + expires_at: 失效時間 + uses: 已使用次數 + title: 邀請用戶 landing_strip_html: "%{name} 是一個在 %{link_to_root_path} 的用戶。只要你有任何 Mastodon 服務站、或者聯盟網站的用戶,便可以跨站關注此站用戶,或者與他們互動。" landing_strip_signup_html: 如果你沒有這類用戶,歡迎在此處登記。 + lists: + errors: + limit: 你所建立的列表數量已經達到上限 media_attachments: validations: images_and_video: 不能在已有圖片的文章上加入影片 too_many: 不可以加入超過 4 個檔案 + migrations: + acct: 新帳戶的 用戶名@域名 + currently_redirecting: 目前你的個人資料頁顯示的新帳戶是: + proceed: 保存 + updated_msg: 帳戶遷移設置更新成功! + moderation: + title: 營運 notification_mailer: digest: + action: 查看所有通知 body: 這是自從你在%{since}使用%{instance}以後,你錯失了的訊息︰ mention: "%{name} 在此提及了你︰" new_followers_summary: @@ -240,21 +489,29 @@ zh-HK: subject: one: "自從上次登入以來,你收到 1 則新的通知 \U0001F418" other: "自從上次登入以來,你收到 %{count} 則新的通知 \U0001F418" + title: 在你不在的這段時間…… favourite: body: 你的文章是 %{name} 的最愛! subject: "%{name} 收藏了你的文章" + title: 新的收藏 follow: body: "%{name} 開始關注你!" subject: "%{name} 現正關注你" + title: 新的關注者 follow_request: + action: 處理關注請求 body: "%{name} 要求關注你" subject: 等候關注你的用戶︰ %{name} + title: 新的關注請求 mention: + action: 回覆 body: "%{name} 在文章中提及你︰" subject: "%{name} 在文章中提及你" + title: 新的提及 reblog: body: 你的文章得到 %{name} 的轉推 subject: "%{name} 轉推了你的文章" + title: 新的轉推 number: human: decimal_units: @@ -270,25 +527,94 @@ zh-HK: next: 下一頁 prev: 上一頁 truncate: "……" + preferences: + languages: 語言 + other: 其他 + publishing: 發佈 + web: 站内 + push_notifications: + favourite: + title: "%{name} 收藏了你的文章" + follow: + title: "%{name} 關注了你" + group: + title: "%{count} 條新通知" + mention: + action_boost: 轉推 + action_expand: 顯示更多 + action_favourite: 收藏 + title: "%{name} 提到了你" + reblog: + title: "%{name} 轉推了你的文章" remote_follow: acct: 請輸入你的︰用戶名稱@服務點域名 missing_resource: 無法找到你用戶的轉接網址 proceed: 下一步 prompt: 你希望關注︰ + sessions: + activity: 最近活動 + browser: 瀏覽器 + browsers: + alipay: 支付寶 + blackberry: Blackberry + chrome: Chrome + edge: Microsoft Edge + firefox: Firefox + generic: 未知的瀏覽器 + ie: Internet Explorer + micro_messenger: 微信 + nokia: Nokia S40 Ovi 瀏覽器 + opera: Opera + phantom_js: PhantomJS + qq: QQ瀏覽器 + safari: Safari + uc_browser: UC瀏覽器 + weibo: 新浪微博 + current_session: 目前的 session + description: "%{platform} 上的 %{browser}" + explanation: 這些是現在正登入於你的 Mastodon 帳號的瀏覽器。 + ip: IP 位址 + platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac + other: 未知平台 + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone + revoke: 取消 + revoke_success: Session 取消成功。 + title: Session settings: authorized_apps: 授權應用程式 back: 回到 Mastodon + delete: 刪除帳戶 + development: 開發 edit_profile: 修改個人資料 export: 匯出 followers: 授權關注 import: 匯入 + migrate: 帳戶遷移 + notifications: 通知 preferences: 偏好設定 settings: 設定 two_factor_authentication: 雙重認證 + your_apps: 你的應用程式 statuses: open_in_web: 開啟網頁 over_character_limit: 超過了 %{max} 字的限制 + pin_errors: + limit: 你所置頂的文章數量已經達到上限 + ownership: 不能置頂他人的文章 + private: 不能置頂非公開的文章 + reblog: 不能置頂轉推 show_more: 顯示更多 + title: "%{name}:「%{quote}」" visibilities: private: 關注者觀看 private_long: 只有關注你的人能看到 @@ -298,8 +624,11 @@ zh-HK: unlisted_long: 所有人都能看到,但不在公共時間軸(本站時間軸、跨站時間軸)顯示 stream_entries: click_to_show: 點擊顯示 + pinned: 置頂文章 reblogged: 轉推 sensitive_content: 敏感內容 + terms: + title: "%{instance} 使用條款和隱私權政策" time: formats: default: "%Y年%-m月%d日 %H:%M" @@ -308,15 +637,37 @@ zh-HK: description_html: 當你啟用雙重認證後,你登入時將需要使你手機、或其他種類認證器產生的代碼。 disable: 停用 enable: 啟用 + enabled: 雙重認證已啟用 enabled_success: 已成功啟用雙重認證 generate_recovery_codes: 產生備用驗證碼 instructions_html: "請用你手機的認證器應用程式(如 Google Authenticator、Authy),掃描這裏的QR 圖形碼。在雙重認證啟用後,你登入時將須要使用此應用程式產生的認證碼。" lost_recovery_codes: 讓你可以在遺失電話時,使用備用驗證碼登入。如果你遺失了備用驗證碼,可以在這裏產生一批新的,舊有的備用驗證碼將會失效。 manual_instructions: 如果你無法掃描 QR 圖形碼,請手動輸入這個文字密碼︰ + recovery_codes: 備份恢復驗證碼 recovery_codes_regenerated: 成功產生新的備用驗證碼 recovery_instructions_html: 如果你遺失了安裝認證器的裝置(如︰你的電話),你可以使用備用驗證碼進行登入。請確保將備用驗證碼收藏穩當,(如列印出來,和你其他重要文件一起存放) setup: 設定 wrong_code: 你輸入的認證碼並不正確!可能伺服器時間和你手機不一致,請檢查你手機的時鐘,或與本站管理員聯絡。 + user_mailer: + welcome: + edit_profile_action: 設定個人資料 + edit_profile_step: 你可以設定你的個人資料,包括上傳頭像、橫幅圖片、更改顯示名稱等等。如果你想在新的關注者關注你之前對他們進行審核,你也可以選擇為你的帳戶設為「私人」。 + explanation: 下面是幾個小貼士,希望它們能幫到你 + final_action: 開始發文 + final_step: '開始發文吧!即使你現在沒有關注者,其他人仍然能在本站時間軸或者話題標籤等地方看到你的公開文章。試著用 #introductions 這個話題標籤介紹一下自己吧。' + full_handle: 你的完整用戶地址 + full_handle_hint: 你需要把這個告訴你的朋友們,這樣他們就能從另一個實例向你發送信息或者關注你。 + review_preferences_action: 更改首選項 + review_preferences_step: 記得調整你的偏好設置,比如你想接收什麼類型的郵件,或者你想把你的文章可見範圍默認設定為什麼級別。如果你沒有暈車的話,考慮一下啟用「自動播放 GIF 動畫」這個選項吧。 + subject: 歡迎來到 Mastodon + tip_bridge_html: 如果你剛從 Twitter 來到這裡,你可以在橋樑站(bridge app)上尋找你的朋友。當然,前提是他們也登錄了橋樑站! + tip_federated_timeline: 跨站公共時間軸可以讓你一窺更廣闊的 Mastodon 網絡。不過,由於它只顯示你的鄰居們所訂閱的內容,所以並不是全部。 + tip_following: 默認情況下,你會自動關注你所在服務站的管理員。想結交更多有趣的人的話,記得多逛逛本站時間軸和跨站公共時間軸哦。 + tip_local_timeline: 本站時間軸可以讓你一窺 %{instance} 上的用戶。他們就是離你最近的鄰居! + tip_mobile_webapp: 如果你的移動設備瀏覽器允許你將 Mastodon 添加到主屏幕,你就能夠接收推送消息。它就像本地應用一樣好使! + tips: 小貼士 + title: "%{name},歡迎你的加入!" users: invalid_email: 電郵地址格式不正確 invalid_otp_token: 雙重認證確認碼不正確 + signed_in_as: 目前登入的帳戶: From 6a895e1ab3d69cd018423460518a1e16307999ad Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Sun, 1 Apr 2018 22:19:43 +0200 Subject: [PATCH 022/442] Fix: Prevent submission using same logic as submit button disabling. (#6993) This prevents submission through ctrl/cmd+enter when the submit button is disabled. --- .../mastodon/features/compose/components/compose_form.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index 663ccfb8e..fe7bb1cb3 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -74,6 +74,14 @@ export default class ComposeForm extends ImmutablePureComponent { this.props.onChange(this.autosuggestTextarea.textarea.value); } + // Submit disabled: + const { is_submitting, is_uploading, anyMedia } = this.props; + const fulltext = [this.props.spoiler_text, countableText(this.props.text)].join(''); + + if (is_submitting || is_uploading || length(fulltext) > 500 || (fulltext.length !== 0 && fulltext.trim().length === 0 && !anyMedia)) { + return; + } + this.props.onSubmit(); } From f464f98fd3b8ef33b3afa5acf09e829c046134de Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Mon, 2 Apr 2018 06:43:08 +0900 Subject: [PATCH 023/442] Update Ruby to version 2.4.4 (#6964) https://www.ruby-lang.org/en/news/2018/03/28/ruby-2-4-4-released/ > This release includes some bug fixes and some security fixes. > > - CVE-2017-17742: HTTP response splitting in WEBrick > - CVE-2018-6914: Unintentional file and directory creation with directory traversal in tempfile and tmpdir > - CVE-2018-8777: DoS by large request in WEBrick > - CVE-2018-8778: Buffer under-read in String#unpack > - CVE-2018-8779: Unintentional socket creation by poisoned NUL byte in UNIXServer and UNIXSocket > - CVE-2018-8780: Unintentional directory traversal by poisoned NUL byte in Dir > - Multiple vulnerabilities in RubyGems --- .ruby-version | 2 +- .travis.yml | 2 +- Dockerfile | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.ruby-version b/.ruby-version index 437459cd9..73462a5a1 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.5.0 +2.5.1 diff --git a/.travis.yml b/.travis.yml index 576659aaf..989237a19 100644 --- a/.travis.yml +++ b/.travis.yml @@ -39,7 +39,7 @@ addons: - yarn rvm: - - 2.4.2 + - 2.4.3 - 2.5.0 services: diff --git a/Dockerfile b/Dockerfile index 0801f5747..5f17c5b89 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ruby:2.4.3-alpine3.6 +FROM ruby:2.4.4-alpine3.6 LABEL maintainer="https://github.com/tootsuite/mastodon" \ description="Your self-hosted, globally interconnected microblogging community" From 123a343d116d3e83cbd04460cc7bd0b6f3d208c4 Mon Sep 17 00:00:00 2001 From: David Underwood Date: Sun, 1 Apr 2018 17:55:42 -0400 Subject: [PATCH 024/442] [WIP] Enable custom emoji on account pages and in the sidebar (#6124) Federate custom emojis with accounts --- app/lib/formatter.rb | 11 ++- app/lib/ostatus/atom_serializer.rb | 3 + app/models/account.rb | 4 + app/models/remote_profile.rb | 4 + .../activitypub/actor_serializer.rb | 9 +++ app/serializers/rest/account_serializer.rb | 2 +- .../activitypub/process_account_service.rb | 28 +++++++ app/services/update_remote_profile_service.rb | 21 +++++ app/views/accounts/_header.html.haml | 2 +- spec/lib/formatter_spec.rb | 79 +++++++++++++++++++ 10 files changed, 158 insertions(+), 5 deletions(-) diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 1df4ff8d4..f7e7a3c23 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -51,9 +51,14 @@ class Formatter strip_tags(text) end - def simplified_format(account) - return reformat(account.note).html_safe unless account.local? # rubocop:disable Rails/OutputSafety - linkify(account.note) + def simplified_format(account, **options) + html = if account.local? + linkify(account.note) + else + reformat(account.note) + end + html = encode_custom_emojis(html, CustomEmoji.from_text(account.note, account.domain)) if options[:custom_emojify] + html.html_safe # rubocop:disable Rails/OutputSafety end def sanitize(html, config) diff --git a/app/lib/ostatus/atom_serializer.rb b/app/lib/ostatus/atom_serializer.rb index 46d0a8b37..055b4649c 100644 --- a/app/lib/ostatus/atom_serializer.rb +++ b/app/lib/ostatus/atom_serializer.rb @@ -26,6 +26,9 @@ class OStatus::AtomSerializer append_element(author, 'link', nil, rel: :alternate, type: 'text/html', href: ::TagManager.instance.url_for(account)) append_element(author, 'link', nil, rel: :avatar, type: account.avatar_content_type, 'media:width': 120, 'media:height': 120, href: full_asset_url(account.avatar.url(:original))) if account.avatar? append_element(author, 'link', nil, rel: :header, type: account.header_content_type, 'media:width': 700, 'media:height': 335, href: full_asset_url(account.header.url(:original))) if account.header? + account.emojis.each do |emoji| + append_element(author, 'link', nil, rel: :emoji, href: full_asset_url(emoji.image.url), name: emoji.shortcode) + end append_element(author, 'poco:preferredUsername', account.username) append_element(author, 'poco:displayName', account.display_name) if account.display_name? append_element(author, 'poco:note', account.local? ? account.note : strip_tags(account.note)) if account.note? diff --git a/app/models/account.rb b/app/models/account.rb index 25e7d7436..a34b6a2d3 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -350,6 +350,10 @@ class Account < ApplicationRecord end end + def emojis + CustomEmoji.from_text(note, domain) + end + before_create :generate_keys before_validation :normalize_domain before_validation :prepare_contents, if: :local? diff --git a/app/models/remote_profile.rb b/app/models/remote_profile.rb index 613911c57..742d2b56f 100644 --- a/app/models/remote_profile.rb +++ b/app/models/remote_profile.rb @@ -41,6 +41,10 @@ class RemoteProfile @header ||= link_href_from_xml(author, 'header') end + def emojis + @emojis ||= author.xpath('./xmlns:link[@rel="emoji"]', xmlns: OStatus::TagManager::XMLNS) + end + def locked? scope == 'private' end diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index afcd37771..df3090726 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -10,6 +10,8 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer has_one :public_key, serializer: ActivityPub::PublicKeySerializer + has_many :virtual_tags, key: :tag + attribute :moved_to, if: :moved? class EndpointsSerializer < ActiveModel::Serializer @@ -101,7 +103,14 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer object.locked end + def virtual_tags + object.emojis + end + def moved_to ActivityPub::TagManager.instance.uri_for(object.moved_to_account) end + + class CustomEmojiSerializer < ActivityPub::EmojiSerializer + end end diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index 19b746520..6097acda5 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -14,7 +14,7 @@ class REST::AccountSerializer < ActiveModel::Serializer end def note - Formatter.instance.simplified_format(object) + Formatter.instance.simplified_format(object, custom_emojify: true) end def url diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 7d8dc1369..cf8462821 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -22,6 +22,7 @@ class ActivityPub::ProcessAccountService < BaseService create_account if @account.nil? update_account + process_tags(@account) end end @@ -187,4 +188,31 @@ class ActivityPub::ProcessAccountService < BaseService def lock_options { redis: Redis.current, key: "process_account:#{@uri}" } end + + def process_tags(account) + return if @json['tag'].blank? + as_array(@json['tag']).each do |tag| + case tag['type'] + when 'Emoji' + process_emoji tag, account + end + end + end + + def process_emoji(tag, _account) + return if skip_download? + return if tag['name'].blank? || tag['icon'].blank? || tag['icon']['url'].blank? + + shortcode = tag['name'].delete(':') + image_url = tag['icon']['url'] + uri = tag['id'] + updated = tag['updated'] + emoji = CustomEmoji.find_by(shortcode: shortcode, domain: @account.domain) + + return unless emoji.nil? || emoji.updated_at >= updated + + emoji ||= CustomEmoji.new(domain: @account.domain, shortcode: shortcode, uri: uri) + emoji.image_remote_url = image_url + emoji.save + end end diff --git a/app/services/update_remote_profile_service.rb b/app/services/update_remote_profile_service.rb index 49a907682..aca1185de 100644 --- a/app/services/update_remote_profile_service.rb +++ b/app/services/update_remote_profile_service.rb @@ -40,6 +40,27 @@ class UpdateRemoteProfileService < BaseService account.header_remote_url = '' account.header.destroy end + + save_emojis(account) if remote_profile.emojis.present? + end + end + + def save_emojis(parent) + do_not_download = DomainBlock.find_by(domain: parent.account.domain)&.reject_media? + + return if do_not_download + + remote_account.emojis.each do |link| + next unless link['href'] && link['name'] + + shortcode = link['name'].delete(':') + emoji = CustomEmoji.find_by(shortcode: shortcode, domain: parent.account.domain) + + next unless emoji.nil? + + emoji = CustomEmoji.new(shortcode: shortcode, domain: parent.account.domain) + emoji.image_remote_url = link['href'] + emoji.save end end end diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index b3c91b869..b78998e9e 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -21,7 +21,7 @@ = t 'accounts.roles.moderator' .bio - .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account) + .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account, custom_emojify: true) .details-counters .counter{ class: active_nav_class(short_account_url(account)) } diff --git a/spec/lib/formatter_spec.rb b/spec/lib/formatter_spec.rb index 67fbfe92d..6e849f379 100644 --- a/spec/lib/formatter_spec.rb +++ b/spec/lib/formatter_spec.rb @@ -394,6 +394,45 @@ RSpec.describe Formatter do end end + context 'with custom_emojify option' do + let!(:emoji) { Fabricate(:custom_emoji) } + + before { account.note = text } + subject { Formatter.instance.simplified_format(account, custom_emojify: true) } + + context 'with emoji at the start' do + let(:text) { ':coolcat: Beep boop' } + + it 'converts shortcode to image tag' do + is_expected.to match(/

:coolcat:alert("Hello")' end + + context 'with custom_emojify option' do + let!(:emoji) { Fabricate(:custom_emoji, domain: remote_account.domain) } + + before { remote_account.note = text } + + subject { Formatter.instance.simplified_format(remote_account, custom_emojify: true) } + + context 'with emoji at the start' do + let(:text) { '

:coolcat: Beep boop
' } + + it 'converts shortcode to image tag' do + is_expected.to match(/

:coolcat:Beep :coolcat: boop

' } + + it 'converts shortcode to image tag' do + is_expected.to match(/Beep :coolcat::coolcat::coolcat:

' } + + it 'does not touch the shortcodes' do + is_expected.to match(/

:coolcat::coolcat:<\/p>/) + end + end + + context 'with emoji at the end' do + let(:text) { '

Beep boop
:coolcat:

' } + + it 'converts shortcode to image tag' do + is_expected.to match(/
:coolcat: Date: Mon, 2 Apr 2018 02:09:50 +0200 Subject: [PATCH 025/442] Fix unpermitted parameters warning when generating pagination URLs (#6995) --- app/controllers/api/v1/accounts/follower_accounts_controller.rb | 2 +- .../api/v1/accounts/following_accounts_controller.rb | 2 +- app/controllers/api/v1/accounts/statuses_controller.rb | 2 +- app/controllers/api/v1/blocks_controller.rb | 2 +- app/controllers/api/v1/domain_blocks_controller.rb | 2 +- app/controllers/api/v1/favourites_controller.rb | 2 +- app/controllers/api/v1/follow_requests_controller.rb | 2 +- app/controllers/api/v1/lists/accounts_controller.rb | 2 +- app/controllers/api/v1/mutes_controller.rb | 2 +- app/controllers/api/v1/notifications_controller.rb | 2 +- .../api/v1/statuses/favourited_by_accounts_controller.rb | 2 +- .../api/v1/statuses/reblogged_by_accounts_controller.rb | 2 +- app/controllers/api/v1/statuses_controller.rb | 2 +- app/controllers/api/v1/timelines/home_controller.rb | 2 +- app/controllers/api/v1/timelines/list_controller.rb | 2 +- app/controllers/api/v1/timelines/public_controller.rb | 2 +- app/controllers/api/v1/timelines/tag_controller.rb | 2 +- 17 files changed, 17 insertions(+), 17 deletions(-) diff --git a/app/controllers/api/v1/accounts/follower_accounts_controller.rb b/app/controllers/api/v1/accounts/follower_accounts_controller.rb index 80b0bef40..c4f600c54 100644 --- a/app/controllers/api/v1/accounts/follower_accounts_controller.rb +++ b/app/controllers/api/v1/accounts/follower_accounts_controller.rb @@ -63,6 +63,6 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end end diff --git a/app/controllers/api/v1/accounts/following_accounts_controller.rb b/app/controllers/api/v1/accounts/following_accounts_controller.rb index 55cffdf37..90b1f7fc5 100644 --- a/app/controllers/api/v1/accounts/following_accounts_controller.rb +++ b/app/controllers/api/v1/accounts/following_accounts_controller.rb @@ -63,6 +63,6 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end end diff --git a/app/controllers/api/v1/accounts/statuses_controller.rb b/app/controllers/api/v1/accounts/statuses_controller.rb index 1e1511a7b..cbcc7ef04 100644 --- a/app/controllers/api/v1/accounts/statuses_controller.rb +++ b/app/controllers/api/v1/accounts/statuses_controller.rb @@ -69,7 +69,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit, :only_media, :exclude_replies).merge(core_params) + params.slice(:limit, :only_media, :exclude_replies).permit(:limit, :only_media, :exclude_replies).merge(core_params) end def insert_pagination_headers diff --git a/app/controllers/api/v1/blocks_controller.rb b/app/controllers/api/v1/blocks_controller.rb index 3a6690766..a39701340 100644 --- a/app/controllers/api/v1/blocks_controller.rb +++ b/app/controllers/api/v1/blocks_controller.rb @@ -57,6 +57,6 @@ class Api::V1::BlocksController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end end diff --git a/app/controllers/api/v1/domain_blocks_controller.rb b/app/controllers/api/v1/domain_blocks_controller.rb index e93dc603b..ae6ad7936 100644 --- a/app/controllers/api/v1/domain_blocks_controller.rb +++ b/app/controllers/api/v1/domain_blocks_controller.rb @@ -67,7 +67,7 @@ class Api::V1::DomainBlocksController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end def domain_block_params diff --git a/app/controllers/api/v1/favourites_controller.rb b/app/controllers/api/v1/favourites_controller.rb index 9d73bb337..b4265ed34 100644 --- a/app/controllers/api/v1/favourites_controller.rb +++ b/app/controllers/api/v1/favourites_controller.rb @@ -66,6 +66,6 @@ class Api::V1::FavouritesController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end end diff --git a/app/controllers/api/v1/follow_requests_controller.rb b/app/controllers/api/v1/follow_requests_controller.rb index b9f50d784..d5c7c565a 100644 --- a/app/controllers/api/v1/follow_requests_controller.rb +++ b/app/controllers/api/v1/follow_requests_controller.rb @@ -71,6 +71,6 @@ class Api::V1::FollowRequestsController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end end diff --git a/app/controllers/api/v1/lists/accounts_controller.rb b/app/controllers/api/v1/lists/accounts_controller.rb index c29c73b3e..f2bded851 100644 --- a/app/controllers/api/v1/lists/accounts_controller.rb +++ b/app/controllers/api/v1/lists/accounts_controller.rb @@ -88,7 +88,7 @@ class Api::V1::Lists::AccountsController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end def unlimited? diff --git a/app/controllers/api/v1/mutes_controller.rb b/app/controllers/api/v1/mutes_controller.rb index 0c43cb943..c457408ba 100644 --- a/app/controllers/api/v1/mutes_controller.rb +++ b/app/controllers/api/v1/mutes_controller.rb @@ -59,6 +59,6 @@ class Api::V1::MutesController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end end diff --git a/app/controllers/api/v1/notifications_controller.rb b/app/controllers/api/v1/notifications_controller.rb index 8910b77e9..ebbe0b292 100644 --- a/app/controllers/api/v1/notifications_controller.rb +++ b/app/controllers/api/v1/notifications_controller.rb @@ -82,6 +82,6 @@ class Api::V1::NotificationsController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit, exclude_types: []).merge(core_params) + params.slice(:limit, :exclude_types).permit(:limit, exclude_types: []).merge(core_params) end end diff --git a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb index f95cf9457..3fe304153 100644 --- a/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb +++ b/app/controllers/api/v1/statuses/favourited_by_accounts_controller.rb @@ -77,6 +77,6 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end end diff --git a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb index 175217e6e..b065db2c7 100644 --- a/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb +++ b/app/controllers/api/v1/statuses/reblogged_by_accounts_controller.rb @@ -74,6 +74,6 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end end diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index 544a4ce21..28c28592a 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -76,7 +76,7 @@ class Api::V1::StatusesController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end def authorize_if_got_token diff --git a/app/controllers/api/v1/timelines/home_controller.rb b/app/controllers/api/v1/timelines/home_controller.rb index bbbcf7f90..cde4e8420 100644 --- a/app/controllers/api/v1/timelines/home_controller.rb +++ b/app/controllers/api/v1/timelines/home_controller.rb @@ -43,7 +43,7 @@ class Api::V1::Timelines::HomeController < Api::BaseController end def pagination_params(core_params) - params.permit(:local, :limit).merge(core_params) + params.slice(:local, :limit).permit(:local, :limit).merge(core_params) end def next_path diff --git a/app/controllers/api/v1/timelines/list_controller.rb b/app/controllers/api/v1/timelines/list_controller.rb index f5db71e46..06d596c08 100644 --- a/app/controllers/api/v1/timelines/list_controller.rb +++ b/app/controllers/api/v1/timelines/list_controller.rb @@ -45,7 +45,7 @@ class Api::V1::Timelines::ListController < Api::BaseController end def pagination_params(core_params) - params.permit(:limit).merge(core_params) + params.slice(:limit).permit(:limit).merge(core_params) end def next_path diff --git a/app/controllers/api/v1/timelines/public_controller.rb b/app/controllers/api/v1/timelines/public_controller.rb index d7d70b94d..13fe015b7 100644 --- a/app/controllers/api/v1/timelines/public_controller.rb +++ b/app/controllers/api/v1/timelines/public_controller.rb @@ -45,7 +45,7 @@ class Api::V1::Timelines::PublicController < Api::BaseController end def pagination_params(core_params) - params.permit(:local, :limit, :only_media).merge(core_params) + params.slice(:local, :limit, :only_media).permit(:local, :limit, :only_media).merge(core_params) end def next_path diff --git a/app/controllers/api/v1/timelines/tag_controller.rb b/app/controllers/api/v1/timelines/tag_controller.rb index eb32611ad..7de49a5ed 100644 --- a/app/controllers/api/v1/timelines/tag_controller.rb +++ b/app/controllers/api/v1/timelines/tag_controller.rb @@ -54,7 +54,7 @@ class Api::V1::Timelines::TagController < Api::BaseController end def pagination_params(core_params) - params.permit(:local, :limit, :only_media).merge(core_params) + params.slice(:local, :limit, :only_media).permit(:local, :limit, :only_media).merge(core_params) end def next_path From f890d2a766ae4c7fd8611dd4f3a15a13408f68c3 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 2 Apr 2018 02:10:53 +0200 Subject: [PATCH 026/442] Support all ActivityPub actor types (#6997) Fix #6973 --- app/services/activitypub/fetch_remote_account_service.rb | 4 +++- app/services/activitypub/fetch_remote_key_service.rb | 4 ++-- app/services/fetch_atom_service.rb | 2 +- app/services/resolve_account_service.rb | 2 +- app/services/resolve_url_service.rb | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/services/activitypub/fetch_remote_account_service.rb b/app/services/activitypub/fetch_remote_account_service.rb index d6ba625a9..5024853ca 100644 --- a/app/services/activitypub/fetch_remote_account_service.rb +++ b/app/services/activitypub/fetch_remote_account_service.rb @@ -3,6 +3,8 @@ class ActivityPub::FetchRemoteAccountService < BaseService include JsonLdHelper + SUPPORTED_TYPES = %w(Application Group Organization Person Service).freeze + # Should be called when uri has already been checked for locality # Does a WebFinger roundtrip on each call def call(uri, id: true, prefetched_body: nil) @@ -54,6 +56,6 @@ class ActivityPub::FetchRemoteAccountService < BaseService end def expected_type? - @json['type'] == 'Person' + SUPPORTED_TYPES.include?(@json['type']) end end diff --git a/app/services/activitypub/fetch_remote_key_service.rb b/app/services/activitypub/fetch_remote_key_service.rb index ce1048fee..41837d462 100644 --- a/app/services/activitypub/fetch_remote_key_service.rb +++ b/app/services/activitypub/fetch_remote_key_service.rb @@ -43,7 +43,7 @@ class ActivityPub::FetchRemoteKeyService < BaseService end def person? - @json['type'] == 'Person' + ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(@json['type']) end def public_key? @@ -55,6 +55,6 @@ class ActivityPub::FetchRemoteKeyService < BaseService end def confirmed_owner? - @owner['type'] == 'Person' && value_or_id(@owner['publicKey']) == @json['id'] + ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(@owner['type']) && value_or_id(@owner['publicKey']) == @json['id'] end end diff --git a/app/services/fetch_atom_service.rb b/app/services/fetch_atom_service.rb index 87076cc07..0444baf74 100644 --- a/app/services/fetch_atom_service.rb +++ b/app/services/fetch_atom_service.rb @@ -42,7 +42,7 @@ class FetchAtomService < BaseService elsif ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(response.mime_type) body = response.body_with_limit json = body_to_json(body) - if supported_context?(json) && json['type'] == 'Person' && json['inbox'].present? + if supported_context?(json) && ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(json['type']) && json['inbox'].present? [json['id'], { prefetched_body: body, id: true }, :activitypub] elsif supported_context?(json) && expected_type?(json) [json['id'], { prefetched_body: body, id: true }, :activitypub] diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb index 744ea24f4..8cba88f01 100644 --- a/app/services/resolve_account_service.rb +++ b/app/services/resolve_account_service.rb @@ -189,7 +189,7 @@ class ResolveAccountService < BaseService return @actor_json if defined?(@actor_json) json = fetch_resource(actor_url, false) - @actor_json = supported_context?(json) && json['type'] == 'Person' ? json : nil + @actor_json = supported_context?(json) && ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(json['type']) ? json : nil end def atom diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb index 9499dc286..c19b568cb 100644 --- a/app/services/resolve_url_service.rb +++ b/app/services/resolve_url_service.rb @@ -17,7 +17,7 @@ class ResolveURLService < BaseService def process_url case type - when 'Person' + when 'Application', 'Group', 'Organization', 'Person', 'Service' FetchRemoteAccountService.new.call(atom_url, body, protocol) when 'Note', 'Article', 'Image', 'Video' FetchRemoteStatusService.new.call(atom_url, body, protocol) From 24611d8deb8eb31e3af1aa2035aab29fbd4510f9 Mon Sep 17 00:00:00 2001 From: luzi82 Date: Mon, 2 Apr 2018 18:11:37 +0800 Subject: [PATCH 027/442] i18n: update zh-HK translation (#7004) * i18n: update zh-HK translation * i18n: update zh-HK translation * i18n-tasks normalize --- app/javascript/mastodon/locales/zh-HK.json | 172 ++++++++++----------- config/locales/simple_form.zh-HK.yml | 2 + config/locales/zh-HK.yml | 31 +++- 3 files changed, 118 insertions(+), 87 deletions(-) diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 0504a8c7a..1cbc9f1c5 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -1,36 +1,36 @@ { "account.block": "封鎖 @{name}", "account.block_domain": "隱藏來自 {domain} 的一切文章", - "account.blocked": "Blocked", - "account.direct": "Direct Message @{name}", + "account.blocked": "封鎖", + "account.direct": "私訊 @{name}", "account.disclaimer_full": "下列資料不一定完整。", - "account.domain_blocked": "Domain hidden", + "account.domain_blocked": "服務站被隱藏", "account.edit_profile": "修改個人資料", "account.follow": "關注", "account.followers": "關注的人", "account.follows": "正關注", "account.follows_you": "關注你", - "account.hide_reblogs": "Hide boosts from @{name}", + "account.hide_reblogs": "隱藏 @{name} 的轉推", "account.media": "媒體", "account.mention": "提及 @{name}", - "account.moved_to": "{name} has moved to:", + "account.moved_to": "{name} 已經遷移到:", "account.mute": "將 @{name} 靜音", - "account.mute_notifications": "Mute notifications from @{name}", - "account.muted": "Muted", + "account.mute_notifications": "將來自 @{name} 的通知靜音", + "account.muted": "靜音", "account.posts": "文章", - "account.posts_with_replies": "Toots with replies", + "account.posts_with_replies": "包含回覆的文章", "account.report": "舉報 @{name}", "account.requested": "等候審批", "account.share": "分享 @{name} 的個人資料", - "account.show_reblogs": "Show boosts from @{name}", + "account.show_reblogs": "顯示 @{name} 的推文", "account.unblock": "解除對 @{name} 的封鎖", "account.unblock_domain": "不再隱藏 {domain}", "account.unfollow": "取消關注", "account.unmute": "取消 @{name} 的靜音", - "account.unmute_notifications": "Unmute notifications from @{name}", + "account.unmute_notifications": "取消來自 @{name} 通知的靜音", "account.view_full_profile": "查看完整資料", - "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.message": "發生不可預期的錯誤。", + "alert.unexpected.title": "噢!", "boost_modal.combo": "如你想在下次路過這顯示,請按{combo},", "bundle_column_error.body": "加載本組件出錯。", "bundle_column_error.retry": "重試", @@ -40,11 +40,11 @@ "bundle_modal_error.retry": "重試", "column.blocks": "封鎖用戶", "column.community": "本站時間軸", - "column.domain_blocks": "Hidden domains", + "column.domain_blocks": "隱藏的服務站", "column.favourites": "最愛的文章", "column.follow_requests": "關注請求", "column.home": "主頁", - "column.lists": "Lists", + "column.lists": "列表", "column.mutes": "靜音名單", "column.notifications": "通知", "column.pins": "置頂文章", @@ -58,25 +58,25 @@ "column_header.unpin": "取下", "column_subheading.navigation": "瀏覽", "column_subheading.settings": "設定", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", - "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "compose_form.direct_message_warning": "這文章只有被提及的用戶才可以看到。", + "compose_form.hashtag_warning": "這文章因為不是公開,所以不會被標籤搜索。只有公開的文章才會被標籤搜索。", "compose_form.lock_disclaimer": "你的用戶狀態為「{locked}」,任何人都能立即關注你,然後看到「只有關注者能看」的文章。", "compose_form.lock_disclaimer.lock": "公共", "compose_form.placeholder": "你在想甚麼?", "compose_form.publish": "發文", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.marked": "Media is 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.unmarked": "Text is not hidden", + "compose_form.sensitive.marked": "媒體被標示為敏感", + "compose_form.sensitive.unmarked": "媒體沒有被標示為敏感", + "compose_form.spoiler.marked": "文字被警告隱藏", + "compose_form.spoiler.unmarked": "文字沒有被隱藏", "compose_form.spoiler_placeholder": "敏感警告訊息", "confirmation_modal.cancel": "取消", "confirmations.block.confirm": "封鎖", "confirmations.block.message": "你確定要封鎖{name}嗎?", "confirmations.delete.confirm": "刪除", "confirmations.delete.message": "你確定要刪除{name}嗎?", - "confirmations.delete_list.confirm": "Delete", - "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.delete_list.confirm": "刪除", + "confirmations.delete_list.message": "你確定要永久刪除這列表嗎?", "confirmations.domain_block.confirm": "隱藏整個網站", "confirmations.domain_block.message": "你真的真的確定要隱藏整個 {domain} ?多數情況下,比較推薦封鎖或靜音幾個特定目標就好。", "confirmations.mute.confirm": "靜音", @@ -86,7 +86,7 @@ "embed.instructions": "要內嵌此文章,請將以下代碼貼進你的網站。", "embed.preview": "看上去會是這樣:", "emoji_button.activity": "活動", - "emoji_button.custom": "Custom", + "emoji_button.custom": "自訂", "emoji_button.flags": "旗幟", "emoji_button.food": "飲飲食食", "emoji_button.label": "加入表情符號", @@ -94,9 +94,9 @@ "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻", "emoji_button.objects": "物品", "emoji_button.people": "人物", - "emoji_button.recent": "Frequently used", + "emoji_button.recent": "常用", "emoji_button.search": "搜尋…", - "emoji_button.search_results": "Search results", + "emoji_button.search_results": "搜尋結果", "emoji_button.symbols": "符號", "emoji_button.travel": "旅遊景物", "empty_column.community": "本站時間軸暫時未有內容,快文章來搶頭香啊!", @@ -119,48 +119,48 @@ "home.column_settings.show_reblogs": "顯示被轉推的文章", "home.column_settings.show_replies": "顯示回應文章", "home.settings": "欄位設定", - "keyboard_shortcuts.back": "to navigate back", - "keyboard_shortcuts.boost": "to boost", - "keyboard_shortcuts.column": "to focus a status in one of the columns", - "keyboard_shortcuts.compose": "to focus the compose textarea", - "keyboard_shortcuts.description": "Description", - "keyboard_shortcuts.down": "to move down in the list", - "keyboard_shortcuts.enter": "to open status", - "keyboard_shortcuts.favourite": "to favourite", - "keyboard_shortcuts.heading": "Keyboard Shortcuts", - "keyboard_shortcuts.hotkey": "Hotkey", - "keyboard_shortcuts.legend": "to display this legend", - "keyboard_shortcuts.mention": "to mention author", - "keyboard_shortcuts.reply": "to reply", - "keyboard_shortcuts.search": "to focus search", - "keyboard_shortcuts.toot": "to start a brand new toot", - "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", - "keyboard_shortcuts.up": "to move up in the list", + "keyboard_shortcuts.back": "後退", + "keyboard_shortcuts.boost": "轉推", + "keyboard_shortcuts.column": "把標示移動到其中一列", + "keyboard_shortcuts.compose": "把標示移動到文字輸入區", + "keyboard_shortcuts.description": "描述", + "keyboard_shortcuts.down": "在列表往下移動", + "keyboard_shortcuts.enter": "打開文章", + "keyboard_shortcuts.favourite": "收藏", + "keyboard_shortcuts.heading": "鍵盤快速鍵", + "keyboard_shortcuts.hotkey": "快速鍵", + "keyboard_shortcuts.legend": "顯示這個說明", + "keyboard_shortcuts.mention": "提及作者", + "keyboard_shortcuts.reply": "回覆", + "keyboard_shortcuts.search": "把標示移動到搜索", + "keyboard_shortcuts.toot": "新的推文", + "keyboard_shortcuts.unfocus": "把標示移離文字輸入和搜索", + "keyboard_shortcuts.up": "在列表往上移動", "lightbox.close": "關閉", "lightbox.next": "繼續", "lightbox.previous": "回退", - "lists.account.add": "Add to list", - "lists.account.remove": "Remove from list", - "lists.delete": "Delete list", - "lists.edit": "Edit list", - "lists.new.create": "Add list", - "lists.new.title_placeholder": "New list title", - "lists.search": "Search among people you follow", - "lists.subheading": "Your lists", + "lists.account.add": "新增到列表", + "lists.account.remove": "從列表刪除", + "lists.delete": "刪除列表", + "lists.edit": "編輯列表", + "lists.new.create": "新增列表", + "lists.new.title_placeholder": "新列表標題", + "lists.search": "從你關注的用戶中搜索", + "lists.subheading": "列表", "loading_indicator.label": "載入中...", "media_gallery.toggle_visible": "打開或關上", "missing_indicator.label": "找不到內容", - "missing_indicator.sublabel": "This resource could not be found", - "mute_modal.hide_notifications": "Hide notifications from this user?", + "missing_indicator.sublabel": "無法找到內容", + "mute_modal.hide_notifications": "隱藏來自這用戶的通知嗎?", "navigation_bar.blocks": "被你封鎖的用戶", "navigation_bar.community_timeline": "本站時間軸", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.domain_blocks": "隱藏的服務站", "navigation_bar.edit_profile": "修改個人資料", "navigation_bar.favourites": "最愛的內容", "navigation_bar.follow_requests": "關注請求", "navigation_bar.info": "關於本服務站", - "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts", - "navigation_bar.lists": "Lists", + "navigation_bar.keyboard_shortcuts": "鍵盤快速鍵", + "navigation_bar.lists": "列表", "navigation_bar.logout": "登出", "navigation_bar.mutes": "被你靜音的用戶", "navigation_bar.pins": "置頂文章", @@ -187,8 +187,8 @@ "onboarding.page_four.home": "「主頁」顯示你所關注用戶的文章", "onboarding.page_four.notifications": "「通知」欄顯示你和其他人的互動。", "onboarding.page_one.federation": "Mastodon(萬象社交)是由一批獨立網站組成的龐大網絡,我們將這些獨立又互連網站稱為「服務站」(instance)", - "onboarding.page_one.full_handle": "Your full handle", - "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.", + "onboarding.page_one.full_handle": "你的帳號全名", + "onboarding.page_one.handle_hint": "朋友可以從這個帳號全名找到你", "onboarding.page_one.welcome": "歡迎使用 Mastodon(萬象社交)", "onboarding.page_six.admin": "你服務站的管理員是{admin}", "onboarding.page_six.almost_done": "差不多了……", @@ -211,33 +211,33 @@ "privacy.public.short": "公共", "privacy.unlisted.long": "公開,但不在公共時間軸顯示", "privacy.unlisted.short": "公開", - "regeneration_indicator.label": "Loading…", - "regeneration_indicator.sublabel": "Your home feed is being prepared!", - "relative_time.days": "{number}d", - "relative_time.hours": "{number}h", - "relative_time.just_now": "now", - "relative_time.minutes": "{number}m", - "relative_time.seconds": "{number}s", + "regeneration_indicator.label": "載入中……", + "regeneration_indicator.sublabel": "你的主頁時間軸正在準備中!", + "relative_time.days": "{number}日", + "relative_time.hours": "{number}小時", + "relative_time.just_now": "剛剛", + "relative_time.minutes": "{number}分鐘", + "relative_time.seconds": "{number}秒", "reply_indicator.cancel": "取消", - "report.forward": "Forward to {target}", - "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?", - "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:", + "report.forward": "轉寄到 {target}", + "report.forward_hint": "這個帳戶屬於其他服務站。要向該服務站發送匿名的舉報訊息嗎?", + "report.hint": "這訊息會發送到你服務站的管理員。你可以提供舉報這個帳戶的理由:", "report.placeholder": "額外訊息", "report.submit": "提交", "report.target": "舉報", "search.placeholder": "搜尋", - "search_popout.search_format": "Advanced search format", - "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", - "search_popout.tips.hashtag": "hashtag", - "search_popout.tips.status": "status", - "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", - "search_popout.tips.user": "user", - "search_results.accounts": "People", - "search_results.hashtags": "Hashtags", - "search_results.statuses": "Toots", + "search_popout.search_format": "高級搜索格式", + "search_popout.tips.full_text": "輸入簡單的文字,搜索由你發放、收藏、轉推和提及你的文章,以及符合的用戶名稱,帳號名稱和標籤。", + "search_popout.tips.hashtag": "標籤", + "search_popout.tips.status": "文章", + "search_popout.tips.text": "輸入簡單的文字,搜索符合的用戶名稱,帳號名稱和標籤。", + "search_popout.tips.user": "用戶", + "search_results.accounts": "使用者", + "search_results.hashtags": "標籤", + "search_results.statuses": "文章", "search_results.total": "{count, number} 項結果", "standalone.public_title": "站點一瞥…", - "status.block": "Block @{name}", + "status.block": "封鎖 @{name}", "status.cannot_reblog": "這篇文章無法被轉推", "status.delete": "刪除", "status.embed": "鑲嵌", @@ -245,12 +245,12 @@ "status.load_more": "載入更多", "status.media_hidden": "隱藏媒體內容", "status.mention": "提及 @{name}", - "status.more": "More", - "status.mute": "Mute @{name}", + "status.more": "更多", + "status.mute": "把 @{name} 靜音", "status.mute_conversation": "靜音對話", "status.open": "展開文章", "status.pin": "置頂到資料頁", - "status.pinned": "Pinned toot", + "status.pinned": "置頂文章", "status.reblog": "轉推", "status.reblogged_by": "{name} 轉推", "status.reply": "回應", @@ -258,22 +258,22 @@ "status.report": "舉報 @{name}", "status.sensitive_toggle": "點擊顯示", "status.sensitive_warning": "敏感內容", - "status.share": "Share", + "status.share": "分享", "status.show_less": "減少顯示", - "status.show_less_all": "Show less for all", + "status.show_less_all": "減少顯示這類文章", "status.show_more": "顯示更多", - "status.show_more_all": "Show more for all", + "status.show_more_all": "顯示更多這類文章", "status.unmute_conversation": "解禁對話", "status.unpin": "解除置頂", "tabs_bar.federated_timeline": "跨站", "tabs_bar.home": "主頁", "tabs_bar.local_timeline": "本站", "tabs_bar.notifications": "通知", - "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", + "ui.beforeunload": "如果你現在離開 Mastodon,你的草稿內容將會被丟棄。", "upload_area.title": "將檔案拖放至此上載", "upload_button.label": "上載媒體檔案", - "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Crop", + "upload_form.description": "為視覺障礙人士添加文字說明", + "upload_form.focus": "裁切", "upload_form.undo": "還原", "upload_progress.label": "上載中……", "video.close": "關閉影片", diff --git a/config/locales/simple_form.zh-HK.yml b/config/locales/simple_form.zh-HK.yml index 01ba61fdf..da0292a90 100644 --- a/config/locales/simple_form.zh-HK.yml +++ b/config/locales/simple_form.zh-HK.yml @@ -41,6 +41,7 @@ zh-HK: setting_default_privacy: 文章預設為 setting_default_sensitive: 預設我的內容為敏感內容 setting_delete_modal: 刪推前詢問我 + setting_display_sensitive_media: 預設我的媒體為敏感內容 setting_noindex: 阻止搜尋引擎檢索 setting_reduce_motion: 減低動畫效果 setting_system_font_ui: 使用系統預設字型 @@ -49,6 +50,7 @@ zh-HK: severity: 等級 type: 匯入資料類型 username: 用戶名稱 + username_or_email: 用戶名稱或電郵 interactions: must_be_follower: 隱藏沒有關注你的用戶的通知 must_be_following: 隱藏你不關注的用戶的通知 diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index cc1cade1e..5c1feabfc 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -101,7 +101,7 @@ zh-HK: most_recent: 按時間 title: 排序 outbox_url: 寄件箱(Outbox)URL - perform_full_suspension: 實行完全暫停 + perform_full_suspension: 完全停權 profile_url: 個人檔案 URL promote: 升任 protocol: 協議 @@ -272,6 +272,9 @@ zh-HK: contact_information: email: 輸入一個公開的電郵地址 username: 輸入用戶名稱 + hero: + desc_html: 在首頁顯示。推薦最小 600x100px。如果留空,就會默認為服務站縮圖。 + title: 主題圖片 peers_api_enabled: desc_html: 現時本服務站在網絡中已發現的域名 title: 公開已知服務站的列表 @@ -288,6 +291,9 @@ zh-HK: open: desc_html: 允許所有人建立帳戶 title: 開放註冊 + show_known_fediverse_at_about_page: + desc_html: 如果開啟,就會在時間軸預覽顯示跨站文章,否則就只會顯示本站文章。 + title: 在時間軸預覽顯示跨站文章 show_staff_badge: desc_html: 在個人資料頁上顯示管理人員標誌 title: 顯示管理人員標誌 @@ -352,6 +358,8 @@ zh-HK: your_token: token auth: agreement_html: 登記即表示你同意遵守本服務站的規則使用條款。 + change_password: 密碼 + confirm_email: 確認電郵 delete_account: 刪除帳戶 delete_account_html: 如果你想刪除你的帳戶,請點擊這裡繼續。你需要確認你的操作。 didnt_get_confirmation: 沒有收到確認指示電郵? @@ -361,7 +369,13 @@ zh-HK: logout: 登出 migrate_account: 轉移到另一個帳號 migrate_account_html: 想要將這個帳號指向另一個帳號可到這裡設定。 + or: 或 + or_log_in_with: 或登入於 + providers: + cas: CAS + saml: SAML register: 登記 + register_elsewhere: 在其他服務站登記 resend_confirmation: 重發確認指示電郵 reset_password: 重設密碼 security: 登入資訊 @@ -411,6 +425,13 @@ zh-HK: title: 這個頁面有問題 noscript_html: 使用 Mastodon 網頁版應用需要啟用 JavaScript。你也可以選擇適用於你的平台的 Mastodon 應用。 exports: + archive_takeout: + date: 日期 + download: 下載檔案 + hint_html: 你可以下載包含你的文章和媒體的檔案。資料以 ActivityPub 格式儲存,可用於相容的軟體。 + in_progress: 檔案製作中... + request: 下載檔案 + size: 檔案大小 blocks: 被你封鎖的用戶 csv: CSV follows: 你所關注的用戶 @@ -524,7 +545,9 @@ zh-HK: trillion: T unit: '' pagination: + newer: 較新 next: 下一頁 + older: 較舊 prev: 上一頁 truncate: "……" preferences: @@ -606,6 +629,11 @@ zh-HK: two_factor_authentication: 雙重認證 your_apps: 你的應用程式 statuses: + attached: + description: 附件: %{attached} + image: "%{count} 張圖片" + video: "%{count} 段影片" + content_warning: 'Content warning: %{warning}' open_in_web: 開啟網頁 over_character_limit: 超過了 %{max} 字的限制 pin_errors: @@ -670,4 +698,5 @@ zh-HK: users: invalid_email: 電郵地址格式不正確 invalid_otp_token: 雙重認證確認碼不正確 + seamless_external_login: 由於你是從外部系統登入,所以不能設定密碼和電郵。 signed_in_as: 目前登入的帳戶: From b04f73ce664b1f2c8f863b53ba8ff8fa0f0b330f Mon Sep 17 00:00:00 2001 From: Evgeny Petrov Date: Mon, 2 Apr 2018 13:49:06 +0300 Subject: [PATCH 028/442] Russian language updated (#7005) * Russian language updated * Small fixes for RU language * bundle exec i18n-tasks normalize --- app/javascript/mastodon/locales/ru.json | 30 +++++++------- config/locales/doorkeeper.ru.yml | 2 +- config/locales/ru.yml | 54 ++++++++++++++++++++++--- config/locales/simple_form.ru.yml | 4 +- 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 15959092c..8616ef98f 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -1,10 +1,10 @@ { "account.block": "Блокировать", "account.block_domain": "Блокировать все с {domain}", - "account.blocked": "Blocked", - "account.direct": "Direct Message @{name}", + "account.blocked": "Заблокирован(а)", + "account.direct": "Написать @{name}", "account.disclaimer_full": "Нижеуказанная информация может не полностью отражать профиль пользователя.", - "account.domain_blocked": "Domain hidden", + "account.domain_blocked": "Домен скрыт", "account.edit_profile": "Изменить профиль", "account.follow": "Подписаться", "account.followers": "Подписаны", @@ -16,9 +16,9 @@ "account.moved_to": "Ищите {name} здесь:", "account.mute": "Заглушить", "account.mute_notifications": "Скрыть уведомления от @{name}", - "account.muted": "Muted", + "account.muted": "Приглушён", "account.posts": "Посты", - "account.posts_with_replies": "Toots with replies", + "account.posts_with_replies": "Посты с ответами", "account.report": "Пожаловаться", "account.requested": "Ожидает подтверждения", "account.share": "Поделиться профилем @{name}", @@ -29,8 +29,8 @@ "account.unmute": "Снять глушение", "account.unmute_notifications": "Показывать уведомления от @{name}", "account.view_full_profile": "Показать полный профиль", - "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.message": "Что-то пошло не так.", + "alert.unexpected.title": "Ой!", "boost_modal.combo": "Нажмите {combo}, чтобы пропустить это в следующий раз", "bundle_column_error.body": "Что-то пошло не так при загрузке этого компонента.", "bundle_column_error.retry": "Попробовать снова", @@ -40,7 +40,7 @@ "bundle_modal_error.retry": "Попробовать снова", "column.blocks": "Список блокировки", "column.community": "Локальная лента", - "column.domain_blocks": "Hidden domains", + "column.domain_blocks": "Скрытые домены", "column.favourites": "Понравившееся", "column.follow_requests": "Запросы на подписку", "column.home": "Главная", @@ -65,10 +65,10 @@ "compose_form.placeholder": "О чем Вы думаете?", "compose_form.publish": "Трубить", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.marked": "Media is 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.unmarked": "Text is not hidden", + "compose_form.sensitive.marked": "Медиафайлы не отмечены как чувствительные", + "compose_form.sensitive.unmarked": "Медиафайлы не отмечены как чувствительные", + "compose_form.spoiler.marked": "Текст скрыт за предупреждением", + "compose_form.spoiler.unmarked": "Текст не скрыт", "compose_form.spoiler_placeholder": "Напишите свое предупреждение здесь", "confirmation_modal.cancel": "Отмена", "confirmations.block.confirm": "Заблокировать", @@ -154,7 +154,7 @@ "mute_modal.hide_notifications": "Убрать уведомления от этого пользователя?", "navigation_bar.blocks": "Список блокировки", "navigation_bar.community_timeline": "Локальная лента", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.domain_blocks": "Скрытые домены", "navigation_bar.edit_profile": "Изменить профиль", "navigation_bar.favourites": "Понравившееся", "navigation_bar.follow_requests": "Запросы на подписку", @@ -221,13 +221,13 @@ "reply_indicator.cancel": "Отмена", "report.forward": "Forward to {target}", "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?", - "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:", + "report.hint": "Жалоба будет отправлена модераторам Вашего сервера. Вы также можете указать подробную причину жалобы ниже:", "report.placeholder": "Комментарий", "report.submit": "Отправить", "report.target": "Жалуемся на", "search.placeholder": "Поиск", "search_popout.search_format": "Продвинутый формат поиска", - "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", + "search_popout.tips.full_text": "Возвращает посты, которые Вы написали, отметили как 'избранное', продвинули или в которых были упомянуты, а также содержащие юзернейм, имя и хэштеги.", "search_popout.tips.hashtag": "хэштег", "search_popout.tips.status": "статус", "search_popout.tips.text": "Простой ввод текста покажет совпадающие имена пользователей, отображаемые имена и хэштеги", diff --git a/config/locales/doorkeeper.ru.yml b/config/locales/doorkeeper.ru.yml index 05c3d971c..28c0ff0bd 100644 --- a/config/locales/doorkeeper.ru.yml +++ b/config/locales/doorkeeper.ru.yml @@ -39,7 +39,7 @@ ru: callback_url: Callback URL delete: Удалить name: Название - new: Новое Приложение + new: Новое приложение scopes: Права show: Показывать title: Ваши приложения diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 81af42556..108ca33e9 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -48,7 +48,7 @@ ru: reserved_username: Имя пользователя зарезервировано roles: admin: Администратор - moderator: Мод + moderator: Модератор unfollow: Отписаться admin: account_moderation_notes: @@ -217,7 +217,7 @@ ru: title: Снять блокировку с домена %{domain} undo: Отменить title: Доменные блокировки - undo: Отемнить + undo: Отменить email_domain_blocks: add_new: Добавить новую created_msg: Доменная блокировка еmail успешно создана @@ -275,6 +275,9 @@ ru: contact_information: email: Введите публичный e-mail username: Введите имя пользователя + hero: + desc_html: Отображается на главной странице. Рекомендуется разрешение не менее 600х100px. Если не установлено, используется изображение узла + title: Баннер узла peers_api_enabled: desc_html: Домены, которые были замечены этим узлом среди всей федерации title: Публикация списка обнаруженных узлов @@ -291,6 +294,9 @@ ru: open: desc_html: Позволяет любому создавать аккаунт title: Открыть регистрацию + show_known_fediverse_at_about_page: + desc_html: Если включено, показывает посты со всех известных узлов в предпросмотре ленты. В противном случае отображаются только локальные посты. + title: Показывать известные узлы в предпросмотре ленты show_staff_badge: desc_html: Показывать метку персонала на странице пользователя title: Показывать метку персонала @@ -308,7 +314,7 @@ ru: desc_html: Используется для предпросмотра с помощью OpenGraph и API. Рекомендуется разрешение 1200x630px title: Картинка узла timeline_preview: - desc_html: Показывать публичную ленту на целевой странице + desc_html: Показывать публичную ленту на приветственной странице title: Предпросмотр ленты title: Настройки сайта statuses: @@ -368,13 +374,17 @@ ru: migrate_account_html: Если Вы хотите перенести этот аккаунт на другой, вы можете сделать это здесь. or: или or_log_in_with: Или войти с помощью + providers: + cas: CAS + saml: SAML register: Зарегистрироваться - register_elsewhere: Зарегистрироваться на другом сервере + register_elsewhere: Зарегистрироваться на другом узле resend_confirmation: Повторить отправку инструкции для подтверждения reset_password: Сбросить пароль - security: Изменить пароль + security: Безопасность set_new_password: Задать новый пароль authorize_follow: + already_following: Вы уже подписаны на этот аккаунт error: К сожалению, при поиске удаленного аккаунта возникла ошибка follow: Подписаться follow_request: 'Вы отправили запрос на подписку:' @@ -467,10 +477,13 @@ ru: '21600': 6 часов '3600': 1 час '43200': 12 часов + '604800': 1 неделю '86400': 1 день expires_in_prompt: Никогда generate: Сгенерировать max_uses: + few: "%{count} исп." + many: "%{count} исп." one: 1 исп. other: "%{count} исп." max_uses_prompt: Без лимита @@ -514,11 +527,13 @@ ru: favourite: body: 'Ваш статус понравился %{name}:' subject: "%{name} понравился Ваш статус" + title: Понравившийся статус follow: body: "%{name} теперь подписан(а) на Вас!" subject: "%{name} теперь подписан(а) на Вас" title: Новый подписчик follow_request: + action: Управление запросами на подписку body: "%{name} запросил Вас о подписке" subject: "%{name} хочет подписаться на Вас" title: Новый запрос о подписке @@ -587,6 +602,7 @@ ru: micro_messenger: MicroMessenger nokia: Nokia S40 Ovi Browser opera: Opera + otter: Otter phantom_js: PhantomJS qq: QQ Browser safari: Safari @@ -628,6 +644,19 @@ ru: two_factor_authentication: Двухфакторная аутентификация your_apps: Ваши приложения statuses: + attached: + description: 'Вложение: %{attached}' + image: + few: "%{count} изображения" + many: "%{count} изображений" + one: "%{count} изображение" + other: "%{count} изображений" + video: + few: "%{count} видео" + many: "%{count} видео" + one: "%{count} видео" + other: "%{count} видео" + content_warning: 'Спойлер: %{warning}' open_in_web: Открыть в WWW over_character_limit: превышен лимит символов (%{max}) pin_errors: @@ -636,6 +665,7 @@ ru: private: Нельзя закрепить непубличный статус reblog: Нельзя закрепить продвинутый статус show_more: Подробнее + title: '%{name}: "%{quote}"' visibilities: private: Для подписчиков private_long: Показывать только подписчикам @@ -717,12 +747,24 @@ ru: title: Вынос архива welcome: edit_profile_action: Настроить профиль + edit_profile_step: Вы можете настроить свой профиль, загрузив аватар, обложку, сменив имя и много чего ещё. Если Вы хотите фильтровать подписчиков до того, как они смогут на Вас подписаться, Вы можете закрыть свой аккаунт. + explanation: Несколько советов для новичков final_action: Начать постить + final_step: 'Начните постить! Ваши публичные посты могут видеть другие, например, в локальной ленте или по хэштегам, даже если у Вас нет подписчиков. Вы также можете поздороваться с остальными и представиться, используя хэштек #приветствие.' + full_handle: Ваше обращение + full_handle_hint: То, что Вы хотите сообщить своим друзьям, чтобы они могли написать Вам или подписаться с другого узла. review_preferences_action: Изменить настройки - subject: Добро пожаловать на Mastodon + review_preferences_step: Проверьте все настройки, например, какие письма Вы хотите получать или уровень приватности статусов по умолчанию. Если Вы не страдаете морской болезнь, можете включить автовоспроизведение GIF. + subject: Добро пожаловать в Mastodon + tip_bridge_html: Если Вы пришли из Twitter, можете поискать своих друзей в Mastodon, используя приложение-мост. Но это работает только если они тоже использовали это приложение! + tip_federated_timeline: В глобальной ленте отображается сеть Mastodon. Но в ней показаны посты только от людей, на которых подписаны Вы и Ваши соседи, поэтому лента может быть неполной. + tip_following: По умолчанию Вы подписаны на администратора(-ов) Вашего узла. Чтобы найти других интересных людей, проверьте локальную и глобальную ленты. + tip_local_timeline: В локальной ленте показаны посты от людей с %{instance}. Это Ваши непосредственные соседи! + tip_mobile_webapp: Если Ваш мобильный браузер предлагает добавить иконку Mastodon на домашний экран, то Вы можете получать push-уведомления. Прямо как полноценное приложение! tips: Советы title: Добро пожаловать на борт, %{name}! users: invalid_email: Введенный e-mail неверен invalid_otp_token: Введен неверный код + seamless_external_login: Вы залогинены через сторонний сервис, поэтому настройки e-mail и пароля недоступны. signed_in_as: 'Выполнен вход под именем:' diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml index 5c4df2189..b8ee5892d 100644 --- a/config/locales/simple_form.ru.yml +++ b/config/locales/simple_form.ru.yml @@ -38,7 +38,7 @@ ru: filtered_languages: Фильтруемые языки header: Заголовок locale: Язык - locked: Сделать аккаунт приватным + locked: Сделать аккаунт закрытым max_uses: Макс. число использований new_password: Новый пароль note: О Вас @@ -49,6 +49,7 @@ ru: setting_default_privacy: Видимость постов setting_default_sensitive: Всегда отмечать медиаконтент как чувствительный setting_delete_modal: Показывать диалог подтверждения перед удалением + setting_display_sensitive_media: Всегда показывать медиаконтент, отмеченный как чувствительный setting_noindex: Отказаться от индексации в поисковых машинах setting_reduce_motion: Уменьшить движение в анимации setting_system_font_ui: Использовать шрифт системы по умолчанию @@ -57,6 +58,7 @@ ru: severity: Строгость type: Тип импорта username: Имя пользователя + username_or_email: Имя пользователя или e-mail interactions: must_be_follower: Заблокировать уведомления не от подписчиков must_be_following: Заблокировать уведомления от людей, на которых Вы не подписаны From 3f51c6efaac0a0705d363e021951b0dd6b071a28 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Mon, 2 Apr 2018 20:43:30 +0900 Subject: [PATCH 029/442] Weblate translations (2018-04-02) (#7007) * Translated using Weblate (Galician) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/ * Translated using Weblate (Dutch) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/ * Translated using Weblate (Catalan) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/ * Translated using Weblate (Arabic) Currently translated at 76.4% (449 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Japanese) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/ * Translated using Weblate (Slovak) Currently translated at 92.3% (542 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 92.3% (542 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 100.0% (58 of 58 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/sk/ * Translated using Weblate (Polish) Currently translated at 98.9% (581 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pl/ * Translated using Weblate (French) Currently translated at 99.6% (585 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/ * Translated using Weblate (Catalan) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/ * Translated using Weblate (Persian) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/fa/ * Translated using Weblate (Persian) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fa/ * 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 (Japanese) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/ * Translated using Weblate (Japanese) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/ * 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 (Chinese (Traditional)) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/zh_Hant/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Devise Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/zh_Hant/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/zh_Hant/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Devise Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/zh_Hant/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/eo/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/zh_Hant/ * Translated using Weblate (Arabic) Currently translated at 76.6% (450 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/eo/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/ * Translated using Weblate (Slovak) Currently translated at 92.6% (544 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/sk/ * Translated using Weblate (Arabic) Currently translated at 82.9% (487 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Arabic) Currently translated at 98.6% (74 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/ar/ * Translated using Weblate (Slovak) Currently translated at 93.6% (550 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 95.4% (560 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * 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 (Indonesian) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/id/ * Translated using Weblate (Korean) Currently translated at 100.0% (58 of 58 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ko/ * Translated using Weblate (Korean) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ko/ * Translated using Weblate (Korean) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ko/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/ * Translated using Weblate (Arabic) Currently translated at 82.9% (487 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Arabic) Currently translated at 99.2% (278 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/ * Translated using Weblate (Arabic) Currently translated at 87.3% (513 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Arabic) Currently translated at 99.6% (279 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/ * bundle exec i18n-tasks normalize && yarn manage:translations * revert --- app/javascript/mastodon/locales/ar.json | 6 +- app/javascript/mastodon/locales/ca.json | 2 +- app/javascript/mastodon/locales/eo.json | 18 ++--- app/javascript/mastodon/locales/fa.json | 10 +-- app/javascript/mastodon/locales/fr.json | 42 ++++++------ app/javascript/mastodon/locales/ja.json | 2 +- app/javascript/mastodon/locales/ko.json | 4 +- app/javascript/mastodon/locales/sk.json | 8 +-- app/javascript/mastodon/locales/zh-TW.json | 4 +- config/locales/ar.yml | 79 +++++++++++++++++++++- config/locales/devise.zh-TW.yml | 25 ++++++- config/locales/doorkeeper.ar.yml | 5 ++ config/locales/doorkeeper.fa.yml | 16 ++--- config/locales/doorkeeper.id.yml | 4 ++ config/locales/doorkeeper.sk.yml | 4 +- config/locales/doorkeeper.zh-TW.yml | 14 ++-- config/locales/eo.yml | 13 +++- config/locales/ja.yml | 4 +- config/locales/ko.yml | 16 +++++ config/locales/simple_form.ko.yml | 2 + config/locales/sk.yml | 24 +++++++ 21 files changed, 233 insertions(+), 69 deletions(-) diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index 3078b5b8c..34e34411f 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -2,7 +2,7 @@ "account.block": "حظر @{name}", "account.block_domain": "إخفاء كل شيئ قادم من إسم النطاق {domain}", "account.blocked": "محظور", - "account.direct": "Direct Message @{name}", + "account.direct": "Direct message @{name}", "account.disclaimer_full": "قد لا تعكس المعلومات أدناه الملف الشخصي الكامل للمستخدم.", "account.domain_blocked": "النطاق مخفي", "account.edit_profile": "تعديل الملف الشخصي", @@ -66,7 +66,7 @@ "compose_form.publish": "بوّق", "compose_form.publish_loud": "{publish}!", "compose_form.sensitive.marked": "لقد تم تحديد هذه الصورة كحساسة", - "compose_form.sensitive.unmarked": "Media is not marked as sensitive", + "compose_form.sensitive.unmarked": "لم يتم تحديد الصورة كحساسة", "compose_form.spoiler.marked": "إنّ النص مخفي وراء تحذير", "compose_form.spoiler.unmarked": "النص غير مخفي", "compose_form.spoiler_placeholder": "تنبيه عن المحتوى", @@ -221,7 +221,7 @@ "reply_indicator.cancel": "إلغاء", "report.forward": "التحويل إلى {target}", "report.forward_hint": "هذا الحساب ينتمي إلى خادوم آخَر. هل تودّ إرسال نسخة مجهولة مِن التقرير إلى هنالك أيضًا ؟", - "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:", + "report.hint": "سوف يتم إرسال التقرير إلى مُشرِفي مثيل خادومكم. بإمكانك الإدلاء بشرح عن سبب الإبلاغ عن الحساب أسفله :", "report.placeholder": "تعليقات إضافية", "report.submit": "إرسال", "report.target": "إبلاغ", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index ec5a8a1d6..b7f95a664 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -2,7 +2,7 @@ "account.block": "Bloca @{name}", "account.block_domain": "Amaga-ho tot de {domain}", "account.blocked": "Bloquejat", - "account.direct": "Direct Message @{name}", + "account.direct": "Direct message @{name}", "account.disclaimer_full": "La informació següent pot reflectir incompleta el perfil de l'usuari.", "account.domain_blocked": "Domini ocult", "account.edit_profile": "Edita el perfil", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 82b749417..6dee6e544 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -2,7 +2,7 @@ "account.block": "Bloki @{name}", "account.block_domain": "Kaŝi ĉion de {domain}", "account.blocked": "Blokita", - "account.direct": "Direct Message @{name}", + "account.direct": "Direct message @{name}", "account.disclaimer_full": "Subaj informoj povas reflekti la profilon de la uzanto nekomplete.", "account.domain_blocked": "Domajno kaŝita", "account.edit_profile": "Redakti profilon", @@ -17,8 +17,8 @@ "account.mute": "Silentigi @{name}", "account.mute_notifications": "Silentigi sciigojn el @{name}", "account.muted": "Silentigita", - "account.posts": "Mesaĝoj", - "account.posts_with_replies": "Mesaĝoj kun respondoj", + "account.posts": "Tootoj", + "account.posts_with_replies": "Kun respondoj", "account.report": "Signali @{name}", "account.requested": "Atendo de aprobo. Alklaku por nuligi peton de sekvado", "account.share": "Diskonigi la profilon de @{name}", @@ -65,10 +65,10 @@ "compose_form.placeholder": "Pri kio vi pensas?", "compose_form.publish": "Hup", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.marked": "Media is 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.unmarked": "Text is not hidden", + "compose_form.sensitive.marked": "Aŭdovidaĵo markita tikla", + "compose_form.sensitive.unmarked": "Aŭdovidaĵo ne markita tikla", + "compose_form.spoiler.marked": "Teksto kaŝita malantaŭ averto", + "compose_form.spoiler.unmarked": "Teksto ne kaŝita", "compose_form.spoiler_placeholder": "Skribu vian averton ĉi tie", "confirmation_modal.cancel": "Nuligi", "confirmations.block.confirm": "Bloki", @@ -260,9 +260,9 @@ "status.sensitive_warning": "Tikla enhavo", "status.share": "Diskonigi", "status.show_less": "Malgrandigi", - "status.show_less_all": "Show less for all", + "status.show_less_all": "Malgrandigi ĉiujn", "status.show_more": "Grandigi", - "status.show_more_all": "Show more for all", + "status.show_more_all": "Grandigi ĉiujn", "status.unmute_conversation": "Malsilentigi konversacion", "status.unpin": "Depingli de profilo", "tabs_bar.federated_timeline": "Fratara tempolinio", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 4b64ca353..61cdcd00a 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -1,10 +1,10 @@ { "account.block": "مسدودسازی @{name}", "account.block_domain": "پنهان‌سازی همه چیز از سرور {domain}", - "account.blocked": "Blocked", - "account.direct": "Direct Message @{name}", + "account.blocked": "مسدودشده", + "account.direct": "Direct message @{name}", "account.disclaimer_full": "اطلاعات زیر ممکن است نمایهٔ این کاربر را به تمامی نشان ندهد.", - "account.domain_blocked": "Domain hidden", + "account.domain_blocked": "دامین پنهان‌شده", "account.edit_profile": "ویرایش نمایه", "account.follow": "پی بگیرید", "account.followers": "پیگیران", @@ -16,9 +16,9 @@ "account.moved_to": "{name} منتقل شده است به:", "account.mute": "بی‌صدا کردن @{name}", "account.mute_notifications": "بی‌صداکردن اعلان‌ها از طرف @{name}", - "account.muted": "Muted", + "account.muted": "بی‌صداشده", "account.posts": "نوشته‌ها", - "account.posts_with_replies": "Toots with replies", + "account.posts_with_replies": "نوشته‌ها و پاسخ‌ها", "account.report": "گزارش @{name}", "account.requested": "در انتظار پذیرش", "account.share": "هم‌رسانی نمایهٔ @{name}", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 58e6ad54d..57c55c9bd 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -2,7 +2,7 @@ "account.block": "Bloquer @{name}", "account.block_domain": "Tout masquer venant de {domain}", "account.blocked": "Bloqué", - "account.direct": "Direct Message @{name}", + "account.direct": "Direct message @{name}", "account.disclaimer_full": "Les données ci-dessous peuvent ne pas refléter ce profil dans sa totalité.", "account.domain_blocked": "Domaine caché", "account.edit_profile": "Modifier le profil", @@ -20,7 +20,7 @@ "account.posts": "Pouets", "account.posts_with_replies": "Pouets avec réponses", "account.report": "Signaler", - "account.requested": "Invitation envoyée", + "account.requested": "En attente d'approbation. Cliquez pour annuler la requête", "account.share": "Partager le profil de @{name}", "account.show_reblogs": "Afficher les partages de @{name}", "account.unblock": "Débloquer", @@ -88,7 +88,7 @@ "emoji_button.activity": "Activités", "emoji_button.custom": "Personnalisés", "emoji_button.flags": "Drapeaux", - "emoji_button.food": "Boire et manger", + "emoji_button.food": "Nourriture & Boisson", "emoji_button.label": "Insérer un émoji", "emoji_button.nature": "Nature", "emoji_button.not_found": "Pas d'emojis !! (╯°□°)╯︵ ┻━┻", @@ -98,14 +98,14 @@ "emoji_button.search": "Recherche…", "emoji_button.search_results": "Résultats de la recherche", "emoji_button.symbols": "Symboles", - "emoji_button.travel": "Lieux et voyages", + "emoji_button.travel": "Lieux & Voyages", "empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir !", "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag.", - "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres utilisateur⋅ice⋅s.", + "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres personnes.", "empty_column.home.public_timeline": "le fil public", "empty_column.list": "Il n'y a rien dans cette liste pour l'instant. Dès que des personnes de cette liste publierons de nouveaux statuts, ils apparaîtront ici.", - "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres utilisateur⋅ice⋅s pour débuter la conversation.", - "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des utilisateur⋅ice·s d’autres instances pour remplir le fil public", + "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres personnes pour débuter la conversation.", + "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres instances pour remplir le fil public", "follow_request.authorize": "Accepter", "follow_request.reject": "Rejeter", "getting_started.appsshort": "Applications", @@ -122,9 +122,9 @@ "keyboard_shortcuts.back": "revenir en arrière", "keyboard_shortcuts.boost": "partager", "keyboard_shortcuts.column": "focaliser un statut dans l'une des colonnes", - "keyboard_shortcuts.compose": "pour centrer la zone de redaction", + "keyboard_shortcuts.compose": "pour centrer la zone de rédaction", "keyboard_shortcuts.description": "Description", - "keyboard_shortcuts.down": "descendre dans la liste", + "keyboard_shortcuts.down": "pour descendre dans la liste", "keyboard_shortcuts.enter": "pour ouvrir le statut", "keyboard_shortcuts.favourite": "vers les favoris", "keyboard_shortcuts.heading": "Raccourcis clavier", @@ -151,7 +151,7 @@ "media_gallery.toggle_visible": "Modifier la visibilité", "missing_indicator.label": "Non trouvé", "missing_indicator.sublabel": "Ressource introuvable", - "mute_modal.hide_notifications": "Masquer les notifications de cet utilisateur ?", + "mute_modal.hide_notifications": "Masquer les notifications de cette personne ?", "navigation_bar.blocks": "Comptes bloqués", "navigation_bar.community_timeline": "Fil public local", "navigation_bar.domain_blocks": "Hidden domains", @@ -170,7 +170,7 @@ "notification.follow": "{name} vous suit", "notification.mention": "{name} vous a mentionné⋅e :", "notification.reblog": "{name} a partagé votre statut :", - "notifications.clear": "Nettoyer", + "notifications.clear": "Nettoyer les notifications", "notifications.clear_confirmation": "Voulez-vous vraiment supprimer toutes vos notifications ?", "notifications.column_settings.alert": "Notifications locales", "notifications.column_settings.favourite": "Favoris :", @@ -183,12 +183,12 @@ "notifications.column_settings.sound": "Émettre un son", "onboarding.done": "Effectué", "onboarding.next": "Suivant", - "onboarding.page_five.public_timelines": "Le fil public global affiche les posts de tou⋅te⋅s les utilisateur⋅ice⋅s suivi⋅es par les membres de {domain}. Le fil public local est identique mais se limite aux utilisateur⋅ice⋅s de {domain}.", - "onboarding.page_four.home": "L’Accueil affiche les posts de tou⋅te·s les utilisateur⋅ice·s que vous suivez.", - "onboarding.page_four.notifications": "Les Notifications vous informent lorsque quelqu’un interagit avec vous.", - "onboarding.page_one.federation": "Mastodon est un réseau social qui appartient à tou⋅te⋅s.", - "onboarding.page_one.full_handle": "Votre pleine maîtrise", - "onboarding.page_one.handle_hint": "C'est ce que vous diriez à vos amis de rechercher.", + "onboarding.page_five.public_timelines": "Le fil public global affiche les messages de toutes les personnes suivies par les membres de {domain}. Le fil public local est identique mais se limite aux membres de {domain}.", + "onboarding.page_four.home": "L’Accueil affiche les messages des personnes que vous suivez.", + "onboarding.page_four.notifications": "La colonne de notification vous avertit lors d'une interaction avec vous.", + "onboarding.page_one.federation": "Mastodon est un réseau de serveurs indépendants qui se joignent pour former un réseau social plus vaste. Nous appelons ces serveurs des instances.", + "onboarding.page_one.full_handle": "Votre identifiant complet", + "onboarding.page_one.handle_hint": "C'est ce que vos amis devront rechercher.", "onboarding.page_one.welcome": "Bienvenue sur Mastodon !", "onboarding.page_six.admin": "L’administrateur⋅ice de votre instance est {admin}.", "onboarding.page_six.almost_done": "Nous y sommes presque…", @@ -199,7 +199,7 @@ "onboarding.page_six.read_guidelines": "S’il vous plaît, n’oubliez pas de lire les {guidelines} !", "onboarding.page_six.various_app": "applications mobiles", "onboarding.page_three.profile": "Modifiez votre profil pour changer votre avatar, votre description ainsi que votre nom. Vous y trouverez également d’autres préférences.", - "onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des utilisateur⋅ice⋅s et regarder des hashtags tels que {illustration} et {introductions}. Pour trouver quelqu’un qui n’est pas sur cette instance, utilisez son nom d’utilisateur⋅ice complet.", + "onboarding.page_three.search": "Utilisez la barre de recherche pour trouver des utilisateur⋅ice⋅s ou regardez des hashtags tels que {illustration} et {introductions}. Pour trouver quelqu’un qui n’est pas sur cette instance, utilisez son identifiant complet.", "onboarding.page_two.compose": "Écrivez depuis la colonne de composition. Vous pouvez ajouter des images, changer les réglages de confidentialité, et ajouter des avertissements de contenu (Content Warning) grâce aux icônes en dessous.", "onboarding.skip": "Passer", "privacy.change": "Ajuster la confidentialité du message", @@ -227,16 +227,16 @@ "report.target": "Signalement", "search.placeholder": "Rechercher", "search_popout.search_format": "Recherche avancée", - "search_popout.tips.full_text": "Les textes simples retournent les pouets que vous avez écris, mis en favori, épinglés, ou ayant été mentionnés, ainsi que les noms d'utilisateurs, les noms affichés, et les hashtags correspondant.", + "search_popout.tips.full_text": "Les textes simples retournent les pouets que vous avez écris, mis en favori, épinglés, ou ayant été mentionnés, ainsi que les identifiants, les noms affichés, et les hashtags des personnes et messages correspondant.", "search_popout.tips.hashtag": "hashtag", "search_popout.tips.status": "statuts", - "search_popout.tips.text": "Un texte simple renvoie les noms affichés, les noms d’utilisateur⋅ice et les hashtags correspondants", + "search_popout.tips.text": "Un texte simple renvoie les noms affichés, les identifiants et les hashtags correspondants", "search_popout.tips.user": "utilisateur⋅ice", "search_results.accounts": "Personnes", "search_results.hashtags": "Hashtags", "search_results.statuses": "Pouets", "search_results.total": "{count, number} {count, plural, one {résultat} other {résultats}}", - "standalone.public_title": "Jeter un coup d’œil…", + "standalone.public_title": "Un aperçu …", "status.block": "Block @{name}", "status.cannot_reblog": "Cette publication ne peut être boostée", "status.delete": "Effacer", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 80a13a9bf..432517716 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -27,7 +27,7 @@ "account.unblock_domain": "{domain}を表示", "account.unfollow": "フォロー解除", "account.unmute": "@{name}さんのミュートを解除", - "account.unmute_notifications": "@{name}さんからの通知を受け取る", + "account.unmute_notifications": "@{name}さんからの通知を受け取るようにする", "account.view_full_profile": "全ての情報を見る", "alert.unexpected.message": "不明なエラーが発生しました", "alert.unexpected.title": "エラー", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 449df42b8..89fde8966 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -2,7 +2,7 @@ "account.block": "@{name}을 차단", "account.block_domain": "{domain} 전체를 숨김", "account.blocked": "차단 됨", - "account.direct": "Direct Message @{name}", + "account.direct": "Direct message @{name}", "account.disclaimer_full": "여기 있는 정보는 유저의 프로파일을 정확히 반영하지 못 할 수도 있습니다.", "account.domain_blocked": "도메인 숨겨짐", "account.edit_profile": "프로필 편집", @@ -17,7 +17,7 @@ "account.mute": "@{name} 뮤트", "account.mute_notifications": "@{name}의 알림을 뮤트", "account.muted": "뮤트 됨", - "account.posts": "게시물", + "account.posts": "툿", "account.posts_with_replies": "툿과 답장", "account.report": "@{name} 신고", "account.requested": "승인 대기 중. 클릭해서 취소하기", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 7bfae0302..925b46df6 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -2,7 +2,7 @@ "account.block": "Blokovať @{name}", "account.block_domain": "Ukryť všetko z {domain}", "account.blocked": "Blokovaný/á", - "account.direct": "Direct Message @{name}", + "account.direct": "Direct message @{name}", "account.disclaimer_full": "Inofrmácie nižšie nemusia byť úplným odrazom uživateľovho účtu.", "account.domain_blocked": "Doména ukrytá", "account.edit_profile": "Upraviť profil", @@ -42,12 +42,12 @@ "column.community": "Lokálna časová os", "column.domain_blocks": "Hidden domains", "column.favourites": "Obľúbené", - "column.follow_requests": "Žiadosti o sledovaní", + "column.follow_requests": "Žiadosti o sledovanie", "column.home": "Domov", "column.lists": "Zoznamy", "column.mutes": "Ignorovaní užívatelia", "column.notifications": "Notifikácie", - "column.pins": "Pripnuté toots", + "column.pins": "Pripnuté tooty", "column.public": "Federovaná časová os", "column_back_button.label": "Späť", "column_header.hide_settings": "Skryť nastavenia", @@ -163,7 +163,7 @@ "navigation_bar.lists": "Zoznamy", "navigation_bar.logout": "Odhlásiť", "navigation_bar.mutes": "Ignorovaní užívatelia", - "navigation_bar.pins": "Pripnuté toots", + "navigation_bar.pins": "Pripnuté tooty", "navigation_bar.preferences": "Voľby", "navigation_bar.public_timeline": "Federovaná časová os", "notification.favourite": "{name} sa páči tvoj status", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index fab7ecf06..c7925829f 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -2,7 +2,7 @@ "account.block": "封鎖 @{name}", "account.block_domain": "隱藏來自 {domain} 的一切貼文", "account.blocked": "Blocked", - "account.direct": "Direct Message @{name}", + "account.direct": "Direct message @{name}", "account.disclaimer_full": "下列資料不一定完整。", "account.domain_blocked": "Domain hidden", "account.edit_profile": "編輯用者資訊", @@ -103,7 +103,7 @@ "empty_column.hashtag": "這個主題標籤下什麼都沒有。", "empty_column.home": "你還沒關注任何人。造訪{public}或利用搜尋功能找到其他用者。", "empty_column.home.public_timeline": "公開時間軸", - "empty_column.list": "There is nothing in this list yet.", + "empty_column.list": "此份清單尚未有東西。當此清單的成員貼出了新的狀態時,它們就會出現在這裡。", "empty_column.notifications": "還沒有任何通知。和別的使用者互動來開始對話。", "empty_column.public": "這裡什麼都沒有!公開寫些什麼,或是關注其他副本的使用者。", "follow_request.authorize": "授權", diff --git a/config/locales/ar.yml b/config/locales/ar.yml index 25ca302d6..c316a2fa5 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -119,6 +119,7 @@ ar: user: مستخدِم salmon_url: عنوان رابط سالمون Salmon search: البحث + shared_inbox_url: رابط الصندوق المُشترَك للبريد الوارد show: created_reports: البلاغات التي أنشأها هذا الحساب report: التقرير @@ -140,14 +141,23 @@ ar: create_email_domain_block: "%{name} قد قام بحظر نطاق البريد الإلكتروني %{target}" demote_user: "%{name} قد قام بإنزال الرتبة الوظيفية لـ %{target}" destroy_domain_block: "%{name} قام بإلغاء الحجب عن النطاق %{target}" + destroy_email_domain_block: قام %{name} بإضافة نطاق البريد الإلكتروني %{target} إلى اللائحة البيضاء + destroy_status: لقد قام %{name} بحذف منشور %{target} disable_2fa_user: "%{name} لقد قام بتعطيل ميزة المصادقة بخطوتين للمستخدم %{target}" disable_custom_emoji: "%{name} قام بتعطيل الإيموجي %{target}" disable_user: "%{name} لقد قام بتعطيل تسجيل الدخول للمستخدِم %{target}" enable_custom_emoji: "%{name} قام بتنشيط الإيموجي %{target}" enable_user: "%{name} لقد قام بتنشيط تسجيل الدخول للمستخدِم %{target}" + memorialize_account: لقد قام %{name} بتحويل حساب %{target} إلى صفحة تذكارية promote_user: "%{name} قام بترقية المستخدم %{target}" reset_password_user: "%{name} لقد قام بإعادة تعيين الكلمة السرية الخاصة بـ %{target}" + resolve_report: قام %{name} بإلغاء التقرير المُرسَل مِن طرف %{target} + silence_account: لقد قام %{name} بكتم حساب %{target} + suspend_account: لقد قام %{name} بتعليق حساب %{target} + unsilence_account: لقد قام %{name} بإلغاء الكتم عن حساب %{target} + unsuspend_account: لقد قام %{name} بإلغاء التعليق المفروض على حساب %{target} update_custom_emoji: "%{name} قام بتحديث الإيموجي %{target}" + update_status: لقد قام %{name} بتحديث منشور %{target} title: سِجلّ التفتيش و المعاينة custom_emojis: by_domain: النطاق @@ -163,35 +173,54 @@ ar: enable: تفعيل enabled_msg: تم تنشيط ذاك الإيموجي بنجاح image_hint: ملف PNG إلى غاية حجم 50 ك.ب + listed: مُدرَج new: title: إضافة إيموجي خاص جديد + overwrite: إعادة الكتابة shortcode: الترميز المُصَغّر shortcode_hint: على الأقل حرفين، و فقط رموز أبجدية عددية و أسطر سفلية title: الإيموجي الخاصة + unlisted: غير مدرج update_failed_msg: تعذرت عملية تحذيث ذاك الإيموجي updated_msg: تم تحديث الإيموجي بنجاح ! upload: رفع domain_blocks: add_new: إضافة نطاق جديد + created_msg: إنّ حجب النطاق حيز التشغيل + destroyed_msg: تم إلغاء الحجب المفروض على النطاق domain: النطاق new: create: إنشاء حظر severity: noop: لا شيء silence: كتم + suspend: تعليق title: حجب نطاق جديد reject_media: رفض ملفات الوسائط severities: noop: لا شيء + silence: إخفاء أو كتم + suspend: تعليق + severity: الشدة show: + affected_accounts: + other: هناك %{count} حسابات في قاعدة البيانات متأثرة بذلك + retroactive: + silence: إلغاء الكتم عن كافة الحسابات المتواجدة على هذا النطاق + suspend: إلغاء التعليق المفروض على كافة حسابات هذا النطاق title: رفع حظر النطاق عن %{domain} undo: إلغاء + title: حظر النطاقات undo: إلغاء email_domain_blocks: + add_new: إضافة + created_msg: لقد دخل حظر نطاق البريد الإلكتروني حيّز الخدمة delete: حذف + destroyed_msg: تم حذف نطاق البريد الإلكتروني من اللائحة السوداء بنجاح domain: النطاق new: create: إضافة نطاق + title: إضافة نطاق بريد جديد إلى اللائحة السوداء title: القائمة السوداء للبريد الإلكتروني instances: account_count: الحسابات المعروفة @@ -214,27 +243,52 @@ ar: none: لا شيء delete: حذف id: معرّف ID + mark_as_resolved: إعتبار التقرير كمحلول nsfw: + 'false': الكشف عن الصور 'true': إخفاء الوسائط المرفقة + report: 'التقرير #%{id}' report_contents: المحتويات + reported_account: حساب مُبلّغ عنه reported_by: أبلغ عنه من طرف + resolved: معالجة + silence_account: كتم و إخفاء الحساب status: الحالة + suspend_account: فرض تعليق على الحساب + target: الهدف title: التقارير + unresolved: غير معالجة view: عرض settings: + activity_api_enabled: + desc_html: عدد المنشورات المحلية و المستخدمين النشطين و التسجيلات الأسبوعية الجديدة + bootstrap_timeline_accounts: + title: الإشتراكات الإفتراضية للمستخدمين الجدد contact_information: email: البريد الإلكتروني المهني username: الإتصال بالمستخدِم + hero: + title: الصورة الرأسية + peers_api_enabled: + desc_html: أسماء النطاقات التي إلتقى بها مثيل الخادوم على البيئة الموحَّدة فيديفرس + title: نشر عدد مثيلات الخوادم التي تم مصادفتها registrations: closed_message: title: رسالة التسجيلات المقفلة deletion: desc_html: السماح لأي مستخدم إغلاق حسابه + title: السماح بحذف الحسابات + min_invite_role: + disabled: لا أحد open: desc_html: السماح للجميع بإنشاء حساب title: فتح التسجيل + show_known_fediverse_at_about_page: + title: إظهار الفيديفرس الموحَّد في خيط المُعايَنة site_description: title: وصف مثيل الخادوم + site_description_extended: + title: الوصف المُفصّل للموقع site_terms: title: شروط الخدمة المخصصة site_title: إسم مثيل الخادم @@ -242,11 +296,14 @@ ar: title: الصورة الرمزية المصغرة لمثيل الخادوم timeline_preview: desc_html: عرض الخيط العمومي على صفحة الإستقبال + title: مُعاينة الخيط العام title: إعدادات الموقع statuses: back_to_account: العودة إلى صفحة الحساب batch: delete: حذف + execute: تفعيل + failed_to_execute: خطأ في التفعيل media: hide: إخفاء الوسائط show: إظهار الوسائط @@ -256,8 +313,13 @@ ar: subscriptions: confirmed: مؤكَّد expires_in: تنتهي مدة صلاحيتها في + last_delivery: آخر إيداع + title: WebSub topic: الموضوع title: الإدارة + admin_mailer: + new_report: + body: قام %{reporter} بالإبلاغ عن %{target} application_mailer: notification_preferences: تعديل خيارات البريد الإلكتروني salutation: "%{name}،" @@ -270,6 +332,7 @@ ar: destroyed: تم حذف التطبيق بنجاح invalid_url: إن الرابط المقدم غير صالح regenerate_token: إعادة توليد رمز النفاذ + warning: كن حذرا مع هذه البيانات. لا تقم أبدا بمشاركتها مع الآخَرين ! your_token: رمز نفاذك auth: agreement_html: بقبولك التسجيل فإنك تُصرِّح قبول قواعد مثيل الخادوم و شروط الخدمة التي نوفرها لك. @@ -283,11 +346,13 @@ ar: logout: خروج migrate_account: الإنتقال إلى حساب آخر migrate_account_html: إن كنت ترغب في تحويل هذا الحساب نحو حساب آخَر، يُمكِنُك إعداده هنا. + or: أو or_log_in_with: أو قم بتسجيل الدخول بواسطة providers: cas: CAS saml: SAML register: إنشاء حساب + register_elsewhere: التسجيل على خادوم آخَر resend_confirmation: إعادة إرسال تعليمات التأكيد reset_password: إعادة تعيين كلمة المرور security: الهوية @@ -296,6 +361,7 @@ ar: error: يا للأسف، وقع هناك خطأ إثر عملية البحث عن الحساب عن بعد follow: إتبع follow_request: 'لقد قمت بإرسال طلب متابعة إلى :' + following: 'مرحى ! أنت الآن تتبع :' post_follow: close: أو يمكنك إغلاق هذه النافذة. return: العودة إلى الملف الشخصي للمستخدم @@ -324,18 +390,24 @@ ar: '403': ليس لك الصلاحيات الكافية لعرض هذه الصفحة. '404': إنّ الصفحة التي تبحث عنها لا وجود لها أصلا. '410': إنّ الصفحة التي تبحث عنها لم تعد موجودة. + '422': + content: فشل التحقق الآمن. ربما منعتَ كعكات الكوكيز ؟ + title: فشِل التحقق الآمن '500': content: نحن متأسفون، لقد حدث خطأ ما مِن جانبنا. title: هذه الصفحة خاطئة exports: archive_takeout: + date: التاريخ download: تنزيل نسخة لحسابك hint_html: بإمكانك طلب نسخة كاملة لـ كافة تبويقاتك و الوسائط التي قمت بنشرها. البيانات المُصدَّرة ستكون محفوظة على شكل نسق ActivityPub و باستطاعتك قراءتها بأي برنامج يدعم هذا النسق. in_progress: عملية جمع نسخة لبيانات حسابك جارية … request: طلب نسخة لحسابك + size: الحجم blocks: قمت بحظر csv: CSV follows: أنت تتبع + mutes: قُمتَ بكتم storage: ذاكرة التخزين followers: domain: النطاق @@ -368,9 +440,14 @@ ar: '86400': يوم واحد expires_in_prompt: أبدا generate: توليد + max_uses: + one: إستعمال واحد + other: "%{count} استخدامات" max_uses_prompt: بلا حدود + prompt: توليد و مشاركة روابط للسماح للآخَرين بالنفاذ إلى مثيل الخادوم هذا table: expires_at: تنتهي مدة صلاحيتها في + uses: يستخدِم title: دعوة أشخاص landing_strip_html: "%{name} هو أحد مُستخدِمي %{link_to_root_path}. بإمكانك متابعته أو التواصل معه إن كنت تملك حسابًا أيا كان على البيئة الموحَّدة فيديفرس." landing_strip_signup_html: إن كنت لا تملك واحدا، يمكنك التسجيل مِن هنا. @@ -433,7 +510,7 @@ ar: next: التالي older: الأقدَم prev: السابق - truncate: "…" + truncate: و preferences: languages: اللغات other: إعدادات أخرى diff --git a/config/locales/devise.zh-TW.yml b/config/locales/devise.zh-TW.yml index e627653f1..976e96be2 100644 --- a/config/locales/devise.zh-TW.yml +++ b/config/locales/devise.zh-TW.yml @@ -2,7 +2,7 @@ zh-TW: devise: confirmations: - confirmed: 信箱驗證成功 + confirmed: 您的電子郵件地址確認成功。 send_instructions: 您將會在幾分鐘內收到驗證信。 send_paranoid_instructions: 如果您的電子信箱已經存在於我們的資料庫,您將會在幾分鐘內收到信,確認您電子信箱的指示。 failure: @@ -10,18 +10,39 @@ zh-TW: inactive: 您的帳號尚未啟用。 invalid: 不正確的 %{authentication_keys} 或密碼。 last_attempt: 若您再次嘗試失敗,我們將鎖定您的帳號,以策安全。 - locked: 您的帳號已被鎖定 + locked: 您的帳號已被鎖定。 not_found_in_database: 不正確的 %{authentication_keys} 或密碼。 timeout: 您的登入階段已經逾期,請重新登入以繼續使用。 unauthenticated: 您必須先登入或註冊,以繼續使用。 unconfirmed: 您必須先完成信箱驗證,以繼續使用。 mailer: confirmation_instructions: + action: 驗證電子郵件地址 + explanation: 您已經在 %{host} 上以此電子郵件地址建立了一個帳號。您距離啟用它只剩一次點擊之遙了。如果這不是你,請忽略此電子郵件。 + extra_html: 同時也請看看該實體的規則我們的服務條款。 subject: 'Mastodon: 信箱驗證' + title: 驗證電子郵件地址 + email_changed: + explanation: 您帳號的電子郵件地址被變更為: + extra: 如果您並未變更您的電子郵件,那麼很有可能是某人取得了您帳號的存取權限。請立刻變更您的密碼,或是若您的帳號已被鎖定,請聯絡所使用實體的管理員。 + subject: Mastodon 電子郵件變更 + title: 新電子郵件地址 password_change: + explanation: 您帳號的密碼已變更。 + extra: 如果您並未變更您的密碼,那麼很有可能是某人取得了您帳號的存取權限。請立刻變更您的密碼,或是若您的帳號已被鎖定,請聯絡所使用實體的管理員。 subject: 'Mastodon: 更改密碼' + title: 密碼已變更 + reconfirmation_instructions: + explanation: 確認新的電子郵件地址以變更您的電子郵件。 + extra: 若此次變更不是由您開啟的,請忽略這個電子郵件。Mastodon 帳號的電子郵件地址在您存取上面的連結前不會變更。 + subject: Mastodon:%{instance} 的確認電子郵件 + title: 驗證電子郵件地址 reset_password_instructions: + action: 變更密碼 + explanation: 您為您的帳號請求了一個新密碼。 + extra: 若您並未請求這個,請忽略此電子郵件。您的密碼在您存取上面的連結並建立一個新的之前不會變更。 subject: 'Mastodon: 重設密碼' + title: 重設密碼 unlock_instructions: subject: 'Mastodon: 帳號解鎖' omniauth_callbacks: diff --git a/config/locales/doorkeeper.ar.yml b/config/locales/doorkeeper.ar.yml index d13c22386..5586b8dc8 100644 --- a/config/locales/doorkeeper.ar.yml +++ b/config/locales/doorkeeper.ar.yml @@ -79,12 +79,17 @@ ar: messages: access_denied: لقد رفض مالك المَورِدِ أو تصريح السيرفر طلبك. invalid_client: فشلت المصادقة مع العميل لأنه العميل مجهول أو لغياب المصادقة ضمن العميل أو أنّ أسلوب المصادقة غير مدعومة. + invalid_grant: إنّ التصريح المقدَّم غير صالح، سواء انتهت مدة صلاحيته أو تم إلغاؤه أو أنه لا يتطابق مع عنوان إعادة التحويل في طلب التصريح أو أنّ هذا التصريح قد تم تقديمه لعميل آخر. invalid_redirect_uri: إنّ عنوان إعادة التحويل غير صالح. + invalid_request: إنّ هذا الطلب يستلزم مؤشرا أو يحمل قيمة مُعامِل غير مدعومة أو فيه خلل ما. + invalid_resource_owner: إنّ المُعرِّفات التي قدّمها صاحب المورِد غير صحيحة أو أنه لا وجود لصاحب المورِد invalid_scope: المجال المطلوب غير صحيح أو مجهول أو مُعبَّر عنه بشكل خاطئ. invalid_token: expired: إنتهت فترة صلاحيته رمز المصادقة revoked: تم إبطال رمز المصادقة unknown: رمز المصادقة غير صالح + resource_owner_authenticator_not_configured: لقد أخفقت عملية البحث عن صاحب المَورِد لغياب الضبط في Doorkeeper.configure.resource_owner_authenticator. + server_error: لقد صادفَ خادوم التصريحات ضروفا غير مواتية، الأمر الذي مَنَعه مِن مواصلة دراسة الطلب. temporarily_unavailable: تعذر على خادم التفويض معالجة الطلب و ذلك بسبب زيادة مؤقتة في التحميل أو عملية صيانة مبرمجة على الخادم. unauthorized_client: لا يصرح للعميل بتنفيذ هذا الطلب باستخدام هذه الطريقة. unsupported_grant_type: هذا النوع من منح التصريح غير معتمد في خادم الترخيص. diff --git a/config/locales/doorkeeper.fa.yml b/config/locales/doorkeeper.fa.yml index 6a4be575e..f3db862ca 100644 --- a/config/locales/doorkeeper.fa.yml +++ b/config/locales/doorkeeper.fa.yml @@ -3,19 +3,19 @@ fa: activerecord: attributes: doorkeeper/application: - name: Application name - redirect_uri: Redirect URI - scopes: Scopes - website: Application website + name: نام برنامه + redirect_uri: نشانی تغییرمسیر + scopes: محدوده + website: وبگاه برنامه errors: models: doorkeeper/application: attributes: redirect_uri: - fragment_present: cannot contain a fragment. - invalid_uri: must be a valid URI. - relative_uri: must be an absolute URI. - secured_uri: must be an HTTPS/SSL URI. + fragment_present: نمی‌تواند چندتکه باشد. + invalid_uri: باید یک نشانی معتبر باشد. + relative_uri: باید یک نشانی مطلق باشد. + secured_uri: باید یک نشانی HTTPS/SSL باشد. doorkeeper: applications: buttons: diff --git a/config/locales/doorkeeper.id.yml b/config/locales/doorkeeper.id.yml index 6db797c87..0a99b86c0 100644 --- a/config/locales/doorkeeper.id.yml +++ b/config/locales/doorkeeper.id.yml @@ -35,9 +35,13 @@ id: redirect_uri: Gunakan satu baris per URI scopes: Pisahkan scope dengan spasi. Biarkan kosong jika ingin menggunakan scope default. index: + application: Aplikasi callback_url: URL Callback + delete: Hapus name: Nama new: Aplikasi baru + scopes: Cakupan + show: Tampilkan title: Aplikasi anda new: title: Aplikasi Baru diff --git a/config/locales/doorkeeper.sk.yml b/config/locales/doorkeeper.sk.yml index b8fd281f7..7a285eb4f 100644 --- a/config/locales/doorkeeper.sk.yml +++ b/config/locales/doorkeeper.sk.yml @@ -19,7 +19,7 @@ sk: doorkeeper: applications: buttons: - authorize: Autorizovať + authorize: Overiť cancel: Zrušiť destroy: Zničiť edit: Upraviť @@ -54,7 +54,7 @@ sk: title: 'Aplikácia: %{name}' authorizations: buttons: - authorize: Autorizovať + authorize: Overiť deny: Zamietnuť error: title: Nastala chyba diff --git a/config/locales/doorkeeper.zh-TW.yml b/config/locales/doorkeeper.zh-TW.yml index 01e62df73..2aa271785 100644 --- a/config/locales/doorkeeper.zh-TW.yml +++ b/config/locales/doorkeeper.zh-TW.yml @@ -5,12 +5,14 @@ zh-TW: doorkeeper/application: name: 名稱 redirect_uri: 重新導向 URI + scopes: 範圍 + website: 應用程式網頁 errors: models: doorkeeper/application: attributes: redirect_uri: - fragment_present: URI 不可包含 "#fragment" 部份 + fragment_present: 不能包含 fragment。 invalid_uri: 必需有正確的 URI. relative_uri: 必需為絕對 URI. secured_uri: 必需使用有 HTTPS/SSL 加密的 URI. @@ -31,11 +33,15 @@ zh-TW: help: native_redirect_uri: 使用 %{native_redirect_uri} 作局部測試 redirect_uri: 每行輸入一個 URI - scopes: 請用半形空格分開權限範圍 (scope)。留空表示使用預設的權限範圍 + scopes: 請用半形空格分開權限範圍 (scope)。留空表示使用預設的權限範圍。 index: + application: 應用程式 callback_url: 回傳網址 + delete: 刪除 name: 名稱 new: 新增應用程式 + scopes: 範圍 + show: 顯示 title: 您的應用程式 new: title: 新增應用程式 @@ -57,7 +63,7 @@ zh-TW: prompt: 應用程式 %{client_name} 要求取得您帳號的部份權限 title: 需要授權 show: - title: Copy this authorization code and paste it to the application. + title: 複製此授權碼並貼上到應用程式中。 authorized_applications: buttons: revoke: 取消授權 @@ -77,7 +83,7 @@ zh-TW: invalid_grant: 授權申請不正確、逾期、已被取消、與授權請求內的重新導向 URI 不符、或屬於別的客戶端程式。 invalid_redirect_uri: 不正確的重新導向網址。 invalid_request: 請求缺少必要的參數、包含不支援的參數、或其他輸入錯誤。 - invalid_resource_owner: 資源擁有者的登入資訊錯誤、或無法找到該資源擁有者。 + invalid_resource_owner: 資源擁有者的登入資訊錯誤、或無法找到該資源擁有者 invalid_scope: 請求的權限範圍不正確、未有定義、或輸入錯誤。 invalid_token: expired: access token 已過期 diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 8bc2aaa41..84d63d831 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -333,7 +333,7 @@ eo: subscriptions: callback_url: Revena URL confirmed: Konfirmita - expires_in: Eksvalidiĝas en + expires_in: Eksvalidiĝas je last_delivery: Lasta livero title: WebSub topic: Temo @@ -485,7 +485,7 @@ eo: max_uses_prompt: Neniu limo prompt: Krei kaj diskonigi ligilojn al aliaj por doni aliron al ĉi tiu nodo table: - expires_at: Eksvalidiĝas + expires_at: Eksvalidiĝas je uses: Uzoj title: Inviti homojn landing_strip_html: "%{name} estas uzanto en %{link_to_root_path}. Vi povas sekvi tiun aŭ interagi kun tiu, se vi havas konton ie ajn en la fediverse." @@ -636,6 +636,15 @@ eo: two_factor_authentication: Dufaktora aŭtentigo your_apps: Viaj aplikaĵoj statuses: + attached: + description: 'Ligita: %{attached}' + image: + one: "%{count} bildo" + other: "%{count} bildoj" + video: + one: "%{count} video" + other: "%{count} videoj" + content_warning: 'Enhava averto: %{warning}' open_in_web: Malfermi retumile over_character_limit: limo de %{max} signoj transpasita pin_errors: diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 1ff309782..090b080e4 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -641,8 +641,8 @@ ja: one: "%{count} 枚の画像" other: "%{count} 枚の画像" video: - one: "%{count} 枚の動画" - other: "%{count} 枚の動画" + one: "%{count} 本の動画" + other: "%{count} 本の動画" content_warning: '閲覧注意: %{warning}' open_in_web: Webで開く over_character_limit: 上限は %{max}文字までです diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 8a11b096a..ba55b3549 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -361,6 +361,7 @@ ko: your_token: 액세스 토큰 auth: agreement_html: 이 등록으로 이용규약약관에 동의하는 것으로 간주됩니다. + change_password: 패스워드 confirm_email: 확인 메일 승인 delete_account: 계정 삭제 delete_account_html: 계정을 삭제하고 싶은 경우, 여기서 삭제할 수 있습니다. 삭제 전 확인 화면이 표시됩니다. @@ -373,7 +374,11 @@ ko: migrate_account_html: 이 계정을 다른 계정으로 리디렉션 하길 원하는 경우 여기에서 설정할 수 있습니다. or: 또는 or_log_in_with: 다른 방법으로 로그인 하려면 + providers: + cas: CAS + saml: SAML register: 등록하기 + register_elsewhere: 다른 인스턴스에서 가입 resend_confirmation: 확인 메일을 다시 보내기 reset_password: 비밀번호 재설정 security: 보안 @@ -545,7 +550,9 @@ ko: trillion: T unit: '' pagination: + newer: 새로운 툿 next: 다음 + older: 오래된 툿 prev: 이전 truncate: "…" preferences: @@ -629,6 +636,15 @@ ko: two_factor_authentication: 2단계 인증 your_apps: 애플리케이션 statuses: + attached: + description: '첨부: %{attached}' + image: + one: "%{count} 이미지" + other: "%{count} 이미지" + video: + one: "%{count} 영상" + other: "%{count} 영상" + content_warning: '열람 주의: %{warning}' open_in_web: Web으로 열기 over_character_limit: 최대 %{max}자까지 입력할 수 있습니다 pin_errors: diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index e49bfd9f9..85eccf091 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -45,6 +45,7 @@ ko: setting_default_privacy: 툿 프라이버시 setting_default_sensitive: 미디어를 언제나 민감한 컨텐츠로 설정 setting_delete_modal: 툿 삭제 전 확인 창을 표시 + setting_display_sensitive_media: 열람주의로 설정 된 이미지도 항상 보여주기 setting_noindex: 검색엔진의 인덱싱을 거절 setting_reduce_motion: 애니메이션 줄이기 setting_system_font_ui: 시스템의 초기 설정 폰트를 사용 @@ -53,6 +54,7 @@ ko: severity: 심각도 type: 불러오기 종류 username: 유저 이름 + username_or_email: 유저네임 또는 이메일 interactions: must_be_follower: 나를 팔로우 하지 않는 사람에게서 온 알림을 차단 must_be_following: 내가 팔로우 하지 않는 사람에게서 온 알림을 차단 diff --git a/config/locales/sk.yml b/config/locales/sk.yml index a0e1a597c..2ee25b372 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -580,7 +580,12 @@ sk: browser: Prehliadač browsers: alipay: Alipay + chrome: Google Chrome + edge: Microsoft Edge + electron: Electron + firefox: Mozilla Firefox generic: Neznámy prehliadač + ie: Internet Explorer current_session: Aktuálna sezóna description: "%{browser} na %{platform}" explanation: Tieto sú prehliadače ktoré sú teraz prihlásené na tvoj Mastodon účet. @@ -607,12 +612,22 @@ sk: two_factor_authentication: Dvoj-faktorové overenie your_apps: Tvoje aplikácie statuses: + attached: + description: 'Priložené: %{attached}' + image: + one: "%{count} obrázok" + other: "%{count} obrázkov" + video: + one: "%{count} video" + other: "%{count} videí" + content_warning: 'Varovanie o obsahu: %{warning}' open_in_web: Otvor v okne prehliadača over_character_limit: limit počtu %{max} znakov bol presiahnutý pin_errors: limit: Už ste si pripli ten najvyšší možný počet príspevkov ownership: Nemožno pripnúť príspevok od niekoho iného private: Neverejné príspevky nemôžu byť pripnuté + reblog: Pozdvihnutie sa nedá pripnúť show_more: Ukáž viac visibilities: private: Iba pre sledovateľov @@ -628,11 +643,20 @@ sk: sensitive_content: Senzitívny obsah terms: title: Podmienky užívania, a pravidlá o súkromí pre %{instance} + themes: + default: Mastodon + time: + formats: + default: "%b %d, %R, %H:%M" two_factor_authentication: + code_hint: Pre potvrdenie teraz zadaj kód vygenerovaný pomocou tvojej overovacej aplikácie + description_html: Ak povolíš dvoj-faktorové overovanie, na prihlásenie potom budeš potrebovať svoj telefón, ktorý vygeneruje prístupové kódy, čo musíš zadať. + disable: Zakáž 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 + instructions_html: "Naskenuj tento QR kód do Google Autentikátora, alebo do podobnej TOTP aplikácie pomocou svojho telefónu. Od tejto chvíle bude táto aplikácia pre teba generovať kódy ktoré musíš zadať aby si sa prihlásil/a." 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 From 4fd71accd419fb79cc75e0ebf30df374d174ebf5 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Mon, 2 Apr 2018 13:44:19 +0200 Subject: [PATCH 030/442] Fix issues with sending direct messages from user profile (#6999) * Clear compose textarea when starting a new direct message Previous behaviour resulted in potentially misdirected direct messages. * Hide search when starting to compose a direct message --- app/javascript/mastodon/reducers/compose.js | 2 +- app/javascript/mastodon/reducers/search.js | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index a48c46941..1f4177585 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -265,7 +265,7 @@ export default function compose(state = initialState, action) { .set('idempotencyKey', uuid()); case COMPOSE_DIRECT: return state - .update('text', text => `${text}@${action.account.get('acct')} `) + .update('text', text => `@${action.account.get('acct')} `) .set('privacy', 'direct') .set('focusDate', new Date()) .set('idempotencyKey', uuid()); diff --git a/app/javascript/mastodon/reducers/search.js b/app/javascript/mastodon/reducers/search.js index 08d90e4e8..56fd7226b 100644 --- a/app/javascript/mastodon/reducers/search.js +++ b/app/javascript/mastodon/reducers/search.js @@ -4,7 +4,11 @@ import { SEARCH_FETCH_SUCCESS, SEARCH_SHOW, } from '../actions/search'; -import { COMPOSE_MENTION, COMPOSE_REPLY } from '../actions/compose'; +import { + COMPOSE_MENTION, + COMPOSE_REPLY, + COMPOSE_DIRECT, +} from '../actions/compose'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; const initialState = ImmutableMap({ @@ -29,6 +33,7 @@ export default function search(state = initialState, action) { return state.set('hidden', false); case COMPOSE_REPLY: case COMPOSE_MENTION: + case COMPOSE_DIRECT: return state.set('hidden', true); case SEARCH_FETCH_SUCCESS: return state.set('results', ImmutableMap({ From e7a17167015dca6864f31152c47334c3b3a857a2 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Mon, 2 Apr 2018 13:45:07 +0200 Subject: [PATCH 031/442] Implement the ability for an Administrator or Moderator to remove an account avatar (#6998) --- app/controllers/admin/accounts_controller.rb | 13 ++++++++++++- app/helpers/admin/action_logs_helper.rb | 2 +- app/policies/account_policy.rb | 4 ++++ app/views/admin/accounts/show.html.haml | 8 ++++++++ config/locales/en.yml | 3 +++ config/routes.rb | 1 + 6 files changed, 29 insertions(+), 2 deletions(-) diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index 7428c3f22..e7ca6b907 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -2,7 +2,7 @@ module Admin class AccountsController < BaseController - before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :enable, :disable, :memorialize] + before_action :set_account, only: [:show, :subscribe, :unsubscribe, :redownload, :remove_avatar, :enable, :disable, :memorialize] before_action :require_remote_account!, only: [:subscribe, :unsubscribe, :redownload] before_action :require_local_account!, only: [:enable, :disable, :memorialize] @@ -60,6 +60,17 @@ module Admin redirect_to admin_account_path(@account.id) end + def remove_avatar + authorize @account, :remove_avatar? + + @account.avatar = nil + @account.save! + + log_action :remove_avatar, @account.user + + redirect_to admin_account_path(@account.id) + end + private def set_account diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb index 4475034a5..78278c700 100644 --- a/app/helpers/admin/action_logs_helper.rb +++ b/app/helpers/admin/action_logs_helper.rb @@ -86,7 +86,7 @@ module Admin::ActionLogsHelper opposite_verbs?(log) ? 'negative' : 'positive' when :update, :reset_password, :disable_2fa, :memorialize 'neutral' - when :demote, :silence, :disable, :suspend + when :demote, :silence, :disable, :suspend, :remove_avatar 'negative' when :destroy opposite_verbs?(log) ? 'positive' : 'negative' diff --git a/app/policies/account_policy.rb b/app/policies/account_policy.rb index 85e2c8419..efabe80d0 100644 --- a/app/policies/account_policy.rb +++ b/app/policies/account_policy.rb @@ -29,6 +29,10 @@ class AccountPolicy < ApplicationPolicy admin? end + def remove_avatar? + staff? + end + def subscribe? admin? end diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index dbbf5fc09..fecfd6cc8 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -14,6 +14,14 @@ %th= t('admin.accounts.display_name') %td= @account.display_name + %tr + %th= t('admin.accounts.avatar') + %th + = link_to @account.avatar.url(:original) do + = image_tag @account.avatar.url(:original), alt: '', width: 40, height: 40, class: 'avatar' + - if @account.local? && @account.avatar? + = table_link_to 'trash', t('admin.accounts.remove_avatar'), remove_avatar_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } if can?(:remove_avatar, @account) + - if @account.local? %tr %th= t('admin.accounts.role') diff --git a/config/locales/en.yml b/config/locales/en.yml index e3d76971b..fb2bbf4fe 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -60,6 +60,7 @@ en: destroyed_msg: Moderation note successfully destroyed! accounts: are_you_sure: Are you sure? + avatar: Avatar by_domain: Domain confirm: Confirm confirmed: Confirmed @@ -108,6 +109,7 @@ en: public: Public push_subscription_expires: PuSH subscription expires redownload: Refresh avatar + remove_avatar: Remove avatar reset: Reset reset_password: Reset password resubscribe: Resubscribe @@ -150,6 +152,7 @@ en: enable_user: "%{name} enabled login for user %{target}" memorialize_account: "%{name} turned %{target}'s account into a memoriam page" promote_user: "%{name} promoted user %{target}" + remove_avatar_user: "%{name} removed %{target}'s avatar" reset_password_user: "%{name} reset password of user %{target}" resolve_report: "%{name} dismissed report %{target}" silence_account: "%{name} silenced %{target}'s account" diff --git a/config/routes.rb b/config/routes.rb index 0542cb680..9a4460562 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -144,6 +144,7 @@ Rails.application.routes.draw do post :enable post :disable post :redownload + post :remove_avatar post :memorialize end From 2c51bc0ca5a4c3a4bb140b4b40dabdda859ebb94 Mon Sep 17 00:00:00 2001 From: unarist Date: Mon, 2 Apr 2018 21:51:02 +0900 Subject: [PATCH 032/442] Add missing rejection handling for Promises (#7008) * Add eslint-plugin-promise to detect uncaught rejections * Move alert generation for errors to actions/alert * Add missing rejection handling for Promises * Use catch() instead of onReject on then() Then it will catches rejection from onFulfilled. This detection can be disabled by `allowThen` option, though. --- .eslintrc.yml | 3 ++ app/javascript/mastodon/actions/accounts.js | 2 +- app/javascript/mastodon/actions/alerts.js | 25 ++++++++++++++++ app/javascript/mastodon/actions/compose.js | 7 ++++- app/javascript/mastodon/actions/lists.js | 3 +- .../actions/push_notifications/registerer.js | 15 ++++------ app/javascript/mastodon/actions/settings.js | 5 +++- .../mastodon/containers/status_container.js | 6 +++- .../features/ui/components/embed_modal.js | 3 ++ app/javascript/mastodon/middleware/errors.js | 24 ++------------- app/javascript/mastodon/storage/modifier.js | 30 ++++++++++++++----- package.json | 1 + yarn.lock | 4 +++ 13 files changed, 84 insertions(+), 44 deletions(-) diff --git a/.eslintrc.yml b/.eslintrc.yml index cf276a16f..576e5b70a 100644 --- a/.eslintrc.yml +++ b/.eslintrc.yml @@ -13,6 +13,7 @@ plugins: - react - jsx-a11y - import +- promise parserOptions: sourceType: module @@ -152,3 +153,5 @@ rules: - "app/javascript/**/__tests__/**" import/no-unresolved: error import/no-webpack-loader-syntax: error + + promise/catch-or-return: error diff --git a/app/javascript/mastodon/actions/accounts.js b/app/javascript/mastodon/actions/accounts.js index 7cacff909..28ae56763 100644 --- a/app/javascript/mastodon/actions/accounts.js +++ b/app/javascript/mastodon/actions/accounts.js @@ -103,7 +103,7 @@ export function fetchAccount(id) { dispatch(importFetchedAccount(response.data)); })).then(() => { dispatch(fetchAccountSuccess()); - }, error => { + }).catch(error => { dispatch(fetchAccountFail(id, error)); }); }; diff --git a/app/javascript/mastodon/actions/alerts.js b/app/javascript/mastodon/actions/alerts.js index f37fdeeb6..3f5d7ef46 100644 --- a/app/javascript/mastodon/actions/alerts.js +++ b/app/javascript/mastodon/actions/alerts.js @@ -1,3 +1,10 @@ +import { defineMessages } from 'react-intl'; + +const messages = defineMessages({ + unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' }, + unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' }, +}); + export const ALERT_SHOW = 'ALERT_SHOW'; export const ALERT_DISMISS = 'ALERT_DISMISS'; export const ALERT_CLEAR = 'ALERT_CLEAR'; @@ -22,3 +29,21 @@ export function showAlert(title, message) { message, }; }; + +export function showAlertForError(error) { + if (error.response) { + const { data, status, statusText } = error.response; + + let message = statusText; + let title = `${status}`; + + if (data.error) { + message = data.error; + } + + return showAlert(title, message); + } else { + console.error(error); + return showAlert(messages.unexpectedTitle, messages.unexpectedMessage); + } +} diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 2138f9426..59aa6f98d 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -1,11 +1,12 @@ import api from '../api'; -import { CancelToken } from 'axios'; +import { CancelToken, isCancel } from 'axios'; import { throttle } from 'lodash'; import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light'; import { tagHistory } from '../settings'; import { useEmoji } from './emojis'; import { importFetchedAccounts } from './importer'; import { updateTimeline } from './timelines'; +import { showAlertForError } from './alerts'; let cancelFetchComposeSuggestionsAccounts; @@ -291,6 +292,10 @@ const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => }).then(response => { dispatch(importFetchedAccounts(response.data)); dispatch(readyComposeSuggestionsAccounts(token, response.data)); + }).catch(error => { + if (!isCancel(error)) { + dispatch(showAlertForError(error)); + } }); }, 200, { leading: true, trailing: true }); diff --git a/app/javascript/mastodon/actions/lists.js b/app/javascript/mastodon/actions/lists.js index 12d60e3a3..12cb17159 100644 --- a/app/javascript/mastodon/actions/lists.js +++ b/app/javascript/mastodon/actions/lists.js @@ -1,5 +1,6 @@ import api from '../api'; import { importFetchedAccounts } from './importer'; +import { showAlertForError } from './alerts'; export const LIST_FETCH_REQUEST = 'LIST_FETCH_REQUEST'; export const LIST_FETCH_SUCCESS = 'LIST_FETCH_SUCCESS'; @@ -236,7 +237,7 @@ export const fetchListSuggestions = q => (dispatch, getState) => { api(getState).get('/api/v1/accounts/search', { params }).then(({ data }) => { dispatch(importFetchedAccounts(data)); dispatch(fetchListSuggestionsReady(q, data)); - }); + }).catch(error => dispatch(showAlertForError(error))); }; export const fetchListSuggestionsReady = (query, accounts) => ({ diff --git a/app/javascript/mastodon/actions/push_notifications/registerer.js b/app/javascript/mastodon/actions/push_notifications/registerer.js index 51e68cad1..f17d929a6 100644 --- a/app/javascript/mastodon/actions/push_notifications/registerer.js +++ b/app/javascript/mastodon/actions/push_notifications/registerer.js @@ -116,14 +116,11 @@ export function register () { pushNotificationsSetting.remove(me); } - try { - getRegistration() - .then(getPushSubscription) - .then(unsubscribe); - } catch (e) { - - } - }); + return getRegistration() + .then(getPushSubscription) + .then(unsubscribe); + }) + .catch(console.warn); } else { console.warn('Your browser does not support Web Push Notifications.'); } @@ -143,6 +140,6 @@ export function saveSettings() { if (me) { pushNotificationsSetting.set(me, data); } - }); + }).catch(console.warn); }; } diff --git a/app/javascript/mastodon/actions/settings.js b/app/javascript/mastodon/actions/settings.js index b96383daa..5634a11ef 100644 --- a/app/javascript/mastodon/actions/settings.js +++ b/app/javascript/mastodon/actions/settings.js @@ -1,5 +1,6 @@ import api from '../api'; import { debounce } from 'lodash'; +import { showAlertForError } from './alerts'; export const SETTING_CHANGE = 'SETTING_CHANGE'; export const SETTING_SAVE = 'SETTING_SAVE'; @@ -23,7 +24,9 @@ const debouncedSave = debounce((dispatch, getState) => { const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS(); - api(getState).put('/api/web/settings', { data }).then(() => dispatch({ type: SETTING_SAVE })); + api(getState).put('/api/web/settings', { data }) + .then(() => dispatch({ type: SETTING_SAVE })) + .catch(error => dispatch(showAlertForError(error))); }, 5000, { trailing: true }); export function saveSettings() { diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js index 8ba1015b5..4579bd132 100644 --- a/app/javascript/mastodon/containers/status_container.js +++ b/app/javascript/mastodon/containers/status_container.js @@ -27,6 +27,7 @@ import { initReport } from '../actions/reports'; import { openModal } from '../actions/modal'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { boostModal, deleteModal } from '../initial_state'; +import { showAlertForError } from '../actions/alerts'; const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, @@ -83,7 +84,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ }, onEmbed (status) { - dispatch(openModal('EMBED', { url: status.get('url') })); + dispatch(openModal('EMBED', { + url: status.get('url'), + onError: error => dispatch(showAlertForError(error)), + })); }, onDelete (status) { diff --git a/app/javascript/mastodon/features/ui/components/embed_modal.js b/app/javascript/mastodon/features/ui/components/embed_modal.js index d440a8826..52aab00d0 100644 --- a/app/javascript/mastodon/features/ui/components/embed_modal.js +++ b/app/javascript/mastodon/features/ui/components/embed_modal.js @@ -10,6 +10,7 @@ export default class EmbedModal extends ImmutablePureComponent { static propTypes = { url: PropTypes.string.isRequired, onClose: PropTypes.func.isRequired, + onError: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, } @@ -35,6 +36,8 @@ export default class EmbedModal extends ImmutablePureComponent { iframeDocument.body.style.margin = 0; this.iframe.width = iframeDocument.body.scrollWidth; this.iframe.height = iframeDocument.body.scrollHeight; + }).catch(error => { + this.props.onError(error); }); } diff --git a/app/javascript/mastodon/middleware/errors.js b/app/javascript/mastodon/middleware/errors.js index 72e5631e6..3cebb42e0 100644 --- a/app/javascript/mastodon/middleware/errors.js +++ b/app/javascript/mastodon/middleware/errors.js @@ -1,34 +1,14 @@ -import { defineMessages } from 'react-intl'; -import { showAlert } from '../actions/alerts'; +import { showAlertForError } from '../actions/alerts'; const defaultFailSuffix = 'FAIL'; -const messages = defineMessages({ - unexpectedTitle: { id: 'alert.unexpected.title', defaultMessage: 'Oops!' }, - unexpectedMessage: { id: 'alert.unexpected.message', defaultMessage: 'An unexpected error occurred.' }, -}); - export default function errorsMiddleware() { return ({ dispatch }) => next => action => { if (action.type && !action.skipAlert) { const isFail = new RegExp(`${defaultFailSuffix}$`, 'g'); if (action.type.match(isFail)) { - if (action.error.response) { - const { data, status, statusText } = action.error.response; - - let message = statusText; - let title = `${status}`; - - if (data.error) { - message = data.error; - } - - dispatch(showAlert(title, message)); - } else { - console.error(action.error); - dispatch(showAlert(messages.unexpectedTitle, messages.unexpectedMessage)); - } + dispatch(showAlertForError(action.error)); } } diff --git a/app/javascript/mastodon/storage/modifier.js b/app/javascript/mastodon/storage/modifier.js index 1bec04d0f..4773d07a9 100644 --- a/app/javascript/mastodon/storage/modifier.js +++ b/app/javascript/mastodon/storage/modifier.js @@ -9,6 +9,12 @@ const limit = 1024; // https://webkit.org/status/#specification-service-workers const asyncCache = window.caches ? caches.open('mastodon-system') : Promise.reject(); +function printErrorIfAvailable(error) { + if (error) { + console.warn(error); + } +} + function put(name, objects, onupdate, oncreate) { return asyncDB.then(db => new Promise((resolve, reject) => { const putTransaction = db.transaction(name, 'readwrite'); @@ -77,7 +83,9 @@ function evictAccountsByRecords(records) { function evict(toEvict) { toEvict.forEach(record => { - asyncCache.then(cache => accountAssetKeys.forEach(key => cache.delete(records[key]))); + asyncCache + .then(cache => accountAssetKeys.forEach(key => cache.delete(records[key]))) + .catch(printErrorIfAvailable); accountsMovedIndex.getAll(record.id).onsuccess = ({ target }) => evict(target.result); @@ -90,11 +98,11 @@ function evictAccountsByRecords(records) { } evict(records); - }); + }).catch(printErrorIfAvailable); } export function evictStatus(id) { - return evictStatuses([id]); + evictStatuses([id]); } export function evictStatuses(ids) { @@ -110,7 +118,7 @@ export function evictStatuses(ids) { idIndex.getKey(id).onsuccess = ({ target }) => target.result && store.delete(target.result); }); - }); + }).catch(printErrorIfAvailable); } function evictStatusesByRecords(records) { @@ -127,7 +135,9 @@ export function putAccounts(records) { const oldURL = target.result[key]; if (newURL !== oldURL) { - asyncCache.then(cache => cache.delete(oldURL)); + asyncCache + .then(cache => cache.delete(oldURL)) + .catch(printErrorIfAvailable); } }); @@ -145,10 +155,14 @@ export function putAccounts(records) { oncomplete(); }).then(records => { evictAccountsByRecords(records); - asyncCache.then(cache => cache.addAll(newURLs)); - }); + asyncCache + .then(cache => cache.addAll(newURLs)) + .catch(printErrorIfAvailable); + }).catch(printErrorIfAvailable); } export function putStatuses(records) { - put('statuses', records).then(evictStatusesByRecords); + put('statuses', records) + .then(evictStatusesByRecords) + .catch(printErrorIfAvailable); } diff --git a/package.json b/package.json index dfba49afc..d4de3a157 100644 --- a/package.json +++ b/package.json @@ -129,6 +129,7 @@ "eslint": "^4.15.0", "eslint-plugin-import": "^2.8.0", "eslint-plugin-jsx-a11y": "^5.1.1", + "eslint-plugin-promise": "^3.7.0", "eslint-plugin-react": "^7.5.1", "jest": "^21.2.1", "raf": "^3.4.0", diff --git a/yarn.lock b/yarn.lock index a306ebf55..866b24c7a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2433,6 +2433,10 @@ eslint-plugin-jsx-a11y@^5.1.1: emoji-regex "^6.1.0" jsx-ast-utils "^1.4.0" +eslint-plugin-promise@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.7.0.tgz#f4bde5c2c77cdd69557a8f69a24d1ad3cfc9e67e" + eslint-plugin-react@^7.5.1: version "7.5.1" resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.5.1.tgz#52e56e8d80c810de158859ef07b880d2f56ee30b" From 36eac8ba9011f225f7f949bbf1ca173832561f10 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 2 Apr 2018 19:19:51 +0200 Subject: [PATCH 033/442] Do not default SMTP verify mode to "peer", default to "none" (#6996) --- lib/tasks/mastodon.rake | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index cfd6a1d25..505c7e0fa 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -256,11 +256,7 @@ namespace :mastodon do q.modify :strip end - env['SMTP_OPENSSL_VERIFY_MODE'] = prompt.ask('SMTP OpenSSL verify mode:') do |q| - q.required - q.default 'peer' - q.modify :strip - end + env['SMTP_OPENSSL_VERIFY_MODE'] = prompt.select('SMTP OpenSSL verify mode:', %w(none peer client_once fail_if_no_peer_cert)) end env['SMTP_FROM_ADDRESS'] = prompt.ask('E-mail address to send e-mails "from":') do |q| From e85cffb2362f914c0f2f7ced4112430b30bc7997 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Mon, 2 Apr 2018 22:04:14 +0200 Subject: [PATCH 034/442] Feature: Report improvements (#6967) (#7000) * Implement Assignment of Reports (#6967) * Change translation of admin.report.comment.label to "Report Comment" for clarity As we'll soon add the ability for reports to have comments on them, this clarification makes sense. * Implement notes for Reports This enables moderators to leave comments about a report whilst they work on it * Fix display of report moderation notes * Allow reports to be reopened / marked as unresolved * Redirect to reports listing upon resolution of report * Implement "resolve with note" functionality * Add inverse relationship for report notes * Remove additional database querying when loading report notes * Fix tests for reports * Fix localisations for report notes / reports --- .../admin/report_notes_controller.rb | 49 +++++++++++ app/controllers/admin/reports_controller.rb | 20 ++++- app/helpers/admin/action_logs_helper.rb | 2 +- app/models/account.rb | 2 + app/models/report.rb | 4 + app/models/report_note.rb | 21 +++++ app/policies/report_note_policy.rb | 17 ++++ .../admin/report_notes/_report_note.html.haml | 11 +++ app/views/admin/reports/_report.html.haml | 5 ++ app/views/admin/reports/index.html.haml | 1 + app/views/admin/reports/show.html.haml | 84 +++++++++++++++---- config/locales/en.yml | 22 ++++- config/routes.rb | 2 + ...1200_add_assigned_account_id_to_reports.rb | 5 ++ .../20180402040909_create_report_notes.rb | 14 ++++ db/schema.rb | 16 +++- .../admin/reports_controller_spec.rb | 40 ++++++++- 17 files changed, 290 insertions(+), 25 deletions(-) create mode 100644 app/controllers/admin/report_notes_controller.rb create mode 100644 app/models/report_note.rb create mode 100644 app/policies/report_note_policy.rb create mode 100644 app/views/admin/report_notes/_report_note.html.haml create mode 100644 db/migrate/20180402031200_add_assigned_account_id_to_reports.rb create mode 100644 db/migrate/20180402040909_create_report_notes.rb diff --git a/app/controllers/admin/report_notes_controller.rb b/app/controllers/admin/report_notes_controller.rb new file mode 100644 index 000000000..ef8c0f469 --- /dev/null +++ b/app/controllers/admin/report_notes_controller.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Admin + class ReportNotesController < BaseController + before_action :set_report_note, only: [:destroy] + + def create + authorize ReportNote, :create? + + @report_note = current_account.report_notes.new(resource_params) + + if @report_note.save + if params[:create_and_resolve] + @report_note.report.update!(action_taken: true, action_taken_by_account_id: current_account.id) + log_action :resolve, @report_note.report + + redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg') + else + redirect_to admin_report_path(@report_note.report_id), notice: I18n.t('admin.report_notes.created_msg') + end + else + @report = @report_note.report + @report_notes = @report.notes.latest + @form = Form::StatusBatch.new + + render template: 'admin/reports/show' + end + end + + def destroy + authorize @report_note, :destroy? + @report_note.destroy! + redirect_to admin_report_path(@report_note.report_id), notice: I18n.t('admin.report_notes.destroyed_msg') + end + + private + + def resource_params + params.require(:report_note).permit( + :content, + :report_id + ) + end + + def set_report_note + @report_note = ReportNote.find(params[:id]) + end + end +end diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index 75db6b78a..fc3785e3b 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -11,19 +11,35 @@ module Admin def show authorize @report, :show? + @report_note = @report.notes.new + @report_notes = @report.notes.latest @form = Form::StatusBatch.new end def update authorize @report, :update? process_report - redirect_to admin_report_path(@report) + + if @report.action_taken? + redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg') + else + redirect_to admin_report_path(@report) + end end private def process_report case params[:outcome].to_s + when 'assign_to_self' + @report.update!(assigned_account_id: current_account.id) + log_action :assigned_to_self, @report + when 'unassign' + @report.update!(assigned_account_id: nil) + log_action :unassigned, @report + when 'reopen' + @report.update!(action_taken: false, action_taken_by_account_id: nil) + log_action :reopen, @report when 'resolve' @report.update!(action_taken_by_current_attributes) log_action :resolve, @report @@ -32,11 +48,13 @@ module Admin log_action :resolve, @report log_action :suspend, @report.target_account resolve_all_target_account_reports + @report.reload when 'silence' @report.target_account.update!(silenced: true) log_action :resolve, @report log_action :silence, @report.target_account resolve_all_target_account_reports + @report.reload else raise ActiveRecord::RecordNotFound end diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb index 78278c700..7c26c0b05 100644 --- a/app/helpers/admin/action_logs_helper.rb +++ b/app/helpers/admin/action_logs_helper.rb @@ -86,7 +86,7 @@ module Admin::ActionLogsHelper opposite_verbs?(log) ? 'negative' : 'positive' when :update, :reset_password, :disable_2fa, :memorialize 'neutral' - when :demote, :silence, :disable, :suspend, :remove_avatar + when :demote, :silence, :disable, :suspend, :remove_avatar, :reopen 'negative' when :destroy opposite_verbs?(log) ? 'positive' : 'negative' diff --git a/app/models/account.rb b/app/models/account.rb index a34b6a2d3..446144a3e 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -95,6 +95,8 @@ class Account < ApplicationRecord has_many :reports has_many :targeted_reports, class_name: 'Report', foreign_key: :target_account_id + has_many :report_notes, dependent: :destroy + # Moderation notes has_many :account_moderation_notes, dependent: :destroy has_many :targeted_moderation_notes, class_name: 'AccountModerationNote', foreign_key: :target_account_id, dependent: :destroy diff --git a/app/models/report.rb b/app/models/report.rb index dd123fc15..f5b37cb6d 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -12,12 +12,16 @@ # account_id :integer not null # action_taken_by_account_id :integer # target_account_id :integer not null +# assigned_account_id :integer # class Report < ApplicationRecord belongs_to :account belongs_to :target_account, class_name: 'Account' belongs_to :action_taken_by_account, class_name: 'Account', optional: true + belongs_to :assigned_account, class_name: 'Account', optional: true + + has_many :notes, class_name: 'ReportNote', foreign_key: :report_id, inverse_of: :report, dependent: :destroy scope :unresolved, -> { where(action_taken: false) } scope :resolved, -> { where(action_taken: true) } diff --git a/app/models/report_note.rb b/app/models/report_note.rb new file mode 100644 index 000000000..3d12cf7b6 --- /dev/null +++ b/app/models/report_note.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true +# == Schema Information +# +# Table name: report_notes +# +# id :integer not null, primary key +# content :text not null +# report_id :integer not null +# account_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class ReportNote < ApplicationRecord + belongs_to :account + belongs_to :report, inverse_of: :notes + + scope :latest, -> { reorder('created_at ASC') } + + validates :content, presence: true, length: { maximum: 500 } +end diff --git a/app/policies/report_note_policy.rb b/app/policies/report_note_policy.rb new file mode 100644 index 000000000..694bc096b --- /dev/null +++ b/app/policies/report_note_policy.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class ReportNotePolicy < ApplicationPolicy + def create? + staff? + end + + def destroy? + admin? || owner? + end + + private + + def owner? + record.account_id == current_account&.id + end +end diff --git a/app/views/admin/report_notes/_report_note.html.haml b/app/views/admin/report_notes/_report_note.html.haml new file mode 100644 index 000000000..60ac5d0d5 --- /dev/null +++ b/app/views/admin/report_notes/_report_note.html.haml @@ -0,0 +1,11 @@ +%tr + %td + %p + %strong= report_note.account.acct + on + %time.formatted{ datetime: report_note.created_at.iso8601, title: l(report_note.created_at) } + = l report_note.created_at + = table_link_to 'trash', t('admin.reports.notes.delete'), admin_report_note_path(report_note), method: :delete if can?(:destroy, report_note) + %br/ + %br/ + = simple_format(h(report_note.content)) diff --git a/app/views/admin/reports/_report.html.haml b/app/views/admin/reports/_report.html.haml index d5eb161b9..d266f4840 100644 --- a/app/views/admin/reports/_report.html.haml +++ b/app/views/admin/reports/_report.html.haml @@ -17,5 +17,10 @@ %span{ title: t('admin.accounts.media_attachments') } = fa_icon('camera') = report.media_attachments.count + %td + - if report.assigned_account.nil? + \- + - else + = link_to report.assigned_account.acct, admin_account_path(report.assigned_account.id) %td = table_link_to 'circle', t('admin.reports.view'), admin_report_path(report) diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index 577c68a86..3b127c4fc 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -20,6 +20,7 @@ %th= t('admin.reports.reported_by') %th= t('admin.reports.comment.label') %th= t('admin.reports.report_contents') + %th= t('admin.reports.assigned') %th %tbody = render @reports diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 5747cc274..e7634a034 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -4,24 +4,68 @@ - content_for :page_title do = t('admin.reports.report', id: @report.id) +%div{ style: 'overflow: hidden; margin-bottom: 20px' } + - if !@report.action_taken? + %div{ style: 'float: right' } + = link_to t('admin.reports.silence_account'), admin_report_path(@report, outcome: 'silence'), method: :put, class: 'button' + = link_to t('admin.reports.suspend_account'), admin_report_path(@report, outcome: 'suspend'), method: :put, class: 'button' + %div{ style: 'float: left' } + = link_to t('admin.reports.mark_as_resolved'), admin_report_path(@report, outcome: 'resolve'), method: :put, class: 'button' + - else + = link_to t('admin.reports.mark_as_unresolved'), admin_report_path(@report, outcome: 'reopen'), method: :put, class: 'button' + +.table-wrapper + %table.table.inline-table + %tbody + %tr + %th= t('admin.reports.updated_at') + %td{colspan: 2} + %time.formatted{ datetime: @report.updated_at.iso8601 } + %tr + %th= t('admin.reports.status') + %td{colspan: 2} + - if @report.action_taken? + = t('admin.reports.resolved') + = table_link_to 'envelope-open', t('admin.reports.reopen'), admin_report_path(@report, outcome: 'reopen'), method: :put + - else + = t('admin.reports.unresolved') + - if !@report.action_taken_by_account.nil? + %tr + %th= t('admin.reports.action_taken_by') + %td= @report.action_taken_by_account.acct + - else + %tr + %th= t('admin.reports.assigned') + %td + - if @report.assigned_account.nil? + \- + - else + = link_to @report.assigned_account.acct, admin_account_path(@report.assigned_account.id) + %td{style: "text-align: right"} + - if @report.assigned_account != current_user.account + = table_link_to 'user', t('admin.reports.assign_to_self'), admin_report_path(@report, outcome: 'assign_to_self'), method: :put + - if !@report.assigned_account.nil? + = table_link_to 'trash', t('admin.reports.unassign'), admin_report_path(@report, outcome: 'unassign'), method: :put + .report-accounts .report-accounts__item - %strong= t('admin.reports.reported_account') + %h3= t('admin.reports.reported_account') = render 'authorize_follows/card', account: @report.target_account, admin: true = render 'admin/accounts/card', account: @report.target_account .report-accounts__item - %strong= t('admin.reports.reported_by') + %h3= t('admin.reports.reported_by') = render 'authorize_follows/card', account: @report.account, admin: true = render 'admin/accounts/card', account: @report.account -%p - %strong= t('admin.reports.comment.label') - \: - = simple_format(@report.comment.presence || t('admin.reports.comment.none')) +%h3= t('admin.reports.comment.label') + += simple_format(@report.comment.presence || t('admin.reports.comment.none')) - unless @report.statuses.empty? %hr/ + %h3= t('admin.reports.statuses') + = form_for(@form, url: admin_report_reported_statuses_path(@report.id)) do |f| .batch-form-box .batch-checkbox-all @@ -46,14 +90,20 @@ %hr/ -- if !@report.action_taken? - %div{ style: 'overflow: hidden' } - %div{ style: 'float: right' } - = link_to t('admin.reports.silence_account'), admin_report_path(@report, outcome: 'silence'), method: :put, class: 'button' - = link_to t('admin.reports.suspend_account'), admin_report_path(@report, outcome: 'suspend'), method: :put, class: 'button' - %div{ style: 'float: left' } - = link_to t('admin.reports.mark_as_resolved'), admin_report_path(@report, outcome: 'resolve'), method: :put, class: 'button' -- elsif !@report.action_taken_by_account.nil? - %p - %strong #{t('admin.reports.action_taken_by')}: - = @report.action_taken_by_account.acct +%h3= t('admin.reports.notes.label') + +- if @report_notes.length > 0 + .table-wrapper + %table.table + %thead + %tr + %th + %tbody + = render @report_notes + += simple_form_for @report_note, url: admin_report_notes_path do |f| + = render 'shared/error_messages', object: @report_note + = f.input :content + = f.hidden_field :report_id + = f.button :button, t('admin.reports.notes.create'), type: :submit + = f.button :button, t('admin.reports.notes.create_and_resolve'), type: :submit, name: :create_and_resolve diff --git a/config/locales/en.yml b/config/locales/en.yml index fb2bbf4fe..51d9c906d 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -137,6 +137,7 @@ en: web: Web action_logs: actions: + assigned_to_self_report: "%{name} assigned report %{target} to themselves" confirm_user: "%{name} confirmed e-mail address of user %{target}" create_custom_emoji: "%{name} uploaded new emoji %{target}" create_domain_block: "%{name} blocked domain %{target}" @@ -153,10 +154,12 @@ en: memorialize_account: "%{name} turned %{target}'s account into a memoriam page" promote_user: "%{name} promoted user %{target}" remove_avatar_user: "%{name} removed %{target}'s avatar" + reopen_report: "%{name} reopened report %{target}" reset_password_user: "%{name} reset password of user %{target}" - resolve_report: "%{name} dismissed report %{target}" + resolve_report: "%{name} resolved report %{target}" silence_account: "%{name} silenced %{target}'s account" suspend_account: "%{name} suspended %{target}'s account" + unassigned_report: "%{name} unassigned report %{target}" unsilence_account: "%{name} unsilenced %{target}'s account" unsuspend_account: "%{name} unsuspended %{target}'s account" update_custom_emoji: "%{name} updated emoji %{target}" @@ -242,15 +245,26 @@ en: expired: Expired title: Filter title: Invites + report_notes: + created_msg: Moderation note successfully created! + destroyed_msg: Moderation note successfully destroyed! reports: action_taken_by: Action taken by are_you_sure: Are you sure? + assign_to_self: Assign to me + assigned: Assigned Moderator comment: - label: Comment + label: Report Comment none: None delete: Delete id: ID mark_as_resolved: Mark as resolved + mark_as_unresolved: Mark as unresolved + notes: + create: Add Note + create_and_resolve: Resolve with Note + delete: Delete + label: Notes nsfw: 'false': Unhide media attachments 'true': Hide media attachments @@ -259,12 +273,16 @@ en: reported_account: Reported account reported_by: Reported by resolved: Resolved + resolved_msg: Report successfully resolved! silence_account: Silence account status: Status + statuses: Reported Toots suspend_account: Suspend account target: Target title: Reports + unassign: Unassign unresolved: Unresolved + updated_at: Updated view: View settings: activity_api_enabled: diff --git a/config/routes.rb b/config/routes.rb index 9a4460562..4b5ba5c96 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -137,6 +137,8 @@ Rails.application.routes.draw do resources :reported_statuses, only: [:create, :update, :destroy] end + resources :report_notes, only: [:create, :destroy] + resources :accounts, only: [:index, :show] do member do post :subscribe diff --git a/db/migrate/20180402031200_add_assigned_account_id_to_reports.rb b/db/migrate/20180402031200_add_assigned_account_id_to_reports.rb new file mode 100644 index 000000000..0456839c4 --- /dev/null +++ b/db/migrate/20180402031200_add_assigned_account_id_to_reports.rb @@ -0,0 +1,5 @@ +class AddAssignedAccountIdToReports < ActiveRecord::Migration[5.1] + def change + add_reference :reports, :assigned_account, null: true, default: nil, foreign_key: { on_delete: :nullify, to_table: :accounts }, index: false + end +end diff --git a/db/migrate/20180402040909_create_report_notes.rb b/db/migrate/20180402040909_create_report_notes.rb new file mode 100644 index 000000000..732ddf825 --- /dev/null +++ b/db/migrate/20180402040909_create_report_notes.rb @@ -0,0 +1,14 @@ +class CreateReportNotes < ActiveRecord::Migration[5.1] + def change + create_table :report_notes do |t| + t.text :content, null: false + t.references :report, null: false + t.references :account, null: false + + t.timestamps + end + + add_foreign_key :report_notes, :reports, column: :report_id, on_delete: :cascade + add_foreign_key :report_notes, :accounts, column: :account_id, on_delete: :cascade + end +end diff --git a/db/schema.rb b/db/schema.rb index 18c61dbe0..a9733a2ae 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180310000000) do +ActiveRecord::Schema.define(version: 20180402040909) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -355,6 +355,16 @@ ActiveRecord::Schema.define(version: 20180310000000) do t.index ["status_id", "preview_card_id"], name: "index_preview_cards_statuses_on_status_id_and_preview_card_id" end + create_table "report_notes", force: :cascade do |t| + t.text "content", null: false + t.bigint "report_id", null: false + t.bigint "account_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["account_id"], name: "index_report_notes_on_account_id" + t.index ["report_id"], name: "index_report_notes_on_report_id" + end + create_table "reports", force: :cascade do |t| t.bigint "status_ids", default: [], null: false, array: true t.text "comment", default: "", null: false @@ -364,6 +374,7 @@ ActiveRecord::Schema.define(version: 20180310000000) do t.bigint "account_id", null: false t.bigint "action_taken_by_account_id" t.bigint "target_account_id", null: false + t.bigint "assigned_account_id" t.index ["account_id"], name: "index_reports_on_account_id" t.index ["target_account_id"], name: "index_reports_on_target_account_id" end @@ -569,7 +580,10 @@ ActiveRecord::Schema.define(version: 20180310000000) do add_foreign_key "oauth_access_tokens", "oauth_applications", column: "application_id", name: "fk_f5fc4c1ee3", on_delete: :cascade add_foreign_key "oauth_access_tokens", "users", column: "resource_owner_id", name: "fk_e84df68546", on_delete: :cascade add_foreign_key "oauth_applications", "users", column: "owner_id", name: "fk_b0988c7c0a", on_delete: :cascade + add_foreign_key "report_notes", "accounts", on_delete: :cascade + add_foreign_key "report_notes", "reports", on_delete: :cascade add_foreign_key "reports", "accounts", column: "action_taken_by_account_id", name: "fk_bca45b75fd", on_delete: :nullify + add_foreign_key "reports", "accounts", column: "assigned_account_id", on_delete: :nullify add_foreign_key "reports", "accounts", column: "target_account_id", name: "fk_eb37af34f0", on_delete: :cascade add_foreign_key "reports", "accounts", name: "fk_4b81f7522c", on_delete: :cascade add_foreign_key "session_activations", "oauth_access_tokens", column: "access_token_id", name: "fk_957e5bda89", on_delete: :cascade diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index 71a185147..9be298df6 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -61,7 +61,7 @@ describe Admin::ReportsController do report = Fabricate(:report) put :update, params: { id: report, outcome: 'resolve' } - expect(response).to redirect_to(admin_report_path(report)) + expect(response).to redirect_to(admin_reports_path) report.reload expect(report.action_taken_by_account).to eq user.account expect(report.action_taken).to eq true @@ -74,7 +74,7 @@ describe Admin::ReportsController do allow(Admin::SuspensionWorker).to receive(:perform_async) put :update, params: { id: report, outcome: 'suspend' } - expect(response).to redirect_to(admin_report_path(report)) + expect(response).to redirect_to(admin_reports_path) report.reload expect(report.action_taken_by_account).to eq user.account expect(report.action_taken).to eq true @@ -88,12 +88,46 @@ describe Admin::ReportsController do report = Fabricate(:report) put :update, params: { id: report, outcome: 'silence' } - expect(response).to redirect_to(admin_report_path(report)) + expect(response).to redirect_to(admin_reports_path) report.reload expect(report.action_taken_by_account).to eq user.account expect(report.action_taken).to eq true expect(report.target_account).to be_silenced end end + + describe 'with an outsome of `reopen`' do + it 'reopens the report' do + report = Fabricate(:report) + + put :update, params: { id: report, outcome: 'reopen' } + expect(response).to redirect_to(admin_report_path(report)) + report.reload + expect(report.action_taken_by_account).to eq nil + expect(report.action_taken).to eq false + end + end + + describe 'with an outsome of `assign_to_self`' do + it 'reopens the report' do + report = Fabricate(:report) + + put :update, params: { id: report, outcome: 'assign_to_self' } + expect(response).to redirect_to(admin_report_path(report)) + report.reload + expect(report.assigned_account).to eq user.account + end + end + + describe 'with an outsome of `unassign`' do + it 'reopens the report' do + report = Fabricate(:report) + + put :update, params: { id: report, outcome: 'unassign' } + expect(response).to redirect_to(admin_report_path(report)) + report.reload + expect(report.assigned_account).to eq nil + end + end end end From 1c293086a16fce465d5bdc123809f2d28b3e2ab6 Mon Sep 17 00:00:00 2001 From: mayaeh Date: Tue, 3 Apr 2018 18:21:33 +0900 Subject: [PATCH 035/442] i18n: Add Japanese translations for #7000 (#7022) * run yarn manage:translations. * run i18n-tasks add-missing ja. * Update Japanese translations. --- .../mastodon/locales/defaultMessages.json | 13 +++++++++++ config/locales/ja.yml | 23 +++++++++++++++++++ 2 files changed, 36 insertions(+) diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 4c9401deb..5059fc67b 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -1,4 +1,17 @@ [ + { + "descriptors": [ + { + "defaultMessage": "Oops!", + "id": "alert.unexpected.title" + }, + { + "defaultMessage": "An unexpected error occurred.", + "id": "alert.unexpected.message" + } + ], + "path": "app/javascript/mastodon/actions/alerts.json" + }, { "descriptors": [ { diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 090b080e4..629d688c9 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -60,6 +60,7 @@ ja: destroyed_msg: モデレーションメモを削除しました! accounts: are_you_sure: 本当に実行しますか? + avatar: アイコン by_domain: ドメイン confirm: 確認 confirmed: 確認済み @@ -108,6 +109,7 @@ ja: public: パブリック push_subscription_expires: PuSH購読期限 redownload: アバターの更新 + remove_avatar: アイコンを削除 reset: リセット reset_password: パスワード再設定 resubscribe: 再講読 @@ -135,6 +137,7 @@ ja: web: Web action_logs: actions: + assigned_to_self_report: "%{name} さんがレポート %{target} を自身の担当に割り当てました" confirm_user: "%{name} さんが %{target} さんのメールアドレスを確認済みにしました" create_custom_emoji: "%{name} さんがカスタム絵文字 %{target} を追加しました" create_domain_block: "%{name} さんがドメイン %{target} をブロックしました" @@ -150,10 +153,13 @@ ja: enable_user: "%{name} さんが %{target} さんのログインを有効化しました" memorialize_account: "%{name} さんが %{target} さんを追悼アカウントページに登録しました" promote_user: "%{name} さんが %{target} さんを昇格しました" + remove_avatar_user: "%{name} さんが %{target} さんのアイコンを削除しました" + reopen_report: "%{name} さんがレポート %{target} を再び開きました" reset_password_user: "%{name} さんが %{target} さんのパスワードをリセットしました" resolve_report: "%{name} さんがレポート %{target} を解決済みにしました" silence_account: "%{name} さんが %{target} さんをサイレンスにしました" suspend_account: "%{name} さんが %{target} さんを停止しました" + unassigned_report: "%{name} さんがレポート %{target} の担当を外しました" unsilence_account: "%{name} さんが %{target} さんのサイレンスを解除しました" unsuspend_account: "%{name} さんが %{target} さんの停止を解除しました" update_custom_emoji: "%{name} さんがカスタム絵文字 %{target} を更新しました" @@ -239,29 +245,45 @@ ja: expired: 期限切れ title: フィルター title: 招待 + report_notes: + created_msg: モデレーションメモを書き込みました! + destroyed_msg: モデレーションメモを削除しました! reports: action_taken_by: レポート処理者 are_you_sure: 本当に実行しますか? + assign_to_self: 担当になる + assigned: 担当者 comment: label: コメント none: なし delete: 削除 id: ID mark_as_resolved: 解決済みとしてマーク + mark_as_unresolved: 未解決として再び開く + notes: + create: 書き込む + create_and_resolve: 書き込み、解決済みにする + delete: 削除 + label: メモ nsfw: 'false': NSFW オフ 'true': NSFW オン + reopen: 再び開く report: レポート#%{id} report_contents: 内容 reported_account: 報告対象アカウント reported_by: 報告者 resolved: 解決済み + resolved_msg: レポートを解決済みにしました! silence_account: アカウントをサイレンス status: ステータス + statuses: 通報されたトゥート suspend_account: アカウントを停止 target: ターゲット title: レポート + unassign: 担当を外す unresolved: 未解決 + updated_at: 更新日時 view: 表示 settings: activity_api_enabled: @@ -475,6 +497,7 @@ ja: '21600': 6 時間 '3600': 1 時間 '43200': 12 時間 + '604800': 1 週間 '86400': 1 日 expires_in_prompt: 無期限 generate: 作成 From 2e59751823585a8ef8729d4287239b326ab02193 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Tue, 3 Apr 2018 13:07:32 +0200 Subject: [PATCH 036/442] Improve require_admin! and require_staff! filters (#7018) Previously these returns 302 redirects instead of 403s, which meant posting links to admin pages in slack caused them to unfurl, rather than stay as a link. Additionally, require_admin! doesn't appear to be actively used, on require_staff! --- app/controllers/application_controller.rb | 4 +- .../controllers/admin/base_controller_spec.rb | 25 +++++++---- .../application_controller_spec.rb | 42 ++++++++++++++++++- 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6e5042617..588526447 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -39,11 +39,11 @@ class ApplicationController < ActionController::Base end def require_admin! - redirect_to root_path unless current_user&.admin? + forbidden unless current_user&.admin? end def require_staff! - redirect_to root_path unless current_user&.staff? + forbidden unless current_user&.staff? end def check_suspension diff --git a/spec/controllers/admin/base_controller_spec.rb b/spec/controllers/admin/base_controller_spec.rb index 2b60e7e92..9ac833623 100644 --- a/spec/controllers/admin/base_controller_spec.rb +++ b/spec/controllers/admin/base_controller_spec.rb @@ -9,18 +9,25 @@ describe Admin::BaseController, type: :controller do end end - it 'renders admin layout' do + it 'requires administrator or moderator' do + routes.draw { get 'success' => 'admin/base#success' } + sign_in(Fabricate(:user, admin: false, moderator: false)) + get :success + + expect(response).to have_http_status(:forbidden) + end + + it 'renders admin layout as a moderator' do + routes.draw { get 'success' => 'admin/base#success' } + sign_in(Fabricate(:user, moderator: true)) + get :success + expect(response).to render_template layout: 'admin' + end + + it 'renders admin layout as an admin' do routes.draw { get 'success' => 'admin/base#success' } sign_in(Fabricate(:user, admin: true)) get :success expect(response).to render_template layout: 'admin' end - - it 'requires administrator' do - routes.draw { get 'success' => 'admin/base#success' } - sign_in(Fabricate(:user, admin: false)) - get :success - - expect(response).to redirect_to('/') - end end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 51c0e3c70..3e4d27e05 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -181,10 +181,48 @@ describe ApplicationController, type: :controller do routes.draw { get 'sucesss' => 'anonymous#sucesss' } end - it 'redirects to root path if current user is not admin' do + it 'returns a 403 if current user is not admin' do sign_in(Fabricate(:user, admin: false)) get 'sucesss' - expect(response).to redirect_to('/') + expect(response).to have_http_status(403) + end + + it 'returns a 403 if current user is only a moderator' do + sign_in(Fabricate(:user, moderator: true)) + get 'sucesss' + expect(response).to have_http_status(403) + end + + it 'does nothing if current user is admin' do + sign_in(Fabricate(:user, admin: true)) + get 'sucesss' + expect(response).to have_http_status(200) + end + end + + describe 'require_staff!' do + controller do + before_action :require_staff! + + def sucesss + head 200 + end + end + + before do + routes.draw { get 'sucesss' => 'anonymous#sucesss' } + end + + it 'returns a 403 if current user is not admin or moderator' do + sign_in(Fabricate(:user, admin: false, moderator: false)) + get 'sucesss' + expect(response).to have_http_status(403) + end + + it 'does nothing if current user is moderator' do + sign_in(Fabricate(:user, moderator: true)) + get 'sucesss' + expect(response).to have_http_status(200) end it 'does nothing if current user is admin' do From 6ff3b3e4db09c8ddf2faad6710e4a25f988e762d Mon Sep 17 00:00:00 2001 From: Nolan Lawson Date: Tue, 3 Apr 2018 04:08:11 -0700 Subject: [PATCH 037/442] Fix nil account issue in ProcessAccountService (#7019) --- app/services/activitypub/process_account_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index cf8462821..21c2fc57a 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -28,7 +28,7 @@ class ActivityPub::ProcessAccountService < BaseService after_protocol_change! if protocol_changed? after_key_change! if key_changed? - check_featured_collection! if @account.featured_collection_url.present? + check_featured_collection! if @account&.featured_collection_url&.present? @account rescue Oj::ParseError From d8d42179590db772cc5b1873385cba7e5afe20df Mon Sep 17 00:00:00 2001 From: ThibG Date: Tue, 3 Apr 2018 21:09:14 +0200 Subject: [PATCH 038/442] Fix french wording of reblogged toots in public views, matching the wording of reblogged_by (#7029) --- config/locales/fr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 6137e1bd4..d7371dc94 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -661,7 +661,7 @@ fr: stream_entries: click_to_show: Cliquer pour afficher pinned: Pouet épinglé - reblogged: partagé + reblogged: a partagé sensitive_content: Contenu sensible terms: body_html: | From 07176fed374b0ef94aacd1f3ea95b6dec2eb79dd Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 4 Apr 2018 01:11:26 +0200 Subject: [PATCH 039/442] Add contact account to landing page ("Administered by") (#6984) --- app/javascript/styles/mastodon/about.scss | 48 +++++++++++++++++++++++ app/views/about/show.html.haml | 22 +++++++++++ config/locales/en.yml | 1 + spec/views/about/show.html.haml_spec.rb | 1 + 4 files changed, 72 insertions(+) diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss index c484f074b..03211036c 100644 --- a/app/javascript/styles/mastodon/about.scss +++ b/app/javascript/styles/mastodon/about.scss @@ -681,6 +681,54 @@ $small-breakpoint: 960px; margin-bottom: 0; } + .account { + border-bottom: 0; + padding: 0; + + &__display-name { + align-items: center; + display: flex; + margin-right: 5px; + } + + div.account__display-name { + &:hover { + .display-name strong { + text-decoration: none; + } + } + + .account__avatar { + cursor: default; + } + } + + &__avatar-wrapper { + margin-left: 0; + flex: 0 0 auto; + } + + &__avatar { + width: 44px; + height: 44px; + background-size: 44px 44px; + } + + .display-name { + font-size: 15px; + + &__account { + font-size: 14px; + } + } + } + + @media screen and (max-width: $small-breakpoint) { + .contact { + margin-top: 30px; + } + } + @media screen and (max-width: $column-breakpoint) { padding: 25px 20px; } diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml index 85e5442af..12213cda2 100644 --- a/app/views/about/show.html.haml +++ b/app/views/about/show.html.haml @@ -108,6 +108,28 @@ %div %h3= t 'about.what_is_mastodon' %p= t 'about.about_mastodon_html' + %div.contact + %h3= t 'about.administered_by' + + .account + .account__wrapper + - if @instance_presenter.contact_account + = link_to TagManager.instance.url_for(@instance_presenter.contact_account), class: 'account__display-name' do + .account__avatar-wrapper + .account__avatar{ style: "background-image: url(#{@instance_presenter.contact_account.avatar.url})" } + %span.display-name + %bdi + %strong.display-name__html.emojify= display_name(@instance_presenter.contact_account) + %span.display-name__account @#{@instance_presenter.contact_account.acct} + - else + .account__display-name + .account__avatar-wrapper + .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})" } + %span.display-name + %strong= t 'about.contact_missing' + %span.display-name__account= t 'about.contact_unavailable' + + = link_to t('about.learn_more'), about_more_path, class: 'button button-alternative' = render 'features' diff --git a/config/locales/en.yml b/config/locales/en.yml index 51d9c906d..945faa1e2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -4,6 +4,7 @@ en: about_hashtag_html: These are public toots tagged with #%{hashtag}. You can interact with them if you have an account anywhere in the fediverse. about_mastodon_html: Mastodon is a social network based on open web protocols and free, open-source software. It is decentralized like e-mail. about_this: About + administered_by: 'Administered by:' closed_registrations: Registrations are currently closed on this instance. However! You can find a different instance to make an account on and get access to the very same network from there. contact: Contact contact_missing: Not set diff --git a/spec/views/about/show.html.haml_spec.rb b/spec/views/about/show.html.haml_spec.rb index 03d6fb7ab..cbe5aa93b 100644 --- a/spec/views/about/show.html.haml_spec.rb +++ b/spec/views/about/show.html.haml_spec.rb @@ -19,6 +19,7 @@ describe 'about/show.html.haml', without_verify_partial_doubles: true do hero: nil, user_count: 0, status_count: 0, + contact_account: nil, closed_registrations_message: 'yes') assign(:instance_presenter, instance_presenter) render From 7a810827040f31fb3936047f444efb77b743ed85 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Wed, 4 Apr 2018 11:58:15 +0200 Subject: [PATCH 040/442] Revert "Add double-tap zoom functionary to `ZoomableImage` (#6944)" (#7035) Unfortunately the new hammer.js functionality wasn't correctly tested and didn't work across devices and browsers, as such, it's best to revert PR #6944 until we can revisit this functionality and make it work across all devices and browsers that are supported by Mastodon. This reverts commit 5021c4e9ca78881f5379a18185a46e580b8f2c34. --- .../features/ui/components/zoomable_image.js | 167 ++++++++---------- .../styles/mastodon/components.scss | 3 + package.json | 1 - yarn.lock | 4 - 4 files changed, 76 insertions(+), 99 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/zoomable_image.js b/app/javascript/mastodon/features/ui/components/zoomable_image.js index 0cae0862d..0a0a4d41a 100644 --- a/app/javascript/mastodon/features/ui/components/zoomable_image.js +++ b/app/javascript/mastodon/features/ui/components/zoomable_image.js @@ -1,10 +1,16 @@ import React from 'react'; import PropTypes from 'prop-types'; -import Hammer from 'hammerjs'; const MIN_SCALE = 1; const MAX_SCALE = 4; -const DOUBLE_TAP_SCALE = 2; + +const getMidpoint = (p1, p2) => ({ + x: (p1.clientX + p2.clientX) / 2, + y: (p1.clientY + p2.clientY) / 2, +}); + +const getDistance = (p1, p2) => + Math.sqrt(Math.pow(p1.clientX - p2.clientX, 2) + Math.pow(p1.clientY - p2.clientY, 2)); const clamp = (min, max, value) => Math.min(max, Math.max(min, value)); @@ -31,97 +37,83 @@ export default class ZoomableImage extends React.PureComponent { removers = []; container = null; image = null; - lastScale = null; - zoomCenter = null; + lastTouchEndTime = 0; + lastDistance = 0; componentDidMount () { - // register pinch event handlers to the container - let hammer = new Hammer.Manager(this.container, { - // required to make container scrollable by touch - touchAction: 'pan-x pan-y', - }); - hammer.add(new Hammer.Pinch()); - hammer.on('pinchstart', this.handlePinchStart); - hammer.on('pinchmove', this.handlePinchMove); - this.removers.push(() => hammer.off('pinchstart pinchmove')); - - // register tap event handlers - hammer = new Hammer.Manager(this.image); - // NOTE the order of adding is also the order of gesture recognition - hammer.add(new Hammer.Tap({ event: 'doubletap', taps: 2 })); - hammer.add(new Hammer.Tap()); - // prevent the 'tap' event handler be fired on double tap - hammer.get('tap').requireFailure('doubletap'); - // NOTE 'tap' and 'doubletap' events are fired by touch and *mouse* - hammer.on('tap', this.handleTap); - hammer.on('doubletap', this.handleDoubleTap); - this.removers.push(() => hammer.off('tap doubletap')); + let handler = this.handleTouchStart; + this.container.addEventListener('touchstart', handler); + this.removers.push(() => this.container.removeEventListener('touchstart', handler)); + handler = this.handleTouchMove; + // on Chrome 56+, touch event listeners will default to passive + // https://www.chromestatus.com/features/5093566007214080 + this.container.addEventListener('touchmove', handler, { passive: false }); + this.removers.push(() => this.container.removeEventListener('touchend', handler)); } componentWillUnmount () { this.removeEventListeners(); } - componentDidUpdate (prevProps, prevState) { - if (!this.zoomCenter) return; - - const { x: cx, y: cy } = this.zoomCenter; - const { scale: prevScale } = prevState; - const { scale: nextScale } = this.state; - const { scrollLeft, scrollTop } = this.container; - - // math memo: - // x = (scrollLeft + cx) / scrollWidth - // x' = (nextScrollLeft + cx) / nextScrollWidth - // scrollWidth = clientWidth * prevScale - // scrollWidth' = clientWidth * nextScale - // Solve x = x' for nextScrollLeft - const nextScrollLeft = (scrollLeft + cx) * nextScale / prevScale - cx; - const nextScrollTop = (scrollTop + cy) * nextScale / prevScale - cy; - - this.container.scrollLeft = nextScrollLeft; - this.container.scrollTop = nextScrollTop; - } - removeEventListeners () { this.removers.forEach(listeners => listeners()); this.removers = []; } - handleClick = e => { - // prevent the click event propagated to parent + handleTouchStart = e => { + if (e.touches.length !== 2) return; + + this.lastDistance = getDistance(...e.touches); + } + + handleTouchMove = e => { + const { scrollTop, scrollHeight, clientHeight } = this.container; + if (e.touches.length === 1 && scrollTop !== scrollHeight - clientHeight) { + // prevent propagating event to MediaModal + e.stopPropagation(); + return; + } + if (e.touches.length !== 2) return; + + e.preventDefault(); e.stopPropagation(); - // the tap event handler is executed at the same time by touch and mouse, - // so we don't need to execute the onClick handler here + const distance = getDistance(...e.touches); + const midpoint = getMidpoint(...e.touches); + const scale = clamp(MIN_SCALE, MAX_SCALE, this.state.scale * distance / this.lastDistance); + + this.zoom(scale, midpoint); + + this.lastMidpoint = midpoint; + this.lastDistance = distance; } - handlePinchStart = () => { - this.lastScale = this.state.scale; + zoom(nextScale, midpoint) { + const { scale } = this.state; + const { scrollLeft, scrollTop } = this.container; + + // math memo: + // x = (scrollLeft + midpoint.x) / scrollWidth + // x' = (nextScrollLeft + midpoint.x) / nextScrollWidth + // scrollWidth = clientWidth * scale + // scrollWidth' = clientWidth * nextScale + // Solve x = x' for nextScrollLeft + const nextScrollLeft = (scrollLeft + midpoint.x) * nextScale / scale - midpoint.x; + const nextScrollTop = (scrollTop + midpoint.y) * nextScale / scale - midpoint.y; + + this.setState({ scale: nextScale }, () => { + this.container.scrollLeft = nextScrollLeft; + this.container.scrollTop = nextScrollTop; + }); } - handlePinchMove = e => { - const scale = clamp(MIN_SCALE, MAX_SCALE, this.lastScale * e.scale); - this.zoom(scale, e.center); - } - - handleTap = () => { + handleClick = e => { + // don't propagate event to MediaModal + e.stopPropagation(); const handler = this.props.onClick; if (handler) handler(); } - handleDoubleTap = e => { - if (this.state.scale === MIN_SCALE) - this.zoom(DOUBLE_TAP_SCALE, e.center); - else - this.zoom(MIN_SCALE, e.center); - } - - zoom (scale, center) { - this.zoomCenter = center; - this.setState({ scale }); - } - setContainerRef = c => { this.container = c; } @@ -134,18 +126,6 @@ export default class ZoomableImage extends React.PureComponent { const { alt, src } = this.props; const { scale } = this.state; const overflow = scale === 1 ? 'hidden' : 'scroll'; - const marginStyle = { - position: 'absolute', - top: 0, - bottom: 0, - left: 0, - right: 0, - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - transform: `scale(${scale})`, - transformOrigin: '0 0', - }; return (
-
- {alt} -
+ {alt}
); } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 6a83be452..d76dc10f1 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1483,6 +1483,9 @@ position: relative; width: 100%; height: 100%; + display: flex; + align-items: center; + justify-content: center; img { max-width: $media-modal-media-max-width; diff --git a/package.json b/package.json index d4de3a157..9858d28d4 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,6 @@ "file-loader": "^0.11.2", "font-awesome": "^4.7.0", "glob": "^7.1.1", - "hammerjs": "^2.0.8", "http-link-header": "^0.8.0", "immutable": "^3.8.2", "imports-loader": "^0.8.0", diff --git a/yarn.lock b/yarn.lock index 866b24c7a..fba2cb9a7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3096,10 +3096,6 @@ gzip-size@^3.0.0: dependencies: duplexer "^0.1.1" -hammerjs@^2.0.8: - version "2.0.8" - resolved "https://registry.yarnpkg.com/hammerjs/-/hammerjs-2.0.8.tgz#04ef77862cff2bb79d30f7692095930222bf60f1" - handle-thing@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/handle-thing/-/handle-thing-1.2.5.tgz#fd7aad726bf1a5fd16dfc29b2f7a6601d27139c4" From 1c1042556d21e4c2eb22b7c5cbc11aa88087ca60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Wed, 4 Apr 2018 20:13:43 +0200 Subject: [PATCH 041/442] i18n: Update Polish translation (#7037) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * i18n: Update Polish translation Signed-off-by: Marcin Mikołajczak * i18n: Update Polish translation Signed-off-by: Marcin Mikołajczak --- config/locales/pl.yml | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/config/locales/pl.yml b/config/locales/pl.yml index e92742ef4..c62636081 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -4,6 +4,7 @@ pl: about_hashtag_html: Znajdują się tu publiczne wpisy oznaczone hashtagiem #%{hashtag}. Możesz dołączyć do dyskusji, jeżeli posiadasz konto gdziekolwiek w Fediwersum. about_mastodon_html: Mastodon jest wolną i otwartą siecią społecznościową, zdecentralizowaną alternatywą dla zamkniętych, komercyjnych platform. about_this: O tej instancji + administered_by: 'Administrowana przez:' closed_registrations: Rejestracja na tej instancji jest obecnie zamknięta. Możesz jednak zarejestrować się na innej instancji, uzyskując dostęp do tej samej sieci. contact: Kontakt contact_missing: Nie ustawiono @@ -60,6 +61,7 @@ pl: destroyed_msg: Pomyślnie usunięto notatkę moderacyjną! accounts: are_you_sure: Jesteś tego pewien? + avatar: Awatar by_domain: Domena confirm: Potwierdź confirmed: Potwierdzono @@ -108,6 +110,7 @@ pl: public: Publiczne push_subscription_expires: Subskrypcja PuSH wygasa redownload: Odśwież awatar + remove_avatar: Usun awatar reset: Resetuj reset_password: Resetuj hasło resubscribe: Ponów subskrypcję @@ -135,6 +138,7 @@ pl: web: Sieć action_logs: actions: + assigned_to_self_report: "%{name} przypisał sobie zgłoszenie %{target}" confirm_user: "%{name} potwierdził adres e-mail użytkownika %{target}" create_custom_emoji: "%{name} dodał nowe emoji %{target}" create_domain_block: "%{name} zablokował domenę %{target}" @@ -150,10 +154,13 @@ pl: enable_user: "%{name} przywrócił możliwość logowania użytkownikowi %{target}" memorialize_account: "%{name} nadał kontu %{target} status in memoriam" promote_user: "%{name} podniósł uprawnienia użytkownikowi %{target}" + remove_avatar_user: "%{name} usunął awatar użytkownikowi %{target}" + reopen_report: "%{name} otworzył ponownie zgłoszenie %{target}" reset_password_user: "%{name} przywrócił hasło użytkownikowi %{target}" - resolve_report: "%{name} odrzucił zgłoszenie %{target}" + resolve_report: "%{name} rozwiązał zgłoszenie %{target}" silence_account: "%{name} wyciszył konto %{target}" suspend_account: "%{name} zawiesił konto %{target}" + unassigned_report: "%{name} cofnął przypisanie zgłoszenia %{target}" unsilence_account: "%{name} cofnął wyciszenie konta %{target}" unsuspend_account: "%{name} cofnął zawieszenie konta %{target}" update_custom_emoji: "%{name} zaktualizował emoji %{target}" @@ -240,15 +247,26 @@ pl: expired: Wygasłe title: Filtruj title: Zaproszenia + report_notes: + created_msg: Pomyslnie utworzono notatkę moderacyjną. + destroyed_msg: Pomyślnie usunięto notatkę moderacyjną. reports: action_taken_by: Działanie podjęte przez are_you_sure: Czy na pewno? + assign_to_self: Przypisz do siebie + assigned: Przypisany moderator comment: - label: Komentarz + label: Komentarz do zgłoszenia none: Brak delete: Usuń id: ID mark_as_resolved: Oznacz jako rozwiązane + mark_as_unresolved: Oznacz jako nierozwiązane + notes: + create: Utwórz notatkę + create_and_resolve: Rozwiąż i pozostaw notatkę + delete: Usuń + label: Notatki nsfw: 'false': Nie oznaczaj jako NSFW 'true': Oznaczaj jako NSFW @@ -257,12 +275,16 @@ pl: reported_account: Zgłoszone konto reported_by: Zgłaszający resolved: Rozwiązane + resolved_msg: Pomyślnie rozwiązano zgłoszenie. silence_account: Wycisz konto status: Stan + statuses: Zgłoszone wpisy suspend_account: Zawieś konto target: Cel title: Zgłoszenia + unassign: Cofnij przypisanie unresolved: Nierozwiązane + updated_at: Zaktualizowano view: Wyświetl settings: activity_api_enabled: From f1867a73881444dfed9e093425435681ee764922 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 4 Apr 2018 21:47:39 +0200 Subject: [PATCH 042/442] Adjust privacy policy (#6666) * Adjust privacy policy to be more specific to Mastodon Fix #6613 * Change data retention of IP addresses from 5 years to 1 year * Add even more information * Remove all (now invalid) translations of the privacy policy * Add information about archive takeout, remove pointless consent section * Emphasis on DM privacy * Improve wording * Add line about data use for moderation purposes --- app/javascript/styles/mastodon/about.scss | 5 ++ app/workers/scheduler/ip_cleanup_scheduler.rb | 4 +- config/locales/ca.yml | 68 ------------------ config/locales/en.yml | 61 +++++++++------- config/locales/eo.yml | 68 ------------------ config/locales/es.yml | 68 ------------------ config/locales/fa.yml | 68 ------------------ config/locales/fr.yml | 68 ------------------ config/locales/gl.yml | 68 ------------------ config/locales/hu.yml | 70 ------------------- config/locales/ja.yml | 68 ------------------ config/locales/ko.yml | 68 ------------------ config/locales/nl.yml | 68 ------------------ config/locales/no.yml | 68 ------------------ config/locales/oc.yml | 68 ------------------ config/locales/pl.yml | 68 ------------------ config/locales/pt-BR.yml | 68 ------------------ config/locales/pt.yml | 68 ------------------ config/locales/ru.yml | 39 ----------- config/locales/sr-Latn.yml | 68 ------------------ config/locales/sr.yml | 68 ------------------ config/locales/sv.yml | 68 ------------------ config/locales/zh-CN.yml | 68 ------------------ 23 files changed, 43 insertions(+), 1360 deletions(-) diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss index 03211036c..034c35e8a 100644 --- a/app/javascript/styles/mastodon/about.scss +++ b/app/javascript/styles/mastodon/about.scss @@ -322,6 +322,11 @@ $small-breakpoint: 960px; border: 0; border-bottom: 1px solid rgba($ui-base-lighter-color, .6); margin: 20px 0; + + &.spacer { + height: 1px; + border: 0; + } } .container-alt { diff --git a/app/workers/scheduler/ip_cleanup_scheduler.rb b/app/workers/scheduler/ip_cleanup_scheduler.rb index 9f1593c91..a33ca031e 100644 --- a/app/workers/scheduler/ip_cleanup_scheduler.rb +++ b/app/workers/scheduler/ip_cleanup_scheduler.rb @@ -4,8 +4,10 @@ require 'sidekiq-scheduler' class Scheduler::IpCleanupScheduler include Sidekiq::Worker + RETENTION_PERIOD = 1.year + def perform - time_ago = 5.years.ago + time_ago = RETENTION_PERIOD.ago SessionActivation.where('updated_at < ?', time_ago).destroy_all User.where('last_sign_in_at < ?', time_ago).update_all(last_sign_in_ip: nil) end diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 7727bad37..fc30b3661 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -665,74 +665,6 @@ ca: reblogged: ha impulsat sensitive_content: Contingut sensible terms: - body_html: | -

Política de privacitat

- -

Quina informació recollim?

- -

Recopilem informació teva quan et registres en aquesta instància i recopilem dades quan participes en el fòrum llegint, escrivint i avaluant el contingut aquí compartit.

- -

En registrar-te en aquesta instància, se't pot demanar que introduexis el teu nom i l'adreça de correu electrònic. També pots visitar el nostre lloc sense registrar-te. La teva adreça de correu electrònic es verificarà mitjançant un correu electrònic que conté un enllaç únic. Si es visita aquest enllaç, sabem que controles l'adreça de correu electrònic.

- -

Quan es registra i publica, registrem l'adreça IP de la qual es va originar la publicació. També podrem conservar els registres del servidor que inclouen l'adreça IP de cada sol·licitud al nostre servidor.

- -

Per a què utilitzem la teva informació?

- -

Qualsevol de la informació que recopilem de tu pot utilitzar-se d'una de les maneres següents:

- -
    -
  • Per a personalitzar la teva experiència — la teva informació ens ajuda a respondre millor a les teves necessitats individuals.
  • -
  • Per millorar el nostre lloc — ens esforcem contínuament per millorar les nostres ofertes de llocs basats en la informació i els comentaris que rebem de tu.
  • -
  • Per millorar el servei al client — la teva informació ens ajuda a respondre més eficaçment a les teves sol·licituds de servei al client i a les necessitats de suport.
  • -
  • Per enviar correus electrònics periòdics — l'adreça electrònica que proporcionis es pot utilitzar per enviar-te informació, notificacions que sol·licitis sobre canvis en temes o en resposta al teu nom d'usuari, respondre a les consultes i/o altres sol·licituds o preguntes.
  • -
- -

Com protegim la teva informació?

- -

Implementem diverses mesures de seguretat per mantenir la seguretat de la teva informació personal quan introdueixes, envies o accedeixes a la teva informació personal.

- -

Quina és la nostre política de retenció de dades?

- -

Farem un esforç de bona fe per a:

- -
    -
  • Conserva els registres de servidor que continguin l'adreça IP de totes les sol·licituds a aquest servidor no més de 90 dies.
  • -
  • Conserva les adreces IP associades als usuaris registrats i les seves publicacions no més de 5 anys.
  • -
- -

Utilitzem galetes?

- -

Sí. Les cookies són fitxers petits que un lloc o el proveïdor de serveis transfereix al disc dur del vostre ordinador a través del navegador web (si ho permet). Aquestes galetes permeten al lloc reconèixer el vostre navegador i, si teniu un compte registrat, associar-lo al vostre compte registrat.

- -

Utilitzem cookies per comprendre i desar les vostres preferències per a futures visites i compilar dades agregades sobre el trànsit del lloc i la interacció del lloc, de manera que podrem oferir millors experiències i eines del lloc en el futur. Podem contractar amb proveïdors de serveis de tercers per ajudar-nos a comprendre millor els visitants del nostre lloc. Aquests proveïdors de serveis no estan autoritzats a utilitzar la informació recollida en nom nostre, excepte per ajudar-nos a dur a terme i millorar el nostre negoci.

- -

Publiquem informació al exterior?

- -

No venem, comercialitzem ni transmetem a tercers la vostra informació d'identificació personal. Això no inclou tercers de confiança que ens ajudin a operar el nostre lloc, a dur a terme el nostre negoci o a fer-ho, sempre que aquestes parts acceptin mantenir confidencial aquesta informació. També podem publicar la vostra informació quan creiem que l'alliberament és apropiat per complir amb la llei, fer complir les polítiques del nostre lloc o protegir els nostres drets o altres drets, propietat o seguretat. No obstant això, la informació de visitant que no sigui personalment identificable es pot proporcionar a altres parts per a la comercialització, la publicitat o altres usos.

- -

Vincles de tercers

- -

De tant en tant, segons el nostre criteri, podem incloure o oferir productes o serveis de tercers al nostre lloc. Aquests llocs de tercers tenen polítiques de privadesa separades i independents. Per tant, no tenim responsabilitat ni responsabilitat civil pel contingut i les activitats d'aquests llocs enllaçats. No obstant això, busquem protegir la integritat del nostre lloc i donem la benvinguda a qualsevol comentari sobre aquests llocs.

- -

Compliment de la Llei de protecció de la privacitat en línia dels nens

- -

El nostre lloc, productes i serveis estan dirigits a persones que tenen almenys 13 anys. Si aquest servidor es troba als EUA, i teniu menys de 13 anys, segons els requisits de COPPA (Children's Online Privacy Protection Act) no feu servir aquest lloc.

- -

Només la política de privacitat en línia

- -

Aquesta política de privacitat en línia només s'aplica a la informació recopilada a través del nostre lloc i no a la informació recopilada fora de línia.

- - - -

En utilitzar el nostre lloc, accepta la política de privadesa del nostre lloc web.

- -

Canvis a la nostra política de privacitat

- -

Si decidim canviar la nostra política de privadesa, publicarem aquests canvis en aquesta pàgina.

- -

Aquest document és CC-BY-SA. Es va actualitzar per última vegada el 31 de maig de 2013.

- -

Originalment adaptat a la política de privadesa del Discurs.

title: "%{instance} Condicions del servei i política de privadesa" themes: default: Mastodont diff --git a/config/locales/en.yml b/config/locales/en.yml index 945faa1e2..70af9530c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -691,70 +691,79 @@ en: terms: body_html: |

Privacy Policy

-

What information do we collect?

-

We collect information from you when you register on our site and gather data when you participate in the forum by reading, writing, and evaluating the content shared here.

+
    +
  • Basic account information: If you register on this server, you may be asked to enter a username, an e-mail address and a password. You may also enter additional profile information such as a display name and biography, and upload a profile picture and header image. The username, display name, biography, profile picture and header image are always listed publicly.
  • +
  • Posts, following and other public information: The list of people you follow is listed publicly, the same is true for your followers. When you submit a message, the date and time is stored as well as the application you submitted the message from. Messages may contain media attachments, such as pictures and videos. Public and unlisted posts are available publicly. When you feature a post on your profile, that is also publicly available information. Your posts are delivered to your followers, in some cases it means they are delivered to different servers and copies are stored there. When you delete posts, this is likewise delivered to your followers. The action of reblogging or favouriting another post is always public.
  • +
  • Direct and followers-only posts: All posts are stored and processed on the server. Followers-only posts are delivered to your followers and users who are mentioned in them, and direct posts are delivered only to users mentioned in them. In some cases it means they are delivered to different servers and copies are stored there. We make a good faith effort to limit the access to those posts only to authorized persons, but other servers may fail to do so. Therefore it's important to review servers your followers belong to. You may toggle an option to approve and reject new followers manually in the settings. Please keep in mind that the operators of the server and any receiving server may view such messages, and that recipients may screenshot, copy or otherwise re-share them. Do not share any dangerous information over Mastodon.
  • +
  • IPs and other metadata: When you log in, we record the IP address you log in from, as well as the name of your browser application. All the logged in sessions are available for your review and revocation in the settings. The latest IP address used is stored for up to 12 months. We also may retain server logs which include the IP address of every request to our server.
  • +
-

When registering on our site, you may be asked to enter your name and e-mail address. You may, however, visit our site without registering. Your e-mail address will be verified by an email containing a unique link. If that link is visited, we know that you control the e-mail address.

- -

When registered and posting, we record the IP address that the post originated from. We also may retain server logs which include the IP address of every request to our server.

+

What do we use your information for?

-

Any of the information we collect from you may be used in one of the following ways:

+

Any of the information we collect from you may be used in the following ways:

    -
  • To personalize your experience — your information helps us to better respond to your individual needs.
  • -
  • To improve our site — we continually strive to improve our site offerings based on the information and feedback we receive from you.
  • -
  • To improve customer service — your information helps us to more effectively respond to your customer service requests and support needs.
  • -
  • To send periodic emails — The email address you provide may be used to send you information, notifications that you request about changes to topics or in response to your user name, respond to inquiries, and/or other requests or questions.
  • +
  • To provide the core functionality of Mastodon. You can only interact with other people's content and post your own content when you are logged in. For example, you may follow other people to view their combined posts in your own personalized home timeline.
  • +
  • To aid moderation of the community, for example comparing your IP address with other known ones to determine ban evasion or other violations.
  • +
  • The email address you provide may be used to send you information, notifications about other people interacting with your content or sending you messages, and to respond to inquiries, and/or other requests or questions.
+
+

How do we protect your information?

-

We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information.

+

We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information. Among other things, your browser session, as well as the traffic between your applications and the API, are secured with SSL, and your password is hashed using a strong one-way algorithm. You may enable two-factor authentication to further secure access to your account.

-

What is your data retention policy?

+
+ +

What is our data retention policy?

We will make a good faith effort to:

    -
  • Retain server logs containing the IP address of all requests to this server no more than 90 days.
  • -
  • Retain the IP addresses associated with registered users and their posts no more than 5 years.
  • +
  • Retain server logs containing the IP address of all requests to this server, in so far as such logs are kept, no more than 90 days.
  • +
  • Retain the IP addresses associated with registered users no more than 12 months.
+

You can request and download an archive of your content, including your posts, media attachments, profile picture, and header image.

+ +

You may irreversibly delete your account at any time.

+ +
+

Do we use cookies?

Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account.

-

We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.

+

We use cookies to understand and save your preferences for future visits.

+ +

Do we disclose any information to outside parties?

-

We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.

+

We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety.

-

Third party links

+

Your public content may be downloaded by other servers in the network. Your public and followers-only posts are delivered to the servers where your followers reside, and direct messages are delivered to the servers of the recipients, in so far as those followers or recipients reside on a different server than this.

-

Occasionally, at our discretion, we may include or offer third party products or services on our site. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.

+

When you authorize an application to use your account, depending on the scope of permissions you approve, it may access your public profile information, your following list, your followers, your lists, all your posts, and your favourites. Applications can never access your e-mail address or password.

+ +

Children's Online Privacy Protection Act Compliance

Our site, products and services are all directed to people who are at least 13 years old. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA (Children's Online Privacy Protection Act) do not use this site.

-

Online Privacy Policy Only

- -

This online privacy policy applies only to information collected through our site and not to information collected offline.

- - - -

By using our site, you consent to our web site privacy policy.

+

Changes to our Privacy Policy

If we decide to change our privacy policy, we will post those changes on this page.

-

This document is CC-BY-SA. It was last updated May 31, 2013.

+

This document is CC-BY-SA. It was last updated March 7, 2018.

Originally adapted from the Discourse privacy policy.

title: "%{instance} Terms of Service and Privacy Policy" diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 84d63d831..a896592b0 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -667,74 +667,6 @@ eo: reblogged: diskonigita sensitive_content: Tikla enhavo terms: - body_html: | -

Privateca politiko

- -

Kiujn informojn ni kolektas?

- -

Ni kolektas informojn de vi, kiam vi registriĝas en nia retejo aŭ partoprenas en la forumo per legado, skribado, kaj traktado de la enhavo diskonigita ĉi tie.

- -

En registriĝo, ni povas peti al vi vian nomon kaj retadreson. Vi tamen povas viziti nian retejon sen registriĝo. Via retadreso estos validigita per retmesaĝo, kiu enhavos unikan ligilon. Se tiu ligilo estas vizitita, ni scios ke vi regas la retadreson.

- -

Post registriĝo, ni registras la IP-adreson de tiu, kiu kreas mesaĝon. Ni ankaŭ povas konservi servilan historion, en kiu troviĝas la IP-adreso de ĉiu peto al nia servilo.

- -

Por kio ni uzas viajn informojn?

- -

Ajna informo, kiun ni kolektas povas esti uzata por unu el tiuj celoj:

- -
    -
  • Proprigi vian sperton — viaj informoj helpas nin pli bone respondi al viaj propraj bezonoj.
  • -
  • Plibonigi nian retejon — ni daŭre klopodas por plibonigi nian retejon uzante la informojn kaj komentojn, kiujn ni ricevas de vi.
  • -
  • Plibonigi nian helpon al klientoj — viaj informoj helpas nin pli bone respondi al klientaj petoj kaj al subtenaj bezonoj.
  • -
  • Sendi periodajn retmesaĝojn — La retadreso, kiun vi donas al ni, povas esti uzata por sendi al vi informojn kaj sciigojn, kiujn vi volas ricevi pri ŝanĝoj rilate al apartaj temoj, aŭ responde al via uzantnomo, al petoj kaj al demandoj.
  • -
- -

Kiel ni protektas viajn informojn?

- -

Ni realigis diversajn sekurigajn procedojn por konservi la sekurecon de viaj personaj informoj kiam vi enmetas, sendas, aŭ aliras viajn personajn informojn.

- -

Kio estas nia politiko pri konservado de datumoj?

- -

Ni honeste klopodas:

- -
    -
  • Ne konservi servilan historion, kiu enhavas la IP-adresojn de ĉiuj petoj, dum pli ol 90 tagoj.
  • -
  • Ne konservi la IP-adresojn de registritaj uzantoj kaj de iliaj mesaĝoj dum pli ol 5 jaroj.
  • -
- -

Ĉu ni uzas kuketojn?

- -

Jes. Kuketoj estas etaj dosieroj, kiujn retejo aŭ ĝia servo donas al la memoro de via komputilo, per via retumilo (se vi permesas tion). Ĉi tiuj kuketoj ebligas al la retejo rekoni vian retumilon, kaj se vi havas registritan konton, ligas ĝin al via registrita konto.

- -

Ni uzas kuketojn por kompreni kaj konservi viajn preferojn por postaj vizitoj, kaj kunmeti informojn pri reteja trafiko kaj interago, por ke ni povu doni pli bonan retejan sperton kaj pli bonajn ilojn estonte. Ni povas kontrakti kun eksteraj servoj por helpi nin pli bone kompreni la vizitantojn de la retejo. Ĉi tiuj eksteraj servoj ne rajtas uzi la informojn, kiujn ni kolektis, krom por helpi nin regi kaj plibonigi nian komercon.

- -

Ĉu ni disdonas informojn al eksteraj personoj?

- -

Ni ne vendas, interŝanĝas aŭ transdonas al eksteraj personoj viajn persone identigeblajn informojn. Ĉi tio ne inkludas la eksterajn servojn, kiujn ni fidas, kiuj helpas nin funkciigi nian retejon, regi nian komercon, aŭ servi vin, kiom longe tiuj personoj konsentas pri la sekura konservado de ĉi tiuj informoj. Ni ankaŭ povas disdoni viajn informojn, kiam ni pensas ke tio estas nepra por respekti leĝojn, por respektigi la politikojn de nia retejo, aŭ por protekti la rajtojn, posedaĵojn, kaj sekurecon de ni kaj de aliaj. Tamen, informoj de vizitantoj, kiuj ne identigas personojn, povas esti donitaj al eksteraj personoj por merkatado, reklamado, aŭ aliaj uzoj.

- -

Eksteraj ligiloj

- -

Foje, laŭ nia elekto, ni povas enmeti aŭ oferti eksterajn produktojn aŭ servojn en nia retejo. Ĉi tiuj eksteraj retejoj havas apartajn kaj sendependajn privatecajn politikojn. Tial, ni havas nek responsojn nek devigojn rilate al la enhavoj kaj agadoj de ĉi tiuj ligitaj retejoj. Tamen, ni celas protekti tiujn, kiuj uzas nian retejon, kaj bonvenigas ajnan komenton pri ĉi tiuj retejoj.

- -

Children's Online Privacy Protection Act Compliance

- -

Niaj retejo, produktoj kaj servoj estas por tiuj, kiuj havas almenaŭ 13 jarojn. Se ĉi tiu servilo estas en Usono, kaj vi havas malpli ol 13 jarojn, pro la postuloj de COPPA (Children's Online Privacy Protection Act) ne uzu ĉi tiun retejon.

- -

Privateca politiko nur rete

- -

Ĉi tiu privateca politiko validas nur por informoj kolektitaj per nia retejo kaj ne por informoj kolektitaj eksterrete.

- - - -

Per uzado de nia retejo, vi konsentas kun nia reta privateca politiko.

- -

Ŝanĝoj al nia privateca politiko

- -

Se ni decidas ŝanĝi nian privatecan politikon, ni afiŝos tiujn ŝanĝojn en ĉi tiu paĝo.

- -

Ĉi tiu dokumento estas laŭ permeso CC-BY-SA. Ĝi estis laste ĝisdatigita je 2018-02-27.

- -

Originale adaptita el la privateca politiko de Discourse.

title: Uzkondiĉoj kaj privateca politiko de %{instance} themes: default: Mastodon diff --git a/config/locales/es.yml b/config/locales/es.yml index 671f17d33..8e7a766a8 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -656,74 +656,6 @@ es: reblogged: retooteado sensitive_content: Contenido sensible terms: - body_html: | -

Políticas de privacidad

- -

¿Qué información recolectamos?

- -

Recolectamos información tuya cuando te registras en nuestro sitio y adquirimos datos cuando participas en el foro leyendo, escribiendo, y evaluando el contenido compartido aquí.

- -

Cuando te registras en nuestro sitio, puede que se te pida tu nombre y dirección de correo electrónico. De todas formas, puedes visitar nuestro sitio sin registrarte. Tu dirección de correo electrónico será verificada por un e-mail conteniendo un enlace único. Si ese enlace es visitado, sabemos que tú controlas esa dirección.

- -

Cuando te registras y posteas, grabamos la IP de la que se origina esa acción. También puede que retengamos logs del servidor, que incluyen la dirección IP de todos los pedidos a nuestro servidor.

- -

¿Para qué usamos tu información?

- -

Toda la información que recolectamos de ti puede usarse en una de las siguientes maneras:

- -
    -
  • Para personalizar tu experiencia — tu información nos ayuda a responder mejor tus necesidades individuales.
  • -
  • Para mejorar nuestro sitio — nos esforzamos continuamente en mejorar nuestras ofertas del sitio basándonos en la información y apoyo que recibimos de ti.
  • -
  • Para mejorar el servicio al cliente — tu información nos ayuda a responder más efectivamente al servicio al cliente y otras necesidades.
  • -
  • Para enviar e-mails periódicos — la dirección de e-mail que provees puede usarse para enviarte información, notificaciones que pides sobre cambios en tópicos o en respuesta a tu nombre de usuario, responder consultas, y/u otros pedidos o preguntas.
  • -
- -

¿Cómo protegemos tu información?

- -

Implementamos una variedad de medidas de seguridad para mantener a salvo tu información personal cuando entras, publicas, o accesas a ella.

- -

¿Cuáles son sus políticas de retención de datos?

- -

Haremos un gran esfuerzo en:

- -
    -
  • Retener logs del servidor conteniendo la dirección IP de todos los pedidos a este servidor en no más de 90 días.
  • -
  • Retener las direcciones IP asociadas con usuarios registrados y sus posts no más de 5 años.
  • -
- -

- -

Sí. Las cookies son pequeños archivos que un sitio web o su proveedor de servicio transfieren al disco duro de tu computadora a través de tu navegador web (si se le permite). Estas cookies permiten al sitio reconocer tu navegador y, si y tienes una cuenta registrada, asociarlo con ella.

- -

Usamos cookies para entender y guardar tus preferencias para futuras visitas y agregar datos compilados sobre el tráfico del sitio e interacción para que podamos ofrecer una mejor experiencia y herramientas en el futuro. Puede que contratemos con proveedores de servicio de tercera mano para que nos asistan en el mejor entendimiento de nuestros visitantes del sitio. A estos proveedores de servicio no se les permite usar la información recolectada a nuestras espaldas excepto para ayudarnos a conducir y mejorar nuestro trabajo.

- -

¿Revelamos alguna información a terceras manos?

- -

No vendemos, intercambiamos, ni de ninguna otra manera transferimos tu información personal identificable a terceras partes. Esto no incluye las terceras manos que nos asisten en operar nuestro sitio, conducción o trabajo, o en servirte, tanto como que éstas acepten en mantener esta información confidencial. Puede que también liberemos tu información cuando creamos que es apropiado para cumplir con la ley, enforzar nuestras políticas del sitio, o proteger la nuestra u otros derechos, propiedad, o seguridad. De todas formas, la información del visitante autorizado no-personal puede proveerse a otras partes por marketing, publicidad, u otros usos.

- -

Enlaces de terceras partes

- -

Ocasionalmente, a nuestra discreción, puede que incluyamos u ofrezcamos productos de terceras partes o servicios en nuestro sitio. Estas terceras partes tienen políticas de privacidad separadas e independientes. Por lo tanto no tenemos responsabilidad u obligación por el contenido y actividades de estos sitios enlazados. Sin embargo, buscamos proteger la integridad de nuestro sitio y dar la bienvenida a cualquier ayuda sobre estos sitios.

- -

Children's Online Privacy Protection Act Compliance (Cumplimiento de la Ley de la Protección Privada en Línea del Niño)

- -

Nuestro sitio y todos nuestros productos y servicios están dirigidos a gente que tiene al menos 13 años de edad. Si el servidor está alojado en EE.UU, y tienes menos de 13 años, no uses este sitio por los requerimientos del COPPA (Children's Online Privacy Protection Act).

- -

Solo Políticas de Privacidad en Línea

- -

Estas políticas de privacidad aplican únicamente a la información recolectada a través de nuestro sitio y no a información recolectada offline.

- - - -

Al usar nuestro sitio, estás consentido a nuestras políticas de privacidad del sitio.

- -

Cambios a nuestras Políticas de Privacidad

- -

Si decidimos cambiar nuestras políticas de privacidad, las publicaremos en esta página.

- -

Este documento está publicado bajo la licencia CC-BY-SA. Última vez actualizado el 31 de Mayo del 2013.

- -

Adaptado originalmente del discurso de las políticas de privacidad.

title: Términos del Servicio y Políticas de Privacidad de %{instance} themes: default: Mastodon diff --git a/config/locales/fa.yml b/config/locales/fa.yml index 86756c01b..ed25ea8c9 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -613,74 +613,6 @@ fa: reblogged: بازبوقید sensitive_content: محتوای حساس terms: - body_html: | -

سیاست رازداری (Privacy Policy)

- -

ما چه اطلاعاتی را گردآوری می‌کنیم؟

- -

این سایت برخی از اطلاعات مربوط به شما را ثبت می‌کند. این موارد شامل اطلاعات ثبت‌نامی شماست، و نیز شامل نوشته‌هایی است که این‌جا می‌خوانید، می‌نویسید، یا واکنش‌هایی که به نوشته‌های دیگران نشان می‌دهید.

- -

وقتی که در این سایت ثبت‌نام می‌کنید، ممکن است از شما بخواهیم که نام و نشانی ایمیل خود را وارد کنید. البته بدون ثبت‌نام نیز می‌توان از این سایت بازدید کرد. برای تأیید ایمیل شما، ما یک نشانی اینترنتی یکتا را به آن می‌فرستیم. اگر آن نشانی را کسی باز کند، ما می‌فهمیم که آن شما بوده‌اید و بنابراین نشانی ایمیل متعلق به شماست.

- -

وقتی که عضو باشید و چیزی بنویسید، ما نشانی اینترنتی‌ای (IP) را که نوشته از آن آمده است ثبت می‌کنیم. سیاههٔ کاری (log) سرور شامل نشانی IP همهٔ درخواست‌ها به سرور است که ما شاید آن را هم ثبت کنیم.

- -

ما با اطلاعات شما چه کار می‌کنیم؟

- -

اطلاعاتی را که ما از شما ثبت می‌کنیم، ممکن است در موارد زیر به کار بروند:

- -
    -
  • برای شخصی‌سازی تجربهٔ کاربری شما — ما به کمک اطلاعات شما بهتر می‌توانیم نیازهای شما را برآورده کنیم.
  • -
  • برای بهتر کردن سایت — ما پیوسته می‌کوشیم تا خدمات این سایت را به کمک اطلاعات و بازخوردی که از شما می‌گیریم بهتر کنیم.
  • -
  • برای بهتر کردن خدمات به کاربران — ما به کمک اطلاعات شما به طور مؤثرتری می‌توانیم به درخواست‌های پشتیبانی شما پاسخ دهیم.
  • -
  • برای فرستادن ایمیل‌های دوره‌ای — ما گاهی به نشانی ایمیلی که وارد کرده‌اید نامه می‌فرستیم تا به درخواست‌های شما پاسخ دهیم یا شما را در جریان پاسخ دیگران به شما قرار دهیم.
  • -
- -

ما چگونه از اطلاعات شما محافظت می‌کنیم؟

- -

ما روش‌های امنیتی گوناگونی را پیاده کرده‌ایم تا امنیت اطلاعات شخصی شما هنگام ثبت، فرستاده‌شدن، و بازیابی آن‌ها حفظ شود.

- -

سیاست ما برای نگهداری اطلاعات شما چیست؟

- -

ما با حسن نیت تلاش می‌کنیم تا:

- -
    -
  • سیاههٔ کاری سرور که شامل نشانی IP همهٔ درخواست‌ها به این سرور است را بیشتر از ۹۰ روز ذخیره نکنیم.
  • -
  • نشانی IP مربوط به کاربران ثبت‌نام‌شده را بیشتر از ۵ سال نگه نداریم.
  • -
- -

آیا ما کوکی‌ها را به‌کار می‌بریم؟

- -

بله. کوکی‌ها پرونده‌های کوچکی هستند که یک سایت یا خدمات‌دهنده‌اش (اگر شما اجازه بدهید) از راه مرورگر در کامپیوتر شما ذخیره می‌کنند. به کمک این کوکی‌ها سایت می‌تواند مرورگر شما را بشناسد و اگر شما ثبت‌نام کرده باشید، حساب شما را به مرورگرتان مرتبط کند.

- -

ما به کمک کوکی‌ها ترجیحات شما را برای بازدیدهای آینده می‌فهمیم و ذخیره می‌کنیم و داده‌های جامعی دربارهٔ بازدیدها از سایت و برهمکنش‌ها با آن را تهیه می‌کنیم. به این ترتیب می‌توانیم در آینده تجربهٔ کاربری سایت و ابزارهای مربوط به آن را بهتر کنیم. برای داشتن درک بهتری از بازدیدکنندگان این سایت، ما گاهی از خدمات‌دهنده‌های دیگر نیز کمک می‌گیریم. این خدمات‌دهنده‌ها اجازه ندارند تا از اطلاعاتی که به جای ما جمع می‌کنند برای کاری به جز بهترکردن کار ما استفاده کنند.

- -

آیا ما اطلاعاتی به نهادهای دیگر فاش می‌کنیم؟

- -

ما اطلاعاتی را که بتواند شما را شناسایی کند به نهادهای دیگر نمی‌فروشیم، معامله نمی‌کنیم، یا به هر روش دیگری منتقل نمی‌کنیم. این شامل نهادهای مورد اعتمادی نمی‌شود که به ما در گرداندن این سایت یا انجام کارهایمان کمک می‌کنند، یا به شما خدمات می‌رسانند، تا جایی که آن‌ها این داده‌ها را محرمانه نگه دارند. ما همچنین ممکن است اطلاعات شما را به حکم قانون یا برای اِعمال سیاست‌های سایت، یا به خاطر حفظ حقوق، دارایی‌ها، یا امنیت خودمان یا دیگران منتشر کنیم. ما ممکن است اطلاعات بازدیدکنندگان سایت را که با آن نمی‌توان شما را شناسایی کرد برای بازاریابی، تبلیغات، یا هدف‌های دیگر به نهادهای دیگر ارائه دهیم.

- -

پیوند (لینک) به صفحه‌های دیگران

- -

ما گاهی ممکن است به صلاحدید خودمان محصولات یا خدمات دیگران را در این سایت بگنجانیم یا پیشنهاد دهیم. سایت‌های مرتبط با این محصولات و خدمات دارای سیاست‌های رازداری جداگانه و مستقل خودشان هستند. بنابراین ما مسئولیتی دربارهٔ محتوا و کنش‌های این سایت‌ها به عهده نمی‌گیریم. با این وجود، ما تلاش می‌کنیم که این سایت به درستی کار کند و از بازخورد شما برای چنین محصولات و خدماتی استقبال می‌کنیم.

- -

پیروی از قانون پشتیبانی از حریم خصوصی آنلاین کودکان

- -

سایت ما، محصولات و خدماتش همه برای کسانی است که دست‌کم ۱۳ سال سن داشته باشند. اگر این سرور در خاک ایالات متحدهٔ امریکا قرار دارد و سن شما کمتر از ۱۳ سال است، به خاطر رعایت قانون COPPA (Children's Online Privacy Protection Act) لطفاً این سایت را به کار نبرید.

- -

تنها سیاست رازداری آنلاین

- -

این سیاست رازداری آنلاین تنها مربوط به اطلاعاتی است که از راه سایت ما گردآوری می‌شود و شامل اطلاعاتی که به طور آفلاین گردآوری شده نیست.

- - - -

با استفاده از این سایت، شما موافقت خود را با سیاست رازداری ما اعلام می‌کنید.

- -

تغییرات در سیاست رازداری ما

- -

اگر ما سیاست رازداری خود را تغییر دهیم، این تغییرات را در این صفحه خواهیم نوشت.

- -

این نوشته تحت اجازه‌نامهٔ CC-BY-SA قرار دارد. تاریخ آخرین به‌روزرسانی آن ۱۰ خرداد ۱۳۹۲ است.

- -

این نوشته اقتباسی است از سیاست رازداری Discourse.

title: شرایط استفاده و سیاست رازداری %{instance} themes: default: ماستدون diff --git a/config/locales/fr.yml b/config/locales/fr.yml index d7371dc94..4571cc375 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -664,74 +664,6 @@ fr: reblogged: a partagé sensitive_content: Contenu sensible terms: - body_html: | -

Politique de confidentialité

- -

Quelles données collectons-nous ?

- -

Nous collectons des données lorsque vous vous enregistrez sur notre site et les récoltons lorsque vous participez dans le forum en lisant, écrivant, et évaluant le contenu partagé ici.

- -

Lors de l’enregistrement sur notre site, il peut vous être demandé de renseigner votre nom et adresse électronique. Vous pouvez, cependant, visiter notre site sans inscription. Votre adresse électronique devra être vérifiée grâce à un courriel contenant un lien unique. Si ce lien est visité, nous savons que vous contrôlez cette adresse.

- -

Lors de l’inscription et de la publication de statuts, nous enregistrons l’adresse IP de laquelle les statuts proviennent. Nous pouvons également conserver des historiques serveurs qui contiendront l’adresse IP de chaque requête adressée à notre serveur.

- -

Que faisons-nous avec vos données ?

- -

Toute information que nous collectons pourra être utilisée d’une des manières suivantes :

- -
    -
  • Pour personnaliser votre expérience — vos données nous aident à mieux répondre à vos besoins individuels.
  • -
  • Pour améliorer notre site — nous faisons tout notre possible pour améliorer notre site en fonction des données, retours et suggestions que nous recevons.
  • -
  • Afin d’améliorer le support client — vos données nous aident à mieux répondre à vos requêtes et demandes de support.
  • -
  • Afin d’envoyer des courriels à intervalles réguliers — l’adresse électronique que vous renseignez peut être utilisée pour vous envoyer des données et notifications concernant des changements ou en réponse à votre nom d’utilisateur⋅ice, en réponse à vos demandes et/ou autres requêtes ou questions
  • -
- -

Comment protégeons-nous vos données ?

- -

Nous appliquons une multitude de mesures afin de maintenir la sécurité de vos données personnelles lorsque vous entrez, soumettez, ou accédez à ces dernières.

- -

Quelle est notre politique de conservation des données ?

- -

Nous nous efforçons de :

- -
    -
  • ne pas garder les historiques serveurs contenant l’adresse IP de chaque requête adressée à ce serveur plus de 90 jours ;
  • -
  • ne pas conserver les adresses IP associées aux utilisateur⋅trices et leur contenu plus de 5 ans.
  • -
- -

Utilisons-nous des « cookies » ?

- -

Oui. Les cookies sont de petits fichiers qu’un site ou prestataires de services transfèrent sur le disque dur de votre ordinateur par le biais de votre navigateur Web (si ce dernier le permet). Ces cookies permettent au site de reconnaître votre navigateur et, si vous disposez d’un compte, de l’associer à celui-ci.

- -

Nous utilisons les cookies pour enregistrer vos préférences pour de futures visites, compiler des données agrégées à propos du trafic et des interactions effectuées sur le site afin de proposer une meilleure expérience dans le futur. Nous pouvons contracter les services de tiers afin de nous aider à mieux comprendre les visiteurs de notre site. Ces tiers ont l’autorisation d’utiliser ces données seulement à des fins d’améliorations.

- -

Divulguons-nous des données à des tiers ?

- -

Nous n’échangeons pas, ne vendons pas ni effectuons de quelconques transferts avec des tiers d’informations permettant de vous identifier personnellement. Cela n’inclut pas les tiers de confiance qui nous aident à gérer notre entreprise et à vous servir tant que ces tiers s’accordent à garder lesdites informations confidentielles. Nous pouvons être amenés à délivrer vos informations lorsque jugé adéquat afin de respecter la loi, d’appliquer la politique de notre site, ou afin de protéger nos droits, ceux des autres, notre propriété ou sécurité. Cependant, aucune information permettant l’identification de nos visiteur⋅euse⋅s ne sera divulguée à des fins publicitaires, commerciales ou tout autre usage.

- -

Liens vers des tiers

- -

Nous pouvons être amenés à inclure ou offrir les services ou produits de tiers sur notre site. Ces tiers possèdent leur propre politique de confidentialité. Nous ne sommes donc pas responsables du contenu ou activités desdits tiers. Néanmoins, nous cherchons à protéger l’intégrité de notre site et sommes ouverts à toute remarque concernant ces tiers.

- -

Children's Online Privacy Protection Act

- -

Notre site, nos produits et services sont tous destinés à l’usage de personnes âgées de 13 ans ou plus. Si ce serveur est hébergé aux États-Unis et que vous êtes âgé⋅e de moins de 13 ans, au vu du COPPA (Children's Online Privacy Protection Act) n’utilisez pas ce site.

- -

Politique de confidentialité en ligne

- -

Cette politique de confidentialité en ligne s'applique uniquement aux informations collectées par le biais de notre site et non aux informations collectées hors ligne.

- - - -

En utilisant notre site, vous consentez à la présente politique de confidentialité.

- -

Changements de notre politique de confidentialité

- -

Si nous décidons d’apporter des changements à notre politique de confidentialité, nous les publierons sur cette page.

- -

Ce document est distribué sous licence CC-BY-SA. Il a été mis à jour pour la dernière fois le 31 mai 2013. Il a été traduit en français en juillet 2017.

- -

Originellement adapté à partir de la politique de confidentialité de Discourse.

title: "%{instance} Conditions d’utilisations et politique de confidentialité" themes: default: Mastodon diff --git a/config/locales/gl.yml b/config/locales/gl.yml index bddc1b789..f4ca7e8c5 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -665,74 +665,6 @@ gl: reblogged: promocionada sensitive_content: Contido sensible terms: - body_html: | -

Política de intimidade

- -

Qué información recollemos?

- -

Recollemos información sobre vostede cando se rexistra no noso sitio web e recollemos datos cando participa no foro lendo, escribindo e evaluando o contido compartido aquí.

- -

Cando se rexistra no noso sitio, podería solicitarselle o seu nome e enderezo de correo electrónico. Vostede podería, porén, visitar o noso sitio sin rexistrarse. O seu enderezo de correo será verificado con un correo con unha ligazón única. Si esa ligazón é visitada, saberemos que vostede controla ese enderezo de correo.

- -

Cando se rexistra e publica, almacenamos o enderezo IP desde onde publicou. Poderiamos tamén gardar rexistros do servidor que inclúan a IP de cada petición ao realizada ao servidor.

- -

Con qué fin utilizamos a súa información?

- -

Toda a información recollida de vostede podería ser utilizada dos seguintes xeitos:

- -
    -
  • Para individualizar a súa experiencia — a súa información axúdanos a respostar mellor as súas necesidades individuais.
  • -
  • Para mellorar o noso sitio —esforzámonos en mellorar o que ofrece o noso sitio baseándonos na información e críticas que vostede nos proporciona.
  • -
  • Para mellorar o servizo ao cliente —a súa información axúdanos a respostar máis eficientemente as súas peticións de servizo ao cliente e axuda.
  • -
  • Para enviar correos electrónicos periodicamente — O enderezo de correo que nos proporciona podería ser utilizado para enviarlle información, notificacións que solicitou sobre cambios ou asuntos ou en resposta ao ser nome de usuaria, responder a enquisas, e/ou outras peticións ou cuestións.
  • -
- -

Cómo protexemos a súa información?

- -

Implementamos varias medidas de seguridade para manter a seguiridade da súa información personal cando introduce, envía ou accede a súa información personal.

- -

Qué é a política de retención dos seus datos?

- -

Faremos un sincero esforzo para:

- -
    -
  • Manter rexistros do sistema con enderezos IP de todas as peticións feitas a este servidor no seu nome non máis de 90 días.
  • -
  • Manter os enderezos IP asociados a usuarias rexistradas e as súas mensaxes non máis de 5 anos.
  • -
- -

Utilizamos cookies?

- -

Si. As cookies son pequenos ficheiros que un sitio ou o seu proveedor de servizo transfire ao disco duro da súa computadora a través do navegador web (se vostede o permite). Estas cookies permiten ao sitio recoñer o seu navegador e, si ten unha conta rexistrada, asocialo coa súa conta.

- -

Utilizamos cookies para comprender e gardar as súas preferencias para futuras visitas e compilar datos agregados sobre o tráfico do sitio e interacción co sitio de tal xeito que no futuro poidamos ofrecer unha mellor experiencia de uso do sitio e ferramentas. Poderiamos contratar servizos de terceiros para axudarnos a entender mellor as nosas visitantes. Estos proveedores de servizo non teñen permiso para utilizar a información recollida no noso nome excepto para axudarnos a xestionar e mellorar o noso negocio.

- -

Mostramos información a terceiros alleos?

- -

Non vendemos, nin negociamos con, ou transmitimos de outros xeitos a axentes terceiros alleos a información que a información que a identifica personalmente. Esto non inclúe terceiros de confianza que non axudan a operar o sitio, xestionar o negocio, ou servila, así estas partes se comprometan coa confidencialidade dos datos. Poderiamos revelar a súa información cando creamos que facelo así é axeitado para cumplir coa lei, cumplir coas normas do sitio ou protexer os nosos dereitos, propiedades ou seguridade e os de outras. Porén, non se proporcionará información identificable a terceiros para publicidade, márquetin ou outros usos.

- -

Ligazóns a terceiros

- -

De xeito ocasional, a nosa discreción, poderiamos incluír ofertas de productos e servizos de terceiros no noso sitio. Estos sitios de terceiros teñen políticas de intimidade propias. Polo tanto non temos responsabilidade ou obligacións polo contido e actividades de esos sitios. Con todo, procuramos protexer a integridade do noso sitio e agradecemos calquer opinión e crítica sobre estos sitios.

- -

Cumplimento coa Children's Online Privacy Protection Act

- -

O noso sitio, productos e servizos están dirixidos as personas con 13 anos como mínimo. Si este servidor está en USA e vostede ten menos de 13 anos, a requerimento da COPPA (Children's Online Privacy Protection Act) non utilice este sitio web.

- -

Política de intimidade só en liña

- -

Esta política de intimidade aplícase só a información recollida no noso sitio e non a información recollida fora de liña.

- - - -

Utilizando o noso sitio, vostede acepta esta política de intimidade.

- -

Cambios na política de intimidade

- -

Si decidimos cambiar a nosa política de intimidade publicaremos esos cambios en esta páxina.

- -

Este documento ten licenza CC-BY-SA. Foi actualizado o 31 de maio de 2013.

- -

Adaptado do orixinal Discourse privacy policy.

title: "%{instance} Termos do Servizo e Política de Intimidade" themes: default: Mastodon diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 6be82c1de..2560b3816 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -633,76 +633,6 @@ hu: reblogged: reblogolt sensitive_content: Szenzitív tartalom terms: - body_html: | -

Adatvédelmi és adatkezelési nyilatkozat

- -

Milyen információt gyűjtünk?

- -

Az oldalra történő regisztráció és a szolgáltatás használata - olvasás, tartalom létrehozása, tartalommegosztás - során információt gyűjtünk veled kapcsolatban.

- -

A regisztráció során kérhetjük nevedet és e-mail címedet. Az oldalt természetesen regisztráció nélkül is felkeresheted. Az e-mail címed megerősítése egy egyedi információt tartalmazó link segítségével történik. Mondott linkre kattintva ellenőrizzük, hogy valóban te vagy a cím kezelője.

- -

Regisztrált felhasználók esetében tülkök írásakor rögzítjük a felhasználó IP-címét. A szerver napjófájlja szintén tárolhatja ezt az IP-címet, valamint a szerverre érkező minden kérés küldő-oldali IP-címét.

- -

Mire használjuk a begyűjtött információt?

- -

Minden begyűjtött információt az alábbi okokból használhatunk fel:

- -
    -
  • A felhasználói élmény személyre szabásához — a tőled gyűjtött információ segítségével biztosíthatjuk számodra az egyedi igényeknek történő megfelelést.
  • -
  • Szolgáltatásunk fejlesztéséhez — folyamatosan igyekszünk fejlődni és jobbá válni, és ezt a tőled kapott adatok és visszajelzések is nagyban segítik.
  • -
  • Az ügyféltámogatás fejlesztéséhez — a gyűjtött adatok segítségével hatékonyabban támogathatjuk felhasználóinkat, ha azok segítségre szorulnak.
  • -
  • E-mail értesítések küldéséhez — a megadott e-mail címedre küldjük ki az általad igényelt értesítéseket, a szolgáltatásra vonatkozó információkat és a válaszokat a tőled beérkező megkeresésekre.
  • -
- -

Hogyan védjük a tőled gyűjtött információt?

- -

Bitonsági mechanizmusok egész sorát vetjük be annak érdekében, hogy biztosítsuk a tőled származó személyes és használatai adatok és információk biztonságát.

- -

Meddig tároljuk a tőled származó adatokat?

- -

Minden tőlünk telhetőt megteszünk annak érdekében, hogy

- -
    -
  • a szerver naplófájljaiban tárolt, a szerverre érkező kérések küldő-oldali IP-címét maximum 90 napig,
  • -
  • a regisztrált felhasználók tülkjeinek eredeti IP-címét pedig maximum 5 évig
  • -
- -

tároljuk.

- -

Használunk-e sütiket?

- -

Igen. A sütik olyan kisméretű fájlok, amelyeket a szolgáltatások vagy internet-szolgáltatók küldenek a felhasználó számítógépére a böngészőn keresztül (természetesen csak abban az esetben, ha a felhasználó ezt engedélyezi). Oldalunk ezen sütik segítségével ismerik fel a böngésződet és - amennyiben rendelkezel nálunk fiókkal - kötik össze azt a felhasználói fiókoddal.

- -

A sütik segítségével jobban megérthetjük használati szokásaidat, eltárolhatjuk beállításaidat következő látogatásodig, valamint így mérhetjük az oldal látogatottságát és használatát, mely adatok segítenek abban, hogy jobbá tehessük az általunk nyújtott szolgáltatást. Esetenként harmadik féllel is kapcsolatba léphetünk a kinyert használati adatok jobb megértése érdekében. Ezen harmadik felek számára azonban az adatok használata szigorú feltételekhez kötött: kizárólag az engedélyünkkel és királólag a mi szolgáltatásunk fejlesztésével összefüggésben használhatják azokat.

- -

Milyen információt adunk ki külső szereplőknek?

- -

Soha, semmilyen körülmények között nem adunk ki, át vagy el külső szereplőknek olyan adatot, amelynek segítségével egyes felhasználóink egyedileg azonosíthatók. Ez nem vonatkozik olyan harmadik felekre, melyek jelen szolgáltatás üzemeltetésében, javításában vagy támogatásában segítségünkre vannak – ezeket a feleket azonban titoktartási szerződés köti mondott adatokkal kapcsolatban. A gyűjtött adatokat ezen felül megfelelő meghagyás megléte esetén kiadhatjuk a törvény és a rendfenntartás képviselőinek, amennyiben ezen adatoknak jog-, élet- vagy vagyonvédelmi jelentőségük van. Hirdetési- és marketing-, valamint egyéb, a fentiekben nem érintett célból csak olyan adatok adhatók ki, amelyek nem teszik lehetővé az egyes felhasználók egyedi azonosítását.

- -

Harmadik felekre mutató hivatkozások

- -

Esetenként elhelyezhetünk harmadik fél által ajánlott termékekre vagy szolgáltatásokra mutató hivatkozásokat az oldalon. Ezen harmadik feleknek saját, tőlünk független adatvédelmi és adatkezelési nyilatkozatuk van. Ennek értelmében az oldal üzemeltetői semmilyen felelősséget nem tudnak vállalni az ezen harmadik fél által üzemeltetett oldalak viselkedésével és tartalmával kapcsolatban. Ugyanakkor arra törekszünk, hogy mindenben saját felhasználóink érdekeit képviseljük, így minden, a fenti harmadik felekkel kacsolatos visszejelzést szívesen veszünk.

- -

Megfelelés a Gyermekek Online Adatvédelméről Szóló Rendeletnek

- -

Az oldal, valamint az azon keresztül nyújtott szolgáltatás a 13 éven felülieket célozza. Amennyiben ez a szerver az Amerikai Egyesült Államok területén található és te nem vagy még 13 éves, a COPPA (Gyermekek Online Adatvédelméről Szóló Rendelet) értelmében kérjük ne használd ezt az oldalt és szolgáltatást. - -

Az adatvédelmi és adatkezelési nyilatkozat hatálya

- -

Jelen adatvédelmi és adatkezelési nyilatkozat kizárólag az oldalunkon keresztül gyűjtött online adatokra vonatkozik, offline módon gyűjtött adatokra nem terjed ki.

- - - -

Az oldal és a szolgáltatás használatával elfogadottnak tekinted jelen adatvédelmi és adatkezelési nyilatkozatot.

- -

A nyilatkozat módosításairól

- -

Amennyiben a jövőben módosítjuk jelen adatvédelmi és adatkezelési nyilatkozatunkat, a módosított szöveg ugyanezen oldalon lesz megtalálható.

- -

Jelen dokumentum a CC-BY-SA licenc alatt érhető el. Angol eredetijének utolsó módosítása: 2013. május 31.

- -

A dokumetum a Discourse adatvédelmi és adatkezelési nyilatkozatán alapul.

title: "%{instance} Felhasználási feltételek és Adatkezelési nyilatkozat" themes: default: Mastodon diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 629d688c9..b943f0f92 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -689,74 +689,6 @@ ja: reblogged: さんがブースト sensitive_content: 閲覧注意 terms: - body_html: | -

プライバシーポリシー

- -

どのような情報を収集するのですか?

- -

あなたがこのサイトに登録すると、ここで共有された情報を読んだり、書いたり、評価したりして、フォーラムでの情報を集める事ができます。

- -

このサイトに登録する際には、名前とメールアドレスの入力を求めることがあります。ただし、登録をすることなくこのサイトを利用することも可能です。あなたのメールアドレスは、固有のリンクを含んだメールで確認されます。そのリンクにアクセスした場合にメールアドレスを制御することとなります。

- -

アカウントを登録し、投稿を行った際にはその投稿が行われたIPアドレスを記録します。また、このサーバーに対する全てのリクエストはIPアドレスを含むサーバーログとして保管されます。

- -

自分の情報を何に使うのですか?

- -

このサイトで収集された情報は、次のいくつかの方法で使用されます:

- -
    -
  • パーソナライズ・エクスペリエンス — あなたの情報は、あなたや他のユーザーのニーズに対応するために役立ちます。
  • -
  • サイトの改善・最適化 — このサービスはあなたから受け取った情報やフィードバックに基づいて提供されるサイトの改善を行いつづけます。
  • -
  • サービスの向上 — あなたの情報は、ユーザーからの要求やサポートへより効果的に対応するために役立ちます。
  • -
  • 定期メールの送信 — メールアドレスは、情報の送信、トピックの変更やユーザー名に関係するお知らせ、お問い合わせに関する返答、その他のリクエストや質問に関してお知らせするために使用されます。
  • -
- -

自分の情報はどのように保護されるのですか?

- -

このサービスはあなたの個人情報の入力、送信、またはアクセスに際してあなたの個人情報の安全性を維持するために様々なセキュリティ手段をとっています。

- -

データ保持のポリシーはどのようになっていますか?

- -

このサービスはデータ保持に関して次のことを行うよう努めます。:

- -
    -
  • このサーバーへのすべての要求に対して、IPアドレスを含むサーバーログを90日以内に渡って保持します。
  • -
  • 登録されたユーザーとその投稿に関連付けされたIPアドレスを5年以内に渡って保持します。
  • -
- -

クッキーを使用していますか?

- -

はい。クッキーはあなたがウェブブラウザ上で許可した場合にコンピュータのストレージに転送される小さなファイルです。これらのクッキーを使用すると、サイトでブラウザが識別され、登録済みのアカウントを持っている場合は登録済みのアカウントに関連付けがされます。

- -

クッキーを使用して、今後再度閲覧された場合に前回のデータから設定を呼び出したり、今後の改善のためにサイトのトラフィックやサイトの相互作用に関する集計データを作成します。このサービスは、サイトを訪れた方との理解を深めるために、第三者のサービス提供者と契約することがあります。これらのサービス提供者というものは、このサービスでの業務を行ったり、改善するためにこのサービスの代わって収集された情報を使用することはできません。

- -

このサイトは外部に何らかの情報を開示していますか?

- -

私たちは、個人を特定出来る情報を外部へ販売、取引、または他の方法で渡すことはありません。これには、このサイトを操作したり、業務を行ったり、サービスを提供するのに役立つ信頼できる第三者は含まれません。法令遵守、サイトポリシーの施行、このサービスや他の人の権利、財産または安全の保護のために適切であると判断した場合に、あなたの情報を公開する場合があります。ただし、マーケティングや広告、その他の目的で匿名での訪問者情報を他者へ提供することができます。

- -

サードパーティのリンク

- -

必要に応じて、このサービスの方針にもとづいてこのサイトや第三者のサービスを提供することがあります。これらの第三者のサイトには、個別の独立したプライバシーポリシーがあります。従って、これらのリンク先のサイトに関するコンテンツや活動にかんしては一切責任を負いません。ですが、サイトの完全性やこれらのサイトに関するフィードバックは非常に重要なものであると認識しております。

- -

子供のオンライン・プライバシー保護法

- -

このサイト、製品、サービスはすべて13歳以上の人を対象としております。このサーバーが米国にあり、13歳未満の場合はCOPPA (Children's Online Privacy Protection Act) にもとづいてこのサイトを使用しないでください。

- -

オンライン限定のプライバシーポリシー

- -

このオンライン・プライバシーポリシーは、このサイトを通じて収集された情報のみに適用され、オフラインで収集される情報には適用されません。

- - - -

このサービスを使用することにより、このサイトのプライバシーポリシーに同意するものとします。

- -

プライバシーポリシーの変更

- -

プライバシーポリシーを変更する場合は、このページへ変更内容を掲載します。

- -

この文章のライセンスはCC-BY-SAです。このページは2017年5月6日が最終更新です。

- -

オリジナルの出典 Discourse privacy policy.

title: "%{instance} 利用規約・プライバシーポリシー" themes: default: Mastodon diff --git a/config/locales/ko.yml b/config/locales/ko.yml index ba55b3549..72c98bc30 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -667,74 +667,6 @@ ko: reblogged: 님이 부스트 했습니다 sensitive_content: 민감한 컨텐츠 terms: - body_html: | -

사생활 정책

- -

우리가 어떤 정보를 수집하나요?

- -

우리는 귀하가 우리 사이트에 가입할 때, 그리고 우리의 포럼에 읽고, 쓸 때, 그리고 포럼에 게시된 공유된 콘텐츠를 평가할 때 정보를 수집합니다.

- -

우리 사이트에 가입할 때, 귀하는 이름과 이메일 주소 입력을 요구 받을 수 있습니다. 하지만 귀하는 우리의 사이트를 가입하지 않고도 방문할 수 있습니다. 귀하의 이메일 주소는 고유한 링크를 담고 있는 이메일로 검증 될 것입니다. 만약 귀하가 그 링크를 방문한다면, 우리는 귀하가 그 이메일 주소를 소유하고 있다는 것을 알 수 있습니다.

- -

가입을 하고 글을 쓸 때, 우리는 글이 어떤 IP에서 작성 되었는지 기록합니다. 또한 우리는 모든 요청에 대한 IP 주소를 담고 있는 서버 로그를 보관할 수 있습니다.

- -

우리가 귀하의 정보를 어떻게 사용하나요?

- -

우리가 귀하에게서 수집하는 어떠한 정보는 다음 중 하나와 같은 방법으로 사용될 수 있습니다:

- -
    -
  • 귀하의 경험을 개인화 하기 위해 — 귀하의 정보는 우리가 귀하의 개별적인 요구에 더 나은 응답을 할 수 있도록 돕습니다.
  • -
  • 우리의 사이트를 개선하기 위해 — 우리는 귀하에게 받는 정보와 귀하에게 받는 피드박을 바탕으로 우리의 사이트 내용을 계속 개선하기 위해 노력합니다.
  • -
  • 고객 서비스를 개선하기 위해 — 귀하의 정보는 우리가 귀하의 서비스 요청과 지원 요청에 더 효과적으로 응답할 수 있게 돕습니다.
  • -
  • 주기적인 이메일을 보내기 위해 — 귀하가 제공하는 이메일 주소는 귀하에게 정보, 귀하가 요청하는 주제에 대한 변경과 귀하의 유저 이름에 대한 응답에 대한 알림, 문의에 대한 답, 또는 다른 요청과 질문을 보내는 데에 사용될 수 있습니다.
  • -
- -

우리가 어떻게 귀하의 정보를 보호하나요?

- -

우리는 귀하가 개인정보를 입력, 제출, 접근 할 때 귀하의 개인정보의 안전을 유지하기 위한 여러가지 보안 방법을 구현합니다.

- -

정보 보관 정책은 어떻게 되나요?

- -

우리는 다음과 같이 노력 하겠습니다:

- -
    -
  • 모든 요청에 대한 IP 주소를 담고 있는 서버 로그를 최대 90일까지 보관합니다.
  • -
  • 등록된 사용자와 관련된 IP 주소와 그들의 게시물들을 최대 5년까지 보관합니다.
  • -
- -

쿠키를 사용하나요?

- -

네. 쿠키는 사이트나 서비스 제공자가 (만약 허용하신다면) 웹 브라우저를 통해 귀하의 컴퓨터 하드디스크에 전송하는 작은 파일들입니다. 이 쿠키들은 사이트가 귀하의 브라우저를 인식하게 하고, 만약 가입한 계정이 있다면 브라우저를 가입한 계정과 연관짓는 일을 가능하게 합니다.

- -

우리는 쿠키를 사용해 귀하의 환경설정을 미래의 방문을 위해 저장하고, 사이트 접근 기록과 사이트 상호작용 기록을 모아 미래에 우리가 더 나은 사이트 경험과 도구를 제공할 수 있도록 합니다. 우리는 제 3자의 서비스 제공자와 계약하여 우리 사이트의 방문자에 대해 더 나은 이해를 하기 위해 도움을 받을 수 있습니다. 이러한 서비스 제공자들은 우리가 더 나은 서비스를 제공하도록 돕는 목적 외에는 이 정보를 사용할 수 없습니다.

- -

우리가 외부에 정보를 공개하나요?

- -

우리는 귀하를 식별할 수 있는 정보를 외부에 팔거나, 거래하거나, 전송하지 않습니다. 이는 우리가 우리의 사이트를 운영하고, 사업을 하고, 귀하에게 서비스를 제공하는 데에 도움을 주는 믿을 수 있는 제 3자의 서비스 제공자를 포함하지 않으며, 이는 그 서비스 제공자가 이 정보를 비밀로 취급하는 것에 동의하는지에 따라 다릅니다. 우리는 또한 법을 지키는 것, 우리 사이트의 정책을 집행하는 것, 우리와 다른 사람들의 권리, 재산, 안전을 보호하는 것으로 인해 정보 공개가 적합하다고 생각되면 정보를 공개 할 수 있습니다. 그러나, 귀하를 식별할 수 없는 방문자 정보는 외부에 마케팅, 광고, 혹은 다른 용도로 제공될 수 있습니다.

- -

제 3자 링크

- -

종종, 우리의 재량에 따라 우리의 사이트에 제 3자의 상품이나 서비스를 포함하거나 제공할 수 있습니다. 이러한 제 3자 사이트는 독립적인 개인정보 정책을 가지고 있습니다. 이러한 링크된 제 3자 사이트의 내용과 활동에 대해서 우리는 어떠한 의무와 법적 책임을 가지고 있지 않습니다. 그래도 우리는 그 사이트에 대한 피드백을 환영하며, 우리 사이트만의 정체성을 유지하도록 노력하겠습니다.

- -

아동 온라인 사생활 보호법 준수

- -

우리 사이트, 제품과 서비스는 적어도 13살인 사람들에게 맞춰져 있습니다. 만약 이 서버가 미합중국에 위치하고, 귀하가 13살이 되지 않는다면, COPPA (Children's Online Privacy Protection Act) 의 요구사항에 따라 이 사이트를 이용하지 마십시오.

- -

온라인 사생활 정책 한정

- -

이 온라인 사생활 정책은 우리 사이트를 통해 수집된 정보에게만 적용되며, 오프라인에서 수집된 정보에는 적용되지 않습니다.

- - - -

우리의 사이트를 사용함으로서, 귀하는 우리 사이트의 사생활 정책에 동의합니다.

- -

사생활 정책의 변경

- -

만약 우리가 사생활 정책을 변경하도록 결정한다면, 우리는 그 변경사항을 이 페이지에 게시하겠습니다.

- -

이 문서는 CC-BY-SA 정책으로 배포됩니다. 마지막으로 2013년 3월 31일에 수정되었습니다.

- -

Discourse privacy policy에서 가져옴.

title: "%{instance} 이용약관과 개인정보 취급 방침" themes: default: 마스토돈 diff --git a/config/locales/nl.yml b/config/locales/nl.yml index f3488f708..a46bb72d7 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -665,74 +665,6 @@ nl: reblogged: boostte sensitive_content: Gevoelige inhoud terms: - body_html: | -

Privacy Policy

- -

What information do we collect?

- -

We collect information from you when you register on our site and gather data when you participate in the forum by reading, writing, and evaluating the content shared here.

- -

When registering on our site, you may be asked to enter your name and e-mail address. You may, however, visit our site without registering. Your e-mail address will be verified by an email containing a unique link. If that link is visited, we know that you control the e-mail address.

- -

When registered and posting, we record the IP address that the post originated from. We also may retain server logs which include the IP address of every request to our server.

- -

What do we use your information for?

- -

Any of the information we collect from you may be used in one of the following ways:

- -
    -
  • To personalize your experience — your information helps us to better respond to your individual needs.
  • -
  • To improve our site — we continually strive to improve our site offerings based on the information and feedback we receive from you.
  • -
  • To improve customer service — your information helps us to more effectively respond to your customer service requests and support needs.
  • -
  • To send periodic emails — The email address you provide may be used to send you information, notifications that you request about changes to topics or in response to your user name, respond to inquiries, and/or other requests or questions.
  • -
- -

How do we protect your information?

- -

We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information.

- -

What is your data retention policy?

- -

We will make a good faith effort to:

- -
    -
  • Retain server logs containing the IP address of all requests to this server no more than 90 days.
  • -
  • Retain the IP addresses associated with registered users and their posts no more than 5 years.
  • -
- -

Do we use cookies?

- -

Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account.

- -

We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.

- -

Do we disclose any information to outside parties?

- -

We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.

- -

Third party links

- -

Occasionally, at our discretion, we may include or offer third party products or services on our site. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.

- -

Children's Online Privacy Protection Act Compliance

- -

Our site, products and services are all directed to people who are at least 13 years old. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA (Children's Online Privacy Protection Act) do not use this site.

- -

Online Privacy Policy Only

- -

This online privacy policy applies only to information collected through our site and not to information collected offline.

- - - -

By using our site, you consent to our web site privacy policy.

- -

Changes to our Privacy Policy

- -

If we decide to change our privacy policy, we will post those changes on this page.

- -

This document is CC-BY-SA. It was last updated May 31, 2013.

- -

Originally adapted from the Discourse privacy policy.

title: "%{instance} Terms of Service and Privacy Policy" themes: default: Mastodon diff --git a/config/locales/no.yml b/config/locales/no.yml index 3adf71bee..d5edb3975 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -633,74 +633,6 @@ reblogged: fremhevde sensitive_content: Følsomt innhold terms: - body_html: | -

Personvernserklæring

- -

Hvilke opplysninger samler vi?

- -

Vi samler opplysninger fra deg når du registrerer deg på nettstedet vårt, og vi samler data når du deltar på forumet ved å lese, skrive og evaluere innholdet som deles her.

- -

Når du registrerer deg på nettstedet vårt, kan du bli bedt om å oppgi navnet og e-postadressen din. Imidlertid kan du besøke nettstedet vårt uten å registrere deg. E-postadressen din vil bli bekreftet med en e-post som inneholder en unik lenke. Hvis siden den lenker til, blir besøkt, vet vi at du har kontroll over e-postadressen.

- -

Når du registrerer deg og skriver innlegg, registrerer vi IP-adressen som innlegget stammer fra. Vi kan også oppbevare logger som inkluderer IP-adressen til alle forespørslene sendt til tjeneren vår.

- -

Hva bruker vi opplysningene dine til?

- -

Alle opplysningene vi samler fra deg, kan bli brukt på en av følgende måter:

- -
    -
  • For å gjøre opplevelsen din mer personlig. Opplysningene dine hjelper oss å svare bedre på dine individuelle behov.
  • -
  • For å forbedre nettstedet vårt. Vi jobber konstant for å forbedre nettstedets tilbud basert på opplysningene og tilbakemeldingene vi mottar fra deg.
  • -
  • For å forbedre vår kundeservice. Dine opplysninger hjelper oss å svare mer effektivt på dine forespørsler sendt til kundeservice eller behov om støtte.
  • -
  • For å sende periodiske e-poster. E-postadressen du oppgir, kan bli brukt til å sende deg informasjon, påminnelser som du ber om ved endringer av emner eller ved svar til brukernavnet ditt, til henvendelser, og/eller andre forspørsler eller andre spørsmål.
  • -
- -

Hvordan sikrer vi opplysningene?

- -

Vi gjennomfører flere sikkerhetstiltak for å holde personopplysningene dine sikre når du skriver inn, lagrer eller henter dem.

- -

Hva er retningslinjene deres for lagring av data?

- -

Vi vil forsøke i god tro å:

- -
    -
  • Ikke oppbevare tjener-logger som inneholder IP-adressen til alle forespørslene til denne tjeneren i lenger enn i 90 dager.
  • -
  • Ikke oppbevare IP-adressene forbundet med registrerte brukere og deres innlegg lenger enn i 5 år.
  • -
- -

Bruker vi informasjonskapsler?

- -

Ja. Informasjonskapsler er små filer som et nettsted eller dets tjenesteleverandør overfører til harddisken på datamaskinen din gjennom nettleseren din (dersom du tillater det). Disse informasjonskapslene gjør det mulig for nettstedet å gjenkjenne nettleseren din og, dersom du har en konto, knytte nettleseren til den.

- -

Vi bruker informasjonskapsler for å forstå og lagre preferansene dine for fremtidige besøk og for å samle aggregatdata om trafikk på og samhandling med nettstedet slik at vi kan tilby bedre opplevelser og verktøy på nettstedet i fremtiden. Vi kan inngå avtaler med tredjeparts tjenesteleverandører for å bistå oss i å forstå besøkerne våres bedre. Disse tjenesteleverandørene har ikke lov til å bruke opplysningene samlet på våres vegne unntatt til å hjelpe oss å gjennomføre og forbedre anliggendet vårt.

- -

Gir vi noen opplysninger videre til andre parter?

- -

Vi verken selger, handler med eller overfører på noen annen måte til andre parter dine identifiserbare personopplysninger. Dette inkluderer ikke tredjeparter som har vår tillit og bistår oss i å drive nettstedet, utføre våre anliggender eller yter tjenester til deg, så lenge disse partene samtykker til å behandle disse opplysningene fortrolig. Vi kan også frigi opplysningene dine dersom vi tror at å frigi dem er hensiktsmessig for å overholde loven, håndheve nettstedet retningslinjer eller beskytte våre og andres rettigheter. Imidlertid kan opplysninger som ikke er personlig identifiserbare, bli delt med andre parter for markedsføring, reklame eller annet bruk.

- -

Tredjeparts lenker

- -

Av og til, etter skjønn, kan vil inkludere eller tilby tredjeparts produkter eller tjenester på nettstedet vårt. Disse tredjeparts nettstedene har separate og selvstendige personvernerklæringer. Vi bærer derfor intet ansvar eller forpliktelser for innholdet eller aktivitetene til disse nettstedene det lenkes til. Ikke mindre prøver vi å bevare vår eget nettsteds integritet og ønsker enhver tilbakemelding om disse nettstedene velkomne.

- -

Overensstemmelse med Children's Online Privacy Protection Act

- -

Nettstedet er rettet mot folk som er minst 13 år gamle. Dersom denne tjeneren er i USA, og du er under 13 år i henhold til kravene i COPPA (Children's Online Privacy Protection Act), ikke bruk dette nettstedet.

- -

Personvernerklæring bare for nettet

- -

Denne nett-personvernerklæringen gjelder bare for informasjon samlet gjennom nettstedet vårt og ikke for opplysninger samlet når en er frakoblet.

- - - -

Ved å bruke dette nettstedet samtykker du til nettstedets personvernerklæring.

- -

Endringer i vår personvernerklæring

- -

Dersom vi beslutter å endre personvernerklæringen vår, vil vi publisere disse endringene på denne siden.

- -

Dette dokumentet er lisensiert under CC-BY-SA. De ble sist oppdatert 12. april 2017.

- -

Dokumentet er en adoptert og endret versjon fra Discourse privacy policy.

title: "%{instance} Personvern og villkår for bruk av nettstedet" themes: default: Mastodon diff --git a/config/locales/oc.yml b/config/locales/oc.yml index 49b1df8bf..f8e819c53 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -742,74 +742,6 @@ oc: reblogged: a partejat sensitive_content: Contengut sensible terms: - body_html: | -

Politica de confidencialitat

- -

Quinas informacions reculhèm ?

- -

Collectem informacions sus vos quand vos marcatz sus nòstre site e juntem las donadas quand participatz a nòstre forum en legir, escriure e notar lo contengut partejat aquí.

- -

Pendent l’inscripcion podèm vos demandar vòstre nom e adreça de corrièl. Podètz çaquelà visitar nòstre site sens vos marcar. Verificarem vòstra adreça amb un messatge donant un ligam unic. Se clicatz sul ligam sauprem qu’avètz lo contraròtle de l’adreça.

- -

Quand sètz marcat e que publicatz quicòm, enregistrem l’adreça IP d’origina. Podèm tanben salvagardar los jornals del servidor que tenon l’adreça IP de totas las demandas fachas al nòstre servidor.

- -

Qué fasèm de vòstras informacions ?

- -

Totas las informacions que collectem de vos pòdon servir dins los cases seguents :

- -
    -
  • Per personalizar vòstre experiéncia — vòstras informacions nos ajudaràn a respondre melhor a vòstres besonhs individuals.
  • -
  • Per melhorar nòstre site — s’eforcem de longa a melhorar çò que nòstre site ofrís segon las informacions e los comentaris que recebèm de vòstra part.
  • -
  • Per melhorar nòstre servici client — vòstras informacions nos ajudan per respondre amb mai eficacitat a vòstras demandas d’assisténcia.
  • -
  • Per enviar periodicament de corrièls — Podèm utilizar l’adreça qu’avètz donada per vos enviar d’informacions e de notificacions que demandatz tocant de cambiaments dins los subjèctes del forum o en responsa a vòstre nom d’utilizaire, en responsa a una demanda, e/o tota autra question.
  • -
- -

Cossí protegèm vòstras informacions ?

- -

Apliquem tota una mena de mesuras de seguretat per manténer la fisança de vòstras informacions personalas quand las picatz, mandatz, o i accedètz.

- -

Quala es vòstra politica de conservacion de donadas ?

- -

Farem esfòrces per :

- -
    -
  • Gardar los jornals del servidor que contenon las adreças IP de totas las demandas al servidor pas mai de 90 jorns.
  • -
  • Gardar las adreças IP ligadas als utilizaires e lors publicacions pas mai de 5 ans.
  • -
- -

Empleguem de cookies ?

- -

Òc-ben. Los cookies son de pichons fichièrs qu’un site o sos provesidors de servicis plaçan dins lo disc dur de vòstre ordenador via lo navigator Web (Se los acceptatz). Aqueles cookies permeton al site de reconéisser vòstre navigator e se tenètz un compte enregistrat de l’associar a vòstre compte.

- -

Empleguem de cookies per comprendre e enregistrar vòstras preferéncias per vòstras visitas venentas, per recampar de donadas sul trafic del site e las interaccions per dire que posquem ofrir una melhora experiéncia del site e de las aisinas pel futur. Pòt arribar que contractèssem amb de provesidors de servicis tèrces per nos ajudar a comprendre melhor nòstres visitors. Aqueles provesidors an pas lo drech que d’utilizar las donadas collectadas per nos ajudar a menar e melhorar nòstre afar.

- -

Divulguem d’informacions a de tèrces ?

- -

Vendèm pas, comercem o qualque transferiment que siasque a de tèrces vòstras informacions personalas identificablas. Aquò inclutz pas los tèrces partits de confisança que nos assiston a menar nòstre site, menar nòstre afar o vos servir, baste que son d’acòrd per gardar aquelas informacions confidencialas. Pòt tanben arribar que liberèssem vòstras informacions quand cresèm qu’es apropriat d’o far per se sometre a la lei, per refortir nòstras politicas, o per protegir los dreches, proprietats o seguritat de qualqu’un o de nosautres. Pasmens es possible que mandèssem d’informacions non-personalas e identificablas de nòstres visitors a d’autres partits per d’utilizacion en marketing, publicitat o un emplec mai.

- -

Ligams de tèrces

- -

Pòt arribar, a nòstra discrecion, qu’incluguèssem o ofriguèssem de produches o servicis de tèrces partits sus nòstre site. Aqueles sites tèrces an de politicas de confidencialitats separadas e independentas. En consequéncia avèm pas cap de responsabilitat pel contengut e las activitats d’aqueles sites ligats. Pasmens cerquem de protegir l’integritat de nòstre site e aculhèm los comentaris tocant aqueles sites.

- -

Conformitat amb la lei de proteccion de la confidencialitat dels mainatges

- -

Nòstre site, nòstres produches e servicis son totes destinats a de monde d’almens 13 ans. S’aqueste servidor se tròba en los Estats Units per acontentar las exigéncias del COPPA (Children's Online Privacy Protection Act) utilizetz pas aqueste site.

- -

Politica de confidencialitat en linha solament

- -

Aquesta politica de confidencialitat s’aplica pas qu’a las informacions collectadas via nòstre site e non pas a las informacions collectadas fòra linha.

- - - -

N’utilizant nòstre site, consentètz a nòstra politica de confidencialitat.

- -

Cambiament dins nòstra politica de confidencialitat

- -

Se decidèm de cambiar nòstra politica de confidencialitat, publicarem los cambiaments sus aquesta pagina.

- -

Aqueste document es jos licéncia CC-BY-SA. Darrièra mesa a jorn lo 31 de mai de 2013

- -

Prima adaptacion de la politica de confidencialitat de Discourse.

title: Condicions d’utilizacion e politica de confidencialitat de %{instance} time: formats: diff --git a/config/locales/pl.yml b/config/locales/pl.yml index c62636081..64c1ff5ea 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -700,74 +700,6 @@ pl: reblogged: podbił sensitive_content: Wrażliwa zawartość terms: - body_html: | -

Polityka prywatności

- -

Jakie informacje zbieramy?

- -

Zbieramy informacje podane przy rejestracji i treści utworzone w trakcie korzystania z serwisu.

- -

Podczas rejestracji, możesz otrzymać prośbę o podanie adresu e-mail. Możesz jednak odwiedzać stronę bez rejestracji. Adres zostanie zweryfikowany przez kliknięcie w link wysłany w wiadomości. Dzięki temu wiemy, że jesteś właścicielem tego adresu.

- -

Podczas rejestracji i tworzenia wpisów, Twój adres IP jest zapisywany na naszych serwerach. Możemy też przechowywać adres IP użyty przy każdej operacji w serwisie.

- -

Jak wykorzystujemy zebrane informacje?

- -

Zebrane informacje mogą zostać w jednym z następujących celach:

- -
    -
  • Aby poprawić wrażenia — informacje o Tobie pomagają w dostosowywaniu serwisu do Twoich potrzeb.
  • -
  • Aby usprawnić stronę — nieustannie staramy się ulepszyć stronę na podstawie informacji o Tobie i Twoich opinii.
  • -
  • Aby usprawnić obsługę klienta — informacje pomogą obsłudze klienta utrzymywać kontakt z Tobą.
  • -
  • Aby okazjonalnie wysyłać wiadomości e-mail — Na podany adres e-mail mogą zostać wysłane wiadomości o wspomnieniu o Tobie we wpisach, przejrzeniu Twojego zgłoszenia i innych interakcji z Tobą.
  • -
- -

Jak zabezpieczamy dane?

- -

Korzystamy z wielu zabezpieczeń, aby utrudnić osobom niepowołanym dostęp do danych, które wprowadzasz, publikujesz i czytasz.

- -

Jak długo przechowujecie dane?

- -

Dołożymy wszelkich starań, aby przechowywać:

- -
    -
  • dzienniki serwera zawierające adresy IP przypisane do każdych operacji nie dłużej niż 90 dni.
  • -
  • adresy IP przypisane do użytkowników i ich wpisów nie dłużej niż 5 lat.
  • -
- -

Czy używamy plików cookies?

- -

Tak. Pliki cookies (zwane często ciasteczkami) są małymi zbiorami danych przechowywanych na Twoim dysku przez stronę internetową, aby rozpoznawać przeglądarkę i powiązać ją (jeżeli jesteś zarejestrowany/a) z Twoim kontem, jeżeli na to pozwolisz.

- -

Możemy używać ciasteczek, aby skonfigurować stronę na podstawie zapisanych preferencji, oraz dostosować ją do potrzeb innych użytkowników. Możemy korzystać z usług firm trzecich pomagających w zrozumieniu potrzeb użytkownika. Te usługi nie mogą korzystać ze zdobytych danych w celach innych niż analiza pomagająca ulepszać ten serwis.

- -

Czy przekazujecie dane podmiotów trzecim?

- -

Nie dokonujemy transakcji danych pozwalających na identyfikację Twojej osoby umieszczonych na tym serwisie. Nie oznacza to, że nie przekazujemy ich zaufanym podmiotom, które korzystają z nich poufnie. Możemy jednak udostępniać dane, jeżeli jest to wymagane prawnie, lub dla utrzymania bezpieczeństwa strony i innych użytkowników. W celach marketingowych (i podobnych) mogą zostać użyte jedynie dane niepozwalające na identyfikację osoby.

- -

Odnośniki do treści stron trzecich

- -

Czasem na stronie mogą pojawić się odnośniki do stron trzecich. Mają one odrębne regulaminy i politykę prywatności. Nie odpowiadamy więc za zawartość tych stron. Dokładamy jednak wszelkich starań, aby nie stanowiły one zagrożenia, prosimy jednak o opinie na temat ich wykorzystania.

- -

Children's Online Privacy Protection Act Compliance

- -

Ta strona i usługa jest przeznaczona dla osób, które ukończyły 13 lat. Jeżeli serwer znajduje się na terenie USA i nie masz ukończonych 13 lat, zgodnie z amerykańską ustawą COPPA (Children's Online Privacy Protection Act) nie możesz korzystać z tego serwisu.

- -

Polityka prywatności dotyczy tylko Internetu

- -

Ta polityka prywatności dotyczy jedynie danych zbieranych w Internecie, nie tych, które przechowywane są na Twoim komputerze, np. pliki cookies.

- - - -

Korzystanie ze strony jest równoznaczne z akceptacją naszej polityki prywatności.

- -

Zmiany w naszej polityce prywatności

- -

Jeżeli zdecydujemy się na zmiany w polityce prywatności, zmiany pojawią się na tej stronie.

- -

Dokument jest dostępny na licencji CC-BY-SA. Ostatnio modyfikowany 31 maja 2013, przetłumaczony 4 lipca 2017. Tłumaczenie (mimo dołożenia wszelkich starań) może nie być w pełni poprawne.

- -

Tekst bazuje na polityce prywatności Discourse.

title: Zasady korzystania i polityka prywatności %{instance} themes: default: Mastodon diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index 589f44fa1..c1225d356 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -665,74 +665,6 @@ pt-BR: reblogged: compartilhou sensitive_content: Conteúdo sensível terms: - body_html: | -

Política de privacidade

- -

Que informações nós coletamos?

- -

Coletamos informações quando você se cadastra em nosso site e capturamos dados quando você participa do fórum lendo, escrevendo e analisando o conteúdo aqui compartilhado.

- -

Quando você se registrar em nosso site, será requisitado que você ceda seu nome e endereço de e-mail. Você pode, porém, visitar nosso site sem se cadastrar. Seu endereço de e-mail será verificado por uma mensagem contendo um link único. Se este link for visitado, saberemos que você controla este endereço de e-mail.

- -

Quando registrado e postando, nós gravamos o endereço de IP de onde a postagem se originou. Nós também podemos reter logs de serviores que incluem o endereço de IP em cada requisição para o nosso servidor.

- -

Para que usamos essas informações?

- -

Quaisquer das informações que coletamos podem ser usadas das seguintes formas:

- -
    -
  • Para personalizar a sua experiência — suas informações nos ajudam a nos adequar melhor às suas necessidades individuais.
  • -
  • Para melhorar nosso site — nós continuamente nos esforçamos para aprimorar nosso site baseados na informação e no feedback que recebemos de você.
  • -
  • Para aprimorar o serviço ao consumidor — suas informações nos ajudam a responder efetivamente às suas requisições e solicitações por suporte.
  • -
  • Para mandar e-mails periódicos — O endereço de e-mail que você forneceu pode ser usado para lhe enviar informações, notificações que você requisitar sobre mudanças a determinados tópicos ou menções ao seu nome de usuário, responder requisições e/ou solicitações e perguntas.
  • -
- -

Como protegemos as suas informações?

- -

Nós implementamos uma variedade de medidas de segurança para manter a segurança de suas informações pessoais quando você insere, submete ou acessa as suas informações pessoais.

- -

Qual a sua política de retenção de dados?

- -

Faremos esforços de boa fé para:

- -
    -
  • Reter logs de servidor contendo o endereço IP de todas as requisições a este servidor por não mais que 90 dias.
  • -
  • Reter os endereços IP associados a usuários cadastrados e suas postagens por não mais que 5 anos.
  • -
- -

Nós usamos cookies?

- -

Sim. Cookies são pequenos arquivos que um site ou o provedor de serviço transfere para o armazenamento interno de seu computador através de seu navegador (se você permitir). Estes cookies habilitam o site para reconhecer o seu navegador e, se você ter um cadastro, associá-lo a esta conta.

- -

Nós usamos cookies para entender e salvar as suas preferências para futuras visitas e compilar dados agregados sobre o tráfego do site para que possamos oferecer melhores experiências e ferramentas no futuro. Nós podemos contratar serviços de terceiros para nos auxiliar a entender melhor nossos visitantes. Estes provedores de serviço não são autoriza usar as informações coletadas em nosso nome exceto para nos ajudar a conduzir e aprimorar nosso funcionamento.

- -

Nós revelamos informações para terceiros?

- -

Nós não vendemos, tocamos ou transferimos para terceiros informações pessoais que te identificam. Isso não inclui partes em que confiamos para nos ajudar a operar nosso site, conduzir nosso funcionamento ou servir você desde que estes terceiros concordem em manter essas informações em segredo. Nós também podemos prover as suas informações para obedecer ordens judiciais, reforçar nossas políticas ou proteger nossos direitos ou de outrem, propriedades ou segurança. Entretanto, informações pessoais não identificáveis podem ser enviadas para outras partes para marketing, propaganda e outros usos.

- -

Links de terceiros

- -

Ocasionalmente, à nossa discrição, podemos icluir ou oferecer produtos ou serviços de terceiros em nosso site. Estes terceiros têm políticas de privacidade separadas e independentes. Nós, portanto, não nos responsabilizamos pelo conteúdo e atividades destes sites de terceiros. Occasionally, at our discretion, we may include or offer third party products or services on our site. Não obstante, nós procuramos proteger a integridade de nosso site e todo feedback sobre estes sites de terceiros é bem-vindo.

- -

Obediência ao Ato de Proteção da Privacidade Online de Crianças

- -

Nosso site, produtos e serviços são todos direcionados a pessoas que têm pelo menos 13 anos de idade. Se este servidor estiver nos EUA, e você tiver menos de 13 anos, pelos requerimentos da COPPA (Children's Online Privacy Protection Act) não use este site.

- -

Política de Apenas Privacidade Online

- -

Esta política de privacidade online se aplica somente a informações coletadas por nosso site e não a informações coletadas offline.

- - - -

Usando o nosso site, você concorda com a nossa política de privacidade.

- -

Mudanças em nossa Política de Privacidade

- -

Se decidirmos mudar a nossa política de privacidade, publicaremos as mudanças nesta página.

- -

Este documento é CC-BY-SA. A sua última atualização aconteceu em 31 de maio de 2013.

- -

Originalmente adaptado da política de privacidade do Discourse.

title: "%{instance} Termos de Serviço e Política de Privacidade" themes: default: Mastodon diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 5012e176f..27d4e88e3 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -635,74 +635,6 @@ pt: reblogged: boosted sensitive_content: Conteúdo sensível terms: - body_html: | -

Política de privacidade

- -

Quais são as informações que recolhemos?

- -

Recolhemos informações quando te registas no nosso site e capturamos dados quando participas do fórum lendo, escrevendo e analisando o conteúdo aqui partilhado.

- -

Quando te registas no nosso site, será requisitado que você ceda seu nome e endereço de e-mail. Você pode, porém, visitar nosso site sem se cadastrar. Seu endereço de e-mail será verificado por uma mensagem contendo um link único. Se este link for visitado, saberemos que você controla este endereço de e-mail.

- -

Quando registrado e postando, nós gravamos o endereço de IP de onde a postagem se originou. Nós também podemos reter logs de serviores que incluem o endereço de IP em cada requisição para o nosso servidor.

- -

Para que usamos essas informações?

- -

Quaisquer das informações que coletamos podem ser usadas das seguintes formas:

- -
    -
  • Para personalizar a sua experiência — suas informações nos ajudam a nos adequar melhor às suas necessidades individuais.
  • -
  • Para melhorar nosso site — nós continuamente nos esforçamos para aprimorar nosso site baseados na informação e no feedback que recebemos de você.
  • -
  • Para aprimorar o serviço ao consumidor — suas informações nos ajudam a responder efetivamente às suas requisições e solicitações por suporte.
  • -
  • Para mandar e-mails periódicos — O endereço de e-mail que você forneceu pode ser usado para lhe enviar informações, notificações que você requisitar sobre mudanças a determinados tópicos ou menções ao seu nome de usuário, responder requisições e/ou solicitações e perguntas.
  • -
- -

Como protegemos as suas informações?

- -

Nós implementamos uma variedade de medidas de segurança para manter a segurança de suas informações pessoais quando você insere, submete ou acessa as suas informações pessoais.

- -

Qual a sua política de retenção de dados?

- -

Faremos esforços de boa fé para:

- -
    -
  • Reter logs de servidor contendo o endereço IP de todas as requisições a este servidor por não mais que 90 dias.
  • -
  • Reter os endereços IP associados a usuários cadastrados e suas postagens por não mais que 5 anos.
  • -
- -

Nós usamos cookies?

- -

Sim. Cookies são pequenos arquivos que um site ou o provedor de serviço transfere para o armazenamento interno de seu computador através de seu navegador (se você permitir). Estes cookies habilitam o site para reconhecer o seu navegador e, se você ter um cadastro, associá-lo a esta conta.

- -

Nós usamos cookies para entender e salvar as suas preferências para futuras visitas e compilar dados agregados sobre o tráfego do site para que possamos oferecer melhores experiências e ferramentas no futuro. Nós podemos contratar serviços de terceiros para nos auxiliar a entender melhor nossos visitantes. Estes provedores de serviço não são autoriza usar as informações coletadas em nosso nome exceto para nos ajudar a conduzir e aprimorar nosso funcionamento.

- -

Nós revelamos informações para terceiros?

- -

Nós não vendemos, tocamos ou transferimos para terceiros informações pessoais que te identificam. Isso não inclui partes em que confiamos para nos ajudar a operar nosso site, conduzir nosso funcionamento ou servir você desde que estes terceiros concordem em manter essas informações em segredo. Nós também podemos prover as suas informações para obedecer ordens judiciais, reforçar nossas políticas ou proteger nossos direitos ou de outrem, propriedades ou segurança. Entretanto, informações pessoais não identificáveis podem ser enviadas para outras partes para marketing, propaganda e outros usos.

- -

Links de terceiros

- -

Ocasionalmente, à nossa discrição, podemos icluir ou oferecer produtos ou serviços de terceiros em nosso site. Estes terceiros têm políticas de privacidade separadas e independentes. Nós, portanto, não nos responsabilizamos pelo conteúdo e atividades destes sites de terceiros. Occasionally, at our discretion, we may include or offer third party products or services on our site. Não obstante, nós procuramos proteger a integridade de nosso site e todo feedback sobre estes sites de terceiros é bem-vindo.

- -

Obediência ao Ato de Proteção da Privacidade Online de Crianças

- -

Nosso site, produtos e serviços são todos direcionados a pessoas que têm pelo menos 13 anos de idade. Se este servidor estiver nos EUA, e você tiver menos de 13 anos, pelos requerimentos da COPPA (Children's Online Privacy Protection Act) não use este site.

- -

Política de Apenas Privacidade Online

- -

Esta política de privacidade online se aplica somente a informações coletadas por nosso site e não a informações coletadas offline.

- - - -

Usando o nosso site, você concorda com a nossa política de privacidade.

- -

Mudanças em nossa Política de Privacidade

- -

Se decidirmos mudar a nossa política de privacidade, publicaremos as mudanças nesta página.

- -

Este documento é CC-BY-SA. A sua última atualização aconteceu em 31 de maio de 2013.

- -

Originalmente adaptado da política de privacidade do Discourse.

title: "%{instance} Termos de Serviço e Política de Privacidade" themes: default: Mastodon diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 108ca33e9..176ace92d 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -679,45 +679,6 @@ ru: reblogged: продвинул(а) sensitive_content: Чувствительный контент terms: - body_html: | -

Privacy Policy

-

What information do we collect?

-

We collect information from you when you register on our site and gather data when you participate in the forum by reading, writing, and evaluating the content shared here.

-

When registering on our site, you may be asked to enter your name and e-mail address. You may, however, visit our site without registering. Your e-mail address will be verified by an email containing a unique link. If that link is visited, we know that you control the e-mail address.

-

When registered and posting, we record the IP address that the post originated from. We also may retain server logs which include the IP address of every request to our server.

-

What do we use your information for?

-

Any of the information we collect from you may be used in one of the following ways:

-
    -
  • To personalize your experience — your information helps us to better respond to your individual needs.
  • -
  • To improve our site — we continually strive to improve our site offerings based on the information and feedback we receive from you.
  • -
  • To improve customer service — your information helps us to more effectively respond to your customer service requests and support needs.
  • -
  • To send periodic emails — The email address you provide may be used to send you information, notifications that you request about changes to topics or in response to your user name, respond to inquiries, and/or other requests or questions.
  • -
-

How do we protect your information?

-

We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information.

-

What is your data retention policy?

-

We will make a good faith effort to:

-
    -
  • Retain server logs containing the IP address of all requests to this server no more than 90 days.
  • -
  • Retain the IP addresses associated with registered users and their posts no more than 5 years.
  • -
-

Do we use cookies?

-

Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account.

-

We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.

-

Do we disclose any information to outside parties?

-

We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.

-

Third party links

-

Occasionally, at our discretion, we may include or offer third party products or services on our site. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.

-

Children's Online Privacy Protection Act Compliance

-

Our site, products and services are all directed to people who are at least 13 years old. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA (Children's Online Privacy Protection Act) do not use this site.

-

Online Privacy Policy Only

-

This online privacy policy applies only to information collected through our site and not to information collected offline.

- -

By using our site, you consent to our web site privacy policy.

-

Changes to our Privacy Policy

-

If we decide to change our privacy policy, we will post those changes on this page.

-

This document is CC-BY-SA. It was last updated May 31, 2013.

-

Originally adapted from the Discourse privacy policy.

title: Условия обслуживания и политика конфиденциальности %{instance} themes: default: Mastodon diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index 2d984049a..8d39d35b0 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -625,74 +625,6 @@ sr-Latn: reblogged: podržano sensitive_content: Osetljiv sadržaj terms: - body_html: | -

Privacy Policy

- -

What information do we collect?

- -

We collect information from you when you register on our site and gather data when you participate in the forum by reading, writing, and evaluating the content shared here.

- -

When registering on our site, you may be asked to enter your name and e-mail address. You may, however, visit our site without registering. Your e-mail address will be verified by an email containing a unique link. If that link is visited, we know that you control the e-mail address.

- -

When registered and posting, we record the IP address that the post originated from. We also may retain server logs which include the IP address of every request to our server.

- -

What do we use your information for?

- -

Any of the information we collect from you may be used in one of the following ways:

- -
    -
  • To personalize your experience — your information helps us to better respond to your individual needs.
  • -
  • To improve our site — we continually strive to improve our site offerings based on the information and feedback we receive from you.
  • -
  • To improve customer service — your information helps us to more effectively respond to your customer service requests and support needs.
  • -
  • To send periodic emails — The email address you provide may be used to send you information, notifications that you request about changes to topics or in response to your user name, respond to inquiries, and/or other requests or questions.
  • -
- -

How do we protect your information?

- -

We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information.

- -

What is your data retention policy?

- -

We will make a good faith effort to:

- -
    -
  • Retain server logs containing the IP address of all requests to this server no more than 90 days.
  • -
  • Retain the IP addresses associated with registered users and their posts no more than 5 years.
  • -
- -

Do we use cookies?

- -

Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account.

- -

We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.

- -

Do we disclose any information to outside parties?

- -

We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.

- -

Third party links

- -

Occasionally, at our discretion, we may include or offer third party products or services on our site. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.

- -

Children's Online Privacy Protection Act Compliance

- -

Our site, products and services are all directed to people who are at least 13 years old. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA (Children's Online Privacy Protection Act) do not use this site.

- -

Online Privacy Policy Only

- -

This online privacy policy applies only to information collected through our site and not to information collected offline.

- - - -

By using our site, you consent to our web site privacy policy.

- -

Changes to our Privacy Policy

- -

If we decide to change our privacy policy, we will post those changes on this page.

- -

This document is CC-BY-SA. It was last updated May 31, 2013.

- -

Originally adapted from the Discourse privacy policy.

title: Uslovi korišćenja i politika privatnosti instance %{instance} themes: default: Mastodont diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 2daf32915..af4c6a846 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -625,74 +625,6 @@ sr: reblogged: подржано sensitive_content: Осетљив садржај terms: - body_html: | -

Privacy Policy

- -

What information do we collect?

- -

We collect information from you when you register on our site and gather data when you participate in the forum by reading, writing, and evaluating the content shared here.

- -

When registering on our site, you may be asked to enter your name and e-mail address. You may, however, visit our site without registering. Your e-mail address will be verified by an email containing a unique link. If that link is visited, we know that you control the e-mail address.

- -

When registered and posting, we record the IP address that the post originated from. We also may retain server logs which include the IP address of every request to our server.

- -

What do we use your information for?

- -

Any of the information we collect from you may be used in one of the following ways:

- -
    -
  • To personalize your experience — your information helps us to better respond to your individual needs.
  • -
  • To improve our site — we continually strive to improve our site offerings based on the information and feedback we receive from you.
  • -
  • To improve customer service — your information helps us to more effectively respond to your customer service requests and support needs.
  • -
  • To send periodic emails — The email address you provide may be used to send you information, notifications that you request about changes to topics or in response to your user name, respond to inquiries, and/or other requests or questions.
  • -
- -

How do we protect your information?

- -

We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information.

- -

What is your data retention policy?

- -

We will make a good faith effort to:

- -
    -
  • Retain server logs containing the IP address of all requests to this server no more than 90 days.
  • -
  • Retain the IP addresses associated with registered users and their posts no more than 5 years.
  • -
- -

Do we use cookies?

- -

Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account.

- -

We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.

- -

Do we disclose any information to outside parties?

- -

We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.

- -

Third party links

- -

Occasionally, at our discretion, we may include or offer third party products or services on our site. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.

- -

Children's Online Privacy Protection Act Compliance

- -

Our site, products and services are all directed to people who are at least 13 years old. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA (Children's Online Privacy Protection Act) do not use this site.

- -

Online Privacy Policy Only

- -

This online privacy policy applies only to information collected through our site and not to information collected offline.

- - - -

By using our site, you consent to our web site privacy policy.

- -

Changes to our Privacy Policy

- -

If we decide to change our privacy policy, we will post those changes on this page.

- -

This document is CC-BY-SA. It was last updated May 31, 2013.

- -

Originally adapted from the Discourse privacy policy.

title: Услови коришћења и политика приватности инстанце %{instance} themes: default: Мастодонт diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 8ce6b3100..f85ed6efb 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -656,74 +656,6 @@ sv: reblogged: boostad sensitive_content: Känsligt innehåll terms: - body_html: | -

Integritetspolicy

- -

Vilken information samlar vi in

- -

Vi samlar in information från dig när du registrerar dig på vår webbplats och samlar in data när du deltar i forumet genom att läsa, skriva och utvärdera innehållet som delas här.

- -

När du registrerar dig på vår webbplats kan du bli ombedd att ange ditt namn och din e-postadress. Du kan dock besöka vår webbplats utan att registrera dig. Din e-postadress kommer att verifieras med ett e-postmeddelande som innehåller en unik länk. Om den länken besöks vet vi att du kontrollerar e-postadressen.

- -

När vi registrerar och postar registrerar vi den IP-adress som posten härstammar från. Vi kan också behålla serverns loggar som innehåller IP-adress för varje begäran till vår server.

- -

Vad använder vi din information för?

- -

Vilken som helst information vi samlar in från dig kan användas på något av följande sätt:

- -
    -
  • För att personifiera din upplevelse — Din information hjälper oss att bättre svara på dina individuella behov.
  • -
  • För att förbättra vår webbplats — Vi strävar kontinuerligt efter att förbättra våra erbjudanden på webbplatsen baserat på information och feedback vi mottar från dig.
  • -
  • För att förbättra kundtjänst — Din information hjälper oss att effektivt svara på dina kundserviceförfrågningar och supportbehov.
  • -
  • För att skicka periodiska e-postmeddelanden — Den e-postadress du anger kan användas för att skicka information, meddelanden som du begär om ändringar i ämnen eller som svar på ditt användarnamn, svara på förfrågningar och / eller andra förfrågningar eller frågor.
  • -
- -

Hur skyddar vi din information?

- -

Vi genomför en rad säkerhetsåtgärder för att upprätthålla säkerheten för din personliga information när du anger, lämnar in eller har tillgång till din personliga information.

- -

Vad är policyn för lagring av data?

- -

Vi kommer att göra en ansträngning för:

- -
    -
  • Behåll serverloggar som innehåller IP-adressen för alla förfrågningar till den här servern inte mer än 90 dagar.
  • -
  • Behåll IP-adresserna i samband med registrerade användare och deras inlägg inte längre än 5 år.
  • -
- -

Använder vi cookies?

- -

Ja. Cookies är små filer som en webbplats eller tjänstleverantör överför till datorns hårddisk via din webbläsare (om du tillåter). Dessa cookies tillåter webbplatsen att känna igen din webbläsare och, om du har ett registrerat konto, associerar det med ditt registrerade konto.

- -

Vi använder cookies för att förstå och spara dina inställningar för framtida besök och sammanställa sammanlagda data om webbplatsstrafik och webbplatsinteraktion så att vi kan erbjuda bättre sajtupplevelser och verktyg i framtiden. Vi kan komma överens med tredje parts tjänsteleverantörer för att hjälpa oss att bättre förstå våra besökare. Dessa tjänsteleverantörer får inte använda den information som samlas in för vår räkning utom för att hjälpa oss att bedriva och förbättra vår verksamhet.

- -

Avslöjar vi information till utomstående parter?

- -

Vi säljer inte, handlar eller på annat sätt överför dina personuppgifter till utomstående parter. Det här omfattar inte betrodda tredje parter som hjälper oss att driva vår webbplats, bedriva vår verksamhet eller service dig, så länge dessa parter är överens om att hålla denna information konfidentiell. Vi kan också släppa din information när vi anser att utgåvan är lämplig för att följa lagen, tillämpa vår webbplatspolicy eller skydda vår eller andra rättigheter, egendom eller säkerhet. Däremot kan personuppgifter som inte identifieras personligen lämnas till andra parter för marknadsföring, reklam eller annan användning.

- -

Tredjepartslänkar

- -

Ibland kan vi, efter eget gottfinnande, inkludera eller erbjuda produkter från tredje part eller tjänster på vår webbplats. Dessa tredje parts webbplatser har separata och oberoende sekretesspolicyer. Vi har därför inget ansvar eller ansvar för innehållet och aktiviteterna för dessa länkade webbplatser. Ändå försöker vi skydda integriteten på vår webbplats och välkomna eventuella återkopplingar om dessa webbplatser.

- -

Children's Online Privacy Protection Act Compliance

- -

Vår webbplats, produkter och tjänster riktas alla till personer som är minst 13 år gamla. Om den här servern är i USA, och du är under 13 år, enligt kraven i COPPA (Children's Online Privacy Protection Act) ska du inte använda denna sida.

- -

Endast online sekretesspolicy

- -

Denna online sekretesspolicy gäller endast information som samlas in via vår webbplats och inte till information som samlas in offline.

- - - -

Genom att använda vår webbplats godkänner du vår hemsida sekretesspolicy.

- -

Ändringar i vår sekretesspolicy

- -

Om vi bestämmer oss för att ändra vår integritetspolicy, lägger vi in de ändringar på den här sidan.

- -

This document is CC-BY-SA. It was last updated May 31, 2013.

- -

Ursprungligen anpassad från Discourse integritetspolicy.

title: "%{instance} Användarvillkor och Sekretesspolicy" themes: default: Mastodon diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index 1254651cd..be868e6e7 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -623,74 +623,6 @@ zh-CN: reblogged: 转嘟 sensitive_content: 敏感内容 terms: - body_html: | -

隐私权政策

- -

我们收集什么信息?

- -

我们从你在我们站点注册开始从你那开始收集信息,并收集关于你在论坛的阅读和写作的数据,并评估分享的内容。

- -

当在我们站点注册时,你可能被要求输入你的名字和邮件地址。然而你可以在不用注册的情况下访问站点。你的邮件地将通过一个独一无二的链接验证。如果链接被访问了,我们就知道你控制了该邮件地址。

- -

当已注册和发帖时,我们记录发布帖子时的 IP 地址。我们也可能保留服务器日志,其中包括了每一个向我们服务器的请求。

- -

我们如何使用你的信息?

- -

从你那收集的任何数据将以以下方式使用:

- -
    -
  • 改进你的个人体验 — 你的信息帮助我们更好地满足你的个人需求。
  • -
  • 改进我们的站点 — 我们基于信息和我们从你那收到的反馈不断地试图改进我们的站点。
  • -
  • 改善我们的客户服务 — 你的信息帮助我们更有效地回应用户服务请求和支持。
  • -
  • 用于发送阶段性的邮件 — 你提供的邮件地址可能用于接受信息、你想看到的通知或与你用户名有关的回复和询问,或是其他的请求和问题。
  • -
- -

我们如何保护你的信息?

- -

我们实现了一系列的安全措施保证你输入、提交或者访问你个人信息的数据安全。

- -

数据保存政策是什么?

- -

我们将善意地:

- -
    -
  • 保存 90 天内的所有向服务器的包含 IP 地址的请求。
  • -
  • 保存 5 年内已注册用户和与他们的帖子有关的 IP 地址。
  • -
- -

我们使用 Cookie 吗?

- -

是的。Cookie 是网站或它的服务商通过网页浏览器存储在你电脑硬盘上的小文件(如果你同意)。这些 Cookie 使站点能分辨你的浏览器,并且,如果你注册了一个账户,与你的注册账户关联。

- -

我们使用 Cookie 为之后的访问和编译一小段关于站点流量和交互的数据来判断并保存你的个人设置,这样我们可以在之后提供更好的站点体验和工具。我们可能使用第三方服务商来帮助我们更好地理解我们的站点访客。这些服务商是不允许代表我们使用收集的信息,除非是在帮助我们执行和改进我们的站点。

- -

我们会在站外提供任何信息吗?

- -

我们绝不销售、交易或任何向外转移你个人信息的行为。这不包括帮助我们管理站点、改进站点或给你提供服务的第三方团体,这些团体需要保证对这些信息保密。当我们认为提交你的信息符合法律、我们的站点政策或保护我们或其他人的权利、知识产权或安全时,我们也可能提交你的信息。然而,非个人访问信息可能供其他团体使用,用于市场、广告或其他用途。

- -

第三方链接

- -

偶尔地,根据我们的判断,我们可能在我们的站点上包括或提供第三方团体的产品或服务。这些第三方站点用于独立和不同的隐私政策。因此我们对链接到的站点或活动没有责任和权利。尽管如此,我们寻求保护我们的整个站点并且欢迎你给这些站点反馈。

- -

儿童在线隐私保护法案合规

- -

我们的站点、产品和服务提供给 13 岁以上的人们。如果服务器位于美国,并且你小于 13 岁,根据儿童在线隐私保护法案合规,不要使用这个站点。

- -

仅用于在线隐私政策

- -

这个线上隐私政策只适用于通过我们站点收集到的信息,并不包括线下收集的信息。

- - - -

你使用站点的同时,代表你同意了我们网站的隐私政策。

- -

隐私政策的更改

- -

如果我们决定更改我们的隐私政策,我们将在此页更新这些改变。

- -

文档以 CC-BY-SA 发布。最后更新时间为2013年5月31日。

- -

原文出自 Discourse 隐私权政策

title: "%{instance} 使用条款和隐私权政策" time: formats: From b08ab329f4d149fd414e0539574f49062c571a8a Mon Sep 17 00:00:00 2001 From: Isatis <515462+Reverite@users.noreply.github.com> Date: Wed, 4 Apr 2018 13:25:34 -0700 Subject: [PATCH 043/442] retrieve custom emoji list via API instead of before page load (#7047) --- .../mastodon/actions/custom_emojis.js | 37 +++++++++++++++++++ .../mastodon/containers/mastodon.js | 4 ++ .../mastodon/reducers/custom_emojis.js | 17 ++++----- app/serializers/initial_state_serializer.rb | 6 --- 4 files changed, 49 insertions(+), 15 deletions(-) create mode 100644 app/javascript/mastodon/actions/custom_emojis.js diff --git a/app/javascript/mastodon/actions/custom_emojis.js b/app/javascript/mastodon/actions/custom_emojis.js new file mode 100644 index 000000000..aa37bc423 --- /dev/null +++ b/app/javascript/mastodon/actions/custom_emojis.js @@ -0,0 +1,37 @@ +import api from '../api'; + +export const CUSTOM_EMOJIS_FETCH_REQUEST = 'CUSTOM_EMOJIS_FETCH_REQUEST'; +export const CUSTOM_EMOJIS_FETCH_SUCCESS = 'CUSTOM_EMOJIS_FETCH_SUCCESS'; +export const CUSTOM_EMOJIS_FETCH_FAIL = 'CUSTOM_EMOJIS_FETCH_FAIL'; + +export function fetchCustomEmojis() { + return (dispatch, getState) => { + dispatch(fetchCustomEmojisRequest()); + + api(getState).get('/api/v1/custom_emojis').then(response => { + dispatch(fetchCustomEmojisSuccess(response.data)); + }).catch(error => { + dispatch(fetchCustomEmojisFail(error)); + }); + }; +}; + +export function fetchCustomEmojisRequest() { + return { + type: CUSTOM_EMOJIS_FETCH_REQUEST, + }; +}; + +export function fetchCustomEmojisSuccess(custom_emojis) { + return { + type: CUSTOM_EMOJIS_FETCH_SUCCESS, + custom_emojis, + }; +}; + +export function fetchCustomEmojisFail(error) { + return { + type: CUSTOM_EMOJIS_FETCH_FAIL, + error, + }; +}; diff --git a/app/javascript/mastodon/containers/mastodon.js b/app/javascript/mastodon/containers/mastodon.js index d1710445b..b29898d3b 100644 --- a/app/javascript/mastodon/containers/mastodon.js +++ b/app/javascript/mastodon/containers/mastodon.js @@ -6,6 +6,7 @@ import { showOnboardingOnce } from '../actions/onboarding'; import { BrowserRouter, Route } from 'react-router-dom'; import { ScrollContext } from 'react-router-scroll-4'; import UI from '../features/ui'; +import { fetchCustomEmojis } from '../actions/custom_emojis'; import { hydrateStore } from '../actions/store'; import { connectUserStream } from '../actions/streaming'; import { IntlProvider, addLocaleData } from 'react-intl'; @@ -19,6 +20,9 @@ export const store = configureStore(); const hydrateAction = hydrateStore(initialState); store.dispatch(hydrateAction); +// load custom emojis +store.dispatch(fetchCustomEmojis()); + export default class Mastodon extends React.PureComponent { static propTypes = { diff --git a/app/javascript/mastodon/reducers/custom_emojis.js b/app/javascript/mastodon/reducers/custom_emojis.js index 307bcc7dc..d2c801ade 100644 --- a/app/javascript/mastodon/reducers/custom_emojis.js +++ b/app/javascript/mastodon/reducers/custom_emojis.js @@ -1,16 +1,15 @@ -import { List as ImmutableList } from 'immutable'; -import { STORE_HYDRATE } from '../actions/store'; +import { List as ImmutableList, fromJS as ConvertToImmutable } from 'immutable'; +import { CUSTOM_EMOJIS_FETCH_SUCCESS } from '../actions/custom_emojis'; import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light'; import { buildCustomEmojis } from '../features/emoji/emoji'; -const initialState = ImmutableList(); +const initialState = ImmutableList([]); export default function custom_emojis(state = initialState, action) { - switch(action.type) { - case STORE_HYDRATE: - emojiSearch('', { custom: buildCustomEmojis(action.state.get('custom_emojis', [])) }); - return action.state.get('custom_emojis'); - default: - return state; + if(action.type === CUSTOM_EMOJIS_FETCH_SUCCESS) { + state = ConvertToImmutable(action.custom_emojis); + emojiSearch('', { custom: buildCustomEmojis(state) }); } + + return state; }; diff --git a/app/serializers/initial_state_serializer.rb b/app/serializers/initial_state_serializer.rb index 216cf5446..3b908e224 100644 --- a/app/serializers/initial_state_serializer.rb +++ b/app/serializers/initial_state_serializer.rb @@ -4,12 +4,6 @@ class InitialStateSerializer < ActiveModel::Serializer attributes :meta, :compose, :accounts, :media_attachments, :settings, :push_subscription - has_many :custom_emojis, serializer: REST::CustomEmojiSerializer - - def custom_emojis - CustomEmoji.local.where(disabled: false) - end - def meta store = { streaming_api_base_url: Rails.configuration.x.streaming_api_base_url, From 98146281e1beaf994710b13ef70f6224e8588cba Mon Sep 17 00:00:00 2001 From: Harmon Date: Sat, 7 Apr 2018 07:53:11 -0500 Subject: [PATCH 044/442] Remove duplicate frequently used emojis (#7064) --- .../compose/containers/emoji_picker_dropdown_container.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js b/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js index e6a535a5d..5ec937a39 100644 --- a/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js +++ b/app/javascript/mastodon/features/compose/containers/emoji_picker_dropdown_container.js @@ -38,7 +38,8 @@ const getFrequentlyUsedEmojis = createSelector([ .toArray(); if (emojis.length < DEFAULTS.length) { - emojis = emojis.concat(DEFAULTS.slice(0, DEFAULTS.length - emojis.length)); + let uniqueDefaults = DEFAULTS.filter(emoji => !emojis.includes(emoji)); + emojis = emojis.concat(uniqueDefaults.slice(0, DEFAULTS.length - emojis.length)); } return emojis; From b5726def55994db8eb5797bbea1d2b79df3e884a Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 7 Apr 2018 18:54:46 +0200 Subject: [PATCH 045/442] Forward deletes on the same path as reply forwarding (#7058) * Forward deletes on the same path as reply forwarding * Remove trailing whitespace --- app/lib/activitypub/activity/delete.rb | 36 +++++++++++++++++++------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/app/lib/activitypub/activity/delete.rb b/app/lib/activitypub/activity/delete.rb index 5fa60a81c..3474d55d9 100644 --- a/app/lib/activitypub/activity/delete.rb +++ b/app/lib/activitypub/activity/delete.rb @@ -17,21 +17,25 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity end def delete_note - status = Status.find_by(uri: object_uri, account: @account) - status ||= Status.find_by(uri: @object['atomUri'], account: @account) if @object.is_a?(Hash) && @object['atomUri'].present? + @status = Status.find_by(uri: object_uri, account: @account) + @status ||= Status.find_by(uri: @object['atomUri'], account: @account) if @object.is_a?(Hash) && @object['atomUri'].present? delete_later!(object_uri) - return if status.nil? + return if @status.nil? - forward_for_reblogs(status) - delete_now!(status) + if @status.public_visibility? || @status.unlisted_visibility? + forward_for_reply + forward_for_reblogs + end + + delete_now! end - def forward_for_reblogs(status) + def forward_for_reblogs return if @json['signature'].blank? - rebloggers_ids = status.reblogs.includes(:account).references(:account).merge(Account.local).pluck(:account_id) + rebloggers_ids = @status.reblogs.includes(:account).references(:account).merge(Account.local).pluck(:account_id) inboxes = Account.where(id: ::Follow.where(target_account_id: rebloggers_ids).select(:account_id)).inboxes - [@account.preferred_inbox_url] ActivityPub::DeliveryWorker.push_bulk(inboxes) do |inbox_url| @@ -39,8 +43,22 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity end end - def delete_now!(status) - RemoveStatusService.new.call(status) + def replied_to_status + return @replied_to_status if defined?(@replied_to_status) + @replied_to_status = @status.thread + end + + def reply_to_local? + !replied_to_status.nil? && replied_to_status.account.local? + end + + def forward_for_reply + return unless @json['signature'].present? && reply_to_local? + ActivityPub::RawDistributionWorker.perform_async(Oj.dump(@json), replied_to_status.account_id, [@account.preferred_inbox_url]) + end + + def delete_now! + RemoveStatusService.new.call(@status) end def payload From b65eb00c53af939444e0e891c0a3a4563f4897ac Mon Sep 17 00:00:00 2001 From: Alda Marteau-Hardi Date: Sat, 7 Apr 2018 21:33:01 +0200 Subject: [PATCH 046/442] Prevent admins and moderators eavesdropping in private and direct toots (#7067) Fix #6986 --- app/controllers/admin/statuses_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/admin/statuses_controller.rb b/app/controllers/admin/statuses_controller.rb index 5d4325f57..d5787acfb 100644 --- a/app/controllers/admin/statuses_controller.rb +++ b/app/controllers/admin/statuses_controller.rb @@ -12,7 +12,7 @@ module Admin def index authorize :status, :index? - @statuses = @account.statuses + @statuses = @account.statuses.where(visibility: [:public, :unlisted]) if params[:media] account_media_status_ids = @account.media_attachments.attached.reorder(nil).select(:status_id).distinct From 4a9becfca2d7399acb422da646c48bdd9f39c989 Mon Sep 17 00:00:00 2001 From: Hisham Muhammad Date: Sat, 7 Apr 2018 16:36:03 -0300 Subject: [PATCH 047/442] i18n: improve "Welcome" translation in Portuguese (#7068) * i18n: update gender-neutral language for pt and pt-BR Instead of using "bem-vindo(a)" (a masculine form of "Welcome" with a "(a)" in the end to mean "bem-vinda" for the feminine form), use "boas-vindas", which is a gender-neutral form of "Welcome"). There is already precedent for using "boas-vindas" in the Brazilian Portuguese localization, in `config/locales/pt-BR.yml`. European Portuguese dictionary Priberam also registers it as a valid form: https://www.priberam.pt/dlpo/boas-vindas * i18n: pt-BR minor orthography fix The form "a bordo" does not take an accent. http://oredator.com.br/curso-de-redacao/uncategorized/a-bordo-ou-a-bordo --- app/javascript/mastodon/locales/pt-BR.json | 2 +- app/javascript/mastodon/locales/pt.json | 2 +- config/locales/pt-BR.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index b056ec8bd..4cd2e0643 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -189,7 +189,7 @@ "onboarding.page_one.federation": "Mastodon é uma rede de servidores independentes que se juntam para fazer uma grande rede social. Nós chamamos estes servidores de instâncias.", "onboarding.page_one.full_handle": "Seu nome de usuário completo", "onboarding.page_one.handle_hint": "Isso é o que você diz aos seus amigos para que eles possam te mandar mensagens ou te seguir a partir de outra instância.", - "onboarding.page_one.welcome": "Seja bem-vindo(a) ao Mastodon!", + "onboarding.page_one.welcome": "Boas-vindas ao Mastodon!", "onboarding.page_six.admin": "O administrador de sua instância é {admin}.", "onboarding.page_six.almost_done": "Quase acabando...", "onboarding.page_six.appetoot": "Bom Apetoot!", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index 65983000c..7a404eaba 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -189,7 +189,7 @@ "onboarding.page_one.federation": "Mastodon é uma rede de servidores independentes ligados entre si para fazer uma grande rede social. Nós chamamos instâncias a estes servidores.", "onboarding.page_one.full_handle": "O teu nome de utilizador completo", "onboarding.page_one.handle_hint": "Isto é o que dizes aos teus amigos para pesquisar.", - "onboarding.page_one.welcome": "Bem-vindo(a) ao Mastodon!", + "onboarding.page_one.welcome": "Boas-vindas ao Mastodon!", "onboarding.page_six.admin": "O administrador da tua instância é {admin}.", "onboarding.page_six.almost_done": "Quase pronto...", "onboarding.page_six.appetoot": "Bon Appetoot!", diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index c1225d356..d6f463a19 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -709,7 +709,7 @@ pt-BR: tip_local_timeline: A timeline local é uma visão contínua das pessoas que estão em %{instance}. Esses são seus vizinhos próximos! tip_mobile_webapp: Se o seu navegador móvel oferecer a opção de adicionar Mastodon à tela inicial, você pode receber notificações push. Vai funcionar quase como um aplicativo nativo! tips: Dicas - title: Boas-vindas à bordo, %{name}! + title: Boas-vindas a bordo, %{name}! users: invalid_email: O endereço de e-mail é inválido invalid_otp_token: Código de autenticação inválido From d4de2239b0ab04bf6a42db9f28d1fdd8e45f7d8b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 7 Apr 2018 21:36:58 +0200 Subject: [PATCH 048/442] Add a circuit breaker for ActivityPub deliveries (#7053) --- Gemfile | 2 ++ Gemfile.lock | 2 ++ app/workers/activitypub/delivery_worker.rb | 14 +++++++++----- config/initializers/stoplight.rb | 3 +++ 4 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 config/initializers/stoplight.rb diff --git a/Gemfile b/Gemfile index 9e644e7ae..4a5a166bd 100644 --- a/Gemfile +++ b/Gemfile @@ -35,6 +35,7 @@ gem 'devise-two-factor', '~> 3.0' group :pam_authentication, optional: true do gem 'devise_pam_authenticatable2', '~> 9.0' end + gem 'net-ldap', '~> 0.10' gem 'omniauth-cas', '~> 1.1' gem 'omniauth-saml', '~> 1.10' @@ -79,6 +80,7 @@ gem 'sidekiq-bulk', '~>0.1.1' gem 'simple-navigation', '~> 4.0' gem 'simple_form', '~> 3.4' gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' +gem 'stoplight', '~> 2.1.3' gem 'strong_migrations' gem 'tty-command' gem 'tty-prompt' diff --git a/Gemfile.lock b/Gemfile.lock index a185a602e..0f5a1fb6a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -550,6 +550,7 @@ GEM net-scp (>= 1.1.2) net-ssh (>= 2.8.0) statsd-ruby (1.2.1) + stoplight (2.1.3) streamio-ffmpeg (3.0.2) multi_json (~> 1.8) strong_migrations (0.1.9) @@ -716,6 +717,7 @@ DEPENDENCIES simple_form (~> 3.4) simplecov (~> 0.14) sprockets-rails (~> 3.2) + stoplight (~> 2.1.3) streamio-ffmpeg (~> 3.0) strong_migrations tty-command diff --git a/app/workers/activitypub/delivery_worker.rb b/app/workers/activitypub/delivery_worker.rb index e6cfd0d07..adffd1d3b 100644 --- a/app/workers/activitypub/delivery_worker.rb +++ b/app/workers/activitypub/delivery_worker.rb @@ -12,9 +12,7 @@ class ActivityPub::DeliveryWorker @source_account = Account.find(source_account_id) @inbox_url = inbox_url - perform_request do |response| - raise Mastodon::UnexpectedResponseError, response unless response_successful? response - end + perform_request failure_tracker.track_success! rescue => e @@ -30,8 +28,14 @@ class ActivityPub::DeliveryWorker request.add_headers(HEADERS) end - def perform_request(&block) - build_request.perform(&block) + def perform_request + light = Stoplight(@inbox_url) do + build_request.perform do |response| + raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) + end + end + + light.run end def response_successful?(response) diff --git a/config/initializers/stoplight.rb b/config/initializers/stoplight.rb new file mode 100644 index 000000000..1bd4ee6e7 --- /dev/null +++ b/config/initializers/stoplight.rb @@ -0,0 +1,3 @@ +require 'stoplight' + +Stoplight::Light.default_data_store = Stoplight::DataStore::Redis.new(Redis.current) From b83ce18b30d33c30b461f593ac4cd6e86057a365 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?THE=20BOSS=20=E2=99=A8?= <30565780+theboss@users.noreply.github.com> Date: Sun, 8 Apr 2018 16:57:16 +0900 Subject: [PATCH 049/442] Ignore elasticsearch directory (#7070) --- .dockerignore | 1 + .gitignore | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.dockerignore b/.dockerignore index 5cd3b179a..5fb9861de 100644 --- a/.dockerignore +++ b/.dockerignore @@ -11,3 +11,4 @@ vendor/bundle *~ postgres redis +elasticsearch diff --git a/.gitignore b/.gitignore index 38ebc934f..51e47bb52 100644 --- a/.gitignore +++ b/.gitignore @@ -36,9 +36,10 @@ config/deploy/* .vscode/ .idea/ -# Ignore postgres + redis volume optionally created by docker-compose +# Ignore postgres + redis + elasticsearch volume optionally created by docker-compose postgres redis +elasticsearch # Ignore Apple files .DS_Store From 1ed1014546bcfef0d2441702673deab586f6bca0 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 8 Apr 2018 20:32:39 +0900 Subject: [PATCH 050/442] Free stroage if it is exceeding disk quota (#7061) --- app/javascript/mastodon/actions/accounts.js | 9 ++- .../mastodon/actions/importer/index.js | 3 +- app/javascript/mastodon/actions/statuses.js | 11 ++- .../mastodon/service_worker/entry.js | 13 ++- app/javascript/mastodon/storage/db.js | 9 +-- app/javascript/mastodon/storage/modifier.js | 80 ++++++++++++++----- 6 files changed, 89 insertions(+), 36 deletions(-) diff --git a/app/javascript/mastodon/actions/accounts.js b/app/javascript/mastodon/actions/accounts.js index 28ae56763..c9e4afcfc 100644 --- a/app/javascript/mastodon/actions/accounts.js +++ b/app/javascript/mastodon/actions/accounts.js @@ -1,5 +1,5 @@ import api, { getLinks } from '../api'; -import asyncDB from '../storage/db'; +import openDB from '../storage/db'; import { importAccount, importFetchedAccount, importFetchedAccounts } from './importer'; export const ACCOUNT_FETCH_REQUEST = 'ACCOUNT_FETCH_REQUEST'; @@ -94,12 +94,15 @@ export function fetchAccount(id) { dispatch(fetchAccountRequest(id)); - asyncDB.then(db => getFromDB( + openDB().then(db => getFromDB( dispatch, getState, db.transaction('accounts', 'read').objectStore('accounts').index('id'), id - )).catch(() => api(getState).get(`/api/v1/accounts/${id}`).then(response => { + ).then(() => db.close(), error => { + db.close(); + throw error; + })).catch(() => api(getState).get(`/api/v1/accounts/${id}`).then(response => { dispatch(importFetchedAccount(response.data)); })).then(() => { dispatch(fetchAccountSuccess()); diff --git a/app/javascript/mastodon/actions/importer/index.js b/app/javascript/mastodon/actions/importer/index.js index e671d417c..5b18cbc1d 100644 --- a/app/javascript/mastodon/actions/importer/index.js +++ b/app/javascript/mastodon/actions/importer/index.js @@ -1,3 +1,4 @@ +import { autoPlayGif } from '../../initial_state'; import { putAccounts, putStatuses } from '../../storage/modifier'; import { normalizeAccount, normalizeStatus } from './normalizer'; @@ -44,7 +45,7 @@ export function importFetchedAccounts(accounts) { } accounts.forEach(processAccount); - putAccounts(normalAccounts); + putAccounts(normalAccounts, !autoPlayGif); return importAccounts(normalAccounts); } diff --git a/app/javascript/mastodon/actions/statuses.js b/app/javascript/mastodon/actions/statuses.js index d28aef880..849cb4f5a 100644 --- a/app/javascript/mastodon/actions/statuses.js +++ b/app/javascript/mastodon/actions/statuses.js @@ -1,5 +1,5 @@ import api from '../api'; -import asyncDB from '../storage/db'; +import openDB from '../storage/db'; import { evictStatus } from '../storage/modifier'; import { deleteFromTimelines } from './timelines'; @@ -92,12 +92,17 @@ export function fetchStatus(id) { dispatch(fetchStatusRequest(id, skipLoading)); - asyncDB.then(db => { + openDB().then(db => { const transaction = db.transaction(['accounts', 'statuses'], 'read'); const accountIndex = transaction.objectStore('accounts').index('id'); const index = transaction.objectStore('statuses').index('id'); - return getFromDB(dispatch, getState, accountIndex, index, id); + return getFromDB(dispatch, getState, accountIndex, index, id).then(() => { + db.close(); + }, error => { + db.close(); + throw error; + }); }).then(() => { dispatch(fetchStatusSuccess(skipLoading)); }, () => api(getState).get(`/api/v1/statuses/${id}`).then(response => { diff --git a/app/javascript/mastodon/service_worker/entry.js b/app/javascript/mastodon/service_worker/entry.js index 160c3fbf2..ba54ae996 100644 --- a/app/javascript/mastodon/service_worker/entry.js +++ b/app/javascript/mastodon/service_worker/entry.js @@ -1,3 +1,4 @@ +import { freeStorage } from '../storage/modifier'; import './web_push_notifications'; function openSystemCache() { @@ -42,8 +43,10 @@ self.addEventListener('fetch', function(event) { event.respondWith(asyncResponse.then(async response => { if (response.ok || response.type === 'opaqueredirect') { - const cache = await asyncCache; - await cache.delete('/'); + await Promise.all([ + asyncCache.then(cache => cache.delete('/')), + indexedDB.deleteDatabase('mastodon'), + ]); } return response; @@ -56,7 +59,11 @@ self.addEventListener('fetch', function(event) { const fetched = await fetch(event.request); if (fetched.ok) { - await cache.put(event.request.url, fetched.clone()); + try { + await cache.put(event.request.url, fetched.clone()); + } finally { + freeStorage(); + } } return fetched; diff --git a/app/javascript/mastodon/storage/db.js b/app/javascript/mastodon/storage/db.js index e08fc3f3d..377a792a7 100644 --- a/app/javascript/mastodon/storage/db.js +++ b/app/javascript/mastodon/storage/db.js @@ -1,15 +1,14 @@ -import { me } from '../initial_state'; - -export default new Promise((resolve, reject) => { +export default () => new Promise((resolve, reject) => { + // ServiceWorker is required to synchronize the login state. // Microsoft Edge 17 does not support getAll according to: // Catalog of standard and vendor APIs across browsers - Microsoft Edge Development // https://developer.microsoft.com/en-us/microsoft-edge/platform/catalog/?q=specName%3Aindexeddb - if (!me || !('getAll' in IDBObjectStore.prototype)) { + if (!('caches' in self && 'getAll' in IDBObjectStore.prototype)) { reject(); return; } - const request = indexedDB.open('mastodon:' + me); + const request = indexedDB.open('mastodon'); request.onerror = reject; request.onsuccess = ({ target }) => resolve(target.result); diff --git a/app/javascript/mastodon/storage/modifier.js b/app/javascript/mastodon/storage/modifier.js index 4773d07a9..c2ed6f807 100644 --- a/app/javascript/mastodon/storage/modifier.js +++ b/app/javascript/mastodon/storage/modifier.js @@ -1,13 +1,14 @@ -import asyncDB from './db'; -import { autoPlayGif } from '../initial_state'; +import openDB from './db'; const accountAssetKeys = ['avatar', 'avatar_static', 'header', 'header_static']; -const avatarKey = autoPlayGif ? 'avatar' : 'avatar_static'; -const limit = 1024; +const storageMargin = 8388608; +const storeLimit = 1024; -// ServiceWorker and Cache API is not available on iOS 11 -// https://webkit.org/status/#specification-service-workers -const asyncCache = window.caches ? caches.open('mastodon-system') : Promise.reject(); +function openCache() { + // ServiceWorker and Cache API is not available on iOS 11 + // https://webkit.org/status/#specification-service-workers + return self.caches ? caches.open('mastodon-system') : Promise.reject(); +} function printErrorIfAvailable(error) { if (error) { @@ -16,7 +17,7 @@ function printErrorIfAvailable(error) { } function put(name, objects, onupdate, oncreate) { - return asyncDB.then(db => new Promise((resolve, reject) => { + return openDB().then(db => (new Promise((resolve, reject) => { const putTransaction = db.transaction(name, 'readwrite'); const putStore = putTransaction.objectStore(name); const putIndex = putStore.index('id'); @@ -53,7 +54,7 @@ function put(name, objects, onupdate, oncreate) { const count = readStore.count(); count.onsuccess = () => { - const excess = count.result - limit; + const excess = count.result - storeLimit; if (excess > 0) { const retrieval = readStore.getAll(null, excess); @@ -69,11 +70,17 @@ function put(name, objects, onupdate, oncreate) { }; putTransaction.onerror = reject; + })).then(resolved => { + db.close(); + return resolved; + }, error => { + db.close(); + throw error; })); } function evictAccountsByRecords(records) { - asyncDB.then(db => { + return openDB().then(db => { const transaction = db.transaction(['accounts', 'statuses'], 'readwrite'); const accounts = transaction.objectStore('accounts'); const accountsIdIndex = accounts.index('id'); @@ -83,7 +90,7 @@ function evictAccountsByRecords(records) { function evict(toEvict) { toEvict.forEach(record => { - asyncCache + openCache() .then(cache => accountAssetKeys.forEach(key => cache.delete(records[key]))) .catch(printErrorIfAvailable); @@ -98,6 +105,8 @@ function evictAccountsByRecords(records) { } evict(records); + + db.close(); }).catch(printErrorIfAvailable); } @@ -106,8 +115,9 @@ export function evictStatus(id) { } export function evictStatuses(ids) { - asyncDB.then(db => { - const store = db.transaction('statuses', 'readwrite').objectStore('statuses'); + return openDB().then(db => { + const transaction = db.transaction('statuses', 'readwrite'); + const store = transaction.objectStore('statuses'); const idIndex = store.index('id'); const reblogIndex = store.index('reblog'); @@ -118,14 +128,17 @@ export function evictStatuses(ids) { idIndex.getKey(id).onsuccess = ({ target }) => target.result && store.delete(target.result); }); + + db.close(); }).catch(printErrorIfAvailable); } function evictStatusesByRecords(records) { - evictStatuses(records.map(({ id }) => id)); + return evictStatuses(records.map(({ id }) => id)); } -export function putAccounts(records) { +export function putAccounts(records, avatarStatic) { + const avatarKey = avatarStatic ? 'avatar_static' : 'avatar'; const newURLs = []; put('accounts', records, (newRecord, oldKey, store, oncomplete) => { @@ -135,7 +148,7 @@ export function putAccounts(records) { const oldURL = target.result[key]; if (newURL !== oldURL) { - asyncCache + openCache() .then(cache => cache.delete(oldURL)) .catch(printErrorIfAvailable); } @@ -153,11 +166,12 @@ export function putAccounts(records) { }, (newRecord, oncomplete) => { newURLs.push(newRecord[avatarKey]); oncomplete(); - }).then(records => { - evictAccountsByRecords(records); - asyncCache - .then(cache => cache.addAll(newURLs)) - .catch(printErrorIfAvailable); + }).then(records => Promise.all([ + evictAccountsByRecords(records), + openCache().then(cache => cache.addAll(newURLs)), + ])).then(freeStorage, error => { + freeStorage(); + throw error; }).catch(printErrorIfAvailable); } @@ -166,3 +180,27 @@ export function putStatuses(records) { .then(evictStatusesByRecords) .catch(printErrorIfAvailable); } + +export function freeStorage() { + return navigator.storage.estimate().then(({ quota, usage }) => { + if (usage + storageMargin < quota) { + return null; + } + + return openDB().then(db => new Promise((resolve, reject) => { + const retrieval = db.transaction('accounts', 'readonly').objectStore('accounts').getAll(null, 1); + + retrieval.onsuccess = () => { + if (retrieval.result.length > 0) { + resolve(evictAccountsByRecords(retrieval.result).then(freeStorage)); + } else { + resolve(caches.delete('mastodon-system')); + } + }; + + retrieval.onerror = reject; + + db.close(); + })); + }); +} From 1364e9e4ae1fb12a1c970795f1d0afd651c7cfe2 Mon Sep 17 00:00:00 2001 From: ThibG Date: Sun, 8 Apr 2018 13:40:22 +0200 Subject: [PATCH 051/442] Fix follow/unfollow buttons on public profile (fixes #7036) (#7040) * Fix follow/unfollow buttons on public profile - Present non-logged users with web+mastodon:// URLs for remote accounts - Present logged-in users with appropriate links (authorize_follows and remote_unfollows) for remote accounts * Do not cache rendered cards if user is logged in --- .../remote_account_controller_concern.rb | 21 ++++++++++ app/controllers/remote_unfollows.rb | 39 +++++++++++++++++++ app/views/accounts/_follow_button.html.haml | 6 +-- app/views/accounts/_follow_grid.html.haml | 2 +- app/views/remote_unfollows/_card.html.haml | 13 +++++++ .../_post_follow_actions.html.haml | 4 ++ app/views/remote_unfollows/error.html.haml | 3 ++ app/views/remote_unfollows/success.html.haml | 10 +++++ config/routes.rb | 1 + 9 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 app/controllers/concerns/remote_account_controller_concern.rb create mode 100644 app/controllers/remote_unfollows.rb create mode 100644 app/views/remote_unfollows/_card.html.haml create mode 100644 app/views/remote_unfollows/_post_follow_actions.html.haml create mode 100644 app/views/remote_unfollows/error.html.haml create mode 100644 app/views/remote_unfollows/success.html.haml diff --git a/app/controllers/concerns/remote_account_controller_concern.rb b/app/controllers/concerns/remote_account_controller_concern.rb new file mode 100644 index 000000000..e17910642 --- /dev/null +++ b/app/controllers/concerns/remote_account_controller_concern.rb @@ -0,0 +1,21 @@ +# frozen_string_literal: true + +module RemoteAccountControllerConcern + extend ActiveSupport::Concern + + included do + layout 'public' + before_action :set_account + before_action :check_account_suspension + end + + private + + def set_account + @account = Account.find_remote!(params[:acct]) + end + + def check_account_suspension + gone if @account.suspended? + end +end diff --git a/app/controllers/remote_unfollows.rb b/app/controllers/remote_unfollows.rb new file mode 100644 index 000000000..af5943363 --- /dev/null +++ b/app/controllers/remote_unfollows.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class RemoteUnfollowsController < ApplicationController + layout 'modal' + + before_action :authenticate_user! + before_action :set_body_classes + + def create + @account = unfollow_attempt.try(:target_account) + + if @account.nil? + render :error + else + render :success + end + rescue ActiveRecord::RecordNotFound, Mastodon::NotPermittedError + render :error + end + + private + + def unfollow_attempt + username, domain = acct_without_prefix.split('@') + UnfollowService.new.call(current_account, Account.find_remote!(username, domain)) + end + + def acct_without_prefix + acct_params.gsub(/\Aacct:/, '') + end + + def acct_params + params.fetch(:acct, '') + end + + def set_body_classes + @body_classes = 'modal-layout' + end +end diff --git a/app/views/accounts/_follow_button.html.haml b/app/views/accounts/_follow_button.html.haml index e476e0aff..96ae23234 100644 --- a/app/views/accounts/_follow_button.html.haml +++ b/app/views/accounts/_follow_button.html.haml @@ -8,16 +8,16 @@ - if user_signed_in? && current_account.id != account.id && !requested .controls - if following - = link_to account_unfollow_path(account), data: { method: :post }, class: 'icon-button' do + = link_to (account.local? ? account_unfollow_path(account) : remote_unfollow_path(acct: account.acct)), data: { method: :post }, class: 'icon-button' do = fa_icon 'user-times' = t('accounts.unfollow') - else - = link_to account_follow_path(account), data: { method: :post }, class: 'icon-button' do + = link_to (account.local? ? account_follow_path(account) : authorize_follow_path(acct: account.acct)), data: { method: :post }, class: 'icon-button' do = fa_icon 'user-plus' = t('accounts.follow') - elsif !user_signed_in? .controls .remote-follow - = link_to account_remote_follow_path(account), class: 'icon-button' do + = link_to (account.local? ? account_remote_follow_path(account) : "web+mastodon://follow?uri=#{account.uri}"), class: 'icon-button' do = fa_icon 'user-plus' = t('accounts.remote_follow') diff --git a/app/views/accounts/_follow_grid.html.haml b/app/views/accounts/_follow_grid.html.haml index 10fbfa546..a6d0ee817 100644 --- a/app/views/accounts/_follow_grid.html.haml +++ b/app/views/accounts/_follow_grid.html.haml @@ -2,6 +2,6 @@ - if accounts.empty? = render partial: 'accounts/nothing_here' - else - = render partial: 'accounts/grid_card', collection: accounts, as: :account, cached: true + = render partial: 'accounts/grid_card', collection: accounts, as: :account, cached: !user_signed_in? = paginate follows diff --git a/app/views/remote_unfollows/_card.html.haml b/app/views/remote_unfollows/_card.html.haml new file mode 100644 index 000000000..e81e292ba --- /dev/null +++ b/app/views/remote_unfollows/_card.html.haml @@ -0,0 +1,13 @@ +.account-card + .detailed-status__display-name + %div + = image_tag account.avatar.url(:original), alt: '', width: 48, height: 48, class: 'avatar' + + %span.display-name + - account_url = local_assigns[:admin] ? admin_account_path(account.id) : TagManager.instance.url_for(account) + = link_to account_url, class: 'detailed-status__display-name p-author h-card', target: '_blank', rel: 'noopener' do + %strong.emojify= display_name(account) + %span @#{account.acct} + + - if account.note? + .account__header__content.emojify= Formatter.instance.simplified_format(account) diff --git a/app/views/remote_unfollows/_post_follow_actions.html.haml b/app/views/remote_unfollows/_post_follow_actions.html.haml new file mode 100644 index 000000000..2a9c062e9 --- /dev/null +++ b/app/views/remote_unfollows/_post_follow_actions.html.haml @@ -0,0 +1,4 @@ +.post-follow-actions + %div= link_to t('authorize_follow.post_follow.web'), web_url("accounts/#{@account.id}"), class: 'button button--block' + %div= link_to t('authorize_follow.post_follow.return'), TagManager.instance.url_for(@account), class: 'button button--block' + %div= t('authorize_follow.post_follow.close') diff --git a/app/views/remote_unfollows/error.html.haml b/app/views/remote_unfollows/error.html.haml new file mode 100644 index 000000000..cb63f02be --- /dev/null +++ b/app/views/remote_unfollows/error.html.haml @@ -0,0 +1,3 @@ +.form-container + .flash-message#error_explanation + = t('remote_unfollow.error') diff --git a/app/views/remote_unfollows/success.html.haml b/app/views/remote_unfollows/success.html.haml new file mode 100644 index 000000000..aa3c838a0 --- /dev/null +++ b/app/views/remote_unfollows/success.html.haml @@ -0,0 +1,10 @@ +- content_for :page_title do + = t('remote_unfollow.title', acct: @account.acct) + +.form-container + .follow-prompt + %h2= t('remote_unfollow.unfollowed') + + = render 'card', account: @account + + = render 'post_follow_actions' diff --git a/config/routes.rb b/config/routes.rb index 4b5ba5c96..7187fd743 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -116,6 +116,7 @@ Rails.application.routes.draw do get '/media_proxy/:id/(*any)', to: 'media_proxy#show', as: :media_proxy # Remote follow + resource :remote_unfollow, only: [:create] resource :authorize_follow, only: [:show, :create] resource :share, only: [:show, :create] From cd0eaa349ca5d7e53e2ed246e70e99fc61c98370 Mon Sep 17 00:00:00 2001 From: Levi Bard Date: Sun, 8 Apr 2018 13:43:10 +0200 Subject: [PATCH 052/442] Enable updating additional account information from user preferences via rest api (#6789) * Enable updating additional account information from user preferences via rest api Resolves #6553 * Pacify rubocop * Decoerce incoming settings in UserSettingsDecorator * Create user preferences hash directly from incoming credentials instead of going through ActionController::Parameters * Clean up user preferences update * Use ActiveModel::Type::Boolean instead of manually checking stringified number equivalence --- .../api/v1/accounts/credentials_controller.rb | 12 ++++++++++++ app/lib/user_settings_decorator.rb | 4 ++-- .../api/v1/accounts/credentials_controller_spec.rb | 6 ++++++ spec/lib/user_settings_decorator_spec.rb | 11 +++++++++++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb index 68af22529..062d490a7 100644 --- a/app/controllers/api/v1/accounts/credentials_controller.rb +++ b/app/controllers/api/v1/accounts/credentials_controller.rb @@ -13,6 +13,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController def update @account = current_account UpdateAccountService.new.call(@account, account_params, raise_error: true) + UserSettingsDecorator.new(current_user).update(user_settings_params) if user_settings_params ActivityPub::UpdateDistributionWorker.perform_async(@account.id) render json: @account, serializer: REST::CredentialAccountSerializer end @@ -22,4 +23,15 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController def account_params params.permit(:display_name, :note, :avatar, :header, :locked) end + + def user_settings_params + return nil unless params.key?(:source) + + source_params = params.require(:source) + + { + 'setting_default_privacy' => source_params.fetch(:privacy, @account.user.setting_default_privacy), + 'setting_default_sensitive' => source_params.fetch(:sensitive, @account.user.setting_default_sensitive), + } + end end diff --git a/app/lib/user_settings_decorator.rb b/app/lib/user_settings_decorator.rb index 4d6f19467..9260a81bc 100644 --- a/app/lib/user_settings_decorator.rb +++ b/app/lib/user_settings_decorator.rb @@ -83,7 +83,7 @@ class UserSettingsDecorator end def boolean_cast_setting(key) - settings[key] == '1' + ActiveModel::Type::Boolean.new.cast(settings[key]) end def coerced_settings(key) @@ -91,7 +91,7 @@ class UserSettingsDecorator end def coerce_values(params_hash) - params_hash.transform_values { |x| x == '1' } + params_hash.transform_values { |x| ActiveModel::Type::Boolean.new.cast(x) } end def change?(key) diff --git a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb index 461b8b34b..87fce64eb 100644 --- a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb @@ -28,6 +28,10 @@ describe Api::V1::Accounts::CredentialsController do note: "Hi!\n\nToot toot!", avatar: fixture_file_upload('files/avatar.gif', 'image/gif'), header: fixture_file_upload('files/attachment.jpg', 'image/jpeg'), + source: { + privacy: 'unlisted', + sensitive: true, + } } end @@ -42,6 +46,8 @@ describe Api::V1::Accounts::CredentialsController do expect(user.account.note).to eq("Hi!\n\nToot toot!") expect(user.account.avatar).to exist expect(user.account.header).to exist + expect(user.setting_default_privacy).to eq('unlisted') + expect(user.setting_default_sensitive).to eq(true) end it 'queues up an account update distribution' do diff --git a/spec/lib/user_settings_decorator_spec.rb b/spec/lib/user_settings_decorator_spec.rb index fee875373..462c5b124 100644 --- a/spec/lib/user_settings_decorator_spec.rb +++ b/spec/lib/user_settings_decorator_spec.rb @@ -69,5 +69,16 @@ describe UserSettingsDecorator do settings.update(values) expect(user.settings['system_font_ui']).to eq false end + + it 'decoerces setting values before applying' do + values = { + 'setting_delete_modal' => 'false', + 'setting_boost_modal' => 'true', + } + + settings.update(values) + expect(user.settings['delete_modal']).to eq false + expect(user.settings['boost_modal']).to eq true + end end end From c9cbb8de703e321c0d152813a2e22471ffe5eef7 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Sun, 8 Apr 2018 14:26:58 +0200 Subject: [PATCH 053/442] Add search item to tab bar for mobile devices (#7072) * Add search item to tab bar for mobile devices * Fix missing prop validation --- app/javascript/mastodon/features/compose/index.js | 5 +++-- app/javascript/mastodon/features/ui/components/tabs_bar.js | 1 + app/javascript/mastodon/features/ui/index.js | 2 ++ app/javascript/mastodon/locales/defaultMessages.json | 4 ++++ app/javascript/mastodon/locales/en.json | 1 + 5 files changed, 11 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index d5cd854db..da5bf7cff 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -38,6 +38,7 @@ export default class Compose extends React.PureComponent { columns: ImmutablePropTypes.list.isRequired, multiColumn: PropTypes.bool, showSearch: PropTypes.bool, + isSearchPage: PropTypes.bool, intl: PropTypes.object.isRequired, }; @@ -58,7 +59,7 @@ export default class Compose extends React.PureComponent { } render () { - const { multiColumn, showSearch, intl } = this.props; + const { multiColumn, showSearch, isSearchPage, intl } = this.props; let header = ''; @@ -102,7 +103,7 @@ export default class Compose extends React.PureComponent { )} - + {({ x }) => (
diff --git a/app/javascript/mastodon/features/ui/components/tabs_bar.js b/app/javascript/mastodon/features/ui/components/tabs_bar.js index dba3be98b..ed6de6f39 100644 --- a/app/javascript/mastodon/features/ui/components/tabs_bar.js +++ b/app/javascript/mastodon/features/ui/components/tabs_bar.js @@ -8,6 +8,7 @@ import { isUserTouching } from '../../../is_mobile'; export const links = [ , , + , , , diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 8894eb4e6..8b905fa1d 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -146,6 +146,8 @@ class SwitchingColumnsArea extends React.PureComponent { + + diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 5059fc67b..52b9db9ac 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -1717,6 +1717,10 @@ "defaultMessage": "Notifications", "id": "tabs_bar.notifications" }, + { + "defaultMessage": "Search", + "id": "tabs_bar.search" + }, { "defaultMessage": "Local", "id": "tabs_bar.local_timeline" diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 23040f7e4..2286d2e83 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -269,6 +269,7 @@ "tabs_bar.home": "Home", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notifications", + "tabs_bar.search": "Search", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Drag & drop to upload", "upload_button.label": "Add media", From 0893b1669548858daee79ab1260fb98646a0b3fa Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Sun, 8 Apr 2018 18:25:08 +0200 Subject: [PATCH 054/442] Hide search from Compose on mobile devices (#7077) * Hide search from Compose on mobile devices We're presently seeing large numbers of users accidentally tooting what they're trying to search for. This PR hides the search form from the Compose view, now that we have a dedicated "search" tab on mobile. * Don't "showSearch" on mobile if we're not currently searching (isSearchPage) --- app/javascript/mastodon/features/compose/index.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index da5bf7cff..67f0e7981 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -24,9 +24,9 @@ const messages = defineMessages({ logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, }); -const mapStateToProps = state => ({ +const mapStateToProps = (state, ownProps) => ({ columns: state.getIn(['settings', 'columns']), - showSearch: state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']), + showSearch: ownProps.multiColumn ? state.getIn(['search', 'submitted']) && !state.getIn(['search', 'hidden']) : ownProps.isSearchPage, }); @connect(mapStateToProps) @@ -90,7 +90,7 @@ export default class Compose extends React.PureComponent {
{header} - + {(multiColumn || isSearchPage) && }
From 498327b2e3f2e64f3a11993afdbd9d87ba73cc92 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 9 Apr 2018 16:58:53 +0900 Subject: [PATCH 055/442] Exclude status itself from context query (#7083) ancestor_statuses and descendant_statuses used to include the root status itself, but the behavior is confusing because the root status is not an ancestor nor descendant. --- app/models/concerns/status_threading_concern.rb | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/app/models/concerns/status_threading_concern.rb b/app/models/concerns/status_threading_concern.rb index 65f8e112e..b539ba10e 100644 --- a/app/models/concerns/status_threading_concern.rb +++ b/app/models/concerns/status_threading_concern.rb @@ -15,16 +15,12 @@ module StatusThreadingConcern def ancestor_ids Rails.cache.fetch("ancestors:#{id}") do - ancestors_without_self.pluck(:id) + ancestor_statuses.pluck(:id) end end - def ancestors_without_self - ancestor_statuses - [self] - end - def ancestor_statuses - Status.find_by_sql([<<-SQL.squish, id: id]) + Status.find_by_sql([<<-SQL.squish, id: in_reply_to_id]) WITH RECURSIVE search_tree(id, in_reply_to_id, path) AS ( SELECT id, in_reply_to_id, ARRAY[id] @@ -43,11 +39,7 @@ module StatusThreadingConcern end def descendant_ids - descendants_without_self.pluck(:id) - end - - def descendants_without_self - descendant_statuses - [self] + descendant_statuses.pluck(:id) end def descendant_statuses @@ -56,7 +48,7 @@ module StatusThreadingConcern AS ( SELECT id, ARRAY[id] FROM statuses - WHERE id = :id + WHERE in_reply_to_id = :id UNION ALL SELECT statuses.id, path || statuses.id FROM search_tree From 07d90b0414a68a7864cf824dcd55c4b7b5b9b984 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Mon, 9 Apr 2018 10:28:53 +0200 Subject: [PATCH 056/442] i18n: Update Polish translation (#7085) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- app/javascript/mastodon/locales/pl.json | 1 + config/locales/pl.yml | 77 +++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index ff7180df3..041935689 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -269,6 +269,7 @@ "tabs_bar.home": "Strona główna", "tabs_bar.local_timeline": "Lokalne", "tabs_bar.notifications": "Powiadomienia", + "tabs_bar.search": "Szukaj", "ui.beforeunload": "Utracisz tworzony wpis, jeżeli opuścisz Mastodona.", "upload_area.title": "Przeciągnij i upuść aby wysłać", "upload_button.label": "Dodaj zawartość multimedialną", diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 64c1ff5ea..d303171fb 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -700,6 +700,83 @@ pl: reblogged: podbił sensitive_content: Wrażliwa zawartość terms: + body_html: | +

Polityka prywatności

+

Jakie informacje zbieramy?

+ +
    +
  • Podstawowe informacje o koncie: Podczas rejestracji na tym serwerze, możesz zostać poproszony o wprowadzenie nazwy użytkownika, adresu e-mail i hasła. Możesz także wprowadzić dodatkowe informacje o profilu, takie jak nazwa wyświetlana i biografia oraz wysłać awatar i obraz nagłówka. Nazwa użytkownika, nazwa wyświetlana, biografia, awatar i obraz nagłówka są zawsze widoczne dla wszystkich.
  • +
  • Wpisy, śledzenie i inne publiczne informacje: Lista osób które śledzisz jest widoczna publicznie, tak jak lista osób, które Cię śledzą. Jeżeli dodasz wpis, data i czas jego utworzenia i aplikacja, z której go wysłano są przechowywane. Wiadomości mogą zawierać załączniki multimedialne, takie jak zdjęcia i filmy. Publiczne i niewidoczne wpisy są dostępne publicznie. Udostępniony wpis również jest widoczny publicznie. Twoje wpisy są dostarczane obserwującym, co oznacza że jego kopie mogą zostać dostarczone i być przechowywane na innych serwerach. Kiedy usuniesz wpis, przestaje być widoczny również dla osób śledzących Cię. „Podbijanie” i dodanie do ulubionych jest zawsze publiczne.
  • +
  • Wpisy bezpośrednie i tylko dla śledzących: Wszystkie wpisy są przechowywane i przetwarzane na serwerze. Wpisy przeznaczone tylko dla śledzących są widoczne tylko dla nich i osób wspomnianych we wpisie, a wpisy bezpośrednie tylko dla wspimnianych. W wielu przypadkach oznacza to, że ich kopie są dostarczane i przechowywane na innych serwerach. Staramy się ograniczać zasięg tych wpisów wyłącznie do właściwych odbiorców, ale inne serwery mogą tego nie robić. Ważne jest, aby sprawdzać jakich serwerów używają osoby, które Cię śledzą. Możesz aktywować opcję pozwalającą na ręczne akceptowanie i odrzucanie nowych śledzących. Pamiętaj, że właściciele serwerów mogą zobaczyć te wiadomości, a odbiorcy mogą wykonać zrzut ekranu, skopiować lub udostępniać ten wpis. Nie udostępniaj wrażliwych danych z użyciem Mastodona.
  • +
  • Adresy IP i inne metadane: Kiedy zalogujesz się, przechowujemy adres IP użyty w trakcie logowania wraz z nazwą używanej przeglądarki. Wszystkie aktywne sesje możesz zobaczyć (i wygasić) w ustawieniach. Ostatnio używany adres IP jest przechowywany przez nas do 12 miesięcy. Możemy również przechowywać adresy IP wykorzystywane przy każdym działaniu na serwerze.
  • +
+ +
+ +

W jakim celu wykorzystujecie informacje?

+ +

Zebrane informacje mogą zostać użyte w następujące sposoby:

+ +
    +
  • Aby dostarczyć podstawową funkcjonalność Mastodona. Możesz wchodzić w interakcje z zawartością tworzoną przez innych tylko gdy jesteś zalogowany. Na przykład, możesz śledzić innych, aby widzieć ich wpisy w dostosowanej osi czasu.
  • +
  • Aby wspomóc moderację społeczności, na przykład porównując Twój adres IP ze znanymi, aby rozpoznać próbę obejścia blokady i inne naruszenia.
  • +
  • Adres e-mail może zostać wykorzystany, aby wysyłać Ci informacje, powiadomienia o osobach wchodzących w interakcje z tworzoną przez Ciebie zawartością, wysyłających Ci wiadomości, odpowiadać na zgłoszenia i inne żądania lub zapytania.
  • +
+ +
+ +

W jaki sposób chronimy Twoje dane?

+ +

Wykorzystujemy różne zabezpieczenia, aby zapewnić bezpieczeństwo informacji, które wprowadzasz, wysyłasz lub do których uzyskujesz dostęp. Poza tym, sesja przeglądarki oraz ruch pomiędzy aplikacją a API jest zabezpieczany z użyciem SSL, a hasło jest hashowane z użyciem silnego algorytmu. Możesz też aktywować uwierzytelnianie dwustopniowe, aby lepiej zabezpieczyć dostęp do konta.

+ +
+ +

Jaka jest nasza polityka przechowywania danych?

+ +

Staramy się:

+ +
    +
  • Przechowywać logi zawierające adresy IP używane przy każdym żądaniu do serwera przez nie dłużej niż 90 dni.
  • +
  • Przechowywać adresy IP przypisane do użytkowników przez nie dłużej niż 12 miesięcy.
  • +
+ +

Możesz zażądać i pobrać archiwum tworzonej zawartości, wliczając Twoje wpisy, załączniki multimedialne, awatar i zdjęcie nagłówka.

+ +

Możesz nieodwracalnie usunąć konto w każdej chwili.

+ +
+ +

Czy używany plików cookies?

+ +

Tak. Pliki cookies są małymi plikami, które strona lub dostawca jej usługi dostarcza na dysk twardy komputera z użyciem przeglądarki internetowej (jeżeli na to pozwoli). Pliki cookies pozwalają na rozpoznanie przeglądarki i – jeśli jesteś zarejestrowany – przypisanie jej do konta.

+ +

Wykorzystujemy pliki cookies, aby przechowywać preferencję użytkowników na przyszłe wizyty.

+ +
+ +

Czy przekazujemy informacje osobom trzecim?

+ +

Nie sprzedajemy, nie wymieniamy i nie przekazujemy osobom trzecim informacji pozwalających na identyfikację Ciebie. Nie dotyczy to zaufanym dostawcom pomagającym w prowadzeniu lub obsługiwaniu użytkowników, jeżeli zgadzają się, aby nie przekazywać dalej tych informacji. Możemy również udostępnić informacje, jeżeli uważany to za wymagane przez prawo, konieczne do wypełnienia polityki strony, przestrzegania naszych lub cudzych praw, własności i bezpieczeństwa.

+ +

Twoja publiczna zawartość może zostać pobrana przez inne serwery w sieci. Wpisy publiczne i tylko dla śledzących są dostarczane na serwery, na których znajdują się śledzący Cię, a wiadomości bezpośrednie trafiają na serwery adresatów, jeżeli są oni użytkownikami innego serwera.

+ +

Kiedy pozwolisz aplikacji na dostęp do Twojego konta, w zależności od nadanych jej pozwoleń, może uzyskać dostęp do publicznych informacji, listy śledzonych, Twoich list, wszystkich wpisów i ulubionych. Aplikacje nie mogą uzyskać dostępu do Twojego adresu e-mail i hasła.

+ +
+ +

Children's Online Privacy Protection Act Compliance

+ +

Ta strona, produkty i usługi są przeznaczone dla osób, które ukończyły 13 lat. Jeżeli serwer znajduje się w USA, a nie ukończyłeś 13 roku życia, zgodnie z wymogami COPPA (Prawo o Ochronie Prywatności Dzieci w Internecie), nie używaj tej strony.

+ +
+ +

Zmiany w naszej polityce prywatności

+ +

Jeżeli zdecydujemy się na zmiany w polityce prywatności, pojawią się na tej stronie.

+ +

Dokument jest dostępny na licencji CC-BY-SA. Ostatnio zmodyfikowano go 7 marca 2018, przetłumaczono 9 kwietnia 2018. Tłumaczenie (mimo dołożenia wszelkich starań) może nie być w pełni poprawne.

+ +

Bazowano na polityce prywatności Discourse.

title: Zasady korzystania i polityka prywatności %{instance} themes: default: Mastodon From e057c0e525692a7dcca188f254f043e3c77e1a8a Mon Sep 17 00:00:00 2001 From: Una Date: Mon, 9 Apr 2018 05:34:48 -0400 Subject: [PATCH 057/442] Optimize public/headers/missing.png (#7084) --- public/headers/original/missing.png | Bin 14573 -> 81 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/public/headers/original/missing.png b/public/headers/original/missing.png index fdc34289db331fee185f39fe660d99412d1b4276..26b59e75a08de1c5d3d7b0705e9fc19d317dc4cb 100644 GIT binary patch delta 63 zcmaD`7&t+~l92%j%=3P#04b)DAirP+hO)_LEr2{OPZ!4!j_b(@AQ=Y6M~Zulfh-13 LS3j3^P6Hn!8G zJs~*ll@mf-x$+Ni-~xXEH%?qZNL)cel<|)^FK)84i}pB{k~}|u-+P|VyqWmoCr5`L z+}(O}3n6s(V844ze{Ut9J1^7kw_f}43H^B`*guO9di7rNxrM&|<_&~eJ-2rnpPC

l*J+EN)J@3@I%MSTUxno4>ab^D zo!hD-?igG8T%!plB({Y4Wb8%Sye+1AHEJhrEDEV4K5vV=NkQS%JQ6y7NQ8Q|QL*Li zme5qIa$RjHwFiPC$qg*2SguthS(8*vl7;*bjV)^Fp)=5qyAShn^sFt8;yBPSp3P>} z*>=?rhgeos6-x?MlnPZ;qDwEf<`plxpCwu3>5|9}-5_>-Pe}4weSaFaMKLK<9Qn8= zK~czya(0v=o?8Kys}f!pYr4Q;Y_L5FgG-RNU?u$-KgOv#mv_fF>S zhOp#H7M8X`HbXictJVpL6_YOg7_@_$w0A0s(vuomO@2?NeI)6)BwJ2#n!xlOcW}8b zfuboA%mI|;9JJjXE4H2~3)GhKq=WIpp6`#1Y{?ug`a$S)k|oj<-Z3rP^^zue7Yr>A z0{I+U8~EYGiVb&S4G9jsp^leMpe&k7>-b|oq{Ae}wvN}iK~&mk+JP6vmS>ZLu0b7D z*LAeM)MzRF`k>O6`^`#CRhyMoLy{`Cy4@Po`*v;Mko3+?6NcYN-SzEhvRLxe&U2;) z;Wtu$6uNYyv&KM^-2L;GSd->PseLz!{qPdBEp6U(D?1^du6h?2#%{VL1y&f5WWlw? z#fmHTuwsi!V$y}HS$49S8Ig5C938LtmN!+z|HX>emZNjCMrAk$s zlSKX?o>?K;9ny|?wzZMvT@Bn^z#duNkT?cjuv<9FAv#X}FUIcb+-2U?DP1)A%@)v; zZBV1_LkDA}(F%FPB4?kso_*Y$$oP zk{d)Ndv&&xoY2P#Vl?XY?YfT3?sCMDJ167Qu1lAElr%CWl7^K1Yvax~!~uUEmrWG- z`%mXF^U{Y4`dovH2br3({cP92XN26mVRS2Gi!an24Z&Yis$cAo1w?fiwCd;2+IjAJCTw1$+P4MCj^m zgg*NMp}()`?{5fAC4_!|jF9#fLa+N@KKkh))!sYk?)K)t^#8iP-nmWBudi>TJO8}& V@6X>(EqaO$_71z>z5n?0{{X8qwp0KB From 904a2479dd2085dfc94f33746ad6f7a755e72609 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Mon, 9 Apr 2018 17:09:11 +0200 Subject: [PATCH 058/442] Feature: Direct message from Statuses (#7089) * Fix: Switching between composing direct message and mention from menus Previously clicking "direct message" followed by "mention" resulted in the composed status staying as "direct", along with weird spacing of items in the text area. This attempts to fix that. * Fix: Add missing proptype check for onMention in Status component * Add the ability to send a direct message to a user from the menu on Statuses * Add space between "Embed" and "Mention" on expanded statuses menu --- app/javascript/mastodon/components/status.js | 2 ++ .../mastodon/components/status_action_bar.js | 7 +++++++ .../mastodon/containers/status_container.js | 5 +++++ .../features/status/components/action_bar.js | 8 ++++++++ .../mastodon/features/status/index.js | 6 ++++++ .../mastodon/locales/defaultMessages.json | 8 ++++++++ app/javascript/mastodon/locales/en.json | 1 + app/javascript/mastodon/reducers/compose.js | 20 ++++++++++--------- 8 files changed, 48 insertions(+), 9 deletions(-) diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index a918a94f8..6129b3f1e 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -31,6 +31,8 @@ export default class Status extends ImmutablePureComponent { onFavourite: PropTypes.func, onReblog: PropTypes.func, onDelete: PropTypes.func, + onDirect: PropTypes.func, + onMention: PropTypes.func, onPin: PropTypes.func, onOpenMedia: PropTypes.func, onOpenVideo: PropTypes.func, diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index e036dc1da..10f34b0c7 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -9,6 +9,7 @@ import { me } from '../initial_state'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, + direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' }, mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' }, mute: { id: 'account.mute', defaultMessage: 'Mute @{name}' }, block: { id: 'account.block', defaultMessage: 'Block @{name}' }, @@ -41,6 +42,7 @@ export default class StatusActionBar extends ImmutablePureComponent { onFavourite: PropTypes.func, onReblog: PropTypes.func, onDelete: PropTypes.func, + onDirect: PropTypes.func, onMention: PropTypes.func, onMute: PropTypes.func, onBlock: PropTypes.func, @@ -92,6 +94,10 @@ export default class StatusActionBar extends ImmutablePureComponent { this.props.onMention(this.props.status.get('account'), this.context.router.history); } + handleDirectClick = () => { + this.props.onDirect(this.props.status.get('account'), this.context.router.history); + } + handleMuteClick = () => { this.props.onMute(this.props.status.get('account')); } @@ -149,6 +155,7 @@ export default class StatusActionBar extends ImmutablePureComponent { menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); } else { menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick }); + menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick }); menu.push(null); menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick }); menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick }); diff --git a/app/javascript/mastodon/containers/status_container.js b/app/javascript/mastodon/containers/status_container.js index 4579bd132..f22509edf 100644 --- a/app/javascript/mastodon/containers/status_container.js +++ b/app/javascript/mastodon/containers/status_container.js @@ -5,6 +5,7 @@ import { makeGetStatus } from '../selectors'; import { replyCompose, mentionCompose, + directCompose, } from '../actions/compose'; import { reblog, @@ -102,6 +103,10 @@ const mapDispatchToProps = (dispatch, { intl }) => ({ } }, + onDirect (account, router) { + dispatch(directCompose(account, router)); + }, + onMention (account, router) { dispatch(mentionCompose(account, router)); }, diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js index 13cc10c9c..4aa6b08f2 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.js +++ b/app/javascript/mastodon/features/status/components/action_bar.js @@ -8,6 +8,7 @@ import { me } from '../../../initial_state'; const messages = defineMessages({ delete: { id: 'status.delete', defaultMessage: 'Delete' }, + direct: { id: 'status.direct', defaultMessage: 'Direct message @{name}' }, mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' }, reply: { id: 'status.reply', defaultMessage: 'Reply' }, reblog: { id: 'status.reblog', defaultMessage: 'Boost' }, @@ -37,6 +38,7 @@ export default class ActionBar extends React.PureComponent { onReblog: PropTypes.func.isRequired, onFavourite: PropTypes.func.isRequired, onDelete: PropTypes.func.isRequired, + onDirect: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, onMute: PropTypes.func, onMuteConversation: PropTypes.func, @@ -63,6 +65,10 @@ export default class ActionBar extends React.PureComponent { this.props.onDelete(this.props.status); } + handleDirectClick = () => { + this.props.onDirect(this.props.status.get('account'), this.context.router.history); + } + handleMentionClick = () => { this.props.onMention(this.props.status.get('account'), this.context.router.history); } @@ -108,6 +114,7 @@ export default class ActionBar extends React.PureComponent { if (publicStatus) { menu.push({ text: intl.formatMessage(messages.embed), action: this.handleEmbed }); + menu.push(null); } if (me === status.getIn(['account', 'id'])) { @@ -121,6 +128,7 @@ export default class ActionBar extends React.PureComponent { menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); } else { menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick }); + menu.push({ text: intl.formatMessage(messages.direct, { name: status.getIn(['account', 'username']) }), action: this.handleDirectClick }); menu.push(null); menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick }); menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick }); diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index 2f482b292..55eff0823 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -19,6 +19,7 @@ import { import { replyCompose, mentionCompose, + directCompose, } from '../../actions/compose'; import { blockAccount } from '../../actions/accounts'; import { @@ -148,6 +149,10 @@ export default class Status extends ImmutablePureComponent { } } + handleDirectClick = (account, router) => { + this.props.dispatch(directCompose(account, router)); + } + handleMentionClick = (account, router) => { this.props.dispatch(mentionCompose(account, router)); } @@ -379,6 +384,7 @@ export default class Status extends ImmutablePureComponent { onFavourite={this.handleFavouriteClick} onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} + onDirect={this.handleDirectClick} onMention={this.handleMentionClick} onMute={this.handleMuteClick} onMuteConversation={this.handleConversationMuteClick} diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 52b9db9ac..03be288bb 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -197,6 +197,10 @@ "defaultMessage": "Delete", "id": "status.delete" }, + { + "defaultMessage": "Direct message @{name}", + "id": "status.direct" + }, { "defaultMessage": "Mention @{name}", "id": "status.mention" @@ -1370,6 +1374,10 @@ "defaultMessage": "Delete", "id": "status.delete" }, + { + "defaultMessage": "Direct message @{name}", + "id": "status.direct" + }, { "defaultMessage": "Mention @{name}", "id": "status.mention" diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 2286d2e83..a389735c1 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Delete", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Favourite", "status.load_more": "Load more", diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 1f4177585..87049ea79 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -259,16 +259,18 @@ export default function compose(state = initialState, action) { case COMPOSE_UPLOAD_PROGRESS: return state.set('progress', Math.round((action.loaded / action.total) * 100)); case COMPOSE_MENTION: - return state - .update('text', text => `${text}@${action.account.get('acct')} `) - .set('focusDate', new Date()) - .set('idempotencyKey', uuid()); + return state.withMutations(map => { + map.update('text', text => [text.trim(), `@${action.account.get('acct')} `].filter((str) => str.length !== 0).join(' ')); + map.set('focusDate', new Date()); + map.set('idempotencyKey', uuid()); + }); case COMPOSE_DIRECT: - return state - .update('text', text => `@${action.account.get('acct')} `) - .set('privacy', 'direct') - .set('focusDate', new Date()) - .set('idempotencyKey', uuid()); + return state.withMutations(map => { + map.update('text', text => [text.trim(), `@${action.account.get('acct')} `].filter((str) => str.length !== 0).join(' ')); + map.set('privacy', 'direct'); + map.set('focusDate', new Date()); + map.set('idempotencyKey', uuid()); + }); case COMPOSE_SUGGESTIONS_CLEAR: return state.update('suggestions', ImmutableList(), list => list.clear()).set('suggestion_token', null); case COMPOSE_SUGGESTIONS_READY: From 0c52654b5275fd0e7a0b544d3c6f895825e4a266 Mon Sep 17 00:00:00 2001 From: "Renato \"Lond\" Cerqueira" Date: Mon, 9 Apr 2018 23:02:42 +0200 Subject: [PATCH 059/442] When creating status, if no sensitive status is given, use default (#7057) Clients using the API that do not provide the sensitive flag are always posting with false sensitive option. --- app/services/post_status_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index c91192181..9d2c7a4dc 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -28,7 +28,7 @@ class PostStatusService < BaseService status = account.statuses.create!(text: text, media_attachments: media || [], thread: in_reply_to, - sensitive: options[:sensitive], + sensitive: (options[:sensitive].nil? ? account.user&.setting_default_sensitive : options[:sensitive]), spoiler_text: options[:spoiler_text] || '', visibility: options[:visibility] || account.user&.setting_default_privacy, language: LanguageDetector.instance.detect(text, account), From 80a944c882ddbca4d546d03503f0ccff15703484 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 10 Apr 2018 01:20:18 +0200 Subject: [PATCH 060/442] Log rate limit hits (#7096) Fix #7095 --- config/initializers/rack_attack_logging.rb | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 config/initializers/rack_attack_logging.rb diff --git a/config/initializers/rack_attack_logging.rb b/config/initializers/rack_attack_logging.rb new file mode 100644 index 000000000..2ddbfb99c --- /dev/null +++ b/config/initializers/rack_attack_logging.rb @@ -0,0 +1,4 @@ +ActiveSupport::Notifications.subscribe('rack.attack') do |_name, _start, _finish, _request_id, req| + next unless [:throttle, :blacklist].include? req.env['rack.attack.match_type'] + Rails.logger.info("Rate limit hit (#{req.env['rack.attack.match_type']}): #{req.ip} #{req.request_method} #{req.fullpath}") +end From e6e93ecd8a45cea5f0c398054c2292a5fdf944cf Mon Sep 17 00:00:00 2001 From: MIYAGI Hikaru Date: Tue, 10 Apr 2018 16:11:55 +0900 Subject: [PATCH 061/442] Fix GIFV encoding params (#7098) - Explicitly specify video codec. When ffmpeg isn't compiled with libx264 but openh264, mpeg4 is selected as video codec. - Swap avarage bitrate and max bitrate. --- app/models/media_attachment.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index ac2aa7ed2..8fd9ac09f 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -130,8 +130,9 @@ class MediaAttachment < ApplicationRecord 'pix_fmt' => 'yuv420p', 'vf' => 'scale=\'trunc(iw/2)*2:trunc(ih/2)*2\'', 'vsync' => 'cfr', - 'b:v' => '1300K', - 'maxrate' => '500K', + 'c:v' => 'h264', + 'b:v' => '500K', + 'maxrate' => '1300K', 'bufsize' => '1300K', 'crf' => 18, }, From 219a4423d8371fc89f122f3ef4874e9121b423f7 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Tue, 10 Apr 2018 09:16:06 +0200 Subject: [PATCH 062/442] Feature: Allow staff to change user emails (#7074) * Admin: Show unconfirmed email address on account page * Admin: Allow staff to change user email addresses * ActionLog: On change_email, log current email address and new unconfirmed email address --- .../admin/change_emails_controller.rb | 49 +++++++++++++++++++ app/helpers/admin/action_logs_helper.rb | 4 +- app/models/account.rb | 1 + app/models/admin/action_log.rb | 5 ++ app/policies/user_policy.rb | 4 ++ app/views/admin/accounts/show.html.haml | 6 ++- app/views/admin/change_emails/show.html.haml | 7 +++ config/locales/en.yml | 9 ++++ config/routes.rb | 1 + .../admin/change_email_controller_spec.rb | 47 ++++++++++++++++++ 10 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 app/controllers/admin/change_emails_controller.rb create mode 100644 app/views/admin/change_emails/show.html.haml create mode 100644 spec/controllers/admin/change_email_controller_spec.rb diff --git a/app/controllers/admin/change_emails_controller.rb b/app/controllers/admin/change_emails_controller.rb new file mode 100644 index 000000000..a689d3a53 --- /dev/null +++ b/app/controllers/admin/change_emails_controller.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +module Admin + class ChangeEmailsController < BaseController + before_action :set_account + before_action :require_local_account! + + def show + authorize @user, :change_email? + end + + def update + authorize @user, :change_email? + + new_email = resource_params.fetch(:unconfirmed_email) + + if new_email != @user.email + @user.update!( + unconfirmed_email: new_email, + # Regenerate the confirmation token: + confirmation_token: nil + ) + + log_action :change_email, @user + + @user.send_confirmation_instructions + end + + redirect_to admin_account_path(@account.id), notice: I18n.t('admin.accounts.change_email.changed_msg') + end + + private + + def set_account + @account = Account.find(params[:account_id]) + @user = @account.user + end + + def require_local_account! + redirect_to admin_account_path(@account.id) unless @account.local? && @account.user.present? + end + + def resource_params + params.require(:user).permit( + :unconfirmed_email + ) + end + end +end diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb index 7c26c0b05..4c663211e 100644 --- a/app/helpers/admin/action_logs_helper.rb +++ b/app/helpers/admin/action_logs_helper.rb @@ -45,6 +45,8 @@ module Admin::ActionLogsHelper log.recorded_changes.slice('domain', 'visible_in_picker') elsif log.target_type == 'User' && [:promote, :demote].include?(log.action) log.recorded_changes.slice('moderator', 'admin') + elsif log.target_type == 'User' && [:change_email].include?(log.action) + log.recorded_changes.slice('email', 'unconfirmed_email') elsif log.target_type == 'DomainBlock' log.recorded_changes.slice('severity', 'reject_media') elsif log.target_type == 'Status' && log.action == :update @@ -84,7 +86,7 @@ module Admin::ActionLogsHelper 'positive' when :create opposite_verbs?(log) ? 'negative' : 'positive' - when :update, :reset_password, :disable_2fa, :memorialize + when :update, :reset_password, :disable_2fa, :memorialize, :change_email 'neutral' when :demote, :silence, :disable, :suspend, :remove_avatar, :reopen 'negative' diff --git a/app/models/account.rb b/app/models/account.rb index 446144a3e..51304fc18 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -124,6 +124,7 @@ class Account < ApplicationRecord scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) } delegate :email, + :unconfirmed_email, :current_sign_in_ip, :current_sign_in_at, :confirmed?, diff --git a/app/models/admin/action_log.rb b/app/models/admin/action_log.rb index c437c8ee8..81f278e07 100644 --- a/app/models/admin/action_log.rb +++ b/app/models/admin/action_log.rb @@ -35,6 +35,11 @@ class Admin::ActionLog < ApplicationRecord self.recorded_changes = target.attributes when :update, :promote, :demote self.recorded_changes = target.previous_changes + when :change_email + self.recorded_changes = ActiveSupport::HashWithIndifferentAccess.new( + email: [target.email, nil], + unconfirmed_email: [nil, target.unconfirmed_email] + ) end end end diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index aae207d06..dabdf707a 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -5,6 +5,10 @@ class UserPolicy < ApplicationPolicy staff? && !record.staff? end + def change_email? + staff? && !record.staff? + end + def disable_2fa? admin? && !record.staff? end diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index fecfd6cc8..7312618ee 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -36,9 +36,13 @@ %th= t('admin.accounts.email') %td = @account.user_email - - if @account.user_confirmed? = fa_icon('check') + = table_link_to 'edit', t('admin.accounts.change_email.label'), admin_account_change_email_path(@account.id) if can?(:change_email, @account.user) + - if @account.user_unconfirmed_email.present? + %th= t('admin.accounts.unconfirmed_email') + %td + = @account.user_unconfirmed_email %tr %th= t('admin.accounts.login_status') %td diff --git a/app/views/admin/change_emails/show.html.haml b/app/views/admin/change_emails/show.html.haml new file mode 100644 index 000000000..a661b1ad6 --- /dev/null +++ b/app/views/admin/change_emails/show.html.haml @@ -0,0 +1,7 @@ +- content_for :page_title do + = t('admin.accounts.change_email.title', username: @account.acct) + += simple_form_for @user, url: admin_account_change_email_path(@account.id) do |f| + = f.input :email, wrapper: :with_label, disabled: true, label: t('admin.accounts.change_email.current_email') + = f.input :unconfirmed_email, wrapper: :with_label, label: t('admin.accounts.change_email.new_email') + = f.button :submit, class: "button", value: t('admin.accounts.change_email.submit') diff --git a/config/locales/en.yml b/config/locales/en.yml index 70af9530c..11f3fb924 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -63,6 +63,13 @@ en: are_you_sure: Are you sure? avatar: Avatar by_domain: Domain + change_email: + changed_msg: Account email successfully changed! + current_email: Current Email + label: Change Email + new_email: New Email + submit: Change Email + title: Change Email for %{username} confirm: Confirm confirmed: Confirmed demote: Demote @@ -131,6 +138,7 @@ en: statuses: Statuses subscribe: Subscribe title: Accounts + unconfirmed_email: Unconfirmed E-mail undo_silenced: Undo silence undo_suspension: Undo suspension unsubscribe: Unsubscribe @@ -139,6 +147,7 @@ en: action_logs: actions: assigned_to_self_report: "%{name} assigned report %{target} to themselves" + change_email_user: "%{name} changed the e-mail address of user %{target}" confirm_user: "%{name} confirmed e-mail address of user %{target}" create_custom_emoji: "%{name} uploaded new emoji %{target}" create_domain_block: "%{name} blocked domain %{target}" diff --git a/config/routes.rb b/config/routes.rb index 7187fd743..2776898ac 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -151,6 +151,7 @@ Rails.application.routes.draw do post :memorialize end + resource :change_email, only: [:show, :update] resource :reset, only: [:create] resource :silence, only: [:create, :destroy] resource :suspension, only: [:create, :destroy] diff --git a/spec/controllers/admin/change_email_controller_spec.rb b/spec/controllers/admin/change_email_controller_spec.rb new file mode 100644 index 000000000..50f94f835 --- /dev/null +++ b/spec/controllers/admin/change_email_controller_spec.rb @@ -0,0 +1,47 @@ +require 'rails_helper' + +RSpec.describe Admin::ChangeEmailsController, type: :controller do + render_views + + let(:admin) { Fabricate(:user, admin: true) } + + before do + sign_in admin + end + + describe "GET #show" do + it "returns http success" do + account = Fabricate(:account) + user = Fabricate(:user, account: account) + + get :show, params: { account_id: account.id } + + expect(response).to have_http_status(:success) + end + end + + describe "GET #update" do + before do + allow(UserMailer).to receive(:confirmation_instructions).and_return(double('email', deliver_later: nil)) + end + + it "returns http success" do + account = Fabricate(:account) + user = Fabricate(:user, account: account) + + previous_email = user.email + + post :update, params: { account_id: account.id, user: { unconfirmed_email: 'test@example.com' } } + + user.reload + + expect(user.email).to eq previous_email + expect(user.unconfirmed_email).to eq 'test@example.com' + expect(user.confirmation_token).not_to be_nil + + expect(UserMailer).to have_received(:confirmation_instructions).with(user, user.confirmation_token, { to: 'test@example.com' }) + + expect(response).to redirect_to(admin_account_path(account.id)) + end + end +end From 8f800ad6917e5fb41d17098f3f860d0d1aedcabe Mon Sep 17 00:00:00 2001 From: Paul Woolcock Date: Tue, 10 Apr 2018 09:46:27 -0400 Subject: [PATCH 063/442] Change custom emoji search to `ILIKE` instead of `=` (#7099) --- app/models/custom_emoji.rb | 4 ++++ app/models/custom_emoji_filter.rb | 2 +- spec/models/custom_emoji_spec.rb | 24 ++++++++++++++++++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 476178e86..1ec21d1a0 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -58,5 +58,9 @@ class CustomEmoji < ApplicationRecord where(shortcode: shortcodes, domain: domain, disabled: false) end + + def search(shortcode) + where('"custom_emojis"."shortcode" ILIKE ?', "%#{shortcode}%") + end end end diff --git a/app/models/custom_emoji_filter.rb b/app/models/custom_emoji_filter.rb index 2c09ed65c..c4bc310bb 100644 --- a/app/models/custom_emoji_filter.rb +++ b/app/models/custom_emoji_filter.rb @@ -28,7 +28,7 @@ class CustomEmojiFilter when 'by_domain' CustomEmoji.where(domain: value) when 'shortcode' - CustomEmoji.where(shortcode: value) + CustomEmoji.search(value) else raise "Unknown filter: #{key}" end diff --git a/spec/models/custom_emoji_spec.rb b/spec/models/custom_emoji_spec.rb index bb150b837..87367df50 100644 --- a/spec/models/custom_emoji_spec.rb +++ b/spec/models/custom_emoji_spec.rb @@ -1,6 +1,30 @@ require 'rails_helper' RSpec.describe CustomEmoji, type: :model do + describe '#search' do + let(:custom_emoji) { Fabricate(:custom_emoji, shortcode: shortcode) } + + subject { described_class.search(search_term) } + + context 'shortcode is exact' do + let(:shortcode) { 'blobpats' } + let(:search_term) { 'blobpats' } + + it 'finds emoji' do + is_expected.to include(custom_emoji) + end + end + + context 'shortcode is partial' do + let(:shortcode) { 'blobpats' } + let(:search_term) { 'blob' } + + it 'finds emoji' do + is_expected.to include(custom_emoji) + end + end + end + describe '#local?' do let(:custom_emoji) { Fabricate(:custom_emoji, domain: domain) } From 49bbef1202f483d4e8abc43d00d12551bb25f80f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 10 Apr 2018 16:08:28 +0200 Subject: [PATCH 064/442] Use RAILS_LOG_LEVEL to set log level of Sidekiq, too (#7079) Fix #3565 (oops) --- config/initializers/sidekiq.rb | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb index f875fbd95..05c804100 100644 --- a/config/initializers/sidekiq.rb +++ b/config/initializers/sidekiq.rb @@ -1,10 +1,10 @@ # frozen_string_literal: true -namespace = ENV.fetch('REDIS_NAMESPACE') { nil } +namespace = ENV.fetch('REDIS_NAMESPACE') { nil } redis_params = { url: ENV['REDIS_URL'] } if namespace - redis_params [:namespace] = namespace + redis_params[:namespace] = namespace end Sidekiq.configure_server do |config| @@ -18,3 +18,5 @@ end Sidekiq.configure_client do |config| config.redis = redis_params end + +Sidekiq::Logging.logger.level = ::Logger::const_get(ENV.fetch('RAILS_LOG_LEVEL', 'info').upcase.to_s) From 45c9f16f714dd6de15391b5e2ae2bf0d30ef20fb Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 10 Apr 2018 17:12:10 +0200 Subject: [PATCH 065/442] Improve load gap styling in web UI (#7100) --- .../mastodon/components/load_gap.js | 33 +++++++++++++++++++ .../mastodon/components/status_list.js | 20 +---------- .../mastodon/features/notifications/index.js | 20 +---------- .../styles/mastodon/components.scss | 4 +++ 4 files changed, 39 insertions(+), 38 deletions(-) create mode 100644 app/javascript/mastodon/components/load_gap.js diff --git a/app/javascript/mastodon/components/load_gap.js b/app/javascript/mastodon/components/load_gap.js new file mode 100644 index 000000000..012303ae1 --- /dev/null +++ b/app/javascript/mastodon/components/load_gap.js @@ -0,0 +1,33 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { injectIntl, defineMessages } from 'react-intl'; + +const messages = defineMessages({ + load_more: { id: 'status.load_more', defaultMessage: 'Load more' }, +}); + +@injectIntl +export default class LoadGap extends React.PureComponent { + + static propTypes = { + disabled: PropTypes.bool, + maxId: PropTypes.string, + onClick: PropTypes.func.isRequired, + intl: PropTypes.object.isRequired, + }; + + handleClick = () => { + this.props.onClick(this.props.maxId); + } + + render () { + const { disabled, intl } = this.props; + + return ( + + ); + } + +} diff --git a/app/javascript/mastodon/components/status_list.js b/app/javascript/mastodon/components/status_list.js index 8c2673f30..c98d4564e 100644 --- a/app/javascript/mastodon/components/status_list.js +++ b/app/javascript/mastodon/components/status_list.js @@ -4,28 +4,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; import PropTypes from 'prop-types'; import StatusContainer from '../containers/status_container'; import ImmutablePureComponent from 'react-immutable-pure-component'; -import LoadMore from './load_more'; +import LoadGap from './load_gap'; import ScrollableList from './scrollable_list'; import { FormattedMessage } from 'react-intl'; -class LoadGap extends ImmutablePureComponent { - - static propTypes = { - disabled: PropTypes.bool, - maxId: PropTypes.string, - onClick: PropTypes.func.isRequired, - }; - - handleClick = () => { - this.props.onClick(this.props.maxId); - } - - render () { - return ; - } - -} - export default class StatusList extends ImmutablePureComponent { static propTypes = { diff --git a/app/javascript/mastodon/features/notifications/index.js b/app/javascript/mastodon/features/notifications/index.js index 9a6fb45c8..94a46b833 100644 --- a/app/javascript/mastodon/features/notifications/index.js +++ b/app/javascript/mastodon/features/notifications/index.js @@ -13,7 +13,7 @@ import { createSelector } from 'reselect'; import { List as ImmutableList } from 'immutable'; import { debounce } from 'lodash'; import ScrollableList from '../../components/scrollable_list'; -import LoadMore from '../../components/load_more'; +import LoadGap from '../../components/load_gap'; const messages = defineMessages({ title: { id: 'column.notifications', defaultMessage: 'Notifications' }, @@ -24,24 +24,6 @@ const getNotifications = createSelector([ state => state.getIn(['notifications', 'items']), ], (excludedTypes, notifications) => notifications.filterNot(item => item !== null && excludedTypes.includes(item.get('type')))); -class LoadGap extends React.PureComponent { - - static propTypes = { - disabled: PropTypes.bool, - maxId: PropTypes.string, - onClick: PropTypes.func.isRequired, - }; - - handleClick = () => { - this.props.onClick(this.props.maxId); - } - - render () { - return ; - } - -} - const mapStateToProps = state => ({ notifications: getNotifications(state), isLoading: state.getIn(['notifications', 'isLoading'], true), diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index d76dc10f1..888a0ad82 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -2455,6 +2455,10 @@ a.status-card { } } +.load-gap { + border-bottom: 1px solid lighten($ui-base-color, 8%); +} + .regeneration-indicator { text-align: center; font-size: 16px; From d9b62e34da0c0238176f27557ac7b953da94df7e Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Tue, 10 Apr 2018 20:27:59 +0200 Subject: [PATCH 066/442] Feature: Improve reports ui (#7032) * Further improvements to Reports UI - Clean up notes display - Clean up add new note form - Simplify controller - Allow reopening a report with a note - Show created at date for reports - Fix report details table formatting * Show history of report using Admin::ActionLog beneath the report * Fix incorrect log message when reopening a report * Implement fetching of all ActionLog items that could be related to the report * Ensure adding a report_note updates the report's updated_at * Limit Report History to actions that happened between the report being created and the report being resolved * Fix linting issues * Improve report history builder Thanks @gargron for the improvements --- .../admin/report_notes_controller.rb | 17 +++++-- app/controllers/admin/reports_controller.rb | 20 ++++---- app/javascript/styles/mastodon/admin.scss | 35 ++++++++++++++ app/models/report.rb | 46 ++++++++++++++++++ app/models/report_note.rb | 2 +- .../admin/report_notes/_report_note.html.haml | 12 ++--- app/views/admin/reports/show.html.haml | 47 ++++++++++++------- config/locales/en.yml | 11 +++-- 8 files changed, 147 insertions(+), 43 deletions(-) diff --git a/app/controllers/admin/report_notes_controller.rb b/app/controllers/admin/report_notes_controller.rb index ef8c0f469..bcb3f2026 100644 --- a/app/controllers/admin/report_notes_controller.rb +++ b/app/controllers/admin/report_notes_controller.rb @@ -8,19 +8,26 @@ module Admin authorize ReportNote, :create? @report_note = current_account.report_notes.new(resource_params) + @report = @report_note.report if @report_note.save if params[:create_and_resolve] - @report_note.report.update!(action_taken: true, action_taken_by_account_id: current_account.id) - log_action :resolve, @report_note.report + @report.resolve!(current_account) + log_action :resolve, @report redirect_to admin_reports_path, notice: I18n.t('admin.reports.resolved_msg') - else - redirect_to admin_report_path(@report_note.report_id), notice: I18n.t('admin.report_notes.created_msg') + return end + + if params[:create_and_unresolve] + @report.unresolve! + log_action :reopen, @report + end + + redirect_to admin_report_path(@report), notice: I18n.t('admin.report_notes.created_msg') else - @report = @report_note.report @report_notes = @report.notes.latest + @report_history = @report.history @form = Form::StatusBatch.new render template: 'admin/reports/show' diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index fc3785e3b..a4ae9507d 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -13,6 +13,7 @@ module Admin authorize @report, :show? @report_note = @report.notes.new @report_notes = @report.notes.latest + @report_history = @report.history @form = Form::StatusBatch.new end @@ -38,36 +39,33 @@ module Admin @report.update!(assigned_account_id: nil) log_action :unassigned, @report when 'reopen' - @report.update!(action_taken: false, action_taken_by_account_id: nil) + @report.unresolve! log_action :reopen, @report when 'resolve' - @report.update!(action_taken_by_current_attributes) + @report.resolve!(current_account) log_action :resolve, @report when 'suspend' Admin::SuspensionWorker.perform_async(@report.target_account.id) + log_action :resolve, @report log_action :suspend, @report.target_account + resolve_all_target_account_reports - @report.reload when 'silence' @report.target_account.update!(silenced: true) + log_action :resolve, @report log_action :silence, @report.target_account + resolve_all_target_account_reports - @report.reload else raise ActiveRecord::RecordNotFound end - end - - def action_taken_by_current_attributes - { action_taken: true, action_taken_by_account_id: current_account.id } + @report.reload end def resolve_all_target_account_reports - unresolved_reports_for_target_account.update_all( - action_taken_by_current_attributes - ) + unresolved_reports_for_target_account.update_all(action_taken: true, action_taken_by_account_id: current_account.id) end def unresolved_reports_for_target_account diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index e6bd0c717..6bd659030 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -145,6 +145,11 @@ border: 0; background: transparent; border-bottom: 1px solid $ui-base-color; + + &.section-break { + margin: 30px 0; + border-bottom: 2px solid $ui-base-lighter-color; + } } .muted-hint { @@ -330,6 +335,36 @@ } } +.report-note__comment { + margin-bottom: 20px; +} + +.report-note__form { + margin-bottom: 20px; + + .report-note__textarea { + box-sizing: border-box; + border: 0; + padding: 7px 4px; + margin-bottom: 10px; + font-size: 16px; + color: $ui-base-color; + display: block; + width: 100%; + outline: 0; + font-family: inherit; + resize: vertical; + } + + .report-note__buttons { + text-align: right; + } + + .report-note__button { + margin: 0 0 5px 5px; + } +} + .batch-form-box { display: flex; flex-wrap: wrap; diff --git a/app/models/report.rb b/app/models/report.rb index f5b37cb6d..5b90c7bce 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -39,4 +39,50 @@ class Report < ApplicationRecord def media_attachments MediaAttachment.where(status_id: status_ids) end + + def assign_to_self!(current_account) + update!(assigned_account_id: current_account.id) + end + + def unassign! + update!(assigned_account_id: nil) + end + + def resolve!(acting_account) + update!(action_taken: true, action_taken_by_account_id: acting_account.id) + end + + def unresolve! + update!(action_taken: false, action_taken_by_account_id: nil) + end + + def unresolved? + !action_taken? + end + + def history + time_range = created_at..updated_at + + sql = [ + Admin::ActionLog.where( + target_type: 'Report', + target_id: id, + created_at: time_range + ).unscope(:order), + + Admin::ActionLog.where( + target_type: 'Account', + target_id: target_account_id, + created_at: time_range + ).unscope(:order), + + Admin::ActionLog.where( + target_type: 'Status', + target_id: status_ids, + created_at: time_range + ).unscope(:order), + ].map { |query| "(#{query.to_sql})" }.join(' UNION ALL ') + + Admin::ActionLog.from("(#{sql}) AS admin_action_logs") + end end diff --git a/app/models/report_note.rb b/app/models/report_note.rb index 3d12cf7b6..6d9dec80a 100644 --- a/app/models/report_note.rb +++ b/app/models/report_note.rb @@ -13,7 +13,7 @@ class ReportNote < ApplicationRecord belongs_to :account - belongs_to :report, inverse_of: :notes + belongs_to :report, inverse_of: :notes, touch: true scope :latest, -> { reorder('created_at ASC') } diff --git a/app/views/admin/report_notes/_report_note.html.haml b/app/views/admin/report_notes/_report_note.html.haml index 60ac5d0d5..1f621e0d3 100644 --- a/app/views/admin/report_notes/_report_note.html.haml +++ b/app/views/admin/report_notes/_report_note.html.haml @@ -1,11 +1,9 @@ -%tr - %td - %p - %strong= report_note.account.acct - on +%li + %h4 + = report_note.account.acct + %div{ style: 'float: right' } %time.formatted{ datetime: report_note.created_at.iso8601, title: l(report_note.created_at) } = l report_note.created_at = table_link_to 'trash', t('admin.reports.notes.delete'), admin_report_note_path(report_note), method: :delete if can?(:destroy, report_note) - %br/ - %br/ + %div{ class: 'report-note__comment' } = simple_format(h(report_note.content)) diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index e7634a034..d57b4adc8 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -5,7 +5,7 @@ = t('admin.reports.report', id: @report.id) %div{ style: 'overflow: hidden; margin-bottom: 20px' } - - if !@report.action_taken? + - if @report.unresolved? %div{ style: 'float: right' } = link_to t('admin.reports.silence_account'), admin_report_path(@report, outcome: 'silence'), method: :put, class: 'button' = link_to t('admin.reports.suspend_account'), admin_report_path(@report, outcome: 'suspend'), method: :put, class: 'button' @@ -17,22 +17,29 @@ .table-wrapper %table.table.inline-table %tbody + %tr + %th= t('admin.reports.created_at') + %td{colspan: 2} + %time.formatted{ datetime: @report.created_at.iso8601 } %tr %th= t('admin.reports.updated_at') %td{colspan: 2} %time.formatted{ datetime: @report.updated_at.iso8601 } %tr %th= t('admin.reports.status') - %td{colspan: 2} + %td - if @report.action_taken? = t('admin.reports.resolved') - = table_link_to 'envelope-open', t('admin.reports.reopen'), admin_report_path(@report, outcome: 'reopen'), method: :put - else = t('admin.reports.unresolved') + %td{style: "text-align: right; overflow: hidden;"} + - if @report.action_taken? + = table_link_to 'envelope-open', t('admin.reports.reopen'), admin_report_path(@report, outcome: 'reopen'), method: :put - if !@report.action_taken_by_account.nil? %tr %th= t('admin.reports.action_taken_by') - %td= @report.action_taken_by_account.acct + %td{colspan: 2} + = @report.action_taken_by_account.acct - else %tr %th= t('admin.reports.assigned') @@ -47,6 +54,8 @@ - if !@report.assigned_account.nil? = table_link_to 'trash', t('admin.reports.unassign'), admin_report_path(@report, outcome: 'unassign'), method: :put +%hr{ class: "section-break"}/ + .report-accounts .report-accounts__item %h3= t('admin.reports.reported_account') @@ -88,22 +97,28 @@ = link_to admin_report_reported_status_path(@report, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') }, remote: true do = fa_icon 'trash' -%hr/ +%hr{ class: "section-break"}/ %h3= t('admin.reports.notes.label') - if @report_notes.length > 0 - .table-wrapper - %table.table - %thead - %tr - %th - %tbody - = render @report_notes + %ul + = render @report_notes -= simple_form_for @report_note, url: admin_report_notes_path do |f| +%h4= t('admin.reports.notes.new_label') += form_for @report_note, url: admin_report_notes_path, html: { class: 'report-note__form' } do |f| = render 'shared/error_messages', object: @report_note - = f.input :content + = f.text_area :content, placeholder: t('admin.reports.notes.placeholder'), rows: 6, class: 'report-note__textarea' = f.hidden_field :report_id - = f.button :button, t('admin.reports.notes.create'), type: :submit - = f.button :button, t('admin.reports.notes.create_and_resolve'), type: :submit, name: :create_and_resolve + %div{ class: 'report-note__buttons' } + - if @report.unresolved? + = f.submit t('admin.reports.notes.create_and_resolve'), name: :create_and_resolve, class: 'button report-note__button' + - else + = f.submit t('admin.reports.notes.create_and_unresolve'), name: :create_and_unresolve, class: 'button report-note__button' + = f.submit t('admin.reports.notes.create'), class: 'button report-note__button' + +- if @report_history.length > 0 + %h3= t('admin.reports.history') + + %ul + = render @report_history diff --git a/config/locales/en.yml b/config/locales/en.yml index 11f3fb924..98f135966 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -256,8 +256,8 @@ en: title: Filter title: Invites report_notes: - created_msg: Moderation note successfully created! - destroyed_msg: Moderation note successfully destroyed! + created_msg: Report note successfully created! + destroyed_msg: Report note successfully deleted! reports: action_taken_by: Action taken by are_you_sure: Are you sure? @@ -266,15 +266,20 @@ en: comment: label: Report Comment none: None + created_at: Reported delete: Delete + history: Moderation History id: ID mark_as_resolved: Mark as resolved mark_as_unresolved: Mark as unresolved notes: create: Add Note create_and_resolve: Resolve with Note + create_and_unresolve: Reopen with Note delete: Delete - label: Notes + label: Moderator Notes + new_label: Add Moderator Note + placeholder: Describe what actions have been taken, or any other updates to this report… nsfw: 'false': Unhide media attachments 'true': Hide media attachments From 519119f657cf97ec187008a28dba00c1125a9292 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 11 Apr 2018 19:35:09 +0900 Subject: [PATCH 067/442] Paginate ancestor statuses in public page (#7102) This also limits the statuses returned by API, but pagination is not implemented in Web API yet. I still expect it brings user experience better than making a user wait to fetch all ancestor statuses and flooding the column with them. --- app/controllers/api/v1/statuses_controller.rb | 2 +- app/controllers/statuses_controller.rb | 7 +++- .../styles/mastodon/stream_entries.scss | 14 ++++++- .../concerns/status_threading_concern.rb | 24 ++++++++---- app/views/stream_entries/_status.html.haml | 4 ++ .../concerns/status_threading_concern_spec.rb | 38 +++++++++++++++---- .../stream_entries/show.html.haml_spec.rb | 2 +- 7 files changed, 70 insertions(+), 21 deletions(-) diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index 28c28592a..e98241323 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -17,7 +17,7 @@ class Api::V1::StatusesController < Api::BaseController end def context - ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(current_account) + ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(DEFAULT_STATUSES_LIMIT, current_account) descendants_results = @status.descendants(current_account) loaded_ancestors = cache_collection(ancestors_results, Status) loaded_descendants = cache_collection(descendants_results, Status) diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index 45226c8d2..41f098a43 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -4,6 +4,8 @@ class StatusesController < ApplicationController include SignatureAuthentication include Authorization + ANCESTORS_LIMIT = 20 + layout 'public' before_action :set_account @@ -16,8 +18,9 @@ class StatusesController < ApplicationController def show respond_to do |format| format.html do - @ancestors = @status.reply? ? cache_collection(@status.ancestors(current_account), Status) : [] - @descendants = cache_collection(@status.descendants(current_account), Status) + @ancestors = @status.reply? ? cache_collection(@status.ancestors(ANCESTORS_LIMIT, current_account), Status) : [] + @next_ancestor = @ancestors.size < ANCESTORS_LIMIT ? nil : @ancestors.shift + @descendants = cache_collection(@status.descendants(current_account), Status) render 'stream_entries/show' end diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss index 442b143a0..dfdc48d06 100644 --- a/app/javascript/styles/mastodon/stream_entries.scss +++ b/app/javascript/styles/mastodon/stream_entries.scss @@ -6,7 +6,8 @@ background: $simple-background-color; .detailed-status.light, - .status.light { + .status.light, + .more.light { border-bottom: 1px solid $ui-secondary-color; animation: none; } @@ -290,6 +291,17 @@ text-decoration: underline; } } + + .more { + color: $classic-primary-color; + display: block; + padding: 14px; + text-align: center; + + &:not(:hover) { + text-decoration: none; + } + } } .embed { diff --git a/app/models/concerns/status_threading_concern.rb b/app/models/concerns/status_threading_concern.rb index b539ba10e..fffc095ee 100644 --- a/app/models/concerns/status_threading_concern.rb +++ b/app/models/concerns/status_threading_concern.rb @@ -3,8 +3,8 @@ module StatusThreadingConcern extend ActiveSupport::Concern - def ancestors(account = nil) - find_statuses_from_tree_path(ancestor_ids, account) + def ancestors(limit, account = nil) + find_statuses_from_tree_path(ancestor_ids(limit), account) end def descendants(account = nil) @@ -13,14 +13,21 @@ module StatusThreadingConcern private - def ancestor_ids - Rails.cache.fetch("ancestors:#{id}") do - ancestor_statuses.pluck(:id) + def ancestor_ids(limit) + key = "ancestors:#{id}" + ancestors = Rails.cache.fetch(key) + + if ancestors.nil? || ancestors[:limit] < limit + ids = ancestor_statuses(limit).pluck(:id).reverse! + Rails.cache.write key, limit: limit, ids: ids + ids + else + ancestors[:ids].last(limit) end end - def ancestor_statuses - Status.find_by_sql([<<-SQL.squish, id: in_reply_to_id]) + def ancestor_statuses(limit) + Status.find_by_sql([<<-SQL.squish, id: in_reply_to_id, limit: limit]) WITH RECURSIVE search_tree(id, in_reply_to_id, path) AS ( SELECT id, in_reply_to_id, ARRAY[id] @@ -34,7 +41,8 @@ module StatusThreadingConcern ) SELECT id FROM search_tree - ORDER BY path DESC + ORDER BY path + LIMIT :limit SQL end diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml index e2e1fdd12..2d0dafcb7 100644 --- a/app/views/stream_entries/_status.html.haml +++ b/app/views/stream_entries/_status.html.haml @@ -14,6 +14,10 @@ entry_classes = h_class + ' ' + mf_classes + ' ' + style_classes - if status.reply? && include_threads + - if @next_ancestor + .entry{ class: entry_classes } + = link_to short_account_status_url(@next_ancestor.account.username, @next_ancestor), class: 'more light' do + = t('statuses.show_more') = render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id } .entry{ class: entry_classes } diff --git a/spec/models/concerns/status_threading_concern_spec.rb b/spec/models/concerns/status_threading_concern_spec.rb index 62f5f6e31..b8ebdd58c 100644 --- a/spec/models/concerns/status_threading_concern_spec.rb +++ b/spec/models/concerns/status_threading_concern_spec.rb @@ -14,34 +14,34 @@ describe StatusThreadingConcern do let!(:viewer) { Fabricate(:account, username: 'viewer') } it 'returns conversation history' do - expect(reply3.ancestors).to include(status, reply1, reply2) + expect(reply3.ancestors(4)).to include(status, reply1, reply2) end it 'does not return conversation history user is not allowed to see' do reply1.update(visibility: :private) status.update(visibility: :direct) - expect(reply3.ancestors(viewer)).to_not include(reply1, status) + expect(reply3.ancestors(4, viewer)).to_not include(reply1, status) end it 'does not return conversation history from blocked users' do viewer.block!(jeff) - expect(reply3.ancestors(viewer)).to_not include(reply1) + expect(reply3.ancestors(4, viewer)).to_not include(reply1) end it 'does not return conversation history from muted users' do viewer.mute!(jeff) - expect(reply3.ancestors(viewer)).to_not include(reply1) + expect(reply3.ancestors(4, viewer)).to_not include(reply1) end it 'does not return conversation history from silenced and not followed users' do jeff.update(silenced: true) - expect(reply3.ancestors(viewer)).to_not include(reply1) + expect(reply3.ancestors(4, viewer)).to_not include(reply1) end it 'does not return conversation history from blocked domains' do viewer.block_domain!('example.com') - expect(reply3.ancestors(viewer)).to_not include(reply2) + expect(reply3.ancestors(4, viewer)).to_not include(reply2) end it 'ignores deleted records' do @@ -49,10 +49,32 @@ describe StatusThreadingConcern do second_status = Fabricate(:status, thread: first_status, account: alice) # Create cache and delete cached record - second_status.ancestors + second_status.ancestors(4) first_status.destroy - expect(second_status.ancestors).to eq([]) + expect(second_status.ancestors(4)).to eq([]) + end + + it 'can return more records than previously requested' do + first_status = Fabricate(:status, account: bob) + second_status = Fabricate(:status, thread: first_status, account: alice) + third_status = Fabricate(:status, thread: second_status, account: alice) + + # Create cache + second_status.ancestors(1) + + expect(third_status.ancestors(2)).to eq([first_status, second_status]) + end + + it 'can return fewer records than previously requested' do + first_status = Fabricate(:status, account: bob) + second_status = Fabricate(:status, thread: first_status, account: alice) + third_status = Fabricate(:status, thread: second_status, account: alice) + + # Create cache + second_status.ancestors(2) + + expect(third_status.ancestors(1)).to eq([second_status]) end end diff --git a/spec/views/stream_entries/show.html.haml_spec.rb b/spec/views/stream_entries/show.html.haml_spec.rb index 59ea40990..6074bbc2e 100644 --- a/spec/views/stream_entries/show.html.haml_spec.rb +++ b/spec/views/stream_entries/show.html.haml_spec.rb @@ -48,7 +48,7 @@ describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true d assign(:stream_entry, reply.stream_entry) assign(:account, alice) assign(:type, reply.stream_entry.activity_type.downcase) - assign(:ancestors, reply.stream_entry.activity.ancestors(bob) ) + assign(:ancestors, reply.stream_entry.activity.ancestors(1, bob) ) assign(:descendants, reply.stream_entry.activity.descendants(bob)) render From 12f5f13fab1023c3cb2a50c7bcb11f281c7984fb Mon Sep 17 00:00:00 2001 From: ThibG Date: Wed, 11 Apr 2018 20:42:50 +0200 Subject: [PATCH 068/442] Place privacy dropdown menu top if it is closer to the bottom of the viewport (#7106) --- .../compose/components/privacy_dropdown.js | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js index e5de13178..6b22ba84a 100644 --- a/app/javascript/mastodon/features/compose/components/privacy_dropdown.js +++ b/app/javascript/mastodon/features/compose/components/privacy_dropdown.js @@ -32,6 +32,10 @@ class PrivacyDropdownMenu extends React.PureComponent { onChange: PropTypes.func.isRequired, }; + state = { + mounted: false, + }; + handleDocumentClick = e => { if (this.node && !this.node.contains(e.target)) { this.props.onClose(); @@ -54,6 +58,7 @@ class PrivacyDropdownMenu extends React.PureComponent { componentDidMount () { document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); + this.setState({ mounted: true }); } componentWillUnmount () { @@ -66,12 +71,16 @@ class PrivacyDropdownMenu extends React.PureComponent { } render () { + const { mounted } = this.state; const { style, items, value } = this.props; return ( {({ opacity, scaleX, scaleY }) => ( -

+ // It should not be transformed when mounting because the resulting + // size will be used to determine the coordinate of the menu by + // react-overlays +
{items.map(item => (
@@ -107,9 +116,10 @@ export default class PrivacyDropdown extends React.PureComponent { state = { open: false, + placement: null, }; - handleToggle = () => { + handleToggle = ({ target }) => { if (this.props.isUserTouching()) { if (this.state.open) { this.props.onModalClose(); @@ -120,6 +130,8 @@ export default class PrivacyDropdown extends React.PureComponent { }); } } else { + const { top } = target.getBoundingClientRect(); + this.setState({ placement: top * 2 < innerHeight ? 'bottom' : 'top' }); this.setState({ open: !this.state.open }); } } @@ -136,7 +148,7 @@ export default class PrivacyDropdown extends React.PureComponent { handleKeyDown = e => { switch(e.key) { case 'Enter': - this.handleToggle(); + this.handleToggle(e); break; case 'Escape': this.handleClose(); @@ -165,7 +177,7 @@ export default class PrivacyDropdown extends React.PureComponent { render () { const { value, intl } = this.props; - const { open } = this.state; + const { open, placement } = this.state; const valueOption = this.options.find(item => item.value === value); @@ -185,7 +197,7 @@ export default class PrivacyDropdown extends React.PureComponent { />
- + Date: Wed, 11 Apr 2018 21:40:38 +0200 Subject: [PATCH 069/442] update gem, test pam authentication (#7028) * update gem, test pam authentication * add description for test parameters * fix inclusion of optional group --- .env.test | 4 ++ .travis.yml | 3 +- Gemfile | 2 +- Gemfile.lock | 8 +-- config/environments/test.rb | 11 ++++ .../auth/sessions_controller_spec.rb | 51 +++++++++++++++++++ 6 files changed, 73 insertions(+), 6 deletions(-) diff --git a/.env.test b/.env.test index b57f52e30..7da76f8ef 100644 --- a/.env.test +++ b/.env.test @@ -1,3 +1,7 @@ # Federation LOCAL_DOMAIN=cb6e6126.ngrok.io LOCAL_HTTPS=true +# test pam authentication +PAM_ENABLED=true +PAM_DEFAULT_SERVICE=pam_test +PAM_CONTROLLED_SERVICE=pam_test_controlled diff --git a/.travis.yml b/.travis.yml index 989237a19..2addd9ba2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,7 @@ env: - RAILS_ENV=test - NOKOGIRI_USE_SYSTEM_LIBRARIES=true - PARALLEL_TEST_PROCESSORS=2 + - ALLOW_NOPAM=true addons: postgresql: 9.4 @@ -47,7 +48,7 @@ services: install: - nvm install - - bundle install --path=vendor/bundle --without development production --retry=3 --jobs=16 + - bundle install --path=vendor/bundle --with pam_authentication --without development production --retry=3 --jobs=16 - yarn install before_script: diff --git a/Gemfile b/Gemfile index 4a5a166bd..7f9591d8d 100644 --- a/Gemfile +++ b/Gemfile @@ -33,7 +33,7 @@ gem 'devise', '~> 4.4' gem 'devise-two-factor', '~> 3.0' group :pam_authentication, optional: true do - gem 'devise_pam_authenticatable2', '~> 9.0' + gem 'devise_pam_authenticatable2', '~> 9.1' end gem 'net-ldap', '~> 0.10' diff --git a/Gemfile.lock b/Gemfile.lock index 0f5a1fb6a..5322b8746 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -146,9 +146,9 @@ GEM devise (~> 4.0) railties (< 5.2) rotp (~> 2.0) - devise_pam_authenticatable2 (9.0.0) + devise_pam_authenticatable2 (9.1.0) devise (>= 4.0.0) - rpam2 (~> 3.0) + rpam2 (~> 4.0) diff-lcs (1.3) docile (1.1.5) domain_name (0.5.20170404) @@ -464,7 +464,7 @@ GEM actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3) rotp (2.1.2) - rpam2 (3.1.0) + rpam2 (4.0.2) rqrcode (0.10.1) chunky_png (~> 1.0) rspec-core (3.7.0) @@ -639,7 +639,7 @@ DEPENDENCIES climate_control (~> 0.2) devise (~> 4.4) devise-two-factor (~> 3.0) - devise_pam_authenticatable2 (~> 9.0) + devise_pam_authenticatable2 (~> 9.1) doorkeeper (~> 4.2) dotenv-rails (~> 2.2) fabrication (~> 2.18) diff --git a/config/environments/test.rb b/config/environments/test.rb index 7d77a170e..122634d5b 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -59,3 +59,14 @@ Rails.application.configure do end Paperclip::Attachment.default_options[:path] = "#{Rails.root}/spec/test_files/:class/:id_partition/:style.:extension" + +# set fake_data for pam, don't do real calls, just use fake data +if ENV['PAM_ENABLED'] == 'true' + Rpam2.fake_data = + { + usernames: Set['pam_user1', 'pam_user2'], + servicenames: Set['pam_test', 'pam_test_controlled'], + password: '123456', + env: { email: 'pam@example.com' } + } +end diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index 88f0a4734..d5fed17d6 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -48,6 +48,57 @@ RSpec.describe Auth::SessionsController, type: :controller do request.env['devise.mapping'] = Devise.mappings[:user] end + context 'using PAM authentication' do + context 'using a valid password' do + before do + post :create, params: { user: { email: "pam_user1", password: '123456' } } + end + + it 'redirects to home' do + expect(response).to redirect_to(root_path) + end + + it 'logs the user in' do + expect(controller.current_user).to be_instance_of(User) + end + end + + context 'using an invalid password' do + before do + post :create, params: { user: { email: "pam_user1", password: 'WRONGPW' } } + end + + it 'shows a login error' do + expect(flash[:alert]).to match I18n.t('devise.failure.invalid', authentication_keys: 'Email') + end + + it "doesn't log the user in" do + expect(controller.current_user).to be_nil + end + end + + context 'using a valid email and existing user' do + let(:user) do + account = Fabricate.build(:account, username: 'pam_user1') + account.save!(validate: false) + user = Fabricate(:user, email: 'pam@example.com', password: nil, account: account) + user + end + + before do + post :create, params: { user: { email: user.email, password: '123456' } } + end + + it 'redirects to home' do + expect(response).to redirect_to(root_path) + end + + it 'logs the user in' do + expect(controller.current_user).to eq user + end + end + end + context 'using password authentication' do let(:user) { Fabricate(:user, email: 'foo@bar.com', password: 'abcdefgh') } From 50529cbceb84e611bca497624a7a4c38113e5135 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Thu, 12 Apr 2018 21:45:17 +0900 Subject: [PATCH 070/442] Upgrade Rails to version 5.2.0 (#5898) --- Gemfile | 60 +-- Gemfile.lock | 374 +++++++++--------- .../api/web/push_subscriptions_controller.rb | 25 +- .../settings/follower_domains_controller.rb | 2 +- app/models/account.rb | 4 +- app/models/notification.rb | 2 - app/models/status.rb | 2 +- bin/bundle | 2 +- bin/setup | 3 +- bin/update | 6 +- bin/webpack | 14 +- bin/webpack-dev-server | 14 +- bin/yarn | 11 + config/application.rb | 11 +- config/boot.rb | 2 +- config/deploy.rb | 2 +- config/environments/development.rb | 3 +- config/environments/production.rb | 4 + config/environments/test.rb | 2 +- .../initializers/content_security_policy.rb | 20 + config/initializers/cors.rb | 26 ++ db/schema.rb | 3 +- 22 files changed, 330 insertions(+), 262 deletions(-) create mode 100755 bin/yarn create mode 100644 config/initializers/content_security_policy.rb create mode 100644 config/initializers/cors.rb diff --git a/Gemfile b/Gemfile index 7f9591d8d..efafe295c 100644 --- a/Gemfile +++ b/Gemfile @@ -5,12 +5,12 @@ ruby '>= 2.3.0', '< 2.6.0' gem 'pkg-config', '~> 1.2' -gem 'puma', '~> 3.10' -gem 'rails', '~> 5.1.4' +gem 'puma', '~> 3.11' +gem 'rails', '~> 5.2.0' gem 'hamlit-rails', '~> 0.2' -gem 'pg', '~> 0.20' -gem 'pghero', '~> 1.7' +gem 'pg', '~> 1.0' +gem 'pghero', '~> 2.1' gem 'dotenv-rails', '~> 2.2' gem 'aws-sdk-s3', '~> 1.8', require: false @@ -23,14 +23,14 @@ gem 'streamio-ffmpeg', '~> 3.0' gem 'active_model_serializers', '~> 0.10' gem 'addressable', '~> 2.5' -gem 'bootsnap' +gem 'bootsnap', '~> 1.3' gem 'browser' gem 'charlock_holmes', '~> 0.7.6' gem 'iso-639' gem 'chewy', '~> 5.0' gem 'cld3', '~> 3.2.0' gem 'devise', '~> 4.4' -gem 'devise-two-factor', '~> 3.0' +gem 'devise-two-factor', '~> 3.0', git: 'https://github.com/ykzts/devise-two-factor.git', branch: 'rails-5.2' group :pam_authentication, optional: true do gem 'devise_pam_authenticatable2', '~> 9.1' @@ -41,7 +41,7 @@ gem 'omniauth-cas', '~> 1.1' gem 'omniauth-saml', '~> 1.10' gem 'omniauth', '~> 1.2' -gem 'doorkeeper', '~> 4.2' +gem 'doorkeeper', '~> 4.3' gem 'fast_blank', '~> 1.0' gem 'fastimage' gem 'goldfinger', '~> 2.1' @@ -50,50 +50,50 @@ gem 'redis-namespace', '~> 1.5' gem 'htmlentities', '~> 4.3' gem 'http', '~> 3.0' gem 'http_accept_language', '~> 2.1' -gem 'httplog', '~> 0.99' +gem 'httplog', '~> 1.0' gem 'idn-ruby', require: 'idn' gem 'kaminari', '~> 1.1' gem 'link_header', '~> 0.0' gem 'mime-types', '~> 3.1' gem 'nokogiri', '~> 1.8' gem 'nsa', '~> 0.2' -gem 'oj', '~> 3.3' +gem 'oj', '~> 3.4' gem 'ostatus2', '~> 2.0' gem 'ox', '~> 2.8' gem 'pundit', '~> 1.1' gem 'premailer-rails' -gem 'rack-attack', '~> 5.0' -gem 'rack-cors', '~> 0.4', require: 'rack/cors' +gem 'rack-attack', '~> 5.2' +gem 'rack-cors', '~> 1.0', require: 'rack/cors' gem 'rack-timeout', '~> 0.4' -gem 'rails-i18n', '~> 5.0' +gem 'rails-i18n', '~> 5.1' gem 'rails-settings-cached', '~> 0.6' -gem 'redis', '~> 3.3', require: ['redis', 'redis/connection/hiredis'] +gem 'redis', '~> 4.0', require: ['redis', 'redis/connection/hiredis'] gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock' gem 'rqrcode', '~> 0.10' gem 'ruby-oembed', '~> 0.12', require: 'oembed' gem 'ruby-progressbar', '~> 1.4' -gem 'sanitize', '~> 4.6.4' -gem 'sidekiq', '~> 5.0' -gem 'sidekiq-scheduler', '~> 2.1' +gem 'sanitize', '~> 4.6' +gem 'sidekiq', '~> 5.1' +gem 'sidekiq-scheduler', '~> 2.2' gem 'sidekiq-unique-jobs', '~> 5.0' gem 'sidekiq-bulk', '~>0.1.1' gem 'simple-navigation', '~> 4.0' -gem 'simple_form', '~> 3.4' +gem 'simple_form', '~> 4.0' gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' gem 'stoplight', '~> 2.1.3' -gem 'strong_migrations' +gem 'strong_migrations', '~> 0.2' gem 'tty-command' gem 'tty-prompt' gem 'twitter-text', '~> 1.14' -gem 'tzinfo-data', '~> 1.2017' -gem 'webpacker', '~> 3.0' +gem 'tzinfo-data', '~> 1.2018' +gem 'webpacker', '~> 3.4' gem 'webpush' -gem 'json-ld-preloaded', '~> 2.2.1' -gem 'rdf-normalize', '~> 0.3.1' +gem 'json-ld-preloaded', '~> 2.2' +gem 'rdf-normalize', '~> 0.3' group :development, :test do - gem 'fabrication', '~> 2.18' + gem 'fabrication', '~> 2.20' gem 'fuubar', '~> 2.2' gem 'i18n-tasks', '~> 0.9', require: false gem 'pry-rails', '~> 0.3' @@ -105,15 +105,15 @@ group :production, :test do end group :test do - gem 'capybara', '~> 2.15' + gem 'capybara', '~> 2.18' gem 'climate_control', '~> 0.2' - gem 'faker', '~> 1.7' + gem 'faker', '~> 1.8' gem 'microformats', '~> 4.0' gem 'rails-controller-testing', '~> 1.0' gem 'rspec-sidekiq', '~> 3.0' gem 'simplecov', '~> 0.14', require: false - gem 'webmock', '~> 3.0' - gem 'parallel_tests', '~> 2.17' + gem 'webmock', '~> 3.3' + gem 'parallel_tests', '~> 2.21' end group :development do @@ -121,12 +121,12 @@ group :development do gem 'annotate', '~> 2.7' gem 'better_errors', '~> 2.4' gem 'binding_of_caller', '~> 0.7' - gem 'bullet', '~> 5.5' + gem 'bullet', '~> 5.7' gem 'letter_opener', '~> 1.4' gem 'letter_opener_web', '~> 1.3' gem 'memory_profiler' gem 'rubocop', require: false - gem 'brakeman', '~> 4.0', require: false + gem 'brakeman', '~> 4.2', require: false gem 'bundler-audit', '~> 0.6', require: false gem 'scss_lint', '~> 0.55', require: false @@ -137,6 +137,6 @@ group :development do end group :production do - gem 'lograge', '~> 0.7' + gem 'lograge', '~> 0.9' gem 'redis-rails', '~> 5.0' end diff --git a/Gemfile.lock b/Gemfile.lock index 5322b8746..e799533ae 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,25 +1,37 @@ +GIT + remote: https://github.com/ykzts/devise-two-factor.git + revision: f60492b29c174d4c959ac02406392f8eb9c4d374 + branch: rails-5.2 + specs: + devise-two-factor (3.0.2) + activesupport (< 5.3) + attr_encrypted (>= 1.3, < 4, != 2) + devise (~> 4.0) + railties (< 5.3) + rotp (~> 2.0) + GEM remote: https://rubygems.org/ specs: - actioncable (5.1.4) - actionpack (= 5.1.4) + actioncable (5.2.0) + actionpack (= 5.2.0) nio4r (~> 2.0) - websocket-driver (~> 0.6.1) - actionmailer (5.1.4) - actionpack (= 5.1.4) - actionview (= 5.1.4) - activejob (= 5.1.4) + websocket-driver (>= 0.6.1) + actionmailer (5.2.0) + actionpack (= 5.2.0) + actionview (= 5.2.0) + activejob (= 5.2.0) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.1.4) - actionview (= 5.1.4) - activesupport (= 5.1.4) + actionpack (5.2.0) + actionview (= 5.2.0) + activesupport (= 5.2.0) rack (~> 2.0) rack-test (>= 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.1.4) - activesupport (= 5.1.4) + actionview (5.2.0) + activesupport (= 5.2.0) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) @@ -30,18 +42,22 @@ GEM case_transform (>= 0.2) jsonapi-renderer (>= 0.1.1.beta1, < 0.3) active_record_query_trace (1.5.4) - activejob (5.1.4) - activesupport (= 5.1.4) + activejob (5.2.0) + activesupport (= 5.2.0) globalid (>= 0.3.6) - activemodel (5.1.4) - activesupport (= 5.1.4) - activerecord (5.1.4) - activemodel (= 5.1.4) - activesupport (= 5.1.4) - arel (~> 8.0) - activesupport (5.1.4) + activemodel (5.2.0) + activesupport (= 5.2.0) + activerecord (5.2.0) + activemodel (= 5.2.0) + activesupport (= 5.2.0) + arel (>= 9.0) + activestorage (5.2.0) + actionpack (= 5.2.0) + activerecord (= 5.2.0) + marcel (~> 0.3.1) + activesupport (5.2.0) concurrent-ruby (~> 1.0, >= 1.0.2) - i18n (~> 0.7) + i18n (>= 0.7, < 2) minitest (~> 5.1) tzinfo (~> 1.1) addressable (2.5.2) @@ -51,9 +67,9 @@ GEM annotate (2.7.2) activerecord (>= 3.2, < 6.0) rake (>= 10.4, < 13.0) - arel (8.0.0) - ast (2.3.0) - attr_encrypted (3.0.3) + arel (9.0.0) + ast (2.4.0) + attr_encrypted (3.1.0) encryptor (~> 3.0.0) av (0.9.0) cocaine (~> 0.5.3) @@ -77,18 +93,18 @@ GEM rack (>= 0.9.0) binding_of_caller (0.8.0) debug_inspector (>= 0.0.1) - bootsnap (1.1.5) + bootsnap (1.3.0) msgpack (~> 1.0) - brakeman (4.0.1) + brakeman (4.2.1) browser (2.5.2) builder (3.2.3) - bullet (5.6.1) + bullet (5.7.5) activesupport (>= 3.0.0) - uniform_notifier (~> 1.10.0) + uniform_notifier (~> 1.11.0) bundler-audit (0.6.0) bundler (~> 1.2) thor (~> 0.18) - capistrano (3.10.0) + capistrano (3.10.1) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) @@ -104,13 +120,13 @@ GEM sshkit (~> 1.3) capistrano-yarn (2.0.2) capistrano (~> 3.0) - capybara (2.16.1) + capybara (2.18.0) addressable mini_mime (>= 0.1.3) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) - xpath (~> 2.0) + xpath (>= 2.0, < 4.0) case_transform (0.2) activesupport charlock_holmes (0.7.6) @@ -118,7 +134,7 @@ GEM activesupport (>= 4.0) elasticsearch (>= 2.0.0) elasticsearch-dsl - chunky_png (1.3.8) + chunky_png (1.3.10) cld3 (3.2.2) ffi (>= 1.1.0, < 1.10.0) climate_control (0.2.0) @@ -130,22 +146,16 @@ GEM connection_pool (2.2.1) crack (0.4.3) safe_yaml (~> 1.0.0) - crass (1.0.3) + crass (1.0.4) css_parser (1.6.0) addressable debug_inspector (0.0.3) - devise (4.4.0) + devise (4.4.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) - railties (>= 4.1.0, < 5.2) + railties (>= 4.1.0, < 6.0) responders warden (~> 1.2.3) - devise-two-factor (3.0.2) - activesupport (< 5.2) - attr_encrypted (>= 1.3, < 4, != 2) - devise (~> 4.0) - railties (< 5.2) - rotp (~> 2.0) devise_pam_authenticatable2 (9.1.0) devise (>= 4.0.0) rpam2 (~> 4.0) @@ -153,14 +163,13 @@ GEM docile (1.1.5) domain_name (0.5.20170404) unf (>= 0.0.5, < 1.0.0) - doorkeeper (4.2.6) + doorkeeper (4.3.2) railties (>= 4.2) - dotenv (2.2.1) - dotenv-rails (2.2.1) - dotenv (= 2.2.1) - railties (>= 3.2, < 5.2) - easy_translate (0.5.0) - json + dotenv (2.2.2) + dotenv-rails (2.2.2) + dotenv (= 2.2.2) + railties (>= 3.2, < 6.0) + easy_translate (0.5.1) thread thread_safe elasticsearch (6.0.1) @@ -174,18 +183,18 @@ GEM multi_json encryptor (3.0.0) equatable (0.5.0) - erubi (1.7.0) - et-orbi (1.0.8) + erubi (1.7.1) + et-orbi (1.0.9) tzinfo - excon (0.59.0) - fabrication (2.18.0) - faker (1.8.4) - i18n (~> 0.5) + excon (0.60.0) + fabrication (2.20.1) + faker (1.8.7) + i18n (>= 0.7) faraday (0.14.0) multipart-post (>= 1.2, < 3) fast_blank (1.0.0) fastimage (2.1.1) - ffi (1.9.18) + ffi (1.9.21) fog-core (1.45.0) builder excon (~> 0.58) @@ -195,12 +204,12 @@ GEM multi_json (~> 1.10) fog-local (0.4.0) fog-core (~> 1.27) - fog-openstack (0.1.22) - fog-core (>= 1.40) + fog-openstack (0.1.23) + fog-core (~> 1.40) fog-json (>= 1.0) ipaddress (>= 0.8) formatador (0.2.5) - fuubar (2.2.0) + fuubar (2.3.1) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) globalid (0.4.1) @@ -210,7 +219,7 @@ GEM http (~> 3.0) nokogiri (~> 1.8) oj (~> 3.0) - hamlit (2.8.5) + hamlit (2.8.8) temple (>= 0.8.0) thor tilt @@ -238,33 +247,33 @@ GEM http-form_data (2.0.0) http_accept_language (2.1.1) http_parser.rb (0.6.0) - httplog (0.99.7) - colorize - rack - i18n (0.9.5) + httplog (1.0.2) + colorize (~> 0.8) + rack (>= 1.0) + i18n (1.0.0) concurrent-ruby (~> 1.0) - i18n-tasks (0.9.19) + i18n-tasks (0.9.21) activesupport (>= 4.0.2) ast (>= 2.1.0) - easy_translate (>= 0.5.0) + easy_translate (>= 0.5.1) erubi highline (>= 1.7.3) i18n parser (>= 2.2.3.0) - rainbow (~> 2.2) + rainbow (>= 2.2.2, < 4.0) terminal-table (>= 1.5.1) idn-ruby (0.1.0) ipaddress (0.8.3) iso-639 (0.2.8) jmespath (1.3.1) json (2.1.0) - json-ld (2.1.7) + json-ld (2.2.1) multi_json (~> 1.12) - rdf (~> 2.2, >= 2.2.8) - json-ld-preloaded (2.2.2) - json-ld (~> 2.1, >= 2.1.5) - multi_json (~> 1.11) - rdf (~> 2.2) + rdf (>= 2.2.8, < 4.0) + json-ld-preloaded (2.2.3) + json-ld (>= 2.2, < 4.0) + multi_json (~> 1.12) + rdf (>= 2.2, < 4.0) jsonapi-renderer (0.2.0) jwt (2.1.0) kaminari (1.1.1) @@ -281,25 +290,27 @@ GEM kaminari-core (1.1.1) launchy (2.4.3) addressable (~> 2.3) - letter_opener (1.4.1) + letter_opener (1.6.0) launchy (~> 2.2) - letter_opener_web (1.3.1) + letter_opener_web (1.3.4) actionmailer (>= 3.2) letter_opener (~> 1.0) railties (>= 3.2) link_header (0.0.8) - lograge (0.7.1) - actionpack (>= 4, < 5.2) - activesupport (>= 4, < 5.2) - railties (>= 4, < 5.2) + lograge (0.9.0) + actionpack (>= 4) + activesupport (>= 4) + railties (>= 4) request_store (~> 1.0) - loofah (2.2.1) + loofah (2.2.2) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.0) mini_mime (>= 0.1.1) - mario-redis-lock (1.2.0) - redis (~> 3, >= 3.0.5) + marcel (0.3.2) + mimemagic (~> 0.3.2) + mario-redis-lock (1.2.1) + redis (>= 3.0.5) memory_profiler (0.9.10) method_source (0.9.0) microformats (4.0.7) @@ -312,15 +323,15 @@ GEM mini_mime (1.0.0) mini_portile2 (2.3.0) minitest (5.11.3) - msgpack (1.1.0) - multi_json (1.12.2) + msgpack (1.2.4) + multi_json (1.13.1) multipart-post (2.0.0) necromancer (0.4.0) net-ldap (0.16.1) net-scp (1.2.1) net-ssh (>= 2.6.5) net-ssh (4.2.0) - nio4r (2.1.0) + nio4r (2.3.0) nokogiri (1.8.2) mini_portile2 (~> 2.3.0) nokogumbo (1.5.0) @@ -330,7 +341,7 @@ GEM concurrent-ruby (~> 1.0.0) sidekiq (>= 3.5.0) statsd-ruby (~> 1.2.0) - oj (3.3.10) + oj (3.4.0) omniauth (1.8.1) hashie (>= 3.4.6, < 3.6.0) rack (>= 1.6.2, < 3) @@ -356,24 +367,24 @@ GEM paperclip-av-transcoder (0.6.4) av (~> 0.9.0) paperclip (>= 2.5.2) - parallel (1.12.0) - parallel_tests (2.19.0) + parallel (1.12.1) + parallel_tests (2.21.1) parallel - parser (2.4.0.2) - ast (~> 2.3) + parser (2.5.1.0) + ast (~> 2.4.0) pastel (0.7.2) equatable (~> 0.5.0) tty-color (~> 0.4.0) - pg (0.21.0) - pghero (1.7.0) + pg (1.0.0) + pghero (2.1.0) activerecord - pkg-config (1.2.8) + pkg-config (1.2.9) powerpack (0.1.1) premailer (1.11.1) addressable css_parser (>= 1.6.0) htmlentities (>= 4.0.0) - premailer-rails (1.10.1) + premailer-rails (1.10.2) actionmailer (>= 3, < 6) premailer (~> 1.7, >= 1.7.9) private_address_check (0.4.1) @@ -382,32 +393,33 @@ GEM method_source (~> 0.9.0) pry-rails (0.3.6) pry (>= 0.10.4) - public_suffix (3.0.1) - puma (3.11.0) + public_suffix (3.0.2) + puma (3.11.3) pundit (1.1.0) activesupport (>= 3.0.0) - rack (2.0.3) - rack-attack (5.0.1) + rack (2.0.4) + rack-attack (5.2.0) rack - rack-cors (0.4.1) - rack-protection (2.0.0) + rack-cors (1.0.2) + rack-protection (2.0.1) rack - rack-proxy (0.6.2) + rack-proxy (0.6.4) rack - rack-test (0.8.2) + rack-test (1.0.0) rack (>= 1.0, < 3) rack-timeout (0.4.2) - rails (5.1.4) - actioncable (= 5.1.4) - actionmailer (= 5.1.4) - actionpack (= 5.1.4) - actionview (= 5.1.4) - activejob (= 5.1.4) - activemodel (= 5.1.4) - activerecord (= 5.1.4) - activesupport (= 5.1.4) + rails (5.2.0) + actioncable (= 5.2.0) + actionmailer (= 5.2.0) + actionpack (= 5.2.0) + actionview (= 5.2.0) + activejob (= 5.2.0) + activemodel (= 5.2.0) + activerecord (= 5.2.0) + activestorage (= 5.2.0) + activesupport (= 5.2.0) bundler (>= 1.3.0) - railties (= 5.1.4) + railties (= 5.2.0) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) @@ -416,31 +428,30 @@ GEM rails-dom-testing (2.0.3) activesupport (>= 4.2.0) nokogiri (>= 1.6) - rails-html-sanitizer (1.0.3) - loofah (~> 2.0) - rails-i18n (5.0.4) - i18n (~> 0.7) - railties (~> 5.0) + rails-html-sanitizer (1.0.4) + loofah (~> 2.2, >= 2.2.2) + rails-i18n (5.1.1) + i18n (>= 0.7, < 2) + railties (>= 5.0, < 6) rails-settings-cached (0.6.6) rails (>= 4.2.0) - railties (5.1.4) - actionpack (= 5.1.4) - activesupport (= 5.1.4) + railties (5.2.0) + actionpack (= 5.2.0) + activesupport (= 5.2.0) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rainbow (2.2.2) - rake - rake (12.3.0) + rainbow (3.0.0) + rake (12.3.1) rb-fsevent (0.10.2) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) - rdf (2.2.12) + rdf (3.0.1) hamster (~> 3.0) link_header (~> 0.0, >= 0.0.8) - rdf-normalize (0.3.2) - rdf (~> 2.0) - redis (3.3.5) + rdf-normalize (0.3.3) + rdf (>= 2.2, < 4.0) + redis (4.0.1) redis-actionpack (5.0.2) actionpack (>= 4.0, < 6) redis-rack (>= 1, < 3) @@ -450,7 +461,7 @@ GEM redis-store (>= 1.3, < 2) redis-namespace (1.6.0) redis (>= 3.0.4) - redis-rack (2.0.3) + redis-rack (2.0.4) rack (>= 1.5, < 3) redis-store (>= 1.2, < 2) redis-rails (5.0.2) @@ -459,7 +470,8 @@ GEM redis-store (>= 1.2, < 2) redis-store (1.4.1) redis (>= 2.2, < 5) - request_store (1.3.2) + request_store (1.4.0) + rack (>= 1.4) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) railties (>= 4.2.0, < 5.3) @@ -467,7 +479,7 @@ GEM rpam2 (4.0.2) rqrcode (0.10.1) chunky_png (~> 1.0) - rspec-core (3.7.0) + rspec-core (3.7.1) rspec-support (~> 3.7.0) rspec-expectations (3.7.0) diff-lcs (>= 1.2.0, < 2.0) @@ -486,12 +498,12 @@ GEM rspec-sidekiq (3.0.3) rspec-core (~> 3.0, >= 3.0.0) sidekiq (>= 2.4.0) - rspec-support (3.7.0) - rubocop (0.51.0) + rspec-support (3.7.1) + rubocop (0.52.1) parallel (~> 1.10) - parser (>= 2.3.3.1, < 3.0) + parser (>= 2.4.0.2, < 3.0) powerpack (~> 0.1) - rainbow (>= 2.2.2, < 3.0) + rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) ruby-oembed (0.12.0) @@ -505,7 +517,7 @@ GEM crass (~> 1.0.2) nokogiri (>= 1.4.4) nokogumbo (~> 1.4) - sass (3.5.3) + sass (3.5.5) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) @@ -513,15 +525,15 @@ GEM scss_lint (0.56.0) rake (>= 0.9, < 13) sass (~> 3.5.3) - sidekiq (5.0.5) + sidekiq (5.1.3) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) rack-protection (>= 1.5.0) - redis (>= 3.3.4, < 5) + redis (>= 3.3.5, < 5) sidekiq-bulk (0.1.1) activesupport sidekiq - sidekiq-scheduler (2.1.10) + sidekiq-scheduler (2.2.1) redis (>= 3, < 5) rufus-scheduler (~> 3.2) sidekiq (>= 3) @@ -531,9 +543,9 @@ GEM thor (~> 0) simple-navigation (4.0.5) activesupport (>= 2.3.2) - simple_form (3.5.0) - actionpack (> 4, < 5.2) - activemodel (> 4, < 5.2) + simple_form (4.0.0) + actionpack (> 4) + activemodel (> 4) simplecov (0.15.1) docile (~> 1.1.0) json (>= 1.8, < 3) @@ -546,14 +558,14 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - sshkit (1.15.1) + sshkit (1.16.0) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) statsd-ruby (1.2.1) stoplight (2.1.3) streamio-ffmpeg (3.0.2) multi_json (~> 1.8) - strong_migrations (0.1.9) + strong_migrations (0.2.2) activerecord (>= 3.2.0) temple (0.8.0) terminal-table (1.8.0) @@ -585,32 +597,32 @@ GEM unf (~> 0.1.0) tzinfo (1.2.5) thread_safe (~> 0.1) - tzinfo-data (1.2017.3) + tzinfo-data (1.2018.4) tzinfo (>= 1.0.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.4) + unf_ext (0.0.7.5) unicode-display_width (1.3.0) - uniform_notifier (1.10.0) + uniform_notifier (1.11.0) warden (1.2.7) rack (>= 1.0) - webmock (3.1.1) + webmock (3.3.0) addressable (>= 2.3.6) crack (>= 0.3.2) hashdiff - webpacker (3.0.2) + webpacker (3.4.3) activesupport (>= 4.2) rack-proxy (>= 0.6.1) railties (>= 4.2) webpush (0.3.3) hkdf (~> 0.2) jwt (~> 2.0) - websocket-driver (0.6.5) + websocket-driver (0.7.0) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.3) wisper (2.0.0) - xpath (2.1.0) - nokogiri (~> 1.3) + xpath (3.0.0) + nokogiri (~> 1.8) PLATFORMS ruby @@ -623,27 +635,27 @@ DEPENDENCIES aws-sdk-s3 (~> 1.8) better_errors (~> 2.4) binding_of_caller (~> 0.7) - bootsnap - brakeman (~> 4.0) + bootsnap (~> 1.3) + brakeman (~> 4.2) browser - bullet (~> 5.5) + bullet (~> 5.7) bundler-audit (~> 0.6) capistrano (~> 3.10) capistrano-rails (~> 1.3) capistrano-rbenv (~> 2.1) capistrano-yarn (~> 2.0) - capybara (~> 2.15) + capybara (~> 2.18) charlock_holmes (~> 0.7.6) chewy (~> 5.0) cld3 (~> 3.2.0) climate_control (~> 0.2) devise (~> 4.4) - devise-two-factor (~> 3.0) + devise-two-factor (~> 3.0)! devise_pam_authenticatable2 (~> 9.1) - doorkeeper (~> 4.2) + doorkeeper (~> 4.3) dotenv-rails (~> 2.2) - fabrication (~> 2.18) - faker (~> 1.7) + fabrication (~> 2.20) + faker (~> 1.8) fast_blank (~> 1.0) fastimage fog-core (~> 1.45) @@ -656,16 +668,16 @@ DEPENDENCIES htmlentities (~> 4.3) http (~> 3.0) http_accept_language (~> 2.1) - httplog (~> 0.99) + httplog (~> 1.0) i18n-tasks (~> 0.9) idn-ruby iso-639 - json-ld-preloaded (~> 2.2.1) + json-ld-preloaded (~> 2.2) kaminari (~> 1.1) letter_opener (~> 1.4) letter_opener_web (~> 1.3) link_header (~> 0.0) - lograge (~> 0.7) + lograge (~> 0.9) mario-redis-lock (~> 1.2) memory_profiler microformats (~> 4.0) @@ -673,7 +685,7 @@ DEPENDENCIES net-ldap (~> 0.10) nokogiri (~> 1.8) nsa (~> 0.2) - oj (~> 3.3) + oj (~> 3.4) omniauth (~> 1.2) omniauth-cas (~> 1.1) omniauth-saml (~> 1.10) @@ -681,24 +693,24 @@ DEPENDENCIES ox (~> 2.8) paperclip (~> 6.0) paperclip-av-transcoder (~> 0.6) - parallel_tests (~> 2.17) - pg (~> 0.20) - pghero (~> 1.7) + parallel_tests (~> 2.21) + pg (~> 1.0) + pghero (~> 2.1) pkg-config (~> 1.2) premailer-rails private_address_check (~> 0.4.1) pry-rails (~> 0.3) - puma (~> 3.10) + puma (~> 3.11) pundit (~> 1.1) - rack-attack (~> 5.0) - rack-cors (~> 0.4) + rack-attack (~> 5.2) + rack-cors (~> 1.0) rack-timeout (~> 0.4) - rails (~> 5.1.4) + rails (~> 5.2.0) rails-controller-testing (~> 1.0) - rails-i18n (~> 5.0) + rails-i18n (~> 5.1) rails-settings-cached (~> 0.6) - rdf-normalize (~> 0.3.1) - redis (~> 3.3) + rdf-normalize (~> 0.3) + redis (~> 4.0) redis-namespace (~> 1.5) redis-rails (~> 5.0) rqrcode (~> 0.10) @@ -707,25 +719,25 @@ DEPENDENCIES rubocop ruby-oembed (~> 0.12) ruby-progressbar (~> 1.4) - sanitize (~> 4.6.4) + sanitize (~> 4.6) scss_lint (~> 0.55) - sidekiq (~> 5.0) + sidekiq (~> 5.1) sidekiq-bulk (~> 0.1.1) - sidekiq-scheduler (~> 2.1) + sidekiq-scheduler (~> 2.2) sidekiq-unique-jobs (~> 5.0) simple-navigation (~> 4.0) - simple_form (~> 3.4) + simple_form (~> 4.0) simplecov (~> 0.14) sprockets-rails (~> 3.2) stoplight (~> 2.1.3) streamio-ffmpeg (~> 3.0) - strong_migrations + strong_migrations (~> 0.2) tty-command tty-prompt twitter-text (~> 1.14) - tzinfo-data (~> 1.2017) - webmock (~> 3.0) - webpacker (~> 3.0) + tzinfo-data (~> 1.2018) + webmock (~> 3.3) + webpacker (~> 3.4) webpush RUBY VERSION diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb index 68ccbd5e2..c611031ab 100644 --- a/app/controllers/api/web/push_subscriptions_controller.rb +++ b/app/controllers/api/web/push_subscriptions_controller.rb @@ -7,9 +7,6 @@ class Api::Web::PushSubscriptionsController < Api::BaseController protect_from_forgery with: :exception def create - params.require(:subscription).require(:endpoint) - params.require(:subscription).require(:keys).require([:auth, :p256dh]) - active_session = current_session unless active_session.web_push_subscription.nil? @@ -29,12 +26,12 @@ class Api::Web::PushSubscriptionsController < Api::BaseController }, } - data.deep_merge!(params[:data]) if params[:data] + data.deep_merge!(data_params) if params[:data] web_subscription = ::Web::PushSubscription.create!( - endpoint: params[:subscription][:endpoint], - key_p256dh: params[:subscription][:keys][:p256dh], - key_auth: params[:subscription][:keys][:auth], + endpoint: subscription_params[:endpoint], + key_p256dh: subscription_params[:keys][:p256dh], + key_auth: subscription_params[:keys][:auth], data: data ) @@ -44,12 +41,22 @@ class Api::Web::PushSubscriptionsController < Api::BaseController end def update - params.require([:id, :data]) + params.require([:id]) web_subscription = ::Web::PushSubscription.find(params[:id]) - web_subscription.update!(data: params[:data]) + web_subscription.update!(data: data_params) render json: web_subscription.as_payload end + + private + + def subscription_params + @subscription_params ||= params.require(:subscription).permit(:endpoint, keys: [:auth, :p256dh]) + end + + def data_params + @data_params ||= params.require(:data).permit(:alerts) + end end diff --git a/app/controllers/settings/follower_domains_controller.rb b/app/controllers/settings/follower_domains_controller.rb index 9968504e5..213d9e96d 100644 --- a/app/controllers/settings/follower_domains_controller.rb +++ b/app/controllers/settings/follower_domains_controller.rb @@ -9,7 +9,7 @@ class Settings::FollowerDomainsController < ApplicationController def show @account = current_account - @domains = current_account.followers.reorder('MIN(follows.id) DESC').group('accounts.domain').select('accounts.domain, count(accounts.id) as accounts_from_domain').page(params[:page]).per(10) + @domains = current_account.followers.reorder(Arel.sql('MIN(follows.id) DESC')).group('accounts.domain').select('accounts.domain, count(accounts.id) as accounts_from_domain').page(params[:page]).per(10) end def update diff --git a/app/models/account.rb b/app/models/account.rb index 51304fc18..5bdcfa99e 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -244,11 +244,11 @@ class Account < ApplicationRecord end def domains - reorder(nil).pluck('distinct accounts.domain') + reorder(nil).pluck(Arel.sql('distinct accounts.domain')) end def inboxes - urls = reorder(nil).where(protocol: :activitypub).pluck("distinct coalesce(nullif(accounts.shared_inbox_url, ''), accounts.inbox_url)") + urls = reorder(nil).where(protocol: :activitypub).pluck(Arel.sql("distinct coalesce(nullif(accounts.shared_inbox_url, ''), accounts.inbox_url)")) DeliveryFailureTracker.filter(urls) end diff --git a/app/models/notification.rb b/app/models/notification.rb index be9964087..0b0f01aa8 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -81,8 +81,6 @@ class Notification < ApplicationRecord end end - private - def activity_types_from_types(types) types.map { |type| TYPE_CLASS_MAP[type.to_sym] }.compact end diff --git a/app/models/status.rb b/app/models/status.rb index 60fa7a22e..f924be47b 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -322,7 +322,7 @@ class Status < ApplicationRecord self.in_reply_to_account_id = carried_over_reply_to_account_id self.conversation_id = thread.conversation_id if conversation_id.nil? elsif conversation_id.nil? - create_conversation + self.conversation = Conversation.new end end diff --git a/bin/bundle b/bin/bundle index 66e9889e8..f19acf5b5 100755 --- a/bin/bundle +++ b/bin/bundle @@ -1,3 +1,3 @@ #!/usr/bin/env ruby -ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) load Gem.bin_path('bundler', 'bundle') diff --git a/bin/setup b/bin/setup index 72b62a028..fc77b0809 100755 --- a/bin/setup +++ b/bin/setup @@ -1,10 +1,9 @@ #!/usr/bin/env ruby -require 'pathname' require 'fileutils' include FileUtils # path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) +APP_ROOT = File.expand_path('..', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") diff --git a/bin/update b/bin/update index a8e4462f2..6d73559a3 100755 --- a/bin/update +++ b/bin/update @@ -1,10 +1,9 @@ #!/usr/bin/env ruby -require 'pathname' require 'fileutils' include FileUtils # path to your application root. -APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) +APP_ROOT = File.expand_path('..', __dir__) def system!(*args) system(*args) || abort("\n== Command #{args} failed ==") @@ -18,6 +17,9 @@ chdir APP_ROOT do system! 'gem install bundler --conservative' system('bundle check') || system!('bundle install') + # Install JavaScript dependencies if using Yarn + system('bin/yarn') + puts "\n== Updating database ==" system! 'bin/rails db:migrate' diff --git a/bin/webpack b/bin/webpack index 9d3800c74..0869ad277 100755 --- a/bin/webpack +++ b/bin/webpack @@ -1,11 +1,7 @@ #!/usr/bin/env ruby -# frozen_string_literal: true -# -# This file was generated by Bundler. -# -# The application 'webpack' is installed as part of a gem, and -# this file is here to facilitate running it. -# + +ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" +ENV["NODE_ENV"] ||= ENV["NODE_ENV"] || "development" require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", @@ -14,4 +10,6 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", require "rubygems" require "bundler/setup" -load Gem.bin_path("webpacker", "webpack") +require "webpacker" +require "webpacker/webpack_runner" +Webpacker::WebpackRunner.run(ARGV) diff --git a/bin/webpack-dev-server b/bin/webpack-dev-server index cf701102a..251f65e8e 100755 --- a/bin/webpack-dev-server +++ b/bin/webpack-dev-server @@ -1,11 +1,7 @@ #!/usr/bin/env ruby -# frozen_string_literal: true -# -# This file was generated by Bundler. -# -# The application 'webpack-dev-server' is installed as part of a gem, and -# this file is here to facilitate running it. -# + +ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development" +ENV["NODE_ENV"] ||= ENV["NODE_ENV"] || "development" require "pathname" ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", @@ -14,4 +10,6 @@ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", require "rubygems" require "bundler/setup" -load Gem.bin_path("webpacker", "webpack-dev-server") +require "webpacker" +require "webpacker/dev_server_runner" +Webpacker::DevServerRunner.run(ARGV) diff --git a/bin/yarn b/bin/yarn new file mode 100755 index 000000000..8c1535a78 --- /dev/null +++ b/bin/yarn @@ -0,0 +1,11 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path('..', __dir__) +Dir.chdir(APP_ROOT) do + begin + exec "yarnpkg #{ARGV.join(' ')}" unless Dir.exist?('node_modules') + rescue Errno::ENOENT + $stderr.puts "Yarn executable was not detected in the system." + $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" + exit 1 + end +end diff --git a/config/application.rb b/config/application.rb index 385bd4704..e989e2333 100644 --- a/config/application.rb +++ b/config/application.rb @@ -23,7 +23,7 @@ require_relative '../lib/mastodon/redis_config' module Mastodon class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. - config.load_defaults 5.1 + config.load_defaults 5.2 # Settings in config/environments/* take precedence over those specified here. # Application configuration should go into files in config/initializers @@ -85,15 +85,6 @@ module Mastodon config.active_job.queue_adapter = :sidekiq - config.middleware.insert_before 0, Rack::Cors do - allow do - origins '*' - resource '/@:username', headers: :any, methods: [:get], credentials: false - resource '/api/*', headers: :any, methods: [:post, :put, :delete, :get, :patch, :options], credentials: false, expose: ['Link', 'X-RateLimit-Reset', 'X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-Request-Id'] - resource '/oauth/token', headers: :any, methods: [:post], credentials: false - end - end - config.middleware.use Rack::Attack config.middleware.use Rack::Deflater diff --git a/config/boot.rb b/config/boot.rb index 703738b76..0a3cd4ebe 100644 --- a/config/boot.rb +++ b/config/boot.rb @@ -1,7 +1,7 @@ ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) require 'bundler/setup' # Set up gems listed in the Gemfile. -require 'bootsnap' +require 'bootsnap' # Speed up boot time by caching expensive operations. Bootsnap.setup( cache_dir: 'tmp/cache', diff --git a/config/deploy.rb b/config/deploy.rb index 3fd149f21..180dd1c2a 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -lock '3.10.0' +lock '3.10.1' set :repo_url, ENV.fetch('REPO', 'https://github.com/tootsuite/mastodon.git') set :branch, ENV.fetch('BRANCH', 'master') diff --git a/config/environments/development.rb b/config/environments/development.rb index 285fea8b8..b6478f16e 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -13,13 +13,14 @@ Rails.application.configure do config.consider_all_requests_local = true # Enable/disable caching. By default caching is disabled. + # Run rails dev:cache to toggle caching. if Rails.root.join('tmp/caching-dev.txt').exist? config.action_controller.perform_caching = true config.cache_store = :redis_store, ENV['REDIS_URL'], REDIS_CACHE_PARAMS config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}", + 'Cache-Control' => "public, max-age=#{2.days.to_i}", } else config.action_controller.perform_caching = false diff --git a/config/environments/production.rb b/config/environments/production.rb index f372cd363..e742f668a 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -15,6 +15,10 @@ Rails.application.configure do config.action_controller.perform_caching = true config.action_controller.asset_host = ENV['CDN_HOST'] if ENV.key?('CDN_HOST') + # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"] + # or in config/master.key. This key is used to decrypt credentials (and other encrypted files). + # config.require_master_key = true + # Disable serving static files from the `/public` folder by default since # Apache or NGINX already handles this. config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? diff --git a/config/environments/test.rb b/config/environments/test.rb index 122634d5b..1c1891561 100644 --- a/config/environments/test.rb +++ b/config/environments/test.rb @@ -15,7 +15,7 @@ Rails.application.configure do # Configure public file server for tests with Cache-Control for performance. config.public_file_server.enabled = true config.public_file_server.headers = { - 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" + 'Cache-Control' => "public, max-age=#{1.hour.to_i}" } config.assets.digest = false diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb new file mode 100644 index 000000000..37f2c0d45 --- /dev/null +++ b/config/initializers/content_security_policy.rb @@ -0,0 +1,20 @@ +# Define an application-wide content security policy +# For further information see the following documentation +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy + +# Rails.application.config.content_security_policy do |p| +# p.default_src :self, :https +# p.font_src :self, :https, :data +# p.img_src :self, :https, :data +# p.object_src :none +# p.script_src :self, :https +# p.style_src :self, :https, :unsafe_inline +# +# # Specify URI for violation reports +# # p.report_uri "/csp-violation-report-endpoint" +# end + +# Report CSP violations to a specified URI +# For further information see the following documentation: +# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only +# Rails.application.config.content_security_policy_report_only = true diff --git a/config/initializers/cors.rb b/config/initializers/cors.rb new file mode 100644 index 000000000..681a7498f --- /dev/null +++ b/config/initializers/cors.rb @@ -0,0 +1,26 @@ +# Be sure to restart your server when you modify this file. + +# Avoid CORS issues when API is called from the frontend app. +# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. + +# Read more: https://github.com/cyu/rack-cors + +Rails.application.config.middleware.insert_before 0, Rack::Cors do + allow do + origins '*' + + resource '/@:username', + headers: :any, + methods: [:get], + credentials: false + resource '/api/*', + headers: :any, + methods: [:post, :put, :delete, :get, :patch, :options], + credentials: false, + expose: ['Link', 'X-RateLimit-Reset', 'X-RateLimit-Limit', 'X-RateLimit-Remaining', 'X-Request-Id'] + resource '/oauth/token', + headers: :any, + methods: [:post], + credentials: false + end +end diff --git a/db/schema.rb b/db/schema.rb index a9733a2ae..218457e65 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,9 +10,10 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180402040909) do +ActiveRecord::Schema.define(version: 2018_04_02_040909) do # These are extensions that must be enabled in order to support this database + enable_extension "pg_stat_statements" enable_extension "plpgsql" create_table "account_domain_blocks", force: :cascade do |t| From 14d86eb0d076cbadc5944d817ddb731d95312ccf Mon Sep 17 00:00:00 2001 From: "Renato \"Lond\" Cerqueira" Date: Thu, 12 Apr 2018 20:36:02 +0200 Subject: [PATCH 071/442] Allow more than the max pins if account is not local (#7105) Sidekiq sometimes throws errors for users that have more pinned items than the allowed by the local instance. It should only validate the number of pins for local accounts. --- app/validators/status_pin_validator.rb | 2 +- spec/models/status_pin_spec.rb | 31 ++++++++++++++++++++++++++ 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/app/validators/status_pin_validator.rb b/app/validators/status_pin_validator.rb index 64da04120..2c7bce674 100644 --- a/app/validators/status_pin_validator.rb +++ b/app/validators/status_pin_validator.rb @@ -5,6 +5,6 @@ class StatusPinValidator < ActiveModel::Validator pin.errors.add(:base, I18n.t('statuses.pin_errors.reblog')) if pin.status.reblog? pin.errors.add(:base, I18n.t('statuses.pin_errors.ownership')) if pin.account_id != pin.status.account_id pin.errors.add(:base, I18n.t('statuses.pin_errors.private')) unless %w(public unlisted).include?(pin.status.visibility) - pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count > 4 + pin.errors.add(:base, I18n.t('statuses.pin_errors.limit')) if pin.account.status_pins.count > 4 && pin.account.local? end end diff --git a/spec/models/status_pin_spec.rb b/spec/models/status_pin_spec.rb index 6f54f80f9..944baf639 100644 --- a/spec/models/status_pin_spec.rb +++ b/spec/models/status_pin_spec.rb @@ -37,5 +37,36 @@ RSpec.describe StatusPin, type: :model do expect(StatusPin.new(account: account, status: status).save).to be false end + + max_pins = 5 + it 'does not allow pins above the max' do + account = Fabricate(:account) + status = [] + + (max_pins + 1).times do |i| + status[i] = Fabricate(:status, account: account) + end + + max_pins.times do |i| + expect(StatusPin.new(account: account, status: status[i]).save).to be true + end + + expect(StatusPin.new(account: account, status: status[max_pins]).save).to be false + end + + it 'allows pins above the max for remote accounts' do + account = Fabricate(:account, domain: 'remote', username: 'bob', url: 'https://remote/') + status = [] + + (max_pins + 1).times do |i| + status[i] = Fabricate(:status, account: account) + end + + max_pins.times do |i| + expect(StatusPin.new(account: account, status: status[i]).save).to be true + end + + expect(StatusPin.new(account: account, status: status[max_pins]).save).to be true + end end end From 9e45b051cfea667f9ca3d3c72d13022259315090 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 13 Apr 2018 01:20:04 +0200 Subject: [PATCH 072/442] When notification type filtered, ignore live updates for it (#7101) Fix #5625 --- .../mastodon/actions/notifications.js | 39 ++++++++++++------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index da77afbe0..7aa070f56 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -9,7 +9,8 @@ import { } from './importer'; import { defineMessages } from 'react-intl'; -export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; +export const NOTIFICATIONS_UPDATE = 'NOTIFICATIONS_UPDATE'; +export const NOTIFICATIONS_UPDATE_NOOP = 'NOTIFICATIONS_UPDATE_NOOP'; export const NOTIFICATIONS_EXPAND_REQUEST = 'NOTIFICATIONS_EXPAND_REQUEST'; export const NOTIFICATIONS_EXPAND_SUCCESS = 'NOTIFICATIONS_EXPAND_SUCCESS'; @@ -39,28 +40,38 @@ const unescapeHTML = (html) => { export function updateNotifications(notification, intlMessages, intlLocale) { return (dispatch, getState) => { - const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true); - const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true); + const showInColumn = getState().getIn(['settings', 'notifications', 'shows', notification.type], true); + const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true); + const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true); - dispatch(importFetchedAccount(notification.account)); - if (notification.status) { - dispatch(importFetchedStatus(notification.status)); + if (showInColumn) { + dispatch(importFetchedAccount(notification.account)); + + if (notification.status) { + dispatch(importFetchedStatus(notification.status)); + } + + dispatch({ + type: NOTIFICATIONS_UPDATE, + notification, + meta: playSound ? { sound: 'boop' } : undefined, + }); + + fetchRelatedRelationships(dispatch, [notification]); + } else if (playSound) { + dispatch({ + type: NOTIFICATIONS_UPDATE_NOOP, + meta: { sound: 'boop' }, + }); } - dispatch({ - type: NOTIFICATIONS_UPDATE, - notification, - meta: playSound ? { sound: 'boop' } : undefined, - }); - - fetchRelatedRelationships(dispatch, [notification]); - // Desktop notifications if (typeof window.Notification !== 'undefined' && showAlert) { const title = new IntlMessageFormat(intlMessages[`notification.${notification.type}`], intlLocale).format({ name: notification.account.display_name.length > 0 ? notification.account.display_name : notification.account.username }); const body = (notification.status && notification.status.spoiler_text.length > 0) ? notification.status.spoiler_text : unescapeHTML(notification.status ? notification.status.content : ''); const notify = new Notification(title, { body, icon: notification.account.avatar, tag: notification.id }); + notify.addEventListener('click', () => { window.focus(); notify.close(); From 778562c223844226a52198100dc081811bdd9d35 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 13 Apr 2018 01:27:22 +0200 Subject: [PATCH 073/442] Ensure SynchronizeFeaturedCollectionWorker is unique and clean up (#7043) * Ensure SynchronizeFeaturedCollectionWorker is unique and clean up Fix #7041 * Fix code style issue --- app/services/activitypub/process_account_service.rb | 13 ++++++++----- .../synchronize_featured_collection_worker.rb | 2 +- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 21c2fc57a..4475a9079 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -22,13 +22,15 @@ class ActivityPub::ProcessAccountService < BaseService create_account if @account.nil? update_account - process_tags(@account) + process_tags end end + return if @account.nil? + after_protocol_change! if protocol_changed? after_key_change! if key_changed? - check_featured_collection! if @account&.featured_collection_url&.present? + check_featured_collection! if @account.featured_collection_url.present? @account rescue Oj::ParseError @@ -189,17 +191,18 @@ class ActivityPub::ProcessAccountService < BaseService { redis: Redis.current, key: "process_account:#{@uri}" } end - def process_tags(account) + def process_tags return if @json['tag'].blank? + as_array(@json['tag']).each do |tag| case tag['type'] when 'Emoji' - process_emoji tag, account + process_emoji tag end end end - def process_emoji(tag, _account) + def process_emoji(tag) return if skip_download? return if tag['name'].blank? || tag['icon'].blank? || tag['icon']['url'].blank? diff --git a/app/workers/activitypub/synchronize_featured_collection_worker.rb b/app/workers/activitypub/synchronize_featured_collection_worker.rb index dd676a3ee..7b16d3426 100644 --- a/app/workers/activitypub/synchronize_featured_collection_worker.rb +++ b/app/workers/activitypub/synchronize_featured_collection_worker.rb @@ -3,7 +3,7 @@ class ActivityPub::SynchronizeFeaturedCollectionWorker include Sidekiq::Worker - sidekiq_options queue: 'pull' + sidekiq_options queue: 'pull', unique: :until_executed def perform(account_id) ActivityPub::FetchFeaturedCollectionService.new.call(Account.find(account_id)) From 85ab30abf7f8da61d37e4711cba350877bfb6f2b Mon Sep 17 00:00:00 2001 From: mayaeh Date: Fri, 13 Apr 2018 20:11:26 +0900 Subject: [PATCH 074/442] i18n: Add Japanese translations for privacy policy and more (#7049) * Update Japanese translations. * Update Japanese translations. * Update Japanese translations. * Update Japanese translations. * Add Japanese translations for #6984, #7040, #7072. Update Japanese translations for privacy policy. * Add Japanese translations for #7032, #7074, #7089. * Proofreading Japanese translations for privacy policy. --- .../mastodon/locales/defaultMessages.json | 9 ++ app/javascript/mastodon/locales/ja.json | 2 + config/locales/ja.yml | 104 +++++++++++++++++- 3 files changed, 111 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 03be288bb..21cd83524 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -114,6 +114,15 @@ ], "path": "app/javascript/mastodon/components/domain.json" }, + { + "descriptors": [ + { + "defaultMessage": "Load more", + "id": "status.load_more" + } + ], + "path": "app/javascript/mastodon/components/load_gap.json" + }, { "descriptors": [ { diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 432517716..a9beaa335 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -240,6 +240,7 @@ "status.block": "@{name}さんをブロック", "status.cannot_reblog": "この投稿はブーストできません", "status.delete": "削除", + "status.direct": "@{name}さんにダイレクトメッセージ", "status.embed": "埋め込み", "status.favourite": "お気に入り", "status.load_more": "もっと見る", @@ -269,6 +270,7 @@ "tabs_bar.home": "ホーム", "tabs_bar.local_timeline": "ローカル", "tabs_bar.notifications": "通知", + "tabs_bar.search": "検索", "ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。", "upload_area.title": "ドラッグ&ドロップでアップロード", "upload_button.label": "メディアを追加", diff --git a/config/locales/ja.yml b/config/locales/ja.yml index b943f0f92..960dd38ef 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -4,6 +4,7 @@ ja: about_hashtag_html: ハッシュタグ #%{hashtag} の付いた公開トゥートです。どこでもいいので、連合に参加しているSNS上にアカウントを作れば会話に参加することができます。 about_mastodon_html: Mastodon は、オープンなウェブプロトコルを採用した、自由でオープンソースなソーシャルネットワークです。電子メールのような分散型の仕組みを採っています。 about_this: 詳細情報 + administered_by: '管理者:' closed_registrations: 現在このインスタンスでの新規登録は受け付けていません。しかし、他のインスタンスにアカウントを作成しても全く同じネットワークに参加することができます。 contact: 連絡先 contact_missing: 未設定 @@ -62,6 +63,13 @@ ja: are_you_sure: 本当に実行しますか? avatar: アイコン by_domain: ドメイン + change_email: + changed_msg: メールアドレスの変更に成功しました! + current_email: 現在のメールアドレス + label: メールアドレスを変更 + new_email: 新しいメールアドレス + submit: Change Email + title: "%{username} さんのメールアドレスを変更" confirm: 確認 confirmed: 確認済み demote: 降格 @@ -71,7 +79,7 @@ ja: display_name: 表示名 domain: ドメイン edit: 編集 - email: E-mail + email: メールアドレス enable: 有効化 enabled: 有効 feed_url: フィードURL @@ -130,6 +138,7 @@ ja: statuses: トゥート数 subscribe: 購読する title: アカウント + unconfirmed_email: 確認待ちのメールアドレス undo_silenced: サイレンスから戻す undo_suspension: 停止から戻す unsubscribe: 購読の解除 @@ -138,6 +147,7 @@ ja: action_logs: actions: assigned_to_self_report: "%{name} さんがレポート %{target} を自身の担当に割り当てました" + change_email_user: "%{name} さんが %{target} さんのメールアドレスを変更しました" confirm_user: "%{name} さんが %{target} さんのメールアドレスを確認済みにしました" create_custom_emoji: "%{name} さんがカスタム絵文字 %{target} を追加しました" create_domain_block: "%{name} さんがドメイン %{target} をブロックしました" @@ -246,8 +256,8 @@ ja: title: フィルター title: 招待 report_notes: - created_msg: モデレーションメモを書き込みました! - destroyed_msg: モデレーションメモを削除しました! + created_msg: レポートメモを書き込みました! + destroyed_msg: レポートメモを削除しました! reports: action_taken_by: レポート処理者 are_you_sure: 本当に実行しますか? @@ -256,15 +266,20 @@ ja: comment: label: コメント none: なし + created_at: レポート日時 delete: 削除 + history: モデレーション履歴 id: ID mark_as_resolved: 解決済みとしてマーク mark_as_unresolved: 未解決として再び開く notes: create: 書き込む create_and_resolve: 書き込み、解決済みにする + create_and_unresolve: 書き込み、未解決として開く delete: 削除 - label: メモ + label: モデレーターメモ + new_label: モデレーターメモの追加 + placeholder: このレポートに取られた措置やその他更新を記述してください nsfw: 'false': NSFW オフ 'true': NSFW オン @@ -601,6 +616,10 @@ ja: missing_resource: リダイレクト先が見つかりませんでした proceed: フォローする prompt: 'フォローしようとしています:' + remote_unfollow: + error: エラー + title: タイトル + unfollowed: フォロー解除しました sessions: activity: 最後のアクティビティ browser: ブラウザ @@ -689,6 +708,83 @@ ja: reblogged: さんがブースト sensitive_content: 閲覧注意 terms: + body_html: | +

プライバシーポリシー

+

どのような情報を収集しますか?

+ +
    +
  • 基本的なアカウント情報: 当サイトに登録すると、ユーザー名・メールアドレス・パスワードの入力を求められることがあります。また表示名や自己紹介・プロフィール画像・ヘッダー画像といった追加のプロフィールを登録できます。ユーザー名・表示名・自己紹介・プロフィール画像・ヘッダー画像は常に公開されます。
  • +
  • 投稿・フォロー・その他公開情報: フォローしているユーザーの一覧は一般公開されます。フォロワーも同様です。メッセージを投稿する際、日時だけでなく投稿に使用したアプリケーション名も記録されます。メッセージには写真や動画といった添付メディアを含むことがあります。「公開」や「未収載」の投稿は一般公開されます。プロフィールに投稿を載せるとそれもまた公開情報となります。投稿はフォロワーに配信されます。場合によっては他のサーバーに配信され、そこにコピーが保存されることを意味します。投稿を削除した場合も同様にフォロワーに配信されます。他の投稿をリブログやお気に入り登録する行動は常に公開されます。
  • +
  • 「ダイレクト」と「非公開」投稿: すべての投稿はサーバーに保存され、処理されます。「非公開」投稿はフォロワーと投稿に書かれたユーザーに配信されます。「ダイレクト」投稿は投稿に書かれたユーザーにのみ配信されます。場合によっては他のサーバーに配信され、そこにコピーが保存されることを意味します。私たちはこれらの閲覧を一部の許可された者に限定するよう誠意を持って努めます。しかし他のサーバーにおいても同様に扱われるとは限りません。したがって、相手の所属するサーバーを吟味することが重要です。設定で新しいフォロワーの承認または拒否を手動で行うよう切り替えることもできます。サーバー管理者は「ダイレクト」や「非公開」投稿も閲覧する可能性があることを忘れないでください。また受信者がスクリーンショットやコピー、もしくは共有する可能性があることを忘れないでください。いかなる危険な情報もMastodon上で共有しないでください。
  • +
  • IPアドレスやその他メタデータ: ログインする際IPアドレスだけでなくブラウザーアプリケーション名を記録します。ログインしたセッションはすべてユーザー設定で見直し、取り消すことができます。使用されている最新のIPアドレスは最大12ヵ月間保存されます。またサーバーへのIPアドレスを含むすべてのリクエストのログを保持することがあります。
  • +
+ +
+ +

情報を何に使用しますか?

+ +

収集した情報は次の用途に使用されることがあります:

+ +
    +
  • Mastodonのコア機能の提供: ログインしている間にかぎり他の人たちと投稿を通じて交流することができます。例えば自分専用のホームタイムラインで投稿をまとめて読むために他の人たちをフォローできます。
  • +
  • コミュニティ維持の補助: 例えばIPアドレスを既知のものと比較し、BAN回避目的の複数登録者やその他違反者を判別します。
  • +
  • 提供されたメールアドレスはお知らせの送信・投稿に対するリアクションやメッセージ送信の通知・お問い合わせやその他要求や質問への返信に使用されることがあります。
  • +
+ +
+ +

情報をどのように保護しますか?

+ +

私たちはあなたが入力・送信する際や自身の情報にアクセスする際に個人情報を安全に保つため、さまざまなセキュリティ上の対策を実施します。特にブラウザーセッションだけでなくアプリケーションとAPI間の通信もSSLによって保護されます。またパスワードは強力な不可逆アルゴリズムでハッシュ化されます。二段階認証を有効にし、アカウントへのアクセスをさらに安全にすることができます。

+ +
+ +

データ保持方針はどうなっていますか?

+ +

私たちは次のように誠意を持って努めます:

+ +
    +
  • 当サイトへのIPアドレスを含むすべての要求に対するサーバーログを90日以内のできるかぎりの間保持します。
  • +
  • 登録されたユーザーに関連付けられたIPアドレスを12ヵ月以内の間保持します。
  • +
+ +

あなたは投稿・添付メディア・プロフィール画像・ヘッダー画像を含む自身のデータのアーカイブを要求し、ダウンロードすることができます。

+ +

あなたはいつでもアカウントの削除を要求できます。削除は取り消すことができません。

+ +
+ +

クッキーを使用していますか?

+ +

はい。クッキーは (あなたが許可した場合に) WebサイトやサービスがWebブラウザーを介してコンピューターに保存する小さなファイルです。使用することで Web サイトがブラウザーを識別し、登録済みのアカウントがある場合関連付けます。

+ +

私たちはクッキーを将来の訪問のために設定を保存し呼び出す用途に使用します。

+ +
+ +

なんらかの情報を外部に提供していますか?

+ +

私たちは個人を特定できる情報を外部へ販売・取引・その他方法で渡すことはありません。これには当サイトの運営・業務遂行・サービス提供を行ううえで補助する信頼できる第三者をこの機密情報の保護に同意するかぎり含みません。法令の遵守やサイトポリシーの施行、権利・財産・安全の保護に適切と判断した場合、あなたの情報を公開することがあります。

+ +

あなたの公開情報はネットワーク上の他のサーバーにダウンロードされることがあります。相手が異なるサーバーに所属する場合、「公開」と「非公開」投稿はフォロワーの所属するサーバーに配信され、「ダイレクト」投稿は受信者の所属するサーバーに配信されます。

+ +

あなたがアカウントの使用をアプリケーションに許可すると、承認した権限の範囲内で公開プロフィール情報・フォローリスト・フォロワー・リスト・すべての投稿・お気に入り登録にアクセスできます。アプリケーションはメールアドレスやパスワードに決してアクセスできません。

+ +
+ +

児童オンラインプライバシー保護法の遵守

+ +

当サイト・製品・サービスは13歳以上の人を対象としています。サーバーが米国にあり、あなたが13歳未満の場合、COPPA (Children's Online Privacy Protection Act - 児童オンラインプライバシー保護法) により当サイトを使用できません。

+ +
+ +

プライバシーポリシーの変更

+ +

プライバシーポリシーの変更を決定した場合、このページに変更点を掲載します。

+ +

この文章のライセンスはCC-BY-SAです。最終更新日は2018年3月7日です。

+ +

オリジナルの出典: Discourse privacy policy

title: "%{instance} 利用規約・プライバシーポリシー" themes: default: Mastodon From 78ed4ab75ff77d7cba60d478aa1f45d1c104785d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 14 Apr 2018 12:41:08 +0200 Subject: [PATCH 075/442] Add bio fields (#6645) * Add bio fields - Fix #3211 - Fix #232 - Fix #121 * Display bio fields in web UI * Fix output of links and missing fields * Federate bio fields over ActivityPub as PropertyValue * Improve how the fields are stored, add to Edit profile form * Add rel=me to links in fields Fix #121 --- .../settings/profiles_controller.rb | 6 ++- .../mastodon/actions/importer/normalizer.js | 8 +++ .../features/account/components/header.js | 14 +++++ app/javascript/styles/mastodon/accounts.scss | 54 +++++++++++++++++++ .../styles/mastodon/components.scss | 37 +++++++++++++ app/javascript/styles/mastodon/forms.scss | 12 +++++ app/lib/activitypub/adapter.rb | 3 ++ app/lib/formatter.rb | 18 +++++-- app/models/account.rb | 36 +++++++++++++ .../activitypub/actor_serializer.rb | 17 ++++++ app/serializers/rest/account_serializer.rb | 10 ++++ .../activitypub/process_account_service.rb | 6 +++ app/views/accounts/_header.html.haml | 8 +++ app/views/settings/profiles/show.html.haml | 10 ++++ config/locales/simple_form.en.yml | 6 +++ .../20180410204633_add_fields_to_accounts.rb | 5 ++ db/schema.rb | 3 +- .../process_account_service_spec.rb | 28 +++++++++- 18 files changed, 274 insertions(+), 7 deletions(-) create mode 100644 db/migrate/20180410204633_add_fields_to_accounts.rb diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index 28f78a4fb..5d81668de 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -11,7 +11,9 @@ class Settings::ProfilesController < ApplicationController obfuscate_filename [:account, :avatar] obfuscate_filename [:account, :header] - def show; end + def show + @account.build_fields + end def update if UpdateAccountService.new.call(@account, account_params) @@ -25,7 +27,7 @@ class Settings::ProfilesController < ApplicationController private def account_params - params.require(:account).permit(:display_name, :note, :avatar, :header, :locked) + params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, fields_attributes: [:name, :value]) end def set_account diff --git a/app/javascript/mastodon/actions/importer/normalizer.js b/app/javascript/mastodon/actions/importer/normalizer.js index 1b09f319f..5f1274fab 100644 --- a/app/javascript/mastodon/actions/importer/normalizer.js +++ b/app/javascript/mastodon/actions/importer/normalizer.js @@ -10,6 +10,14 @@ export function normalizeAccount(account) { account.display_name_html = emojify(escapeTextContentForBrowser(displayName)); account.note_emojified = emojify(account.note); + if (account.fields) { + account.fields = account.fields.map(pair => ({ + ...pair, + name_emojified: emojify(escapeTextContentForBrowser(pair.name)), + value_emojified: emojify(pair.value), + })); + } + if (account.moved) { account.moved = account.moved.id; } diff --git a/app/javascript/mastodon/features/account/components/header.js b/app/javascript/mastodon/features/account/components/header.js index bb7b3b632..bbf886dca 100644 --- a/app/javascript/mastodon/features/account/components/header.js +++ b/app/javascript/mastodon/features/account/components/header.js @@ -130,6 +130,7 @@ export default class Header extends ImmutablePureComponent { const content = { __html: account.get('note_emojified') }; const displayNameHtml = { __html: account.get('display_name_html') }; + const fields = account.get('fields'); return (
@@ -140,6 +141,19 @@ export default class Header extends ImmutablePureComponent { @{account.get('acct')} {lockedIcon}
+ {fields.size > 0 && ( + + + {fields.map((pair, i) => ( + + + ))} + +
+ +
+ )} + {info} {mutingInfo} {actionBtn} diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index dd82ab375..0b49da1ad 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -563,3 +563,57 @@ border-color: rgba(lighten($error-red, 12%), 0.5); } } + +.account__header__fields { + border-collapse: collapse; + padding: 0; + margin: 15px -15px -15px; + border: 0 none; + border-top: 1px solid lighten($ui-base-color, 4%); + border-bottom: 1px solid lighten($ui-base-color, 4%); + + th, + td { + padding: 15px; + padding-left: 15px; + border: 0 none; + border-bottom: 1px solid lighten($ui-base-color, 4%); + vertical-align: middle; + } + + th { + padding-left: 15px; + font-weight: 500; + text-align: center; + width: 94px; + color: $ui-secondary-color; + background: rgba(darken($ui-base-color, 8%), 0.5); + } + + td { + color: $ui-primary-color; + text-align: center; + width: 100%; + padding-left: 0; + } + + a { + color: $ui-highlight-color; + text-decoration: none; + + &:hover, + &:focus, + &:active { + text-decoration: underline; + } + } + + tr { + &:last-child { + th, + td { + border-bottom: 0; + } + } + } +} diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 888a0ad82..96112d84a 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -5176,3 +5176,40 @@ noscript { background: lighten($ui-highlight-color, 7%); } } + +.account__header .account__header__fields { + font-size: 14px; + line-height: 20px; + overflow: hidden; + border-collapse: collapse; + margin: 20px -10px -20px; + border-bottom: 0; + + tr { + border-top: 1px solid lighten($ui-base-color, 8%); + text-align: center; + } + + th, + td { + padding: 14px 20px; + vertical-align: middle; + max-height: 40px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + } + + th { + color: $ui-primary-color; + background: darken($ui-base-color, 4%); + max-width: 120px; + font-weight: 500; + } + + td { + flex: auto; + color: $primary-text-color; + background: $ui-base-color; + } +} diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index d74c5a4fd..945579a9c 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -15,6 +15,18 @@ code { overflow: hidden; } + .row { + display: flex; + margin: 0 -5px; + + .input { + box-sizing: border-box; + flex: 1 1 auto; + width: 50%; + padding: 0 5px; + } + } + span.hint { display: block; color: $ui-primary-color; diff --git a/app/lib/activitypub/adapter.rb b/app/lib/activitypub/adapter.rb index f19b04ae6..e880499f1 100644 --- a/app/lib/activitypub/adapter.rb +++ b/app/lib/activitypub/adapter.rb @@ -19,6 +19,9 @@ class ActivityPub::Adapter < ActiveModelSerializers::Adapter::Base 'Emoji' => 'toot:Emoji', 'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' }, 'featured' => 'toot:featured', + 'schema' => 'http://schema.org#', + 'PropertyValue' => 'schema:PropertyValue', + 'value' => 'schema:value', }, ], }.freeze diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index f7e7a3c23..4124f1660 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -71,6 +71,11 @@ class Formatter html.html_safe # rubocop:disable Rails/OutputSafety end + def format_field(account, str) + return reformat(str).html_safe unless account.local? # rubocop:disable Rails/OutputSafety + encode_and_link_urls(str, me: true).html_safe # rubocop:disable Rails/OutputSafety + end + def linkify(text) html = encode_and_link_urls(text) html = simple_format(html, {}, sanitize: false) @@ -85,12 +90,17 @@ class Formatter HTMLEntities.new.encode(html) end - def encode_and_link_urls(html, accounts = nil) + def encode_and_link_urls(html, accounts = nil, options = {}) entities = Extractor.extract_entities_with_indices(html, extract_url_without_protocol: false) + if accounts.is_a?(Hash) + options = accounts + accounts = nil + end + rewrite(html.dup, entities) do |entity| if entity[:url] - link_to_url(entity) + link_to_url(entity, options) elsif entity[:hashtag] link_to_hashtag(entity) elsif entity[:screen_name] @@ -177,10 +187,12 @@ class Formatter result.flatten.join end - def link_to_url(entity) + def link_to_url(entity, options = {}) url = Addressable::URI.parse(entity[:url]) html_attrs = { target: '_blank', rel: 'nofollow noopener' } + html_attrs[:rel] = "me #{html_attrs[:rel]}" if options[:me] + Twitter::Autolink.send(:link_to_text, entity, link_html(entity[:url]), url, html_attrs) rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError encode(entity[:url]) diff --git a/app/models/account.rb b/app/models/account.rb index 5bdcfa99e..05e817f63 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -44,6 +44,7 @@ # memorial :boolean default(FALSE), not null # moved_to_account_id :integer # featured_collection_url :string +# fields :jsonb # class Account < ApplicationRecord @@ -189,6 +190,30 @@ class Account < ApplicationRecord @keypair ||= OpenSSL::PKey::RSA.new(private_key || public_key) end + def fields + (self[:fields] || []).map { |f| Field.new(self, f) } + end + + def fields_attributes=(attributes) + fields = [] + + attributes.each_value do |attr| + next if attr[:name].blank? + fields << attr + end + + self[:fields] = fields + end + + def build_fields + return if fields.size >= 4 + + raw_fields = self[:fields] || [] + add_fields = 4 - raw_fields.size + add_fields.times { raw_fields << { name: '', value: '' } } + self.fields = raw_fields + end + def magic_key modulus, exponent = [keypair.public_key.n, keypair.public_key.e].map do |component| result = [] @@ -238,6 +263,17 @@ class Account < ApplicationRecord shared_inbox_url.presence || inbox_url end + class Field < ActiveModelSerializers::Model + attributes :name, :value, :account, :errors + + def initialize(account, attr) + @account = account + @name = attr['name'] + @value = attr['value'] + @errors = {} + end + end + class << self def readonly_attributes super - %w(statuses_count following_count followers_count) diff --git a/app/serializers/activitypub/actor_serializer.rb b/app/serializers/activitypub/actor_serializer.rb index df3090726..fcf3bdf17 100644 --- a/app/serializers/activitypub/actor_serializer.rb +++ b/app/serializers/activitypub/actor_serializer.rb @@ -11,6 +11,7 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer has_one :public_key, serializer: ActivityPub::PublicKeySerializer has_many :virtual_tags, key: :tag + has_many :virtual_attachments, key: :attachment attribute :moved_to, if: :moved? @@ -107,10 +108,26 @@ class ActivityPub::ActorSerializer < ActiveModel::Serializer object.emojis end + def virtual_attachments + object.fields + end + def moved_to ActivityPub::TagManager.instance.uri_for(object.moved_to_account) end class CustomEmojiSerializer < ActivityPub::EmojiSerializer end + + class Account::FieldSerializer < ActiveModel::Serializer + attributes :type, :name, :value + + def type + 'PropertyValue' + end + + def value + Formatter.instance.format_field(object.account, object.value) + end + end end diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index 6097acda5..863238eb7 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -9,6 +9,16 @@ class REST::AccountSerializer < ActiveModel::Serializer has_one :moved_to_account, key: :moved, serializer: REST::AccountSerializer, if: :moved_and_not_nested? + class FieldSerializer < ActiveModel::Serializer + attributes :name, :value + + def value + Formatter.instance.format_field(object.account, object.value) + end + end + + has_many :fields + def id object.id.to_s end diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 4475a9079..da32f9615 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -70,6 +70,7 @@ class ActivityPub::ProcessAccountService < BaseService @account.display_name = @json['name'] || '' @account.note = @json['summary'] || '' @account.locked = @json['manuallyApprovesFollowers'] || false + @account.fields = property_values || {} end def set_fetchable_attributes! @@ -126,6 +127,11 @@ class ActivityPub::ProcessAccountService < BaseService end end + def property_values + return unless @json['attachment'].is_a?(Array) + @json['attachment'].select { |attachment| attachment['type'] == 'PropertyValue' }.map { |attachment| attachment.slice('name', 'value') } + end + def mismatching_origin?(url) needle = Addressable::URI.parse(url).host haystack = Addressable::URI.parse(@uri).host diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index b78998e9e..0d3a0d08d 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -23,6 +23,14 @@ .bio .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account, custom_emojify: true) + - unless account.fields.empty? + %table.account__header__fields + %tbody + - account.fields.each do |field| + %tr + %th.emojify= field.name + %td.emojify= Formatter.instance.format_field(account, field.value) + .details-counters .counter{ class: active_nav_class(short_account_url(account)) } = link_to short_account_url(account), class: 'u-url u-uid' do diff --git a/app/views/settings/profiles/show.html.haml b/app/views/settings/profiles/show.html.haml index 0fc9db2b9..f28834d72 100644 --- a/app/views/settings/profiles/show.html.haml +++ b/app/views/settings/profiles/show.html.haml @@ -19,6 +19,16 @@ .fields-group = f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked') + .fields-group + .input.with_block_label + %label= t('simple_form.labels.defaults.fields') + %span.hint= t('simple_form.hints.defaults.fields') + + = f.simple_fields_for :fields do |fields_f| + .row + = fields_f.input :name, placeholder: t('simple_form.labels.account.fields.name') + = fields_f.input :value, placeholder: t('simple_form.labels.account.fields.value') + .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 37a02bde6..1a0d60f71 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -8,6 +8,7 @@ en: display_name: one: 1 character left other: %{count} 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 locked: Requires you to manually approve followers note: @@ -22,6 +23,10 @@ en: user: filtered_languages: Checked languages will be filtered from public timelines for you labels: + account: + fields: + name: Label + value: Content defaults: avatar: Avatar confirm_new_password: Confirm new password @@ -31,6 +36,7 @@ en: display_name: Display name email: E-mail address expires_in: Expire after + fields: Profile metadata filtered_languages: Filtered languages header: Header locale: Language diff --git a/db/migrate/20180410204633_add_fields_to_accounts.rb b/db/migrate/20180410204633_add_fields_to_accounts.rb new file mode 100644 index 000000000..5b8c17480 --- /dev/null +++ b/db/migrate/20180410204633_add_fields_to_accounts.rb @@ -0,0 +1,5 @@ +class AddFieldsToAccounts < ActiveRecord::Migration[5.1] + def change + add_column :accounts, :fields, :jsonb + end +end diff --git a/db/schema.rb b/db/schema.rb index 218457e65..10a8f2edc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2018_04_02_040909) do +ActiveRecord::Schema.define(version: 2018_04_10_204633) do # These are extensions that must be enabled in order to support this database enable_extension "pg_stat_statements" @@ -75,6 +75,7 @@ ActiveRecord::Schema.define(version: 2018_04_02_040909) do t.boolean "memorial", default: false, null: false t.bigint "moved_to_account_id" t.string "featured_collection_url" + t.jsonb "fields" t.index "(((setweight(to_tsvector('simple'::regconfig, (display_name)::text), 'A'::\"char\") || setweight(to_tsvector('simple'::regconfig, (username)::text), 'B'::\"char\")) || setweight(to_tsvector('simple'::regconfig, (COALESCE(domain, ''::character varying))::text), 'C'::\"char\")))", name: "search_index", using: :gin t.index "lower((username)::text), lower((domain)::text)", name: "index_accounts_on_username_and_domain_lower" t.index ["uri"], name: "index_accounts_on_uri" diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index 84a74c231..15e1f4bb2 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -1,5 +1,31 @@ require 'rails_helper' RSpec.describe ActivityPub::ProcessAccountService do - pending + subject { described_class.new } + + context 'property values' do + let(:payload) do + { + id: 'https://foo', + type: 'Actor', + inbox: 'https://foo/inbox', + attachment: [ + { type: 'PropertyValue', name: 'Pronouns', value: 'They/them' }, + { type: 'PropertyValue', name: 'Occupation', value: 'Unit test' }, + ], + }.with_indifferent_access + end + + it 'parses out of attachment' do + account = subject.call('alice', 'example.com', payload) + expect(account.fields).to be_a Array + expect(account.fields.size).to eq 2 + expect(account.fields[0]).to be_a Account::Field + expect(account.fields[0].name).to eq 'Pronouns' + expect(account.fields[0].value).to eq 'They/them' + expect(account.fields[1]).to be_a Account::Field + expect(account.fields[1].name).to eq 'Occupation' + expect(account.fields[1].value).to eq 'Unit test' + end + end end From fed0b5ed0453b0347b27acfb2950d94af870b62b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Sun, 15 Apr 2018 07:12:41 +0200 Subject: [PATCH 076/442] i18n: Update Polish translation (#7131) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- .../mastodon/locales/defaultMessages.json | 13 ------------- app/javascript/mastodon/locales/pl.json | 3 ++- config/locales/en.yml | 5 +++++ config/locales/pl.yml | 19 +++++++++++++++++++ 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 21cd83524..a3c4f775c 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -1815,18 +1815,5 @@ } ], "path": "app/javascript/mastodon/features/video/index.json" - }, - { - "descriptors": [ - { - "defaultMessage": "Oops!", - "id": "alert.unexpected.title" - }, - { - "defaultMessage": "An unexpected error occurred.", - "id": "alert.unexpected.message" - } - ], - "path": "app/javascript/mastodon/middleware/errors.json" } ] \ No newline at end of file diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 041935689..5360b6f33 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -2,7 +2,7 @@ "account.block": "Blokuj @{name}", "account.block_domain": "Blokuj wszystko z {domain}", "account.blocked": "Zablokowany", - "account.direct": "Direct Message @{name}", + "account.direct": "Wyślij wiadomość bezpośrednią do @{name}", "account.disclaimer_full": "Poniższe informacje mogą nie odwzorowywać bezbłędnie profilu użytkownika.", "account.domain_blocked": "Ukryto domenę", "account.edit_profile": "Edytuj profil", @@ -240,6 +240,7 @@ "status.block": "Zablokuj @{name}", "status.cannot_reblog": "Ten wpis nie może zostać podbity", "status.delete": "Usuń", + "status.direct": "Direct message @{name}", "status.embed": "Osadź", "status.favourite": "Ulubione", "status.load_more": "Załaduj więcej", diff --git a/config/locales/en.yml b/config/locales/en.yml index 98f135966..4816cc599 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -283,6 +283,7 @@ en: nsfw: 'false': Unhide media attachments 'true': Hide media attachments + reopen: Reopen Report report: 'Report #%{id}' report_contents: Contents reported_account: Reported account @@ -615,6 +616,10 @@ en: missing_resource: Could not find the required redirect URL for your account proceed: Proceed to follow prompt: 'You are going to follow:' + remote_unfollow: + error: Error + title: Title + unfollowed: Unfollowed sessions: activity: Last activity browser: Browser diff --git a/config/locales/pl.yml b/config/locales/pl.yml index d303171fb..0f18ace37 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -63,6 +63,13 @@ pl: are_you_sure: Jesteś tego pewien? avatar: Awatar by_domain: Domena + change_email: + changed_msg: Pomyślnie zmieniono adres e-mail konta! + current_email: Obecny adres e-mail + label: Zmień adres e-mail + new_email: Nowy adres e-mail + submit: Zmień adres e-mail + title: Zmień adres e-mail dla %{username} confirm: Potwierdź confirmed: Potwierdzono demote: Degraduj @@ -131,6 +138,7 @@ pl: statuses: Wpisy subscribe: Subskrybuj title: Konta + unconfirmed_email: Niepotwierdzony adres e-mail undo_silenced: Cofnij wyciszenie undo_suspension: Cofnij zawieszenie unsubscribe: Przestań subskrybować @@ -139,6 +147,7 @@ pl: action_logs: actions: assigned_to_self_report: "%{name} przypisał sobie zgłoszenie %{target}" + change_email_user: "%{name} zmienił adres-email użytkownika %{target}" confirm_user: "%{name} potwierdził adres e-mail użytkownika %{target}" create_custom_emoji: "%{name} dodał nowe emoji %{target}" create_domain_block: "%{name} zablokował domenę %{target}" @@ -258,18 +267,24 @@ pl: comment: label: Komentarz do zgłoszenia none: Brak + created_at: Zgłoszono delete: Usuń + history: Historia moderacji id: ID mark_as_resolved: Oznacz jako rozwiązane mark_as_unresolved: Oznacz jako nierozwiązane notes: create: Utwórz notatkę create_and_resolve: Rozwiąż i pozostaw notatkę + create_and_unresolve: Cofnij rozwiązanie i pozostaw notatkę delete: Usuń label: Notatki + new_label: Dodaj notatkę moderacyjną + placeholder: Opisz wykonane akcje i inne szczegóły dotyczące tego zgłoszenia… nsfw: 'false': Nie oznaczaj jako NSFW 'true': Oznaczaj jako NSFW + reopen: Otwórz ponownie report: 'Zgłoszenie #%{id}' report_contents: Zawartość reported_account: Zgłoszone konto @@ -608,6 +623,10 @@ pl: missing_resource: Nie udało się znaleźć adresu przekierowania z Twojej domeny proceed: Śledź prompt: 'Zamierzasz śledzić:' + remote_unfollow: + error: Błąd + title: Tytuł + unfollowed: Przestałeś śledzić sessions: activity: Ostatnia aktywność browser: Przeglądarka From 1e87ed44d551e1ff9ea9e0d7219386ecf15173cb Mon Sep 17 00:00:00 2001 From: Valentin Lorentz Date: Sun, 15 Apr 2018 13:57:58 +0200 Subject: [PATCH 077/442] docker-compose: Only bind ports 3000 and 4000 on localhost. (#7138) --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 836cb00b8..8058326dc 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -40,7 +40,7 @@ services: - external_network - internal_network ports: - - "3000:3000" + - "127.0.0.1:3000:3000" depends_on: - db - redis @@ -60,7 +60,7 @@ services: - external_network - internal_network ports: - - "4000:4000" + - "127.0.0.1:4000:4000" depends_on: - db - redis From fa04945365bc552748b9d312f936a74cfd8d35b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Sun, 15 Apr 2018 13:58:47 +0200 Subject: [PATCH 078/442] Change icon for domain blocks (#7139) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change icon for domain blocks Both domain blocks and user blocks uses the same icon… * Update index.js --- app/javascript/mastodon/features/domain_blocks/index.js | 2 +- app/javascript/mastodon/features/getting_started/index.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/features/domain_blocks/index.js b/app/javascript/mastodon/features/domain_blocks/index.js index b17c47e91..3b29e2a26 100644 --- a/app/javascript/mastodon/features/domain_blocks/index.js +++ b/app/javascript/mastodon/features/domain_blocks/index.js @@ -52,7 +52,7 @@ export default class Blocks extends ImmutablePureComponent { } return ( - + {domains.map(domain => diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index 1a71cff94..053a1ca17 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -122,7 +122,7 @@ export default class GettingStarted extends ImmutablePureComponent { - +
From 9f5ae7c2e81d898496743e68bc295e42515189aa Mon Sep 17 00:00:00 2001 From: YMHuang Date: Sun, 15 Apr 2018 12:00:32 +0000 Subject: [PATCH 079/442] Add and revise Traditional Chinese (zh-TW) translation for serveral strings (#7002) * Translation: add and revise Traditional Chinese translation for serveral strings * Translation: polish zh-TW locale * Translation: polish zh-TW locale --- app/javascript/mastodon/locales/zh-TW.json | 124 ++++++++++----------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index c7925829f..3edb3fa50 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -1,24 +1,24 @@ { "account.block": "封鎖 @{name}", "account.block_domain": "隱藏來自 {domain} 的一切貼文", - "account.blocked": "Blocked", - "account.direct": "Direct message @{name}", + "account.blocked": "已被封鎖的", + "account.direct": "給 @{name} 私人訊息", "account.disclaimer_full": "下列資料不一定完整。", - "account.domain_blocked": "Domain hidden", - "account.edit_profile": "編輯用者資訊", + "account.domain_blocked": "域名隱藏", + "account.edit_profile": "編輯使用者資訊", "account.follow": "關注", - "account.followers": "專注者", + "account.followers": "關注者", "account.follows": "正關注", "account.follows_you": "關注你", - "account.hide_reblogs": "Hide boosts from @{name}", + "account.hide_reblogs": "隱藏來自 @{name} 的轉推", "account.media": "媒體", "account.mention": "提到 @{name}", - "account.moved_to": "{name} has moved to:", + "account.moved_to": "{name} 已經移至:", "account.mute": "消音 @{name}", - "account.mute_notifications": "Mute notifications from @{name}", - "account.muted": "Muted", + "account.mute_notifications": "消音來自 @{name} 的通知", + "account.muted": "消音的", "account.posts": "貼文", - "account.posts_with_replies": "Toots with replies", + "account.posts_with_replies": "貼文及回覆", "account.report": "檢舉 @{name}", "account.requested": "正在等待許可", "account.share": "分享 @{name} 的用者資訊", @@ -27,10 +27,10 @@ "account.unblock_domain": "不再隱藏 {domain}", "account.unfollow": "取消關注", "account.unmute": "不再消音 @{name}", - "account.unmute_notifications": "Unmute notifications from @{name}", + "account.unmute_notifications": "不再對來自 @{name} 的通知消音", "account.view_full_profile": "查看完整資訊", - "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.message": "發生非預期的錯誤。", + "alert.unexpected.title": "哎呀!", "boost_modal.combo": "下次你可以按 {combo} 來跳過", "bundle_column_error.body": "加載本組件出錯。", "bundle_column_error.retry": "重試", @@ -40,11 +40,11 @@ "bundle_modal_error.retry": "重試", "column.blocks": "封鎖的使用者", "column.community": "本地時間軸", - "column.domain_blocks": "Hidden domains", + "column.domain_blocks": "隱藏域名", "column.favourites": "最愛", "column.follow_requests": "關注請求", "column.home": "家", - "column.lists": "Lists", + "column.lists": "名單", "column.mutes": "消音的使用者", "column.notifications": "通知", "column.pins": "置頂貼文", @@ -58,17 +58,17 @@ "column_header.unpin": "取下", "column_subheading.navigation": "瀏覽", "column_subheading.settings": "設定", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", - "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "compose_form.direct_message_warning": "此則推文只會被所有提到的使用者看見。", + "compose_form.hashtag_warning": "此則推文將不會在任何主題標籤中看見,只有公開的推文可以用主題標籤來搜尋。", "compose_form.lock_disclaimer": "你的帳號沒有{locked}。任何人都可以關注你,看到發給關注者的貼文。", "compose_form.lock_disclaimer.lock": "上鎖", "compose_form.placeholder": "在想些什麼?", "compose_form.publish": "貼掉", "compose_form.publish_loud": "{publish}!", - "compose_form.sensitive.marked": "Media is 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.unmarked": "Text is not hidden", + "compose_form.sensitive.marked": "此媒體已被標註為敏感的", + "compose_form.sensitive.unmarked": "此媒體未被標註為敏感的", + "compose_form.spoiler.marked": "文字隱藏在警告後", + "compose_form.spoiler.unmarked": "文字不是隱藏的", "compose_form.spoiler_placeholder": "內容警告", "confirmation_modal.cancel": "取消", "confirmations.block.confirm": "封鎖", @@ -86,17 +86,17 @@ "embed.instructions": "要內嵌此貼文,請將以下代碼貼進你的網站。", "embed.preview": "看上去會變成這樣:", "emoji_button.activity": "活動", - "emoji_button.custom": "Custom", + "emoji_button.custom": "自訂", "emoji_button.flags": "旗幟", - "emoji_button.food": "食物與飲料", + "emoji_button.food": "飲食", "emoji_button.label": "插入表情符號", "emoji_button.nature": "自然", - "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻", + "emoji_button.not_found": "沒有表情符號吼!! (╯°□°)╯︵ ┻━┻", "emoji_button.objects": "物件", "emoji_button.people": "人", - "emoji_button.recent": "Frequently used", + "emoji_button.recent": "常用", "emoji_button.search": "搜尋…", - "emoji_button.search_results": "Search results", + "emoji_button.search_results": "搜尋結果", "emoji_button.symbols": "符號", "emoji_button.travel": "旅遊與地點", "empty_column.community": "本地時間軸是空的。公開寫點什麼吧!", @@ -108,8 +108,8 @@ "empty_column.public": "這裡什麼都沒有!公開寫些什麼,或是關注其他副本的使用者。", "follow_request.authorize": "授權", "follow_request.reject": "拒絕", - "getting_started.appsshort": "Apps", - "getting_started.faq": "FAQ", + "getting_started.appsshort": "應用程式", + "getting_started.faq": "常見問答", "getting_started.heading": "馬上開始", "getting_started.open_source_notice": "Mastodon 是開源軟體。你可以在 GitHub {github} 上做出貢獻或是回報問題。", "getting_started.userguide": "使用者指南", @@ -119,19 +119,19 @@ "home.column_settings.show_reblogs": "顯示轉推", "home.column_settings.show_replies": "顯示回應", "home.settings": "欄位設定", - "keyboard_shortcuts.back": "to navigate back", - "keyboard_shortcuts.boost": "to boost", + "keyboard_shortcuts.back": "回到上一個", + "keyboard_shortcuts.boost": "到轉推", "keyboard_shortcuts.column": "to focus a status in one of the columns", - "keyboard_shortcuts.compose": "to focus the compose textarea", - "keyboard_shortcuts.description": "Description", + "keyboard_shortcuts.compose": "焦點移至撰寫文字區塊", + "keyboard_shortcuts.description": "描述", "keyboard_shortcuts.down": "to move down in the list", "keyboard_shortcuts.enter": "to open status", - "keyboard_shortcuts.favourite": "to favourite", - "keyboard_shortcuts.heading": "Keyboard Shortcuts", - "keyboard_shortcuts.hotkey": "Hotkey", + "keyboard_shortcuts.favourite": "收藏", + "keyboard_shortcuts.heading": "鍵盤快速鍵", + "keyboard_shortcuts.hotkey": "快速鍵", "keyboard_shortcuts.legend": "to display this legend", - "keyboard_shortcuts.mention": "to mention author", - "keyboard_shortcuts.reply": "to reply", + "keyboard_shortcuts.mention": "到提到的作者", + "keyboard_shortcuts.reply": "到回應", "keyboard_shortcuts.search": "to focus search", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", @@ -150,11 +150,11 @@ "loading_indicator.label": "讀取中...", "media_gallery.toggle_visible": "切換可見性", "missing_indicator.label": "找不到", - "missing_indicator.sublabel": "This resource could not be found", - "mute_modal.hide_notifications": "Hide notifications from this user?", + "missing_indicator.sublabel": "找不到此資源", + "mute_modal.hide_notifications": "隱藏來自這個使用者的通知?", "navigation_bar.blocks": "封鎖的使用者", "navigation_bar.community_timeline": "本地時間軸", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.domain_blocks": "隱藏的域名", "navigation_bar.edit_profile": "編輯用者資訊", "navigation_bar.favourites": "最愛", "navigation_bar.follow_requests": "關注請求", @@ -197,7 +197,7 @@ "onboarding.page_six.github": "Mastodon 是自由的開源軟體。你可以在 {github} 上回報臭蟲、請求新功能或是做出貢獻。", "onboarding.page_six.guidelines": "社群指南", "onboarding.page_six.read_guidelines": "請閱讀 {domain} 的 {guidelines} !", - "onboarding.page_six.various_app": "行動 apps", + "onboarding.page_six.various_app": "行動版應用程式", "onboarding.page_three.profile": "編輯你的大頭貼、自傳和顯示名稱。你也可以在這邊找到其他設定。", "onboarding.page_three.search": "利用搜尋列來找到其他人或是主題標籤,像是 {illustration} 或 {introductions} 。用完整的帳號名稱來找不在這個副本上的使用者。", "onboarding.page_two.compose": "在編輯欄寫些什麼。可以上傳圖片、改變隱私設定或是用下面的圖示加上內容警告。", @@ -211,13 +211,13 @@ "privacy.public.short": "公開貼", "privacy.unlisted.long": "不要貼到公開時間軸", "privacy.unlisted.short": "不列出來", - "regeneration_indicator.label": "Loading…", + "regeneration_indicator.label": "載入中…", "regeneration_indicator.sublabel": "Your home feed is being prepared!", - "relative_time.days": "{number}d", - "relative_time.hours": "{number}h", - "relative_time.just_now": "now", - "relative_time.minutes": "{number}m", - "relative_time.seconds": "{number}s", + "relative_time.days": "{number} 天", + "relative_time.hours": "{number} 時", + "relative_time.just_now": "現在", + "relative_time.minutes": "{number} 分", + "relative_time.seconds": "{number} 秒", "reply_indicator.cancel": "取消", "report.forward": "Forward to {target}", "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?", @@ -226,18 +226,18 @@ "report.submit": "送出", "report.target": "通報中", "search.placeholder": "搜尋", - "search_popout.search_format": "Advanced search format", + "search_popout.search_format": "進階搜尋格式", "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", - "search_popout.tips.hashtag": "hashtag", - "search_popout.tips.status": "status", + "search_popout.tips.hashtag": "主題標籤", + "search_popout.tips.status": "狀態", "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", - "search_popout.tips.user": "user", - "search_results.accounts": "People", - "search_results.hashtags": "Hashtags", - "search_results.statuses": "Toots", + "search_popout.tips.user": "使用者", + "search_results.accounts": "人", + "search_results.hashtags": "主題標籤", + "search_results.statuses": "推文", "search_results.total": "{count, number} 項結果", "standalone.public_title": "站點一瞥…", - "status.block": "Block @{name}", + "status.block": "封鎖 @{name}", "status.cannot_reblog": "此貼文無法轉推", "status.delete": "刪除", "status.embed": "Embed", @@ -250,7 +250,7 @@ "status.mute_conversation": "消音對話", "status.open": "展開這個狀態", "status.pin": "置頂到個人資訊頁", - "status.pinned": "Pinned toot", + "status.pinned": "置頂的推文", "status.reblog": "轉推", "status.reblogged_by": "{name} 轉推了", "status.reply": "回應", @@ -258,7 +258,7 @@ "status.report": "通報 @{name}", "status.sensitive_toggle": "點來看", "status.sensitive_warning": "敏感內容", - "status.share": "Share", + "status.share": "分享", "status.show_less": "看少點", "status.show_less_all": "Show less for all", "status.show_more": "看更多", @@ -269,17 +269,17 @@ "tabs_bar.home": "家", "tabs_bar.local_timeline": "本地", "tabs_bar.notifications": "通知", - "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", + "ui.beforeunload": "如果離開 Mastodon,你的草稿將會不見。", "upload_area.title": "拖放來上傳", "upload_button.label": "增加媒體", - "upload_form.description": "Describe for the visually impaired", - "upload_form.focus": "Crop", + "upload_form.description": "爲視障者加上描述", + "upload_form.focus": "裁切", "upload_form.undo": "復原", "upload_progress.label": "上傳中...", "video.close": "關閉影片", - "video.exit_fullscreen": "退出全熒幕", + "video.exit_fullscreen": "退出全螢幕", "video.expand": "展開影片", - "video.fullscreen": "全熒幕", + "video.fullscreen": "全螢幕", "video.hide": "隱藏影片", "video.mute": "消音", "video.pause": "暫停", From 7c43ed04fec0603073fe0b5a687992cab684a3b5 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sun, 15 Apr 2018 23:56:03 +0900 Subject: [PATCH 080/442] Weblate translations (2018-04-15) (#7141) * Translated using Weblate (Galician) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/ * Translated using Weblate (Dutch) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/ * Translated using Weblate (Catalan) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/ * Translated using Weblate (Arabic) Currently translated at 76.4% (449 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Japanese) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/ * Translated using Weblate (Slovak) Currently translated at 92.3% (542 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 92.3% (542 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 100.0% (58 of 58 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/sk/ * Translated using Weblate (Polish) Currently translated at 98.9% (581 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pl/ * Translated using Weblate (French) Currently translated at 99.6% (585 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/ * Translated using Weblate (Catalan) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/ * Translated using Weblate (Persian) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/fa/ * Translated using Weblate (Persian) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fa/ * 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 (Japanese) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/ * Translated using Weblate (Japanese) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/ * 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 (Chinese (Traditional)) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/zh_Hant/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Devise Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/zh_Hant/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/zh_Hant/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Devise Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/zh_Hant/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/eo/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/ * Translated using Weblate (Chinese (Traditional)) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/zh_Hant/ * Translated using Weblate (Arabic) Currently translated at 76.6% (450 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/eo/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/ * Translated using Weblate (Slovak) Currently translated at 92.6% (544 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/sk/ * Translated using Weblate (Arabic) Currently translated at 82.9% (487 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Arabic) Currently translated at 98.6% (74 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/ar/ * Translated using Weblate (Slovak) Currently translated at 93.6% (550 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 95.4% (560 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * 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 (Indonesian) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/id/ * Translated using Weblate (Korean) Currently translated at 100.0% (58 of 58 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ko/ * Translated using Weblate (Korean) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ko/ * Translated using Weblate (Korean) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ko/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/ * Translated using Weblate (Arabic) Currently translated at 82.9% (487 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Arabic) Currently translated at 99.2% (278 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/ * Translated using Weblate (Arabic) Currently translated at 87.3% (513 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Arabic) Currently translated at 99.6% (279 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/ * 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 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 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/sk/ * Translated using Weblate (Slovak) Currently translated at 100.0% (58 of 58 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/sk/ * Translated using Weblate (Finnish) Currently translated at 100.0% (58 of 58 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/fi/ * Translated using Weblate (Arabic) Currently translated at 88.0% (517 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * 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 (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 (Spanish) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/es/ * 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 (Finnish) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/fi/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/eo/ * Translated using Weblate (Slovak) Currently translated at 95.4% (560 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/ * 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 100.0% (62 of 62 strings) Translation: Mastodon/Devise Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/sk/ * Translated using Weblate (Finnish) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/fi/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (58 of 58 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/eo/ * Translated using Weblate (Slovak) Currently translated at 100.0% (58 of 58 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/sk/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/ * Translated using Weblate (Finnish) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Devise Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/fi/ * Translated using Weblate (Finnish) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Devise Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/fi/ * 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 (Finnish) Currently translated at 100.0% (58 of 58 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/fi/ * Translated using Weblate (Finnish) Currently translated at 25.8% (152 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fi/ * Translated using Weblate (Finnish) Currently translated at 25.8% (152 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fi/ * Translated using Weblate (Japanese) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/ * Translated using Weblate (Finnish) Currently translated at 33.0% (194 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fi/ * Translated using Weblate (Esperanto) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eo/ * Translated using Weblate (Finnish) Currently translated at 99.8% (586 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fi/ * Translated using Weblate (Slovak) Currently translated at 95.5% (561 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * 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 (Esperanto) Currently translated at 100.0% (587 of 587 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/eo/ * Translated using Weblate (Galician) Currently translated at 100.0% (280 of 280 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/ * yarn manage:translations --- app/javascript/mastodon/locales/ar.json | 2 + app/javascript/mastodon/locales/bg.json | 2 + app/javascript/mastodon/locales/ca.json | 2 + app/javascript/mastodon/locales/de.json | 2 + app/javascript/mastodon/locales/eo.json | 14 +- app/javascript/mastodon/locales/es.json | 2 + app/javascript/mastodon/locales/fa.json | 2 + app/javascript/mastodon/locales/fi.json | 228 +++--- app/javascript/mastodon/locales/fr.json | 2 + app/javascript/mastodon/locales/gl.json | 6 +- app/javascript/mastodon/locales/he.json | 2 + app/javascript/mastodon/locales/hr.json | 2 + app/javascript/mastodon/locales/hu.json | 2 + app/javascript/mastodon/locales/hy.json | 2 + app/javascript/mastodon/locales/id.json | 2 + app/javascript/mastodon/locales/io.json | 2 + app/javascript/mastodon/locales/it.json | 2 + app/javascript/mastodon/locales/ja.json | 2 +- app/javascript/mastodon/locales/ko.json | 2 + app/javascript/mastodon/locales/nl.json | 2 + app/javascript/mastodon/locales/no.json | 2 + app/javascript/mastodon/locales/oc.json | 2 + app/javascript/mastodon/locales/pt-BR.json | 2 + app/javascript/mastodon/locales/pt.json | 2 + app/javascript/mastodon/locales/ru.json | 2 + app/javascript/mastodon/locales/sk.json | 28 +- app/javascript/mastodon/locales/sr-Latn.json | 2 + app/javascript/mastodon/locales/sr.json | 2 + app/javascript/mastodon/locales/sv.json | 2 + app/javascript/mastodon/locales/th.json | 2 + app/javascript/mastodon/locales/tr.json | 2 + app/javascript/mastodon/locales/uk.json | 2 + app/javascript/mastodon/locales/zh-CN.json | 2 + app/javascript/mastodon/locales/zh-HK.json | 2 + app/javascript/mastodon/locales/zh-TW.json | 2 + config/locales/ar.yml | 4 + config/locales/devise.fi.yml | 91 ++- config/locales/devise.sk.yml | 10 +- config/locales/doorkeeper.fi.yml | 108 +-- config/locales/doorkeeper.sk.yml | 2 +- config/locales/eo.yml | 6 +- config/locales/es.yml | 13 +- config/locales/fi.yml | 698 ++++++++++++++++--- config/locales/ja.yml | 2 +- config/locales/simple_form.eo.yml | 4 +- config/locales/simple_form.fi.yml | 66 +- config/locales/simple_form.sk.yml | 16 +- config/locales/sk.yml | 27 +- 48 files changed, 1011 insertions(+), 374 deletions(-) diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index 34e34411f..c13ff773d 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "تعذرت ترقية هذا المنشور", "status.delete": "إحذف", + "status.direct": "Direct message @{name}", "status.embed": "إدماج", "status.favourite": "أضف إلى المفضلة", "status.load_more": "حمّل المزيد", @@ -269,6 +270,7 @@ "tabs_bar.home": "الرئيسية", "tabs_bar.local_timeline": "المحلي", "tabs_bar.notifications": "الإخطارات", + "tabs_bar.search": "Search", "ui.beforeunload": "سوف تفقد مسودتك إن تركت ماستدون.", "upload_area.title": "إسحب ثم أفلت للرفع", "upload_button.label": "إضافة وسائط", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 9aaff0ddf..981aced0b 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Изтриване", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Предпочитани", "status.load_more": "Load more", @@ -269,6 +270,7 @@ "tabs_bar.home": "Начало", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Известия", + "tabs_bar.search": "Search", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Drag & drop to upload", "upload_button.label": "Добави медия", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index b7f95a664..d9270e0ce 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Aquesta publicació no pot ser retootejada", "status.delete": "Esborrar", + "status.direct": "Direct message @{name}", "status.embed": "Incrustar", "status.favourite": "Favorit", "status.load_more": "Carrega més", @@ -269,6 +270,7 @@ "tabs_bar.home": "Inici", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notificacions", + "tabs_bar.search": "Search", "ui.beforeunload": "El vostre esborrany es perdrà si sortiu de Mastodon.", "upload_area.title": "Arrossega i deixa anar per carregar", "upload_button.label": "Afegir multimèdia", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index a618b853e..6eb5e6840 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden", "status.delete": "Löschen", + "status.direct": "Direct message @{name}", "status.embed": "Einbetten", "status.favourite": "Favorisieren", "status.load_more": "Weitere laden", @@ -269,6 +270,7 @@ "tabs_bar.home": "Startseite", "tabs_bar.local_timeline": "Lokal", "tabs_bar.notifications": "Mitteilungen", + "tabs_bar.search": "Search", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Zum Hochladen hereinziehen", "upload_button.label": "Mediendatei hinzufügen", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 6dee6e544..19f3c5907 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -60,7 +60,7 @@ "column_subheading.settings": "Agordado", "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", "compose_form.hashtag_warning": "Ĉi tiu mesaĝo ne estos listigita per ajna kradvorto. Nur publikaj mesaĝoj estas serĉeblaj per kradvortoj.", - "compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaĝojn nur por sekvantoj.", + "compose_form.lock_disclaimer": "Via konta ne estas {locked}. Iu ajn povas sekvi vin por vidi viajn mesaĝojn, kiuj estas nur por sekvantoj.", "compose_form.lock_disclaimer.lock": "ŝlosita", "compose_form.placeholder": "Pri kio vi pensas?", "compose_form.publish": "Hup", @@ -102,7 +102,7 @@ "empty_column.community": "La loka tempolinio estas malplena. Skribu ion por plenigi ĝin!", "empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.", "empty_column.home": "Via hejma tempolinio estas malplena! Vizitu {public} aŭ uzu la serĉilon por renkonti aliajn uzantojn.", - "empty_column.home.public_timeline": "la publika tempolinio", + "empty_column.home.public_timeline": "la publikan tempolinion", "empty_column.list": "Ankoraŭ estas nenio en ĉi tiu listo. Kiam membroj de ĉi tiu listo afiŝos novajn mesaĝojn, ili aperos ĉi tie.", "empty_column.notifications": "Vi ankoraŭ ne havas sciigojn. Interagu kun aliaj por komenci konversacion.", "empty_column.public": "Estas nenio ĉi tie! Publike skribu ion, aŭ mane sekvu uzantojn de aliaj nodoj por plenigi la publikan tempolinion", @@ -150,8 +150,8 @@ "loading_indicator.label": "Ŝargado…", "media_gallery.toggle_visible": "Baskuligi videblecon", "missing_indicator.label": "Ne trovita", - "missing_indicator.sublabel": "Ĉi tiu rimedo ne estis trovita", - "mute_modal.hide_notifications": "Ĉu kaŝi sciigojn el ĉi tiu uzanto?", + "missing_indicator.sublabel": "Ĉi tiu elemento ne estis trovita", + "mute_modal.hide_notifications": "Ĉu vi volas kaŝi la sciigojn el ĉi tiu uzanto?", "navigation_bar.blocks": "Blokitaj uzantoj", "navigation_bar.community_timeline": "Loka tempolinio", "navigation_bar.domain_blocks": "Hidden domains", @@ -240,6 +240,7 @@ "status.block": "Bloki @{name}", "status.cannot_reblog": "Ĉi tiu mesaĝo ne diskonigeblas", "status.delete": "Forigi", + "status.direct": "Direct message @{name}", "status.embed": "Enkorpigi", "status.favourite": "Stelumi", "status.load_more": "Ŝargi pli", @@ -248,8 +249,8 @@ "status.more": "Pli", "status.mute": "Silentigi @{name}", "status.mute_conversation": "Silentigi konversacion", - "status.open": "Grandigi ĉi tiun mesaĝon", - "status.pin": "Alpingli en la profilo", + "status.open": "Grandigi", + "status.pin": "Alpingli profile", "status.pinned": "Alpinglita mesaĝo", "status.reblog": "Diskonigi", "status.reblogged_by": "{name} diskonigis", @@ -269,6 +270,7 @@ "tabs_bar.home": "Hejmo", "tabs_bar.local_timeline": "Loka tempolinio", "tabs_bar.notifications": "Sciigoj", + "tabs_bar.search": "Search", "ui.beforeunload": "Via malneto perdiĝos se vi eliras de Mastodon.", "upload_area.title": "Altreni kaj lasi por alŝuti", "upload_button.label": "Aldoni aŭdovidaĵon", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 6f9c06c5f..e765cc078 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Este toot no puede retootearse", "status.delete": "Borrar", + "status.direct": "Direct message @{name}", "status.embed": "Incrustado", "status.favourite": "Favorito", "status.load_more": "Cargar más", @@ -269,6 +270,7 @@ "tabs_bar.home": "Inicio", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notificaciones", + "tabs_bar.search": "Search", "ui.beforeunload": "Tu borrador se perderá si sales de Mastodon.", "upload_area.title": "Arrastra y suelta para subir", "upload_button.label": "Subir multimedia", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 61cdcd00a..822c998ce 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید", "status.delete": "پاک‌کردن", + "status.direct": "Direct message @{name}", "status.embed": "جاگذاری", "status.favourite": "پسندیدن", "status.load_more": "بیشتر نشان بده", @@ -269,6 +270,7 @@ "tabs_bar.home": "خانه", "tabs_bar.local_timeline": "محلی", "tabs_bar.notifications": "اعلان‌ها", + "tabs_bar.search": "Search", "ui.beforeunload": "اگر از ماستدون خارج شوید پیش‌نویس شما پاک خواهد شد.", "upload_area.title": "برای بارگذاری به این‌جا بکشید", "upload_button.label": "افزودن تصویر", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index f4be80514..5763ac442 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -2,7 +2,7 @@ "account.block": "Estä @{name}", "account.block_domain": "Piilota kaikki sisältö verkkotunnuksesta {domain}", "account.blocked": "Estetty", - "account.direct": "Direct Message @{name}", + "account.direct": "Direct message @{name}", "account.disclaimer_full": "Alla olevat käyttäjän profiilitiedot saattavat olla epätäydellisiä.", "account.domain_blocked": "Verkko-osoite piilotettu", "account.edit_profile": "Muokkaa", @@ -17,37 +17,37 @@ "account.mute": "Mykistä @{name}", "account.mute_notifications": "Mykistä ilmoitukset käyttäjältä @{name}", "account.muted": "Mykistetty", - "account.posts": "Töötit", - "account.posts_with_replies": "Töötit ja vastaukset", - "account.report": "Report @{name}", - "account.requested": "Odottaa hyväksyntää. Klikkaa peruuttaaksesi seurauspyynnön", + "account.posts": "Tuuttaukset", + "account.posts_with_replies": "Tuuttaukset ja vastaukset", + "account.report": "Raportoi @{name}", + "account.requested": "Odottaa hyväksyntää. Peruuta seuraamispyyntö klikkaamalla", "account.share": "Jaa käyttäjän @{name} profiili", - "account.show_reblogs": "Näytä boostaukset käyttäjältä @{name}", + "account.show_reblogs": "Näytä buustaukset käyttäjältä @{name}", "account.unblock": "Salli @{name}", "account.unblock_domain": "Näytä {domain}", "account.unfollow": "Lakkaa seuraamasta", - "account.unmute": "Poista mykistys käyttäjältä @{name}", + "account.unmute": "Poista käyttäjän @{name} mykistys", "account.unmute_notifications": "Poista mykistys käyttäjän @{name} ilmoituksilta", "account.view_full_profile": "Näytä koko profiili", "alert.unexpected.message": "An unexpected error occurred.", "alert.unexpected.title": "Oops!", - "boost_modal.combo": "Voit painaa näppäimiä {combo} ohittaaksesi tämän ensi kerralla", - "bundle_column_error.body": "Jokin meni vikaan tätä komponenttia ladatessa.", + "boost_modal.combo": "Ensi kerralla voit ohittaa tämän painamalla {combo}", + "bundle_column_error.body": "Jokin meni vikaan komponenttia ladattaessa.", "bundle_column_error.retry": "Yritä uudestaan", - "bundle_column_error.title": "Network error", + "bundle_column_error.title": "Verkkovirhe", "bundle_modal_error.close": "Sulje", - "bundle_modal_error.message": "Jokin meni vikaan tätä komponenttia ladatessa.", + "bundle_modal_error.message": "Jokin meni vikaan komponenttia ladattaessa.", "bundle_modal_error.retry": "Yritä uudestaan", "column.blocks": "Estetyt käyttäjät", "column.community": "Paikallinen aikajana", "column.domain_blocks": "Hidden domains", "column.favourites": "Suosikit", - "column.follow_requests": "Seurauspyynnöt", + "column.follow_requests": "Seuraamispyynnöt", "column.home": "Koti", "column.lists": "Listat", "column.mutes": "Mykistetyt käyttäjät", "column.notifications": "Ilmoitukset", - "column.pins": "Pinned toot", + "column.pins": "Kiinnitetty tuuttaus", "column.public": "Yleinen aikajana", "column_back_button.label": "Takaisin", "column_header.hide_settings": "Piilota asetukset", @@ -59,32 +59,32 @@ "column_subheading.navigation": "Navigaatio", "column_subheading.settings": "Asetukset", "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", - "compose_form.hashtag_warning": "Tämä töötti ei tule näkymään hashtag-hauissa, koska se ei näy julkisilla aikajanoilla. Vain julkisia tööttejä voi hakea hashtageilla.", - "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille -postauksesi.", + "compose_form.hashtag_warning": "Tämä tuuttaus ei näy hashtag-hauissa, koska se on listaamaton. Hashtagien avulla voi hakea vain julkisia tuuttauksia.", + "compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille rajaamasi julkaisut.", "compose_form.lock_disclaimer.lock": "lukittu", - "compose_form.placeholder": "Mitä sinulla on mielessä?", - "compose_form.publish": "Toot", + "compose_form.placeholder": "Mitä mietit?", + "compose_form.publish": "Tuuttaa", "compose_form.publish_loud": "{publish}!", "compose_form.sensitive.marked": "Media on merkitty arkaluontoiseksi", "compose_form.sensitive.unmarked": "Mediaa ei ole merkitty arkaluontoiseksi", "compose_form.spoiler.marked": "Teksti on piilotettu varoituksen taakse", "compose_form.spoiler.unmarked": "Teksti ei ole piilotettu", - "compose_form.spoiler_placeholder": "Content warning", + "compose_form.spoiler_placeholder": "Sisältövaroitus", "confirmation_modal.cancel": "Peruuta", "confirmations.block.confirm": "Estä", - "confirmations.block.message": "Oletko varma, että haluat estää käyttäjän {name}?", - "confirmations.delete.confirm": "Delete", - "confirmations.delete.message": "Oletko varma, että haluat poistaa tämän statuspäivityksen?", - "confirmations.delete_list.confirm": "Delete", - "confirmations.delete_list.message": "Oletko varma, että haluat poistaa tämän listan pysyvästi?", + "confirmations.block.message": "Haluatko varmasti estää käyttäjän {name}?", + "confirmations.delete.confirm": "Poista", + "confirmations.delete.message": "Haluatko varmasti poistaa tämän tilapäivityksen?", + "confirmations.delete_list.confirm": "Poista", + "confirmations.delete_list.message": "Haluatko varmasti poistaa tämän listan kokonaan?", "confirmations.domain_block.confirm": "Piilota koko verkko-osoite", - "confirmations.domain_block.message": "Oletko aivan oikeasti varma että haluat estää koko verkko-osoitteen {domain}? Useimmissa tapauksissa muutamat kohdistetut estot ja mykistykset ovat riittäviä ja suositeltavampia.", + "confirmations.domain_block.message": "Haluatko aivan varmasti estää koko verkko-osoitteen {domain}? Useimmiten jokunen kohdistettu esto ja mykistys riittää, ja se on suositeltavampi tapa toimia.", "confirmations.mute.confirm": "Mykistä", - "confirmations.mute.message": "Oletko varma että haluat mykistää käyttäjän {name}?", + "confirmations.mute.message": "Haluatko varmasti mykistää käyttäjän {name}?", "confirmations.unfollow.confirm": "Lakkaa seuraamasta", - "confirmations.unfollow.message": "Oletko varma, että haluat lakata seuraamasta käyttäjää {name}?", - "embed.instructions": "Upota tämä statuspäivitys sivullesi kopioimalla alla oleva koodi.", - "embed.preview": "Tältä se tulee näyttämään:", + "confirmations.unfollow.message": "Haluatko varmasti lakata seuraamasta käyttäjää {name}?", + "embed.instructions": "Upota statuspäivitys sivullesi kopioimalla alla oleva koodi.", + "embed.preview": "Se tulee näyttämään tältä:", "emoji_button.activity": "Aktiviteetit", "emoji_button.custom": "Mukautetut", "emoji_button.flags": "Liput", @@ -92,154 +92,155 @@ "emoji_button.label": "Lisää emoji", "emoji_button.nature": "Luonto", "emoji_button.not_found": "Ei emojeja!! (╯°□°)╯︵ ┻━┻", - "emoji_button.objects": "Objektit", + "emoji_button.objects": "Esineet", "emoji_button.people": "Ihmiset", "emoji_button.recent": "Usein käytetyt", "emoji_button.search": "Etsi...", "emoji_button.search_results": "Hakutulokset", "emoji_button.symbols": "Symbolit", "emoji_button.travel": "Matkailu", - "empty_column.community": "Paikallinen aikajana on tyhjä. Kirjoita jotain julkista saadaksesi pyörät pyörimään!", - "empty_column.hashtag": "Tässä hashtagissa ei ole vielä mitään.", - "empty_column.home": "Kotiaikajanasi on tyhjä! Käy vierailemassa {public}ssa tai käytä hakutoimintoa aloittaaksesi ja tavataksesi muita käyttäjiä.", + "empty_column.community": "Paikallinen aikajana on tyhjä. Homma lähtee käyntiin, kun kirjoitat jotain julkista!", + "empty_column.hashtag": "Tällä hashtagilla ei ole vielä mitään.", + "empty_column.home": "Kotiaikajanasi on tyhjä! {public} ja hakutoiminto auttavat alkuun ja kohtaamaan muita käyttäjiä.", "empty_column.home.public_timeline": "yleinen aikajana", - "empty_column.list": "Tämä lista on vielä tyhjä. Kun listan jäsenet julkaisevat statuspäivityksiä, ne näkyvät tässä.", - "empty_column.notifications": "Sinulle ei ole vielä ilmoituksia. Juttele muille aloittaaksesi keskustelun.", - "empty_column.public": "Täällä ei ole mitään! Kirjoita jotain julkisesti, tai käy manuaalisesti seuraamassa käyttäjiä muista instansseista saadaksesi sisältöä", + "empty_column.list": "Lista on vielä tyhjä. Listan jäsenten julkaisemat tilapäivitykset tulevat tähän näkyviin.", + "empty_column.notifications": "Sinulle ei ole vielä ilmoituksia. Aloita keskustelu juttelemalla muille.", + "empty_column.public": "Täällä ei ole mitään! Saat sisältöä, kun kirjoitat jotain julkisesti tai käyt manuaalisesti seuraamassa muiden instanssien käyttäjiä", "follow_request.authorize": "Valtuuta", "follow_request.reject": "Hylkää", "getting_started.appsshort": "Sovellukset", "getting_started.faq": "FAQ", "getting_started.heading": "Aloitus", - "getting_started.open_source_notice": "Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHub palvelussa {github}.", + "getting_started.open_source_notice": "Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHubissa: {github}.", "getting_started.userguide": "Käyttöopas", - "home.column_settings.advanced": "Tarkemmat asetukset", + "home.column_settings.advanced": "Lisäasetukset", "home.column_settings.basic": "Perusasetukset", - "home.column_settings.filter_regex": "Suodata säännöllisten lauseiden avulla", + "home.column_settings.filter_regex": "Suodata säännöllisillä lausekkeilla", "home.column_settings.show_reblogs": "Näytä buustaukset", "home.column_settings.show_replies": "Näytä vastaukset", "home.settings": "Sarakeasetukset", - "keyboard_shortcuts.back": "liikkuaksesi taaksepäin", - "keyboard_shortcuts.boost": "buustataksesi", - "keyboard_shortcuts.column": "keskittääksesi statuspäivitykseen yhdessä sarakkeista", - "keyboard_shortcuts.compose": "aktivoidaksesi tekstinkirjoitusalueen", - "keyboard_shortcuts.description": "Description", - "keyboard_shortcuts.down": "liikkuaksesi listassa alaspäin", - "keyboard_shortcuts.enter": "to open status", - "keyboard_shortcuts.favourite": "tykätäksesi", - "keyboard_shortcuts.heading": "Näppäinoikotiet", + "keyboard_shortcuts.back": "liiku taaksepäin", + "keyboard_shortcuts.boost": "buustaa", + "keyboard_shortcuts.column": "siirrä fokus tietyn sarakkeen tilapäivitykseen", + "keyboard_shortcuts.compose": "siirry tekstinsyöttöön", + "keyboard_shortcuts.description": "Kuvaus", + "keyboard_shortcuts.down": "siirry listassa alaspäin", + "keyboard_shortcuts.enter": "avaa tilapäivitys", + "keyboard_shortcuts.favourite": "tykkää", + "keyboard_shortcuts.heading": "Näppäinkomennot", "keyboard_shortcuts.hotkey": "Pikanäppäin", - "keyboard_shortcuts.legend": "näyttääksesi tämän selitteen", - "keyboard_shortcuts.mention": "mainitaksesi julkaisijan", - "keyboard_shortcuts.reply": "vastataksesi", - "keyboard_shortcuts.search": "aktivoidaksesi hakukentän", - "keyboard_shortcuts.toot": "aloittaaksesi uuden töötin kirjoittamisen", - "keyboard_shortcuts.unfocus": "poistaaksesi aktivoinnin tekstikentästä/hakukentästä", - "keyboard_shortcuts.up": "liikkuaksesi listassa ylöspäin", + "keyboard_shortcuts.legend": "näytä tämä selite", + "keyboard_shortcuts.mention": "mainitse julkaisija", + "keyboard_shortcuts.reply": "vastaa", + "keyboard_shortcuts.search": "siirry hakukenttään", + "keyboard_shortcuts.toot": "ala kirjoittaa uutta tuuttausta", + "keyboard_shortcuts.unfocus": "siirry pois tekstikentästä tai hakukentästä", + "keyboard_shortcuts.up": "siirry listassa ylöspäin", "lightbox.close": "Sulje", "lightbox.next": "Seuraava", "lightbox.previous": "Edellinen", "lists.account.add": "Lisää listaan", - "lists.account.remove": "Poista listalta", - "lists.delete": "Delete list", + "lists.account.remove": "Poista listasta", + "lists.delete": "Poista lista", "lists.edit": "Muokkaa listaa", "lists.new.create": "Lisää lista", - "lists.new.title_placeholder": "Uuden listan otsikko", - "lists.search": "Etsi seuraamiesi henkilöiden joukosta", + "lists.new.title_placeholder": "Uuden listan nimi", + "lists.search": "Etsi seuraamistasi henkilöistä", "lists.subheading": "Omat listat", "loading_indicator.label": "Ladataan...", "media_gallery.toggle_visible": "Säädä näkyvyyttä", - "missing_indicator.label": "Ei löydetty", + "missing_indicator.label": "Ei löytynyt", "missing_indicator.sublabel": "Tätä resurssia ei löytynyt", - "mute_modal.hide_notifications": "Piilota ilmoitukset tältä käyttäjältä?", + "mute_modal.hide_notifications": "Piilota tältä käyttäjältä tulevat ilmoitukset?", "navigation_bar.blocks": "Estetyt käyttäjät", "navigation_bar.community_timeline": "Paikallinen aikajana", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Muokkaa profiilia", "navigation_bar.favourites": "Suosikit", - "navigation_bar.follow_requests": "Seurauspyynnöt", + "navigation_bar.follow_requests": "Seuraamispyynnöt", "navigation_bar.info": "Tietoa tästä instanssista", - "navigation_bar.keyboard_shortcuts": "Näppäinoikotiet", + "navigation_bar.keyboard_shortcuts": "Näppäinkomennot", "navigation_bar.lists": "Listat", "navigation_bar.logout": "Kirjaudu ulos", "navigation_bar.mutes": "Mykistetyt käyttäjät", - "navigation_bar.pins": "Kiinnitetyt töötit", - "navigation_bar.preferences": "Ominaisuudet", + "navigation_bar.pins": "Kiinnitetyt tuuttaukset", + "navigation_bar.preferences": "Asetukset", "navigation_bar.public_timeline": "Yleinen aikajana", - "notification.favourite": "{name} tykkäsi statuksestasi", + "notification.favourite": "{name} tykkäsi tilastasi", "notification.follow": "{name} seurasi sinua", "notification.mention": "{name} mainitsi sinut", - "notification.reblog": "{name} buustasi statustasi", + "notification.reblog": "{name} buustasi tilaasi", "notifications.clear": "Tyhjennä ilmoitukset", - "notifications.clear_confirmation": "Oletko varma, että haluat lopullisesti tyhjentää kaikki ilmoituksesi?", - "notifications.column_settings.alert": "Työpöytä ilmoitukset", - "notifications.column_settings.favourite": "Tykkäyksiä:", - "notifications.column_settings.follow": "Uusia seuraajia:", - "notifications.column_settings.mention": "Mainintoja:", + "notifications.clear_confirmation": "Haluatko varmasti poistaa kaikki ilmoitukset pysyvästi?", + "notifications.column_settings.alert": "Työpöytäilmoitukset", + "notifications.column_settings.favourite": "Tykkäykset:", + "notifications.column_settings.follow": "Uudet seuraajat:", + "notifications.column_settings.mention": "Maininnat:", "notifications.column_settings.push": "Push-ilmoitukset", "notifications.column_settings.push_meta": "Tämä laite", - "notifications.column_settings.reblog": "Buusteja:", + "notifications.column_settings.reblog": "Buustit:", "notifications.column_settings.show": "Näytä sarakkeessa", - "notifications.column_settings.sound": "Soita ääni", + "notifications.column_settings.sound": "Äänimerkki", "onboarding.done": "Valmis", "onboarding.next": "Seuraava", - "onboarding.page_five.public_timelines": "Paikallinen aikajana näyttää kaikki julkiset julkaisut kaikilta, jotka ovat verkko-osoitteessa {domain}. Yleinen aikajana näyttää julkiset julkaisut kaikilta niiltä, joita käyttäjät verkko-osoitteessa {domain} seuraavat. Nämä ovat julkiset aikajanat, ja ne ovat hyviä tapoja löytää uusia ihmisiä.", - "onboarding.page_four.home": "Kotiaikajana näyttää julkaisut ihmisiltä joita seuraat.", - "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_five.public_timelines": "Paikallisella aikajanalla näytetään instanssin {domain} kaikkien käyttäjien julkiset julkaisut. Yleisellä aikajanalla näytetään kaikkien instanssin {domain} käyttäjien seuraamien käyttäjien julkiset julkaisut. Nämä julkiset aikajanat ovat loistavia paikkoja löytää uusia ihmisiä.", + "onboarding.page_four.home": "Kotiaikajanalla näytetään seuraamiesi ihmisten julkaisut.", + "onboarding.page_four.notifications": "Ilmoitukset-sarakkeessa näytetään muiden sinuun liittyvä toiminta.", + "onboarding.page_one.federation": "Mastodon on usean itsenäisen palvelimen muodostama yhteisöpalvelu. Näitä palvelimia kutsutaan instansseiksi.", "onboarding.page_one.full_handle": "Koko käyttäjänimesi", - "onboarding.page_one.handle_hint": "Tämä on se, mitä voisit ehdottaa ystäviäsi etsimään.", + "onboarding.page_one.handle_hint": "Tällä nimellä ystäväsi löytävät sinut.", "onboarding.page_one.welcome": "Tervetuloa Mastodoniin!", - "onboarding.page_six.admin": "Instanssisi ylläpitäjä on {admin}.", + "onboarding.page_six.admin": "Instanssin ylläpitäjä on {admin}.", "onboarding.page_six.almost_done": "Melkein valmista...", - "onboarding.page_six.appetoot": "Bon Appetööt!", + "onboarding.page_six.appetoot": "Tuuttailun iloa!", "onboarding.page_six.apps_available": "{apps} on saatavilla iOS:lle, Androidille ja muille alustoille.", - "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.github": "Mastodon on ilmainen, vapaan lähdekoodin ohjelma. Voit raportoida bugeja, ehdottaa ominaisuuksia tai osallistua kehittämiseen GitHubissa: {github}.", "onboarding.page_six.guidelines": "yhteisön säännöt", "onboarding.page_six.read_guidelines": "Ole hyvä ja lue {domain}:n {guidelines}!", "onboarding.page_six.various_app": "mobiilisovellukset", - "onboarding.page_three.profile": "Muokkaa profiiliasi muuttaaksesi kuvakettasi, esittelyäsi ja nimimerkkiäsi. Löydät sieltä myös muita henkilökohtaisia asetuksia.", - "onboarding.page_three.search": "Käytä hakukenttää löytääksesi ihmisiä ja etsiäksesi hashtageja, kuten {illustration} tai {introductions}. Hakeaksesi henkilöä joka on toisessa instanssissa, käytä hänen käyttäjänimeään kokonaisuudessaan.", - "onboarding.page_two.compose": "Kirjoita postauksia kirjoita-sarakkeessa. Voit ladata kuvia, vaihtaa yksityisyysasetuksia ja lisätä sisältövaroituksia alla olevista painikkeista.", + "onboarding.page_three.profile": "Voit muuttaa profiilikuvaasi, esittelyäsi ja nimimerkkiäsi sekä muita asetuksia muokkaamalla profiiliasi.", + "onboarding.page_three.search": "Etsi ihmisiä ja hashtageja (esimerkiksi {illustration} tai {introductions}) hakukentän avulla. Jos haet toista instanssia käyttävää henkilöä, käytä hänen koko käyttäjänimeään.", + "onboarding.page_two.compose": "Kirjoita julkaisuja kirjoitussarakkeessa. Voit ladata kuvia, vaihtaa näkyvyysasetuksia ja lisätä sisältövaroituksia alla olevista painikkeista.", "onboarding.skip": "Ohita", - "privacy.change": "Säädä töötin yksityisyysasetuksia", + "privacy.change": "Säädä tuuttauksen näkyvyyttä", "privacy.direct.long": "Julkaise vain mainituille käyttäjille", - "privacy.direct.short": "Yksityisviesti", + "privacy.direct.short": "Suora viesti", "privacy.private.long": "Julkaise vain seuraajille", "privacy.private.short": "Vain seuraajat", "privacy.public.long": "Julkaise julkisille aikajanoille", "privacy.public.short": "Julkinen", - "privacy.unlisted.long": "Älä julkaise yleisillä aikajanoilla", - "privacy.unlisted.short": "Julkinen, mutta älä näytä julkisella aikajanalla", + "privacy.unlisted.long": "Älä julkaise julkisilla aikajanoilla", + "privacy.unlisted.short": "Listaamaton julkinen", "regeneration_indicator.label": "Ladataan…", "regeneration_indicator.sublabel": "Kotinäkymääsi valmistellaan!", - "relative_time.days": "{number}d", - "relative_time.hours": "{number}h", + "relative_time.days": "{number} pv", + "relative_time.hours": "{number} h", "relative_time.just_now": "nyt", - "relative_time.minutes": "{number}m", - "relative_time.seconds": "{number}s", + "relative_time.minutes": "{number} m", + "relative_time.seconds": "{number} s", "reply_indicator.cancel": "Peruuta", - "report.forward": "Uudelleenohjaa kohteeseen {target}", - "report.forward_hint": "Tämä tili on toiselta serveriltä. Haluatko, että myös sinne lähetetään anonymisoitu kopio ilmiantoraportista?", - "report.hint": "Ilmianto lähetetään instanssisi moderaattoreille. Voit antaa kuvauksen käyttäjän ilmiantamisen syystä alle:", + "report.forward": "Välitä kohteeseen {target}", + "report.forward_hint": "Tämä tili on toisella palvelimella. Haluatko lähettää nimettömän raportin myös sinne?", + "report.hint": "Raportti lähetetään oman instanssisi moderaattoreille. Seuraavassa voit kertoa, miksi raportoit tästä tilistä:", "report.placeholder": "Lisäkommentit", - "report.submit": "Submit", - "report.target": "Reporting", + "report.submit": "Lähetä", + "report.target": "Raportoidaan {target}", "search.placeholder": "Hae", "search_popout.search_format": "Tarkennettu haku", - "search_popout.tips.full_text": "Tekstihaku palauttaa statuspäivitykset jotka olet kirjoittanut, lisännyt suosikkeihisi, boostannut tai joissa sinut mainitaan, sekä käyttäjänimet, nimimerkit ja hastagit jotka sisältävät tekstin.", - "search_popout.tips.hashtag": "hashtagi", - "search_popout.tips.status": "status", - "search_popout.tips.text": "Pelkkä tekstihaku palauttaa hakua vastaavat nimimerkit, käyttäjänimet ja hastagit", + "search_popout.tips.full_text": "Tekstihaku palauttaa tilapäivitykset, jotka olet kirjoittanut, lisännyt suosikkeihisi, boostannut tai joissa sinut mainitaan, sekä tekstin sisältävät käyttäjänimet, nimimerkit ja hastagit.", + "search_popout.tips.hashtag": "hashtag", + "search_popout.tips.status": "tila", + "search_popout.tips.text": "Tekstihaku palauttaa hakua vastaavat nimimerkit, käyttäjänimet ja hastagit", "search_popout.tips.user": "käyttäjä", "search_results.accounts": "Ihmiset", "search_results.hashtags": "Hashtagit", - "search_results.statuses": "Töötit", + "search_results.statuses": "Tuuttaukset", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "Kurkistus sisälle...", - "status.block": "Block @{name}", - "status.cannot_reblog": "Tätä postausta ei voi buustata", + "status.block": "Estä @{name}", + "status.cannot_reblog": "Tätä julkaisua ei voi buustata", "status.delete": "Poista", + "status.direct": "Direct message @{name}", "status.embed": "Upota", "status.favourite": "Tykkää", "status.load_more": "Lataa lisää", @@ -248,29 +249,30 @@ "status.more": "Lisää", "status.mute": "Mykistä @{name}", "status.mute_conversation": "Mykistä keskustelu", - "status.open": "Laajenna statuspäivitys", + "status.open": "Laajenna tilapäivitys", "status.pin": "Kiinnitä profiiliin", - "status.pinned": "Kiinnitetty töötti", + "status.pinned": "Kiinnitetty tuuttaus", "status.reblog": "Buustaa", "status.reblogged_by": "{name} buustasi", "status.reply": "Vastaa", "status.replyAll": "Vastaa ketjuun", - "status.report": "Report @{name}", + "status.report": "Raportoi @{name}", "status.sensitive_toggle": "Klikkaa nähdäksesi", "status.sensitive_warning": "Arkaluontoista sisältöä", "status.share": "Jaa", "status.show_less": "Näytä vähemmän", "status.show_less_all": "Näytä vähemmän kaikista", "status.show_more": "Näytä lisää", - "status.show_more_all": "Näytä enemmän kaikista", - "status.unmute_conversation": "Poista mykistys keskustelulta", + "status.show_more_all": "Näytä lisää kaikista", + "status.unmute_conversation": "Poista keskustelun mykistys", "status.unpin": "Irrota profiilista", "tabs_bar.federated_timeline": "Yleinen", "tabs_bar.home": "Koti", "tabs_bar.local_timeline": "Paikallinen", "tabs_bar.notifications": "Ilmoitukset", - "ui.beforeunload": "Luonnoksesi menetetään, jos poistut Mastodonista.", - "upload_area.title": "Raahaa ja pudota tähän ladataksesi", + "tabs_bar.search": "Search", + "ui.beforeunload": "Luonnos häviää, jos poistut Mastodonista.", + "upload_area.title": "Lataa raahaamalla ja pudottamalla tähän", "upload_button.label": "Lisää mediaa", "upload_form.description": "Anna kuvaus näkörajoitteisia varten", "upload_form.focus": "Rajaa", @@ -279,10 +281,10 @@ "video.close": "Sulje video", "video.exit_fullscreen": "Poistu koko näytön tilasta", "video.expand": "Laajenna video", - "video.fullscreen": "Full screen", + "video.fullscreen": "Koko näyttö", "video.hide": "Piilota video", "video.mute": "Mykistä ääni", "video.pause": "Keskeytä", "video.play": "Toista", - "video.unmute": "Poista mykistys ääneltä" + "video.unmute": "Poista äänen mykistys" } diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 57c55c9bd..e340fda9e 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Cette publication ne peut être boostée", "status.delete": "Effacer", + "status.direct": "Direct message @{name}", "status.embed": "Intégrer", "status.favourite": "Ajouter aux favoris", "status.load_more": "Charger plus", @@ -269,6 +270,7 @@ "tabs_bar.home": "Accueil", "tabs_bar.local_timeline": "Fil public local", "tabs_bar.notifications": "Notifications", + "tabs_bar.search": "Search", "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.", "upload_area.title": "Glissez et déposez pour envoyer", "upload_button.label": "Joindre un média", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 8d586404d..5cbb7d31d 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -2,7 +2,7 @@ "account.block": "Bloquear @{name}", "account.block_domain": "Ocultar calquer contido de {domain}", "account.blocked": "Blocked", - "account.direct": "Direct Message @{name}", + "account.direct": "Direct message @{name}", "account.disclaimer_full": "A información inferior podería mostrar un perfil incompleto da usuaria.", "account.domain_blocked": "Domain hidden", "account.edit_profile": "Editar perfil", @@ -148,7 +148,7 @@ "lists.search": "Procurar entre a xente que segues", "lists.subheading": "As túas listas", "loading_indicator.label": "Cargando...", - "media_gallery.toggle_visible": "Dar visibilidade", + "media_gallery.toggle_visible": "Ocultar", "missing_indicator.label": "Non atopado", "missing_indicator.sublabel": "This resource could not be found", "mute_modal.hide_notifications": "Esconder notificacións deste usuario?", @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Esta mensaxe non pode ser promocionada", "status.delete": "Eliminar", + "status.direct": "Direct message @{name}", "status.embed": "Incrustar", "status.favourite": "Favorita", "status.load_more": "Cargar máis", @@ -269,6 +270,7 @@ "tabs_bar.home": "Inicio", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notificacións", + "tabs_bar.search": "Search", "ui.beforeunload": "O borrador perderase se sae de Mastodon.", "upload_area.title": "Arrastre e solte para subir", "upload_button.label": "Engadir medios", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 6bec26fd8..656d93cdf 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "לא ניתן להדהד הודעה זו", "status.delete": "מחיקה", + "status.direct": "Direct message @{name}", "status.embed": "הטמעה", "status.favourite": "חיבוב", "status.load_more": "עוד", @@ -269,6 +270,7 @@ "tabs_bar.home": "בבית", "tabs_bar.local_timeline": "ציר זמן מקומי", "tabs_bar.notifications": "התראות", + "tabs_bar.search": "Search", "ui.beforeunload": "הטיוטא תאבד אם תעזבו את מסטודון.", "upload_area.title": "ניתן להעלות על ידי Drag & drop", "upload_button.label": "הוספת מדיה", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index f7a5d0a3f..2d7d0a5a4 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Ovaj post ne može biti boostan", "status.delete": "Obriši", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Označi omiljenim", "status.load_more": "Učitaj više", @@ -269,6 +270,7 @@ "tabs_bar.home": "Dom", "tabs_bar.local_timeline": "Lokalno", "tabs_bar.notifications": "Notifikacije", + "tabs_bar.search": "Search", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Povuci i spusti kako bi uploadao", "upload_button.label": "Dodaj media", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 8b9c14993..24f3a7816 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Ezen státusz nem rebloggolható", "status.delete": "Törlés", + "status.direct": "Direct message @{name}", "status.embed": "Beágyaz", "status.favourite": "Kedvenc", "status.load_more": "Többet", @@ -269,6 +270,7 @@ "tabs_bar.home": "Kezdőlap", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Értesítések", + "tabs_bar.search": "Search", "ui.beforeunload": "A piszkozata el fog vesztődni ha elhagyja Mastodon-t.", "upload_area.title": "Húzza ide a feltöltéshez", "upload_button.label": "Média hozzáadása", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index 22ba89a43..2ba52c5c0 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -240,6 +240,7 @@ "status.block": "Արգելափակել @{name}֊ին", "status.cannot_reblog": "Այս թութը չի կարող տարածվել", "status.delete": "Ջնջել", + "status.direct": "Direct message @{name}", "status.embed": "Ներդնել", "status.favourite": "Հավանել", "status.load_more": "Բեռնել ավելին", @@ -269,6 +270,7 @@ "tabs_bar.home": "Հիմնական", "tabs_bar.local_timeline": "Տեղական", "tabs_bar.notifications": "Ծանուցումներ", + "tabs_bar.search": "Search", "ui.beforeunload": "Քո սեւագիրը կկորի, եթե լքես Մաստոդոնը։", "upload_area.title": "Քաշիր ու նետիր՝ վերբեռնելու համար", "upload_button.label": "Ավելացնել մեդիա", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 1ef610fba..e1518c1aa 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Hapus", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Difavoritkan", "status.load_more": "Tampilkan semua", @@ -269,6 +270,7 @@ "tabs_bar.home": "Beranda", "tabs_bar.local_timeline": "Lokal", "tabs_bar.notifications": "Notifikasi", + "tabs_bar.search": "Search", "ui.beforeunload": "Naskah anda akan hilang jika anda keluar dari Mastodon.", "upload_area.title": "Seret & lepaskan untuk mengunggah", "upload_button.label": "Tambahkan media", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 1435178a8..c79d4a634 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Efacar", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Favorizar", "status.load_more": "Kargar pluse", @@ -269,6 +270,7 @@ "tabs_bar.home": "Hemo", "tabs_bar.local_timeline": "Lokala", "tabs_bar.notifications": "Savigi", + "tabs_bar.search": "Search", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Tranar faligar por kargar", "upload_button.label": "Adjuntar kontenajo", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 226127e6b..3c85a3e20 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Elimina", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Apprezzato", "status.load_more": "Mostra di più", @@ -269,6 +270,7 @@ "tabs_bar.home": "Home", "tabs_bar.local_timeline": "Locale", "tabs_bar.notifications": "Notifiche", + "tabs_bar.search": "Search", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Trascina per caricare", "upload_button.label": "Aggiungi file multimediale", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index a9beaa335..2da9192ca 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -186,7 +186,7 @@ "onboarding.page_five.public_timelines": "連合タイムラインでは{domain}の人がフォローしているMastodon全体での公開投稿を表示します。同じくローカルタイムラインでは{domain}のみの公開投稿を表示します。", "onboarding.page_four.home": "「ホーム」タイムラインではあなたがフォローしている人の投稿を表示します。", "onboarding.page_four.notifications": "「通知」ではあなたへの他の人からの関わりを表示します。", - "onboarding.page_one.federation": "Mastodonは誰でも参加できるSNSです。", + "onboarding.page_one.federation": "Mastodonは独立したインスタンス(サーバー)の集合体です。", "onboarding.page_one.full_handle": "あなたのフルハンドル", "onboarding.page_one.handle_hint": "あなたを探している友達に伝えるといいでしょう。", "onboarding.page_one.welcome": "Mastodonへようこそ!", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 89fde8966..e2fadff3c 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -240,6 +240,7 @@ "status.block": "@{name} 차단", "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다", "status.delete": "삭제", + "status.direct": "Direct message @{name}", "status.embed": "공유하기", "status.favourite": "즐겨찾기", "status.load_more": "더 보기", @@ -269,6 +270,7 @@ "tabs_bar.home": "홈", "tabs_bar.local_timeline": "로컬", "tabs_bar.notifications": "알림", + "tabs_bar.search": "Search", "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.", "upload_area.title": "드래그 & 드롭으로 업로드", "upload_button.label": "미디어 추가", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 7bfb74a5c..0222432a0 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -240,6 +240,7 @@ "status.block": "Blokkeer @{name}", "status.cannot_reblog": "Deze toot kan niet geboost worden", "status.delete": "Verwijderen", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Favoriet", "status.load_more": "Meer laden", @@ -269,6 +270,7 @@ "tabs_bar.home": "Start", "tabs_bar.local_timeline": "Lokaal", "tabs_bar.notifications": "Meldingen", + "tabs_bar.search": "Search", "ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.", "upload_area.title": "Hierin slepen om te uploaden", "upload_button.label": "Media toevoegen", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index b79277ce3..20b2cbb26 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Denne posten kan ikke fremheves", "status.delete": "Slett", + "status.direct": "Direct message @{name}", "status.embed": "Bygge inn", "status.favourite": "Lik", "status.load_more": "Last mer", @@ -269,6 +270,7 @@ "tabs_bar.home": "Hjem", "tabs_bar.local_timeline": "Lokal", "tabs_bar.notifications": "Varslinger", + "tabs_bar.search": "Search", "ui.beforeunload": "Din kladd vil bli forkastet om du forlater Mastodon.", "upload_area.title": "Dra og slipp for å laste opp", "upload_button.label": "Legg til media", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 5b12f8811..32133c1f4 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -240,6 +240,7 @@ "status.block": "Blocar @{name}", "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat", "status.delete": "Escafar", + "status.direct": "Direct message @{name}", "status.embed": "Embarcar", "status.favourite": "Apondre als favorits", "status.load_more": "Cargar mai", @@ -269,6 +270,7 @@ "tabs_bar.home": "Acuèlh", "tabs_bar.local_timeline": "Flux public local", "tabs_bar.notifications": "Notificacions", + "tabs_bar.search": "Search", "ui.beforeunload": "Vòstre brolhon serà perdut se quitatz Mastodon.", "upload_area.title": "Lisatz e depausatz per mandar", "upload_button.label": "Ajustar un mèdia", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 4cd2e0643..b4be0bbc7 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Esta postagem não pode ser compartilhada", "status.delete": "Excluir", + "status.direct": "Direct message @{name}", "status.embed": "Incorporar", "status.favourite": "Adicionar aos favoritos", "status.load_more": "Carregar mais", @@ -269,6 +270,7 @@ "tabs_bar.home": "Página inicial", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notificações", + "tabs_bar.search": "Search", "ui.beforeunload": "Seu rascunho será perdido se você sair do Mastodon.", "upload_area.title": "Arraste e solte para enviar", "upload_button.label": "Adicionar mídia", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index 7a404eaba..132de5293 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Este post não pode ser partilhado", "status.delete": "Eliminar", + "status.direct": "Direct message @{name}", "status.embed": "Incorporar", "status.favourite": "Adicionar aos favoritos", "status.load_more": "Carregar mais", @@ -269,6 +270,7 @@ "tabs_bar.home": "Home", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notificações", + "tabs_bar.search": "Search", "ui.beforeunload": "O teu rascunho vai ser perdido se abandonares o Mastodon.", "upload_area.title": "Arraste e solte para enviar", "upload_button.label": "Adicionar media", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index 8616ef98f..b56ccf1df 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -240,6 +240,7 @@ "status.block": "Заблокировать @{name}", "status.cannot_reblog": "Этот статус не может быть продвинут", "status.delete": "Удалить", + "status.direct": "Direct message @{name}", "status.embed": "Встроить", "status.favourite": "Нравится", "status.load_more": "Показать еще", @@ -269,6 +270,7 @@ "tabs_bar.home": "Главная", "tabs_bar.local_timeline": "Локальная", "tabs_bar.notifications": "Уведомления", + "tabs_bar.search": "Search", "ui.beforeunload": "Ваш черновик будет утерян, если вы покинете Mastodon.", "upload_area.title": "Перетащите сюда, чтобы загрузить", "upload_button.label": "Добавить медиаконтент", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 925b46df6..159315137 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -8,11 +8,11 @@ "account.edit_profile": "Upraviť profil", "account.follow": "Následovať", "account.followers": "Sledujúci", - "account.follows": "Sledujete", + "account.follows": "Následuje", "account.follows_you": "Následuje ťa", "account.hide_reblogs": "Skryť povýšenia od @{name}", "account.media": "Médiá", - "account.mention": "Spomeňte @{name}", + "account.mention": "Spomeň @{name}", "account.moved_to": "{name} sa presunul/a na:", "account.mute": "Ignorovať @{name}", "account.mute_notifications": "Stĺmiť notifikácie od @{name}", @@ -22,7 +22,7 @@ "account.report": "Nahlás @{name}", "account.requested": "Čaká na schválenie. Kliknite pre zrušenie žiadosti", "account.share": "Zdieľať @{name} profil", - "account.show_reblogs": "Zobraziť povýšenia od @{name}", + "account.show_reblogs": "Ukáž povýšenia od @{name}", "account.unblock": "Odblokovať @{name}", "account.unblock_domain": "Prestať blokovať {domain}", "account.unfollow": "Prestať nasledovať", @@ -83,7 +83,7 @@ "confirmations.mute.message": "Naozaj chcete ignorovať {name}?", "confirmations.unfollow.confirm": "Nesledovať", "confirmations.unfollow.message": "Naozaj chcete prestať sledovať {name}?", - "embed.instructions": "Umiestnite kód uvedený nižšie pre pridanie tohto statusu na vašu web stránku.", + "embed.instructions": "Umiestni kód uvedený nižšie pre pridanie tohto statusu na tvoju web stránku.", "embed.preview": "Tu je ako to bude vyzerať:", "emoji_button.activity": "Aktivita", "emoji_button.custom": "Vlastné", @@ -111,13 +111,13 @@ "getting_started.appsshort": "Aplikácie", "getting_started.faq": "Časté otázky", "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 prispieť môžeš na GitHube v {github}.", "getting_started.userguide": "Používateľská príručka", "home.column_settings.advanced": "Pokročilé", "home.column_settings.basic": "Základné", "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_replies": "Zobraziť odpovede", + "home.column_settings.show_replies": "Ukázať odpovede", "home.settings": "Nastavenia stĺpcov", "keyboard_shortcuts.back": "dostať sa naspäť", "keyboard_shortcuts.boost": "vyzdvihnúť", @@ -169,7 +169,7 @@ "notification.favourite": "{name} sa páči tvoj status", "notification.follow": "{name} ťa začal/a následovať", "notification.mention": "{name} ťa spomenul/a", - "notification.reblog": "{name} re-tootol tvoj status", + "notification.reblog": "{name} zdieľal/a tvoj status", "notifications.clear": "Vyčistiť zoznam notifikácii", "notifications.clear_confirmation": "Naozaj chcete nenávratne prečistiť všetky vaše notifikácie?", "notifications.column_settings.alert": "Notifikácie na ploche", @@ -235,24 +235,25 @@ "search_results.accounts": "Ľudia", "search_results.hashtags": "Haštagy", "search_results.statuses": "Príspevky", - "search_results.total": "{count, number} {count, plural, one {result} ostatné {results}}", - "standalone.public_title": "Pohľad dovnútra...", + "search_results.total": "{count, number} {count, plural, jeden {výsledok} ostatné {výsledky}}", + "standalone.public_title": "Náhľad dovnútra...", "status.block": "Blokovať @{name}", "status.cannot_reblog": "Tento príspevok nemôže byť re-tootnutý", "status.delete": "Zmazať", + "status.direct": "Direct message @{name}", "status.embed": "Vložiť", "status.favourite": "Páči sa mi", "status.load_more": "Zobraz viac", "status.media_hidden": "Skryté médiá", - "status.mention": "Napísať @{name}", + "status.mention": "Spomeň @{name}", "status.more": "Viac", "status.mute": "Utíšiť @{name}", "status.mute_conversation": "Ignorovať konverzáciu", "status.open": "Otvoriť tento status", - "status.pin": "Pripnúť na profil", + "status.pin": "Pripni na profil", "status.pinned": "Pripnutý príspevok", "status.reblog": "Povýšiť", - "status.reblogged_by": "{name} povýšil", + "status.reblogged_by": "{name} povýšil/a", "status.reply": "Odpovedať", "status.replyAll": "Odpovedať na diskusiu", "status.report": "Nahlásiť @{name}", @@ -261,7 +262,7 @@ "status.share": "Zdieľať", "status.show_less": "Zobraz menej", "status.show_less_all": "Všetkým ukáž menej", - "status.show_more": "Zobraz viac", + "status.show_more": "Zobraziť viac", "status.show_more_all": "Všetkým ukáž viac", "status.unmute_conversation": "Prestať ignorovať konverzáciu", "status.unpin": "Odopnúť z profilu", @@ -269,6 +270,7 @@ "tabs_bar.home": "Domov", "tabs_bar.local_timeline": "Lokálna", "tabs_bar.notifications": "Notifikácie", + "tabs_bar.search": "Search", "ui.beforeunload": "Čo máte rozpísané sa stratí, ak opustíte Mastodon.", "upload_area.title": "Ťahaj a pusti pre nahratie", "upload_button.label": "Pridať médiá", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index 8fae49a03..69c7aa62b 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Ovaj status ne može da se podrži", "status.delete": "Obriši", + "status.direct": "Direct message @{name}", "status.embed": "Ugradi na sajt", "status.favourite": "Omiljeno", "status.load_more": "Učitaj još", @@ -269,6 +270,7 @@ "tabs_bar.home": "Početna", "tabs_bar.local_timeline": "Lokalno", "tabs_bar.notifications": "Obaveštenja", + "tabs_bar.search": "Search", "ui.beforeunload": "Ako napustite Mastodont, izgubićete napisani nacrt.", "upload_area.title": "Prevucite ovde da otpremite", "upload_button.label": "Dodaj multimediju", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index a39fea582..e9739451e 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Овај статус не може да се подржи", "status.delete": "Обриши", + "status.direct": "Direct message @{name}", "status.embed": "Угради на сајт", "status.favourite": "Омиљено", "status.load_more": "Учитај још", @@ -269,6 +270,7 @@ "tabs_bar.home": "Почетна", "tabs_bar.local_timeline": "Локално", "tabs_bar.notifications": "Обавештења", + "tabs_bar.search": "Search", "ui.beforeunload": "Ако напустите Мастодонт, изгубићете написани нацрт.", "upload_area.title": "Превуците овде да отпремите", "upload_button.label": "Додај мултимедију", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 014492e19..b063adb0f 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Detta inlägg kan inte knuffas", "status.delete": "Ta bort", + "status.direct": "Direct message @{name}", "status.embed": "Bädda in", "status.favourite": "Favorit", "status.load_more": "Ladda fler", @@ -269,6 +270,7 @@ "tabs_bar.home": "Hem", "tabs_bar.local_timeline": "Lokal", "tabs_bar.notifications": "Meddelanden", + "tabs_bar.search": "Search", "ui.beforeunload": "Ditt utkast kommer att förloras om du lämnar Mastodon.", "upload_area.title": "Dra & släpp för att ladda upp", "upload_button.label": "Lägg till media", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index ecfe7c9b5..22a75c237 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Delete", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Favourite", "status.load_more": "Load more", @@ -269,6 +270,7 @@ "tabs_bar.home": "Home", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notifications", + "tabs_bar.search": "Search", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Drag & drop to upload", "upload_button.label": "Add media", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 58d0e5785..8e36c512f 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Bu gönderi boost edilemez", "status.delete": "Sil", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Favorilere ekle", "status.load_more": "Daha fazla", @@ -269,6 +270,7 @@ "tabs_bar.home": "Ana sayfa", "tabs_bar.local_timeline": "Yerel", "tabs_bar.notifications": "Bildirimler", + "tabs_bar.search": "Search", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Upload için sürükle bırak yapınız", "upload_button.label": "Görsel ekle", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 63866d339..09210a325 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -240,6 +240,7 @@ "status.block": "Block @{name}", "status.cannot_reblog": "Цей допис не може бути передмухнутий", "status.delete": "Видалити", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "Подобається", "status.load_more": "Завантажити більше", @@ -269,6 +270,7 @@ "tabs_bar.home": "Головна", "tabs_bar.local_timeline": "Локальна", "tabs_bar.notifications": "Сповіщення", + "tabs_bar.search": "Search", "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", "upload_area.title": "Перетягніть сюди, щоб завантажити", "upload_button.label": "Додати медіаконтент", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index f7cb49632..f0772ffaa 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -240,6 +240,7 @@ "status.block": "屏蔽 @{name}", "status.cannot_reblog": "无法转嘟这条嘟文", "status.delete": "删除", + "status.direct": "Direct message @{name}", "status.embed": "嵌入", "status.favourite": "收藏", "status.load_more": "加载更多", @@ -269,6 +270,7 @@ "tabs_bar.home": "主页", "tabs_bar.local_timeline": "本站", "tabs_bar.notifications": "通知", + "tabs_bar.search": "Search", "ui.beforeunload": "如果你现在离开 Mastodon,你的草稿内容将会被丢弃。", "upload_area.title": "将文件拖放到此处开始上传", "upload_button.label": "上传媒体文件", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 1cbc9f1c5..bebb33e57 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -240,6 +240,7 @@ "status.block": "封鎖 @{name}", "status.cannot_reblog": "這篇文章無法被轉推", "status.delete": "刪除", + "status.direct": "Direct message @{name}", "status.embed": "鑲嵌", "status.favourite": "收藏", "status.load_more": "載入更多", @@ -269,6 +270,7 @@ "tabs_bar.home": "主頁", "tabs_bar.local_timeline": "本站", "tabs_bar.notifications": "通知", + "tabs_bar.search": "Search", "ui.beforeunload": "如果你現在離開 Mastodon,你的草稿內容將會被丟棄。", "upload_area.title": "將檔案拖放至此上載", "upload_button.label": "上載媒體檔案", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 3edb3fa50..efed9cd4d 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -240,6 +240,7 @@ "status.block": "封鎖 @{name}", "status.cannot_reblog": "此貼文無法轉推", "status.delete": "刪除", + "status.direct": "Direct message @{name}", "status.embed": "Embed", "status.favourite": "收藏", "status.load_more": "載入更多", @@ -269,6 +270,7 @@ "tabs_bar.home": "家", "tabs_bar.local_timeline": "本地", "tabs_bar.notifications": "通知", + "tabs_bar.search": "Search", "ui.beforeunload": "如果離開 Mastodon,你的草稿將會不見。", "upload_area.title": "拖放來上傳", "upload_button.label": "增加媒體", diff --git a/config/locales/ar.yml b/config/locales/ar.yml index c316a2fa5..8b9a6688a 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -204,6 +204,7 @@ ar: severity: الشدة show: affected_accounts: + one: هناك حساب واحد متأثر في قاعدة البيانات other: هناك %{count} حسابات في قاعدة البيانات متأثرة بذلك retroactive: silence: إلغاء الكتم عن كافة الحسابات المتواجدة على هذا النطاق @@ -262,6 +263,7 @@ ar: settings: activity_api_enabled: desc_html: عدد المنشورات المحلية و المستخدمين النشطين و التسجيلات الأسبوعية الجديدة + title: نشر مُجمل الإحصائيات عن نشاط المستخدمين bootstrap_timeline_accounts: title: الإشتراكات الإفتراضية للمستخدمين الجدد contact_information: @@ -274,12 +276,14 @@ ar: title: نشر عدد مثيلات الخوادم التي تم مصادفتها registrations: closed_message: + desc_html: يتم عرضه على الصفحة الرئيسية عندما يتم غلق تسجيل الحسابات الجديدة. يمكنكم إستخدام علامات الأيتش تي أم أل HTML title: رسالة التسجيلات المقفلة deletion: desc_html: السماح لأي مستخدم إغلاق حسابه title: السماح بحذف الحسابات min_invite_role: disabled: لا أحد + title: المستخدِمون المصرح لهم لإرسال الدعوات open: desc_html: السماح للجميع بإنشاء حساب title: فتح التسجيل diff --git a/config/locales/devise.fi.yml b/config/locales/devise.fi.yml index 91ab9559c..e356abf9f 100644 --- a/config/locales/devise.fi.yml +++ b/config/locales/devise.fi.yml @@ -2,60 +2,81 @@ fi: devise: confirmations: - confirmed: Sähköpostisi on onnistuneesti vahvistettu. - send_instructions: Saat kohta sähköpostiisi ohjeet kuinka voit aktivoida tilisi. - send_paranoid_instructions: Jos sähköpostisi on meidän tietokannassa, saat pian ohjeet sen varmentamiseen. + confirmed: Sähköpostiosoitteen vahvistus onnistui. + send_instructions: Saat kohta sähköpostitse ohjeet, kuinka vahvistat sähköpostiosoitteen. Jos et saa viestiä, tarkista roskapostikansio. + send_paranoid_instructions: Jos sähköpostiosoite on tietokannassamme, saat pian ohjeet, kuinka vahvistat osoitteen. Jos et saa viestiä, tarkista roskapostikansio. failure: already_authenticated: Olet jo kirjautunut sisään. - inactive: Tiliäsi ei ole viellä aktivoitu. + inactive: Tiliäsi ei ole vielä aktivoitu. invalid: Virheellinen %{authentication_keys} tai salasana. - last_attempt: Sinulla on yksi yritys jäljellä tai tili lukitaan. + last_attempt: Voit yrittää enää kerran, ennen kuin tili lukitaan. locked: Tili on lukittu. not_found_in_database: Virheellinen %{authentication_keys} tai salasana. - timeout: Sessiosi on umpeutunut. Kirjaudu sisään jatkaaksesi. - unauthenticated: Sinun tarvitsee kirjautua sisään tai rekisteröityä jatkaaksesi. - unconfirmed: Sinun tarvitsee varmentaa sähköpostisi jatkaaksesi. + timeout: Istunto on umpeutunut. Jatka kirjautumalla sisään. + unauthenticated: Kirjaudu sisään tai rekisteröidy, jos haluat jatkaa. + unconfirmed: Vahvista sähköpostiosoitteesi, ennen kuin jatkat. mailer: confirmation_instructions: - subject: 'Mastodon: Varmistus ohjeet' + action: Vahvista sähköpostiosoite + explanation: Olet luonut tilin palvelimelle %{host} käyttäen tätä sähköpostiosoitetta. Aktivoi tili yhdellä klikkauksella. Jos et luonut tiliä itse, voit jättää tämän viestin huomiotta. + extra_html: Katso myös instanssin säännöt ja käyttöehdot. + subject: 'Mastodon: Vahvistusohjeet - %{instance}' + title: Vahvista sähköpostiosoite + email_changed: + explanation: 'Tilin sähköpostiosoitteeksi vaihdetaan:' + extra: Jos et vaihtanut sähköpostiosoitettasi, joku muu on todennäköisesti päässyt käyttämään tiliäsi. Vaihda salasanasi viipymättä. Jos et pääse kirjautumaan tilillesi, ota yhteyttä instanssin ylläpitäjään. + subject: 'Mastodon: Sähköpostiosoite vaihdettu' + title: Uusi sähköpostiosoite password_change: + explanation: Tilin salasana on vaihdettu. + extra: Jos et vaihtanut salasanaasi, joku muu on todennäköisesti päässyt käyttämään tiliäsi. Vaihda salasanasi viipymättä. Jos et pääse kirjautumaan tilillesi, ota yhteyttä instanssin ylläpitäjään. subject: 'Mastodon: Salasana vaihdettu' + title: Salasana vaihdettu + reconfirmation_instructions: + explanation: Vahvista uusi sähköpostiosoite, niin muutos astuu voimaan. + extra: Jos et tehnyt muutosta itse, voit jättää tämän viestin huomiotta. Mastodon-tilin sähköpostiosoitetta ei vaihdeta, ennen kuin klikkaat yllä olevaa linkkiä. + subject: 'Mastodon: Vahvista sähköpostiosoite - %{instance}' + title: Vahvista sähköpostiosoite reset_password_instructions: - subject: 'Mastodon: Salasanan vaihto ohjeet' + action: Vaihda salasana + explanation: Pyysit tilillesi uuden salasanan. + extra: Jos et tehnyt pyyntöä itse, voit jättää tämän viestin huomiotta. Salasanaasi ei vaihdeta, ennen kuin klikkaat yllä olevaa linkkiä ja luot uuden salasanan. + subject: 'Mastodon: Ohjeet salasanan vaihtoon' + title: Salasanan vaihto unlock_instructions: - subject: 'Mastodon: Avauksen ohjeet' + subject: 'Mastodon: Ohjeet lukituksen poistoon' omniauth_callbacks: - failure: Varmennus %{kind} epäonnistui koska "%{reason}". - success: Onnistuneesti varmennettu %{kind} tilillä. + failure: Tunnistautuminen lähteestä %{kind} ei onnistunut, koska "%{reason}". + success: Tunnistautuminen tililtä %{kind} onnistui. passwords: - no_token: Et pääse tälle sivulle ilman salasanan vaihto sähköpostia. Jos tulet tämmöisestä postista, varmista että sinulla on täydellinen URL. - send_instructions: Jos sähköpostisi on meidän tietokannassa, saat pian ohjeet salasanan palautukseen. - send_paranoid_instructions: Jos sähköpostisi on meidän tietokannassa, saat pian ohjeet salasanan palautukseen. - updated: Salasanasi vaihdettu onnistuneesti. Olet nyt kirjautunut sisään. - updated_not_active: Salasanasi vaihdettu onnistuneesti. + no_token: Tälle sivulle pääsee vain salasananvaihtoviestin kautta. Jos tiedät tulevasi salasananvaihtoviestin kautta, varmista, että käytät koko viestissä mainittua URL-osoitetta. + send_instructions: Jos sähköpostiosoite on tietokannassamme, siihen lähetetään pian linkki salasanan vaihtoon. Jos et saa viestiä, tarkista roskapostikansio. + send_paranoid_instructions: Jos sähköpostiosoite on tietokannassamme, siihen lähetetään pian linkki salasanan vaihtoon. Jos et saa viestiä, tarkista roskapostikansio. + updated: Salasanan vaihto onnistui. Olet nyt kirjautunut sisään. + updated_not_active: Salasanan vaihto onnistui. registrations: - destroyed: Näkemiin! Tilisi on onnistuneesti peruttu. Toivottavasti näemme joskus uudestaan. - signed_up: Tervetuloa! Rekisteröitymisesi onnistu. - signed_up_but_inactive: Olet onnistuneesti rekisteröitynyt, mutta emme voi kirjata sinua sisään koska tiliäsi ei ole viellä aktivoitu. - signed_up_but_locked: Olet onnistuneesti rekisteröitynyt, mutta emme voi kirjata sinua sisään koska tilisi on lukittu. - signed_up_but_unconfirmed: Varmistuslinkki on lähetty sähköpostiisi. Seuraa sitä jotta tilisi voidaan aktivoida. - update_needs_confirmation: Tilisi on onnistuneesti päivitetty, mutta meidän tarvitsee vahvistaa sinun uusi sähköpostisi. Tarkista sähköpostisi ja seuraa viestissä tullutta linkkiä varmistaaksesi uuden osoitteen.. - updated: Tilisi on onnistuneesti päivitetty. + destroyed: Tilisi on poistettu. Näkemiin ja tervetuloa uudelleen! + signed_up: Tervetuloa! Rekisteröityminen onnistui. + signed_up_but_inactive: Rekisteröityminen onnistui. Emme kuitenkaan voi kirjata sinua sisään, sillä tiliäsi ei ole vielä aktivoitu. + signed_up_but_locked: Rekisteröityminen onnistui. Emme kuitenkaan voi kirjata sinua sisään, sillä tilisi on lukittu. + signed_up_but_unconfirmed: Sähköpostiosoitteeseesi on lähetetty vahvistuslinkki. Aktivoi tili seuraamalla linkkiä. Jos et saanut viestiä, tarkista roskapostikansio. + update_needs_confirmation: Tilin päivitys onnistui, mutta uusi sähköpostiosoite on vahvistettava. Tarkista sähköpostisi ja vahvista uusi sähköpostiosoite seuraamalla vahvistuslinkkiä. Jos et saanut viestiä, tarkista roskapostikansio. + updated: Tilin päivitys onnistui. sessions: - already_signed_out: Ulos kirjautuminen onnistui. + already_signed_out: Uloskirjautuminen onnistui. signed_in: Sisäänkirjautuminen onnistui. - signed_out: Ulos kirjautuminen onnistui. + signed_out: Uloskirjautuminen onnistui. unlocks: - send_instructions: Saat sähköpostiisi pian ohjeet, jolla voit avata tilisi uudestaan. - send_paranoid_instructions: Jos tilisi on olemassa, saat sähköpostiisi pian ohjeet tilisi avaamiseen. - unlocked: Tilisi on avattu onnistuneesti. Kirjaudu normaalisti sisään. + send_instructions: Saat pian sähköpostitse ohjeet tilin lukituksen poistoon. Jos et saanut viestiä, tarkista roskapostikansio. + send_paranoid_instructions: Jos tili on olemassa, saat pian sähköpostitse ohjeet tilin lukituksen poistoon. Jos et saanut viestiä, tarkista roskapostikansio. + unlocked: Tilin lukituksen poisto onnistui. Jatka kirjautumalla sisään. errors: messages: - already_confirmed: on jo varmistettu. Yritä kirjautua sisään - confirmation_period_expired: pitää varmistaa %{period} sisällä, ole hyvä ja pyydä uusi - expired: on erääntynyt, ole hyvä ja pyydä uusi + already_confirmed: on jo vahvistettu. Yritä kirjautua sisään + confirmation_period_expired: on vahvistettava %{period} sisällä. Pyydä uusi + expired: on vanhentunut. Pyydä uusi not_found: ei löydy not_locked: ei ollut lukittu not_saved: - one: '1 virhe esti %{resource} tallennuksen:' - other: "%{count} virhettä esti %{resource} tallennuksen:" + one: '1 virhe esti kohteen %{resource} tallennuksen:' + other: "%{count} virhettä esti kohteen %{resource} tallennuksen:" diff --git a/config/locales/devise.sk.yml b/config/locales/devise.sk.yml index 2ce328d22..e9c5dd455 100644 --- a/config/locales/devise.sk.yml +++ b/config/locales/devise.sk.yml @@ -13,13 +13,13 @@ sk: locked: Váš účet je zamknutý. not_found_in_database: Nesprávny %{authentication_keys} alebo heslo. timeout: Vaša aktívna sezóna vypršala. Pre pokračovanie sa prosím znovu prihláste. - unauthenticated: Pred pokračovaním sa musíte zaregistrovať alebo prihlásiť. - unconfirmed: Pred pokračovaním musíte potvrdiť svoj email. + unauthenticated: K pokračovaniu sa musíš zaregistrovať alebo prihlásiť. + unconfirmed: Pred pokračovaním musíš potvrdiť svoj email. mailer: confirmation_instructions: - action: Potvrite emailovú adresu - explanation: S touto email adresou ste si vytvoril/a účet na %{host}. Si iba jeden klik od jeho aktivácie. Pokiaľ ste to ale nebol/a vy, prosím ignoruj tento email. - extra_html: Prosím pozri sa aj na pravidla tohto servera, a naše užívaťeľské podiemky. + action: Potvŕď emailovú adresu + explanation: S touto emailovou adresou si si vytvoril/a účet na %{host}. Si iba jeden klik od jeho aktivácie. Pokiaľ si to ale nebol/a ty, prosím ignoruj tento email. + extra_html: Prosím pozri sa aj na pravidlá tohto servera, a naše užívaťeľské podiemky. subject: 'Mastodon: Potvrdzovacie inštrukcie pre %{instance}' title: Potvrď emailovú adresu email_changed: diff --git a/config/locales/doorkeeper.fi.yml b/config/locales/doorkeeper.fi.yml index 8c1baf909..a3b878b65 100644 --- a/config/locales/doorkeeper.fi.yml +++ b/config/locales/doorkeeper.fi.yml @@ -3,17 +3,19 @@ fi: activerecord: attributes: doorkeeper/application: - name: Nimi - redirect_uri: Uudelleenohjaus URI + name: Sovelluksen nimi + redirect_uri: Uudelleenohjauksen URI + scopes: Oikeudet + website: Sovelluksen verkkosivu errors: models: doorkeeper/application: attributes: redirect_uri: fragment_present: ei voi sisältää osia. - invalid_uri: pitää olla validi URI. - relative_uri: pitää olla täydellinen URI. - secured_uri: pitää olla HTTPS/SSL URI. + invalid_uri: on oltava kelvollinen URI. + relative_uri: on oltava täydellinen URI. + secured_uri: on oltava HTTPS/SSL-URI. doorkeeper: applications: buttons: @@ -25,89 +27,93 @@ fi: confirmations: destroy: Oletko varma? edit: - title: Muokkaa applikaatiota + title: Muokkaa sovellusta form: - error: Whoops! Tarkista lomakkeesi mahdollisten virheiden varalta + error: Hups! Tarkista, että lomakkeessa ei ole virheitä help: native_redirect_uri: Käytä %{native_redirect_uri} paikallisiin testeihin - redirect_uri: Käytä yhtä riviä per URI - scopes: Erota scopet välilyönnein. Jätä tyhjäksi käyteksi oletus scopeja. + redirect_uri: Lisää jokainen URI omalle rivilleen + scopes: Erota oikeudet välilyönnein. Jos kenttä jätetään tyhjäksi, käytetään oletusoikeuksia. index: - callback_url: Callback URL + application: Sovellus + callback_url: Takaisinkutsu-URL + delete: Poista name: Nimi - new: Uusi applikaatio - title: Sinun applikaatiosi + new: Uusi sovellus + scopes: Oikeudet + show: Näytä + title: Omat sovellukset new: - title: Uusi applikaatio + title: Uusi sovellus show: actions: Toiminnot - application_id: Applikaation Id - callback_urls: Callback urls - scopes: Scopet - secret: Salainen avain - title: 'Applikaatio: %{name}' + application_id: Asiakasohjelman tunnus + callback_urls: Takaisinkutsu-URL:t + scopes: Oikeudet + secret: Asiakasohjelman salainen avain + title: 'Sovellus: %{name}' authorizations: buttons: authorize: Valtuuta deny: Evää error: - title: Virhe on tapahtunut + title: Tapahtui virhe new: able_to: Se voi - prompt: Applikaatio %{client_name} pyytää lupaa tilillesi + prompt: Sovellus %{client_name} pyytää lupaa käyttää tiliäsi title: Valtuutus vaaditaan show: - title: Kopioi tämä valtuutuskoodi ja liitä se applikaatioon. + title: Kopioi tämä valtuutuskoodi ja liitä se sovellukseen. authorized_applications: buttons: - revoke: Evää + revoke: Peru confirmations: revoke: Oletko varma? index: - application: Applikaatio + application: Sovellus created_at: Valtuutettu date_format: "%Y-%m-%d %H:%M:%S" - scopes: Scopet - title: Valtuuttamasi applikaatiot + scopes: Oikeudet + title: Valtuutetut sovellukset errors: messages: - access_denied: Resurssin omistaja tai valtuutus palvelin hylkäsi pyynnönr. - credential_flow_not_configured: Resurssin omistajan salasana epäonnistui koska Doorkeeper.configure.resource_owner_from_credentials ei ole konfiguroitu. - invalid_client: Asiakkaan valtuutus epäonnistui koska tuntematon asiakas, asiakas ei sisältänyt valtuutusta, tai tukematon valtuutus tapa. - invalid_grant: Antamasi valtuutus lupa on joko väärä, erääntynyt, peruttu, ei vastaa uudelleenohjaus URI jota käytetään valtuutus pyynnössä, tai se myönnettin toiselle asiakkaalle. - invalid_redirect_uri: Uudelleenohjaus uri ei ole oikein. - invalid_request: Pyynnöstä puutti parametri, sisältää tukemattoman parametri arvonn, tai on korruptoitunut. - invalid_resource_owner: Annetut resurssin omistajan tunnnukset ovat väärät, tai resurssin omistajaa ei löydy - invalid_scope: Pyydetty scope on väärä, tuntemat, tai korruptoitunut. + access_denied: Resurssin omistaja tai valtuutuspalvelin hylkäsi pyynnön. + credential_flow_not_configured: Resurssin omistajan salasana epäonnistui, koska asetusta Doorkeeper.configure.resource_owner_from_credentials ei ole konfiguroitu. + invalid_client: Asiakasohjelman valtuutus epäonnistui, koska asiakas on tuntematon, asiakkaan valtuutus ei ollut mukana tai valtuutustapaa ei tueta. + invalid_grant: Valtuutuslupa on virheellinen, umpeutunut, peruttu, valtuutuspyynnössä käytettyä uudelleenohjaus-URI:tä vastaamaton tai myönnetty toiselle asiakkaalle. + invalid_redirect_uri: Uudelleenohjaus-URI on virheellinen. + invalid_request: Pyynnöstä puuttuu vaadittu parametri, se sisältää tukemattoman parametriarvon tai on muulla tavoin väärin muotoiltu. + invalid_resource_owner: Annetut resurssin omistajan tunnnukset ovat virheelliset, tai resurssin omistajaa ei löydy + invalid_scope: Pyydetyt oikeudet ovat virheellisiä, tuntemattomia tai väärin muotoiltuja. invalid_token: - expired: Access token vanhentunut - revoked: Access token evätty - unknown: Access token väärä - resource_owner_authenticator_not_configured: Resurssin omistajan etsiminen epäonnistui koska Doorkeeper.configure.resource_owner_authenticator ei ole konfiguroitu. - server_error: Valtuutus palvelin kohtasi odottamattoman virheen joka esti sitä täyttämästä pyyntöä. - temporarily_unavailable: Valtuutus palvelin ei voi tällä hetkellä käsitellä pyyntöäsi joko väliaikaisen ruuhkan tai huollon takia. - unauthorized_client: Asiakas ei ole valtuutettu tekemään tätä pyyntöä käyttäen tätä metodia. - unsupported_grant_type: Valtuutus grant type ei ole tuettu valtuutus palvelimella. - unsupported_response_type: Valtuutus palvelin ei tue tätä vastaus tyyppiä. + expired: Käyttöoikeustunnus on vanhentunut + revoked: Käyttöoikeustunnus on peruttu + unknown: Käyttöoikeustunnus on virheellinen + resource_owner_authenticator_not_configured: Resurssin omistajaa ei löytynyt, koska asetusta Doorkeeper.configure.resource_owner_authenticator ei ole konfiguroitu. + server_error: Valtuutuspalvelin kohtasi odottamattoman virheen, joka esti pyynnön täyttämisen. + temporarily_unavailable: Valtuutuspalvelin ei voi tällä hetkellä käsitellä pyyntöä joko väliaikaisen ruuhkan tai huollon takia. + unauthorized_client: Asiakkaalla ei ole valtuuksia tehdä tätä pyyntöä tällä metodilla. + unsupported_grant_type: Valtuutuspalvelin ei tue tätä valtuutusluvan tyyppiä. + unsupported_response_type: Valtuutuspalvelin ei tue tätä vastauksen tyyppiä. flash: applications: create: - notice: Applikaatio luotu. + notice: Sovellus luotu. destroy: - notice: Applikaatio poistettu. + notice: Sovellus poistettu. update: - notice: Applikaatio päivitetty. + notice: Sovellus päivitetty. authorized_applications: destroy: - notice: Applikaatio tuhottu. + notice: Sovellus peruttu. layouts: admin: nav: - applications: Applikaatiot - oauth2_provider: OAuth2 Provider + applications: Sovellukset + oauth2_provider: OAuth2-palveluntarjoaja application: - title: OAuth valtuutus tarvitaan + title: OAuth-valtuutus tarvitaan scopes: - follow: seuraa, estä, peru esto ja lopeta tilien seuraaminen - read: lukea tilin dataa + follow: seurata, estää, perua eston ja lopettaa tilien seuraaminen + read: lukea tilin tietoja write: julkaista puolestasi diff --git a/config/locales/doorkeeper.sk.yml b/config/locales/doorkeeper.sk.yml index 7a285eb4f..bda26429e 100644 --- a/config/locales/doorkeeper.sk.yml +++ b/config/locales/doorkeeper.sk.yml @@ -63,7 +63,7 @@ sk: prompt: Aplikácia %{client_name} žiada prístup k vašemu účtu title: Je potrebná autorizácia show: - title: Skopírujte tento autorizačný kód a vložte ho do aplikácie. + title: Skopíruj tento autorizačný kód a vlož ho do aplikácie. authorized_applications: buttons: revoke: Zrušiť oprávnenie diff --git a/config/locales/eo.yml b/config/locales/eo.yml index a896592b0..27c62f899 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -24,12 +24,12 @@ eo: within_reach_body: Pluraj aplikaĵoj por iOS, Android, kaj aliaj platformoj danke al API-medio bonveniga por programistoj permesas resti en kontakto kun viaj amikoj ĉie. within_reach_title: Ĉiam kontaktebla generic_description: "%{domain} estas unu servilo en la reto" - hosted_on: Mastodon gastigita en %{domain} + hosted_on: "%{domain} estas nodo de Mastodon" learn_more: Lerni pli other_instances: Listo de nodoj source_code: Fontkodo status_count_after: mesaĝoj - status_count_before: Kiu publikigis + status_count_before: Kie skribiĝis user_count_after: uzantoj user_count_before: Hejmo de what_is_mastodon: Kio estas Mastodon? @@ -358,7 +358,7 @@ eo: warning: Estu tre atenta kun ĉi tiu datumo. Neniam diskonigu ĝin al iu ajn! your_token: Via alira ĵetono auth: - agreement_html: Per registriĝo, vi konsentas kun la reguloj de la nodo kaj niaj uzkondiĉoj. + agreement_html: Per registriĝo, vi konsentas kun la reguloj de nia nodo kaj niaj uzkondiĉoj. change_password: Pasvorto confirm_email: Konfirmi retadreson delete_account: Forigi konton diff --git a/config/locales/es.yml b/config/locales/es.yml index 8e7a766a8..a5a20aa3c 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -546,7 +546,7 @@ es: quadrillion: Q thousand: K trillion: T - unit: '' + unit: " " pagination: newer: Más nuevo next: Próximo @@ -634,6 +634,15 @@ es: two_factor_authentication: Autenticación de dos factores your_apps: Tus aplicaciones statuses: + attached: + description: 'Adjunto: %{attached}' + image: + one: "%{count} imagen" + other: "%{count} imágenes" + video: + one: "%{count} vídeo" + other: "%{count} vídeos" + content_warning: 'Alerta de contenido: %{warning}' open_in_web: Abrir en web over_character_limit: Límite de caracteres de %{max} superado pin_errors: @@ -682,7 +691,7 @@ es: backup_ready: explanation: Has solicitado una copia completa de tu cuenta de Mastodon. ¡Ya está preparada para descargar! subject: Tu archivo está preparado para descargar - title: Recogida del archivo + title: Descargar archivo welcome: edit_profile_action: Configurar el perfil edit_profile_step: Puedes personalizar tu perfil subiendo un avatar, cabecera, cambiando tu nombre para mostrar y más. Si te gustaría revisar seguidores antes de autorizarlos a que te sigan, puedes bloquear tu cuenta. diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 939ebd10a..62f6560bf 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -1,31 +1,35 @@ --- fi: about: - about_mastodon_html: Mastodon on vapaa, avoimeen lähdekoodiin perustuva sosiaalinen verkosto. Hajautettu vaihtoehto kaupallisille alustoille, se välttää eiskit yhden yrityksen monopolisoinnin sinun viestinnässäsi. Valitse palvelin mihin luotat — minkä tahansa valitset, voit vuorovaikuttaa muiden kanssa. Kuka tahansa voi luoda Mastodon palvelimen ja ottaa osaa sosiaaliseen verkkoon saumattomasti. + about_hashtag_html: Nämä ovat hashtagilla #%{hashtag} merkittyjä julkisia tuuttauksia. Voit vastata niihin, jos sinulla on tili jossain päin fediversumia. + about_mastodon_html: Mastodon on sosiaalinen verkosto. Se on toteutettu avoimilla verkkoprotokollilla ja vapailla, avoimen lähdekoodin ohjelmistoilla, ja se toimii hajautetusti samaan tapaan kuin sähköposti. about_this: Tietoja tästä palvelimesta - closed_registrations: Rekisteröityminen tässä instanssissa on juuri nyt suljettu. Mutta! Voit yhdistää täysin samaan, yhteiseen verkostoon rekisteröitymällä jossain toisessa instanssissa. + closed_registrations: Tähän instanssiin ei voi tällä hetkellä rekisteröityä. Voit kuitenkin luoda tilin johonkin toiseen instanssiin ja käyttää samaa verkostoa sitä kautta. contact: Ota yhteyttä contact_missing: Ei asetettu contact_unavailable: Ei saatavilla description_headline: Mikä on %{domain}? - domain_count_after: muuhun palvelimeen + domain_count_after: muuhun instanssiin domain_count_before: Yhdistyneenä extended_description_html: |

Hyvä paikka säännöille

-

Pidennettyä kuvausta ei ole vielä asetettu.

+

Pidempää kuvausta ei ole vielä laadittu.

features: - humane_approach_body: Muiden verkostojen virheistä oppien, Mastodon pyrkii tekemään eettisiä valintoja suunnittelussa taistellakseen sosiaalisen median väärinkäyttöä vastaan. - humane_approach_title: Humaanimpi lähestymistapa - not_a_product_body: Mastodon ei ole kaupallinen verkosto. Ei mainoksia, ei tiedonlouhintaa, ei suljettuja sisäpiirejä. Mastodonissa ei ole keskitettyä auktoriteettiä. + humane_approach_body: Mastodonissa otetaan oppia muiden verkostojen virheistä, ja sen suunnittelussa pyritään toimimaan eettisesti ja ehkäisemään sosiaalisen median väärinkäyttöä. + humane_approach_title: Ihmisläheisempi ote + not_a_product_body: Mastodon ei ole kaupallinen verkosto. Ei mainoksia, ei tiedonlouhintaa, ei suljettuja protokollia. Mastodonissa ei ole keskusjohtoa. not_a_product_title: Olet henkilö, et tuote - real_conversation_title: Rakennettu oikealle keskustelulle - within_reach_body: Kehittäjäystävällisen rajapintaekosysteemin ansiosta useita appeja Androidille, iOS:lle ja muille alustoille, jotka mahdollistavat yhteydenpidon ystäviesi kanssa missä vain. + real_conversation_body: 'Voit ilmaista itseäsi niin kuin itse haluat: tilaa on 500 merkkiä, ja sisältövaroituksia voi tehdä monin tavoin.' + real_conversation_title: Tehty oikeaa keskustelua varten + within_reach_body: Rajapintoja on tarjolla moniin eri kehitysympäristöihin, minkä ansiosta iOS:lle, Androidille ja muille alustoille on saatavana useita eri sovelluksia. Näin voit pitää yhteyttä ystäviisi missä vain. within_reach_title: Aina lähellä + generic_description: "%{domain} on yksi verkostoon kuuluvista palvelimista" + hosted_on: Mastodon palvelimella %{domain} learn_more: Lisätietoja other_instances: Muut palvelimet source_code: Lähdekoodi status_count_after: statusta - status_count_before: Ovat luoneet + status_count_before: He ovat luoneet user_count_after: käyttäjälle user_count_before: Koti what_is_mastodon: Mikä on Mastodon? @@ -33,161 +37,681 @@ fi: follow: Seuraa followers: Seuraajat following: Seuratut + media: Media + moved_html: "%{name} on muuttanut osoitteeseen %{new_profile_link}:" nothing_here: Täällä ei ole mitään! - people_followed_by: Henkilöitä joita %{name} seuraa - people_who_follow: Henkilöt jotka seuraa %{name} - posts: Postaukset + people_followed_by: Henkilöt, joita %{name} seuraa + people_who_follow: Käyttäjän %{name} seuraajat + posts: Tuuttaukset + posts_with_replies: Tuuttaukset ja vastaukset remote_follow: Etäseuranta reserved_username: Käyttäjänimi on varattu roles: admin: Ylläpitäjä + moderator: Moderaattori unfollow: Lopeta seuraaminen admin: account_moderation_notes: account: Moderaattori create: Luo created_at: Päiväys - created_msg: Moderointimerkintä luotu onnistuneesti! + created_msg: Moderointimerkinnän luonti onnistui! delete: Poista - destroyed_msg: Moderointimerkintä tuhottu onnistuneesti! + destroyed_msg: Moderointimerkinnän poisto onnistui! accounts: are_you_sure: Oletko varma? - confirm: Hyväksy - confirmed: Hyväksytty + by_domain: Verkko-osoite + confirm: Vahvista + confirmed: Vahvistettu + demote: Alenna disable: Poista käytöstä disable_two_factor_authentication: Poista 2FA käytöstä disabled: Poistettu käytöstä + display_name: Näyttönimi + domain: Verkko-osoite edit: Muokkaa email: Sähköposti + enable: Ota käyttöön + enabled: Käytössä + feed_url: Syötteen URL followers: Seuraajat - followers_url: Seuraajat URL + followers_url: Seuraajien URL + follows: Seuraa + inbox_url: Saapuvan postilaatikon URL + ip: IP + location: + all: Kaikki + local: Paikalliset + remote: Etätilit + title: Sijainti + login_status: Sisäänkirjautumisen tila + media_attachments: Medialiitteet + memorialize: Muuta muistosivuksi + moderation: + all: Kaikki + silenced: Hiljennetty + suspended: Jäähyllä + title: Moderointi + moderation_notes: Moderointimerkinnät + most_recent_activity: Viimeisin toiminta + most_recent_ip: Viimeisin IP + not_subscribed: Ei tilaaja + order: + alphabetic: Aakkosjärjestys + most_recent: Uusin + title: Järjestys + outbox_url: Lähtevän postilaatikon URL + perform_full_suspension: Siirrä kokonaan jäähylle + profile_url: Profiilin URL + promote: Ylennä + protocol: Protokolla + public: Julkinen + push_subscription_expires: PuSH-tilaus vanhenee + redownload: Päivitä profiilikuva + reset: Palauta + reset_password: Palauta salasana + resubscribe: Tilaa uudelleen + role: Oikeudet + roles: + admin: Ylläpitäjä + moderator: Moderaattori + staff: Henkilöstö + user: Käyttäjä + salmon_url: Salmon-URL + search: Haku + shared_inbox_url: Jaetun saapuvan postilaatikon URL + show: + created_reports: Tilin luomat raportit + report: raportti + targeted_reports: Tästä tilistä tehdyt raportit + silence: Hiljennä + statuses: Tilat + subscribe: Tilaa + title: Tilit + undo_silenced: Peru hiljennys + undo_suspension: Peru jäähy + unsubscribe: Lopeta tilaus + username: Käyttäjänimi + web: Web + action_logs: + actions: + confirm_user: "%{name} vahvisti käyttäjän %{target} sähköpostiosoitteen" + create_custom_emoji: "%{name} lähetti uuden emojin %{target}" + create_domain_block: "%{name} esti verkkotunnuksen %{target}" + create_email_domain_block: "%{name} lisäsi sähköpostiverkkotunnuksen %{target} estolistalle" + demote_user: "%{name} alensi käyttäjän %{target}" + destroy_domain_block: "%{name} poisti verkkotunnuksen %{target} eston" + destroy_email_domain_block: "%{name} lisäsi sähköpostiverkkotunnuksen %{target} sallittujen listalle" + destroy_status: "%{name} poisti käyttäjän %{target} tilan" + disable_2fa_user: "%{name} poisti käyttäjältä %{target} kaksivaiheisen todentamisen vaatimuksen" + disable_custom_emoji: "%{name} poisti emojin %{target} käytöstä" + disable_user: "%{name} poisti sisäänkirjautumisen käytöstä käyttäjältä %{target}" + enable_custom_emoji: "%{name} salli emojin %{target} käyttöön" + enable_user: "%{name} salli sisäänkirjautumisen käyttäjälle %{target}" + memorialize_account: "%{name} muutti käyttäjän %{target} tilin muistosivuksi" + promote_user: "%{name} ylensi käyttäjän %{target}" + reset_password_user: "%{name} palautti käyttäjän %{target} salasanan" + resolve_report: "%{name} hylkäsi raportin %{target}" + silence_account: "%{name} hiljensi käyttäjän %{target}" + suspend_account: "%{name} siirsi käyttäjän %{target} jäähylle" + unsilence_account: "%{name} poisti käyttäjän %{target} hiljennyksen" + unsuspend_account: "%{name} perui käyttäjän %{target} jäähyn" + update_custom_emoji: "%{name} päivitti emojin %{target}" + update_status: "%{name} päivitti käyttäjän %{target} tilan" + title: Auditointiloki + custom_emojis: + by_domain: Verkkotunnus + copied_msg: Emojin paikallisen kopion luonti onnistui + copy: Kopioi + copy_failed_msg: Emojista ei voitu tehdä paikallista kopiota + created_msg: Emojin luonti onnistui! + delete: Poista + destroyed_msg: Emojon poisto onnistui! + disable: Poista käytöstä + disabled_msg: Emojin käytöstäpoisto onnistui + emoji: Emoji + enable: Ota käyttöön + enabled_msg: Emojin käyttöönotto onnistui + image_hint: PNG enintään 50 kt + listed: Listassa + new: + title: Lisää uusi mukautettu emoji + overwrite: Kirjoita yli + shortcode: Lyhennekoodi + shortcode_hint: Vähintään kaksi merkkiä, vain kirjaimia, numeroita ja alaviivoja + title: Mukautetut emojit + unlisted: Ei listassa + update_failed_msg: Emojin päivitys epäonnistui + updated_msg: Emojin päivitys onnistui! + upload: Lähetä + domain_blocks: + add_new: Lisää uusi + created_msg: Verkkotunnuksen estoa käsitellään + destroyed_msg: Verkkotunnuksen esto on peruttu + domain: Verkkotunnus + new: + create: Luo esto + hint: Verkkotunnuksen esto ei estä tilien luomista ja lisäämistä tietokantaan, mutta se soveltaa näihin tileihin automaattisesti määrättyjä moderointitoimia tilin luomisen jälkeen. + severity: + desc_html: "Hiljennys estää tilin julkaisuja näkymästä muille kuin tilin seuraajille. Jäähy poistaa tilin kaiken sisällön, median ja profiilitiedot. Jos haluat vain hylätä mediatiedostot, valitse Ei mitään." + noop: Ei mitään + silence: Hiljennys + suspend: Jäähy + title: Uusi verkkotunnuksen esto + reject_media: Hylkää mediatiedostot + reject_media_hint: Poistaa paikallisesti tallennetut mediatiedostot eikä lataa niitä enää jatkossa. Ei merkitystä jäähyn kohdalla + severities: + noop: Ei mitään + silence: Hiljennys + suspend: Jäähy + severity: Vakavuus + show: + affected_accounts: + one: Vaikuttaa yhteen tiliin tietokannassa + other: Vaikuttaa %{count} tiliin tietokannassa + retroactive: + silence: Peru kaikkien tässä verkkotunnuksessa jo olemassa olevien tilien hiljennys + suspend: Peru kaikkien tässä verkkotunnuksessa jo olemassa olevien tilien jäähy + title: Peru verkkotunnuksen %{domain} esto + undo: Peru + title: Verkkotunnusten estot + undo: Peru + email_domain_blocks: + add_new: Lisää uusi + created_msg: Sähköpostiverkkotunnuksen lisäys estolistalle onnistui + delete: Poista + destroyed_msg: Sähköpostiverkkotunnuksen poisto estolistalta onnistui + domain: Verkkotunnus + new: + create: Lisää verkkotunnus + title: Uusi sähköpostiestolistan merkintä + title: Sähköpostiestolista + instances: + account_count: Tiedossa olevat tilit + domain_name: Verkkotunnus + reset: Palauta + search: Hae + title: Tiedossa olevat instanssit + invites: + filter: + all: Kaikki + available: Saatavilla + expired: Vanhentunut + title: Suodata + title: Kutsut + reports: + action_taken_by: Toimenpiteen tekijä + are_you_sure: Oletko varma? + comment: + label: Kommentti + none: Ei mitään + delete: Poista + id: Tunniste + mark_as_resolved: Merkitse ratkaistuksi + nsfw: + 'false': Peru medialiitteiden piilotus + 'true': Piilota medialiitteet + report: Raportti nro %{id} + report_contents: Sisältö + reported_account: Raportoitu tili + reported_by: Raportoija + resolved: Ratkaistut + silence_account: Hiljennä tili + status: Tila + suspend_account: Siirrä tili jäähylle + target: Kohde + title: Raportit + unresolved: Ratkaisemattomat + view: Näytä + settings: + activity_api_enabled: + desc_html: Paikallisesti julkaistujen tilojen, aktiivisten käyttäjien ja uusien rekisteröintien määrät viikoittain + title: Julkaise koostetilastoja käyttäjien aktiivisuudesta + bootstrap_timeline_accounts: + desc_html: Erota käyttäjänimet pilkulla. Vain paikalliset ja lukitsemattomat tilit toimivat. Jos kenttä jätetään tyhjäksi, oletusarvona ovat kaikki paikalliset ylläpitäjät. + title: Uudet käyttäjät seuraavat oletuksena seuraavia tilejä + contact_information: + email: Työsähköposti + username: Yhteyshenkilön käyttäjänimi + hero: + desc_html: Näytetään etusivulla. Suosituskoko vähintään 600x100 pikseliä. Jos kuvaa ei aseteta, käytetään instanssin pikkukuvaa + title: Sankarin kuva + peers_api_enabled: + desc_html: Verkkotunnukset, jotka tämä instanssi on kohdannut fediversumissa + title: Julkaise löydettyjen instanssien luettelo + registrations: + closed_message: + desc_html: Näytetään etusivulla, kun rekisteröinti on suljettu. HTML-tagit käytössä + title: Viesti, kun rekisteröinti on suljettu + deletion: + desc_html: Salli jokaisen poistaa oma tilinsä + title: Avoin tilin poisto + min_invite_role: + disabled: Ei kukaan + title: Salli kutsut käyttäjältä + open: + desc_html: Salli kenen tahansa luoda tili + title: Avoin rekisteröinti + show_known_fediverse_at_about_page: + desc_html: Kun tämä on valittu, esikatselussa näytetään tuuttaukset kaikkialta tunnetusta fediversumista. Muutoin näytetään vain paikalliset tuuttaukset. + title: Näytä aikajanan esikatselussa koko tunnettu fediversumi + show_staff_badge: + desc_html: Näytä käyttäjäsivulla henkilöstömerkki + title: Näytä henkilöstömerkki + site_description: + desc_html: Esittelykappale etusivulla ja metatunnisteissa. HTML-tagit käytössä, tärkeimmät ovat <a> ja <em>. + title: Instanssin kuvaus + site_description_extended: + desc_html: Hyvä paikka käytösohjeille, säännöille, ohjeistuksille ja muille instanssin muista erottaville asioille. HTML-tagit käytössä + title: Omavalintaiset laajat tiedot + site_terms: + desc_html: Tähän voi kirjoittaa instanssin tietosuojakäytännöstä, käyttöehdoista ja sen sellaisista asioista. HTML-tagit käytössä + title: Omavalintaiset käyttöehdot + site_title: Instanssin nimi + thumbnail: + desc_html: Käytetään esikatseluissa OpenGraphin ja API:n kautta. Suosituskoko 1200x630 pikseliä + title: Instanssin pikkukuva + timeline_preview: + desc_html: Näytä julkinen aikajana aloitussivulla + title: Aikajanan esikatselu + title: Sivuston asetukset + statuses: + back_to_account: Takaisin tilin sivulle + batch: + delete: Poista + nsfw_off: NSFW POIS + nsfw_on: NSFW PÄÄLLÄ + execute: Suorita + failed_to_execute: Suoritus epäonnistui + media: + hide: Piilota media + show: Näytä media + title: Media + no_media: Ei mediaa + title: Tilin tilat + with_media: Sisältää mediaa + subscriptions: + callback_url: Paluu-URL + confirmed: Vahvistettu + expires_in: Vanhenee + last_delivery: Viimeisin toimitus + title: WebSub + topic: Aihe + title: Ylläpito + admin_mailer: + new_report: + body: "%{reporter} on raportoinut kohteen %{target}" + subject: Uusi raportti instanssista %{instance} (nro %{id}) application_mailer: - settings: 'Muokkaa sähköpostiasetuksia: %{link}' - view: 'Katso:' + notification_preferences: Muuta sähköpostiasetuksia + salutation: "%{name}," + settings: 'Muuta sähköpostiasetuksia: %{link}' + view: 'Näytä:' + view_profile: Näytä profiili + view_status: Näytä tila applications: - invalid_url: Annettu URL on väärä + created: Sovelluksen luonti onnistui + destroyed: Sovelluksen poisto onnistui + invalid_url: Annettu URL on virheellinen + regenerate_token: Luo pääsytunnus uudelleen + token_regenerated: Pääsytunnuksen uudelleenluonti onnistui + warning: Säilytä tietoa hyvin. Älä milloinkaan jaa sitä muille! + your_token: Pääsytunnus auth: - didnt_get_confirmation: Etkö saanut varmennusohjeita? + agreement_html: Rekisteröityessäsi sitoudut noudattamaan instanssin sääntöjä ja käyttöehtoja. + change_password: Salasana + confirm_email: Vahvista sähköpostiosoite + delete_account: Poista tili + delete_account_html: Jos haluat poistaa tilisi, paina tästä. Poisto on vahvistettava. + didnt_get_confirmation: Etkö saanut vahvistusohjeita? forgot_password: Unohditko salasanasi? + invalid_reset_password_token: Salasananpalautustunnus on virheellinen tai vanhentunut. Pyydä uusi. login: Kirjaudu sisään logout: Kirjaudu ulos + migrate_account: Muuta toiseen tiliin + migrate_account_html: Jos haluat ohjata tämän tilin toiseen tiliin, voit asettaa toisen tilin tästä. + or: tai + or_log_in_with: Tai käytä kirjautumiseen + providers: + cas: CAS + saml: SAML register: Rekisteröidy - resend_confirmation: Lähetä varmennusohjeet uudestaan + register_elsewhere: Rekisteröidy toiselle palvelimelle + resend_confirmation: Lähetä vahvistusohjeet uudestaan reset_password: Palauta salasana security: Tunnukset set_new_password: Aseta uusi salasana authorize_follow: - error: Valitettavasti tapahtui virhe etätilin haussa. + error: Valitettavasti etätilin haussa tapahtui virhe follow: Seuraa - title: Seuraa %{acct} + follow_request: 'Olet lähettänyt seuraamispyynnön käyttäjälle:' + following: 'Onnistui! Seuraat käyttäjää:' + post_follow: + close: Tai voit sulkea tämän ikkunan. + return: Palaa käyttäjän profiiliin + web: Siirry verkkosivulle + title: Seuraa käyttäjää %{acct} datetime: distance_in_words: - about_x_hours: "%{count}t" - about_x_months: "%{count}kk" - about_x_years: "%{count}v" - almost_x_years: "%{count}v" - half_a_minute: Juuri nyt - less_than_x_minutes: "%{count}m" - less_than_x_seconds: Juuri nyt - over_x_years: "%{count}v" - x_days: "%{count}pv" - x_minutes: "%{count}m" - x_months: "%{count}kk" - x_seconds: "%{count}s" + about_x_hours: "%{count} h" + about_x_months: "%{count} kk" + about_x_years: "%{count} v" + almost_x_years: "%{count} v" + half_a_minute: Nyt + less_than_x_minutes: "%{count} m" + less_than_x_seconds: Nyt + over_x_years: "%{count} v" + x_days: "%{count} pv" + x_minutes: "%{count} m" + x_months: "%{count} kk" + x_seconds: "%{count} s" + deletes: + bad_password_msg: Hyvä yritys, hakkerit! Väärä salasana + confirm_password: Tunnistaudu syöttämällä nykyinen salasanasi + description_html: Tämä poistaa pysyvästi ja peruuttamattomasti kaiken tilisi sisällön ja poistaa tilin käytöstä. Käyttäjänimesi pysyy varattuna, jotta identiteettiäsi ei myöhemmin varasteta. + proceed: Poista tili + success_msg: Tilin poisto onnistui + warning_html: Sisällön poistaminen taataan vain tämän instanssin osalta. Jos sisältöä on jaettu paljon, siitä todennäköisesti jää jälkiä. Palvelimet, joihin ei saada yhteyttä tai jotka ovat lopettaneet päivitystesi tilaamisen, eivät päivitä tietokantojaan. + warning_title: Sisällön saatavuustieto levitetty + errors: + '403': Sinulla ei ole lupaa nähdä tätä sivua. + '404': Etsimääsi sivua ei ole olemassa. + '410': Etsimääsi sivua ei ole enää olemassa. + '422': + content: Turvallisuusvahvistus epäonnistui. Oletko estänyt evästeet? + title: Turvallisuusvahvistus epäonnistui + '429': Rajoitettu + '500': + content: Valitettavasti jokin meni pieleen meidän päässämme. + title: Sivu ei ole oikein + noscript_html: Mastodon-selainsovelluksen käyttöön vaaditaan JavaScript. Voit vaihtoehtoisesti kokeilla jotakin omalle käyttöjärjestelmällesi tehtyä Mastodonsovellusta. exports: - blocks: Estosi + archive_takeout: + date: Päiväys + download: Lataa arkisto + hint_html: Voit pyytää arkistoa omista tuuttauksistasi ja mediastasi. Vientitiedot ovat ActivityPub-muodossa, ja ne voi lukea millä tahansa yhteensopivalla ohjelmalla. + in_progress: Arkistoa kootaan... + request: Pyydä arkisto + size: Koko + blocks: Estot csv: CSV follows: Seurattavat - storage: Mediasi + mutes: Mykistetyt + storage: Media-arkisto + followers: + domain: Verkkotunnus + explanation_html: Jos haluat olla varma tilapäivitystesi yksityisyydestä, sinun täytyy tietää, ketkä seuraavat sinua. Yksityiset tilapäivityksesi lähetetään kaikkiin niihin instansseihin, joissa sinulla on seuraajia. Jos et luota siihen, että näiden instanssien ylläpitäjät tai ohjelmisto kunnioittavat yksityisyyttäsi, käy läpi seuraajaluettelosi ja poista tarvittaessa käyttäjiä. + followers_count: Seuraajien määrä + lock_link: Lukitse tili + purge: Poista seuraajista + success: + one: Estetään kevyesti seuraajia yhdestä verkkotunnuksesta... + other: Estetään kevyesti seuraajia %{count} verkkotunnuksesta... + true_privacy_html: Muista, että kunnollinen yksityisyys voidaan varmistaa vain päästä päähän -salauksella. + unlocked_warning_html: Kuka tahansa voi seurata sinua ja nähdä saman tien yksityiset tilapäivityksesi. %{lock_link}, niin voit tarkastaa ja torjua seuraajia. + unlocked_warning_title: Tiliäsi ei ole lukittu generic: - changes_saved_msg: Muutokset onnistuneesti tallennettu! - powered_by: powered by %{link} + changes_saved_msg: Muutosten tallennus onnistui! + powered_by: voimanlähteenä %{link} save_changes: Tallenna muutokset validation_errors: - one: Jokin ei ole viellä oikein! Katso virhe alapuolelta. - other: Jokin ei ole viellä oikein! Katso %{count} virhettä alapuolelta. + one: Kaikki ei ole aivan oikein! Tarkasta alla oleva virhe + other: Kaikki ei ole aivan oikein! Tarkasta alla olevat %{count} virhettä imports: - preface: Voit tuoda tiettyä dataa kaikista ihmisistä joita seuraat tai estät tilillesi tälle palvelimelle tiedostoista, jotka on luotu toisella palvelimella. - success: Datasi on onnistuneesti ladattu ja käsitellään pian + preface: Voit tuoda toisesta instanssista viemiäsi tietoja, kuten esimerkiksi seuraamiesi tai estämiesi henkilöiden listan. + success: Tietojen lähettäminen onnistui, ja ne käsitellään kohtapuoliin types: - blocking: Estetyt lista - following: Seuratut lista + blocking: Estettyjen lista + following: Seurattujen lista + muting: Mykistettyjen lista upload: Lähetä - landing_strip_html: "%{name} on käyttäjä domainilla %{link_to_root_path}. Voit seurata tai vuorovaikuttaa heidän kanssaan jos sinulla on tili yleisessä verkossa." - landing_strip_signup_html: Jos sinulla ei ole tiliä, voit rekisteröityä täällä. + in_memoriam_html: Muistoissamme. + invites: + delete: Poista käytöstä + expired: Vanhentunut + expires_in: + '1800': 30 minuuttia + '21600': 6 tuntia + '3600': 1 tunti + '43200': 12 tuntia + '86400': 1 vuorokausi + expires_in_prompt: Ei koskaan + generate: Luo + max_uses: + one: kertakäyttöinen + other: "%{count} käyttökertaa" + max_uses_prompt: Ei rajoitusta + prompt: Luo linkkejä ja jaa niiden avulla muille pääsyoikeus tähän instanssiin + table: + expires_at: Vanhenee + uses: Käytetty + title: Kutsu ihmisiä + landing_strip_html: "%{name} on käyttäjänä palvelimella %{link_to_root_path}. Voit seurata heitä tai pitää heihin yhteyttä, jos sinulla on tili missä tahansa fediversumin kolkassa." + landing_strip_signup_html: Jos sinulla ei ole tiliä, voit rekisteröityä tätä kautta. + lists: + errors: + limit: Sinulla on jo suurin sallittu määrä listoja + media_attachments: + validations: + images_and_video: Videota ei voi liittää tilapäivitykseen, jossa on jo kuvia + too_many: Tiedostoja voi liittää enintään 4 + migrations: + acct: uuden tilin käyttäjätunnus@verkkotunnus + currently_redirecting: 'Profiiliisi on asetettu uudelleenohjaus:' + proceed: Tallenna + updated_msg: Tilinsiirtoasetusten päivitys onnistui! + moderation: + title: Moderointi notification_mailer: digest: - body: 'Tässä on pieni yhteenveto palvelimelta %{instance} viimeksi kun olit paikalla %{since}:' + action: Näytä kaikki ilmoitukset + body: Tässä lyhyt yhteenveto viime käyntisi (%{since}) jälkeen tulleista viesteistä mention: "%{name} mainitsi sinut:" new_followers_summary: - one: Olet myös saanut yhden uuden seuraajan poissaollessasi! Jee! - other: Olet saanut %{count} uutta seuraajaa poissaollessasi! Loistavaa! + one: Olet myös saanut yhden uuden seuraajan! Juhuu! + other: Olet myös saanut %{count} uutta seuraajaa! Aivan mahtavaa! subject: - one: "1 uusi ilmoitus viimeisen käyntisi jälkeen \U0001F418" - other: "%{count} uutta ilmoitusta viimeisen käyntisi jälkeen \U0001F418" + one: "1 uusi ilmoitus viime käyntisi jälkeen \U0001F418" + other: "%{count} uutta ilmoitusta viime käyntisi jälkeen \U0001F418" + title: Poissaollessasi… favourite: - body: 'Statuksestasi tykkäsi %{name}:' - subject: "%{name} tykkäsi sinun statuksestasi" + body: "%{name} tykkäsi tilastasi:" + subject: "%{name} tykkäsi tilastasi" + title: Uusi tykkäys follow: body: "%{name} seuraa nyt sinua!" subject: "%{name} seuraa nyt sinua" + title: Uusi seuraaja follow_request: - body: "%{name} on pyytänyt seurata sinua" - subject: 'Odottava seuraus pyyntö: %{name}' + action: Hallinnoi seuraamispyyntöjä + body: "%{name} haluaa seurata sinua" + subject: 'Odottava seuraamispyyntö: %{name}' + title: Uusi seuraamispyyntö mention: - body: 'Sinut mainitsi %{name} postauksessa:' - subject: Sinut mainitsi %{name} + action: Vastaa + body: "%{name} mainitsi sinut:" + subject: "%{name} mainitsi sinut" + title: Uusi maininta reblog: - body: 'Sinun statustasi boostasi %{name}:' - subject: "%{name} boostasi statustasi" + body: "%{name} buustasi tilaasi:" + subject: "%{name} boostasi tilaasi" + title: Uusi buustaus number: human: decimal_units: - format: "%n%u" + format: "%n %u" units: - billion: B + billion: Mrd million: M - quadrillion: Q - thousand: K - trillion: T + quadrillion: Brd + thousand: k + trillion: B unit: '' pagination: + newer: Uudemmat next: Seuraava + older: Vanhemmat prev: Edellinen + truncate: "…" + preferences: + languages: Kielet + other: Muut + publishing: Julkaiseminen + web: Web + push_notifications: + favourite: + title: "%{name} tykkäsi tilastasi" + follow: + title: "%{name} seuraa nyt sinua" + group: + title: "%{count} ilmoitusta" + mention: + action_boost: Buustaa + action_expand: Näytä lisää + action_favourite: Tykkää + title: "%{nimi} mainitsi sinut" + reblog: + title: "%{name} buustasi tilaasi" remote_follow: - acct: Syötä sinun käyttäjänimesi@domain jos haluat seurata palvelimelta - missing_resource: Ei löydetty tarvittavaa uudelleenohjaavaa URL-linkkiä tilillesi - proceed: Siirry seuraamiseen - prompt: 'Sinä aiot seurata:' + acct: Syötä se käyttäjätunnus@verkkotunnus, josta haluat seurata + missing_resource: Vaadittavaa uudelleenohjaus-URL:ää tiliisi ei löytynyt + proceed: Siirry seuraamaan + prompt: 'Olet aikeissa seurata:' + sessions: + activity: Viimeisin toiminta + browser: Selain + browsers: + alipay: Alipay + blackberry: Blackberry + chrome: Chrome + edge: Microsoft Edge + electron: Electron + firefox: Firefox + generic: Tuntematon selain + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi -selain + opera: Opera + otter: Otter + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UCBrowser + weibo: Weibo + current_session: Nykyinen istunto + description: "%{selain}, %{platform}" + explanation: Nämä verkkoselaimet ovat tällä hetkellä kirjautuneet Mastodon-tilillesi. + ip: IP + platforms: + adobe_air: Adobe Air + android: Android + blackberry: Blackberry + chrome_os: ChromeOS + firefox_os: Firefox OS + ios: iOS + linux: Linux + mac: Mac + other: tuntematon järjestelmä + windows: Windows + windows_mobile: Windows Mobile + windows_phone: Windows Phone + revoke: Hylkää + revoke_success: Istunnon hylkäys onnistui + title: Istunnot settings: - authorized_apps: Valtuutetut ohjelmat + authorized_apps: Valtuutetut sovellukset back: Takaisin Mastodoniin + delete: Tilin poisto + development: Kehittäminen edit_profile: Muokkaa profiilia - export: Vie dataa - import: Tuo dataa + export: Vie tietoja + followers: Valtuutetut seuraajat + import: Tuo + migrate: Tilin muutto muualle + notifications: Ilmoitukset preferences: Ominaisuudet settings: Asetukset - two_factor_authentication: Kaksivaiheinen tunnistus + two_factor_authentication: Kaksivaiheinen todentaminen + your_apps: Omat sovellukset statuses: - open_in_web: Avaa webissä - over_character_limit: sallittu kirjanmäärä %{max} ylitetty + attached: + description: 'Liitetty: %{attached}' + image: + one: "%{count} kuva" + other: "%{count} kuvaa" + video: + one: "%{count} video" + other: "%{count} videota" + content_warning: 'Sisältövaroitus: %{warning}' + open_in_web: Avaa selaimessa + over_character_limit: merkkimäärän rajoitus %{max} ylitetty + pin_errors: + limit: Olet jo kiinnittänyt suurimman mahdollisen määrän tuuttauksia + ownership: Muiden tuuttauksia ei voi kiinnittää + private: Piilotettua tuuttausta ei voi kiinnittää + reblog: Buustausta ei voi kiinnittää show_more: Näytä lisää + title: "%{name}: ”%{quote}”" visibilities: - private: Näytä vain seuraajille + private: Vain seuraajille + private_long: Näytä vain seuraajille public: Julkinen - unlisted: Julkinen, mutta älä näytä julkisella aikajanalla + public_long: Kaikki voivat nähdä + unlisted: Listaamaton julkinen + unlisted_long: Kaikki voivat nähdä, mutta ei näytetä julkisilla aikajanoilla stream_entries: - click_to_show: Klikkaa näyttääksesi - reblogged: boosted - sensitive_content: Herkkä materiaali + click_to_show: Katso napsauttamalla + pinned: Kiinnitetty tuuttaus + reblogged: buustasi + sensitive_content: Arkaluontoista sisältöä + terms: + title: "%{instance}, käyttöehdot ja tietosuojakäytäntö" + themes: + default: Mastodon time: formats: - default: "%b %d, %Y, %H:%M" + default: "%d.%m.%Y klo %H.%M" two_factor_authentication: - description_html: Jos otat käyttöön kaksivaiheisen tunnistuksen, kirjautumiseen vaaditaan puhelin, joka voi luoda tokeneita kirjautumista varten. + code_hint: Vahvista syöttämällä todentamissovelluksen generoima koodi + description_html: Jos otat käyttöön kaksivaiheisen todentamisen, kirjautumiseen vaaditaan puhelin, jolla voidaan luoda kirjautumistunnuksia. disable: Poista käytöstä enable: Ota käyttöön - instructions_html: "Skannaa tämä QR-koodi Google Authenticator- tai vastaavaan sovellukseen puhelimellasi. Tästä hetkestä lähtien ohjelma luo koodin, mikä sinun tarvitsee syöttää sisäänkirjautuessa." + enabled: Kaksivaiheinen todentaminen käytössä + enabled_success: Kaksivaiheisen todentamisen käyttöönotto onnistui + generate_recovery_codes: Luo palautuskoodit + instructions_html: "Lue tämä QR-koodi puhelimen Google Authenticator- tai vastaavalla TOTP-sovelluksella. Sen jälkeen sovellus luo tunnuksia, joita tarvitset sisäänkirjautuessasi." + lost_recovery_codes: Palautuskoodien avulla voit käyttää tiliä, jos menetät puhelimesi. Jos olet hukannut palautuskoodit, voit luoda uudet tästä. Vanhat palautuskoodit poistetaan käytöstä. + manual_instructions: 'Jos et voi lukea QR-koodia ja haluat syöttää sen käsin, tässä on salainen koodi tekstinä:' + recovery_codes: Varapalautuskoodit + recovery_codes_regenerated: Uusien palautuskoodien luonti onnistui + recovery_instructions_html: Jos menetät puhelimesi, voit kirjautua tilillesi jollakin alla olevista palautuskoodeista. Pidä palautuskoodit hyvässä tallessa. Voit esimerkiksi tulostaa ne ja säilyttää muiden tärkeiden papereiden joukossa. + setup: Ota käyttöön + wrong_code: Annettu koodi oli virheellinen! Ovatko palvelimen aika ja laitteen aika oikein? + user_mailer: + backup_ready: + explanation: Pyysit täydellistä varmuuskopiota Mastodon-tilistäsi. Voit nyt ladata sen! + subject: Arkisto on valmiina ladattavaksi + title: Arkiston tallennus + welcome: + edit_profile_action: Aseta profiili + edit_profile_step: Voit mukauttaa profiiliasi lataamalla profiilikuvan ja otsakekuvan, muuttamalla näyttönimeäsi ym. Jos haluat hyväksyä uudet seuraajat ennen kuin he voivat seurata sinua, voit lukita tilisi. + explanation: Näillä vinkeillä pääset alkuun + final_action: Ala julkaista + final_step: 'Ala julkaista! Vaikkei sinulla olisi seuraajia, monet voivat nähdä julkiset viestisi esimerkiksi paikallisella aikajanalla ja hashtagien avulla. Kannattaa esittäytyä! Käytä hashtagia #introductions. (Jos haluat esittäytyä myös suomeksi, se kannattaa tehdä erillisessä tuuttauksessa ja käyttää hashtagia #esittely.)' + full_handle: Koko käyttäjätunnuksesi + full_handle_hint: Kerro tämä ystävillesi, niin he voivat lähettää sinulle viestejä tai löytää sinut toisen instanssin kautta. + review_preferences_action: Muuta asetuksia + review_preferences_step: Käy tarkistamassa, että asetukset ovat haluamallasi tavalla. Voit valita, missä tilanteissa haluat saada sähköpostia, mikä on julkaisujesi oletusnäkyvyys jne. Jos et saa helposti pahoinvointia, voit valita, että GIF-animaatiot toistetaan automaattisesti. + subject: Tervetuloa Mastodoniin + tip_bridge_html: Jos tulet Twitteristä, voit etsiä ystäviäsi Mastodonista siltasovelluksen avulla. Se kuitenkin löytää heidät vain, jos hekin käyttävät sitä! + tip_federated_timeline: Yleinen aikajana näyttää sisältöä koko Mastodon-verkostosta. Siinä näkyvät kuitenkin vain ne henkilöt, joita oman instanssisi käyttäjät seuraavat. Siinä ei siis näytetä aivan kaikkea. + tip_following: Oletusarvoisesti seuraat oman palvelimesi ylläpitäjiä. Etsi lisää kiinnostavia ihmisiä paikalliselta ja yleiseltä aikajanalta. + tip_local_timeline: Paikallinen aikajana näyttää instanssin %{instance} käyttäjien julkaisut. He ovat naapureitasi! + tip_mobile_webapp: Jos voit lisätä Mastodonin mobiiliselaimen kautta aloitusnäytöllesi, voit vastaanottaa push-ilmoituksia. Toiminta vastaa monin tavoin tavanomaista sovellusta! + tips: Vinkkejä + title: Tervetuloa mukaan, %name}! users: - invalid_email: Virheellinen sähköposti - invalid_otp_token: Virheellinen kaksivaihetunnistuskoodi + invalid_email: Virheellinen sähköpostiosoite + invalid_otp_token: Virheellinen kaksivaiheisen todentamisen koodi + seamless_external_login: Olet kirjautunut ulkoisen palvelun kautta, joten salasana- ja sähköpostiasetukset eivät ole käytettävissä. + signed_in_as: 'Kirjautunut henkilönä:' diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 960dd38ef..fecf996d5 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -18,7 +18,7 @@ ja: features: humane_approach_body: 他の SNS の失敗から学び、Mastodon はソーシャルメディアが誤った使い方をされることの無いように倫理的な設計を目指しています。 humane_approach_title: より思いやりのある設計 - not_a_product_body: Mastodon は営利的な SNS ではありません。広告や、データの収集・解析は無く、またユーザーの囲い込みもありません。 + not_a_product_body: Mastodon は営利的な SNS ではありません。広告や、データの収集・解析は無く、またユーザーの囲い込みもありません。ここには中央権力はありません。 not_a_product_title: あなたは人間であり、商品ではありません real_conversation_body: 好きなように書ける500文字までの投稿や、文章やメディアの内容に警告をつけられる機能で、思い通りに自分自身を表現することができます。 real_conversation_title: 本当のコミュニケーションのために diff --git a/config/locales/simple_form.eo.yml b/config/locales/simple_form.eo.yml index 17862f916..41a0c26aa 100644 --- a/config/locales/simple_form.eo.yml +++ b/config/locales/simple_form.eo.yml @@ -14,7 +14,7 @@ eo: one: 1 signo restas other: %{count} signoj restas setting_noindex: Influas vian publikan profilon kaj mesaĝajn paĝojn - setting_theme: Influas kiel Mastodon aspektas kiam vi ensalutis en ajna aparato. + setting_theme: Influas kiel Mastodon aspektas post ensaluto de ajna aparato. imports: data: CSV-dosiero el alia nodo de Mastodon sessions: @@ -45,7 +45,7 @@ eo: setting_default_privacy: Mesaĝa videbleco setting_default_sensitive: Ĉiam marki aŭdovidaĵojn tiklaj setting_delete_modal: Montri fenestron por konfirmi antaŭ ol forigi mesaĝon - setting_display_sensitive_media: Ĉiam montri aŭdovidaĵon markitajn tiklaj + setting_display_sensitive_media: Ĉiam montri aŭdovidaĵojn markitajn tiklaj setting_noindex: Ellistiĝi de retserĉila indeksado setting_reduce_motion: Malrapidigi animaciojn setting_system_font_ui: Uzi la dekomencan tiparon de la sistemo diff --git a/config/locales/simple_form.fi.yml b/config/locales/simple_form.fi.yml index 34605c4f6..f48e9ab23 100644 --- a/config/locales/simple_form.fi.yml +++ b/config/locales/simple_form.fi.yml @@ -3,64 +3,68 @@ fi: simple_form: hints: defaults: - 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 - display_name: Korkeintaan 30 merkkiä - header: PNG, GIF tai JPG. Korkeintaan 2MB. Skaalataan kokoon 700x335px - locked: Vaatii sinua manuaalisesti hyväksymään seuraajat - 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. + avatar: PNG, GIF tai JPG. Enintään 2 Mt. Skaalataan kokoon 400 x 400 px + digest: Lähetetään vain pitkän poissaolon jälkeen ja vain, jos olet saanut suoria viestejä poissaolosi aikana + display_name: + one: 1 merkki jäljellä + other: %{count} merkkiä jäljellä + header: PNG, GIF tai JPG. Enintään 2 Mt. Skaalataan kokoon 700 x 335 px + locked: Sinun täytyy hyväksyä seuraajat manuaalisesti + note: + one: 1 merkki jäljellä + other: %{count} merkkiä jäljellä + setting_noindex: Vaikuttaa julkiseen profiiliisi ja tilasivuihisi + setting_theme: Vaikuttaa Mastodonin ulkoasuun millä tahansa laitteella kirjauduttaessa. imports: - data: CSV tiedosto, joka on tuotu toiselta Mastodon-palvelimelta + data: Toisesta Mastodon-instanssista tuotu CSV-tiedosto sessions: - otp: Syötä kaksivaiheisen tunnistuksen koodi puhelimestasi tai käytä yhtä palautuskoodeistasi. + otp: Syötä puhelimeen saamasi kaksivaiheisen tunnistautumisen koodi tai käytä palautuskoodia. user: - filtered_languages: Valitut kielet suodatetaan julkisilta aikajanoilta + filtered_languages: Valitut kielet suodatetaan pois julkisilta aikajanoilta labels: defaults: avatar: Profiilikuva - confirm_new_password: Varmista uusi salasana - confirm_password: Varmista salasana + confirm_new_password: Vahvista uusi salasana + confirm_password: Vahvista salasana current_password: Nykyinen salasana - data: Data + data: Tiedot display_name: Nimimerkki email: Sähköpostiosoite - expires_in: Vanhentuu + expires_in: Vanhenee filtered_languages: Suodatetut kielet header: Otsakekuva locale: Kieli - locked: Tee tilistä yksityinen - max_uses: Max käyttökerrat + locked: Lukitse tili + max_uses: Käyttökertoja enintään new_password: Uusi salasana note: Kuvaus - otp_attempt: Kaksivaiheinen koodi + otp_attempt: Kaksivaiheisen tunnistautumisen koodi password: Salasana - setting_auto_play_gif: Animoitujen GIFfien automaattitoisto - setting_boost_modal: Näytä vahvistusikkuna ennen boostausta - setting_default_privacy: Julkaisun yksityisyys + setting_auto_play_gif: Toista GIF-animaatiot automaattisesti + setting_boost_modal: Kysy vahvistusta ennen buustausta + setting_default_privacy: Julkaisun näkyvyys setting_default_sensitive: Merkitse media aina arkaluontoiseksi - setting_delete_modal: Näytä vahvistusikkuna ennen töötin poistamista + setting_delete_modal: Kysy vahvistusta ennen tuuttauksen 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_reduce_motion: Vähennä animaatioiden liikettä + setting_system_font_ui: Käytä järjestelmän oletusfonttia setting_theme: Sivuston teema - setting_unfollow_modal: Näytä vahvistusikkuna ennen seuraamisen lopettamista - severity: Vakavuusaste - type: Tuontityyppi + setting_unfollow_modal: Kysy vahvistusta, ennen kuin lopetat seuraamisen + severity: Vakavuus + type: Tietojen laji username: Käyttäjänimi username_or_email: Käyttäjänimi tai sähköposti interactions: 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_dm: Estä suorat viestit ihmisiltä, joita et seuraa + must_be_following_dm: Estä suorat viestit käyttäjiltä, joita et seuraa notification_emails: - digest: Lähetä koosteviestejä sähköpostilla - favourite: Lähetä sähköposti, kun joku tykkää statuksestasi + digest: Lähetä koosteviestejä sähköpostitse + favourite: Lähetä sähköposti, kun joku tykkää tilastasi follow: Lähetä sähköposti, kun joku seuraa sinua follow_request: Lähetä sähköposti, kun joku pyytää seurata sinua - mention: Lähetä sähköposti, kun joku mainitsee sinut + mention: Lähetä sähköposti, kun sinut mainitaan reblog: Lähetä sähköposti, kun joku buustaa julkaisusi 'no': Ei required: diff --git a/config/locales/simple_form.sk.yml b/config/locales/simple_form.sk.yml index 7d4241bac..e504c9774 100644 --- a/config/locales/simple_form.sk.yml +++ b/config/locales/simple_form.sk.yml @@ -6,21 +6,21 @@ sk: avatar: PNG, GIF alebo JPG. Maximálne 2MB. Bude zmenšený na 400x400px digest: Odoslané iba v prípade dlhodobej neprítomnosti, a len ak ste obdŕžali nejaké osobné správy kým ste boli preč display_name: - one: Ostáva vám 1 znak - other: Ostáva vám %{count} znakov + one: Ostáva ti 1 znak + other: Ostáva ti %{count} znakov header: PNG, GIF alebo JPG. Maximálne 2MB. Bude zmenšený na 700x335px locked: Musíte manuálne schváliť sledujúcich note: one: Ostáva vám 1 znak - other: Ostáva vám %{count}znakov - setting_noindex: Ovplyvňuje profil a správy tak, že ich nebude možné nájsť vyhľadávaním - setting_theme: Ovplyvní ako bude Mastodon vyzerať pri prihlásení z hociktorého zariadenia. + other: Ostáva ti %{count} znakov + setting_noindex: Ovplyvňuje profil a správy tak, že ich nebude možné nájsť vyhľadávaním + setting_theme: Toto ovplyvní ako bude Mastodon vyzerať pri prihlásení z hociktorého zariadenia. imports: data: CSV súbor vyexportovaný z inej Mastodon inštancie sessions: - otp: Vložte 2FA kód z telefónu alebo použite jeden z vašich obnovovacích kódov. + otp: Napíš sem dvoj-faktorový kód z telefónu, alebo použite jeden z vašich obnovovacích kódov. user: - filtered_languages: Zaškrtnuté jazyky vám nebudú zobrazené vo verejnej časovej osi + filtered_languages: Zaškrtnuté jazyky budú pre teba vynechané nebudú z verejnej časovej osi labels: defaults: avatar: Avatar @@ -53,7 +53,7 @@ sk: setting_unfollow_modal: Zobrazovať potvrdzovacie okno pred skončením sledovania iného používateľa severity: Závažnosť type: Typ importu - username: Používateľské meno + username: Užívateľské meno username_or_email: Prezívka, alebo Email interactions: must_be_follower: Blokovať notifikácie pod používateľov, ktorí ťa nesledujú diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 2ee25b372..25e672604 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -149,15 +149,15 @@ sk: enable_custom_emoji: "%{name} povolil emoji %{target}" enable_user: "%{name} povolil prihlásenie pre používateľa %{target}" memorialize_account: '%{name} zmenil účet %{target} na stránku "Navždy budeme spomínať"' - promote_user: "%{name} povýšil používateľa %{target}" - reset_password_user: "%{name} resetoval heslo pre používateľa %{target}" - resolve_report: "%{name} zamietol nahlásenie %{target}" - silence_account: "%{name} stíšil účet %{target}" - suspend_account: "%{name} suspendoval účet používateľa %{target}" - unsilence_account: "%{name} zrušil stíšenie účtu používateľa %{target}" - unsuspend_account: "%{name} zrušil suspendáciu účtu používateľa %{target}" - update_custom_emoji: "%{name} aktualizoval emoji %{target}" - update_status: "%{name} aktualizoval status %{target}" + promote_user: "%{name} povýšil/a používateľa %{target}" + reset_password_user: "%{name} resetoval/a heslo pre používateľa %{target}" + resolve_report: "%{name} zamietli nahlásenie %{target}" + silence_account: "%{name} utíšil/a účet %{target}" + suspend_account: "%{name} zablokoval/a účet používateľa %{target}" + unsilence_account: "%{name} zrušil/a utíšenie účtu používateľa %{target}" + unsuspend_account: "%{name} zrušil/a blokovanie účtu používateľa %{target}" + update_custom_emoji: "%{name} aktualizoval/a emoji %{target}" + update_status: "%{name} aktualizoval/a status pre %{target}" title: Kontrólny záznam custom_emojis: by_domain: Doména @@ -358,7 +358,7 @@ sk: warning: Na tieto údaje dávajte ohromný pozor. Nikdy ich s nikým nezďieľajte! your_token: Váš prístupový token auth: - agreement_html: V rámci registrácie súhlasíte, že sa budete riadiť 1 pravidlami tejto instancie 2 a taktiež 3 našími servisnými podmienkami 4. + agreement_html: V rámci registrácie súhlasíš, že sa budeš riadiť pravidlami tejto instancie, a taktiež našími servisnými podmienkami . change_password: Heslo confirm_email: Potvrdiť email delete_account: Vymazať účet @@ -366,8 +366,8 @@ sk: didnt_get_confirmation: Neobdŕžali ste kroky pre potvrdenie? forgot_password: Zabudli ste heslo? invalid_reset_password_token: Token na obnovu hesla vypršal. Prosím vypítajte si nový. - login: Prihlásenie - logout: Odhlásiť sa + login: Prihlás sa + logout: Odhlás sa migrate_account: Presunúť sa na iný účet migrate_account_html: Pokiaľ si želáte presmerovať tento účet na nejaký iný, môžete tak urobiť tu. or: alebo @@ -375,7 +375,7 @@ sk: providers: cas: CAS saml: SAML - register: Zaregistrovať sa + register: Zaregistruj sa register_elsewhere: Zaregistruj sa na inom serveri resend_confirmation: Poslať potvrdzujúce pokyny znovu reset_password: Resetovať heslo @@ -677,6 +677,7 @@ sk: 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_step: Daj si záležať na svojích nastaveniach, napríklad že aké emailové notifikácie chceš dostávať, alebo pod aký level súkromia sa tvoje príspevky majú sami automaticky zaradiť. Pokiaľ nemáš malátnosť z pohybu, môžeš si zvoliť aj automatické spúšťanie GIF animácií. subject: Vitaj na Mastodone tip_bridge_html: Ak prichádzaš z Twitteru, môžeš svojích priateľov nájsť na Mastodone pomocou tzv. mostíkovej aplikácie. 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á. From a1049e93807f57796eb6229f2dd1388be8b18225 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 16 Apr 2018 17:04:31 +0900 Subject: [PATCH 081/442] Redirect to account status page for page of status stream entry (#7104) Commit 519119f657cf97ec187008a28dba00c1125a9292 missed a change for stream entry page. Instead of duplicating the change, redirect to account status page. It would also help crawlers (of search engines, for example) to understand a stream entry URL and its corresponding status URL points to the same page. --- app/controllers/stream_entries_controller.rb | 3 +-- spec/controllers/stream_entries_controller_spec.rb | 10 +++------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb index f81856cc6..97cf85079 100644 --- a/app/controllers/stream_entries_controller.rb +++ b/app/controllers/stream_entries_controller.rb @@ -15,8 +15,7 @@ class StreamEntriesController < ApplicationController def show respond_to do |format| format.html do - @ancestors = @stream_entry.activity.reply? ? cache_collection(@stream_entry.activity.ancestors(current_account), Status) : [] - @descendants = cache_collection(@stream_entry.activity.descendants(current_account), Status) + redirect_to short_account_status_url(params[:account_username], @stream_entry.activity) if @type == 'status' end format.atom do diff --git a/spec/controllers/stream_entries_controller_spec.rb b/spec/controllers/stream_entries_controller_spec.rb index f81e2be7b..665c5b747 100644 --- a/spec/controllers/stream_entries_controller_spec.rb +++ b/spec/controllers/stream_entries_controller_spec.rb @@ -66,16 +66,12 @@ RSpec.describe StreamEntriesController, type: :controller do describe 'GET #show' do include_examples 'before_action', :show - it 'renders with HTML' do - ancestor = Fabricate(:status) - status = Fabricate(:status, in_reply_to_id: ancestor.id) - descendant = Fabricate(:status, in_reply_to_id: status.id) + it 'redirects to status page' do + status = Fabricate(:status) get :show, params: { account_username: status.account.username, id: status.stream_entry.id } - expect(assigns(:ancestors)).to eq [ancestor] - expect(assigns(:descendants)).to eq [descendant] - expect(response).to have_http_status(:success) + expect(response).to redirect_to(short_account_status_url(status.account, status)) end it 'returns http success with Atom' do From 1a37d7e252aa41fd1c66e780e1a2a1426d8f3545 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Mon, 16 Apr 2018 18:34:34 +0900 Subject: [PATCH 082/442] Fix status filtering in contexts reducer (#7149) --- app/javascript/mastodon/reducers/contexts.js | 38 +++++++++++--------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/app/javascript/mastodon/reducers/contexts.js b/app/javascript/mastodon/reducers/contexts.js index fe8308d0c..c1ecf7e8a 100644 --- a/app/javascript/mastodon/reducers/contexts.js +++ b/app/javascript/mastodon/reducers/contexts.js @@ -21,24 +21,30 @@ const normalizeContext = (state, id, ancestors, descendants) => { }); }; -const deleteFromContexts = (state, id) => { - state.getIn(['descendants', id], ImmutableList()).forEach(descendantId => { - state = state.updateIn(['ancestors', descendantId], ImmutableList(), list => list.filterNot(itemId => itemId === id)); - }); +const deleteFromContexts = (immutableState, ids) => immutableState.withMutations(state => { + state.update('ancestors', immutableAncestors => immutableAncestors.withMutations(ancestors => { + state.update('descendants', immutableDescendants => immutableDescendants.withMutations(descendants => { + ids.forEach(id => { + descendants.get(id, ImmutableList()).forEach(descendantId => { + ancestors.update(descendantId, ImmutableList(), list => list.filterNot(itemId => itemId === id)); + }); - state.getIn(['ancestors', id], ImmutableList()).forEach(ancestorId => { - state = state.updateIn(['descendants', ancestorId], ImmutableList(), list => list.filterNot(itemId => itemId === id)); - }); + ancestors.get(id, ImmutableList()).forEach(ancestorId => { + descendants.update(ancestorId, ImmutableList(), list => list.filterNot(itemId => itemId === id)); + }); - state = state.deleteIn(['descendants', id]).deleteIn(['ancestors', id]); + descendants.delete(id); + ancestors.delete(id); + }); + })); + })); +}); - return state; -}; +const filterContexts = (state, relationship, statuses) => { + const ownedStatusIds = statuses.filter(status => status.get('account') === relationship.id) + .map(status => status.get('id')); -const filterContexts = (state, relationship) => { - return state.map( - statuses => statuses.filter( - status => status.get('account') !== relationship.id)); + return deleteFromContexts(state, ownedStatusIds); }; const updateContext = (state, status, references) => { @@ -61,11 +67,11 @@ export default function contexts(state = initialState, action) { switch(action.type) { case ACCOUNT_BLOCK_SUCCESS: case ACCOUNT_MUTE_SUCCESS: - return filterContexts(state, action.relationship); + return filterContexts(state, action.relationship, action.statuses); case CONTEXT_FETCH_SUCCESS: return normalizeContext(state, action.id, action.ancestors, action.descendants); case TIMELINE_DELETE: - return deleteFromContexts(state, action.id); + return deleteFromContexts(state, [action.id]); case TIMELINE_CONTEXT_UPDATE: return updateContext(state, action.status, action.references); default: From 1c379b7ef46edeaa7a8238902546ed7215b02fe2 Mon Sep 17 00:00:00 2001 From: ThibG Date: Mon, 16 Apr 2018 17:19:04 +0200 Subject: [PATCH 083/442] Remove extra spaces from search API queries and public account headers (fixes #7129) (#7152) --- app/services/search_service.rb | 2 +- app/views/accounts/_header.html.haml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/services/search_service.rb b/app/services/search_service.rb index 00a8b3dd7..5bb395942 100644 --- a/app/services/search_service.rb +++ b/app/services/search_service.rb @@ -4,7 +4,7 @@ class SearchService < BaseService attr_accessor :query, :account, :limit, :resolve def call(query, limit, resolve = false, account = nil) - @query = query + @query = query.strip @account = account @limit = limit @resolve = resolve diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index 0d3a0d08d..f246f5326 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -6,8 +6,8 @@ .card__bio %h1.name %span.p-name.emojify= display_name(account) - %small - %span @#{account.local_username_and_domain} + %small< + %span>< @#{account.local_username_and_domain} = fa_icon('lock') if account.locked? - if Setting.show_staff_badge From 7e0aed398f7e3e74a3c15e426082f88cf8b79bcf Mon Sep 17 00:00:00 2001 From: ThibG Date: Mon, 16 Apr 2018 21:04:24 +0200 Subject: [PATCH 084/442] Fix scrolling behavior (#7151) * Update React.JS * Use React's new lifecycles for scrollable lists * Clean up dead code * Make CodeClimate happy --- .../mastodon/components/scrollable_list.js | 32 +++++++------------ package.json | 4 +-- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/app/javascript/mastodon/components/scrollable_list.js b/app/javascript/mastodon/components/scrollable_list.js index ee07106f7..fd6858d05 100644 --- a/app/javascript/mastodon/components/scrollable_list.js +++ b/app/javascript/mastodon/components/scrollable_list.js @@ -34,7 +34,7 @@ export default class ScrollableList extends PureComponent { }; state = { - lastMouseMove: null, + fullscreen: null, }; intersectionObserverWrapper = new IntersectionObserverWrapper(); @@ -43,7 +43,6 @@ export default class ScrollableList extends PureComponent { if (this.node) { const { scrollTop, scrollHeight, clientHeight } = this.node; const offset = scrollHeight - scrollTop - clientHeight; - this._oldScrollPosition = scrollHeight - scrollTop; if (400 > offset && this.props.onLoadMore && !this.props.isLoading) { this.props.onLoadMore(); @@ -59,14 +58,6 @@ export default class ScrollableList extends PureComponent { trailing: true, }); - handleMouseMove = throttle(() => { - this._lastMouseMove = new Date(); - }, 300); - - handleMouseLeave = () => { - this._lastMouseMove = null; - } - componentDidMount () { this.attachScrollListener(); this.attachIntersectionObserver(); @@ -76,21 +67,26 @@ export default class ScrollableList extends PureComponent { this.handleScroll(); } - componentDidUpdate (prevProps) { + getSnapshotBeforeUpdate (prevProps) { const someItemInserted = React.Children.count(prevProps.children) > 0 && React.Children.count(prevProps.children) < React.Children.count(this.props.children) && this.getFirstChildKey(prevProps) !== this.getFirstChildKey(this.props); + if (someItemInserted && this.node.scrollTop > 0) { + return this.node.scrollHeight - this.node.scrollTop; + } else { + return null; + } + } + componentDidUpdate (prevProps, prevState, snapshot) { // Reset the scroll position when a new child comes in in order not to // jerk the scrollbar around if you're already scrolled down the page. - if (someItemInserted && this._oldScrollPosition && this.node.scrollTop > 0) { - const newScrollTop = this.node.scrollHeight - this._oldScrollPosition; + if (snapshot !== null) { + const newScrollTop = this.node.scrollHeight - snapshot; if (this.node.scrollTop !== newScrollTop) { this.node.scrollTop = newScrollTop; } - } else { - this._oldScrollPosition = this.node.scrollHeight - this.node.scrollTop; } } @@ -143,10 +139,6 @@ export default class ScrollableList extends PureComponent { this.props.onLoadMore(); } - _recentlyMoved () { - return this._lastMouseMove !== null && ((new Date()) - this._lastMouseMove < 600); - } - render () { const { children, scrollKey, trackScroll, shouldUpdateScroll, isLoading, hasMore, prepend, emptyMessage, onLoadMore } = this.props; const { fullscreen } = this.state; @@ -157,7 +149,7 @@ export default class ScrollableList extends PureComponent { if (isLoading || childrenCount > 0 || !emptyMessage) { scrollableArea = ( -
+
{prepend} diff --git a/package.json b/package.json index 9858d28d4..75e9e9f6d 100644 --- a/package.json +++ b/package.json @@ -83,8 +83,8 @@ "prop-types": "^15.5.10", "punycode": "^2.1.0", "rails-ujs": "^5.1.2", - "react": "^16.2.0", - "react-dom": "^16.2.0", + "react": "^16.3.0", + "react-dom": "^16.3.0", "react-hotkeys": "^0.10.0", "react-immutable-proptypes": "^2.1.0", "react-immutable-pure-component": "^1.1.1", From 3c722fe687de0c6e6ba144a8c87c34465fca3af9 Mon Sep 17 00:00:00 2001 From: Remi Rampin Date: Tue, 17 Apr 2018 04:37:51 +0200 Subject: [PATCH 085/442] Update French javascript locale file (#7165) Match config/locales/fr.yml: "private" was changed to "followers-only" by 501514960a9de238e23cd607d2e8f4c1ff9f16c1. --- app/javascript/mastodon/locales/fr.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index e340fda9e..ef1c115e3 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -205,8 +205,8 @@ "privacy.change": "Ajuster la confidentialité du message", "privacy.direct.long": "N'envoyer qu'aux personnes mentionnées", "privacy.direct.short": "Direct", - "privacy.private.long": "N'envoyer qu'à vos abonné⋅e⋅s", - "privacy.private.short": "Privé", + "privacy.private.long": "Seul⋅e⋅s vos abonné⋅e⋅s verront vos statuts", + "privacy.private.short": "Abonné⋅e⋅s uniquement", "privacy.public.long": "Afficher dans les fils publics", "privacy.public.short": "Public", "privacy.unlisted.long": "Ne pas afficher dans les fils publics", From 609bf9302976768c56116f9d920f34a430d538b5 Mon Sep 17 00:00:00 2001 From: abcang Date: Tue, 17 Apr 2018 20:49:09 +0900 Subject: [PATCH 086/442] Perform processing that does not use the database before connecting to the database (#7168) --- streaming/index.js | 89 +++++++++++++++++++++++----------------------- 1 file changed, 45 insertions(+), 44 deletions(-) diff --git a/streaming/index.js b/streaming/index.js index a42a91242..1b4f8596c 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -329,52 +329,53 @@ const startWorker = (workerId) => { // Only messages that may require filtering are statuses, since notifications // are already personalized and deletes do not matter - if (needsFiltering && event === 'update') { - pgPool.connect((err, client, done) => { - if (err) { - log.error(err); - return; - } - - const unpackedPayload = payload; - 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) { - log.silly(req.requestId, `Message ${unpackedPayload.id} filtered by language (${unpackedPayload.language})`); - done(); - return; - } - - if (req.accountId) { - const queries = [ - client.query(`SELECT 1 FROM blocks WHERE (account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 2)})) OR (account_id = $2 AND target_account_id = $1) UNION SELECT 1 FROM mutes WHERE account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 2)})`, [req.accountId, unpackedPayload.account.id].concat(targetAccountIds)), - ]; - - if (accountDomain) { - queries.push(client.query('SELECT 1 FROM account_domain_blocks WHERE account_id = $1 AND domain = $2', [req.accountId, accountDomain])); - } - - Promise.all(queries).then(values => { - done(); - - if (values[0].rows.length > 0 || (values.length > 1 && values[1].rows.length > 0)) { - return; - } - - transmit(); - }).catch(err => { - done(); - log.error(err); - }); - } else { - done(); - transmit(); - } - }); - } else { + if (!needsFiltering || event !== 'update') { transmit(); + return; } + + const unpackedPayload = payload; + 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) { + log.silly(req.requestId, `Message ${unpackedPayload.id} filtered by language (${unpackedPayload.language})`); + return; + } + + // When the account is not logged in, it is not necessary to confirm the block or mute + if (!req.accountId) { + transmit(); + return; + } + + pgPool.connect((err, client, done) => { + if (err) { + log.error(err); + return; + } + + const queries = [ + client.query(`SELECT 1 FROM blocks WHERE (account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 2)})) OR (account_id = $2 AND target_account_id = $1) UNION SELECT 1 FROM mutes WHERE account_id = $1 AND target_account_id IN (${placeholders(targetAccountIds, 2)})`, [req.accountId, unpackedPayload.account.id].concat(targetAccountIds)), + ]; + + if (accountDomain) { + queries.push(client.query('SELECT 1 FROM account_domain_blocks WHERE account_id = $1 AND domain = $2', [req.accountId, accountDomain])); + } + + Promise.all(queries).then(values => { + done(); + + if (values[0].rows.length > 0 || (values.length > 1 && values[1].rows.length > 0)) { + return; + } + + transmit(); + }).catch(err => { + done(); + log.error(err); + }); + }); }; subscribe(`${redisPrefix}${id}`, listener); From 727917e91eea3582586d3ce710826ef33ae26ffb Mon Sep 17 00:00:00 2001 From: abcang Date: Tue, 17 Apr 2018 20:50:33 +0900 Subject: [PATCH 087/442] Fix caret position after inserting emoji (#7167) --- app/javascript/mastodon/actions/compose.js | 3 ++- .../mastodon/features/compose/components/compose_form.js | 9 +++++++-- .../compose/containers/compose_form_container.js | 4 ++-- app/javascript/mastodon/reducers/compose.js | 7 ++----- 4 files changed, 13 insertions(+), 10 deletions(-) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 59aa6f98d..ea9d9f359 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -446,11 +446,12 @@ export function changeComposeVisibility(value) { }; }; -export function insertEmojiCompose(position, emoji) { +export function insertEmojiCompose(position, emoji, needsSpace) { return { type: COMPOSE_EMOJI_INSERT, position, emoji, + needsSpace, }; }; diff --git a/app/javascript/mastodon/features/compose/components/compose_form.js b/app/javascript/mastodon/features/compose/components/compose_form.js index fe7bb1cb3..39eb02362 100644 --- a/app/javascript/mastodon/features/compose/components/compose_form.js +++ b/app/javascript/mastodon/features/compose/components/compose_form.js @@ -19,6 +19,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; import { length } from 'stringz'; import { countableText } from '../util/counter'; +const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d'; + const messages = defineMessages({ placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' }, spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Write your warning here' }, @@ -144,10 +146,13 @@ export default class ComposeForm extends ImmutablePureComponent { } handleEmojiPick = (data) => { + const { text } = this.props; const position = this.autosuggestTextarea.textarea.selectionStart; const emojiChar = data.native; - this._restoreCaret = position + emojiChar.length + 1; - this.props.onPickEmoji(position, data); + const needsSpace = data.custom && position > 0 && !allowedAroundShortCode.includes(text[position - 1]); + + this._restoreCaret = position + emojiChar.length + 1 + (needsSpace ? 1 : 0); + this.props.onPickEmoji(position, data, needsSpace); } render () { diff --git a/app/javascript/mastodon/features/compose/containers/compose_form_container.js b/app/javascript/mastodon/features/compose/containers/compose_form_container.js index ede23d361..c3aa580ee 100644 --- a/app/javascript/mastodon/features/compose/containers/compose_form_container.js +++ b/app/javascript/mastodon/features/compose/containers/compose_form_container.js @@ -56,8 +56,8 @@ const mapDispatchToProps = (dispatch) => ({ dispatch(uploadCompose(files)); }, - onPickEmoji (position, data) { - dispatch(insertEmojiCompose(position, data)); + onPickEmoji (position, data, needsSpace) { + dispatch(insertEmojiCompose(position, data, needsSpace)); }, }); diff --git a/app/javascript/mastodon/reducers/compose.js b/app/javascript/mastodon/reducers/compose.js index 87049ea79..46d9d6c8e 100644 --- a/app/javascript/mastodon/reducers/compose.js +++ b/app/javascript/mastodon/reducers/compose.js @@ -36,8 +36,6 @@ import { Map as ImmutableMap, List as ImmutableList, OrderedSet as ImmutableOrde import uuid from '../uuid'; import { me } from '../initial_state'; -const allowedAroundShortCode = '><\u0085\u0020\u00a0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\u2028\u2029\u0009\u000a\u000b\u000c\u000d'; - const initialState = ImmutableMap({ mounted: 0, sensitive: false, @@ -137,9 +135,8 @@ const updateSuggestionTags = (state, token) => { }); }; -const insertEmoji = (state, position, emojiData) => { +const insertEmoji = (state, position, emojiData, needsSpace) => { const oldText = state.get('text'); - const needsSpace = emojiData.custom && position > 0 && !allowedAroundShortCode.includes(oldText[position - 1]); const emoji = needsSpace ? ' ' + emojiData.native : emojiData.native; return state.merge({ @@ -288,7 +285,7 @@ export default function compose(state = initialState, action) { return state; } case COMPOSE_EMOJI_INSERT: - return insertEmoji(state, action.position, action.emoji); + return insertEmoji(state, action.position, action.emoji, action.needsSpace); case COMPOSE_UPLOAD_CHANGE_SUCCESS: return state .set('is_submitting', false) From bb58fc003b5d9ac521a89e7f37c0b7fc1d45a4c9 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 17 Apr 2018 13:50:48 +0200 Subject: [PATCH 088/442] Fix warning about using SQL in order for Account#partitioned (#7159) --- app/models/account.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/account.rb b/app/models/account.rb index 05e817f63..a3436b47c 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -114,7 +114,7 @@ class Account < ApplicationRecord scope :without_followers, -> { where(followers_count: 0) } scope :with_followers, -> { where('followers_count > 0') } scope :expiring, ->(time) { remote.where.not(subscription_expires_at: nil).where('subscription_expires_at < ?', time) } - scope :partitioned, -> { order('row_number() over (partition by domain)') } + scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) } scope :silenced, -> { where(silenced: true) } scope :suspended, -> { where(suspended: true) } scope :recent, -> { reorder(id: :desc) } From aab5581c436c306e08df2668c530aab1cf526f20 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 17 Apr 2018 13:51:01 +0200 Subject: [PATCH 089/442] Set Referrer-Policy to origin in web UI and public pages of private toots (#7162) Fix #7115 --- app/controllers/home_controller.rb | 5 +++++ app/controllers/statuses_controller.rb | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index b1f8f1ad9..b71424107 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -2,6 +2,7 @@ class HomeController < ApplicationController before_action :authenticate_user! + before_action :set_referrer_policy_header before_action :set_initial_state_json def index @@ -62,4 +63,8 @@ class HomeController < ApplicationController about_path end end + + def set_referrer_policy_header + response.headers['Referrer-Policy'] = 'origin' + end end diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index 41f098a43..a2943982a 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -13,6 +13,7 @@ class StatusesController < ApplicationController before_action :set_link_headers before_action :check_account_suspension before_action :redirect_to_original, only: [:show] + before_action :set_referrer_policy_header, only: [:show] before_action :set_cache_headers def show @@ -81,4 +82,9 @@ class StatusesController < ApplicationController def redirect_to_original redirect_to ::TagManager.instance.url_for(@status.reblog) if @status.reblog? end + + def set_referrer_policy_header + return if @status.public_visibility? || @status.unlisted_visibility? + response.headers['Referrer-Policy'] = 'origin' + end end From 07a7d5959cb722ca77b65b4f7458f861e4b6eb61 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 17 Apr 2018 13:51:14 +0200 Subject: [PATCH 090/442] Fix missing "Administered by" when timeline preview disabled (#7161) --- app/views/about/_administration.html.haml | 19 ++++++++++++++ app/views/about/show.html.haml | 30 ++++++----------------- 2 files changed, 27 insertions(+), 22 deletions(-) create mode 100644 app/views/about/_administration.html.haml diff --git a/app/views/about/_administration.html.haml b/app/views/about/_administration.html.haml new file mode 100644 index 000000000..ec5834f9c --- /dev/null +++ b/app/views/about/_administration.html.haml @@ -0,0 +1,19 @@ +.account + .account__wrapper + - if @instance_presenter.contact_account + = link_to TagManager.instance.url_for(@instance_presenter.contact_account), class: 'account__display-name' do + .account__avatar-wrapper + .account__avatar{ style: "background-image: url(#{@instance_presenter.contact_account.avatar.url})" } + %span.display-name + %bdi + %strong.display-name__html.emojify= display_name(@instance_presenter.contact_account) + %span.display-name__account @#{@instance_presenter.contact_account.acct} + - else + .account__display-name + .account__avatar-wrapper + .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})" } + %span.display-name + %strong= t 'about.contact_missing' + %span.display-name__account= t 'about.contact_unavailable' + + = link_to t('about.learn_more'), about_more_path, class: 'button button-alternative' diff --git a/app/views/about/show.html.haml b/app/views/about/show.html.haml index 12213cda2..870dafdf5 100644 --- a/app/views/about/show.html.haml +++ b/app/views/about/show.html.haml @@ -110,26 +110,7 @@ %p= t 'about.about_mastodon_html' %div.contact %h3= t 'about.administered_by' - - .account - .account__wrapper - - if @instance_presenter.contact_account - = link_to TagManager.instance.url_for(@instance_presenter.contact_account), class: 'account__display-name' do - .account__avatar-wrapper - .account__avatar{ style: "background-image: url(#{@instance_presenter.contact_account.avatar.url})" } - %span.display-name - %bdi - %strong.display-name__html.emojify= display_name(@instance_presenter.contact_account) - %span.display-name__account @#{@instance_presenter.contact_account.acct} - - else - .account__display-name - .account__avatar-wrapper - .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)})" } - %span.display-name - %strong= t 'about.contact_missing' - %span.display-name__account= t 'about.contact_unavailable' - - = link_to t('about.learn_more'), about_more_path, class: 'button button-alternative' + = render 'administration' = render 'features' @@ -144,8 +125,13 @@ - else .column-4.non-preview.landing-page__information .landing-page__features - %h3= t 'about.what_is_mastodon' - %p= t 'about.about_mastodon_html' + .features-list + %div + %h3= t 'about.what_is_mastodon' + %p= t 'about.about_mastodon_html' + %div.contact + %h3= t 'about.administered_by' + = render 'administration' = render 'features' From ef12a2b74c735d78371be3f52b372c03318c3b16 Mon Sep 17 00:00:00 2001 From: Jennifer Kruse Date: Tue, 17 Apr 2018 06:52:08 -0500 Subject: [PATCH 091/442] Able to deactivate invites if they aren't expired (#7163) --- app/views/invites/_invite.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/invites/_invite.html.haml b/app/views/invites/_invite.html.haml index 81d67eb7d..1c7ec311d 100644 --- a/app/views/invites/_invite.html.haml +++ b/app/views/invites/_invite.html.haml @@ -13,5 +13,5 @@ = l invite.expires_at %td= table_link_to 'link', public_invite_url(invite_code: invite.code), public_invite_url(invite_code: invite.code) %td - - if invite.expired? && policy(invite).destroy? + - if !invite.expired? && policy(invite).destroy? = table_link_to 'times', t('invites.delete'), invite_path(invite), method: :delete From 204d72fbe49b3749e168ed2cd0ff83706946352a Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Tue, 17 Apr 2018 14:58:11 +0200 Subject: [PATCH 092/442] Feature: add count of account notes to reports (#7130) * Reports: Colocate account details with reports * Reports: Add count of account moderation notes Sometimes an account will be left with a note instead of the report, this adds a way to quickly see from a given report if this is the case. --- app/views/admin/accounts/_card.html.haml | 17 ---------------- .../admin/reports/_account_details.html.haml | 20 +++++++++++++++++++ app/views/admin/reports/show.html.haml | 4 ++-- config/locales/en.yml | 10 ++++++++++ 4 files changed, 32 insertions(+), 19 deletions(-) delete mode 100644 app/views/admin/accounts/_card.html.haml create mode 100644 app/views/admin/reports/_account_details.html.haml diff --git a/app/views/admin/accounts/_card.html.haml b/app/views/admin/accounts/_card.html.haml deleted file mode 100644 index 2f5955011..000000000 --- a/app/views/admin/accounts/_card.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -.table-wrapper - %table.table - %tbody - %tr - %td= t('admin.accounts.show.created_reports') - %td= link_to pluralize(account.reports.count, t('admin.accounts.show.report')), admin_reports_path(account_id: account.id) - %tr - %td= t('admin.accounts.show.targeted_reports') - %td= link_to pluralize(account.targeted_reports.count, t('admin.accounts.show.report')), admin_reports_path(target_account_id: account.id) - - if account.silenced? || account.suspended? - %tr - %td= t('admin.accounts.moderation.title') - %td - - if account.silenced? - %p= t('admin.accounts.moderation.silenced') - - if account.suspended? - %p= t('admin.accounts.moderation.suspended') diff --git a/app/views/admin/reports/_account_details.html.haml b/app/views/admin/reports/_account_details.html.haml new file mode 100644 index 000000000..a8af39bef --- /dev/null +++ b/app/views/admin/reports/_account_details.html.haml @@ -0,0 +1,20 @@ +.table-wrapper + %table.table + %tbody + %tr + %td= t('admin.reports.account.created_reports') + %td= link_to pluralize(account.reports.count, t('admin.reports.account.report')), admin_reports_path(account_id: account.id) + %tr + %td= t('admin.reports.account.targeted_reports') + %td= link_to pluralize(account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: account.id) + %tr + %td= t('admin.reports.account.moderation_notes') + %td= link_to pluralize(account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: account.id) + - if account.silenced? || account.suspended? + %tr + %td= t('admin.reports.account.moderation.title') + %td + - if account.silenced? + %p= t('admin.reports.account.moderation.silenced') + - if account.suspended? + %p= t('admin.reports.account.moderation.suspended') diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index d57b4adc8..130650001 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -60,11 +60,11 @@ .report-accounts__item %h3= t('admin.reports.reported_account') = render 'authorize_follows/card', account: @report.target_account, admin: true - = render 'admin/accounts/card', account: @report.target_account + = render 'admin/reports/account_details', account: @report.target_account .report-accounts__item %h3= t('admin.reports.reported_by') = render 'authorize_follows/card', account: @report.account, admin: true - = render 'admin/accounts/card', account: @report.account + = render 'admin/reports/account_details', account: @report.account %h3= t('admin.reports.comment.label') diff --git a/config/locales/en.yml b/config/locales/en.yml index 4816cc599..20bfd0f8c 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -259,6 +259,16 @@ en: created_msg: Report note successfully created! destroyed_msg: Report note successfully deleted! reports: + account: + created_reports: Reports created by this account + moderation: + silenced: Silenced + suspended: Suspended + title: Moderation + moderation_notes: Moderation Notes + note: note + report: report + targeted_reports: Reports made about this account action_taken_by: Action taken by are_you_sure: Are you sure? assign_to_self: Assign to me From 897199910fc29d17b4a019b6ee2473e138d777a2 Mon Sep 17 00:00:00 2001 From: abcang Date: Tue, 17 Apr 2018 22:23:46 +0900 Subject: [PATCH 093/442] Improve web api protect (#6343) --- app/controllers/api/web/base_controller.rb | 9 +++++++++ app/controllers/api/web/embeds_controller.rb | 2 +- .../api/web/push_subscriptions_controller.rb | 3 +-- app/controllers/api/web/settings_controller.rb | 2 +- .../mastodon/actions/push_notifications/registerer.js | 10 +++++----- app/javascript/mastodon/actions/settings.js | 2 +- 6 files changed, 18 insertions(+), 10 deletions(-) create mode 100644 app/controllers/api/web/base_controller.rb diff --git a/app/controllers/api/web/base_controller.rb b/app/controllers/api/web/base_controller.rb new file mode 100644 index 000000000..8da549b3a --- /dev/null +++ b/app/controllers/api/web/base_controller.rb @@ -0,0 +1,9 @@ +# frozen_string_literal: true + +class Api::Web::BaseController < Api::BaseController + protect_from_forgery with: :exception + + rescue_from ActionController::InvalidAuthenticityToken do + render json: { error: "Can't verify CSRF token authenticity." }, status: 422 + end +end diff --git a/app/controllers/api/web/embeds_controller.rb b/app/controllers/api/web/embeds_controller.rb index 2ed516161..f2fe74b17 100644 --- a/app/controllers/api/web/embeds_controller.rb +++ b/app/controllers/api/web/embeds_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Api::Web::EmbedsController < Api::BaseController +class Api::Web::EmbedsController < Api::Web::BaseController respond_to :json before_action :require_user! diff --git a/app/controllers/api/web/push_subscriptions_controller.rb b/app/controllers/api/web/push_subscriptions_controller.rb index c611031ab..249e7c186 100644 --- a/app/controllers/api/web/push_subscriptions_controller.rb +++ b/app/controllers/api/web/push_subscriptions_controller.rb @@ -1,10 +1,9 @@ # frozen_string_literal: true -class Api::Web::PushSubscriptionsController < Api::BaseController +class Api::Web::PushSubscriptionsController < Api::Web::BaseController respond_to :json before_action :require_user! - protect_from_forgery with: :exception def create active_session = current_session diff --git a/app/controllers/api/web/settings_controller.rb b/app/controllers/api/web/settings_controller.rb index f6739d506..e3178bf48 100644 --- a/app/controllers/api/web/settings_controller.rb +++ b/app/controllers/api/web/settings_controller.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class Api::Web::SettingsController < Api::BaseController +class Api::Web::SettingsController < Api::Web::BaseController respond_to :json before_action :require_user! diff --git a/app/javascript/mastodon/actions/push_notifications/registerer.js b/app/javascript/mastodon/actions/push_notifications/registerer.js index f17d929a6..60b215f02 100644 --- a/app/javascript/mastodon/actions/push_notifications/registerer.js +++ b/app/javascript/mastodon/actions/push_notifications/registerer.js @@ -36,7 +36,7 @@ const subscribe = (registration) => const unsubscribe = ({ registration, subscription }) => subscription ? subscription.unsubscribe().then(() => registration) : registration; -const sendSubscriptionToBackend = (getState, subscription) => { +const sendSubscriptionToBackend = (subscription) => { const params = { subscription }; if (me) { @@ -46,7 +46,7 @@ const sendSubscriptionToBackend = (getState, subscription) => { } } - return api(getState).post('/api/web/push_subscriptions', params).then(response => response.data); + return api().post('/api/web/push_subscriptions', params).then(response => response.data); }; // Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload @@ -85,13 +85,13 @@ export function register () { } else { // Something went wrong, try to subscribe again return unsubscribe({ registration, subscription }).then(subscribe).then( - subscription => sendSubscriptionToBackend(getState, subscription)); + subscription => sendSubscriptionToBackend(subscription)); } } // No subscription, try to subscribe return subscribe(registration).then( - subscription => sendSubscriptionToBackend(getState, subscription)); + subscription => sendSubscriptionToBackend(subscription)); }) .then(subscription => { // If we got a PushSubscription (and not a subscription object from the backend) @@ -134,7 +134,7 @@ export function saveSettings() { const alerts = state.get('alerts'); const data = { alerts }; - api(getState).put(`/api/web/push_subscriptions/${subscription.get('id')}`, { + api().put(`/api/web/push_subscriptions/${subscription.get('id')}`, { data, }).then(() => { if (me) { diff --git a/app/javascript/mastodon/actions/settings.js b/app/javascript/mastodon/actions/settings.js index 5634a11ef..6bf85e464 100644 --- a/app/javascript/mastodon/actions/settings.js +++ b/app/javascript/mastodon/actions/settings.js @@ -24,7 +24,7 @@ const debouncedSave = debounce((dispatch, getState) => { const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS(); - api(getState).put('/api/web/settings', { data }) + api().put('/api/web/settings', { data }) .then(() => dispatch({ type: SETTING_SAVE })) .catch(error => dispatch(showAlertForError(error))); }, 5000, { trailing: true }); From 11715454d033784bf6176b75a954e5c28b5d79e5 Mon Sep 17 00:00:00 2001 From: Sam Schlinkert Date: Tue, 17 Apr 2018 17:25:54 -0400 Subject: [PATCH 094/442] Make scroll bars wider (#7060) * Set scrollbars to 12 px wide rather than 8px Should overwrite the setting in reset.scss. This is untested at this point. * removes scrollbar height and width specifications from reset.scss and basics.scss --- app/javascript/styles/mastodon/reset.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/javascript/styles/mastodon/reset.scss b/app/javascript/styles/mastodon/reset.scss index cc5ba9d7c..58d4de35f 100644 --- a/app/javascript/styles/mastodon/reset.scss +++ b/app/javascript/styles/mastodon/reset.scss @@ -53,11 +53,6 @@ table { border-spacing: 0; } -::-webkit-scrollbar { - width: 8px; - height: 8px; -} - ::-webkit-scrollbar-thumb { background: lighten($ui-base-color, 4%); border: 0px none $base-border-color; From e5dd385431d8a3c35ceb3fd9d76fb0549c720f35 Mon Sep 17 00:00:00 2001 From: ThibG Date: Tue, 17 Apr 2018 23:35:45 +0200 Subject: [PATCH 095/442] Allow boosting own private toots (#6157) * Adjust policy to allow boosting own private toots * Add ability to reblog private toots from dropdown menu --- app/javascript/mastodon/components/status_action_bar.js | 4 ++++ .../mastodon/features/status/components/action_bar.js | 4 ++++ app/policies/status_policy.rb | 2 +- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index 10f34b0c7..e58625582 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -18,6 +18,8 @@ const messages = defineMessages({ more: { id: 'status.more', defaultMessage: 'More' }, replyAll: { id: 'status.replyAll', defaultMessage: 'Reply to thread' }, reblog: { id: 'status.reblog', defaultMessage: 'Boost' }, + reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' }, + cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' }, cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' }, favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, open: { id: 'status.open', defaultMessage: 'Expand this status' }, @@ -150,6 +152,8 @@ export default class StatusActionBar extends ImmutablePureComponent { if (status.getIn(['account', 'id']) === me) { if (publicStatus) { menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick }); + } else { + menu.push({ text: intl.formatMessage(status.get('reblog') ? messages.reblog_private : messages.cancel_reblog_private), action: this.handleReblogClick }); } menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js index 4aa6b08f2..fc34c8cdc 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.js +++ b/app/javascript/mastodon/features/status/components/action_bar.js @@ -12,6 +12,8 @@ const messages = defineMessages({ mention: { id: 'status.mention', defaultMessage: 'Mention @{name}' }, reply: { id: 'status.reply', defaultMessage: 'Reply' }, reblog: { id: 'status.reblog', defaultMessage: 'Boost' }, + reblog_private: { id: 'status.reblog_private', defaultMessage: 'Boost to original audience' }, + cancel_reblog_private: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' }, cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' }, favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' }, @@ -120,6 +122,8 @@ export default class ActionBar extends React.PureComponent { if (me === status.getIn(['account', 'id'])) { if (publicStatus) { menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick }); + } else { + menu.push({ text: intl.formatMessage(status.get('reblog') ? messages.reblog_private : messages.cancel_reblog_private), action: this.handleReblogClick }); } menu.push(null); diff --git a/app/policies/status_policy.rb b/app/policies/status_policy.rb index 0373fdf04..5573289b6 100644 --- a/app/policies/status_policy.rb +++ b/app/policies/status_policy.rb @@ -16,7 +16,7 @@ class StatusPolicy < ApplicationPolicy end def reblog? - !direct? && !private? && show? + !direct? && (!private? || owned?) && show? end def destroy? From fad7b9f5f297f970056d86cef0139e709ed47516 Mon Sep 17 00:00:00 2001 From: Neil Moore Date: Tue, 17 Apr 2018 21:33:59 -0400 Subject: [PATCH 096/442] Adds keyboard hotkey for revealing/hiding statuses (#7173) Resolves #5550 --- app/javascript/mastodon/components/status.js | 5 +++++ app/javascript/mastodon/features/status/index.js | 5 +++++ app/javascript/mastodon/features/ui/index.js | 1 + 3 files changed, 11 insertions(+) diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index 6129b3f1e..e5f7c9399 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -122,6 +122,10 @@ export default class Status extends ImmutablePureComponent { this.props.onMoveDown(this.props.status.get('id')); } + handleHotkeyToggleHidden = () => { + this.props.onToggleHidden(this._properStatus()); + } + _properStatus () { const { status } = this.props; @@ -224,6 +228,7 @@ export default class Status extends ImmutablePureComponent { openProfile: this.handleHotkeyOpenProfile, moveUp: this.handleHotkeyMoveUp, moveDown: this.handleHotkeyMoveDown, + toggleHidden: this.handleHotkeyToggleHidden, }; return ( diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index 55eff0823..d5af2a459 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -244,6 +244,10 @@ export default class Status extends ImmutablePureComponent { this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`); } + handleHotkeyToggleHidden = () => { + this.handleToggleHidden(this.props.status); + } + handleMoveUp = id => { const { status, ancestorsIds, descendantsIds } = this.props; @@ -354,6 +358,7 @@ export default class Status extends ImmutablePureComponent { boost: this.handleHotkeyBoost, mention: this.handleHotkeyMention, openProfile: this.handleHotkeyOpenProfile, + toggleHidden: this.handleHotkeyToggleHidden, }; return ( diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index 8b905fa1d..d2ef19e47 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -85,6 +85,7 @@ const keyMap = { goToProfile: 'g u', goToBlocked: 'g b', goToMuted: 'g m', + toggleHidden: 'x', }; class SwitchingColumnsArea extends React.PureComponent { From aedfea3554dc5cd60f4bebd828109d1ef4327814 Mon Sep 17 00:00:00 2001 From: luzi82 Date: Wed, 18 Apr 2018 13:28:26 +0800 Subject: [PATCH 097/442] zh-HK translation (#7177) * zh-HK translation * zh-HK fix * zh-HK translation * add missing zh-HK translate * fix translate * i18n-tasks normalize --- app/javascript/mastodon/locales/zh-HK.json | 10 ++-- config/locales/devise.zh-HK.yml | 4 +- config/locales/doorkeeper.zh-HK.yml | 4 +- config/locales/simple_form.zh-HK.yml | 6 +++ config/locales/zh-HK.yml | 63 ++++++++++++++++++++-- 5 files changed, 75 insertions(+), 12 deletions(-) diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index bebb33e57..28685f4d8 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -74,7 +74,7 @@ "confirmations.block.confirm": "封鎖", "confirmations.block.message": "你確定要封鎖{name}嗎?", "confirmations.delete.confirm": "刪除", - "confirmations.delete.message": "你確定要刪除{name}嗎?", + "confirmations.delete.message": "你確定要刪除這文章嗎?", "confirmations.delete_list.confirm": "刪除", "confirmations.delete_list.message": "你確定要永久刪除這列表嗎?", "confirmations.domain_block.confirm": "隱藏整個網站", @@ -99,11 +99,11 @@ "emoji_button.search_results": "搜尋結果", "emoji_button.symbols": "符號", "emoji_button.travel": "旅遊景物", - "empty_column.community": "本站時間軸暫時未有內容,快文章來搶頭香啊!", + "empty_column.community": "本站時間軸暫時未有內容,快寫一點東西來搶頭香啊!", "empty_column.hashtag": "這個標籤暫時未有內容。", "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。", "empty_column.home.public_timeline": "公共時間軸", - "empty_column.list": "There is nothing in this list yet.", + "empty_column.list": "這個列表暫時未有內容。", "empty_column.notifications": "你沒有任何通知紀錄,快向其他用戶搭訕吧。", "empty_column.public": "跨站時間軸暫時沒有內容!快寫一些公共的文章,或者關注另一些服務站的用戶吧!你和本站、友站的交流,將決定這裏出現的內容。", "follow_request.authorize": "批准", @@ -240,7 +240,7 @@ "status.block": "封鎖 @{name}", "status.cannot_reblog": "這篇文章無法被轉推", "status.delete": "刪除", - "status.direct": "Direct message @{name}", + "status.direct": "私訊 @{name}", "status.embed": "鑲嵌", "status.favourite": "收藏", "status.load_more": "載入更多", @@ -270,7 +270,7 @@ "tabs_bar.home": "主頁", "tabs_bar.local_timeline": "本站", "tabs_bar.notifications": "通知", - "tabs_bar.search": "Search", + "tabs_bar.search": "搜尋", "ui.beforeunload": "如果你現在離開 Mastodon,你的草稿內容將會被丟棄。", "upload_area.title": "將檔案拖放至此上載", "upload_button.label": "上載媒體檔案", diff --git a/config/locales/devise.zh-HK.yml b/config/locales/devise.zh-HK.yml index 12d6e3c2c..ca16e06aa 100644 --- a/config/locales/devise.zh-HK.yml +++ b/config/locales/devise.zh-HK.yml @@ -78,5 +78,5 @@ zh-HK: not_found: 找不到 not_locked: 並未被鎖定 not_saved: - one: 1 個錯誤令 %{resource} 被法被儲存︰ - other: "%{count} 個錯誤令 %{resource} 被法被儲存︰" + one: 1 個錯誤令 %{resource} 無法被儲存︰ + other: "%{count} 個錯誤令 %{resource} 無法被儲存︰" diff --git a/config/locales/doorkeeper.zh-HK.yml b/config/locales/doorkeeper.zh-HK.yml index 747108762..4f46a416a 100644 --- a/config/locales/doorkeeper.zh-HK.yml +++ b/config/locales/doorkeeper.zh-HK.yml @@ -14,7 +14,7 @@ zh-HK: redirect_uri: fragment_present: URI 不可包含 "#fragment" 部份 invalid_uri: 必需有正確的 URI. - relative_uri: 必需為絕對 URI. + relative_uri: 必需為完整 URI. secured_uri: 必需使用有 HTTPS/SSL 加密的 URI. doorkeeper: applications: @@ -63,7 +63,7 @@ zh-HK: prompt: 應用程式 %{client_name} 要求得到你用戶的部份權限 title: 需要用戶授權 show: - title: Copy this authorization code and paste it to the application. + title: 請把這個授權碼複製到應用程式中。 authorized_applications: buttons: revoke: 取消授權 diff --git a/config/locales/simple_form.zh-HK.yml b/config/locales/simple_form.zh-HK.yml index da0292a90..6b890b036 100644 --- a/config/locales/simple_form.zh-HK.yml +++ b/config/locales/simple_form.zh-HK.yml @@ -6,6 +6,7 @@ zh-HK: avatar: 支援 PNG, GIF 或 JPG 圖片,檔案最大為 2MB,會縮裁成 400x400px digest: 僅在你長時間未登錄,且收到了私信時發送 display_name: 最多 30 個字元 + fields: 個人資料頁可顯示多至 4 個項目 header: 支援 PNG, GIF 或 JPG 圖片,檔案最大為 2MB,會縮裁成 700x335px locked: 你必須人手核准每個用戶對你的關注請求,而你的文章私隱會被預設為「只有關注你的人能看」 note: 最多 160 個字元 @@ -18,6 +19,10 @@ zh-HK: user: filtered_languages: 下面被選擇的語言的文章將不會出現在你的公共時間軸上。 labels: + account: + fields: + name: 標籤 + value: 內容 defaults: avatar: 個人頭像 confirm_new_password: 確認新密碼 @@ -27,6 +32,7 @@ zh-HK: display_name: 顯示名稱 email: 電郵地址 expires_in: 失效時間 + fields: 資料 filtered_languages: 封鎖下面語言的文章 header: 個人頁面頂部 locale: 語言 diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index 5c1feabfc..964ff5811 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -4,6 +4,7 @@ zh-HK: about_hashtag_html: 這些是包含「#%{hashtag}」標籤的公開文章。只要你有任何 Mastodon 服務站、或者聯盟網站的用戶,便可以與他們互動。 about_mastodon_html: Mastodon(萬象)是自由、開源的社交網絡。服務站各自獨立而互連,避免單一商業機構壟斷。找你所信任的服務站,建立帳號,你即可與任何服務站上的用戶溝通,享受無縫的網絡交流。 about_this: 關於本服務站 + administered_by: 管理者: closed_registrations: 本服務站暫時停止接受登記。 contact: 聯絡 contact_missing: 未設定 @@ -60,7 +61,15 @@ zh-HK: destroyed_msg: 管理記錄已被刪除 accounts: are_you_sure: 你確定嗎? + avatar: 頭像 by_domain: 域名 + change_email: + changed_msg: 帳號電郵更新成功! + current_email: 現時電郵 + label: 改變電郵 + new_email: 新的電郵 + submit: 改變電郵 + title: 改變 %{username} 的電郵 confirm: 確定 confirmed: 已確定 demote: 降任 @@ -108,6 +117,7 @@ zh-HK: public: 公共 push_subscription_expires: PuSH 訂閱過期 redownload: 更新頭像 + remove_avatar: 取消頭像 reset: 重設 reset_password: 重設密碼 resubscribe: 重新訂閱 @@ -128,6 +138,7 @@ zh-HK: statuses: 文章 subscribe: 訂閱 title: 用戶 + unconfirmed_email: 未確認的電郵 undo_silenced: 解除靜音 undo_suspension: 解除停權 unsubscribe: 取消訂閱 @@ -135,6 +146,8 @@ zh-HK: web: 用戶頁面 action_logs: actions: + assigned_to_self_report: "%{name} 指派了 %{target} 的舉報給自己" + change_email_user: "%{name} 改變了用戶 %{target} 的電郵地址" confirm_user: "%{name} 確認了用戶 %{target} 的電郵地址" create_custom_emoji: "%{name} 加入自訂表情符號 %{target}" create_domain_block: "%{name} 阻隔了網域 %{target}" @@ -150,10 +163,13 @@ zh-HK: enable_user: "%{name} 把用戶 %{target} 設定為允許登入" memorialize_account: "%{name} 把 %{target} 設定為追悼帳戶" promote_user: "%{name} 對用戶 %{target} 进行了升任操作" + remove_avatar_user: "%{name} 取消了 %{target} 的頭像" + reopen_report: "%{name} 重開 %{target} 的舉報" reset_password_user: "%{name} 重設了用戶 %{target} 的密碼" resolve_report: "%{name} 處理了 %{target} 的舉報" silence_account: "%{name} 靜音了用戶 %{target}" suspend_account: "%{name} 停權了用戶 %{target}" + unassigned_report: "%{name} 取消指派 %{target} 的舉報" unsilence_account: "%{name} 取消了用戶 %{target} 的靜音狀態" unsuspend_account: "%{name} 取消了用戶 %{target} 的停權狀態" update_custom_emoji: "%{name} 更新了自訂表情符號 %{target}" @@ -238,29 +254,60 @@ zh-HK: expired: 已失效 title: 篩選 title: 邀請用戶 + report_notes: + created_msg: 舉報筆記已建立。 + destroyed_msg: 舉報筆記已刪除。 reports: + account: + created_reports: 由此帳號發出的舉報 + moderation: + silenced: 被靜音的 + suspended: 被停權的 + title: 管理操作 + moderation_notes: 管理筆記 + note: 筆記 + report: 舉報 + targeted_reports: 關於此帳號的舉報 action_taken_by: 操作執行者 are_you_sure: 你確認嗎? + assign_to_self: 指派給自己 + assigned: 指派負責人 comment: label: 詳細解釋 none: 沒有 + created_at: 日期 delete: 刪除 + history: 執行紀錄 id: ID mark_as_resolved: 標示為「已處理」 + mark_as_unresolved: 標示為「未處理」 + notes: + create: 建立筆記 + create_and_resolve: 建立筆記並標示為「已處理」 + create_and_unresolve: 建立筆記並標示為「未處理」 + delete: 刪除 + label: 管理筆記 + new_label: 建立管理筆記 + placeholder: 記錄已執行的動作,或其他更新 nsfw: 'false': 取消 NSFW 標記 'true': 添加 NSFW 標記 + reopen: 重開舉報 report: '舉報 #%{id}' report_contents: 舉報內容 reported_account: 舉報用戶 reported_by: 舉報者 resolved: 已處理 + resolved_msg: 舉報已處理。 silence_account: 將用戶靜音 status: 狀態 + statuses: 被舉報的文章 suspend_account: 將用戶停權 target: 對象 title: 舉報 + unassign: 取消指派 unresolved: 未處理 + updated_at: 更新 view: 檢視 settings: activity_api_enabled: @@ -381,6 +428,7 @@ zh-HK: security: 登入資訊 set_new_password: 設定新密碼 authorize_follow: + already_following: 你已經關注了這個帳號 error: 對不起,尋找這個跨站用戶的過程發生錯誤 follow: 關注 follow_request: 關注請求已發送给: @@ -473,6 +521,7 @@ zh-HK: '21600': 6 小時後 '3600': 1 小時後 '43200': 12 小時後 + '604800': 1 週後 '86400': 1 天後 expires_in_prompt: 永不過期 generate: 生成邀請連結 @@ -574,6 +623,10 @@ zh-HK: missing_resource: 無法找到你用戶的轉接網址 proceed: 下一步 prompt: 你希望關注︰ + remote_unfollow: + error: 錯誤 + title: 標題 + unfollowed: 取消關注 sessions: activity: 最近活動 browser: 瀏覽器 @@ -593,7 +646,7 @@ zh-HK: safari: Safari uc_browser: UC瀏覽器 weibo: 新浪微博 - current_session: 目前的 session + current_session: 目前的作業階段 description: "%{platform} 上的 %{browser}" explanation: 這些是現在正登入於你的 Mastodon 帳號的瀏覽器。 ip: IP 位址 @@ -611,8 +664,8 @@ zh-HK: windows_mobile: Windows Mobile windows_phone: Windows Phone revoke: 取消 - revoke_success: Session 取消成功。 - title: Session + revoke_success: 作業階段取消成功。 + title: 作業階段 settings: authorized_apps: 授權應用程式 back: 回到 Mastodon @@ -677,6 +730,10 @@ zh-HK: setup: 設定 wrong_code: 你輸入的認證碼並不正確!可能伺服器時間和你手機不一致,請檢查你手機的時鐘,或與本站管理員聯絡。 user_mailer: + backup_ready: + explanation: 你要求的 Mastodon 帳號完整備份檔案現已就緒,可供下載。 + subject: 你的備份檔已可供下載 + title: 檔案匯出 welcome: edit_profile_action: 設定個人資料 edit_profile_step: 你可以設定你的個人資料,包括上傳頭像、橫幅圖片、更改顯示名稱等等。如果你想在新的關注者關注你之前對他們進行審核,你也可以選擇為你的帳戶設為「私人」。 From 156b916caf4ec902a8db525843e95c1a42350207 Mon Sep 17 00:00:00 2001 From: Kaito Sinclaire Date: Wed, 18 Apr 2018 04:09:06 -0700 Subject: [PATCH 098/442] Direct messages column (#4514) * Added a timeline for Direct statuses * Lists all Direct statuses you've sent and received * Displayed in Getting Started * Streaming server support for direct TL * Changes to match other timelines in 2.0 --- .../api/v1/timelines/direct_controller.rb | 60 ++++++++++ app/javascript/mastodon/actions/compose.js | 2 + app/javascript/mastodon/actions/streaming.js | 1 + app/javascript/mastodon/actions/timelines.js | 1 + .../containers/column_settings_container.js | 17 +++ .../features/direct_timeline/index.js | 104 ++++++++++++++++++ .../features/getting_started/index.js | 15 ++- .../features/ui/components/columns_area.js | 3 +- app/javascript/mastodon/features/ui/index.js | 8 ++ .../features/ui/util/async-components.js | 4 + app/javascript/mastodon/locales/ar.json | 5 + app/javascript/mastodon/locales/bg.json | 5 + app/javascript/mastodon/locales/ca.json | 5 + app/javascript/mastodon/locales/de.json | 5 + .../mastodon/locales/defaultMessages.json | 33 ++++++ app/javascript/mastodon/locales/en.json | 5 + app/javascript/mastodon/locales/eo.json | 5 + app/javascript/mastodon/locales/es.json | 5 + app/javascript/mastodon/locales/fa.json | 5 + app/javascript/mastodon/locales/fi.json | 5 + app/javascript/mastodon/locales/fr.json | 5 + app/javascript/mastodon/locales/gl.json | 5 + app/javascript/mastodon/locales/he.json | 5 + app/javascript/mastodon/locales/hr.json | 5 + app/javascript/mastodon/locales/hu.json | 5 + app/javascript/mastodon/locales/hy.json | 5 + app/javascript/mastodon/locales/id.json | 5 + app/javascript/mastodon/locales/io.json | 5 + app/javascript/mastodon/locales/it.json | 5 + app/javascript/mastodon/locales/ja.json | 5 + app/javascript/mastodon/locales/ko.json | 5 + app/javascript/mastodon/locales/nl.json | 5 + app/javascript/mastodon/locales/no.json | 5 + app/javascript/mastodon/locales/oc.json | 5 + app/javascript/mastodon/locales/pl.json | 5 + app/javascript/mastodon/locales/pt-BR.json | 5 + app/javascript/mastodon/locales/pt.json | 5 + app/javascript/mastodon/locales/ru.json | 5 + app/javascript/mastodon/locales/sk.json | 5 + app/javascript/mastodon/locales/sr-Latn.json | 5 + app/javascript/mastodon/locales/sr.json | 5 + app/javascript/mastodon/locales/sv.json | 5 + app/javascript/mastodon/locales/th.json | 5 + app/javascript/mastodon/locales/tr.json | 5 + app/javascript/mastodon/locales/uk.json | 5 + app/javascript/mastodon/locales/zh-CN.json | 5 + app/javascript/mastodon/locales/zh-HK.json | 5 + app/javascript/mastodon/locales/zh-TW.json | 5 + app/javascript/mastodon/reducers/contexts.js | 5 +- app/javascript/mastodon/reducers/settings.js | 6 + app/models/status.rb | 8 ++ app/services/batched_remove_status_service.rb | 11 ++ app/services/fan_out_on_write_service.rb | 13 ++- app/services/remove_status_service.rb | 8 ++ config/routes.rb | 1 + spec/models/status_spec.rb | 49 +++++++++ streaming/index.js | 7 ++ yarn.lock | 12 +- 58 files changed, 538 insertions(+), 15 deletions(-) create mode 100644 app/controllers/api/v1/timelines/direct_controller.rb create mode 100644 app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js create mode 100644 app/javascript/mastodon/features/direct_timeline/index.js diff --git a/app/controllers/api/v1/timelines/direct_controller.rb b/app/controllers/api/v1/timelines/direct_controller.rb new file mode 100644 index 000000000..d455227eb --- /dev/null +++ b/app/controllers/api/v1/timelines/direct_controller.rb @@ -0,0 +1,60 @@ +# frozen_string_literal: true + +class Api::V1::Timelines::DirectController < Api::BaseController + before_action -> { doorkeeper_authorize! :read }, only: [:show] + before_action :require_user!, only: [:show] + after_action :insert_pagination_headers, unless: -> { @statuses.empty? } + + respond_to :json + + def show + @statuses = load_statuses + render json: @statuses, each_serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new(@statuses, current_user&.account_id) + end + + private + + def load_statuses + cached_direct_statuses + end + + def cached_direct_statuses + cache_collection direct_statuses, Status + end + + def direct_statuses + direct_timeline_statuses.paginate_by_max_id( + limit_param(DEFAULT_STATUSES_LIMIT), + params[:max_id], + params[:since_id] + ) + end + + def direct_timeline_statuses + Status.as_direct_timeline(current_account) + end + + def insert_pagination_headers + set_pagination_headers(next_path, prev_path) + end + + def pagination_params(core_params) + params.permit(:local, :limit).merge(core_params) + end + + def next_path + api_v1_timelines_direct_url pagination_params(max_id: pagination_max_id) + end + + def prev_path + api_v1_timelines_direct_url pagination_params(since_id: pagination_since_id) + end + + def pagination_max_id + @statuses.last.id + end + + def pagination_since_id + @statuses.first.id + end +end diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index ea9d9f359..eee9c6928 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -145,6 +145,8 @@ export function submitCompose() { if (response.data.in_reply_to_id === null && response.data.visibility === 'public') { insertIfOnline('community'); insertIfOnline('public'); + } else if (response.data.visibility === 'direct') { + insertIfOnline('direct'); } }).catch(function (error) { dispatch(submitComposeFail(error)); diff --git a/app/javascript/mastodon/actions/streaming.js b/app/javascript/mastodon/actions/streaming.js index f76510cdb..14215ab6d 100644 --- a/app/javascript/mastodon/actions/streaming.js +++ b/app/javascript/mastodon/actions/streaming.js @@ -46,4 +46,5 @@ export const connectCommunityStream = () => connectTimelineStream('community', ' export const connectMediaStream = () => connectTimelineStream('community', 'public:local'); export const connectPublicStream = () => connectTimelineStream('public', 'public'); export const connectHashtagStream = (tag) => connectTimelineStream(`hashtag:${tag}`, `hashtag&tag=${tag}`); +export const connectDirectStream = () => connectTimelineStream('direct', 'direct'); export const connectListStream = (id) => connectTimelineStream(`list:${id}`, `list&list=${id}`); diff --git a/app/javascript/mastodon/actions/timelines.js b/app/javascript/mastodon/actions/timelines.js index 5be07126d..eca847ee7 100644 --- a/app/javascript/mastodon/actions/timelines.js +++ b/app/javascript/mastodon/actions/timelines.js @@ -87,6 +87,7 @@ export function expandTimeline(timelineId, path, params = {}) { export const expandHomeTimeline = ({ maxId } = {}) => expandTimeline('home', '/api/v1/timelines/home', { max_id: maxId }); export const expandPublicTimeline = ({ maxId } = {}) => expandTimeline('public', '/api/v1/timelines/public', { max_id: maxId }); export const expandCommunityTimeline = ({ maxId } = {}) => expandTimeline('community', '/api/v1/timelines/public', { local: true, max_id: maxId }); +export const expandDirectTimeline = ({ maxId } = {}) => expandTimeline('direct', '/api/v1/timelines/direct', { max_id: maxId }); export const expandAccountTimeline = (accountId, { maxId, withReplies } = {}) => expandTimeline(`account:${accountId}${withReplies ? ':with_replies' : ''}`, `/api/v1/accounts/${accountId}/statuses`, { exclude_replies: !withReplies, max_id: maxId }); export const expandAccountFeaturedTimeline = accountId => expandTimeline(`account:${accountId}:pinned`, `/api/v1/accounts/${accountId}/statuses`, { pinned: true }); export const expandAccountMediaTimeline = (accountId, { maxId } = {}) => expandTimeline(`account:${accountId}:media`, `/api/v1/accounts/${accountId}/statuses`, { max_id: maxId, only_media: true }); diff --git a/app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js b/app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js new file mode 100644 index 000000000..1833f69e5 --- /dev/null +++ b/app/javascript/mastodon/features/direct_timeline/containers/column_settings_container.js @@ -0,0 +1,17 @@ +import { connect } from 'react-redux'; +import ColumnSettings from '../../community_timeline/components/column_settings'; +import { changeSetting } from '../../../actions/settings'; + +const mapStateToProps = state => ({ + settings: state.getIn(['settings', 'direct']), +}); + +const mapDispatchToProps = dispatch => ({ + + onChange (key, checked) { + dispatch(changeSetting(['direct', ...key], checked)); + }, + +}); + +export default connect(mapStateToProps, mapDispatchToProps)(ColumnSettings); diff --git a/app/javascript/mastodon/features/direct_timeline/index.js b/app/javascript/mastodon/features/direct_timeline/index.js new file mode 100644 index 000000000..fda57f69a --- /dev/null +++ b/app/javascript/mastodon/features/direct_timeline/index.js @@ -0,0 +1,104 @@ +import React from 'react'; +import { connect } from 'react-redux'; +import PropTypes from 'prop-types'; +import StatusListContainer from '../ui/containers/status_list_container'; +import Column from '../../components/column'; +import ColumnHeader from '../../components/column_header'; +import { expandDirectTimeline } from '../../actions/timelines'; +import { addColumn, removeColumn, moveColumn } from '../../actions/columns'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; +import ColumnSettingsContainer from './containers/column_settings_container'; +import { connectDirectStream } from '../../actions/streaming'; + +const messages = defineMessages({ + title: { id: 'column.direct', defaultMessage: 'Direct messages' }, +}); + +const mapStateToProps = state => ({ + hasUnread: state.getIn(['timelines', 'direct', 'unread']) > 0, +}); + +@connect(mapStateToProps) +@injectIntl +export default class DirectTimeline extends React.PureComponent { + + static propTypes = { + dispatch: PropTypes.func.isRequired, + columnId: PropTypes.string, + intl: PropTypes.object.isRequired, + hasUnread: PropTypes.bool, + multiColumn: PropTypes.bool, + }; + + handlePin = () => { + const { columnId, dispatch } = this.props; + + if (columnId) { + dispatch(removeColumn(columnId)); + } else { + dispatch(addColumn('DIRECT', {})); + } + } + + handleMove = (dir) => { + const { columnId, dispatch } = this.props; + dispatch(moveColumn(columnId, dir)); + } + + handleHeaderClick = () => { + this.column.scrollTop(); + } + + componentDidMount () { + const { dispatch } = this.props; + + dispatch(expandDirectTimeline()); + this.disconnect = dispatch(connectDirectStream()); + } + + componentWillUnmount () { + if (this.disconnect) { + this.disconnect(); + this.disconnect = null; + } + } + + setRef = c => { + this.column = c; + } + + handleLoadMore = maxId => { + this.props.dispatch(expandDirectTimeline({ maxId })); + } + + render () { + const { intl, hasUnread, columnId, multiColumn } = this.props; + const pinned = !!columnId; + + return ( + + + + + + } + /> + + ); + } + +} diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index 053a1ca17..4a249f301 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -19,6 +19,7 @@ const messages = defineMessages({ navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation' }, settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings' }, community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' }, + direct: { id: 'navigation_bar.direct', defaultMessage: 'Direct messages' }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, @@ -98,20 +99,24 @@ export default class GettingStarted extends ImmutablePureComponent { } } + if (!multiColumn || !columns.find(item => item.get('id') === 'DIRECT')) { + navItems.push(); + } + navItems.push( - , - + , + ); if (myAccount.get('locked')) { - navItems.push(); + navItems.push(); } if (multiColumn) { - navItems.push(); + navItems.push(); } - navItems.push(); + navItems.push(); return ( diff --git a/app/javascript/mastodon/features/ui/components/columns_area.js b/app/javascript/mastodon/features/ui/components/columns_area.js index 05cdb4e3b..0a62cbbeb 100644 --- a/app/javascript/mastodon/features/ui/components/columns_area.js +++ b/app/javascript/mastodon/features/ui/components/columns_area.js @@ -12,7 +12,7 @@ import BundleContainer from '../containers/bundle_container'; import ColumnLoading from './column_loading'; import DrawerLoading from './drawer_loading'; import BundleColumnError from './bundle_column_error'; -import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components'; +import { Compose, Notifications, HomeTimeline, CommunityTimeline, PublicTimeline, HashtagTimeline, DirectTimeline, FavouritedStatuses, ListTimeline } from '../../ui/util/async-components'; import detectPassiveEvents from 'detect-passive-events'; import { scrollRight } from '../../../scroll'; @@ -24,6 +24,7 @@ const componentMap = { 'PUBLIC': PublicTimeline, 'COMMUNITY': CommunityTimeline, 'HASHTAG': HashtagTimeline, + 'DIRECT': DirectTimeline, 'FAVOURITES': FavouritedStatuses, 'LIST': ListTimeline, }; diff --git a/app/javascript/mastodon/features/ui/index.js b/app/javascript/mastodon/features/ui/index.js index d2ef19e47..adca0d617 100644 --- a/app/javascript/mastodon/features/ui/index.js +++ b/app/javascript/mastodon/features/ui/index.js @@ -30,6 +30,7 @@ import { Following, Reblogs, Favourites, + DirectTimeline, HashtagTimeline, Notifications, FollowRequests, @@ -79,6 +80,7 @@ const keyMap = { goToNotifications: 'g n', goToLocal: 'g l', goToFederated: 'g t', + goToDirect: 'g d', goToStart: 'g s', goToFavourites: 'g f', goToPinned: 'g p', @@ -140,6 +142,7 @@ class SwitchingColumnsArea extends React.PureComponent { + @@ -386,6 +389,10 @@ export default class UI extends React.PureComponent { this.context.router.history.push('/timelines/public'); } + handleHotkeyGoToDirect = () => { + this.context.router.history.push('/timelines/direct'); + } + handleHotkeyGoToStart = () => { this.context.router.history.push('/getting-started'); } @@ -425,6 +432,7 @@ export default class UI extends React.PureComponent { goToNotifications: this.handleHotkeyGoToNotifications, goToLocal: this.handleHotkeyGoToLocal, goToFederated: this.handleHotkeyGoToFederated, + goToDirect: this.handleHotkeyGoToDirect, goToStart: this.handleHotkeyGoToStart, goToFavourites: this.handleHotkeyGoToFavourites, goToPinned: this.handleHotkeyGoToPinned, diff --git a/app/javascript/mastodon/features/ui/util/async-components.js b/app/javascript/mastodon/features/ui/util/async-components.js index 19957208f..8cf2a6e7d 100644 --- a/app/javascript/mastodon/features/ui/util/async-components.js +++ b/app/javascript/mastodon/features/ui/util/async-components.js @@ -26,6 +26,10 @@ export function HashtagTimeline () { return import(/* webpackChunkName: "features/hashtag_timeline" */'../../hashtag_timeline'); } +export function DirectTimeline() { + return import(/* webpackChunkName: "features/direct_timeline" */'../../direct_timeline'); +} + export function ListTimeline () { return import(/* webpackChunkName: "features/list_timeline" */'../../list_timeline'); } diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index c13ff773d..24c8a5b54 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "إعادة المحاولة", "column.blocks": "الحسابات المحجوبة", "column.community": "الخيط العام المحلي", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "المفضلة", "column.follow_requests": "طلبات المتابعة", @@ -100,6 +101,7 @@ "emoji_button.symbols": "رموز", "emoji_button.travel": "أماكن و أسفار", "empty_column.community": "الخط الزمني المحلي فارغ. أكتب شيئا ما للعامة كبداية !", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.", "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.", "empty_column.home.public_timeline": "الخيط العام", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "هل تود إخفاء الإخطارات القادمة من هذا المستخدم ؟", "navigation_bar.blocks": "الحسابات المحجوبة", "navigation_bar.community_timeline": "الخيط العام المحلي", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "تعديل الملف الشخصي", "navigation_bar.favourites": "المفضلة", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {result} و {results}}", "standalone.public_title": "نظرة على ...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "تعذرت ترقية هذا المنشور", "status.delete": "إحذف", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "تدبيس على الملف الشخصي", "status.pinned": "تبويق مثبَّت", "status.reblog": "رَقِّي", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} رقى", "status.reply": "ردّ", "status.replyAll": "رُد على الخيط", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 981aced0b..25ef6db65 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Local timeline", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symbols", "emoji_button.travel": "Travel & Places", "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "There is nothing in this hashtag yet.", "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.", "empty_column.home.public_timeline": "the public timeline", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Blocked users", "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Редактирай профил", "navigation_bar.favourites": "Favourites", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "A look inside...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Изтриване", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Pin on profile", "status.pinned": "Pinned toot", "status.reblog": "Споделяне", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} сподели", "status.reply": "Отговор", "status.replyAll": "Reply to thread", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index d9270e0ce..6a44808e0 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Torna-ho a provar", "column.blocks": "Usuaris blocats", "column.community": "Línia de temps local", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favorits", "column.follow_requests": "Peticions per seguir-te", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Símbols", "emoji_button.travel": "Viatges i Llocs", "empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per fer rodar la pilota!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.", "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.", "empty_column.home.public_timeline": "la línia de temps pública", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Amagar notificacions d'aquest usuari?", "navigation_bar.blocks": "Usuaris bloquejats", "navigation_bar.community_timeline": "Línia de temps Local", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favorits", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, un {result} altres {results}}", "standalone.public_title": "Una mirada a l'interior ...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Aquesta publicació no pot ser retootejada", "status.delete": "Esborrar", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Fixat en el perfil", "status.pinned": "Toot fixat", "status.reblog": "Impuls", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} ha retootejat", "status.reply": "Respondre", "status.replyAll": "Respondre al tema", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 6eb5e6840..69c2ae8d8 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Erneut versuchen", "column.blocks": "Blockierte Profile", "column.community": "Lokale Zeitleiste", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favoriten", "column.follow_requests": "Folgeanfragen", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symbole", "emoji_button.travel": "Reisen und Orte", "empty_column.community": "Die lokale Zeitleiste ist leer. Schreibe einen öffentlichen Beitrag, um den Ball ins Rollen zu bringen!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Unter diesem Hashtag gibt es noch nichts.", "empty_column.home": "Deine Startseite ist leer! Besuche {public} oder nutze die Suche, um loszulegen und andere Leute zu finden.", "empty_column.home.public_timeline": "die öffentliche Zeitleiste", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Benachrichtigungen von diesem Account verbergen?", "navigation_bar.blocks": "Blockierte Profile", "navigation_bar.community_timeline": "Lokale Zeitleiste", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Profil bearbeiten", "navigation_bar.favourites": "Favoriten", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}", "standalone.public_title": "Ein kleiner Einblick …", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden", "status.delete": "Löschen", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Im Profil anheften", "status.pinned": "Pinned toot", "status.reblog": "Teilen", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} teilte", "status.reply": "Antworten", "status.replyAll": "Auf Thread antworten", diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index a3c4f775c..5a02e0c72 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -242,6 +242,14 @@ "defaultMessage": "Boost", "id": "status.reblog" }, + { + "defaultMessage": "Boost to original audience", + "id": "status.reblog_private" + }, + { + "defaultMessage": "Unboost", + "id": "status.cancel_reblog_private" + }, { "defaultMessage": "This post cannot be boosted", "id": "status.cannot_reblog" @@ -897,6 +905,19 @@ ], "path": "app/javascript/mastodon/features/compose/index.json" }, + { + "descriptors": [ + { + "defaultMessage": "Direct messages", + "id": "column.direct" + }, + { + "defaultMessage": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "id": "empty_column.direct" + } + ], + "path": "app/javascript/mastodon/features/direct_timeline/index.json" + }, { "descriptors": [ { @@ -971,6 +992,10 @@ "defaultMessage": "Local timeline", "id": "navigation_bar.community_timeline" }, + { + "defaultMessage": "Direct messages", + "id": "navigation_bar.direct" + }, { "defaultMessage": "Preferences", "id": "navigation_bar.preferences" @@ -1399,6 +1424,14 @@ "defaultMessage": "Boost", "id": "status.reblog" }, + { + "defaultMessage": "Boost to original audience", + "id": "status.reblog_private" + }, + { + "defaultMessage": "Unboost", + "id": "status.cancel_reblog_private" + }, { "defaultMessage": "This post cannot be boosted", "id": "status.cannot_reblog" diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index a389735c1..d8bd7e3e0 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Local timeline", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symbols", "emoji_button.travel": "Travel & Places", "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "There is nothing in this hashtag yet.", "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.", "empty_column.home.public_timeline": "the public timeline", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Blocked users", "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Edit profile", "navigation_bar.favourites": "Favourites", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "A look inside...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Delete", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Pin on profile", "status.pinned": "Pinned toot", "status.reblog": "Boost", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} boosted", "status.reply": "Reply", "status.replyAll": "Reply to thread", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 19f3c5907..e51163971 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Bonvolu reprovi", "column.blocks": "Blokitaj uzantoj", "column.community": "Loka tempolinio", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Stelumoj", "column.follow_requests": "Petoj de sekvado", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Simboloj", "emoji_button.travel": "Vojaĝoj kaj lokoj", "empty_column.community": "La loka tempolinio estas malplena. Skribu ion por plenigi ĝin!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Ankoraŭ estas nenio per ĉi tiu kradvorto.", "empty_column.home": "Via hejma tempolinio estas malplena! Vizitu {public} aŭ uzu la serĉilon por renkonti aliajn uzantojn.", "empty_column.home.public_timeline": "la publikan tempolinion", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Ĉu vi volas kaŝi la sciigojn el ĉi tiu uzanto?", "navigation_bar.blocks": "Blokitaj uzantoj", "navigation_bar.community_timeline": "Loka tempolinio", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Redakti profilon", "navigation_bar.favourites": "Stelumoj", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezultoj}}", "standalone.public_title": "Enrigardo…", "status.block": "Bloki @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Ĉi tiu mesaĝo ne diskonigeblas", "status.delete": "Forigi", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Alpingli profile", "status.pinned": "Alpinglita mesaĝo", "status.reblog": "Diskonigi", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} diskonigis", "status.reply": "Respondi", "status.replyAll": "Respondi al la fadeno", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index e765cc078..61ea0588d 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Inténtalo de nuevo", "column.blocks": "Usuarios bloqueados", "column.community": "Línea de tiempo local", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favoritos", "column.follow_requests": "Solicitudes de seguimiento", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Símbolos", "emoji_button.travel": "Viajes y lugares", "empty_column.community": "La línea de tiempo local está vacía. ¡Escribe algo para empezar la fiesta!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "No hay nada en este hashtag aún.", "empty_column.home": "No estás siguiendo a nadie aún. Visita {public} o haz búsquedas para empezar y conocer gente nueva.", "empty_column.home.public_timeline": "la línea de tiempo pública", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Ocultar notificaciones de este usuario?", "navigation_bar.blocks": "Usuarios bloqueados", "navigation_bar.community_timeline": "Historia local", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favoritos", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "standalone.public_title": "Un pequeño vistazo...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Este toot no puede retootearse", "status.delete": "Borrar", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Fijar", "status.pinned": "Toot fijado", "status.reblog": "Retootear", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "Retooteado por {name}", "status.reply": "Responder", "status.replyAll": "Responder al hilo", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 822c998ce..cfe93007d 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "تلاش دوباره", "column.blocks": "کاربران مسدودشده", "column.community": "نوشته‌های محلی", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "پسندیده‌ها", "column.follow_requests": "درخواست‌های پیگیری", @@ -100,6 +101,7 @@ "emoji_button.symbols": "نمادها", "emoji_button.travel": "سفر و مکان", "empty_column.community": "فهرست نوشته‌های محلی خالی است. چیزی بنویسید تا چرخش بچرخد!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "هنوز هیچ چیزی با این هشتگ نیست.", "empty_column.home": "شما هنوز پیگیر کسی نیستید. {public} را ببینید یا چیزی را جستجو کنید تا کاربران دیگر را ببینید.", "empty_column.home.public_timeline": "فهرست نوشته‌های همه‌جا", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "اعلان‌های این کاربر پنهان شود؟", "navigation_bar.blocks": "کاربران مسدودشده", "navigation_bar.community_timeline": "نوشته‌های محلی", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "ویرایش نمایه", "navigation_bar.favourites": "پسندیده‌ها", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}", "standalone.public_title": "نگاهی به کاربران این سرور...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید", "status.delete": "پاک‌کردن", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "نوشتهٔ ثابت نمایه", "status.pinned": "Pinned toot", "status.reblog": "بازبوقیدن", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "‫{name}‬ بازبوقید", "status.reply": "پاسخ", "status.replyAll": "به نوشته پاسخ دهید", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 5763ac442..1677c3c6c 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Yritä uudestaan", "column.blocks": "Estetyt käyttäjät", "column.community": "Paikallinen aikajana", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Suosikit", "column.follow_requests": "Seuraamispyynnöt", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symbolit", "emoji_button.travel": "Matkailu", "empty_column.community": "Paikallinen aikajana on tyhjä. Homma lähtee käyntiin, kun kirjoitat jotain julkista!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Tällä hashtagilla ei ole vielä mitään.", "empty_column.home": "Kotiaikajanasi on tyhjä! {public} ja hakutoiminto auttavat alkuun ja kohtaamaan muita käyttäjiä.", "empty_column.home.public_timeline": "yleinen aikajana", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Piilota tältä käyttäjältä tulevat ilmoitukset?", "navigation_bar.blocks": "Estetyt käyttäjät", "navigation_bar.community_timeline": "Paikallinen aikajana", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Muokkaa profiilia", "navigation_bar.favourites": "Suosikit", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "Kurkistus sisälle...", "status.block": "Estä @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Tätä julkaisua ei voi buustata", "status.delete": "Poista", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Kiinnitä profiiliin", "status.pinned": "Kiinnitetty tuuttaus", "status.reblog": "Buustaa", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} buustasi", "status.reply": "Vastaa", "status.replyAll": "Vastaa ketjuun", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index ef1c115e3..98c1c43d2 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Réessayer", "column.blocks": "Comptes bloqués", "column.community": "Fil public local", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favoris", "column.follow_requests": "Demandes de suivi", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symboles", "emoji_button.travel": "Lieux & Voyages", "empty_column.community": "Le fil public local est vide. Écrivez donc quelque chose pour le remplir !", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag.", "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres personnes.", "empty_column.home.public_timeline": "le fil public", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Masquer les notifications de cette personne ?", "navigation_bar.blocks": "Comptes bloqués", "navigation_bar.community_timeline": "Fil public local", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Modifier le profil", "navigation_bar.favourites": "Favoris", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {résultat} other {résultats}}", "standalone.public_title": "Un aperçu …", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Cette publication ne peut être boostée", "status.delete": "Effacer", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Épingler sur le profil", "status.pinned": "Pouet épinglé", "status.reblog": "Partager", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} a partagé :", "status.reply": "Répondre", "status.replyAll": "Répondre au fil", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 5cbb7d31d..fca42374d 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Inténteo de novo", "column.blocks": "Usuarias bloqueadas", "column.community": "Liña temporal local", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favoritas", "column.follow_requests": "Peticións de seguimento", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Símbolos", "emoji_button.travel": "Viaxes e Lugares", "empty_column.community": "A liña temporal local está baldeira. Escriba algo de xeito público para que rule!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Aínda non hai nada con esta etiqueta.", "empty_column.home": "A súa liña temporal de inicio está baldeira! Visite {public} ou utilice a busca para atopar outras usuarias.", "empty_column.home.public_timeline": "a liña temporal pública", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Esconder notificacións deste usuario?", "navigation_bar.blocks": "Usuarias bloqueadas", "navigation_bar.community_timeline": "Liña temporal local", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favoritas", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count,plural,one {result} outros {results}}", "standalone.public_title": "Ollada dentro...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Esta mensaxe non pode ser promocionada", "status.delete": "Eliminar", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Fixar no perfil", "status.pinned": "Pinned toot", "status.reblog": "Promover", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} promoveu", "status.reply": "Resposta", "status.replyAll": "Resposta a conversa", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 656d93cdf..e3e87f1d0 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "לנסות שוב", "column.blocks": "חסימות", "column.community": "ציר זמן מקומי", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "חיבובים", "column.follow_requests": "בקשות מעקב", @@ -100,6 +101,7 @@ "emoji_button.symbols": "סמלים", "emoji_button.travel": "טיולים ואתרים", "empty_column.community": "טור הסביבה ריק. יש לפרסם משהו כדי שדברים יתרחילו להתגלגל!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "אין כלום בהאשתג הזה עדיין.", "empty_column.home": "אף אחד לא במעקב עדיין. אפשר לבקר ב{public} או להשתמש בחיפוש כדי להתחיל ולהכיר חצוצרנים אחרים.", "empty_column.home.public_timeline": "ציר זמן בין-קהילתי", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "להסתיר הודעות מחשבון זה?", "navigation_bar.blocks": "חסימות", "navigation_bar.community_timeline": "ציר זמן מקומי", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "עריכת פרופיל", "navigation_bar.favourites": "חיבובים", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {תוצאה} other {תוצאות}}", "standalone.public_title": "הצצה פנימה...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "לא ניתן להדהד הודעה זו", "status.delete": "מחיקה", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "לקבע באודות", "status.pinned": "Pinned toot", "status.reblog": "הדהוד", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "הודהד על ידי {name}", "status.reply": "תגובה", "status.replyAll": "תגובה לכולם", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index 2d7d0a5a4..b41c98394 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Blokirani korisnici", "column.community": "Lokalni timeline", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favoriti", "column.follow_requests": "Zahtjevi za slijeđenje", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Simboli", "emoji_button.travel": "Putovanja & Mjesta", "empty_column.community": "Lokalni timeline je prazan. Napiši nešto javno kako bi pokrenuo stvari!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Još ne postoji ništa s ovim hashtagom.", "empty_column.home": "Još ne slijediš nikoga. Posjeti {public} ili koristi tražilicu kako bi počeo i upoznao druge korisnike.", "empty_column.home.public_timeline": "javni timeline", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Blokirani korisnici", "navigation_bar.community_timeline": "Lokalni timeline", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Uredi profil", "navigation_bar.favourites": "Favoriti", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "A look inside...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Ovaj post ne može biti boostan", "status.delete": "Obriši", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Pin on profile", "status.pinned": "Pinned toot", "status.reblog": "Podigni", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} je podigao", "status.reply": "Odgovori", "status.replyAll": "Odgovori na temu", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 24f3a7816..956accc67 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Próbálja újra", "column.blocks": "Letiltott felhasználók", "column.community": "Helyi idővonal", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Kedvencek", "column.follow_requests": "Követési kérések", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Szimbólumok", "emoji_button.travel": "Utazás és Helyek", "empty_column.community": "A helyi idővonal üres. Írj egy publikus stástuszt, hogy elindítsd a labdát!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Jelenleg nem található semmi ezen hashtaggel.", "empty_column.home": "A hazai idővonala üres! Látogasd meg a {public} vagy használd a keresőt, hogy ismerj meg más felhasználókat.", "empty_column.home.public_timeline": "publikus idővonal", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Értesítések elrejtése ezen felhasználótól?", "navigation_bar.blocks": "Tiltott felhasználók", "navigation_bar.community_timeline": "Helyi idővonal", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Profil szerkesztése", "navigation_bar.favourites": "Kedvencek", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "Betekintés...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Ezen státusz nem rebloggolható", "status.delete": "Törlés", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Kitűzés a profilra", "status.pinned": "Pinned toot", "status.reblog": "Reblog", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} reblogolta", "status.reply": "Válasz", "status.replyAll": "Válaszolj a beszélgetésre", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index 2ba52c5c0..33e079201 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Կրկին փորձել", "column.blocks": "Արգելափակված օգտատերեր", "column.community": "Տեղական հոսք", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Հավանածներ", "column.follow_requests": "Հետեւելու հայցեր", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Նշաններ", "emoji_button.travel": "Ուղեւորություն եւ տեղանքներ", "empty_column.community": "Տեղական հոսքը դատա՛րկ է։ Հրապարակային մի բան գրիր շարժիչը խոդ տալու համար։", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Այս պիտակով դեռ ոչինչ չկա։", "empty_column.home": "Քո հիմնական հոսքը դատա՛րկ է։ Այցելի՛ր {public}ը կամ օգտվիր որոնումից՝ այլ մարդկանց հանդիպելու համար։", "empty_column.home.public_timeline": "հրապարակային հոսք", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Թաքցնե՞լ ցանուցումներն այս օգտատիրոջից։", "navigation_bar.blocks": "Արգելափակված օգտատերեր", "navigation_bar.community_timeline": "Տեղական հոսք", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Խմբագրել անձնական էջը", "navigation_bar.favourites": "Հավանածներ", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "Այս պահին…", "status.block": "Արգելափակել @{name}֊ին", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Այս թութը չի կարող տարածվել", "status.delete": "Ջնջել", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Ամրացնել անձնական էջում", "status.pinned": "Pinned toot", "status.reblog": "Տարածել", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} տարածել է", "status.reply": "Պատասխանել", "status.replyAll": "Պատասխանել թելին", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index e1518c1aa..412ffd3a0 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Coba lagi", "column.blocks": "Pengguna diblokir", "column.community": "Linimasa Lokal", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favorit", "column.follow_requests": "Permintaan mengikuti", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Simbol", "emoji_button.travel": "Tempat Wisata", "empty_column.community": "Linimasa lokal masih kosong. Tulis sesuatu secara publik dan buat roda berputar!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Tidak ada apapun dalam hashtag ini.", "empty_column.home": "Linimasa anda kosong! Kunjungi {public} atau gunakan pencarian untuk memulai dan bertemu pengguna lain.", "empty_column.home.public_timeline": "linimasa publik", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Pengguna diblokir", "navigation_bar.community_timeline": "Linimasa lokal", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Ubah profil", "navigation_bar.favourites": "Favorit", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {hasil} other {hasil}}", "standalone.public_title": "A look inside...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Hapus", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Pin on profile", "status.pinned": "Pinned toot", "status.reblog": "Boost", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "di-boost {name}", "status.reply": "Balas", "status.replyAll": "Balas ke semua", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index c79d4a634..9730bf934 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Blokusita uzeri", "column.community": "Lokala tempolineo", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favorati", "column.follow_requests": "Demandi di sequado", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symbols", "emoji_button.travel": "Travel & Places", "empty_column.community": "La lokala tempolineo esas vakua. Skribez ulo publike por iniciar la agiveso!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Esas ankore nulo en ta gretovorto.", "empty_column.home": "Tu sequas ankore nulu. Vizitez {public} od uzez la serchilo por komencar e renkontrar altra uzeri.", "empty_column.home.public_timeline": "la publika tempolineo", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Blokusita uzeri", "navigation_bar.community_timeline": "Lokala tempolineo", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Modifikar profilo", "navigation_bar.favourites": "Favorati", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezulti}}", "standalone.public_title": "A look inside...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Efacar", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Pin on profile", "status.pinned": "Pinned toot", "status.reblog": "Repetar", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} repetita", "status.reply": "Respondar", "status.replyAll": "Respondar a filo", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 3c85a3e20..5146d7ca2 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Utenti bloccati", "column.community": "Timeline locale", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Apprezzati", "column.follow_requests": "Richieste di amicizia", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symbols", "emoji_button.travel": "Travel & Places", "empty_column.community": "La timeline locale è vuota. Condividi qualcosa pubblicamente per dare inizio alla festa!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Non c'è ancora nessun post con questo hashtag.", "empty_column.home": "Non stai ancora seguendo nessuno. Visita {public} o usa la ricerca per incontrare nuove persone.", "empty_column.home.public_timeline": "la timeline pubblica", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Utenti bloccati", "navigation_bar.community_timeline": "Timeline locale", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Modifica profilo", "navigation_bar.favourites": "Apprezzati", @@ -238,6 +241,7 @@ "search_results.total": "{count} {count, plural, one {risultato} other {risultati}}", "standalone.public_title": "A look inside...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Elimina", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Pin on profile", "status.pinned": "Pinned toot", "status.reblog": "Condividi", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} ha condiviso", "status.reply": "Rispondi", "status.replyAll": "Reply to thread", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 2da9192ca..a4aa06a06 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "再試行", "column.blocks": "ブロックしたユーザー", "column.community": "ローカルタイムライン", + "column.direct": "Direct messages", "column.domain_blocks": "非表示にしたドメイン", "column.favourites": "お気に入り", "column.follow_requests": "フォローリクエスト", @@ -100,6 +101,7 @@ "emoji_button.symbols": "記号", "emoji_button.travel": "旅行と場所", "empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "このハッシュタグはまだ使われていません。", "empty_column.home": "まだ誰もフォローしていません。{public}を見に行くか、検索を使って他のユーザーを見つけましょう。", "empty_column.home.public_timeline": "連合タイムライン", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "このユーザーからの通知を隠しますか?", "navigation_bar.blocks": "ブロックしたユーザー", "navigation_bar.community_timeline": "ローカルタイムライン", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "非表示にしたドメイン", "navigation_bar.edit_profile": "プロフィールを編集", "navigation_bar.favourites": "お気に入り", @@ -238,6 +241,7 @@ "search_results.total": "{count, number}件の結果", "standalone.public_title": "今こんな話をしています...", "status.block": "@{name}さんをブロック", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "この投稿はブーストできません", "status.delete": "削除", "status.direct": "@{name}さんにダイレクトメッセージ", @@ -253,6 +257,7 @@ "status.pin": "プロフィールに固定表示", "status.pinned": "固定されたトゥート", "status.reblog": "ブースト", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name}さんがブースト", "status.reply": "返信", "status.replyAll": "全員に返信", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index e2fadff3c..92367dc95 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "다시 시도", "column.blocks": "차단 중인 사용자", "column.community": "로컬 타임라인", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "즐겨찾기", "column.follow_requests": "팔로우 요청", @@ -100,6 +101,7 @@ "emoji_button.symbols": "기호", "emoji_button.travel": "여행과 장소", "empty_column.community": "로컬 타임라인에 아무 것도 없습니다. 아무거나 적어 보세요!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.", "empty_column.home": "아직 아무도 팔로우 하고 있지 않습니다. {public}를 보러 가거나, 검색하여 다른 사용자를 찾아 보세요.", "empty_column.home.public_timeline": "연합 타임라인", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "이 사용자로부터의 알림을 뮤트하시겠습니까?", "navigation_bar.blocks": "차단한 사용자", "navigation_bar.community_timeline": "로컬 타임라인", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "프로필 편집", "navigation_bar.favourites": "즐겨찾기", @@ -238,6 +241,7 @@ "search_results.total": "{count, number}건의 결과", "standalone.public_title": "지금 이런 이야기를 하고 있습니다…", "status.block": "@{name} 차단", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다", "status.delete": "삭제", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "고정", "status.pinned": "고정 된 툿", "status.reblog": "부스트", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name}님이 부스트 했습니다", "status.reply": "답장", "status.replyAll": "전원에게 답장", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 0222432a0..c18ddbd01 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Opnieuw proberen", "column.blocks": "Geblokkeerde gebruikers", "column.community": "Lokale tijdlijn", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favorieten", "column.follow_requests": "Volgverzoeken", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symbolen", "emoji_button.travel": "Reizen en plekken", "empty_column.community": "De lokale tijdlijn is nog leeg. Toot iets in het openbaar om de bal aan het rollen te krijgen!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.", "empty_column.home": "Jij volgt nog niemand. Bezoek {public} of gebruik het zoekvenster om andere mensen te ontmoeten.", "empty_column.home.public_timeline": "de globale tijdlijn", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Verberg meldingen van deze persoon?", "navigation_bar.blocks": "Geblokkeerde gebruikers", "navigation_bar.community_timeline": "Lokale tijdlijn", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Profiel bewerken", "navigation_bar.favourites": "Favorieten", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}", "standalone.public_title": "Een kijkje binnenin...", "status.block": "Blokkeer @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Deze toot kan niet geboost worden", "status.delete": "Verwijderen", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Aan profielpagina vastmaken", "status.pinned": "Vastgemaakte toot", "status.reblog": "Boost", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} boostte", "status.reply": "Reageren", "status.replyAll": "Reageer op iedereen", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 20b2cbb26..282a72acb 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Prøv igjen", "column.blocks": "Blokkerte brukere", "column.community": "Lokal tidslinje", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Likt", "column.follow_requests": "Følgeforespørsler", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symboler", "emoji_button.travel": "Reise & steder", "empty_column.community": "Den lokale tidslinjen er tom. Skriv noe offentlig for å få snøballen til å rulle!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Det er ingenting i denne hashtagen ennå.", "empty_column.home": "Du har ikke fulgt noen ennå. Besøk {publlic} eller bruk søk for å komme i gang og møte andre brukere.", "empty_column.home.public_timeline": "en offentlig tidslinje", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Skjul varslinger fra denne brukeren?", "navigation_bar.blocks": "Blokkerte brukere", "navigation_bar.community_timeline": "Lokal tidslinje", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Rediger profil", "navigation_bar.favourites": "Favoritter", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}", "standalone.public_title": "En titt inni...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Denne posten kan ikke fremheves", "status.delete": "Slett", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Fest på profilen", "status.pinned": "Pinned toot", "status.reblog": "Fremhev", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "Fremhevd av {name}", "status.reply": "Svar", "status.replyAll": "Svar til samtale", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 32133c1f4..7170aefb8 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Tornar ensajar", "column.blocks": "Personas blocadas", "column.community": "Flux public local", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favorits", "column.follow_requests": "Demandas d’abonament", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Simbòls", "emoji_button.travel": "Viatges & lòcs", "empty_column.community": "Lo flux public local es void. Escrivètz quicòm per lo garnir !", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "I a pas encara de contengut ligat a aquesta etiqueta.", "empty_column.home": "Vòstre flux d’acuèlh es void. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.", "empty_column.home.public_timeline": "lo flux public", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Rescondre las notificacions d’aquesta persona ?", "navigation_bar.blocks": "Personas blocadas", "navigation_bar.community_timeline": "Flux public local", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Modificar lo perfil", "navigation_bar.favourites": "Favorits", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}", "standalone.public_title": "Una ulhada dedins…", "status.block": "Blocar @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat", "status.delete": "Escafar", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Penjar al perfil", "status.pinned": "Tut penjat", "status.reblog": "Partejar", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} a partejat", "status.reply": "Respondre", "status.replyAll": "Respondre a la conversacion", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 5360b6f33..c55603a99 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Spróbuj ponownie", "column.blocks": "Zablokowani użytkownicy", "column.community": "Lokalna oś czasu", + "column.direct": "Direct messages", "column.domain_blocks": "Ukryte domeny", "column.favourites": "Ulubione", "column.follow_requests": "Prośby o śledzenie", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symbole", "emoji_button.travel": "Podróże i miejsca", "empty_column.community": "Lokalna oś czasu jest pusta. Napisz coś publicznie, aby zagaić!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Nie ma wpisów oznaczonych tym hashtagiem. Możesz napisać pierwszy!", "empty_column.home": "Nie śledzisz nikogo. Odwiedź publiczną oś czasu lub użyj wyszukiwarki, aby znaleźć interesujące Cię profile.", "empty_column.home.public_timeline": "publiczna oś czasu", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?", "navigation_bar.blocks": "Zablokowani użytkownicy", "navigation_bar.community_timeline": "Lokalna oś czasu", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Ukryte domeny", "navigation_bar.edit_profile": "Edytuj profil", "navigation_bar.favourites": "Ulubione", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {wynik} few {wyniki} many {wyników} more {wyników}}", "standalone.public_title": "Spojrzenie w głąb…", "status.block": "Zablokuj @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Ten wpis nie może zostać podbity", "status.delete": "Usuń", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Przypnij do profilu", "status.pinned": "Przypięty wpis", "status.reblog": "Podbij", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} podbił", "status.reply": "Odpowiedz", "status.replyAll": "Odpowiedz na wątek", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index b4be0bbc7..c604476c7 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Tente novamente", "column.blocks": "Usuários bloqueados", "column.community": "Local", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favoritos", "column.follow_requests": "Seguidores pendentes", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Símbolos", "emoji_button.travel": "Viagens & Lugares", "empty_column.community": "A timeline local está vazia. Escreva algo publicamente para começar!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag.", "empty_column.home": "Você ainda não segue usuário algum. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.", "empty_column.home.public_timeline": "global", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Esconder notificações deste usuário?", "navigation_bar.blocks": "Usuários bloqueados", "navigation_bar.community_timeline": "Local", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favoritos", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "standalone.public_title": "Dê uma espiada...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Esta postagem não pode ser compartilhada", "status.delete": "Excluir", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Fixar no perfil", "status.pinned": "Toot fixado", "status.reblog": "Compartilhar", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} compartilhou", "status.reply": "Responder", "status.replyAll": "Responder à sequência", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index 132de5293..826785aad 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Tente de novo", "column.blocks": "Utilizadores Bloqueados", "column.community": "Local", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favoritos", "column.follow_requests": "Seguidores Pendentes", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Símbolos", "emoji_button.travel": "Viagens & Lugares", "empty_column.community": "Ainda não existe conteúdo local para mostrar!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Não foram encontradas publicações com essa hashtag.", "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.", "empty_column.home.public_timeline": "global", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Esconder notificações deste utilizador?", "navigation_bar.blocks": "Utilizadores bloqueados", "navigation_bar.community_timeline": "Local", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favoritos", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "standalone.public_title": "Espreitar lá dentro...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Este post não pode ser partilhado", "status.delete": "Eliminar", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Fixar no perfil", "status.pinned": "Pinned toot", "status.reblog": "Partilhar", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} partilhou", "status.reply": "Responder", "status.replyAll": "Responder à conversa", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index b56ccf1df..bb3cc1794 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Попробовать снова", "column.blocks": "Список блокировки", "column.community": "Локальная лента", + "column.direct": "Direct messages", "column.domain_blocks": "Скрытые домены", "column.favourites": "Понравившееся", "column.follow_requests": "Запросы на подписку", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Символы", "emoji_button.travel": "Путешествия", "empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Статусов с таким хэштегом еще не существует.", "empty_column.home": "Пока Вы ни на кого не подписаны. Полистайте {public} или используйте поиск, чтобы освоиться и завести новые знакомства.", "empty_column.home.public_timeline": "публичные ленты", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Убрать уведомления от этого пользователя?", "navigation_bar.blocks": "Список блокировки", "navigation_bar.community_timeline": "Локальная лента", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Скрытые домены", "navigation_bar.edit_profile": "Изменить профиль", "navigation_bar.favourites": "Понравившееся", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {результат} few {результата} many {результатов} other {результатов}}", "standalone.public_title": "Прямо сейчас", "status.block": "Заблокировать @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Этот статус не может быть продвинут", "status.delete": "Удалить", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Закрепить в профиле", "status.pinned": "Pinned toot", "status.reblog": "Продвинуть", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} продвинул(а)", "status.reply": "Ответить", "status.replyAll": "Ответить на тред", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 159315137..58274fd2d 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Skúsiť znova", "column.blocks": "Blokovaní užívatelia", "column.community": "Lokálna časová os", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Obľúbené", "column.follow_requests": "Žiadosti o sledovanie", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symboly", "emoji_button.travel": "Cestovanie a miesta", "empty_column.community": "Lokálna časová os je prázdna. Napíšte niečo, aby sa to tu začalo hýbať!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Pod týmto hashtagom sa ešte nič nenachádza.", "empty_column.home": "Vaša lokálna osa je zatiaľ prázdna! Pre začiatok pozrite {public} alebo použite vyhľadávanie a nájdite tak ostatných používateľov.", "empty_column.home.public_timeline": "verejná časová os", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?", "navigation_bar.blocks": "Blokovaní užívatelia", "navigation_bar.community_timeline": "Lokálna časová os", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Upraviť profil", "navigation_bar.favourites": "Obľúbené", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, jeden {výsledok} ostatné {výsledky}}", "standalone.public_title": "Náhľad dovnútra...", "status.block": "Blokovať @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Tento príspevok nemôže byť re-tootnutý", "status.delete": "Zmazať", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Pripni na profil", "status.pinned": "Pripnutý príspevok", "status.reblog": "Povýšiť", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} povýšil/a", "status.reply": "Odpovedať", "status.replyAll": "Odpovedať na diskusiu", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index 69c7aa62b..e4d07edd1 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Pokušajte ponovo", "column.blocks": "Blokirani korisnici", "column.community": "Lokalna lajna", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Omiljeni", "column.follow_requests": "Zahtevi za praćenje", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Simboli", "emoji_button.travel": "Putovanja & mesta", "empty_column.community": "Lokalna lajna je prazna. Napišite nešto javno da lajna produva!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Trenutno nema ništa na ovom heštegu.", "empty_column.home": "Vaša lajna je prazna! Posetite {public} ili koristite pretragu da počnete i upoznajete nove ljude.", "empty_column.home.public_timeline": "javna lajna", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Sakrij obaveštenja od ovog korisnika?", "navigation_bar.blocks": "Blokirani korisnici", "navigation_bar.community_timeline": "Lokalna lajna", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Izmeni profil", "navigation_bar.favourites": "Omiljeni", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}", "standalone.public_title": "Pogled iznutra...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Ovaj status ne može da se podrži", "status.delete": "Obriši", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Prikači na profil", "status.pinned": "Pinned toot", "status.reblog": "Podrži", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} podržao(la)", "status.reply": "Odgovori", "status.replyAll": "Odgovori na diskusiju", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index e9739451e..60c781e9d 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Покушајте поново", "column.blocks": "Блокирани корисници", "column.community": "Локална лајна", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Омиљени", "column.follow_requests": "Захтеви за праћење", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Симболи", "emoji_button.travel": "Путовања & места", "empty_column.community": "Локална лајна је празна. Напишите нешто јавно да лајна продува!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Тренутно нема ништа на овом хештегу.", "empty_column.home": "Ваша лајна је празна! Посетите {public} или користите претрагу да почнете и упознајете нове људе.", "empty_column.home.public_timeline": "јавна лајна", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Сакриј обавештења од овог корисника?", "navigation_bar.blocks": "Блокирани корисници", "navigation_bar.community_timeline": "Локална лајна", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Измени профил", "navigation_bar.favourites": "Омиљени", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {резултат} few {резултата} other {резултата}}", "standalone.public_title": "Поглед изнутра...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Овај статус не може да се подржи", "status.delete": "Обриши", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Прикачи на профил", "status.pinned": "Pinned toot", "status.reblog": "Подржи", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} подржао(ла)", "status.reply": "Одговори", "status.replyAll": "Одговори на дискусију", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index b063adb0f..8fa6992f1 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Försök igen", "column.blocks": "Blockerade användare", "column.community": "Lokal tidslinje", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favoriter", "column.follow_requests": "Följ förfrågningar", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symboler", "emoji_button.travel": "Resor & Platser", "empty_column.community": "Den lokala tidslinjen är tom. Skriv något offentligt för att få bollen att rulla!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Det finns inget i denna hashtag ännu.", "empty_column.home": "Din hemma-tidslinje är tom! Besök {public} eller använd sökning för att komma igång och träffa andra användare.", "empty_column.home.public_timeline": "den publika tidslinjen", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Dölj notifikationer från denna användare?", "navigation_bar.blocks": "Blockerade användare", "navigation_bar.community_timeline": "Lokal tidslinje", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Redigera profil", "navigation_bar.favourites": "Favoriter", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, ett {result} andra {results}}", "standalone.public_title": "En titt inuti...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Detta inlägg kan inte knuffas", "status.delete": "Ta bort", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Fäst i profil", "status.pinned": "Fäst toot", "status.reblog": "Knuff", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} knuffade", "status.reply": "Svara", "status.replyAll": "Svara på tråden", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 22a75c237..3b91c0d2c 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Blocked users", "column.community": "Local timeline", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favourites", "column.follow_requests": "Follow requests", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Symbols", "emoji_button.travel": "Travel & Places", "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "There is nothing in this hashtag yet.", "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.", "empty_column.home.public_timeline": "the public timeline", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Blocked users", "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Edit profile", "navigation_bar.favourites": "Favourites", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "A look inside...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Delete", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Pin on profile", "status.pinned": "Pinned toot", "status.reblog": "Boost", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} boosted", "status.reply": "Reply", "status.replyAll": "Reply to thread", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index 8e36c512f..cdf6f46a3 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Engellenen kullanıcılar", "column.community": "Yerel zaman tüneli", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Favoriler", "column.follow_requests": "Takip istekleri", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Semboller", "emoji_button.travel": "Seyahat ve Yerler", "empty_column.community": "Yerel zaman tüneliniz boş. Daha fazla eğlence için herkese açık bir gönderi paylaşın.", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Henüz bu hashtag’e sahip hiçbir gönderi yok.", "empty_column.home": "Henüz kimseyi takip etmiyorsunuz. {public} ziyaret edebilir veya arama kısmını kullanarak diğer kullanıcılarla iletişime geçebilirsiniz.", "empty_column.home.public_timeline": "herkese açık zaman tüneli", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Engellenen kullanıcılar", "navigation_bar.community_timeline": "Yerel zaman tüneli", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Profili düzenle", "navigation_bar.favourites": "Favoriler", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {sonuç} other {sonuçlar}}", "standalone.public_title": "A look inside...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Bu gönderi boost edilemez", "status.delete": "Sil", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Pin on profile", "status.pinned": "Pinned toot", "status.reblog": "Boost'la", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} boost etti", "status.reply": "Cevapla", "status.replyAll": "Konuşmayı cevapla", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 09210a325..261e5795e 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "Try again", "column.blocks": "Заблоковані користувачі", "column.community": "Локальна стрічка", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "Вподобане", "column.follow_requests": "Запити на підписку", @@ -100,6 +101,7 @@ "emoji_button.symbols": "Символи", "emoji_button.travel": "Подорожі", "empty_column.community": "Локальна стрічка пуста. Напишіть щось, щоб розігріти народ!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "Дописів з цим хештегом поки не існує.", "empty_column.home": "Ви поки ні на кого не підписані. Погортайте {public}, або скористуйтесь пошуком, щоб освоїтися та познайомитися з іншими користувачами.", "empty_column.home.public_timeline": "публічні стрічки", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "Hide notifications from this user?", "navigation_bar.blocks": "Заблоковані користувачі", "navigation_bar.community_timeline": "Локальна стрічка", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Редагувати профіль", "navigation_bar.favourites": "Вподобане", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} {count, plural, one {результат} few {результати} many {результатів} other {результатів}}", "standalone.public_title": "A look inside...", "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Цей допис не може бути передмухнутий", "status.delete": "Видалити", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "Pin on profile", "status.pinned": "Pinned toot", "status.reblog": "Передмухнути", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} передмухнув(-ла)", "status.reply": "Відповісти", "status.replyAll": "Відповісти на тред", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index f0772ffaa..aba0bde83 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "重试", "column.blocks": "屏蔽用户", "column.community": "本站时间轴", + "column.direct": "Direct messages", "column.domain_blocks": "Hidden domains", "column.favourites": "收藏过的嘟文", "column.follow_requests": "关注请求", @@ -100,6 +101,7 @@ "emoji_button.symbols": "符号", "emoji_button.travel": "旅行和地点", "empty_column.community": "本站时间轴暂时没有内容,快嘟几个来抢头香啊!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "这个话题标签下暂时没有内容。", "empty_column.home": "你还没有关注任何用户。快看看{public},向其他用户搭讪吧。", "empty_column.home.public_timeline": "公共时间轴", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "同时隐藏来自这个用户的通知", "navigation_bar.blocks": "被屏蔽的用户", "navigation_bar.community_timeline": "本站时间轴", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "修改个人资料", "navigation_bar.favourites": "收藏的内容", @@ -238,6 +241,7 @@ "search_results.total": "共 {count, number} 个结果", "standalone.public_title": "大家都在干啥?", "status.block": "屏蔽 @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "无法转嘟这条嘟文", "status.delete": "删除", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "在个人资料页面置顶", "status.pinned": "Pinned toot", "status.reblog": "转嘟", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} 转嘟了", "status.reply": "回复", "status.replyAll": "回复所有人", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 28685f4d8..b5ebd20fc 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "重試", "column.blocks": "封鎖用戶", "column.community": "本站時間軸", + "column.direct": "Direct messages", "column.domain_blocks": "隱藏的服務站", "column.favourites": "最愛的文章", "column.follow_requests": "關注請求", @@ -100,6 +101,7 @@ "emoji_button.symbols": "符號", "emoji_button.travel": "旅遊景物", "empty_column.community": "本站時間軸暫時未有內容,快寫一點東西來搶頭香啊!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "這個標籤暫時未有內容。", "empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。", "empty_column.home.public_timeline": "公共時間軸", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "隱藏來自這用戶的通知嗎?", "navigation_bar.blocks": "被你封鎖的用戶", "navigation_bar.community_timeline": "本站時間軸", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "隱藏的服務站", "navigation_bar.edit_profile": "修改個人資料", "navigation_bar.favourites": "最愛的內容", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} 項結果", "standalone.public_title": "站點一瞥…", "status.block": "封鎖 @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "這篇文章無法被轉推", "status.delete": "刪除", "status.direct": "私訊 @{name}", @@ -253,6 +257,7 @@ "status.pin": "置頂到資料頁", "status.pinned": "置頂文章", "status.reblog": "轉推", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} 轉推", "status.reply": "回應", "status.replyAll": "回應所有人", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index efed9cd4d..28d634600 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -40,6 +40,7 @@ "bundle_modal_error.retry": "重試", "column.blocks": "封鎖的使用者", "column.community": "本地時間軸", + "column.direct": "Direct messages", "column.domain_blocks": "隱藏域名", "column.favourites": "最愛", "column.follow_requests": "關注請求", @@ -100,6 +101,7 @@ "emoji_button.symbols": "符號", "emoji_button.travel": "旅遊與地點", "empty_column.community": "本地時間軸是空的。公開寫點什麼吧!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", "empty_column.hashtag": "這個主題標籤下什麼都沒有。", "empty_column.home": "你還沒關注任何人。造訪{public}或利用搜尋功能找到其他用者。", "empty_column.home.public_timeline": "公開時間軸", @@ -154,6 +156,7 @@ "mute_modal.hide_notifications": "隱藏來自這個使用者的通知?", "navigation_bar.blocks": "封鎖的使用者", "navigation_bar.community_timeline": "本地時間軸", + "navigation_bar.direct": "Direct messages", "navigation_bar.domain_blocks": "隱藏的域名", "navigation_bar.edit_profile": "編輯用者資訊", "navigation_bar.favourites": "最愛", @@ -238,6 +241,7 @@ "search_results.total": "{count, number} 項結果", "standalone.public_title": "站點一瞥…", "status.block": "封鎖 @{name}", + "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "此貼文無法轉推", "status.delete": "刪除", "status.direct": "Direct message @{name}", @@ -253,6 +257,7 @@ "status.pin": "置頂到個人資訊頁", "status.pinned": "置頂的推文", "status.reblog": "轉推", + "status.reblog_private": "Boost to original audience", "status.reblogged_by": "{name} 轉推了", "status.reply": "回應", "status.replyAll": "回應這串", diff --git a/app/javascript/mastodon/reducers/contexts.js b/app/javascript/mastodon/reducers/contexts.js index c1ecf7e8a..ebd01e532 100644 --- a/app/javascript/mastodon/reducers/contexts.js +++ b/app/javascript/mastodon/reducers/contexts.js @@ -41,8 +41,9 @@ const deleteFromContexts = (immutableState, ids) => immutableState.withMutations }); const filterContexts = (state, relationship, statuses) => { - const ownedStatusIds = statuses.filter(status => status.get('account') === relationship.id) - .map(status => status.get('id')); + const ownedStatusIds = statuses + .filter(status => status.get('account') === relationship.id) + .map(status => status.get('id')); return deleteFromContexts(state, ownedStatusIds); }; diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js index 390b2a13a..9ec52a7fa 100644 --- a/app/javascript/mastodon/reducers/settings.js +++ b/app/javascript/mastodon/reducers/settings.js @@ -58,6 +58,12 @@ const initialState = ImmutableMap({ body: '', }), }), + + direct: ImmutableMap({ + regex: ImmutableMap({ + body: '', + }), + }), }); const defaultColumns = fromJS([ diff --git a/app/models/status.rb b/app/models/status.rb index f924be47b..62857dd6b 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -183,6 +183,14 @@ class Status < ApplicationRecord where(account: [account] + account.following).where(visibility: [:public, :unlisted, :private]) end + def as_direct_timeline(account) + query = joins("LEFT OUTER JOIN mentions ON statuses.id = mentions.status_id AND mentions.account_id = #{account.id}") + .where("mentions.account_id = #{account.id} OR statuses.account_id = #{account.id}") + .where(visibility: [:direct]) + + apply_timeline_filters(query, account, false) + end + def as_public_timeline(account = nil, local_only = false) query = timeline_scope(local_only).without_replies diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb index e2763c2b9..cb65a2256 100644 --- a/app/services/batched_remove_status_service.rb +++ b/app/services/batched_remove_status_service.rb @@ -36,6 +36,7 @@ class BatchedRemoveStatusService < BaseService # Cannot be batched statuses.each do |status| unpush_from_public_timelines(status) + unpush_from_direct_timelines(status) if status.direct_visibility? batch_salmon_slaps(status) if status.local? end @@ -87,6 +88,16 @@ class BatchedRemoveStatusService < BaseService end end + def unpush_from_direct_timelines(status) + payload = @json_payloads[status.id] + redis.pipelined do + @mentions[status.id].each do |mention| + redis.publish("timeline:direct:#{mention.account.id}", payload) if mention.account.local? + end + redis.publish("timeline:direct:#{status.account.id}", payload) if status.account.local? + end + end + def batch_salmon_slaps(status) return if @mentions[status.id].empty? diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index bbaf3094b..0f77556dc 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -10,8 +10,11 @@ class FanOutOnWriteService < BaseService deliver_to_self(status) if status.account.local? + render_anonymous_payload(status) + if status.direct_visibility? deliver_to_mentioned_followers(status) + deliver_to_direct_timelines(status) else deliver_to_followers(status) deliver_to_lists(status) @@ -19,7 +22,6 @@ class FanOutOnWriteService < BaseService return if status.account.silenced? || !status.public_visibility? || status.reblog? - render_anonymous_payload(status) deliver_to_hashtags(status) return if status.reply? && status.in_reply_to_account_id != status.account_id @@ -84,4 +86,13 @@ class FanOutOnWriteService < BaseService Redis.current.publish('timeline:public', @payload) Redis.current.publish('timeline:public:local', @payload) if status.local? end + + def deliver_to_direct_timelines(status) + Rails.logger.debug "Delivering status #{status.id} to direct timelines" + + status.mentions.includes(:account).each do |mention| + Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local? + end + Redis.current.publish("timeline:direct:#{status.account.id}", @payload) if status.account.local? + end end diff --git a/app/services/remove_status_service.rb b/app/services/remove_status_service.rb index a100f73ce..e164c03ab 100644 --- a/app/services/remove_status_service.rb +++ b/app/services/remove_status_service.rb @@ -20,6 +20,7 @@ class RemoveStatusService < BaseService remove_reblogs remove_from_hashtags remove_from_public + remove_from_direct if status.direct_visibility? @status.destroy! @@ -130,6 +131,13 @@ class RemoveStatusService < BaseService Redis.current.publish('timeline:public:local', @payload) if @status.local? end + def remove_from_direct + @mentions.each do |mention| + Redis.current.publish("timeline:direct:#{mention.account.id}", @payload) if mention.account.local? + end + Redis.current.publish("timeline:direct:#{@account.id}", @payload) if @account.local? + end + def redis Redis.current end diff --git a/config/routes.rb b/config/routes.rb index 2776898ac..d959301e9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -229,6 +229,7 @@ Rails.application.routes.draw do end namespace :timelines do + resource :direct, only: :show, controller: :direct resource :home, only: :show, controller: :home resource :public, only: :show, controller: :public resources :tag, only: :show diff --git a/spec/models/status_spec.rb b/spec/models/status_spec.rb index 4b5c20871..c6701018e 100644 --- a/spec/models/status_spec.rb +++ b/spec/models/status_spec.rb @@ -304,6 +304,55 @@ RSpec.describe Status, type: :model do end end + describe '.as_direct_timeline' do + let(:account) { Fabricate(:account) } + let(:followed) { Fabricate(:account) } + let(:not_followed) { Fabricate(:account) } + + before do + Fabricate(:follow, account: account, target_account: followed) + + @self_public_status = Fabricate(:status, account: account, visibility: :public) + @self_direct_status = Fabricate(:status, account: account, visibility: :direct) + @followed_public_status = Fabricate(:status, account: followed, visibility: :public) + @followed_direct_status = Fabricate(:status, account: followed, visibility: :direct) + @not_followed_direct_status = Fabricate(:status, account: not_followed, visibility: :direct) + + @results = Status.as_direct_timeline(account) + end + + it 'does not include public statuses from self' do + expect(@results).to_not include(@self_public_status) + end + + it 'includes direct statuses from self' do + expect(@results).to include(@self_direct_status) + end + + it 'does not include public statuses from followed' do + expect(@results).to_not include(@followed_public_status) + end + + it 'includes direct statuses mentioning recipient from followed' do + Fabricate(:mention, account: account, status: @followed_direct_status) + expect(@results).to include(@followed_direct_status) + end + + it 'does not include direct statuses not mentioning recipient from followed' do + expect(@results).to_not include(@followed_direct_status) + end + + it 'includes direct statuses mentioning recipient from non-followed' do + Fabricate(:mention, account: account, status: @not_followed_direct_status) + expect(@results).to include(@not_followed_direct_status) + end + + it 'does not include direct statuses not mentioning recipient from non-followed' do + expect(@results).to_not include(@not_followed_direct_status) + end + + end + describe '.as_public_timeline' do it 'only includes statuses with public visibility' do public_status = Fabricate(:status, visibility: :public) diff --git a/streaming/index.js b/streaming/index.js index 1b4f8596c..48bab8078 100644 --- a/streaming/index.js +++ b/streaming/index.js @@ -466,6 +466,10 @@ const startWorker = (workerId) => { streamFrom('timeline:public:local', req, streamToHttp(req, res), streamHttpEnd(req), true); }); + app.get('/api/v1/streaming/direct', (req, res) => { + streamFrom(`timeline:direct:${req.accountId}`, req, streamToHttp(req, res), streamHttpEnd(req), true); + }); + app.get('/api/v1/streaming/hashtag', (req, res) => { streamFrom(`timeline:hashtag:${req.query.tag.toLowerCase()}`, req, streamToHttp(req, res), streamHttpEnd(req), true); }); @@ -517,6 +521,9 @@ const startWorker = (workerId) => { case 'public:local': streamFrom('timeline:public:local', req, streamToWs(req, ws), streamWsEnd(req, ws), true); break; + case 'direct': + streamFrom(`timeline:direct:${req.accountId}`, req, streamToWs(req, ws), streamWsEnd(req, ws), true); + break; case 'hashtag': streamFrom(`timeline:hashtag:${location.query.tag.toLowerCase()}`, req, streamToWs(req, ws), streamWsEnd(req, ws), true); break; diff --git a/yarn.lock b/yarn.lock index fba2cb9a7..c5a49a497 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5994,9 +5994,9 @@ rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -react-dom@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.2.0.tgz#69003178601c0ca19b709b33a83369fe6124c044" +react-dom@^16.3.0: + version "16.3.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.3.2.tgz#cb90f107e09536d683d84ed5d4888e9640e0e4df" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0" @@ -6180,9 +6180,9 @@ react-transition-group@^2.2.0: prop-types "^15.5.8" warning "^3.0.0" -react@^16.2.0: - version "16.2.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.2.0.tgz#a31bd2dab89bff65d42134fa187f24d054c273ba" +react@^16.3.0: + version "16.3.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.3.2.tgz#fdc8420398533a1e58872f59091b272ce2f91ea9" dependencies: fbjs "^0.8.16" loose-envify "^1.1.0" From 7162a28c34868eb4b8cdd23a4078d99b46949491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Wed, 18 Apr 2018 13:15:57 +0200 Subject: [PATCH 099/442] Add revealing/hiding statuses button to keyboard shortcuts legend column (#7178) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- app/javascript/mastodon/features/keyboard_shortcuts/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.js b/app/javascript/mastodon/features/keyboard_shortcuts/index.js index 8531e03c0..5ae7b34a2 100644 --- a/app/javascript/mastodon/features/keyboard_shortcuts/index.js +++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.js @@ -52,6 +52,10 @@ export default class KeyboardShortcuts extends ImmutablePureComponent { enter + + x + + up From 0ba49eca8b49c6ce0ec04fd546951c95938da4e6 Mon Sep 17 00:00:00 2001 From: abcang Date: Wed, 18 Apr 2018 23:50:19 +0900 Subject: [PATCH 100/442] Fix comparing id (#7180) --- app/javascript/mastodon/compare_id.js | 10 ++++++++++ app/javascript/mastodon/reducers/notifications.js | 12 +++--------- app/javascript/mastodon/reducers/timelines.js | 5 +++-- 3 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 app/javascript/mastodon/compare_id.js diff --git a/app/javascript/mastodon/compare_id.js b/app/javascript/mastodon/compare_id.js new file mode 100644 index 000000000..aaff66481 --- /dev/null +++ b/app/javascript/mastodon/compare_id.js @@ -0,0 +1,10 @@ +export default function compareId(id1, id2) { + if (id1 === id2) { + return 0; + } + if (id1.length === id2.length) { + return id1 > id2 ? 1 : -1; + } else { + return id1.length > id2.length ? 1 : -1; + } +} diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js index 1ac7eb706..da9b8c420 100644 --- a/app/javascript/mastodon/reducers/notifications.js +++ b/app/javascript/mastodon/reducers/notifications.js @@ -12,6 +12,7 @@ import { } from '../actions/accounts'; import { TIMELINE_DELETE, TIMELINE_DISCONNECT } from '../actions/timelines'; import { Map as ImmutableMap, List as ImmutableList } from 'immutable'; +import compareId from '../compare_id'; const initialState = ImmutableMap({ items: ImmutableList(), @@ -44,13 +45,6 @@ const normalizeNotification = (state, notification) => { }); }; -const newer = (m, n) => { - const mId = m.get('id'); - const nId = n.get('id'); - - return mId.length === nId.length ? mId > nId : mId.length > nId.length; -}; - const expandNormalizedNotifications = (state, notifications, next) => { let items = ImmutableList(); @@ -62,11 +56,11 @@ const expandNormalizedNotifications = (state, notifications, next) => { if (!items.isEmpty()) { mutable.update('items', list => { const lastIndex = 1 + list.findLastIndex( - item => item !== null && (newer(item, items.last()) || item.get('id') === items.last().get('id')) + item => item !== null && (compareId(item.get('id'), items.last().get('id')) > 0 || item.get('id') === items.last().get('id')) ); const firstIndex = 1 + list.take(lastIndex).findLastIndex( - item => item !== null && newer(item, items.first()) + item => item !== null && compareId(item.get('id'), items.first().get('id')) > 0 ); return list.take(firstIndex).concat(items, list.skip(lastIndex)); diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js index f795e7e08..ad897bcc9 100644 --- a/app/javascript/mastodon/reducers/timelines.js +++ b/app/javascript/mastodon/reducers/timelines.js @@ -13,6 +13,7 @@ import { ACCOUNT_UNFOLLOW_SUCCESS, } from '../actions/accounts'; import { Map as ImmutableMap, List as ImmutableList, fromJS } from 'immutable'; +import compareId from '../compare_id'; const initialState = ImmutableMap(); @@ -32,8 +33,8 @@ const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial) => if (!statuses.isEmpty()) { mMap.update('items', ImmutableList(), oldIds => { const newIds = statuses.map(status => status.get('id')); - const lastIndex = oldIds.findLastIndex(id => id !== null && id >= newIds.last()) + 1; - const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && id > newIds.first()); + const lastIndex = oldIds.findLastIndex(id => id !== null && compareId(id, newIds.last()) >= 0) + 1; + const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && compareId(id, newIds.first()) >= 0); if (firstIndex < 0) { return (isPartial ? newIds.unshift(null) : newIds).concat(oldIds.skip(lastIndex)); From 74dae9458d118b066cd74b16aab2aa9cafbf3fba Mon Sep 17 00:00:00 2001 From: Lynx Kotoura Date: Wed, 18 Apr 2018 23:52:15 +0900 Subject: [PATCH 101/442] Add color variables of texts for better accesibility (#7125) * Add variables for text colors * Change variables in sass files * Apply text color variables for recently added colors * Fix text colors of emoji mart anchors * Fix text colors of search__input * Fix text colors of text area of compose-form * Fix icon colors of privacy dropdown and modal * Inverted icon colors by classname * Change variables in boost.scss * Change action-button-color * Fix text colors of pre-header --- .../features/ui/components/actions_modal.js | 2 +- app/javascript/styles/mastodon/about.scss | 64 ++-- app/javascript/styles/mastodon/accounts.scss | 42 +-- app/javascript/styles/mastodon/admin.scss | 42 +-- app/javascript/styles/mastodon/basics.scss | 2 +- app/javascript/styles/mastodon/boost.scss | 6 +- .../styles/mastodon/compact_header.scss | 4 +- .../styles/mastodon/components.scss | 353 ++++++++---------- .../styles/mastodon/containers.scss | 2 +- .../styles/mastodon/emoji_picker.scss | 16 +- app/javascript/styles/mastodon/footer.scss | 2 +- app/javascript/styles/mastodon/forms.scss | 39 +- .../styles/mastodon/landing_strip.scss | 8 +- .../styles/mastodon/stream_entries.scss | 32 +- app/javascript/styles/mastodon/tables.scss | 4 +- app/javascript/styles/mastodon/variables.scss | 7 +- 16 files changed, 285 insertions(+), 340 deletions(-) diff --git a/app/javascript/mastodon/features/ui/components/actions_modal.js b/app/javascript/mastodon/features/ui/components/actions_modal.js index 79a5a20ef..9792eba5f 100644 --- a/app/javascript/mastodon/features/ui/components/actions_modal.js +++ b/app/javascript/mastodon/features/ui/components/actions_modal.js @@ -27,7 +27,7 @@ export default class ActionsModal extends ImmutablePureComponent { return (
  • - {icon && } + {icon && }
    {text}
    {meta}
    diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss index 034c35e8a..0a09a38d2 100644 --- a/app/javascript/styles/mastodon/about.scss +++ b/app/javascript/styles/mastodon/about.scss @@ -169,7 +169,7 @@ $small-breakpoint: 960px; background: $ui-base-color; font-size: 12px; font-weight: 500; - color: $ui-primary-color; + color: $darker-text-color; text-transform: uppercase; position: relative; z-index: 1; @@ -186,10 +186,10 @@ $small-breakpoint: 960px; font-size: 16px; line-height: 30px; margin-bottom: 12px; - color: $ui-primary-color; + color: $darker-text-color; a { - color: $ui-highlight-color; + color: $highlight-text-color; text-decoration: underline; } } @@ -202,11 +202,11 @@ $small-breakpoint: 960px; text-align: center; font-size: 12px; line-height: 18px; - color: $ui-primary-color; + color: $darker-text-color; margin-bottom: 0; a { - color: $ui-highlight-color; + color: $highlight-text-color; text-decoration: underline; } } @@ -225,7 +225,7 @@ $small-breakpoint: 960px; font-family: inherit; font-size: inherit; line-height: inherit; - color: lighten($ui-primary-color, 10%); + color: transparentize($darker-text-color, 0.1); } h1 { @@ -234,14 +234,14 @@ $small-breakpoint: 960px; line-height: 30px; font-weight: 500; margin-bottom: 20px; - color: $ui-secondary-color; + color: $primary-text-color; small { font-family: 'mastodon-font-sans-serif', sans-serif; display: block; font-size: 18px; font-weight: 400; - color: $ui-base-lighter-color; + color: opacify($darker-text-color, 0.1); } } @@ -251,7 +251,7 @@ $small-breakpoint: 960px; line-height: 26px; font-weight: 500; margin-bottom: 20px; - color: $ui-secondary-color; + color: $primary-text-color; } h3 { @@ -260,7 +260,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $ui-secondary-color; + color: $primary-text-color; } h4 { @@ -269,7 +269,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $ui-secondary-color; + color: $primary-text-color; } h5 { @@ -278,7 +278,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $ui-secondary-color; + color: $primary-text-color; } h6 { @@ -287,7 +287,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $ui-secondary-color; + color: $primary-text-color; } ul, @@ -354,10 +354,10 @@ $small-breakpoint: 960px; font-weight: 400; font-size: 16px; line-height: 30px; - color: $ui-primary-color; + color: $darker-text-color; a { - color: $ui-highlight-color; + color: $highlight-text-color; text-decoration: underline; } } @@ -405,7 +405,7 @@ $small-breakpoint: 960px; font-size: 14px; &:hover { - color: $ui-secondary-color; + color: $darker-text-color; } } @@ -478,10 +478,10 @@ $small-breakpoint: 960px; font-weight: 400; font-size: 16px; line-height: 30px; - color: $ui-primary-color; + color: $darker-text-color; a { - color: $ui-highlight-color; + color: $highlight-text-color; text-decoration: underline; } } @@ -517,7 +517,7 @@ $small-breakpoint: 960px; span { &:last-child { - color: $ui-secondary-color; + color: $darker-text-color; } } @@ -548,7 +548,7 @@ $small-breakpoint: 960px; font-size: 14px; line-height: 24px; font-weight: 500; - color: $ui-primary-color; + color: $darker-text-color; padding-bottom: 5px; margin-bottom: 15px; border-bottom: 1px solid lighten($ui-base-color, 4%); @@ -559,7 +559,7 @@ $small-breakpoint: 960px; a, span { font-weight: 400; - color: darken($ui-primary-color, 10%); + color: opacify($darker-text-color, 0.1); } a { @@ -602,7 +602,7 @@ $small-breakpoint: 960px; .username { display: block; - color: $ui-primary-color; + color: $darker-text-color; } } } @@ -775,7 +775,7 @@ $small-breakpoint: 960px; } p a { - color: $ui-secondary-color; + color: $darker-text-color; } h1 { @@ -784,10 +784,10 @@ $small-breakpoint: 960px; margin-bottom: 0; small { - color: $ui-primary-color; + color: $darker-text-color; span { - color: $ui-secondary-color; + color: $darker-text-color; } } } @@ -896,7 +896,7 @@ $small-breakpoint: 960px; } a { - color: $ui-secondary-color; + color: $darker-text-color; text-decoration: none; } } @@ -935,7 +935,7 @@ $small-breakpoint: 960px; .fa { display: block; - color: $ui-primary-color; + color: $darker-text-color; font-size: 48px; } } @@ -943,7 +943,7 @@ $small-breakpoint: 960px; .text { font-size: 16px; line-height: 30px; - color: $ui-primary-color; + color: $darker-text-color; h6 { font-size: inherit; @@ -969,10 +969,10 @@ $small-breakpoint: 960px; font-weight: 400; font-size: 16px; line-height: 30px; - color: $ui-primary-color; + color: $darker-text-color; a { - color: $ui-highlight-color; + color: $highlight-text-color; text-decoration: underline; } } @@ -980,7 +980,7 @@ $small-breakpoint: 960px; .footer-links { padding-bottom: 50px; text-align: right; - color: $ui-base-lighter-color; + color: $darker-text-color; p { font-size: 14px; @@ -995,7 +995,7 @@ $small-breakpoint: 960px; &__footer { margin-top: 10px; text-align: center; - color: $ui-base-lighter-color; + color: $darker-text-color; p { font-size: 14px; diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index 0b49da1ad..f9af6f288 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -75,7 +75,7 @@ small { display: block; font-size: 14px; - color: $ui-highlight-color; + color: $highlight-text-color; font-weight: 400; overflow: hidden; text-overflow: ellipsis; @@ -113,7 +113,7 @@ width: 33.3%; box-sizing: border-box; flex: 0 0 auto; - color: $ui-primary-color; + color: $darker-text-color; padding: 5px 10px 0; margin-bottom: 10px; border-right: 1px solid lighten($ui-base-color, 4%); @@ -143,7 +143,7 @@ &.active { &::after { - border-bottom: 4px solid $ui-highlight-color; + border-bottom: 4px solid $highlight-text-color; opacity: 1; } } @@ -178,7 +178,7 @@ font-size: 14px; line-height: 18px; padding: 0 15px; - color: $ui-secondary-color; + color: $darker-text-color; } @media screen and (max-width: 480px) { @@ -256,7 +256,7 @@ .current { background: $simple-background-color; border-radius: 100px; - color: $ui-base-color; + color: $lighter-text-color; cursor: default; margin: 0 10px; } @@ -268,7 +268,7 @@ .older, .newer { text-transform: uppercase; - color: $ui-secondary-color; + color: $primary-text-color; } .older { @@ -293,7 +293,7 @@ .disabled { cursor: default; - color: lighten($ui-base-color, 10%); + color: opacify($lighter-text-color, 0.1); } @media screen and (max-width: 700px) { @@ -332,7 +332,7 @@ width: 335px; background: $simple-background-color; border-radius: 4px; - color: $ui-base-color; + color: $lighter-text-color; margin: 0 5px 10px; position: relative; @@ -344,7 +344,7 @@ overflow: hidden; height: 100px; border-radius: 4px 4px 0 0; - background-color: lighten($ui-base-color, 4%); + background-color: opacify($lighter-text-color, 0.04); background-size: cover; background-position: center; position: relative; @@ -392,7 +392,7 @@ a { display: block; - color: $ui-base-color; + color: $inverted-text-color; text-decoration: none; text-overflow: ellipsis; overflow: hidden; @@ -414,7 +414,7 @@ } .username { - color: lighten($ui-base-color, 34%); + color: $lighter-text-color; font-size: 14px; font-weight: 400; } @@ -422,7 +422,7 @@ .account__header__content { padding: 10px 15px; padding-top: 15px; - color: lighten($ui-base-color, 26%); + color: transparentize($lighter-text-color, 0.1); word-wrap: break-word; overflow: hidden; text-overflow: ellipsis; @@ -434,7 +434,7 @@ .nothing-here { width: 100%; display: block; - color: $ui-primary-color; + color: $lighter-text-color; font-size: 14px; font-weight: 500; text-align: center; @@ -493,7 +493,7 @@ span { font-size: 14px; - color: $ui-primary-color; + color: $inverted-text-color; } } @@ -508,7 +508,7 @@ .account__header__content { font-size: 14px; - color: $ui-base-color; + color: $darker-text-color; } } @@ -522,18 +522,18 @@ display: inline-block; padding: 15px; text-decoration: none; - color: $ui-highlight-color; + color: $highlight-text-color; text-transform: uppercase; font-weight: 500; &:hover, &:active, &:focus { - color: lighten($ui-highlight-color, 8%); + color: lighten($highlight-text-color, 8%); } &.active { - color: $ui-base-color; + color: $inverted-text-color; cursor: default; } } @@ -586,19 +586,19 @@ font-weight: 500; text-align: center; width: 94px; - color: $ui-secondary-color; + color: opacify($darker-text-color, 0.1); background: rgba(darken($ui-base-color, 8%), 0.5); } td { - color: $ui-primary-color; + color: $darker-text-color; text-align: center; width: 100%; padding-left: 0; } a { - color: $ui-highlight-color; + color: $highlight-text-color; text-decoration: none; &:hover, diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 6bd659030..348f72078 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -33,7 +33,7 @@ a { display: block; padding: 15px; - color: rgba($primary-text-color, 0.7); + color: $darker-text-color; text-decoration: none; transition: all 200ms linear; border-radius: 4px 0 0 4px; @@ -90,7 +90,7 @@ padding-left: 25px; h2 { - color: $ui-secondary-color; + color: $primary-text-color; font-size: 24px; line-height: 28px; font-weight: 400; @@ -98,7 +98,7 @@ } h3 { - color: $ui-secondary-color; + color: $primary-text-color; font-size: 20px; line-height: 28px; font-weight: 400; @@ -109,7 +109,7 @@ text-transform: uppercase; font-size: 13px; font-weight: 500; - color: $ui-primary-color; + color: $primary-text-color; padding-bottom: 8px; margin-bottom: 8px; border-bottom: 1px solid lighten($ui-base-color, 8%); @@ -117,7 +117,7 @@ h6 { font-size: 16px; - color: $ui-secondary-color; + color: $primary-text-color; line-height: 28px; font-weight: 400; } @@ -125,7 +125,7 @@ & > p { font-size: 14px; line-height: 18px; - color: $ui-secondary-color; + color: $darker-text-color; margin-bottom: 20px; strong { @@ -153,10 +153,10 @@ } .muted-hint { - color: $ui-primary-color; + color: $darker-text-color; a { - color: $ui-highlight-color; + color: $highlight-text-color; } } @@ -253,7 +253,7 @@ a { display: inline-block; - color: rgba($primary-text-color, 0.7); + color: $darker-text-color; text-decoration: none; text-transform: uppercase; font-size: 12px; @@ -266,7 +266,7 @@ } &.selected { - color: $ui-highlight-color; + color: $highlight-text-color; border-bottom: 2px solid $ui-highlight-color; } } @@ -291,7 +291,7 @@ font-weight: 500; font-size: 14px; line-height: 18px; - color: $ui-secondary-color; + color: $primary-text-color; @each $lang in $cjk-langs { &:lang(#{$lang}) { @@ -348,7 +348,7 @@ padding: 7px 4px; margin-bottom: 10px; font-size: 16px; - color: $ui-base-color; + color: $inverted-text-color; display: block; width: 100%; outline: 0; @@ -402,7 +402,7 @@ font-size: 14px; a { - color: $classic-highlight-color; + color: $highlight-text-color; text-decoration: none; &:hover { @@ -425,7 +425,7 @@ align-items: center; padding: 10px; background: $ui-base-color; - color: $ui-primary-color; + color: $darker-text-color; border-radius: 4px 4px 0 0; font-size: 14px; position: relative; @@ -452,14 +452,14 @@ } &__timestamp { - color: lighten($ui-base-color, 34%); + color: $darker-text-color; } &__extras { background: lighten($ui-base-color, 6%); border-radius: 0 0 4px 4px; padding: 10px; - color: $ui-primary-color; + color: $darker-text-color; font-family: 'mastodon-font-monospace', monospace; font-size: 12px; word-wrap: break-word; @@ -469,7 +469,7 @@ &__icon { font-size: 28px; margin-right: 10px; - color: lighten($ui-base-color, 34%); + color: $darker-text-color; } &__icon__overlay { @@ -485,7 +485,7 @@ } &.negative { - background: $error-red; + background: lighten($error-red, 12%); } &.neutral { @@ -496,17 +496,17 @@ a, .username, .target { - color: $ui-secondary-color; + color: $primary-text-color; text-decoration: none; font-weight: 500; } .diff-old { - color: $error-red; + color: lighten($error-red, 12%); } .diff-neutral { - color: $ui-secondary-color; + color: $darker-text-color; } .diff-new { diff --git a/app/javascript/styles/mastodon/basics.scss b/app/javascript/styles/mastodon/basics.scss index bec0d4d91..c52e069be 100644 --- a/app/javascript/styles/mastodon/basics.scss +++ b/app/javascript/styles/mastodon/basics.scss @@ -75,7 +75,7 @@ body { &.error { position: absolute; text-align: center; - color: $ui-primary-color; + color: $darker-text-color; background: $ui-base-color; width: 100%; height: 100%; diff --git a/app/javascript/styles/mastodon/boost.scss b/app/javascript/styles/mastodon/boost.scss index 31053decc..8e11cb596 100644 --- a/app/javascript/styles/mastodon/boost.scss +++ b/app/javascript/styles/mastodon/boost.scss @@ -6,13 +6,13 @@ } button.icon-button i.fa-retweet { - background-image: url("data:image/svg+xml;utf8,"); + background-image: url("data:image/svg+xml;utf8,"); &:hover { - background-image: url("data:image/svg+xml;utf8,"); + background-image: url("data:image/svg+xml;utf8,"); } } button.icon-button.disabled i.fa-retweet { - background-image: url("data:image/svg+xml;utf8,"); + background-image: url("data:image/svg+xml;utf8,"); } diff --git a/app/javascript/styles/mastodon/compact_header.scss b/app/javascript/styles/mastodon/compact_header.scss index 90d98cc8c..83ac7a8d0 100644 --- a/app/javascript/styles/mastodon/compact_header.scss +++ b/app/javascript/styles/mastodon/compact_header.scss @@ -2,7 +2,7 @@ h1 { font-size: 24px; line-height: 28px; - color: $ui-primary-color; + color: $primary-text-color; font-weight: 500; margin-bottom: 20px; padding: 0 10px; @@ -20,7 +20,7 @@ small { font-weight: 400; - color: $ui-secondary-color; + color: $darker-text-color; } img { diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 96112d84a..cdf3a9daa 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -4,7 +4,7 @@ } .button { - background-color: darken($ui-highlight-color, 3%); + background-color: $ui-highlight-color; border: 10px none; border-radius: 4px; box-sizing: border-box; @@ -31,7 +31,7 @@ &:active, &:focus, &:hover { - background-color: lighten($ui-highlight-color, 7%); + background-color: lighten($ui-highlight-color, 4%); transition: all 200ms ease-out; } @@ -52,7 +52,7 @@ } &.button-alternative { - color: $ui-base-color; + color: $inverted-text-color; background: $ui-primary-color; &:active, @@ -98,26 +98,10 @@ position: relative; } -.column-icon { - background: lighten($ui-base-color, 4%); - color: $ui-primary-color; - cursor: pointer; - font-size: 16px; - padding: 15px; - position: absolute; - right: 0; - top: -48px; - z-index: 3; - - &:hover { - color: lighten($ui-primary-color, 7%); - } -} - .icon-button { display: inline-block; padding: 0; - color: $ui-base-lighter-color; + color: $action-button-color; border: none; background: transparent; cursor: pointer; @@ -126,17 +110,17 @@ &:hover, &:active, &:focus { - color: lighten($ui-base-color, 33%); + color: lighten($action-button-color, 7%); transition: color 200ms ease-out; } &.disabled { - color: lighten($ui-base-color, 13%); + color: darken($action-button-color, 13%); cursor: default; } &.active { - color: $ui-highlight-color; + color: $highlight-text-color; } &::-moz-focus-inner { @@ -150,23 +134,23 @@ } &.inverted { - color: lighten($ui-base-color, 33%); + color: $lighter-text-color; &:hover, &:active, &:focus { - color: $ui-base-lighter-color; + color: transparentize($lighter-text-color, 0.07); } &.disabled { - color: $ui-primary-color; + color: opacify($lighter-text-color, 0.07); } &.active { - color: $ui-highlight-color; + color: $highlight-text-color; &.disabled { - color: lighten($ui-highlight-color, 13%); + color: opacify($lighter-text-color, 0.13); } } } @@ -185,7 +169,7 @@ } .text-icon-button { - color: lighten($ui-base-color, 33%); + color: $lighter-text-color; border: none; background: transparent; cursor: pointer; @@ -199,17 +183,17 @@ &:hover, &:active, &:focus { - color: $ui-base-lighter-color; + color: opacify($lighter-text-color, 0.07); transition: color 200ms ease-out; } &.disabled { - color: lighten($ui-base-color, 13%); + color: transparentize($lighter-text-color, 0.2); cursor: default; } &.active { - color: $ui-highlight-color; + color: $highlight-text-color; } &::-moz-focus-inner { @@ -228,25 +212,6 @@ transform-origin: 50% 0; } -.dropdown--active .icon-button { - color: $ui-highlight-color; -} - -.dropdown--active::after { - @media screen and (min-width: 631px) { - content: ""; - display: block; - position: absolute; - width: 0; - height: 0; - border-style: solid; - border-width: 0 4.5px 7.8px; - border-color: transparent transparent $ui-secondary-color; - bottom: 8px; - right: 104px; - } -} - .invisible { font-size: 0; line-height: 0; @@ -271,15 +236,11 @@ } } -.lightbox .icon-button { - color: $ui-base-color; -} - .compose-form { padding: 10px; .compose-form__warning { - color: darken($ui-secondary-color, 65%); + color: $inverted-text-color; margin-bottom: 15px; background: $ui-primary-color; box-shadow: 0 2px 6px rgba($base-shadow-color, 0.3); @@ -289,7 +250,7 @@ font-weight: 400; strong { - color: darken($ui-secondary-color, 65%); + color: $inverted-text-color; font-weight: 500; @each $lang in $cjk-langs { @@ -300,7 +261,7 @@ } a { - color: darken($ui-primary-color, 33%); + color: $lighter-text-color; font-weight: 500; text-decoration: underline; @@ -333,7 +294,7 @@ box-sizing: border-box; width: 100%; margin: 0; - color: $ui-base-color; + color: $inverted-text-color; background: $simple-background-color; padding: 10px; font-family: inherit; @@ -378,7 +339,7 @@ box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4); background: $ui-secondary-color; border-radius: 0 0 4px 4px; - color: $ui-base-color; + color: $lighter-text-color; font-size: 14px; padding: 6px; @@ -419,11 +380,11 @@ } .autosuggest-account .display-name__account { - color: lighten($ui-base-color, 36%); + color: $lighter-text-color; } .compose-form__modifiers { - color: $ui-base-color; + color: $inverted-text-color; font-family: inherit; font-size: 14px; background: $simple-background-color; @@ -454,7 +415,7 @@ .icon-button { flex: 0 1 auto; - color: $ui-secondary-color; + color: $action-button-color; font-size: 14px; font-weight: 500; padding: 10px; @@ -463,7 +424,7 @@ &:hover, &:focus, &:active { - color: lighten($ui-secondary-color, 4%); + color: lighten($action-button-color, 7%); } } @@ -486,7 +447,7 @@ input { background: transparent; - color: $ui-secondary-color; + color: $primary-text-color; border: 0; padding: 0; margin: 0; @@ -501,7 +462,7 @@ &::placeholder { opacity: 0.54; - color: $ui-secondary-color; + color: $darker-text-color; } } @@ -563,7 +524,7 @@ font-family: 'mastodon-font-sans-serif', sans-serif; font-size: 14px; font-weight: 600; - color: lighten($ui-base-color, 12%); + color: $lighter-text-color; &.character-counter--over { color: $warning-red; @@ -617,7 +578,7 @@ } .reply-indicator__display-name { - color: $ui-base-color; + color: $lighter-text-color; display: block; max-width: 100%; line-height: 24px; @@ -679,7 +640,7 @@ text-decoration: underline; .fa { - color: lighten($ui-base-color, 40%); + color: lighten($action-button-color, 7%); } } @@ -694,15 +655,15 @@ } .fa { - color: lighten($ui-base-color, 30%); + color: $action-button-color; } } .status__content__spoiler-link { - background: lighten($ui-base-color, 30%); + background: $action-button-color; &:hover { - background: lighten($ui-base-color, 33%); + background: lighten($action-button-color, 7%); text-decoration: none; } } @@ -721,7 +682,7 @@ border-radius: 2px; background: transparent; border: 0; - color: lighten($ui-base-color, 8%); + color: $lighter-text-color; font-weight: 700; font-size: 11px; padding: 0 6px; @@ -784,36 +745,32 @@ &.status-direct { background: lighten($ui-base-color, 8%); - - .icon-button.disabled { - color: lighten($ui-base-color, 16%); - } } &.light { .status__relative-time { - color: $ui-primary-color; + color: $lighter-text-color; } .status__display-name { - color: $ui-base-color; + color: $inverted-text-color; } .display-name { strong { - color: $ui-base-color; + color: $inverted-text-color; } span { - color: $ui-primary-color; + color: $lighter-text-color; } } .status__content { - color: $ui-base-color; + color: $inverted-text-color; a { - color: $ui-highlight-color; + color: $highlight-text-color; } a.status__content__spoiler-link { @@ -833,19 +790,19 @@ background: transparent; .icon-button.disabled { - color: lighten($ui-base-color, 13%); + color: lighten($action-button-color, 13%); } } } .status__relative-time { - color: $ui-base-lighter-color; + color: $darker-text-color; float: right; font-size: 14px; } .status__display-name { - color: $ui-base-lighter-color; + color: $darker-text-color; } .status__info .status__display-name { @@ -896,14 +853,14 @@ .status__prepend { margin-left: 68px; - color: $ui-base-lighter-color; + color: $darker-text-color; padding: 8px 0; padding-bottom: 2px; font-size: 14px; position: relative; .status__display-name strong { - color: $ui-base-lighter-color; + color: $darker-text-color; } > span { @@ -965,7 +922,7 @@ .detailed-status__meta { margin-top: 15px; - color: $ui-base-lighter-color; + color: $darker-text-color; font-size: 14px; line-height: 18px; } @@ -993,11 +950,11 @@ } .reply-indicator__content { - color: $ui-base-color; + color: $inverted-text-color; font-size: 14px; a { - color: lighten($ui-base-color, 20%); + color: $lighter-text-color; } } @@ -1032,7 +989,7 @@ .account__display-name { flex: 1 1 auto; display: block; - color: $ui-primary-color; + color: $darker-text-color; overflow: hidden; text-decoration: none; font-size: 14px; @@ -1102,7 +1059,7 @@ } .account__header__username { - color: $ui-primary-color; + color: $darker-text-color; } } @@ -1112,7 +1069,7 @@ } .account__header__content { - color: $ui-secondary-color; + color: $darker-text-color; } .account__header__display-name { @@ -1127,7 +1084,7 @@ } .account__header__username { - color: $ui-highlight-color; + color: $highlight-text-color; font-size: 14px; font-weight: 400; display: block; @@ -1140,7 +1097,7 @@ .account__disclaimer { padding: 10px; border-top: 1px solid lighten($ui-base-color, 8%); - color: $ui-base-lighter-color; + color: $darker-text-color; strong { font-weight: 500; @@ -1166,7 +1123,7 @@ } .account__header__content { - color: $ui-primary-color; + color: $darker-text-color; font-size: 14px; font-weight: 400; overflow: hidden; @@ -1243,7 +1200,7 @@ display: block; text-transform: uppercase; font-size: 11px; - color: $ui-primary-color; + color: $darker-text-color; } strong { @@ -1258,10 +1215,6 @@ } } } - - abbr { - color: $ui-base-lighter-color; - } } .account__header__avatar { @@ -1331,7 +1284,7 @@ } .detailed-status__display-name { - color: $ui-secondary-color; + color: $darker-text-color; display: block; line-height: 24px; margin-bottom: 15px; @@ -1366,11 +1319,11 @@ .muted { .status__content p, .status__content a { - color: $ui-base-lighter-color; + color: $darker-text-color; } .status__display-name strong { - color: $ui-base-lighter-color; + color: $darker-text-color; } .status__avatar { @@ -1378,11 +1331,11 @@ } a.status__content__spoiler-link { - background: $ui-base-lighter-color; + background: $darker-text-color; color: lighten($ui-base-color, 4%); &:hover { - background: lighten($ui-base-color, 29%); + background: transparentize($darker-text-color, 0.07); text-decoration: none; } } @@ -1398,7 +1351,7 @@ position: relative; .fa { - color: $ui-highlight-color; + color: $highlight-text-color; } > span { @@ -1501,7 +1454,7 @@ display: flex; flex-shrink: 0; cursor: default; - color: $ui-primary-color; + color: $darker-text-color; strong { color: $primary-text-color; @@ -1609,7 +1562,7 @@ box-sizing: border-box; text-decoration: none; background: $ui-secondary-color; - color: $ui-base-color; + color: $inverted-text-color; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -1618,7 +1571,7 @@ &:hover, &:active { background: $ui-highlight-color; - color: $ui-secondary-color; + color: $primary-text-color; outline: 0; } } @@ -1660,7 +1613,7 @@ box-sizing: border-box; text-decoration: none; background: $ui-secondary-color; - color: $ui-base-color; + color: $inverted-text-color; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -1671,7 +1624,7 @@ &:hover { background: $ui-highlight-color; - color: $ui-secondary-color; + color: $primary-text-color; } } } @@ -1683,7 +1636,7 @@ .static-content { padding: 10px; padding-top: 20px; - color: $ui-base-lighter-color; + color: $darker-text-color; h1 { font-size: 16px; @@ -1935,8 +1888,8 @@ } &.active { - border-bottom: 2px solid $ui-highlight-color; - color: $ui-highlight-color; + border-bottom: 2px solid $highlight-text-color; + color: $highlight-text-color; } &:hover, @@ -1991,7 +1944,7 @@ .column-back-button { background: lighten($ui-base-color, 4%); - color: $ui-highlight-color; + color: $highlight-text-color; cursor: pointer; flex: 0 0 auto; font-size: 16px; @@ -2010,7 +1963,7 @@ background: lighten($ui-base-color, 4%); border: 0; font-family: inherit; - color: $ui-highlight-color; + color: $highlight-text-color; cursor: pointer; white-space: nowrap; font-size: 16px; @@ -2182,7 +2135,7 @@ .column-subheading { background: $ui-base-color; - color: $ui-base-lighter-color; + color: $darker-text-color; padding: 8px 20px; font-size: 12px; font-weight: 500; @@ -2205,11 +2158,11 @@ flex: 1 0 auto; p { - color: $ui-secondary-color; + color: $darker-text-color; } a { - color: $ui-base-lighter-color; + color: opacify($darker-text-color, 0.07); } } @@ -2235,7 +2188,7 @@ } .setting-text { - color: $ui-primary-color; + color: $darker-text-color; background: transparent; border: none; border-bottom: 2px solid $ui-primary-color; @@ -2249,23 +2202,12 @@ &:focus, &:active { color: $primary-text-color; - border-bottom-color: $ui-highlight-color; + border-bottom-color: $highlight-text-color; } @media screen and (max-width: 600px) { font-size: 16px; } - - &.light { - color: $ui-base-color; - border-bottom: 2px solid lighten($ui-base-color, 27%); - - &:focus, - &:active { - color: $ui-base-color; - border-bottom-color: $ui-highlight-color; - } - } } .no-reduce-motion button.icon-button i.fa-retweet { @@ -2288,12 +2230,12 @@ } .reduce-motion button.icon-button i.fa-retweet { - color: $ui-base-lighter-color; + color: $action-button-color; transition: color 100ms ease-in; } .reduce-motion button.icon-button.active i.fa-retweet { - color: $ui-highlight-color; + color: $highlight-text-color; } .status-card { @@ -2301,7 +2243,7 @@ font-size: 14px; border: 1px solid lighten($ui-base-color, 8%); border-radius: 4px; - color: $ui-base-lighter-color; + color: $darker-text-color; margin-top: 14px; text-decoration: none; overflow: hidden; @@ -2439,7 +2381,7 @@ a.status-card { .load-more { display: block; - color: $ui-base-lighter-color; + color: $darker-text-color; background-color: transparent; border: 0; font-size: inherit; @@ -2463,7 +2405,7 @@ a.status-card { text-align: center; font-size: 16px; font-weight: 500; - color: lighten($ui-base-color, 16%); + color: opacify($darker-text-color, 0.07); background: $ui-base-color; cursor: default; display: flex; @@ -2503,7 +2445,7 @@ a.status-card { strong { display: block; margin-bottom: 10px; - color: lighten($ui-base-color, 34%); + color: $darker-text-color; } span { @@ -2561,15 +2503,15 @@ a.status-card { } & > .column-header__back-button { - color: $ui-highlight-color; + color: $highlight-text-color; } &.active { - box-shadow: 0 1px 0 rgba($ui-highlight-color, 0.3); + box-shadow: 0 1px 0 rgba($highlight-text-color, 0.3); .column-header__icon { - color: $ui-highlight-color; - text-shadow: 0 0 10px rgba($ui-highlight-color, 0.4); + color: $highlight-text-color; + text-shadow: 0 0 10px rgba($highlight-text-color, 0.4); } } @@ -2615,7 +2557,7 @@ a.status-card { max-height: 70vh; overflow: hidden; overflow-y: auto; - color: $ui-primary-color; + color: $darker-text-color; transition: max-height 150ms ease-in-out, opacity 300ms linear; opacity: 1; @@ -2644,7 +2586,7 @@ a.status-card { .column-header__setting-btn { &:hover { - color: lighten($ui-primary-color, 4%); + color: $darker-text-color; text-decoration: underline; } } @@ -2678,7 +2620,7 @@ a.status-card { } .loading-indicator { - color: lighten($ui-base-color, 26%); + color: $darker-text-color; font-size: 12px; font-weight: 400; text-transform: uppercase; @@ -2763,7 +2705,7 @@ a.status-card { .media-spoiler { background: $base-overlay-background; - color: $ui-primary-color; + color: $darker-text-color; border: 0; padding: 0; width: 100%; @@ -2775,7 +2717,7 @@ a.status-card { &:active, &:focus { padding: 0; - color: lighten($ui-primary-color, 8%); + color: transparentize($darker-text-color, 0.07); } } @@ -2828,7 +2770,7 @@ a.status-card { } .column-settings__section { - color: $ui-primary-color; + color: $darker-text-color; cursor: default; display: block; font-weight: 500; @@ -2886,7 +2828,7 @@ a.status-card { .setting-toggle__label, .setting-meta__label { - color: $ui-primary-color; + color: $darker-text-color; display: inline-block; margin-bottom: 14px; margin-left: 8px; @@ -2894,13 +2836,12 @@ a.status-card { } .setting-meta__label { - color: $ui-primary-color; float: right; } .empty-column-indicator, .error-column { - color: lighten($ui-base-color, 20%); + color: $darker-text-color; background: $ui-base-color; text-align: center; padding: 20px; @@ -2917,7 +2858,7 @@ a.status-card { } a { - color: $ui-highlight-color; + color: $highlight-text-color; text-decoration: none; &:hover { @@ -3102,7 +3043,7 @@ a.status-card { display: flex; align-items: center; justify-content: center; - color: $ui-secondary-color; + color: $primary-text-color; font-size: 18px; font-weight: 500; border: 2px dashed $ui-base-lighter-color; @@ -3111,7 +3052,7 @@ a.status-card { .upload-progress { padding: 10px; - color: $ui-base-lighter-color; + color: $lighter-text-color; overflow: hidden; display: flex; @@ -3200,7 +3141,7 @@ a.status-card { } .privacy-dropdown__option { - color: $ui-base-color; + color: $lighter-text-color; padding: 10px; cursor: pointer; display: flex; @@ -3233,12 +3174,12 @@ a.status-card { .privacy-dropdown__option__content { flex: 1 1 auto; - color: darken($ui-primary-color, 24%); + color: $lighter-text-color; strong { font-weight: 500; display: block; - color: $ui-base-color; + color: $inverted-text-color; @each $lang in $cjk-langs { &:lang(#{$lang}) { @@ -3287,7 +3228,7 @@ a.status-card { padding-right: 30px; font-family: inherit; background: $ui-base-color; - color: $ui-primary-color; + color: $darker-text-color; font-size: 14px; margin: 0; @@ -3344,6 +3285,7 @@ a.status-card { .fa-times-circle { top: 11px; transform: rotate(0deg); + color: $action-button-color; cursor: pointer; &.active { @@ -3351,13 +3293,13 @@ a.status-card { } &:hover { - color: $primary-text-color; + color: lighten($action-button-color, 7%); } } } .search-results__header { - color: $ui-base-lighter-color; + color: $darker-text-color; background: lighten($ui-base-color, 2%); border-bottom: 1px solid darken($ui-base-color, 4%); padding: 15px 10px; @@ -3386,7 +3328,7 @@ a.status-card { span { display: inline-block; background: $ui-base-color; - color: $ui-primary-color; + color: $darker-text-color; font-size: 14px; font-weight: 500; padding: 10px; @@ -3405,13 +3347,13 @@ a.status-card { .search-results__hashtag { display: block; padding: 10px; - color: $ui-secondary-color; + color: darken($primary-text-color, 4%); text-decoration: none; &:hover, &:active, &:focus { - color: lighten($ui-secondary-color, 4%); + color: $primary-text-color; text-decoration: underline; } } @@ -3549,7 +3491,7 @@ a.status-card { } .media-modal__button { - background-color: $white; + background-color: $primary-text-color; height: 12px; width: 12px; border-radius: 6px; @@ -3560,7 +3502,7 @@ a.status-card { } .media-modal__button--active { - background-color: $ui-highlight-color; + background-color: $highlight-text-color; } .media-modal__close { @@ -3574,7 +3516,7 @@ a.status-card { .error-modal, .embed-modal { background: $ui-secondary-color; - color: $ui-base-color; + color: $inverted-text-color; border-radius: 8px; overflow: hidden; display: flex; @@ -3662,7 +3604,7 @@ a.status-card { .onboarding-modal__nav, .error-modal__nav { - color: darken($ui-secondary-color, 34%); + color: $lighter-text-color; border: 0; font-size: 14px; font-weight: 500; @@ -3676,18 +3618,18 @@ a.status-card { &:hover, &:focus, &:active { - color: darken($ui-secondary-color, 38%); + color: transparentize($lighter-text-color, 0.04); background-color: darken($ui-secondary-color, 16%); } &.onboarding-modal__done, &.onboarding-modal__next { - color: $ui-base-color; + color: $inverted-text-color; &:hover, &:focus, &:active { - color: darken($ui-base-color, 4%); + color: lighten($inverted-text-color, 4%); } } } @@ -3739,17 +3681,17 @@ a.status-card { h1 { font-size: 18px; font-weight: 500; - color: $ui-base-color; + color: $inverted-text-color; margin-bottom: 20px; } a { - color: $ui-highlight-color; + color: $highlight-text-color; &:hover, &:focus, &:active { - color: lighten($ui-highlight-color, 4%); + color: lighten($highlight-text-color, 4%); } } @@ -3759,7 +3701,7 @@ a.status-card { p { font-size: 16px; - color: lighten($ui-base-color, 8%); + color: $lighter-text-color; margin-top: 10px; margin-bottom: 10px; @@ -3770,7 +3712,7 @@ a.status-card { strong { font-weight: 500; background: $ui-base-color; - color: $ui-secondary-color; + color: $primary-text-color; border-radius: 4px; font-size: 14px; padding: 3px 6px; @@ -3822,7 +3764,7 @@ a.status-card { &__label { font-weight: 500; - color: $ui-base-color; + color: $inverted-text-color; margin-bottom: 5px; text-transform: uppercase; font-size: 12px; @@ -3830,7 +3772,7 @@ a.status-card { &__case { background: $ui-base-color; - color: $ui-secondary-color; + color: $primary-text-color; font-weight: 500; padding: 10px; border-radius: 4px; @@ -3847,7 +3789,7 @@ a.status-card { .figure { background: darken($ui-base-color, 8%); - color: $ui-secondary-color; + color: $darker-text-color; margin-bottom: 20px; border-radius: 4px; padding: 10px; @@ -3936,7 +3878,7 @@ a.status-card { .actions-modal, .mute-modal { background: lighten($ui-secondary-color, 8%); - color: $ui-base-color; + color: $inverted-text-color; border-radius: 8px; overflow: hidden; max-width: 90vw; @@ -3994,7 +3936,7 @@ a.status-card { & > div { flex: 1 1 auto; text-align: right; - color: lighten($ui-base-color, 33%); + color: $lighter-text-color; padding-right: 10px; } @@ -4081,7 +4023,7 @@ a.status-card { box-sizing: border-box; width: 100%; margin: 0; - color: $ui-base-color; + color: $inverted-text-color; background: $white; padding: 10px; font-family: inherit; @@ -4103,7 +4045,7 @@ a.status-card { margin-bottom: 24px; &__label { - color: $ui-base-color; + color: $inverted-text-color; font-size: 14px; } } @@ -4142,7 +4084,7 @@ a.status-card { li:not(:empty) { a { - color: $ui-base-color; + color: $inverted-text-color; display: flex; padding: 12px 16px; font-size: 15px; @@ -4178,14 +4120,14 @@ a.status-card { .confirmation-modal__cancel-button, .mute-modal__cancel-button { background-color: transparent; - color: darken($ui-secondary-color, 34%); + color: $lighter-text-color; font-size: 14px; font-weight: 500; &:hover, &:focus, &:active { - color: darken($ui-secondary-color, 38%); + color: transparentize($lighter-text-color, 0.04); } } } @@ -4218,7 +4160,7 @@ a.status-card { } .loading-bar { - background-color: $ui-highlight-color; + background-color: $highlight-text-color; height: 3px; position: absolute; top: 0; @@ -4266,7 +4208,7 @@ a.status-card { &__icon { flex: 0 0 auto; - color: $ui-base-lighter-color; + color: $darker-text-color; padding: 8px 18px; cursor: default; border-right: 1px solid lighten($ui-base-color, 8%); @@ -4296,7 +4238,7 @@ a.status-card { a { text-decoration: none; - color: $ui-base-lighter-color; + color: $darker-text-color; font-weight: 500; &:hover { @@ -4315,7 +4257,7 @@ a.status-card { } .fa { - color: $ui-base-lighter-color; + color: $darker-text-color; } } } @@ -4511,7 +4453,7 @@ a.status-card { z-index: 4; border: 0; background: $base-shadow-color; - color: $ui-primary-color; + color: $darker-text-color; transition: none; pointer-events: none; @@ -4522,7 +4464,7 @@ a.status-card { &:hover, &:active, &:focus { - color: lighten($ui-primary-color, 8%); + color: transparentize($darker-text-color, 0.07); } } @@ -4719,7 +4661,7 @@ a.status-card { background-size: cover; background-position: center; position: absolute; - color: $ui-primary-color; + color: $darker-text-color; text-decoration: none; border-radius: 4px; @@ -4727,7 +4669,7 @@ a.status-card { &:active, &:focus { outline: 0; - color: $ui-secondary-color; + color: transparentize($darker-text-color, 0.07); &::before { content: ""; @@ -4758,7 +4700,7 @@ a.status-card { a { display: block; flex: 1 1 auto; - color: $ui-primary-color; + color: $darker-text-color; padding: 15px 0; font-size: 14px; font-weight: 500; @@ -4767,7 +4709,7 @@ a.status-card { position: relative; &.active { - color: $ui-secondary-color; + color: transparentize($darker-text-color, 0.07); &::before, &::after { @@ -4802,12 +4744,12 @@ a.status-card { padding: 10px 14px; padding-bottom: 14px; margin-top: 10px; - color: $ui-primary-color; + color: $lighter-text-color; box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); h4 { text-transform: uppercase; - color: $ui-primary-color; + color: $lighter-text-color; font-size: 13px; font-weight: 500; margin-bottom: 10px; @@ -4823,7 +4765,7 @@ a.status-card { em { font-weight: 500; - color: $ui-base-color; + color: $inverted-text-color; } } @@ -4839,11 +4781,11 @@ noscript { div { font-size: 14px; margin: 30px auto; - color: $ui-secondary-color; + color: $primary-text-color; max-width: 400px; a { - color: $ui-highlight-color; + color: $highlight-text-color; text-decoration: underline; &:hover { @@ -4941,7 +4883,6 @@ noscript { } .embed-modal__html { - color: $ui-secondary-color; outline: 0; box-sizing: border-box; display: block; @@ -4950,7 +4891,7 @@ noscript { padding: 10px; font-family: 'mastodon-font-monospace', monospace; background: $ui-base-color; - color: $ui-primary-color; + color: $primary-text-color; font-size: 14px; margin: 0; margin-bottom: 15px; @@ -4993,7 +4934,7 @@ noscript { &__message { position: relative; margin-left: 58px; - color: $ui-base-lighter-color; + color: $darker-text-color; padding: 8px 0; padding-top: 0; padding-bottom: 4px; @@ -5201,7 +5142,7 @@ noscript { } th { - color: $ui-primary-color; + color: $darker-text-color; background: darken($ui-base-color, 4%); max-width: 120px; font-weight: 500; diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss index e761f58eb..8df2902d2 100644 --- a/app/javascript/styles/mastodon/containers.scss +++ b/app/javascript/styles/mastodon/containers.scss @@ -100,7 +100,7 @@ .name { flex: 1 1 auto; - color: $ui-secondary-color; + color: $darker-text-color; width: calc(100% - 88px); .username { diff --git a/app/javascript/styles/mastodon/emoji_picker.scss b/app/javascript/styles/mastodon/emoji_picker.scss index 4161cc045..3620a6f54 100644 --- a/app/javascript/styles/mastodon/emoji_picker.scss +++ b/app/javascript/styles/mastodon/emoji_picker.scss @@ -7,7 +7,7 @@ font-size: 13px; display: inline-block; - color: $ui-base-color; + color: $inverted-text-color; .emoji-mart-emoji { padding: 6px; @@ -36,7 +36,7 @@ display: flex; justify-content: space-between; padding: 0 6px; - color: $ui-primary-color; + color: $lighter-text-color; line-height: 0; } @@ -50,15 +50,15 @@ cursor: pointer; &:hover { - color: darken($ui-primary-color, 4%); + color: opacify($lighter-text-color, 0.04); } } .emoji-mart-anchor-selected { - color: darken($ui-highlight-color, 3%); + color: $highlight-text-color; &:hover { - color: darken($ui-highlight-color, 3%); + color: darken($highlight-text-color, 4%); } .emoji-mart-anchor-bar { @@ -72,7 +72,7 @@ left: 0; width: 100%; height: 3px; - background-color: darken($ui-highlight-color, 3%); + background-color: $highlight-text-color; } .emoji-mart-anchors { @@ -115,7 +115,7 @@ display: block; width: 100%; background: rgba($ui-secondary-color, 0.3); - color: $ui-primary-color; + color: $inverted-text-color; border: 1px solid $ui-secondary-color; border-radius: 4px; @@ -184,7 +184,7 @@ font-size: 14px; text-align: center; padding-top: 70px; - color: $ui-primary-color; + color: $lighter-text-color; .emoji-mart-category-label { display: none; diff --git a/app/javascript/styles/mastodon/footer.scss b/app/javascript/styles/mastodon/footer.scss index 2d953b34e..ba2a06954 100644 --- a/app/javascript/styles/mastodon/footer.scss +++ b/app/javascript/styles/mastodon/footer.scss @@ -2,7 +2,7 @@ text-align: center; margin-top: 30px; font-size: 12px; - color: darken($ui-secondary-color, 25%); + color: $darker-text-color; .domain { font-weight: 500; diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 945579a9c..3a3b4c326 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -29,14 +29,14 @@ code { span.hint { display: block; - color: $ui-primary-color; + color: $darker-text-color; font-size: 12px; margin-top: 4px; } p.hint { margin-bottom: 15px; - color: $ui-primary-color; + color: $darker-text-color; &.subtle-hint { text-align: center; @@ -44,10 +44,10 @@ code { line-height: 18px; margin-top: 15px; margin-bottom: 0; - color: $ui-primary-color; + color: $darker-text-color; a { - color: $ui-highlight-color; + color: $highlight-text-color; } } } @@ -244,35 +244,35 @@ code { } &:focus:invalid { - border-bottom-color: $error-value-color; + border-bottom-color: lighten($error-red, 12%); } &:required:valid { - border-bottom-color: $valid-value-color; + border-bottom-color: lighten($error-red, 12%); } &:active, &:focus { - border-bottom-color: $ui-highlight-color; + border-bottom-color: $highlight-text-color; background: rgba($base-overlay-background, 0.1); } } .input.field_with_errors { label { - color: $error-value-color; + color: lighten($error-red, 12%); } input[type=text], input[type=email], input[type=password] { - border-bottom-color: $error-value-color; + border-bottom-color: lighten($error-red, 12%); } .error { display: block; font-weight: 500; - color: $error-value-color; + color: lighten($error-red, 12%); margin-top: 4px; } } @@ -356,7 +356,7 @@ code { padding: 7px 4px; padding-bottom: 9px; font-size: 16px; - color: $ui-base-lighter-color; + color: $darker-text-color; font-family: inherit; pointer-events: none; cursor: default; @@ -366,7 +366,7 @@ code { .flash-message { background: lighten($ui-base-color, 8%); - color: $ui-primary-color; + color: $darker-text-color; border-radius: 4px; padding: 15px 10px; margin-bottom: 30px; @@ -378,7 +378,6 @@ code { } .oauth-code { - color: $ui-secondary-color; outline: 0; box-sizing: border-box; display: block; @@ -387,7 +386,7 @@ code { padding: 10px; font-family: 'mastodon-font-monospace', monospace; background: $ui-base-color; - color: $ui-primary-color; + color: $primary-text-color; font-size: 14px; margin: 0; @@ -426,7 +425,7 @@ code { text-align: center; a { - color: $ui-primary-color; + color: $darker-text-color; text-decoration: none; &:hover { @@ -439,7 +438,7 @@ code { .follow-prompt { margin-bottom: 30px; text-align: center; - color: $ui-primary-color; + color: $darker-text-color; h2 { font-size: 16px; @@ -447,7 +446,7 @@ code { } strong { - color: $ui-secondary-color; + color: $primary-text-color; font-weight: 500; @each $lang in $cjk-langs { @@ -484,7 +483,7 @@ code { .qr-alternative { margin-bottom: 20px; - color: $ui-secondary-color; + color: $darker-text-color; flex: 150px; samp { @@ -569,7 +568,7 @@ code { .post-follow-actions { text-align: center; - color: $ui-primary-color; + color: $darker-text-color; div { margin-bottom: 4px; @@ -582,7 +581,7 @@ code { h4 { font-size: 16px; - color: $ui-base-lighter-color; + color: $primary-text-color; text-align: center; margin-bottom: 20px; border: 0; diff --git a/app/javascript/styles/mastodon/landing_strip.scss b/app/javascript/styles/mastodon/landing_strip.scss index ffa1e149d..651c06ced 100644 --- a/app/javascript/styles/mastodon/landing_strip.scss +++ b/app/javascript/styles/mastodon/landing_strip.scss @@ -1,7 +1,7 @@ .landing-strip, .memoriam-strip { background: rgba(darken($ui-base-color, 7%), 0.8); - color: $ui-primary-color; + color: $darker-text-color; font-weight: 400; padding: 14px; border-radius: 4px; @@ -45,7 +45,7 @@ padding: 14px; border-radius: 4px; background: rgba(darken($ui-base-color, 7%), 0.8); - color: $ui-secondary-color; + color: $darker-text-color; font-weight: 400; margin-bottom: 20px; @@ -88,7 +88,7 @@ .fa { margin-right: 5px; - color: $ui-primary-color; + color: $darker-text-color; } } @@ -103,7 +103,7 @@ text-decoration: none; span { - color: $ui-highlight-color; + color: $highlight-text-color; font-weight: 400; } } diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss index dfdc48d06..c39163ba8 100644 --- a/app/javascript/styles/mastodon/stream_entries.scss +++ b/app/javascript/styles/mastodon/stream_entries.scss @@ -84,7 +84,7 @@ font-size: 14px; .status__relative-time { - color: $ui-primary-color; + color: $lighter-text-color; } } } @@ -93,7 +93,7 @@ display: block; max-width: 100%; padding-right: 25px; - color: $ui-base-color; + color: $lighter-text-color; } .status__avatar { @@ -123,7 +123,7 @@ strong { font-weight: 500; - color: $ui-base-color; + color: $inverted-text-color; @each $lang in $cjk-langs { &:lang(#{$lang}) { @@ -134,15 +134,15 @@ span { font-size: 14px; - color: $ui-primary-color; + color: $inverted-text-color; } } .status__content { - color: $ui-base-color; + color: $inverted-text-color; a { - color: $ui-highlight-color; + color: $highlight-text-color; } a.status__content__spoiler-link { @@ -180,7 +180,7 @@ strong { font-weight: 500; - color: $ui-base-color; + color: $inverted-text-color; @each $lang in $cjk-langs { &:lang(#{$lang}) { @@ -191,7 +191,7 @@ span { font-size: 14px; - color: $ui-primary-color; + color: $lighter-text-color; } } } @@ -207,10 +207,10 @@ } .status__content { - color: $ui-base-color; + color: $inverted-text-color; a { - color: $ui-highlight-color; + color: $highlight-text-color; } a.status__content__spoiler-link { @@ -225,7 +225,7 @@ .detailed-status__meta { margin-top: 15px; - color: $ui-primary-color; + color: $lighter-text-color; font-size: 14px; line-height: 18px; @@ -243,7 +243,7 @@ .status-card { border-color: lighten($ui-secondary-color, 4%); - color: darken($ui-primary-color, 4%); + color: $lighter-text-color; &:hover { background: lighten($ui-secondary-color, 4%); @@ -252,7 +252,7 @@ .status-card__title, .status-card__description { - color: $ui-base-color; + color: $inverted-text-color; } .status-card__image { @@ -262,7 +262,7 @@ .media-spoiler { background: $ui-base-color; - color: $ui-primary-color; + color: $darker-text-color; } .pre-header { @@ -270,7 +270,7 @@ padding-left: (48px + 14px * 2); padding-bottom: 0; margin-bottom: -4px; - color: $ui-primary-color; + color: $lighter-text-color; font-size: 14px; position: relative; @@ -280,7 +280,7 @@ } .status__display-name.muted strong { - color: $ui-primary-color; + color: $lighter-text-color; } } diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index 92870e679..c12d84f1c 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -30,7 +30,7 @@ } a { - color: $ui-highlight-color; + color: $highlight-text-color; text-decoration: underline; &:hover { @@ -68,7 +68,7 @@ a.table-action-link { display: inline-block; margin-right: 5px; padding: 0 10px; - color: rgba($primary-text-color, 0.7); + color: $darker-text-color; font-weight: 500; &:hover { diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss index e456c27ee..dc4e72a2e 100644 --- a/app/javascript/styles/mastodon/variables.scss +++ b/app/javascript/styles/mastodon/variables.scss @@ -18,6 +18,11 @@ $base-overlay-background: $black !default; $base-border-color: $white !default; $simple-background-color: $white !default; $primary-text-color: $white !default; +$darker-text-color: rgba($primary-text-color, 0.7) !default; +$highlight-text-color: $classic-highlight-color !default; +$inverted-text-color: $black !default; +$lighter-text-color: rgba($inverted-text-color, 0.7) !default; +$action-button-color: #8d9ac2; $valid-value-color: $success-green !default; $error-value-color: $error-red !default; @@ -26,7 +31,7 @@ $ui-base-color: $classic-base-color !default; // Darkest $ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest $ui-primary-color: $classic-primary-color !default; // Lighter $ui-secondary-color: $classic-secondary-color !default; // Lightest -$ui-highlight-color: $classic-highlight-color !default; // Vibrant +$ui-highlight-color: #2b5fd9; // Language codes that uses CJK fonts $cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW; From 1957209efd4054842a2ae0afd91b9fe0c38cbf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Wed, 18 Apr 2018 17:14:21 +0200 Subject: [PATCH 102/442] i18n: Update Polish translation (#7181) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- .../mastodon/locales/defaultMessages.json | 4 ++++ app/javascript/mastodon/locales/pl.json | 13 +++++++------ config/locales/pl.yml | 10 ++++++++++ config/locales/simple_form.pl.yml | 6 ++++++ 4 files changed, 27 insertions(+), 6 deletions(-) diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index 5a02e0c72..86cf83993 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -1148,6 +1148,10 @@ "defaultMessage": "to open status", "id": "keyboard_shortcuts.enter" }, + { + "defaultMessage": "to show/hide text behind CW", + "id": "keyboard_shortcuts.toggle_hidden" + }, { "defaultMessage": "to move up in the list", "id": "keyboard_shortcuts.up" diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index c55603a99..22ae2de47 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -40,7 +40,7 @@ "bundle_modal_error.retry": "Spróbuj ponownie", "column.blocks": "Zablokowani użytkownicy", "column.community": "Lokalna oś czasu", - "column.direct": "Direct messages", + "column.direct": "Wiadomości bezpośrednie", "column.domain_blocks": "Ukryte domeny", "column.favourites": "Ulubione", "column.follow_requests": "Prośby o śledzenie", @@ -101,7 +101,7 @@ "emoji_button.symbols": "Symbole", "emoji_button.travel": "Podróże i miejsca", "empty_column.community": "Lokalna oś czasu jest pusta. Napisz coś publicznie, aby zagaić!", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "Nie masz żadnych wiadomości bezpośrednich. Kiedy dostaniesz lub wyślesz jakąś, pojawi się ona tutaj.", "empty_column.hashtag": "Nie ma wpisów oznaczonych tym hashtagiem. Możesz napisać pierwszy!", "empty_column.home": "Nie śledzisz nikogo. Odwiedź publiczną oś czasu lub użyj wyszukiwarki, aby znaleźć interesujące Cię profile.", "empty_column.home.public_timeline": "publiczna oś czasu", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "aby wspomnieć o autorze", "keyboard_shortcuts.reply": "aby odpowiedzieć", "keyboard_shortcuts.search": "aby przejść do pola wyszukiwania", + "keyboard_shortcuts.toggle_hidden": "aby wyświetlić lub ukryć wpis spod CW", "keyboard_shortcuts.toot": "aby utworzyć nowy wpis", "keyboard_shortcuts.unfocus": "aby opuścić pole wyszukiwania/pisania", "keyboard_shortcuts.up": "aby przejść na górę listy", @@ -156,7 +157,7 @@ "mute_modal.hide_notifications": "Chcesz ukryć powiadomienia od tego użytkownika?", "navigation_bar.blocks": "Zablokowani użytkownicy", "navigation_bar.community_timeline": "Lokalna oś czasu", - "navigation_bar.direct": "Direct messages", + "navigation_bar.direct": "Wiadomości bezpośrednie", "navigation_bar.domain_blocks": "Ukryte domeny", "navigation_bar.edit_profile": "Edytuj profil", "navigation_bar.favourites": "Ulubione", @@ -241,10 +242,10 @@ "search_results.total": "{count, number} {count, plural, one {wynik} few {wyniki} many {wyników} more {wyników}}", "standalone.public_title": "Spojrzenie w głąb…", "status.block": "Zablokuj @{name}", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "Cofnij podbicie", "status.cannot_reblog": "Ten wpis nie może zostać podbity", "status.delete": "Usuń", - "status.direct": "Direct message @{name}", + "status.direct": "Wyślij wiadomość bezpośrednią do @{name}", "status.embed": "Osadź", "status.favourite": "Ulubione", "status.load_more": "Załaduj więcej", @@ -257,7 +258,7 @@ "status.pin": "Przypnij do profilu", "status.pinned": "Przypięty wpis", "status.reblog": "Podbij", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "Podbij dla odbiorców oryginalnego wpisu", "status.reblogged_by": "{name} podbił", "status.reply": "Odpowiedz", "status.replyAll": "Odpowiedz na wątek", diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 0f18ace37..faa549472 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -260,6 +260,16 @@ pl: created_msg: Pomyslnie utworzono notatkę moderacyjną. destroyed_msg: Pomyślnie usunięto notatkę moderacyjną. reports: + account: + created_reports: Zgłoszenia utworzone z tego konta + moderation: + silenced: Wyciszone + suspended: Zawieszone + title: Moderacja + moderation_notes: Notatki moderacyjne + note: notatka + report: zgłoszenie + targeted_reports: Zgłoszenia dotycząće tego konta action_taken_by: Działanie podjęte przez are_you_sure: Czy na pewno? assign_to_self: Przypisz do siebie diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml index 2b55f60d0..cbca232fe 100644 --- a/config/locales/simple_form.pl.yml +++ b/config/locales/simple_form.pl.yml @@ -10,6 +10,7 @@ pl: many: Pozostało %{count} znaków one: Pozostał 1 znak other: Pozostało %{count} znaków + fields: Możesz ustawić maksymalnie 4 niestandardowe pola wyświetlane jako tabela na Twoim profilu header: PNG, GIF lub JPG. Maksymalnie 2MB. Zostanie zmniejszony do 700x335px locked: Musisz akceptować prośby o śledzenie note: @@ -26,6 +27,10 @@ pl: user: filtered_languages: Wpisy w wybranych językach nie będą wyświetlać się na publicznych osiach czasu labels: + account: + fields: + name: Nazwa + value: Zawartość defaults: avatar: Awatar confirm_new_password: Potwierdź nowe hasło @@ -35,6 +40,7 @@ pl: display_name: Widoczna nazwa email: Adres e-mail expires_in: Wygaśnie po + fields: Metadane profilu filtered_languages: Filtrowane języki header: Nagłówek locale: Język From ff87d1bc3ecdb81ff5c523f0964ecf223a503d30 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 19 Apr 2018 00:53:31 +0200 Subject: [PATCH 103/442] Rescue SSL errors when processing mentions, remove useless line (#7184) --- app/services/process_mentions_service.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb index 8e285e1f7..dc8df4a9a 100644 --- a/app/services/process_mentions_service.rb +++ b/app/services/process_mentions_service.rb @@ -17,13 +17,11 @@ class ProcessMentionsService < BaseService if mention_undeliverable?(status, mentioned_account) begin mentioned_account = resolve_account_service.call($1) - rescue Goldfinger::Error, HTTP::Error + rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError, Mastodon::UnexpectedResponseError mentioned_account = nil end end - mentioned_account ||= Account.find_remote(username, domain) - next match if mention_undeliverable?(status, mentioned_account) mentioned_account.mentions.where(status: status).first_or_create(status: status) From ba917e15f66c7848fe943e571d1ec5eeb549b59d Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Thu, 19 Apr 2018 02:36:31 +0200 Subject: [PATCH 104/442] Fix text color in "show more" link inside boost confirmation modal (#7183) --- .../styles/mastodon/components.scss | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index cdf3a9daa..9b5ab6f01 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -40,6 +40,16 @@ cursor: default; } + &::-moz-focus-inner { + border: 0; + } + + &::-moz-focus-inner, + &:focus, + &:active { + outline: 0 !important; + } + &.button-primary, &.button-alternative, &.button-secondary, @@ -666,6 +676,16 @@ background: lighten($action-button-color, 7%); text-decoration: none; } + + &::-moz-focus-inner { + border: 0; + } + + &::-moz-focus-inner, + &:focus, + &:active { + outline: 0 !important; + } } .status__content__text { @@ -3899,6 +3919,10 @@ a.status-card { top: 10px; width: 48px; } + + .status__content__spoiler-link { + color: lighten($ui-secondary-color, 8%); + } } .actions-modal { From b9c35785e2513ff5ea33c1247eb18e48aa70fc5f Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Thu, 19 Apr 2018 15:39:54 +0200 Subject: [PATCH 105/442] Reports: Merge contents and comment columns (#7189) --- app/views/admin/reports/_report.html.haml | 22 +++++++++++----------- app/views/admin/reports/index.html.haml | 1 - 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/views/admin/reports/_report.html.haml b/app/views/admin/reports/_report.html.haml index d266f4840..84db00ad5 100644 --- a/app/views/admin/reports/_report.html.haml +++ b/app/views/admin/reports/_report.html.haml @@ -5,18 +5,18 @@ = link_to report.target_account.acct, admin_account_path(report.target_account.id) %td.reporter = link_to report.account.acct, admin_account_path(report.account.id) - %td.comment - %span{ title: report.comment } + %td + %div{ title: report.comment } = truncate(report.comment, length: 30, separator: ' ') - %td.stats - - unless report.statuses.empty? - %span{ title: t('admin.accounts.statuses') } - = fa_icon('comment') - = report.statuses.count - - unless report.media_attachments.empty? - %span{ title: t('admin.accounts.media_attachments') } - = fa_icon('camera') - = report.media_attachments.count + %div + - unless report.statuses.empty? + %span{ title: t('admin.accounts.statuses') } + = fa_icon('comment') + = report.statuses.count + - unless report.media_attachments.empty? + %span{ title: t('admin.accounts.media_attachments') } + = fa_icon('camera') + = report.media_attachments.count %td - if report.assigned_account.nil? \- diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index 3b127c4fc..c3baaf6be 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -18,7 +18,6 @@ %th= t('admin.reports.id') %th= t('admin.reports.target') %th= t('admin.reports.reported_by') - %th= t('admin.reports.comment.label') %th= t('admin.reports.report_contents') %th= t('admin.reports.assigned') %th From ca2cbe8f0f6eb1efb095817f1dba26f89f1b4a54 Mon Sep 17 00:00:00 2001 From: beatrix Date: Thu, 19 Apr 2018 17:35:47 -0400 Subject: [PATCH 106/442] Fix webkit scrollbars (#7191) * Revert "Make scroll bars wider (#7060)" This reverts commit 11715454d033784bf6176b75a954e5c28b5d79e5. * for real make webkit scrollbar wider --- app/javascript/styles/mastodon/reset.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/javascript/styles/mastodon/reset.scss b/app/javascript/styles/mastodon/reset.scss index 58d4de35f..ff3b2c022 100644 --- a/app/javascript/styles/mastodon/reset.scss +++ b/app/javascript/styles/mastodon/reset.scss @@ -53,6 +53,11 @@ table { border-spacing: 0; } +::-webkit-scrollbar { + width: 12px; + height: 12px; +} + ::-webkit-scrollbar-thumb { background: lighten($ui-base-color, 4%); border: 0px none $base-border-color; From 1663368724b0a0076823aa31459eb77b9264969b Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Fri, 20 Apr 2018 09:06:53 +0900 Subject: [PATCH 107/442] Replace preload link tag to Rails helper (#7192) --- app/views/home/index.html.haml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 8c88d2d64..7b1a7e50a 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,8 +1,9 @@ - content_for :header_tags do - %link{ href: asset_pack_path('features/getting_started.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ - %link{ href: asset_pack_path('features/compose.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ - %link{ href: asset_pack_path('features/home_timeline.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ - %link{ href: asset_pack_path('features/notifications.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ + = preload_link_tag asset_pack_path('features/getting_started.js'), crossorigin: 'anonymous' + = preload_link_tag asset_pack_path('features/compose.js'), crossorigin: 'anonymous' + = preload_link_tag asset_pack_path('features/home_timeline.js'), crossorigin: 'anonymous' + = preload_link_tag asset_pack_path('features/notifications.js'), crossorigin: 'anonymous' + %meta{name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key} %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) From a9c440637ca9f36bcf051094abe3bcba1da63166 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 20 Apr 2018 02:28:48 +0200 Subject: [PATCH 108/442] Improve report layout (#7188) * Use table for statuses in report * Display reported account and reporter in the same table * Split accounts and general report info into two tables again * Redesign report statuses table, notes, merge notes and action log * Remove unused translations * Fix code style issue * Fix code style issue * Fix code style issue --- .../admin/reported_statuses_controller.rb | 14 ++- app/controllers/admin/reports_controller.rb | 8 +- .../admin/account_moderation_notes_helper.rb | 16 +++ app/helpers/application_helper.rb | 4 + app/helpers/stream_entries_helper.rb | 13 ++ app/javascript/packs/admin.js | 1 + app/javascript/styles/mastodon/admin.scss | 103 ++++++++------- .../styles/mastodon/components.scss | 16 ++- app/javascript/styles/mastodon/tables.scss | 116 ++++++++++++++++- .../admin/action_logs/_action_log.html.haml | 2 +- app/views/admin/action_logs/index.html.haml | 3 +- .../admin/report_notes/_report_note.html.haml | 14 +-- app/views/admin/reports/_account.html.haml | 19 +++ .../admin/reports/_account_details.html.haml | 20 --- app/views/admin/reports/_action_log.html.haml | 6 + app/views/admin/reports/_report.html.haml | 6 +- app/views/admin/reports/_status.html.haml | 28 +++++ app/views/admin/reports/index.html.haml | 27 ++-- app/views/admin/reports/show.html.haml | 117 ++++++++---------- .../stream_entries/_detailed_status.html.haml | 6 +- .../stream_entries/_simple_status.html.haml | 4 +- config/locales/ar.yml | 1 - config/locales/ca.yml | 1 - config/locales/de.yml | 1 - config/locales/en.yml | 26 ++-- config/locales/eo.yml | 1 - config/locales/es.yml | 1 - config/locales/fa.yml | 1 - config/locales/fi.yml | 1 - config/locales/fr.yml | 1 - config/locales/gl.yml | 1 - config/locales/he.yml | 1 - config/locales/hu.yml | 1 - config/locales/id.yml | 1 - config/locales/io.yml | 1 - config/locales/ja.yml | 5 - config/locales/ko.yml | 1 - config/locales/nl.yml | 1 - config/locales/no.yml | 1 - config/locales/oc.yml | 1 - config/locales/pl.yml | 12 -- config/locales/pt-BR.yml | 1 - config/locales/pt.yml | 1 - config/locales/ru.yml | 1 - config/locales/sk.yml | 1 - config/locales/sr-Latn.yml | 1 - config/locales/sr.yml | 1 - config/locales/sv.yml | 1 - config/locales/th.yml | 1 - config/locales/tr.yml | 1 - config/locales/uk.yml | 1 - config/locales/zh-CN.yml | 1 - config/locales/zh-HK.yml | 12 -- config/locales/zh-TW.yml | 1 - .../reported_statuses_controller_spec.rb | 2 +- 55 files changed, 380 insertions(+), 249 deletions(-) create mode 100644 app/views/admin/reports/_account.html.haml delete mode 100644 app/views/admin/reports/_account_details.html.haml create mode 100644 app/views/admin/reports/_action_log.html.haml create mode 100644 app/views/admin/reports/_status.html.haml diff --git a/app/controllers/admin/reported_statuses_controller.rb b/app/controllers/admin/reported_statuses_controller.rb index 535bd11d4..522f68c98 100644 --- a/app/controllers/admin/reported_statuses_controller.rb +++ b/app/controllers/admin/reported_statuses_controller.rb @@ -8,7 +8,7 @@ module Admin def create authorize :status, :update? - @form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account)) + @form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button)) flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save redirect_to admin_report_path(@report) @@ -35,7 +35,17 @@ module Admin end def form_status_batch_params - params.require(:form_status_batch).permit(:action, status_ids: []) + params.require(:form_status_batch).permit(status_ids: []) + end + + def action_from_button + if params[:nsfw_on] + 'nsfw_on' + elsif params[:nsfw_off] + 'nsfw_off' + elsif params[:delete] + 'delete' + end end def set_report diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index a4ae9507d..d00b3d222 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -11,10 +11,10 @@ module Admin def show authorize @report, :show? - @report_note = @report.notes.new - @report_notes = @report.notes.latest - @report_history = @report.history - @form = Form::StatusBatch.new + + @report_note = @report.notes.new + @report_notes = (@report.notes.latest + @report.history).sort_by(&:created_at) + @form = Form::StatusBatch.new end def update diff --git a/app/helpers/admin/account_moderation_notes_helper.rb b/app/helpers/admin/account_moderation_notes_helper.rb index b17c52264..fdfadef08 100644 --- a/app/helpers/admin/account_moderation_notes_helper.rb +++ b/app/helpers/admin/account_moderation_notes_helper.rb @@ -1,4 +1,20 @@ # frozen_string_literal: true module Admin::AccountModerationNotesHelper + def admin_account_link_to(account) + link_to admin_account_path(account.id), class: name_tag_classes(account) do + safe_join([ + image_tag(account.avatar.url, width: 15, height: 15, alt: display_name(account), class: 'avatar'), + content_tag(:span, account.acct, class: 'username'), + ], ' ') + end + end + + private + + def name_tag_classes(account) + classes = ['name-tag'] + classes << 'suspended' if account.suspended? + classes.join(' ') + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bab4615a1..95863ab1f 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -63,4 +63,8 @@ module ApplicationHelper def opengraph(property, content) tag(:meta, content: content, property: property) end + + def react_component(name, props = {}) + content_tag(:div, nil, data: { component: name.to_s.camelcase, props: Oj.dump(props) }) + end end diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb index 3992432db..8254ef4dc 100644 --- a/app/helpers/stream_entries_helper.rb +++ b/app/helpers/stream_entries_helper.rb @@ -113,6 +113,19 @@ module StreamEntriesHelper end end + def fa_visibility_icon(status) + case status.visibility + when 'public' + fa_icon 'globe fw' + when 'unlisted' + fa_icon 'unlock-alt fw' + when 'private' + fa_icon 'lock fw' + when 'direct' + fa_icon 'envelope fw' + end + end + private def simplified_text(text) diff --git a/app/javascript/packs/admin.js b/app/javascript/packs/admin.js index 2bf1514a9..5dbcc03d3 100644 --- a/app/javascript/packs/admin.js +++ b/app/javascript/packs/admin.js @@ -24,6 +24,7 @@ delegate(document, batchCheckboxClassName, 'change', () => { const checkAllElement = document.querySelector('#batch_checkbox_all'); if (checkAllElement) { checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); + checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); } }); diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 348f72078..a0f69449a 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -141,14 +141,15 @@ } hr { - margin: 20px 0; + width: 100%; + height: 0; border: 0; - background: transparent; - border-bottom: 1px solid $ui-base-color; + border-bottom: 1px solid rgba($ui-base-lighter-color, .6); + margin: 20px 0; - &.section-break { - margin: 30px 0; - border-bottom: 2px solid $ui-base-lighter-color; + &.spacer { + height: 1px; + border: 0; } } @@ -335,34 +336,8 @@ } } -.report-note__comment { - margin-bottom: 20px; -} - -.report-note__form { - margin-bottom: 20px; - - .report-note__textarea { - box-sizing: border-box; - border: 0; - padding: 7px 4px; - margin-bottom: 10px; - font-size: 16px; - color: $inverted-text-color; - display: block; - width: 100%; - outline: 0; - font-family: inherit; - resize: vertical; - } - - .report-note__buttons { - text-align: right; - } - - .report-note__button { - margin: 0 0 5px 5px; - } +.simple_form.new_report_note { + max-width: 100%; } .batch-form-box { @@ -390,13 +365,6 @@ } } -.batch-checkbox, -.batch-checkbox-all { - display: flex; - align-items: center; - margin-right: 5px; -} - .back-link { margin-bottom: 10px; font-size: 14px; @@ -416,7 +384,7 @@ } .log-entry { - margin-bottom: 8px; + margin-bottom: 20px; line-height: 20px; &__header { @@ -514,9 +482,12 @@ } } +a.name-tag, .name-tag { display: flex; align-items: center; + text-decoration: none; + color: $ui-secondary-color; .avatar { display: block; @@ -528,4 +499,52 @@ .username { font-weight: 500; } + + &.suspended { + .username { + text-decoration: line-through; + color: lighten($error-red, 12%); + } + + .avatar { + filter: grayscale(100%); + opacity: 0.8; + } + } +} + +.speech-bubble { + margin-bottom: 20px; + border-left: 4px solid $ui-highlight-color; + + &.positive { + border-left-color: $success-green; + } + + &.negative { + border-left-color: lighten($error-red, 12%); + } + + &__bubble { + padding: 16px; + padding-left: 14px; + font-size: 15px; + line-height: 20px; + border-radius: 4px 4px 4px 0; + position: relative; + font-weight: 500; + + a { + color: $ui-primary-color; + } + } + + &__owner { + padding: 8px; + padding-left: 12px; + } + + time { + color: $darker-text-color; + } } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 9b5ab6f01..908fa8a07 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1006,6 +1006,15 @@ padding: 10px; border-bottom: 1px solid lighten($ui-base-color, 8%); + &.compact { + padding: 0; + border-bottom: 0; + + .account__avatar-wrapper { + margin-left: 0; + } + } + .account__display-name { flex: 1 1 auto; display: block; @@ -1029,7 +1038,6 @@ .account__avatar { @include avatar-radius(); position: relative; - cursor: pointer; &-inline { display: inline-block; @@ -1038,6 +1046,10 @@ } } +a .account__avatar { + cursor: pointer; +} + .account__avatar-overlay { @include avatar-size(48px); @@ -1286,7 +1298,7 @@ .status__display-name, .reply-indicator__display-name, .detailed-status__display-name, -.account__display-name { +a.account__display-name { &:hover strong { text-decoration: underline; } diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index c12d84f1c..fa876e603 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -11,6 +11,7 @@ vertical-align: top; border-top: 1px solid $ui-base-color; text-align: left; + background: darken($ui-base-color, 4%); } & > thead > tr > th { @@ -48,9 +49,38 @@ } } - &.inline-table > tbody > tr:nth-child(odd) > td, - &.inline-table > tbody > tr:nth-child(odd) > th { - background: transparent; + &.inline-table { + & > tbody > tr:nth-child(odd) { + & > td, + & > th { + background: transparent; + } + } + + & > tbody > tr:first-child { + & > td, + & > th { + border-top: 0; + } + } + } + + &.batch-table { + & > thead > tr > th { + background: $ui-base-color; + border-top: 1px solid darken($ui-base-color, 8%); + border-bottom: 1px solid darken($ui-base-color, 8%); + + &:first-child { + border-radius: 4px 0 0; + border-left: 1px solid darken($ui-base-color, 8%); + } + + &:last-child { + border-radius: 0 4px 0 0; + border-right: 1px solid darken($ui-base-color, 8%); + } + } } } @@ -63,6 +93,13 @@ samp { font-family: 'mastodon-font-monospace', monospace; } +button.table-action-link { + background: transparent; + border: 0; + font: inherit; +} + +button.table-action-link, a.table-action-link { text-decoration: none; display: inline-block; @@ -79,4 +116,77 @@ a.table-action-link { font-weight: 400; margin-right: 5px; } + + &:first-child { + padding-left: 0; + } +} + +.batch-table { + &__toolbar, + &__row { + display: flex; + + &__select { + box-sizing: border-box; + padding: 8px 16px; + cursor: pointer; + min-height: 100%; + + input { + margin-top: 8px; + } + } + + &__actions, + &__content { + padding: 8px 0; + padding-right: 16px; + flex: 1 1 auto; + } + } + + &__toolbar { + border: 1px solid darken($ui-base-color, 8%); + background: $ui-base-color; + border-radius: 4px 0 0; + height: 47px; + align-items: center; + + &__actions { + text-align: right; + padding-right: 16px - 5px; + } + } + + &__row { + border: 1px solid darken($ui-base-color, 8%); + border-top: 0; + background: darken($ui-base-color, 4%); + + &:hover { + background: darken($ui-base-color, 2%); + } + + &:nth-child(even) { + background: $ui-base-color; + + &:hover { + background: lighten($ui-base-color, 2%); + } + } + + &__content { + padding-top: 12px; + padding-bottom: 16px; + } + } + + .status__content { + padding-top: 0; + + strong { + font-weight: 700; + } + } } diff --git a/app/views/admin/action_logs/_action_log.html.haml b/app/views/admin/action_logs/_action_log.html.haml index ec90961cb..f059814bd 100644 --- a/app/views/admin/action_logs/_action_log.html.haml +++ b/app/views/admin/action_logs/_action_log.html.haml @@ -1,4 +1,4 @@ -%li.log-entry +.log-entry .log-entry__header .log-entry__avatar = image_tag action_log.account.avatar.url(:original), alt: '', width: 40, height: 40, class: 'avatar' diff --git a/app/views/admin/action_logs/index.html.haml b/app/views/admin/action_logs/index.html.haml index bb6d7b5d7..a4d3871a9 100644 --- a/app/views/admin/action_logs/index.html.haml +++ b/app/views/admin/action_logs/index.html.haml @@ -1,7 +1,6 @@ - content_for :page_title do = t('admin.action_logs.title') -%ul - = render @action_logs += render @action_logs = paginate @action_logs diff --git a/app/views/admin/report_notes/_report_note.html.haml b/app/views/admin/report_notes/_report_note.html.haml index 1f621e0d3..d34dc3d15 100644 --- a/app/views/admin/report_notes/_report_note.html.haml +++ b/app/views/admin/report_notes/_report_note.html.haml @@ -1,9 +1,7 @@ -%li - %h4 - = report_note.account.acct - %div{ style: 'float: right' } - %time.formatted{ datetime: report_note.created_at.iso8601, title: l(report_note.created_at) } - = l report_note.created_at - = table_link_to 'trash', t('admin.reports.notes.delete'), admin_report_note_path(report_note), method: :delete if can?(:destroy, report_note) - %div{ class: 'report-note__comment' } +.speech-bubble + .speech-bubble__bubble = simple_format(h(report_note.content)) + .speech-bubble__owner + = admin_account_link_to report_note.account + %time.formatted{ datetime: report_note.created_at.iso8601 }= l report_note.created_at + = table_link_to 'trash', t('admin.reports.notes.delete'), admin_report_note_path(report_note), method: :delete if can?(:destroy, report_note) diff --git a/app/views/admin/reports/_account.html.haml b/app/views/admin/reports/_account.html.haml new file mode 100644 index 000000000..22b7a0861 --- /dev/null +++ b/app/views/admin/reports/_account.html.haml @@ -0,0 +1,19 @@ +- size ||= 36 + +.account.compact + .account__wrapper + - if account.nil? + .account__display-name + .account__avatar-wrapper + .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)}); width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px" } + %span.display-name + %strong= t 'about.contact_missing' + %span.display-name__account= t 'about.contact_unavailable' + - else + = link_to TagManager.instance.url_for(account), class: 'account__display-name' do + .account__avatar-wrapper + .account__avatar{ style: "background-image: url(#{account.avatar.url}); width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px" } + %span.display-name + %bdi + %strong.display-name__html.emojify= display_name(account) + %span.display-name__account @#{account.acct} diff --git a/app/views/admin/reports/_account_details.html.haml b/app/views/admin/reports/_account_details.html.haml deleted file mode 100644 index a8af39bef..000000000 --- a/app/views/admin/reports/_account_details.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -.table-wrapper - %table.table - %tbody - %tr - %td= t('admin.reports.account.created_reports') - %td= link_to pluralize(account.reports.count, t('admin.reports.account.report')), admin_reports_path(account_id: account.id) - %tr - %td= t('admin.reports.account.targeted_reports') - %td= link_to pluralize(account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: account.id) - %tr - %td= t('admin.reports.account.moderation_notes') - %td= link_to pluralize(account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: account.id) - - if account.silenced? || account.suspended? - %tr - %td= t('admin.reports.account.moderation.title') - %td - - if account.silenced? - %p= t('admin.reports.account.moderation.silenced') - - if account.suspended? - %p= t('admin.reports.account.moderation.suspended') diff --git a/app/views/admin/reports/_action_log.html.haml b/app/views/admin/reports/_action_log.html.haml new file mode 100644 index 000000000..024078eb9 --- /dev/null +++ b/app/views/admin/reports/_action_log.html.haml @@ -0,0 +1,6 @@ +.speech-bubble.positive + .speech-bubble__bubble + = t("admin.action_logs.actions.#{action_log.action}_#{action_log.target_type.underscore}", name: content_tag(:span, action_log.account.username, class: 'username'), target: content_tag(:span, log_target(action_log), class: 'target')).html_safe + .speech-bubble__owner + = admin_account_link_to(action_log.account) + %time.formatted{ datetime: action_log.created_at.iso8601 }= l action_log.created_at diff --git a/app/views/admin/reports/_report.html.haml b/app/views/admin/reports/_report.html.haml index 84db00ad5..d6c881955 100644 --- a/app/views/admin/reports/_report.html.haml +++ b/app/views/admin/reports/_report.html.haml @@ -2,9 +2,9 @@ %td.id = "##{report.id}" %td.target - = link_to report.target_account.acct, admin_account_path(report.target_account.id) + = admin_account_link_to report.target_account %td.reporter - = link_to report.account.acct, admin_account_path(report.account.id) + = admin_account_link_to report.account %td %div{ title: report.comment } = truncate(report.comment, length: 30, separator: ' ') @@ -21,6 +21,6 @@ - if report.assigned_account.nil? \- - else - = link_to report.assigned_account.acct, admin_account_path(report.assigned_account.id) + = admin_account_link_to report.assigned_account %td = table_link_to 'circle', t('admin.reports.view'), admin_report_path(report) diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml new file mode 100644 index 000000000..137609539 --- /dev/null +++ b/app/views/admin/reports/_status.html.haml @@ -0,0 +1,28 @@ +.batch-table__row + %label.batch-table__row__select.batch-checkbox + = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id + .batch-table__row__content + .status__content>< + - unless status.spoiler_text.blank? + %p>< + %strong= Formatter.instance.format_spoiler(status) + + = Formatter.instance.format(status) + + - unless status.media_attachments.empty? + - if status.media_attachments.first.video? + - video = status.media_attachments.first + = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true + - else + = react_component :media_gallery, height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } + + .detailed-status__meta + = link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime', target: stream_link_target, rel: 'noopener' do + %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) + · + = fa_visibility_icon(status) + = t("statuses.visibilities.#{status.visibility}") + - if status.sensitive? + · + = fa_icon('eye-slash fw') + = t('stream_entries.sensitive_content') diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index c3baaf6be..44a531f2c 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -8,20 +8,17 @@ %li= filter_link_to t('admin.reports.unresolved'), resolved: nil %li= filter_link_to t('admin.reports.resolved'), resolved: '1' -= form_tag do - - .table-wrapper - %table.table - %thead - %tr - -# %th - %th= t('admin.reports.id') - %th= t('admin.reports.target') - %th= t('admin.reports.reported_by') - %th= t('admin.reports.report_contents') - %th= t('admin.reports.assigned') - %th - %tbody - = render @reports +.table-wrapper + %table.table + %thead + %tr + %th= t('admin.reports.id') + %th= t('admin.reports.target') + %th= t('admin.reports.reported_by') + %th= t('admin.reports.report_contents') + %th= t('admin.reports.assigned') + %th + %tbody + = render @reports = paginate @reports diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 130650001..2bba3079e 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -14,16 +14,28 @@ - else = link_to t('admin.reports.mark_as_unresolved'), admin_report_path(@report, outcome: 'reopen'), method: :put, class: 'button' +%hr.spacer + .table-wrapper %table.table.inline-table %tbody + %tr + %th= t('admin.reports.reported_account') + %td= admin_account_link_to @report.target_account + %td= table_link_to 'flag', pluralize(@report.target_account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: @report.target_account.id) + %td= table_link_to 'file', pluralize(@report.target_account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: @report.target_account.id) + %tr + %th= t('admin.reports.reported_by') + %td= admin_account_link_to @report.account + %td= table_link_to 'flag', pluralize(@report.account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: @report.account.id) + %td= table_link_to 'file', pluralize(@report.account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: @report.account.id) %tr %th= t('admin.reports.created_at') - %td{colspan: 2} + %td{ colspan: 3 } %time.formatted{ datetime: @report.created_at.iso8601 } %tr %th= t('admin.reports.updated_at') - %td{colspan: 2} + %td{ colspan: 3 } %time.formatted{ datetime: @report.updated_at.iso8601 } %tr %th= t('admin.reports.status') @@ -32,14 +44,14 @@ = t('admin.reports.resolved') - else = t('admin.reports.unresolved') - %td{style: "text-align: right; overflow: hidden;"} + %td{ colspan: 2 } - if @report.action_taken? = table_link_to 'envelope-open', t('admin.reports.reopen'), admin_report_path(@report, outcome: 'reopen'), method: :put - if !@report.action_taken_by_account.nil? %tr %th= t('admin.reports.action_taken_by') - %td{colspan: 2} - = @report.action_taken_by_account.acct + %td{ colspan: 3 } + = admin_account_link_to @report.action_taken_by_account - else %tr %th= t('admin.reports.assigned') @@ -47,78 +59,55 @@ - if @report.assigned_account.nil? \- - else - = link_to @report.assigned_account.acct, admin_account_path(@report.assigned_account.id) - %td{style: "text-align: right"} + = admin_account_link_to @report.assigned_account + %td - if @report.assigned_account != current_user.account = table_link_to 'user', t('admin.reports.assign_to_self'), admin_report_path(@report, outcome: 'assign_to_self'), method: :put + %td - if !@report.assigned_account.nil? = table_link_to 'trash', t('admin.reports.unassign'), admin_report_path(@report, outcome: 'unassign'), method: :put -%hr{ class: "section-break"}/ +%hr.spacer -.report-accounts - .report-accounts__item - %h3= t('admin.reports.reported_account') - = render 'authorize_follows/card', account: @report.target_account, admin: true - = render 'admin/reports/account_details', account: @report.target_account - .report-accounts__item - %h3= t('admin.reports.reported_by') - = render 'authorize_follows/card', account: @report.account, admin: true - = render 'admin/reports/account_details', account: @report.account - -%h3= t('admin.reports.comment.label') - -= simple_format(@report.comment.presence || t('admin.reports.comment.none')) +.speech-bubble + .speech-bubble__bubble= simple_format(@report.comment.presence || t('admin.reports.comment.none')) + .speech-bubble__owner + = admin_account_link_to @report.account + %time.formatted{ datetime: @report.created_at.iso8601 } - unless @report.statuses.empty? - %hr/ - - %h3= t('admin.reports.statuses') + %hr.spacer/ = form_for(@form, url: admin_report_reported_statuses_path(@report.id)) do |f| - .batch-form-box - .batch-checkbox-all - = check_box_tag :batch_checkbox_all, nil, false - = f.select :action, Form::StatusBatch::ACTION_TYPE.map{|action| [t("admin.statuses.batch.#{action}"), action]} - = f.submit t('admin.statuses.execute'), data: { confirm: t('admin.reports.are_you_sure') }, class: 'button' - .media-spoiler-toggle-buttons - .media-spoiler-show-button.button= t('admin.statuses.media.show') - .media-spoiler-hide-button.button= t('admin.statuses.media.hide') - - @report.statuses.each do |status| - .report-status{ data: { id: status.id } } - .batch-checkbox - = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id - .activity-stream.activity-stream-headless - .entry= render 'stream_entries/simple_status', status: status - .report-status__actions - - unless status.media_attachments.empty? - = link_to admin_report_reported_status_path(@report, status, status: { sensitive: !status.sensitive }), method: :put, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do - = fa_icon status.sensitive? ? 'eye' : 'eye-slash' - = link_to admin_report_reported_status_path(@report, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') }, remote: true do - = fa_icon 'trash' + .batch-table + .batch-table__toolbar + %label.batch-table__toolbar__select.batch-checkbox-all + = check_box_tag :batch_checkbox_all, nil, false + .batch-table__toolbar__actions + = f.button safe_join([fa_icon('eye-slash'), t('admin.statuses.batch.nsfw_on')]), name: :nsfw_on, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + = f.button safe_join([fa_icon('eye'), t('admin.statuses.batch.nsfw_off')]), name: :nsfw_off, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + = f.button safe_join([fa_icon('trash'), t('admin.statuses.batch.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + .batch-table__body + = render partial: 'admin/reports/status', collection: @report.statuses, locals: { f: f } -%hr{ class: "section-break"}/ +%hr.spacer/ -%h3= t('admin.reports.notes.label') +- @report_notes.each do |item| + - if item.is_a?(Admin::ActionLog) + = render partial: 'action_log', locals: { action_log: item } + - elsif item.is_a?(ReportNote) + = render item -- if @report_notes.length > 0 - %ul - = render @report_notes - -%h4= t('admin.reports.notes.new_label') -= form_for @report_note, url: admin_report_notes_path, html: { class: 'report-note__form' } do |f| += simple_form_for @report_note, url: admin_report_notes_path do |f| = render 'shared/error_messages', object: @report_note - = f.text_area :content, placeholder: t('admin.reports.notes.placeholder'), rows: 6, class: 'report-note__textarea' - = f.hidden_field :report_id - %div{ class: 'report-note__buttons' } + = f.input :report_id, as: :hidden + + .field-group + = f.input :content, placeholder: t('admin.reports.notes.placeholder'), rows: 6 + + .actions - if @report.unresolved? - = f.submit t('admin.reports.notes.create_and_resolve'), name: :create_and_resolve, class: 'button report-note__button' + = f.button :button, t('admin.reports.notes.create_and_resolve'), name: :create_and_resolve, type: :submit - else - = f.submit t('admin.reports.notes.create_and_unresolve'), name: :create_and_unresolve, class: 'button report-note__button' - = f.submit t('admin.reports.notes.create'), class: 'button report-note__button' - -- if @report_history.length > 0 - %h3= t('admin.reports.history') - - %ul - = render @report_history + = f.button :button, t('admin.reports.notes.create_and_unresolve'), name: :create_and_unresolve, type: :submit + = f.button :button, t('admin.reports.notes.create'), type: :submit diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml index e1122d5a2..afc66d148 100644 --- a/app/views/stream_entries/_detailed_status.html.haml +++ b/app/views/stream_entries/_detailed_status.html.haml @@ -22,11 +22,11 @@ - if !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first - %div{ data: { component: 'Video', props: Oj.dump(src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 670, height: 380, detailed: true, inline: true) }} + = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 670, height: 380, detailed: true, inline: true - else - %div{ data: { component: 'MediaGallery', props: Oj.dump(height: 380, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }) }} + = react_component :media_gallery, height: 380, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } - elsif status.preview_cards.first - %div{ data: { component: 'Card', props: Oj.dump('maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_cards.first, serializer: REST::PreviewCardSerializer).as_json) }} + = react_component :card, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_cards.first, serializer: REST::PreviewCardSerializer).as_json .detailed-status__meta %data.dt-published{ value: status.created_at.to_time.iso8601 } diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml index 984691097..a6f5120fb 100644 --- a/app/views/stream_entries/_simple_status.html.haml +++ b/app/views/stream_entries/_simple_status.html.haml @@ -23,6 +23,6 @@ - unless status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first - %div{ data: { component: 'Video', props: Oj.dump(src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true) }} + = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true - else - %div{ data: { component: 'MediaGallery', props: Oj.dump(height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }) }} + = react_component :media_gallery, height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } diff --git a/config/locales/ar.yml b/config/locales/ar.yml index 8b9a6688a..b3cb71d28 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -240,7 +240,6 @@ ar: action_taken_by: تم اتخاذ الإجراء مِن طرف are_you_sure: هل أنت متأكد ؟ comment: - label: تعليق none: لا شيء delete: حذف id: معرّف ID diff --git a/config/locales/ca.yml b/config/locales/ca.yml index fc30b3661..c9173cd5e 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -243,7 +243,6 @@ ca: action_taken_by: Mesures adoptades per are_you_sure: N'estàs segur? comment: - label: Comentari none: Cap delete: Suprimeix id: ID diff --git a/config/locales/de.yml b/config/locales/de.yml index 6233d299e..51936aa36 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -243,7 +243,6 @@ de: action_taken_by: Maßnahme ergriffen durch are_you_sure: Bist du dir sicher? comment: - label: Kommentar none: Kein delete: Löschen id: ID diff --git a/config/locales/en.yml b/config/locales/en.yml index 20bfd0f8c..53b64a100 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -260,40 +260,29 @@ en: destroyed_msg: Report note successfully deleted! reports: account: - created_reports: Reports created by this account - moderation: - silenced: Silenced - suspended: Suspended - title: Moderation - moderation_notes: Moderation Notes note: note report: report - targeted_reports: Reports made about this account action_taken_by: Action taken by are_you_sure: Are you sure? assign_to_self: Assign to me - assigned: Assigned Moderator + assigned: Assigned moderator comment: - label: Report Comment none: None created_at: Reported delete: Delete - history: Moderation History id: ID mark_as_resolved: Mark as resolved mark_as_unresolved: Mark as unresolved notes: - create: Add Note - create_and_resolve: Resolve with Note - create_and_unresolve: Reopen with Note + create: Add note + create_and_resolve: Resolve with note + create_and_unresolve: Reopen with note delete: Delete - label: Moderator Notes - new_label: Add Moderator Note placeholder: Describe what actions have been taken, or any other updates to this report… nsfw: 'false': Unhide media attachments 'true': Hide media attachments - reopen: Reopen Report + reopen: Reopen report report: 'Report #%{id}' report_contents: Contents reported_account: Reported account @@ -302,7 +291,6 @@ en: resolved_msg: Report successfully resolved! silence_account: Silence account status: Status - statuses: Reported Toots suspend_account: Suspend account target: Target title: Reports @@ -366,8 +354,8 @@ en: back_to_account: Back to account page batch: delete: Delete - nsfw_off: NSFW OFF - nsfw_on: NSFW ON + nsfw_off: Mark as not sensitive + nsfw_on: Mark as sensitive execute: Execute failed_to_execute: Failed to execute media: diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 27c62f899..c768d8a03 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -243,7 +243,6 @@ eo: action_taken_by: Ago farita de are_you_sure: Ĉu vi certas? comment: - label: Komento none: Nenio delete: Forigi id: ID diff --git a/config/locales/es.yml b/config/locales/es.yml index a5a20aa3c..3143adda1 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -243,7 +243,6 @@ es: action_taken_by: Acción tomada por are_you_sure: "¿Estás seguro?" comment: - label: Comentario none: Ninguno delete: Eliminar id: ID diff --git a/config/locales/fa.yml b/config/locales/fa.yml index ed25ea8c9..a3005547a 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -243,7 +243,6 @@ fa: action_taken_by: انجام‌دهنده are_you_sure: آیا مطمئن هستید؟ comment: - label: توضیح none: خالی delete: پاک‌کردن id: شناسه diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 62f6560bf..550ad1805 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -243,7 +243,6 @@ fi: action_taken_by: Toimenpiteen tekijä are_you_sure: Oletko varma? comment: - label: Kommentti none: Ei mitään delete: Poista id: Tunniste diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 4571cc375..ebe579354 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -243,7 +243,6 @@ fr: action_taken_by: Intervention de are_you_sure: Êtes vous certain⋅e ? comment: - label: Commentaire none: Aucun delete: Supprimer id: ID diff --git a/config/locales/gl.yml b/config/locales/gl.yml index f4ca7e8c5..89de27a9a 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -243,7 +243,6 @@ gl: action_taken_by: Acción tomada por are_you_sure: Está segura? comment: - label: Comentario none: Nada delete: Eliminar id: ID diff --git a/config/locales/he.yml b/config/locales/he.yml index 1a7c84d7c..d641c6e1a 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -180,7 +180,6 @@ he: reports: are_you_sure: 100% על בטוח? comment: - label: הערה none: ללא delete: מחיקה id: ID diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 2560b3816..7fe431d37 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -243,7 +243,6 @@ hu: action_taken_by: 'Kezelte:' are_you_sure: Biztos vagy benne? comment: - label: Hozzászólás none: Egyik sem delete: Törlés id: ID diff --git a/config/locales/id.yml b/config/locales/id.yml index 0ef1d5040..5a63b8038 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -106,7 +106,6 @@ id: title: Server yang diketahui reports: comment: - label: Komentar none: Tidak ada delete: Hapus id: ID diff --git a/config/locales/io.yml b/config/locales/io.yml index 29ab4516b..7c25acc47 100644 --- a/config/locales/io.yml +++ b/config/locales/io.yml @@ -105,7 +105,6 @@ io: title: Known Instances reports: comment: - label: Comment none: None delete: Delete id: ID diff --git a/config/locales/ja.yml b/config/locales/ja.yml index fecf996d5..b23c02754 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -264,11 +264,9 @@ ja: assign_to_self: 担当になる assigned: 担当者 comment: - label: コメント none: なし created_at: レポート日時 delete: 削除 - history: モデレーション履歴 id: ID mark_as_resolved: 解決済みとしてマーク mark_as_unresolved: 未解決として再び開く @@ -277,8 +275,6 @@ ja: create_and_resolve: 書き込み、解決済みにする create_and_unresolve: 書き込み、未解決として開く delete: 削除 - label: モデレーターメモ - new_label: モデレーターメモの追加 placeholder: このレポートに取られた措置やその他更新を記述してください nsfw: 'false': NSFW オフ @@ -292,7 +288,6 @@ ja: resolved_msg: レポートを解決済みにしました! silence_account: アカウントをサイレンス status: ステータス - statuses: 通報されたトゥート suspend_account: アカウントを停止 target: ターゲット title: レポート diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 72c98bc30..3470085cf 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -245,7 +245,6 @@ ko: action_taken_by: 신고 처리자 are_you_sure: 정말로 실행하시겠습니까? comment: - label: 코멘트 none: 없음 delete: 삭제 id: ID diff --git a/config/locales/nl.yml b/config/locales/nl.yml index a46bb72d7..2342ef6a2 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -243,7 +243,6 @@ nl: action_taken_by: Actie uitgevoerd door are_you_sure: Weet je het zeker? comment: - label: Opmerking none: Geen delete: Verwijderen id: ID diff --git a/config/locales/no.yml b/config/locales/no.yml index d5edb3975..8b84182af 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -243,7 +243,6 @@ action_taken_by: Handling utført av are_you_sure: Er du sikker? comment: - label: Kommentar none: Ingen delete: Slett id: ID diff --git a/config/locales/oc.yml b/config/locales/oc.yml index f8e819c53..195a1d909 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -243,7 +243,6 @@ oc: action_taken_by: Mesura menada per are_you_sure: Es segur ? comment: - label: Comentari none: Pas cap delete: Suprimir id: ID diff --git a/config/locales/pl.yml b/config/locales/pl.yml index faa549472..839767c8f 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -261,25 +261,16 @@ pl: destroyed_msg: Pomyślnie usunięto notatkę moderacyjną. reports: account: - created_reports: Zgłoszenia utworzone z tego konta - moderation: - silenced: Wyciszone - suspended: Zawieszone - title: Moderacja - moderation_notes: Notatki moderacyjne note: notatka report: zgłoszenie - targeted_reports: Zgłoszenia dotycząće tego konta action_taken_by: Działanie podjęte przez are_you_sure: Czy na pewno? assign_to_self: Przypisz do siebie assigned: Przypisany moderator comment: - label: Komentarz do zgłoszenia none: Brak created_at: Zgłoszono delete: Usuń - history: Historia moderacji id: ID mark_as_resolved: Oznacz jako rozwiązane mark_as_unresolved: Oznacz jako nierozwiązane @@ -288,8 +279,6 @@ pl: create_and_resolve: Rozwiąż i pozostaw notatkę create_and_unresolve: Cofnij rozwiązanie i pozostaw notatkę delete: Usuń - label: Notatki - new_label: Dodaj notatkę moderacyjną placeholder: Opisz wykonane akcje i inne szczegóły dotyczące tego zgłoszenia… nsfw: 'false': Nie oznaczaj jako NSFW @@ -303,7 +292,6 @@ pl: resolved_msg: Pomyślnie rozwiązano zgłoszenie. silence_account: Wycisz konto status: Stan - statuses: Zgłoszone wpisy suspend_account: Zawieś konto target: Cel title: Zgłoszenia diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index d6f463a19..d3c1d532b 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -243,7 +243,6 @@ pt-BR: action_taken_by: Ação realizada por are_you_sure: Você tem certeza? comment: - label: Comentário none: Nenhum delete: Excluir id: ID diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 27d4e88e3..fb2a6cad1 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -243,7 +243,6 @@ pt: action_taken_by: Ação tomada por are_you_sure: Tens a certeza? comment: - label: Comentário none: Nenhum delete: Eliminar id: ID diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 176ace92d..bf4225758 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -245,7 +245,6 @@ ru: action_taken_by: 'Действие предпринято:' are_you_sure: Вы уверены? comment: - label: Комментарий none: Нет delete: Удалить id: ID diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 25e672604..37f711323 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -243,7 +243,6 @@ sk: action_taken_by: Zákrok vykonal are_you_sure: Ste si istý/á? comment: - label: Vyjadriť sa none: Žiadne delete: Vymazať id: Identifikácia diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index 8d39d35b0..742c976d1 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -245,7 +245,6 @@ sr-Latn: action_taken_by: Akciju izveo are_you_sure: Da li ste sigurni? comment: - label: Komentar none: Ništa delete: Obriši id: ID diff --git a/config/locales/sr.yml b/config/locales/sr.yml index af4c6a846..0d55910a6 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -245,7 +245,6 @@ sr: action_taken_by: Акцију извео are_you_sure: Да ли сте сигурни? comment: - label: Коментар none: Ништа delete: Обриши id: ID diff --git a/config/locales/sv.yml b/config/locales/sv.yml index f85ed6efb..7e763c2aa 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -243,7 +243,6 @@ sv: action_taken_by: Åtgärder vidtagna av are_you_sure: Är du säker? comment: - label: Kommentar none: Ingen delete: Radera id: ID diff --git a/config/locales/th.yml b/config/locales/th.yml index 45fe1e475..350b93b52 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -108,7 +108,6 @@ th: title: Known Instances reports: comment: - label: คอมเม้นต์ none: None delete: ลบ id: ไอดี diff --git a/config/locales/tr.yml b/config/locales/tr.yml index ee0e33074..6e7aeb77e 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -107,7 +107,6 @@ tr: title: Bilinen Sunucular reports: comment: - label: Yorum none: Yok delete: Sil id: ID diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 4c1c66b31..44f64b5c9 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -99,7 +99,6 @@ uk: undo: Відмінити reports: comment: - label: Коментар none: Немає delete: Видалити id: ID diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index be868e6e7..78c72bd30 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -241,7 +241,6 @@ zh-CN: action_taken_by: 操作执行者 are_you_sure: 你确定吗? comment: - label: 备注 none: 没有 delete: 删除 id: ID diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index 964ff5811..a27b0c04c 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -259,25 +259,16 @@ zh-HK: destroyed_msg: 舉報筆記已刪除。 reports: account: - created_reports: 由此帳號發出的舉報 - moderation: - silenced: 被靜音的 - suspended: 被停權的 - title: 管理操作 - moderation_notes: 管理筆記 note: 筆記 report: 舉報 - targeted_reports: 關於此帳號的舉報 action_taken_by: 操作執行者 are_you_sure: 你確認嗎? assign_to_self: 指派給自己 assigned: 指派負責人 comment: - label: 詳細解釋 none: 沒有 created_at: 日期 delete: 刪除 - history: 執行紀錄 id: ID mark_as_resolved: 標示為「已處理」 mark_as_unresolved: 標示為「未處理」 @@ -286,8 +277,6 @@ zh-HK: create_and_resolve: 建立筆記並標示為「已處理」 create_and_unresolve: 建立筆記並標示為「未處理」 delete: 刪除 - label: 管理筆記 - new_label: 建立管理筆記 placeholder: 記錄已執行的動作,或其他更新 nsfw: 'false': 取消 NSFW 標記 @@ -301,7 +290,6 @@ zh-HK: resolved_msg: 舉報已處理。 silence_account: 將用戶靜音 status: 狀態 - statuses: 被舉報的文章 suspend_account: 將用戶停權 target: 對象 title: 舉報 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 2fec09ed8..f69d22d79 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -79,7 +79,6 @@ zh-TW: title: 網域封鎖 reports: comment: - label: 留言 none: 無 delete: 刪除 id: ID diff --git a/spec/controllers/admin/reported_statuses_controller_spec.rb b/spec/controllers/admin/reported_statuses_controller_spec.rb index 297807d41..41e032fc7 100644 --- a/spec/controllers/admin/reported_statuses_controller_spec.rb +++ b/spec/controllers/admin/reported_statuses_controller_spec.rb @@ -13,7 +13,7 @@ describe Admin::ReportedStatusesController do describe 'POST #create' do subject do - -> { post :create, params: { report_id: report, form_status_batch: { action: action, status_ids: status_ids } } } + -> { post :create, params: { :report_id => report, action => '', :form_status_batch => { status_ids: status_ids } } } end let(:action) { 'nsfw_on' } From 084cf0babffec9e7bee537fd4f6b2294de6c33dc Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Fri, 20 Apr 2018 19:21:28 +0900 Subject: [PATCH 109/442] Add extract_foreign_key_action to Mastodon::MigrationHelpers (#7195) --- lib/mastodon/migration_helpers.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/mastodon/migration_helpers.rb b/lib/mastodon/migration_helpers.rb index 6f6f99f63..e154b5a2c 100644 --- a/lib/mastodon/migration_helpers.rb +++ b/lib/mastodon/migration_helpers.rb @@ -985,6 +985,17 @@ into similar problems in the future (e.g. when new tables are created). BackgroundMigrationWorker.perform_in(delay_interval * index, job_class_name, [start_id, end_id]) end end + + private + + # https://github.com/rails/rails/blob/v5.2.0/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb#L678-L684 + def extract_foreign_key_action(specifier) + case specifier + when 'c'; :cascade + when 'n'; :nullify + when 'r'; :restrict + end + end end end From 6f63cbb53c6ba7a5e2224c4bf846ccff1cac6a3d Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Fri, 20 Apr 2018 20:46:08 +0900 Subject: [PATCH 110/442] Replace Travis to CircleCI (#7196) --- .circleci/config.yml | 169 +++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 60 --------------- 2 files changed, 169 insertions(+), 60 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 .travis.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 000000000..e3a9628ac --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,169 @@ +version: 2 + +aliases: + - &defaults + docker: + - image: circleci/ruby:2.5.1-stretch-node + environment: &ruby_environment + BUNDLE_APP_CONFIG: ./.bundle/ + RAILS_ENV: test + NODE_ENV: test + DB_HOST: localhost + DB_USER: root + LOCAL_DOMAIN: cb6e6126.ngrok.io + LOCAL_HTTPS: true + PARALLEL_TEST_PROCESSORS: 2 + ALLOW_NOPAM: true + working_directory: ~/projects/mastodon/ + + - &attach_workspace + attach_workspace: + at: ~/projects/ + + - &persist_to_workspace + persist_to_workspace: + root: ~/projects/ + paths: + - ./mastodon/ + + - &install_steps + steps: + - checkout + - *attach_workspace + + - restore_cache: + keys: + - v1-node-dependencies-{{ checksum "yarn.lock" }} + - v1-node-dependencies- + - run: yarn install --frozen-lockfile + - save_cache: + key: v1-node-dependencies-{{ checksum "yarn.lock" }} + paths: + - ./node_modules/ + + - *persist_to_workspace + + - &install_system_dependencies + run: + name: Install system dependencies + command: | + sudo apt-get update + sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler + + - &install_ruby_dependencies + steps: + - *attach_workspace + + - *install_system_dependencies + + - run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version + - restore_cache: + keys: + - v1-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} + - v1-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}- + - v1-ruby-dependencies-- + - run: bundle install --clean --jobs 16 --path ./vendor/bundle/ --retry 3 --with pam_authentication --without development production + - save_cache: + key: v1-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} + paths: + - ./vendor/bundle/ + + - run: + name: Precompile Assets + command: | + if [ ! -d ./public/assets/ -o ! -d ./public/packs-test/ ]; then + ./bin/rails assets:precompile + fi + + - *persist_to_workspace + + - &test_steps + steps: + - *attach_workspace + + - *install_system_dependencies + - run: sudo apt-get install -y ffmpeg + + - run: + name: Prepare Tests + command: ./bin/rails parallel:create parallel:load_schema parallel:prepare + - run: + name: Run Tests + command: bundle exec parallel_test ./spec/ --group-by filesize --type rspec + +jobs: + install: + <<: *defaults + <<: *install_steps + + install-ruby2.5: + <<: *defaults + <<: *install_ruby_dependencies + + install-ruby2.4: + <<: *defaults + docker: + - image: circleci/ruby:2.4.4-stretch-node + environment: *ruby_environment + <<: *install_ruby_dependencies + + test-ruby2.5: + <<: *defaults + docker: + - image: circleci/ruby:2.5.1-stretch-node + environment: *ruby_environment + - image: circleci/postgres:10.3-alpine + environment: + POSTGRES_USER: root + - image: circleci/redis:4.0.9-alpine + <<: *test_steps + + test-ruby2.4: + <<: *defaults + docker: + - image: circleci/ruby:2.4.4-stretch-node + environment: *ruby_environment + - image: circleci/postgres:10.3-alpine + environment: + POSTGRES_USER: root + - image: circleci/redis:4.0.9-alpine + <<: *test_steps + + test-webui: + <<: *defaults + docker: + - image: circleci/node:8.11.1-stretch + steps: + - *attach_workspace + - run: yarn test:jest + + check-i18n: + <<: *defaults + steps: + - *attach_workspace + - run: bundle exec i18n-tasks check-normalized + - run: bundle exec i18n-tasks unused + +workflows: + version: 2 + build-and-test: + jobs: + - install + - install-ruby2.5: + requires: + - install + - install-ruby2.4: + requires: + - install-ruby2.5 + - test-ruby2.5: + requires: + - install-ruby2.5 + - test-ruby2.4: + requires: + - install-ruby2.4 + - test-webui: + requires: + - install + - check-i18n: + requires: + - install-ruby2.5 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2addd9ba2..000000000 --- a/.travis.yml +++ /dev/null @@ -1,60 +0,0 @@ -language: ruby -cache: - bundler: true - yarn: true - directories: - - node_modules - - public/assets - - public/packs-test - - tmp/cache/babel-loader -dist: trusty -sudo: false -branches: - only: - - master - -notifications: - email: false - -env: - global: - - LOCAL_DOMAIN=cb6e6126.ngrok.io - - LOCAL_HTTPS=true - - RAILS_ENV=test - - NOKOGIRI_USE_SYSTEM_LIBRARIES=true - - PARALLEL_TEST_PROCESSORS=2 - - ALLOW_NOPAM=true - -addons: - postgresql: 9.4 - apt: - sources: - - trusty-media - - sourceline: deb https://dl.yarnpkg.com/debian/ stable main - key_url: https://dl.yarnpkg.com/debian/pubkey.gpg - packages: - - ffmpeg - - libicu-dev - - libprotobuf-dev - - protobuf-compiler - - yarn - -rvm: - - 2.4.3 - - 2.5.0 - -services: - - redis-server - -install: - - nvm install - - bundle install --path=vendor/bundle --with pam_authentication --without development production --retry=3 --jobs=16 - - yarn install - -before_script: - - ./bin/rails parallel:create parallel:load_schema parallel:prepare assets:precompile - -script: - - travis_retry bundle exec parallel_test spec/ --group-by filesize --type rspec - - yarn run test:jest - - bundle exec i18n-tasks check-normalized && bundle exec i18n-tasks unused From 4e35ce82691dff9cd81dae2f0e2f566f6f3ef85c Mon Sep 17 00:00:00 2001 From: unarist Date: Fri, 20 Apr 2018 21:04:16 +0900 Subject: [PATCH 111/442] Fix Esc hotkey behavior (#7199) This fixes following cases which causes hotkey action accidentally: * hitting Esc key to cancel text composition (mostly in CJK) Although events on cancelling composition are still heavily browser / input method dependent, but this implementation would covers current UI Events spec and some exceptions. * hitting Esc key to close autocomplete suggestions This PR changes to use keydown event instead of keyup event as well as other hotkeys. --- .../components/autosuggest_textarea.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/app/javascript/mastodon/components/autosuggest_textarea.js b/app/javascript/mastodon/components/autosuggest_textarea.js index 34904194f..5474771c9 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.js +++ b/app/javascript/mastodon/components/autosuggest_textarea.js @@ -86,7 +86,9 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { switch(e.key) { case 'Escape': - if (!suggestionsHidden) { + if (suggestions.size === 0 || suggestionsHidden) { + document.querySelector('.ui').parentElement.focus(); + } else { e.preventDefault(); this.setState({ suggestionsHidden: true }); } @@ -125,16 +127,6 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { this.props.onKeyDown(e); } - onKeyUp = e => { - if (e.key === 'Escape' && this.state.suggestionsHidden) { - document.querySelector('.ui').parentElement.focus(); - } - - if (this.props.onKeyUp) { - this.props.onKeyUp(e); - } - } - onBlur = () => { this.setState({ suggestionsHidden: true }); } @@ -186,7 +178,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { } render () { - const { value, suggestions, disabled, placeholder, autoFocus } = this.props; + const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus } = this.props; const { suggestionsHidden } = this.state; const style = { direction: 'ltr' }; @@ -208,7 +200,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { value={value} onChange={this.onChange} onKeyDown={this.onKeyDown} - onKeyUp={this.onKeyUp} + onKeyUp={onKeyUp} onBlur={this.onBlur} onPaste={this.onPaste} style={style} From ee2e0f694a2ebd403834e55192897d5995a20cf4 Mon Sep 17 00:00:00 2001 From: mayaeh Date: Fri, 20 Apr 2018 21:58:33 +0900 Subject: [PATCH 112/442] Fix #6157: boosting own private toots (#7200) * Fix boosting own private toots. * Run yarn manage:translations and update Japanese translations. --- app/javascript/mastodon/components/dropdown_menu.js | 2 +- app/javascript/mastodon/components/status_action_bar.js | 4 +++- .../mastodon/features/status/components/action_bar.js | 4 +++- app/javascript/mastodon/locales/en.json | 1 + app/javascript/mastodon/locales/ja.json | 9 +++++---- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.js index c5c6f73b3..982d34718 100644 --- a/app/javascript/mastodon/components/dropdown_menu.js +++ b/app/javascript/mastodon/components/dropdown_menu.js @@ -63,7 +63,7 @@ class DropdownMenu extends React.PureComponent { if (typeof action === 'function') { e.preventDefault(); - action(); + action(e); } else if (to) { e.preventDefault(); this.context.router.history.push(to); diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index e58625582..d605dbc8a 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -153,7 +153,9 @@ export default class StatusActionBar extends ImmutablePureComponent { if (publicStatus) { menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick }); } else { - menu.push({ text: intl.formatMessage(status.get('reblog') ? messages.reblog_private : messages.cancel_reblog_private), action: this.handleReblogClick }); + if (status.get('visibility') === 'private') { + menu.push({ text: intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleReblogClick }); + } } menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js index fc34c8cdc..bb9b75505 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.js +++ b/app/javascript/mastodon/features/status/components/action_bar.js @@ -123,7 +123,9 @@ export default class ActionBar extends React.PureComponent { if (publicStatus) { menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick }); } else { - menu.push({ text: intl.formatMessage(status.get('reblog') ? messages.reblog_private : messages.cancel_reblog_private), action: this.handleReblogClick }); + if (status.get('visibility') === 'private') { + menu.push({ text: intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleReblogClick }); + } } menu.push(null); diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index d8bd7e3e0..98d6c0dd7 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index a4aa06a06..23223cac3 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -101,7 +101,7 @@ "emoji_button.symbols": "記号", "emoji_button.travel": "旅行と場所", "empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "ダイレクトメッセージはまだありません。ダイレクトメッセージをやりとりすると、ここに表示されます。", "empty_column.hashtag": "このハッシュタグはまだ使われていません。", "empty_column.home": "まだ誰もフォローしていません。{public}を見に行くか、検索を使って他のユーザーを見つけましょう。", "empty_column.home.public_timeline": "連合タイムライン", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "メンション", "keyboard_shortcuts.reply": "返信", "keyboard_shortcuts.search": "検索欄に移動", + "keyboard_shortcuts.toggle_hidden": "CWで隠れた文を見る/隠す", "keyboard_shortcuts.toot": "新規トゥート", "keyboard_shortcuts.unfocus": "トゥート入力欄・検索欄から離れる", "keyboard_shortcuts.up": "カラム内一つ上に移動", @@ -156,7 +157,7 @@ "mute_modal.hide_notifications": "このユーザーからの通知を隠しますか?", "navigation_bar.blocks": "ブロックしたユーザー", "navigation_bar.community_timeline": "ローカルタイムライン", - "navigation_bar.direct": "Direct messages", + "navigation_bar.direct": "ダイレクトメッセージ", "navigation_bar.domain_blocks": "非表示にしたドメイン", "navigation_bar.edit_profile": "プロフィールを編集", "navigation_bar.favourites": "お気に入り", @@ -241,7 +242,7 @@ "search_results.total": "{count, number}件の結果", "standalone.public_title": "今こんな話をしています...", "status.block": "@{name}さんをブロック", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "ブースト解除", "status.cannot_reblog": "この投稿はブーストできません", "status.delete": "削除", "status.direct": "@{name}さんにダイレクトメッセージ", @@ -257,7 +258,7 @@ "status.pin": "プロフィールに固定表示", "status.pinned": "固定されたトゥート", "status.reblog": "ブースト", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "ブースト", "status.reblogged_by": "{name}さんがブースト", "status.reply": "返信", "status.replyAll": "全員に返信", From 23106844a10606dd0e04c8382281d5ff80d4bdd9 Mon Sep 17 00:00:00 2001 From: TakesxiSximada Date: Sat, 21 Apr 2018 01:14:21 +0900 Subject: [PATCH 113/442] Fix the hot key (j, k) does not function correctly when there is a pinned toot in account timeline. (#7202) * Fix the hot key (j, k) does not function correctly when there is a pinned toot in account timeline. * Fix typo * Add custom attribute prefix --- app/javascript/mastodon/components/status.js | 10 +++++----- .../mastodon/components/status_list.js | 20 +++++++++++++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index e5f7c9399..402d558c4 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -114,12 +114,12 @@ export default class Status extends ImmutablePureComponent { this.context.router.history.push(`/accounts/${this._properStatus().getIn(['account', 'id'])}`); } - handleHotkeyMoveUp = () => { - this.props.onMoveUp(this.props.status.get('id')); + handleHotkeyMoveUp = e => { + this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured')); } - handleHotkeyMoveDown = () => { - this.props.onMoveDown(this.props.status.get('id')); + handleHotkeyMoveDown = e => { + this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured')); } handleHotkeyToggleHidden = () => { @@ -233,7 +233,7 @@ export default class Status extends ImmutablePureComponent { return ( -
    +
    {prepend}
    diff --git a/app/javascript/mastodon/components/status_list.js b/app/javascript/mastodon/components/status_list.js index c98d4564e..0c971ceb0 100644 --- a/app/javascript/mastodon/components/status_list.js +++ b/app/javascript/mastodon/components/status_list.js @@ -30,13 +30,25 @@ export default class StatusList extends ImmutablePureComponent { trackScroll: true, }; - handleMoveUp = id => { - const elementIndex = this.props.statusIds.indexOf(id) - 1; + getFeaturedStatusCount = () => { + return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0; + } + + getCurrentStatusIndex = (id, featured) => { + if (featured) { + return this.props.featuredStatusIds.indexOf(id); + } else { + return this.props.statusIds.indexOf(id) + this.getFeaturedStatusCount(); + } + } + + handleMoveUp = (id, featured) => { + const elementIndex = this.getCurrentStatusIndex(id, featured) - 1; this._selectChild(elementIndex); } - handleMoveDown = id => { - const elementIndex = this.props.statusIds.indexOf(id) + 1; + handleMoveDown = (id, featured) => { + const elementIndex = this.getCurrentStatusIndex(id, featured) + 1; this._selectChild(elementIndex); } From 87e3f0a41d2a2d223c663365a199c01989afc8ed Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sat, 21 Apr 2018 01:14:31 +0900 Subject: [PATCH 114/442] Fix spec for sr-Latn (#7203) --- app/controllers/concerns/localized.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/controllers/concerns/localized.rb b/app/controllers/concerns/localized.rb index abd85ea27..145549bcd 100644 --- a/app/controllers/concerns/localized.rb +++ b/app/controllers/concerns/localized.rb @@ -29,10 +29,14 @@ module Localized end def preferred_locale - http_accept_language.preferred_language_from(I18n.available_locales) + http_accept_language.preferred_language_from(available_locales) end def compatible_locale - http_accept_language.compatible_language_from(I18n.available_locales) + http_accept_language.compatible_language_from(available_locales) + end + + def available_locales + I18n.available_locales.reverse end end From 84214b864c63aee08357a719ab386b8e4ed5b901 Mon Sep 17 00:00:00 2001 From: unarist Date: Sat, 21 Apr 2018 01:36:52 +0900 Subject: [PATCH 115/442] Ignore keyevents during text composition (#7205) KeyboardEvent.key may be physical key name (Escape, Tab, etc.) even in text composition and it causes hotkeys or suggestion selection. So we need to check e.which or e.isComposing. Checking e.which also allows us to avoid Esc key on compositionend in Safari. --- app/javascript/mastodon/components/autosuggest_textarea.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/javascript/mastodon/components/autosuggest_textarea.js b/app/javascript/mastodon/components/autosuggest_textarea.js index 5474771c9..a4f5cf50c 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.js +++ b/app/javascript/mastodon/components/autosuggest_textarea.js @@ -84,6 +84,12 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { return; } + if (e.which === 229 || e.isComposing) { + // Ignore key events during text composition + // e.key may be a name of the physical key even in this case (e.x. Safari / Chrome on Mac) + return; + } + switch(e.key) { case 'Escape': if (suggestions.size === 0 || suggestionsHidden) { From b4382247515728521275002643e4d1b7360bf7fb Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sat, 21 Apr 2018 02:31:30 +0900 Subject: [PATCH 116/442] Introduce rspec-retry (#7206) --- Gemfile | 1 + Gemfile.lock | 3 +++ spec/spec_helper.rb | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/Gemfile b/Gemfile index efafe295c..8055f1561 100644 --- a/Gemfile +++ b/Gemfile @@ -111,6 +111,7 @@ group :test do gem 'microformats', '~> 4.0' gem 'rails-controller-testing', '~> 1.0' gem 'rspec-sidekiq', '~> 3.0' + gem 'rspec-retry', '~> 0.5', require: false gem 'simplecov', '~> 0.14', require: false gem 'webmock', '~> 3.3' gem 'parallel_tests', '~> 2.21' diff --git a/Gemfile.lock b/Gemfile.lock index e799533ae..eeb4bf17e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -495,6 +495,8 @@ GEM rspec-expectations (~> 3.7.0) rspec-mocks (~> 3.7.0) rspec-support (~> 3.7.0) + rspec-retry (0.5.7) + rspec-core (> 3.3) rspec-sidekiq (3.0.3) rspec-core (~> 3.0, >= 3.0.0) sidekiq (>= 2.4.0) @@ -715,6 +717,7 @@ DEPENDENCIES redis-rails (~> 5.0) rqrcode (~> 0.10) rspec-rails (~> 3.7) + rspec-retry (~> 0.5) rspec-sidekiq (~> 3.0) rubocop ruby-oembed (~> 0.12) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a0466dd4b..66ac75ee0 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,4 @@ +require 'rspec/retry' require 'simplecov' GC.disable @@ -11,6 +12,9 @@ end gc_counter = -1 RSpec.configure do |config| + config.verbose_retry = true + config.display_try_failure_messages = true + config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end @@ -25,6 +29,10 @@ RSpec.configure do |config| end end + config.around :each do |ex| + ex.run_with_retry retry: 3 + end + config.before :suite do Chewy.strategy(:bypass) end From 1a27f9f46fb751fa4b3bff9aff3d712bb04b1481 Mon Sep 17 00:00:00 2001 From: goofy-bz Date: Sat, 21 Apr 2018 20:07:25 +0200 Subject: [PATCH 117/442] one grammar fix (#7212) --- app/javascript/mastodon/locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 98c1c43d2..9a9e8ab79 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -105,7 +105,7 @@ "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag.", "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres personnes.", "empty_column.home.public_timeline": "le fil public", - "empty_column.list": "Il n'y a rien dans cette liste pour l'instant. Dès que des personnes de cette liste publierons de nouveaux statuts, ils apparaîtront ici.", + "empty_column.list": "Il n'y a rien dans cette liste pour l'instant. Dès que des personnes de cette liste publieront de nouveaux statuts, ils apparaîtront ici.", "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres personnes pour débuter la conversation.", "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres instances pour remplir le fil public", "follow_request.authorize": "Accepter", From bfe26ef67b6ee2ce4a4ca05565cd383074de3a8b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 21 Apr 2018 21:34:36 +0200 Subject: [PATCH 118/442] Force convert to JPG for preview card thumbnails to avoid animations (#7109) * Force convert to JPG for preview card thumbnails to avoid animations Fix #7093 * Conditionally convert to JPG only if original is GIF Coalesce and strip on all formats to ensure no animated APNGs --- app/models/preview_card.rb | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb index 0c82f06ce..0ffa6b10f 100644 --- a/app/models/preview_card.rb +++ b/app/models/preview_card.rb @@ -34,7 +34,7 @@ class PreviewCard < ApplicationRecord has_and_belongs_to_many :statuses - has_attached_file :image, styles: { original: { geometry: '400x400>', file_geometry_parser: FastGeometryParser } }, convert_options: { all: '-quality 80 -strip' } + has_attached_file :image, styles: ->(f) { image_styles(f) }, convert_options: { all: '-quality 80 -strip' } include Attachmentable @@ -52,6 +52,23 @@ class PreviewCard < ApplicationRecord save! end + class << self + private + + def image_styles(f) + styles = { + original: { + geometry: '400x400>', + file_geometry_parser: FastGeometryParser, + convert_options: '-coalesce -strip', + }, + } + + styles[:original][:format] = 'jpg' if f.instance.image_content_type == 'image/gif' + styles + end + end + private def extract_dimensions From d10447c3a82d771f8ab61837128b011254894694 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sun, 22 Apr 2018 04:35:07 +0900 Subject: [PATCH 119/442] Use raw status code on have_http_status (#7214) --- spec/controllers/about_controller_spec.rb | 6 +++--- spec/controllers/accounts_controller_spec.rb | 2 +- .../activitypub/outboxes_controller_spec.rb | 2 +- .../admin/accounts_controller_spec.rb | 4 ++-- .../admin/change_email_controller_spec.rb | 2 +- .../admin/confirmations_controller_spec.rb | 4 ++-- .../admin/domain_blocks_controller_spec.rb | 6 +++--- .../email_domain_blocks_controller_spec.rb | 4 ++-- .../admin/instances_controller_spec.rb | 2 +- .../reported_statuses_controller_spec.rb | 2 +- .../admin/reports_controller_spec.rb | 8 ++++---- .../admin/settings_controller_spec.rb | 2 +- .../admin/statuses_controller_spec.rb | 6 +++--- .../admin/subscriptions_controller_spec.rb | 2 +- spec/controllers/api/base_controller_spec.rb | 2 +- .../controllers/api/oembed_controller_spec.rb | 2 +- spec/controllers/api/push_controller_spec.rb | 4 ++-- .../controllers/api/salmon_controller_spec.rb | 2 +- .../api/subscriptions_controller_spec.rb | 6 +++--- .../accounts/credentials_controller_spec.rb | 4 ++-- .../follower_accounts_controller_spec.rb | 2 +- .../following_accounts_controller_spec.rb | 2 +- .../api/v1/accounts/lists_controller_spec.rb | 2 +- .../accounts/relationships_controller_spec.rb | 4 ++-- .../api/v1/accounts/search_controller_spec.rb | 2 +- .../v1/accounts/statuses_controller_spec.rb | 8 ++++---- .../api/v1/accounts_controller_spec.rb | 18 ++++++++--------- .../v1/apps/credentials_controller_spec.rb | 2 +- .../api/v1/apps_controller_spec.rb | 2 +- .../api/v1/blocks_controller_spec.rb | 2 +- .../api/v1/custom_emojis_controller_spec.rb | 2 +- .../api/v1/domain_blocks_controller_spec.rb | 6 +++--- .../api/v1/follow_requests_controller_spec.rb | 6 +++--- .../api/v1/follows_controller_spec.rb | 4 ++-- .../api/v1/instances_controller_spec.rb | 2 +- .../api/v1/lists/accounts_controller_spec.rb | 6 +++--- .../api/v1/lists_controller_spec.rb | 10 +++++----- .../api/v1/media_controller_spec.rb | 8 ++++---- .../api/v1/mutes_controller_spec.rb | 2 +- .../api/v1/notifications_controller_spec.rb | 10 +++++----- .../api/v1/reports_controller_spec.rb | 4 ++-- .../api/v1/search_controller_spec.rb | 2 +- .../favourited_by_accounts_controller_spec.rb | 6 +++--- .../v1/statuses/favourites_controller_spec.rb | 4 ++-- .../api/v1/statuses/mutes_controller_spec.rb | 4 ++-- .../api/v1/statuses/pins_controller_spec.rb | 4 ++-- .../reblogged_by_accounts_controller_spec.rb | 6 +++--- .../v1/statuses/reblogs_controller_spec.rb | 4 ++-- .../api/v1/statuses_controller_spec.rb | 20 +++++++++---------- .../api/v1/timelines/home_controller_spec.rb | 2 +- .../api/v1/timelines/list_controller_spec.rb | 2 +- .../v1/timelines/public_controller_spec.rb | 6 +++--- .../api/v1/timelines/tag_controller_spec.rb | 4 ++-- .../api/web/settings_controller_spec.rb | 2 +- .../application_controller_spec.rb | 6 +++--- .../auth/confirmations_controller_spec.rb | 2 +- .../auth/passwords_controller_spec.rb | 4 ++-- .../auth/registrations_controller_spec.rb | 6 +++--- .../auth/sessions_controller_spec.rb | 2 +- .../authorize_follows_controller_spec.rb | 4 ++-- .../account_controller_concern_spec.rb | 2 +- .../export_controller_concern_spec.rb | 2 +- .../follower_accounts_controller_spec.rb | 2 +- .../following_accounts_controller_spec.rb | 2 +- spec/controllers/manifests_controller_spec.rb | 2 +- spec/controllers/media_controller_spec.rb | 6 +++--- .../oauth/authorizations_controller_spec.rb | 2 +- ...authorized_applications_controller_spec.rb | 2 +- .../remote_follow_controller_spec.rb | 4 ++-- .../settings/applications_controller_spec.rb | 10 +++++----- .../settings/deletes_controller_spec.rb | 2 +- .../settings/exports_controller_spec.rb | 2 +- .../follower_domains_controller_spec.rb | 2 +- .../settings/imports_controller_spec.rb | 2 +- .../settings/notifications_controller_spec.rb | 2 +- .../settings/preferences_controller_spec.rb | 2 +- .../settings/profiles_controller_spec.rb | 2 +- .../confirmations_controller_spec.rb | 4 ++-- .../recovery_codes_controller_spec.rb | 2 +- ..._factor_authentications_controller_spec.rb | 4 ++-- spec/controllers/statuses_controller_spec.rb | 2 +- .../stream_entries_controller_spec.rb | 2 +- spec/controllers/tags_controller_spec.rb | 4 ++-- .../well_known/host_meta_controller_spec.rb | 2 +- .../well_known/webfinger_controller_spec.rb | 6 +++--- spec/requests/host_meta_request_spec.rb | 2 +- spec/requests/webfinger_request_spec.rb | 10 +++++----- 87 files changed, 176 insertions(+), 176 deletions(-) diff --git a/spec/controllers/about_controller_spec.rb b/spec/controllers/about_controller_spec.rb index c2c34d34a..2089b3b16 100644 --- a/spec/controllers/about_controller_spec.rb +++ b/spec/controllers/about_controller_spec.rb @@ -17,7 +17,7 @@ RSpec.describe AboutController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -35,7 +35,7 @@ RSpec.describe AboutController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -49,7 +49,7 @@ RSpec.describe AboutController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb index a8ade790c..18c249c07 100644 --- a/spec/controllers/accounts_controller_spec.rb +++ b/spec/controllers/accounts_controller_spec.rb @@ -40,7 +40,7 @@ RSpec.describe AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns correct format' do diff --git a/spec/controllers/activitypub/outboxes_controller_spec.rb b/spec/controllers/activitypub/outboxes_controller_spec.rb index a25998021..47460b22c 100644 --- a/spec/controllers/activitypub/outboxes_controller_spec.rb +++ b/spec/controllers/activitypub/outboxes_controller_spec.rb @@ -13,7 +13,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns application/activity+json' do diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb index 8be27d866..ff9dbbfb8 100644 --- a/spec/controllers/admin/accounts_controller_spec.rb +++ b/spec/controllers/admin/accounts_controller_spec.rb @@ -63,7 +63,7 @@ RSpec.describe Admin::AccountsController, type: :controller do it 'returns http success' do get :index - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -72,7 +72,7 @@ RSpec.describe Admin::AccountsController, type: :controller do it 'returns http success' do get :show, params: { id: account.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/admin/change_email_controller_spec.rb b/spec/controllers/admin/change_email_controller_spec.rb index 50f94f835..31df0f0fc 100644 --- a/spec/controllers/admin/change_email_controller_spec.rb +++ b/spec/controllers/admin/change_email_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Admin::ChangeEmailsController, type: :controller do get :show, params: { account_id: account.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/admin/confirmations_controller_spec.rb b/spec/controllers/admin/confirmations_controller_spec.rb index 3f2b28c0e..7c8034964 100644 --- a/spec/controllers/admin/confirmations_controller_spec.rb +++ b/spec/controllers/admin/confirmations_controller_spec.rb @@ -20,14 +20,14 @@ RSpec.describe Admin::ConfirmationsController, type: :controller do it 'raises an error when there is no account' do post :create, params: { account_id: 'fake' } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end it 'raises an error when there is no user' do account = Fabricate(:account, user: nil) post :create, params: { account_id: account.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/admin/domain_blocks_controller_spec.rb b/spec/controllers/admin/domain_blocks_controller_spec.rb index b9e73c04b..79e7fea42 100644 --- a/spec/controllers/admin/domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/domain_blocks_controller_spec.rb @@ -23,7 +23,7 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do assigned = assigns(:domain_blocks) expect(assigned.count).to eq 1 expect(assigned.klass).to be DomainBlock - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -32,7 +32,7 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do get :new expect(assigns(:domain_block)).to be_instance_of(DomainBlock) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -41,7 +41,7 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do domain_block = Fabricate(:domain_block) get :show, params: { id: domain_block.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/admin/email_domain_blocks_controller_spec.rb b/spec/controllers/admin/email_domain_blocks_controller_spec.rb index 295de9073..133d38ff1 100644 --- a/spec/controllers/admin/email_domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/email_domain_blocks_controller_spec.rb @@ -25,7 +25,7 @@ RSpec.describe Admin::EmailDomainBlocksController, type: :controller do assigned = assigns(:email_domain_blocks) expect(assigned.count).to eq 1 expect(assigned.klass).to be EmailDomainBlock - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -34,7 +34,7 @@ RSpec.describe Admin::EmailDomainBlocksController, type: :controller do get :new expect(assigns(:email_domain_block)).to be_instance_of(EmailDomainBlock) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/admin/instances_controller_spec.rb b/spec/controllers/admin/instances_controller_spec.rb index f57e3fa97..412b81443 100644 --- a/spec/controllers/admin/instances_controller_spec.rb +++ b/spec/controllers/admin/instances_controller_spec.rb @@ -26,7 +26,7 @@ RSpec.describe Admin::InstancesController, type: :controller do expect(instances.size).to eq 1 expect(instances[0].domain).to eq 'less.popular' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/admin/reported_statuses_controller_spec.rb b/spec/controllers/admin/reported_statuses_controller_spec.rb index 41e032fc7..29957ed37 100644 --- a/spec/controllers/admin/reported_statuses_controller_spec.rb +++ b/spec/controllers/admin/reported_statuses_controller_spec.rb @@ -84,7 +84,7 @@ describe Admin::ReportedStatusesController do allow(RemovalWorker).to receive(:perform_async) delete :destroy, params: { report_id: report, id: status } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(RemovalWorker). to have_received(:perform_async).with(status.id) end diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index 9be298df6..e50c02a72 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -18,7 +18,7 @@ describe Admin::ReportsController do reports = assigns(:reports).to_a expect(reports.size).to eq 1 expect(reports[0]).to eq specified - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns http success with resolved filter' do @@ -31,7 +31,7 @@ describe Admin::ReportsController do expect(reports.size).to eq 1 expect(reports[0]).to eq specified - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -42,7 +42,7 @@ describe Admin::ReportsController do get :show, params: { id: report } expect(assigns(:report)).to eq report - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -52,7 +52,7 @@ describe Admin::ReportsController do report = Fabricate(:report) put :update, params: { id: report, outcome: 'unknown' } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end diff --git a/spec/controllers/admin/settings_controller_spec.rb b/spec/controllers/admin/settings_controller_spec.rb index 609bc762b..eaf99679a 100644 --- a/spec/controllers/admin/settings_controller_spec.rb +++ b/spec/controllers/admin/settings_controller_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Admin::SettingsController, type: :controller do it 'returns http success' do get :edit - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/admin/statuses_controller_spec.rb b/spec/controllers/admin/statuses_controller_spec.rb index 1515e299b..cbaf39786 100644 --- a/spec/controllers/admin/statuses_controller_spec.rb +++ b/spec/controllers/admin/statuses_controller_spec.rb @@ -20,7 +20,7 @@ describe Admin::StatusesController do statuses = assigns(:statuses).to_a expect(statuses.size).to eq 2 - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns http success with media' do @@ -28,7 +28,7 @@ describe Admin::StatusesController do statuses = assigns(:statuses).to_a expect(statuses.size).to eq 1 - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -99,7 +99,7 @@ describe Admin::StatusesController do allow(RemovalWorker).to receive(:perform_async) delete :destroy, params: { account_id: account.id, id: status } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(RemovalWorker). to have_received(:perform_async).with(status.id) end diff --git a/spec/controllers/admin/subscriptions_controller_spec.rb b/spec/controllers/admin/subscriptions_controller_spec.rb index eb6f12b16..967152abe 100644 --- a/spec/controllers/admin/subscriptions_controller_spec.rb +++ b/spec/controllers/admin/subscriptions_controller_spec.rb @@ -26,7 +26,7 @@ RSpec.describe Admin::SubscriptionsController, type: :controller do expect(subscriptions.count).to eq 1 expect(subscriptions[0]).to eq specified - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/base_controller_spec.rb b/spec/controllers/api/base_controller_spec.rb index 0c7ca8990..750ccc8cf 100644 --- a/spec/controllers/api/base_controller_spec.rb +++ b/spec/controllers/api/base_controller_spec.rb @@ -23,7 +23,7 @@ describe Api::BaseController do it 'does not protect from forgery' do ActionController::Base.allow_forgery_protection = true post 'success' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/api/oembed_controller_spec.rb b/spec/controllers/api/oembed_controller_spec.rb index 7af4a6a5b..7fee15a35 100644 --- a/spec/controllers/api/oembed_controller_spec.rb +++ b/spec/controllers/api/oembed_controller_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Api::OEmbedController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/push_controller_spec.rb b/spec/controllers/api/push_controller_spec.rb index 647698bd1..d769d8554 100644 --- a/spec/controllers/api/push_controller_spec.rb +++ b/spec/controllers/api/push_controller_spec.rb @@ -23,7 +23,7 @@ RSpec.describe Api::PushController, type: :controller do '3600', nil ) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(202) end end @@ -43,7 +43,7 @@ RSpec.describe Api::PushController, type: :controller do account, 'https://callback.host/api', ) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(202) end end diff --git a/spec/controllers/api/salmon_controller_spec.rb b/spec/controllers/api/salmon_controller_spec.rb index 8af8b83a8..5f01f8073 100644 --- a/spec/controllers/api/salmon_controller_spec.rb +++ b/spec/controllers/api/salmon_controller_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Api::SalmonController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(202) end it 'creates remote account' do diff --git a/spec/controllers/api/subscriptions_controller_spec.rb b/spec/controllers/api/subscriptions_controller_spec.rb index d90da9e32..48eb1fc64 100644 --- a/spec/controllers/api/subscriptions_controller_spec.rb +++ b/spec/controllers/api/subscriptions_controller_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Api::SubscriptionsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'echoes back the challenge' do @@ -27,7 +27,7 @@ RSpec.describe Api::SubscriptionsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end @@ -59,7 +59,7 @@ RSpec.describe Api::SubscriptionsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates statuses for feed' do diff --git a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb index 87fce64eb..9a52fd14c 100644 --- a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb @@ -14,7 +14,7 @@ describe Api::V1::Accounts::CredentialsController do describe 'GET #show' do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -36,7 +36,7 @@ describe Api::V1::Accounts::CredentialsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates account info' do diff --git a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb index 33982cb8f..b47af4963 100644 --- a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb @@ -15,7 +15,7 @@ describe Api::V1::Accounts::FollowerAccountsController do it 'returns http success' do get :index, params: { account_id: user.account.id, limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb index e22f54a31..29fd7cd5b 100644 --- a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb @@ -15,7 +15,7 @@ describe Api::V1::Accounts::FollowingAccountsController do it 'returns http success' do get :index, params: { account_id: user.account.id, limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/accounts/lists_controller_spec.rb b/spec/controllers/api/v1/accounts/lists_controller_spec.rb index 0a372f65b..df9fe0e34 100644 --- a/spec/controllers/api/v1/accounts/lists_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/lists_controller_spec.rb @@ -17,7 +17,7 @@ describe Api::V1::Accounts::ListsController do describe 'GET #index' do it 'returns http success' do get :index, params: { account_id: account.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/accounts/relationships_controller_spec.rb b/spec/controllers/api/v1/accounts/relationships_controller_spec.rb index e0de790c8..7e350da7e 100644 --- a/spec/controllers/api/v1/accounts/relationships_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/relationships_controller_spec.rb @@ -25,7 +25,7 @@ describe Api::V1::Accounts::RelationshipsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns JSON with correct data' do @@ -43,7 +43,7 @@ describe Api::V1::Accounts::RelationshipsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns JSON with correct data' do diff --git a/spec/controllers/api/v1/accounts/search_controller_spec.rb b/spec/controllers/api/v1/accounts/search_controller_spec.rb index 42cc3f64d..dbc4b9f3e 100644 --- a/spec/controllers/api/v1/accounts/search_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/search_controller_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Api::V1::Accounts::SearchController, type: :controller do it 'returns http success' do get :show, params: { q: 'query' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb index c49a77ac3..09bb46937 100644 --- a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb @@ -15,7 +15,7 @@ describe Api::V1::Accounts::StatusesController do it 'returns http success' do get :index, params: { account_id: user.account.id, limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end @@ -23,7 +23,7 @@ describe Api::V1::Accounts::StatusesController do it 'returns http success' do get :index, params: { account_id: user.account.id, only_media: true } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -35,7 +35,7 @@ describe Api::V1::Accounts::StatusesController do it 'returns http success' do get :index, params: { account_id: user.account.id, exclude_replies: true } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -47,7 +47,7 @@ describe Api::V1::Accounts::StatusesController do it 'returns http success' do get :index, params: { account_id: user.account.id, pinned: true } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/controllers/api/v1/accounts_controller_spec.rb index 053c53e5a..7a9e0f8e4 100644 --- a/spec/controllers/api/v1/accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts_controller_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do describe 'GET #show' do it 'returns http success' do get :show, params: { id: user.account.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -28,7 +28,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do let(:locked) { false } it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns JSON with following=true and requested=false' do @@ -47,7 +47,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do let(:locked) { true } it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns JSON with following=false and requested=true' do @@ -72,7 +72,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes the following relation between user and target user' do @@ -89,7 +89,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes the following relation between user and target user' do @@ -110,7 +110,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes the blocking relation between user and target user' do @@ -127,7 +127,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'does not remove the following relation between user and target user' do @@ -152,7 +152,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'does not remove the following relation between user and target user' do @@ -177,7 +177,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes the muting relation between user and target user' do diff --git a/spec/controllers/api/v1/apps/credentials_controller_spec.rb b/spec/controllers/api/v1/apps/credentials_controller_spec.rb index 38f2a4e10..0f811d5f3 100644 --- a/spec/controllers/api/v1/apps/credentials_controller_spec.rb +++ b/spec/controllers/api/v1/apps/credentials_controller_spec.rb @@ -16,7 +16,7 @@ describe Api::V1::Apps::CredentialsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'does not contain client credentials' do diff --git a/spec/controllers/api/v1/apps_controller_spec.rb b/spec/controllers/api/v1/apps_controller_spec.rb index 1ad9d6383..60a4c3b41 100644 --- a/spec/controllers/api/v1/apps_controller_spec.rb +++ b/spec/controllers/api/v1/apps_controller_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Api::V1::AppsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates an OAuth app' do diff --git a/spec/controllers/api/v1/blocks_controller_spec.rb b/spec/controllers/api/v1/blocks_controller_spec.rb index 9b2bbdf0e..eff5fb9da 100644 --- a/spec/controllers/api/v1/blocks_controller_spec.rb +++ b/spec/controllers/api/v1/blocks_controller_spec.rb @@ -47,7 +47,7 @@ RSpec.describe Api::V1::BlocksController, type: :controller do it 'returns http success' do get :index - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/custom_emojis_controller_spec.rb b/spec/controllers/api/v1/custom_emojis_controller_spec.rb index 9f3522812..fe8daa7c5 100644 --- a/spec/controllers/api/v1/custom_emojis_controller_spec.rb +++ b/spec/controllers/api/v1/custom_emojis_controller_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Api::V1::CustomEmojisController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/domain_blocks_controller_spec.rb b/spec/controllers/api/v1/domain_blocks_controller_spec.rb index 3713931dc..bae4612a2 100644 --- a/spec/controllers/api/v1/domain_blocks_controller_spec.rb +++ b/spec/controllers/api/v1/domain_blocks_controller_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Api::V1::DomainBlocksController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns blocked domains' do @@ -31,7 +31,7 @@ RSpec.describe Api::V1::DomainBlocksController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates a domain block' do @@ -45,7 +45,7 @@ RSpec.describe Api::V1::DomainBlocksController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'deletes a domain block' do diff --git a/spec/controllers/api/v1/follow_requests_controller_spec.rb b/spec/controllers/api/v1/follow_requests_controller_spec.rb index 51df006a2..3c0b84af8 100644 --- a/spec/controllers/api/v1/follow_requests_controller_spec.rb +++ b/spec/controllers/api/v1/follow_requests_controller_spec.rb @@ -18,7 +18,7 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -28,7 +28,7 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'allows follower to follow' do @@ -42,7 +42,7 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes follow request' do diff --git a/spec/controllers/api/v1/follows_controller_spec.rb b/spec/controllers/api/v1/follows_controller_spec.rb index ea9e76d68..38badb80a 100644 --- a/spec/controllers/api/v1/follows_controller_spec.rb +++ b/spec/controllers/api/v1/follows_controller_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Api::V1::FollowsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates account for remote user' do @@ -45,7 +45,7 @@ RSpec.describe Api::V1::FollowsController, type: :controller do it 'returns http success if already following, too' do post :create, params: { uri: 'gargron@quitter.no' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/instances_controller_spec.rb b/spec/controllers/api/v1/instances_controller_spec.rb index eba233b05..7397d25d6 100644 --- a/spec/controllers/api/v1/instances_controller_spec.rb +++ b/spec/controllers/api/v1/instances_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Api::V1::InstancesController, type: :controller do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/lists/accounts_controller_spec.rb b/spec/controllers/api/v1/lists/accounts_controller_spec.rb index 953e5909d..c37a481d6 100644 --- a/spec/controllers/api/v1/lists/accounts_controller_spec.rb +++ b/spec/controllers/api/v1/lists/accounts_controller_spec.rb @@ -17,7 +17,7 @@ describe Api::V1::Lists::AccountsController do it 'returns http success' do get :show, params: { list_id: list.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -30,7 +30,7 @@ describe Api::V1::Lists::AccountsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'adds account to the list' do @@ -44,7 +44,7 @@ describe Api::V1::Lists::AccountsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes account from the list' do diff --git a/spec/controllers/api/v1/lists_controller_spec.rb b/spec/controllers/api/v1/lists_controller_spec.rb index be08c221f..213429581 100644 --- a/spec/controllers/api/v1/lists_controller_spec.rb +++ b/spec/controllers/api/v1/lists_controller_spec.rb @@ -12,14 +12,14 @@ RSpec.describe Api::V1::ListsController, type: :controller do describe 'GET #index' do it 'returns http success' do get :index - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end describe 'GET #show' do it 'returns http success' do get :show, params: { id: list.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -29,7 +29,7 @@ RSpec.describe Api::V1::ListsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates list' do @@ -44,7 +44,7 @@ RSpec.describe Api::V1::ListsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the list' do @@ -58,7 +58,7 @@ RSpec.describe Api::V1::ListsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'deletes the list' do diff --git a/spec/controllers/api/v1/media_controller_spec.rb b/spec/controllers/api/v1/media_controller_spec.rb index 0e494638f..ce260eb90 100644 --- a/spec/controllers/api/v1/media_controller_spec.rb +++ b/spec/controllers/api/v1/media_controller_spec.rb @@ -30,7 +30,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do end it 'returns http 422' do - expect(response).to have_http_status(:error) + expect(response).to have_http_status(500) end end end @@ -41,7 +41,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates a media attachment' do @@ -63,7 +63,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates a media attachment' do @@ -85,7 +85,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do end xit 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end xit 'creates a media attachment' do diff --git a/spec/controllers/api/v1/mutes_controller_spec.rb b/spec/controllers/api/v1/mutes_controller_spec.rb index 97d6c2773..dc4a9753a 100644 --- a/spec/controllers/api/v1/mutes_controller_spec.rb +++ b/spec/controllers/api/v1/mutes_controller_spec.rb @@ -15,7 +15,7 @@ RSpec.describe Api::V1::MutesController, type: :controller do it 'returns http success' do get :index, params: { limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/notifications_controller_spec.rb b/spec/controllers/api/v1/notifications_controller_spec.rb index f493d0d38..2e6163fcd 100644 --- a/spec/controllers/api/v1/notifications_controller_spec.rb +++ b/spec/controllers/api/v1/notifications_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do notification = Fabricate(:notification, account: user.account) get :show, params: { id: notification.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -25,7 +25,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do notification = Fabricate(:notification, account: user.account) post :dismiss, params: { id: notification.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound) end end @@ -36,7 +36,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do post :clear expect(notification.account.reload.notifications).to be_empty - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -56,7 +56,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'includes reblog' do @@ -82,7 +82,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'includes reblog' do diff --git a/spec/controllers/api/v1/reports_controller_spec.rb b/spec/controllers/api/v1/reports_controller_spec.rb index 1eb5a4353..1e1ef9308 100644 --- a/spec/controllers/api/v1/reports_controller_spec.rb +++ b/spec/controllers/api/v1/reports_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Api::V1::ReportsController, type: :controller do it 'returns http success' do get :index - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -31,7 +31,7 @@ RSpec.describe Api::V1::ReportsController, type: :controller do it 'creates a report' do expect(status.reload.account.targeted_reports).not_to be_empty - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'sends e-mails to admins' do diff --git a/spec/controllers/api/v1/search_controller_spec.rb b/spec/controllers/api/v1/search_controller_spec.rb index ff0c254b1..024703867 100644 --- a/spec/controllers/api/v1/search_controller_spec.rb +++ b/spec/controllers/api/v1/search_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Api::V1::SearchController, type: :controller do it 'returns http success' do get :index, params: { q: 'test' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb b/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb index 556731d57..c873e05dd 100644 --- a/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control it 'returns http success' do get :index, params: { status_id: status.id, limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end @@ -43,7 +43,7 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control it 'returns http unautharized' do get :index, params: { status_id: status.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end @@ -58,7 +58,7 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control it 'returns http success' do get :index, params: { status_id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/statuses/favourites_controller_spec.rb b/spec/controllers/api/v1/statuses/favourites_controller_spec.rb index aba7cd458..53f602616 100644 --- a/spec/controllers/api/v1/statuses/favourites_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/favourites_controller_spec.rb @@ -22,7 +22,7 @@ describe Api::V1::Statuses::FavouritesController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the favourites count' do @@ -51,7 +51,7 @@ describe Api::V1::Statuses::FavouritesController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the favourites count' do diff --git a/spec/controllers/api/v1/statuses/mutes_controller_spec.rb b/spec/controllers/api/v1/statuses/mutes_controller_spec.rb index 54c594e92..13b4625d1 100644 --- a/spec/controllers/api/v1/statuses/mutes_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/mutes_controller_spec.rb @@ -22,7 +22,7 @@ describe Api::V1::Statuses::MutesController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates a conversation mute' do @@ -39,7 +39,7 @@ describe Api::V1::Statuses::MutesController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'destroys the conversation mute' do diff --git a/spec/controllers/api/v1/statuses/pins_controller_spec.rb b/spec/controllers/api/v1/statuses/pins_controller_spec.rb index 79005c9de..8f5b0800b 100644 --- a/spec/controllers/api/v1/statuses/pins_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/pins_controller_spec.rb @@ -22,7 +22,7 @@ describe Api::V1::Statuses::PinsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the pinned attribute' do @@ -46,7 +46,7 @@ describe Api::V1::Statuses::PinsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the pinned attribute' do diff --git a/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb b/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb index ba022a96e..9c0c2b60c 100644 --- a/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll it 'returns http success' do get :index, params: { status_id: status.id, limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end @@ -42,7 +42,7 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll it 'returns http unautharized' do get :index, params: { status_id: status.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end @@ -57,7 +57,7 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll it 'returns http success' do get :index, params: { status_id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb b/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb index 7417ff672..e60f8da2a 100644 --- a/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb @@ -22,7 +22,7 @@ describe Api::V1::Statuses::ReblogsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the reblogs count' do @@ -51,7 +51,7 @@ describe Api::V1::Statuses::ReblogsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the reblogs count' do diff --git a/spec/controllers/api/v1/statuses_controller_spec.rb b/spec/controllers/api/v1/statuses_controller_spec.rb index a36265395..27e4f4eb2 100644 --- a/spec/controllers/api/v1/statuses_controller_spec.rb +++ b/spec/controllers/api/v1/statuses_controller_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do it 'returns http success' do get :show, params: { id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -30,7 +30,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do it 'returns http success' do get :context, params: { id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -40,7 +40,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -52,7 +52,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes the status' do @@ -72,7 +72,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do describe 'GET #show' do it 'returns http unautharized' do get :show, params: { id: status.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end @@ -83,14 +83,14 @@ RSpec.describe Api::V1::StatusesController, type: :controller do it 'returns http unautharized' do get :context, params: { id: status.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end describe 'GET #card' do it 'returns http unautharized' do get :card, params: { id: status.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end @@ -101,7 +101,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do describe 'GET #show' do it 'returns http success' do get :show, params: { id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -112,14 +112,14 @@ RSpec.describe Api::V1::StatusesController, type: :controller do it 'returns http success' do get :context, params: { id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end describe 'GET #card' do it 'returns http success' do get :card, params: { id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/timelines/home_controller_spec.rb b/spec/controllers/api/v1/timelines/home_controller_spec.rb index 4d4523520..85b031641 100644 --- a/spec/controllers/api/v1/timelines/home_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/home_controller_spec.rb @@ -23,7 +23,7 @@ describe Api::V1::Timelines::HomeController do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end diff --git a/spec/controllers/api/v1/timelines/list_controller_spec.rb b/spec/controllers/api/v1/timelines/list_controller_spec.rb index 07eba955a..1729217c9 100644 --- a/spec/controllers/api/v1/timelines/list_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/list_controller_spec.rb @@ -24,7 +24,7 @@ describe Api::V1::Timelines::ListController do it 'returns http success' do get :show, params: { id: list.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/timelines/public_controller_spec.rb b/spec/controllers/api/v1/timelines/public_controller_spec.rb index 3acf2e267..68d87bbcb 100644 --- a/spec/controllers/api/v1/timelines/public_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/public_controller_spec.rb @@ -22,7 +22,7 @@ describe Api::V1::Timelines::PublicController do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end @@ -35,7 +35,7 @@ describe Api::V1::Timelines::PublicController do it 'returns http success' do get :show, params: { local: true } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end @@ -48,7 +48,7 @@ describe Api::V1::Timelines::PublicController do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link']).to be_nil end end diff --git a/spec/controllers/api/v1/timelines/tag_controller_spec.rb b/spec/controllers/api/v1/timelines/tag_controller_spec.rb index 6c66ee58e..472779f54 100644 --- a/spec/controllers/api/v1/timelines/tag_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/tag_controller_spec.rb @@ -21,7 +21,7 @@ describe Api::V1::Timelines::TagController do it 'returns http success' do get :show, params: { id: 'test' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end @@ -33,7 +33,7 @@ describe Api::V1::Timelines::TagController do describe 'GET #show' do it 'returns http success' do get :show, params: { id: 'test' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link']).to be_nil end end diff --git a/spec/controllers/api/web/settings_controller_spec.rb b/spec/controllers/api/web/settings_controller_spec.rb index ff211c7b1..815da04c4 100644 --- a/spec/controllers/api/web/settings_controller_spec.rb +++ b/spec/controllers/api/web/settings_controller_spec.rb @@ -13,7 +13,7 @@ describe Api::Web::SettingsController do patch :update, format: :json, params: { data: { 'onboarded' => true } } user.reload - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(user_web_setting.data['onboarded']).to eq('true') end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 3e4d27e05..c6c78d3f7 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -51,7 +51,7 @@ describe ApplicationController, type: :controller do routes.draw { get 'success' => 'anonymous#success' } allow(Rails.env).to receive(:production?).and_return(false) get 'success' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it "forces ssl if Rails.env.production? is 'true'" do @@ -145,13 +145,13 @@ describe ApplicationController, type: :controller do it 'does nothing if not signed in' do get 'success' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'does nothing if user who signed in is not suspended' do sign_in(Fabricate(:user, account: Fabricate(:account, suspended: false))) get 'success' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns http 403 if user who signed in is suspended' do diff --git a/spec/controllers/auth/confirmations_controller_spec.rb b/spec/controllers/auth/confirmations_controller_spec.rb index 80a06c43a..b3af5e0ec 100644 --- a/spec/controllers/auth/confirmations_controller_spec.rb +++ b/spec/controllers/auth/confirmations_controller_spec.rb @@ -7,7 +7,7 @@ describe Auth::ConfirmationsController, type: :controller do it 'returns http success' do @request.env['devise.mapping'] = Devise.mappings[:user] get :new - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/auth/passwords_controller_spec.rb b/spec/controllers/auth/passwords_controller_spec.rb index 992d2e29d..dcfdebb17 100644 --- a/spec/controllers/auth/passwords_controller_spec.rb +++ b/spec/controllers/auth/passwords_controller_spec.rb @@ -9,7 +9,7 @@ describe Auth::PasswordsController, type: :controller do it 'returns http success' do @request.env['devise.mapping'] = Devise.mappings[:user] get :new - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -24,7 +24,7 @@ describe Auth::PasswordsController, type: :controller do context 'with valid reset_password_token' do it 'returns http success' do get :edit, params: { reset_password_token: @token } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb index 97d2c53df..9cfbd481f 100644 --- a/spec/controllers/auth/registrations_controller_spec.rb +++ b/spec/controllers/auth/registrations_controller_spec.rb @@ -35,7 +35,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do request.env["devise.mapping"] = Devise.mappings[:user] sign_in(Fabricate(:user)) get :edit - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -44,7 +44,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do request.env["devise.mapping"] = Devise.mappings[:user] sign_in(Fabricate(:user), scope: :user) post :update - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -63,7 +63,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do it 'returns http success' do Setting.open_registrations = true get :new - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index d5fed17d6..97719a606 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Auth::SessionsController, type: :controller do it 'returns http success' do get :new - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/authorize_follows_controller_spec.rb b/spec/controllers/authorize_follows_controller_spec.rb index b1cbef7ea..52971c724 100644 --- a/spec/controllers/authorize_follows_controller_spec.rb +++ b/spec/controllers/authorize_follows_controller_spec.rb @@ -47,7 +47,7 @@ describe AuthorizeFollowsController do get :show, params: { acct: 'http://example.com' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(assigns(:account)).to eq account end @@ -59,7 +59,7 @@ describe AuthorizeFollowsController do get :show, params: { acct: 'acct:found@hostname' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(assigns(:account)).to eq account end end diff --git a/spec/controllers/concerns/account_controller_concern_spec.rb b/spec/controllers/concerns/account_controller_concern_spec.rb index ae46f9ba6..93685103f 100644 --- a/spec/controllers/concerns/account_controller_concern_spec.rb +++ b/spec/controllers/concerns/account_controller_concern_spec.rb @@ -39,7 +39,7 @@ describe ApplicationController, type: :controller do it 'returns http success' do account = Fabricate(:account) get 'success', params: { account_username: account.username } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/concerns/export_controller_concern_spec.rb b/spec/controllers/concerns/export_controller_concern_spec.rb index 9d6f782b9..6a13db69d 100644 --- a/spec/controllers/concerns/export_controller_concern_spec.rb +++ b/spec/controllers/concerns/export_controller_concern_spec.rb @@ -19,7 +19,7 @@ describe ApplicationController, type: :controller do sign_in user get :index, format: :csv - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'text/csv' expect(response.headers['Content-Disposition']).to eq 'attachment; filename="anonymous.csv"' expect(response.body).to eq user.account.username diff --git a/spec/controllers/follower_accounts_controller_spec.rb b/spec/controllers/follower_accounts_controller_spec.rb index b9b7fef73..3a42a6e18 100644 --- a/spec/controllers/follower_accounts_controller_spec.rb +++ b/spec/controllers/follower_accounts_controller_spec.rb @@ -19,7 +19,7 @@ describe FollowerAccountsController do expect(assigned[0]).to eq follow1 expect(assigned[1]).to eq follow0 - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/following_accounts_controller_spec.rb b/spec/controllers/following_accounts_controller_spec.rb index 55e7265c7..33376365d 100644 --- a/spec/controllers/following_accounts_controller_spec.rb +++ b/spec/controllers/following_accounts_controller_spec.rb @@ -19,7 +19,7 @@ describe FollowingAccountsController do expect(assigned[0]).to eq follow1 expect(assigned[1]).to eq follow0 - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/manifests_controller_spec.rb b/spec/controllers/manifests_controller_spec.rb index 71967e4f0..a549adef3 100644 --- a/spec/controllers/manifests_controller_spec.rb +++ b/spec/controllers/manifests_controller_spec.rb @@ -9,7 +9,7 @@ describe ManifestsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/media_controller_spec.rb b/spec/controllers/media_controller_spec.rb index 5b03899e4..ac44a76f2 100644 --- a/spec/controllers/media_controller_spec.rb +++ b/spec/controllers/media_controller_spec.rb @@ -18,13 +18,13 @@ describe MediaController do media_attachment = Fabricate(:media_attachment, status: nil) get :show, params: { id: media_attachment.to_param } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end it 'raises when shortcode cant be found' do get :show, params: { id: 'missing' } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end it 'raises when not permitted to view' do @@ -33,7 +33,7 @@ describe MediaController do allow_any_instance_of(MediaController).to receive(:authorize).and_raise(ActiveRecord::RecordNotFound) get :show, params: { id: media_attachment.to_param } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb index 5c2a62b48..91c2d03ef 100644 --- a/spec/controllers/oauth/authorizations_controller_spec.rb +++ b/spec/controllers/oauth/authorizations_controller_spec.rb @@ -26,7 +26,7 @@ RSpec.describe Oauth::AuthorizationsController, type: :controller do it 'returns http success' do subject - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'gives options to authorize and deny' do diff --git a/spec/controllers/oauth/authorized_applications_controller_spec.rb b/spec/controllers/oauth/authorized_applications_controller_spec.rb index 2a2b92283..f967b507f 100644 --- a/spec/controllers/oauth/authorized_applications_controller_spec.rb +++ b/spec/controllers/oauth/authorized_applications_controller_spec.rb @@ -24,7 +24,7 @@ describe Oauth::AuthorizedApplicationsController do it 'returns http success' do subject - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end include_examples 'stores location for user' diff --git a/spec/controllers/remote_follow_controller_spec.rb b/spec/controllers/remote_follow_controller_spec.rb index 86b1eb8d0..5088c2e65 100644 --- a/spec/controllers/remote_follow_controller_spec.rb +++ b/spec/controllers/remote_follow_controller_spec.rb @@ -10,7 +10,7 @@ describe RemoteFollowController do account = Fabricate(:account) get :new, params: { account_username: account.to_param } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response).to render_template(:new) expect(assigns(:remote_follow).acct).to be_nil end @@ -20,7 +20,7 @@ describe RemoteFollowController do account = Fabricate(:account) get :new, params: { account_username: account.to_param } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response).to render_template(:new) expect(assigns(:remote_follow).acct).to eq 'user@example.com' end diff --git a/spec/controllers/settings/applications_controller_spec.rb b/spec/controllers/settings/applications_controller_spec.rb index 90e6a63d5..f87107695 100644 --- a/spec/controllers/settings/applications_controller_spec.rb +++ b/spec/controllers/settings/applications_controller_spec.rb @@ -15,7 +15,7 @@ describe Settings::ApplicationsController do it 'shows apps' do get :index - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(assigns(:applications)).to include(app) expect(assigns(:applications)).to_not include(other_app) end @@ -25,7 +25,7 @@ describe Settings::ApplicationsController do describe 'GET #show' do it 'returns http success' do get :show, params: { id: app.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(assigns[:application]).to eql(app) end @@ -40,7 +40,7 @@ describe Settings::ApplicationsController do describe 'GET #new' do it 'works' do get :new - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -102,7 +102,7 @@ describe Settings::ApplicationsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'renders form again' do @@ -151,7 +151,7 @@ describe Settings::ApplicationsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'renders form again' do diff --git a/spec/controllers/settings/deletes_controller_spec.rb b/spec/controllers/settings/deletes_controller_spec.rb index 9b55090df..35fd64e9b 100644 --- a/spec/controllers/settings/deletes_controller_spec.rb +++ b/spec/controllers/settings/deletes_controller_spec.rb @@ -13,7 +13,7 @@ describe Settings::DeletesController do it 'renders confirmation page' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/exports_controller_spec.rb b/spec/controllers/settings/exports_controller_spec.rb index 19cb0abda..b7cab4d8f 100644 --- a/spec/controllers/settings/exports_controller_spec.rb +++ b/spec/controllers/settings/exports_controller_spec.rb @@ -17,7 +17,7 @@ describe Settings::ExportsController do export = assigns(:export) expect(export).to be_instance_of Export expect(export.account).to eq user.account - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/follower_domains_controller_spec.rb b/spec/controllers/settings/follower_domains_controller_spec.rb index 333223c61..6d415a654 100644 --- a/spec/controllers/settings/follower_domains_controller_spec.rb +++ b/spec/controllers/settings/follower_domains_controller_spec.rb @@ -36,7 +36,7 @@ describe Settings::FollowerDomainsController do it 'returns http success' do sign_in user, scope: :user subject - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end include_examples 'authenticate user' diff --git a/spec/controllers/settings/imports_controller_spec.rb b/spec/controllers/settings/imports_controller_spec.rb index 59b10e0da..7a9b02195 100644 --- a/spec/controllers/settings/imports_controller_spec.rb +++ b/spec/controllers/settings/imports_controller_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Settings::ImportsController, type: :controller do describe "GET #show" do it "returns http success" do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/notifications_controller_spec.rb b/spec/controllers/settings/notifications_controller_spec.rb index 0bd993448..981ef674e 100644 --- a/spec/controllers/settings/notifications_controller_spec.rb +++ b/spec/controllers/settings/notifications_controller_spec.rb @@ -12,7 +12,7 @@ describe Settings::NotificationsController do describe 'GET #show' do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/preferences_controller_spec.rb b/spec/controllers/settings/preferences_controller_spec.rb index 0f9431673..7877c7362 100644 --- a/spec/controllers/settings/preferences_controller_spec.rb +++ b/spec/controllers/settings/preferences_controller_spec.rb @@ -12,7 +12,7 @@ describe Settings::PreferencesController do describe 'GET #show' do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/profiles_controller_spec.rb b/spec/controllers/settings/profiles_controller_spec.rb index ee3315be6..a453200af 100644 --- a/spec/controllers/settings/profiles_controller_spec.rb +++ b/spec/controllers/settings/profiles_controller_spec.rb @@ -11,7 +11,7 @@ RSpec.describe Settings::ProfilesController, type: :controller do describe "GET #show" do it "returns http success" do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb index aee82a3d8..7612bf90e 100644 --- a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb @@ -15,7 +15,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do expect(assigns(:confirmation)).to be_instance_of Form::TwoFactorConfirmation expect(assigns(:provision_url)).to eq 'otpauth://totp/local-part@domain?secret=thisisasecretforthespecofnewview&issuer=cb6e6126.ngrok.io' expect(assigns(:qrcode)).to be_instance_of RQRCode::QRCode - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response).to render_template(:new) end end @@ -71,7 +71,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do expect(assigns(:recovery_codes)).to eq otp_backup_codes expect(flash[:notice]).to eq 'Two-factor authentication successfully enabled' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response).to render_template('settings/two_factor_authentication/recovery_codes/index') end end diff --git a/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb index aa28cdf3f..c04760e53 100644 --- a/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb @@ -19,7 +19,7 @@ describe Settings::TwoFactorAuthentication::RecoveryCodesController do expect(assigns(:recovery_codes)).to eq otp_backup_codes expect(flash[:notice]).to eq 'Recovery codes successfully regenerated' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response).to render_template(:index) end diff --git a/spec/controllers/settings/two_factor_authentications_controller_spec.rb b/spec/controllers/settings/two_factor_authentications_controller_spec.rb index 6c49f6f0d..9f27222ad 100644 --- a/spec/controllers/settings/two_factor_authentications_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentications_controller_spec.rb @@ -18,7 +18,7 @@ describe Settings::TwoFactorAuthenticationsController do user.update(otp_required_for_login: true) get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -27,7 +27,7 @@ describe Settings::TwoFactorAuthenticationsController do user.update(otp_required_for_login: false) get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb index 95fb4d594..89af55688 100644 --- a/spec/controllers/statuses_controller_spec.rb +++ b/spec/controllers/statuses_controller_spec.rb @@ -85,7 +85,7 @@ describe StatusesController do it 'returns a success' do status = Fabricate(:status) get :show, params: { account_username: status.account.username, id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'renders stream_entries/show' do diff --git a/spec/controllers/stream_entries_controller_spec.rb b/spec/controllers/stream_entries_controller_spec.rb index 665c5b747..534bc393d 100644 --- a/spec/controllers/stream_entries_controller_spec.rb +++ b/spec/controllers/stream_entries_controller_spec.rb @@ -77,7 +77,7 @@ RSpec.describe StreamEntriesController, type: :controller do it 'returns http success with Atom' do status = Fabricate(:status) get :show, params: { account_username: status.account.username, id: status.stream_entry.id }, format: 'atom' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/tags_controller_spec.rb b/spec/controllers/tags_controller_spec.rb index b04666c0f..33ccaed61 100644 --- a/spec/controllers/tags_controller_spec.rb +++ b/spec/controllers/tags_controller_spec.rb @@ -12,7 +12,7 @@ RSpec.describe TagsController, type: :controller do context 'when tag exists' do it 'returns http success' do get :show, params: { id: 'test', max_id: late.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'renders application layout' do @@ -25,7 +25,7 @@ RSpec.describe TagsController, type: :controller do it 'returns http missing for non-existent tag' do get :show, params: { id: 'none' } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/well_known/host_meta_controller_spec.rb b/spec/controllers/well_known/host_meta_controller_spec.rb index 87c1485ed..8147e8f61 100644 --- a/spec/controllers/well_known/host_meta_controller_spec.rb +++ b/spec/controllers/well_known/host_meta_controller_spec.rb @@ -7,7 +7,7 @@ describe WellKnown::HostMetaController, type: :controller do it 'returns http success' do get :show, format: :xml - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/xrd+xml' expect(response.body).to eq < diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb index 466f87c45..b05745ea3 100644 --- a/spec/controllers/well_known/webfinger_controller_spec.rb +++ b/spec/controllers/well_known/webfinger_controller_spec.rb @@ -50,7 +50,7 @@ PEM json = body_as_json - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/jrd+json' expect(json[:subject]).to eq 'acct:alice@cb6e6126.ngrok.io' expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice') @@ -61,7 +61,7 @@ PEM xml = Nokogiri::XML(response.body) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/xrd+xml' expect(xml.at_xpath('//xmlns:Subject').content).to eq 'acct:alice@cb6e6126.ngrok.io' expect(xml.xpath('//xmlns:Alias').map(&:content)).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice') @@ -81,7 +81,7 @@ PEM json = body_as_json - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/jrd+json' expect(json[:subject]).to eq 'acct:alice@cb6e6126.ngrok.io' expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice') diff --git a/spec/requests/host_meta_request_spec.rb b/spec/requests/host_meta_request_spec.rb index 0c51b5f48..beb33a859 100644 --- a/spec/requests/host_meta_request_spec.rb +++ b/spec/requests/host_meta_request_spec.rb @@ -5,7 +5,7 @@ describe "The host_meta route" do it "returns an xml response" do get host_meta_url - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq "application/xrd+xml" end end diff --git a/spec/requests/webfinger_request_spec.rb b/spec/requests/webfinger_request_spec.rb index a17d6cc22..7f9e1162e 100644 --- a/spec/requests/webfinger_request_spec.rb +++ b/spec/requests/webfinger_request_spec.rb @@ -7,7 +7,7 @@ describe 'The webfinger route' do it 'returns a json response' do get webfinger_url(resource: alice.to_webfinger_s) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/jrd+json' end end @@ -16,7 +16,7 @@ describe 'The webfinger route' do it 'returns an xml response for xml format' do get webfinger_url(resource: alice.to_webfinger_s, format: :xml) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/xrd+xml' end @@ -24,7 +24,7 @@ describe 'The webfinger route' do headers = { 'HTTP_ACCEPT' => 'application/xrd+xml' } get webfinger_url(resource: alice.to_webfinger_s), headers: headers - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/xrd+xml' end end @@ -33,7 +33,7 @@ describe 'The webfinger route' do it 'returns a json response for json format' do get webfinger_url(resource: alice.to_webfinger_s, format: :json) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/jrd+json' end @@ -41,7 +41,7 @@ describe 'The webfinger route' do headers = { 'HTTP_ACCEPT' => 'application/jrd+json' } get webfinger_url(resource: alice.to_webfinger_s), headers: headers - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/jrd+json' end end From a4a36d994b0949aff134a2c1ba4efc361b7fb358 Mon Sep 17 00:00:00 2001 From: Lynx Kotoura Date: Sun, 22 Apr 2018 04:35:55 +0900 Subject: [PATCH 120/442] Separate high contrast theme (#7213) --- app/javascript/styles/contrast.scss | 3 + app/javascript/styles/contrast/diff.scss | 14 ++ app/javascript/styles/contrast/variables.scss | 22 +++ app/javascript/styles/mastodon/about.scss | 32 ++-- app/javascript/styles/mastodon/accounts.scss | 22 +-- app/javascript/styles/mastodon/admin.scss | 26 +-- .../styles/mastodon/compact_header.scss | 4 +- .../styles/mastodon/components.scss | 150 +++++++++--------- .../styles/mastodon/containers.scss | 2 +- .../styles/mastodon/emoji_picker.scss | 4 +- app/javascript/styles/mastodon/forms.scss | 10 +- .../styles/mastodon/landing_strip.scss | 2 +- .../styles/mastodon/stream_entries.scss | 14 +- app/javascript/styles/mastodon/variables.scss | 20 ++- config/locales/en.yml | 1 + config/themes.yml | 1 + 16 files changed, 187 insertions(+), 140 deletions(-) create mode 100644 app/javascript/styles/contrast.scss create mode 100644 app/javascript/styles/contrast/diff.scss create mode 100644 app/javascript/styles/contrast/variables.scss diff --git a/app/javascript/styles/contrast.scss b/app/javascript/styles/contrast.scss new file mode 100644 index 000000000..5b43aecbe --- /dev/null +++ b/app/javascript/styles/contrast.scss @@ -0,0 +1,3 @@ +@import 'contrast/variables'; +@import 'application'; +@import 'contrast/diff'; diff --git a/app/javascript/styles/contrast/diff.scss b/app/javascript/styles/contrast/diff.scss new file mode 100644 index 000000000..eee9ecc3e --- /dev/null +++ b/app/javascript/styles/contrast/diff.scss @@ -0,0 +1,14 @@ +// components.scss +.compose-form { + .compose-form__modifiers { + .compose-form__upload { + &-description { + input { + &::placeholder { + opacity: 1.0; + } + } + } + } + } +} diff --git a/app/javascript/styles/contrast/variables.scss b/app/javascript/styles/contrast/variables.scss new file mode 100644 index 000000000..35d11060e --- /dev/null +++ b/app/javascript/styles/contrast/variables.scss @@ -0,0 +1,22 @@ +// Dependent colors +$black: #000000; + +$classic-base-color: #282c37; +$classic-primary-color: #9baec8; +$classic-secondary-color: #d9e1e8; + +$ui-base-color: $classic-base-color !default; +$ui-primary-color: $classic-primary-color !default; +$ui-secondary-color: $classic-secondary-color !default; + +// Differences +$ui-highlight-color: #2b5fd9; + +$darker-text-color: lighten($ui-primary-color, 20%) !default; +$dark-text-color: lighten($ui-primary-color, 12%) !default; +$secondary-text-color: lighten($ui-secondary-color, 6%) !default; +$action-button-color: #8d9ac2; + +$inverted-text-color: $black !default; +$lighter-text-color: darken($ui-base-color,6%) !default; +$light-text-color: darken($ui-primary-color, 40%) !default; diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss index 0a09a38d2..c9c0e3081 100644 --- a/app/javascript/styles/mastodon/about.scss +++ b/app/javascript/styles/mastodon/about.scss @@ -225,7 +225,7 @@ $small-breakpoint: 960px; font-family: inherit; font-size: inherit; line-height: inherit; - color: transparentize($darker-text-color, 0.1); + color: lighten($darker-text-color, 10%); } h1 { @@ -234,14 +234,14 @@ $small-breakpoint: 960px; line-height: 30px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; small { font-family: 'mastodon-font-sans-serif', sans-serif; display: block; font-size: 18px; font-weight: 400; - color: opacify($darker-text-color, 0.1); + color: lighten($darker-text-color, 10%); } } @@ -251,7 +251,7 @@ $small-breakpoint: 960px; line-height: 26px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; } h3 { @@ -260,7 +260,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; } h4 { @@ -269,7 +269,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; } h5 { @@ -278,7 +278,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; } h6 { @@ -287,7 +287,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; } ul, @@ -405,7 +405,7 @@ $small-breakpoint: 960px; font-size: 14px; &:hover { - color: $darker-text-color; + color: $secondary-text-color; } } @@ -517,7 +517,7 @@ $small-breakpoint: 960px; span { &:last-child { - color: $darker-text-color; + color: $secondary-text-color; } } @@ -559,7 +559,7 @@ $small-breakpoint: 960px; a, span { font-weight: 400; - color: opacify($darker-text-color, 0.1); + color: darken($darker-text-color, 10%); } a { @@ -775,7 +775,7 @@ $small-breakpoint: 960px; } p a { - color: $darker-text-color; + color: $secondary-text-color; } h1 { @@ -787,7 +787,7 @@ $small-breakpoint: 960px; color: $darker-text-color; span { - color: $darker-text-color; + color: $secondary-text-color; } } } @@ -896,7 +896,7 @@ $small-breakpoint: 960px; } a { - color: $darker-text-color; + color: $secondary-text-color; text-decoration: none; } } @@ -980,7 +980,7 @@ $small-breakpoint: 960px; .footer-links { padding-bottom: 50px; text-align: right; - color: $darker-text-color; + color: $dark-text-color; p { font-size: 14px; @@ -995,7 +995,7 @@ $small-breakpoint: 960px; &__footer { margin-top: 10px; text-align: center; - color: $darker-text-color; + color: $dark-text-color; p { font-size: 14px; diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index f9af6f288..c2d0de4b9 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -178,7 +178,7 @@ font-size: 14px; line-height: 18px; padding: 0 15px; - color: $darker-text-color; + color: $secondary-text-color; } @media screen and (max-width: 480px) { @@ -256,7 +256,7 @@ .current { background: $simple-background-color; border-radius: 100px; - color: $lighter-text-color; + color: $inverted-text-color; cursor: default; margin: 0 10px; } @@ -268,7 +268,7 @@ .older, .newer { text-transform: uppercase; - color: $primary-text-color; + color: $secondary-text-color; } .older { @@ -293,7 +293,7 @@ .disabled { cursor: default; - color: opacify($lighter-text-color, 0.1); + color: lighten($inverted-text-color, 10%); } @media screen and (max-width: 700px) { @@ -332,7 +332,7 @@ width: 335px; background: $simple-background-color; border-radius: 4px; - color: $lighter-text-color; + color: $inverted-text-color; margin: 0 5px 10px; position: relative; @@ -344,7 +344,7 @@ overflow: hidden; height: 100px; border-radius: 4px 4px 0 0; - background-color: opacify($lighter-text-color, 0.04); + background-color: lighten($inverted-text-color, 4%); background-size: cover; background-position: center; position: relative; @@ -422,7 +422,7 @@ .account__header__content { padding: 10px 15px; padding-top: 15px; - color: transparentize($lighter-text-color, 0.1); + color: $lighter-text-color; word-wrap: break-word; overflow: hidden; text-overflow: ellipsis; @@ -434,7 +434,7 @@ .nothing-here { width: 100%; display: block; - color: $lighter-text-color; + color: $light-text-color; font-size: 14px; font-weight: 500; text-align: center; @@ -493,7 +493,7 @@ span { font-size: 14px; - color: $inverted-text-color; + color: $light-text-color; } } @@ -508,7 +508,7 @@ .account__header__content { font-size: 14px; - color: $darker-text-color; + color: $inverted-text-color; } } @@ -586,7 +586,7 @@ font-weight: 500; text-align: center; width: 94px; - color: opacify($darker-text-color, 0.1); + color: $secondary-text-color; background: rgba(darken($ui-base-color, 8%), 0.5); } diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index a0f69449a..a6cc8b62b 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -90,7 +90,7 @@ padding-left: 25px; h2 { - color: $primary-text-color; + color: $secondary-text-color; font-size: 24px; line-height: 28px; font-weight: 400; @@ -98,7 +98,7 @@ } h3 { - color: $primary-text-color; + color: $secondary-text-color; font-size: 20px; line-height: 28px; font-weight: 400; @@ -109,7 +109,7 @@ text-transform: uppercase; font-size: 13px; font-weight: 500; - color: $primary-text-color; + color: $darker-text-color; padding-bottom: 8px; margin-bottom: 8px; border-bottom: 1px solid lighten($ui-base-color, 8%); @@ -117,7 +117,7 @@ h6 { font-size: 16px; - color: $primary-text-color; + color: $secondary-text-color; line-height: 28px; font-weight: 400; } @@ -125,7 +125,7 @@ & > p { font-size: 14px; line-height: 18px; - color: $darker-text-color; + color: $secondary-text-color; margin-bottom: 20px; strong { @@ -292,7 +292,7 @@ font-weight: 500; font-size: 14px; line-height: 18px; - color: $primary-text-color; + color: $secondary-text-color; @each $lang in $cjk-langs { &:lang(#{$lang}) { @@ -420,7 +420,7 @@ } &__timestamp { - color: $darker-text-color; + color: $dark-text-color; } &__extras { @@ -437,7 +437,7 @@ &__icon { font-size: 28px; margin-right: 10px; - color: $darker-text-color; + color: $dark-text-color; } &__icon__overlay { @@ -464,7 +464,7 @@ a, .username, .target { - color: $primary-text-color; + color: $secondary-text-color; text-decoration: none; font-weight: 500; } @@ -474,7 +474,7 @@ } .diff-neutral { - color: $darker-text-color; + color: $secondary-text-color; } .diff-new { @@ -487,7 +487,7 @@ a.name-tag, display: flex; align-items: center; text-decoration: none; - color: $ui-secondary-color; + color: $secondary-text-color; .avatar { display: block; @@ -535,7 +535,7 @@ a.name-tag, font-weight: 500; a { - color: $ui-primary-color; + color: $darker-text-color; } } @@ -545,6 +545,6 @@ a.name-tag, } time { - color: $darker-text-color; + color: $dark-text-color; } } diff --git a/app/javascript/styles/mastodon/compact_header.scss b/app/javascript/styles/mastodon/compact_header.scss index 83ac7a8d0..4980ab5f1 100644 --- a/app/javascript/styles/mastodon/compact_header.scss +++ b/app/javascript/styles/mastodon/compact_header.scss @@ -2,7 +2,7 @@ h1 { font-size: 24px; line-height: 28px; - color: $primary-text-color; + color: $darker-text-color; font-weight: 500; margin-bottom: 20px; padding: 0 10px; @@ -20,7 +20,7 @@ small { font-weight: 400; - color: $darker-text-color; + color: $secondary-text-color; } img { diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 908fa8a07..1d0a7e945 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -31,7 +31,7 @@ &:active, &:focus, &:hover { - background-color: lighten($ui-highlight-color, 4%); + background-color: lighten($ui-highlight-color, 10%); transition: all 200ms ease-out; } @@ -83,7 +83,7 @@ } &.button-secondary { - color: $ui-primary-color; + color: $darker-text-color; background: transparent; padding: 3px 15px; border: 1px solid $ui-primary-color; @@ -92,7 +92,7 @@ &:focus, &:hover { border-color: lighten($ui-primary-color, 4%); - color: lighten($ui-primary-color, 4%); + color: lighten($darker-text-color, 4%); } } @@ -149,18 +149,18 @@ &:hover, &:active, &:focus { - color: transparentize($lighter-text-color, 0.07); + color: darken($lighter-text-color, 7%); } &.disabled { - color: opacify($lighter-text-color, 0.07); + color: lighten($lighter-text-color, 7%); } &.active { color: $highlight-text-color; &.disabled { - color: opacify($lighter-text-color, 0.13); + color: lighten($highlight-text-color, 13%); } } } @@ -193,12 +193,12 @@ &:hover, &:active, &:focus { - color: opacify($lighter-text-color, 0.07); + color: darken($lighter-text-color, 7%); transition: color 200ms ease-out; } &.disabled { - color: transparentize($lighter-text-color, 0.2); + color: lighten($lighter-text-color, 20%); cursor: default; } @@ -349,7 +349,7 @@ box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4); background: $ui-secondary-color; border-radius: 0 0 4px 4px; - color: $lighter-text-color; + color: $inverted-text-color; font-size: 14px; padding: 6px; @@ -457,7 +457,7 @@ input { background: transparent; - color: $primary-text-color; + color: $secondary-text-color; border: 0; padding: 0; margin: 0; @@ -471,8 +471,8 @@ } &::placeholder { - opacity: 0.54; - color: $darker-text-color; + opacity: 0.75; + color: $secondary-text-color; } } @@ -588,7 +588,7 @@ } .reply-indicator__display-name { - color: $lighter-text-color; + color: $inverted-text-color; display: block; max-width: 100%; line-height: 24px; @@ -643,14 +643,14 @@ } a { - color: $ui-secondary-color; + color: $secondary-text-color; text-decoration: none; &:hover { text-decoration: underline; .fa { - color: lighten($action-button-color, 7%); + color: lighten($dark-text-color, 7%); } } @@ -665,7 +665,7 @@ } .fa { - color: $action-button-color; + color: $dark-text-color; } } @@ -769,7 +769,7 @@ &.light { .status__relative-time { - color: $lighter-text-color; + color: $light-text-color; } .status__display-name { @@ -782,7 +782,7 @@ } span { - color: $lighter-text-color; + color: $light-text-color; } } @@ -816,13 +816,13 @@ } .status__relative-time { - color: $darker-text-color; + color: $dark-text-color; float: right; font-size: 14px; } .status__display-name { - color: $darker-text-color; + color: $dark-text-color; } .status__info .status__display-name { @@ -873,14 +873,14 @@ .status__prepend { margin-left: 68px; - color: $darker-text-color; + color: $dark-text-color; padding: 8px 0; padding-bottom: 2px; font-size: 14px; position: relative; .status__display-name strong { - color: $darker-text-color; + color: $dark-text-color; } > span { @@ -942,7 +942,7 @@ .detailed-status__meta { margin-top: 15px; - color: $darker-text-color; + color: $dark-text-color; font-size: 14px; line-height: 18px; } @@ -1091,7 +1091,7 @@ a .account__avatar { } .account__header__username { - color: $darker-text-color; + color: $secondary-text-color; } } @@ -1101,7 +1101,7 @@ a .account__avatar { } .account__header__content { - color: $darker-text-color; + color: $secondary-text-color; } .account__header__display-name { @@ -1129,7 +1129,7 @@ a .account__avatar { .account__disclaimer { padding: 10px; border-top: 1px solid lighten($ui-base-color, 8%); - color: $darker-text-color; + color: $dark-text-color; strong { font-weight: 500; @@ -1316,7 +1316,7 @@ a.account__display-name { } .detailed-status__display-name { - color: $darker-text-color; + color: $secondary-text-color; display: block; line-height: 24px; margin-bottom: 15px; @@ -1351,11 +1351,11 @@ a.account__display-name { .muted { .status__content p, .status__content a { - color: $darker-text-color; + color: $dark-text-color; } .status__display-name strong { - color: $darker-text-color; + color: $dark-text-color; } .status__avatar { @@ -1363,11 +1363,11 @@ a.account__display-name { } a.status__content__spoiler-link { - background: $darker-text-color; - color: lighten($ui-base-color, 4%); + background: $ui-base-lighter-color; + color: $inverted-text-color; &:hover { - background: transparentize($darker-text-color, 0.07); + background: lighten($ui-base-lighter-color, 7%); text-decoration: none; } } @@ -1378,7 +1378,7 @@ a.account__display-name { padding: 8px 0; padding-bottom: 0; cursor: default; - color: $ui-primary-color; + color: $darker-text-color; font-size: 15px; position: relative; @@ -1489,7 +1489,7 @@ a.account__display-name { color: $darker-text-color; strong { - color: $primary-text-color; + color: $secondary-text-color; } a { @@ -1603,7 +1603,7 @@ a.account__display-name { &:hover, &:active { background: $ui-highlight-color; - color: $primary-text-color; + color: $secondary-text-color; outline: 0; } } @@ -1656,7 +1656,7 @@ a.account__display-name { &:hover { background: $ui-highlight-color; - color: $primary-text-color; + color: $secondary-text-color; } } } @@ -1668,7 +1668,7 @@ a.account__display-name { .static-content { padding: 10px; padding-top: 20px; - color: $darker-text-color; + color: $dark-text-color; h1 { font-size: 16px; @@ -1755,7 +1755,7 @@ a.account__display-name { display: block; flex: 1 1 auto; padding: 15px 5px 13px; - color: $ui-primary-color; + color: $darker-text-color; text-decoration: none; text-align: center; font-size: 16px; @@ -2167,7 +2167,7 @@ a.account__display-name { .column-subheading { background: $ui-base-color; - color: $darker-text-color; + color: $dark-text-color; padding: 8px 20px; font-size: 12px; font-weight: 500; @@ -2190,11 +2190,11 @@ a.account__display-name { flex: 1 0 auto; p { - color: $darker-text-color; + color: $secondary-text-color; } a { - color: opacify($darker-text-color, 0.07); + color: $dark-text-color; } } @@ -2275,7 +2275,7 @@ a.account__display-name { font-size: 14px; border: 1px solid lighten($ui-base-color, 8%); border-radius: 4px; - color: $darker-text-color; + color: $dark-text-color; margin-top: 14px; text-decoration: none; overflow: hidden; @@ -2355,7 +2355,7 @@ a.status-card { display: block; font-weight: 500; margin-bottom: 5px; - color: $ui-primary-color; + color: $darker-text-color; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -2369,7 +2369,7 @@ a.status-card { } .status-card__description { - color: $ui-primary-color; + color: $darker-text-color; } .status-card__host { @@ -2413,7 +2413,7 @@ a.status-card { .load-more { display: block; - color: $darker-text-color; + color: $dark-text-color; background-color: transparent; border: 0; font-size: inherit; @@ -2437,7 +2437,7 @@ a.status-card { text-align: center; font-size: 16px; font-weight: 500; - color: opacify($darker-text-color, 0.07); + color: $dark-text-color; background: $ui-base-color; cursor: default; display: flex; @@ -2477,7 +2477,7 @@ a.status-card { strong { display: block; margin-bottom: 10px; - color: $darker-text-color; + color: $dark-text-color; } span { @@ -2565,13 +2565,13 @@ a.status-card { .column-header__button { background: lighten($ui-base-color, 4%); border: 0; - color: $ui-primary-color; + color: $darker-text-color; cursor: pointer; font-size: 16px; padding: 0 15px; &:hover { - color: lighten($ui-primary-color, 7%); + color: lighten($darker-text-color, 7%); } &.active { @@ -2652,7 +2652,7 @@ a.status-card { } .loading-indicator { - color: $darker-text-color; + color: $dark-text-color; font-size: 12px; font-weight: 400; text-transform: uppercase; @@ -2749,7 +2749,7 @@ a.status-card { &:active, &:focus { padding: 0; - color: transparentize($darker-text-color, 0.07); + color: lighten($darker-text-color, 8%); } } @@ -2873,7 +2873,7 @@ a.status-card { .empty-column-indicator, .error-column { - color: $darker-text-color; + color: $dark-text-color; background: $ui-base-color; text-align: center; padding: 20px; @@ -3075,7 +3075,7 @@ a.status-card { display: flex; align-items: center; justify-content: center; - color: $primary-text-color; + color: $secondary-text-color; font-size: 18px; font-weight: 500; border: 2px dashed $ui-base-lighter-color; @@ -3173,7 +3173,7 @@ a.status-card { } .privacy-dropdown__option { - color: $lighter-text-color; + color: $inverted-text-color; padding: 10px; cursor: pointer; display: flex; @@ -3295,7 +3295,7 @@ a.status-card { font-size: 18px; width: 18px; height: 18px; - color: $ui-secondary-color; + color: $secondary-text-color; cursor: default; pointer-events: none; @@ -3331,7 +3331,7 @@ a.status-card { } .search-results__header { - color: $darker-text-color; + color: $dark-text-color; background: lighten($ui-base-color, 2%); border-bottom: 1px solid darken($ui-base-color, 4%); padding: 15px 10px; @@ -3379,13 +3379,13 @@ a.status-card { .search-results__hashtag { display: block; padding: 10px; - color: darken($primary-text-color, 4%); + color: $secondary-text-color; text-decoration: none; &:hover, &:active, &:focus { - color: $primary-text-color; + color: lighten($secondary-text-color, 4%); text-decoration: underline; } } @@ -3650,7 +3650,7 @@ a.status-card { &:hover, &:focus, &:active { - color: transparentize($lighter-text-color, 0.04); + color: darken($lighter-text-color, 4%); background-color: darken($ui-secondary-color, 16%); } @@ -3744,7 +3744,7 @@ a.status-card { strong { font-weight: 500; background: $ui-base-color; - color: $primary-text-color; + color: $secondary-text-color; border-radius: 4px; font-size: 14px; padding: 3px 6px; @@ -3804,7 +3804,7 @@ a.status-card { &__case { background: $ui-base-color; - color: $primary-text-color; + color: $secondary-text-color; font-weight: 500; padding: 10px; border-radius: 4px; @@ -3821,7 +3821,7 @@ a.status-card { .figure { background: darken($ui-base-color, 8%); - color: $darker-text-color; + color: $secondary-text-color; margin-bottom: 20px; border-radius: 4px; padding: 10px; @@ -3933,7 +3933,7 @@ a.status-card { } .status__content__spoiler-link { - color: lighten($ui-secondary-color, 8%); + color: lighten($secondary-text-color, 8%); } } @@ -4163,7 +4163,7 @@ a.status-card { &:hover, &:focus, &:active { - color: transparentize($lighter-text-color, 0.04); + color: darken($lighter-text-color, 4%); } } } @@ -4244,7 +4244,7 @@ a.status-card { &__icon { flex: 0 0 auto; - color: $darker-text-color; + color: $dark-text-color; padding: 8px 18px; cursor: default; border-right: 1px solid lighten($ui-base-color, 8%); @@ -4274,7 +4274,7 @@ a.status-card { a { text-decoration: none; - color: $darker-text-color; + color: $dark-text-color; font-weight: 500; &:hover { @@ -4293,7 +4293,7 @@ a.status-card { } .fa { - color: $darker-text-color; + color: $dark-text-color; } } } @@ -4329,7 +4329,7 @@ a.status-card { cursor: zoom-in; display: block; text-decoration: none; - color: $ui-secondary-color; + color: $secondary-text-color; line-height: 0; &, @@ -4500,7 +4500,7 @@ a.status-card { &:hover, &:active, &:focus { - color: transparentize($darker-text-color, 0.07); + color: lighten($darker-text-color, 7%); } } @@ -4705,7 +4705,7 @@ a.status-card { &:active, &:focus { outline: 0; - color: transparentize($darker-text-color, 0.07); + color: $secondary-text-color; &::before { content: ""; @@ -4745,7 +4745,7 @@ a.status-card { position: relative; &.active { - color: transparentize($darker-text-color, 0.07); + color: $secondary-text-color; &::before, &::after { @@ -4780,12 +4780,12 @@ a.status-card { padding: 10px 14px; padding-bottom: 14px; margin-top: 10px; - color: $lighter-text-color; + color: $light-text-color; box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); h4 { text-transform: uppercase; - color: $lighter-text-color; + color: $light-text-color; font-size: 13px; font-weight: 500; margin-bottom: 10px; @@ -4817,7 +4817,7 @@ noscript { div { font-size: 14px; margin: 30px auto; - color: $primary-text-color; + color: $secondary-text-color; max-width: 400px; a { @@ -4970,7 +4970,7 @@ noscript { &__message { position: relative; margin-left: 58px; - color: $darker-text-color; + color: $dark-text-color; padding: 8px 0; padding-top: 0; padding-bottom: 4px; diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss index 8df2902d2..9d5ab66a4 100644 --- a/app/javascript/styles/mastodon/containers.scss +++ b/app/javascript/styles/mastodon/containers.scss @@ -100,7 +100,7 @@ .name { flex: 1 1 auto; - color: $darker-text-color; + color: $secondary-text-color; width: calc(100% - 88px); .username { diff --git a/app/javascript/styles/mastodon/emoji_picker.scss b/app/javascript/styles/mastodon/emoji_picker.scss index 3620a6f54..cf9547586 100644 --- a/app/javascript/styles/mastodon/emoji_picker.scss +++ b/app/javascript/styles/mastodon/emoji_picker.scss @@ -50,7 +50,7 @@ cursor: pointer; &:hover { - color: opacify($lighter-text-color, 0.04); + color: darken($lighter-text-color, 4%); } } @@ -184,7 +184,7 @@ font-size: 14px; text-align: center; padding-top: 70px; - color: $lighter-text-color; + color: $light-text-color; .emoji-mart-category-label { display: none; diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 3a3b4c326..f97890187 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -248,7 +248,7 @@ code { } &:required:valid { - border-bottom-color: lighten($error-red, 12%); + border-bottom-color: $valid-value-color; } &:active, @@ -266,7 +266,7 @@ code { input[type=text], input[type=email], input[type=password] { - border-bottom-color: lighten($error-red, 12%); + border-bottom-color: $valid-value-color; } .error { @@ -356,7 +356,7 @@ code { padding: 7px 4px; padding-bottom: 9px; font-size: 16px; - color: $darker-text-color; + color: $dark-text-color; font-family: inherit; pointer-events: none; cursor: default; @@ -446,7 +446,7 @@ code { } strong { - color: $primary-text-color; + color: $secondary-text-color; font-weight: 500; @each $lang in $cjk-langs { @@ -483,7 +483,7 @@ code { .qr-alternative { margin-bottom: 20px; - color: $darker-text-color; + color: $secondary-text-color; flex: 150px; samp { diff --git a/app/javascript/styles/mastodon/landing_strip.scss b/app/javascript/styles/mastodon/landing_strip.scss index 651c06ced..86614b89b 100644 --- a/app/javascript/styles/mastodon/landing_strip.scss +++ b/app/javascript/styles/mastodon/landing_strip.scss @@ -45,7 +45,7 @@ padding: 14px; border-radius: 4px; background: rgba(darken($ui-base-color, 7%), 0.8); - color: $darker-text-color; + color: $secondary-text-color; font-weight: 400; margin-bottom: 20px; diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss index c39163ba8..281cbaf83 100644 --- a/app/javascript/styles/mastodon/stream_entries.scss +++ b/app/javascript/styles/mastodon/stream_entries.scss @@ -93,7 +93,7 @@ display: block; max-width: 100%; padding-right: 25px; - color: $lighter-text-color; + color: $inverted-text-color; } .status__avatar { @@ -134,7 +134,7 @@ span { font-size: 14px; - color: $inverted-text-color; + color: $light-text-color; } } @@ -191,7 +191,7 @@ span { font-size: 14px; - color: $lighter-text-color; + color: $light-text-color; } } } @@ -225,7 +225,7 @@ .detailed-status__meta { margin-top: 15px; - color: $lighter-text-color; + color: $light-text-color; font-size: 14px; line-height: 18px; @@ -270,7 +270,7 @@ padding-left: (48px + 14px * 2); padding-bottom: 0; margin-bottom: -4px; - color: $lighter-text-color; + color: $light-text-color; font-size: 14px; position: relative; @@ -280,7 +280,7 @@ } .status__display-name.muted strong { - color: $lighter-text-color; + color: $light-text-color; } } @@ -293,7 +293,7 @@ } .more { - color: $classic-primary-color; + color: $darker-text-color; display: block; padding: 14px; text-align: center; diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss index dc4e72a2e..cbefe35b4 100644 --- a/app/javascript/styles/mastodon/variables.scss +++ b/app/javascript/styles/mastodon/variables.scss @@ -17,12 +17,6 @@ $base-shadow-color: $black !default; $base-overlay-background: $black !default; $base-border-color: $white !default; $simple-background-color: $white !default; -$primary-text-color: $white !default; -$darker-text-color: rgba($primary-text-color, 0.7) !default; -$highlight-text-color: $classic-highlight-color !default; -$inverted-text-color: $black !default; -$lighter-text-color: rgba($inverted-text-color, 0.7) !default; -$action-button-color: #8d9ac2; $valid-value-color: $success-green !default; $error-value-color: $error-red !default; @@ -31,7 +25,19 @@ $ui-base-color: $classic-base-color !default; // Darkest $ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest $ui-primary-color: $classic-primary-color !default; // Lighter $ui-secondary-color: $classic-secondary-color !default; // Lightest -$ui-highlight-color: #2b5fd9; +$ui-highlight-color: $classic-highlight-color !default; + +// Variables for texts +$primary-text-color: $white !default; +$darker-text-color: $ui-primary-color !default; +$dark-text-color: $ui-base-lighter-color !default; +$secondary-text-color: $ui-secondary-color !default; +$highlight-text-color: $ui-highlight-color !default; +$action-button-color: $ui-base-lighter-color !default; +// For texts on inverted backgrounds +$inverted-text-color: $ui-base-color !default; +$lighter-text-color: $ui-base-lighter-color !default; +$light-text-color: $ui-primary-color !default; // Language codes that uses CJK fonts $cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW; diff --git a/config/locales/en.yml b/config/locales/en.yml index 53b64a100..8b66b91ec 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -785,6 +785,7 @@ en:

    Originally adapted from the Discourse privacy policy.

    title: "%{instance} Terms of Service and Privacy Policy" themes: + contrast: High contrast default: Mastodon time: formats: diff --git a/config/themes.yml b/config/themes.yml index a1049fae7..f0bb1e6f7 100644 --- a/config/themes.yml +++ b/config/themes.yml @@ -1 +1,2 @@ default: styles/application.scss +contrast: styles/contrast.scss From b8f0cfd6e34bc1285e952072a8c8a0eeafd8918c Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sun, 22 Apr 2018 04:36:22 +0900 Subject: [PATCH 121/442] Add parallel test processors (#7215) --- .circleci/config.yml | 7 ++----- .env.test | 2 ++ 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index e3a9628ac..c5d6ec9d1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,13 +6,10 @@ aliases: - image: circleci/ruby:2.5.1-stretch-node environment: &ruby_environment BUNDLE_APP_CONFIG: ./.bundle/ - RAILS_ENV: test - NODE_ENV: test DB_HOST: localhost DB_USER: root - LOCAL_DOMAIN: cb6e6126.ngrok.io - LOCAL_HTTPS: true - PARALLEL_TEST_PROCESSORS: 2 + RAILS_ENV: test + PARALLEL_TEST_PROCESSORS: 4 ALLOW_NOPAM: true working_directory: ~/projects/mastodon/ diff --git a/.env.test b/.env.test index 7da76f8ef..726351c5e 100644 --- a/.env.test +++ b/.env.test @@ -1,3 +1,5 @@ +# Node.js +NODE_ENV=test # Federation LOCAL_DOMAIN=cb6e6126.ngrok.io LOCAL_HTTPS=true From 9b8bb2a5df2839041ccd33e4ae7ec81b8746ddb2 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sun, 22 Apr 2018 04:56:40 +0900 Subject: [PATCH 122/442] Replace badge to CircleCI (#7216) --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 7b85b165b..34d56c96f 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ ![Mastodon](https://i.imgur.com/NhZc40l.png) ======== -[![Build Status](https://img.shields.io/travis/tootsuite/mastodon.svg)][travis] +[![Build Status](https://img.shields.io/circleci/project/github/tootsuite/mastodon.svg)][circleci] [![Code Climate](https://img.shields.io/codeclimate/maintainability/tootsuite/mastodon.svg)][code_climate] -[travis]: https://travis-ci.org/tootsuite/mastodon +[circleci]: https://circleci.com/gh/tootsuite/mastodon [code_climate]: https://codeclimate.com/github/tootsuite/mastodon Mastodon is a **free, open-source social network server** based on **open web protocols** like ActivityPub and OStatus. The social focus of the project is a viable decentralized alternative to commercial social media silos that returns the control of the content distribution channels to the people. The technical focus of the project is a good user interface, a clean REST API for 3rd party apps and robust anti-abuse tools. From 3f6893c6419c842f32dc0727db8d46dd8e457777 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sun, 22 Apr 2018 06:37:07 +0900 Subject: [PATCH 123/442] Reset locale on registration tests (#7219) --- spec/controllers/auth/registrations_controller_spec.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb index 9cfbd481f..eeb01d5ad 100644 --- a/spec/controllers/auth/registrations_controller_spec.rb +++ b/spec/controllers/auth/registrations_controller_spec.rb @@ -73,6 +73,12 @@ RSpec.describe Auth::RegistrationsController, type: :controller do describe 'POST #create' do let(:accept_language) { Rails.application.config.i18n.available_locales.sample.to_s } + around do |example| + current_locale = I18n.locale + example.run + I18n.locale = current_locale + end + before { request.env["devise.mapping"] = Devise.mappings[:user] } context do From 3fa316147228819c48fd4f20c5c30f4f34b85f07 Mon Sep 17 00:00:00 2001 From: Matthias Beyer Date: Sun, 22 Apr 2018 05:28:12 +0200 Subject: [PATCH 124/442] Fix: Use "welches" instead of "dass" in translation (#7185) --- config/locales/de.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/de.yml b/config/locales/de.yml index 51936aa36..221437a08 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -428,7 +428,7 @@ de: archive_takeout: date: Datum download: Dein Archiv herunterladen - hint_html: Du kannst ein Archiv deiner Beiträge und hochgeladenen Medien anfragen. Die exportieren Daten werden im ActivityPub-Format gespeichert, dass lesbar mit jeder Software ist, die das Format unterstützt. + hint_html: Du kannst ein Archiv deiner Beiträge und hochgeladenen Medien anfragen. Die exportieren Daten werden im ActivityPub-Format gespeichert, welches mit jeder Software lesbar ist die das Format unterstützt. in_progress: Stelle dein Archiv zusammen... request: Dein Archiv anfragen size: Größe From 648d645c2fc4e7b266cb4d83bd4ed62e929dd363 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sun, 22 Apr 2018 18:41:39 +0900 Subject: [PATCH 125/442] Fix randomly fail (similar #7219) (#7225) --- spec/controllers/concerns/localized_spec.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spec/controllers/concerns/localized_spec.rb b/spec/controllers/concerns/localized_spec.rb index f71c96aff..8c80b7d2a 100644 --- a/spec/controllers/concerns/localized_spec.rb +++ b/spec/controllers/concerns/localized_spec.rb @@ -11,13 +11,17 @@ describe ApplicationController, type: :controller do end end + around do |example| + current_locale = I18n.locale + example.run + I18n.locale = current_locale + end + before do routes.draw { get 'success' => 'anonymous#success' } end shared_examples 'default locale' do - after { I18n.locale = I18n.default_locale } - it 'sets available and preferred language' do request.headers['Accept-Language'] = 'ca-ES, fa' get 'success' From ca9192d9ba1c27689d7a14d0fb5b426c3d73c9f4 Mon Sep 17 00:00:00 2001 From: David Baucum Date: Sun, 22 Apr 2018 05:49:16 -0400 Subject: [PATCH 126/442] Ability to specify Redis passwd on mastodon:setup (#7222) Closes #7221 --- lib/tasks/mastodon.rake | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 505c7e0fa..b5a148337 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -107,9 +107,16 @@ namespace :mastodon do q.convert :int end + env['REDIS_PASSWORD'] = prompt.ask('Redis password:') do |q| + q.required false + a.default nil + q.modify :strip + end + redis_options = { host: env['REDIS_HOST'], port: env['REDIS_PORT'], + password: env['REDIS_PASSWORD'], driver: :hiredis, } From 597948fb1316d2e1de76be330db441786c571132 Mon Sep 17 00:00:00 2001 From: ThibG Date: Sun, 22 Apr 2018 12:10:37 +0200 Subject: [PATCH 127/442] Do not set emoji as inline-block (fixes #5743) (#7207) --- app/javascript/styles/mastodon/components.scss | 1 - 1 file changed, 1 deletion(-) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 1d0a7e945..f6e403b7d 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -556,7 +556,6 @@ } .emojione { - display: inline-block; font-size: inherit; vertical-align: middle; object-fit: contain; From 3c5006ec7fa98ec53df619b0e6e52b3bbdb6f046 Mon Sep 17 00:00:00 2001 From: Lynx Kotoura Date: Sun, 22 Apr 2018 21:29:40 +0900 Subject: [PATCH 128/442] Fix text colors (#7227) --- app/javascript/styles/contrast/variables.scss | 2 ++ app/javascript/styles/mastodon/components.scss | 6 +++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/javascript/styles/contrast/variables.scss b/app/javascript/styles/contrast/variables.scss index 35d11060e..f6cadf029 100644 --- a/app/javascript/styles/contrast/variables.scss +++ b/app/javascript/styles/contrast/variables.scss @@ -4,6 +4,7 @@ $black: #000000; $classic-base-color: #282c37; $classic-primary-color: #9baec8; $classic-secondary-color: #d9e1e8; +$classic-highlight-color: #2b90d9; $ui-base-color: $classic-base-color !default; $ui-primary-color: $classic-primary-color !default; @@ -15,6 +16,7 @@ $ui-highlight-color: #2b5fd9; $darker-text-color: lighten($ui-primary-color, 20%) !default; $dark-text-color: lighten($ui-primary-color, 12%) !default; $secondary-text-color: lighten($ui-secondary-color, 6%) !default; +$highlight-text-color: $classic-highlight-color !default; $action-button-color: #8d9ac2; $inverted-text-color: $black !default; diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index f6e403b7d..c84e61306 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -701,7 +701,7 @@ border-radius: 2px; background: transparent; border: 0; - color: $lighter-text-color; + color: $inverted-text-color; font-weight: 700; font-size: 11px; padding: 0 6px; @@ -4037,6 +4037,10 @@ a.status-card { overflow-y: auto; overflow-x: hidden; + .status__content a { + color: $highlight-text-color; + } + @media screen and (max-width: 480px) { max-height: 10vh; } From b305a2393378fb365f26f7b440a67be2d415c4e2 Mon Sep 17 00:00:00 2001 From: jumoru Date: Sun, 22 Apr 2018 14:46:19 +0200 Subject: [PATCH 129/442] Fix: Use "exportierten" instead of "exportieren" in translation (#7186) Spotted when looking at https://metalhead.club/@thomas/99881521526619858 --- config/locales/de.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/locales/de.yml b/config/locales/de.yml index 221437a08..f4f842aef 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -428,7 +428,7 @@ de: archive_takeout: date: Datum download: Dein Archiv herunterladen - hint_html: Du kannst ein Archiv deiner Beiträge und hochgeladenen Medien anfragen. Die exportieren Daten werden im ActivityPub-Format gespeichert, welches mit jeder Software lesbar ist die das Format unterstützt. + hint_html: Du kannst ein Archiv deiner Beiträge und hochgeladenen Medien anfragen. Die exportierten Daten werden im ActivityPub-Format gespeichert, welches mit jeder Software lesbar ist die das Format unterstützt. in_progress: Stelle dein Archiv zusammen... request: Dein Archiv anfragen size: Größe From 4ca2f73b126de89a917680d60f40cae7f589f55f Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 22 Apr 2018 15:42:00 +0200 Subject: [PATCH 130/442] Rescue Mastodon::LengthValidationError in Remoteable (#7228) Fix #7198 by allowing records with optional attachments to save --- app/models/concerns/remotable.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/models/concerns/remotable.rb b/app/models/concerns/remotable.rb index 3b8c507c3..7f1ef5191 100644 --- a/app/models/concerns/remotable.rb +++ b/app/models/concerns/remotable.rb @@ -38,7 +38,7 @@ module Remotable self[attribute_name] = url if has_attribute?(attribute_name) end - rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError => e + rescue HTTP::TimeoutError, HTTP::ConnectionError, OpenSSL::SSL::SSLError, Paperclip::Errors::NotIdentifiedByImageMagickError, Addressable::URI::InvalidURIError, Mastodon::HostValidationError, Mastodon::LengthValidationError => e Rails.logger.debug "Error fetching remote #{attachment_name}: #{e}" nil end From 75c4ab9d12d3a2f3de52c51b5006fe9d5d9afae4 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sun, 22 Apr 2018 22:09:03 +0200 Subject: [PATCH 131/442] Remove "nsfw" category for sensitive statuses in OStatus serializer (#7048) Fix #7011 --- app/lib/ostatus/atom_serializer.rb | 2 -- app/services/process_hashtags_service.rb | 2 -- spec/lib/ostatus/atom_serializer_spec.rb | 6 ------ 3 files changed, 10 deletions(-) diff --git a/app/lib/ostatus/atom_serializer.rb b/app/lib/ostatus/atom_serializer.rb index 055b4649c..7c66f2066 100644 --- a/app/lib/ostatus/atom_serializer.rb +++ b/app/lib/ostatus/atom_serializer.rb @@ -364,8 +364,6 @@ class OStatus::AtomSerializer append_element(entry, 'category', nil, term: tag.name) end - append_element(entry, 'category', nil, term: 'nsfw') if status.sensitive? - status.media_attachments.each do |media| append_element(entry, 'link', nil, rel: :enclosure, type: media.file_content_type, length: media.file_file_size, href: full_asset_url(media.file.url(:original, false))) end diff --git a/app/services/process_hashtags_service.rb b/app/services/process_hashtags_service.rb index 990e01a4b..5b45c865f 100644 --- a/app/services/process_hashtags_service.rb +++ b/app/services/process_hashtags_service.rb @@ -7,7 +7,5 @@ class ProcessHashtagsService < BaseService tags.map { |str| str.mb_chars.downcase }.uniq(&:to_s).each do |tag| status.tags << Tag.where(name: tag).first_or_initialize(name: tag) end - - status.update(sensitive: true) if tags.include?('nsfw') end end diff --git a/spec/lib/ostatus/atom_serializer_spec.rb b/spec/lib/ostatus/atom_serializer_spec.rb index 00e6f09dc..d46791307 100644 --- a/spec/lib/ostatus/atom_serializer_spec.rb +++ b/spec/lib/ostatus/atom_serializer_spec.rb @@ -386,12 +386,6 @@ RSpec.describe OStatus::AtomSerializer do expect(entry.category[:term]).to eq 'tag' end - it 'appends category element for NSFW if status is sensitive' do - status = Fabricate(:status, sensitive: true) - entry = OStatus::AtomSerializer.new.entry(status.stream_entry) - expect(entry.category[:term]).to eq 'nsfw' - end - it 'appends link elements for media attachments' do file = attachment_fixture('attachment.jpg') media_attachment = Fabricate(:media_attachment, file: file) From 05fb6f096db7c58698f8e0bc7fc79e129b241176 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 23 Apr 2018 00:43:36 +0200 Subject: [PATCH 132/442] Resize images before upload in web UI to reduce bandwidth (#7223) * Resize images before upload in web UI to reduce bandwidth Fix #7218 * Fix issues * Do not resize GIFs in JS --- app/javascript/mastodon/actions/compose.js | 95 +++++++++++++++++++--- 1 file changed, 85 insertions(+), 10 deletions(-) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index eee9c6928..c8ea5aaba 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -174,6 +174,79 @@ export function submitComposeFail(error) { }; }; +const MAX_IMAGE_DIMENSION = 1280; + +const dataURLtoBlob = dataURL => { + const BASE64_MARKER = ';base64,'; + + if (dataURL.indexOf(BASE64_MARKER) === -1) { + const parts = dataURL.split(','); + const contentType = parts[0].split(':')[1]; + const raw = parts[1]; + + return new Blob([raw], { type: contentType }); + } + + const parts = dataURL.split(BASE64_MARKER); + const contentType = parts[0].split(':')[1]; + const raw = window.atob(parts[1]); + const rawLength = raw.length; + + const uInt8Array = new Uint8Array(rawLength); + + for (let i = 0; i < rawLength; ++i) { + uInt8Array[i] = raw.charCodeAt(i); + } + + return new Blob([uInt8Array], { type: contentType }); +}; + +const resizeImage = (inputFile, callback) => { + if (inputFile.type.match(/image.*/) && inputFile.type !== 'image/gif') { + const reader = new FileReader(); + + reader.onload = e => { + const img = new Image(); + + img.onload = () => { + const canvas = document.createElement('canvas'); + const { width, height } = img; + + let newWidth, newHeight; + + if (width < MAX_IMAGE_DIMENSION && height < MAX_IMAGE_DIMENSION) { + callback(inputFile); + return; + } + + if (width > height) { + newHeight = height * MAX_IMAGE_DIMENSION / width; + newWidth = MAX_IMAGE_DIMENSION; + } else if (height > width) { + newWidth = width * MAX_IMAGE_DIMENSION / height; + newHeight = MAX_IMAGE_DIMENSION; + } else { + newWidth = MAX_IMAGE_DIMENSION; + newHeight = MAX_IMAGE_DIMENSION; + } + + canvas.width = newWidth; + canvas.height = newHeight; + + canvas.getContext('2d').drawImage(img, 0, 0, newWidth, newHeight); + + callback(dataURLtoBlob(canvas.toDataURL(inputFile.type))); + }; + + img.src = e.target.result; + }; + + reader.readAsDataURL(inputFile); + } else { + callback(inputFile); + } +}; + export function uploadCompose(files) { return function (dispatch, getState) { if (getState().getIn(['compose', 'media_attachments']).size > 3) { @@ -182,17 +255,19 @@ export function uploadCompose(files) { dispatch(uploadComposeRequest()); - let data = new FormData(); - data.append('file', files[0]); + resizeImage(files[0], file => { + let data = new FormData(); + data.append('file', file); - api(getState).post('/api/v1/media', data, { - onUploadProgress: function (e) { - dispatch(uploadComposeProgress(e.loaded, e.total)); - }, - }).then(function (response) { - dispatch(uploadComposeSuccess(response.data)); - }).catch(function (error) { - dispatch(uploadComposeFail(error)); + api(getState).post('/api/v1/media', data, { + onUploadProgress: function (e) { + dispatch(uploadComposeProgress(e.loaded, e.total)); + }, + }).then(function (response) { + dispatch(uploadComposeSuccess(response.data)); + }).catch(function (error) { + dispatch(uploadComposeFail(error)); + }); }); }; }; From 660cb058e18f8607a0044b5a89614e1caeb07ed9 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 23 Apr 2018 00:43:53 +0200 Subject: [PATCH 133/442] Improve relative timestamps in web UI (#7233) Use short instead of numeric month, display year when different year E.g.: "Apr 4" instead of "4/4", "Apr 4, 2017" if different year --- .../mastodon/components/relative_timestamp.js | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/app/javascript/mastodon/components/relative_timestamp.js b/app/javascript/mastodon/components/relative_timestamp.js index 51588e78c..3c8db7092 100644 --- a/app/javascript/mastodon/components/relative_timestamp.js +++ b/app/javascript/mastodon/components/relative_timestamp.js @@ -20,7 +20,7 @@ const dateFormatOptions = { }; const shortDateFormatOptions = { - month: 'numeric', + month: 'short', day: 'numeric', }; @@ -66,12 +66,17 @@ export default class RelativeTimestamp extends React.Component { static propTypes = { intl: PropTypes.object.isRequired, timestamp: PropTypes.string.isRequired, + year: PropTypes.number.isRequired, }; state = { now: this.props.intl.now(), }; + static defaultProps = { + year: (new Date()).getFullYear(), + }; + shouldComponentUpdate (nextProps, nextState) { // As of right now the locale doesn't change without a new page load, // but we might as well check in case that ever changes. @@ -114,7 +119,7 @@ export default class RelativeTimestamp extends React.Component { } render () { - const { timestamp, intl } = this.props; + const { timestamp, intl, year } = this.props; const date = new Date(timestamp); const delta = this.state.now - date.getTime(); @@ -123,7 +128,7 @@ export default class RelativeTimestamp extends React.Component { if (delta < 10 * SECOND) { relativeTime = intl.formatMessage(messages.just_now); - } else if (delta < 3 * DAY) { + } else if (delta < 7 * DAY) { if (delta < MINUTE) { relativeTime = intl.formatMessage(messages.seconds, { number: Math.floor(delta / SECOND) }); } else if (delta < HOUR) { @@ -133,8 +138,10 @@ export default class RelativeTimestamp extends React.Component { } else { relativeTime = intl.formatMessage(messages.days, { number: Math.floor(delta / DAY) }); } - } else { + } else if (date.getFullYear() === year) { relativeTime = intl.formatDate(date, shortDateFormatOptions); + } else { + relativeTime = intl.formatDate(date, { ...shortDateFormatOptions, year: 'numeric' }); } return ( From 0758b00bfddf4a2c845d0d611e50717a268fd48a Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Mon, 23 Apr 2018 16:15:51 +0900 Subject: [PATCH 134/442] Refactor resizeImage method (#7236) - Use URL.createObjectURL (replace from FileReader) - Use HTMLCanvasElement.prototype.toBlob (replace from HTMLCanvasElement.prototype.toDataURL) - Use Promise (replace callback interface) --- app/javascript/mastodon/actions/compose.js | 92 ++----------------- .../actions/push_notifications/registerer.js | 9 +- app/javascript/mastodon/base_polyfills.js | 21 +++++ app/javascript/mastodon/load_polyfills.js | 7 +- .../mastodon/utils/__tests__/base64-test.js | 10 ++ app/javascript/mastodon/utils/base64.js | 10 ++ app/javascript/mastodon/utils/resize_image.js | 66 +++++++++++++ 7 files changed, 120 insertions(+), 95 deletions(-) create mode 100644 app/javascript/mastodon/utils/__tests__/base64-test.js create mode 100644 app/javascript/mastodon/utils/base64.js create mode 100644 app/javascript/mastodon/utils/resize_image.js diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index c8ea5aaba..fe3e831d5 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -4,6 +4,7 @@ import { throttle } from 'lodash'; import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light'; import { tagHistory } from '../settings'; import { useEmoji } from './emojis'; +import resizeImage from '../utils/resize_image'; import { importFetchedAccounts } from './importer'; import { updateTimeline } from './timelines'; import { showAlertForError } from './alerts'; @@ -174,79 +175,6 @@ export function submitComposeFail(error) { }; }; -const MAX_IMAGE_DIMENSION = 1280; - -const dataURLtoBlob = dataURL => { - const BASE64_MARKER = ';base64,'; - - if (dataURL.indexOf(BASE64_MARKER) === -1) { - const parts = dataURL.split(','); - const contentType = parts[0].split(':')[1]; - const raw = parts[1]; - - return new Blob([raw], { type: contentType }); - } - - const parts = dataURL.split(BASE64_MARKER); - const contentType = parts[0].split(':')[1]; - const raw = window.atob(parts[1]); - const rawLength = raw.length; - - const uInt8Array = new Uint8Array(rawLength); - - for (let i = 0; i < rawLength; ++i) { - uInt8Array[i] = raw.charCodeAt(i); - } - - return new Blob([uInt8Array], { type: contentType }); -}; - -const resizeImage = (inputFile, callback) => { - if (inputFile.type.match(/image.*/) && inputFile.type !== 'image/gif') { - const reader = new FileReader(); - - reader.onload = e => { - const img = new Image(); - - img.onload = () => { - const canvas = document.createElement('canvas'); - const { width, height } = img; - - let newWidth, newHeight; - - if (width < MAX_IMAGE_DIMENSION && height < MAX_IMAGE_DIMENSION) { - callback(inputFile); - return; - } - - if (width > height) { - newHeight = height * MAX_IMAGE_DIMENSION / width; - newWidth = MAX_IMAGE_DIMENSION; - } else if (height > width) { - newWidth = width * MAX_IMAGE_DIMENSION / height; - newHeight = MAX_IMAGE_DIMENSION; - } else { - newWidth = MAX_IMAGE_DIMENSION; - newHeight = MAX_IMAGE_DIMENSION; - } - - canvas.width = newWidth; - canvas.height = newHeight; - - canvas.getContext('2d').drawImage(img, 0, 0, newWidth, newHeight); - - callback(dataURLtoBlob(canvas.toDataURL(inputFile.type))); - }; - - img.src = e.target.result; - }; - - reader.readAsDataURL(inputFile); - } else { - callback(inputFile); - } -}; - export function uploadCompose(files) { return function (dispatch, getState) { if (getState().getIn(['compose', 'media_attachments']).size > 3) { @@ -255,20 +183,14 @@ export function uploadCompose(files) { dispatch(uploadComposeRequest()); - resizeImage(files[0], file => { - let data = new FormData(); + resizeImage(files[0]).then(file => { + const data = new FormData(); data.append('file', file); - api(getState).post('/api/v1/media', data, { - onUploadProgress: function (e) { - dispatch(uploadComposeProgress(e.loaded, e.total)); - }, - }).then(function (response) { - dispatch(uploadComposeSuccess(response.data)); - }).catch(function (error) { - dispatch(uploadComposeFail(error)); - }); - }); + return api(getState).post('/api/v1/media', data, { + onUploadProgress: ({ loaded, total }) => dispatch(uploadComposeProgress(loaded, total)), + }).then(({ data }) => dispatch(uploadComposeSuccess(data))); + }).catch(error => dispatch(uploadComposeFail(error))); }; }; diff --git a/app/javascript/mastodon/actions/push_notifications/registerer.js b/app/javascript/mastodon/actions/push_notifications/registerer.js index 60b215f02..82fe4519a 100644 --- a/app/javascript/mastodon/actions/push_notifications/registerer.js +++ b/app/javascript/mastodon/actions/push_notifications/registerer.js @@ -1,4 +1,5 @@ import api from '../../api'; +import { decode as decodeBase64 } from '../../utils/base64'; import { pushNotificationsSetting } from '../../settings'; import { setBrowserSupport, setSubscription, clearSubscription } from './setter'; import { me } from '../../initial_state'; @@ -10,13 +11,7 @@ const urlBase64ToUint8Array = (base64String) => { .replace(/\-/g, '+') .replace(/_/g, '/'); - const rawData = window.atob(base64); - const outputArray = new Uint8Array(rawData.length); - - for (let i = 0; i < rawData.length; ++i) { - outputArray[i] = rawData.charCodeAt(i); - } - return outputArray; + return decodeBase64(base64); }; const getApplicationServerKey = () => document.querySelector('[name="applicationServerKey"]').getAttribute('content'); diff --git a/app/javascript/mastodon/base_polyfills.js b/app/javascript/mastodon/base_polyfills.js index 8fbb17785..997813a04 100644 --- a/app/javascript/mastodon/base_polyfills.js +++ b/app/javascript/mastodon/base_polyfills.js @@ -5,6 +5,7 @@ import includes from 'array-includes'; import assign from 'object-assign'; import values from 'object.values'; import isNaN from 'is-nan'; +import { decode as decodeBase64 } from './utils/base64'; if (!Array.prototype.includes) { includes.shim(); @@ -21,3 +22,23 @@ if (!Object.values) { if (!Number.isNaN) { Number.isNaN = isNaN; } + +if (!HTMLCanvasElement.prototype.toBlob) { + const BASE64_MARKER = ';base64,'; + + Object.defineProperty(HTMLCanvasElement.prototype, 'toBlob', { + value(callback, type = 'image/png', quality) { + const dataURL = this.toDataURL(type, quality); + let data; + + if (dataURL.indexOf(BASE64_MARKER) >= 0) { + const [, base64] = dataURL.split(BASE64_MARKER); + data = decodeBase64(base64); + } else { + [, data] = dataURL.split(','); + } + + callback(new Blob([data], { type })); + }, + }); +} diff --git a/app/javascript/mastodon/load_polyfills.js b/app/javascript/mastodon/load_polyfills.js index 815e1905b..8cb81c1a6 100644 --- a/app/javascript/mastodon/load_polyfills.js +++ b/app/javascript/mastodon/load_polyfills.js @@ -12,12 +12,13 @@ function importExtraPolyfills() { function loadPolyfills() { const needsBasePolyfills = !( + Array.prototype.includes && + HTMLCanvasElement.prototype.toBlob && window.Intl && + Number.isNaN && Object.assign && Object.values && - Number.isNaN && - window.Symbol && - Array.prototype.includes + window.Symbol ); // Latest version of Firefox and Safari do not have IntersectionObserver. diff --git a/app/javascript/mastodon/utils/__tests__/base64-test.js b/app/javascript/mastodon/utils/__tests__/base64-test.js new file mode 100644 index 000000000..1b3260faa --- /dev/null +++ b/app/javascript/mastodon/utils/__tests__/base64-test.js @@ -0,0 +1,10 @@ +import * as base64 from '../base64'; + +describe('base64', () => { + describe('decode', () => { + it('returns a uint8 array', () => { + const arr = base64.decode('dGVzdA=='); + expect(arr).toEqual(new Uint8Array([116, 101, 115, 116])); + }); + }); +}); diff --git a/app/javascript/mastodon/utils/base64.js b/app/javascript/mastodon/utils/base64.js new file mode 100644 index 000000000..8226e2c54 --- /dev/null +++ b/app/javascript/mastodon/utils/base64.js @@ -0,0 +1,10 @@ +export const decode = base64 => { + const rawData = window.atob(base64); + const outputArray = new Uint8Array(rawData.length); + + for (let i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i); + } + + return outputArray; +}; diff --git a/app/javascript/mastodon/utils/resize_image.js b/app/javascript/mastodon/utils/resize_image.js new file mode 100644 index 000000000..6442eda38 --- /dev/null +++ b/app/javascript/mastodon/utils/resize_image.js @@ -0,0 +1,66 @@ +const MAX_IMAGE_DIMENSION = 1280; + +const getImageUrl = inputFile => new Promise((resolve, reject) => { + if (window.URL && URL.createObjectURL) { + try { + resolve(URL.createObjectURL(inputFile)); + } catch (error) { + reject(error); + } + return; + } + + const reader = new FileReader(); + reader.onerror = (...args) => reject(...args); + reader.onload = ({ target }) => resolve(target.result); + + reader.readAsDataURL(inputFile); +}); + +const loadImage = inputFile => new Promise((resolve, reject) => { + getImageUrl(inputFile).then(url => { + const img = new Image(); + + img.onerror = (...args) => reject(...args); + img.onload = () => resolve(img); + + img.src = url; + }).catch(reject); +}); + +export default inputFile => new Promise((resolve, reject) => { + if (!inputFile.type.match(/image.*/) || inputFile.type === 'image/gif') { + resolve(inputFile); + return; + } + + loadImage(inputFile).then(img => { + const canvas = document.createElement('canvas'); + const { width, height } = img; + + let newWidth, newHeight; + + if (width < MAX_IMAGE_DIMENSION && height < MAX_IMAGE_DIMENSION) { + resolve(inputFile); + return; + } + + if (width > height) { + newHeight = height * MAX_IMAGE_DIMENSION / width; + newWidth = MAX_IMAGE_DIMENSION; + } else if (height > width) { + newWidth = width * MAX_IMAGE_DIMENSION / height; + newHeight = MAX_IMAGE_DIMENSION; + } else { + newWidth = MAX_IMAGE_DIMENSION; + newHeight = MAX_IMAGE_DIMENSION; + } + + canvas.width = newWidth; + canvas.height = newHeight; + + canvas.getContext('2d').drawImage(img, 0, 0, newWidth, newHeight); + + canvas.toBlob(resolve, inputFile.type); + }).catch(reject); +}); From 3bf6da1ffcf8208a0608de7bff6e2155c40e2871 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Mon, 23 Apr 2018 16:16:26 +0900 Subject: [PATCH 135/442] Move precompile step to build stage (#7235) --- .circleci/config.yml | 55 ++++++++++++++++++++++++++++++++------------ 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c5d6ec9d1..fbaf60aad 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -23,6 +23,13 @@ aliases: paths: - ./mastodon/ + - &restore_ruby_dependencies + restore_cache: + keys: + - v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} + - v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}- + - v2-ruby-dependencies-- + - &install_steps steps: - checkout @@ -54,26 +61,14 @@ aliases: - *install_system_dependencies - run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version - - restore_cache: - keys: - - v1-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} - - v1-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}- - - v1-ruby-dependencies-- + - *restore_ruby_dependencies - run: bundle install --clean --jobs 16 --path ./vendor/bundle/ --retry 3 --with pam_authentication --without development production - save_cache: - key: v1-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} + key: v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} paths: + - ./.bundle/ - ./vendor/bundle/ - - run: - name: Precompile Assets - command: | - if [ ! -d ./public/assets/ -o ! -d ./public/packs-test/ ]; then - ./bin/rails assets:precompile - fi - - - *persist_to_workspace - - &test_steps steps: - *attach_workspace @@ -81,6 +76,15 @@ aliases: - *install_system_dependencies - run: sudo apt-get install -y ffmpeg + - run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version + - *restore_ruby_dependencies + + - restore_cache: + keys: + - precompiled-assets-{{ .Branch }}-{{ .Revision }} + - precompiled-assets-{{ .Branch }}- + - precompiled-assets-- + - run: name: Prepare Tests command: ./bin/rails parallel:create parallel:load_schema parallel:prepare @@ -104,6 +108,20 @@ jobs: environment: *ruby_environment <<: *install_ruby_dependencies + build: + <<: *defaults + steps: + - *attach_workspace + - *install_system_dependencies + - run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version + - *restore_ruby_dependencies + - run: ./bin/rails assets:precompile + - save_cache: + key: precompiled-assets-{{ .Branch }}-{{ .Revision }} + paths: + - ./public/assets + - ./public/packs-test/ + test-ruby2.5: <<: *defaults docker: @@ -138,6 +156,8 @@ jobs: <<: *defaults steps: - *attach_workspace + - run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version + - *restore_ruby_dependencies - run: bundle exec i18n-tasks check-normalized - run: bundle exec i18n-tasks unused @@ -150,14 +170,19 @@ workflows: requires: - install - install-ruby2.4: + requires: + - install + - build: requires: - install-ruby2.5 - test-ruby2.5: requires: - install-ruby2.5 + - build - test-ruby2.4: requires: - install-ruby2.4 + - build - test-webui: requires: - install From 7db7d68136d8c58c6d354e85096137c39d421671 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 23 Apr 2018 09:16:38 +0200 Subject: [PATCH 136/442] Detect and prevent image bombs, max. processable dimension 4096^2 (#7229) --- app/lib/exceptions.rb | 1 + app/models/concerns/attachmentable.rb | 32 ++++++++++++++++++++++++--- app/models/custom_emoji.rb | 2 ++ app/models/media_attachment.rb | 16 ++------------ 4 files changed, 34 insertions(+), 17 deletions(-) diff --git a/app/lib/exceptions.rb b/app/lib/exceptions.rb index e88e98eae..01346bfe5 100644 --- a/app/lib/exceptions.rb +++ b/app/lib/exceptions.rb @@ -6,6 +6,7 @@ module Mastodon class ValidationError < Error; end class HostValidationError < ValidationError; end class LengthValidationError < ValidationError; end + class DimensionsValidationError < ValidationError; end class RaceConditionError < Error; end class UnexpectedResponseError < Error diff --git a/app/models/concerns/attachmentable.rb b/app/models/concerns/attachmentable.rb index 90ce88463..6f8489b89 100644 --- a/app/models/concerns/attachmentable.rb +++ b/app/models/concerns/attachmentable.rb @@ -1,10 +1,15 @@ # frozen_string_literal: true +require 'mime/types' + module Attachmentable extend ActiveSupport::Concern + MAX_MATRIX_LIMIT = 16_777_216 # 4096x4096px or approx. 16MB + included do before_post_process :set_file_extensions + before_post_process :check_image_dimensions end private @@ -12,10 +17,31 @@ module Attachmentable def set_file_extensions self.class.attachment_definitions.each_key do |attachment_name| attachment = send(attachment_name) + next if attachment.blank? - extension = Paperclip::Interpolations.content_type_extension(attachment, :original) - basename = Paperclip::Interpolations.basename(attachment, :original) - attachment.instance_write :file_name, [basename, extension].delete_if(&:blank?).join('.') + + attachment.instance_write :file_name, [Paperclip::Interpolations.basename(attachment, :original), appropriate_extension(attachment)].delete_if(&:blank?).join('.') end end + + def check_image_dimensions + self.class.attachment_definitions.each_key do |attachment_name| + attachment = send(attachment_name) + + next if attachment.blank? || !attachment.content_type.match?(/image.*/) || attachment.queued_for_write[:original].blank? + + width, height = FastImage.size(attachment.queued_for_write[:original].path) + + raise Mastodon::DimensionsValidationError, "#{width}x#{height} images are not supported" if width.present? && height.present? && (width * height >= MAX_MATRIX_LIMIT) + end + end + + def appropriate_extension(attachment) + mime_type = MIME::Types[attachment.content_type] + + extensions_for_mime_type = mime_type.empty? ? [] : mime_type.first.extensions + original_extension = Paperclip::Interpolations.extension(attachment, :original) + + extensions_for_mime_type.include?(original_extension) ? original_extension : extensions_for_mime_type.first + end end diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 1ec21d1a0..2dd3cac61 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -40,6 +40,8 @@ class CustomEmoji < ApplicationRecord remotable_attachment :image, LIMIT + include Attachmentable + def local? domain.nil? end diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index 8fd9ac09f..c9abab9e2 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -19,8 +19,6 @@ # description :text # -require 'mime/types' - class MediaAttachment < ApplicationRecord self.inheritance_column = nil @@ -70,6 +68,8 @@ class MediaAttachment < ApplicationRecord validates_attachment_size :file, less_than: LIMIT remotable_attachment :file, LIMIT + include Attachmentable + validates :account, presence: true validates :description, length: { maximum: 420 }, if: :local? @@ -176,9 +176,6 @@ class MediaAttachment < ApplicationRecord def set_type_and_extension self.type = VIDEO_MIME_TYPES.include?(file_content_type) ? :video : :image - extension = appropriate_extension - basename = Paperclip::Interpolations.basename(file, :original) - file.instance_write :file_name, [basename, extension].delete_if(&:blank?).join('.') end def set_meta @@ -223,13 +220,4 @@ class MediaAttachment < ApplicationRecord bitrate: movie.bitrate, } end - - def appropriate_extension - mime_type = MIME::Types[file.content_type] - - extensions_for_mime_type = mime_type.empty? ? [] : mime_type.first.extensions - original_extension = Paperclip::Interpolations.extension(file, :original) - - extensions_for_mime_type.include?(original_extension) ? original_extension : extensions_for_mime_type.first - end end From 9613a53cb36a2d4cb1dd1c9a1eced0f61b7970dc Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Mon, 23 Apr 2018 18:29:17 +0900 Subject: [PATCH 137/442] Update dependencies for Ruby (2018-04-23) (#7237) * Update annotate to version 2.7.3 * Update aws-sdk-s3 to version 1.9.2 * Update browser to version 2.5.3 * Update capistrano to version 3.10.2 * Update domain_name to version 0.5.20180417 * Update http to version 3.2.0 * Update lograge to version 0.10.0 * Update oj to version 3.5.1 * Update parallel_tests to version 2.21.3 * Update puma to version 3.11.4 * Update rubocop to version 0.55.0 * Update scss_lint to version 0.57.0 * Update simplecov to version 0.16.1 * Update tty-command to version 0.8.0 * Update tty-prompt to version 0.16.0 * Update pkg-config to version 1.3.0 * Update fog-local to version 0.5.0 * Update fog-openstack to version 0.1.25 * Update devise-two-factor to version 3.0.3 * bundle update --- Gemfile | 28 +++--- Gemfile.lock | 136 ++++++++++++-------------- app/models/account.rb | 4 +- app/models/account_domain_block.rb | 4 +- app/models/account_moderation_note.rb | 6 +- app/models/admin/action_log.rb | 6 +- app/models/backup.rb | 4 +- app/models/block.rb | 6 +- app/models/conversation.rb | 2 +- app/models/conversation_mute.rb | 6 +- app/models/custom_emoji.rb | 2 +- app/models/domain_block.rb | 2 +- app/models/email_domain_block.rb | 2 +- app/models/favourite.rb | 6 +- app/models/follow.rb | 6 +- app/models/follow_request.rb | 6 +- app/models/import.rb | 4 +- app/models/invite.rb | 4 +- app/models/list.rb | 4 +- app/models/list_account.rb | 8 +- app/models/media_attachment.rb | 6 +- app/models/mention.rb | 6 +- app/models/mute.rb | 6 +- app/models/notification.rb | 8 +- app/models/preview_card.rb | 2 +- app/models/report.rb | 12 +-- app/models/report_note.rb | 6 +- app/models/session_activation.rb | 8 +- app/models/setting.rb | 4 +- app/models/site_upload.rb | 2 +- app/models/status.rb | 14 +-- app/models/status_pin.rb | 6 +- app/models/stream_entry.rb | 6 +- app/models/subscription.rb | 4 +- app/models/tag.rb | 2 +- app/models/user.rb | 6 +- app/models/web/push_subscription.rb | 2 +- app/models/web/setting.rb | 4 +- config/deploy.rb | 2 +- 39 files changed, 173 insertions(+), 179 deletions(-) diff --git a/Gemfile b/Gemfile index 8055f1561..a33748568 100644 --- a/Gemfile +++ b/Gemfile @@ -3,7 +3,7 @@ source 'https://rubygems.org' ruby '>= 2.3.0', '< 2.6.0' -gem 'pkg-config', '~> 1.2' +gem 'pkg-config', '~> 1.3' gem 'puma', '~> 3.11' gem 'rails', '~> 5.2.0' @@ -11,11 +11,11 @@ gem 'rails', '~> 5.2.0' gem 'hamlit-rails', '~> 0.2' gem 'pg', '~> 1.0' gem 'pghero', '~> 2.1' -gem 'dotenv-rails', '~> 2.2' +gem 'dotenv-rails', '~> 2.2', '< 2.3' -gem 'aws-sdk-s3', '~> 1.8', require: false +gem 'aws-sdk-s3', '~> 1.9', require: false gem 'fog-core', '~> 1.45' -gem 'fog-local', '~> 0.4', require: false +gem 'fog-local', '~> 0.5', require: false gem 'fog-openstack', '~> 0.1', require: false gem 'paperclip', '~> 6.0' gem 'paperclip-av-transcoder', '~> 0.6' @@ -30,7 +30,7 @@ gem 'iso-639' gem 'chewy', '~> 5.0' gem 'cld3', '~> 3.2.0' gem 'devise', '~> 4.4' -gem 'devise-two-factor', '~> 3.0', git: 'https://github.com/ykzts/devise-two-factor.git', branch: 'rails-5.2' +gem 'devise-two-factor', '~> 3.0' group :pam_authentication, optional: true do gem 'devise_pam_authenticatable2', '~> 9.1' @@ -48,7 +48,7 @@ gem 'goldfinger', '~> 2.1' gem 'hiredis', '~> 0.6' gem 'redis-namespace', '~> 1.5' gem 'htmlentities', '~> 4.3' -gem 'http', '~> 3.0' +gem 'http', '~> 3.2' gem 'http_accept_language', '~> 2.1' gem 'httplog', '~> 1.0' gem 'idn-ruby', require: 'idn' @@ -57,9 +57,9 @@ gem 'link_header', '~> 0.0' gem 'mime-types', '~> 3.1' gem 'nokogiri', '~> 1.8' gem 'nsa', '~> 0.2' -gem 'oj', '~> 3.4' +gem 'oj', '~> 3.5' gem 'ostatus2', '~> 2.0' -gem 'ox', '~> 2.8' +gem 'ox', '~> 2.9' gem 'pundit', '~> 1.1' gem 'premailer-rails' gem 'rack-attack', '~> 5.2' @@ -82,8 +82,8 @@ gem 'simple_form', '~> 4.0' gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' gem 'stoplight', '~> 2.1.3' gem 'strong_migrations', '~> 0.2' -gem 'tty-command' -gem 'tty-prompt' +gem 'tty-command', '~> 0.8' +gem 'tty-prompt', '~> 0.16' gem 'twitter-text', '~> 1.14' gem 'tzinfo-data', '~> 1.2018' gem 'webpacker', '~> 3.4' @@ -112,7 +112,7 @@ group :test do gem 'rails-controller-testing', '~> 1.0' gem 'rspec-sidekiq', '~> 3.0' gem 'rspec-retry', '~> 0.5', require: false - gem 'simplecov', '~> 0.14', require: false + gem 'simplecov', '~> 0.16', require: false gem 'webmock', '~> 3.3' gem 'parallel_tests', '~> 2.21' end @@ -126,10 +126,10 @@ group :development do gem 'letter_opener', '~> 1.4' gem 'letter_opener_web', '~> 1.3' gem 'memory_profiler' - gem 'rubocop', require: false + gem 'rubocop', '~> 0.55', require: false gem 'brakeman', '~> 4.2', require: false gem 'bundler-audit', '~> 0.6', require: false - gem 'scss_lint', '~> 0.55', require: false + gem 'scss_lint', '~> 0.57', require: false gem 'capistrano', '~> 3.10' gem 'capistrano-rails', '~> 1.3' @@ -138,6 +138,6 @@ group :development do end group :production do - gem 'lograge', '~> 0.9' + gem 'lograge', '~> 0.10' gem 'redis-rails', '~> 5.0' end diff --git a/Gemfile.lock b/Gemfile.lock index eeb4bf17e..d96165dcf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,15 +1,3 @@ -GIT - remote: https://github.com/ykzts/devise-two-factor.git - revision: f60492b29c174d4c959ac02406392f8eb9c4d374 - branch: rails-5.2 - specs: - devise-two-factor (3.0.2) - activesupport (< 5.3) - attr_encrypted (>= 1.3, < 4, != 2) - devise (~> 4.0) - railties (< 5.3) - rotp (~> 2.0) - GEM remote: https://rubygems.org/ specs: @@ -64,7 +52,7 @@ GEM public_suffix (>= 2.0.2, < 4.0) airbrussh (1.3.0) sshkit (>= 1.6.1, != 1.7.0) - annotate (2.7.2) + annotate (2.7.3) activerecord (>= 3.2, < 6.0) rake (>= 10.4, < 13.0) arel (9.0.0) @@ -73,15 +61,15 @@ GEM encryptor (~> 3.0.0) av (0.9.0) cocaine (~> 0.5.3) - aws-partitions (1.70.0) - aws-sdk-core (3.17.0) + aws-partitions (1.80.0) + aws-sdk-core (3.19.0) aws-partitions (~> 1.0) aws-sigv4 (~> 1.0) jmespath (~> 1.0) aws-sdk-kms (1.5.0) aws-sdk-core (~> 3) aws-sigv4 (~> 1.0) - aws-sdk-s3 (1.8.2) + aws-sdk-s3 (1.9.1) aws-sdk-core (~> 3) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.0) @@ -96,7 +84,7 @@ GEM bootsnap (1.3.0) msgpack (~> 1.0) brakeman (4.2.1) - browser (2.5.2) + browser (2.5.3) builder (3.2.3) bullet (5.7.5) activesupport (>= 3.0.0) @@ -104,7 +92,7 @@ GEM bundler-audit (0.6.0) bundler (~> 1.2) thor (~> 0.18) - capistrano (3.10.1) + capistrano (3.10.2) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) @@ -156,12 +144,18 @@ GEM railties (>= 4.1.0, < 6.0) responders warden (~> 1.2.3) + devise-two-factor (3.0.3) + activesupport (< 5.3) + attr_encrypted (>= 1.3, < 4, != 2) + devise (~> 4.0) + railties (< 5.3) + rotp (~> 2.0) devise_pam_authenticatable2 (9.1.0) devise (>= 4.0.0) rpam2 (~> 4.0) diff-lcs (1.3) - docile (1.1.5) - domain_name (0.5.20170404) + docile (1.3.0) + domain_name (0.5.20180417) unf (>= 0.0.5, < 1.0.0) doorkeeper (4.3.2) railties (>= 4.2) @@ -172,29 +166,29 @@ GEM easy_translate (0.5.1) thread thread_safe - elasticsearch (6.0.1) - elasticsearch-api (= 6.0.1) - elasticsearch-transport (= 6.0.1) - elasticsearch-api (6.0.1) + elasticsearch (6.0.2) + elasticsearch-api (= 6.0.2) + elasticsearch-transport (= 6.0.2) + elasticsearch-api (6.0.2) multi_json elasticsearch-dsl (0.1.5) - elasticsearch-transport (6.0.1) + elasticsearch-transport (6.0.2) faraday multi_json encryptor (3.0.0) equatable (0.5.0) erubi (1.7.1) - et-orbi (1.0.9) + et-orbi (1.1.0) tzinfo - excon (0.60.0) + excon (0.62.0) fabrication (2.20.1) faker (1.8.7) i18n (>= 0.7) - faraday (0.14.0) + faraday (0.15.0) multipart-post (>= 1.2, < 3) fast_blank (1.0.0) fastimage (2.1.1) - ffi (1.9.21) + ffi (1.9.23) fog-core (1.45.0) builder excon (~> 0.58) @@ -202,9 +196,9 @@ GEM fog-json (1.0.2) fog-core (~> 1.0) multi_json (~> 1.10) - fog-local (0.4.0) - fog-core (~> 1.27) - fog-openstack (0.1.23) + fog-local (0.5.0) + fog-core (>= 1.27, < 3.0) + fog-openstack (0.1.25) fog-core (~> 1.40) fog-json (>= 1.0) ipaddress (>= 0.8) @@ -237,20 +231,20 @@ GEM hitimes (1.2.6) hkdf (0.3.0) htmlentities (4.3.4) - http (3.0.0) + http (3.2.0) addressable (~> 2.3) http-cookie (~> 1.0) - http-form_data (>= 2.0.0.pre.pre2, < 3) + http-form_data (~> 2.0) http_parser.rb (~> 0.6.0) http-cookie (1.0.3) domain_name (~> 0.5) - http-form_data (2.0.0) + http-form_data (2.1.0) http_accept_language (2.1.1) http_parser.rb (0.6.0) httplog (1.0.2) colorize (~> 0.8) rack (>= 1.0) - i18n (1.0.0) + i18n (1.0.1) concurrent-ruby (~> 1.0) i18n-tasks (0.9.21) activesupport (>= 4.0.2) @@ -265,7 +259,7 @@ GEM idn-ruby (0.1.0) ipaddress (0.8.3) iso-639 (0.2.8) - jmespath (1.3.1) + jmespath (1.4.0) json (2.1.0) json-ld (2.2.1) multi_json (~> 1.12) @@ -297,7 +291,7 @@ GEM letter_opener (~> 1.0) railties (>= 3.2) link_header (0.0.8) - lograge (0.9.0) + lograge (0.10.0) actionpack (>= 4) activesupport (>= 4) railties (>= 4) @@ -341,7 +335,7 @@ GEM concurrent-ruby (~> 1.0.0) sidekiq (>= 3.5.0) statsd-ruby (~> 1.2.0) - oj (3.4.0) + oj (3.5.1) omniauth (1.8.1) hashie (>= 3.4.6, < 3.6.0) rack (>= 1.6.2, < 3) @@ -357,7 +351,7 @@ GEM addressable (~> 2.5) http (~> 3.0) nokogiri (~> 1.8) - ox (2.8.2) + ox (2.9.2) paperclip (6.0.0) activemodel (>= 4.2.0) activesupport (>= 4.2.0) @@ -368,7 +362,7 @@ GEM av (~> 0.9.0) paperclip (>= 2.5.2) parallel (1.12.1) - parallel_tests (2.21.1) + parallel_tests (2.21.3) parallel parser (2.5.1.0) ast (~> 2.4.0) @@ -378,7 +372,7 @@ GEM pg (1.0.0) pghero (2.1.0) activerecord - pkg-config (1.2.9) + pkg-config (1.3.0) powerpack (0.1.1) premailer (1.11.1) addressable @@ -394,7 +388,7 @@ GEM pry-rails (0.3.6) pry (>= 0.10.4) public_suffix (3.0.2) - puma (3.11.3) + puma (3.11.4) pundit (1.1.0) activesupport (>= 3.0.0) rack (2.0.4) @@ -443,10 +437,10 @@ GEM thor (>= 0.18.1, < 2.0) rainbow (3.0.0) rake (12.3.1) - rb-fsevent (0.10.2) + rb-fsevent (0.10.3) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) - rdf (3.0.1) + rdf (3.0.2) hamster (~> 3.0) link_header (~> 0.0, >= 0.0.8) rdf-normalize (0.3.3) @@ -468,9 +462,9 @@ GEM redis-actionpack (>= 5.0, < 6) redis-activesupport (>= 5.0, < 6) redis-store (>= 1.2, < 2) - redis-store (1.4.1) + redis-store (1.5.0) redis (>= 2.2, < 5) - request_store (1.4.0) + request_store (1.4.1) rack (>= 1.4) responders (2.4.0) actionpack (>= 4.2.0, < 5.3) @@ -501,9 +495,9 @@ GEM rspec-core (~> 3.0, >= 3.0.0) sidekiq (>= 2.4.0) rspec-support (3.7.1) - rubocop (0.52.1) + rubocop (0.55.0) parallel (~> 1.10) - parser (>= 2.4.0.2, < 3.0) + parser (>= 2.5) powerpack (~> 0.1) rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) @@ -519,14 +513,14 @@ GEM crass (~> 1.0.2) nokogiri (>= 1.4.4) nokogumbo (~> 1.4) - sass (3.5.5) + sass (3.5.6) sass-listen (~> 4.0.0) sass-listen (4.0.0) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) - scss_lint (0.56.0) + scss_lint (0.57.0) rake (>= 0.9, < 13) - sass (~> 3.5.3) + sass (~> 3.5.5) sidekiq (5.1.3) concurrent-ruby (~> 1.0) connection_pool (~> 2.2, >= 2.2.0) @@ -548,8 +542,8 @@ GEM simple_form (4.0.0) actionpack (> 4) activemodel (> 4) - simplecov (0.15.1) - docile (~> 1.1.0) + simplecov (0.16.1) + docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) @@ -581,10 +575,10 @@ GEM timers (4.1.2) hitimes tty-color (0.4.2) - tty-command (0.7.0) + tty-command (0.8.0) pastel (~> 0.7.0) tty-cursor (0.5.0) - tty-prompt (0.15.0) + tty-prompt (0.16.0) necromancer (~> 0.4.0) pastel (~> 0.7.0) timers (~> 4.0) @@ -604,7 +598,7 @@ GEM unf (0.1.4) unf_ext unf_ext (0.0.7.5) - unicode-display_width (1.3.0) + unicode-display_width (1.3.2) uniform_notifier (1.11.0) warden (1.2.7) rack (>= 1.0) @@ -634,7 +628,7 @@ DEPENDENCIES active_record_query_trace (~> 1.5) addressable (~> 2.5) annotate (~> 2.7) - aws-sdk-s3 (~> 1.8) + aws-sdk-s3 (~> 1.9) better_errors (~> 2.4) binding_of_caller (~> 0.7) bootsnap (~> 1.3) @@ -652,23 +646,23 @@ DEPENDENCIES cld3 (~> 3.2.0) climate_control (~> 0.2) devise (~> 4.4) - devise-two-factor (~> 3.0)! + devise-two-factor (~> 3.0) devise_pam_authenticatable2 (~> 9.1) doorkeeper (~> 4.3) - dotenv-rails (~> 2.2) + dotenv-rails (~> 2.2, < 2.3) fabrication (~> 2.20) faker (~> 1.8) fast_blank (~> 1.0) fastimage fog-core (~> 1.45) - fog-local (~> 0.4) + fog-local (~> 0.5) fog-openstack (~> 0.1) fuubar (~> 2.2) goldfinger (~> 2.1) hamlit-rails (~> 0.2) hiredis (~> 0.6) htmlentities (~> 4.3) - http (~> 3.0) + http (~> 3.2) http_accept_language (~> 2.1) httplog (~> 1.0) i18n-tasks (~> 0.9) @@ -679,7 +673,7 @@ DEPENDENCIES letter_opener (~> 1.4) letter_opener_web (~> 1.3) link_header (~> 0.0) - lograge (~> 0.9) + lograge (~> 0.10) mario-redis-lock (~> 1.2) memory_profiler microformats (~> 4.0) @@ -687,18 +681,18 @@ DEPENDENCIES net-ldap (~> 0.10) nokogiri (~> 1.8) nsa (~> 0.2) - oj (~> 3.4) + oj (~> 3.5) omniauth (~> 1.2) omniauth-cas (~> 1.1) omniauth-saml (~> 1.10) ostatus2 (~> 2.0) - ox (~> 2.8) + ox (~> 2.9) paperclip (~> 6.0) paperclip-av-transcoder (~> 0.6) parallel_tests (~> 2.21) pg (~> 1.0) pghero (~> 2.1) - pkg-config (~> 1.2) + pkg-config (~> 1.3) premailer-rails private_address_check (~> 0.4.1) pry-rails (~> 0.3) @@ -719,24 +713,24 @@ DEPENDENCIES rspec-rails (~> 3.7) rspec-retry (~> 0.5) rspec-sidekiq (~> 3.0) - rubocop + rubocop (~> 0.55) ruby-oembed (~> 0.12) ruby-progressbar (~> 1.4) sanitize (~> 4.6) - scss_lint (~> 0.55) + scss_lint (~> 0.57) sidekiq (~> 5.1) sidekiq-bulk (~> 0.1.1) sidekiq-scheduler (~> 2.2) sidekiq-unique-jobs (~> 5.0) simple-navigation (~> 4.0) simple_form (~> 4.0) - simplecov (~> 0.14) + simplecov (~> 0.16) sprockets-rails (~> 3.2) stoplight (~> 2.1.3) streamio-ffmpeg (~> 3.0) strong_migrations (~> 0.2) - tty-command - tty-prompt + tty-command (~> 0.8) + tty-prompt (~> 0.16) twitter-text (~> 1.14) tzinfo-data (~> 1.2018) webmock (~> 3.3) diff --git a/app/models/account.rb b/app/models/account.rb index a3436b47c..0a4370be4 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -3,7 +3,7 @@ # # Table name: accounts # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # username :string default(""), not null # domain :string # secret :string default(""), not null @@ -42,7 +42,7 @@ # followers_url :string default(""), not null # protocol :integer default("ostatus"), not null # memorial :boolean default(FALSE), not null -# moved_to_account_id :integer +# moved_to_account_id :bigint(8) # featured_collection_url :string # fields :jsonb # diff --git a/app/models/account_domain_block.rb b/app/models/account_domain_block.rb index bc00b4f32..e352000c3 100644 --- a/app/models/account_domain_block.rb +++ b/app/models/account_domain_block.rb @@ -3,11 +3,11 @@ # # Table name: account_domain_blocks # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # domain :string # created_at :datetime not null # updated_at :datetime not null -# account_id :integer +# account_id :bigint(8) # class AccountDomainBlock < ApplicationRecord diff --git a/app/models/account_moderation_note.rb b/app/models/account_moderation_note.rb index 3ac9b1ac1..22e312bb2 100644 --- a/app/models/account_moderation_note.rb +++ b/app/models/account_moderation_note.rb @@ -3,10 +3,10 @@ # # Table name: account_moderation_notes # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # content :text not null -# account_id :integer not null -# target_account_id :integer not null +# account_id :bigint(8) not null +# target_account_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/admin/action_log.rb b/app/models/admin/action_log.rb index 81f278e07..1d1db1b7a 100644 --- a/app/models/admin/action_log.rb +++ b/app/models/admin/action_log.rb @@ -3,11 +3,11 @@ # # Table name: admin_action_logs # -# id :integer not null, primary key -# account_id :integer +# id :bigint(8) not null, primary key +# account_id :bigint(8) # action :string default(""), not null # target_type :string -# target_id :integer +# target_id :bigint(8) # recorded_changes :text default(""), not null # created_at :datetime not null # updated_at :datetime not null diff --git a/app/models/backup.rb b/app/models/backup.rb index 5a7e6a14d..c2651313b 100644 --- a/app/models/backup.rb +++ b/app/models/backup.rb @@ -3,8 +3,8 @@ # # Table name: backups # -# id :integer not null, primary key -# user_id :integer +# id :bigint(8) not null, primary key +# user_id :bigint(8) # dump_file_name :string # dump_content_type :string # dump_file_size :integer diff --git a/app/models/block.rb b/app/models/block.rb index d6ecabd3b..df4a6bbac 100644 --- a/app/models/block.rb +++ b/app/models/block.rb @@ -3,11 +3,11 @@ # # Table name: blocks # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # created_at :datetime not null # updated_at :datetime not null -# account_id :integer not null -# target_account_id :integer not null +# account_id :bigint(8) not null +# target_account_id :bigint(8) not null # class Block < ApplicationRecord diff --git a/app/models/conversation.rb b/app/models/conversation.rb index 08c1ce945..4dfaea889 100644 --- a/app/models/conversation.rb +++ b/app/models/conversation.rb @@ -3,7 +3,7 @@ # # Table name: conversations # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # uri :string # created_at :datetime not null # updated_at :datetime not null diff --git a/app/models/conversation_mute.rb b/app/models/conversation_mute.rb index 272eb81af..52c1a33e0 100644 --- a/app/models/conversation_mute.rb +++ b/app/models/conversation_mute.rb @@ -3,9 +3,9 @@ # # Table name: conversation_mutes # -# id :integer not null, primary key -# conversation_id :integer not null -# account_id :integer not null +# id :bigint(8) not null, primary key +# conversation_id :bigint(8) not null +# account_id :bigint(8) not null # class ConversationMute < ApplicationRecord diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 2dd3cac61..8235332f1 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -3,7 +3,7 @@ # # Table name: custom_emojis # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # shortcode :string default(""), not null # domain :string # image_file_name :string diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index aea8919af..93658793b 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -3,7 +3,7 @@ # # Table name: domain_blocks # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # domain :string default(""), not null # created_at :datetime not null # updated_at :datetime not null diff --git a/app/models/email_domain_block.rb b/app/models/email_domain_block.rb index a104810d1..10490375b 100644 --- a/app/models/email_domain_block.rb +++ b/app/models/email_domain_block.rb @@ -3,7 +3,7 @@ # # Table name: email_domain_blocks # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # domain :string default(""), not null # created_at :datetime not null # updated_at :datetime not null diff --git a/app/models/favourite.rb b/app/models/favourite.rb index fa1884b86..c998a67eb 100644 --- a/app/models/favourite.rb +++ b/app/models/favourite.rb @@ -3,11 +3,11 @@ # # Table name: favourites # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # created_at :datetime not null # updated_at :datetime not null -# account_id :integer not null -# status_id :integer not null +# account_id :bigint(8) not null +# status_id :bigint(8) not null # class Favourite < ApplicationRecord diff --git a/app/models/follow.rb b/app/models/follow.rb index 8e6fe537a..2ca42ff70 100644 --- a/app/models/follow.rb +++ b/app/models/follow.rb @@ -3,11 +3,11 @@ # # Table name: follows # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # created_at :datetime not null # updated_at :datetime not null -# account_id :integer not null -# target_account_id :integer not null +# account_id :bigint(8) not null +# target_account_id :bigint(8) not null # show_reblogs :boolean default(TRUE), not null # diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb index cde26ceed..d559a8f62 100644 --- a/app/models/follow_request.rb +++ b/app/models/follow_request.rb @@ -3,11 +3,11 @@ # # Table name: follow_requests # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # created_at :datetime not null # updated_at :datetime not null -# account_id :integer not null -# target_account_id :integer not null +# account_id :bigint(8) not null +# target_account_id :bigint(8) not null # show_reblogs :boolean default(TRUE), not null # diff --git a/app/models/import.rb b/app/models/import.rb index fdb4c6b80..55e970b0d 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -3,7 +3,7 @@ # # Table name: imports # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # type :integer not null # approved :boolean default(FALSE), not null # created_at :datetime not null @@ -12,7 +12,7 @@ # data_content_type :string # data_file_size :integer # data_updated_at :datetime -# account_id :integer not null +# account_id :bigint(8) not null # class Import < ApplicationRecord diff --git a/app/models/invite.rb b/app/models/invite.rb index 4ba5432d2..2250e588e 100644 --- a/app/models/invite.rb +++ b/app/models/invite.rb @@ -3,8 +3,8 @@ # # Table name: invites # -# id :integer not null, primary key -# user_id :integer not null +# id :bigint(8) not null, primary key +# user_id :bigint(8) not null # code :string default(""), not null # expires_at :datetime # max_uses :integer diff --git a/app/models/list.rb b/app/models/list.rb index a2ec7e84a..c9c94fca1 100644 --- a/app/models/list.rb +++ b/app/models/list.rb @@ -3,8 +3,8 @@ # # Table name: lists # -# id :integer not null, primary key -# account_id :integer not null +# id :bigint(8) not null, primary key +# account_id :bigint(8) not null # title :string default(""), not null # created_at :datetime not null # updated_at :datetime not null diff --git a/app/models/list_account.rb b/app/models/list_account.rb index da46cf032..87b498224 100644 --- a/app/models/list_account.rb +++ b/app/models/list_account.rb @@ -3,10 +3,10 @@ # # Table name: list_accounts # -# id :integer not null, primary key -# list_id :integer not null -# account_id :integer not null -# follow_id :integer not null +# id :bigint(8) not null, primary key +# list_id :bigint(8) not null +# account_id :bigint(8) not null +# follow_id :bigint(8) not null # class ListAccount < ApplicationRecord diff --git a/app/models/media_attachment.rb b/app/models/media_attachment.rb index c9abab9e2..f9a8f322e 100644 --- a/app/models/media_attachment.rb +++ b/app/models/media_attachment.rb @@ -3,8 +3,8 @@ # # Table name: media_attachments # -# id :integer not null, primary key -# status_id :integer +# id :bigint(8) not null, primary key +# status_id :bigint(8) # file_file_name :string # file_content_type :string # file_file_size :integer @@ -15,7 +15,7 @@ # shortcode :string # type :integer default("image"), not null # file_meta :json -# account_id :integer +# account_id :bigint(8) # description :text # diff --git a/app/models/mention.rb b/app/models/mention.rb index f864bf8e1..8ab886b18 100644 --- a/app/models/mention.rb +++ b/app/models/mention.rb @@ -3,11 +3,11 @@ # # Table name: mentions # -# id :integer not null, primary key -# status_id :integer +# id :bigint(8) not null, primary key +# status_id :bigint(8) # created_at :datetime not null # updated_at :datetime not null -# account_id :integer +# account_id :bigint(8) # class Mention < ApplicationRecord diff --git a/app/models/mute.rb b/app/models/mute.rb index 8efa27ac0..0e00c2278 100644 --- a/app/models/mute.rb +++ b/app/models/mute.rb @@ -3,11 +3,11 @@ # # Table name: mutes # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # created_at :datetime not null # updated_at :datetime not null -# account_id :integer not null -# target_account_id :integer not null +# account_id :bigint(8) not null +# target_account_id :bigint(8) not null # hide_notifications :boolean default(TRUE), not null # diff --git a/app/models/notification.rb b/app/models/notification.rb index 0b0f01aa8..4f6ec8e8e 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -3,13 +3,13 @@ # # Table name: notifications # -# id :integer not null, primary key -# activity_id :integer not null +# id :bigint(8) not null, primary key +# activity_id :bigint(8) not null # activity_type :string not null # created_at :datetime not null # updated_at :datetime not null -# account_id :integer not null -# from_account_id :integer not null +# account_id :bigint(8) not null +# from_account_id :bigint(8) not null # class Notification < ApplicationRecord diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb index 0ffa6b10f..a792b352b 100644 --- a/app/models/preview_card.rb +++ b/app/models/preview_card.rb @@ -3,7 +3,7 @@ # # Table name: preview_cards # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # url :string default(""), not null # title :string default(""), not null # description :string default(""), not null diff --git a/app/models/report.rb b/app/models/report.rb index 5b90c7bce..efe385b2d 100644 --- a/app/models/report.rb +++ b/app/models/report.rb @@ -3,16 +3,16 @@ # # Table name: reports # -# id :integer not null, primary key -# status_ids :integer default([]), not null, is an Array +# id :bigint(8) not null, primary key +# status_ids :bigint(8) default([]), not null, is an Array # comment :text default(""), not null # action_taken :boolean default(FALSE), not null # created_at :datetime not null # updated_at :datetime not null -# account_id :integer not null -# action_taken_by_account_id :integer -# target_account_id :integer not null -# assigned_account_id :integer +# account_id :bigint(8) not null +# action_taken_by_account_id :bigint(8) +# target_account_id :bigint(8) not null +# assigned_account_id :bigint(8) # class Report < ApplicationRecord diff --git a/app/models/report_note.rb b/app/models/report_note.rb index 6d9dec80a..54b416577 100644 --- a/app/models/report_note.rb +++ b/app/models/report_note.rb @@ -3,10 +3,10 @@ # # Table name: report_notes # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # content :text not null -# report_id :integer not null -# account_id :integer not null +# report_id :bigint(8) not null +# account_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/session_activation.rb b/app/models/session_activation.rb index d364f03df..34d25c83d 100644 --- a/app/models/session_activation.rb +++ b/app/models/session_activation.rb @@ -3,15 +3,15 @@ # # Table name: session_activations # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # session_id :string not null # created_at :datetime not null # updated_at :datetime not null # user_agent :string default(""), not null # ip :inet -# access_token_id :integer -# user_id :integer not null -# web_push_subscription_id :integer +# access_token_id :bigint(8) +# user_id :bigint(8) not null +# web_push_subscription_id :bigint(8) # class SessionActivation < ApplicationRecord diff --git a/app/models/setting.rb b/app/models/setting.rb index df93590ce..033d09fd5 100644 --- a/app/models/setting.rb +++ b/app/models/setting.rb @@ -3,13 +3,13 @@ # # Table name: settings # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # var :string not null # value :text # thing_type :string # created_at :datetime # updated_at :datetime -# thing_id :integer +# thing_id :bigint(8) # class Setting < RailsSettings::Base diff --git a/app/models/site_upload.rb b/app/models/site_upload.rb index 641128adf..14d683767 100644 --- a/app/models/site_upload.rb +++ b/app/models/site_upload.rb @@ -3,7 +3,7 @@ # # Table name: site_uploads # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # var :string default(""), not null # file_file_name :string # file_content_type :string diff --git a/app/models/status.rb b/app/models/status.rb index 62857dd6b..ed4bcefca 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -3,13 +3,13 @@ # # Table name: statuses # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # uri :string # text :text default(""), not null # created_at :datetime not null # updated_at :datetime not null -# in_reply_to_id :integer -# reblog_of_id :integer +# in_reply_to_id :bigint(8) +# reblog_of_id :bigint(8) # url :string # sensitive :boolean default(FALSE), not null # visibility :integer default("public"), not null @@ -18,11 +18,11 @@ # favourites_count :integer default(0), not null # reblogs_count :integer default(0), not null # language :string -# conversation_id :integer +# conversation_id :bigint(8) # local :boolean -# account_id :integer not null -# application_id :integer -# in_reply_to_account_id :integer +# account_id :bigint(8) not null +# application_id :bigint(8) +# in_reply_to_account_id :bigint(8) # class Status < ApplicationRecord diff --git a/app/models/status_pin.rb b/app/models/status_pin.rb index d3a98d8bd..afc76bded 100644 --- a/app/models/status_pin.rb +++ b/app/models/status_pin.rb @@ -3,9 +3,9 @@ # # Table name: status_pins # -# id :integer not null, primary key -# account_id :integer not null -# status_id :integer not null +# id :bigint(8) not null, primary key +# account_id :bigint(8) not null +# status_id :bigint(8) not null # created_at :datetime not null # updated_at :datetime not null # diff --git a/app/models/stream_entry.rb b/app/models/stream_entry.rb index 2ae034d93..a2f273281 100644 --- a/app/models/stream_entry.rb +++ b/app/models/stream_entry.rb @@ -3,13 +3,13 @@ # # Table name: stream_entries # -# id :integer not null, primary key -# activity_id :integer +# id :bigint(8) not null, primary key +# activity_id :bigint(8) # activity_type :string # created_at :datetime not null # updated_at :datetime not null # hidden :boolean default(FALSE), not null -# account_id :integer +# account_id :bigint(8) # class StreamEntry < ApplicationRecord diff --git a/app/models/subscription.rb b/app/models/subscription.rb index ea1173160..79b81828d 100644 --- a/app/models/subscription.rb +++ b/app/models/subscription.rb @@ -3,7 +3,7 @@ # # Table name: subscriptions # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # callback_url :string default(""), not null # secret :string # expires_at :datetime @@ -12,7 +12,7 @@ # updated_at :datetime not null # last_successful_delivery_at :datetime # domain :string -# account_id :integer not null +# account_id :bigint(8) not null # class Subscription < ApplicationRecord diff --git a/app/models/tag.rb b/app/models/tag.rb index 9fa9405d7..8b1b02412 100644 --- a/app/models/tag.rb +++ b/app/models/tag.rb @@ -3,7 +3,7 @@ # # Table name: tags # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # name :string default(""), not null # created_at :datetime not null # updated_at :datetime not null diff --git a/app/models/user.rb b/app/models/user.rb index 2d5f145fa..a9f3e1da2 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,7 +3,7 @@ # # Table name: users # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # email :string default(""), not null # created_at :datetime not null # updated_at :datetime not null @@ -30,10 +30,10 @@ # last_emailed_at :datetime # otp_backup_codes :string is an Array # filtered_languages :string default([]), not null, is an Array -# account_id :integer not null +# account_id :bigint(8) not null # disabled :boolean default(FALSE), not null # moderator :boolean default(FALSE), not null -# invite_id :integer +# invite_id :bigint(8) # remember_token :string # diff --git a/app/models/web/push_subscription.rb b/app/models/web/push_subscription.rb index 5aee92d27..1736106f7 100644 --- a/app/models/web/push_subscription.rb +++ b/app/models/web/push_subscription.rb @@ -3,7 +3,7 @@ # # Table name: web_push_subscriptions # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # endpoint :string not null # key_p256dh :string not null # key_auth :string not null diff --git a/app/models/web/setting.rb b/app/models/web/setting.rb index 0a5129d17..99588d26c 100644 --- a/app/models/web/setting.rb +++ b/app/models/web/setting.rb @@ -3,11 +3,11 @@ # # Table name: web_settings # -# id :integer not null, primary key +# id :bigint(8) not null, primary key # data :json # created_at :datetime not null # updated_at :datetime not null -# user_id :integer not null +# user_id :bigint(8) not null # class Web::Setting < ApplicationRecord diff --git a/config/deploy.rb b/config/deploy.rb index 180dd1c2a..e0cd60f54 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -lock '3.10.1' +lock '3.10.2' set :repo_url, ENV.fetch('REPO', 'https://github.com/tootsuite/mastodon.git') set :branch, ENV.fetch('BRANCH', 'master') From 3ccca6cece51fd071fed8c1e05af6a48c00c8897 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Mon, 23 Apr 2018 11:49:03 +0200 Subject: [PATCH 138/442] =?UTF-8?q?=F0=9F=8C=8D:=20Make=20=F0=9F=87=B5?= =?UTF-8?q?=F0=9F=87=B1=20translation=20more=20consistent=20(#7239)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- app/javascript/mastodon/locales/pl.json | 10 +++++----- config/locales/pl.yml | 12 +++++++----- config/locales/simple_form.pl.yml | 2 +- 3 files changed, 13 insertions(+), 11 deletions(-) diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 22ae2de47..9dff9d1a3 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -103,8 +103,8 @@ "empty_column.community": "Lokalna oś czasu jest pusta. Napisz coś publicznie, aby zagaić!", "empty_column.direct": "Nie masz żadnych wiadomości bezpośrednich. Kiedy dostaniesz lub wyślesz jakąś, pojawi się ona tutaj.", "empty_column.hashtag": "Nie ma wpisów oznaczonych tym hashtagiem. Możesz napisać pierwszy!", - "empty_column.home": "Nie śledzisz nikogo. Odwiedź publiczną oś czasu lub użyj wyszukiwarki, aby znaleźć interesujące Cię profile.", - "empty_column.home.public_timeline": "publiczna oś czasu", + "empty_column.home": "Nie śledzisz nikogo. Odwiedź globalną oś czasu lub użyj wyszukiwarki, aby znaleźć interesujące Cię profile.", + "empty_column.home.public_timeline": "globalna oś czasu", "empty_column.list": "Nie ma nic na tej liście. Kiedy członkowie listy dodadzą nowe wpisy, pojawia się one tutaj.", "empty_column.notifications": "Nie masz żadnych powiadomień. Rozpocznij interakcje z innymi użytkownikami.", "empty_column.public": "Tu nic nie ma! Napisz coś publicznie, lub dodaj ludzi z innych instancji, aby to wyświetlić", @@ -169,7 +169,7 @@ "navigation_bar.mutes": "Wyciszeni użytkownicy", "navigation_bar.pins": "Przypięte wpisy", "navigation_bar.preferences": "Preferencje", - "navigation_bar.public_timeline": "Oś czasu federacji", + "navigation_bar.public_timeline": "Globalna oś czasu", "notification.favourite": "{name} dodał Twój wpis do ulubionych", "notification.follow": "{name} zaczął Cię śledzić", "notification.mention": "{name} wspomniał o tobie", @@ -187,7 +187,7 @@ "notifications.column_settings.sound": "Odtwarzaj dźwięk", "onboarding.done": "Gotowe", "onboarding.next": "Dalej", - "onboarding.page_five.public_timelines": "Lokalna oś czasu zawiera wszystkie publiczne wpisy z {domain}. Federalna oś czasu wyświetla publiczne wpisy śledzonych przez członków {domain}. Są to publiczne osie czasu – najlepszy sposób na poznanie nowych osób.", + "onboarding.page_five.public_timelines": "Lokalna oś czasu zawiera wszystkie publiczne wpisy z {domain}. Globalna oś czasu wyświetla publiczne wpisy śledzonych przez członków {domain}. Są to publiczne osie czasu – najlepszy sposób na poznanie nowych osób.", "onboarding.page_four.home": "Główna oś czasu wyświetla publiczne wpisy.", "onboarding.page_four.notifications": "Kolumna powiadomień wyświetla, gdy ktoś dokonuje interakcji z tobą.", "onboarding.page_one.federation": "Mastodon jest siecią niezależnych serwerów połączonych w jeden portal społecznościowy. Nazywamy te serwery instancjami.", @@ -247,7 +247,7 @@ "status.delete": "Usuń", "status.direct": "Wyślij wiadomość bezpośrednią do @{name}", "status.embed": "Osadź", - "status.favourite": "Ulubione", + "status.favourite": "Dodaj do ulubionych", "status.load_more": "Załaduj więcej", "status.media_hidden": "Zawartość multimedialna ukryta", "status.mention": "Wspomnij o @{name}", diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 839767c8f..71bf6bf18 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -466,7 +466,7 @@ pl: archive_takeout: date: Data download: Pobierz swoje archiwum - hint_html: Możesz uzyskać archiwum swoich wpisów i wysłanej zawartości multimedialnej. Wyeksportowane dane będą dostępne w formacie ActivityPub, obsługiwanym przez odpowiednie programy. + hint_html: Możesz uzyskać archiwum swoich wpisów i wysłanej zawartości multimedialnej. Wyeksportowane dane będą dostępne w formacie ActivityPub, który możesz otworzyć w obsługujących go programach. in_progress: Tworzenie archiwum… request: Uzyskaj archiwum size: Rozmiar @@ -485,17 +485,19 @@ pl: one: W trakcie usuwania śledzących z jednej domeny… other: W trakcie usuwania śledzących z %{count} domen… true_privacy_html: Pamiętaj, że rzeczywista prywatność może zostać uzyskana wyłącznie dzięki szyfrowaniu end-to-end. - unlocked_warning_html: Każdy może Cię śledzić, aby natychmiastowo zobaczyć twoje wpisy. %{lock_link} aby móc kontrolować, kto Cię śledzi. + unlocked_warning_html: Każdy może Cię śledzić, dzięki czemu może zobaczyć Twoje niepubliczne wpisy. %{lock_link} aby móc kontrolować, kto Cię śledzi. unlocked_warning_title: Twoje konto nie jest zablokowane generic: changes_saved_msg: Ustawienia zapisane! powered_by: uruchomione na %{link} save_changes: Zapisz zmiany validation_errors: - one: Coś jest wciąż nie tak! Przyjrzyj się błędowi poniżej - other: Coś jest wciąż nie tak! Przejrzyj błędy (%{count}) poniżej + few: Coś jest wciąż nie tak! Przejrzyj %{count} poniższe błędy + many: Coś jest wciąż nie tak! Przejrzyj %{count} poniższych błędów + one: Coś jest wciąż nie tak! Przyjrzyj się poniższemu błędowi + other: Coś jest wciąż nie tak! Przejrzyj poniższe błędy (%{count}) imports: - preface: Możesz zaimportować pewne dane (jak dane kont, które śledzisz lub blokujesz) do swojego konta na tym serwerze, korzystając z danych wyeksportowanych z innego serwera. + preface: Możesz zaimportować pewne dane (np. lista kont, które śledzisz lub blokujesz) do swojego konta na tym serwerze, korzystając z danych wyeksportowanych z innego serwera. success: Twoje dane zostały załadowane i zostaną niebawem przetworzone types: blocking: Lista blokowanych diff --git a/config/locales/simple_form.pl.yml b/config/locales/simple_form.pl.yml index cbca232fe..4c1833b1c 100644 --- a/config/locales/simple_form.pl.yml +++ b/config/locales/simple_form.pl.yml @@ -62,7 +62,7 @@ pl: setting_theme: Motyw strony setting_unfollow_modal: Pytaj o potwierdzenie przed cofnięciem śledzenia severity: Priorytet - type: Typ importu + type: Importowane dane username: Nazwa użytkownika username_or_email: Nazwa użytkownika lub adres e-mail interactions: From 06817b3c1fdcc9c2b3484478588cc348a4a06537 Mon Sep 17 00:00:00 2001 From: Alejandro Martinez Ruiz Date: Mon, 23 Apr 2018 15:03:58 +0100 Subject: [PATCH 139/442] tasks/mastodon: fix prompt for Redis password (#7241) --- lib/tasks/mastodon.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index b5a148337..7a337845e 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -109,7 +109,7 @@ namespace :mastodon do env['REDIS_PASSWORD'] = prompt.ask('Redis password:') do |q| q.required false - a.default nil + q.default nil q.modify :strip end From 1258efa882b7a0eedc868640eb8e5a9075445ca0 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 24 Apr 2018 02:27:35 +0900 Subject: [PATCH 140/442] Paginate descendant statuses in public page (#7148) --- app/controllers/api/v1/statuses_controller.rb | 2 +- app/controllers/statuses_controller.rb | 73 ++++++++++++++++++- .../concerns/status_threading_concern.rb | 17 +++-- app/views/stream_entries/_more.html.haml | 2 + app/views/stream_entries/_status.html.haml | 15 +++- spec/controllers/statuses_controller_spec.rb | 43 +++++++++++ .../concerns/status_threading_concern_spec.rb | 12 +-- .../stream_entries/show.html.haml_spec.rb | 4 +- 8 files changed, 146 insertions(+), 22 deletions(-) create mode 100644 app/views/stream_entries/_more.html.haml diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index e98241323..01880565c 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -18,7 +18,7 @@ class Api::V1::StatusesController < Api::BaseController def context ancestors_results = @status.in_reply_to_id.nil? ? [] : @status.ancestors(DEFAULT_STATUSES_LIMIT, current_account) - descendants_results = @status.descendants(current_account) + descendants_results = @status.descendants(DEFAULT_STATUSES_LIMIT, current_account) loaded_ancestors = cache_collection(ancestors_results, Status) loaded_descendants = cache_collection(descendants_results, Status) diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index a2943982a..01dac35e4 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -5,6 +5,8 @@ class StatusesController < ApplicationController include Authorization ANCESTORS_LIMIT = 20 + DESCENDANTS_LIMIT = 20 + DESCENDANTS_DEPTH_LIMIT = 4 layout 'public' @@ -19,9 +21,8 @@ class StatusesController < ApplicationController def show respond_to do |format| format.html do - @ancestors = @status.reply? ? cache_collection(@status.ancestors(ANCESTORS_LIMIT, current_account), Status) : [] - @next_ancestor = @ancestors.size < ANCESTORS_LIMIT ? nil : @ancestors.shift - @descendants = cache_collection(@status.descendants(current_account), Status) + set_ancestors + set_descendants render 'stream_entries/show' end @@ -51,10 +52,76 @@ class StatusesController < ApplicationController private + def create_descendant_thread(depth, statuses) + if depth < DESCENDANTS_DEPTH_LIMIT + { statuses: statuses } + else + next_status = statuses.pop + { statuses: statuses, next_status: next_status } + end + end + def set_account @account = Account.find_local!(params[:account_username]) end + def set_ancestors + @ancestors = @status.reply? ? cache_collection(@status.ancestors(ANCESTORS_LIMIT, current_account), Status) : [] + @next_ancestor = @ancestors.size < ANCESTORS_LIMIT ? nil : @ancestors.shift + end + + def set_descendants + @max_descendant_thread_id = params[:max_descendant_thread_id]&.to_i + @since_descendant_thread_id = params[:since_descendant_thread_id]&.to_i + + descendants = cache_collection( + @status.descendants( + DESCENDANTS_LIMIT, + current_account, + @max_descendant_thread_id, + @since_descendant_thread_id, + DESCENDANTS_DEPTH_LIMIT + ), + Status + ) + @descendant_threads = [] + + if descendants.present? + statuses = [descendants.first] + depth = 1 + + descendants.drop(1).each_with_index do |descendant, index| + if descendants[index].id == descendant.in_reply_to_id + depth += 1 + statuses << descendant + else + @descendant_threads << create_descendant_thread(depth, statuses) + + @descendant_threads.reverse_each do |descendant_thread| + statuses = descendant_thread[:statuses] + + index = statuses.find_index do |thread_status| + thread_status.id == descendant.in_reply_to_id + end + + if index.present? + depth += index - statuses.size + break + end + + depth -= statuses.size + end + + statuses = [descendant] + end + end + + @descendant_threads << create_descendant_thread(depth, statuses) + end + + @max_descendant_thread_id = @descendant_threads.pop[:statuses].first.id if descendants.size >= DESCENDANTS_LIMIT + end + def set_link_headers response.headers['Link'] = LinkHeader.new( [ diff --git a/app/models/concerns/status_threading_concern.rb b/app/models/concerns/status_threading_concern.rb index fffc095ee..a3fd34e94 100644 --- a/app/models/concerns/status_threading_concern.rb +++ b/app/models/concerns/status_threading_concern.rb @@ -7,8 +7,8 @@ module StatusThreadingConcern find_statuses_from_tree_path(ancestor_ids(limit), account) end - def descendants(account = nil) - find_statuses_from_tree_path(descendant_ids, account) + def descendants(limit, account = nil, max_child_id = nil, since_child_id = nil, depth = nil) + find_statuses_from_tree_path(descendant_ids(limit, max_child_id, since_child_id, depth), account) end private @@ -46,26 +46,27 @@ module StatusThreadingConcern SQL end - def descendant_ids - descendant_statuses.pluck(:id) + def descendant_ids(limit, max_child_id, since_child_id, depth) + descendant_statuses(limit, max_child_id, since_child_id, depth).pluck(:id) end - def descendant_statuses - Status.find_by_sql([<<-SQL.squish, id: id]) + def descendant_statuses(limit, max_child_id, since_child_id, depth) + Status.find_by_sql([<<-SQL.squish, id: id, limit: limit, max_child_id: max_child_id, since_child_id: since_child_id, depth: depth]) WITH RECURSIVE search_tree(id, path) AS ( SELECT id, ARRAY[id] FROM statuses - WHERE in_reply_to_id = :id + WHERE in_reply_to_id = :id AND COALESCE(id < :max_child_id, TRUE) AND COALESCE(id > :since_child_id, TRUE) UNION ALL SELECT statuses.id, path || statuses.id FROM search_tree JOIN statuses ON statuses.in_reply_to_id = search_tree.id - WHERE NOT statuses.id = ANY(path) + WHERE COALESCE(array_length(path, 1) < :depth, TRUE) AND NOT statuses.id = ANY(path) ) SELECT id FROM search_tree ORDER BY path + LIMIT :limit SQL end diff --git a/app/views/stream_entries/_more.html.haml b/app/views/stream_entries/_more.html.haml new file mode 100644 index 000000000..9b1dfe4a7 --- /dev/null +++ b/app/views/stream_entries/_more.html.haml @@ -0,0 +1,2 @@ += link_to url, class: 'more light' do + = t('statuses.show_more') diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml index 2d0dafcb7..8decdf6b5 100644 --- a/app/views/stream_entries/_status.html.haml +++ b/app/views/stream_entries/_status.html.haml @@ -16,8 +16,7 @@ - if status.reply? && include_threads - if @next_ancestor .entry{ class: entry_classes } - = link_to short_account_status_url(@next_ancestor.account.username, @next_ancestor), class: 'more light' do - = t('statuses.show_more') + = render 'stream_entries/more', url: short_account_status_url(@next_ancestor.account.username, @next_ancestor) = render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id } .entry{ class: entry_classes } @@ -40,4 +39,14 @@ = render (centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status'), status: status.proper - if include_threads - = render partial: 'stream_entries/status', collection: @descendants, as: :status, locals: { is_successor: true, parent_id: status.id } + - if @since_descendant_thread_id + .entry{ class: entry_classes } + = render 'stream_entries/more', url: short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1) + - @descendant_threads.each do |thread| + = render partial: 'stream_entries/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id } + - if thread[:next_status] + .entry{ class: entry_classes } + = render 'stream_entries/more', url: short_account_status_url(thread[:next_status].account.username, thread[:next_status]) + - if @next_descendant_thread + .entry{ class: entry_classes } + = render 'stream_entries/more', url: short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1) diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb index 89af55688..b4f3c5a08 100644 --- a/spec/controllers/statuses_controller_spec.rb +++ b/spec/controllers/statuses_controller_spec.rb @@ -82,6 +82,49 @@ describe StatusesController do expect(assigns(:ancestors)).to eq [] end + it 'assigns @descendant_threads for a thread with several statuses' do + status = Fabricate(:status) + child = Fabricate(:status, in_reply_to_id: status.id) + grandchild = Fabricate(:status, in_reply_to_id: child.id) + + get :show, params: { account_username: status.account.username, id: status.id } + + expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).to eq [child.id, grandchild.id] + end + + it 'assigns @descendant_threads for several threads sharing the same descendant' do + status = Fabricate(:status) + child = Fabricate(:status, in_reply_to_id: status.id) + grandchildren = 2.times.map { Fabricate(:status, in_reply_to_id: child.id) } + + get :show, params: { account_username: status.account.username, id: status.id } + + expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).to eq [child.id, grandchildren[0].id] + expect(assigns(:descendant_threads)[1][:statuses].pluck(:id)).to eq [grandchildren[1].id] + end + + it 'assigns @max_descendant_thread_id for the last thread if it is hitting the status limit' do + stub_const 'StatusesController::DESCENDANTS_LIMIT', 1 + status = Fabricate(:status) + child = Fabricate(:status, in_reply_to_id: status.id) + + get :show, params: { account_username: status.account.username, id: status.id } + + expect(assigns(:descendant_threads)).to eq [] + expect(assigns(:max_descendant_thread_id)).to eq child.id + end + + it 'assigns @descendant_threads for threads with :next_status key if they are hitting the depth limit' do + stub_const 'StatusesController::DESCENDANTS_DEPTH_LIMIT', 1 + status = Fabricate(:status) + child = Fabricate(:status, in_reply_to_id: status.id) + + get :show, params: { account_username: status.account.username, id: status.id } + + expect(assigns(:descendant_threads)[0][:statuses].pluck(:id)).not_to include child.id + expect(assigns(:descendant_threads)[0][:next_status].id).to eq child.id + end + it 'returns a success' do status = Fabricate(:status) get :show, params: { account_username: status.account.username, id: status.id } diff --git a/spec/models/concerns/status_threading_concern_spec.rb b/spec/models/concerns/status_threading_concern_spec.rb index b8ebdd58c..e5736a307 100644 --- a/spec/models/concerns/status_threading_concern_spec.rb +++ b/spec/models/concerns/status_threading_concern_spec.rb @@ -89,34 +89,34 @@ describe StatusThreadingConcern do let!(:viewer) { Fabricate(:account, username: 'viewer') } it 'returns replies' do - expect(status.descendants).to include(reply1, reply2, reply3) + expect(status.descendants(4)).to include(reply1, reply2, reply3) end it 'does not return replies user is not allowed to see' do reply1.update(visibility: :private) reply3.update(visibility: :direct) - expect(status.descendants(viewer)).to_not include(reply1, reply3) + expect(status.descendants(4, viewer)).to_not include(reply1, reply3) end it 'does not return replies from blocked users' do viewer.block!(jeff) - expect(status.descendants(viewer)).to_not include(reply3) + expect(status.descendants(4, viewer)).to_not include(reply3) end it 'does not return replies from muted users' do viewer.mute!(jeff) - expect(status.descendants(viewer)).to_not include(reply3) + expect(status.descendants(4, viewer)).to_not include(reply3) end it 'does not return replies from silenced and not followed users' do jeff.update(silenced: true) - expect(status.descendants(viewer)).to_not include(reply3) + expect(status.descendants(4, viewer)).to_not include(reply3) end it 'does not return replies from blocked domains' do viewer.block_domain!('example.com') - expect(status.descendants(viewer)).to_not include(reply2) + expect(status.descendants(4, viewer)).to_not include(reply2) end end end diff --git a/spec/views/stream_entries/show.html.haml_spec.rb b/spec/views/stream_entries/show.html.haml_spec.rb index 6074bbc2e..560039ffa 100644 --- a/spec/views/stream_entries/show.html.haml_spec.rb +++ b/spec/views/stream_entries/show.html.haml_spec.rb @@ -24,6 +24,7 @@ describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true d assign(:stream_entry, status.stream_entry) assign(:account, alice) assign(:type, status.stream_entry.activity_type.downcase) + assign(:descendant_threads, []) render @@ -49,7 +50,7 @@ describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true d assign(:account, alice) assign(:type, reply.stream_entry.activity_type.downcase) assign(:ancestors, reply.stream_entry.activity.ancestors(1, bob) ) - assign(:descendants, reply.stream_entry.activity.descendants(bob)) + assign(:descendant_threads, [{ statuses: reply.stream_entry.activity.descendants(1)}]) render @@ -75,6 +76,7 @@ describe 'stream_entries/show.html.haml', without_verify_partial_doubles: true d assign(:stream_entry, status.stream_entry) assign(:account, alice) assign(:type, status.stream_entry.activity_type.downcase) + assign(:descendant_threads, []) render From 53b1d8887325160934dec7557e97a43ce2896264 Mon Sep 17 00:00:00 2001 From: ThibG Date: Mon, 23 Apr 2018 20:12:16 +0200 Subject: [PATCH 141/442] Fix fullscreen video player (fixes #7244) (#7245) --- app/javascript/styles/mastodon/components.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index c84e61306..f1284b388 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -4446,6 +4446,8 @@ a.status-card { video { max-width: 100% !important; max-height: 100% !important; + width: 100% !important; + height: 100% !important; } } From 495303d9b86919c72bf1948a714bf8d00b41fa0f Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Mon, 23 Apr 2018 21:27:18 +0200 Subject: [PATCH 142/442] Prevent suspended accounts from appearing in AccountSearchService (#7246) --- app/models/account.rb | 1 + app/services/account_search_service.rb | 4 ++-- spec/services/account_search_service_spec.rb | 19 +++++++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index 0a4370be4..ee47f04af 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -117,6 +117,7 @@ class Account < ApplicationRecord scope :partitioned, -> { order(Arel.sql('row_number() over (partition by domain)')) } scope :silenced, -> { where(silenced: true) } scope :suspended, -> { where(suspended: true) } + scope :without_suspended, -> { where(suspended: false) } scope :recent, -> { reorder(id: :desc) } scope :alphabetic, -> { order(domain: :asc, username: :asc) } scope :by_domain_accounts, -> { group(:domain).select(:domain, 'COUNT(*) AS accounts_count').order('accounts_count desc') } diff --git a/app/services/account_search_service.rb b/app/services/account_search_service.rb index 3860a9cbd..7edbd9b47 100644 --- a/app/services/account_search_service.rb +++ b/app/services/account_search_service.rb @@ -65,9 +65,9 @@ class AccountSearchService < BaseService def exact_match @_exact_match ||= begin if domain_is_local? - search_from.find_local(query_username) + search_from.without_suspended.find_local(query_username) else - search_from.find_remote(query_username, query_domain) + search_from.without_suspended.find_remote(query_username, query_domain) end end end diff --git a/spec/services/account_search_service_spec.rb b/spec/services/account_search_service_spec.rb index 9bb27edad..c5ddc5844 100644 --- a/spec/services/account_search_service_spec.rb +++ b/spec/services/account_search_service_spec.rb @@ -137,5 +137,24 @@ describe AccountSearchService do expect(service).not_to have_received(:call) end end + + describe 'should not include suspended accounts' do + it 'returns the fuzzy match first, and does not return suspended exacts' do + partial = Fabricate(:account, username: 'exactness') + exact = Fabricate(:account, username: 'exact', suspended: true) + + results = subject.call('exact', 10) + expect(results.size).to eq 1 + expect(results).to eq [partial] + end + + it "does not return suspended remote accounts" do + remote = Fabricate(:account, username: 'a', domain: 'remote', display_name: 'e', suspended: true) + + results = subject.call('a@example.com', 2) + expect(results.size).to eq 0 + expect(results).to eq [] + end + end end end From 60b871d56c554ddb22b7890d803e4ba6650ed286 Mon Sep 17 00:00:00 2001 From: Emelia Smith Date: Mon, 23 Apr 2018 23:52:58 +0200 Subject: [PATCH 143/442] Implement the ability for instances to define a list of disallowed hashtags (#7176) The goal here isn't to prevent these hashtags from existing, but just to strongly curtail their usage; The hashtags may still exist in the database via federated status, or from being created prior to this feature. --- app/models/status.rb | 1 + .../disallowed_hashtags_validator.rb | 22 +++++++++++++++++++ config/locales/en.yml | 3 +++ config/settings.yml | 1 + 4 files changed, 27 insertions(+) create mode 100644 app/validators/disallowed_hashtags_validator.rb diff --git a/app/models/status.rb b/app/models/status.rb index ed4bcefca..37f2db562 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -59,6 +59,7 @@ class Status < ApplicationRecord validates :uri, uniqueness: true, presence: true, unless: :local? validates :text, presence: true, unless: -> { with_media? || reblog? } validates_with StatusLengthValidator + validates_with DisallowedHashtagsValidator validates :reblog, uniqueness: { scope: :account }, if: :reblog? default_scope { recent } diff --git a/app/validators/disallowed_hashtags_validator.rb b/app/validators/disallowed_hashtags_validator.rb new file mode 100644 index 000000000..22c027b0f --- /dev/null +++ b/app/validators/disallowed_hashtags_validator.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class DisallowedHashtagsValidator < ActiveModel::Validator + def validate(status) + return unless status.local? && !status.reblog? + + tags = Extractor.extract_hashtags(status.text) + tags.keep_if { |tag| disallowed_hashtags.include? tag.downcase } + + status.errors.add(:text, I18n.t('statuses.disallowed_hashtags', tags: tags.join(', '), count: tags.size)) unless tags.empty? + end + + private + + def disallowed_hashtags + return @disallowed_hashtags if @disallowed_hashtags + + @disallowed_hashtags = Setting.disallowed_hashtags.nil? ? [] : Setting.disallowed_hashtags + @disallowed_hashtags = @disallowed_hashtags.split(' ') if @disallowed_hashtags.is_a? String + @disallowed_hashtags = @disallowed_hashtags.map(&:downcase) + end +end diff --git a/config/locales/en.yml b/config/locales/en.yml index 8b66b91ec..1468d8559 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -684,6 +684,9 @@ en: one: "%{count} video" other: "%{count} videos" content_warning: 'Content warning: %{warning}' + disallowed_hashtags: + one: 'contained a disallowed hashtag: %{tags}' + other: 'contained the disallowed hashtags: %{tags}' open_in_web: Open in web over_character_limit: character limit of %{max} exceeded pin_errors: diff --git a/config/settings.yml b/config/settings.yml index 68579ad0f..dcf655008 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -47,6 +47,7 @@ defaults: &defaults - root - webmaster - administrator + disallowed_hashtags: # space separated string or list of hashtags without the hash bootstrap_timeline_accounts: '' activity_api_enabled: true peers_api_enabled: true From 306267dbd275363422f9288c91e634a92511620c Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Tue, 24 Apr 2018 18:47:27 +0900 Subject: [PATCH 144/442] Fix ID duplication in timelines (#7251) --- app/javascript/mastodon/reducers/timelines.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/reducers/timelines.js b/app/javascript/mastodon/reducers/timelines.js index ad897bcc9..dd675d78f 100644 --- a/app/javascript/mastodon/reducers/timelines.js +++ b/app/javascript/mastodon/reducers/timelines.js @@ -34,7 +34,7 @@ const expandNormalizedTimeline = (state, timeline, statuses, next, isPartial) => mMap.update('items', ImmutableList(), oldIds => { const newIds = statuses.map(status => status.get('id')); const lastIndex = oldIds.findLastIndex(id => id !== null && compareId(id, newIds.last()) >= 0) + 1; - const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && compareId(id, newIds.first()) >= 0); + const firstIndex = oldIds.take(lastIndex).findLastIndex(id => id !== null && compareId(id, newIds.first()) > 0); if (firstIndex < 0) { return (isPartial ? newIds.unshift(null) : newIds).concat(oldIds.skip(lastIndex)); From 7681ad80449dafc6fb4572fa66c463930ef82f19 Mon Sep 17 00:00:00 2001 From: "Renato \"Lond\" Cerqueira" Date: Tue, 24 Apr 2018 11:48:11 +0200 Subject: [PATCH 145/442] Weblate translations (2018-04-24) (#7252) * Translated using Weblate (Dutch) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/ * Translated using Weblate (French) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fr/ * Translated using Weblate (Dutch) Currently translated at 100.0% (626 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/ * Translated using Weblate (Dutch) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/ * Translated using Weblate (Dutch) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/nl/ * Translated using Weblate (Japanese) Currently translated at 99.6% (287 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ja/ * Translated using Weblate (Japanese) Currently translated at 99.5% (623 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/ * Translated using Weblate (Galician) Currently translated at 99.8% (625 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/ * Translated using Weblate (French) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/fr/ * Translated using Weblate (French) Currently translated at 99.6% (624 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/ * Translated using Weblate (French) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/fr/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.5% (631 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/ * Translated using Weblate (Korean) Currently translated at 99.8% (633 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ko/ * Translated using Weblate (Korean) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ko/ * Translated using Weblate (French) Currently translated at 99.6% (632 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/fr/ * Translated using Weblate (Japanese) Currently translated at 99.6% (632 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/ * Translated using Weblate (Dutch) Currently translated at 100.0% (634 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/ * Translated using Weblate (Dutch) Currently translated at 100.0% (634 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/ * Translated using Weblate (Dutch) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/nl/ * Translated using Weblate (Japanese) Currently translated at 93.5% (58 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ja/ * Added translation using Weblate (Basque) * Added translation using Weblate (Basque) * Translated using Weblate (Galician) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/gl/ * Added translation using Weblate (Basque) * Added translation using Weblate (Basque) * Added translation using Weblate (Basque) * Added translation using Weblate (Basque) * Translated using Weblate (Galician) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/gl/ * Translated using Weblate (Galician) Currently translated at 99.8% (633 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/ * Translated using Weblate (Basque) Currently translated at 0.3% (1 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eu/ * Translated using Weblate (Basque) Currently translated at 50.0% (1 of 2 strings) Translation: Mastodon/Activerecord Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/activerecord/eu/ * Translated using Weblate (Basque) Currently translated at 1.6% (1 of 62 strings) Translation: Mastodon/Devise Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/eu/ * Translated using Weblate (Basque) Currently translated at 1.3% (1 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/eu/ * Translated using Weblate (Basque) Currently translated at 1.6% (1 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/eu/ * Translated using Weblate (Catalan) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ca/ * Translated using Weblate (Catalan) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/ * Translated using Weblate (Catalan) Currently translated at 100.0% (634 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/ * Translated using Weblate (Catalan) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/ * Translated using Weblate (Catalan) Currently translated at 100.0% (634 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/ * Translated using Weblate (Catalan) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/ * Translated using Weblate (Japanese) Currently translated at 93.5% (58 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ja/ * Translated using Weblate (Arabic) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ar/ * Translated using Weblate (Arabic) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/ * Translated using Weblate (Arabic) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/ * Translated using Weblate (Slovak) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/sk/ * Translated using Weblate (Japanese) Currently translated at 93.5% (58 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/ja/ * Translated using Weblate (Slovak) Currently translated at 99.6% (287 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/sk/ * Translated using Weblate (Slovak) Currently translated at 88.4% (561 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 90.3% (573 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/sk/ * Translated using Weblate (Korean) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ko/ * Translated using Weblate (Italian) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/it/ * Translated using Weblate (Italian) Currently translated at 3.2% (2 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/it/ * Translated using Weblate (Italian) Currently translated at 100.0% (288 of 288 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/it/ * Translated using Weblate (Italian) Currently translated at 100.0% (75 of 75 strings) Translation: Mastodon/Doorkeeper Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/doorkeeper/it/ * Translated using Weblate (Italian) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/it/ * Translated using Weblate (Italian) Currently translated at 32.4% (206 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/it/ * Translated using Weblate (Italian) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Devise Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/devise/it/ * Translated using Weblate (Arabic) Currently translated at 81.3% (516 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Italian) Currently translated at 51.2% (325 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/it/ * Translated using Weblate (Slovak) Currently translated at 91.6% (581 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Italian) Currently translated at 51.8% (329 of 634 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/it/ * Normalize translations Ran yarn build:development && i18n-tasks normalize && yarn manage:translations * Remove unused translations Ran i18n-tasks remove-unused --- app/javascript/mastodon/locales/ar.json | 17 +- app/javascript/mastodon/locales/bg.json | 1 + app/javascript/mastodon/locales/ca.json | 21 +- app/javascript/mastodon/locales/de.json | 1 + app/javascript/mastodon/locales/eo.json | 1 + app/javascript/mastodon/locales/es.json | 1 + app/javascript/mastodon/locales/eu.json | 296 +++++++++++++++++++ app/javascript/mastodon/locales/fa.json | 1 + app/javascript/mastodon/locales/fi.json | 1 + app/javascript/mastodon/locales/fr.json | 21 +- app/javascript/mastodon/locales/gl.json | 3 +- app/javascript/mastodon/locales/he.json | 1 + app/javascript/mastodon/locales/hr.json | 1 + app/javascript/mastodon/locales/hu.json | 1 + app/javascript/mastodon/locales/hy.json | 1 + app/javascript/mastodon/locales/id.json | 1 + app/javascript/mastodon/locales/io.json | 1 + app/javascript/mastodon/locales/it.json | 7 +- app/javascript/mastodon/locales/ja.json | 4 +- app/javascript/mastodon/locales/ko.json | 5 +- app/javascript/mastodon/locales/nl.json | 17 +- app/javascript/mastodon/locales/no.json | 1 + app/javascript/mastodon/locales/oc.json | 1 + app/javascript/mastodon/locales/pt-BR.json | 7 +- app/javascript/mastodon/locales/pt.json | 1 + app/javascript/mastodon/locales/ru.json | 1 + app/javascript/mastodon/locales/sk.json | 15 +- app/javascript/mastodon/locales/sr-Latn.json | 1 + app/javascript/mastodon/locales/sr.json | 1 + app/javascript/mastodon/locales/sv.json | 1 + app/javascript/mastodon/locales/th.json | 1 + app/javascript/mastodon/locales/tr.json | 1 + app/javascript/mastodon/locales/uk.json | 1 + app/javascript/mastodon/locales/zh-CN.json | 1 + app/javascript/mastodon/locales/zh-HK.json | 1 + app/javascript/mastodon/locales/zh-TW.json | 1 + config/locales/activerecord.eu.yml | 9 + config/locales/ca.yml | 121 +++++++- config/locales/devise.eu.yml | 5 + config/locales/devise.it.yml | 21 ++ config/locales/doorkeeper.eu.yml | 6 + config/locales/doorkeeper.it.yml | 16 +- config/locales/eu.yml | 1 + config/locales/fr.yml | 45 ++- config/locales/gl.yml | 44 ++- config/locales/it.yml | 251 +++++++++++++++- config/locales/ja.yml | 5 +- config/locales/ko.yml | 44 ++- config/locales/nl.yml | 121 +++++++- config/locales/pt-BR.yml | 43 ++- config/locales/simple_form.ar.yml | 12 + config/locales/simple_form.ca.yml | 6 + config/locales/simple_form.eu.yml | 6 + config/locales/simple_form.fr.yml | 6 + config/locales/simple_form.gl.yml | 6 + config/locales/simple_form.it.yml | 84 ++++-- config/locales/simple_form.ja.yml | 8 +- config/locales/simple_form.ko.yml | 6 + config/locales/simple_form.nl.yml | 8 +- config/locales/simple_form.sk.yml | 12 +- config/locales/sk.yml | 22 +- 61 files changed, 1243 insertions(+), 103 deletions(-) create mode 100644 app/javascript/mastodon/locales/eu.json create mode 100644 config/locales/activerecord.eu.yml create mode 100644 config/locales/devise.eu.yml create mode 100644 config/locales/doorkeeper.eu.yml create mode 100644 config/locales/eu.yml create mode 100644 config/locales/simple_form.eu.yml diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index 24c8a5b54..7975ac1c5 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -2,7 +2,7 @@ "account.block": "حظر @{name}", "account.block_domain": "إخفاء كل شيئ قادم من إسم النطاق {domain}", "account.blocked": "محظور", - "account.direct": "Direct message @{name}", + "account.direct": "رسالة خاصة إلى @{name}", "account.disclaimer_full": "قد لا تعكس المعلومات أدناه الملف الشخصي الكامل للمستخدم.", "account.domain_blocked": "النطاق مخفي", "account.edit_profile": "تعديل الملف الشخصي", @@ -18,7 +18,7 @@ "account.mute_notifications": "كتم إخطارات @{name}", "account.muted": "مكتوم", "account.posts": "التبويقات", - "account.posts_with_replies": "تبويقات تحتوي على رُدود", + "account.posts_with_replies": "التبويقات و الردود", "account.report": "أبلغ عن @{name}", "account.requested": "في انتظار الموافقة", "account.share": "مشاركة @{name}'s profile", @@ -29,8 +29,8 @@ "account.unmute": "إلغاء الكتم عن @{name}", "account.unmute_notifications": "إلغاء كتم إخطارات @{name}", "account.view_full_profile": "عرض الملف الشخصي كاملا", - "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.message": "لقد طرأ هناك خطأ غير متوقّع.", + "alert.unexpected.title": "المعذرة !", "boost_modal.combo": "يمكنك ضغط {combo} لتخطّي هذه في المرّة القادمة", "bundle_column_error.body": "لقد وقع هناك خطأ أثناء عملية تحميل هذا العنصر.", "bundle_column_error.retry": "إعادة المحاولة", @@ -41,7 +41,7 @@ "column.blocks": "الحسابات المحجوبة", "column.community": "الخيط العام المحلي", "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.domain_blocks": "النطاقات المخفية", "column.favourites": "المفضلة", "column.follow_requests": "طلبات المتابعة", "column.home": "الرئيسية", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "لذِكر الناشر", "keyboard_shortcuts.reply": "للردّ", "keyboard_shortcuts.search": "للتركيز على البحث", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "لتحرير تبويق جديد", "keyboard_shortcuts.unfocus": "لإلغاء التركيز على حقل النص أو نافذة البحث", "keyboard_shortcuts.up": "للإنتقال إلى أعلى القائمة", @@ -157,7 +158,7 @@ "navigation_bar.blocks": "الحسابات المحجوبة", "navigation_bar.community_timeline": "الخيط العام المحلي", "navigation_bar.direct": "Direct messages", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.domain_blocks": "النطاقات المخفية", "navigation_bar.edit_profile": "تعديل الملف الشخصي", "navigation_bar.favourites": "المفضلة", "navigation_bar.follow_requests": "طلبات المتابعة", @@ -244,7 +245,7 @@ "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "تعذرت ترقية هذا المنشور", "status.delete": "إحذف", - "status.direct": "Direct message @{name}", + "status.direct": "رسالة خاصة إلى @{name}", "status.embed": "إدماج", "status.favourite": "أضف إلى المفضلة", "status.load_more": "حمّل المزيد", @@ -275,7 +276,7 @@ "tabs_bar.home": "الرئيسية", "tabs_bar.local_timeline": "المحلي", "tabs_bar.notifications": "الإخطارات", - "tabs_bar.search": "Search", + "tabs_bar.search": "البحث", "ui.beforeunload": "سوف تفقد مسودتك إن تركت ماستدون.", "upload_area.title": "إسحب ثم أفلت للرفع", "upload_button.label": "إضافة وسائط", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 25ef6db65..971475114 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 6a44808e0..2e5004cc9 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -2,7 +2,7 @@ "account.block": "Bloca @{name}", "account.block_domain": "Amaga-ho tot de {domain}", "account.blocked": "Bloquejat", - "account.direct": "Direct message @{name}", + "account.direct": "Missatge directe @{name}", "account.disclaimer_full": "La informació següent pot reflectir incompleta el perfil de l'usuari.", "account.domain_blocked": "Domini ocult", "account.edit_profile": "Edita el perfil", @@ -18,7 +18,7 @@ "account.mute_notifications": "Notificacions desactivades de @{name}", "account.muted": "Silenciat", "account.posts": "Toots", - "account.posts_with_replies": "Toots amb respostes", + "account.posts_with_replies": "Toots i respostes", "account.report": "Informe @{name}", "account.requested": "Esperant aprovació. Clic per a cancel·lar la petició de seguiment", "account.share": "Comparteix el perfil de @{name}", @@ -29,8 +29,8 @@ "account.unmute": "Treure silenci de @{name}", "account.unmute_notifications": "Activar notificacions de @{name}", "account.view_full_profile": "Mostra el perfil complet", - "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.message": "S'ha produït un error inesperat.", + "alert.unexpected.title": "Vaja!", "boost_modal.combo": "Pots premer {combo} per saltar-te això el proper cop", "bundle_column_error.body": "S'ha produït un error en carregar aquest component.", "bundle_column_error.retry": "Torna-ho a provar", @@ -41,7 +41,7 @@ "column.blocks": "Usuaris blocats", "column.community": "Línia de temps local", "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.domain_blocks": "Dominis ocults", "column.favourites": "Favorits", "column.follow_requests": "Peticions per seguir-te", "column.home": "Inici", @@ -59,7 +59,7 @@ "column_header.unpin": "No fixis", "column_subheading.navigation": "Navegació", "column_subheading.settings": "Configuració", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "Aquest toot només serà visible per a tots els usuaris esmentats.", "compose_form.hashtag_warning": "Aquest toot no es mostrarà en cap etiqueta ja que no està llistat. Només els toots públics poden ser cercats per etiqueta.", "compose_form.lock_disclaimer": "El teu compte no està bloquejat {locked}. Tothom pot seguir-te i veure els teus missatges a seguidors.", "compose_form.lock_disclaimer.lock": "blocat", @@ -68,7 +68,7 @@ "compose_form.publish_loud": "{publish}!", "compose_form.sensitive.marked": "Mèdia marcat com a sensible", "compose_form.sensitive.unmarked": "Mèdia no està marcat com a sensible", - "compose_form.spoiler.marked": "Text ocult sota l'avís", + "compose_form.spoiler.marked": "Text es ocult sota l'avís", "compose_form.spoiler.unmarked": "Text no ocult", "compose_form.spoiler_placeholder": "Escriu l'avís aquí", "confirmation_modal.cancel": "Cancel·la", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "per esmentar l'autor", "keyboard_shortcuts.reply": "respondre", "keyboard_shortcuts.search": "per centrar la cerca", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "per a començar un toot nou de trinca", "keyboard_shortcuts.unfocus": "descentrar l'area de composició de text/cerca", "keyboard_shortcuts.up": "moure amunt en la llista", @@ -157,7 +158,7 @@ "navigation_bar.blocks": "Usuaris bloquejats", "navigation_bar.community_timeline": "Línia de temps Local", "navigation_bar.direct": "Direct messages", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.domain_blocks": "Dominis ocults", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favorits", "navigation_bar.follow_requests": "Sol·licituds de seguiment", @@ -244,7 +245,7 @@ "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Aquesta publicació no pot ser retootejada", "status.delete": "Esborrar", - "status.direct": "Direct message @{name}", + "status.direct": "Missatge directe @{name}", "status.embed": "Incrustar", "status.favourite": "Favorit", "status.load_more": "Carrega més", @@ -275,7 +276,7 @@ "tabs_bar.home": "Inici", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notificacions", - "tabs_bar.search": "Search", + "tabs_bar.search": "Cerca", "ui.beforeunload": "El vostre esborrany es perdrà si sortiu de Mastodon.", "upload_area.title": "Arrossega i deixa anar per carregar", "upload_button.label": "Afegir multimèdia", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 69c2ae8d8..5ce90812e 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "um Autor_in zu erwähnen", "keyboard_shortcuts.reply": "um zu antworten", "keyboard_shortcuts.search": "um die Suche zu fokussieren", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "um einen neuen Toot zu beginnen", "keyboard_shortcuts.unfocus": "um das Textfeld/die Suche nicht mehr zu fokussieren", "keyboard_shortcuts.up": "sich in der Liste hinauf bewegen", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index e51163971..37587c14c 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "por mencii la aŭtoron", "keyboard_shortcuts.reply": "por respondi", "keyboard_shortcuts.search": "por fokusigi la serĉilon", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "por komenci tute novan mesaĝon", "keyboard_shortcuts.unfocus": "por malfokusigi la tekstujon aŭ la serĉilon", "keyboard_shortcuts.up": "por iri supren en la listo", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 61ea0588d..41d7db9da 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "para mencionar al autor", "keyboard_shortcuts.reply": "para responder", "keyboard_shortcuts.search": "para poner el foco en la búsqueda", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "para comenzar un nuevo toot", "keyboard_shortcuts.unfocus": "para retirar el foco de la caja de redacción/búsqueda", "keyboard_shortcuts.up": "para ir hacia arriba en la lista", diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json new file mode 100644 index 000000000..02b7bf7e5 --- /dev/null +++ b/app/javascript/mastodon/locales/eu.json @@ -0,0 +1,296 @@ +{ + "account.block": "Block @{name}", + "account.block_domain": "Hide everything from {domain}", + "account.blocked": "Blokeatuta", + "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.follows_you": "Follows you", + "account.hide_reblogs": "Hide boosts from @{name}", + "account.media": "Media", + "account.mention": "Mention @{name}", + "account.moved_to": "{name} has moved to:", + "account.mute": "Mute @{name}", + "account.mute_notifications": "Mute notifications from @{name}", + "account.muted": "Muted", + "account.posts": "Toots", + "account.posts_with_replies": "Toots and replies", + "account.report": "Report @{name}", + "account.requested": "Awaiting approval. Click to cancel follow request", + "account.share": "Share @{name}'s profile", + "account.show_reblogs": "Show boosts from @{name}", + "account.unblock": "Unblock @{name}", + "account.unblock_domain": "Unhide {domain}", + "account.unfollow": "Unfollow", + "account.unmute": "Unmute @{name}", + "account.unmute_notifications": "Unmute notifications from @{name}", + "account.view_full_profile": "View full profile", + "alert.unexpected.message": "An unexpected error occurred.", + "alert.unexpected.title": "Oops!", + "boost_modal.combo": "You can press {combo} to skip this next time", + "bundle_column_error.body": "Something went wrong while loading this component.", + "bundle_column_error.retry": "Try again", + "bundle_column_error.title": "Network error", + "bundle_modal_error.close": "Close", + "bundle_modal_error.message": "Something went wrong while loading this component.", + "bundle_modal_error.retry": "Try again", + "column.blocks": "Blocked users", + "column.community": "Local timeline", + "column.direct": "Direct messages", + "column.domain_blocks": "Hidden domains", + "column.favourites": "Favourites", + "column.follow_requests": "Follow requests", + "column.home": "Home", + "column.lists": "Lists", + "column.mutes": "Muted users", + "column.notifications": "Notifications", + "column.pins": "Pinned toot", + "column.public": "Federated timeline", + "column_back_button.label": "Back", + "column_header.hide_settings": "Hide settings", + "column_header.moveLeft_settings": "Move column to the left", + "column_header.moveRight_settings": "Move column to the right", + "column_header.pin": "Pin", + "column_header.show_settings": "Show settings", + "column_header.unpin": "Unpin", + "column_subheading.navigation": "Navigation", + "column_subheading.settings": "Settings", + "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", + "compose_form.lock_disclaimer.lock": "locked", + "compose_form.placeholder": "What is on your mind?", + "compose_form.publish": "Toot", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive.marked": "Media is 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.unmarked": "Text is not hidden", + "compose_form.spoiler_placeholder": "Write your warning here", + "confirmation_modal.cancel": "Cancel", + "confirmations.block.confirm": "Block", + "confirmations.block.message": "Are you sure you want to block {name}?", + "confirmations.delete.confirm": "Delete", + "confirmations.delete.message": "Are you sure you want to delete this status?", + "confirmations.delete_list.confirm": "Delete", + "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.domain_block.confirm": "Hide entire domain", + "confirmations.domain_block.message": "Are you really, really sure you want to block the entire {domain}? In most cases a few targeted blocks or mutes are sufficient and preferable.", + "confirmations.mute.confirm": "Mute", + "confirmations.mute.message": "Are you sure you want to mute {name}?", + "confirmations.unfollow.confirm": "Unfollow", + "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "embed.instructions": "Embed this status on your website by copying the code below.", + "embed.preview": "Here is what it will look like:", + "emoji_button.activity": "Activity", + "emoji_button.custom": "Custom", + "emoji_button.flags": "Flags", + "emoji_button.food": "Food & Drink", + "emoji_button.label": "Insert emoji", + "emoji_button.nature": "Nature", + "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻", + "emoji_button.objects": "Objects", + "emoji_button.people": "People", + "emoji_button.recent": "Frequently used", + "emoji_button.search": "Search...", + "emoji_button.search_results": "Search results", + "emoji_button.symbols": "Symbols", + "emoji_button.travel": "Travel & Places", + "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", + "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.hashtag": "There is nothing in this hashtag yet.", + "empty_column.home": "Your home timeline is empty! Visit {public} or use search to get started and meet other users.", + "empty_column.home.public_timeline": "the public timeline", + "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", + "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", + "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", + "follow_request.authorize": "Authorize", + "follow_request.reject": "Reject", + "getting_started.appsshort": "Apps", + "getting_started.faq": "FAQ", + "getting_started.heading": "Getting started", + "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}.", + "getting_started.userguide": "User Guide", + "home.column_settings.advanced": "Advanced", + "home.column_settings.basic": "Basic", + "home.column_settings.filter_regex": "Filter out by regular expressions", + "home.column_settings.show_reblogs": "Show boosts", + "home.column_settings.show_replies": "Show replies", + "home.settings": "Column settings", + "keyboard_shortcuts.back": "to navigate back", + "keyboard_shortcuts.boost": "to boost", + "keyboard_shortcuts.column": "to focus a status in one of the columns", + "keyboard_shortcuts.compose": "to focus the compose textarea", + "keyboard_shortcuts.description": "Description", + "keyboard_shortcuts.down": "to move down in the list", + "keyboard_shortcuts.enter": "to open status", + "keyboard_shortcuts.favourite": "to favourite", + "keyboard_shortcuts.heading": "Keyboard Shortcuts", + "keyboard_shortcuts.hotkey": "Hotkey", + "keyboard_shortcuts.legend": "to display this legend", + "keyboard_shortcuts.mention": "to mention author", + "keyboard_shortcuts.reply": "to reply", + "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toot": "to start a brand new toot", + "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", + "keyboard_shortcuts.up": "to move up in the list", + "lightbox.close": "Close", + "lightbox.next": "Next", + "lightbox.previous": "Previous", + "lists.account.add": "Add to list", + "lists.account.remove": "Remove from list", + "lists.delete": "Delete list", + "lists.edit": "Edit list", + "lists.new.create": "Add list", + "lists.new.title_placeholder": "New list title", + "lists.search": "Search among people you follow", + "lists.subheading": "Your lists", + "loading_indicator.label": "Loading...", + "media_gallery.toggle_visible": "Toggle visibility", + "missing_indicator.label": "Not found", + "missing_indicator.sublabel": "This resource could not be found", + "mute_modal.hide_notifications": "Hide notifications from this user?", + "navigation_bar.blocks": "Blocked users", + "navigation_bar.community_timeline": "Local timeline", + "navigation_bar.direct": "Direct messages", + "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.edit_profile": "Edit profile", + "navigation_bar.favourites": "Favourites", + "navigation_bar.follow_requests": "Follow requests", + "navigation_bar.info": "Extended information", + "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts", + "navigation_bar.lists": "Lists", + "navigation_bar.logout": "Logout", + "navigation_bar.mutes": "Muted users", + "navigation_bar.pins": "Pinned toots", + "navigation_bar.preferences": "Preferences", + "navigation_bar.public_timeline": "Federated timeline", + "notification.favourite": "{name} favourited your status", + "notification.follow": "{name} followed you", + "notification.mention": "{name} mentioned you", + "notification.reblog": "{name} boosted your status", + "notifications.clear": "Clear notifications", + "notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?", + "notifications.column_settings.alert": "Desktop notifications", + "notifications.column_settings.favourite": "Favourites:", + "notifications.column_settings.follow": "New followers:", + "notifications.column_settings.mention": "Mentions:", + "notifications.column_settings.push": "Push notifications", + "notifications.column_settings.push_meta": "This device", + "notifications.column_settings.reblog": "Boosts:", + "notifications.column_settings.show": "Show in column", + "notifications.column_settings.sound": "Play sound", + "onboarding.done": "Done", + "onboarding.next": "Next", + "onboarding.page_five.public_timelines": "The local timeline shows public posts from everyone on {domain}. The federated timeline shows public posts from everyone who people on {domain} follow. These are the Public Timelines, a great way to discover new people.", + "onboarding.page_four.home": "The home timeline shows posts from people you follow.", + "onboarding.page_four.notifications": "The notifications column shows when someone interacts with you.", + "onboarding.page_one.federation": "Mastodon is a network of independent servers joining up to make one larger social network. We call these servers instances.", + "onboarding.page_one.full_handle": "Your full handle", + "onboarding.page_one.handle_hint": "This is what you would tell your friends to search for.", + "onboarding.page_one.welcome": "Welcome to Mastodon!", + "onboarding.page_six.admin": "Your instance's admin is {admin}.", + "onboarding.page_six.almost_done": "Almost done...", + "onboarding.page_six.appetoot": "Bon Appetoot!", + "onboarding.page_six.apps_available": "There are {apps} available for iOS, Android and other platforms.", + "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.guidelines": "community guidelines", + "onboarding.page_six.read_guidelines": "Please read {domain}'s {guidelines}!", + "onboarding.page_six.various_app": "mobile apps", + "onboarding.page_three.profile": "Edit your profile to change your avatar, bio, and display name. There, you will also find other preferences.", + "onboarding.page_three.search": "Use the search bar to find people and look at hashtags, such as {illustration} and {introductions}. To look for a person who is not on this instance, use their full handle.", + "onboarding.page_two.compose": "Write posts from the compose column. You can upload images, change privacy settings, and add content warnings with the icons below.", + "onboarding.skip": "Skip", + "privacy.change": "Adjust status privacy", + "privacy.direct.long": "Post to mentioned users only", + "privacy.direct.short": "Direct", + "privacy.private.long": "Post to followers only", + "privacy.private.short": "Followers-only", + "privacy.public.long": "Post to public timelines", + "privacy.public.short": "Public", + "privacy.unlisted.long": "Do not show in public timelines", + "privacy.unlisted.short": "Unlisted", + "regeneration_indicator.label": "Loading…", + "regeneration_indicator.sublabel": "Your home feed is being prepared!", + "relative_time.days": "{number}d", + "relative_time.hours": "{number}h", + "relative_time.just_now": "now", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Cancel", + "report.forward": "Forward to {target}", + "report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?", + "report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:", + "report.placeholder": "Additional comments", + "report.submit": "Submit", + "report.target": "Report {target}", + "search.placeholder": "Search", + "search_popout.search_format": "Advanced search format", + "search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.", + "search_popout.tips.hashtag": "hashtag", + "search_popout.tips.status": "status", + "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.user": "user", + "search_results.accounts": "People", + "search_results.hashtags": "Hashtags", + "search_results.statuses": "Toots", + "search_results.total": "{count, number} {count, plural, one {result} other {results}}", + "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", + "status.cancel_reblog_private": "Unboost", + "status.cannot_reblog": "This post cannot be boosted", + "status.delete": "Delete", + "status.direct": "Direct message @{name}", + "status.embed": "Embed", + "status.favourite": "Favourite", + "status.load_more": "Load more", + "status.media_hidden": "Media hidden", + "status.mention": "Mention @{name}", + "status.more": "More", + "status.mute": "Mute @{name}", + "status.mute_conversation": "Mute conversation", + "status.open": "Expand this status", + "status.pin": "Pin on profile", + "status.pinned": "Pinned toot", + "status.reblog": "Boost", + "status.reblog_private": "Boost to original audience", + "status.reblogged_by": "{name} boosted", + "status.reply": "Reply", + "status.replyAll": "Reply to thread", + "status.report": "Report @{name}", + "status.sensitive_toggle": "Click to view", + "status.sensitive_warning": "Sensitive content", + "status.share": "Share", + "status.show_less": "Show less", + "status.show_less_all": "Show less for all", + "status.show_more": "Show more", + "status.show_more_all": "Show more for all", + "status.unmute_conversation": "Unmute conversation", + "status.unpin": "Unpin from profile", + "tabs_bar.federated_timeline": "Federated", + "tabs_bar.home": "Home", + "tabs_bar.local_timeline": "Local", + "tabs_bar.notifications": "Notifications", + "tabs_bar.search": "Search", + "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", + "upload_area.title": "Drag & drop to upload", + "upload_button.label": "Add media", + "upload_form.description": "Describe for the visually impaired", + "upload_form.focus": "Crop", + "upload_form.undo": "Undo", + "upload_progress.label": "Uploading...", + "video.close": "Close video", + "video.exit_fullscreen": "Exit full screen", + "video.expand": "Expand video", + "video.fullscreen": "Full screen", + "video.hide": "Hide video", + "video.mute": "Mute sound", + "video.pause": "Pause", + "video.play": "Play", + "video.unmute": "Unmute sound" +} diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index cfe93007d..99aba00c3 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "برای نام‌بردن از نویسنده", "keyboard_shortcuts.reply": "برای پاسخ‌دادن", "keyboard_shortcuts.search": "برای فعال‌کردن جستجو", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "برای آغاز یک بوق تازه", "keyboard_shortcuts.unfocus": "برای برداشتن توجه از نوشتن/جستجو", "keyboard_shortcuts.up": "برای بالا رفتن در فهرست", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 1677c3c6c..07d4d9aa5 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "mainitse julkaisija", "keyboard_shortcuts.reply": "vastaa", "keyboard_shortcuts.search": "siirry hakukenttään", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "ala kirjoittaa uutta tuuttausta", "keyboard_shortcuts.unfocus": "siirry pois tekstikentästä tai hakukentästä", "keyboard_shortcuts.up": "siirry listassa ylöspäin", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 9a9e8ab79..0343cc618 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -2,7 +2,7 @@ "account.block": "Bloquer @{name}", "account.block_domain": "Tout masquer venant de {domain}", "account.blocked": "Bloqué", - "account.direct": "Direct message @{name}", + "account.direct": "Message direct @{name}", "account.disclaimer_full": "Les données ci-dessous peuvent ne pas refléter ce profil dans sa totalité.", "account.domain_blocked": "Domaine caché", "account.edit_profile": "Modifier le profil", @@ -18,7 +18,7 @@ "account.mute_notifications": "Ignorer les notifications de @{name}", "account.muted": "Silencé", "account.posts": "Pouets", - "account.posts_with_replies": "Pouets avec réponses", + "account.posts_with_replies": "Pouets et réponses", "account.report": "Signaler", "account.requested": "En attente d'approbation. Cliquez pour annuler la requête", "account.share": "Partager le profil de @{name}", @@ -29,8 +29,8 @@ "account.unmute": "Ne plus masquer", "account.unmute_notifications": "Réactiver les notifications de @{name}", "account.view_full_profile": "Afficher le profil complet", - "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.message": "Une erreur non-attendue s'est produite.", + "alert.unexpected.title": "Oups !", "boost_modal.combo": "Vous pouvez appuyer sur {combo} pour pouvoir passer ceci, la prochaine fois", "bundle_column_error.body": "Une erreur s’est produite lors du chargement de ce composant.", "bundle_column_error.retry": "Réessayer", @@ -41,7 +41,7 @@ "column.blocks": "Comptes bloqués", "column.community": "Fil public local", "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.domain_blocks": "Domaines cachés", "column.favourites": "Favoris", "column.follow_requests": "Demandes de suivi", "column.home": "Accueil", @@ -59,7 +59,7 @@ "column_header.unpin": "Retirer", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Paramètres", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "Ce pouet sera uniquement visible à tous les utilisateurs mentionnés.", "compose_form.hashtag_warning": "Ce pouet ne sera pas listé dans les recherches par hashtag car sa visibilité est réglée sur \"non-listé\". Seuls les pouets avec une visibilité \"publique\" peuvent être recherchés par hashtag.", "compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre et voir vos pouets privés.", "compose_form.lock_disclaimer.lock": "verrouillé", @@ -105,7 +105,7 @@ "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag.", "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres personnes.", "empty_column.home.public_timeline": "le fil public", - "empty_column.list": "Il n'y a rien dans cette liste pour l'instant. Dès que des personnes de cette liste publieront de nouveaux statuts, ils apparaîtront ici.", + "empty_column.list": "Il n'y a rien dans cette liste pour l'instant. Dès que des personnes de cette liste publierons de nouveaux statuts, ils apparaîtront ici.", "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres personnes pour débuter la conversation.", "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres instances pour remplir le fil public", "follow_request.authorize": "Accepter", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "pour mentionner l'auteur", "keyboard_shortcuts.reply": "pour répondre", "keyboard_shortcuts.search": "pour cibler la recherche", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "pour démarrer un tout nouveau pouet", "keyboard_shortcuts.unfocus": "pour recentrer composer textarea/search", "keyboard_shortcuts.up": "pour remonter dans la liste", @@ -157,7 +158,7 @@ "navigation_bar.blocks": "Comptes bloqués", "navigation_bar.community_timeline": "Fil public local", "navigation_bar.direct": "Direct messages", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.domain_blocks": "Domaines cachés", "navigation_bar.edit_profile": "Modifier le profil", "navigation_bar.favourites": "Favoris", "navigation_bar.follow_requests": "Demandes de suivi", @@ -244,7 +245,7 @@ "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Cette publication ne peut être boostée", "status.delete": "Effacer", - "status.direct": "Direct message @{name}", + "status.direct": "Message direct @{name}", "status.embed": "Intégrer", "status.favourite": "Ajouter aux favoris", "status.load_more": "Charger plus", @@ -275,7 +276,7 @@ "tabs_bar.home": "Accueil", "tabs_bar.local_timeline": "Fil public local", "tabs_bar.notifications": "Notifications", - "tabs_bar.search": "Search", + "tabs_bar.search": "Chercher", "ui.beforeunload": "Votre brouillon sera perdu si vous quittez Mastodon.", "upload_area.title": "Glissez et déposez pour envoyer", "upload_button.label": "Joindre un média", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index fca42374d..f7ad3e590 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -18,7 +18,7 @@ "account.mute_notifications": "Acalar as notificacións de @{name}", "account.muted": "Muted", "account.posts": "Toots", - "account.posts_with_replies": "Toots with replies", + "account.posts_with_replies": "Toots e respostas", "account.report": "Informar sobre @{name}", "account.requested": "Agardando aceptación. Pulse para cancelar a solicitude de seguimento", "account.share": "Compartir o perfil de @{name}", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "para mencionar o autor", "keyboard_shortcuts.reply": "para responder", "keyboard_shortcuts.search": "para centrar a busca", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "escribir un toot novo", "keyboard_shortcuts.unfocus": "quitar o foco do área de escritura/busca", "keyboard_shortcuts.up": "ir hacia arriba na lista", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index e3e87f1d0..0ffbb14f3 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "לאזכר את המחבר(ת)", "keyboard_shortcuts.reply": "לענות", "keyboard_shortcuts.search": "להתמקד בחלון החיפוש", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "להתחיל חיצרוץ חדש", "keyboard_shortcuts.unfocus": "לצאת מתיבת חיבור/חיפוש", "keyboard_shortcuts.up": "לנוע במעלה הרשימה", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index b41c98394..c41cc3ea1 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 956accc67..a0c186184 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "szerző megjelenítése", "keyboard_shortcuts.reply": "válaszolás", "keyboard_shortcuts.search": "kereső kiemelése", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "új tülk megkezdése", "keyboard_shortcuts.unfocus": "tülk szerkesztés/keresés fókuszpontból való kivétele", "keyboard_shortcuts.up": "fennebb helyezés a listában", diff --git a/app/javascript/mastodon/locales/hy.json b/app/javascript/mastodon/locales/hy.json index 33e079201..a0442bad4 100644 --- a/app/javascript/mastodon/locales/hy.json +++ b/app/javascript/mastodon/locales/hy.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "հեղինակին նշելու համար", "keyboard_shortcuts.reply": "պատասխանելու համար", "keyboard_shortcuts.search": "որոնման դաշտին սեւեռվելու համար", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "թարմ թութ սկսելու համար", "keyboard_shortcuts.unfocus": "տեքստի/որոնման տիրույթից ապասեւեռվելու համար", "keyboard_shortcuts.up": "ցանկով վերեւ շարժվելու համար", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 412ffd3a0..2fd922544 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.search": "untuk fokus mencari", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 9730bf934..ed45ee11e 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index 5146d7ca2..c80fc0bd6 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -17,8 +17,8 @@ "account.mute": "Silenzia @{name}", "account.mute_notifications": "Mute notifications from @{name}", "account.muted": "Muted", - "account.posts": "Posts", - "account.posts_with_replies": "Toots with replies", + "account.posts": "Toot", + "account.posts_with_replies": "Toot con risposte", "account.report": "Segnala @{name}", "account.requested": "In attesa di approvazione", "account.share": "Share @{name}'s profile", @@ -105,7 +105,7 @@ "empty_column.hashtag": "Non c'è ancora nessun post con questo hashtag.", "empty_column.home": "Non stai ancora seguendo nessuno. Visita {public} o usa la ricerca per incontrare nuove persone.", "empty_column.home.public_timeline": "la timeline pubblica", - "empty_column.list": "There is nothing in this list yet.", + "empty_column.list": "Non c'è niente in questo elenco ancora. Quando i membri di questo elenco postano nuovi stati, questi appariranno qui.", "empty_column.notifications": "Non hai ancora nessuna notifica. Interagisci con altri per iniziare conversazioni.", "empty_column.public": "Qui non c'è nulla! Scrivi qualcosa pubblicamente, o aggiungi utenti da altri server per riempire questo spazio.", "follow_request.authorize": "Autorizza", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 23223cac3..a06bdad24 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -29,8 +29,8 @@ "account.unmute": "@{name}さんのミュートを解除", "account.unmute_notifications": "@{name}さんからの通知を受け取るようにする", "account.view_full_profile": "全ての情報を見る", - "alert.unexpected.message": "不明なエラーが発生しました", - "alert.unexpected.title": "エラー", + "alert.unexpected.message": "不明なエラーが発生しました。", + "alert.unexpected.title": "エラー!", "boost_modal.combo": "次からは{combo}を押せばスキップできます", "bundle_column_error.body": "コンポーネントの読み込み中に問題が発生しました。", "bundle_column_error.retry": "再試行", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 92367dc95..2a2734673 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -2,7 +2,7 @@ "account.block": "@{name}을 차단", "account.block_domain": "{domain} 전체를 숨김", "account.blocked": "차단 됨", - "account.direct": "Direct message @{name}", + "account.direct": "@{name}으로부터의 다이렉트 메시지", "account.disclaimer_full": "여기 있는 정보는 유저의 프로파일을 정확히 반영하지 못 할 수도 있습니다.", "account.domain_blocked": "도메인 숨겨짐", "account.edit_profile": "프로필 편집", @@ -12,7 +12,7 @@ "account.follows_you": "날 팔로우합니다", "account.hide_reblogs": "@{name}의 부스트를 숨기기", "account.media": "미디어", - "account.mention": "답장", + "account.mention": "@{name}에게 글쓰기", "account.moved_to": "{name}는 계정을 이동했습니다:", "account.mute": "@{name} 뮤트", "account.mute_notifications": "@{name}의 알림을 뮤트", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "멘션", "keyboard_shortcuts.reply": "답장", "keyboard_shortcuts.search": "검색창에 포커스", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "새 툿 작성", "keyboard_shortcuts.unfocus": "작성창에서 포커스 해제", "keyboard_shortcuts.up": "리스트에서 위로 이동", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index c18ddbd01..338b7aebc 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -18,7 +18,7 @@ "account.mute_notifications": "Negeer meldingen van @{name}", "account.muted": "Genegeerd", "account.posts": "Toots", - "account.posts_with_replies": "Toots met reacties", + "account.posts_with_replies": "Toots en reacties", "account.report": "Rapporteer @{name}", "account.requested": "Wacht op goedkeuring. Klik om het volgverzoek te annuleren", "account.share": "Profiel van @{name} delen", @@ -29,8 +29,8 @@ "account.unmute": "@{name} niet meer negeren", "account.unmute_notifications": "@{name} meldingen niet meer negeren", "account.view_full_profile": "Volledig profiel tonen", - "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.message": "Er deed zich een onverwachte fout voor", + "alert.unexpected.title": "Oeps!", "boost_modal.combo": "Je kunt {combo} klikken om dit de volgende keer over te slaan", "bundle_column_error.body": "Tijdens het laden van dit onderdeel is er iets fout gegaan.", "bundle_column_error.retry": "Opnieuw proberen", @@ -41,7 +41,7 @@ "column.blocks": "Geblokkeerde gebruikers", "column.community": "Lokale tijdlijn", "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.domain_blocks": "Verborgen domeinen", "column.favourites": "Favorieten", "column.follow_requests": "Volgverzoeken", "column.home": "Start", @@ -59,7 +59,7 @@ "column_header.unpin": "Losmaken", "column_subheading.navigation": "Navigatie", "column_subheading.settings": "Instellingen", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "Deze toot zal alleen zichtbaar zijn voor alle vermelde gebruikers.", "compose_form.hashtag_warning": "Deze toot valt niet onder een hashtag te bekijken, omdat deze niet op openbare tijdlijnen wordt getoond. Alleen openbare toots kunnen via hashtags gevonden worden.", "compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en toots zien die je alleen aan volgers hebt gericht.", "compose_form.lock_disclaimer.lock": "besloten", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "om de auteur te vermelden", "keyboard_shortcuts.reply": "om te reageren", "keyboard_shortcuts.search": "om het zoekvak te focussen", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "om een nieuwe toot te starten", "keyboard_shortcuts.unfocus": "om het tekst- en zoekvak te ontfocussen", "keyboard_shortcuts.up": "om omhoog te bewegen in de lijst", @@ -157,7 +158,7 @@ "navigation_bar.blocks": "Geblokkeerde gebruikers", "navigation_bar.community_timeline": "Lokale tijdlijn", "navigation_bar.direct": "Direct messages", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.domain_blocks": "Verborgen domeinen", "navigation_bar.edit_profile": "Profiel bewerken", "navigation_bar.favourites": "Favorieten", "navigation_bar.follow_requests": "Volgverzoeken", @@ -244,7 +245,7 @@ "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Deze toot kan niet geboost worden", "status.delete": "Verwijderen", - "status.direct": "Direct message @{name}", + "status.direct": "Directe toot @{name}", "status.embed": "Embed", "status.favourite": "Favoriet", "status.load_more": "Meer laden", @@ -275,7 +276,7 @@ "tabs_bar.home": "Start", "tabs_bar.local_timeline": "Lokaal", "tabs_bar.notifications": "Meldingen", - "tabs_bar.search": "Search", + "tabs_bar.search": "Zoeken", "ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.", "upload_area.title": "Hierin slepen om te uploaden", "upload_button.label": "Media toevoegen", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index 282a72acb..0ee6d0722 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "å nevne forfatter", "keyboard_shortcuts.reply": "for å svare", "keyboard_shortcuts.search": "å fokusere søk", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "å starte en helt ny tut", "keyboard_shortcuts.unfocus": "å ufokusere komponerings-/søkefeltet", "keyboard_shortcuts.up": "å flytte opp i listen", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 7170aefb8..66f3fa275 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "mencionar l’autor", "keyboard_shortcuts.reply": "respondre", "keyboard_shortcuts.search": "anar a la recèrca", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "començar un estatut tot novèl", "keyboard_shortcuts.unfocus": "quitar lo camp tèxte/de recèrca", "keyboard_shortcuts.up": "far montar dins la lista", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index c604476c7..7013822bf 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -29,7 +29,7 @@ "account.unmute": "Não silenciar @{name}", "account.unmute_notifications": "Retirar silêncio das notificações vindas de @{name}", "account.view_full_profile": "Ver perfil completo", - "alert.unexpected.message": "An unexpected error occurred.", + "alert.unexpected.message": "Um erro inesperado ocorreu.", "alert.unexpected.title": "Oops!", "boost_modal.combo": "Você pode pressionar {combo} para ignorar este diálogo na próxima vez", "bundle_column_error.body": "Algo de errado aconteceu enquanto este componente era carregado.", @@ -59,7 +59,7 @@ "column_header.unpin": "Desafixar", "column_subheading.navigation": "Navegação", "column_subheading.settings": "Configurações", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "Este toot só será visível a todos os usuários mencionados.", "compose_form.hashtag_warning": "Esse toot não será listado em nenhuma hashtag por ser não listado. Somente toots públicos podem ser pesquisados por hashtag.", "compose_form.lock_disclaimer": "A sua conta não está {locked}. Qualquer pessoa pode te seguir e visualizar postagens direcionadas a apenas seguidores.", "compose_form.lock_disclaimer.lock": "trancada", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "para mencionar o autor", "keyboard_shortcuts.reply": "para responder", "keyboard_shortcuts.search": "para focar a pesquisa", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "para compor um novo toot", "keyboard_shortcuts.unfocus": "para remover o foco da área de composição/pesquisa", "keyboard_shortcuts.up": "para mover para cima na lista", @@ -275,7 +276,7 @@ "tabs_bar.home": "Página inicial", "tabs_bar.local_timeline": "Local", "tabs_bar.notifications": "Notificações", - "tabs_bar.search": "Search", + "tabs_bar.search": "Buscar", "ui.beforeunload": "Seu rascunho será perdido se você sair do Mastodon.", "upload_area.title": "Arraste e solte para enviar", "upload_button.label": "Adicionar mídia", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index 826785aad..ce816dc41 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "para mencionar o autor", "keyboard_shortcuts.reply": "para responder", "keyboard_shortcuts.search": "para focar na pesquisa", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "para compor um novo post", "keyboard_shortcuts.unfocus": "para remover o foco da área de publicação/pesquisa", "keyboard_shortcuts.up": "para mover para cima na lista", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index bb3cc1794..8eeebaf73 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "упомянуть автора поста", "keyboard_shortcuts.reply": "ответить", "keyboard_shortcuts.search": "перейти к поиску", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "начать писать новый пост", "keyboard_shortcuts.unfocus": "убрать фокус с поля ввода/поиска", "keyboard_shortcuts.up": "вверх по списку", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 58274fd2d..8b19cac6f 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -2,7 +2,7 @@ "account.block": "Blokovať @{name}", "account.block_domain": "Ukryť všetko z {domain}", "account.blocked": "Blokovaný/á", - "account.direct": "Direct message @{name}", + "account.direct": "Súkromná správa pre @{name}", "account.disclaimer_full": "Inofrmácie nižšie nemusia byť úplným odrazom uživateľovho účtu.", "account.domain_blocked": "Doména ukrytá", "account.edit_profile": "Upraviť profil", @@ -29,7 +29,7 @@ "account.unmute": "Prestať ignorovať @{name}", "account.unmute_notifications": "Odtĺmiť notifikácie od @{name}", "account.view_full_profile": "Pozri celý profil", - "alert.unexpected.message": "An unexpected error occurred.", + "alert.unexpected.message": "Vyskytla sa neočakávaná chyba.", "alert.unexpected.title": "Oops!", "boost_modal.combo": "Nabudúce môžete kliknúť {combo} aby ste preskočili", "bundle_column_error.body": "Nastala chyba pri načítaní tohto komponentu.", @@ -41,7 +41,7 @@ "column.blocks": "Blokovaní užívatelia", "column.community": "Lokálna časová os", "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.domain_blocks": "Skryté domény", "column.favourites": "Obľúbené", "column.follow_requests": "Žiadosti o sledovanie", "column.home": "Domov", @@ -59,7 +59,7 @@ "column_header.unpin": "Odopnúť", "column_subheading.navigation": "Navigácia", "column_subheading.settings": "Nastavenia", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "Tento príspevok bude videný výhradne iba spomenutými užívateľmi.", "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.lock": "zamknutý", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "spomenúť autora", "keyboard_shortcuts.reply": "odpovedať", "keyboard_shortcuts.search": "zamerať sa na vyhľadávanie", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "začať úplne novú hlášku", "keyboard_shortcuts.unfocus": "nesústrediť sa na písaciu plochu, alebo hľadanie", "keyboard_shortcuts.up": "posunúť sa vyššie v zozname", @@ -157,7 +158,7 @@ "navigation_bar.blocks": "Blokovaní užívatelia", "navigation_bar.community_timeline": "Lokálna časová os", "navigation_bar.direct": "Direct messages", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.domain_blocks": "Skryté domény", "navigation_bar.edit_profile": "Upraviť profil", "navigation_bar.favourites": "Obľúbené", "navigation_bar.follow_requests": "Žiadosti o sledovanie", @@ -244,7 +245,7 @@ "status.cancel_reblog_private": "Unboost", "status.cannot_reblog": "Tento príspevok nemôže byť re-tootnutý", "status.delete": "Zmazať", - "status.direct": "Direct message @{name}", + "status.direct": "Súkromná správa @{name}", "status.embed": "Vložiť", "status.favourite": "Páči sa mi", "status.load_more": "Zobraz viac", @@ -275,7 +276,7 @@ "tabs_bar.home": "Domov", "tabs_bar.local_timeline": "Lokálna", "tabs_bar.notifications": "Notifikácie", - "tabs_bar.search": "Search", + "tabs_bar.search": "Hľadaj", "ui.beforeunload": "Čo máte rozpísané sa stratí, ak opustíte Mastodon.", "upload_area.title": "Ťahaj a pusti pre nahratie", "upload_button.label": "Pridať médiá", diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json index e4d07edd1..b1ea0d179 100644 --- a/app/javascript/mastodon/locales/sr-Latn.json +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "da pomenete autora", "keyboard_shortcuts.reply": "da odgovorite", "keyboard_shortcuts.search": "da se prebacite na pretragu", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "da započnete skroz novi tut", "keyboard_shortcuts.unfocus": "da ne budete više na pretrazi/pravljenju novog tuta", "keyboard_shortcuts.up": "da se pomerite na gore u listi", diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json index 60c781e9d..aa978675f 100644 --- a/app/javascript/mastodon/locales/sr.json +++ b/app/javascript/mastodon/locales/sr.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "да поменете аутора", "keyboard_shortcuts.reply": "да одговорите", "keyboard_shortcuts.search": "да се пребаците на претрагу", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "да започнете скроз нови тут", "keyboard_shortcuts.unfocus": "да не будете више на претрази/прављењу новог тута", "keyboard_shortcuts.up": "да се померите на горе у листи", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 8fa6992f1..7db4d57a7 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "att nämna författaren", "keyboard_shortcuts.reply": "att svara", "keyboard_shortcuts.search": "att fokusera sökfältet", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "att börja en helt ny toot", "keyboard_shortcuts.unfocus": "att avfokusera komponera text fält / sökfält", "keyboard_shortcuts.up": "att flytta upp i listan", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index 3b91c0d2c..82b44fe30 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index cdf6f46a3..056fbfe8f 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 261e5795e..1a7b58789 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index aba0bde83..a3a4de0af 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "提及嘟文作者", "keyboard_shortcuts.reply": "回复嘟文", "keyboard_shortcuts.search": "选择搜索框", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "发送新嘟文", "keyboard_shortcuts.unfocus": "取消输入", "keyboard_shortcuts.up": "在列表中让光标上移", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index b5ebd20fc..7719e08a6 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "提及作者", "keyboard_shortcuts.reply": "回覆", "keyboard_shortcuts.search": "把標示移動到搜索", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "新的推文", "keyboard_shortcuts.unfocus": "把標示移離文字輸入和搜索", "keyboard_shortcuts.up": "在列表往上移動", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 28d634600..84ff25e03 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "到提到的作者", "keyboard_shortcuts.reply": "到回應", "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/config/locales/activerecord.eu.yml b/config/locales/activerecord.eu.yml new file mode 100644 index 000000000..7b0ebe0b0 --- /dev/null +++ b/config/locales/activerecord.eu.yml @@ -0,0 +1,9 @@ +--- +eu: + activerecord: + errors: + models: + account: + attributes: + username: + invalid: letrak, zenbakiak eta gidoi baxuak besterik ez diff --git a/config/locales/ca.yml b/config/locales/ca.yml index c9173cd5e..9e927f74f 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -4,6 +4,7 @@ ca: about_hashtag_html: Aquests són toots públics etiquetats amb #%{hashtag}. Pots interactuar amb ells si tens un compte a qualsevol lloc del fediverse. about_mastodon_html: Mastodon és una xarxa social basada en protocols web oberts i en programari lliure i de codi obert. Està descentralitzat com el correu electrònic. about_this: Quant a + administered_by: 'Administrat per:' closed_registrations: Actualment, el registre està tancat en aquesta instància. Malgrat això! Pots trobar una altra instància per fer-te un compte i obtenir accés a la mateixa xarxa des d'allà. contact: Contacte contact_missing: No configurat @@ -60,7 +61,15 @@ ca: destroyed_msg: Nota de moderació destruïda amb èxit! accounts: are_you_sure: N'estàs segur? + avatar: Avatar by_domain: Domini + change_email: + changed_msg: El correu electrònic del compte s'ha canviat correctament! + current_email: Correu electrònic actual + label: Canviar l'adreça de correu + new_email: Nou correu + submit: Canviar adreça de correu + title: Canviar adreça de correu de %{username} confirm: Confirma confirmed: Confirmat demote: Degrada @@ -108,6 +117,7 @@ ca: public: Públic push_subscription_expires: La subscripció PuSH expira redownload: Actualitza l'avatar + remove_avatar: Eliminar avatar reset: Reinicialitza reset_password: Restableix la contrasenya resubscribe: Torna a subscriure @@ -128,6 +138,7 @@ ca: statuses: Estats subscribe: Subscriu title: Comptes + unconfirmed_email: Correu electrònic sense confirmar undo_silenced: Deixa de silenciar undo_suspension: Desfés la suspensió unsubscribe: Cancel·la la subscripció @@ -135,6 +146,8 @@ ca: web: Web action_logs: actions: + assigned_to_self_report: "%{name} han assignat l'informe %{target} a ells mateixos" + change_email_user: "%{name} ha canviat l'adreça de correu electrònic del usuari %{target}" confirm_user: "%{name} ha confirmat l'adreça de correu electrònic de l'usuari %{target}" create_custom_emoji: "%{name} ha pujat un nou emoji %{target}" create_domain_block: "%{name} ha blocat el domini %{target}" @@ -150,10 +163,13 @@ ca: enable_user: "%{name} ha activat l'accés per a l'usuari %{target}" memorialize_account: "%{name} ha convertit el compte %{target} en una pàgina de memorial" promote_user: "%{name} ha promogut l'usuari %{target}" + remove_avatar_user: "%{name} ha eliminat l'avatar de %{target}" + reopen_report: "%{name} ha reobert l'informe %{target}" reset_password_user: "%{name} ha restablert la contrasenya de l'usuari %{target}" - resolve_report: "%{name} ha descartat l'informe %{target}" + resolve_report: "%{name} ha resolt l'informe %{target}" silence_account: "%{name} ha silenciat el compte de %{target}" suspend_account: "%{name} ha suspès el compte de %{target}" + unassigned_report: "%{name} ha des-assignat l'informe %{target}" unsilence_account: "%{name} ha silenciat el compte de %{target}" unsuspend_account: "%{name} ha llevat la suspensió del compte de %{target}" update_custom_emoji: "%{name} ha actualitzat l'emoji %{target}" @@ -239,28 +255,48 @@ ca: expired: Caducat title: Filtre title: Convida + report_notes: + created_msg: La nota del informe s'ha creat correctament! + destroyed_msg: La nota del informe s'ha esborrat correctament! reports: + account: + note: nota + report: informe action_taken_by: Mesures adoptades per are_you_sure: N'estàs segur? + assign_to_self: Assignar-me + assigned: Assignar Moderador comment: none: Cap + created_at: Reportat delete: Suprimeix id: ID mark_as_resolved: Marca com a resolt + mark_as_unresolved: Marcar sense resoldre + notes: + create: Afegir nota + create_and_resolve: Resoldre amb Nota + create_and_unresolve: Reobrir amb Nota + delete: Esborrar + placeholder: Descriu les accions que s'han pres o qualsevol altra actualització d'aquest informe… nsfw: 'false': Mostra els fitxers multimèdia adjunts 'true': Amaga els fitxers multimèdia adjunts + reopen: Reobrir Informe report: 'Informe #%{id}' report_contents: Contingut reported_account: Compte reportat reported_by: Reportat per resolved: Resolt + resolved_msg: Informe resolt amb èxit! silence_account: Silencia el compte status: Estat suspend_account: Suspèn el compte target: Objectiu title: Informes + unassign: Treure assignació unresolved: No resolt + updated_at: Actualitzat view: Visualització settings: activity_api_enabled: @@ -381,6 +417,7 @@ ca: security: Seguretat set_new_password: Estableix una contrasenya nova authorize_follow: + already_following: Ja estàs seguint aquest compte error: Malauradament, ha ocorregut un error cercant el compte remot follow: Segueix follow_request: 'Has enviat una sol·licitud de seguiment a:' @@ -473,6 +510,7 @@ ca: '21600': 6 hores '3600': 1 hora '43200': 12 hores + '604800': 1 setmana '86400': 1 dia expires_in_prompt: Mai generate: Genera @@ -576,6 +614,10 @@ ca: missing_resource: No s'ha pogut trobar la URL de redirecció necessaria per al compte proceed: Comença a seguir prompt: 'Seguiràs a:' + remote_unfollow: + error: Error + title: Títol + unfollowed: Sense seguir sessions: activity: Última activitat browser: Navegador @@ -664,6 +706,83 @@ ca: reblogged: ha impulsat sensitive_content: Contingut sensible terms: + body_html: | +

    Privacy Policy

    +

    Quina informació recollim?

    + +
      +
    • Informació bàsica del compte: Si et registres en aquest servidor, se´t pot demanar que introdueixis un nom d'usuari, una adreça de correu electrònic i una contrasenya. També pots introduir informació de perfil addicional, com ara un nom de visualització i una biografia, i carregar una imatge de perfil i de capçalera. El nom d'usuari, el nom de visualització, la biografia, la imatge de perfil i la imatge de capçalera sempre apareixen públicament.
    • +
    • Publicacions, seguiment i altra informació pública: La llista de persones que segueixes s'enumeren públicament i el mateix passa amb els teus seguidors. Quan envies un missatge, la data i l'hora s'emmagatzemen, així com l'aplicació que va enviar el missatge. Els missatges poden contenir multimèdia, com ara imatges i vídeos. Els toots públics i no llistats estan disponibles públicament. En quan tinguis un toot en el teu perfil, aquest també és informació pública. Les teves entrades es lliuren als teus seguidors que en alguns casos significa que es lliuren a diferents servidors i s'hi emmagatzemen còpies. Quan suprimeixes publicacions, també es lliurarà als vostres seguidors. L'acció d'impulsar o marcar com a favorit una publicació sempre és pública.
    • +
    • Toots directes i per a només seguidors: Totes les publicacions s'emmagatzemen i processen al servidor. Els toots per a només seguidors només es lliuren als teus seguidors i als usuaris que s'esmenten en ells i els toots directes només es lliuren als usuaris esmentats. En alguns casos, significa que es lliuren a diferents servidors i s'hi emmagatzemen còpies. Fem un esforç de bona fe per limitar l'accés a aquestes publicacions només a les persones autoritzades, però és possible que altres servidors no ho facin. Per tant, és important revisar els servidors als quals pertanyen els teus seguidors. Pots canviar d'opció per aprovar i rebutjar els nous seguidors manualment a la configuració. Tingues en compte que els operadors del servidor i qualsevol servidor receptor poden visualitzar aquests missatges i els destinataris poden fer una captura de pantalla, copiar-los o tornar-los a compartir. No comparteixis cap informació perillosa a Mastodon.
    • +
    • IPs i altres metadades: Quan inicies sessió registrem l'adreça IP en què has iniciat la sessió, així com el nom de l'aplicació o navegador. Totes les sessions registrades estan disponibles per a la teva revisió i revocació a la configuració. L'última adreça IP utilitzada s'emmagatzema durant un màxim de 12 mesos. També podrem conservar els registres del servidor que inclouen l'adreça IP de cada sol·licitud al nostre servidor.
    • +
    + +
    + +

    Per a què utilitzem la teva informació?

    + +

    Qualsevol de la informació que recopilem de tu es pot utilitzar de la manera següent:

    + +
      +
    • Per proporcionar la funcionalitat bàsica de Mastodon. Només pots interactuar amb el contingut d'altres persones i publicar el teu propi contingut quan hàgis iniciat la sessió. Per exemple, pots seguir altres persones per veure les publicacions combinades a la teva pròpia línia de temps personalitzada.
    • +
    • Per ajudar a la moderació de la comunitat, per exemple comparar la teva adreça IP amb altres conegudes per determinar l'evasió de la prohibició o altres infraccions.
    • +
    • L'adreça electrònica que proporciones pot utilitzar-se per enviar-te informació, notificacions sobre altres persones que interactuen amb el teu contingut o t'envien missatges, i per respondre a les consultes i / o altres sol·licituds o preguntes.
    • +
    + +
    + +

    Com protegim la teva informació

    + +

    Implementem diverses mesures per mantenir la seguretat de la teva informació personal quan introdueixes, envies o accedeixes a la teva informació personal. Entre altres coses, la sessió del teu navegador així com el trànsit entre les teves aplicacions i l'API estan protegides amb SSL i la teva contrasenya es codifica utilitzant un algoritme de direcció única. Pots habilitar l'autenticació de dos factors per a garantir l'accés segur al teu compte.

    + +
    + +

    Quina és la nostra política de retenció de dades?

    + +

    Farem un esforç de bona fe per:

    + +
      +
    • Conservar els registres del servidor que continguin l'adreça IP de totes les sol·licituds a aquest servidor, tenint em compte que aquests registres es mantenen no més de 90 dies.
    • +
    • Conservar les adreces IP associades als usuaris registrats no més de 12 mesos.
    • +
    + +

    Pots sol·licitar i descarregar un arxiu del teu contingut incloses les publicacions, els fitxers adjunts multimèdia, la imatge de perfil i la imatge de capçalera.

    + +

    Pots eliminar el compte de forma irreversible en qualsevol moment.

    + +
    + +

    Utilitzem cookies?

    + +

    Sí. Les cookies són petits fitxers que un lloc o el proveïdor de serveis transfereix al disc dur del teu ordinador a través del navegador web (si ho permet). Aquestes galetes permeten al lloc reconèixer el teu navegador i, si teniu un compte registrat, associar-lo al teu compte registrat.

    + +

    Utilitzem cookies per entendre i guardar les teves preferències per a futures visites.

    + +
    + +

    Revelem informació a terceres parts?

    + +

    No venem, comercialitzem ni transmetem a tercers la teva informació d'identificació personal. Això no inclou tercers de confiança que ens ajuden a operar el nostre lloc, a dur a terme el nostre negoci o a servir-te, sempre que aquestes parts acceptin mantenir confidencial aquesta informació. També podem publicar la teva informació quan creiem que l'alliberament és apropiat per complir amb la llei, fer complir les polítiques del nostre lloc o protegir els nostres drets o altres drets, propietat o seguretat.

    + +

    Els altres servidors de la teva xarxa poden descarregar contingut públic. Els teus toots públics i per a només seguidors es lliuren als servidors on resideixen els teus seguidors i els missatges directes s'envien als servidors dels destinataris, sempre que aquests seguidors o destinataris resideixin en un servidor diferent d'això.

    + +

    Quan autoritzes una aplicació a utilitzar el teu compte, segons l'abast dels permisos que aprovis, pot accedir a la teva informació de perfil pública, a la teva llista de seguits, als teus seguidors, a les teves llistes, a totes les teves publicacions i als teus favorits. Les aplicacions mai no poden accedir a la teva adreça de correu electrònic o contrasenya.

    + +
    + +

    Compliment de la Llei de protecció de la privacitat en línia dels nens

    + +

    El nostre lloc, productes i serveis estan dirigits a persones que tenen almenys 13 anys. Si aquest servidor es troba als EUA, i teniu menys de 13 anys, segons els requisits de COPPA (Children's Online Privacy Protection Act) no utilitzis aquest lloc.

    + +
    + +

    Canvis a la nostra política de privacitat

    + +

    Si decidim canviar la nostra política de privadesa, publicarem aquests canvis en aquesta pàgina.

    + +

    Aquest document és CC-BY-SA. Actualitzat per darrera vegada el 7 de Març del 2018.

    + +

    Originalment adaptat des del Discourse privacy policy.

    title: "%{instance} Condicions del servei i política de privadesa" themes: default: Mastodont diff --git a/config/locales/devise.eu.yml b/config/locales/devise.eu.yml new file mode 100644 index 000000000..215b72e52 --- /dev/null +++ b/config/locales/devise.eu.yml @@ -0,0 +1,5 @@ +--- +eu: + devise: + failure: + already_authenticated: Saioa hasi duzu jada. diff --git a/config/locales/devise.it.yml b/config/locales/devise.it.yml index e1ba7bb22..0c5d8963c 100644 --- a/config/locales/devise.it.yml +++ b/config/locales/devise.it.yml @@ -17,11 +17,32 @@ it: unconfirmed: Devi confermare il tuo indirizzo email per continuare. mailer: confirmation_instructions: + action: Verifica indirizzo email + explanation: Hai creato un account su %{host} con questo indirizzo email. Sei lonatno solo un clic dall'attivarlo. Se non sei stato tu, per favore ignora questa email. + extra_html: Per favore controllale regole dell'istanza e i nostri termini di servizio. subject: 'Mastodon: Istruzioni di conferma per %{instance}' + title: Verifica indirizzo email + email_changed: + explanation: 'L''indirizzo email del tuo account sta per essere cambiato in:' + extra: Se non hai cambiato la tua email, è probabile che qualcuno abbia accesso al tuo account. Cambia immediatamente la tua password e contatta l'amministratore dell'istanza se sei bloccato fuori dal tuo account. + subject: 'Mastodon: Email cambiata' + title: Nuovo indirizzo email password_change: + explanation: La password del tuo account è stata cambiata. + extra: Se non hai cambiato la password, è probabile che qualcuno abbia accesso al tuo account. Cambia immediatamente la tua password e contatta l'amministratore dell'istanza se sei bloccato fuori dal tuo account. subject: 'Mastodon: Password modificata' + title: Password cambiata + reconfirmation_instructions: + explanation: Conferma il nuovo indirizzo per cambiare la tua email. + extra: Se questo cambiamento non è stato chiesto da te, ignora questa email. L'indirizzo email per l'account Mastodon non verrà cambiato finché non accedi dal link qui sopra. + subject: 'Mastodon: Email di conferma per %{instance}' + title: Verifica indirizzo email reset_password_instructions: + action: Cambia password + explanation: Hai richiesto una nuova password per il tuo account. + extra: Se questo cambiamento non è stato chiesto da te, ignora questa email. La tua password non verrà cambiata finché non accedi tramite il link qui sopra e ne crei una nuova. subject: 'Mastodon: Istruzioni per il reset della password' + title: Ripristino password unlock_instructions: subject: 'Mastodon: Istruzioni di sblocco' omniauth_callbacks: diff --git a/config/locales/doorkeeper.eu.yml b/config/locales/doorkeeper.eu.yml new file mode 100644 index 000000000..a51b1dc8b --- /dev/null +++ b/config/locales/doorkeeper.eu.yml @@ -0,0 +1,6 @@ +--- +eu: + activerecord: + attributes: + doorkeeper/application: + name: Aplikazioaren izena diff --git a/config/locales/doorkeeper.it.yml b/config/locales/doorkeeper.it.yml index e5a2d3f6e..50b2c9780 100644 --- a/config/locales/doorkeeper.it.yml +++ b/config/locales/doorkeeper.it.yml @@ -3,8 +3,10 @@ it: activerecord: attributes: doorkeeper/application: - name: Nome + name: Nome applicazione redirect_uri: URI di reindirizzamento + scopes: Scopi + website: Sito web applicazione errors: models: doorkeeper/application: @@ -33,9 +35,13 @@ it: redirect_uri: Usa una riga per URI scopes: Dividi gli scopes con spazi. Lascia vuoto per utilizzare gli scopes di default. index: + application: Applicazione callback_url: Callback URL + delete: Elimina name: Nome new: Nuova applicazione + scopes: Scopes + show: Mostra title: Le tue applicazioni new: title: Nuova applicazione @@ -43,7 +49,7 @@ it: actions: Azioni application_id: Id applicazione callback_urls: Callback urls - scopes: Scopes + scopes: Scopi secret: Secret title: 'Applicazione: %{name}' authorizations: @@ -57,7 +63,7 @@ it: prompt: L'applicazione %{client_name} richiede l'accesso al tuo account title: Autorizzazione richiesta show: - title: Copy this authorization code and paste it to the application. + title: Copia questo codice di autorizzazione e incollalo nell'applicazione. authorized_applications: buttons: revoke: Disabilita @@ -67,7 +73,7 @@ it: application: Applicazione created_at: Autorizzato date_format: "%d-%m-%Y %H:%M:%S" - scopes: Scopes + scopes: Scopi title: Applicazioni autorizzate errors: messages: @@ -104,7 +110,7 @@ it: admin: nav: applications: Applicazioni - oauth2_provider: OAuth2 Provider + oauth2_provider: Provider OAuth2 application: title: Autorizzazione OAuth richiesta scopes: diff --git a/config/locales/eu.yml b/config/locales/eu.yml new file mode 100644 index 000000000..0967ef424 --- /dev/null +++ b/config/locales/eu.yml @@ -0,0 +1 @@ +{} diff --git a/config/locales/fr.yml b/config/locales/fr.yml index ebe579354..fa96d9d05 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -4,6 +4,7 @@ fr: about_hashtag_html: Figurent ci-dessous les pouets tagués avec #%{hashtag}. Vous pouvez interagir avec eux si vous avez un compte n’importe où dans le Fediverse. about_mastodon_html: Mastodon est un réseau social utilisant des formats ouverts et des logiciels libres. Comme le courriel, il est décentralisé. about_this: À propos + administered_by: 'Administré par :' closed_registrations: Les inscriptions sont actuellement fermées sur cette instance. Cependant, vous pouvez trouver une autre instance sur laquelle vous créer un compte et à partir de laquelle vous pourrez accéder au même réseau. contact: Contact contact_missing: Manquant @@ -60,7 +61,15 @@ fr: destroyed_msg: Note de modération supprimée avec succès ! accounts: are_you_sure: Êtes-vous certain⋅e ? + avatar: Avatar by_domain: Domaine + change_email: + changed_msg: Courriel du compte modifié avec succès ! + current_email: Courriel actuel + label: Modifier le courriel + new_email: Nouveau courriel + submit: Modifier le courriel + title: Modifier le courriel pour %{username} confirm: Confirmer confirmed: Confirmé demote: Rétrograder @@ -108,6 +117,7 @@ fr: public: Publique push_subscription_expires: Expiration de l’abonnement PuSH redownload: Rafraîchir les avatars + remove_avatar: Supprimer l'avatar reset: Réinitialiser reset_password: Réinitialiser le mot de passe resubscribe: Se réabonner @@ -128,6 +138,7 @@ fr: statuses: Statuts subscribe: S’abonner title: Comptes + unconfirmed_email: Courriel non-confirmé undo_silenced: Démasquer undo_suspension: Annuler la suspension unsubscribe: Se désabonner @@ -135,6 +146,8 @@ fr: web: Web action_logs: actions: + assigned_to_self_report: "%{name} s'est assigné le signalement de %{target} à eux-même" + change_email_user: "%{name} a modifié l'adresse de courriel de l'utilisateur %{target}" confirm_user: "%{name} adresse courriel confirmée de l'utilisateur %{target}" create_custom_emoji: "%{name} a importé de nouveaux emoji %{target}" create_domain_block: "%{name} a bloqué le domaine %{target}" @@ -150,10 +163,13 @@ fr: enable_user: "%{name} a activé le login pour l'utilisateur %{target}" memorialize_account: "%{name} a transformé le compte de %{target} en une page de mémorial" promote_user: "%{name} a promu l'utilisateur %{target}" + remove_avatar_user: "%{name} a supprimé l'avatar de %{target}'s" + reopen_report: "%{name} a ré-ouvert le signalement %{target}" reset_password_user: "%{name} a réinitialisé le mot de passe de %{target}" - resolve_report: "%{name} n'a pas pris en compte la dénonciation de %{target}" + resolve_report: "%{name} a résolu la dénonciation de %{target}" silence_account: "%{name} a mis le compte %{target} en mode silence" suspend_account: "%{name} a suspendu le compte %{target}" + unassigned_report: "%{name} a dés-assigné le signalement %{target}" unsilence_account: "%{name} a mis fin au mode silence de %{target}" unsuspend_account: "%{name} a réactivé le compte de %{target}" update_custom_emoji: "%{name} a mis à jour l'emoji %{target}" @@ -239,28 +255,48 @@ fr: expired: Expiré title: Filtre title: Invitations + report_notes: + created_msg: Note de signalement créée avec succès ! + destroyed_msg: Note de signalement effacée avec succès ! reports: + account: + note: note + report: signaler action_taken_by: Intervention de are_you_sure: Êtes vous certain⋅e ? + assign_to_self: Me l'assigner + assigned: Modérateur assigné comment: none: Aucun + created_at: Signalé delete: Supprimer id: ID mark_as_resolved: Marquer comme résolu + mark_as_unresolved: Marquer comme non-résolu + notes: + create: Ajouter une note + create_and_resolve: Résoudre avec une note + create_and_unresolve: Ré-ouvrir avec une note + delete: Effacer + placeholder: Décrivez quelles actions ont été prises, ou toute autre mise à jour de ce signalement… nsfw: 'false': Ré-afficher les médias 'true': Masquer les médias + reopen: Ré-ouvrir le signalement report: 'Signalement #%{id}' report_contents: Contenu reported_account: Compte signalé reported_by: Signalé par resolved: Résolus + resolved_msg: Signalement résolu avec succès ! silence_account: Masquer le compte status: Statut suspend_account: Suspendre le compte target: Cible title: Signalements + unassign: Dés-assigner unresolved: Non résolus + updated_at: Mis à jour view: Voir settings: activity_api_enabled: @@ -381,6 +417,7 @@ fr: security: Sécurité set_new_password: Définir le nouveau mot de passe authorize_follow: + already_following: Vous suivez déjà ce compte error: Malheureusement, il y a eu une erreur en cherchant les détails du compte distant follow: Suivre follow_request: 'Vous avez demandé à suivre :' @@ -473,6 +510,7 @@ fr: '21600': 6 heures '3600': 1 heure '43200': 12 heures + '604800': 1 semaine '86400': 1 jour expires_in_prompt: Jamais generate: Générer @@ -576,6 +614,10 @@ fr: missing_resource: L’URL de redirection n’a pas pu être trouvée proceed: Continuez pour suivre prompt: 'Vous allez suivre :' + remote_unfollow: + error: Erreur + title: Titre + unfollowed: Non-suivi sessions: activity: Dernière activité browser: Navigateur @@ -641,6 +683,7 @@ fr: video: one: "%{count} vidéo" other: "%{count} vidéos" + content_warning: 'Attention au contenu : %{warning}' open_in_web: Ouvrir sur le web over_character_limit: limite de caractères dépassée de %{max} caractères pin_errors: diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 89de27a9a..3f936c0ea 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -4,6 +4,7 @@ gl: about_hashtag_html: Estas son mensaxes públicas etiquetadas con #%{hashtag}. Pode interactuar con elas si ten unha conta nalgures do fediverso. about_mastodon_html: Mastodon é unha rede social que se basea en protocolos web abertos e libres, software de código aberto. É descentralizada como o correo electrónico. about_this: Sobre + administered_by: 'Administrada por:' closed_registrations: O rexistro en esta instancia está pechado en este intre. Porén! Pode atopar unha instancia diferente para obter unha conta e ter acceso exactamente a misma rede desde alí. contact: Contacto contact_missing: Non establecido @@ -60,7 +61,15 @@ gl: destroyed_msg: Nota a moderación destruída con éxito! accounts: are_you_sure: Está segura? + avatar: Avatar by_domain: Dominio + change_email: + changed_msg: Cambiouse correctamente o correo-e da conta! + current_email: Correo-e actual + label: Cambiar correo-e + new_email: Novo correo-e + submit: Cambiar correo-e + title: Cambiar o correo-e de %{username} confirm: Confirmar confirmed: Confirmado demote: Degradar @@ -108,6 +117,7 @@ gl: public: Público push_subscription_expires: A suscrición PuSH caduca redownload: Actualizar avatar + remove_avatar: Eliminar avatar reset: Restablecer reset_password: Restablecer contrasinal resubscribe: Voltar a suscribir @@ -128,6 +138,7 @@ gl: statuses: Estados subscribe: Subscribir title: Contas + unconfirmed_email: Correo-e non confirmado undo_silenced: Desfacer acalar undo_suspension: Desfacer suspensión unsubscribe: Non subscribir @@ -135,6 +146,8 @@ gl: web: Web action_logs: actions: + assigned_to_self_report: "%{name} asignou o informe %{target} a ela misma" + change_email_user: "%{name} cambiou o enderezo de correo-e da usuaria %{target}" confirm_user: "%{name} comfirmou o enderezo de correo da usuaria %{target}" create_custom_emoji: "%{name} subeu un novo emoji %{target}" create_domain_block: "%{name} bloqueou o dominio %{target}" @@ -150,10 +163,13 @@ gl: enable_user: "%{name} habilitou a conexión para a usuaria %{target}" memorialize_account: "%{name} converteu a conta de %{target} nunha páxina para a lembranza" promote_user: "%{name} promoveu a usuaria %{target}" + remove_avatar_user: "%{name} eliminou o avatar de %{target}" + reopen_report: "%{name} voltou abrir informe %{target}" reset_password_user: "%{name} restableceu o contrasinal da usuaria %{target}" - resolve_report: "%{name} rexeitou o informe %{target}" + resolve_report: "%{name} solucionou o informe %{target}" silence_account: "%{name} acalou a conta de %{target}" suspend_account: "%{name} suspendeu a conta de %{target}" + unassigned_report: "%{name} non asignou informe %{target}" unsilence_account: "%{name} deulle voz a conta de %{target}" unsuspend_account: "%{name} activou a conta de %{target}" update_custom_emoji: "%{name} actualizou emoji %{target}" @@ -239,28 +255,48 @@ gl: expired: Cadudado title: Filtro title: Convida + report_notes: + created_msg: Creouse correctamente a nota do informe! + destroyed_msg: Nota do informe eliminouse con éxito! reports: + account: + note: nota + report: informe action_taken_by: Acción tomada por are_you_sure: Está segura? + assign_to_self: Asignarmo + assigned: Asignado ao Moderador comment: none: Nada + created_at: Reportado delete: Eliminar id: ID mark_as_resolved: Marcar como resolto + mark_as_unresolved: Marcar como non resolto + notes: + create: Engadir nota + create_and_resolve: Resolver con nota + create_and_unresolve: Votar a abrir con Nota + delete: Eliminar + placeholder: Describir qué decisións foron tomadas, ou calquer actualización a este informe… nsfw: 'false': Non agochar anexos de medios 'true': Agochar anexos de medios + reopen: Voltar a abrir o informe report: 'Informe #%{id}' report_contents: Contidos reported_account: Conta reportada reported_by: Reportada por resolved: Resolto + resolved_msg: Resolveuse con éxito o informe! silence_account: Acalar conta status: Estado suspend_account: Suspender conta target: Obxetivo title: Informes + unassign: Non asignar unresolved: Non resolto + updated_at: Actualizado view: Vista settings: activity_api_enabled: @@ -381,6 +417,7 @@ gl: security: Seguridade set_new_password: Establecer novo contrasinal authorize_follow: + already_following: Xa está a seguir esta conta error: Desgraciadamente, algo fallou ao buscar a conta remota follow: Seguir follow_request: 'Enviou unha petición de seguimento a:' @@ -473,6 +510,7 @@ gl: '21600': 6 horas '3600': 1 hora '43200': 12 horas + '604800': 1 semana '86400': 1 día expires_in_prompt: Nunca generate: Xerar @@ -576,6 +614,10 @@ gl: missing_resource: Non se puido atopar o URL de redirecionamento requerido para a súa conta proceed: Proceda para seguir prompt: 'Vostede vai seguir:' + remote_unfollow: + error: Fallo + title: Título + unfollowed: Deixou de seguir sessions: activity: Última actividade browser: Navegador diff --git a/config/locales/it.yml b/config/locales/it.yml index 7e5bfd20e..78bf8ba2b 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -3,43 +3,283 @@ it: about: about_mastodon_html: Mastodon è un social network gratuito e open-source. Un'alternativa decentralizzata alle piattaforme commerciali che evita che una singola compagnia monopolizzi il tuo modo di comunicare. Scegli un server di cui ti fidi — qualunque sia la tua scelta, potrai interagire con chiunque altro. Chiunque può sviluppare un suo server Mastodon e partecipare alla vita del social network. about_this: A proposito di questo server - closed_registrations: Al momento le iscrizioni a questo server sono chiuse. + administered_by: 'Amministrato da:' + closed_registrations: Al momento le iscrizioni a questo server sono chiuse. Tuttavia! Puoi provare a cercare un istanza diversa su cui creare un account ed avere accesso alla stessa identica rete di questa. contact: Contatti + contact_missing: Non impostato + contact_unavailable: N/D description_headline: Cos'è %{domain}? domain_count_after: altri server domain_count_before: Connesso a - other_instances: Altri server + features: + humane_approach_title: Un approccio più umano + not_a_product_body: Mastodon non è una rete commerciale. Niente pubblicità, niente data mining, nessun giardino murato. Non c'è nessuna autorità centrale. + not_a_product_title: Tu sei una persona, non un prodotto + real_conversation_title: Creato per conversazioni reali + generic_description: "%{domain} è un server nella rete" + learn_more: Scopri altro + other_instances: Elenco istanze source_code: Codice sorgente - status_count_after: status + status_count_after: stati status_count_before: Che hanno pubblicato user_count_after: utenti - user_count_before: Casa di + user_count_before: Home di + what_is_mastodon: Che cos'è Mastodon? accounts: follow: Segui followers: Seguaci following: Seguiti + media: Media nothing_here: Qui non c'è nulla! people_followed_by: Persone seguite da %{name} people_who_follow: Persone che seguono %{name} posts: Posts + posts_with_replies: Toot e repliche remote_follow: Segui da remoto + reserved_username: Il nome utente è riservato + roles: + admin: Amministratore + moderator: Mod unfollow: Non seguire più + admin: + account_moderation_notes: + account: Moderatore + create: Crea + created_at: Data + created_msg: Nota di moderazione creata con successo! + delete: Elimina + destroyed_msg: Nota di moderazione distrutta con successo! + accounts: + are_you_sure: Sei sicuro? + avatar: Avatar + by_domain: Dominio + change_email: + changed_msg: Account email cambiato con successo! + current_email: Email corrente + label: Cambia email + new_email: Nuova email + submit: Cambia email + title: Cambia email per %{username} + confirm: Conferma + confirmed: Confermato + demote: Declassa + disable: Disabilita + disable_two_factor_authentication: Disabilita 2FA + disabled: Disabilitato + display_name: Nome visualizzato + domain: Dominio + edit: Modifica + email: Email + enable: Abilita + enabled: Abilitato + feed_url: URL Feed + followers: Follower + followers_url: URL follower + follows: Follows + inbox_url: URL inbox + ip: IP + location: + all: Tutto + local: Locale + remote: Remoto + title: Luogo + login_status: Stato login + media_attachments: Media allegati + memorialize: Trasforma in memoriam + moderation: + all: Tutto + silenced: Silenziati + suspended: Sospesi + title: Moderazione + moderation_notes: Note di moderazione + most_recent_activity: Attività più recenti + most_recent_ip: IP più recenti + not_subscribed: Non sottoscritto + order: + alphabetic: Alfabetico + most_recent: Più recente + title: Ordine + outbox_url: URL outbox + perform_full_suspension: Esegui sospensione completa + profile_url: URL profilo + promote: Promuovi + protocol: Protocollo + public: Pubblico + redownload: Aggiorna avatar + remove_avatar: Rimuovi avatar + reset: Reimposta + reset_password: Reimposta password + role: Permessi + roles: + admin: Amministratore + moderator: Moderatore + staff: Staff + user: Utente + search: Cerca + silence: Silenzia + statuses: Stati + subscribe: Sottoscrivi + title: Account + unconfirmed_email: Email non confermata + undo_silenced: Rimuovi silenzia + undo_suspension: Rimuovi sospensione + username: Nome utente + web: Web + action_logs: + actions: + confirm_user: "%{name} ha confermato l'indirizzo email per l'utente %{target}" + create_custom_emoji: "%{name} ha caricato un nuovo emoji %{target}" + create_domain_block: "%{name} ha bloccato il dominio %{target}" + custom_emojis: + by_domain: Dominio + copied_msg: Creata con successo una copia locale dell'emoji + copy: Copia + copy_failed_msg: Impossibile creare una copia locale di questo emoji + created_msg: Emoji creato con successo! + delete: Elimina + destroyed_msg: Emoji distrutto con successo! + disable: Disabilita + disabled_msg: Questa emoji è stata disabilitata con successo + emoji: Emoji + enable: Abilita + enabled_msg: Questa emoji è stata abilitata con successo + image_hint: PNG fino a 50KB + listed: Elencato + new: + title: Aggiungi nuovo emoji personalizzato + overwrite: Sovrascrivi + shortcode: Shortcode + title: Emoji personalizzate + unlisted: Non elencato + update_failed_msg: Impossibile aggiornare questa emojii + updated_msg: Emoji aggiornata con successo! + upload: Carica + domain_blocks: + add_new: Aggiungi nuovo + created_msg: Il blocco del dominio sta venendo processato + destroyed_msg: Il blocco del dominio è stato rimosso + domain: Dominio + new: + create: Crea blocco + severity: + noop: Nessuno + silence: Silenzia + suspend: Sospendi + title: Nuovo blocco dominio + severities: + noop: Nessuno + silence: Silenzia + suspend: Sospendi + severity: Severità + title: Blocchi dominio + email_domain_blocks: + add_new: Aggiungi nuovo + delete: Elimina + domain: Dominio + new: + create: Aggiungi dominio + instances: + domain_name: Dominio + reset: Reimposta + search: Cerca + title: Istanze conosciute + invites: + filter: + all: Tutto + available: Disponibile + expired: Scaduto + title: Filtro + title: Inviti + reports: + account: + note: note + are_you_sure: Sei sicuro? + assign_to_self: Assegna a me + comment: + none: Nessuno + delete: Elimina + id: ID + mark_as_resolved: Segna come risolto + mark_as_unresolved: Segna come non risolto + notes: + create: Aggiungi nota + create_and_resolve: Risolvi con nota + create_and_unresolve: Riapri con nota + delete: Elimina + report_contents: Contenuti + resolved: Risolto + status: Stati + target: Obbiettivo + unassign: Non assegnare + unresolved: Non risolto + updated_at: Aggiornato + view: Mostra + settings: + activity_api_enabled: + title: Pubblica statistiche aggregate circa l'attività dell'utente + peers_api_enabled: + title: Pubblica elenco di istanze scoperte + registrations: + min_invite_role: + disabled: Nessuno + show_staff_badge: + title: Mostra badge staff + site_description: + title: Descrizione istanza + site_terms: + title: Termini di servizio personalizzati + site_title: Nome istanza + timeline_preview: + title: Anteprima timeline + title: Impostazioni sito + statuses: + batch: + delete: Elimina + nsfw_off: NSFW OFF + nsfw_on: NSFW ON + execute: Esegui + failed_to_execute: Impossibile eseguire + media: + hide: Nascondi media + show: Mostra media + title: Media + no_media: Nessun media + with_media: con media + subscriptions: + callback_url: URL Callback + confirmed: Confermato + expires_in: Scade in + topic: Argomento + title: Amministrazione application_mailer: + notification_preferences: Cambia preferenze email + salutation: "%{name}," settings: 'Cambia le impostazioni per le e-mail: %{link}' view: 'Guarda:' + view_profile: Mostra profilo + view_status: Mostra stati applications: + created: Applicazione creata con successo + destroyed: Applicazione eliminata con successo invalid_url: L'URL fornito non è valido auth: + change_password: Password + confirm_email: Conferma email + delete_account: Elimina account didnt_get_confirmation: Non hai ricevuto le istruzioni di conferma? forgot_password: Hai dimenticato la tua password? login: Entra logout: Logout + migrate_account: Sposta ad un account differente + or: o register: Iscriviti + register_elsewhere: Iscriviti su un altro server resend_confirmation: Invia di nuovo le istruzioni di conferma reset_password: Resetta la password security: Credenziali set_new_password: Imposta una nuova password authorize_follow: + already_following: Stai già seguendo questo account error: Sfortunatamente c'è stato un errore nel consultare l'account remoto follow: Segui title: Segui %{acct} @@ -161,6 +401,9 @@ it: manual_instructions: 'Se non puoi scannerizzare il QR code e hai bisogno di inserirlo manualmente, questo è il codice segreto in chiaro:' setup: Configura wrong_code: Il codice inserito non è corretto! Assicurati che l'orario del server e l'orario del telefono siano corretti. + user_mailer: + welcome: + title: Benvenuto a bordo, %{name}! users: invalid_email: L'indirizzo e-mail inserito non è valido invalid_otp_token: Codice d'accesso non valido diff --git a/config/locales/ja.yml b/config/locales/ja.yml index b23c02754..aaebec654 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -68,7 +68,7 @@ ja: current_email: 現在のメールアドレス label: メールアドレスを変更 new_email: 新しいメールアドレス - submit: Change Email + submit: メールアドレスの変更 title: "%{username} さんのメールアドレスを変更" confirm: 確認 confirmed: 確認済み @@ -259,6 +259,9 @@ ja: created_msg: レポートメモを書き込みました! destroyed_msg: レポートメモを削除しました! reports: + account: + note: メモ + report: レポート action_taken_by: レポート処理者 are_you_sure: 本当に実行しますか? assign_to_self: 担当になる diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 3470085cf..0f3c6483f 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -4,6 +4,7 @@ ko: about_hashtag_html: "#%{hashtag} 라는 해시태그가 붙은 공개 툿 입니다. 같은 연합에 속한 임의의 인스턴스에 계정을 생성하면 당신도 대화에 참여할 수 있습니다." about_mastodon_html: Mastodon은 오픈 소스 기반의 소셜 네트워크 서비스 입니다. 상용 플랫폼의 대체로서 분산형 구조를 채택해, 여러분의 대화가 한 회사에 독점되는 것을 방지합니다. 신뢰할 수 있는 인스턴스를 선택하세요 — 어떤 인스턴스를 고르더라도, 누구와도 대화할 수 있습니다. 누구나 자신만의 Mastodon 인스턴스를 만들 수 있으며, 아주 매끄럽게 소셜 네트워크에 참가할 수 있습니다. about_this: 이 인스턴스에 대해서 + administered_by: '관리자:' closed_registrations: 현재 이 인스턴스에서는 신규 등록을 받고 있지 않습니다. contact: 연락처 contact_missing: 미설정 @@ -60,7 +61,15 @@ ko: destroyed_msg: 모더레이션 기록이 성공적으로 삭제되었습니다! accounts: are_you_sure: 정말로 실행하시겠습니까? + avatar: 아바타 by_domain: 도메인 + change_email: + changed_msg: 이메일이 성공적으로 바뀌었습니다! + current_email: 현재 이메일 주소 + label: 이메일 주소 변경 + new_email: 새 이메일 주소 + submit: 이메일 주소 변경 + title: "%{username}의 이메일 주소 변경" confirm: 확인 confirmed: 확인됨 demote: 모더레이터 강등 @@ -108,6 +117,7 @@ ko: public: 전체 공개 push_subscription_expires: PuSH 구독 기간 만료 redownload: 아바타 업데이트 + remove_avatar: 아바타 지우기 reset: 초기화 reset_password: 비밀번호 초기화 resubscribe: 다시 구독 @@ -128,6 +138,7 @@ ko: statuses: 툿 수 subscribe: 구독하기 title: 계정 + unconfirmed_email: 미확인 된 이메일 주소 undo_silenced: 침묵 해제 undo_suspension: 정지 해제 unsubscribe: 구독 해제 @@ -135,6 +146,8 @@ ko: web: 웹 action_logs: actions: + assigned_to_self_report: "%{name}이 리포트 %{target}을 자신에게 할당했습니다" + change_email_user: "%{name}이 %{target}의 이메일 주소를 변경했습니다" confirm_user: "%{name}이 %{target}의 이메일 주소를 컨펌했습니다" create_custom_emoji: "%{name}이 새로운 에모지 %{target}를 추가했습니다" create_domain_block: "%{name}이 도메인 %{target}를 차단했습니다" @@ -150,10 +163,13 @@ ko: enable_user: "%{name}이 %{target}의 로그인을 활성화 했습니다" memorialize_account: "%{name}이 %{target}의 계정을 메모리엄으로 전환했습니다" promote_user: "%{name}이 %{target}를 승급시켰습니다" + remove_avatar_user: "%{name}이 %{target}의 아바타를 지웠습니다" + reopen_report: "%{name}이 리포트 %{target}을 다시 열었습니다" reset_password_user: "%{name}이 %{target}의 암호를 초기화했습니다" resolve_report: "%{name}이 %{target} 신고를 처리됨으로 변경하였습니다" silence_account: "%{name}이 %{target}의 계정을 뮤트시켰습니다" suspend_account: "%{name}이 %{target}의 계정을 정지시켰습니다" + unassigned_report: "%{name}이 리포트 %{target}을 할당 해제했습니다" unsilence_account: "%{name}이 %{target}에 대한 뮤트를 해제했습니다" unsuspend_account: "%{name}이 %{target}에 대한 정지를 해제했습니다" update_custom_emoji: "%{name}이 에모지 %{target}를 업데이트 했습니다" @@ -241,28 +257,48 @@ ko: expired: 만료됨 title: 필터 title: 초대 + report_notes: + created_msg: 리포트 노트가 성공적으로 작성되었습니다! + destroyed_msg: 리포트 노트가 성공적으로 삭제되었습니다! reports: + account: + note: 노트 + report: 리포트 action_taken_by: 신고 처리자 are_you_sure: 정말로 실행하시겠습니까? + assign_to_self: 나에게 할당 됨 + assigned: 할당 된 모더레이터 comment: none: 없음 + created_at: 리포트 시각 delete: 삭제 id: ID mark_as_resolved: 해결 완료 처리 + mark_as_unresolved: 미해결로 표시 + notes: + create: 노트 추가 + create_and_resolve: 노트를 작성하고 해결됨으로 표시 + create_and_unresolve: 노트 작성과 함께 미해결로 표시 + delete: 삭제 + placeholder: 이 리포트에 대한 조치, 다른 업데이트 사항에 대해 설명합니다… nsfw: 'false': NSFW 꺼짐 'true': NSFW 켜짐 + reopen: 리포트 다시 열기 report: '신고 #%{id}' report_contents: 내용 reported_account: 신고 대상 계정 reported_by: 신고자 resolved: 해결됨 + resolved_msg: 리포트가 성공적으로 해결되었습니다! silence_account: 계정을 침묵 처리 status: 상태 suspend_account: 계정을 정지 target: 대상 title: 신고 + unassign: 할당 해제 unresolved: 미해결 + updated_at: 업데이트 시각 view: 표시 settings: activity_api_enabled: @@ -383,6 +419,7 @@ ko: security: 보안 set_new_password: 새 비밀번호 authorize_follow: + already_following: 이미 이 계정을 팔로우 하고 있습니다 error: 리모트 계정을 확인하는 도중 오류가 발생했습니다 follow: 팔로우 follow_request: '당신은 다음 계정에 팔로우 신청을 했습니다:' @@ -475,6 +512,7 @@ ko: '21600': 6 시간 '3600': 1 시간 '43200': 12 시간 + '604800': 1주일 '86400': 하루 expires_in_prompt: 영원히 generate: 생성 @@ -547,7 +585,7 @@ ko: quadrillion: Q thousand: K trillion: T - unit: '' + unit: "." pagination: newer: 새로운 툿 next: 다음 @@ -578,6 +616,10 @@ ko: missing_resource: 리디렉션 대상을 찾을 수 없습니다 proceed: 팔로우 하기 prompt: '팔로우 하려 하고 있습니다:' + remote_unfollow: + error: 에러 + title: 타이틀 + unfollowed: 언팔로우됨 sessions: activity: 마지막 활동 browser: 브라우저 diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 2342ef6a2..64bc71874 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -4,6 +4,7 @@ nl: about_hashtag_html: Dit zijn openbare toots die getagged zijn met #%{hashtag}. Je kunt er op reageren of iets anders mee doen als je op Mastodon (of ergens anders in de fediverse) een account hebt. about_mastodon_html: Mastodon is een sociaal netwerk dat gebruikt maakt van open webprotocollen en vrije software. Het is net zoals e-mail gedecentraliseerd. about_this: Over deze server + administered_by: 'Beheerd door:' closed_registrations: Registreren op deze server is momenteel uitgeschakeld. contact: Contact contact_missing: Niet ingesteld @@ -60,7 +61,15 @@ nl: destroyed_msg: Verwijderen van opmerking voor moderatoren geslaagd! accounts: are_you_sure: Weet je het zeker? + avatar: Avatar by_domain: Domein + change_email: + changed_msg: E-mailadres van account succesvol veranderd! + current_email: Huidig e-mailadres + label: E-mailadres veranderen + new_email: Nieuw e-mailadres + submit: E-mailadres veranderen + title: E-mailadres veranderen voor %{username} confirm: Bevestigen confirmed: Bevestigd demote: Degraderen @@ -108,6 +117,7 @@ nl: public: Openbaar push_subscription_expires: PuSH-abonnement verloopt op redownload: Avatar vernieuwen + remove_avatar: Avatar verwijderen reset: Opnieuw reset_password: Wachtwoord opnieuw instellen resubscribe: Opnieuw abonneren @@ -128,6 +138,7 @@ nl: statuses: Toots subscribe: Abonneren title: Accounts + unconfirmed_email: Onbevestigd e-mailadres undo_silenced: Niet meer negeren undo_suspension: Niet meer opschorten unsubscribe: Opzeggen @@ -135,6 +146,8 @@ nl: web: Webapp action_logs: actions: + assigned_to_self_report: "%{name} heeft gerapporteerde toot %{target} aan zichzelf toegewezen" + change_email_user: "%{name} veranderde het e-mailadres van gebruiker %{target}" confirm_user: E-mailadres van gebruiker %{target} is door %{name} bevestigd create_custom_emoji: Nieuwe emoji %{target} is door %{name} geüpload create_domain_block: Domein %{target} is door %{name} geblokkeerd @@ -150,10 +163,13 @@ nl: enable_user: Inloggen voor %{target} is door %{name} ingeschakeld memorialize_account: Account %{target} is door %{name} in een in-memoriampagina veranderd promote_user: Gebruiker %{target} is door %{name} gepromoveerd + remove_avatar_user: "%{name} verwijderde de avatar van %{target}" + reopen_report: "%{name} heeft gerapporteerde toot %{target} heropend" reset_password_user: Wachtwoord van gebruiker %{target} is door %{name} opnieuw ingesteld - resolve_report: Gerapporteerde toots van %{target} zijn door %{name} verworpen + resolve_report: "%{name} heeft gerapporteerde toot %{target} opgelost" silence_account: Account %{target} is door %{name} genegeerd suspend_account: Account %{target} is door %{name} opgeschort + unassigned_report: "%{name} heeft het toewijzen van gerapporteerde toot %{target} ongedaan gemaakt" unsilence_account: Negeren van account %{target} is door %{name} opgeheven unsuspend_account: Opschorten van account %{target} is door %{name} opgeheven update_custom_emoji: Emoji %{target} is door %{name} bijgewerkt @@ -239,28 +255,48 @@ nl: expired: Verlopen title: Filter title: Uitnodigingen + report_notes: + created_msg: Opmerking bij gerapporteerde toot succesvol aangemaakt! + destroyed_msg: Opmerking bij gerapporteerde toot succesvol verwijderd! reports: + account: + note: opmerking + report: gerapporteerde toot action_taken_by: Actie uitgevoerd door are_you_sure: Weet je het zeker? + assign_to_self: Aan mij toewijzen + assigned: Toegewezen moderator comment: none: Geen + created_at: Gerapporteerd op delete: Verwijderen id: ID mark_as_resolved: Markeer als opgelost + mark_as_unresolved: Markeer als onopgelost + notes: + create: Opmerking toevoegen + create_and_resolve: Oplossen met opmerking + create_and_unresolve: Heropenen met opmerking + delete: Verwijderen + placeholder: Beschrijf welke acties zijn ondernomen of andere opmerkingen over deze gerapporteerde toot… nsfw: 'false': Media tonen 'true': Media verbergen + reopen: Gerapporteerde toot heropenen report: 'Gerapporteerde toot #%{id}' report_contents: Inhoud reported_account: Gerapporteerde account reported_by: Gerapporteerd door resolved: Opgelost + resolved_msg: Gerapporteerde toot succesvol opgelost! silence_account: Account negeren status: Toot suspend_account: Account opschorten target: Gerapporteerde account title: Gerapporteerde toots + unassign: Niet meer toewijzen unresolved: Onopgelost + updated_at: Bijgewerkt view: Weergeven settings: activity_api_enabled: @@ -381,6 +417,7 @@ nl: security: Beveiliging set_new_password: Nieuw wachtwoord instellen authorize_follow: + already_following: Je volgt dit account al error: Helaas, er is een fout opgetreden bij het opzoeken van de externe account follow: Volgen follow_request: 'Jij hebt een volgverzoek ingediend bij:' @@ -473,6 +510,7 @@ nl: '21600': 6 uur '3600': 1 uur '43200': 12 uur + '604800': 1 week '86400': 1 dag expires_in_prompt: Nooit generate: Genereren @@ -576,6 +614,10 @@ nl: missing_resource: Kon vereiste doorverwijzings-URL voor jouw account niet vinden proceed: Ga door om te volgen prompt: 'Jij gaat volgen:' + remote_unfollow: + error: Fout + title: Titel + unfollowed: Ontvolgd sessions: activity: Laatst actief browser: Webbrowser @@ -664,6 +706,83 @@ nl: reblogged: boostte sensitive_content: Gevoelige inhoud terms: + body_html: | +

    Privacy Policy

    +

    What information do we collect?

    + +
      +
    • Basic account information: If you register on this server, you may be asked to enter a username, an e-mail address and a password. You may also enter additional profile information such as a display name and biography, and upload a profile picture and header image. The username, display name, biography, profile picture and header image are always listed publicly.
    • +
    • Posts, following and other public information: The list of people you follow is listed publicly, the same is true for your followers. When you submit a message, the date and time is stored as well as the application you submitted the message from. Messages may contain media attachments, such as pictures and videos. Public and unlisted posts are available publicly. When you feature a post on your profile, that is also publicly available information. Your posts are delivered to your followers, in some cases it means they are delivered to different servers and copies are stored there. When you delete posts, this is likewise delivered to your followers. The action of reblogging or favouriting another post is always public.
    • +
    • Direct and followers-only posts: All posts are stored and processed on the server. Followers-only posts are delivered to your followers and users who are mentioned in them, and direct posts are delivered only to users mentioned in them. In some cases it means they are delivered to different servers and copies are stored there. We make a good faith effort to limit the access to those posts only to authorized persons, but other servers may fail to do so. Therefore it's important to review servers your followers belong to. You may toggle an option to approve and reject new followers manually in the settings. Please keep in mind that the operators of the server and any receiving server may view such messages, and that recipients may screenshot, copy or otherwise re-share them. Do not share any dangerous information over Mastodon.
    • +
    • IPs and other metadata: When you log in, we record the IP address you log in from, as well as the name of your browser application. All the logged in sessions are available for your review and revocation in the settings. The latest IP address used is stored for up to 12 months. We also may retain server logs which include the IP address of every request to our server.
    • +
    + +
    + +

    What do we use your information for?

    + +

    Any of the information we collect from you may be used in the following ways:

    + +
      +
    • To provide the core functionality of Mastodon. You can only interact with other people's content and post your own content when you are logged in. For example, you may follow other people to view their combined posts in your own personalized home timeline.
    • +
    • To aid moderation of the community, for example comparing your IP address with other known ones to determine ban evasion or other violations.
    • +
    • The email address you provide may be used to send you information, notifications about other people interacting with your content or sending you messages, and to respond to inquiries, and/or other requests or questions.
    • +
    + +
    + +

    How do we protect your information?

    + +

    We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information. Among other things, your browser session, as well as the traffic between your applications and the API, are secured with SSL, and your password is hashed using a strong one-way algorithm. You may enable two-factor authentication to further secure access to your account.

    + +
    + +

    What is our data retention policy?

    + +

    We will make a good faith effort to:

    + +
      +
    • Retain server logs containing the IP address of all requests to this server, in so far as such logs are kept, no more than 90 days.
    • +
    • Retain the IP addresses associated with registered users no more than 12 months.
    • +
    + +

    You can request and download an archive of your content, including your posts, media attachments, profile picture, and header image.

    + +

    You may irreversibly delete your account at any time.

    + +
    + +

    Do we use cookies?

    + +

    Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account.

    + +

    We use cookies to understand and save your preferences for future visits.

    + +
    + +

    Do we disclose any information to outside parties?

    + +

    We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety.

    + +

    Your public content may be downloaded by other servers in the network. Your public and followers-only posts are delivered to the servers where your followers reside, and direct messages are delivered to the servers of the recipients, in so far as those followers or recipients reside on a different server than this.

    + +

    When you authorize an application to use your account, depending on the scope of permissions you approve, it may access your public profile information, your following list, your followers, your lists, all your posts, and your favourites. Applications can never access your e-mail address or password.

    + +
    + +

    Children's Online Privacy Protection Act Compliance

    + +

    Our site, products and services are all directed to people who are at least 13 years old. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA (Children's Online Privacy Protection Act) do not use this site.

    + +
    + +

    Changes to our Privacy Policy

    + +

    If we decide to change our privacy policy, we will post those changes on this page.

    + +

    This document is CC-BY-SA. It was last updated March 7, 2018.

    + +

    Originally adapted from the Discourse privacy policy.

    title: "%{instance} Terms of Service and Privacy Policy" themes: default: Mastodon diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index d3c1d532b..aeafe77bd 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -4,6 +4,7 @@ pt-BR: about_hashtag_html: Estes são toots públicos com a hashtag #%{hashtag}. Você pode interagir com eles se tiver uma conta em qualquer lugar no fediverso. about_mastodon_html: Mastodon é uma rede social baseada em protocolos abertos e software gratuito e de código aberto. É descentralizada como e-mail. about_this: Sobre + administered_by: 'Administrado por:' closed_registrations: Os cadastros estão atualmente fechados nesta instância. No entanto, você pode procurar uma instância diferente na qual possa criar uma conta e acessar a mesma rede por lá. contact: Contato contact_missing: Não definido @@ -60,7 +61,15 @@ pt-BR: destroyed_msg: Nota de moderação excluída com sucesso! accounts: are_you_sure: Você tem certeza? + avatar: Avatar by_domain: Domínio + change_email: + changed_msg: E-mail da conta modificado com sucesso! + current_email: E-mail atual + label: Mudar e-mail + new_email: Novo e-mail + submit: Mudar e-mail + title: Mudar e-mail para %{username} confirm: Confirmar confirmed: Confirmado demote: Rebaixar @@ -108,6 +117,7 @@ pt-BR: public: Público push_subscription_expires: Inscrição PuSH expira redownload: Atualizar avatar + remove_avatar: Remover avatar reset: Anular reset_password: Modificar senha resubscribe: Reinscrever-se @@ -128,6 +138,7 @@ pt-BR: statuses: Postagens subscribe: Inscrever-se title: Contas + unconfirmed_email: E-mail não confirmado undo_silenced: Retirar silenciamento undo_suspension: Retirar suspensão unsubscribe: Desinscrever-se @@ -135,6 +146,8 @@ pt-BR: web: Web action_logs: actions: + assigned_to_self_report: "%{name} designou a denúncia %{target} para si" + change_email_user: "%{name} mudou o endereço de e-mail do usuário %{target}" confirm_user: "%{name} confirmou o endereço de e-mail do usuário %{target}" create_custom_emoji: "%{name} enviou o emoji novo %{target}" create_domain_block: "%{name} bloqueou o domínio %{target}" @@ -150,10 +163,13 @@ pt-BR: enable_user: "%{name} habilitou o acesso para o usuário %{target}" memorialize_account: "%{name} transformou a conta de %{target} em um memorial" promote_user: "%{name} promoveu o usuário %{target}" + remove_avatar_user: "%{name} removeu o avatar de %{target}" + reopen_report: "%{name} reabriu a denúncia %{target}" reset_password_user: "%{name} redefiniu a senha do usuário %{target}" - resolve_report: "%{name} dispensou a denúncia %{target}" + resolve_report: "%{name} resolveu a denúncia %{target}" silence_account: "%{name} silenciou a conta de %{target}" suspend_account: "%{name} suspendeu a conta de %{target}" + unassigned_report: "%{name} desatribuiu a denúncia %{target}" unsilence_account: "%{name} desativou o silêncio de %{target}" unsuspend_account: "%{name} desativou a suspensão de %{target}" update_custom_emoji: "%{name} atualizou o emoji %{target}" @@ -239,28 +255,48 @@ pt-BR: expired: Expirados title: Filtro title: Convites + report_notes: + created_msg: Nota de denúncia criada com sucesso! + destroyed_msg: Nota de denúncia excluída com sucesso! reports: + account: + note: nota + report: denúncia action_taken_by: Ação realizada por are_you_sure: Você tem certeza? + assign_to_self: Designar para mim + assigned: Moderador designado comment: none: Nenhum + created_at: Denunciado delete: Excluir id: ID mark_as_resolved: Marcar como resolvido + mark_as_unresolved: Marcar como não resolvido + notes: + create: Adicionar nota + create_and_resolve: Resolver com nota + create_and_unresolve: Reabrir com nota + delete: Excluir + placeholder: Descreva que ações foram tomadas, ou quaisquer atualizações sobre esta denúncia… nsfw: 'false': Mostrar mídias anexadas 'true': Esconder mídias anexadas + reopen: Reabrir denúncia report: 'Denúncia #%{id}' report_contents: Conteúdos reported_account: Conta denunciada reported_by: Denunciada por resolved: Resolvido + resolved_msg: Denúncia resolvida com sucesso! silence_account: Silenciar conta status: Status suspend_account: Suspender conta target: Alvo title: Denúncias + unassign: Desatribuir unresolved: Não resolvido + updated_at: Atualizado view: Visualizar settings: activity_api_enabled: @@ -381,6 +417,7 @@ pt-BR: security: Segurança set_new_password: Definir uma nova senha authorize_follow: + already_following: Você já está seguindo esta conta error: Infelizmente, ocorreu um erro ao buscar a conta remota follow: Seguir follow_request: 'Você mandou uma solicitação de seguidor para:' @@ -473,6 +510,7 @@ pt-BR: '21600': 6 horas '3600': 1 hora '43200': 12 horas + '604800': 1 semana '86400': 1 dia expires_in_prompt: Nunca generate: Gerar @@ -576,6 +614,9 @@ pt-BR: missing_resource: Não foi possível encontrar a URL de direcionamento para a sua conta proceed: Prosseguir para seguir prompt: 'Você irá seguir:' + remote_unfollow: + error: Erro + title: Título sessions: activity: Última atividade browser: Navegador diff --git a/config/locales/simple_form.ar.yml b/config/locales/simple_form.ar.yml index 414f0c342..28cfa8ab7 100644 --- a/config/locales/simple_form.ar.yml +++ b/config/locales/simple_form.ar.yml @@ -5,8 +5,15 @@ ar: defaults: avatar: ملف PNG أو GIF أو JPG. حجمه على أقصى تصدير 2MB. سيتم تصغيره إلى 400x400px digest: تُرسَل إليك بعد مُضيّ مدة مِن خمول نشاطك و فقط إذا ما تلقيت رسائل شخصية مباشِرة أثناء فترة غيابك مِن الشبكة + display_name: + one: 1 حرف باقي + other: %{count} حروف متبقية + fields: يُمكنك عرض 4 عناصر على شكل جدول في ملفك الشخصي header: ملف PNG أو GIF أو JPG. حجمه على أقصى تصدير 2MB. سيتم تصغيره إلى 700x335px locked: يتطلب منك الموافقة يدويا على طلبات المتابعة + note: + one: 1 حرف متبقي + other: %{count} حروف متبقية setting_noindex: ذلك يؤثر على حالة ملفك الشخصي و صفحاتك setting_theme: ذلك يؤثر على الشكل الذي سيبدو عليه ماستدون عندما تقوم بالدخول مِن أي جهاز. imports: @@ -16,6 +23,10 @@ ar: user: filtered_languages: سوف يتم تصفية و إخفاء اللغات المختارة من خيوطك العمومية labels: + account: + fields: + name: التسمية + value: المحتوى defaults: avatar: الصورة الرمزية confirm_new_password: تأكيد كلمة السر الجديدة @@ -25,6 +36,7 @@ ar: display_name: الإسم المعروض email: عنوان البريد الإلكتروني expires_in: تنتهي مدة صلاحيته بعد + fields: واصفات بيانات الملف الشخصي filtered_languages: اللغات التي تم تصفيتها header: الرأسية locale: اللغة diff --git a/config/locales/simple_form.ca.yml b/config/locales/simple_form.ca.yml index 300da45a5..1b04da90a 100644 --- a/config/locales/simple_form.ca.yml +++ b/config/locales/simple_form.ca.yml @@ -8,6 +8,7 @@ ca: display_name: one: 1 càracter restant other: %{count} càracters restans + fields: Pots tenir fins a 4 elements que es mostren com a taula al teu perfil header: PNG, GIF o JPG. Màxim 2MB. S'escalarà a 700x335px locked: Requereix que aprovis manualment els seguidors note: @@ -22,6 +23,10 @@ ca: user: filtered_languages: Les llengües seleccionades s'eliminaran de les línies de temps públiques labels: + account: + fields: + name: Etiqueta + value: Contingut defaults: avatar: Avatar confirm_new_password: Confirma la contrasenya nova @@ -31,6 +36,7 @@ ca: display_name: Nom visible email: Adreça de correu electrònic expires_in: Expira després + fields: Metadades del perfil filtered_languages: Llengües filtrades header: Capçalera locale: Llengua diff --git a/config/locales/simple_form.eu.yml b/config/locales/simple_form.eu.yml new file mode 100644 index 000000000..9664357ab --- /dev/null +++ b/config/locales/simple_form.eu.yml @@ -0,0 +1,6 @@ +--- +eu: + simple_form: + hints: + defaults: + avatar: PNG, GIF edo JPG. Gehienez 2MB. 400x400px neurrira eskalatuko da diff --git a/config/locales/simple_form.fr.yml b/config/locales/simple_form.fr.yml index 71674199d..88e1b8873 100644 --- a/config/locales/simple_form.fr.yml +++ b/config/locales/simple_form.fr.yml @@ -8,6 +8,7 @@ fr: display_name: one: 1 caractère restant other: %{count} caractères restants + fields: Vous pouvez avoir jusqu'à 4 éléments affichés en tant que tableau sur votre profil header: Au format PNG, GIF ou JPG. 2 Mo maximum. Sera réduit à 700x335px locked: Vous devrez approuver chaque abonné⋅e et vos statuts ne s’afficheront qu’à vos abonné⋅es note: @@ -22,6 +23,10 @@ fr: user: filtered_languages: Les langues sélectionnées seront filtrées hors de vos fils publics pour vous labels: + account: + fields: + name: Étiquette + value: Contenu defaults: avatar: Image de profil confirm_new_password: Confirmation du nouveau mot de passe @@ -31,6 +36,7 @@ fr: display_name: Nom public email: Adresse courriel expires_in: Expire après + fields: Métadonnées du profil filtered_languages: Langues filtrées header: Image d’en-tête locale: Langue diff --git a/config/locales/simple_form.gl.yml b/config/locales/simple_form.gl.yml index 4dcdd0459..a4903885e 100644 --- a/config/locales/simple_form.gl.yml +++ b/config/locales/simple_form.gl.yml @@ -8,6 +8,7 @@ gl: display_name: one: 1 caracter restante other: %{count} caracteres restantes + fields: Pode ter ate 4 elementos no seu perfil mostrados como unha táboa header: PNG, GIF ou JPG. Como moito 2MB. Será reducida a 700x335px locked: Require que vostede aprove as seguidoras de xeito manual note: @@ -22,6 +23,10 @@ gl: user: filtered_languages: Os idiomas marcados filtraranse das liñas temporais públicas para vostede labels: + account: + fields: + name: Etiqueta + value: Contido defaults: avatar: Avatar confirm_new_password: Confirme o novo contrasinal @@ -31,6 +36,7 @@ gl: display_name: Nome mostrado email: enderezo correo electrónico expires_in: Caducidade despois de + fields: Metadatos do perfil filtered_languages: Idiomas filtrados header: Cabezallo locale: Idioma diff --git a/config/locales/simple_form.it.yml b/config/locales/simple_form.it.yml index b2fcef109..5d9ae18f5 100644 --- a/config/locales/simple_form.it.yml +++ b/config/locales/simple_form.it.yml @@ -3,45 +3,77 @@ it: simple_form: hints: defaults: - avatar: PNG, GIF o JPG. Al massimo 2MB. Sarà ridotto a 400x400px - display_name: Al massimo 30 characters - header: PNG, GIF or JPG. Al massimo 2MB. Sarà ridotto a 700x335px - locked: Richiede la tua approvazione per i nuovi seguaci e rende i nuovi post automaticamente visibili solo ai seguaci - note: Al massimo 160 caratteri + avatar: PNG, GIF o JPG. Al massimo 2MB. Verranno scalate a 400x400px + digest: Inviata solo dopo un lungo periodo di intattività e solo se hai ricevuto qualsiasi messaggio personale in tua assenza + display_name: + one: 1 carattere rimanente + other: %{count} caratteri rimanenti + fields: Puoi avere fino a 4 voci visualizzate come una tabella sul tuo profilo + header: PNG, GIF o JPG. Al massimo 2MB. Verranno scalate a 700x335px + locked: Richiede che approvi i follower manualmente + note: + one: 1 carattere rimanente + other: %{count} caratteri rimanenti + setting_noindex: Coinvolge il tuo profilo pubblico e le pagine di stato + setting_theme: Coinvolge il modo in cui Mastodon verrà visualizzato quando sarai collegato da qualsiasi dispositivo. imports: - data: CSV esportato da un altro server Mastodon + data: File CSV esportato da un altra istanza di Mastodon + sessions: + otp: Inserisci il codice due-fattori dal tuo telefono o usa uno dei codici di recupero. + user: + filtered_languages: Le lingue selezionate verranno filtrate dalla timeline pubblica per te labels: + account: + fields: + name: Etichetta + value: Contenuto defaults: avatar: Avatar - confirm_new_password: Conferma la nuova password - confirm_password: Conferma la password + confirm_new_password: Conferma nuova password + confirm_password: Conferma password current_password: Password corrente data: Data - display_name: Nome pubblico - email: Indirizzo e-mail + display_name: Nome visualizzato + email: Indirizzo email + expires_in: Scade dopo + fields: Metadata profilo + filtered_languages: Lingue filtrate header: Header locale: Lingua - locked: Rendi l'account privato + locked: Blocca account + max_uses: Numero massimo di utilizzi new_password: Nuova password - note: Biografia - otp_attempt: Codice d'accesso + note: Bio + otp_attempt: Codice due-fattori password: Password - setting_boost_modal: Mostra finestra di conferma prima di condividere - setting_default_privacy: Privacy del post - type: Importa - username: Username + setting_auto_play_gif: Play automatico GIF animate + setting_boost_modal: Mostra dialogo di conferma prima del boost + setting_default_privacy: Privacy post + setting_default_sensitive: Segna sempre i media come sensibili + setting_delete_modal: Mostra dialogo di conferma prima di eliminare un toot + setting_display_sensitive_media: Mostra sempre i media segnati come sensibili + setting_noindex: Non indicizzare dai motori di ricerca + setting_reduce_motion: Riduci movimento nelle animazioni + setting_system_font_ui: Usa il carattere di default del sistema + setting_theme: Tema sito + setting_unfollow_modal: Mostra dialogo di conferma prima di smettere di seguire qualcuno + severity: Severità + type: Tipo importazione + username: Nome utente + username_or_email: Nome utente o email interactions: - must_be_follower: Blocca notifiche da chi non ti segue - must_be_following: Blocca notifiche da chi non segui + must_be_follower: Blocca notifiche dai non follower + must_be_following: Blocca notifiche dalle persone che non segui + must_be_following_dm: Blocca i messaggi diretti dalle persone che non segui notification_emails: - digest: Invia riassunto via e-mail - favourite: Invia e-mail quando qualcuno apprezza i tuoi status - follow: Invia e-mail quando qualcuno ti segue - follow_request: Invia e-mail quando qualcuno ti richiede di seguirti - mention: Invia e-mail quando qualcuno ti menziona - reblog: Invia e-mail quando qualcuno condivide i tuoi status + digest: Invia email riassuntive + favourite: Invia email quando segna come preferito al tuo stato + follow: Invia email quando qualcuno ti segue + follow_request: Invia email quando qualcuno richiede di seguirti + mention: Invia email quando qualcuno ti menziona + reblog: Invia email quando qualcuno da un boost al tuo stato 'no': 'No' required: mark: "*" text: richiesto - 'yes': Sì + 'yes': Si diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index d7cccfc54..6b2d93454 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -6,6 +6,7 @@ ja: avatar: 2MBまでのPNGやGIF、JPGが利用可能です。400x400pxまで縮小されます digest: 長期間使用していない場合と不在時に返信を受けた場合のみ送信されます display_name: あと%{count}文字入力できます。 + fields: プロフィールに表として4つまでの項目を表示することができます header: 2MBまでのPNGやGIF、JPGが利用可能です。 700x335pxまで縮小されます locked: フォロワーを手動で承認する必要があります note: あと%{count}文字入力できます。 @@ -18,6 +19,10 @@ ja: user: filtered_languages: 選択した言語があなたの公開タイムラインから取り除かれます labels: + account: + fields: + name: ラベル + value: 内容 defaults: avatar: アイコン confirm_new_password: 新しいパスワード(確認用) @@ -27,6 +32,7 @@ ja: display_name: 表示名 email: メールアドレス expires_in: 有効期限 + fields: プロフィール補足情報 filtered_languages: 除外する言語 header: ヘッダー locale: 言語 @@ -46,7 +52,7 @@ ja: setting_reduce_motion: アニメーションの動きを減らす setting_system_font_ui: システムのデフォルトフォントを使う setting_theme: サイトテーマ - setting_unfollow_modal: フォロー解除する前に確認ダイアログを表示する + setting_unfollow_modal: フォローを解除する前に確認ダイアログを表示する severity: 重大性 type: インポートする項目 username: ユーザー名 diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index 85eccf091..ccb05fd25 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -8,6 +8,7 @@ ko: display_name: one: 1 글자 남음 other: %{count} 글자 남음 + fields: 당신의 프로파일에 최대 4개까지 표 형식으로 나타낼 수 있습니다 header: PNG, GIF 혹은 JPG. 최대 2MB. 700x335px로 다운스케일 됨 locked: 수동으로 팔로워를 승인하고, 기본 툿 프라이버시 설정을 팔로워 전용으로 변경 note: @@ -22,6 +23,10 @@ ko: user: filtered_languages: 선택된 언어가 공개 타임라인에서 제외 될 것입니다 labels: + account: + fields: + name: 라벨 + value: 내용 defaults: avatar: 아바타 confirm_new_password: 새로운 비밀번호 다시 입력 @@ -31,6 +36,7 @@ ko: display_name: 표시되는 이름 email: 이메일 주소 expires_in: 만료시각 + fields: 프로필 메타데이터 filtered_languages: 숨긴 언어들 header: 헤더 locale: 언어 diff --git a/config/locales/simple_form.nl.yml b/config/locales/simple_form.nl.yml index 9876230b3..ec42adfd7 100644 --- a/config/locales/simple_form.nl.yml +++ b/config/locales/simple_form.nl.yml @@ -8,6 +8,7 @@ nl: display_name: one: 1 teken over other: %{count} 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 locked: Vereist dat je handmatig volgers moet accepteren note: @@ -22,6 +23,10 @@ nl: user: filtered_languages: De geselecteerde talen worden uit de lokale en globale tijdlijn verwijderd labels: + account: + fields: + name: Label + value: Inhoud defaults: avatar: Avatar confirm_new_password: Nieuw wachtwoord bevestigen @@ -31,6 +36,7 @@ nl: display_name: Weergavenaam email: E-mailadres expires_in: Vervalt na + fields: Metadata profiel filtered_languages: Talen filteren header: Omslagfoto locale: Taal @@ -63,7 +69,7 @@ nl: digest: Periodiek e-mails met een samenvatting versturen favourite: Een e-mail versturen wanneer iemand jouw toot als favoriet markeert follow: Een e-mail versturen wanneer iemand jou volgt - follow_request: Een e-mail versturen wanneer iemand jou wilt volgen + follow_request: Een e-mail versturen wanneer iemand jou wil volgen mention: Een e-mail versturen wanneer iemand jou vermeld reblog: Een e-mail versturen wanneer iemand jouw toot heeft geboost 'no': Nee diff --git a/config/locales/simple_form.sk.yml b/config/locales/simple_form.sk.yml index e504c9774..f928fd6a9 100644 --- a/config/locales/simple_form.sk.yml +++ b/config/locales/simple_form.sk.yml @@ -8,6 +8,7 @@ sk: display_name: one: Ostáva ti 1 znak other: Ostáva ti %{count} znakov + fields: Môžeš mať 4 položky na svojom profile zobrazené vo forme tabuľky header: PNG, GIF alebo JPG. Maximálne 2MB. Bude zmenšený na 700x335px locked: Musíte manuálne schváliť sledujúcich note: @@ -22,6 +23,10 @@ sk: user: filtered_languages: Zaškrtnuté jazyky budú pre teba vynechané nebudú z verejnej časovej osi labels: + account: + fields: + name: Označenie + value: Obsah defaults: avatar: Avatar confirm_new_password: Opäť vaše nové heslo pre potvrdenie @@ -31,6 +36,7 @@ sk: display_name: Meno email: Emailová adresa expires_in: Expirovať po + fields: Metadáta profilu filtered_languages: Filtrované jazyky header: Obrázok v hlavičke locale: Jazyk @@ -43,9 +49,9 @@ sk: setting_auto_play_gif: Automaticky prehrávať animované GIFy setting_boost_modal: Zobrazovať potvrdzovacie okno pred re-toot setting_default_privacy: Nastavenie súkromia príspevkov - setting_default_sensitive: Označiť každý obrázok/video/súbor ako chúlostivý - setting_delete_modal: Zobrazovať potvrdzovacie okno pred zmazaním toot-u - setting_display_sensitive_media: Vždy zobrazovať médiá označované ako senzitívne + setting_default_sensitive: Označ všetky mediálne súbory ako chúlostivé + setting_delete_modal: Zobrazuj potvrdzovacie okno pred vymazaním toot-u + setting_display_sensitive_media: Vždy zobraz médiá označené ako chúlostivé setting_noindex: Nezaraďuj príspevky do indexu pre vyhľadávče setting_reduce_motion: Redukovať pohyb v animáciách setting_system_font_ui: Použiť základné systémové písmo diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 37f711323..a38b245b3 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -4,6 +4,7 @@ sk: about_hashtag_html: Toto sú verejné toot príspevky otagované #%{tagom}. Ak máš účet niekde vo fediverse, môžeš ich používať. about_mastodon_html: Mastodon je sociálna sieť založená na otvorených webových protokoloch. Jej zrojový kód je otvorený a je decentralizovaná podobne ako email. about_this: O tejto instancii + administered_by: 'Správca je:' closed_registrations: Registrácie sú momentálne uzatvorené. Avšak, môžeš nájsť nejaký iný Mastodon server kde si založ účet a získaj tak prístup do presne tej istej siete, odtiaľ. contact: Kontakt contact_missing: Nezadané @@ -60,7 +61,15 @@ sk: destroyed_msg: Poznámka moderátora bola úspešne zmazaná! accounts: are_you_sure: Ste si istý? + avatar: Maskot by_domain: Doména + change_email: + changed_msg: Email k tomuto účtu bol úspešne zmenený! + current_email: Súčastný email + label: Zmeniť email + new_email: Nový email + submit: Zmeniť email + title: Zmeň email pre %{username} confirm: Potvrdiť confirmed: Potvrdený demote: Degradovať @@ -108,6 +117,7 @@ sk: public: Verejná os push_subscription_expires: PuSH odoberanie expiruje redownload: Obnoviť avatar + remove_avatar: Odstrániť avatár reset: Reset reset_password: Obnoviť heslo resubscribe: Znovu odoberať @@ -128,6 +138,7 @@ sk: statuses: Príspevky subscribe: Odoberať title: Účty + unconfirmed_email: Nepotvrdený email undo_silenced: Zrušiť stíšenie undo_suspension: Zrušiť suspendáciu unsubscribe: Prestať odoberať @@ -150,8 +161,9 @@ sk: enable_user: "%{name} povolil prihlásenie pre používateľa %{target}" memorialize_account: '%{name} zmenil účet %{target} na stránku "Navždy budeme spomínať"' promote_user: "%{name} povýšil/a používateľa %{target}" + remove_avatar_user: "%{name} odstránil/a %{target}ov avatár" reset_password_user: "%{name} resetoval/a heslo pre používateľa %{target}" - resolve_report: "%{name} zamietli nahlásenie %{target}" + resolve_report: "%{name} vyriešili nahlásenie užívateľa %{target}" silence_account: "%{name} utíšil/a účet %{target}" suspend_account: "%{name} zablokoval/a účet používateľa %{target}" unsilence_account: "%{name} zrušil/a utíšenie účtu používateľa %{target}" @@ -239,8 +251,14 @@ sk: expired: Expirované title: Filtrovať title: Pozvánky + report_notes: + created_msg: Poznámka o nahlásení úspešne vytvorená! + destroyed_msg: Poznámka o nahlásení úspešne vymazaná! reports: - action_taken_by: Zákrok vykonal + account: + note: poznámka + report: nahlás + action_taken_by: Zákrok vykonal/a are_you_sure: Ste si istý/á? comment: none: Žiadne From bfc41711dd5b9725d135c11807aa645ebc78bc18 Mon Sep 17 00:00:00 2001 From: Stefan Midjich Date: Wed, 25 Apr 2018 01:54:24 +0200 Subject: [PATCH 146/442] =?UTF-8?q?more=20sane=20phrasing=20in=20?= =?UTF-8?q?=F0=9F=87=B8=F0=9F=87=AA=20translation=20(#7256)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * more sane phrasing in 🇸🇪 translation * another small issue in 🇸🇪 translation --- config/locales/sv.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 7e763c2aa..993bdd909 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -9,7 +9,7 @@ sv: contact_missing: Inte inställd contact_unavailable: N/A description_headline: Vad är %{domain}? - domain_count_after: annan instans + domain_count_after: andra instanser domain_count_before: Uppkopplad mot extended_description_html: |

    En bra plats för regler

    @@ -29,7 +29,7 @@ sv: other_instances: Instanslista source_code: Källkod status_count_after: statusar - status_count_before: Vem författade + status_count_before: Som skapat user_count_after: användare user_count_before: Hem till what_is_mastodon: Vad är Mastodon? From 9d4710ed0059b2f789e6b32b9f81d4ce90b98907 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 25 Apr 2018 02:10:02 +0200 Subject: [PATCH 147/442] Add RSS feeds for end-users (#7259) * Add RSS feed for accounts * Add RSS feeds for hashtags * Fix code style issues * Fix code style issues --- app/controllers/accounts_controller.rb | 10 +- app/controllers/tags_controller.rb | 11 +- app/helpers/stream_entries_helper.rb | 12 +- app/lib/rss_builder.rb | 130 ++++++++++++++++++++++ app/serializers/rss/account_serializer.rb | 39 +++++++ app/serializers/rss/tag_serializer.rb | 37 ++++++ 6 files changed, 230 insertions(+), 9 deletions(-) create mode 100644 app/lib/rss_builder.rb create mode 100644 app/serializers/rss/account_serializer.rb create mode 100644 app/serializers/rss/tag_serializer.rb diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 7bf35825f..1152d4aca 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -20,9 +20,10 @@ class AccountsController < ApplicationController @pinned_statuses = cache_collection(@account.pinned_statuses, Status) if show_pinned_statuses? @statuses = filtered_status_page(params) @statuses = cache_collection(@statuses, Status) + unless @statuses.empty? - @older_url = older_url if @statuses.last.id > filtered_statuses.last.id - @newer_url = newer_url if @statuses.first.id < filtered_statuses.first.id + @older_url = older_url if @statuses.last.id > filtered_statuses.last.id + @newer_url = newer_url if @statuses.first.id < filtered_statuses.first.id end end @@ -31,6 +32,11 @@ class AccountsController < ApplicationController render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.feed(@account, @entries.reject { |entry| entry.status.nil? })) end + format.rss do + @statuses = cache_collection(default_statuses.without_reblogs.without_replies.limit(PAGE_SIZE), Status) + render xml: RSS::AccountSerializer.render(@account, @statuses) + end + format.json do skip_session! diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index 9f3090e37..014a5c9b8 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -1,6 +1,8 @@ # frozen_string_literal: true class TagsController < ApplicationController + PAGE_SIZE = 20 + before_action :set_body_classes before_action :set_instance_presenter @@ -13,8 +15,15 @@ class TagsController < ApplicationController @initial_state_json = serializable_resource.to_json end + format.rss do + @statuses = Status.as_tag_timeline(@tag).limit(PAGE_SIZE) + @statuses = cache_collection(@statuses, Status) + + render xml: RSS::TagSerializer.render(@tag, @statuses) + end + format.json do - @statuses = Status.as_tag_timeline(@tag, current_account, params[:local]).paginate_by_max_id(20, params[:max_id]) + @statuses = Status.as_tag_timeline(@tag, current_account, params[:local]).paginate_by_max_id(PAGE_SIZE, params[:max_id]) @statuses = cache_collection(@statuses, Status) render json: collection_presenter, diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb index 8254ef4dc..c6f12ecd4 100644 --- a/app/helpers/stream_entries_helper.rb +++ b/app/helpers/stream_entries_helper.rb @@ -12,17 +12,17 @@ module StreamEntriesHelper prepend_str = [ [ number_to_human(account.statuses_count, strip_insignificant_zeros: true), - t('accounts.posts'), + I18n.t('accounts.posts'), ].join(' '), [ number_to_human(account.following_count, strip_insignificant_zeros: true), - t('accounts.following'), + I18n.t('accounts.following'), ].join(' '), [ number_to_human(account.followers_count, strip_insignificant_zeros: true), - t('accounts.followers'), + I18n.t('accounts.followers'), ].join(' '), ].join(', ') @@ -40,16 +40,16 @@ module StreamEntriesHelper end end - text = attachments.to_a.reject { |_, value| value.zero? }.map { |key, value| t("statuses.attached.#{key}", count: value) }.join(' · ') + text = attachments.to_a.reject { |_, value| value.zero? }.map { |key, value| I18n.t("statuses.attached.#{key}", count: value) }.join(' · ') return if text.blank? - t('statuses.attached.description', attached: text) + I18n.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) + I18n.t('statuses.content_warning', warning: status.spoiler_text) end def status_description(status) diff --git a/app/lib/rss_builder.rb b/app/lib/rss_builder.rb new file mode 100644 index 000000000..63ddba2e8 --- /dev/null +++ b/app/lib/rss_builder.rb @@ -0,0 +1,130 @@ +# frozen_string_literal: true + +class RSSBuilder + class ItemBuilder + def initialize + @item = Ox::Element.new('item') + end + + def title(str) + @item << (Ox::Element.new('title') << str) + + self + end + + def link(str) + @item << Ox::Element.new('guid').tap do |guid| + guid['isPermalink'] = 'true' + guid << str + end + + @item << (Ox::Element.new('link') << str) + + self + end + + def pub_date(date) + @item << (Ox::Element.new('pubDate') << date.to_formatted_s(:rfc822)) + + self + end + + def description(str) + @item << (Ox::Element.new('description') << str) + + self + end + + def enclosure(url, type, size) + @item << Ox::Element.new('enclosure').tap do |enclosure| + enclosure['url'] = url + enclosure['length'] = size + enclosure['type'] = type + end + + self + end + + def to_element + @item + end + end + + def initialize + @document = Ox::Document.new(version: '1.0') + @channel = Ox::Element.new('channel') + + @document << (rss << @channel) + end + + def title(str) + @channel << (Ox::Element.new('title') << str) + + self + end + + def link(str) + @channel << (Ox::Element.new('link') << str) + + self + end + + def image(str) + @channel << Ox::Element.new('image').tap do |image| + image << (Ox::Element.new('url') << str) + image << (Ox::Element.new('title') << '') + image << (Ox::Element.new('link') << '') + end + + @channel << (Ox::Element.new('webfeeds:icon') << str) + + self + end + + def cover(str) + @channel << Ox::Element.new('webfeeds:cover').tap do |cover| + cover['image'] = str + end + + self + end + + def logo(str) + @channel << (Ox::Element.new('webfeeds:logo') << str) + + self + end + + def accent_color(str) + @channel << (Ox::Element.new('webfeeds:accentColor') << str) + + self + end + + def description(str) + @channel << (Ox::Element.new('description') << str) + + self + end + + def item + @channel << ItemBuilder.new.tap do |item| + yield item + end.to_element + + self + end + + def to_xml + ('' + Ox.dump(@document, effort: :tolerant)).force_encoding('UTF-8') + end + + private + + def rss + Ox::Element.new('rss').tap do |rss| + rss['version'] = '2.0' + rss['xmlns:webfeeds'] = 'http://webfeeds.org/rss/1.0' + end + end +end diff --git a/app/serializers/rss/account_serializer.rb b/app/serializers/rss/account_serializer.rb new file mode 100644 index 000000000..bde360a41 --- /dev/null +++ b/app/serializers/rss/account_serializer.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +class RSS::AccountSerializer + include ActionView::Helpers::NumberHelper + include StreamEntriesHelper + include RoutingHelper + + def render(account, statuses) + builder = RSSBuilder.new + + builder.title("#{display_name(account)} (@#{account.local_username_and_domain})") + .description(account_description(account)) + .link(TagManager.instance.url_for(account)) + .logo(full_asset_url(asset_pack_path('logo.svg'))) + .accent_color('2b90d9') + + builder.image(full_asset_url(account.avatar.url(:original))) if account.avatar? + builder.cover(full_asset_url(account.header.url(:original))) if account.header? + + statuses.each do |status| + builder.item do |item| + item.title(status.title) + .link(TagManager.instance.url_for(status)) + .pub_date(status.created_at) + .description(status.spoiler_text.presence || Formatter.instance.format(status).to_str) + + status.media_attachments.each do |media| + item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, length: media.file.size) + end + end + end + + builder.to_xml + end + + def self.render(account, statuses) + new.render(account, statuses) + end +end diff --git a/app/serializers/rss/tag_serializer.rb b/app/serializers/rss/tag_serializer.rb new file mode 100644 index 000000000..7680a8da5 --- /dev/null +++ b/app/serializers/rss/tag_serializer.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class RSS::TagSerializer + include ActionView::Helpers::NumberHelper + include ActionView::Helpers::SanitizeHelper + include StreamEntriesHelper + include RoutingHelper + + def render(tag, statuses) + builder = RSSBuilder.new + + builder.title("##{tag.name}") + .description(strip_tags(I18n.t('about.about_hashtag_html', hashtag: tag.name))) + .link(tag_url(tag)) + .logo(full_asset_url(asset_pack_path('logo.svg'))) + .accent_color('2b90d9') + + statuses.each do |status| + builder.item do |item| + item.title(status.title) + .link(TagManager.instance.url_for(status)) + .pub_date(status.created_at) + .description(status.spoiler_text.presence || Formatter.instance.format(status).to_str) + + status.media_attachments.each do |media| + item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, length: media.file.size) + end + end + end + + builder.to_xml + end + + def self.render(tag, statuses) + new.render(tag, statuses) + end +end From f58dcbc9814b5ba2fd4f7d7af643aa25dcf40594 Mon Sep 17 00:00:00 2001 From: MIYAGI Hikaru Date: Wed, 25 Apr 2018 09:14:49 +0900 Subject: [PATCH 148/442] HTTP proxy support for outgoing request, manage access to hidden service (#7134) * Add support for HTTP client proxy * Add access control for darknet Supress error when access to darknet via transparent proxy * Fix the codes pointed out * Lint * Fix an omission + lint * any? -> include? * Change detection method to regexp to avoid test fail --- .env.production.sample | 7 +++++++ app/lib/request.rb | 16 +++++++++++++++- config/initializers/http_client_proxy.rb | 24 ++++++++++++++++++++++++ 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 config/initializers/http_client_proxy.rb diff --git a/.env.production.sample b/.env.production.sample index 9de2c0650..c936546da 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -214,3 +214,10 @@ STREAMING_CLUSTER_NUM=1 # SAML_UID_ATTRIBUTE="urn:oid:0.9.2342.19200300.100.1.1" # SAML_ATTRIBUTES_STATEMENTS_VERIFIED= # SAML_ATTRIBUTES_STATEMENTS_VERIFIED_EMAIL= + +# Use HTTP proxy for outgoing request (optional) +# http_proxy=http://gateway.local:8118 +# Access control for hidden service. +# ALLOW_ACCESS_TO_HIDDEN_SERVICE=true +# If you use transparent proxy to access to hidden service, uncomment following for skipping private address check. +# HIDDEN_SERVICE_VIA_TRANSPARENT_PROXY=true diff --git a/app/lib/request.rb b/app/lib/request.rb index dca93a6e9..0acd654da 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -11,9 +11,10 @@ class Request def initialize(verb, url, **options) @verb = verb @url = Addressable::URI.parse(url).normalize - @options = options.merge(socket_class: Socket) + @options = options.merge(use_proxy? ? Rails.configuration.x.http_client_proxy : { socket_class: Socket }) @headers = {} + raise Mastodon::HostValidationError, 'Instance does not support hidden service connections' if block_hidden_service? set_common_headers! set_digest! if options.key?(:body) end @@ -99,6 +100,14 @@ class Request @http_client ||= HTTP.timeout(:per_operation, timeout).follow(max_hops: 2) end + def use_proxy? + Rails.configuration.x.http_client_proxy.present? + end + + def block_hidden_service? + !Rails.configuration.x.access_to_hidden_service && /\.(onion|i2p)$/.match(@url.host) + end + module ClientLimit def body_with_limit(limit = 1.megabyte) raise Mastodon::LengthValidationError if content_length.present? && content_length > limit @@ -129,6 +138,7 @@ class Request class Socket < TCPSocket class << self def open(host, *args) + return super host, *args if thru_hidden_service? host outer_e = nil Addrinfo.foreach(host, nil, nil, :SOCK_STREAM) do |address| begin @@ -142,6 +152,10 @@ class Request end alias new open + + def thru_hidden_service?(host) + Rails.configuration.x.hidden_service_via_transparent_proxy && /\.(onion|i2p)$/.match(host) + end end end diff --git a/config/initializers/http_client_proxy.rb b/config/initializers/http_client_proxy.rb new file mode 100644 index 000000000..f5026d59e --- /dev/null +++ b/config/initializers/http_client_proxy.rb @@ -0,0 +1,24 @@ +Rails.application.configure do + config.x.http_client_proxy = {} + if ENV['http_proxy'].present? + proxy = URI.parse(ENV['http_proxy']) + raise "Unsupported proxy type: #{proxy.scheme}" unless %w(http https).include? proxy.scheme + raise "No proxy host" unless proxy.host + + host = proxy.host + host = host[1...-1] if host[0] == '[' #for IPv6 address + config.x.http_client_proxy[:proxy] = { proxy_address: host, proxy_port: proxy.port, proxy_username: proxy.user, proxy_password: proxy.password }.compact + end + + config.x.access_to_hidden_service = ENV['ALLOW_ACCESS_TO_HIDDEN_SERVICE'] == 'true' + config.x.hidden_service_via_transparent_proxy = ENV['HIDDEN_SERVICE_VIA_TRANSPARENT_PROXY'] == 'true' +end + +module Goldfinger + def self.finger(uri, opts = {}) + to_hidden = /\.(onion|i2p)(:\d+)?$/.match(uri) + raise Mastodon::HostValidationError, 'Instance does not support hidden service connections' if !Rails.configuration.x.access_to_hidden_service && to_hidden + opts = opts.merge(Rails.configuration.x.http_client_proxy).merge(ssl: !to_hidden) + Goldfinger::Client.new(uri, opts).finger + end +end From eb593a5a0c9ee9749115f867a5675ceb1f231379 Mon Sep 17 00:00:00 2001 From: MIYAGI Hikaru Date: Wed, 25 Apr 2018 21:12:28 +0900 Subject: [PATCH 149/442] Append '.test' to hostname in stub data (#7260) --- spec/fixtures/requests/oembed_json.html | 2 +- spec/fixtures/requests/oembed_json_xml.html | 4 +- spec/fixtures/requests/oembed_xml.html | 2 +- spec/helpers/jsonld_helper_spec.rb | 24 ++++++------ spec/lib/formatter_spec.rb | 6 +-- spec/lib/ostatus/atom_serializer_spec.rb | 26 ++++++------- spec/lib/provider_discovery_spec.rb | 38 +++++++++---------- spec/lib/tag_manager_spec.rb | 34 ++++++++--------- spec/models/account_spec.rb | 10 ++--- spec/models/status_pin_spec.rb | 2 +- .../process_account_service_spec.rb | 4 +- 11 files changed, 76 insertions(+), 76 deletions(-) diff --git a/spec/fixtures/requests/oembed_json.html b/spec/fixtures/requests/oembed_json.html index 773a4f92a..167085871 100644 --- a/spec/fixtures/requests/oembed_json.html +++ b/spec/fixtures/requests/oembed_json.html @@ -1,7 +1,7 @@ - + diff --git a/spec/fixtures/requests/oembed_json_xml.html b/spec/fixtures/requests/oembed_json_xml.html index 8afd8e997..9f5b9e8be 100644 --- a/spec/fixtures/requests/oembed_json_xml.html +++ b/spec/fixtures/requests/oembed_json_xml.html @@ -7,8 +7,8 @@ > The type attribute must contain either application/json+oembed for JSON > responses, or text/xml+oembed for XML. --> - - + + diff --git a/spec/fixtures/requests/oembed_xml.html b/spec/fixtures/requests/oembed_xml.html index bdfcca170..788dfaabd 100644 --- a/spec/fixtures/requests/oembed_xml.html +++ b/spec/fixtures/requests/oembed_xml.html @@ -7,7 +7,7 @@ > The type attribute must contain either application/json+oembed for JSON > responses, or text/xml+oembed for XML. --> - + diff --git a/spec/helpers/jsonld_helper_spec.rb b/spec/helpers/jsonld_helper_spec.rb index 48bfdc306..a5ab249c2 100644 --- a/spec/helpers/jsonld_helper_spec.rb +++ b/spec/helpers/jsonld_helper_spec.rb @@ -32,37 +32,37 @@ describe JsonLdHelper do describe '#fetch_resource' do context 'when the second argument is false' do it 'returns resource even if the retrieved ID and the given URI does not match' do - stub_request(:get, 'https://bob/').to_return body: '{"id": "https://alice/"}' - stub_request(:get, 'https://alice/').to_return body: '{"id": "https://alice/"}' + stub_request(:get, 'https://bob.test/').to_return body: '{"id": "https://alice.test/"}' + stub_request(:get, 'https://alice.test/').to_return body: '{"id": "https://alice.test/"}' - expect(fetch_resource('https://bob/', false)).to eq({ 'id' => 'https://alice/' }) + expect(fetch_resource('https://bob.test/', false)).to eq({ 'id' => 'https://alice.test/' }) end it 'returns nil if the object identified by the given URI and the object identified by the retrieved ID does not match' do - stub_request(:get, 'https://mallory/').to_return body: '{"id": "https://marvin/"}' - stub_request(:get, 'https://marvin/').to_return body: '{"id": "https://alice/"}' + stub_request(:get, 'https://mallory.test/').to_return body: '{"id": "https://marvin.test/"}' + stub_request(:get, 'https://marvin.test/').to_return body: '{"id": "https://alice.test/"}' - expect(fetch_resource('https://mallory/', false)).to eq nil + expect(fetch_resource('https://mallory.test/', false)).to eq nil end end context 'when the second argument is true' do it 'returns nil if the retrieved ID and the given URI does not match' do - stub_request(:get, 'https://mallory/').to_return body: '{"id": "https://alice/"}' - expect(fetch_resource('https://mallory/', true)).to eq nil + stub_request(:get, 'https://mallory.test/').to_return body: '{"id": "https://alice.test/"}' + expect(fetch_resource('https://mallory.test/', true)).to eq nil end end end describe '#fetch_resource_without_id_validation' do it 'returns nil if the status code is not 200' do - stub_request(:get, 'https://host/').to_return status: 400, body: '{}' - expect(fetch_resource_without_id_validation('https://host/')).to eq nil + stub_request(:get, 'https://host.test/').to_return status: 400, body: '{}' + expect(fetch_resource_without_id_validation('https://host.test/')).to eq nil end it 'returns hash' do - stub_request(:get, 'https://host/').to_return status: 200, body: '{}' - expect(fetch_resource_without_id_validation('https://host/')).to eq({}) + stub_request(:get, 'https://host.test/').to_return status: 200, body: '{}' + expect(fetch_resource_without_id_validation('https://host.test/')).to eq({}) end end end diff --git a/spec/lib/formatter_spec.rb b/spec/lib/formatter_spec.rb index 6e849f379..b8683e720 100644 --- a/spec/lib/formatter_spec.rb +++ b/spec/lib/formatter_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' RSpec.describe Formatter do let(:local_account) { Fabricate(:account, domain: nil, username: 'alice') } - let(:remote_account) { Fabricate(:account, domain: 'remote', username: 'bob', url: 'https://remote/') } + let(:remote_account) { Fabricate(:account, domain: 'remote.test', username: 'bob', url: 'https://remote.test/') } shared_examples 'encode and link URLs' do context 'matches a stand-alone medium URL' do @@ -377,12 +377,12 @@ RSpec.describe Formatter do end context 'contains linkable mentions for remote accounts' do - let(:text) { '@bob@remote' } + let(:text) { '@bob@remote.test' } before { remote_account } it 'links' do - is_expected.to eq '

    @bob

    ' + is_expected.to eq '

    @bob

    ' end end diff --git a/spec/lib/ostatus/atom_serializer_spec.rb b/spec/lib/ostatus/atom_serializer_spec.rb index d46791307..0bd22880e 100644 --- a/spec/lib/ostatus/atom_serializer_spec.rb +++ b/spec/lib/ostatus/atom_serializer_spec.rb @@ -30,13 +30,13 @@ RSpec.describe OStatus::AtomSerializer do end it 'appends activity:object with target account' do - target_account = Fabricate(:account, domain: 'domain', uri: 'https://domain/id') + target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id') follow_request = Fabricate(:follow_request, target_account: target_account) follow_request_salmon = serialize(follow_request) object = follow_request_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq 'https://domain/id' + expect(object.id.text).to eq 'https://domain.test/id' end end @@ -413,20 +413,20 @@ RSpec.describe OStatus::AtomSerializer do entry = OStatus::AtomSerializer.new.entry(remote_status.stream_entry, true) entry.nodes.delete_if { |node| node[:type] == 'application/activity+json' } # Remove ActivityPub link to simplify test - xml = OStatus::AtomSerializer.render(entry).gsub('cb6e6126.ngrok.io', 'remote') + xml = OStatus::AtomSerializer.render(entry).gsub('cb6e6126.ngrok.io', 'remote.test') remote_status.destroy! remote_account.destroy! account = Account.create!( - domain: 'remote', + domain: 'remote.test', username: 'username', last_webfingered_at: Time.now.utc ) ProcessFeedService.new.call(xml, account) - expect(Status.find_by(uri: "https://remote/users/#{remote_status.account.to_param}/statuses/#{remote_status.id}")).to be_instance_of Status + expect(Status.find_by(uri: "https://remote.test/users/#{remote_status.account.to_param}/statuses/#{remote_status.id}")).to be_instance_of Status end end @@ -776,13 +776,13 @@ RSpec.describe OStatus::AtomSerializer do end it 'appends activity:object element with target account' do - target_account = Fabricate(:account, domain: 'domain', uri: 'https://domain/id') + target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id') block = Fabricate(:block, target_account: target_account) block_salmon = OStatus::AtomSerializer.new.block_salmon(block) object = block_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq 'https://domain/id' + expect(object.id.text).to eq 'https://domain.test/id' end it 'returns element whose rendered view triggers block when processed' do @@ -863,13 +863,13 @@ RSpec.describe OStatus::AtomSerializer do end it 'appends activity:object element with target account' do - target_account = Fabricate(:account, domain: 'domain', uri: 'https://domain/id') + target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id') block = Fabricate(:block, target_account: target_account) unblock_salmon = OStatus::AtomSerializer.new.unblock_salmon(block) object = unblock_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq 'https://domain/id' + expect(object.id.text).to eq 'https://domain.test/id' end it 'returns element whose rendered view triggers block when processed' do @@ -1124,13 +1124,13 @@ RSpec.describe OStatus::AtomSerializer do end it 'appends activity:object element with target account' do - target_account = Fabricate(:account, domain: 'domain', uri: 'https://domain/id') + target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id') follow = Fabricate(:follow, target_account: target_account) follow_salmon = OStatus::AtomSerializer.new.follow_salmon(follow) object = follow_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq 'https://domain/id' + expect(object.id.text).to eq 'https://domain.test/id' end it 'includes description' do @@ -1242,14 +1242,14 @@ RSpec.describe OStatus::AtomSerializer do end it 'appends activity:object element with target account' do - target_account = Fabricate(:account, domain: 'domain', uri: 'https://domain/id') + target_account = Fabricate(:account, domain: 'domain.test', uri: 'https://domain.test/id') follow = Fabricate(:follow, target_account: target_account) follow.destroy! unfollow_salmon = OStatus::AtomSerializer.new.unfollow_salmon(follow) object = unfollow_salmon.nodes.find { |node| node.name == 'activity:object' } - expect(object.id.text).to eq 'https://domain/id' + expect(object.id.text).to eq 'https://domain.test/id' end it 'returns element whose rendered view triggers unfollow when processed' do diff --git a/spec/lib/provider_discovery_spec.rb b/spec/lib/provider_discovery_spec.rb index 12e2616c9..de2ac16f9 100644 --- a/spec/lib/provider_discovery_spec.rb +++ b/spec/lib/provider_discovery_spec.rb @@ -7,7 +7,7 @@ describe ProviderDiscovery do context 'when status code is 200 and MIME type is text/html' do context 'Both of JSON and XML provider are discoverable' do before do - stub_request(:get, 'https://host/oembed.html').to_return( + stub_request(:get, 'https://host.test/oembed.html').to_return( status: 200, headers: { 'Content-Type': 'text/html' }, body: request_fixture('oembed_json_xml.html') @@ -15,21 +15,21 @@ describe ProviderDiscovery do end it 'returns new OEmbed::Provider for JSON provider if :format option is set to :json' do - provider = ProviderDiscovery.discover_provider('https://host/oembed.html', format: :json) - expect(provider.endpoint).to eq 'https://host/provider.json' + provider = ProviderDiscovery.discover_provider('https://host.test/oembed.html', format: :json) + expect(provider.endpoint).to eq 'https://host.test/provider.json' expect(provider.format).to eq :json end it 'returns new OEmbed::Provider for XML provider if :format option is set to :xml' do - provider = ProviderDiscovery.discover_provider('https://host/oembed.html', format: :xml) - expect(provider.endpoint).to eq 'https://host/provider.xml' + provider = ProviderDiscovery.discover_provider('https://host.test/oembed.html', format: :xml) + expect(provider.endpoint).to eq 'https://host.test/provider.xml' expect(provider.format).to eq :xml end end context 'JSON provider is discoverable while XML provider is not' do before do - stub_request(:get, 'https://host/oembed.html').to_return( + stub_request(:get, 'https://host.test/oembed.html').to_return( status: 200, headers: { 'Content-Type': 'text/html' }, body: request_fixture('oembed_json.html') @@ -37,15 +37,15 @@ describe ProviderDiscovery do end it 'returns new OEmbed::Provider for JSON provider' do - provider = ProviderDiscovery.discover_provider('https://host/oembed.html') - expect(provider.endpoint).to eq 'https://host/provider.json' + provider = ProviderDiscovery.discover_provider('https://host.test/oembed.html') + expect(provider.endpoint).to eq 'https://host.test/provider.json' expect(provider.format).to eq :json end end context 'XML provider is discoverable while JSON provider is not' do before do - stub_request(:get, 'https://host/oembed.html').to_return( + stub_request(:get, 'https://host.test/oembed.html').to_return( status: 200, headers: { 'Content-Type': 'text/html' }, body: request_fixture('oembed_xml.html') @@ -53,15 +53,15 @@ describe ProviderDiscovery do end it 'returns new OEmbed::Provider for XML provider' do - provider = ProviderDiscovery.discover_provider('https://host/oembed.html') - expect(provider.endpoint).to eq 'https://host/provider.xml' + provider = ProviderDiscovery.discover_provider('https://host.test/oembed.html') + expect(provider.endpoint).to eq 'https://host.test/provider.xml' expect(provider.format).to eq :xml end end context 'Invalid XML provider is discoverable while JSON provider is not' do before do - stub_request(:get, 'https://host/oembed.html').to_return( + stub_request(:get, 'https://host.test/oembed.html').to_return( status: 200, headers: { 'Content-Type': 'text/html' }, body: request_fixture('oembed_invalid_xml.html') @@ -69,13 +69,13 @@ describe ProviderDiscovery do end it 'raises OEmbed::NotFound' do - expect { ProviderDiscovery.discover_provider('https://host/oembed.html') }.to raise_error OEmbed::NotFound + expect { ProviderDiscovery.discover_provider('https://host.test/oembed.html') }.to raise_error OEmbed::NotFound end end context 'Neither of JSON and XML provider is discoverable' do before do - stub_request(:get, 'https://host/oembed.html').to_return( + stub_request(:get, 'https://host.test/oembed.html').to_return( status: 200, headers: { 'Content-Type': 'text/html' }, body: request_fixture('oembed_undiscoverable.html') @@ -83,14 +83,14 @@ describe ProviderDiscovery do end it 'raises OEmbed::NotFound' do - expect { ProviderDiscovery.discover_provider('https://host/oembed.html') }.to raise_error OEmbed::NotFound + expect { ProviderDiscovery.discover_provider('https://host.test/oembed.html') }.to raise_error OEmbed::NotFound end end end context 'when status code is not 200' do before do - stub_request(:get, 'https://host/oembed.html').to_return( + stub_request(:get, 'https://host.test/oembed.html').to_return( status: 400, headers: { 'Content-Type': 'text/html' }, body: request_fixture('oembed_xml.html') @@ -98,20 +98,20 @@ describe ProviderDiscovery do end it 'raises OEmbed::NotFound' do - expect { ProviderDiscovery.discover_provider('https://host/oembed.html') }.to raise_error OEmbed::NotFound + expect { ProviderDiscovery.discover_provider('https://host.test/oembed.html') }.to raise_error OEmbed::NotFound end end context 'when MIME type is not text/html' do before do - stub_request(:get, 'https://host/oembed.html').to_return( + stub_request(:get, 'https://host.test/oembed.html').to_return( status: 200, body: request_fixture('oembed_xml.html') ) end it 'raises OEmbed::NotFound' do - expect { ProviderDiscovery.discover_provider('https://host/oembed.html') }.to raise_error OEmbed::NotFound + expect { ProviderDiscovery.discover_provider('https://host.test/oembed.html') }.to raise_error OEmbed::NotFound end end end diff --git a/spec/lib/tag_manager_spec.rb b/spec/lib/tag_manager_spec.rb index 5427a2929..3a804ac0f 100644 --- a/spec/lib/tag_manager_spec.rb +++ b/spec/lib/tag_manager_spec.rb @@ -6,7 +6,7 @@ RSpec.describe TagManager do around do |example| original_local_domain = Rails.configuration.x.local_domain - Rails.configuration.x.local_domain = 'domain' + Rails.configuration.x.local_domain = 'domain.test' example.run @@ -18,11 +18,11 @@ RSpec.describe TagManager do end it 'returns true if the slash-stripped string equals to local domain' do - expect(TagManager.instance.local_domain?('DoMaIn/')).to eq true + expect(TagManager.instance.local_domain?('DoMaIn.Test/')).to eq true end it 'returns false for irrelevant string' do - expect(TagManager.instance.local_domain?('DoMaIn!')).to eq false + expect(TagManager.instance.local_domain?('DoMaIn.Test!')).to eq false end end @@ -31,7 +31,7 @@ RSpec.describe TagManager do around do |example| original_web_domain = Rails.configuration.x.web_domain - Rails.configuration.x.web_domain = 'domain' + Rails.configuration.x.web_domain = 'domain.test' example.run @@ -43,11 +43,11 @@ RSpec.describe TagManager do end it 'returns true if the slash-stripped string equals to web domain' do - expect(TagManager.instance.web_domain?('DoMaIn/')).to eq true + expect(TagManager.instance.web_domain?('DoMaIn.Test/')).to eq true end it 'returns false for string with irrelevant characters' do - expect(TagManager.instance.web_domain?('DoMaIn!')).to eq false + expect(TagManager.instance.web_domain?('DoMaIn.Test!')).to eq false end end @@ -57,7 +57,7 @@ RSpec.describe TagManager do end it 'returns normalized domain' do - expect(TagManager.instance.normalize_domain('DoMaIn/')).to eq 'domain' + expect(TagManager.instance.normalize_domain('DoMaIn.Test/')).to eq 'domain.test' end end @@ -69,18 +69,18 @@ RSpec.describe TagManager do end it 'returns true if the normalized string with port is local URL' do - Rails.configuration.x.web_domain = 'domain:42' - expect(TagManager.instance.local_url?('https://DoMaIn:42/')).to eq true + Rails.configuration.x.web_domain = 'domain.test:42' + expect(TagManager.instance.local_url?('https://DoMaIn.Test:42/')).to eq true end it 'returns true if the normalized string without port is local URL' do - Rails.configuration.x.web_domain = 'domain' - expect(TagManager.instance.local_url?('https://DoMaIn/')).to eq true + Rails.configuration.x.web_domain = 'domain.test' + expect(TagManager.instance.local_url?('https://DoMaIn.Test/')).to eq true end it 'returns false for string with irrelevant characters' do - Rails.configuration.x.web_domain = 'domain' - expect(TagManager.instance.local_url?('https://domainn/')).to eq false + Rails.configuration.x.web_domain = 'domain.test' + expect(TagManager.instance.local_url?('https://domainn.test/')).to eq false end end @@ -88,19 +88,19 @@ RSpec.describe TagManager do # The following comparisons MUST be case-insensitive. it 'returns true if the needle has a correct username and domain for remote user' do - expect(TagManager.instance.same_acct?('username@domain', 'UsErNaMe@DoMaIn')).to eq true + expect(TagManager.instance.same_acct?('username@domain.test', 'UsErNaMe@DoMaIn.Test')).to eq true end it 'returns false if the needle is missing a domain for remote user' do - expect(TagManager.instance.same_acct?('username@domain', 'UsErNaMe')).to eq false + expect(TagManager.instance.same_acct?('username@domain.test', 'UsErNaMe')).to eq false end it 'returns false if the needle has an incorrect domain for remote user' do - expect(TagManager.instance.same_acct?('username@domain', 'UsErNaMe@incorrect')).to eq false + expect(TagManager.instance.same_acct?('username@domain.test', 'UsErNaMe@incorrect.test')).to eq false end it 'returns false if the needle has an incorrect username for remote user' do - expect(TagManager.instance.same_acct?('username@domain', 'incorrect@DoMaIn')).to eq false + expect(TagManager.instance.same_acct?('username@domain.test', 'incorrect@DoMaIn.test')).to eq false end it 'returns true if the needle has a correct username and domain for local user' do diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index a8b24d0e2..fb7ddfa83 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -94,14 +94,14 @@ RSpec.describe Account, type: :model do describe '#save_with_optional_media!' do before do - stub_request(:get, 'https://remote/valid_avatar').to_return(request_fixture('avatar.txt')) - stub_request(:get, 'https://remote/invalid_avatar').to_return(request_fixture('feed.txt')) + stub_request(:get, 'https://remote.test/valid_avatar').to_return(request_fixture('avatar.txt')) + stub_request(:get, 'https://remote.test/invalid_avatar').to_return(request_fixture('feed.txt')) end let(:account) do Fabricate(:account, - avatar_remote_url: 'https://remote/valid_avatar', - header_remote_url: 'https://remote/valid_avatar') + avatar_remote_url: 'https://remote.test/valid_avatar', + header_remote_url: 'https://remote.test/valid_avatar') end let!(:expectation) { account.dup } @@ -121,7 +121,7 @@ RSpec.describe Account, type: :model do context 'with invalid properties' do before do - account.avatar_remote_url = 'https://remote/invalid_avatar' + account.avatar_remote_url = 'https://remote.test/invalid_avatar' account.save_with_optional_media! end diff --git a/spec/models/status_pin_spec.rb b/spec/models/status_pin_spec.rb index 944baf639..6f0b2feb8 100644 --- a/spec/models/status_pin_spec.rb +++ b/spec/models/status_pin_spec.rb @@ -55,7 +55,7 @@ RSpec.describe StatusPin, type: :model do end it 'allows pins above the max for remote accounts' do - account = Fabricate(:account, domain: 'remote', username: 'bob', url: 'https://remote/') + account = Fabricate(:account, domain: 'remote.test', username: 'bob', url: 'https://remote.test/') status = [] (max_pins + 1).times do |i| diff --git a/spec/services/activitypub/process_account_service_spec.rb b/spec/services/activitypub/process_account_service_spec.rb index 15e1f4bb2..d67d72acb 100644 --- a/spec/services/activitypub/process_account_service_spec.rb +++ b/spec/services/activitypub/process_account_service_spec.rb @@ -6,9 +6,9 @@ RSpec.describe ActivityPub::ProcessAccountService do context 'property values' do let(:payload) do { - id: 'https://foo', + id: 'https://foo.test', type: 'Actor', - inbox: 'https://foo/inbox', + inbox: 'https://foo.test/inbox', attachment: [ { type: 'PropertyValue', name: 'Pronouns', value: 'They/them' }, { type: 'PropertyValue', name: 'Occupation', value: 'Unit test' }, From fa5b28df8aaa564623f98f315436f4b260a12030 Mon Sep 17 00:00:00 2001 From: Stefan Midjich Date: Thu, 26 Apr 2018 00:36:52 +0200 Subject: [PATCH 150/442] Better phrasing in swedish translation (#7263) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * more sane phrasing in 🇸🇪 translation * another small issue in 🇸🇪 translation * better phrasing in 🇸🇪 translation --- app/javascript/mastodon/locales/sv.json | 4 ++-- config/locales/sv.yml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 7db4d57a7..479fc9a1a 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -113,7 +113,7 @@ "getting_started.appsshort": "Appar", "getting_started.faq": "FAQ", "getting_started.heading": "Kom igång", - "getting_started.open_source_notice": "Mastodon är programvara med öppen källkod. Du kan bidra eller rapportera problem på GitHub på {github}.", + "getting_started.open_source_notice": "Mastodon är programvara med öppen källkod. Du kan bidra eller rapportera problem via GitHub på {github}.", "getting_started.userguide": "Användarguide", "home.column_settings.advanced": "Avancerad", "home.column_settings.basic": "Grundläggande", @@ -206,7 +206,7 @@ "onboarding.page_three.search": "Använd sökfältet för att hitta personer och titta på hashtags, till exempel {illustration} och {introductions}. För att leta efter en person som inte befinner sig i detta fall använd deras fulla handhavande.", "onboarding.page_two.compose": "Skriv inlägg från skrivkolumnen. Du kan ladda upp bilder, ändra integritetsinställningar och lägga till varningar med ikonerna nedan.", "onboarding.skip": "Hoppa över", - "privacy.change": "Justera status sekretess", + "privacy.change": "Justera sekretess", "privacy.direct.long": "Skicka endast till nämnda användare", "privacy.direct.short": "Direkt", "privacy.private.long": "Skicka endast till följare", diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 993bdd909..9a559c030 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -545,7 +545,7 @@ sv: quadrillion: Q thousand: K trillion: T - unit: enhet + unit: '' pagination: newer: Nyare next: Nästa From 3afdd6a17b2e539cc4684cb7adeab9423f375e2f Mon Sep 17 00:00:00 2001 From: "Renato \"Lond\" Cerqueira" Date: Thu, 26 Apr 2018 11:58:22 +0200 Subject: [PATCH 151/442] Weblate translations 20180426 (#7266) * Translated using Weblate (Swedish) Currently translated at 100.0% (294 of 294 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/sv/ * Translated using Weblate (Slovak) Currently translated at 92.0% (576 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Slovak) Currently translated at 100.0% (294 of 294 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/sk/ * Translated using Weblate (Dutch) Currently translated at 100.0% (626 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/nl/ * Translated using Weblate (Dutch) Currently translated at 100.0% (294 of 294 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/nl/ * Translated using Weblate (Swedish) Currently translated at 100.0% (294 of 294 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/sv/ * Translated using Weblate (Swedish) Currently translated at 99.6% (624 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sv/ * Translated using Weblate (Japanese) Currently translated at 100.0% (294 of 294 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ja/ * Translated using Weblate (Japanese) Currently translated at 99.3% (622 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ja/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (62 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/pt_BR/ * Translated using Weblate (Galician) Currently translated at 100.0% (626 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/gl/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 100.0% (294 of 294 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/pt_BR/ * Translated using Weblate (Portuguese (Brazil)) Currently translated at 99.2% (621 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/pt_BR/ * Translated using Weblate (Basque) Currently translated at 32.2% (20 of 62 strings) Translation: Mastodon/Preferences Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/simple_form/eu/ * Translated using Weblate (Arabic) Currently translated at 99.6% (293 of 294 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ar/ * Translated using Weblate (Arabic) Currently translated at 82.4% (516 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ar/ * Translated using Weblate (Slovak) Currently translated at 92.1% (577 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Catalan) Currently translated at 100.0% (626 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/ca/ * Translated using Weblate (Basque) Currently translated at 100.0% (294 of 294 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eu/ * Translated using Weblate (Slovak) Currently translated at 92.3% (578 of 626 strings) Translation: Mastodon/Backend Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/backend/sk/ * Translated using Weblate (Basque) Currently translated at 100.0% (294 of 294 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/eu/ https://sustatu.eus/1380226549995 * Translated using Weblate (Catalan) Currently translated at 100.0% (294 of 294 strings) Translation: Mastodon/React Translate-URL: https://weblate.joinmastodon.org/projects/mastodon/frontend/ca/ * Normalize translations ran yarn build:development && i18n-tasks normalize && yarn manage:translations && i18n-tasks remove-unused --- app/javascript/mastodon/locales/ar.json | 12 +-- app/javascript/mastodon/locales/ca.json | 12 +-- app/javascript/mastodon/locales/eu.json | 46 +++++------ app/javascript/mastodon/locales/ja.json | 2 +- app/javascript/mastodon/locales/nl.json | 14 ++-- app/javascript/mastodon/locales/pt-BR.json | 18 ++--- app/javascript/mastodon/locales/sk.json | 6 +- app/javascript/mastodon/locales/sv.json | 26 +++---- config/locales/ar.yml | 2 +- config/locales/ca.yml | 16 ++-- config/locales/gl.yml | 89 +++++++++++++++++++++- config/locales/ja.yml | 5 +- config/locales/nl.yml | 8 +- config/locales/pt-BR.yml | 8 +- config/locales/simple_form.eu.yml | 26 +++++++ config/locales/simple_form.pt-BR.yml | 6 ++ config/locales/sk.yml | 10 ++- config/locales/sv.yml | 60 ++++++++++++++- 18 files changed, 277 insertions(+), 89 deletions(-) diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index 7975ac1c5..947348f70 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -40,7 +40,7 @@ "bundle_modal_error.retry": "إعادة المحاولة", "column.blocks": "الحسابات المحجوبة", "column.community": "الخيط العام المحلي", - "column.direct": "Direct messages", + "column.direct": "الرسائل المباشرة", "column.domain_blocks": "النطاقات المخفية", "column.favourites": "المفضلة", "column.follow_requests": "طلبات المتابعة", @@ -59,7 +59,7 @@ "column_header.unpin": "فك التدبيس", "column_subheading.navigation": "التصفح", "column_subheading.settings": "الإعدادات", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "لن يَظهر هذا التبويق إلا للمستخدمين المذكورين.", "compose_form.hashtag_warning": "هذا التبويق لن يُدرَج تحت أي وسم كان بما أنه غير مُدرَج. لا يُسمح بالبحث إلّا عن التبويقات العمومية عن طريق الوسوم.", "compose_form.lock_disclaimer": "حسابك ليس {locked}. يمكن لأي شخص متابعتك و عرض المنشورات.", "compose_form.lock_disclaimer.lock": "مقفل", @@ -101,7 +101,7 @@ "emoji_button.symbols": "رموز", "emoji_button.travel": "أماكن و أسفار", "empty_column.community": "الخط الزمني المحلي فارغ. أكتب شيئا ما للعامة كبداية !", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "لم تتلق أية رسالة خاصة مباشِرة بعد. سوف يتم عرض الرسائل المباشرة هنا إن قمت بإرسال واحدة أو تلقيت البعض منها.", "empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.", "empty_column.home": "إنك لا تتبع بعد أي شخص إلى حد الآن. زر {public} أو استخدام حقل البحث لكي تبدأ على التعرف على مستخدمين آخرين.", "empty_column.home.public_timeline": "الخيط العام", @@ -135,7 +135,7 @@ "keyboard_shortcuts.mention": "لذِكر الناشر", "keyboard_shortcuts.reply": "للردّ", "keyboard_shortcuts.search": "للتركيز على البحث", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_hidden": "لعرض أو إخفاء النص مِن وراء التحذير", "keyboard_shortcuts.toot": "لتحرير تبويق جديد", "keyboard_shortcuts.unfocus": "لإلغاء التركيز على حقل النص أو نافذة البحث", "keyboard_shortcuts.up": "للإنتقال إلى أعلى القائمة", @@ -157,7 +157,7 @@ "mute_modal.hide_notifications": "هل تود إخفاء الإخطارات القادمة من هذا المستخدم ؟", "navigation_bar.blocks": "الحسابات المحجوبة", "navigation_bar.community_timeline": "الخيط العام المحلي", - "navigation_bar.direct": "Direct messages", + "navigation_bar.direct": "الرسائل المباشِرة", "navigation_bar.domain_blocks": "النطاقات المخفية", "navigation_bar.edit_profile": "تعديل الملف الشخصي", "navigation_bar.favourites": "المفضلة", @@ -242,7 +242,7 @@ "search_results.total": "{count, number} {count, plural, one {result} و {results}}", "standalone.public_title": "نظرة على ...", "status.block": "Block @{name}", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "إلغاء الترقية", "status.cannot_reblog": "تعذرت ترقية هذا المنشور", "status.delete": "إحذف", "status.direct": "رسالة خاصة إلى @{name}", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 2e5004cc9..f2e3699d5 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -40,7 +40,7 @@ "bundle_modal_error.retry": "Torna-ho a provar", "column.blocks": "Usuaris blocats", "column.community": "Línia de temps local", - "column.direct": "Direct messages", + "column.direct": "Missatges directes", "column.domain_blocks": "Dominis ocults", "column.favourites": "Favorits", "column.follow_requests": "Peticions per seguir-te", @@ -101,7 +101,7 @@ "emoji_button.symbols": "Símbols", "emoji_button.travel": "Viatges i Llocs", "empty_column.community": "La línia de temps local és buida. Escriu alguna cosa públicament per fer rodar la pilota!", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "Encara no tens missatges directes. Quan enviïs o rebis un, es mostrarà aquí.", "empty_column.hashtag": "Encara no hi ha res amb aquesta etiqueta.", "empty_column.home": "Encara no segueixes ningú. Visita {public} o fes cerca per començar i conèixer altres usuaris.", "empty_column.home.public_timeline": "la línia de temps pública", @@ -135,7 +135,7 @@ "keyboard_shortcuts.mention": "per esmentar l'autor", "keyboard_shortcuts.reply": "respondre", "keyboard_shortcuts.search": "per centrar la cerca", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_hidden": "per a mostrar/amagar text sota CW", "keyboard_shortcuts.toot": "per a començar un toot nou de trinca", "keyboard_shortcuts.unfocus": "descentrar l'area de composició de text/cerca", "keyboard_shortcuts.up": "moure amunt en la llista", @@ -157,7 +157,7 @@ "mute_modal.hide_notifications": "Amagar notificacions d'aquest usuari?", "navigation_bar.blocks": "Usuaris bloquejats", "navigation_bar.community_timeline": "Línia de temps Local", - "navigation_bar.direct": "Direct messages", + "navigation_bar.direct": "Missatges directes", "navigation_bar.domain_blocks": "Dominis ocults", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favorits", @@ -242,7 +242,7 @@ "search_results.total": "{count, number} {count, plural, un {result} altres {results}}", "standalone.public_title": "Una mirada a l'interior ...", "status.block": "Block @{name}", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "Desfer l'impuls", "status.cannot_reblog": "Aquesta publicació no pot ser retootejada", "status.delete": "Esborrar", "status.direct": "Missatge directe @{name}", @@ -258,7 +258,7 @@ "status.pin": "Fixat en el perfil", "status.pinned": "Toot fixat", "status.reblog": "Impuls", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "Impulsar a l'audiència original", "status.reblogged_by": "{name} ha retootejat", "status.reply": "Respondre", "status.replyAll": "Respondre al tema", diff --git a/app/javascript/mastodon/locales/eu.json b/app/javascript/mastodon/locales/eu.json index 02b7bf7e5..49cdf5630 100644 --- a/app/javascript/mastodon/locales/eu.json +++ b/app/javascript/mastodon/locales/eu.json @@ -1,31 +1,31 @@ { - "account.block": "Block @{name}", - "account.block_domain": "Hide everything from {domain}", + "account.block": "Blokeatu @{name}", + "account.block_domain": "{domain}(e)ko guztia ezkutatu", "account.blocked": "Blokeatuta", - "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.follows_you": "Follows you", - "account.hide_reblogs": "Hide boosts from @{name}", + "account.direct": "@{name}(e)ri mezu zuzena bidali", + "account.disclaimer_full": "Baliteke beheko informazioak erabiltzailearen profilaren zati bat baino ez erakustea.", + "account.domain_blocked": "Ezkutatutako domeinua", + "account.edit_profile": "Profila aldatu", + "account.follow": "Jarraitu", + "account.followers": "Jarraitzaileak", + "account.follows": "Jarraitzen", + "account.follows_you": "Jarraitzen dizu", + "account.hide_reblogs": "@{name}(e)k sustatutakoak ezkutatu", "account.media": "Media", - "account.mention": "Mention @{name}", - "account.moved_to": "{name} has moved to:", - "account.mute": "Mute @{name}", - "account.mute_notifications": "Mute notifications from @{name}", - "account.muted": "Muted", + "account.mention": "@{name} aipatu", + "account.moved_to": "{name} hona lekualdatu da:", + "account.mute": "@{name} isilarazi", + "account.mute_notifications": "@{name}(e)ren jakinarazpenak isilarazi", + "account.muted": "Isilarazita", "account.posts": "Toots", "account.posts_with_replies": "Toots and replies", - "account.report": "Report @{name}", - "account.requested": "Awaiting approval. Click to cancel follow request", - "account.share": "Share @{name}'s profile", - "account.show_reblogs": "Show boosts from @{name}", - "account.unblock": "Unblock @{name}", - "account.unblock_domain": "Unhide {domain}", - "account.unfollow": "Unfollow", + "account.report": "@{name} salatu", + "account.requested": "Onarpenaren zain. Klikatu jarraitzeko eskaera ezeztatzeko", + "account.share": "@{name}(e)ren profila elkarbanatu", + "account.show_reblogs": "@{name}(e)k sustatutakoak erakutsi", + "account.unblock": "@{name} desblokeatu", + "account.unblock_domain": "Berriz erakutsi {domain}", + "account.unfollow": "Jarraitzeari utzi", "account.unmute": "Unmute @{name}", "account.unmute_notifications": "Unmute notifications from @{name}", "account.view_full_profile": "View full profile", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index a06bdad24..a5857c115 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -40,7 +40,7 @@ "bundle_modal_error.retry": "再試行", "column.blocks": "ブロックしたユーザー", "column.community": "ローカルタイムライン", - "column.direct": "Direct messages", + "column.direct": "ダイレクトメッセージ", "column.domain_blocks": "非表示にしたドメイン", "column.favourites": "お気に入り", "column.follow_requests": "フォローリクエスト", diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index 338b7aebc..adc1d19a7 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -40,7 +40,7 @@ "bundle_modal_error.retry": "Opnieuw proberen", "column.blocks": "Geblokkeerde gebruikers", "column.community": "Lokale tijdlijn", - "column.direct": "Direct messages", + "column.direct": "Directe berichten", "column.domain_blocks": "Verborgen domeinen", "column.favourites": "Favorieten", "column.follow_requests": "Volgverzoeken", @@ -101,7 +101,7 @@ "emoji_button.symbols": "Symbolen", "emoji_button.travel": "Reizen en plekken", "empty_column.community": "De lokale tijdlijn is nog leeg. Toot iets in het openbaar om de bal aan het rollen te krijgen!", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "Je hebt nog geen directe berichten. Wanneer je er een verzend of ontvangt, zijn deze hier te zien.", "empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.", "empty_column.home": "Jij volgt nog niemand. Bezoek {public} of gebruik het zoekvenster om andere mensen te ontmoeten.", "empty_column.home.public_timeline": "de globale tijdlijn", @@ -127,7 +127,7 @@ "keyboard_shortcuts.compose": "om het tekstvak voor toots te focussen", "keyboard_shortcuts.description": "Omschrijving", "keyboard_shortcuts.down": "om naar beneden door de lijst te bewegen", - "keyboard_shortcuts.enter": "to open status", + "keyboard_shortcuts.enter": "om toot volledig te tonen", "keyboard_shortcuts.favourite": "om als favoriet te markeren", "keyboard_shortcuts.heading": "Sneltoetsen", "keyboard_shortcuts.hotkey": "Sneltoets", @@ -135,7 +135,7 @@ "keyboard_shortcuts.mention": "om de auteur te vermelden", "keyboard_shortcuts.reply": "om te reageren", "keyboard_shortcuts.search": "om het zoekvak te focussen", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_hidden": "om tekst achter een waarschuwing (CW) te tonen/verbergen", "keyboard_shortcuts.toot": "om een nieuwe toot te starten", "keyboard_shortcuts.unfocus": "om het tekst- en zoekvak te ontfocussen", "keyboard_shortcuts.up": "om omhoog te bewegen in de lijst", @@ -157,7 +157,7 @@ "mute_modal.hide_notifications": "Verberg meldingen van deze persoon?", "navigation_bar.blocks": "Geblokkeerde gebruikers", "navigation_bar.community_timeline": "Lokale tijdlijn", - "navigation_bar.direct": "Direct messages", + "navigation_bar.direct": "Directe berichten", "navigation_bar.domain_blocks": "Verborgen domeinen", "navigation_bar.edit_profile": "Profiel bewerken", "navigation_bar.favourites": "Favorieten", @@ -242,7 +242,7 @@ "search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}", "standalone.public_title": "Een kijkje binnenin...", "status.block": "Blokkeer @{name}", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "Niet meer boosten", "status.cannot_reblog": "Deze toot kan niet geboost worden", "status.delete": "Verwijderen", "status.direct": "Directe toot @{name}", @@ -258,7 +258,7 @@ "status.pin": "Aan profielpagina vastmaken", "status.pinned": "Vastgemaakte toot", "status.reblog": "Boost", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "Boost naar oorspronkelijke ontvangers", "status.reblogged_by": "{name} boostte", "status.reply": "Reageren", "status.replyAll": "Reageer op iedereen", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 7013822bf..7f8690f91 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -40,8 +40,8 @@ "bundle_modal_error.retry": "Tente novamente", "column.blocks": "Usuários bloqueados", "column.community": "Local", - "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.direct": "Mensagens diretas", + "column.domain_blocks": "Domínios escondidos", "column.favourites": "Favoritos", "column.follow_requests": "Seguidores pendentes", "column.home": "Página inicial", @@ -101,7 +101,7 @@ "emoji_button.symbols": "Símbolos", "emoji_button.travel": "Viagens & Lugares", "empty_column.community": "A timeline local está vazia. Escreva algo publicamente para começar!", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "Você não tem nenhuma mensagem direta ainda. Quando você enviar ou receber uma, as mensagens aparecerão por aqui.", "empty_column.hashtag": "Ainda não há qualquer conteúdo com essa hashtag.", "empty_column.home": "Você ainda não segue usuário algum. Visite a timeline {public} ou use o buscador para procurar e conhecer outros usuários.", "empty_column.home.public_timeline": "global", @@ -135,7 +135,7 @@ "keyboard_shortcuts.mention": "para mencionar o autor", "keyboard_shortcuts.reply": "para responder", "keyboard_shortcuts.search": "para focar a pesquisa", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_hidden": "mostrar/esconder o texto com aviso de conteúdo", "keyboard_shortcuts.toot": "para compor um novo toot", "keyboard_shortcuts.unfocus": "para remover o foco da área de composição/pesquisa", "keyboard_shortcuts.up": "para mover para cima na lista", @@ -157,8 +157,8 @@ "mute_modal.hide_notifications": "Esconder notificações deste usuário?", "navigation_bar.blocks": "Usuários bloqueados", "navigation_bar.community_timeline": "Local", - "navigation_bar.direct": "Direct messages", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.direct": "Mensagens diretas", + "navigation_bar.domain_blocks": "Domínios escondidos", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.favourites": "Favoritos", "navigation_bar.follow_requests": "Seguidores pendentes", @@ -242,10 +242,10 @@ "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "standalone.public_title": "Dê uma espiada...", "status.block": "Block @{name}", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "Retirar o compartilhamento", "status.cannot_reblog": "Esta postagem não pode ser compartilhada", "status.delete": "Excluir", - "status.direct": "Direct message @{name}", + "status.direct": "Enviar mensagem direta à @{name}", "status.embed": "Incorporar", "status.favourite": "Adicionar aos favoritos", "status.load_more": "Carregar mais", @@ -258,7 +258,7 @@ "status.pin": "Fixar no perfil", "status.pinned": "Toot fixado", "status.reblog": "Compartilhar", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "Compartilhar com a audiência original", "status.reblogged_by": "{name} compartilhou", "status.reply": "Responder", "status.replyAll": "Responder à sequência", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json index 8b19cac6f..1977fe50a 100644 --- a/app/javascript/mastodon/locales/sk.json +++ b/app/javascript/mastodon/locales/sk.json @@ -40,7 +40,7 @@ "bundle_modal_error.retry": "Skúsiť znova", "column.blocks": "Blokovaní užívatelia", "column.community": "Lokálna časová os", - "column.direct": "Direct messages", + "column.direct": "Súkromné správy", "column.domain_blocks": "Skryté domény", "column.favourites": "Obľúbené", "column.follow_requests": "Žiadosti o sledovanie", @@ -101,7 +101,7 @@ "emoji_button.symbols": "Symboly", "emoji_button.travel": "Cestovanie a miesta", "empty_column.community": "Lokálna časová os je prázdna. Napíšte niečo, aby sa to tu začalo hýbať!", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "Ešte nemáš žiadne súkromné správy. Keď nejakú pošleš, alebo dostaneš, ukáže sa tu.", "empty_column.hashtag": "Pod týmto hashtagom sa ešte nič nenachádza.", "empty_column.home": "Vaša lokálna osa je zatiaľ prázdna! Pre začiatok pozrite {public} alebo použite vyhľadávanie a nájdite tak ostatných používateľov.", "empty_column.home.public_timeline": "verejná časová os", @@ -157,7 +157,7 @@ "mute_modal.hide_notifications": "Skryť notifikácie od tohoto užívateľa?", "navigation_bar.blocks": "Blokovaní užívatelia", "navigation_bar.community_timeline": "Lokálna časová os", - "navigation_bar.direct": "Direct messages", + "navigation_bar.direct": "Súkromné správy", "navigation_bar.domain_blocks": "Skryté domény", "navigation_bar.edit_profile": "Upraviť profil", "navigation_bar.favourites": "Obľúbené", diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 479fc9a1a..4efe88a7e 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -18,7 +18,7 @@ "account.mute_notifications": "Stäng av notifieringar från @{name}", "account.muted": "Nertystad", "account.posts": "Inlägg", - "account.posts_with_replies": "Toots med svar", + "account.posts_with_replies": "Toots och svar", "account.report": "Rapportera @{name}", "account.requested": "Inväntar godkännande. Klicka för att avbryta följförfrågan", "account.share": "Dela @{name}'s profil", @@ -29,7 +29,7 @@ "account.unmute": "Ta bort tystad @{name}", "account.unmute_notifications": "Återaktivera notifikationer från @{name}", "account.view_full_profile": "Visa hela profilen", - "alert.unexpected.message": "An unexpected error occurred.", + "alert.unexpected.message": "Ett oväntat fel uppstod.", "alert.unexpected.title": "Oops!", "boost_modal.combo": "Du kan trycka {combo} för att slippa denna nästa gång", "bundle_column_error.body": "Något gick fel när du laddade denna komponent.", @@ -40,8 +40,8 @@ "bundle_modal_error.retry": "Försök igen", "column.blocks": "Blockerade användare", "column.community": "Lokal tidslinje", - "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.direct": "Direktmeddelande", + "column.domain_blocks": "Dolda domäner", "column.favourites": "Favoriter", "column.follow_requests": "Följ förfrågningar", "column.home": "Hem", @@ -59,7 +59,7 @@ "column_header.unpin": "Ångra fäst", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Inställningar", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "Denna toot kommer endast vara synlig för nämnda användare.", "compose_form.hashtag_warning": "Denna toot kommer inte att listas under någon hashtag eftersom den är onoterad. Endast offentliga toots kan sökas med hashtag.", "compose_form.lock_disclaimer": "Ditt konto är inte {locked}. Vemsomhelst kan följa dig och även se dina inlägg skrivna för endast dina följare.", "compose_form.lock_disclaimer.lock": "låst", @@ -101,7 +101,7 @@ "emoji_button.symbols": "Symboler", "emoji_button.travel": "Resor & Platser", "empty_column.community": "Den lokala tidslinjen är tom. Skriv något offentligt för att få bollen att rulla!", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "Du har inga direktmeddelanden än. När du skickar eller tar emot kommer den att dyka upp här.", "empty_column.hashtag": "Det finns inget i denna hashtag ännu.", "empty_column.home": "Din hemma-tidslinje är tom! Besök {public} eller använd sökning för att komma igång och träffa andra användare.", "empty_column.home.public_timeline": "den publika tidslinjen", @@ -135,7 +135,7 @@ "keyboard_shortcuts.mention": "att nämna författaren", "keyboard_shortcuts.reply": "att svara", "keyboard_shortcuts.search": "att fokusera sökfältet", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_hidden": "att visa/gömma text bakom CW", "keyboard_shortcuts.toot": "att börja en helt ny toot", "keyboard_shortcuts.unfocus": "att avfokusera komponera text fält / sökfält", "keyboard_shortcuts.up": "att flytta upp i listan", @@ -157,8 +157,8 @@ "mute_modal.hide_notifications": "Dölj notifikationer från denna användare?", "navigation_bar.blocks": "Blockerade användare", "navigation_bar.community_timeline": "Lokal tidslinje", - "navigation_bar.direct": "Direct messages", - "navigation_bar.domain_blocks": "Hidden domains", + "navigation_bar.direct": "Direktmeddelanden", + "navigation_bar.domain_blocks": "Dolda domäner", "navigation_bar.edit_profile": "Redigera profil", "navigation_bar.favourites": "Favoriter", "navigation_bar.follow_requests": "Följförfrågningar", @@ -242,10 +242,10 @@ "search_results.total": "{count, number} {count, plural, ett {result} andra {results}}", "standalone.public_title": "En titt inuti...", "status.block": "Block @{name}", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "Ta bort knuff", "status.cannot_reblog": "Detta inlägg kan inte knuffas", "status.delete": "Ta bort", - "status.direct": "Direct message @{name}", + "status.direct": "Direktmeddela @{name}", "status.embed": "Bädda in", "status.favourite": "Favorit", "status.load_more": "Ladda fler", @@ -258,7 +258,7 @@ "status.pin": "Fäst i profil", "status.pinned": "Fäst toot", "status.reblog": "Knuff", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "Knuffa till de ursprungliga åhörarna", "status.reblogged_by": "{name} knuffade", "status.reply": "Svara", "status.replyAll": "Svara på tråden", @@ -276,7 +276,7 @@ "tabs_bar.home": "Hem", "tabs_bar.local_timeline": "Lokal", "tabs_bar.notifications": "Meddelanden", - "tabs_bar.search": "Search", + "tabs_bar.search": "Sök", "ui.beforeunload": "Ditt utkast kommer att förloras om du lämnar Mastodon.", "upload_area.title": "Dra & släpp för att ladda upp", "upload_button.label": "Lägg till media", diff --git a/config/locales/ar.yml b/config/locales/ar.yml index b3cb71d28..e9ca3038e 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -151,7 +151,7 @@ ar: memorialize_account: لقد قام %{name} بتحويل حساب %{target} إلى صفحة تذكارية promote_user: "%{name} قام بترقية المستخدم %{target}" reset_password_user: "%{name} لقد قام بإعادة تعيين الكلمة السرية الخاصة بـ %{target}" - resolve_report: قام %{name} بإلغاء التقرير المُرسَل مِن طرف %{target} + resolve_report: قام %{name} بحل التقرير %{target} silence_account: لقد قام %{name} بكتم حساب %{target} suspend_account: لقد قام %{name} بتعليق حساب %{target} unsilence_account: لقد قام %{name} بإلغاء الكتم عن حساب %{target} diff --git a/config/locales/ca.yml b/config/locales/ca.yml index 9e927f74f..53f8be15c 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -265,7 +265,7 @@ ca: action_taken_by: Mesures adoptades per are_you_sure: N'estàs segur? assign_to_self: Assignar-me - assigned: Assignar Moderador + assigned: Moderador assignat comment: none: Cap created_at: Reportat @@ -275,14 +275,14 @@ ca: mark_as_unresolved: Marcar sense resoldre notes: create: Afegir nota - create_and_resolve: Resoldre amb Nota - create_and_unresolve: Reobrir amb Nota + create_and_resolve: Resoldre amb nota + create_and_unresolve: Reobrir amb nota delete: Esborrar placeholder: Descriu les accions que s'han pres o qualsevol altra actualització d'aquest informe… nsfw: 'false': Mostra els fitxers multimèdia adjunts 'true': Amaga els fitxers multimèdia adjunts - reopen: Reobrir Informe + reopen: Reobrir informe report: 'Informe #%{id}' report_contents: Contingut reported_account: Compte reportat @@ -354,8 +354,8 @@ ca: back_to_account: Torna a la pàgina del compte batch: delete: Suprimeix - nsfw_off: NSFW OFF - nsfw_on: NSFW ON + nsfw_off: Marcar com a no sensible + nsfw_on: Marcar com a sensible execute: Executa failed_to_execute: No s'ha pogut executar media: @@ -684,6 +684,9 @@ ca: one: "%{count} vídeo" other: "%{count} vídeos" content_warning: 'Avís de contingut: %{warning}' + disallowed_hashtags: + one: 'conté una etiqueta no permesa: %{tags}' + other: 'conté les etiquetes no permeses: %{tags}' open_in_web: Obre en la web over_character_limit: Límit de caràcters de %{max} superat pin_errors: @@ -785,6 +788,7 @@ ca:

    Originalment adaptat des del Discourse privacy policy.

    title: "%{instance} Condicions del servei i política de privadesa" themes: + contrast: Alt contrast default: Mastodont time: formats: diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 3f936c0ea..129d8261e 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -265,7 +265,7 @@ gl: action_taken_by: Acción tomada por are_you_sure: Está segura? assign_to_self: Asignarmo - assigned: Asignado ao Moderador + assigned: Moderador asignado comment: none: Nada created_at: Reportado @@ -276,7 +276,7 @@ gl: notes: create: Engadir nota create_and_resolve: Resolver con nota - create_and_unresolve: Votar a abrir con Nota + create_and_unresolve: Voltar a abrir con nota delete: Eliminar placeholder: Describir qué decisións foron tomadas, ou calquer actualización a este informe… nsfw: @@ -354,8 +354,8 @@ gl: back_to_account: Voltar a páxina da conta batch: delete: Eliminar - nsfw_off: NSFW apagado - nsfw_on: NSFW acendido + nsfw_off: Marcar como non sensible + nsfw_on: Marcar como sensible execute: Executar failed_to_execute: Fallou a execución media: @@ -684,6 +684,9 @@ gl: one: "%{count} vídeo" other: "%{count} vídeos" content_warning: 'Aviso sobre o contido: %{warning}' + disallowed_hashtags: + one: 'contiña unha etiqueta non permitida: %{tags}' + other: 'contiña etiquetas non permitidas: %{tags}' open_in_web: Abrir na web over_character_limit: Excedeu o límite de caracteres %{max} pin_errors: @@ -706,8 +709,86 @@ gl: reblogged: promocionada sensitive_content: Contido sensible terms: + body_html: | +

    Intimidade

    +

    Qué información recollemos?

    + +
      +
    • Información básica da conta: Si se rexistra en este servidor, pediráselle un nome de usuaria, un enderezo de correo electrónico e un contrasinal. De xeito adicional tamén poderá introducir información como un nome público e biografía, tamén subir unha fotografía de perfil e unha imaxe para a cabeceira. O nome de usuaria, o nome público, a biografía e as imaxes de perfil e cabeceira sempre se mostran publicamente.
    • +
    • Publicacións, seguimento e outra información pública: O listado das persoas que segue é un listado público, o mesmo acontece coas súas seguidoras. Cando evía unha mensaxe, a data e hora gárdanse así como o aplicativo que utilizou para enviar a mensaxe. As publicacións poderían conter ficheiros de medios anexos, como fotografías e vídeos. As publicacións públicas e as non listadas están dispoñibles de xeito público. Cando destaca unha publicación no seu perfil tamén é pública. As publicacións son enviadas as súas seguidoras, en algúns casos pode acontecer que estén en diferentes servidores e gárdanse copias neles. Cando elemina unha publicación tamén se envía as súas seguidoras. A acción de voltar a publicar ou marcar como favorita outra publicación sempre é pública.
    • +
    • Mensaxes directas e só para seguidoras: Todas as mensaxes gárdanse e procésanse no servidor. As mensaxes só para seguidoras son entregadas as súas seguidoras e as usuarias que son mencionadas en elas, e as mensaxes directas entréganse só as usuarias mencionadas en elas. En algúns casos esto implica que son entregadas a diferentes servidores e gárdanse copias alí. Facemos un esforzo sincero para limitar o acceso a esas publicacións só as persoas autorizadas, pero outros servidores poderían non ser tan escrupulosos. Polo tanto, é importante revisar os servidores onde se hospedan as súas seguidoras. Nos axustes pode activar a opción de aprovar ou rexeitar novas seguidoras de xeito manual. Teña en conta que a administración do servidor e todos os outros servidores implicados poden ver as mensaxes., e as destinatarias poderían facer capturas de pantalla, copiar e voltar a compartir as mensaxes. Non comparta información comprometida en Mastodon.
    • +
    • IPs e outros metadatos: Cando se conecta, gravamos o IP desde onde se conecta, así como o nome do aplicativo desde onde o fai. Todas as sesións conectadas están dispoñibles para revisar e revogar nos axustes. O último enderezo IP utilizado gárdase ate por 12 meses. Tamén poderiamos gardar informes do servidor que inclúan o enderezo IP de cada petición ao servidor.
    • +
    + +
    + +

    De qué xeito utilizamos os seus datos?

    + +

    Toda a información que recollemos podería ser utilizada dos seguintes xeitos:

    + +
      +
    • Para proporcionar a funcionabiliade básica de Mastodon. Só pode interactuar co contido de outra xente e publicar o seu propio contido si está conectada. Por exemplo, podería seguir outra xente e ver as súas publicacións combinadas nunha liña temporal inicial personalizada.
    • +
    • Para axudar a moderar a comunidade, por exemplo comparando o seu enderezo IP con outros coñecidos para evitar esquivar os rexeitamentos ou outras infraccións.
    • +
    • O endero de correo electrónico que nos proporciona podería ser utilizado para enviarlle información, notificacións sobre outra xente que interactúa cos seus contidos ou lle envía mensaxes, e para respostar a consultas, e/ou outras cuestións ou peticións.
    • +
    + +
    + +

    Cómo proxetemos os seus datos?

    + +

    Implementamos varias medidas de seguridade para protexer os seus datos personais cando introduce, envía ou accede a súa información personal. Entre outras medidas, a súa sesión de navegación, así como o tráfico entre os seus aplicativos e o API están aseguradas mediante SSL, e o seu contrasinal está camuflado utilizando un algoritmo potente de unha sóa vía. Pode habilitar a autenticación de doble factor para protexer o acceso a súa conta aínda máis.

    + +
    + +

    Cal é a nosa política de retención de datos?

    + +

    Faremos un sincero esforzo en:

    + +
      +
    • Protexer informes do servidor que conteñan direccións IP das peticións ao servidor, ate a data estos informes gárdanse por non máis de 90 días.
    • +
    • Reter os enderezos IP asociados con usuarias rexistradas non máis de 12 meses.
    • +
    + +

    Pode solicitar e descargar un ficheiro cos seus contidos, incluíndo publicacións, anexos de medios, imaxes de perfil e imaxe da cabeceira.

    + +

    En calquer momento pode eliminar de xeito irreversible a súa conta.

    + +
    + +

    Utilizamos testemuños?

    + +

    Si. Os testemuños son pequenos ficheiros que un sitio web ou o provedor de servizo transfiren ao disco duro da súa computadora a través do navegador web (si vostede o permite). Estos testemuños posibilitan ao sitio web recoñecer o seu navegador e, si ten unha conta rexistrada, asocialo con dita conta.

    + +

    Utilizamos testemuños para comprender e gardar as súas preferencias para futuras visitas.

    + +
    + +

    Entregamos algunha información a terceiras alleas?

    + +

    Non vendemos, negociamos ou transferimos de algún xeito a terceiras partes alleas a súa información identificativa persoal. Esto non inclúe terceiras partes de confianza que nos axudan a operar o sitio web, a xestionar a empresa, ou darlle servizo si esas partes aceptan manter esa información baixo confidencialidade. Poderiamos liberar esa información si cremos que eso da cumplimento axeitado a lei, reforza as políticas do noso sitio ou protexe os nosos, e de outros, dereitos, propiedade ou seguridade.

    + +

    O seu contido público podería ser descargado por outros servidores na rede. As súas publicacións públicas e para só seguidoras son entregadas aos servidores onde residen as súas seguidoras na rede, e as mensaxes directas son entregadas aos servidores das destinatarias sempre que esas seguidoras ou destinatarios residan en servidores distintos de este.

    + +

    Cado autoriza a este aplicativo a utilizar a súa conta, dependendo da amplitude dos permisos que autorice, podería acceder a información pública de perfil, ao listado de seguimento, as súas seguidoras, os seus listados, todas as súas publicacións, as publicacións favoritas. Os aplicativos non poden nunca acceder ao seu enderezo de correo nin ao seu contrasinal.

    + +
    + +

    Children's Online Privacy Protection Act Compliance

    + +

    O noso sitio, productos e servizos diríxense a persoas que teñen un mínimo de 13 anos. Si este servidor está en EEUU, e ten vostede menos de 13 anos, a requerimento da COPPA (Children's Online Privacy Protection Act) non utilice este sitio.

    + +
    + +

    Cambios na nosa política de intimidade

    + +

    Si decidimos cambiar a nosa política de intimidade publicaremos os cambios en esta páxina.

    + +

    Este documento ten licenza CC-BY-SA. Actualizouse o 7 de Marzo de 2018.

    + +

    Adaptado do orixinal Discourse privacy policy.

    title: "%{instance} Termos do Servizo e Política de Intimidade" themes: + contrast: Alto contraste default: Mastodon time: formats: diff --git a/config/locales/ja.yml b/config/locales/ja.yml index aaebec654..aafd29fbf 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -278,7 +278,7 @@ ja: create_and_resolve: 書き込み、解決済みにする create_and_unresolve: 書き込み、未解決として開く delete: 削除 - placeholder: このレポートに取られた措置やその他更新を記述してください + placeholder: このレポートに取られた措置や、その他の更新を記述してください… nsfw: 'false': NSFW オフ 'true': NSFW オン @@ -684,6 +684,9 @@ ja: one: "%{count} 本の動画" other: "%{count} 本の動画" content_warning: '閲覧注意: %{warning}' + disallowed_hashtags: + one: '許可されていないハッシュタグが含まれています: %{tags}' + other: '許可されていないハッシュタグが含まれています: %{tags}' open_in_web: Webで開く over_character_limit: 上限は %{max}文字までです pin_errors: diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 64bc71874..7a488bb0f 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -354,8 +354,8 @@ nl: back_to_account: Terug naar accountpagina batch: delete: Verwijderen - nsfw_off: NSFW UIT - nsfw_on: NSFW AAN + nsfw_off: Als niet gevoelig markeren + nsfw_on: Als gevoelig markeren execute: Uitvoeren failed_to_execute: Uitvoeren mislukt media: @@ -684,6 +684,9 @@ nl: one: "%{count} video" other: "%{count} video's" content_warning: 'Tekstwaarschuwing: %{warning}' + disallowed_hashtags: + one: 'bevatte een niet toegestane hashtag: %{tags}' + other: 'bevatte niet toegestane hashtags: %{tags}' open_in_web: In de webapp openen over_character_limit: Limiet van %{max} tekens overschreden pin_errors: @@ -785,6 +788,7 @@ nl:

    Originally adapted from the Discourse privacy policy.

    title: "%{instance} Terms of Service and Privacy Policy" themes: + contrast: Hoog contrast default: Mastodon time: formats: diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index aeafe77bd..c3620b4a8 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -354,8 +354,8 @@ pt-BR: back_to_account: Voltar para página da conta batch: delete: Deletar - nsfw_off: NSFW ATIVADO - nsfw_on: NSFW DESATIVADO + nsfw_off: Marcar como não-sensível + nsfw_on: Marcar como sensível execute: Executar failed_to_execute: Falha em executar media: @@ -683,6 +683,9 @@ pt-BR: one: "%{count} vídeo" other: "%{count} vídeos" content_warning: 'Aviso de conteúdo: %{warning}' + disallowed_hashtags: + one: 'continha a hashtag não permitida: %{tags}' + other: 'continha as hashtags não permitidas: %{tags}' open_in_web: Abrir na web over_character_limit: limite de caracteres de %{max} excedido pin_errors: @@ -707,6 +710,7 @@ pt-BR: terms: title: "%{instance} Termos de Serviço e Política de Privacidade" themes: + contrast: Alto contraste default: Mastodon time: formats: diff --git a/config/locales/simple_form.eu.yml b/config/locales/simple_form.eu.yml index 9664357ab..d856feac5 100644 --- a/config/locales/simple_form.eu.yml +++ b/config/locales/simple_form.eu.yml @@ -4,3 +4,29 @@ eu: hints: defaults: avatar: PNG, GIF edo JPG. Gehienez 2MB. 400x400px neurrira eskalatuko da + locked: Jarraitzaileak eskuz onartu behar dituzu + note: + other: %{count} karaktere faltan + setting_noindex: Zure profil publiko eta egoera orrietan eragina du + setting_theme: Edozein gailutik konektatzean Mastodon-en itxuran eragiten du. + imports: + data: Mastodon-en beste instantzia batetik CSV fitxategia esportatu da + user: + filtered_languages: Aukeratutako hizkuntzak timeline publikotik filtratuko dira + labels: + account: + fields: + name: Etiketa + value: Edukia + defaults: + confirm_new_password: Pasahitz berria berretsi + confirm_password: Pasahitza berretsi + current_password: Oraingo pasahitza + display_name: Izena erakutsi + email: Helbide elektronikoa + fields: Profilaren metadatuak + filtered_languages: Iragazitako hizkuntzak + locale: Hizkuntza + new_password: Pasahitz berria + note: Bio + password: Pasahitza diff --git a/config/locales/simple_form.pt-BR.yml b/config/locales/simple_form.pt-BR.yml index 0c22b2608..cae1f671d 100644 --- a/config/locales/simple_form.pt-BR.yml +++ b/config/locales/simple_form.pt-BR.yml @@ -8,6 +8,7 @@ pt-BR: display_name: one: 1 caracter restante other: %{count} caracteres restantes + fields: Você pode ter até 4 itens exibidos em forma de tabela no seu perfil header: PNG, GIF or JPG. Arquivos de até 2MB. Eles serão diminuídos para 700x335px locked: Requer aprovação manual de seguidores note: @@ -22,6 +23,10 @@ pt-BR: user: filtered_languages: Selecione os idiomas que devem ser removidos de suas timelines públicas labels: + account: + fields: + name: Rótulo + value: Conteúdo defaults: avatar: Avatar confirm_new_password: Confirmar nova senha @@ -31,6 +36,7 @@ pt-BR: display_name: Nome de exibição email: Endereço de e-mail expires_in: Expira em + fields: Metadados do perfil filtered_languages: Idiomas filtrados header: Cabeçalho locale: Idioma diff --git a/config/locales/sk.yml b/config/locales/sk.yml index a38b245b3..e9fa55a12 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -146,6 +146,8 @@ sk: web: Web action_logs: actions: + assigned_to_self_report: "%{name}pridelil/a hlásenie užívateľa %{target}sebe" + change_email_user: "%{name} zmenil/a emailovú adresu užívateľa %{target}" confirm_user: "%{name} potvrdil e-mailovú adresu používateľa %{target}" create_custom_emoji: "%{name} nahral nový emoji %{target}" create_domain_block: "%{name} zablokoval doménu %{target}" @@ -162,6 +164,7 @@ sk: memorialize_account: '%{name} zmenil účet %{target} na stránku "Navždy budeme spomínať"' promote_user: "%{name} povýšil/a používateľa %{target}" remove_avatar_user: "%{name} odstránil/a %{target}ov avatár" + reopen_report: "%{name} znovu otvoril/a hlásenie užívateľa %{target}" reset_password_user: "%{name} resetoval/a heslo pre používateľa %{target}" resolve_report: "%{name} vyriešili nahlásenie užívateľa %{target}" silence_account: "%{name} utíšil/a účet %{target}" @@ -260,8 +263,11 @@ sk: report: nahlás action_taken_by: Zákrok vykonal/a are_you_sure: Ste si istý/á? + assign_to_self: '' + assigned: '' comment: none: Žiadne + created_at: '' delete: Vymazať id: Identifikácia mark_as_resolved: Označiť ako vyriešené @@ -336,8 +342,8 @@ sk: back_to_account: Späť na účet batch: delete: Vymazať - nsfw_off: Nevhodný obsah je vypnutý - nsfw_on: Nevhodný obsah je zapnutý + nsfw_off: Obsah nieje chúlostivý + nsfw_on: Označ obeah aka chúlostivý execute: Vykonať failed_to_execute: Nepodarilo sa vykonať media: diff --git a/config/locales/sv.yml b/config/locales/sv.yml index 9a559c030..6f648ab08 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -4,6 +4,7 @@ sv: about_hashtag_html: Dessa är offentliga toots märkta med #%{hashtag}. Du kan interagera med dem om du har ett konto någonstans i federationen. about_mastodon_html: Mastodon är ett socialt nätverk baserat på öppna webbprotokoll och gratis, öppen källkodsprogramvara. Det är decentraliserat som e-post. about_this: Om + administered_by: 'Administreras av:' closed_registrations: Registreringar är för närvarande stängda i denna instans. Dock så kan du hitta en annan instans för att skapa ett konto och få tillgång till samma nätverk från det. contact: Kontakt contact_missing: Inte inställd @@ -60,7 +61,15 @@ sv: destroyed_msg: Modereringsnotering borttagen utan problem! accounts: are_you_sure: Är du säker? + avatar: Avatar by_domain: Domän + change_email: + changed_msg: E-postadressen har ändrats! + current_email: Nuvarande E-postadress + label: Byt E-postadress + new_email: Ny E-postadress + submit: Byt E-postadress + title: Byt E-postadress för %{username} confirm: Bekräfta confirmed: Bekräftad demote: Degradera @@ -108,6 +117,7 @@ sv: public: Offentlig push_subscription_expires: PuSH-prenumerationen löper ut redownload: Uppdatera avatar + remove_avatar: Ta bort avatar reset: Återställ reset_password: Återställ lösenord resubscribe: Starta en ny prenumeration @@ -128,6 +138,7 @@ sv: statuses: Status subscribe: Prenumerera title: Konton + unconfirmed_email: Obekräftad E-postadress undo_silenced: Ångra tystnad undo_suspension: Ångra avstängning unsubscribe: Avsluta prenumeration @@ -135,6 +146,8 @@ sv: web: Webb action_logs: actions: + assigned_to_self_report: "%{name} tilldelade anmälan %{target} till sig själv" + change_email_user: "%{name} bytte e-postadress för användare %{target}" confirm_user: "%{name} bekräftade e-postadress för användare %{target}" create_custom_emoji: "%{name} laddade upp ny emoji %{target}" create_domain_block: "%{name} blockerade domän %{target}" @@ -150,10 +163,13 @@ sv: enable_user: "%{name} aktiverade inloggning för användare %{target}" memorialize_account: "%{name} omvandlade %{target}s konto till en memoriam-sida" promote_user: "%{name} flyttade upp användare %{target}" + remove_avatar_user: "%{name} tog bort %{target}s avatar" + reopen_report: "%{name} återupptog anmälan %{target}" reset_password_user: "%{name} återställde lösenord för användaren %{target}" - resolve_report: "%{name} avvisade anmälan %{target}" + resolve_report: "%{name} löste anmälan %{target}" silence_account: "%{name} tystade ner %{target}s konto" suspend_account: "%{name} suspenderade %{target}s konto" + unassigned_report: "%{name} otilldelade anmälan %{target}" unsilence_account: "%{name} återljudade %{target}s konto" unsuspend_account: "%{name} aktiverade %{target}s konto" update_custom_emoji: "%{name} uppdaterade emoji %{target}" @@ -239,28 +255,48 @@ sv: expired: Utgångna title: Filtrera title: Inbjudningar + report_notes: + created_msg: Anmälningsanteckning har skapats! + destroyed_msg: Anmälningsanteckning har raderats! reports: + account: + note: anteckning + report: anmälan action_taken_by: Åtgärder vidtagna av are_you_sure: Är du säker? + assign_to_self: Tilldela till mig + assigned: Tilldelad moderator comment: none: Ingen + created_at: Anmäld delete: Radera id: ID mark_as_resolved: Markera som löst + mark_as_unresolved: Markera som olöst + notes: + create: Lägg till anteckning + create_and_resolve: Lös med anteckning + create_and_unresolve: Återuppta med anteckning + delete: Radera + placeholder: Beskriv vilka åtgärder som vidtagits eller andra uppdateringar till den här anmälan… nsfw: 'false': Visa bifogade mediafiler 'true': Dölj bifogade mediafiler + reopen: Återuppta anmälan report: 'Anmäl #%{id}' report_contents: Innehåll reported_account: Anmält konto reported_by: Anmäld av resolved: Löst + resolved_msg: Anmälan har lösts framgångsrikt! silence_account: Tysta ner konto status: Status suspend_account: Suspendera konto target: Mål title: Anmälningar + unassign: Otilldela unresolved: Olösta + updated_at: Uppdaterad view: Granska settings: activity_api_enabled: @@ -318,8 +354,8 @@ sv: back_to_account: Tillbaka till kontosidan batch: delete: Radera - nsfw_off: NSFW AV - nsfw_on: NSFW PÅ + nsfw_off: Markera som ej känslig + nsfw_on: Markera som känslig execute: Utför failed_to_execute: Misslyckades att utföra media: @@ -381,6 +417,7 @@ sv: security: Säkerhet set_new_password: Skriv in nytt lösenord authorize_follow: + already_following: Du följer redan detta konto error: Tyvärr inträffade ett fel när vi kontrollerade fjärrkontot follow: Följ follow_request: 'Du har skickat en följaförfrågan till:' @@ -473,6 +510,7 @@ sv: '21600': 6 timmar '3600': 1 timma '43200': 12 timmar + '604800': 1 vecka '86400': 1 dag expires_in_prompt: Aldrig generate: Skapa @@ -576,6 +614,10 @@ sv: missing_resource: Det gick inte att hitta den begärda omdirigeringsadressen för ditt konto proceed: Fortsätt för att följa prompt: 'Du kommer att följa:' + remote_unfollow: + error: Fel + title: Titel + unfollowed: Slutade följa sessions: activity: Senaste aktivitet browser: Webbläsare @@ -633,6 +675,18 @@ sv: two_factor_authentication: Tvåstegsautentisering your_apps: Dina applikationer statuses: + attached: + description: 'Bifogad: %{attached}' + image: + one: "%{count} bild" + other: "%{count} bilder" + video: + one: "%{count} video" + other: "%{count} videor" + content_warning: 'Innehållsvarning: %{warning}' + disallowed_hashtags: + one: 'innehöll en otillåten hashtag: %{tags}' + other: 'innehöll de otillåtna hashtagarna: %{tags}' open_in_web: Öppna på webben over_character_limit: teckengräns på %{max} har överskridits pin_errors: From 36b6631c1261f35ca22b9d2f9e99b72f83ed3492 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Thu, 26 Apr 2018 20:56:45 +0900 Subject: [PATCH 152/442] Add Basque language support (#7267) --- app/helpers/settings_helper.rb | 1 + config/application.rb | 1 + 2 files changed, 2 insertions(+) diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index a2f5917f9..2252aab2f 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -9,6 +9,7 @@ module SettingsHelper de: 'Deutsch', eo: 'Esperanto', es: 'Español', + eu: 'Euskara', fa: 'فارسی', gl: 'Galego', fi: 'Suomi', diff --git a/config/application.rb b/config/application.rb index e989e2333..ec3ff47a4 100644 --- a/config/application.rb +++ b/config/application.rb @@ -43,6 +43,7 @@ module Mastodon :de, :eo, :es, + :eu, :fa, :fi, :fr, From 63553c6b5c927950a45c5acb5af32af0dacee8c9 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 27 Apr 2018 01:37:59 +0200 Subject: [PATCH 153/442] Add support for separate Redis for cache (#7272) * Add support for separate Redis for cache CACHE_REDIS_URL to allow using a different Redis server for cache purposes, with cache-specific configuration such as key eviction * Fix code style issues --- config/environments/production.rb | 2 +- lib/mastodon/redis_config.rb | 27 ++++++++++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/config/environments/production.rb b/config/environments/production.rb index e742f668a..30239671c 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -52,7 +52,7 @@ Rails.application.configure do config.log_tags = [:request_id] # Use a different cache store in production. - config.cache_store = :redis_store, ENV['REDIS_URL'], REDIS_CACHE_PARAMS + config.cache_store = :redis_store, ENV['CACHE_REDIS_URL'], REDIS_CACHE_PARAMS # Ignore bad email addresses and do not raise email delivery errors. # Set this to true and configure the email server for immediate delivery to raise delivery errors. diff --git a/lib/mastodon/redis_config.rb b/lib/mastodon/redis_config.rb index cf4f20f76..f11d94a45 100644 --- a/lib/mastodon/redis_config.rb +++ b/lib/mastodon/redis_config.rb @@ -1,16 +1,29 @@ # frozen_string_literal: true -if ENV['REDIS_URL'].blank? - password = ENV.fetch('REDIS_PASSWORD') { '' } - host = ENV.fetch('REDIS_HOST') { 'localhost' } - port = ENV.fetch('REDIS_PORT') { 6379 } - db = ENV.fetch('REDIS_DB') { 0 } +def setup_redis_env_url(prefix = nil, defaults = true) + prefix = prefix.to_s.upcase + '_' unless prefix.nil? + prefix = '' if prefix.nil? - ENV['REDIS_URL'] = "redis://#{password.blank? ? '' : ":#{password}@"}#{host}:#{port}/#{db}" + return if ENV[prefix + 'REDIS_URL'].present? + + password = ENV.fetch(prefix + 'REDIS_PASSWORD') { '' if defaults } + host = ENV.fetch(prefix + 'REDIS_HOST') { 'localhost' if defaults } + port = ENV.fetch(prefix + 'REDIS_PORT') { 6379 if defaults } + db = ENV.fetch(prefix + 'REDIS_DB') { 0 if defaults } + + ENV[prefix + 'REDIS_URL'] = if [password, host, port, db].all?(&:nil?) + ENV['REDIS_URL'] + else + "redis://#{password.blank? ? '' : ":#{password}@"}#{host}:#{port}/#{db}" + end end -namespace = ENV.fetch('REDIS_NAMESPACE') { nil } +setup_redis_env_url +setup_redis_env_url(:cache, false) + +namespace = ENV.fetch('REDIS_NAMESPACE') { nil } cache_namespace = namespace ? namespace + '_cache' : 'cache' + REDIS_CACHE_PARAMS = { expires_in: 10.minutes, namespace: cache_namespace, From a872392cd958167d5d9dd3fef613415cc9068774 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 27 Apr 2018 01:38:10 +0200 Subject: [PATCH 154/442] Add entity cache (#7271) * Add entity cache Use a caching layer for mentions and custom emojis that are dynamically extracted from text. Reduce duplicate text extractions * Fix code style issue --- app/lib/entity_cache.rb | 34 ++++++++++++++++++++++++++++++++++ app/lib/formatter.rb | 10 +++------- app/models/account.rb | 2 +- app/models/custom_emoji.rb | 10 +++++++++- app/models/status.rb | 2 +- 5 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 app/lib/entity_cache.rb diff --git a/app/lib/entity_cache.rb b/app/lib/entity_cache.rb new file mode 100644 index 000000000..0c4edbfab --- /dev/null +++ b/app/lib/entity_cache.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +require 'singleton' + +class EntityCache + include Singleton + + MAX_EXPIRATION = 7.days.freeze + + def mention(username, domain) + Rails.cache.fetch(to_key(:mention, username, domain), expires_in: MAX_EXPIRATION) { Account.select(:username, :domain, :url).find_remote(username, domain) } + end + + def emoji(shortcodes, domain) + shortcodes = [shortcodes] unless shortcodes.is_a?(Array) + cached = Rails.cache.read_multi(*shortcodes.map { |shortcode| to_key(:emoji, shortcode, domain) }) + uncached_ids = [] + + shortcodes.each do |shortcode| + uncached_ids << shortcode unless cached.key?(to_key(:emoji, shortcode, domain)) + end + + unless uncached_ids.empty? + uncached = CustomEmoji.where(shortcode: shortcodes, domain: domain, disabled: false).select(:shortcode, :id, :image_file_name, :visible_in_picker).map { |item| [item.shortcode, item] }.to_h + uncached.each_value { |item| Rails.cache.write(to_key(:emoji, item.shortcode, domain), item, expires_in: MAX_EXPIRATION) } + end + + shortcodes.map { |shortcode| cached[to_key(:emoji, shortcode, domain)] || uncached[shortcode] }.compact + end + + def to_key(type, *ids) + "#{type}:#{ids.compact.map(&:downcase).join(':')}" + end +end diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index 4124f1660..050c651ee 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -52,12 +52,8 @@ class Formatter end def simplified_format(account, **options) - html = if account.local? - linkify(account.note) - else - reformat(account.note) - end - html = encode_custom_emojis(html, CustomEmoji.from_text(account.note, account.domain)) if options[:custom_emojify] + html = account.local? ? linkify(account.note) : reformat(account.note) + html = encode_custom_emojis(html, account.emojis) if options[:custom_emojify] html.html_safe # rubocop:disable Rails/OutputSafety end @@ -211,7 +207,7 @@ class Formatter username, domain = acct.split('@') domain = nil if TagManager.instance.local_domain?(domain) - account = Account.find_remote(username, domain) + account = EntityCache.instance.mention(username, domain) account ? mention_html(account) : "@#{acct}" end diff --git a/app/models/account.rb b/app/models/account.rb index ee47f04af..647b5c358 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -391,7 +391,7 @@ class Account < ApplicationRecord end def emojis - CustomEmoji.from_text(note, domain) + @emojis ||= CustomEmoji.from_text(note, domain) end before_create :generate_keys diff --git a/app/models/custom_emoji.rb b/app/models/custom_emoji.rb index 8235332f1..b99ed01f0 100644 --- a/app/models/custom_emoji.rb +++ b/app/models/custom_emoji.rb @@ -42,6 +42,8 @@ class CustomEmoji < ApplicationRecord include Attachmentable + after_commit :remove_entity_cache + def local? domain.nil? end @@ -58,11 +60,17 @@ class CustomEmoji < ApplicationRecord return [] if shortcodes.empty? - where(shortcode: shortcodes, domain: domain, disabled: false) + EntityCache.instance.emoji(shortcodes, domain) end def search(shortcode) where('"custom_emojis"."shortcode" ILIKE ?', "%#{shortcode}%") end end + + private + + def remove_entity_cache + Rails.cache.delete(EntityCache.instance.to_key(:emoji, shortcode, domain)) + end end diff --git a/app/models/status.rb b/app/models/status.rb index 37f2db562..fbb1f89aa 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -160,7 +160,7 @@ class Status < ApplicationRecord end def emojis - CustomEmoji.from_text([spoiler_text, text].join(' '), account.domain) + @emojis ||= CustomEmoji.from_text([spoiler_text, text].join(' '), account.domain) end after_create_commit :store_uri, if: :local? From fc01ae31c60f68c629f33816a7aee9c32307ba00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Miko=C5=82ajczak?= Date: Fri, 27 Apr 2018 15:12:14 +0200 Subject: [PATCH 155/442] =?UTF-8?q?=F0=9F=8C=8D:=20=F0=9F=87=B5?= =?UTF-8?q?=F0=9F=87=B1=E2=AC=86=EF=B8=8F=20(#7275)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Marcin Mikołajczak --- config/locales/pl.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 71bf6bf18..7455df2c0 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -697,6 +697,9 @@ pl: one: "%{count} film" other: "%{count} filmów" content_warning: 'Ostrzeżenie o zawartości: %{warning}' + disallowed_hashtags: + one: 'zawiera niedozwolony hashtag: %{tags}' + other: 'zawiera niedozwolone hashtagi: %{tags}' open_in_web: Otwórz w przeglądarce over_character_limit: limit %{max} znaków przekroczony pin_errors: @@ -798,6 +801,7 @@ pl:

    Bazowano na polityce prywatności Discourse.

    title: Zasady korzystania i polityka prywatności %{instance} themes: + contrast: Wysoki kontrast default: Mastodon time: formats: From 1c3ace23cbaa8590ab58ed0fd9d4d90ccb3d1eeb Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sat, 28 Apr 2018 18:20:30 +0900 Subject: [PATCH 156/442] Remove unnecessary hyphen from restore_cache key (#7276) --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index fbaf60aad..70d03f6b9 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,7 +28,7 @@ aliases: keys: - v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} - v2-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}- - - v2-ruby-dependencies-- + - v2-ruby-dependencies- - &install_steps steps: @@ -83,7 +83,7 @@ aliases: keys: - precompiled-assets-{{ .Branch }}-{{ .Revision }} - precompiled-assets-{{ .Branch }}- - - precompiled-assets-- + - precompiled-assets- - run: name: Prepare Tests From da61352fab2e59ba42caf59d4e2e33d62eebe060 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 30 Apr 2018 01:59:42 +0200 Subject: [PATCH 157/442] Fix "Show more" URL on paginated threads for remote statuses (#7285) * Fix URL of "Show more" link in paginated threads (ancestors side) Increase item limits in threads Fix #7268 * Fix "Show more" link in paginated threads (descendants side) --- app/controllers/statuses_controller.rb | 11 ++++++----- app/controllers/stream_entries_controller.rb | 1 + app/views/stream_entries/_status.html.haml | 20 +++++++++++--------- 3 files changed, 18 insertions(+), 14 deletions(-) diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index 01dac35e4..645995c2a 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -4,9 +4,9 @@ class StatusesController < ApplicationController include SignatureAuthentication include Authorization - ANCESTORS_LIMIT = 20 - DESCENDANTS_LIMIT = 20 - DESCENDANTS_DEPTH_LIMIT = 4 + ANCESTORS_LIMIT = 40 + DESCENDANTS_LIMIT = 60 + DESCENDANTS_DEPTH_LIMIT = 20 layout 'public' @@ -71,7 +71,7 @@ class StatusesController < ApplicationController end def set_descendants - @max_descendant_thread_id = params[:max_descendant_thread_id]&.to_i + @max_descendant_thread_id = params[:max_descendant_thread_id]&.to_i @since_descendant_thread_id = params[:since_descendant_thread_id]&.to_i descendants = cache_collection( @@ -84,11 +84,12 @@ class StatusesController < ApplicationController ), Status ) + @descendant_threads = [] if descendants.present? statuses = [descendants.first] - depth = 1 + depth = 1 descendants.drop(1).each_with_index do |descendant, index| if descendants[index].id == descendant.in_reply_to_id diff --git a/app/controllers/stream_entries_controller.rb b/app/controllers/stream_entries_controller.rb index 97cf85079..8568b151c 100644 --- a/app/controllers/stream_entries_controller.rb +++ b/app/controllers/stream_entries_controller.rb @@ -23,6 +23,7 @@ class StreamEntriesController < ApplicationController skip_session! expires_in 3.minutes, public: true end + render xml: OStatus::AtomSerializer.render(OStatus::AtomSerializer.new.entry(@stream_entry, true)) end end diff --git a/app/views/stream_entries/_status.html.haml b/app/views/stream_entries/_status.html.haml index 8decdf6b5..9764bc74d 100644 --- a/app/views/stream_entries/_status.html.haml +++ b/app/views/stream_entries/_status.html.haml @@ -5,18 +5,19 @@ is_successor ||= false direct_reply_id ||= false parent_id ||= false - is_direct_parent = direct_reply_id == status.id - is_direct_child = parent_id == status.in_reply_to_id - centered ||= include_threads && !is_predecessor && !is_successor - h_class = microformats_h_class(status, is_predecessor, is_successor, include_threads) - style_classes = style_classes(status, is_predecessor, is_successor, include_threads) - mf_classes = microformats_classes(status, is_direct_parent, is_direct_child) - entry_classes = h_class + ' ' + mf_classes + ' ' + style_classes + is_direct_parent = direct_reply_id == status.id + is_direct_child = parent_id == status.in_reply_to_id + centered ||= include_threads && !is_predecessor && !is_successor + h_class = microformats_h_class(status, is_predecessor, is_successor, include_threads) + style_classes = style_classes(status, is_predecessor, is_successor, include_threads) + mf_classes = microformats_classes(status, is_direct_parent, is_direct_child) + entry_classes = h_class + ' ' + mf_classes + ' ' + style_classes - if status.reply? && include_threads - if @next_ancestor .entry{ class: entry_classes } - = render 'stream_entries/more', url: short_account_status_url(@next_ancestor.account.username, @next_ancestor) + = render 'stream_entries/more', url: TagManager.instance.url_for(@next_ancestor) + = render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id } .entry{ class: entry_classes } @@ -44,9 +45,10 @@ = render 'stream_entries/more', url: short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1) - @descendant_threads.each do |thread| = render partial: 'stream_entries/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id } + - if thread[:next_status] .entry{ class: entry_classes } - = render 'stream_entries/more', url: short_account_status_url(thread[:next_status].account.username, thread[:next_status]) + = render 'stream_entries/more', url: TagManager.instance.url_for(thread[:next_status]) - if @next_descendant_thread .entry{ class: entry_classes } = render 'stream_entries/more', url: short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1) From 54f34d3f2ab4dbe3a4b572d60af18a4b49358ec6 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 30 Apr 2018 09:12:36 +0200 Subject: [PATCH 158/442] Return HTTP 410 for suspended accounts in GET /api/v1/accounts/:id (#7287) Fix #7243 --- app/controllers/api/v1/accounts_controller.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index d64325944..b7133ca8e 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -5,6 +5,7 @@ class Api::V1::AccountsController < Api::BaseController before_action -> { doorkeeper_authorize! :follow }, only: [:follow, :unfollow, :block, :unblock, :mute, :unmute] before_action :require_user!, except: [:show] before_action :set_account + before_action :check_account_suspension, only: [:show] respond_to :json @@ -54,4 +55,8 @@ class Api::V1::AccountsController < Api::BaseController def relationships(**options) AccountRelationshipsPresenter.new([@account.id], current_user.account_id, options) end + + def check_account_suspension + gone if @account.suspended? + end end From 295e3ef02bb3fcdd4d8992ad6105c0ada2b3db0c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 30 Apr 2018 09:12:55 +0200 Subject: [PATCH 159/442] Fix missing domain attribute in EntityCache for emoji (#7290) --- app/lib/entity_cache.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/entity_cache.rb b/app/lib/entity_cache.rb index 0c4edbfab..03bfb7cf0 100644 --- a/app/lib/entity_cache.rb +++ b/app/lib/entity_cache.rb @@ -21,7 +21,7 @@ class EntityCache end unless uncached_ids.empty? - uncached = CustomEmoji.where(shortcode: shortcodes, domain: domain, disabled: false).select(:shortcode, :id, :image_file_name, :visible_in_picker).map { |item| [item.shortcode, item] }.to_h + uncached = CustomEmoji.where(shortcode: shortcodes, domain: domain, disabled: false).select(:shortcode, :id, :domain, :image_file_name, :visible_in_picker).map { |item| [item.shortcode, item] }.to_h uncached.each_value { |item| Rails.cache.write(to_key(:emoji, item.shortcode, domain), item, expires_in: MAX_EXPIRATION) } end From f62ee1ddb0364d749e9df5559a243ebe3570cd2a Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 30 Apr 2018 09:13:14 +0200 Subject: [PATCH 160/442] Disable API access when login is disabled (#7289) --- app/controllers/api/base_controller.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/base_controller.rb b/app/controllers/api/base_controller.rb index 7b5168b31..b5c084e14 100644 --- a/app/controllers/api/base_controller.rb +++ b/app/controllers/api/base_controller.rb @@ -66,8 +66,10 @@ class Api::BaseController < ApplicationController end def require_user! - if current_user + if current_user && !current_user.disabled? set_user_activity + elsif current_user + render json: { error: 'Your login is currently disabled' }, status: 403 else render json: { error: 'This method requires an authenticated user' }, status: 422 end From 16468bdf1bf04ecab18ebd89dcded35ad0899439 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Mon, 30 Apr 2018 20:14:50 +0900 Subject: [PATCH 161/442] [i18n] Occitan update (#7294) * Translated new strings + few changes * First update * Almost complet Missing the changes in the privacy policy * Update simple_form.oc.yml * bundle exec i18n-tasks normalize * bundle exec i18n-tasks remove-unused --- app/javascript/mastodon/locales/oc.json | 38 +++++++++++----------- config/locales/oc.yml | 43 +++++++++++++++++++++++-- config/locales/simple_form.oc.yml | 6 ++++ 3 files changed, 66 insertions(+), 21 deletions(-) diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 66f3fa275..d4836e9fe 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -18,7 +18,7 @@ "account.mute_notifications": "Rescondre las notificacions de @{name}", "account.muted": "Mes en silenci", "account.posts": "Tuts", - "account.posts_with_replies": "Tuts amb responsas", + "account.posts_with_replies": "Tuts e responsas", "account.report": "Senhalar @{name}", "account.requested": "Invitacion mandada. Clicatz per anullar", "account.share": "Partejar lo perfil a @{name}", @@ -29,8 +29,8 @@ "account.unmute": "Quitar de rescondre @{name}", "account.unmute_notifications": "Mostrar las notificacions de @{name}", "account.view_full_profile": "Veire lo perfil complèt", - "alert.unexpected.message": "An unexpected error occurred.", - "alert.unexpected.title": "Oops!", + "alert.unexpected.message": "Una error s’es producha.", + "alert.unexpected.title": "Ops !", "boost_modal.combo": "Podètz botar {combo} per passar aquò lo còp que ven", "bundle_column_error.body": "Quicòm a fach mèuca pendent lo cargament d’aqueste compausant.", "bundle_column_error.retry": "Tornar ensajar", @@ -40,8 +40,8 @@ "bundle_modal_error.retry": "Tornar ensajar", "column.blocks": "Personas blocadas", "column.community": "Flux public local", - "column.direct": "Direct messages", - "column.domain_blocks": "Hidden domains", + "column.direct": "Messatges dirèctes", + "column.domain_blocks": "Domenis blocats", "column.favourites": "Favorits", "column.follow_requests": "Demandas d’abonament", "column.home": "Acuèlh", @@ -59,7 +59,7 @@ "column_header.unpin": "Despenjar", "column_subheading.navigation": "Navigacion", "column_subheading.settings": "Paramètres", - "compose_form.direct_message_warning": "This toot will only be visible to all the mentioned users.", + "compose_form.direct_message_warning": "Aqueste tut serà pas que visibile pel monde mencionat.", "compose_form.hashtag_warning": "Aqueste tut serà pas ligat a cap etiqueta estant qu’es pas listat. Òm pas cercar que los tuts publics per etiqueta.", "compose_form.lock_disclaimer": "Vòstre compte es pas {locked}. Tot lo mond pòt vos sègre e veire los estatuts reservats als seguidors.", "compose_form.lock_disclaimer.lock": "clavat", @@ -73,13 +73,13 @@ "compose_form.spoiler_placeholder": "Escrivètz l’avertiment aquí", "confirmation_modal.cancel": "Anullar", "confirmations.block.confirm": "Blocar", - "confirmations.block.message": "Sètz segur de voler blocar {name} ?", + "confirmations.block.message": "Volètz vertadièrament blocar {name} ?", "confirmations.delete.confirm": "Escafar", - "confirmations.delete.message": "Sètz segur de voler escafar l’estatut ?", + "confirmations.delete.message": "Volètz vertadièrament escafar l’estatut ?", "confirmations.delete_list.confirm": "Suprimir", - "confirmations.delete_list.message": "Sètz segur de voler suprimir aquesta lista per totjorn ?", + "confirmations.delete_list.message": "Volètz vertadièrament suprimir aquesta lista per totjorn ?", "confirmations.domain_block.confirm": "Amagar tot lo domeni", - "confirmations.domain_block.message": "Sètz segur segur de voler blocar complètament {domain} ? De còps cal pas que blocar o rescondre unas personas solament.", + "confirmations.domain_block.message": "Volètz vertadièrament blocar complètament {domain} ? De còps cal pas que blocar o rescondre unas personas solament.", "confirmations.mute.confirm": "Rescondre", "confirmations.mute.message": "Sètz segur de voler rescondre {name} ?", "confirmations.unfollow.confirm": "Quitar de sègre", @@ -101,7 +101,7 @@ "emoji_button.symbols": "Simbòls", "emoji_button.travel": "Viatges & lòcs", "empty_column.community": "Lo flux public local es void. Escrivètz quicòm per lo garnir !", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "Avètz pas encara de messatges. Quand ne mandatz un o que ne recebètz un, serà mostrat aquí.", "empty_column.hashtag": "I a pas encara de contengut ligat a aquesta etiqueta.", "empty_column.home": "Vòstre flux d’acuèlh es void. Visitatz {public} o utilizatz la recèrca per vos connectar a d’autras personas.", "empty_column.home.public_timeline": "lo flux public", @@ -135,7 +135,7 @@ "keyboard_shortcuts.mention": "mencionar l’autor", "keyboard_shortcuts.reply": "respondre", "keyboard_shortcuts.search": "anar a la recèrca", - "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", + "keyboard_shortcuts.toggle_hidden": "mostrar/amagar lo tèxte dels avertiments", "keyboard_shortcuts.toot": "començar un estatut tot novèl", "keyboard_shortcuts.unfocus": "quitar lo camp tèxte/de recèrca", "keyboard_shortcuts.up": "far montar dins la lista", @@ -157,7 +157,7 @@ "mute_modal.hide_notifications": "Rescondre las notificacions d’aquesta persona ?", "navigation_bar.blocks": "Personas blocadas", "navigation_bar.community_timeline": "Flux public local", - "navigation_bar.direct": "Direct messages", + "navigation_bar.direct": "Messatges dirèctes", "navigation_bar.domain_blocks": "Hidden domains", "navigation_bar.edit_profile": "Modificar lo perfil", "navigation_bar.favourites": "Favorits", @@ -217,7 +217,7 @@ "privacy.unlisted.short": "Pas-listat", "regeneration_indicator.label": "Cargament…", "regeneration_indicator.sublabel": "Sèm a preparar vòstre flux d’acuèlh !", - "relative_time.days": "fa {number} d", + "relative_time.days": "fa {number}d", "relative_time.hours": "fa {number}h", "relative_time.just_now": "ara", "relative_time.minutes": "fa {number} min", @@ -236,16 +236,16 @@ "search_popout.tips.status": "estatut", "search_popout.tips.text": "Lo tèxt brut tòrna escais, noms d’utilizaire e etiquetas correspondents", "search_popout.tips.user": "utilizaire", - "search_results.accounts": "Monde", + "search_results.accounts": "Gents", "search_results.hashtags": "Etiquetas", "search_results.statuses": "Tuts", "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}", "standalone.public_title": "Una ulhada dedins…", "status.block": "Blocar @{name}", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "Quitar de partejar", "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat", "status.delete": "Escafar", - "status.direct": "Direct message @{name}", + "status.direct": "Messatge per @{name}", "status.embed": "Embarcar", "status.favourite": "Apondre als favorits", "status.load_more": "Cargar mai", @@ -258,7 +258,7 @@ "status.pin": "Penjar al perfil", "status.pinned": "Tut penjat", "status.reblog": "Partejar", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "Partejar al l’audiéncia d’origina", "status.reblogged_by": "{name} a partejat", "status.reply": "Respondre", "status.replyAll": "Respondre a la conversacion", @@ -276,7 +276,7 @@ "tabs_bar.home": "Acuèlh", "tabs_bar.local_timeline": "Flux public local", "tabs_bar.notifications": "Notificacions", - "tabs_bar.search": "Search", + "tabs_bar.search": "Recèrcas", "ui.beforeunload": "Vòstre brolhon serà perdut se quitatz Mastodon.", "upload_area.title": "Lisatz e depausatz per mandar", "upload_button.label": "Ajustar un mèdia", diff --git a/config/locales/oc.yml b/config/locales/oc.yml index 195a1d909..c248ffd85 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -4,6 +4,7 @@ oc: about_hashtag_html: Vaquí los estatuts publics ligats a #%{hashtag}. Podètz interagir amb eles s’avètz un compte ont que siasque sul fediverse. about_mastodon_html: Mastodon es un malhum social bastit amb de protocòls liures e gratuits. Es descentralizat coma los corrièls. about_this: A prepaus d’aquesta instància + administered_by: 'Gerida per :' closed_registrations: Las inscripcions son clavadas pel moment sus aquesta instància. contact: Contacte contact_missing: Pas parametrat @@ -60,7 +61,15 @@ oc: destroyed_msg: Nòta de moderacion ben suprimida ! accounts: are_you_sure: Sètz segur ? + avatar: Avatar by_domain: Domeni + change_email: + changed_msg: Adreça corrèctament cambiada ! + current_email: Adreça actuala + label: Cambiar d’adreça + new_email: Novèla adreça + submit: Cambiar + title: Cambiar l’adreça a %{username} confirm: Confirmar confirmed: Confirmat demote: Retrogradar @@ -108,6 +117,7 @@ oc: public: Public push_subscription_expires: Fin de l’abonament PuSH redownload: Actualizar los avatars + remove_avatar: Supriir l’avatar reset: Reïnicializar reset_password: Reïnicializar lo senhal resubscribe: Se tornar abonar @@ -128,6 +138,7 @@ oc: statuses: Estatuts subscribe: S’abonar title: Comptes + unconfirmed_email: Adreça pas confirmada undo_silenced: Levar lo silenci undo_suspension: Levar la suspension unsubscribe: Se desabonar @@ -135,6 +146,8 @@ oc: web: Web action_logs: actions: + assigned_to_self_report: "%{name} s’assignèt lo rapòrt %{target}" + change_email_user: "%{name} cambièt l’adreça de corrièl de %{target}" confirm_user: "%{name} confirmèt l’adreça a %{target}" create_custom_emoji: "%{name} mandèt un nòu emoji %{target}" create_domain_block: "%{name} bloquèt lo domeni %{target}" @@ -150,6 +163,7 @@ oc: enable_user: "%{name} activèt la connexion per %{target}" memorialize_account: "%{name} transformèt en memorial la pagina de perfil a %{target}" promote_user: "%{name} promoguèt %{target}" + remove_avatar_user: "%{name} suprimèt l’avatar a %{target}" reset_password_user: "%{name} reïnicializèt lo senhal a %{target}" resolve_report: "%{name} anullèt lo rapòrt de %{target}" silence_account: "%{name} metèt en silenci lo compte a %{target}" @@ -239,17 +253,31 @@ oc: expired: Expirats title: Filtre title: Convits + report_notes: + created_msg: Nòta de moderacion corrèctament creada ! + destroyed_msg: Nòta de moderacion corrèctament suprimida ! reports: + account: + note: nòta + report: rapòrt action_taken_by: Mesura menada per are_you_sure: Es segur ? comment: none: Pas cap + created_at: Creacion delete: Suprimir id: ID - mark_as_resolved: Marcat coma resolgut + mark_as_resolved: Marcar coma resolgut + mark_as_unresolved: Marcar coma pas resolgut + notes: + create: Ajustar una nòta + create_and_resolve: Resòlvre amb una nòta + create_and_unresolve: Tornar dobrir amb una nòta + placeholder: Explicatz las accions que son estadas menadas o çò qu’es estat fach per aqueste rapòrt… nsfw: 'false': Sens contengut sensible 'true': Contengut sensible activat + reopen: Tornar dobrir lo rapòrt report: 'senhalament #%{id}' report_contents: Contenguts reported_account: Compte senhalat @@ -381,6 +409,7 @@ oc: security: Seguretat set_new_password: Picar un nòu senhal authorize_follow: + already_following: Seguètz ja aqueste compte error: O planhèm, i a agut una error al moment de cercar lo compte follow: Sègre follow_request: 'Avètz demandat de sègre :' @@ -551,6 +580,7 @@ oc: '21600': 6 oras '3600': 1 ora '43200': 12 oras + '604800': 1 setmana '86400': 1 jorn expires_in_prompt: Jamai generate: Generar @@ -652,8 +682,12 @@ oc: remote_follow: acct: Picatz vòstre utilizaire@instància que cal utilizar per sègre aqueste utilizaire missing_resource: URL de redireccion pas trobada - proceed: Contunhatz per sègre + proceed: Clicatz per sègre prompt: 'Sètz per sègre :' + remote_unfollow: + error: Error + title: Títol + unfollowed: Pas mai seguit sessions: activity: Darrièra activitat browser: Navigator @@ -719,6 +753,9 @@ oc: video: one: "%{count} vidèo" other: "%{count} vidèos" + disallowed_hashtags: + one: 'conten una etiqueta desactivada : %{tags}' + other: 'conten las etiquetas desactivadas : %{tags}' open_in_web: Dobrir sul web over_character_limit: limit de %{max} caractèrs passat pin_errors: @@ -742,6 +779,8 @@ oc: sensitive_content: Contengut sensible terms: title: Condicions d’utilizacion e politica de confidencialitat de %{instance} + themes: + contrast: Fòrt contrast time: formats: default: Lo %d %b de %Y a %Ho%M diff --git a/config/locales/simple_form.oc.yml b/config/locales/simple_form.oc.yml index 690d1de20..4ca58c102 100644 --- a/config/locales/simple_form.oc.yml +++ b/config/locales/simple_form.oc.yml @@ -8,6 +8,7 @@ oc: display_name: one: Demòra encara 1 caractèr other: Demòran encara %{count} caractèrs + fields: Podètz far veire cap a 4 elements sus vòstre perfil header: PNG, GIF o JPG. Maximum 2 Mo. Serà retalhada en 700x335px locked: Demanda qu’acceptetz manualament lo mond que vos sègon e botarà la visibilitat de vòstras publicacions coma accessiblas a vòstres seguidors solament note: @@ -22,6 +23,10 @@ oc: user: filtered_languages: Las lengas seleccionadas seràn levadas de vòstre flux d’actualitat labels: + account: + fields: + name: Nom + value: Contengut defaults: avatar: Avatar confirm_new_password: Confirmacion del nòu senhal @@ -31,6 +36,7 @@ oc: display_name: Escais email: Corrièl expires_in: Expira aprèp + fields: Metadonada del perfil filtered_languages: Lengas filtradas header: Bandièra locale: Lenga From 705f1d7bf15b7dc46256ab4a3bfff4075c79a8e7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 30 Apr 2018 22:49:33 +0200 Subject: [PATCH 162/442] Fix missing updated_at attribute on emoji EntityCache (#7297) Just don't try to save space by only selecting few attributes. If anyone is wondering, this is needed because the emoji entity cache is not really only used for entities, it's accessed again to generate Emoji tags in ActivityPub/OStatus, so a lot more properties are used than what is needed in HTML alone... --- app/lib/entity_cache.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/lib/entity_cache.rb b/app/lib/entity_cache.rb index 03bfb7cf0..2aa37389c 100644 --- a/app/lib/entity_cache.rb +++ b/app/lib/entity_cache.rb @@ -21,7 +21,7 @@ class EntityCache end unless uncached_ids.empty? - uncached = CustomEmoji.where(shortcode: shortcodes, domain: domain, disabled: false).select(:shortcode, :id, :domain, :image_file_name, :visible_in_picker).map { |item| [item.shortcode, item] }.to_h + uncached = CustomEmoji.where(shortcode: shortcodes, domain: domain, disabled: false).map { |item| [item.shortcode, item] }.to_h uncached.each_value { |item| Rails.cache.write(to_key(:emoji, item.shortcode, domain), item, expires_in: MAX_EXPIRATION) } end From 86efccce2a874d16aa783d989ff4824bcfac40b5 Mon Sep 17 00:00:00 2001 From: Lynx Kotoura Date: Tue, 1 May 2018 21:02:04 +0900 Subject: [PATCH 163/442] Fix low-contrasted cancel button of reply indicator (#7300) --- .../mastodon/features/compose/components/reply_indicator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/features/compose/components/reply_indicator.js b/app/javascript/mastodon/features/compose/components/reply_indicator.js index d8cda96f3..5b4b81eac 100644 --- a/app/javascript/mastodon/features/compose/components/reply_indicator.js +++ b/app/javascript/mastodon/features/compose/components/reply_indicator.js @@ -51,7 +51,7 @@ export default class ReplyIndicator extends ImmutablePureComponent { return (
    -
    +
    From dc786c0cf4467ade8db7d8b17e09f16923bfc1e8 Mon Sep 17 00:00:00 2001 From: Surinna Curtis Date: Wed, 2 May 2018 05:40:24 -0500 Subject: [PATCH 164/442] Support Actors/Statuses with multiple types (#7305) * Add equals_or_includes_any? helper in JsonLdHelper * Support arrays in JSON-LD type fields for actors/tags/objects. * Spec for resolving accounts with extension types * Style tweaks for codeclimate --- app/helpers/jsonld_helper.rb | 4 ++++ app/lib/activitypub/activity/create.rb | 11 +++++------ app/lib/activitypub/activity/update.rb | 5 +---- .../activitypub/fetch_remote_account_service.rb | 2 +- .../activitypub/fetch_remote_key_service.rb | 4 ++-- .../activitypub/fetch_remote_status_service.rb | 2 +- .../activitypub/process_account_service.rb | 5 +---- app/services/fetch_atom_service.rb | 4 ++-- app/services/resolve_account_service.rb | 2 +- app/services/resolve_url_service.rb | 5 ++--- .../requests/activitypub-actor-individual.txt | 9 +++++++++ spec/services/resolve_account_service_spec.rb | 14 ++++++++++++++ 12 files changed, 43 insertions(+), 24 deletions(-) create mode 100644 spec/fixtures/requests/activitypub-actor-individual.txt diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index dfb8fcb8b..a3cfdadb8 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -5,6 +5,10 @@ module JsonLdHelper haystack.is_a?(Array) ? haystack.include?(needle) : haystack == needle end + def equals_or_includes_any?(haystack, needles) + needles.any? { |needle| equals_or_includes?(haystack, needle) } + end + def first_of_value(value) value.is_a?(Array) ? value.first : value end diff --git a/app/lib/activitypub/activity/create.rb b/app/lib/activitypub/activity/create.rb index 45c0e91cb..411286fa5 100644 --- a/app/lib/activitypub/activity/create.rb +++ b/app/lib/activitypub/activity/create.rb @@ -61,12 +61,11 @@ class ActivityPub::Activity::Create < ActivityPub::Activity return if @object['tag'].nil? as_array(@object['tag']).each do |tag| - case tag['type'] - when 'Hashtag' + if equals_or_includes?(tag['type'], 'Hashtag') process_hashtag tag, status - when 'Mention' + elsif equals_or_includes?(tag['type'], 'Mention') process_mention tag, status - when 'Emoji' + elsif equals_or_includes?(tag['type'], 'Emoji') process_emoji tag, status end end @@ -235,11 +234,11 @@ class ActivityPub::Activity::Create < ActivityPub::Activity end def supported_object_type? - SUPPORTED_TYPES.include?(@object['type']) + equals_or_includes_any?(@object['type'], SUPPORTED_TYPES) end def converted_object_type? - CONVERTED_TYPES.include?(@object['type']) + equals_or_includes_any?(@object['type'], CONVERTED_TYPES) end def skip_download? diff --git a/app/lib/activitypub/activity/update.rb b/app/lib/activitypub/activity/update.rb index 0134b4015..47e98e041 100644 --- a/app/lib/activitypub/activity/update.rb +++ b/app/lib/activitypub/activity/update.rb @@ -2,10 +2,7 @@ class ActivityPub::Activity::Update < ActivityPub::Activity def perform - case @object['type'] - when 'Person' - update_account - end + update_account if equals_or_includes?(@object['type'], 'Person') end private diff --git a/app/services/activitypub/fetch_remote_account_service.rb b/app/services/activitypub/fetch_remote_account_service.rb index 5024853ca..867e70876 100644 --- a/app/services/activitypub/fetch_remote_account_service.rb +++ b/app/services/activitypub/fetch_remote_account_service.rb @@ -56,6 +56,6 @@ class ActivityPub::FetchRemoteAccountService < BaseService end def expected_type? - SUPPORTED_TYPES.include?(@json['type']) + equals_or_includes_any?(@json['type'], SUPPORTED_TYPES) end end diff --git a/app/services/activitypub/fetch_remote_key_service.rb b/app/services/activitypub/fetch_remote_key_service.rb index 41837d462..505baccd4 100644 --- a/app/services/activitypub/fetch_remote_key_service.rb +++ b/app/services/activitypub/fetch_remote_key_service.rb @@ -43,7 +43,7 @@ class ActivityPub::FetchRemoteKeyService < BaseService end def person? - ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(@json['type']) + equals_or_includes_any?(@json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) end def public_key? @@ -55,6 +55,6 @@ class ActivityPub::FetchRemoteKeyService < BaseService end def confirmed_owner? - ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(@owner['type']) && value_or_id(@owner['publicKey']) == @json['id'] + equals_or_includes_any?(@owner['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) && value_or_id(@owner['publicKey']) == @json['id'] end end diff --git a/app/services/activitypub/fetch_remote_status_service.rb b/app/services/activitypub/fetch_remote_status_service.rb index 503c175d8..930fbad1f 100644 --- a/app/services/activitypub/fetch_remote_status_service.rb +++ b/app/services/activitypub/fetch_remote_status_service.rb @@ -42,7 +42,7 @@ class ActivityPub::FetchRemoteStatusService < BaseService end def expected_type? - (ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES).include? @json['type'] + equals_or_includes_any?(@json['type'], ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES) end def needs_update(actor) diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index da32f9615..f67ebb443 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -201,10 +201,7 @@ class ActivityPub::ProcessAccountService < BaseService return if @json['tag'].blank? as_array(@json['tag']).each do |tag| - case tag['type'] - when 'Emoji' - process_emoji tag - end + process_emoji tag if equals_or_includes?(tag['type'], 'Emoji') end end diff --git a/app/services/fetch_atom_service.rb b/app/services/fetch_atom_service.rb index 0444baf74..550e75f33 100644 --- a/app/services/fetch_atom_service.rb +++ b/app/services/fetch_atom_service.rb @@ -42,7 +42,7 @@ class FetchAtomService < BaseService elsif ['application/activity+json', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"'].include?(response.mime_type) body = response.body_with_limit json = body_to_json(body) - if supported_context?(json) && ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(json['type']) && json['inbox'].present? + if supported_context?(json) && equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) && json['inbox'].present? [json['id'], { prefetched_body: body, id: true }, :activitypub] elsif supported_context?(json) && expected_type?(json) [json['id'], { prefetched_body: body, id: true }, :activitypub] @@ -62,7 +62,7 @@ class FetchAtomService < BaseService end def expected_type?(json) - (ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES).include? json['type'] + equals_or_includes_any?(json['type'], ActivityPub::Activity::Create::SUPPORTED_TYPES + ActivityPub::Activity::Create::CONVERTED_TYPES) end def process_html(response) diff --git a/app/services/resolve_account_service.rb b/app/services/resolve_account_service.rb index 8cba88f01..de8d1151d 100644 --- a/app/services/resolve_account_service.rb +++ b/app/services/resolve_account_service.rb @@ -189,7 +189,7 @@ class ResolveAccountService < BaseService return @actor_json if defined?(@actor_json) json = fetch_resource(actor_url, false) - @actor_json = supported_context?(json) && ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES.include?(json['type']) ? json : nil + @actor_json = supported_context?(json) && equals_or_includes_any?(json['type'], ActivityPub::FetchRemoteAccountService::SUPPORTED_TYPES) ? json : nil end def atom diff --git a/app/services/resolve_url_service.rb b/app/services/resolve_url_service.rb index c19b568cb..a068c1ed8 100644 --- a/app/services/resolve_url_service.rb +++ b/app/services/resolve_url_service.rb @@ -16,10 +16,9 @@ class ResolveURLService < BaseService private def process_url - case type - when 'Application', 'Group', 'Organization', 'Person', 'Service' + if equals_or_includes_any?(type, %w(Application Group Organization Person Service)) FetchRemoteAccountService.new.call(atom_url, body, protocol) - when 'Note', 'Article', 'Image', 'Video' + elsif equals_or_includes_any?(type, %w(Note Article Image Video)) FetchRemoteStatusService.new.call(atom_url, body, protocol) end end diff --git a/spec/fixtures/requests/activitypub-actor-individual.txt b/spec/fixtures/requests/activitypub-actor-individual.txt new file mode 100644 index 000000000..74411e544 --- /dev/null +++ b/spec/fixtures/requests/activitypub-actor-individual.txt @@ -0,0 +1,9 @@ +HTTP/1.1 200 OK +Cache-Control: max-age=0, private, must-revalidate +Content-Type: application/activity+json; charset=utf-8 +Link: ; rel="lrdd"; type="application/xrd+xml", ; rel="alternate"; type="application/atom+xml", ; rel="alternate"; type="application/activity+json" +Vary: Accept-Encoding +X-Content-Type-Options: nosniff +X-Xss-Protection: 1; mode=block + +{"@context":["https://www.w3.org/ns/activitystreams","https://w3id.org/security/v1",{"vcard": "http://www.w3.org/2006/vcard/ns#"},{"manuallyApprovesFollowers":"as:manuallyApprovesFollowers","sensitive":"as:sensitive","Hashtag":"as:Hashtag","ostatus":"http://ostatus.org#","atomUri":"ostatus:atomUri","inReplyToAtomUri":"ostatus:inReplyToAtomUri","conversation":"ostatus:conversation"}],"id":"https://ap.example.com/users/foo","type":["Person","vcard:individual"],"following":"https://ap.example.com/users/foo/following","followers":"https://ap.example.com/users/foo/followers","inbox":"https://ap.example.com/users/foo/inbox","outbox":"https://ap.example.com/users/foo/outbox","preferredUsername":"foo","vcard:fn":"foo","name":"","summary":"\u003cp\u003etest\u003c/p\u003e","url":"https://ap.example.com/@foo","manuallyApprovesFollowers":false,"publicKey":{"id":"https://ap.example.com/users/foo#main-key","owner":"https://ap.example.com/users/foo","publicKeyPem":"-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAu3L4vnpNLzVH31MeWI39\n4F0wKeJFsLDAsNXGeOu0QF2x+h1zLWZw/agqD2R3JPU9/kaDJGPIV2Sn5zLyUA9S\n6swCCMOtn7BBR9g9sucgXJmUFB0tACH2QSgHywMAybGfmSb3LsEMNKsGJ9VsvYoh\n8lDET6X4Pyw+ZJU0/OLo/41q9w+OrGtlsTm/PuPIeXnxa6BLqnDaxC+4IcjG/FiP\nahNCTINl/1F/TgSSDZ4Taf4U9XFEIFw8wmgploELozzIzKq+t8nhQYkgAkt64euW\npva3qL5KD1mTIZQEP+LZvh3s2WHrLi3fhbdRuwQ2c0KkJA2oSTFPDpqqbPGZ3Qvu\nHQIDAQAB\n-----END PUBLIC KEY-----\n"},"endpoints":{"sharedInbox":"https://ap.example.com/inbox"},"icon":{"type":"Image","url":"https://quitter.no/avatar/7477-300-20160211190340.png"}} \ No newline at end of file diff --git a/spec/services/resolve_account_service_spec.rb b/spec/services/resolve_account_service_spec.rb index 5f1b4467b..84dfe578a 100644 --- a/spec/services/resolve_account_service_spec.rb +++ b/spec/services/resolve_account_service_spec.rb @@ -105,6 +105,20 @@ RSpec.describe ResolveAccountService do expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' end + context 'with multiple types' do + before do + stub_request(:get, "https://ap.example.com/users/foo").to_return(request_fixture('activitypub-actor-individual.txt')) + end + + it 'returns new remote account' do + account = subject.call('foo@ap.example.com') + + expect(account.activitypub?).to eq true + expect(account.domain).to eq 'ap.example.com' + expect(account.inbox_url).to eq 'https://ap.example.com/users/foo/inbox' + end + end + pending end From 6c40e567aaa166eb618c316d1de2eb81b8eced9a Mon Sep 17 00:00:00 2001 From: Shuhei Kitagawa Date: Wed, 2 May 2018 21:13:52 +0900 Subject: [PATCH 165/442] Add missing tests for user.rb (#7306) --- dump.rdb | Bin 0 -> 851 bytes spec/models/user_spec.rb | 214 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+) create mode 100644 dump.rdb diff --git a/dump.rdb b/dump.rdb new file mode 100644 index 0000000000000000000000000000000000000000..ade9c6abc4f911663879c263bd869f94e8649256 GIT binary patch literal 851 zcmbVKId2m|6dpT1$ib05hSD#DuR$1?>>`NW_RqFSww`8 z=umzL1r6dSSV8n@>1fkYn}Au{N(kTviW%w6`{wC8-q!ls#zR7gH<%R=M$&i=qG(k~ zwK15RJhUSv`^`ZyP*DsAH{Tv@56-0$LL-Lw07Z*vut0go=n9~5n?WD_K;P!_=dZ~q zgx#oT$Y2M;l!5RQc%fRODROlcA%wG&M`$S(l;r{NqMrT5xMYvG^27K*(z z0#)o#CZP}|9saR-*!)^~iFdeE35@~!cnY7&FG5(ySsFq?*rE#iHQTh!x_i}f+^Xd` zX5BJ%+>Js!A+BRZpJ@-uapoKkd7|B^1Uv~+As`9%uy5;lk|g#Y<4KlCY1d~)kv%OT z)=VzOqva72KhZIxZD37Zn=fT6o2W=>>t8N^c)edf@ke_%({S*sLK~=B98+^@4za6V X-6f`5Xs2?okA_>H?`?el#P@#!$ioff literal 0 HcmV?d00001 diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 8171c939a..760214ded 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -324,4 +324,218 @@ RSpec.describe User, type: :model do expect(admin.role?('moderator')).to be true end end + + describe '#disable!' do + subject(:user) { Fabricate(:user, disabled: false, current_sign_in_at: current_sign_in_at, last_sign_in_at: nil) } + let(:current_sign_in_at) { Time.zone.now } + + before do + user.disable! + end + + it 'disables user' do + expect(user).to have_attributes(disabled: true, current_sign_in_at: nil, last_sign_in_at: current_sign_in_at) + end + end + + describe '#disable!' do + subject(:user) { Fabricate(:user, disabled: false, current_sign_in_at: current_sign_in_at, last_sign_in_at: nil) } + let(:current_sign_in_at) { Time.zone.now } + + before do + user.disable! + end + + it 'disables user' do + expect(user).to have_attributes(disabled: true, current_sign_in_at: nil, last_sign_in_at: current_sign_in_at) + end + end + + describe '#enable!' do + subject(:user) { Fabricate(:user, disabled: true) } + + before do + user.enable! + end + + it 'enables user' do + expect(user).to have_attributes(disabled: false) + end + end + + describe '#confirm!' do + subject(:user) { Fabricate(:user, confirmed_at: confirmed_at) } + + before do + ActionMailer::Base.deliveries.clear + user.confirm! + end + + after { ActionMailer::Base.deliveries.clear } + + context 'when user is new' do + let(:confirmed_at) { nil } + + it 'confirms user' do + expect(user.confirmed_at).to be_present + end + + it 'delivers mails' do + expect(ActionMailer::Base.deliveries.count).to eq 2 + end + end + + context 'when user is not new' do + let(:confirmed_at) { Time.zone.now } + + it 'confirms user' do + expect(user.confirmed_at).to be_present + end + + it 'does not deliver mail' do + expect(ActionMailer::Base.deliveries.count).to eq 0 + end + end + end + + describe '#promote!' do + subject(:user) { Fabricate(:user, admin: is_admin, moderator: is_moderator) } + + before do + user.promote! + end + + context 'when user is an admin' do + let(:is_admin) { true } + + context 'when user is a moderator' do + let(:is_moderator) { true } + + it 'changes moderator filed false' do + expect(user).to be_admin + expect(user).not_to be_moderator + end + end + + context 'when user is not a moderator' do + let(:is_moderator) { false } + + it 'does not change status' do + expect(user).to be_admin + expect(user).not_to be_moderator + end + end + end + + context 'when user is not admin' do + let(:is_admin) { false } + + context 'when user is a moderator' do + let(:is_moderator) { true } + + it 'changes user into an admin' do + expect(user).to be_admin + expect(user).not_to be_moderator + end + end + + context 'when user is not a moderator' do + let(:is_moderator) { false } + + it 'changes user into a moderator' do + expect(user).not_to be_admin + expect(user).to be_moderator + end + end + end + end + + describe '#demote!' do + subject(:user) { Fabricate(:user, admin: admin, moderator: moderator) } + + before do + user.demote! + end + + context 'when user is an admin' do + let(:admin) { true } + + context 'when user is a moderator' do + let(:moderator) { true } + + it 'changes user into a moderator' do + expect(user).not_to be_admin + expect(user).to be_moderator + end + end + + context 'when user is not a moderator' do + let(:moderator) { false } + + it 'changes user into a moderator' do + expect(user).not_to be_admin + expect(user).to be_moderator + end + end + end + + context 'when user is not an admin' do + let(:admin) { false } + + context 'when user is a moderator' do + let(:moderator) { true } + + it 'changes user into a plain user' do + expect(user).not_to be_admin + expect(user).not_to be_moderator + end + end + + context 'when user is not a moderator' do + let(:moderator) { false } + + it 'does not change any fields' do + expect(user).not_to be_admin + expect(user).not_to be_moderator + end + end + end + end + + describe '#active_for_authentication?' do + subject { user.active_for_authentication? } + let(:user) { Fabricate(:user, disabled: disabled, confirmed_at: confirmed_at) } + + context 'when user is disabled' do + let(:disabled) { true } + + context 'when user is confirmed' do + let(:confirmed_at) { Time.zone.now } + + it { is_expected.to be false } + end + + context 'when user is not confirmed' do + let(:confirmed_at) { nil } + + it { is_expected.to be false } + end + end + + context 'when user is not disabled' do + let(:disabled) { false } + + context 'when user is confirmed' do + let(:confirmed_at) { Time.zone.now } + + it { is_expected.to be true } + end + + context 'when user is not confirmed' do + let(:confirmed_at) { nil } + + it { is_expected.to be false } + end + end + end end From 965345316fb3fef640a6bcc463d09d4a38b28608 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 2 May 2018 15:44:22 +0200 Subject: [PATCH 166/442] Guard against nil URLs in Request class (#7284) Fix #7265 --- app/lib/request.rb | 3 +++ app/services/activitypub/fetch_featured_collection_service.rb | 2 ++ 2 files changed, 5 insertions(+) diff --git a/app/lib/request.rb b/app/lib/request.rb index 0acd654da..00f94dacf 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -9,12 +9,15 @@ class Request include RoutingHelper def initialize(verb, url, **options) + raise ArgumentError if url.blank? + @verb = verb @url = Addressable::URI.parse(url).normalize @options = options.merge(use_proxy? ? Rails.configuration.x.http_client_proxy : { socket_class: Socket }) @headers = {} raise Mastodon::HostValidationError, 'Instance does not support hidden service connections' if block_hidden_service? + set_common_headers! set_digest! if options.key?(:body) end diff --git a/app/services/activitypub/fetch_featured_collection_service.rb b/app/services/activitypub/fetch_featured_collection_service.rb index 40714e980..6a137b520 100644 --- a/app/services/activitypub/fetch_featured_collection_service.rb +++ b/app/services/activitypub/fetch_featured_collection_service.rb @@ -4,6 +4,8 @@ class ActivityPub::FetchFeaturedCollectionService < BaseService include JsonLdHelper def call(account) + return if account.featured_collection_url.blank? + @account = account @json = fetch_resource(@account.featured_collection_url, true) From c5dcd7d836d53ede4751405ec5701f9f3b48102c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 2 May 2018 15:45:24 +0200 Subject: [PATCH 167/442] Speed up test suite by not generating RSA keys in test environment (#7296) One RSA keypair for all fabricated test accounts is enough --- app/models/account.rb | 4 ++-- spec/fabricators/account_fabricator.rb | 8 +++++++- spec/models/account_spec.rb | 3 ++- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/app/models/account.rb b/app/models/account.rb index 647b5c358..0cd2a10d5 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -406,9 +406,9 @@ class Account < ApplicationRecord end def generate_keys - return unless local? + return unless local? && !Rails.env.test? - keypair = OpenSSL::PKey::RSA.new(Rails.env.test? ? 512 : 2048) + keypair = OpenSSL::PKey::RSA.new(2048) self.private_key = keypair.to_pem self.public_key = keypair.public_key.to_pem end diff --git a/spec/fabricators/account_fabricator.rb b/spec/fabricators/account_fabricator.rb index 446f8ea27..7aa983f82 100644 --- a/spec/fabricators/account_fabricator.rb +++ b/spec/fabricators/account_fabricator.rb @@ -1,4 +1,10 @@ +keypair = OpenSSL::PKey::RSA.new(2048) +public_key = keypair.public_key.to_pem +private_key = keypair.to_pem + Fabricator(:account) do - username { sequence(:username) { |i| "#{Faker::Internet.user_name(nil, %w(_))}#{i}" } } + username { sequence(:username) { |i| "#{Faker::Internet.user_name(nil, %w(_))}#{i}" } } last_webfingered_at { Time.now.utc } + public_key { public_key } + private_key { private_key} end diff --git a/spec/models/account_spec.rb b/spec/models/account_spec.rb index fb7ddfa83..3aaaa55eb 100644 --- a/spec/models/account_spec.rb +++ b/spec/models/account_spec.rb @@ -815,7 +815,8 @@ RSpec.describe Account, type: :model do end context 'when is local' do - it 'generates keys' do + # Test disabled because test environment omits autogenerating keys for performance + xit 'generates keys' do account = Account.create!(domain: nil, username: Faker::Internet.user_name(nil, ['_'])) expect(account.keypair.private?).to eq true end From f62539ce5c106e27a371702d499ec4df52eccde6 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 2 May 2018 15:50:20 +0200 Subject: [PATCH 168/442] Remove most behaviour disparities between blocks and mutes (#7231) * Remove most behaviour disparities between blocks and mutes The only differences between block and mute should be: - Mutes can optionally NOT affect notifications - Mutes should not be visible to the muted Fix #7230 Fix #5713 * Do not allow boosting someone you blocked Fix #7248 * Do not allow favouriting someone you blocked * Fix nil error in StatusPolicy --- app/lib/feed_manager.rb | 21 ++++++++++++--------- app/policies/status_policy.rb | 6 +++++- app/services/favourite_service.rb | 2 +- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/app/lib/feed_manager.rb b/app/lib/feed_manager.rb index dd78e543f..c18c07b33 100644 --- a/app/lib/feed_manager.rb +++ b/app/lib/feed_manager.rb @@ -145,19 +145,20 @@ class FeedManager redis.exists("subscribed:#{timeline_id}") end + def blocks_or_mutes?(receiver_id, account_ids, context) + Block.where(account_id: receiver_id, target_account_id: account_ids).any? || + (context == :home ? Mute.where(account_id: receiver_id, target_account_id: account_ids).any? : Mute.where(account_id: receiver_id, target_account_id: account_ids, hide_notifications: true).any?) + end + def filter_from_home?(status, receiver_id) return false if receiver_id == status.account_id return true if status.reply? && (status.in_reply_to_id.nil? || status.in_reply_to_account_id.nil?) - check_for_mutes = [status.account_id] - check_for_mutes.concat([status.reblog.account_id]) if status.reblog? - - return true if Mute.where(account_id: receiver_id, target_account_id: check_for_mutes).any? - check_for_blocks = status.mentions.pluck(:account_id) + check_for_blocks.concat([status.account_id]) check_for_blocks.concat([status.reblog.account_id]) if status.reblog? - return true if Block.where(account_id: receiver_id, target_account_id: check_for_blocks).any? + return true if blocks_or_mutes?(receiver_id, check_for_blocks, :home) if status.reply? && !status.in_reply_to_account_id.nil? # Filter out if it's a reply should_filter = !Follow.where(account_id: receiver_id, target_account_id: status.in_reply_to_account_id).exists? # and I'm not following the person it's a reply to @@ -177,11 +178,13 @@ class FeedManager def filter_from_mentions?(status, receiver_id) return true if receiver_id == status.account_id - check_for_blocks = [status.account_id] - check_for_blocks.concat(status.mentions.pluck(:account_id)) + # This filter is called from NotifyService, but already after the sender of + # the notification has been checked for mute/block. Therefore, it's not + # necessary to check the author of the toot for mute/block again + check_for_blocks = status.mentions.pluck(:account_id) check_for_blocks.concat([status.in_reply_to_account]) if status.reply? && !status.in_reply_to_account_id.nil? - should_filter = Block.where(account_id: receiver_id, target_account_id: check_for_blocks).any? # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked + should_filter = blocks_or_mutes?(receiver_id, check_for_blocks, :mentions) # Filter if it's from someone I blocked, in reply to someone I blocked, or mentioning someone I blocked (or muted) should_filter ||= (status.account.silenced? && !Follow.where(account_id: receiver_id, target_account_id: status.account_id).exists?) # of if the account is silenced and I'm not following them should_filter diff --git a/app/policies/status_policy.rb b/app/policies/status_policy.rb index 5573289b6..4145d7e9c 100644 --- a/app/policies/status_policy.rb +++ b/app/policies/status_policy.rb @@ -16,7 +16,11 @@ class StatusPolicy < ApplicationPolicy end def reblog? - !direct? && (!private? || owned?) && show? + !direct? && (!private? || owned?) && show? && !current_account&.blocking?(author) + end + + def favourite? + show? && !current_account&.blocking?(author) end def destroy? diff --git a/app/services/favourite_service.rb b/app/services/favourite_service.rb index 44df3ed13..bc2d1547a 100644 --- a/app/services/favourite_service.rb +++ b/app/services/favourite_service.rb @@ -8,7 +8,7 @@ class FavouriteService < BaseService # @param [Status] status # @return [Favourite] def call(account, status) - authorize_with account, status, :show? + authorize_with account, status, :favourite? favourite = Favourite.find_by(account: account, status: status) From cae933510cbc64db27aeb44e205ce17ff4974da7 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 2 May 2018 15:57:37 +0200 Subject: [PATCH 169/442] Allow updating bio fields via PUT /api/v1/accounts/update_credentials (#7288) Add raw bio fields to the source attribute on GET /api/v1/accounts/verify_credentials --- app/controllers/api/v1/accounts/credentials_controller.rb | 2 +- app/models/account.rb | 4 ++++ app/serializers/rest/credential_account_serializer.rb | 2 ++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/controllers/api/v1/accounts/credentials_controller.rb b/app/controllers/api/v1/accounts/credentials_controller.rb index 062d490a7..a3c4008e6 100644 --- a/app/controllers/api/v1/accounts/credentials_controller.rb +++ b/app/controllers/api/v1/accounts/credentials_controller.rb @@ -21,7 +21,7 @@ class Api::V1::Accounts::CredentialsController < Api::BaseController private def account_params - params.permit(:display_name, :note, :avatar, :header, :locked) + params.permit(:display_name, :note, :avatar, :header, :locked, fields_attributes: [:name, :value]) end def user_settings_params diff --git a/app/models/account.rb b/app/models/account.rb index 0cd2a10d5..a166fb474 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -273,6 +273,10 @@ class Account < ApplicationRecord @value = attr['value'] @errors = {} end + + def to_h + { name: @name, value: @value } + end end class << self diff --git a/app/serializers/rest/credential_account_serializer.rb b/app/serializers/rest/credential_account_serializer.rb index 870d8b71f..56857cba8 100644 --- a/app/serializers/rest/credential_account_serializer.rb +++ b/app/serializers/rest/credential_account_serializer.rb @@ -5,10 +5,12 @@ class REST::CredentialAccountSerializer < REST::AccountSerializer def source user = object.user + { privacy: user.setting_default_privacy, sensitive: user.setting_default_sensitive, note: object.note, + fields: object.fields.map(&:to_h), } end end From d0cdd5cf94ff479e4037dc47539f7f9c408831b3 Mon Sep 17 00:00:00 2001 From: ThibG Date: Wed, 2 May 2018 16:08:16 +0200 Subject: [PATCH 170/442] Accept actor object updates from all supported actor types (#7312) --- app/lib/activitypub/activity/update.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/lib/activitypub/activity/update.rb b/app/lib/activitypub/activity/update.rb index 47e98e041..aa5907f03 100644 --- a/app/lib/activitypub/activity/update.rb +++ b/app/lib/activitypub/activity/update.rb @@ -1,8 +1,10 @@ # frozen_string_literal: true class ActivityPub::Activity::Update < ActivityPub::Activity + SUPPORTED_TYPES = %w(Application Group Organization Person Service).freeze + def perform - update_account if equals_or_includes?(@object['type'], 'Person') + update_account if equals_or_includes_any?(@object['type'], SUPPORTED_TYPES) end private From 71a7cea73fdfb45d06986e108b2ce1dbf7e32579 Mon Sep 17 00:00:00 2001 From: abcang Date: Wed, 2 May 2018 23:14:51 +0900 Subject: [PATCH 171/442] Keep notification when muting_notifications is true (#7311) * Keep notification when muting_notifications is true * Retrun mute object * Fix test --- .../mastodon/reducers/notifications.js | 2 +- app/models/concerns/account_interactions.rb | 1 + app/services/mute_service.rb | 8 ++- .../concerns/account_interactions_spec.rb | 62 ++++++++----------- 4 files changed, 34 insertions(+), 39 deletions(-) diff --git a/app/javascript/mastodon/reducers/notifications.js b/app/javascript/mastodon/reducers/notifications.js index da9b8c420..84d4fc698 100644 --- a/app/javascript/mastodon/reducers/notifications.js +++ b/app/javascript/mastodon/reducers/notifications.js @@ -105,7 +105,7 @@ export default function notifications(state = initialState, action) { return expandNormalizedNotifications(state, action.notifications, action.next); case ACCOUNT_BLOCK_SUCCESS: case ACCOUNT_MUTE_SUCCESS: - return filterNotifications(state, action.relationship); + return action.relationship.muting_notifications ? filterNotifications(state, action.relationship) : state; case NOTIFICATIONS_CLEAR: return state.set('items', ImmutableList()).set('hasMore', false); case TIMELINE_DELETE: diff --git a/app/models/concerns/account_interactions.rb b/app/models/concerns/account_interactions.rb index fdf35a4e3..2dbd2590d 100644 --- a/app/models/concerns/account_interactions.rb +++ b/app/models/concerns/account_interactions.rb @@ -93,6 +93,7 @@ module AccountInteractions if mute.hide_notifications? != notifications mute.update!(hide_notifications: notifications) end + mute end def mute_conversation!(conversation) diff --git a/app/services/mute_service.rb b/app/services/mute_service.rb index 9b7cbd81f..c6122a152 100644 --- a/app/services/mute_service.rb +++ b/app/services/mute_service.rb @@ -3,9 +3,13 @@ class MuteService < BaseService def call(account, target_account, notifications: nil) return if account.id == target_account.id - FeedManager.instance.clear_from_timeline(account, target_account) + mute = account.mute!(target_account, notifications: notifications) - BlockWorker.perform_async(account.id, target_account.id) + if mute.hide_notifications? + BlockWorker.perform_async(account.id, target_account.id) + else + FeedManager.instance.clear_from_timeline(account, target_account) + end mute end end diff --git a/spec/models/concerns/account_interactions_spec.rb b/spec/models/concerns/account_interactions_spec.rb index d08bdc8b9..8df52b770 100644 --- a/spec/models/concerns/account_interactions_spec.rb +++ b/spec/models/concerns/account_interactions_spec.rb @@ -108,13 +108,15 @@ describe AccountInteractions do end describe '#mute!' do + subject { account.mute!(target_account, notifications: arg_notifications) } + context 'Mute does not exist yet' do context 'arg :notifications is nil' do let(:arg_notifications) { nil } - it 'creates Mute, and returns nil' do + it 'creates Mute, and returns Mute' do expect do - expect(account.mute!(target_account, notifications: arg_notifications)).to be nil + expect(subject).to be_kind_of Mute end.to change { account.mute_relationships.count }.by 1 end end @@ -122,9 +124,9 @@ describe AccountInteractions do context 'arg :notifications is false' do let(:arg_notifications) { false } - it 'creates Mute, and returns nil' do + it 'creates Mute, and returns Mute' do expect do - expect(account.mute!(target_account, notifications: arg_notifications)).to be nil + expect(subject).to be_kind_of Mute end.to change { account.mute_relationships.count }.by 1 end end @@ -132,9 +134,9 @@ describe AccountInteractions do context 'arg :notifications is true' do let(:arg_notifications) { true } - it 'creates Mute, and returns nil' do + it 'creates Mute, and returns Mute' do expect do - expect(account.mute!(target_account, notifications: arg_notifications)).to be nil + expect(subject).to be_kind_of Mute end.to change { account.mute_relationships.count }.by 1 end end @@ -158,36 +160,30 @@ describe AccountInteractions do context 'arg :notifications is nil' do let(:arg_notifications) { nil } - it 'returns nil without updating mute.hide_notifications' do + it 'returns Mute without updating mute.hide_notifications' do expect do - expect(account.mute!(target_account, notifications: arg_notifications)).to be nil - mute = account.mute_relationships.find_by(target_account: target_account) - expect(mute.hide_notifications?).to be true - end + expect(subject).to be_kind_of Mute + end.not_to change { mute.reload.hide_notifications? }.from(true) end end context 'arg :notifications is false' do let(:arg_notifications) { false } - it 'returns true, and updates mute.hide_notifications false' do + it 'returns Mute, and updates mute.hide_notifications false' do expect do - expect(account.mute!(target_account, notifications: arg_notifications)).to be true - mute = account.mute_relationships.find_by(target_account: target_account) - expect(mute.hide_notifications?).to be false - end + expect(subject).to be_kind_of Mute + end.to change { mute.reload.hide_notifications? }.from(true).to(false) end end context 'arg :notifications is true' do let(:arg_notifications) { true } - it 'returns nil without updating mute.hide_notifications' do + it 'returns Mute without updating mute.hide_notifications' do expect do - expect(account.mute!(target_account, notifications: arg_notifications)).to be nil - mute = account.mute_relationships.find_by(target_account: target_account) - expect(mute.hide_notifications?).to be true - end + expect(subject).to be_kind_of Mute + end.not_to change { mute.reload.hide_notifications? }.from(true) end end end @@ -198,36 +194,30 @@ describe AccountInteractions do context 'arg :notifications is nil' do let(:arg_notifications) { nil } - it 'returns true, and updates mute.hide_notifications true' do + it 'returns Mute, and updates mute.hide_notifications true' do expect do - expect(account.mute!(target_account, notifications: arg_notifications)).to be true - mute = account.mute_relationships.find_by(target_account: target_account) - expect(mute.hide_notifications?).to be true - end + expect(subject).to be_kind_of Mute + end.to change { mute.reload.hide_notifications? }.from(false).to(true) end end context 'arg :notifications is false' do let(:arg_notifications) { false } - it 'returns nil without updating mute.hide_notifications' do + it 'returns Mute without updating mute.hide_notifications' do expect do - expect(account.mute!(target_account, notifications: arg_notifications)).to be nil - mute = account.mute_relationships.find_by(target_account: target_account) - expect(mute.hide_notifications?).to be false - end + expect(subject).to be_kind_of Mute + end.not_to change { mute.reload.hide_notifications? }.from(false) end end context 'arg :notifications is true' do let(:arg_notifications) { true } - it 'returns true, and updates mute.hide_notifications true' do + it 'returns Mute, and updates mute.hide_notifications true' do expect do - expect(account.mute!(target_account, notifications: arg_notifications)).to be true - mute = account.mute_relationships.find_by(target_account: target_account) - expect(mute.hide_notifications?).to be true - end + expect(subject).to be_kind_of Mute + end.to change { mute.reload.hide_notifications? }.from(false).to(true) end end end From cb5b5cb5f79bb2187d8124df91af4c8e1bfd7256 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 2 May 2018 18:58:48 +0200 Subject: [PATCH 172/442] Slightly reduce RAM usage (#7301) * No need to re-require sidekiq plugins, they are required via Gemfile * Add derailed_benchmarks tool, no need to require TTY gems in Gemfile * Replace ruby-oembed with FetchOEmbedService Reduce startup by 45382 allocated objects * Remove preloaded JSON-LD in favour of caching HTTP responses Reduce boot RAM by about 6 MiB * Fix tests * Fix test suite by stubbing out JSON-LD contexts --- Gemfile | 12 +- Gemfile.lock | 22 +- app/controllers/api/web/embeds_controller.rb | 11 +- .../settings/follower_domains_controller.rb | 2 - app/helpers/jsonld_helper.rb | 17 +- app/lib/provider_discovery.rb | 47 --- app/services/fan_out_on_write_service.rb | 2 - app/services/fetch_link_card_service.rb | 38 +- app/services/fetch_oembed_service.rb | 71 ++++ .../scheduler/backup_cleanup_scheduler.rb | 1 - .../scheduler/doorkeeper_cleanup_scheduler.rb | 1 - app/workers/scheduler/email_scheduler.rb | 1 - .../scheduler/feed_cleanup_scheduler.rb | 1 - app/workers/scheduler/ip_cleanup_scheduler.rb | 1 - .../scheduler/media_cleanup_scheduler.rb | 1 - .../subscriptions_cleanup_scheduler.rb | 2 - .../scheduler/subscriptions_scheduler.rb | 3 - .../scheduler/user_cleanup_scheduler.rb | 1 - .../soft_block_domain_followers_worker.rb | 2 - config/initializers/json_ld.rb | 5 - config/initializers/oembed.rb | 4 - lib/json_ld/activitystreams.rb | 153 ------- lib/json_ld/identity.rb | 86 ---- lib/json_ld/security.rb | 50 --- lib/tasks/mastodon.rake | 2 + .../requests/json-ld.activitystreams.txt | 391 ++++++++++++++++++ spec/fixtures/requests/json-ld.identity.txt | 100 +++++ spec/fixtures/requests/json-ld.security.txt | 61 +++ .../activitypub/linked_data_signature_spec.rb | 4 + spec/rails_helper.rb | 14 + spec/services/account_search_service_spec.rb | 2 +- .../fetch_remote_account_service_spec.rb | 2 +- .../fetch_remote_status_service_spec.rb | 2 +- .../process_account_service_spec.rb | 2 +- .../process_collection_service_spec.rb | 2 +- spec/services/after_block_service_spec.rb | 2 +- .../services/authorize_follow_service_spec.rb | 2 +- .../batched_remove_status_service_spec.rb | 2 +- .../block_domain_from_account_service_spec.rb | 2 +- spec/services/block_domain_service_spec.rb | 2 +- spec/services/block_service_spec.rb | 2 +- .../bootstrap_timeline_service_spec.rb | 2 +- .../services/fan_out_on_write_service_spec.rb | 2 +- spec/services/favourite_service_spec.rb | 2 +- spec/services/fetch_atom_service_spec.rb | 2 +- spec/services/fetch_link_card_service_spec.rb | 2 +- .../fetch_oembed_service_spec.rb} | 49 ++- .../fetch_remote_account_service_spec.rb | 2 +- .../fetch_remote_status_service_spec.rb | 2 +- spec/services/follow_service_spec.rb | 2 +- spec/services/mute_service_spec.rb | 2 +- spec/services/notify_service_spec.rb | 2 +- spec/services/post_status_service_spec.rb | 2 +- spec/services/precompute_feed_service_spec.rb | 2 +- spec/services/process_feed_service_spec.rb | 2 +- .../process_interaction_service_spec.rb | 2 +- .../services/process_mentions_service_spec.rb | 2 +- .../pubsubhubbub/subscribe_service_spec.rb | 2 +- .../pubsubhubbub/unsubscribe_service_spec.rb | 2 +- spec/services/reblog_service_spec.rb | 2 +- spec/services/reject_follow_service_spec.rb | 2 +- spec/services/remove_status_service_spec.rb | 2 +- spec/services/report_service_spec.rb | 2 +- spec/services/resolve_account_service_spec.rb | 2 +- spec/services/resolve_url_service_spec.rb | 2 +- spec/services/search_service_spec.rb | 2 +- .../services/send_interaction_service_spec.rb | 2 +- spec/services/subscribe_service_spec.rb | 2 +- spec/services/suspend_account_service_spec.rb | 2 +- spec/services/unblock_domain_service_spec.rb | 2 +- spec/services/unblock_service_spec.rb | 2 +- spec/services/unfollow_service_spec.rb | 2 +- spec/services/unmute_service_spec.rb | 2 +- spec/services/unsubscribe_service_spec.rb | 2 +- .../update_remote_profile_service_spec.rb | 2 +- spec/spec_helper.rb | 12 +- 76 files changed, 784 insertions(+), 471 deletions(-) delete mode 100644 app/lib/provider_discovery.rb create mode 100644 app/services/fetch_oembed_service.rb delete mode 100644 config/initializers/json_ld.rb delete mode 100644 config/initializers/oembed.rb delete mode 100644 lib/json_ld/activitystreams.rb delete mode 100644 lib/json_ld/identity.rb delete mode 100644 lib/json_ld/security.rb create mode 100644 spec/fixtures/requests/json-ld.activitystreams.txt create mode 100644 spec/fixtures/requests/json-ld.identity.txt create mode 100644 spec/fixtures/requests/json-ld.security.txt rename spec/{lib/provider_discovery_spec.rb => services/fetch_oembed_service_spec.rb} (64%) diff --git a/Gemfile b/Gemfile index a33748568..f1665ce95 100644 --- a/Gemfile +++ b/Gemfile @@ -54,7 +54,7 @@ gem 'httplog', '~> 1.0' gem 'idn-ruby', require: 'idn' gem 'kaminari', '~> 1.1' gem 'link_header', '~> 0.0' -gem 'mime-types', '~> 3.1' +gem 'mime-types', '~> 3.1', require: 'mime/types/columnar' gem 'nokogiri', '~> 1.8' gem 'nsa', '~> 0.2' gem 'oj', '~> 3.5' @@ -70,7 +70,6 @@ gem 'rails-settings-cached', '~> 0.6' gem 'redis', '~> 4.0', require: ['redis', 'redis/connection/hiredis'] gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock' gem 'rqrcode', '~> 0.10' -gem 'ruby-oembed', '~> 0.12', require: 'oembed' gem 'ruby-progressbar', '~> 1.4' gem 'sanitize', '~> 4.6' gem 'sidekiq', '~> 5.1' @@ -82,14 +81,14 @@ gem 'simple_form', '~> 4.0' gem 'sprockets-rails', '~> 3.2', require: 'sprockets/railtie' gem 'stoplight', '~> 2.1.3' gem 'strong_migrations', '~> 0.2' -gem 'tty-command', '~> 0.8' -gem 'tty-prompt', '~> 0.16' +gem 'tty-command', '~> 0.8', require: false +gem 'tty-prompt', '~> 0.16', require: false gem 'twitter-text', '~> 1.14' gem 'tzinfo-data', '~> 1.2018' gem 'webpacker', '~> 3.4' gem 'webpush' -gem 'json-ld-preloaded', '~> 2.2' +gem 'json-ld', '~> 2.2' gem 'rdf-normalize', '~> 0.3' group :development, :test do @@ -135,6 +134,9 @@ group :development do gem 'capistrano-rails', '~> 1.3' gem 'capistrano-rbenv', '~> 2.1' gem 'capistrano-yarn', '~> 2.0' + + gem 'derailed_benchmarks' + gem 'stackprof' end group :production do diff --git a/Gemfile.lock b/Gemfile.lock index d96165dcf..94ab0b7ca 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -75,6 +75,7 @@ GEM aws-sigv4 (~> 1.0) aws-sigv4 (1.0.2) bcrypt (3.1.11) + benchmark-ips (2.7.2) better_errors (2.4.0) coderay (>= 1.0.0) erubi (>= 1.0.0) @@ -138,6 +139,14 @@ GEM css_parser (1.6.0) addressable debug_inspector (0.0.3) + derailed_benchmarks (1.3.4) + benchmark-ips (~> 2) + get_process_mem (~> 0) + heapy (~> 0) + memory_profiler (~> 0) + rack (>= 1) + rake (> 10, < 13) + thor (~> 0.19) devise (4.4.3) bcrypt (~> 3.0) orm_adapter (~> 0.1) @@ -206,6 +215,7 @@ GEM fuubar (2.3.1) rspec-core (~> 3.0) ruby-progressbar (~> 1.4) + get_process_mem (0.2.1) globalid (0.4.1) activesupport (>= 4.2.0) goldfinger (2.1.0) @@ -226,6 +236,7 @@ GEM concurrent-ruby (~> 1.0) hashdiff (0.3.7) hashie (3.5.7) + heapy (0.1.3) highline (1.7.10) hiredis (0.6.1) hitimes (1.2.6) @@ -264,10 +275,6 @@ GEM json-ld (2.2.1) multi_json (~> 1.12) rdf (>= 2.2.8, < 4.0) - json-ld-preloaded (2.2.3) - json-ld (>= 2.2, < 4.0) - multi_json (~> 1.12) - rdf (>= 2.2, < 4.0) jsonapi-renderer (0.2.0) jwt (2.1.0) kaminari (1.1.1) @@ -502,7 +509,6 @@ GEM rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (~> 1.0, >= 1.0.1) - ruby-oembed (0.12.0) ruby-progressbar (1.9.0) ruby-saml (1.7.2) nokogiri (>= 1.5.10) @@ -557,6 +563,7 @@ GEM sshkit (1.16.0) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) + stackprof (0.2.11) statsd-ruby (1.2.1) stoplight (2.1.3) streamio-ffmpeg (3.0.2) @@ -645,6 +652,7 @@ DEPENDENCIES chewy (~> 5.0) cld3 (~> 3.2.0) climate_control (~> 0.2) + derailed_benchmarks devise (~> 4.4) devise-two-factor (~> 3.0) devise_pam_authenticatable2 (~> 9.1) @@ -668,7 +676,7 @@ DEPENDENCIES i18n-tasks (~> 0.9) idn-ruby iso-639 - json-ld-preloaded (~> 2.2) + json-ld (~> 2.2) kaminari (~> 1.1) letter_opener (~> 1.4) letter_opener_web (~> 1.3) @@ -714,7 +722,6 @@ DEPENDENCIES rspec-retry (~> 0.5) rspec-sidekiq (~> 3.0) rubocop (~> 0.55) - ruby-oembed (~> 0.12) ruby-progressbar (~> 1.4) sanitize (~> 4.6) scss_lint (~> 0.57) @@ -726,6 +733,7 @@ DEPENDENCIES simple_form (~> 4.0) simplecov (~> 0.16) sprockets-rails (~> 3.2) + stackprof stoplight (~> 2.1.3) streamio-ffmpeg (~> 3.0) strong_migrations (~> 0.2) diff --git a/app/controllers/api/web/embeds_controller.rb b/app/controllers/api/web/embeds_controller.rb index f2fe74b17..987290a14 100644 --- a/app/controllers/api/web/embeds_controller.rb +++ b/app/controllers/api/web/embeds_controller.rb @@ -9,9 +9,12 @@ class Api::Web::EmbedsController < Api::Web::BaseController status = StatusFinder.new(params[:url]).status render json: status, serializer: OEmbedSerializer, width: 400 rescue ActiveRecord::RecordNotFound - oembed = OEmbed::Providers.get(params[:url]) - render json: Oj.dump(oembed.fields) - rescue OEmbed::NotFound - render json: {}, status: :not_found + oembed = FetchOEmbedService.new.call(params[:url]) + + if oembed + render json: oembed + else + render json: {}, status: :not_found + end end end diff --git a/app/controllers/settings/follower_domains_controller.rb b/app/controllers/settings/follower_domains_controller.rb index 213d9e96d..91b521e7f 100644 --- a/app/controllers/settings/follower_domains_controller.rb +++ b/app/controllers/settings/follower_domains_controller.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'sidekiq-bulk' - class Settings::FollowerDomainsController < ApplicationController layout 'admin' diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index a3cfdadb8..e9056166c 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -48,7 +48,7 @@ module JsonLdHelper end def canonicalize(json) - graph = RDF::Graph.new << JSON::LD::API.toRdf(json) + graph = RDF::Graph.new << JSON::LD::API.toRdf(json, documentLoader: method(:load_jsonld_context)) graph.dump(:normalize) end @@ -90,4 +90,19 @@ module JsonLdHelper request.add_headers('Accept' => 'application/activity+json, application/ld+json') request end + + def load_jsonld_context(url, _options = {}, &_block) + json = Rails.cache.fetch("jsonld:context:#{url}", expires_in: 30.days, raw: true) do + request = Request.new(:get, url) + request.add_headers('Accept' => 'application/ld+json') + + request.perform do |res| + raise JSON::LD::JsonLdError::LoadingDocumentFailed unless res.code == 200 && res.mime_type == 'application/ld+json' + res.body_with_limit + end + end + + doc = JSON::LD::API::RemoteDocument.new(url, json) + block_given? ? yield(doc) : doc + end end diff --git a/app/lib/provider_discovery.rb b/app/lib/provider_discovery.rb deleted file mode 100644 index 3bec7211b..000000000 --- a/app/lib/provider_discovery.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -class ProviderDiscovery < OEmbed::ProviderDiscovery - class << self - def get(url, **options) - provider = discover_provider(url, options) - - options.delete(:html) - - provider.get(url, options) - end - - def discover_provider(url, **options) - format = options[:format] - - html = if options[:html] - Nokogiri::HTML(options[:html]) - else - Request.new(:get, url).perform do |res| - raise OEmbed::NotFound, url if res.code != 200 || res.mime_type != 'text/html' - Nokogiri::HTML(res.body_with_limit) - end - end - - if format.nil? || format == :json - provider_endpoint ||= html.at_xpath('//link[@type="application/json+oembed"]')&.attribute('href')&.value - format ||= :json if provider_endpoint - end - - if format.nil? || format == :xml - provider_endpoint ||= html.at_xpath('//link[@type="text/xml+oembed"]')&.attribute('href')&.value - format ||= :xml if provider_endpoint - end - - raise OEmbed::NotFound, url if provider_endpoint.nil? - begin - provider_endpoint = Addressable::URI.parse(provider_endpoint) - provider_endpoint.query = nil - provider_endpoint = provider_endpoint.to_s - rescue Addressable::URI::InvalidURIError - raise OEmbed::NotFound, url - end - - OEmbed::Provider.new(provider_endpoint, format) - end - end -end diff --git a/app/services/fan_out_on_write_service.rb b/app/services/fan_out_on_write_service.rb index 0f77556dc..510b80c82 100644 --- a/app/services/fan_out_on_write_service.rb +++ b/app/services/fan_out_on_write_service.rb @@ -1,7 +1,5 @@ # frozen_string_literal: true -require 'sidekiq-bulk' - class FanOutOnWriteService < BaseService # Push a status into home and mentions feeds # @param [Status] status diff --git a/app/services/fetch_link_card_service.rb b/app/services/fetch_link_card_service.rb index d5920a417..77d4aa538 100644 --- a/app/services/fetch_link_card_service.rb +++ b/app/services/fetch_link_card_service.rb @@ -85,42 +85,40 @@ class FetchLinkCardService < BaseService end def attempt_oembed - embed = OEmbed::Providers.get(@url, html: @html) + embed = FetchOEmbedService.new.call(@url, html: @html) - return false unless embed.respond_to?(:type) + return false if embed.nil? - @card.type = embed.type - @card.title = embed.respond_to?(:title) ? embed.title : '' - @card.author_name = embed.respond_to?(:author_name) ? embed.author_name : '' - @card.author_url = embed.respond_to?(:author_url) ? embed.author_url : '' - @card.provider_name = embed.respond_to?(:provider_name) ? embed.provider_name : '' - @card.provider_url = embed.respond_to?(:provider_url) ? embed.provider_url : '' + @card.type = embed[:type] + @card.title = embed[:title] || '' + @card.author_name = embed[:author_name] || '' + @card.author_url = embed[:author_url] || '' + @card.provider_name = embed[:provider_name] || '' + @card.provider_url = embed[:provider_url] || '' @card.width = 0 @card.height = 0 case @card.type when 'link' - @card.image_remote_url = embed.thumbnail_url if embed.respond_to?(:thumbnail_url) + @card.image_remote_url = embed[:thumbnail_url] if embed[:thumbnail_url].present? when 'photo' - return false unless embed.respond_to?(:url) + return false if embed[:url].blank? - @card.embed_url = embed.url - @card.image_remote_url = embed.url - @card.width = embed.width.presence || 0 - @card.height = embed.height.presence || 0 + @card.embed_url = embed[:url] + @card.image_remote_url = embed[:url] + @card.width = embed[:width].presence || 0 + @card.height = embed[:height].presence || 0 when 'video' - @card.width = embed.width.presence || 0 - @card.height = embed.height.presence || 0 - @card.html = Formatter.instance.sanitize(embed.html, Sanitize::Config::MASTODON_OEMBED) - @card.image_remote_url = embed.thumbnail_url if embed.respond_to?(:thumbnail_url) + @card.width = embed[:width].presence || 0 + @card.height = embed[:height].presence || 0 + @card.html = Formatter.instance.sanitize(embed[:html], Sanitize::Config::MASTODON_OEMBED) + @card.image_remote_url = embed[:thumbnail_url] if embed[:thumbnail_url].present? when 'rich' # Most providers rely on