Merge branch 'master' into glitch-soc/merge-upstream
This commit is contained in:
		
						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,7 +133,7 @@ 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) {
 | 
				
			||||||
@ -139,17 +141,17 @@ export default class Card extends React.PureComponent {
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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
 | 
				
			||||||
      dead_servers      = []
 | 
					      skip_domains   = Set.new
 | 
				
			||||||
      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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										40
									
								
								lib/mastodon/domains_cli.rb
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								lib/mastodon/domains_cli.rb
									
									
									
									
									
										Normal file
									
								
							@ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user