Merge branch 'master' into glitch-soc/merge-upstream
commit
b00f60f1d3
|
@ -178,7 +178,7 @@ jobs:
|
||||||
- *attach_workspace
|
- *attach_workspace
|
||||||
- run: bundle exec i18n-tasks check-normalized
|
- run: bundle exec i18n-tasks check-normalized
|
||||||
- run: bundle exec i18n-tasks unused
|
- run: bundle exec i18n-tasks unused
|
||||||
- run: bundle exec i18n-tasks missing-plural-keys
|
- run: bundle exec i18n-tasks missing -t plural
|
||||||
- run: bundle exec i18n-tasks check-consistent-interpolations
|
- run: bundle exec i18n-tasks check-consistent-interpolations
|
||||||
|
|
||||||
workflows:
|
workflows:
|
||||||
|
|
2
Gemfile
2
Gemfile
|
@ -96,7 +96,7 @@ gem 'rdf-normalize', '~> 0.3'
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
gem 'fabrication', '~> 2.20'
|
gem 'fabrication', '~> 2.20'
|
||||||
gem 'fuubar', '~> 2.3'
|
gem 'fuubar', '~> 2.3'
|
||||||
gem 'i18n-tasks', '~> 0.9', require: false, git: 'https://github.com/Gargron/i18n-tasks.git', ref: '7a57fbe7000f4f8120e250a757ab345c28c6885c'
|
gem 'i18n-tasks', '~> 0.9', require: false, git: 'https://github.com/Gargron/i18n-tasks.git', ref: 'ab6e10878ccdb6243f934f30372276d260c14251'
|
||||||
gem 'pry-byebug', '~> 3.6'
|
gem 'pry-byebug', '~> 3.6'
|
||||||
gem 'pry-rails', '~> 0.3'
|
gem 'pry-rails', '~> 0.3'
|
||||||
gem 'rspec-rails', '~> 3.8'
|
gem 'rspec-rails', '~> 3.8'
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
GIT
|
GIT
|
||||||
remote: https://github.com/Gargron/i18n-tasks.git
|
remote: https://github.com/Gargron/i18n-tasks.git
|
||||||
revision: 7a57fbe7000f4f8120e250a757ab345c28c6885c
|
revision: ab6e10878ccdb6243f934f30372276d260c14251
|
||||||
ref: 7a57fbe7000f4f8120e250a757ab345c28c6885c
|
ref: ab6e10878ccdb6243f934f30372276d260c14251
|
||||||
specs:
|
specs:
|
||||||
i18n-tasks (0.9.27)
|
i18n-tasks (0.9.27)
|
||||||
activesupport (>= 4.0.2)
|
activesupport (>= 4.0.2)
|
||||||
|
|
|
@ -201,6 +201,7 @@ class ApplicationController < ActionController::Base
|
||||||
def respond_with_error(code)
|
def respond_with_error(code)
|
||||||
respond_to do |format|
|
respond_to do |format|
|
||||||
format.any { head code }
|
format.any { head code }
|
||||||
|
|
||||||
format.html do
|
format.html do
|
||||||
set_locale
|
set_locale
|
||||||
use_pack 'error'
|
use_pack 'error'
|
||||||
|
|
|
@ -9,6 +9,7 @@ import DisplayName from './display_name';
|
||||||
import StatusContent from './status_content';
|
import StatusContent from './status_content';
|
||||||
import StatusActionBar from './status_action_bar';
|
import StatusActionBar from './status_action_bar';
|
||||||
import AttachmentList from './attachment_list';
|
import AttachmentList from './attachment_list';
|
||||||
|
import Card from '../features/status/components/card';
|
||||||
import { injectIntl, FormattedMessage } from 'react-intl';
|
import { injectIntl, FormattedMessage } from 'react-intl';
|
||||||
import ImmutablePureComponent from 'react-immutable-pure-component';
|
import ImmutablePureComponent from 'react-immutable-pure-component';
|
||||||
import { MediaGallery, Video } from '../features/ui/util/async-components';
|
import { MediaGallery, Video } from '../features/ui/util/async-components';
|
||||||
|
@ -256,6 +257,14 @@ class Status extends ImmutablePureComponent {
|
||||||
</Bundle>
|
</Bundle>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else if (status.get('spoiler_text').length === 0 && status.get('card')) {
|
||||||
|
media = (
|
||||||
|
<Card
|
||||||
|
onOpenMedia={this.props.onOpenMedia}
|
||||||
|
card={status.get('card')}
|
||||||
|
compact
|
||||||
|
/>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (otherAccounts) {
|
if (otherAccounts) {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
// https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/emoji-index.js
|
// https://github.com/missive/emoji-mart/blob/5f2ffcc/src/utils/emoji-index.js
|
||||||
|
|
||||||
import data from './emoji_mart_data_light';
|
import data from './emoji_mart_data_light';
|
||||||
import { getData, getSanitizedData, intersect } from './emoji_utils';
|
import { getData, getSanitizedData, uniq, intersect } from './emoji_utils';
|
||||||
|
|
||||||
let originalPool = {};
|
let originalPool = {};
|
||||||
let index = {};
|
let index = {};
|
||||||
|
@ -103,7 +103,7 @@ function search(value, { emojisToShowFilter, maxResults, include, exclude, custo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allResults = values.map((value) => {
|
const searchValue = (value) => {
|
||||||
let aPool = pool,
|
let aPool = pool,
|
||||||
aIndex = index,
|
aIndex = index,
|
||||||
length = 0;
|
length = 0;
|
||||||
|
@ -150,15 +150,23 @@ function search(value, { emojisToShowFilter, maxResults, include, exclude, custo
|
||||||
}
|
}
|
||||||
|
|
||||||
return aIndex.results;
|
return aIndex.results;
|
||||||
}).filter(a => a);
|
};
|
||||||
|
|
||||||
if (allResults.length > 1) {
|
if (values.length > 1) {
|
||||||
results = intersect.apply(null, allResults);
|
results = searchValue(value);
|
||||||
} else if (allResults.length) {
|
|
||||||
results = allResults[0];
|
|
||||||
} else {
|
} else {
|
||||||
results = [];
|
results = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
allResults = values.map(searchValue).filter(a => a);
|
||||||
|
|
||||||
|
if (allResults.length > 1) {
|
||||||
|
allResults = intersect.apply(null, allResults);
|
||||||
|
} else if (allResults.length) {
|
||||||
|
allResults = allResults[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
results = uniq(results.concat(allResults));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (results) {
|
if (results) {
|
||||||
|
|
|
@ -59,10 +59,12 @@ export default class Card extends React.PureComponent {
|
||||||
card: ImmutablePropTypes.map,
|
card: ImmutablePropTypes.map,
|
||||||
maxDescription: PropTypes.number,
|
maxDescription: PropTypes.number,
|
||||||
onOpenMedia: PropTypes.func.isRequired,
|
onOpenMedia: PropTypes.func.isRequired,
|
||||||
|
compact: PropTypes.boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
maxDescription: 50,
|
maxDescription: 50,
|
||||||
|
compact: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -131,25 +133,25 @@ export default class Card extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { card, maxDescription } = this.props;
|
const { card, maxDescription, compact } = this.props;
|
||||||
const { width, embedded } = this.state;
|
const { width, embedded } = this.state;
|
||||||
|
|
||||||
if (card === null) {
|
if (card === null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name');
|
const provider = card.get('provider_name').length === 0 ? decodeIDNA(getHostname(card.get('url'))) : card.get('provider_name');
|
||||||
const horizontal = card.get('width') > card.get('height') && (card.get('width') + 100 >= width) || card.get('type') !== 'link';
|
const horizontal = (!compact && card.get('width') > card.get('height') && (card.get('width') + 100 >= width)) || card.get('type') !== 'link' || embedded;
|
||||||
const className = classnames('status-card', { horizontal });
|
|
||||||
const interactive = card.get('type') !== 'link';
|
const interactive = card.get('type') !== 'link';
|
||||||
|
const className = classnames('status-card', { horizontal, compact, interactive });
|
||||||
const title = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
|
const title = interactive ? <a className='status-card__title' href={card.get('url')} title={card.get('title')} rel='noopener' target='_blank'><strong>{card.get('title')}</strong></a> : <strong className='status-card__title' title={card.get('title')}>{card.get('title')}</strong>;
|
||||||
const ratio = card.get('width') / card.get('height');
|
const ratio = compact ? 16 / 9 : card.get('width') / card.get('height');
|
||||||
const height = card.get('width') > card.get('height') ? (width / ratio) : (width * ratio);
|
const height = card.get('width') > card.get('height') ? (width / ratio) : (width * ratio);
|
||||||
|
|
||||||
const description = (
|
const description = (
|
||||||
<div className='status-card__content'>
|
<div className='status-card__content'>
|
||||||
{title}
|
{title}
|
||||||
{!horizontal && <p className='status-card__description'>{trim(card.get('description') || '', maxDescription)}</p>}
|
{!(horizontal || compact) && <p className='status-card__description'>{trim(card.get('description') || '', maxDescription)}</p>}
|
||||||
<span className='status-card__host'>{provider}</span>
|
<span className='status-card__host'>{provider}</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -174,7 +176,7 @@ export default class Card extends React.PureComponent {
|
||||||
<div className='status-card__actions'>
|
<div className='status-card__actions'>
|
||||||
<div>
|
<div>
|
||||||
<button onClick={this.handleEmbedClick}><i className={`fa fa-${iconVariant}`} /></button>
|
<button onClick={this.handleEmbedClick}><i className={`fa fa-${iconVariant}`} /></button>
|
||||||
<a href={card.get('url')} target='_blank' rel='noopener'><i className='fa fa-external-link' /></a>
|
{horizontal && <a href={card.get('url')} target='_blank' rel='noopener'><i className='fa fa-external-link' /></a>}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -184,7 +186,7 @@ export default class Card extends React.PureComponent {
|
||||||
return (
|
return (
|
||||||
<div className={className} ref={this.setRef}>
|
<div className={className} ref={this.setRef}>
|
||||||
{embed}
|
{embed}
|
||||||
{description}
|
{!compact && description}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
} else if (card.get('image')) {
|
} else if (card.get('image')) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { connect } from 'react-redux';
|
||||||
import Card from '../components/card';
|
import Card from '../components/card';
|
||||||
|
|
||||||
const mapStateToProps = (state, { statusId }) => ({
|
const mapStateToProps = (state, { statusId }) => ({
|
||||||
card: state.getIn(['cards', statusId], null),
|
card: state.getIn(['statuses', statusId, 'card'], null),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(mapStateToProps)(Card);
|
export default connect(mapStateToProps)(Card);
|
||||||
|
|
|
@ -14,7 +14,6 @@ import relationships from './relationships';
|
||||||
import settings from './settings';
|
import settings from './settings';
|
||||||
import push_notifications from './push_notifications';
|
import push_notifications from './push_notifications';
|
||||||
import status_lists from './status_lists';
|
import status_lists from './status_lists';
|
||||||
import cards from './cards';
|
|
||||||
import mutes from './mutes';
|
import mutes from './mutes';
|
||||||
import reports from './reports';
|
import reports from './reports';
|
||||||
import contexts from './contexts';
|
import contexts from './contexts';
|
||||||
|
@ -46,7 +45,6 @@ const reducers = {
|
||||||
relationships,
|
relationships,
|
||||||
settings,
|
settings,
|
||||||
push_notifications,
|
push_notifications,
|
||||||
cards,
|
|
||||||
mutes,
|
mutes,
|
||||||
reports,
|
reports,
|
||||||
contexts,
|
contexts,
|
||||||
|
|
|
@ -10,6 +10,7 @@ import {
|
||||||
STATUS_REVEAL,
|
STATUS_REVEAL,
|
||||||
STATUS_HIDE,
|
STATUS_HIDE,
|
||||||
} from '../actions/statuses';
|
} from '../actions/statuses';
|
||||||
|
import { STATUS_CARD_FETCH_SUCCESS } from '../actions/cards';
|
||||||
import { TIMELINE_DELETE } from '../actions/timelines';
|
import { TIMELINE_DELETE } from '../actions/timelines';
|
||||||
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
|
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
|
||||||
import { Map as ImmutableMap, fromJS } from 'immutable';
|
import { Map as ImmutableMap, fromJS } from 'immutable';
|
||||||
|
@ -65,6 +66,8 @@ export default function statuses(state = initialState, action) {
|
||||||
});
|
});
|
||||||
case TIMELINE_DELETE:
|
case TIMELINE_DELETE:
|
||||||
return deleteStatus(state, action.id, action.references);
|
return deleteStatus(state, action.id, action.references);
|
||||||
|
case STATUS_CARD_FETCH_SUCCESS:
|
||||||
|
return state.setIn([action.id, 'card'], fromJS(action.card));
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1669,6 +1669,7 @@ a.account__display-name {
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4);
|
||||||
|
z-index: 9999;
|
||||||
|
|
||||||
ul {
|
ul {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
@ -2560,6 +2561,9 @@ a.status-card {
|
||||||
display: block;
|
display: block;
|
||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status-card__image {
|
.status-card__image {
|
||||||
|
@ -2584,6 +2588,31 @@ a.status-card {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.status-card.compact {
|
||||||
|
border-color: lighten($ui-base-color, 4%);
|
||||||
|
|
||||||
|
&.interactive {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-card__content {
|
||||||
|
padding: 8px;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-card__title {
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-card__image {
|
||||||
|
flex: 0 0 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.status-card.compact:hover {
|
||||||
|
background-color: lighten($ui-base-color, 4%);
|
||||||
|
}
|
||||||
|
|
||||||
.status-card__image-image {
|
.status-card__image-image {
|
||||||
border-radius: 4px 0 0 4px;
|
border-radius: 4px 0 0 4px;
|
||||||
display: block;
|
display: block;
|
||||||
|
|
|
@ -148,6 +148,7 @@ class MediaAttachment < ApplicationRecord
|
||||||
"#{x},#{y}"
|
"#{x},#{y}"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
after_commit :reset_parent_cache, on: :update
|
||||||
before_create :prepare_description, unless: :local?
|
before_create :prepare_description, unless: :local?
|
||||||
before_create :set_shortcode
|
before_create :set_shortcode
|
||||||
before_post_process :set_type_and_extension
|
before_post_process :set_type_and_extension
|
||||||
|
@ -252,4 +253,9 @@ class MediaAttachment < ApplicationRecord
|
||||||
bitrate: movie.bitrate,
|
bitrate: movie.bitrate,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def reset_parent_cache
|
||||||
|
return if status_id.nil?
|
||||||
|
Rails.cache.delete("statuses/#{status_id}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -94,6 +94,7 @@ class Status < ApplicationRecord
|
||||||
:conversation,
|
:conversation,
|
||||||
:status_stat,
|
:status_stat,
|
||||||
:tags,
|
:tags,
|
||||||
|
:preview_cards,
|
||||||
:stream_entry,
|
:stream_entry,
|
||||||
active_mentions: :account,
|
active_mentions: :account,
|
||||||
reblog: [
|
reblog: [
|
||||||
|
@ -101,6 +102,7 @@ class Status < ApplicationRecord
|
||||||
:application,
|
:application,
|
||||||
:stream_entry,
|
:stream_entry,
|
||||||
:tags,
|
:tags,
|
||||||
|
:preview_cards,
|
||||||
:media_attachments,
|
:media_attachments,
|
||||||
:conversation,
|
:conversation,
|
||||||
:status_stat,
|
:status_stat,
|
||||||
|
@ -168,6 +170,10 @@ class Status < ApplicationRecord
|
||||||
reblog
|
reblog
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def preview_card
|
||||||
|
preview_cards.first
|
||||||
|
end
|
||||||
|
|
||||||
def title
|
def title
|
||||||
if destroyed?
|
if destroyed?
|
||||||
"#{account.acct} deleted status"
|
"#{account.acct} deleted status"
|
||||||
|
@ -241,10 +247,6 @@ class Status < ApplicationRecord
|
||||||
before_validation :set_local
|
before_validation :set_local
|
||||||
|
|
||||||
class << self
|
class << self
|
||||||
def cache_ids
|
|
||||||
left_outer_joins(:status_stat).select('statuses.id, greatest(statuses.updated_at, status_stats.updated_at) AS updated_at')
|
|
||||||
end
|
|
||||||
|
|
||||||
def selectable_visibilities
|
def selectable_visibilities
|
||||||
visibilities.keys - %w(direct limited)
|
visibilities.keys - %w(direct limited)
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,4 +14,12 @@
|
||||||
|
|
||||||
class StatusStat < ApplicationRecord
|
class StatusStat < ApplicationRecord
|
||||||
belongs_to :status, inverse_of: :status_stat
|
belongs_to :status, inverse_of: :status_stat
|
||||||
|
|
||||||
|
after_commit :reset_parent_cache
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def reset_parent_cache
|
||||||
|
Rails.cache.delete("statuses/#{status_id}")
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,6 +21,8 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
||||||
has_many :tags
|
has_many :tags
|
||||||
has_many :emojis, serializer: REST::CustomEmojiSerializer
|
has_many :emojis, serializer: REST::CustomEmojiSerializer
|
||||||
|
|
||||||
|
has_one :preview_card, key: :card, serializer: REST::PreviewCardSerializer
|
||||||
|
|
||||||
def id
|
def id
|
||||||
object.id.to_s
|
object.id.to_s
|
||||||
end
|
end
|
||||||
|
|
|
@ -63,6 +63,7 @@ class FetchLinkCardService < BaseService
|
||||||
|
|
||||||
def attach_card
|
def attach_card
|
||||||
@status.preview_cards << @card
|
@status.preview_cards << @card
|
||||||
|
Rails.cache.delete(@status)
|
||||||
end
|
end
|
||||||
|
|
||||||
def parse_urls
|
def parse_urls
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
---
|
||||||
ast: {}
|
ast: {}
|
||||||
|
|
|
@ -21,8 +21,7 @@ ast:
|
||||||
hosted_on: Mastodon ta agospiáu en %{domain}
|
hosted_on: Mastodon ta agospiáu en %{domain}
|
||||||
learn_more: Deprendi más
|
learn_more: Deprendi más
|
||||||
source_code: Códigu fonte
|
source_code: Códigu fonte
|
||||||
status_count_after:
|
status_count_after: estaos
|
||||||
other: estaos
|
|
||||||
terms: Términos del serviciu
|
terms: Términos del serviciu
|
||||||
user_count_after: usuarios
|
user_count_after: usuarios
|
||||||
what_is_mastodon: "¿Qué ye Mastodon?"
|
what_is_mastodon: "¿Qué ye Mastodon?"
|
||||||
|
|
|
@ -30,22 +30,16 @@ cs:
|
||||||
other_instances: Seznam instancí
|
other_instances: Seznam instancí
|
||||||
privacy_policy: Zásady soukromí
|
privacy_policy: Zásady soukromí
|
||||||
source_code: Zdrojový kód
|
source_code: Zdrojový kód
|
||||||
status_count_after:
|
status_count_after: příspěvků
|
||||||
one: příspěvek
|
|
||||||
other: příspěvků
|
|
||||||
status_count_before: Kteří napsali
|
status_count_before: Kteří napsali
|
||||||
terms: Podmínky používání
|
terms: Podmínky používání
|
||||||
user_count_after:
|
user_count_after: uživatelů
|
||||||
one: uživatele
|
|
||||||
other: uživatelů
|
|
||||||
user_count_before: Domov
|
user_count_before: Domov
|
||||||
what_is_mastodon: Co je Mastodon?
|
what_is_mastodon: Co je Mastodon?
|
||||||
accounts:
|
accounts:
|
||||||
choices_html: 'Volby uživatele %{name}:'
|
choices_html: 'Volby uživatele %{name}:'
|
||||||
follow: Sledovat
|
follow: Sledovat
|
||||||
followers:
|
followers: Sledovatelé
|
||||||
one: Sledovatel
|
|
||||||
other: Sledovatelé
|
|
||||||
following: Sledovaní
|
following: Sledovaní
|
||||||
joined: Připojil/a se v %{date}
|
joined: Připojil/a se v %{date}
|
||||||
link_verified_on: Vlastnictví tohoto odkazu bylo zkontrolováno %{date}
|
link_verified_on: Vlastnictví tohoto odkazu bylo zkontrolováno %{date}
|
||||||
|
@ -57,9 +51,7 @@ cs:
|
||||||
people_who_follow: Lidé, kteří sledují uživatele %{name}
|
people_who_follow: Lidé, kteří sledují uživatele %{name}
|
||||||
pin_errors:
|
pin_errors:
|
||||||
following: Musíte již sledovat osobu, kterou chcete podpořit
|
following: Musíte již sledovat osobu, kterou chcete podpořit
|
||||||
posts:
|
posts: Tooty
|
||||||
one: Toot
|
|
||||||
other: Tooty
|
|
||||||
posts_tab_heading: Tooty
|
posts_tab_heading: Tooty
|
||||||
posts_with_replies: Tooty a odpovědi
|
posts_with_replies: Tooty a odpovědi
|
||||||
reserved_username: Toto uživatelské jméno je rezervováno
|
reserved_username: Toto uživatelské jméno je rezervováno
|
||||||
|
@ -268,9 +260,7 @@ cs:
|
||||||
suspend: Suspendovat
|
suspend: Suspendovat
|
||||||
severity: Přísnost
|
severity: Přísnost
|
||||||
show:
|
show:
|
||||||
affected_accounts:
|
affected_accounts: "%{count} účtů v databázi byl ovlivněn"
|
||||||
one: Jeden účet v databázi byl ovlivněn
|
|
||||||
other: "%{count} účtů v databázi byl ovlivněn"
|
|
||||||
retroactive:
|
retroactive:
|
||||||
silence: Odtišit všechny existující účty z této domény
|
silence: Odtišit všechny existující účty z této domény
|
||||||
suspend: Zrušit suspenzaci všech existujících účtů z této domény
|
suspend: Zrušit suspenzaci všech existujících účtů z této domény
|
||||||
|
@ -562,9 +552,7 @@ cs:
|
||||||
followers_count: Počet sledovatelů
|
followers_count: Počet sledovatelů
|
||||||
lock_link: Zamkněte svůj účet
|
lock_link: Zamkněte svůj účet
|
||||||
purge: Odstranit ze sledovatelů
|
purge: Odstranit ze sledovatelů
|
||||||
success:
|
success: V průběhu utišování sledovatelů z %{count} domén...
|
||||||
one: V průběhu utišování sledovatelů z jedné domény...
|
|
||||||
other: V průběhu utišování sledovatelů z %{count} domén...
|
|
||||||
true_privacy_html: Berte prosím na vědomí, že <strong>skutečného soukromí se dá dosáhnout pouze za pomoci end-to-end šifrování</strong>.
|
true_privacy_html: Berte prosím na vědomí, že <strong>skutečného soukromí se dá dosáhnout pouze za pomoci end-to-end šifrování</strong>.
|
||||||
unlocked_warning_html: Kdokoliv vás může sledovat a okamžitě vidět vaše soukromé příspěvky. %{lock_link}, abyste mohl/a zkontrolovat a odmítnout sledovatele.
|
unlocked_warning_html: Kdokoliv vás může sledovat a okamžitě vidět vaše soukromé příspěvky. %{lock_link}, abyste mohl/a zkontrolovat a odmítnout sledovatele.
|
||||||
unlocked_warning_title: Váš účet není zamknutý
|
unlocked_warning_title: Váš účet není zamknutý
|
||||||
|
@ -575,9 +563,7 @@ cs:
|
||||||
generic:
|
generic:
|
||||||
changes_saved_msg: Změny byly úspěšně uloženy!
|
changes_saved_msg: Změny byly úspěšně uloženy!
|
||||||
save_changes: Uložit změny
|
save_changes: Uložit změny
|
||||||
validation_errors:
|
validation_errors: Něco ještě není úplně v pořádku! Prosím zkontrolujte %{count} chyb níže
|
||||||
one: Něco ještě není úplně v pořádku! Prosím zkontrolujte chybu níže
|
|
||||||
other: Něco ještě není úplně v pořádku! Prosím zkontrolujte %{count} chyb níže
|
|
||||||
imports:
|
imports:
|
||||||
preface: Můžete importovat data, která jste exportoval/a z jiné instance, jako například seznam lidí, které sledujete či blokujete.
|
preface: Můžete importovat data, která jste exportoval/a z jiné instance, jako například seznam lidí, které sledujete či blokujete.
|
||||||
success: Vaše data byla úspěšně nahrána a nyní budou zpracována v daný čas
|
success: Vaše data byla úspěšně nahrána a nyní budou zpracována v daný čas
|
||||||
|
@ -600,9 +586,7 @@ cs:
|
||||||
expires_in_prompt: Nikdy
|
expires_in_prompt: Nikdy
|
||||||
generate: Vygenerovat
|
generate: Vygenerovat
|
||||||
invited_by: 'Byl/a jste pozván/a uživatelem:'
|
invited_by: 'Byl/a jste pozván/a uživatelem:'
|
||||||
max_uses:
|
max_uses: "%{count} použití"
|
||||||
one: 1 použití
|
|
||||||
other: "%{count} použití"
|
|
||||||
max_uses_prompt: Bez limitu
|
max_uses_prompt: Bez limitu
|
||||||
prompt: Vygenerujte a sdílejte s ostatními odkazy a umožněte jim přístup na tuto instanci
|
prompt: Vygenerujte a sdílejte s ostatními odkazy a umožněte jim přístup na tuto instanci
|
||||||
table:
|
table:
|
||||||
|
@ -628,12 +612,8 @@ cs:
|
||||||
action: Zobrazit všechna oznámení
|
action: Zobrazit všechna oznámení
|
||||||
body: Zde najdete stručný souhrn zpráv, které jste zmeškal/a od vaší poslední návštěvy %{since}
|
body: Zde najdete stručný souhrn zpráv, které jste zmeškal/a od vaší poslední návštěvy %{since}
|
||||||
mention: "%{name} vás zmínil/a v:"
|
mention: "%{name} vás zmínil/a v:"
|
||||||
new_followers_summary:
|
new_followers_summary: Navíc jste získal/a %{count} nových sledovatelů, zatímco jste byl/a pryč! Hurá!
|
||||||
one: Navíc jste získal/a jednoho nového sledovatele, zatímco jste byl/a pryč! Hurá!
|
subject: "%{count} nových oznámení od vaší poslední návštěvy \U0001F418"
|
||||||
other: Navíc jste získal/a %{count} nových sledovatelů, zatímco jste byl/a pryč! Hurá!
|
|
||||||
subject:
|
|
||||||
one: "Jedno nové oznámení od vaší poslední návštěvy \U0001F418"
|
|
||||||
other: "%{count} nových oznámení od vaší poslední návštěvy \U0001F418"
|
|
||||||
title: Ve vaší absenci...
|
title: Ve vaší absenci...
|
||||||
favourite:
|
favourite:
|
||||||
body: 'Váš příspěvek si oblíbil/a %{name}:'
|
body: 'Váš příspěvek si oblíbil/a %{name}:'
|
||||||
|
@ -750,17 +730,11 @@ cs:
|
||||||
statuses:
|
statuses:
|
||||||
attached:
|
attached:
|
||||||
description: 'Přiloženo: %{attached}'
|
description: 'Přiloženo: %{attached}'
|
||||||
image:
|
image: "%{count} obrázků"
|
||||||
one: "%{count} obrázek"
|
video: "%{count} videí"
|
||||||
other: "%{count} obrázků"
|
|
||||||
video:
|
|
||||||
one: "%{count} video"
|
|
||||||
other: "%{count} videí"
|
|
||||||
boosted_from_html: Boostnuto z %{acct_link}
|
boosted_from_html: Boostnuto z %{acct_link}
|
||||||
content_warning: 'Varování o obsahu: %{warning}'
|
content_warning: 'Varování o obsahu: %{warning}'
|
||||||
disallowed_hashtags:
|
disallowed_hashtags: 'obsahuje nepovolené hashtagy: %{tags}'
|
||||||
one: 'obsahuje nepovolený hashtag: %{tags}'
|
|
||||||
other: 'obsahuje nepovolené hashtagy: %{tags}'
|
|
||||||
language_detection: Zjistit jazyk automaticky
|
language_detection: Zjistit jazyk automaticky
|
||||||
open_in_web: Otevřít na webu
|
open_in_web: Otevřít na webu
|
||||||
over_character_limit: limit %{max} znaků byl překročen
|
over_character_limit: limit %{max} znaků byl překročen
|
||||||
|
|
|
@ -30,22 +30,16 @@ cy:
|
||||||
other_instances: Rhestr achosion
|
other_instances: Rhestr achosion
|
||||||
privacy_policy: Polisi preifatrwydd
|
privacy_policy: Polisi preifatrwydd
|
||||||
source_code: Cod ffynhonnell
|
source_code: Cod ffynhonnell
|
||||||
status_count_after:
|
status_count_after: statws
|
||||||
one: statws
|
|
||||||
other: statws
|
|
||||||
status_count_before: Pwy ysgrifennodd
|
status_count_before: Pwy ysgrifennodd
|
||||||
terms: Telerau gwasanaeth
|
terms: Telerau gwasanaeth
|
||||||
user_count_after:
|
user_count_after: defnyddwyr
|
||||||
one: defnyddiwr
|
|
||||||
other: defnyddwyr
|
|
||||||
user_count_before: Cartref i
|
user_count_before: Cartref i
|
||||||
what_is_mastodon: Beth yw Mastodon?
|
what_is_mastodon: Beth yw Mastodon?
|
||||||
accounts:
|
accounts:
|
||||||
choices_html: 'Dewisiadau %{name}:'
|
choices_html: 'Dewisiadau %{name}:'
|
||||||
follow: Dilynwch
|
follow: Dilynwch
|
||||||
followers:
|
followers: Dilynwyr
|
||||||
one: Dilynwr
|
|
||||||
other: Dilynwyr
|
|
||||||
following: Yn dilyn
|
following: Yn dilyn
|
||||||
joined: Ymunodd %{date}
|
joined: Ymunodd %{date}
|
||||||
media: Cyfryngau
|
media: Cyfryngau
|
||||||
|
@ -56,9 +50,7 @@ cy:
|
||||||
people_who_follow: Pobl sy'n dilyn %{name}
|
people_who_follow: Pobl sy'n dilyn %{name}
|
||||||
pin_errors:
|
pin_errors:
|
||||||
following: Rhaid i ti fod yn dilyn y person yr ydych am ei gymeradwyo yn barod
|
following: Rhaid i ti fod yn dilyn y person yr ydych am ei gymeradwyo yn barod
|
||||||
posts:
|
posts: Tŵtiau
|
||||||
one: Tŵt
|
|
||||||
other: Tŵtiau
|
|
||||||
posts_tab_heading: Tŵtiau
|
posts_tab_heading: Tŵtiau
|
||||||
posts_with_replies: Tŵtiau ac atebion
|
posts_with_replies: Tŵtiau ac atebion
|
||||||
reserved_username: Mae'r enw defnyddior yn neilltuedig
|
reserved_username: Mae'r enw defnyddior yn neilltuedig
|
||||||
|
@ -262,9 +254,7 @@ cy:
|
||||||
suspend: Atal
|
suspend: Atal
|
||||||
severity: Difrifoldeb
|
severity: Difrifoldeb
|
||||||
show:
|
show:
|
||||||
affected_accounts:
|
affected_accounts: "%{count} o gyfrifoedd yn y bas data wedi eu hefeithio"
|
||||||
one: Mae un cyfri yn y bas data wedi ei effeithio
|
|
||||||
other: "%{count} o gyfrifoedd yn y bas data wedi eu hefeithio"
|
|
||||||
retroactive:
|
retroactive:
|
||||||
silence: Dad-dawelu pob cyfri presennol o'r parth hwn
|
silence: Dad-dawelu pob cyfri presennol o'r parth hwn
|
||||||
suspend: Dad-atal pob cyfrif o'r parth hwn sy'n bodoli
|
suspend: Dad-atal pob cyfrif o'r parth hwn sy'n bodoli
|
||||||
|
@ -508,9 +498,7 @@ cy:
|
||||||
generic:
|
generic:
|
||||||
changes_saved_msg: Llwyddwyd i gadw y newidiadau!
|
changes_saved_msg: Llwyddwyd i gadw y newidiadau!
|
||||||
save_changes: Cadw newidiadau
|
save_changes: Cadw newidiadau
|
||||||
validation_errors:
|
validation_errors: Mae rhywbeth o'i le o hyd! Edrychwch ar y %{count} gwall isod os gwelwch yn dda
|
||||||
one: Mae rhywbeth o'i le o hyd! Edrychwch ar y gwall isod os gwelwch yn dda
|
|
||||||
other: Mae rhywbeth o'i le o hyd! Edrychwch ar y %{count} gwall isod os gwelwch yn dda
|
|
||||||
imports:
|
imports:
|
||||||
preface: Mae modd mewnforio data yr ydych wedi allforio o achos arall, megis rhestr o bobl yr ydych yn ei ddilyn neu yn blocio.
|
preface: Mae modd mewnforio data yr ydych wedi allforio o achos arall, megis rhestr o bobl yr ydych yn ei ddilyn neu yn blocio.
|
||||||
success: Uwchlwyddwyd eich data yn llwyddiannus ac fe fydd yn cael ei brosesu mewn da bryd
|
success: Uwchlwyddwyd eich data yn llwyddiannus ac fe fydd yn cael ei brosesu mewn da bryd
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
---
|
||||||
ast: {}
|
ast: {}
|
||||||
|
|
|
@ -77,6 +77,4 @@ cs:
|
||||||
expired: vypršel, prosím vyžádejte si nový
|
expired: vypršel, prosím vyžádejte si nový
|
||||||
not_found: nenalezen
|
not_found: nenalezen
|
||||||
not_locked: nebyl uzamčen
|
not_locked: nebyl uzamčen
|
||||||
not_saved:
|
not_saved: "%{count} chyb zabránila uložení tohoto %{resource}:"
|
||||||
one: '1 chyba zabránila uložení tohoto %{resource}:'
|
|
||||||
other: "%{count} chyb zabránila uložení tohoto %{resource}:"
|
|
||||||
|
|
|
@ -77,6 +77,4 @@ cy:
|
||||||
expired: wedi dod i ben, gwnewch gais am un newydd os gwelwch yn dda
|
expired: wedi dod i ben, gwnewch gais am un newydd os gwelwch yn dda
|
||||||
not_found: heb ei ganfod
|
not_found: heb ei ganfod
|
||||||
not_locked: heb ei gloi
|
not_locked: heb ei gloi
|
||||||
not_saved:
|
not_saved: 'Gwaharddwyd yr %{resource} rhag cael ei arbed oherwydd %{count} gwall:'
|
||||||
one: 'Gwaharddwyd yr %{resource} rhag cael ei arbed oherwydd 1 gwall:'
|
|
||||||
other: 'Gwaharddwyd yr %{resource} rhag cael ei arbed oherwydd %{count} gwall:'
|
|
||||||
|
|
|
@ -58,6 +58,4 @@ hr:
|
||||||
expired: je istekao, zatraži novu
|
expired: je istekao, zatraži novu
|
||||||
not_found: nije nađen
|
not_found: nije nađen
|
||||||
not_locked: nije zaključan
|
not_locked: nije zaključan
|
||||||
not_saved:
|
not_saved: "%{count} greške su zabranile da ovaj %{resource} bude sačuvan:"
|
||||||
one: '1 greška je zabranila da ovaj %{resource} bude sačuvan:'
|
|
||||||
other: "%{count} greške su zabranile da ovaj %{resource} bude sačuvan:"
|
|
||||||
|
|
|
@ -77,6 +77,4 @@ pl:
|
||||||
expired: wygasło, poproś o nowe
|
expired: wygasło, poproś o nowe
|
||||||
not_found: nie znaleziono
|
not_found: nie znaleziono
|
||||||
not_locked: było zablokowane
|
not_locked: było zablokowane
|
||||||
not_saved:
|
not_saved: 'Błędy (%{count}) uniemożliwiły zapisanie zasobu %{resource}:'
|
||||||
one: '1 błąd uniemożliwił zapisanie zasobu %{resource}:'
|
|
||||||
other: 'Błędy (%{count}) uniemożliwiły zapisanie zasobu %{resource}:'
|
|
||||||
|
|
|
@ -77,6 +77,4 @@ zh-TW:
|
||||||
expired: 已經過期,請重新申請
|
expired: 已經過期,請重新申請
|
||||||
not_found: 找不到
|
not_found: 找不到
|
||||||
not_locked: 並未被鎖定
|
not_locked: 並未被鎖定
|
||||||
not_saved:
|
not_saved: "%{count} 個錯誤使 %{resource} 無法被儲存︰"
|
||||||
one: 1 個錯誤使 %{resource} 無法被儲存︰
|
|
||||||
other: "%{count} 個錯誤使 %{resource} 無法被儲存︰"
|
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
---
|
||||||
ast: {}
|
ast: {}
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
---
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -61,9 +61,7 @@ hr:
|
||||||
generic:
|
generic:
|
||||||
changes_saved_msg: Izmjene su uspješno sačuvane!
|
changes_saved_msg: Izmjene su uspješno sačuvane!
|
||||||
save_changes: Sačuvaj izmjene
|
save_changes: Sačuvaj izmjene
|
||||||
validation_errors:
|
validation_errors: Nešto još uvijek ne štima! Vidi %{count} greške ispod
|
||||||
one: Nešto ne štima! Vidi grešku ispod
|
|
||||||
other: Nešto još uvijek ne štima! Vidi %{count} greške ispod
|
|
||||||
imports:
|
imports:
|
||||||
preface: Možeš uvesti određene podatke kao što su svi ljudi koje slijediš ili blokiraš u svoj račun na ovoj instanci, sa fajlova kreiranih izvozom sa druge instance.
|
preface: Možeš uvesti određene podatke kao što su svi ljudi koje slijediš ili blokiraš u svoj račun na ovoj instanci, sa fajlova kreiranih izvozom sa druge instance.
|
||||||
success: Tvoji podaci su uspješno uploadani i bit će obrađeni u dogledno vrijeme
|
success: Tvoji podaci su uspješno uploadani i bit će obrađeni u dogledno vrijeme
|
||||||
|
@ -76,12 +74,8 @@ hr:
|
||||||
digest:
|
digest:
|
||||||
body: 'Ovo je kratak sažetak propuštenog od tvog prošlog posjeta %{since}:'
|
body: 'Ovo je kratak sažetak propuštenog od tvog prošlog posjeta %{since}:'
|
||||||
mention: "%{name} te je spomenuo:"
|
mention: "%{name} te je spomenuo:"
|
||||||
new_followers_summary:
|
new_followers_summary: Imaš %{count} novih sljedbenika! Prekrašno!
|
||||||
one: Imaš novog sljedbenika! Yay!
|
subject: "%{count} novih notifikacija od tvog prošlog posjeta \U0001F418"
|
||||||
other: Imaš %{count} novih sljedbenika! Prekrašno!
|
|
||||||
subject:
|
|
||||||
one: "1 nova notifikacija od tvog prošlog posjeta \U0001F418"
|
|
||||||
other: "%{count} novih notifikacija od tvog prošlog posjeta \U0001F418"
|
|
||||||
favourite:
|
favourite:
|
||||||
body: 'Tvoj status je %{name} označio kao omiljen:'
|
body: 'Tvoj status je %{name} označio kao omiljen:'
|
||||||
subject: "%{name} je označio kao omiljen tvoj status"
|
subject: "%{name} je označio kao omiljen tvoj status"
|
||||||
|
|
|
@ -279,10 +279,7 @@ pl:
|
||||||
suspend: Zawieś
|
suspend: Zawieś
|
||||||
severity: Priorytet
|
severity: Priorytet
|
||||||
show:
|
show:
|
||||||
affected_accounts:
|
affected_accounts: Dotyczy %{count} kont w bazie danych
|
||||||
many: Dotyczy %{count} kont w bazie danych
|
|
||||||
one: Dotyczy jednego konta w bazie danych
|
|
||||||
other: Dotyczy %{count} kont w bazie danych
|
|
||||||
retroactive:
|
retroactive:
|
||||||
silence: Odwołaj wyciszenie wszystkich kont w tej domenie
|
silence: Odwołaj wyciszenie wszystkich kont w tej domenie
|
||||||
suspend: Odwołaj zawieszenie wszystkich kont w tej domenie
|
suspend: Odwołaj zawieszenie wszystkich kont w tej domenie
|
||||||
|
@ -577,9 +574,7 @@ pl:
|
||||||
followers_count: Liczba śledzących
|
followers_count: Liczba śledzących
|
||||||
lock_link: Zablokuj swoje konto
|
lock_link: Zablokuj swoje konto
|
||||||
purge: Przestań śledzić
|
purge: Przestań śledzić
|
||||||
success:
|
success: W trakcie usuwania śledzących z %{count} domen…
|
||||||
one: W trakcie usuwania śledzących z jednej domeny…
|
|
||||||
other: W trakcie usuwania śledzących z %{count} domen…
|
|
||||||
true_privacy_html: Pamiętaj, że <strong>rzeczywista prywatność może zostać uzyskana wyłącznie dzięki szyfrowaniu end-to-end</strong>.
|
true_privacy_html: Pamiętaj, że <strong>rzeczywista prywatność może zostać uzyskana wyłącznie dzięki szyfrowaniu end-to-end</strong>.
|
||||||
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_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
|
unlocked_warning_title: Twoje konto nie jest zablokowane
|
||||||
|
@ -788,9 +783,7 @@ pl:
|
||||||
other: "%{count} filmów"
|
other: "%{count} filmów"
|
||||||
boosted_from_html: Podbito przez %{acct_link}
|
boosted_from_html: Podbito przez %{acct_link}
|
||||||
content_warning: 'Ostrzeżenie o zawartości: %{warning}'
|
content_warning: 'Ostrzeżenie o zawartości: %{warning}'
|
||||||
disallowed_hashtags:
|
disallowed_hashtags: 'zawiera niedozwolone hashtagi: %{tags}'
|
||||||
one: 'zawiera niedozwolony hashtag: %{tags}'
|
|
||||||
other: 'zawiera niedozwolone hashtagi: %{tags}'
|
|
||||||
language_detection: Automatycznie wykrywaj język
|
language_detection: Automatycznie wykrywaj język
|
||||||
open_in_web: Otwórz w przeglądarce
|
open_in_web: Otwórz w przeglądarce
|
||||||
over_character_limit: limit %{max} znaków przekroczony
|
over_character_limit: limit %{max} znaków przekroczony
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
|
---
|
||||||
{}
|
{}
|
||||||
|
|
|
@ -30,22 +30,16 @@ sk:
|
||||||
other_instances: Zoznam ďalších inštancií
|
other_instances: Zoznam ďalších inštancií
|
||||||
privacy_policy: Ustanovenia o súkromí
|
privacy_policy: Ustanovenia o súkromí
|
||||||
source_code: Zdrojový kód
|
source_code: Zdrojový kód
|
||||||
status_count_after:
|
status_count_after: statusy
|
||||||
one: status
|
|
||||||
other: statusy
|
|
||||||
status_count_before: Ktorí napísali
|
status_count_before: Ktorí napísali
|
||||||
terms: Podmienky užívania
|
terms: Podmienky užívania
|
||||||
user_count_after:
|
user_count_after: užívateľov
|
||||||
one: užívateľ
|
|
||||||
other: užívateľov
|
|
||||||
user_count_before: Domov pre
|
user_count_before: Domov pre
|
||||||
what_is_mastodon: Čo je Mastodon?
|
what_is_mastodon: Čo je Mastodon?
|
||||||
accounts:
|
accounts:
|
||||||
choices_html: "%{name}vé voľby:"
|
choices_html: "%{name}vé voľby:"
|
||||||
follow: Sledovať
|
follow: Sledovať
|
||||||
followers:
|
followers: Sledovatelia
|
||||||
one: Následovateľ
|
|
||||||
other: Sledovatelia
|
|
||||||
following: Sledovaní
|
following: Sledovaní
|
||||||
joined: Pridal/a sa %{date}
|
joined: Pridal/a sa %{date}
|
||||||
media: Médiá
|
media: Médiá
|
||||||
|
@ -56,9 +50,7 @@ sk:
|
||||||
people_who_follow: Ľudia sledujúci %{name}
|
people_who_follow: Ľudia sledujúci %{name}
|
||||||
pin_errors:
|
pin_errors:
|
||||||
following: Musíš už následovať toho človeka, ktorého si praješ zviditeľniť
|
following: Musíš už následovať toho človeka, ktorého si praješ zviditeľniť
|
||||||
posts:
|
posts: Príspevky
|
||||||
one: Príspevok
|
|
||||||
other: Príspevky
|
|
||||||
posts_tab_heading: Príspevky
|
posts_tab_heading: Príspevky
|
||||||
posts_with_replies: Príspevky s odpoveďami
|
posts_with_replies: Príspevky s odpoveďami
|
||||||
reserved_username: Prihlasovacie meno je rezervované
|
reserved_username: Prihlasovacie meno je rezervované
|
||||||
|
@ -746,9 +738,7 @@ sk:
|
||||||
other: "%{count} videí"
|
other: "%{count} videí"
|
||||||
boosted_from_html: Povýšené od %{acct_link}
|
boosted_from_html: Povýšené od %{acct_link}
|
||||||
content_warning: 'Varovanie o obsahu: %{warning}'
|
content_warning: 'Varovanie o obsahu: %{warning}'
|
||||||
disallowed_hashtags:
|
disallowed_hashtags: 'obsahuje nepovolené haštagy: %{tags}'
|
||||||
one: 'obsahuje nepovolený haštag: %{tags}'
|
|
||||||
other: 'obsahuje nepovolené haštagy: %{tags}'
|
|
||||||
language_detection: Zisti jazyk automaticky
|
language_detection: Zisti jazyk automaticky
|
||||||
open_in_web: Otvor v okne prehliadača
|
open_in_web: Otvor v okne prehliadača
|
||||||
over_character_limit: limit počtu %{max} znakov bol presiahnutý
|
over_character_limit: limit počtu %{max} znakov bol presiahnutý
|
||||||
|
|
|
@ -30,22 +30,16 @@ sr:
|
||||||
other_instances: Листа инстанци
|
other_instances: Листа инстанци
|
||||||
privacy_policy: Полиса приватности
|
privacy_policy: Полиса приватности
|
||||||
source_code: Изворни код
|
source_code: Изворни код
|
||||||
status_count_after:
|
status_count_after: статуси
|
||||||
one: статус
|
|
||||||
other: статуси
|
|
||||||
status_count_before: Који су написали
|
status_count_before: Који су написали
|
||||||
terms: Услови коришћења
|
terms: Услови коришћења
|
||||||
user_count_after:
|
user_count_after: корисници
|
||||||
one: корисник
|
|
||||||
other: корисници
|
|
||||||
user_count_before: Дом за
|
user_count_before: Дом за
|
||||||
what_is_mastodon: Шта је Мастодон?
|
what_is_mastodon: Шта је Мастодон?
|
||||||
accounts:
|
accounts:
|
||||||
choices_html: "%{name}'s избори:"
|
choices_html: "%{name}'s избори:"
|
||||||
follow: Запрати
|
follow: Запрати
|
||||||
followers:
|
followers: Пратиоци
|
||||||
one: Пратилац
|
|
||||||
other: Пратиоци
|
|
||||||
following: Пратим
|
following: Пратим
|
||||||
joined: Придружио/ла се %{date}
|
joined: Придружио/ла се %{date}
|
||||||
media: Медији
|
media: Медији
|
||||||
|
@ -56,9 +50,7 @@ sr:
|
||||||
people_who_follow: Људи који прате %{name}
|
people_who_follow: Људи који прате %{name}
|
||||||
pin_errors:
|
pin_errors:
|
||||||
following: Морате пратити ову особу ако хоћете да потврдите
|
following: Морате пратити ову особу ако хоћете да потврдите
|
||||||
posts:
|
posts: Трубе
|
||||||
one: Труба
|
|
||||||
other: Трубе
|
|
||||||
posts_tab_heading: Трубе
|
posts_tab_heading: Трубе
|
||||||
posts_with_replies: Трубе и одговори
|
posts_with_replies: Трубе и одговори
|
||||||
reserved_username: Корисничко име је резервисано
|
reserved_username: Корисничко име је резервисано
|
||||||
|
@ -754,17 +746,11 @@ sr:
|
||||||
statuses:
|
statuses:
|
||||||
attached:
|
attached:
|
||||||
description: 'У прилогу: %{attached}'
|
description: 'У прилогу: %{attached}'
|
||||||
image:
|
image: "%{count} слике"
|
||||||
one: "%{count} слику"
|
video: "%{count} видеа"
|
||||||
other: "%{count} слике"
|
|
||||||
video:
|
|
||||||
one: "%{count} видео"
|
|
||||||
other: "%{count} видеа"
|
|
||||||
boosted_from_html: Подржано од %{acct_link}
|
boosted_from_html: Подржано од %{acct_link}
|
||||||
content_warning: 'Упозорење на садржај: %{warning}'
|
content_warning: 'Упозорење на садржај: %{warning}'
|
||||||
disallowed_hashtags:
|
disallowed_hashtags: 'садржи забрањене тарабе: %{tags}'
|
||||||
one: 'садржи забрањену тарабу: %{tags}'
|
|
||||||
other: 'садржи забрањене тарабе: %{tags}'
|
|
||||||
language_detection: Аутоматскo откривање језика
|
language_detection: Аутоматскo откривање језика
|
||||||
open_in_web: Отвори у вебу
|
open_in_web: Отвори у вебу
|
||||||
over_character_limit: ограничење од %{max} карактера прекорачено
|
over_character_limit: ограничење од %{max} карактера прекорачено
|
||||||
|
|
|
@ -518,18 +518,14 @@ uk:
|
||||||
followers_count: Кількість підписників
|
followers_count: Кількість підписників
|
||||||
lock_link: Закрийте акаунт
|
lock_link: Закрийте акаунт
|
||||||
purge: Видалити з підписників
|
purge: Видалити з підписників
|
||||||
success:
|
success: У процесі м'якого блокування підписників з %{count} доменів...
|
||||||
one: У процесі м'якого блокування підписників з одного домену...
|
|
||||||
other: У процесі м'якого блокування підписників з %{count} доменів...
|
|
||||||
true_privacy_html: Будь ласка, помітьте, що <strong>справжняя конфіденційність може бути досягнена тільки за допомогою end-to-end шифрування</strong>.
|
true_privacy_html: Будь ласка, помітьте, що <strong>справжняя конфіденційність може бути досягнена тільки за допомогою end-to-end шифрування</strong>.
|
||||||
unlocked_warning_html: Хто завгодно може підписатися на Вас та отримати доступ до перегляду Ваших приватних статусів. %{lock_link}, щоб отримати можливість роздивлятися та вручну підтверджувати запити щодо підписки.
|
unlocked_warning_html: Хто завгодно може підписатися на Вас та отримати доступ до перегляду Ваших приватних статусів. %{lock_link}, щоб отримати можливість роздивлятися та вручну підтверджувати запити щодо підписки.
|
||||||
unlocked_warning_title: Ваш аккаунт не закритий для підписки
|
unlocked_warning_title: Ваш аккаунт не закритий для підписки
|
||||||
generic:
|
generic:
|
||||||
changes_saved_msg: Зміни успішно збережені!
|
changes_saved_msg: Зміни успішно збережені!
|
||||||
save_changes: Зберегти зміни
|
save_changes: Зберегти зміни
|
||||||
validation_errors:
|
validation_errors: Щось тут не так! Будь ласка, ознайомтеся з %{count} помилками нижче
|
||||||
one: Щось тут не так! Будь ласка, ознайомтеся з помилкою нижче
|
|
||||||
other: Щось тут не так! Будь ласка, ознайомтеся з %{count} помилками нижче
|
|
||||||
imports:
|
imports:
|
||||||
preface: Вы можете завантажити деякі дані, наприклад, списки людей, на яких Ви підписані чи яких блокуєте, в Ваш акаунт на цій інстанції з файлів, експортованих з іншої інстанції.
|
preface: Вы можете завантажити деякі дані, наприклад, списки людей, на яких Ви підписані чи яких блокуєте, в Ваш акаунт на цій інстанції з файлів, експортованих з іншої інстанції.
|
||||||
success: Ваші дані були успішно загружені та будуть оброблені в найближчий момент
|
success: Ваші дані були успішно загружені та будуть оброблені в найближчий момент
|
||||||
|
@ -552,9 +548,7 @@ uk:
|
||||||
expires_in_prompt: Ніколи
|
expires_in_prompt: Ніколи
|
||||||
generate: Згенерувати
|
generate: Згенерувати
|
||||||
invited_by: 'Вас запросив(-ла):'
|
invited_by: 'Вас запросив(-ла):'
|
||||||
max_uses:
|
max_uses: "%{count} використань"
|
||||||
one: 1 використання
|
|
||||||
other: "%{count} використань"
|
|
||||||
max_uses_prompt: Без обмеження
|
max_uses_prompt: Без обмеження
|
||||||
prompt: Генеруйте та діліться посиланням з іншими для надання доступу до сайту
|
prompt: Генеруйте та діліться посиланням з іншими для надання доступу до сайту
|
||||||
table:
|
table:
|
||||||
|
@ -703,17 +697,11 @@ uk:
|
||||||
statuses:
|
statuses:
|
||||||
attached:
|
attached:
|
||||||
description: 'Прикріплено: %{attached}'
|
description: 'Прикріплено: %{attached}'
|
||||||
image:
|
image: "%{count} картинки"
|
||||||
one: "%{count} картинка"
|
video: "%{count} відео"
|
||||||
other: "%{count} картинки"
|
|
||||||
video:
|
|
||||||
one: "%{count} відео"
|
|
||||||
other: "%{count} відео"
|
|
||||||
boosted_from_html: Просунуто від %{acct_link}
|
boosted_from_html: Просунуто від %{acct_link}
|
||||||
content_warning: 'Попередження про контент: %{warning}'
|
content_warning: 'Попередження про контент: %{warning}'
|
||||||
disallowed_hashtags:
|
disallowed_hashtags: 'містив заборонені хештеґи: %{tags}'
|
||||||
one: 'містив заборонений хештеґ: %{tags}'
|
|
||||||
other: 'містив заборонені хештеґи: %{tags}'
|
|
||||||
language_detection: Автоматично визначати мову
|
language_detection: Автоматично визначати мову
|
||||||
open_in_web: Відкрити у вебі
|
open_in_web: Відкрити у вебі
|
||||||
over_character_limit: перевищено ліміт символів (%{max})
|
over_character_limit: перевищено ліміт символів (%{max})
|
||||||
|
|
|
@ -30,13 +30,10 @@ zh-CN:
|
||||||
other_instances: 其他实例
|
other_instances: 其他实例
|
||||||
privacy_policy: 隐私政策
|
privacy_policy: 隐私政策
|
||||||
source_code: 源代码
|
source_code: 源代码
|
||||||
status_count_after:
|
status_count_after: 条嘟文
|
||||||
one: 条嘟文
|
|
||||||
status_count_before: 他们共嘟出了
|
status_count_before: 他们共嘟出了
|
||||||
terms: 使用条款
|
terms: 使用条款
|
||||||
user_count_after:
|
user_count_after: 位用户
|
||||||
one: 位用户
|
|
||||||
other: 位用户
|
|
||||||
user_count_before: 这里共注册有
|
user_count_before: 这里共注册有
|
||||||
what_is_mastodon: Mastodon 是什么?
|
what_is_mastodon: Mastodon 是什么?
|
||||||
accounts:
|
accounts:
|
||||||
|
|
|
@ -29,18 +29,15 @@ zh-TW:
|
||||||
learn_more: 了解詳細
|
learn_more: 了解詳細
|
||||||
other_instances: 其他站點
|
other_instances: 其他站點
|
||||||
source_code: 原始碼
|
source_code: 原始碼
|
||||||
status_count_after:
|
status_count_after: 狀態
|
||||||
one: 狀態
|
|
||||||
status_count_before: 他們共嘟出了
|
status_count_before: 他們共嘟出了
|
||||||
terms: 使用條款
|
terms: 使用條款
|
||||||
user_count_after:
|
user_count_after: 使用者
|
||||||
one: 使用者
|
|
||||||
user_count_before: 這裡共註冊有
|
user_count_before: 這裡共註冊有
|
||||||
what_is_mastodon: 什麼是 Mastodon?
|
what_is_mastodon: 什麼是 Mastodon?
|
||||||
accounts:
|
accounts:
|
||||||
follow: 關注
|
follow: 關注
|
||||||
followers:
|
followers: 關注者
|
||||||
other: 關注者
|
|
||||||
following: 正在關注
|
following: 正在關注
|
||||||
media: 媒體
|
media: 媒體
|
||||||
moved_html: "%{name} 已經搬遷到 %{new_profile_link}:"
|
moved_html: "%{name} 已經搬遷到 %{new_profile_link}:"
|
||||||
|
@ -48,9 +45,7 @@ zh-TW:
|
||||||
nothing_here: 暫時沒有內容可供顯示!
|
nothing_here: 暫時沒有內容可供顯示!
|
||||||
people_followed_by: "%{name} 關注的人"
|
people_followed_by: "%{name} 關注的人"
|
||||||
people_who_follow: 關注 %{name} 的人
|
people_who_follow: 關注 %{name} 的人
|
||||||
posts:
|
posts: 嘟文
|
||||||
one: 嘟掉
|
|
||||||
other: 嘟文
|
|
||||||
posts_tab_heading: 嘟文
|
posts_tab_heading: 嘟文
|
||||||
posts_with_replies: 嘟文與回覆
|
posts_with_replies: 嘟文與回覆
|
||||||
reserved_username: 此用戶名已被保留
|
reserved_username: 此用戶名已被保留
|
||||||
|
@ -234,9 +229,7 @@ zh-TW:
|
||||||
suspend: 自動封鎖
|
suspend: 自動封鎖
|
||||||
severity: 嚴重度
|
severity: 嚴重度
|
||||||
show:
|
show:
|
||||||
affected_accounts:
|
affected_accounts: 資料庫中有%{count}個使用者受影響
|
||||||
one: 資料庫中有一個使用者受到影響
|
|
||||||
other: 資料庫中有%{count}個使用者受影響
|
|
||||||
retroactive:
|
retroactive:
|
||||||
silence: 對此網域的所有使用者取消靜音
|
silence: 對此網域的所有使用者取消靜音
|
||||||
suspend: 對此網域的所有使用者取消封鎖
|
suspend: 對此網域的所有使用者取消封鎖
|
||||||
|
@ -480,18 +473,14 @@ zh-TW:
|
||||||
followers_count: 關注者數量
|
followers_count: 關注者數量
|
||||||
lock_link: 將你的帳戶設定為私人
|
lock_link: 將你的帳戶設定為私人
|
||||||
purge: 移除關注者
|
purge: 移除關注者
|
||||||
success:
|
success: 正準備軟性封鎖 %{count} 個網域的關注者……
|
||||||
one: 正準備軟性封鎖 1 個網域的關注者……
|
|
||||||
other: 正準備軟性封鎖 %{count} 個網域的關注者……
|
|
||||||
true_privacy_html: 請謹記,唯有<strong>點對點加密方可以真正確保你的隱私</strong>。
|
true_privacy_html: 請謹記,唯有<strong>點對點加密方可以真正確保你的隱私</strong>。
|
||||||
unlocked_warning_html: 任何人都可以在關注你後立即查看非公開的嘟文。只要%{lock_link},你就可以審核並拒絕關注請求。
|
unlocked_warning_html: 任何人都可以在關注你後立即查看非公開的嘟文。只要%{lock_link},你就可以審核並拒絕關注請求。
|
||||||
unlocked_warning_title: 你的帳戶是公開的
|
unlocked_warning_title: 你的帳戶是公開的
|
||||||
generic:
|
generic:
|
||||||
changes_saved_msg: 已成功儲存修改!
|
changes_saved_msg: 已成功儲存修改!
|
||||||
save_changes: 儲存修改
|
save_changes: 儲存修改
|
||||||
validation_errors:
|
validation_errors: 送出的資料有 %{count} 個問題
|
||||||
one: 送出的資料有問題
|
|
||||||
other: 送出的資料有 %{count} 個問題
|
|
||||||
imports:
|
imports:
|
||||||
preface: 您可以在此匯入您在其他站點所匯出的資料檔,包括關注的使用者、封鎖的使用者名單。
|
preface: 您可以在此匯入您在其他站點所匯出的資料檔,包括關注的使用者、封鎖的使用者名單。
|
||||||
success: 資料檔上傳成功,正在匯入,請稍候
|
success: 資料檔上傳成功,正在匯入,請稍候
|
||||||
|
@ -514,9 +503,7 @@ zh-TW:
|
||||||
expires_in_prompt: 永不過期
|
expires_in_prompt: 永不過期
|
||||||
generate: 建立邀請連結
|
generate: 建立邀請連結
|
||||||
invited_by: 你的邀請人是:
|
invited_by: 你的邀請人是:
|
||||||
max_uses:
|
max_uses: "%{count} 次"
|
||||||
one: 1 次
|
|
||||||
other: "%{count} 次"
|
|
||||||
max_uses_prompt: 無限制
|
max_uses_prompt: 無限制
|
||||||
prompt: 建立分享連結,邀請他人在本站點註冊
|
prompt: 建立分享連結,邀請他人在本站點註冊
|
||||||
table:
|
table:
|
||||||
|
@ -542,12 +529,8 @@ zh-TW:
|
||||||
action: 閱覽所有通知
|
action: 閱覽所有通知
|
||||||
body: 以下是自%{since}你最後一次登入以來錯過的訊息摘要
|
body: 以下是自%{since}你最後一次登入以來錯過的訊息摘要
|
||||||
mention: "%{name} 在此提及了你:"
|
mention: "%{name} 在此提及了你:"
|
||||||
new_followers_summary:
|
new_followers_summary: 而且,你不在的時候,有 %{count} 個人關注你了! 好棒!
|
||||||
one: 而且,你不在的時候,有一個人關注你! 耶!
|
subject: "自從上次登入以來,你收到 %{count} 則新的通知 \U0001F418"
|
||||||
other: 而且,你不在的時候,有 %{count} 個人關注你了! 好棒!
|
|
||||||
subject:
|
|
||||||
one: "自從上次登入以來,你收到 1 則新的通知 \U0001F418"
|
|
||||||
other: "自從上次登入以來,你收到 %{count} 則新的通知 \U0001F418"
|
|
||||||
title: 你不在的時候...
|
title: 你不在的時候...
|
||||||
favourite:
|
favourite:
|
||||||
body: '你的嘟文被 %{name} 加入了最愛:'
|
body: '你的嘟文被 %{name} 加入了最愛:'
|
||||||
|
@ -653,17 +636,11 @@ zh-TW:
|
||||||
statuses:
|
statuses:
|
||||||
attached:
|
attached:
|
||||||
description: 附件: %{attached}
|
description: 附件: %{attached}
|
||||||
image:
|
image: "%{count} 幅圖片"
|
||||||
one: "%{count} 幅圖片"
|
video: "%{count} 段影片"
|
||||||
other: "%{count} 幅圖片"
|
|
||||||
video:
|
|
||||||
one: "%{count} 段影片"
|
|
||||||
other: "%{count} 段影片"
|
|
||||||
boosted_from_html: 轉嘟自 %{acct_link}
|
boosted_from_html: 轉嘟自 %{acct_link}
|
||||||
content_warning: 內容警告: %{warning}
|
content_warning: 內容警告: %{warning}
|
||||||
disallowed_hashtags:
|
disallowed_hashtags: 包含不允許的標籤: %{tags}
|
||||||
one: 包含不允許的標籤: %{tags}
|
|
||||||
other: 包含不允許的標籤: %{tags}
|
|
||||||
language_detection: 自動偵測語言
|
language_detection: 自動偵測語言
|
||||||
open_in_web: 以網頁開啟
|
open_in_web: 以網頁開啟
|
||||||
over_character_limit: 超過了 %{max} 字的限制
|
over_character_limit: 超過了 %{max} 字的限制
|
||||||
|
|
|
@ -14,12 +14,29 @@ class MigrateAccountConversations < ActiveRecord::Migration[5.2]
|
||||||
sleep 1
|
sleep 1
|
||||||
end
|
end
|
||||||
|
|
||||||
local_direct_statuses.find_each do |status|
|
total = estimate_rows(local_direct_statuses) + estimate_rows(notifications_about_direct_statuses)
|
||||||
|
migrated = 0
|
||||||
|
started_time = Time.zone.now
|
||||||
|
last_time = Time.zone.now
|
||||||
|
|
||||||
|
local_direct_statuses.includes(:account, mentions: :account).find_each do |status|
|
||||||
AccountConversation.add_status(status.account, status)
|
AccountConversation.add_status(status.account, status)
|
||||||
|
migrated += 1
|
||||||
|
|
||||||
|
if Time.zone.now - last_time > 1
|
||||||
|
say_progress(migrated, total, started_time)
|
||||||
|
last_time = Time.zone.now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
notifications_about_direct_statuses.find_each do |notification|
|
notifications_about_direct_statuses.includes(:account, mention: { status: [:account, mentions: :account] }).find_each do |notification|
|
||||||
AccountConversation.add_status(notification.account, notification.target_status)
|
AccountConversation.add_status(notification.account, notification.target_status)
|
||||||
|
migrated += 1
|
||||||
|
|
||||||
|
if Time.zone.now - last_time > 1
|
||||||
|
say_progress(migrated, total, started_time)
|
||||||
|
last_time = Time.zone.now
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -28,16 +45,31 @@ class MigrateAccountConversations < ActiveRecord::Migration[5.2]
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def estimate_rows(query)
|
||||||
|
result = exec_query("EXPLAIN #{query.to_sql}").first
|
||||||
|
result['QUERY PLAN'].scan(/ rows=([\d]+)/).first&.first&.to_i || 0
|
||||||
|
end
|
||||||
|
|
||||||
|
def say_progress(migrated, total, started_time)
|
||||||
|
status = "Migrated #{migrated} rows"
|
||||||
|
|
||||||
|
percentage = 100.0 * migrated / total
|
||||||
|
status += " (~#{sprintf('%.2f', percentage)}%, "
|
||||||
|
|
||||||
|
remaining_time = (100.0 - percentage) * (Time.zone.now - started_time) / percentage
|
||||||
|
|
||||||
|
status += "#{(remaining_time / 60).to_i}:"
|
||||||
|
status += sprintf('%02d', remaining_time.to_i % 60)
|
||||||
|
status += ' remaining)'
|
||||||
|
|
||||||
|
say status, true
|
||||||
|
end
|
||||||
|
|
||||||
def local_direct_statuses
|
def local_direct_statuses
|
||||||
Status.unscoped
|
Status.unscoped.local.where(visibility: :direct)
|
||||||
.local
|
|
||||||
.where(visibility: :direct)
|
|
||||||
.includes(:account, mentions: :account)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def notifications_about_direct_statuses
|
def notifications_about_direct_statuses
|
||||||
Notification.joins(mention: :status)
|
Notification.joins(mention: :status).where(activity_type: 'Mention', statuses: { visibility: :direct })
|
||||||
.where(activity_type: 'Mention', statuses: { visibility: :direct })
|
|
||||||
.includes(:account, mention: { status: [:account, mentions: :account] })
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ require_relative 'mastodon/emoji_cli'
|
||||||
require_relative 'mastodon/accounts_cli'
|
require_relative 'mastodon/accounts_cli'
|
||||||
require_relative 'mastodon/feeds_cli'
|
require_relative 'mastodon/feeds_cli'
|
||||||
require_relative 'mastodon/settings_cli'
|
require_relative 'mastodon/settings_cli'
|
||||||
|
require_relative 'mastodon/domains_cli'
|
||||||
|
|
||||||
module Mastodon
|
module Mastodon
|
||||||
class CLI < Thor
|
class CLI < Thor
|
||||||
|
@ -27,5 +28,8 @@ module Mastodon
|
||||||
|
|
||||||
desc 'settings SUBCOMMAND ...ARGS', 'Manage dynamic settings'
|
desc 'settings SUBCOMMAND ...ARGS', 'Manage dynamic settings'
|
||||||
subcommand 'settings', Mastodon::SettingsCLI
|
subcommand 'settings', Mastodon::SettingsCLI
|
||||||
|
|
||||||
|
desc 'domains SUBCOMMAND ...ARGS', 'Manage account domains'
|
||||||
|
subcommand 'domains', Mastodon::DomainsCLI
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require 'rubygems/package'
|
require 'set'
|
||||||
require_relative '../../config/boot'
|
require_relative '../../config/boot'
|
||||||
require_relative '../../config/environment'
|
require_relative '../../config/environment'
|
||||||
require_relative 'cli_helper'
|
require_relative 'cli_helper'
|
||||||
|
@ -10,6 +10,7 @@ module Mastodon
|
||||||
def self.exit_on_failure?
|
def self.exit_on_failure?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
option :all, type: :boolean
|
option :all, type: :boolean
|
||||||
desc 'rotate [USERNAME]', 'Generate and broadcast new keys'
|
desc 'rotate [USERNAME]', 'Generate and broadcast new keys'
|
||||||
long_desc <<-LONG_DESC
|
long_desc <<-LONG_DESC
|
||||||
|
@ -210,33 +211,25 @@ module Mastodon
|
||||||
Accounts that have had confirmed activity within the last week
|
Accounts that have had confirmed activity within the last week
|
||||||
are excluded from the checks.
|
are excluded from the checks.
|
||||||
|
|
||||||
If 10 or more accounts from the same domain cannot be queried
|
Domains that are unreachable are not checked.
|
||||||
due to a connection error (such as missing DNS records) then
|
|
||||||
the domain is considered dead, and all other accounts from it
|
|
||||||
are deleted without further querying.
|
|
||||||
|
|
||||||
With the --dry-run option, no deletes will actually be carried
|
With the --dry-run option, no deletes will actually be carried
|
||||||
out.
|
out.
|
||||||
LONG_DESC
|
LONG_DESC
|
||||||
def cull
|
def cull
|
||||||
domain_thresholds = Hash.new { |hash, key| hash[key] = 0 }
|
skip_threshold = 7.days.ago
|
||||||
skip_threshold = 7.days.ago
|
culled = 0
|
||||||
culled = 0
|
skip_domains = Set.new
|
||||||
dead_servers = []
|
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
|
||||||
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
|
|
||||||
|
|
||||||
Account.remote.where(protocol: :activitypub).partitioned.find_each do |account|
|
Account.remote.where(protocol: :activitypub).partitioned.find_each do |account|
|
||||||
next if account.updated_at >= skip_threshold || (account.last_webfingered_at.present? && account.last_webfingered_at >= skip_threshold)
|
next if account.updated_at >= skip_threshold || (account.last_webfingered_at.present? && account.last_webfingered_at >= skip_threshold)
|
||||||
|
|
||||||
unless dead_servers.include?(account.domain)
|
unless skip_domains.include?(account.domain)
|
||||||
begin
|
begin
|
||||||
code = Request.new(:head, account.uri).perform(&:code)
|
code = Request.new(:head, account.uri).perform(&:code)
|
||||||
rescue HTTP::ConnectionError
|
rescue HTTP::ConnectionError
|
||||||
domain_thresholds[account.domain] += 1
|
skip_domains << account.domain
|
||||||
|
|
||||||
if domain_thresholds[account.domain] >= 10
|
|
||||||
dead_servers << account.domain
|
|
||||||
end
|
|
||||||
rescue StandardError
|
rescue StandardError
|
||||||
next
|
next
|
||||||
end
|
end
|
||||||
|
@ -255,24 +248,12 @@ module Mastodon
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Remove dead servers
|
|
||||||
unless dead_servers.empty? || options[:dry_run]
|
|
||||||
dead_servers.each do |domain|
|
|
||||||
Account.where(domain: domain).find_each do |account|
|
|
||||||
SuspendAccountService.new.call(account)
|
|
||||||
account.destroy
|
|
||||||
culled += 1
|
|
||||||
say('.', :green, false)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
say
|
say
|
||||||
say("Removed #{culled} accounts (#{dead_servers.size} dead servers)#{dry_run}", :green)
|
say("Removed #{culled} accounts. #{skip_domains.size} servers skipped#{dry_run}", skip_domains.empty? ? :green : :yellow)
|
||||||
|
|
||||||
unless dead_servers.empty?
|
unless skip_domains.empty?
|
||||||
say('R.I.P.:', :yellow)
|
say('The following servers were not available during the check:', :yellow)
|
||||||
dead_servers.each { |domain| say(' ' + domain) }
|
skip_domains.each { |domain| say(' ' + domain) }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative '../../config/boot'
|
||||||
|
require_relative '../../config/environment'
|
||||||
|
require_relative 'cli_helper'
|
||||||
|
|
||||||
|
module Mastodon
|
||||||
|
class DomainsCLI < Thor
|
||||||
|
def self.exit_on_failure?
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
option :dry_run, type: :boolean
|
||||||
|
desc 'purge DOMAIN', 'Remove accounts from a DOMAIN without a trace'
|
||||||
|
long_desc <<-LONG_DESC
|
||||||
|
Remove all accounts from a given DOMAIN without leaving behind any
|
||||||
|
records. Unlike a suspension, if the DOMAIN still exists in the wild,
|
||||||
|
it means the accounts could return if they are resolved again.
|
||||||
|
LONG_DESC
|
||||||
|
def purge(domain)
|
||||||
|
removed = 0
|
||||||
|
dry_run = options[:dry_run] ? ' (DRY RUN)' : ''
|
||||||
|
|
||||||
|
Account.where(domain: domain).find_each do |account|
|
||||||
|
unless options[:dry_run]
|
||||||
|
SuspendAccountService.new.call(account)
|
||||||
|
account.destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
removed += 1
|
||||||
|
say('.', :green, false)
|
||||||
|
end
|
||||||
|
|
||||||
|
DomainBlock.where(domain: domain).destroy_all
|
||||||
|
|
||||||
|
say
|
||||||
|
say("Removed #{removed} accounts#{dry_run}", :green)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -10,6 +10,7 @@ module Mastodon
|
||||||
def self.exit_on_failure?
|
def self.exit_on_failure?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
option :prefix
|
option :prefix
|
||||||
option :suffix
|
option :suffix
|
||||||
option :overwrite, type: :boolean
|
option :overwrite, type: :boolean
|
||||||
|
|
|
@ -9,6 +9,7 @@ module Mastodon
|
||||||
def self.exit_on_failure?
|
def self.exit_on_failure?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
option :all, type: :boolean, default: false
|
option :all, type: :boolean, default: false
|
||||||
option :background, type: :boolean, default: false
|
option :background, type: :boolean, default: false
|
||||||
option :dry_run, type: :boolean, default: false
|
option :dry_run, type: :boolean, default: false
|
||||||
|
@ -58,7 +59,7 @@ module Mastodon
|
||||||
account = Account.find_local(username)
|
account = Account.find_local(username)
|
||||||
|
|
||||||
if account.nil?
|
if account.nil?
|
||||||
say("Account #{username} is not found", :red)
|
say('No such account', :red)
|
||||||
exit(1)
|
exit(1)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ module Mastodon
|
||||||
def self.exit_on_failure?
|
def self.exit_on_failure?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
option :days, type: :numeric, default: 7
|
option :days, type: :numeric, default: 7
|
||||||
option :background, type: :boolean, default: false
|
option :background, type: :boolean, default: false
|
||||||
option :verbose, type: :boolean, default: false
|
option :verbose, type: :boolean, default: false
|
||||||
|
|
|
@ -9,6 +9,7 @@ module Mastodon
|
||||||
def self.exit_on_failure?
|
def self.exit_on_failure?
|
||||||
true
|
true
|
||||||
end
|
end
|
||||||
|
|
||||||
desc 'open', 'Open registrations'
|
desc 'open', 'Open registrations'
|
||||||
def open
|
def open
|
||||||
Setting.open_registrations = true
|
Setting.open_registrations = true
|
||||||
|
|
Loading…
Reference in New Issue