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

Conflicts:
- app/models/media_attachment.rb
  Upstream added audio attachment support
- app/serializers/initial_state_serializer.rb
  Upstream added audio attachment support and how mimetypes are returned
- app/serializers/rest/instance_serializer.rb
  Upstream added a few fields
- config/application.rb
  Upstream added a different paperclip transcoder
This commit is contained in:
Thibaut Girka
2019-06-24 14:47:48 +02:00
87 changed files with 1585 additions and 397 deletions
@@ -127,6 +127,7 @@ module Admin
:by_domain,
:active,
:pending,
:disabled,
:silenced,
:suspended,
:username,
@@ -13,7 +13,7 @@ module Admin
authorize :domain_block, :create?
@domain_block = DomainBlock.new(resource_params)
existing_domain_block = resource_params[:domain].present? ? DomainBlock.find_by(domain: resource_params[:domain]) : nil
existing_domain_block = resource_params[:domain].present? ? DomainBlock.rule_for(resource_params[:domain]) : nil
if existing_domain_block.present? && !@domain_block.stricter_than?(existing_domain_block)
@domain_block.save
@@ -18,7 +18,7 @@ module Admin
@blocks_count = Block.where(target_account: Account.where(domain: params[:id])).count
@available = DeliveryFailureTracker.available?(Account.select(:shared_inbox_url).where(domain: params[:id]).first&.shared_inbox_url)
@media_storage = MediaAttachment.where(account: Account.where(domain: params[:id])).sum(:file_file_size)
@domain_block = DomainBlock.find_by(domain: params[:id])
@domain_block = DomainBlock.rule_for(params[:id])
end
private
@@ -0,0 +1,32 @@
# frozen_string_literal: true
class Api::V1::Admin::AccountActionsController < Api::BaseController
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }
before_action :require_staff!
before_action :set_account
def create
account_action = Admin::AccountAction.new(resource_params)
account_action.target_account = @account
account_action.current_account = current_account
account_action.save!
render_empty
end
private
def set_account
@account = Account.find(params[:account_id])
end
def resource_params
params.permit(
:type,
:report_id,
:warning_preset_id,
:text,
:send_email_notification
)
end
end
@@ -0,0 +1,128 @@
# frozen_string_literal: true
class Api::V1::Admin::AccountsController < Api::BaseController
include Authorization
include AccountableConcern
LIMIT = 100
before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:accounts' }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:accounts' }, except: [:index, :show]
before_action :require_staff!
before_action :set_accounts, only: :index
before_action :set_account, except: :index
before_action :require_local_account!, only: [:enable, :approve, :reject]
after_action :insert_pagination_headers, only: :index
FILTER_PARAMS = %i(
local
remote
by_domain
active
pending
disabled
silenced
suspended
username
display_name
email
ip
staff
).freeze
PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze
def index
authorize :account, :index?
render json: @accounts, each_serializer: REST::Admin::AccountSerializer
end
def show
authorize @account, :show?
render json: @account, serializer: REST::Admin::AccountSerializer
end
def enable
authorize @account.user, :enable?
@account.user.enable!
log_action :enable, @account.user
render json: @account, serializer: REST::Admin::AccountSerializer
end
def approve
authorize @account.user, :approve?
@account.user.approve!
render json: @account, serializer: REST::Admin::AccountSerializer
end
def reject
authorize @account.user, :reject?
SuspendAccountService.new.call(@account, including_user: true, destroy: true, skip_distribution: true)
render json: @account, serializer: REST::Admin::AccountSerializer
end
def unsilence
authorize @account, :unsilence?
@account.unsilence!
log_action :unsilence, @account
render json: @account, serializer: REST::Admin::AccountSerializer
end
def unsuspend
authorize @account, :unsuspend?
@account.unsuspend!
log_action :unsuspend, @account
render json: @account, serializer: REST::Admin::AccountSerializer
end
private
def set_accounts
@accounts = filtered_accounts.order(id: :desc).includes(user: [:invite_request, :invite]).paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end
def set_account
@account = Account.find(params[:id])
end
def filtered_accounts
AccountFilter.new(filter_params).results
end
def filter_params
params.permit(*FILTER_PARAMS)
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def next_path
api_v1_admin_accounts_url(pagination_params(max_id: pagination_max_id)) if records_continue?
end
def prev_path
api_v1_admin_accounts_url(pagination_params(min_id: pagination_since_id)) unless @accounts.empty?
end
def pagination_max_id
@accounts.last.id
end
def pagination_since_id
@accounts.first.id
end
def records_continue?
@accounts.size == limit_param(LIMIT)
end
def pagination_params(core_params)
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
end
def require_local_account!
forbidden unless @account.local? && @account.user.present?
end
end
@@ -0,0 +1,108 @@
# frozen_string_literal: true
class Api::V1::Admin::ReportsController < Api::BaseController
include Authorization
include AccountableConcern
LIMIT = 100
before_action -> { doorkeeper_authorize! :'admin:read', :'admin:read:reports' }, only: [:index, :show]
before_action -> { doorkeeper_authorize! :'admin:write', :'admin:write:reports' }, except: [:index, :show]
before_action :require_staff!
before_action :set_reports, only: :index
before_action :set_report, except: :index
after_action :insert_pagination_headers, only: :index
FILTER_PARAMS = %i(
resolved
account_id
target_account_id
).freeze
PAGINATION_PARAMS = (%i(limit) + FILTER_PARAMS).freeze
def index
authorize :report, :index?
render json: @reports, each_serializer: REST::Admin::ReportSerializer
end
def show
authorize @report, :show?
render json: @report, serializer: REST::Admin::ReportSerializer
end
def assign_to_self
authorize @report, :update?
@report.update!(assigned_account_id: current_account.id)
log_action :assigned_to_self, @report
render json: @report, serializer: REST::Admin::ReportSerializer
end
def unassign
authorize @report, :update?
@report.update!(assigned_account_id: nil)
log_action :unassigned, @report
render json: @report, serializer: REST::Admin::ReportSerializer
end
def reopen
authorize @report, :update?
@report.unresolve!
log_action :reopen, @report
render json: @report, serializer: REST::Admin::ReportSerializer
end
def resolve
authorize @report, :update?
@report.resolve!(current_account)
log_action :resolve, @report
render json: @report, serializer: REST::Admin::ReportSerializer
end
private
def set_reports
@reports = filtered_reports.order(id: :desc).with_accounts.paginate_by_id(limit_param(LIMIT), params_slice(:max_id, :since_id, :min_id))
end
def set_report
@report = Report.find(params[:id])
end
def filtered_reports
ReportFilter.new(filter_params).results
end
def filter_params
params.permit(*FILTER_PARAMS)
end
def insert_pagination_headers
set_pagination_headers(next_path, prev_path)
end
def next_path
api_v1_admin_reports_url(pagination_params(max_id: pagination_max_id)) if records_continue?
end
def prev_path
api_v1_admin_reports_url(pagination_params(min_id: pagination_since_id)) unless @reports.empty?
end
def pagination_max_id
@reports.last.id
end
def pagination_since_id
@reports.first.id
end
def records_continue?
@reports.size == limit_param(LIMIT)
end
def pagination_params(core_params)
params.slice(*PAGINATION_PARAMS).permit(*PAGINATION_PARAMS).merge(core_params)
end
end
+10 -2
View File
@@ -7,6 +7,8 @@ class MediaController < ApplicationController
before_action :set_media_attachment
before_action :verify_permitted_status!
before_action :check_playable, only: :player
before_action :allow_iframing, only: :player
content_security_policy only: :player do |p|
p.frame_ancestors(false)
@@ -18,8 +20,6 @@ class MediaController < ApplicationController
def player
@body_classes = 'player'
response.headers['X-Frame-Options'] = 'ALLOWALL'
raise ActiveRecord::RecordNotFound unless @media_attachment.video? || @media_attachment.gifv?
end
private
@@ -34,4 +34,12 @@ class MediaController < ApplicationController
# Reraise in order to get a 404 instead of a 403 error code
raise ActiveRecord::RecordNotFound
end
def check_playable
not_found unless @media_attachment.larger_media_format?
end
def allow_iframing
response.headers['X-Frame-Options'] = 'ALLOWALL'
end
end
+1 -1
View File
@@ -39,6 +39,6 @@ class MediaProxyController < ApplicationController
end
def reject_media?
DomainBlock.find_by(domain: @media_attachment.account.domain)&.reject_media?
DomainBlock.reject_media?(@media_attachment.account.domain)
end
end
@@ -61,8 +61,4 @@ class Settings::IdentityProofsController < Settings::BaseController
def post_params
params.require(:account_identity_proof).permit(:post_status, :status_text)
end
def set_body_classes
@body_classes = ''
end
end
@@ -157,7 +157,7 @@ class Item extends React.PureComponent {
if (attachment.get('type') === 'unknown') {
return (
<div className={classNames('media-gallery__item', { standalone })} key={attachment.get('id')} style={{ left: left, top: top, right: right, bottom: bottom, width: `${width}%`, height: `${height}%` }}>
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url')} target='_blank' style={{ cursor: 'pointer' }}>
<a className='media-gallery__item-thumbnail' href={attachment.get('remote_url')} target='_blank' style={{ cursor: 'pointer' }} title={attachment.get('description')}>
<canvas width={32} height={32} ref={this.setCanvasRef} className='media-gallery__preview' />
</a>
</div>
+6 -6
View File
@@ -333,17 +333,17 @@ class Status extends ImmutablePureComponent {
media={status.get('media_attachments')}
/>
);
} else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
const video = status.getIn(['media_attachments', 0]);
} else if (['video', 'audio'].includes(status.getIn(['media_attachments', 0, 'type']))) {
const attachment = status.getIn(['media_attachments', 0]);
media = (
<Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} >
{Component => (
<Component
preview={video.get('preview_url')}
blurhash={video.get('blurhash')}
src={video.get('url')}
alt={video.get('description')}
preview={attachment.get('preview_url')}
blurhash={attachment.get('blurhash')}
src={attachment.get('url')}
alt={attachment.get('description')}
width={this.props.cachedMediaWidth}
height={110}
inline
@@ -7,9 +7,11 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import ImmutablePropTypes from 'react-immutable-proptypes';
const messages = defineMessages({
upload: { id: 'upload_button.label', defaultMessage: 'Add media (JPEG, PNG, GIF, WebM, MP4, MOV)' },
upload: { id: 'upload_button.label', defaultMessage: 'Add media ({formats})' },
});
const SUPPORTED_FORMATS = 'JPEG, PNG, GIF, WebM, MP4, MOV, OGG, WAV, MP3, FLAC';
const makeMapStateToProps = () => {
const mapStateToProps = state => ({
acceptContentTypes: state.getIn(['media_attachments', 'accept_content_types']),
@@ -60,9 +62,9 @@ class UploadButton extends ImmutablePureComponent {
return (
<div className='compose-form__upload-button'>
<IconButton icon='camera' title={intl.formatMessage(messages.upload)} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} />
<IconButton icon='paperclip' title={intl.formatMessage(messages.upload, { formats: SUPPORTED_FORMATS })} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} />
<label>
<span style={{ display: 'none' }}>{intl.formatMessage(messages.upload)}</span>
<span style={{ display: 'none' }}>{intl.formatMessage(messages.upload, { formats: SUPPORTED_FORMATS })}</span>
<input
key={resetFileKey}
ref={this.setRef}
@@ -3,7 +3,7 @@ import UploadButton from '../components/upload_button';
import { uploadCompose } from '../../../actions/compose';
const mapStateToProps = state => ({
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => m.get('type') === 'video')),
disabled: state.getIn(['compose', 'is_uploading']) || (state.getIn(['compose', 'media_attachments']).size > 3 || state.getIn(['compose', 'media_attachments']).some(m => ['video', 'audio'].includes(m.get('type')))),
unavailable: state.getIn(['compose', 'poll']) !== null,
resetFileKey: state.getIn(['compose', 'resetFileKey']),
});
@@ -107,15 +107,15 @@ export default class DetailedStatus extends ImmutablePureComponent {
}
if (status.get('media_attachments').size > 0) {
if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
const video = status.getIn(['media_attachments', 0]);
if (['video', 'audio'].includes(status.getIn(['media_attachments', 0, 'type']))) {
const attachment = status.getIn(['media_attachments', 0]);
media = (
<Video
preview={video.get('preview_url')}
blurhash={video.get('blurhash')}
src={video.get('url')}
alt={video.get('description')}
preview={attachment.get('preview_url')}
blurhash={attachment.get('blurhash')}
src={attachment.get('url')}
alt={attachment.get('description')}
width={300}
height={150}
inline
+1 -1
View File
@@ -369,7 +369,7 @@
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} آخرون {people}} يتحدثون",
"ui.beforeunload": "سوف تفقد مسودتك إن تركت ماستدون.",
"upload_area.title": "اسحب ثم أفلت للرفع",
"upload_button.label": "إضافة وسائط (JPEG، PNG، GIF، WebM، MP4، MOV)",
"upload_button.label": "إضافة وسائط ({formats})",
"upload_error.limit": "لقد تم بلوغ الحد الأقصى المسموح به لإرسال الملفات.",
"upload_error.poll": "لا يمكن إدراج ملفات في استطلاعات الرأي.",
"upload_form.description": "وصف للمعاقين بصريا",
+2 -2
View File
@@ -314,7 +314,7 @@
"search_results.accounts": "Gent",
"search_results.hashtags": "Etiquetes",
"search_results.statuses": "Toots",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}",
"status.admin_account": "Obre l'interfície de moderació per a @{name}",
"status.admin_status": "Obre aquest toot a la interfície de moderació",
"status.block": "Bloqueja @{name}",
@@ -366,7 +366,7 @@
"time_remaining.minutes": "{number, plural, one {# minut} other {# minuts}} restants",
"time_remaining.moments": "Moments restants",
"time_remaining.seconds": "{number, plural, one {# segon} other {# segons}} restants",
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
"trends.count_by_accounts": "{count} {rawCount, plural, one {persona} other {gent}} talking",
"ui.beforeunload": "El teu esborrany es perdrà si surts de Mastodon.",
"upload_area.title": "Arrossega i deixa anar per a carregar",
"upload_button.label": "Afegir multimèdia (JPEG, PNG, GIF, WebM, MP4, MOV)",
+1 -1
View File
@@ -369,7 +369,7 @@
"trends.count_by_accounts": "{count} {rawCount, plural, eine {Person} other {Personen}} reden darüber",
"ui.beforeunload": "Dein Entwurf geht verloren, wenn du Mastodon verlässt.",
"upload_area.title": "Zum Hochladen hereinziehen",
"upload_button.label": "Mediendatei hinzufügen (JPEG, PNG, GIF, WebM, MP4, MOV)",
"upload_button.label": "Mediendatei hinzufügen ({formats})",
"upload_error.limit": "Dateiupload-Limit erreicht.",
"upload_error.poll": "Dateiuploads sind in Kombination mit Umfragen nicht erlaubt.",
"upload_form.description": "Für Menschen mit Sehbehinderung beschreiben",
@@ -1051,7 +1051,7 @@
{
"descriptors": [
{
"defaultMessage": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)",
"defaultMessage": "Add media ({formats})",
"id": "upload_button.label"
}
],
+1 -1
View File
@@ -374,7 +374,7 @@
"trends.count_by_accounts": "{count} {rawCount, plural, one {person} other {people}} talking",
"ui.beforeunload": "Your draft will be lost if you leave Mastodon.",
"upload_area.title": "Drag & drop to upload",
"upload_button.label": "Add media (JPEG, PNG, GIF, WebM, MP4, MOV)",
"upload_button.label": "Add media ({formats})",
"upload_error.limit": "File upload limit exceeded.",
"upload_error.poll": "File upload not allowed with polls.",
"upload_form.description": "Describe for the visually impaired",
+29 -29
View File
@@ -71,20 +71,20 @@
"compose_form.lock_disclaimer": "Tilisi ei ole {locked}. Kuka tahansa voi seurata tiliäsi ja nähdä vain seuraajille rajaamasi julkaisut.",
"compose_form.lock_disclaimer.lock": "lukittu",
"compose_form.placeholder": "Mitä mietit?",
"compose_form.poll.add_option": "Add a choice",
"compose_form.poll.duration": "Poll duration",
"compose_form.poll.option_placeholder": "Choice {number}",
"compose_form.poll.remove_option": "Remove this choice",
"compose_form.poll.add_option": "Lisää valinta",
"compose_form.poll.duration": "Äänestyksen kesto",
"compose_form.poll.option_placeholder": "Valinta numero",
"compose_form.poll.remove_option": "Poista tämä valinta",
"compose_form.publish": "Tuuttaa",
"compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.hide": "Mark media as sensitive",
"compose_form.publish_loud": "Julkista!",
"compose_form.sensitive.hide": "Valitse tämä arkaluontoisena",
"compose_form.sensitive.marked": "Media on merkitty arkaluontoiseksi",
"compose_form.sensitive.unmarked": "Mediaa ei ole merkitty arkaluontoiseksi",
"compose_form.spoiler.marked": "Teksti on piilotettu varoituksen taakse",
"compose_form.spoiler.unmarked": "Teksti ei ole piilotettu",
"compose_form.spoiler_placeholder": "Sisältövaroitus",
"confirmation_modal.cancel": "Peruuta",
"confirmations.block.block_and_report": "Block & Report",
"confirmations.block.block_and_report": "Estä ja raportoi",
"confirmations.block.confirm": "Estä",
"confirmations.block.message": "Haluatko varmasti estää käyttäjän {name}?",
"confirmations.delete.confirm": "Poista",
@@ -118,7 +118,7 @@
"emoji_button.symbols": "Symbolit",
"emoji_button.travel": "Matkailu",
"empty_column.account_timeline": "Ei ole 'toots' täällä!",
"empty_column.account_unavailable": "Profile unavailable",
"empty_column.account_unavailable": "Profiilia ei löydy",
"empty_column.blocks": "Et ole vielä estänyt yhtään käyttäjää.",
"empty_column.community": "Paikallinen aikajana on tyhjä. Homma lähtee käyntiin, kun kirjoitat jotain julkista!",
"empty_column.direct": "Sinulla ei ole vielä yhtään viestiä yksittäiselle käyttäjälle. Kun lähetät tai vastaanotat sellaisen, se näkyy täällä.",
@@ -138,7 +138,7 @@
"follow_request.reject": "Hylkää",
"getting_started.developers": "Kehittäjille",
"getting_started.directory": "Profiili hakemisto",
"getting_started.documentation": "Documentation",
"getting_started.documentation": "Documentaatio",
"getting_started.heading": "Aloitus",
"getting_started.invite": "Kutsu ihmisiä",
"getting_started.open_source_notice": "Mastodon on avoimen lähdekoodin ohjelma. Voit avustaa tai raportoida ongelmia GitHubissa: {github}.",
@@ -147,8 +147,8 @@
"hashtag.column_header.tag_mode.all": "ja {additional}",
"hashtag.column_header.tag_mode.any": "tai {additional}",
"hashtag.column_header.tag_mode.none": "ilman {additional}",
"hashtag.column_settings.select.no_options_message": "No suggestions found",
"hashtag.column_settings.select.placeholder": "Enter hashtags…",
"hashtag.column_settings.select.no_options_message": "Ehdostuta ei löydetty",
"hashtag.column_settings.select.placeholder": "Laita häshtägejä…",
"hashtag.column_settings.tag_mode.all": "Kaikki",
"hashtag.column_settings.tag_mode.any": "Kaikki",
"hashtag.column_settings.tag_mode.none": "Ei mikään",
@@ -156,25 +156,25 @@
"home.column_settings.basic": "Perusasetukset",
"home.column_settings.show_reblogs": "Näytä buustaukset",
"home.column_settings.show_replies": "Näytä vastaukset",
"intervals.full.days": "{number, plural, one {# day} other {# days}}",
"intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
"intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
"intervals.full.days": "Päivä päiviä",
"intervals.full.hours": "Tunti tunteja",
"intervals.full.minutes": "Minuuti minuuteja",
"introduction.federation.action": "Seuraava",
"introduction.federation.federated.headline": "Federated",
"introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
"introduction.federation.home.headline": "Home",
"introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
"introduction.federation.local.headline": "Local",
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
"introduction.interactions.action": "Finish toot-orial!",
"introduction.interactions.favourite.headline": "Favourite",
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
"introduction.interactions.reblog.headline": "Boost",
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.",
"introduction.interactions.reply.headline": "Reply",
"introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.",
"introduction.welcome.action": "Let's go!",
"introduction.welcome.headline": "First steps",
"introduction.federation.federated.headline": "Federaatioitettu",
"introduction.federation.federated.text": "Julkisia viestejä muiden serverien that is not a word aikoo tulla federoituun aikajanaan.",
"introduction.federation.home.headline": "Koti",
"introduction.federation.home.text": "Viestit muilta pelaajilta jota seuraat aikovat tulla koti sivuusi. Voit seurata ketä vain missä vain serverillä!",
"introduction.federation.local.headline": "Paikallinen",
"introduction.federation.local.text": "Julkiset viestit muilta pelaajilta samalla serverillä tulevat sinun paikalliseen aikajanaan.",
"introduction.interactions.action": "Suorita harjoitus!",
"introduction.interactions.favourite.headline": "Lempi",
"introduction.interactions.favourite.text": "Toot is not a word.",
"introduction.interactions.reblog.headline": "Nopeutus",
"introduction.interactions.reblog.text": "Toot is not a word",
"introduction.interactions.reply.headline": "Vastaa",
"introduction.interactions.reply.text": "TOOT IS NOT A WORD",
"introduction.welcome.action": "Mennään!",
"introduction.welcome.headline": "Ensimmäiset askeleet",
"introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.",
"keyboard_shortcuts.back": "liiku taaksepäin",
"keyboard_shortcuts.blocked": "avaa lista estetyistä käyttäjistä",
+28 -28
View File
@@ -40,7 +40,7 @@
"boost_modal.combo": "Puoi premere {combo} per saltare questo passaggio la prossima volta",
"bundle_column_error.body": "E' avvenuto un errore durante il caricamento di questo componente.",
"bundle_column_error.retry": "Riprova",
"bundle_column_error.title": "Network error",
"bundle_column_error.title": "Errore di rete",
"bundle_modal_error.close": "Chiudi",
"bundle_modal_error.message": "C'è stato un errore mentre questo componente veniva caricato.",
"bundle_modal_error.retry": "Riprova",
@@ -71,20 +71,20 @@
"compose_form.lock_disclaimer": "Il tuo account non è {bloccato}. Chiunque può decidere di seguirti per vedere i tuoi post per soli seguaci.",
"compose_form.lock_disclaimer.lock": "bloccato",
"compose_form.placeholder": "A cosa stai pensando?",
"compose_form.poll.add_option": "Add a choice",
"compose_form.poll.duration": "Poll duration",
"compose_form.poll.option_placeholder": "Choice {number}",
"compose_form.poll.remove_option": "Remove this choice",
"compose_form.poll.add_option": "Aggiungi una scelta",
"compose_form.poll.duration": "Durata del sondaggio",
"compose_form.poll.option_placeholder": "Scelta {number}",
"compose_form.poll.remove_option": "Rimuovi questa scelta",
"compose_form.publish": "Toot",
"compose_form.publish_loud": "{publish}!",
"compose_form.sensitive.hide": "Mark media as sensitive",
"compose_form.sensitive.hide": "Segna media come sensibile",
"compose_form.sensitive.marked": "Questo media è contrassegnato come sensibile",
"compose_form.sensitive.unmarked": "Questo media non è contrassegnato come sensibile",
"compose_form.spoiler.marked": "Il testo è nascosto dall'avviso",
"compose_form.spoiler.unmarked": "Il testo non è nascosto",
"compose_form.spoiler_placeholder": "Content warning",
"confirmation_modal.cancel": "Annulla",
"confirmations.block.block_and_report": "Block & Report",
"confirmations.block.block_and_report": "Blocca & Segnala",
"confirmations.block.confirm": "Blocca",
"confirmations.block.message": "Sei sicuro di voler bloccare {name}?",
"confirmations.delete.confirm": "Cancella",
@@ -118,7 +118,7 @@
"emoji_button.symbols": "Simboli",
"emoji_button.travel": "Viaggi e luoghi",
"empty_column.account_timeline": "Non ci sono toot qui!",
"empty_column.account_unavailable": "Profile unavailable",
"empty_column.account_unavailable": "Profilo non disponibile",
"empty_column.blocks": "Non hai ancora bloccato nessun utente.",
"empty_column.community": "La timeline locale è vuota. Condividi qualcosa pubblicamente per dare inizio alla festa!",
"empty_column.direct": "Non hai ancora nessun messaggio diretto. Quando ne manderai o riceverai qualcuno, apparirà qui.",
@@ -156,15 +156,15 @@
"home.column_settings.basic": "Semplice",
"home.column_settings.show_reblogs": "Mostra post condivisi",
"home.column_settings.show_replies": "Mostra risposte",
"intervals.full.days": "{number, plural, one {# day} other {# days}}",
"intervals.full.hours": "{number, plural, one {# hour} other {# hours}}",
"intervals.full.minutes": "{number, plural, one {# minute} other {# minutes}}",
"intervals.full.days": "{number, plural, one {# giorno} other {# giorni}}",
"intervals.full.hours": "{number, plural, one {# ora} other {# ore}}",
"intervals.full.minutes": "{number, plural, one {# minuto} other {# minuti}}",
"introduction.federation.action": "Avanti",
"introduction.federation.federated.headline": "Federated",
"introduction.federation.federated.headline": "Federato",
"introduction.federation.federated.text": "I post pubblici provenienti da altri server del fediverse saranno mostrati nella timeline federata.",
"introduction.federation.home.headline": "Home",
"introduction.federation.home.text": "I post scritti da persone che segui saranno mostrati nella timeline home. Puoi seguire chiunque su qualunque server!",
"introduction.federation.local.headline": "Local",
"introduction.federation.local.headline": "Locale",
"introduction.federation.local.text": "I post pubblici scritti da persone sul tuo stesso server saranno mostrati nella timeline locale.",
"introduction.interactions.action": "Finisci il tutorial!",
"introduction.interactions.favourite.headline": "Apprezza",
@@ -204,17 +204,17 @@
"keyboard_shortcuts.search": "per spostare il focus sulla ricerca",
"keyboard_shortcuts.start": "per aprire la colonna \"Come iniziare\"",
"keyboard_shortcuts.toggle_hidden": "per mostrare/nascondere il testo dei CW",
"keyboard_shortcuts.toggle_sensitivity": "to show/hide media",
"keyboard_shortcuts.toggle_sensitivity": "mostrare/nascondere media",
"keyboard_shortcuts.toot": "per iniziare a scrivere un toot completamente nuovo",
"keyboard_shortcuts.unfocus": "per uscire dall'area di composizione o dalla ricerca",
"keyboard_shortcuts.up": "per spostarsi in alto nella lista",
"lightbox.close": "Chiudi",
"lightbox.next": "Successivo",
"lightbox.previous": "Precedente",
"lightbox.view_context": "View context",
"lightbox.view_context": "Mostra contesto",
"lists.account.add": "Aggiungi alla lista",
"lists.account.remove": "Togli dalla lista",
"lists.delete": "Delete list",
"lists.delete": "Elimina lista",
"lists.edit": "Modifica lista",
"lists.edit.submit": "Cambia titolo",
"lists.new.create": "Aggiungi lista",
@@ -243,16 +243,16 @@
"navigation_bar.lists": "Liste",
"navigation_bar.logout": "Esci",
"navigation_bar.mutes": "Utenti silenziati",
"navigation_bar.personal": "Personal",
"navigation_bar.personal": "Personale",
"navigation_bar.pins": "Toot fissati in cima",
"navigation_bar.preferences": "Impostazioni",
"navigation_bar.profile_directory": "Profile directory",
"navigation_bar.profile_directory": "Directory dei profili",
"navigation_bar.public_timeline": "Timeline federata",
"navigation_bar.security": "Sicurezza",
"notification.favourite": "{name} ha apprezzato il tuo post",
"notification.follow": "{name} ha iniziato a seguirti",
"notification.mention": "{name} ti ha menzionato",
"notification.poll": "A poll you have voted in has ended",
"notification.poll": "Un sondaggio in cui hai votato è terminato",
"notification.reblog": "{name} ha condiviso il tuo post",
"notifications.clear": "Cancella notifiche",
"notifications.clear_confirmation": "Vuoi davvero cancellare tutte le notifiche?",
@@ -263,7 +263,7 @@
"notifications.column_settings.filter_bar.show": "Mostra",
"notifications.column_settings.follow": "Nuovi seguaci:",
"notifications.column_settings.mention": "Menzioni:",
"notifications.column_settings.poll": "Poll results:",
"notifications.column_settings.poll": "Risultati del sondaggio:",
"notifications.column_settings.push": "Notifiche push",
"notifications.column_settings.reblog": "Post condivisi:",
"notifications.column_settings.show": "Mostra in colonna",
@@ -273,14 +273,14 @@
"notifications.filter.favourites": "Apprezzati",
"notifications.filter.follows": "Seguaci",
"notifications.filter.mentions": "Menzioni",
"notifications.filter.polls": "Poll results",
"notifications.filter.polls": "Risultati del sondaggio",
"notifications.group": "{count} notifiche",
"poll.closed": "Chiuso",
"poll.refresh": "Aggiorna",
"poll.total_votes": "{count, plural, one {# voto} other {# voti}}",
"poll.vote": "Vota",
"poll_button.add_poll": "Add a poll",
"poll_button.remove_poll": "Remove poll",
"poll_button.add_poll": "Aggiungi un sondaggio",
"poll_button.remove_poll": "Rimuovi sondaggio",
"privacy.change": "Modifica privacy del post",
"privacy.direct.long": "Invia solo a utenti menzionati",
"privacy.direct.short": "Diretto",
@@ -292,8 +292,8 @@
"privacy.unlisted.short": "Non elencato",
"regeneration_indicator.label": "Caricamento in corso…",
"regeneration_indicator.sublabel": "Stiamo preparando il tuo home feed!",
"relative_time.days": "{number}d",
"relative_time.hours": "{number}h",
"relative_time.days": "{number}g",
"relative_time.hours": "{number}o",
"relative_time.just_now": "ora",
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
@@ -307,8 +307,8 @@
"search.placeholder": "Cerca",
"search_popout.search_format": "Formato di ricerca avanzato",
"search_popout.tips.full_text": "Testo semplice per trovare gli status che hai scritto, segnato come apprezzati, condiviso o in cui sei stato citato, e inoltre i nomi utente, nomi visualizzati e hashtag che lo contengono.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.hashtag": "etichetta",
"search_popout.tips.status": "stato",
"search_popout.tips.text": "Testo semplice per trovare nomi visualizzati, nomi utente e hashtag che lo contengono",
"search_popout.tips.user": "utente",
"search_results.accounts": "Gente",
@@ -371,7 +371,7 @@
"upload_area.title": "Trascina per caricare",
"upload_button.label": "Aggiungi file multimediale",
"upload_error.limit": "Limite al caricamento di file superato.",
"upload_error.poll": "File upload not allowed with polls.",
"upload_error.poll": "Caricamento file non consentito nei sondaggi.",
"upload_form.description": "Descrizione per utenti con disabilità visive",
"upload_form.focus": "Modifica anteprima",
"upload_form.undo": "Cancella",
+1 -1
View File
@@ -374,7 +374,7 @@
"trends.count_by_accounts": "{count}人がトゥート",
"ui.beforeunload": "Mastodonから離れると送信前の投稿は失われます。",
"upload_area.title": "ドラッグ&ドロップでアップロード",
"upload_button.label": "メディアを追加 (JPEG, PNG, GIF, WebM, MP4, MOV)",
"upload_button.label": "メディアを追加 ({formats})",
"upload_error.limit": "アップロードできる上限を超えています。",
"upload_error.poll": "アンケートではファイルをアップロードできません。",
"upload_form.description": "視覚障害者のための説明",
+6 -6
View File
@@ -361,15 +361,15 @@
"tabs_bar.local_timeline": "Lokaal",
"tabs_bar.notifications": "Meldingen",
"tabs_bar.search": "Zoeken",
"time_remaining.days": "{number, plural, one {# dag} other {# dagen}} left",
"time_remaining.hours": "{number, plural, one {# uur} other {# uur}} left",
"time_remaining.minutes": "{number, plural, one {# minuut} other {# minuten}} left",
"time_remaining.days": "{number, plural, one {# dag} other {# dagen}} te gaan",
"time_remaining.hours": "{number, plural, one {# uur} other {# uur}} te gaan",
"time_remaining.minutes": "{number, plural, one {# minuut} other {# minuten}} te gaan",
"time_remaining.moments": "Nog enkele ogenblikken resterend",
"time_remaining.seconds": "{number, plural, one {# seconde} other {# seconden}} left",
"time_remaining.seconds": "{number, plural, one {# seconde} other {# seconden}} te gaan",
"trends.count_by_accounts": "{count} {rawCount, plural, one {persoon praat} other {mensen praten}} hierover",
"ui.beforeunload": "Je concept zal verloren gaan als je Mastodon verlaat.",
"upload_area.title": "Hierin slepen om te uploaden",
"upload_button.label": "Media toevoegen (JPEG, PNG, GIF, WebM, MP4, MOV)",
"upload_area.title": "Hiernaar toe slepen om te uploaden",
"upload_button.label": "Media toevoegen ({formats})",
"upload_error.limit": "Uploadlimiet van bestand overschreden.",
"upload_error.poll": "Het uploaden van bestanden is in polls niet toegestaan.",
"upload_form.description": "Omschrijf dit voor mensen met een visuele beperking",
+1 -1
View File
@@ -1,5 +1,5 @@
{
"account.add_or_remove_from_list": "Pridaj, alebo odstráň zo zoznamov",
"account.add_or_remove_from_list": "Pridaj do, alebo odober zo zoznamov",
"account.badges.bot": "Bot",
"account.block": "Blokuj @{name}",
"account.block_domain": "Ukry všetko z {domain}",
+173 -173
View File
@@ -149,10 +149,10 @@
"hashtag.column_header.tag_mode.none": "brez {additional}",
"hashtag.column_settings.select.no_options_message": "Ni najdenih predlogov",
"hashtag.column_settings.select.placeholder": "Vpiši ključnik…",
"hashtag.column_settings.tag_mode.all": "Vse našteto",
"hashtag.column_settings.tag_mode.all": "Vse od naštetega",
"hashtag.column_settings.tag_mode.any": "Karkoli od naštetega",
"hashtag.column_settings.tag_mode.none": "Nič od naštetega",
"hashtag.column_settings.tag_toggle": "V ta stolpec vključite dodatne oznake",
"hashtag.column_settings.tag_toggle": "Za ta stolpec vključi dodatne oznake",
"home.column_settings.basic": "Osnovno",
"home.column_settings.show_reblogs": "Pokaži spodbude",
"home.column_settings.show_replies": "Pokaži odgovore",
@@ -161,187 +161,187 @@
"intervals.full.minutes": "{number, plural, one {# minuta} two {# minuti} few {# minute} other {# minut}}",
"introduction.federation.action": "Naprej",
"introduction.federation.federated.headline": "Združeno",
"introduction.federation.federated.text": "Public posts from other servers of the fediverse will appear in the federated timeline.",
"introduction.federation.home.headline": "Home",
"introduction.federation.home.text": "Posts from people you follow will appear in your home feed. You can follow anyone on any server!",
"introduction.federation.local.headline": "Local",
"introduction.federation.local.text": "Public posts from people on the same server as you will appear in the local timeline.",
"introduction.interactions.action": "Finish toot-orial!",
"introduction.interactions.favourite.headline": "Favourite",
"introduction.interactions.favourite.text": "You can save a toot for later, and let the author know that you liked it, by favouriting it.",
"introduction.interactions.reblog.headline": "Boost",
"introduction.interactions.reblog.text": "You can share other people's toots with your followers by boosting them.",
"introduction.interactions.reply.headline": "Reply",
"introduction.interactions.reply.text": "You can reply to other people's and your own toots, which will chain them together in a conversation.",
"introduction.welcome.action": "Let's go!",
"introduction.welcome.headline": "First steps",
"introduction.welcome.text": "Welcome to the fediverse! In a few moments, you'll be able to broadcast messages and talk to your friends across a wide variety of servers. But this server, {domain}, is special—it hosts your profile, so remember its name.",
"keyboard_shortcuts.back": "za krmarjenje nazaj",
"keyboard_shortcuts.blocked": "to open blocked users list",
"keyboard_shortcuts.boost": "suniti",
"keyboard_shortcuts.column": "osredotočiti status v enega od stolpcev",
"keyboard_shortcuts.compose": "osredotočiti na sestavljanje besedila",
"introduction.federation.federated.text": "Javne objave iz drugih strežnikov fediverse-a bodo prikazane v združeni časovnici.",
"introduction.federation.home.headline": "Domov",
"introduction.federation.home.text": "Objave oseb, ki jim sledite, bodo prikazane v vaši domači časovnici. Lahko sledite vsakomur na katerem koli strežniku!",
"introduction.federation.local.headline": "Lokalno",
"introduction.federation.local.text": "Javne objave ljudi na istem strežniku, se bodo prikazale na lokalni časovnici.",
"introduction.interactions.action": "Zaključi vadnico!",
"introduction.interactions.favourite.headline": "Priljubljeni",
"introduction.interactions.favourite.text": "Tut lahko shranite za pozneje in ga vzljubite ter s tem pokažete avtorju, da vam je ta tut priljubljen.",
"introduction.interactions.reblog.headline": "Spodbudi",
"introduction.interactions.reblog.text": "Tute drugih ljudi lahko delite z vašimi sledilci, tako da spodbudite tute.",
"introduction.interactions.reply.headline": "Odgovori",
"introduction.interactions.reply.text": "Lahko odgovarjate na tuje in vaše tute, kar bo odgovore povezalo v pogovor.",
"introduction.welcome.action": "Gremo!",
"introduction.welcome.headline": "Prvi koraki",
"introduction.welcome.text": "Dobrodošli v fediverse-u! Čez nekaj trenutkov boste lahko oddajali sporočila in se pogovarjali s prijatelji prek različnih strežnikov. Vendar je ta strežnik {domain} poseben - gosti vaš profil, zato si zapomnite njegovo ime.",
"keyboard_shortcuts.back": "pojdi nazaj",
"keyboard_shortcuts.blocked": "odpri seznam blokiranih uporabnikov",
"keyboard_shortcuts.boost": "spodbudi",
"keyboard_shortcuts.column": "fokusiraj na status v enemu od stolpcev",
"keyboard_shortcuts.compose": "fokusiraj na območje za sestavljanje besedila",
"keyboard_shortcuts.description": "Opis",
"keyboard_shortcuts.direct": "to open direct messages column",
"keyboard_shortcuts.down": "premakniti navzdol po seznamu",
"keyboard_shortcuts.enter": "odpreti status",
"keyboard_shortcuts.favourite": "to favourite",
"keyboard_shortcuts.favourites": "to open favourites list",
"keyboard_shortcuts.federated": "to open federated timeline",
"keyboard_shortcuts.direct": "odpri stolpec za neposredna sporočila",
"keyboard_shortcuts.down": "premakni se navzdol po seznamu",
"keyboard_shortcuts.enter": "odpri status",
"keyboard_shortcuts.favourite": "vzljubi",
"keyboard_shortcuts.favourites": "odpri seznam priljubljenih",
"keyboard_shortcuts.federated": "odpri združeno časovnico",
"keyboard_shortcuts.heading": "Tipkovne bližnjice",
"keyboard_shortcuts.home": "to open home timeline",
"keyboard_shortcuts.home": "odpri domačo časovnico",
"keyboard_shortcuts.hotkey": "Hitra tipka",
"keyboard_shortcuts.legend": "to display this legend",
"keyboard_shortcuts.local": "to open local timeline",
"keyboard_shortcuts.mention": "to mention author",
"keyboard_shortcuts.muted": "to open muted users list",
"keyboard_shortcuts.my_profile": "to open your profile",
"keyboard_shortcuts.notifications": "to open notifications column",
"keyboard_shortcuts.pinned": "to open pinned toots list",
"keyboard_shortcuts.profile": "to open author's profile",
"keyboard_shortcuts.reply": "to reply",
"keyboard_shortcuts.requests": "to open follow requests list",
"keyboard_shortcuts.search": "to focus search",
"keyboard_shortcuts.start": "to open \"get started\" column",
"keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW",
"keyboard_shortcuts.toggle_sensitivity": "to show/hide media",
"keyboard_shortcuts.toot": "da začnete povsem nov tut",
"keyboard_shortcuts.unfocus": "to un-focus compose textarea/search",
"keyboard_shortcuts.up": "to move up in the list",
"lightbox.close": "Close",
"lightbox.next": "Next",
"lightbox.previous": "Previous",
"lightbox.view_context": "View context",
"lists.account.add": "Add to list",
"lists.account.remove": "Remove from list",
"lists.delete": "Delete list",
"lists.edit": "Edit list",
"lists.edit.submit": "Change title",
"lists.new.create": "Add list",
"lists.new.title_placeholder": "New list title",
"lists.search": "Search among people you follow",
"lists.subheading": "Your lists",
"loading_indicator.label": "Loading...",
"media_gallery.toggle_visible": "Toggle visibility",
"missing_indicator.label": "Not found",
"missing_indicator.sublabel": "This resource could not be found",
"mute_modal.hide_notifications": "Hide notifications from this user?",
"navigation_bar.apps": "Mobile apps",
"navigation_bar.blocks": "Blocked users",
"navigation_bar.community_timeline": "Local timeline",
"navigation_bar.compose": "Compose new toot",
"navigation_bar.direct": "Direct messages",
"navigation_bar.discover": "Discover",
"navigation_bar.domain_blocks": "Hidden domains",
"navigation_bar.edit_profile": "Edit profile",
"navigation_bar.favourites": "Favourites",
"navigation_bar.filters": "Muted words",
"navigation_bar.follow_requests": "Follow requests",
"navigation_bar.follows_and_followers": "Follows and followers",
"navigation_bar.info": "O tem vozlišču",
"navigation_bar.keyboard_shortcuts": "Keyboard shortcuts",
"navigation_bar.lists": "Lists",
"navigation_bar.logout": "Logout",
"navigation_bar.mutes": "Muted users",
"navigation_bar.personal": "Personal",
"keyboard_shortcuts.legend": "pokaži to legendo",
"keyboard_shortcuts.local": "odpri lokalno časovnico",
"keyboard_shortcuts.mention": "omeni avtorja",
"keyboard_shortcuts.muted": "odpri seznam utišanih uporabnikov",
"keyboard_shortcuts.my_profile": "odpri svoj profil",
"keyboard_shortcuts.notifications": "odpri stolpec z obvestili",
"keyboard_shortcuts.pinned": "odpri seznam pripetih tutov",
"keyboard_shortcuts.profile": "odpri avtorjev profil",
"keyboard_shortcuts.reply": "odgovori",
"keyboard_shortcuts.requests": "odpri seznam s prošnjami za sledenje",
"keyboard_shortcuts.search": "fokusiraj na iskanje",
"keyboard_shortcuts.start": "odpri stolpec \"začni\"",
"keyboard_shortcuts.toggle_hidden": "prikaži/skrij besedilo za CW",
"keyboard_shortcuts.toggle_sensitivity": "prikaži/skrij medije",
"keyboard_shortcuts.toot": "začni povsem nov tut",
"keyboard_shortcuts.unfocus": "odfokusiraj območje za sestavljanje besedila/iskanje",
"keyboard_shortcuts.up": "premakni se navzgor po seznamu",
"lightbox.close": "Zapri",
"lightbox.next": "Naslednji",
"lightbox.previous": "Prejšnji",
"lightbox.view_context": "Poglej kontekst",
"lists.account.add": "Dodaj na seznam",
"lists.account.remove": "Odstrani s seznama",
"lists.delete": "Izbriši seznam",
"lists.edit": "Uredi seznam",
"lists.edit.submit": "Spremeni naslov",
"lists.new.create": "Dodaj seznam",
"lists.new.title_placeholder": "Nov naslov seznama",
"lists.search": "Išči med ljudmi, katerim sledite",
"lists.subheading": "Vaši seznami",
"loading_indicator.label": "Nalaganje...",
"media_gallery.toggle_visible": "Preklopi vidljivost",
"missing_indicator.label": "Ni najdeno",
"missing_indicator.sublabel": "Tega vira ni bilo mogoče najti",
"mute_modal.hide_notifications": "Skrij obvestila tega uporabnika?",
"navigation_bar.apps": "Mobilne aplikacije",
"navigation_bar.blocks": "Blokirani uporabniki",
"navigation_bar.community_timeline": "Lokalna časovnica",
"navigation_bar.compose": "Sestavi nov tut",
"navigation_bar.direct": "Neposredna sporočila",
"navigation_bar.discover": "Odkrijte",
"navigation_bar.domain_blocks": "Skrite domene",
"navigation_bar.edit_profile": "Uredi profil",
"navigation_bar.favourites": "Priljubljeni",
"navigation_bar.filters": "Utišane besede",
"navigation_bar.follow_requests": "Prošnje za sledenje",
"navigation_bar.follows_and_followers": "Sledenja in sledilci",
"navigation_bar.info": "O tem strežniku",
"navigation_bar.keyboard_shortcuts": "Hitre tipke",
"navigation_bar.lists": "Seznami",
"navigation_bar.logout": "Odjava",
"navigation_bar.mutes": "Utišani uporabniki",
"navigation_bar.personal": "Osebno",
"navigation_bar.pins": "Pripeti tuti",
"navigation_bar.preferences": "Preferences",
"navigation_bar.profile_directory": "Profile directory",
"navigation_bar.public_timeline": "Federated timeline",
"navigation_bar.security": "Security",
"notification.favourite": "{name} favourited your status",
"notification.follow": "{name} followed you",
"notification.mention": "{name} mentioned you",
"notification.poll": "A poll you have voted in has ended",
"notification.reblog": "{name} boosted your status",
"notifications.clear": "Clear notifications",
"notifications.clear_confirmation": "Are you sure you want to permanently clear all your notifications?",
"notifications.column_settings.alert": "Desktop notifications",
"notifications.column_settings.favourite": "Favourites:",
"notifications.column_settings.filter_bar.advanced": "Display all categories",
"notifications.column_settings.filter_bar.category": "Quick filter bar",
"notifications.column_settings.filter_bar.show": "Show",
"notifications.column_settings.follow": "New followers:",
"notifications.column_settings.mention": "Mentions:",
"notifications.column_settings.poll": "Poll results:",
"notifications.column_settings.push": "Push notifications",
"notifications.column_settings.reblog": "Boosts:",
"notifications.column_settings.show": "Show in column",
"notifications.column_settings.sound": "Play sound",
"notifications.filter.all": "All",
"notifications.filter.boosts": "Boosts",
"notifications.filter.favourites": "Favourites",
"notifications.filter.follows": "Follows",
"notifications.filter.mentions": "Mentions",
"notifications.filter.polls": "Poll results",
"notifications.group": "{count} notifications",
"poll.closed": "Closed",
"poll.refresh": "Refresh",
"poll.total_votes": "{count, plural, one {# vote} other {# votes}}",
"poll.vote": "Vote",
"poll_button.add_poll": "Add a poll",
"poll_button.remove_poll": "Remove poll",
"privacy.change": "Adjust status privacy",
"privacy.direct.long": "Post to mentioned users only",
"privacy.direct.short": "Direct",
"privacy.private.long": "Post to followers only",
"privacy.private.short": "Followers-only",
"privacy.public.long": "Post to public timelines",
"privacy.public.short": "Public",
"privacy.unlisted.long": "Do not show in public timelines",
"privacy.unlisted.short": "Unlisted",
"regeneration_indicator.label": "Loading…",
"regeneration_indicator.sublabel": "Your home feed is being prepared!",
"navigation_bar.preferences": "Nastavitve",
"navigation_bar.profile_directory": "Imenik profilov",
"navigation_bar.public_timeline": "Združena časovnica",
"navigation_bar.security": "Varnost",
"notification.favourite": "{name} je vzljubil/a vaš status",
"notification.follow": "{name} vam sledi",
"notification.mention": "{name} vas je omenil/a",
"notification.poll": "Glasovanje, v katerem ste sodelovali, se je končalo",
"notification.reblog": "{name} je spodbudil/a vaš status",
"notifications.clear": "Počisti obvestila",
"notifications.clear_confirmation": "Ali ste prepričani, da želite trajno izbrisati vsa vaša obvestila?",
"notifications.column_settings.alert": "Namizna obvestila",
"notifications.column_settings.favourite": "Priljubljeni:",
"notifications.column_settings.filter_bar.advanced": "Prikaži vse kategorije",
"notifications.column_settings.filter_bar.category": "Vrstica za hitro filtriranje",
"notifications.column_settings.filter_bar.show": "Pokaži",
"notifications.column_settings.follow": "Novi sledilci:",
"notifications.column_settings.mention": "Omembe:",
"notifications.column_settings.poll": "Rezultati glasovanja:",
"notifications.column_settings.push": "Potisna obvestila",
"notifications.column_settings.reblog": "Spodbude:",
"notifications.column_settings.show": "Prikaži v stolpcu",
"notifications.column_settings.sound": "Predvajaj zvok",
"notifications.filter.all": "Vse",
"notifications.filter.boosts": "Spodbude",
"notifications.filter.favourites": "Priljubljeni",
"notifications.filter.follows": "Sledi",
"notifications.filter.mentions": "Omembe",
"notifications.filter.polls": "Rezultati glasovanj",
"notifications.group": "{count} obvestil",
"poll.closed": "Zaprto",
"poll.refresh": "Osveži",
"poll.total_votes": "{count, plural,one {# glas} other {# glasov}}",
"poll.vote": "Glasuj",
"poll_button.add_poll": "Dodaj anketo",
"poll_button.remove_poll": "Odstrani anketo",
"privacy.change": "Prilagodi zasebnost statusa",
"privacy.direct.long": "Objavi samo omenjenim uporabnikom",
"privacy.direct.short": "Neposredno",
"privacy.private.long": "Objavi samo sledilcem",
"privacy.private.short": "Samo sledilci",
"privacy.public.long": "Objavi na javne časovnice",
"privacy.public.short": "Javno",
"privacy.unlisted.long": "Ne objavi na javne časovnice",
"privacy.unlisted.short": "Ni prikazano",
"regeneration_indicator.label": "Nalaganje…",
"regeneration_indicator.sublabel": "Vaš domači vir se pripravlja!",
"relative_time.days": "{number}d",
"relative_time.hours": "{number}h",
"relative_time.just_now": "now",
"relative_time.just_now": "zdaj",
"relative_time.minutes": "{number}m",
"relative_time.seconds": "{number}s",
"reply_indicator.cancel": "Cancel",
"report.forward": "Forward to {target}",
"report.forward_hint": "The account is from another server. Send an anonymized copy of the report there as well?",
"report.hint": "The report will be sent to your instance moderators. You can provide an explanation of why you are reporting this account below:",
"report.placeholder": "Additional comments",
"report.submit": "Submit",
"report.target": "Report {target}",
"search.placeholder": "Search",
"search_popout.search_format": "Advanced search format",
"search_popout.tips.full_text": "Simple text returns statuses you have written, favourited, boosted, or have been mentioned in, as well as matching usernames, display names, and hashtags.",
"search_popout.tips.hashtag": "hashtag",
"search_popout.tips.status": "status",
"search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags",
"search_popout.tips.user": "user",
"search_results.accounts": "People",
"search_results.hashtags": "Hashtags",
"reply_indicator.cancel": "Prekliči",
"report.forward": "Posreduj do {target}",
"report.forward_hint": "Račun je iz drugega strežnika. Pošljem anonimno kopijo poročila tudi na drugi strežnik?",
"report.hint": "Poročilo bo poslano moderatorjem vašega vozlišča. Spodaj lahko navedete, zakaj prijavljate ta račun:",
"report.placeholder": "Dodatni komentarji",
"report.submit": "Pošlji",
"report.target": "Prijavi {target}",
"search.placeholder": "Iskanje",
"search_popout.search_format": "Napredna oblika iskanja",
"search_popout.tips.full_text": "Enostavno besedilo vrne statuse, ki ste jih napisali, vzljubili, spodbudili ali ste bili v njih omenjeni, kot tudi ujemajoča se uporabniška imena, prikazna imena in ključnike.",
"search_popout.tips.hashtag": "ključnik",
"search_popout.tips.status": "stanje",
"search_popout.tips.text": "Enostavno besedilo vrne ujemajoča se prikazna imena, uporabniška imena in ključnike",
"search_popout.tips.user": "uporabnik",
"search_results.accounts": "Ljudje",
"search_results.hashtags": "Ključniki",
"search_results.statuses": "Tuti",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"status.admin_account": "Open moderation interface for @{name}",
"status.admin_status": "Open this status in the moderation interface",
"status.block": "Block @{name}",
"status.cancel_reblog_private": "Unboost",
"status.cannot_reblog": "This post cannot be boosted",
"status.copy": "Copy link to status",
"status.delete": "Delete",
"status.detailed_status": "Detailed conversation view",
"status.direct": "Direct message @{name}",
"status.embed": "Embed",
"status.favourite": "Favourite",
"status.filtered": "Filtered",
"status.load_more": "Load more",
"status.media_hidden": "Media hidden",
"status.mention": "Mention @{name}",
"status.more": "More",
"status.mute": "Mute @{name}",
"status.mute_conversation": "Mute conversation",
"status.open": "Expand this status",
"status.pin": "Pin on profile",
"search_results.total": "{count, number} {count, plural, one {rezultat} other {rezultatov}}",
"status.admin_account": "Odpri vmesnik za moderiranje za @{name}",
"status.admin_status": "Odpri status v vmesniku za moderiranje",
"status.block": "Blokiraj @{name}",
"status.cancel_reblog_private": "Prekini spodbudo",
"status.cannot_reblog": "Te objave ni mogoče spodbuditi",
"status.copy": "Kopiraj povezavo do statusa",
"status.delete": "Izbriši",
"status.detailed_status": "Podroben pogled pogovora",
"status.direct": "Neposredno sporočilo @{name}",
"status.embed": "Vgradi",
"status.favourite": "Priljubljen",
"status.filtered": "Filtrirano",
"status.load_more": "Naloži več",
"status.media_hidden": "Mediji so skriti",
"status.mention": "Omeni @{name}",
"status.more": "Več",
"status.mute": "Utišaj @{name}",
"status.mute_conversation": "Utišaj pogovor",
"status.open": "Razširi ta status",
"status.pin": "Pripni na profil",
"status.pinned": "Pripeti tut",
"status.read_more": "Read more",
"status.reblog": "Suni",
"status.reblog_private": "Suni v prvotno občinstvo",
"status.reblogged_by": "{name} sunjen",
"status.reblogs.empty": "No one has boosted this toot yet. When someone does, they will show up here.",
"status.redraft": "Delete & re-draft",
"status.read_more": "Preberi več",
"status.reblog": "Spodbudi",
"status.reblog_private": "Spodbudi izvirnemu občinstvu",
"status.reblogged_by": "{name} spodbujen",
"status.reblogs.empty": "Nihče še ni spodbudil tega tuta. Ko se bo to zgodilo, se bodo pojavili tukaj.",
"status.redraft": "Izbriši in preoblikuj",
"status.reply": "Odgovori",
"status.replyAll": "Odgovori na objavo",
"status.report": "Prijavi @{name}",
+1 -1
View File
@@ -320,7 +320,7 @@
"status.block": "屏蔽 @{name}",
"status.cancel_reblog_private": "取消转嘟",
"status.cannot_reblog": "无法转嘟这条嘟文",
"status.copy": "复制链接到嘟文中",
"status.copy": "复制嘟文链接",
"status.delete": "删除",
"status.detailed_status": "对话详情",
"status.direct": "发送私信给 @{name}",
@@ -557,6 +557,7 @@
.compose-form__upload-thumbnail {
border-radius: 4px;
background-color: $base-shadow-color;
background-position: center;
background-size: cover;
background-repeat: no-repeat;
+2 -2
View File
@@ -370,7 +370,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
end
def unsupported_media_type?(mime_type)
mime_type.present? && !(MediaAttachment::IMAGE_MIME_TYPES + MediaAttachment::VIDEO_MIME_TYPES).include?(mime_type)
mime_type.present? && !MediaAttachment.supported_mime_types.include?(mime_type)
end
def supported_blurhash?(blurhash)
@@ -380,7 +380,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def skip_download?
return @skip_download if defined?(@skip_download)
@skip_download ||= DomainBlock.find_by(domain: @account.domain)&.reject_media?
@skip_download ||= DomainBlock.reject_media?(@account.domain)
end
def reply_to_local?
+1 -1
View File
@@ -23,7 +23,7 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity
private
def skip_reports?
DomainBlock.find_by(domain: @account.domain)&.reject_reports?
DomainBlock.reject_reports?(@account.domain)
end
def object_uris
+2 -2
View File
@@ -148,7 +148,7 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
end
def save_media
do_not_download = DomainBlock.find_by(domain: @account.domain)&.reject_media?
do_not_download = DomainBlock.reject_media?(@account.domain)
media_attachments = []
@xml.xpath('./xmlns:link[@rel="enclosure"]', xmlns: OStatus::TagManager::XMLNS).each do |link|
@@ -176,7 +176,7 @@ class OStatus::Activity::Creation < OStatus::Activity::Base
end
def save_emojis(parent)
do_not_download = DomainBlock.find_by(domain: parent.account.domain)&.reject_media?
do_not_download = DomainBlock.reject_media?(parent.account.domain)
return if do_not_download
+3
View File
@@ -102,6 +102,7 @@ class Account < ApplicationRecord
scope :tagged_with, ->(tag) { joins(:accounts_tags).where(accounts_tags: { tag_id: tag }) }
scope :by_recent_status, -> { order(Arel.sql('(case when account_stats.last_status_at is null then 1 else 0 end) asc, account_stats.last_status_at desc')) }
scope :popular, -> { order('account_stats.followers_count desc') }
scope :by_domain_and_subdomains, ->(domain) { where(domain: domain).or(where(arel_table[:domain].matches('%.' + domain))) }
delegate :email,
:unconfirmed_email,
@@ -110,6 +111,8 @@ class Account < ApplicationRecord
:confirmed?,
:approved?,
:pending?,
:disabled?,
:role,
:admin?,
:moderator?,
:staff?,
+2
View File
@@ -37,6 +37,8 @@ class AccountFilter
Account.without_suspended
when 'pending'
accounts_with_users.merge User.pending
when 'disabled'
accounts_with_users.merge User.disabled
when 'silenced'
Account.silenced
when 'suspended'
+18 -1
View File
@@ -1,6 +1,6 @@
# frozen_string_literal: true
require 'mime/types'
require 'mime/types/columnar'
module Attachmentable
extend ActiveSupport::Concern
@@ -10,10 +10,21 @@ module Attachmentable
included do
before_post_process :set_file_extensions
before_post_process :check_image_dimensions
before_post_process :set_file_content_type
end
private
def set_file_content_type
self.class.attachment_definitions.each_key do |attachment_name|
attachment = send(attachment_name)
next if attachment.blank? || attachment.queued_for_write[:original].blank?
attachment.instance_write :content_type, calculated_content_type(attachment)
end
end
def set_file_extensions
self.class.attachment_definitions.each_key do |attachment_name|
attachment = send(attachment_name)
@@ -47,4 +58,10 @@ module Attachmentable
extension
end
def calculated_content_type(attachment)
Paperclip.run('file', '-b --mime :file', file: attachment.queued_for_write[:original].path).split(/[:;\s]+/).first.chomp
rescue Terrapin::CommandLineError
''
end
end
+14
View File
@@ -13,6 +13,20 @@ module UserRoles
admin? || moderator?
end
def role=(value)
case value
when 'admin'
self.admin = true
self.moderator = false
when 'moderator'
self.admin = false
self.moderator = true
else
self.admin = false
self.moderator = false
end
end
def role
if admin?
'admin'
+1
View File
@@ -39,6 +39,7 @@ class CustomEmoji < ApplicationRecord
scope :local, -> { where(domain: nil) }
scope :remote, -> { where.not(domain: nil) }
scope :alphabetic, -> { order(domain: :asc, shortcode: :asc) }
scope :by_domain_and_subdomains, ->(domain) { where(domain: domain).or(where(arel_table[:domain].matches('%.' + domain))) }
remotable_attachment :image, LIMIT
+30 -3
View File
@@ -24,14 +24,41 @@ class DomainBlock < ApplicationRecord
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
def self.blocked?(domain)
where(domain: domain, severity: :suspend).exists?
class << self
def suspend?(domain)
!!rule_for(domain)&.suspend?
end
def silence?(domain)
!!rule_for(domain)&.silence?
end
def reject_media?(domain)
!!rule_for(domain)&.reject_media?
end
def reject_reports?(domain)
!!rule_for(domain)&.reject_reports?
end
alias blocked? suspend?
def rule_for(domain)
return if domain.blank?
uri = Addressable::URI.new.tap { |u| u.host = domain.gsub(/[\/]/, '') }
segments = uri.normalized_host.split('.')
variants = segments.map.with_index { |_, i| segments[i..-1].join('.') }
where(domain: variants[0..-2]).order(Arel.sql('char_length(domain) desc')).first
end
end
def stricter_than?(other_block)
return true if suspend?
return true if suspend?
return false if other_block.suspend? && (silence? || noop?)
return false if other_block.silence? && noop?
(reject_media || !other_block.reject_media) && (reject_reports || !other_block.reject_reports)
end
+3 -7
View File
@@ -8,15 +8,11 @@ class Instance
def initialize(resource)
@domain = resource.domain
@accounts_count = resource.is_a?(DomainBlock) ? nil : resource.accounts_count
@domain_block = resource.is_a?(DomainBlock) ? resource : DomainBlock.find_by(domain: domain)
@domain_block = resource.is_a?(DomainBlock) ? resource : DomainBlock.rule_for(domain)
end
def cached_sample_accounts
Rails.cache.fetch("#{cache_key}/sample_accounts", expires_in: 12.hours) { Account.where(domain: domain).searchable.joins(:account_stat).popular.limit(3) }
end
def cached_accounts_count
@accounts_count || Rails.cache.fetch("#{cache_key}/count", expires_in: 12.hours) { Account.where(domain: domain).count }
def countable?
@accounts_count.present?
end
def to_param
+59 -45
View File
@@ -24,16 +24,16 @@
class MediaAttachment < ApplicationRecord
self.inheritance_column = nil
enum type: [:image, :gifv, :video, :audio, :unknown]
enum type: [:image, :gifv, :video, :unknown, :audio]
IMAGE_FILE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.gif', '.webp'].freeze
VIDEO_FILE_EXTENSIONS = ['.webm', '.mp4', '.m4v', '.mov'].freeze
AUDIO_FILE_EXTENSIONS = ['.mp3', '.m4a', '.wav', '.ogg'].freeze
AUDIO_FILE_EXTENSIONS = ['.ogg', '.oga', '.mp3', '.m4a', '.wav', '.flac', '.opus'].freeze
IMAGE_MIME_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'].freeze
VIDEO_MIME_TYPES = ['video/webm', 'video/mp4', 'video/quicktime'].freeze
VIDEO_MIME_TYPES = ['video/webm', 'video/mp4', 'video/quicktime', 'video/ogg'].freeze
VIDEO_CONVERTIBLE_MIME_TYPES = ['video/webm', 'video/quicktime'].freeze
AUDIO_MIME_TYPES = ['audio/mpeg', 'audio/mp4', 'audio/vnd.wav', 'audio/wav', 'audio/x-wav', 'audio/x-wave', 'audio/ogg',].freeze
AUDIO_MIME_TYPES = ['audio/wave', 'audio/wav', 'audio/x-wav', 'audio/x-wave', 'audio/vdn.wav', 'audio/x-pn-wave', 'audio/ogg', 'audio/mpeg', 'audio/mp3', 'audio/mp4', 'audio/webm', 'audio/flac'].freeze
BLURHASH_OPTIONS = {
x_comp: 4,
@@ -53,22 +53,6 @@ class MediaAttachment < ApplicationRecord
},
}.freeze
AUDIO_STYLES = {
original: {
format: 'mp4',
convert_options: {
output: {
filter_complex: '"[0:a]compand,showwaves=s=640x360:mode=line,format=yuv420p[v]"',
map: '"[v]" -map 0:a',
threads: 2,
vcodec: 'libx264',
acodec: 'aac',
movflags: '+faststart',
},
},
},
}.freeze
VIDEO_STYLES = {
small: {
convert_options: {
@@ -83,8 +67,21 @@ class MediaAttachment < ApplicationRecord
},
}.freeze
AUDIO_STYLES = {
original: {
format: 'mp3',
content_type: 'audio/mpeg',
convert_options: {
output: {
'q:a' => 2,
},
},
},
}.freeze
VIDEO_FORMAT = {
format: 'mp4',
content_type: 'video/mp4',
convert_options: {
output: {
'loglevel' => 'fatal',
@@ -101,6 +98,11 @@ class MediaAttachment < ApplicationRecord
},
}.freeze
VIDEO_CONVERTED_STYLES = {
small: VIDEO_STYLES[:small],
original: VIDEO_FORMAT,
}.freeze
IMAGE_LIMIT = (ENV['MAX_IMAGE_SIZE'] || 8.megabytes).to_i
VIDEO_LIMIT = (ENV['MAX_VIDEO_SIZE'] || 40.megabytes).to_i
@@ -114,8 +116,8 @@ class MediaAttachment < ApplicationRecord
convert_options: { all: '-quality 90 -strip' }
validates_attachment_content_type :file, content_type: IMAGE_MIME_TYPES + VIDEO_MIME_TYPES + AUDIO_MIME_TYPES
validates_attachment_size :file, less_than: IMAGE_LIMIT, unless: :video_or_gifv?
validates_attachment_size :file, less_than: VIDEO_LIMIT, if: :video_or_gifv?
validates_attachment_size :file, less_than: IMAGE_LIMIT, unless: :larger_media_format?
validates_attachment_size :file, less_than: VIDEO_LIMIT, if: :larger_media_format?
remotable_attachment :file, VIDEO_LIMIT
include Attachmentable
@@ -138,8 +140,12 @@ class MediaAttachment < ApplicationRecord
file.blank? && remote_url.present?
end
def video_or_gifv?
video? || gifv?
def larger_media_format?
video? || gifv? || audio?
end
def audio_or_video?
audio? || video?
end
def to_param
@@ -171,37 +177,37 @@ class MediaAttachment < ApplicationRecord
before_save :set_meta
class << self
def supported_mime_types
IMAGE_MIME_TYPES + VIDEO_MIME_TYPES + AUDIO_MIME_TYPES
end
def supported_file_extensions
IMAGE_FILE_EXTENSIONS + VIDEO_FILE_EXTENSIONS + AUDIO_FILE_EXTENSIONS
end
private
def file_styles(f)
if f.instance.file_content_type == 'image/gif'
{
small: IMAGE_STYLES[:small],
original: VIDEO_FORMAT,
}
elsif IMAGE_MIME_TYPES.include? f.instance.file_content_type
if f.instance.file_content_type == 'image/gif' || VIDEO_CONVERTIBLE_MIME_TYPES.include?(f.instance.file_content_type)
VIDEO_CONVERTED_STYLES
elsif IMAGE_MIME_TYPES.include?(f.instance.file_content_type)
IMAGE_STYLES
elsif AUDIO_MIME_TYPES.include? f.instance.file_content_type
AUDIO_STYLES
elsif VIDEO_CONVERTIBLE_MIME_TYPES.include?(f.instance.file_content_type)
{
small: VIDEO_STYLES[:small],
original: VIDEO_FORMAT,
}
else
elsif VIDEO_MIME_TYPES.include?(f.instance.file_content_type)
VIDEO_STYLES
else
AUDIO_STYLES
end
end
def file_processors(f)
if f.file_content_type == 'image/gif'
[:gif_transcoder, :blurhash_transcoder]
elsif VIDEO_MIME_TYPES.include? f.file_content_type
[:video_transcoder, :blurhash_transcoder]
elsif AUDIO_MIME_TYPES.include? f.file_content_type
[:audio_transcoder]
elsif VIDEO_MIME_TYPES.include?(f.file_content_type)
[:video_transcoder, :blurhash_transcoder, :type_corrector]
elsif AUDIO_MIME_TYPES.include?(f.file_content_type)
[:transcoder, :type_corrector]
else
[:lazy_thumbnail, :blurhash_transcoder]
[:lazy_thumbnail, :blurhash_transcoder, :type_corrector]
end
end
end
@@ -224,7 +230,15 @@ class MediaAttachment < ApplicationRecord
end
def set_type_and_extension
self.type = VIDEO_MIME_TYPES.include?(file_content_type) ? :video : AUDIO_MIME_TYPES.include?(file_content_type) ? :audio : :image
self.type = begin
if VIDEO_MIME_TYPES.include?(file_content_type)
:video
elsif AUDIO_MIME_TYPES.include?(file_content_type)
:audio
else
:image
end
end
end
def set_meta
@@ -267,7 +281,7 @@ class MediaAttachment < ApplicationRecord
frame_rate: movie.frame_rate,
duration: movie.duration,
bitrate: movie.bitrate,
}
}.compact
end
def reset_parent_cache
+3
View File
@@ -17,6 +17,8 @@
#
class Report < ApplicationRecord
include Paginable
belongs_to :account
belongs_to :target_account, class_name: 'Account'
belongs_to :action_taken_by_account, class_name: 'Account', optional: true
@@ -26,6 +28,7 @@ class Report < ApplicationRecord
scope :unresolved, -> { where(action_taken: false) }
scope :resolved, -> { where(action_taken: true) }
scope :with_accounts, -> { includes([:account, :target_account, :action_taken_by_account, :assigned_account].each_with_object({}) { |k, h| h[k] = { user: [:invite_request, :invite] } }) }
validates :comment, length: { maximum: 1000 }
+2
View File
@@ -9,9 +9,11 @@ class ReportFilter
def results
scope = Report.unresolved
params.each do |key, value|
scope = scope.merge scope_for(key, value)
end
scope
end
+1
View File
@@ -87,6 +87,7 @@ class User < ApplicationRecord
scope :approved, -> { where(approved: true) }
scope :confirmed, -> { where.not(confirmed_at: nil) }
scope :enabled, -> { where(disabled: false) }
scope :disabled, -> { where(disabled: true) }
scope :inactive, -> { where(arel_table[:current_sign_in_at].lt(ACTIVE_DURATION.ago)) }
scope :active, -> { confirmed.where(arel_table[:current_sign_in_at].gteq(ACTIVE_DURATION.ago)).joins(:account).where(accounts: { suspended_at: nil }) }
scope :matches_email, ->(value) { where(arel_table[:email].matches("#{value}%")) }
+1 -1
View File
@@ -76,7 +76,7 @@ class InitialStateSerializer < ActiveModel::Serializer
end
def media_attachments
{ accept_content_types: MediaAttachment::IMAGE_FILE_EXTENSIONS + MediaAttachment::VIDEO_FILE_EXTENSIONS + MediaAttachment::AUDIO_FILE_EXTENSIONS + MediaAttachment::IMAGE_MIME_TYPES + MediaAttachment::VIDEO_MIME_TYPES + MediaAttachment::AUDIO_MIME_TYPES }
{ accept_content_types: MediaAttachment.supported_file_extensions + MediaAttachment.supported_mime_types }
end
private
@@ -0,0 +1,77 @@
# frozen_string_literal: true
class REST::Admin::AccountSerializer < ActiveModel::Serializer
attributes :id, :username, :domain, :created_at,
:email, :ip, :role, :confirmed, :suspended,
:silenced, :disabled, :approved, :locale,
:invite_request
attribute :created_by_application_id, if: :created_by_application?
attribute :invited_by_account_id, if: :invited?
has_one :account, serializer: REST::AccountSerializer
def id
object.id.to_s
end
def email
object.user_email
end
def ip
object.user_current_sign_in_ip.to_s.presence
end
def role
object.user_role
end
def suspended
object.suspended?
end
def silenced
object.silenced?
end
def confirmed
object.user_confirmed?
end
def disabled
object.user_disabled?
end
def approved
object.user_approved?
end
def account
object
end
def locale
object.user_locale
end
def created_by_application_id
object.user&.created_by_application_id&.to_s&.presence
end
def invite_request
object.user&.invite_request&.text
end
def invited_by_account_id
object.user&.invite&.user&.account_id&.to_s&.presence
end
def invited?
object.user&.invited?
end
def created_by_application?
object.user&.created_by_application_id&.present?
end
end
@@ -0,0 +1,16 @@
# frozen_string_literal: true
class REST::Admin::ReportSerializer < ActiveModel::Serializer
attributes :id, :action_taken, :comment, :created_at, :updated_at
has_one :account, serializer: REST::Admin::AccountSerializer
has_one :target_account, serializer: REST::Admin::AccountSerializer
has_one :assigned_account, serializer: REST::Admin::AccountSerializer
has_one :action_taken_by_account, serializer: REST::Admin::AccountSerializer
has_many :statuses, serializer: REST::StatusSerializer
def id
object.id.to_s
end
end
+10 -2
View File
@@ -3,9 +3,9 @@
class REST::InstanceSerializer < ActiveModel::Serializer
include RoutingHelper
attributes :uri, :title, :description, :email,
attributes :uri, :title, :short_description, :description, :email,
:version, :urls, :stats, :thumbnail, :max_toot_chars, :poll_limits,
:languages, :registrations
:languages, :registrations, :approval_required
has_one :contact_account, serializer: REST::AccountSerializer
@@ -19,6 +19,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer
Setting.site_title
end
def short_description
Setting.site_short_description
end
def description
Setting.site_description
end
@@ -68,6 +72,10 @@ class REST::InstanceSerializer < ActiveModel::Serializer
Setting.registrations_mode != 'none' && !Rails.configuration.x.single_user_mode
end
def approval_required
Setting.registrations_mode == 'approved'
end
private
def instance_presenter
@@ -205,7 +205,7 @@ class ActivityPub::ProcessAccountService < BaseService
def domain_block
return @domain_block if defined?(@domain_block)
@domain_block = DomainBlock.find_by(domain: @domain)
@domain_block = DomainBlock.rule_for(@domain)
end
def key_changed?
+2 -2
View File
@@ -76,7 +76,7 @@ class BlockDomainService < BaseService
end
def blocked_domain_accounts
Account.where(domain: blocked_domain)
Account.by_domain_and_subdomains(blocked_domain)
end
def media_from_blocked_domain
@@ -84,6 +84,6 @@ class BlockDomainService < BaseService
end
def emojis_from_blocked_domains
CustomEmoji.where(domain: blocked_domain)
CustomEmoji.by_domain_and_subdomains(blocked_domain)
end
end
+1 -1
View File
@@ -107,7 +107,7 @@ class PostStatusService < BaseService
@media = @account.media_attachments.where(status_id: nil).where(id: @options[:media_ids].take(4).map(&:to_i))
raise Mastodon::ValidationError, I18n.t('media_attachments.validations.images_and_video') if @media.size > 1 && @media.find(&:video?)
raise Mastodon::ValidationError, I18n.t('media_attachments.validations.images_and_video') if @media.size > 1 && @media.find(&:audio_or_video?)
end
def language_from_option(str)
+1 -1
View File
@@ -146,7 +146,7 @@ class ResolveAccountService < BaseService
def domain_block
return @domain_block if defined?(@domain_block)
@domain_block = DomainBlock.find_by(domain: @domain)
@domain_block = DomainBlock.rule_for(@domain)
end
def atom_url
+2 -1
View File
@@ -14,7 +14,8 @@ class UnblockDomainService < BaseService
end
def blocked_accounts
scope = Account.where(domain: domain_block.domain)
scope = Account.by_domain_and_subdomains(domain_block.domain)
if domain_block.silence?
scope.where(silenced_at: @domain_block.created_at)
else
@@ -26,7 +26,7 @@ class UpdateRemoteProfileService < BaseService
account.note = remote_profile.note || ''
account.locked = remote_profile.locked?
if !account.suspended? && !DomainBlock.find_by(domain: account.domain)&.reject_media?
if !account.suspended? && !DomainBlock.reject_media?(account.domain)
if remote_profile.avatar.present?
account.avatar_remote_url = remote_profile.avatar
else
@@ -46,7 +46,7 @@ class UpdateRemoteProfileService < BaseService
end
def save_emojis
do_not_download = DomainBlock.find_by(domain: account.domain)&.reject_media?
do_not_download = DomainBlock.reject_media?(account.domain)
return if do_not_download
+11 -10
View File
@@ -33,21 +33,22 @@
%h4
= instance.domain
%small
= t('admin.instances.known_accounts', count: instance.cached_accounts_count)
- if instance.domain_block
- first_item = true
- if !instance.domain_block.noop?
&bull;
= t("admin.domain_blocks.severity.#{instance.domain_block.severity}")
- first_item = false
- if instance.domain_block.reject_media?
&bull;
- unless first_item
&bull;
= t('admin.domain_blocks.rejecting_media')
- first_item = false
- if instance.domain_block.reject_reports?
&bull;
- unless first_item
&bull;
= t('admin.domain_blocks.rejecting_reports')
.avatar-stack
- instance.cached_sample_accounts.each do |account|
= image_tag current_account&.user&.setting_auto_play_gif ? account.avatar_original_url : account.avatar_static_url, width: 48, height: 48, alt: '', class: 'account__avatar'
- else
= t('admin.accounts.no_limits_imposed')
- if instance.countable?
.trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true
= paginate paginated_instances
@@ -27,7 +27,7 @@
= render partial: 'stream_entries/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay }
- if !status.media_attachments.empty?
- if status.media_attachments.first.video?
- if status.media_attachments.first.audio_or_video?
- video = status.media_attachments.first
= react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do
= render partial: 'stream_entries/attachment_list', locals: { attachments: status.media_attachments }
@@ -31,7 +31,7 @@
= render partial: 'stream_entries/poll', locals: { status: status, poll: status.preloadable_poll, autoplay: autoplay }
- if !status.media_attachments.empty?
- if status.media_attachments.first.video?
- if status.media_attachments.first.audio_or_video?
- video = status.media_attachments.first
= react_component :video, src: video.file.url(:original), preview: video.file.url(:small), blurhash: video.blurhash, sensitive: !current_account&.user&.show_all_media? && status.sensitive? || current_account&.user&.hide_all_media?, width: 610, height: 343, inline: true, alt: video.description do
= render partial: 'stream_entries/attachment_list', locals: { attachments: status.media_attachments }