From 1663368724b0a0076823aa31459eb77b9264969b Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Fri, 20 Apr 2018 09:06:53 +0900 Subject: [PATCH 01/82] Replace preload link tag to Rails helper (#7192) --- app/views/home/index.html.haml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/views/home/index.html.haml b/app/views/home/index.html.haml index 8c88d2d64f..7b1a7e50ae 100644 --- a/app/views/home/index.html.haml +++ b/app/views/home/index.html.haml @@ -1,8 +1,9 @@ - content_for :header_tags do - %link{ href: asset_pack_path('features/getting_started.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ - %link{ href: asset_pack_path('features/compose.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ - %link{ href: asset_pack_path('features/home_timeline.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ - %link{ href: asset_pack_path('features/notifications.js'), crossorigin: 'anonymous', rel: 'preload', as: 'script' }/ + = preload_link_tag asset_pack_path('features/getting_started.js'), crossorigin: 'anonymous' + = preload_link_tag asset_pack_path('features/compose.js'), crossorigin: 'anonymous' + = preload_link_tag asset_pack_path('features/home_timeline.js'), crossorigin: 'anonymous' + = preload_link_tag asset_pack_path('features/notifications.js'), crossorigin: 'anonymous' + %meta{name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key} %script#initial-state{ type: 'application/json' }!= json_escape(@initial_state_json) From a9c440637ca9f36bcf051094abe3bcba1da63166 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Fri, 20 Apr 2018 02:28:48 +0200 Subject: [PATCH 02/82] Improve report layout (#7188) * Use table for statuses in report * Display reported account and reporter in the same table * Split accounts and general report info into two tables again * Redesign report statuses table, notes, merge notes and action log * Remove unused translations * Fix code style issue * Fix code style issue * Fix code style issue --- .../admin/reported_statuses_controller.rb | 14 ++- app/controllers/admin/reports_controller.rb | 8 +- .../admin/account_moderation_notes_helper.rb | 16 +++ app/helpers/application_helper.rb | 4 + app/helpers/stream_entries_helper.rb | 13 ++ app/javascript/packs/admin.js | 1 + app/javascript/styles/mastodon/admin.scss | 103 ++++++++------- .../styles/mastodon/components.scss | 16 ++- app/javascript/styles/mastodon/tables.scss | 116 ++++++++++++++++- .../admin/action_logs/_action_log.html.haml | 2 +- app/views/admin/action_logs/index.html.haml | 3 +- .../admin/report_notes/_report_note.html.haml | 14 +-- app/views/admin/reports/_account.html.haml | 19 +++ .../admin/reports/_account_details.html.haml | 20 --- app/views/admin/reports/_action_log.html.haml | 6 + app/views/admin/reports/_report.html.haml | 6 +- app/views/admin/reports/_status.html.haml | 28 +++++ app/views/admin/reports/index.html.haml | 27 ++-- app/views/admin/reports/show.html.haml | 117 ++++++++---------- .../stream_entries/_detailed_status.html.haml | 6 +- .../stream_entries/_simple_status.html.haml | 4 +- config/locales/ar.yml | 1 - config/locales/ca.yml | 1 - config/locales/de.yml | 1 - config/locales/en.yml | 26 ++-- config/locales/eo.yml | 1 - config/locales/es.yml | 1 - config/locales/fa.yml | 1 - config/locales/fi.yml | 1 - config/locales/fr.yml | 1 - config/locales/gl.yml | 1 - config/locales/he.yml | 1 - config/locales/hu.yml | 1 - config/locales/id.yml | 1 - config/locales/io.yml | 1 - config/locales/ja.yml | 5 - config/locales/ko.yml | 1 - config/locales/nl.yml | 1 - config/locales/no.yml | 1 - config/locales/oc.yml | 1 - config/locales/pl.yml | 12 -- config/locales/pt-BR.yml | 1 - config/locales/pt.yml | 1 - config/locales/ru.yml | 1 - config/locales/sk.yml | 1 - config/locales/sr-Latn.yml | 1 - config/locales/sr.yml | 1 - config/locales/sv.yml | 1 - config/locales/th.yml | 1 - config/locales/tr.yml | 1 - config/locales/uk.yml | 1 - config/locales/zh-CN.yml | 1 - config/locales/zh-HK.yml | 12 -- config/locales/zh-TW.yml | 1 - .../reported_statuses_controller_spec.rb | 2 +- 55 files changed, 380 insertions(+), 249 deletions(-) create mode 100644 app/views/admin/reports/_account.html.haml delete mode 100644 app/views/admin/reports/_account_details.html.haml create mode 100644 app/views/admin/reports/_action_log.html.haml create mode 100644 app/views/admin/reports/_status.html.haml diff --git a/app/controllers/admin/reported_statuses_controller.rb b/app/controllers/admin/reported_statuses_controller.rb index 535bd11d48..522f68c98e 100644 --- a/app/controllers/admin/reported_statuses_controller.rb +++ b/app/controllers/admin/reported_statuses_controller.rb @@ -8,7 +8,7 @@ module Admin def create authorize :status, :update? - @form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account)) + @form = Form::StatusBatch.new(form_status_batch_params.merge(current_account: current_account, action: action_from_button)) flash[:alert] = I18n.t('admin.statuses.failed_to_execute') unless @form.save redirect_to admin_report_path(@report) @@ -35,7 +35,17 @@ module Admin end def form_status_batch_params - params.require(:form_status_batch).permit(:action, status_ids: []) + params.require(:form_status_batch).permit(status_ids: []) + end + + def action_from_button + if params[:nsfw_on] + 'nsfw_on' + elsif params[:nsfw_off] + 'nsfw_off' + elsif params[:delete] + 'delete' + end end def set_report diff --git a/app/controllers/admin/reports_controller.rb b/app/controllers/admin/reports_controller.rb index a4ae9507d4..d00b3d2227 100644 --- a/app/controllers/admin/reports_controller.rb +++ b/app/controllers/admin/reports_controller.rb @@ -11,10 +11,10 @@ module Admin def show authorize @report, :show? - @report_note = @report.notes.new - @report_notes = @report.notes.latest - @report_history = @report.history - @form = Form::StatusBatch.new + + @report_note = @report.notes.new + @report_notes = (@report.notes.latest + @report.history).sort_by(&:created_at) + @form = Form::StatusBatch.new end def update diff --git a/app/helpers/admin/account_moderation_notes_helper.rb b/app/helpers/admin/account_moderation_notes_helper.rb index b17c522643..fdfadef080 100644 --- a/app/helpers/admin/account_moderation_notes_helper.rb +++ b/app/helpers/admin/account_moderation_notes_helper.rb @@ -1,4 +1,20 @@ # frozen_string_literal: true module Admin::AccountModerationNotesHelper + def admin_account_link_to(account) + link_to admin_account_path(account.id), class: name_tag_classes(account) do + safe_join([ + image_tag(account.avatar.url, width: 15, height: 15, alt: display_name(account), class: 'avatar'), + content_tag(:span, account.acct, class: 'username'), + ], ' ') + end + end + + private + + def name_tag_classes(account) + classes = ['name-tag'] + classes << 'suspended' if account.suspended? + classes.join(' ') + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index bab4615a18..95863ab1f0 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -63,4 +63,8 @@ module ApplicationHelper def opengraph(property, content) tag(:meta, content: content, property: property) end + + def react_component(name, props = {}) + content_tag(:div, nil, data: { component: name.to_s.camelcase, props: Oj.dump(props) }) + end end diff --git a/app/helpers/stream_entries_helper.rb b/app/helpers/stream_entries_helper.rb index 3992432dbd..8254ef4dc9 100644 --- a/app/helpers/stream_entries_helper.rb +++ b/app/helpers/stream_entries_helper.rb @@ -113,6 +113,19 @@ module StreamEntriesHelper end end + def fa_visibility_icon(status) + case status.visibility + when 'public' + fa_icon 'globe fw' + when 'unlisted' + fa_icon 'unlock-alt fw' + when 'private' + fa_icon 'lock fw' + when 'direct' + fa_icon 'envelope fw' + end + end + private def simplified_text(text) diff --git a/app/javascript/packs/admin.js b/app/javascript/packs/admin.js index 2bf1514a99..5dbcc03d39 100644 --- a/app/javascript/packs/admin.js +++ b/app/javascript/packs/admin.js @@ -24,6 +24,7 @@ delegate(document, batchCheckboxClassName, 'change', () => { const checkAllElement = document.querySelector('#batch_checkbox_all'); if (checkAllElement) { checkAllElement.checked = [].every.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); + checkAllElement.indeterminate = !checkAllElement.checked && [].some.call(document.querySelectorAll(batchCheckboxClassName), (content) => content.checked); } }); diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index 348f72078e..a0f69449a7 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -141,14 +141,15 @@ } hr { - margin: 20px 0; + width: 100%; + height: 0; border: 0; - background: transparent; - border-bottom: 1px solid $ui-base-color; + border-bottom: 1px solid rgba($ui-base-lighter-color, .6); + margin: 20px 0; - &.section-break { - margin: 30px 0; - border-bottom: 2px solid $ui-base-lighter-color; + &.spacer { + height: 1px; + border: 0; } } @@ -335,34 +336,8 @@ } } -.report-note__comment { - margin-bottom: 20px; -} - -.report-note__form { - margin-bottom: 20px; - - .report-note__textarea { - box-sizing: border-box; - border: 0; - padding: 7px 4px; - margin-bottom: 10px; - font-size: 16px; - color: $inverted-text-color; - display: block; - width: 100%; - outline: 0; - font-family: inherit; - resize: vertical; - } - - .report-note__buttons { - text-align: right; - } - - .report-note__button { - margin: 0 0 5px 5px; - } +.simple_form.new_report_note { + max-width: 100%; } .batch-form-box { @@ -390,13 +365,6 @@ } } -.batch-checkbox, -.batch-checkbox-all { - display: flex; - align-items: center; - margin-right: 5px; -} - .back-link { margin-bottom: 10px; font-size: 14px; @@ -416,7 +384,7 @@ } .log-entry { - margin-bottom: 8px; + margin-bottom: 20px; line-height: 20px; &__header { @@ -514,9 +482,12 @@ } } +a.name-tag, .name-tag { display: flex; align-items: center; + text-decoration: none; + color: $ui-secondary-color; .avatar { display: block; @@ -528,4 +499,52 @@ .username { font-weight: 500; } + + &.suspended { + .username { + text-decoration: line-through; + color: lighten($error-red, 12%); + } + + .avatar { + filter: grayscale(100%); + opacity: 0.8; + } + } +} + +.speech-bubble { + margin-bottom: 20px; + border-left: 4px solid $ui-highlight-color; + + &.positive { + border-left-color: $success-green; + } + + &.negative { + border-left-color: lighten($error-red, 12%); + } + + &__bubble { + padding: 16px; + padding-left: 14px; + font-size: 15px; + line-height: 20px; + border-radius: 4px 4px 4px 0; + position: relative; + font-weight: 500; + + a { + color: $ui-primary-color; + } + } + + &__owner { + padding: 8px; + padding-left: 12px; + } + + time { + color: $darker-text-color; + } } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 9b5ab6f01d..908fa8a072 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -1006,6 +1006,15 @@ padding: 10px; border-bottom: 1px solid lighten($ui-base-color, 8%); + &.compact { + padding: 0; + border-bottom: 0; + + .account__avatar-wrapper { + margin-left: 0; + } + } + .account__display-name { flex: 1 1 auto; display: block; @@ -1029,7 +1038,6 @@ .account__avatar { @include avatar-radius(); position: relative; - cursor: pointer; &-inline { display: inline-block; @@ -1038,6 +1046,10 @@ } } +a .account__avatar { + cursor: pointer; +} + .account__avatar-overlay { @include avatar-size(48px); @@ -1286,7 +1298,7 @@ .status__display-name, .reply-indicator__display-name, .detailed-status__display-name, -.account__display-name { +a.account__display-name { &:hover strong { text-decoration: underline; } diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index c12d84f1c0..fa876e6031 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -11,6 +11,7 @@ vertical-align: top; border-top: 1px solid $ui-base-color; text-align: left; + background: darken($ui-base-color, 4%); } & > thead > tr > th { @@ -48,9 +49,38 @@ } } - &.inline-table > tbody > tr:nth-child(odd) > td, - &.inline-table > tbody > tr:nth-child(odd) > th { - background: transparent; + &.inline-table { + & > tbody > tr:nth-child(odd) { + & > td, + & > th { + background: transparent; + } + } + + & > tbody > tr:first-child { + & > td, + & > th { + border-top: 0; + } + } + } + + &.batch-table { + & > thead > tr > th { + background: $ui-base-color; + border-top: 1px solid darken($ui-base-color, 8%); + border-bottom: 1px solid darken($ui-base-color, 8%); + + &:first-child { + border-radius: 4px 0 0; + border-left: 1px solid darken($ui-base-color, 8%); + } + + &:last-child { + border-radius: 0 4px 0 0; + border-right: 1px solid darken($ui-base-color, 8%); + } + } } } @@ -63,6 +93,13 @@ samp { font-family: 'mastodon-font-monospace', monospace; } +button.table-action-link { + background: transparent; + border: 0; + font: inherit; +} + +button.table-action-link, a.table-action-link { text-decoration: none; display: inline-block; @@ -79,4 +116,77 @@ a.table-action-link { font-weight: 400; margin-right: 5px; } + + &:first-child { + padding-left: 0; + } +} + +.batch-table { + &__toolbar, + &__row { + display: flex; + + &__select { + box-sizing: border-box; + padding: 8px 16px; + cursor: pointer; + min-height: 100%; + + input { + margin-top: 8px; + } + } + + &__actions, + &__content { + padding: 8px 0; + padding-right: 16px; + flex: 1 1 auto; + } + } + + &__toolbar { + border: 1px solid darken($ui-base-color, 8%); + background: $ui-base-color; + border-radius: 4px 0 0; + height: 47px; + align-items: center; + + &__actions { + text-align: right; + padding-right: 16px - 5px; + } + } + + &__row { + border: 1px solid darken($ui-base-color, 8%); + border-top: 0; + background: darken($ui-base-color, 4%); + + &:hover { + background: darken($ui-base-color, 2%); + } + + &:nth-child(even) { + background: $ui-base-color; + + &:hover { + background: lighten($ui-base-color, 2%); + } + } + + &__content { + padding-top: 12px; + padding-bottom: 16px; + } + } + + .status__content { + padding-top: 0; + + strong { + font-weight: 700; + } + } } diff --git a/app/views/admin/action_logs/_action_log.html.haml b/app/views/admin/action_logs/_action_log.html.haml index ec90961cbd..f059814bd6 100644 --- a/app/views/admin/action_logs/_action_log.html.haml +++ b/app/views/admin/action_logs/_action_log.html.haml @@ -1,4 +1,4 @@ -%li.log-entry +.log-entry .log-entry__header .log-entry__avatar = image_tag action_log.account.avatar.url(:original), alt: '', width: 40, height: 40, class: 'avatar' diff --git a/app/views/admin/action_logs/index.html.haml b/app/views/admin/action_logs/index.html.haml index bb6d7b5d73..a4d3871a90 100644 --- a/app/views/admin/action_logs/index.html.haml +++ b/app/views/admin/action_logs/index.html.haml @@ -1,7 +1,6 @@ - content_for :page_title do = t('admin.action_logs.title') -%ul - = render @action_logs += render @action_logs = paginate @action_logs diff --git a/app/views/admin/report_notes/_report_note.html.haml b/app/views/admin/report_notes/_report_note.html.haml index 1f621e0d3b..d34dc3d157 100644 --- a/app/views/admin/report_notes/_report_note.html.haml +++ b/app/views/admin/report_notes/_report_note.html.haml @@ -1,9 +1,7 @@ -%li - %h4 - = report_note.account.acct - %div{ style: 'float: right' } - %time.formatted{ datetime: report_note.created_at.iso8601, title: l(report_note.created_at) } - = l report_note.created_at - = table_link_to 'trash', t('admin.reports.notes.delete'), admin_report_note_path(report_note), method: :delete if can?(:destroy, report_note) - %div{ class: 'report-note__comment' } +.speech-bubble + .speech-bubble__bubble = simple_format(h(report_note.content)) + .speech-bubble__owner + = admin_account_link_to report_note.account + %time.formatted{ datetime: report_note.created_at.iso8601 }= l report_note.created_at + = table_link_to 'trash', t('admin.reports.notes.delete'), admin_report_note_path(report_note), method: :delete if can?(:destroy, report_note) diff --git a/app/views/admin/reports/_account.html.haml b/app/views/admin/reports/_account.html.haml new file mode 100644 index 0000000000..22b7a08618 --- /dev/null +++ b/app/views/admin/reports/_account.html.haml @@ -0,0 +1,19 @@ +- size ||= 36 + +.account.compact + .account__wrapper + - if account.nil? + .account__display-name + .account__avatar-wrapper + .account__avatar{ style: "background-image: url(#{full_asset_url('avatars/original/missing.png', skip_pipeline: true)}); width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px" } + %span.display-name + %strong= t 'about.contact_missing' + %span.display-name__account= t 'about.contact_unavailable' + - else + = link_to TagManager.instance.url_for(account), class: 'account__display-name' do + .account__avatar-wrapper + .account__avatar{ style: "background-image: url(#{account.avatar.url}); width: #{size}px; height: #{size}px; background-size: #{size}px #{size}px" } + %span.display-name + %bdi + %strong.display-name__html.emojify= display_name(account) + %span.display-name__account @#{account.acct} diff --git a/app/views/admin/reports/_account_details.html.haml b/app/views/admin/reports/_account_details.html.haml deleted file mode 100644 index a8af39bef9..0000000000 --- a/app/views/admin/reports/_account_details.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -.table-wrapper - %table.table - %tbody - %tr - %td= t('admin.reports.account.created_reports') - %td= link_to pluralize(account.reports.count, t('admin.reports.account.report')), admin_reports_path(account_id: account.id) - %tr - %td= t('admin.reports.account.targeted_reports') - %td= link_to pluralize(account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: account.id) - %tr - %td= t('admin.reports.account.moderation_notes') - %td= link_to pluralize(account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: account.id) - - if account.silenced? || account.suspended? - %tr - %td= t('admin.reports.account.moderation.title') - %td - - if account.silenced? - %p= t('admin.reports.account.moderation.silenced') - - if account.suspended? - %p= t('admin.reports.account.moderation.suspended') diff --git a/app/views/admin/reports/_action_log.html.haml b/app/views/admin/reports/_action_log.html.haml new file mode 100644 index 0000000000..024078eb9a --- /dev/null +++ b/app/views/admin/reports/_action_log.html.haml @@ -0,0 +1,6 @@ +.speech-bubble.positive + .speech-bubble__bubble + = t("admin.action_logs.actions.#{action_log.action}_#{action_log.target_type.underscore}", name: content_tag(:span, action_log.account.username, class: 'username'), target: content_tag(:span, log_target(action_log), class: 'target')).html_safe + .speech-bubble__owner + = admin_account_link_to(action_log.account) + %time.formatted{ datetime: action_log.created_at.iso8601 }= l action_log.created_at diff --git a/app/views/admin/reports/_report.html.haml b/app/views/admin/reports/_report.html.haml index 84db00ad50..d6c881955c 100644 --- a/app/views/admin/reports/_report.html.haml +++ b/app/views/admin/reports/_report.html.haml @@ -2,9 +2,9 @@ %td.id = "##{report.id}" %td.target - = link_to report.target_account.acct, admin_account_path(report.target_account.id) + = admin_account_link_to report.target_account %td.reporter - = link_to report.account.acct, admin_account_path(report.account.id) + = admin_account_link_to report.account %td %div{ title: report.comment } = truncate(report.comment, length: 30, separator: ' ') @@ -21,6 +21,6 @@ - if report.assigned_account.nil? \- - else - = link_to report.assigned_account.acct, admin_account_path(report.assigned_account.id) + = admin_account_link_to report.assigned_account %td = table_link_to 'circle', t('admin.reports.view'), admin_report_path(report) diff --git a/app/views/admin/reports/_status.html.haml b/app/views/admin/reports/_status.html.haml new file mode 100644 index 0000000000..137609539b --- /dev/null +++ b/app/views/admin/reports/_status.html.haml @@ -0,0 +1,28 @@ +.batch-table__row + %label.batch-table__row__select.batch-checkbox + = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id + .batch-table__row__content + .status__content>< + - unless status.spoiler_text.blank? + %p>< + %strong= Formatter.instance.format_spoiler(status) + + = Formatter.instance.format(status) + + - unless status.media_attachments.empty? + - if status.media_attachments.first.video? + - video = status.media_attachments.first + = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true + - else + = react_component :media_gallery, height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } + + .detailed-status__meta + = link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime', target: stream_link_target, rel: 'noopener' do + %time.formatted{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at) + · + = fa_visibility_icon(status) + = t("statuses.visibilities.#{status.visibility}") + - if status.sensitive? + · + = fa_icon('eye-slash fw') + = t('stream_entries.sensitive_content') diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml index c3baaf6be7..44a531f2c6 100644 --- a/app/views/admin/reports/index.html.haml +++ b/app/views/admin/reports/index.html.haml @@ -8,20 +8,17 @@ %li= filter_link_to t('admin.reports.unresolved'), resolved: nil %li= filter_link_to t('admin.reports.resolved'), resolved: '1' -= form_tag do - - .table-wrapper - %table.table - %thead - %tr - -# %th - %th= t('admin.reports.id') - %th= t('admin.reports.target') - %th= t('admin.reports.reported_by') - %th= t('admin.reports.report_contents') - %th= t('admin.reports.assigned') - %th - %tbody - = render @reports +.table-wrapper + %table.table + %thead + %tr + %th= t('admin.reports.id') + %th= t('admin.reports.target') + %th= t('admin.reports.reported_by') + %th= t('admin.reports.report_contents') + %th= t('admin.reports.assigned') + %th + %tbody + = render @reports = paginate @reports diff --git a/app/views/admin/reports/show.html.haml b/app/views/admin/reports/show.html.haml index 1306500011..2bba3079ed 100644 --- a/app/views/admin/reports/show.html.haml +++ b/app/views/admin/reports/show.html.haml @@ -14,16 +14,28 @@ - else = link_to t('admin.reports.mark_as_unresolved'), admin_report_path(@report, outcome: 'reopen'), method: :put, class: 'button' +%hr.spacer + .table-wrapper %table.table.inline-table %tbody + %tr + %th= t('admin.reports.reported_account') + %td= admin_account_link_to @report.target_account + %td= table_link_to 'flag', pluralize(@report.target_account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: @report.target_account.id) + %td= table_link_to 'file', pluralize(@report.target_account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: @report.target_account.id) + %tr + %th= t('admin.reports.reported_by') + %td= admin_account_link_to @report.account + %td= table_link_to 'flag', pluralize(@report.account.targeted_reports.count, t('admin.reports.account.report')), admin_reports_path(target_account_id: @report.account.id) + %td= table_link_to 'file', pluralize(@report.account.targeted_moderation_notes.count, t('admin.reports.account.note')), admin_reports_path(target_account_id: @report.account.id) %tr %th= t('admin.reports.created_at') - %td{colspan: 2} + %td{ colspan: 3 } %time.formatted{ datetime: @report.created_at.iso8601 } %tr %th= t('admin.reports.updated_at') - %td{colspan: 2} + %td{ colspan: 3 } %time.formatted{ datetime: @report.updated_at.iso8601 } %tr %th= t('admin.reports.status') @@ -32,14 +44,14 @@ = t('admin.reports.resolved') - else = t('admin.reports.unresolved') - %td{style: "text-align: right; overflow: hidden;"} + %td{ colspan: 2 } - if @report.action_taken? = table_link_to 'envelope-open', t('admin.reports.reopen'), admin_report_path(@report, outcome: 'reopen'), method: :put - if !@report.action_taken_by_account.nil? %tr %th= t('admin.reports.action_taken_by') - %td{colspan: 2} - = @report.action_taken_by_account.acct + %td{ colspan: 3 } + = admin_account_link_to @report.action_taken_by_account - else %tr %th= t('admin.reports.assigned') @@ -47,78 +59,55 @@ - if @report.assigned_account.nil? \- - else - = link_to @report.assigned_account.acct, admin_account_path(@report.assigned_account.id) - %td{style: "text-align: right"} + = admin_account_link_to @report.assigned_account + %td - if @report.assigned_account != current_user.account = table_link_to 'user', t('admin.reports.assign_to_self'), admin_report_path(@report, outcome: 'assign_to_self'), method: :put + %td - if !@report.assigned_account.nil? = table_link_to 'trash', t('admin.reports.unassign'), admin_report_path(@report, outcome: 'unassign'), method: :put -%hr{ class: "section-break"}/ +%hr.spacer -.report-accounts - .report-accounts__item - %h3= t('admin.reports.reported_account') - = render 'authorize_follows/card', account: @report.target_account, admin: true - = render 'admin/reports/account_details', account: @report.target_account - .report-accounts__item - %h3= t('admin.reports.reported_by') - = render 'authorize_follows/card', account: @report.account, admin: true - = render 'admin/reports/account_details', account: @report.account - -%h3= t('admin.reports.comment.label') - -= simple_format(@report.comment.presence || t('admin.reports.comment.none')) +.speech-bubble + .speech-bubble__bubble= simple_format(@report.comment.presence || t('admin.reports.comment.none')) + .speech-bubble__owner + = admin_account_link_to @report.account + %time.formatted{ datetime: @report.created_at.iso8601 } - unless @report.statuses.empty? - %hr/ - - %h3= t('admin.reports.statuses') + %hr.spacer/ = form_for(@form, url: admin_report_reported_statuses_path(@report.id)) do |f| - .batch-form-box - .batch-checkbox-all - = check_box_tag :batch_checkbox_all, nil, false - = f.select :action, Form::StatusBatch::ACTION_TYPE.map{|action| [t("admin.statuses.batch.#{action}"), action]} - = f.submit t('admin.statuses.execute'), data: { confirm: t('admin.reports.are_you_sure') }, class: 'button' - .media-spoiler-toggle-buttons - .media-spoiler-show-button.button= t('admin.statuses.media.show') - .media-spoiler-hide-button.button= t('admin.statuses.media.hide') - - @report.statuses.each do |status| - .report-status{ data: { id: status.id } } - .batch-checkbox - = f.check_box :status_ids, { multiple: true, include_hidden: false }, status.id - .activity-stream.activity-stream-headless - .entry= render 'stream_entries/simple_status', status: status - .report-status__actions - - unless status.media_attachments.empty? - = link_to admin_report_reported_status_path(@report, status, status: { sensitive: !status.sensitive }), method: :put, class: 'icon-button nsfw-button', title: t("admin.reports.nsfw.#{!status.sensitive}") do - = fa_icon status.sensitive? ? 'eye' : 'eye-slash' - = link_to admin_report_reported_status_path(@report, status), method: :delete, class: 'icon-button trash-button', title: t('admin.reports.delete'), data: { confirm: t('admin.reports.are_you_sure') }, remote: true do - = fa_icon 'trash' + .batch-table + .batch-table__toolbar + %label.batch-table__toolbar__select.batch-checkbox-all + = check_box_tag :batch_checkbox_all, nil, false + .batch-table__toolbar__actions + = f.button safe_join([fa_icon('eye-slash'), t('admin.statuses.batch.nsfw_on')]), name: :nsfw_on, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + = f.button safe_join([fa_icon('eye'), t('admin.statuses.batch.nsfw_off')]), name: :nsfw_off, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + = f.button safe_join([fa_icon('trash'), t('admin.statuses.batch.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } + .batch-table__body + = render partial: 'admin/reports/status', collection: @report.statuses, locals: { f: f } -%hr{ class: "section-break"}/ +%hr.spacer/ -%h3= t('admin.reports.notes.label') +- @report_notes.each do |item| + - if item.is_a?(Admin::ActionLog) + = render partial: 'action_log', locals: { action_log: item } + - elsif item.is_a?(ReportNote) + = render item -- if @report_notes.length > 0 - %ul - = render @report_notes - -%h4= t('admin.reports.notes.new_label') -= form_for @report_note, url: admin_report_notes_path, html: { class: 'report-note__form' } do |f| += simple_form_for @report_note, url: admin_report_notes_path do |f| = render 'shared/error_messages', object: @report_note - = f.text_area :content, placeholder: t('admin.reports.notes.placeholder'), rows: 6, class: 'report-note__textarea' - = f.hidden_field :report_id - %div{ class: 'report-note__buttons' } + = f.input :report_id, as: :hidden + + .field-group + = f.input :content, placeholder: t('admin.reports.notes.placeholder'), rows: 6 + + .actions - if @report.unresolved? - = f.submit t('admin.reports.notes.create_and_resolve'), name: :create_and_resolve, class: 'button report-note__button' + = f.button :button, t('admin.reports.notes.create_and_resolve'), name: :create_and_resolve, type: :submit - else - = f.submit t('admin.reports.notes.create_and_unresolve'), name: :create_and_unresolve, class: 'button report-note__button' - = f.submit t('admin.reports.notes.create'), class: 'button report-note__button' - -- if @report_history.length > 0 - %h3= t('admin.reports.history') - - %ul - = render @report_history + = f.button :button, t('admin.reports.notes.create_and_unresolve'), name: :create_and_unresolve, type: :submit + = f.button :button, t('admin.reports.notes.create'), type: :submit diff --git a/app/views/stream_entries/_detailed_status.html.haml b/app/views/stream_entries/_detailed_status.html.haml index e1122d5a2e..afc66d1487 100644 --- a/app/views/stream_entries/_detailed_status.html.haml +++ b/app/views/stream_entries/_detailed_status.html.haml @@ -22,11 +22,11 @@ - if !status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first - %div{ data: { component: 'Video', props: Oj.dump(src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 670, height: 380, detailed: true, inline: true) }} + = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 670, height: 380, detailed: true, inline: true - else - %div{ data: { component: 'MediaGallery', props: Oj.dump(height: 380, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }) }} + = react_component :media_gallery, height: 380, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, standalone: true, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, 'reduceMotion': current_account&.user&.setting_reduce_motion, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } - elsif status.preview_cards.first - %div{ data: { component: 'Card', props: Oj.dump('maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_cards.first, serializer: REST::PreviewCardSerializer).as_json) }} + = react_component :card, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_cards.first, serializer: REST::PreviewCardSerializer).as_json .detailed-status__meta %data.dt-published{ value: status.created_at.to_time.iso8601 } diff --git a/app/views/stream_entries/_simple_status.html.haml b/app/views/stream_entries/_simple_status.html.haml index 984691097b..a6f5120fb1 100644 --- a/app/views/stream_entries/_simple_status.html.haml +++ b/app/views/stream_entries/_simple_status.html.haml @@ -23,6 +23,6 @@ - unless status.media_attachments.empty? - if status.media_attachments.first.video? - video = status.media_attachments.first - %div{ data: { component: 'Video', props: Oj.dump(src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true) }} + = react_component :video, src: video.file.url(:original), preview: video.file.url(:small), sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, width: 610, height: 343, inline: true - else - %div{ data: { component: 'MediaGallery', props: Oj.dump(height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json }) }} + = react_component :media_gallery, height: 343, sensitive: status.sensitive? && !current_account&.user&.setting_display_sensitive_media, 'autoPlayGif': current_account&.user&.setting_auto_play_gif, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } diff --git a/config/locales/ar.yml b/config/locales/ar.yml index 8b9a6688ab..b3cb71d28b 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -240,7 +240,6 @@ ar: action_taken_by: تم اتخاذ الإجراء مِن طرف are_you_sure: هل أنت متأكد ؟ comment: - label: تعليق none: لا شيء delete: حذف id: معرّف ID diff --git a/config/locales/ca.yml b/config/locales/ca.yml index fc30b36611..c9173cd5e7 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -243,7 +243,6 @@ ca: action_taken_by: Mesures adoptades per are_you_sure: N'estàs segur? comment: - label: Comentari none: Cap delete: Suprimeix id: ID diff --git a/config/locales/de.yml b/config/locales/de.yml index 6233d299e2..51936aa36a 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -243,7 +243,6 @@ de: action_taken_by: Maßnahme ergriffen durch are_you_sure: Bist du dir sicher? comment: - label: Kommentar none: Kein delete: Löschen id: ID diff --git a/config/locales/en.yml b/config/locales/en.yml index 20bfd0f8c3..53b64a1003 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -260,40 +260,29 @@ en: destroyed_msg: Report note successfully deleted! reports: account: - created_reports: Reports created by this account - moderation: - silenced: Silenced - suspended: Suspended - title: Moderation - moderation_notes: Moderation Notes note: note report: report - targeted_reports: Reports made about this account action_taken_by: Action taken by are_you_sure: Are you sure? assign_to_self: Assign to me - assigned: Assigned Moderator + assigned: Assigned moderator comment: - label: Report Comment none: None created_at: Reported delete: Delete - history: Moderation History id: ID mark_as_resolved: Mark as resolved mark_as_unresolved: Mark as unresolved notes: - create: Add Note - create_and_resolve: Resolve with Note - create_and_unresolve: Reopen with Note + create: Add note + create_and_resolve: Resolve with note + create_and_unresolve: Reopen with note delete: Delete - label: Moderator Notes - new_label: Add Moderator Note placeholder: Describe what actions have been taken, or any other updates to this report… nsfw: 'false': Unhide media attachments 'true': Hide media attachments - reopen: Reopen Report + reopen: Reopen report report: 'Report #%{id}' report_contents: Contents reported_account: Reported account @@ -302,7 +291,6 @@ en: resolved_msg: Report successfully resolved! silence_account: Silence account status: Status - statuses: Reported Toots suspend_account: Suspend account target: Target title: Reports @@ -366,8 +354,8 @@ en: back_to_account: Back to account page batch: delete: Delete - nsfw_off: NSFW OFF - nsfw_on: NSFW ON + nsfw_off: Mark as not sensitive + nsfw_on: Mark as sensitive execute: Execute failed_to_execute: Failed to execute media: diff --git a/config/locales/eo.yml b/config/locales/eo.yml index 27c62f8993..c768d8a03d 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -243,7 +243,6 @@ eo: action_taken_by: Ago farita de are_you_sure: Ĉu vi certas? comment: - label: Komento none: Nenio delete: Forigi id: ID diff --git a/config/locales/es.yml b/config/locales/es.yml index a5a20aa3c4..3143adda1c 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -243,7 +243,6 @@ es: action_taken_by: Acción tomada por are_you_sure: "¿Estás seguro?" comment: - label: Comentario none: Ninguno delete: Eliminar id: ID diff --git a/config/locales/fa.yml b/config/locales/fa.yml index ed25ea8c96..a3005547a5 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -243,7 +243,6 @@ fa: action_taken_by: انجام‌دهنده are_you_sure: آیا مطمئن هستید؟ comment: - label: توضیح none: خالی delete: پاک‌کردن id: شناسه diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 62f6560bf1..550ad1805e 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -243,7 +243,6 @@ fi: action_taken_by: Toimenpiteen tekijä are_you_sure: Oletko varma? comment: - label: Kommentti none: Ei mitään delete: Poista id: Tunniste diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 4571cc3758..ebe5793548 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -243,7 +243,6 @@ fr: action_taken_by: Intervention de are_you_sure: Êtes vous certain⋅e ? comment: - label: Commentaire none: Aucun delete: Supprimer id: ID diff --git a/config/locales/gl.yml b/config/locales/gl.yml index f4ca7e8c5a..89de27a9a9 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -243,7 +243,6 @@ gl: action_taken_by: Acción tomada por are_you_sure: Está segura? comment: - label: Comentario none: Nada delete: Eliminar id: ID diff --git a/config/locales/he.yml b/config/locales/he.yml index 1a7c84d7cb..d641c6e1a5 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -180,7 +180,6 @@ he: reports: are_you_sure: 100% על בטוח? comment: - label: הערה none: ללא delete: מחיקה id: ID diff --git a/config/locales/hu.yml b/config/locales/hu.yml index 2560b38160..7fe431d377 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -243,7 +243,6 @@ hu: action_taken_by: 'Kezelte:' are_you_sure: Biztos vagy benne? comment: - label: Hozzászólás none: Egyik sem delete: Törlés id: ID diff --git a/config/locales/id.yml b/config/locales/id.yml index 0ef1d50404..5a63b8038a 100644 --- a/config/locales/id.yml +++ b/config/locales/id.yml @@ -106,7 +106,6 @@ id: title: Server yang diketahui reports: comment: - label: Komentar none: Tidak ada delete: Hapus id: ID diff --git a/config/locales/io.yml b/config/locales/io.yml index 29ab4516b5..7c25acc47a 100644 --- a/config/locales/io.yml +++ b/config/locales/io.yml @@ -105,7 +105,6 @@ io: title: Known Instances reports: comment: - label: Comment none: None delete: Delete id: ID diff --git a/config/locales/ja.yml b/config/locales/ja.yml index fecf996d5c..b23c02754a 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -264,11 +264,9 @@ ja: assign_to_self: 担当になる assigned: 担当者 comment: - label: コメント none: なし created_at: レポート日時 delete: 削除 - history: モデレーション履歴 id: ID mark_as_resolved: 解決済みとしてマーク mark_as_unresolved: 未解決として再び開く @@ -277,8 +275,6 @@ ja: create_and_resolve: 書き込み、解決済みにする create_and_unresolve: 書き込み、未解決として開く delete: 削除 - label: モデレーターメモ - new_label: モデレーターメモの追加 placeholder: このレポートに取られた措置やその他更新を記述してください nsfw: 'false': NSFW オフ @@ -292,7 +288,6 @@ ja: resolved_msg: レポートを解決済みにしました! silence_account: アカウントをサイレンス status: ステータス - statuses: 通報されたトゥート suspend_account: アカウントを停止 target: ターゲット title: レポート diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 72c98bc305..3470085cf2 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -245,7 +245,6 @@ ko: action_taken_by: 신고 처리자 are_you_sure: 정말로 실행하시겠습니까? comment: - label: 코멘트 none: 없음 delete: 삭제 id: ID diff --git a/config/locales/nl.yml b/config/locales/nl.yml index a46bb72d75..2342ef6a29 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -243,7 +243,6 @@ nl: action_taken_by: Actie uitgevoerd door are_you_sure: Weet je het zeker? comment: - label: Opmerking none: Geen delete: Verwijderen id: ID diff --git a/config/locales/no.yml b/config/locales/no.yml index d5edb3975d..8b84182af6 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -243,7 +243,6 @@ action_taken_by: Handling utført av are_you_sure: Er du sikker? comment: - label: Kommentar none: Ingen delete: Slett id: ID diff --git a/config/locales/oc.yml b/config/locales/oc.yml index f8e819c532..195a1d9096 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -243,7 +243,6 @@ oc: action_taken_by: Mesura menada per are_you_sure: Es segur ? comment: - label: Comentari none: Pas cap delete: Suprimir id: ID diff --git a/config/locales/pl.yml b/config/locales/pl.yml index faa5494721..839767c8f4 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -261,25 +261,16 @@ pl: destroyed_msg: Pomyślnie usunięto notatkę moderacyjną. reports: account: - created_reports: Zgłoszenia utworzone z tego konta - moderation: - silenced: Wyciszone - suspended: Zawieszone - title: Moderacja - moderation_notes: Notatki moderacyjne note: notatka report: zgłoszenie - targeted_reports: Zgłoszenia dotycząće tego konta action_taken_by: Działanie podjęte przez are_you_sure: Czy na pewno? assign_to_self: Przypisz do siebie assigned: Przypisany moderator comment: - label: Komentarz do zgłoszenia none: Brak created_at: Zgłoszono delete: Usuń - history: Historia moderacji id: ID mark_as_resolved: Oznacz jako rozwiązane mark_as_unresolved: Oznacz jako nierozwiązane @@ -288,8 +279,6 @@ pl: create_and_resolve: Rozwiąż i pozostaw notatkę create_and_unresolve: Cofnij rozwiązanie i pozostaw notatkę delete: Usuń - label: Notatki - new_label: Dodaj notatkę moderacyjną placeholder: Opisz wykonane akcje i inne szczegóły dotyczące tego zgłoszenia… nsfw: 'false': Nie oznaczaj jako NSFW @@ -303,7 +292,6 @@ pl: resolved_msg: Pomyślnie rozwiązano zgłoszenie. silence_account: Wycisz konto status: Stan - statuses: Zgłoszone wpisy suspend_account: Zawieś konto target: Cel title: Zgłoszenia diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index d6f463a198..d3c1d532b1 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -243,7 +243,6 @@ pt-BR: action_taken_by: Ação realizada por are_you_sure: Você tem certeza? comment: - label: Comentário none: Nenhum delete: Excluir id: ID diff --git a/config/locales/pt.yml b/config/locales/pt.yml index 27d4e88e31..fb2a6cad1c 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -243,7 +243,6 @@ pt: action_taken_by: Ação tomada por are_you_sure: Tens a certeza? comment: - label: Comentário none: Nenhum delete: Eliminar id: ID diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 176ace92dd..bf42257581 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -245,7 +245,6 @@ ru: action_taken_by: 'Действие предпринято:' are_you_sure: Вы уверены? comment: - label: Комментарий none: Нет delete: Удалить id: ID diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 25e672604d..37f711323a 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -243,7 +243,6 @@ sk: action_taken_by: Zákrok vykonal are_you_sure: Ste si istý/á? comment: - label: Vyjadriť sa none: Žiadne delete: Vymazať id: Identifikácia diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index 8d39d35b0b..742c976d19 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -245,7 +245,6 @@ sr-Latn: action_taken_by: Akciju izveo are_you_sure: Da li ste sigurni? comment: - label: Komentar none: Ništa delete: Obriši id: ID diff --git a/config/locales/sr.yml b/config/locales/sr.yml index af4c6a846f..0d55910a6c 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -245,7 +245,6 @@ sr: action_taken_by: Акцију извео are_you_sure: Да ли сте сигурни? comment: - label: Коментар none: Ништа delete: Обриши id: ID diff --git a/config/locales/sv.yml b/config/locales/sv.yml index f85ed6efba..7e763c2aad 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -243,7 +243,6 @@ sv: action_taken_by: Åtgärder vidtagna av are_you_sure: Är du säker? comment: - label: Kommentar none: Ingen delete: Radera id: ID diff --git a/config/locales/th.yml b/config/locales/th.yml index 45fe1e4752..350b93b521 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -108,7 +108,6 @@ th: title: Known Instances reports: comment: - label: คอมเม้นต์ none: None delete: ลบ id: ไอดี diff --git a/config/locales/tr.yml b/config/locales/tr.yml index ee0e330748..6e7aeb77e9 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -107,7 +107,6 @@ tr: title: Bilinen Sunucular reports: comment: - label: Yorum none: Yok delete: Sil id: ID diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 4c1c66b318..44f64b5c9e 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -99,7 +99,6 @@ uk: undo: Відмінити reports: comment: - label: Коментар none: Немає delete: Видалити id: ID diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index be868e6e73..78c72bd302 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -241,7 +241,6 @@ zh-CN: action_taken_by: 操作执行者 are_you_sure: 你确定吗? comment: - label: 备注 none: 没有 delete: 删除 id: ID diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index 964ff58112..a27b0c04c8 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -259,25 +259,16 @@ zh-HK: destroyed_msg: 舉報筆記已刪除。 reports: account: - created_reports: 由此帳號發出的舉報 - moderation: - silenced: 被靜音的 - suspended: 被停權的 - title: 管理操作 - moderation_notes: 管理筆記 note: 筆記 report: 舉報 - targeted_reports: 關於此帳號的舉報 action_taken_by: 操作執行者 are_you_sure: 你確認嗎? assign_to_self: 指派給自己 assigned: 指派負責人 comment: - label: 詳細解釋 none: 沒有 created_at: 日期 delete: 刪除 - history: 執行紀錄 id: ID mark_as_resolved: 標示為「已處理」 mark_as_unresolved: 標示為「未處理」 @@ -286,8 +277,6 @@ zh-HK: create_and_resolve: 建立筆記並標示為「已處理」 create_and_unresolve: 建立筆記並標示為「未處理」 delete: 刪除 - label: 管理筆記 - new_label: 建立管理筆記 placeholder: 記錄已執行的動作,或其他更新 nsfw: 'false': 取消 NSFW 標記 @@ -301,7 +290,6 @@ zh-HK: resolved_msg: 舉報已處理。 silence_account: 將用戶靜音 status: 狀態 - statuses: 被舉報的文章 suspend_account: 將用戶停權 target: 對象 title: 舉報 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 2fec09ed8e..f69d22d79a 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -79,7 +79,6 @@ zh-TW: title: 網域封鎖 reports: comment: - label: 留言 none: 無 delete: 刪除 id: ID diff --git a/spec/controllers/admin/reported_statuses_controller_spec.rb b/spec/controllers/admin/reported_statuses_controller_spec.rb index 297807d411..41e032fc77 100644 --- a/spec/controllers/admin/reported_statuses_controller_spec.rb +++ b/spec/controllers/admin/reported_statuses_controller_spec.rb @@ -13,7 +13,7 @@ describe Admin::ReportedStatusesController do describe 'POST #create' do subject do - -> { post :create, params: { report_id: report, form_status_batch: { action: action, status_ids: status_ids } } } + -> { post :create, params: { :report_id => report, action => '', :form_status_batch => { status_ids: status_ids } } } end let(:action) { 'nsfw_on' } From 084cf0babffec9e7bee537fd4f6b2294de6c33dc Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Fri, 20 Apr 2018 19:21:28 +0900 Subject: [PATCH 03/82] Add extract_foreign_key_action to Mastodon::MigrationHelpers (#7195) --- lib/mastodon/migration_helpers.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/lib/mastodon/migration_helpers.rb b/lib/mastodon/migration_helpers.rb index 6f6f99f63c..e154b5a2cd 100644 --- a/lib/mastodon/migration_helpers.rb +++ b/lib/mastodon/migration_helpers.rb @@ -985,6 +985,17 @@ into similar problems in the future (e.g. when new tables are created). BackgroundMigrationWorker.perform_in(delay_interval * index, job_class_name, [start_id, end_id]) end end + + private + + # https://github.com/rails/rails/blob/v5.2.0/activerecord/lib/active_record/connection_adapters/postgresql/schema_statements.rb#L678-L684 + def extract_foreign_key_action(specifier) + case specifier + when 'c'; :cascade + when 'n'; :nullify + when 'r'; :restrict + end + end end end From 6f63cbb53c6ba7a5e2224c4bf846ccff1cac6a3d Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Fri, 20 Apr 2018 20:46:08 +0900 Subject: [PATCH 04/82] Replace Travis to CircleCI (#7196) --- .circleci/config.yml | 169 +++++++++++++++++++++++++++++++++++++++++++ .travis.yml | 60 --------------- 2 files changed, 169 insertions(+), 60 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 .travis.yml diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 0000000000..e3a9628acf --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,169 @@ +version: 2 + +aliases: + - &defaults + docker: + - image: circleci/ruby:2.5.1-stretch-node + environment: &ruby_environment + BUNDLE_APP_CONFIG: ./.bundle/ + RAILS_ENV: test + NODE_ENV: test + DB_HOST: localhost + DB_USER: root + LOCAL_DOMAIN: cb6e6126.ngrok.io + LOCAL_HTTPS: true + PARALLEL_TEST_PROCESSORS: 2 + ALLOW_NOPAM: true + working_directory: ~/projects/mastodon/ + + - &attach_workspace + attach_workspace: + at: ~/projects/ + + - &persist_to_workspace + persist_to_workspace: + root: ~/projects/ + paths: + - ./mastodon/ + + - &install_steps + steps: + - checkout + - *attach_workspace + + - restore_cache: + keys: + - v1-node-dependencies-{{ checksum "yarn.lock" }} + - v1-node-dependencies- + - run: yarn install --frozen-lockfile + - save_cache: + key: v1-node-dependencies-{{ checksum "yarn.lock" }} + paths: + - ./node_modules/ + + - *persist_to_workspace + + - &install_system_dependencies + run: + name: Install system dependencies + command: | + sudo apt-get update + sudo apt-get install -y libicu-dev libidn11-dev libprotobuf-dev protobuf-compiler + + - &install_ruby_dependencies + steps: + - *attach_workspace + + - *install_system_dependencies + + - run: ruby -e 'puts RUBY_VERSION' | tee /tmp/.ruby-version + - restore_cache: + keys: + - v1-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} + - v1-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}- + - v1-ruby-dependencies-- + - run: bundle install --clean --jobs 16 --path ./vendor/bundle/ --retry 3 --with pam_authentication --without development production + - save_cache: + key: v1-ruby-dependencies-{{ checksum "/tmp/.ruby-version" }}-{{ checksum "Gemfile.lock" }} + paths: + - ./vendor/bundle/ + + - run: + name: Precompile Assets + command: | + if [ ! -d ./public/assets/ -o ! -d ./public/packs-test/ ]; then + ./bin/rails assets:precompile + fi + + - *persist_to_workspace + + - &test_steps + steps: + - *attach_workspace + + - *install_system_dependencies + - run: sudo apt-get install -y ffmpeg + + - run: + name: Prepare Tests + command: ./bin/rails parallel:create parallel:load_schema parallel:prepare + - run: + name: Run Tests + command: bundle exec parallel_test ./spec/ --group-by filesize --type rspec + +jobs: + install: + <<: *defaults + <<: *install_steps + + install-ruby2.5: + <<: *defaults + <<: *install_ruby_dependencies + + install-ruby2.4: + <<: *defaults + docker: + - image: circleci/ruby:2.4.4-stretch-node + environment: *ruby_environment + <<: *install_ruby_dependencies + + test-ruby2.5: + <<: *defaults + docker: + - image: circleci/ruby:2.5.1-stretch-node + environment: *ruby_environment + - image: circleci/postgres:10.3-alpine + environment: + POSTGRES_USER: root + - image: circleci/redis:4.0.9-alpine + <<: *test_steps + + test-ruby2.4: + <<: *defaults + docker: + - image: circleci/ruby:2.4.4-stretch-node + environment: *ruby_environment + - image: circleci/postgres:10.3-alpine + environment: + POSTGRES_USER: root + - image: circleci/redis:4.0.9-alpine + <<: *test_steps + + test-webui: + <<: *defaults + docker: + - image: circleci/node:8.11.1-stretch + steps: + - *attach_workspace + - run: yarn test:jest + + check-i18n: + <<: *defaults + steps: + - *attach_workspace + - run: bundle exec i18n-tasks check-normalized + - run: bundle exec i18n-tasks unused + +workflows: + version: 2 + build-and-test: + jobs: + - install + - install-ruby2.5: + requires: + - install + - install-ruby2.4: + requires: + - install-ruby2.5 + - test-ruby2.5: + requires: + - install-ruby2.5 + - test-ruby2.4: + requires: + - install-ruby2.4 + - test-webui: + requires: + - install + - check-i18n: + requires: + - install-ruby2.5 diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2addd9ba2f..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,60 +0,0 @@ -language: ruby -cache: - bundler: true - yarn: true - directories: - - node_modules - - public/assets - - public/packs-test - - tmp/cache/babel-loader -dist: trusty -sudo: false -branches: - only: - - master - -notifications: - email: false - -env: - global: - - LOCAL_DOMAIN=cb6e6126.ngrok.io - - LOCAL_HTTPS=true - - RAILS_ENV=test - - NOKOGIRI_USE_SYSTEM_LIBRARIES=true - - PARALLEL_TEST_PROCESSORS=2 - - ALLOW_NOPAM=true - -addons: - postgresql: 9.4 - apt: - sources: - - trusty-media - - sourceline: deb https://dl.yarnpkg.com/debian/ stable main - key_url: https://dl.yarnpkg.com/debian/pubkey.gpg - packages: - - ffmpeg - - libicu-dev - - libprotobuf-dev - - protobuf-compiler - - yarn - -rvm: - - 2.4.3 - - 2.5.0 - -services: - - redis-server - -install: - - nvm install - - bundle install --path=vendor/bundle --with pam_authentication --without development production --retry=3 --jobs=16 - - yarn install - -before_script: - - ./bin/rails parallel:create parallel:load_schema parallel:prepare assets:precompile - -script: - - travis_retry bundle exec parallel_test spec/ --group-by filesize --type rspec - - yarn run test:jest - - bundle exec i18n-tasks check-normalized && bundle exec i18n-tasks unused From 4e35ce82691dff9cd81dae2f0e2f566f6f3ef85c Mon Sep 17 00:00:00 2001 From: unarist Date: Fri, 20 Apr 2018 21:04:16 +0900 Subject: [PATCH 05/82] Fix Esc hotkey behavior (#7199) This fixes following cases which causes hotkey action accidentally: * hitting Esc key to cancel text composition (mostly in CJK) Although events on cancelling composition are still heavily browser / input method dependent, but this implementation would covers current UI Events spec and some exceptions. * hitting Esc key to close autocomplete suggestions This PR changes to use keydown event instead of keyup event as well as other hotkeys. --- .../components/autosuggest_textarea.js | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/app/javascript/mastodon/components/autosuggest_textarea.js b/app/javascript/mastodon/components/autosuggest_textarea.js index 34904194f7..5474771c99 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.js +++ b/app/javascript/mastodon/components/autosuggest_textarea.js @@ -86,7 +86,9 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { switch(e.key) { case 'Escape': - if (!suggestionsHidden) { + if (suggestions.size === 0 || suggestionsHidden) { + document.querySelector('.ui').parentElement.focus(); + } else { e.preventDefault(); this.setState({ suggestionsHidden: true }); } @@ -125,16 +127,6 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { this.props.onKeyDown(e); } - onKeyUp = e => { - if (e.key === 'Escape' && this.state.suggestionsHidden) { - document.querySelector('.ui').parentElement.focus(); - } - - if (this.props.onKeyUp) { - this.props.onKeyUp(e); - } - } - onBlur = () => { this.setState({ suggestionsHidden: true }); } @@ -186,7 +178,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { } render () { - const { value, suggestions, disabled, placeholder, autoFocus } = this.props; + const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus } = this.props; const { suggestionsHidden } = this.state; const style = { direction: 'ltr' }; @@ -208,7 +200,7 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { value={value} onChange={this.onChange} onKeyDown={this.onKeyDown} - onKeyUp={this.onKeyUp} + onKeyUp={onKeyUp} onBlur={this.onBlur} onPaste={this.onPaste} style={style} From ee2e0f694a2ebd403834e55192897d5995a20cf4 Mon Sep 17 00:00:00 2001 From: mayaeh Date: Fri, 20 Apr 2018 21:58:33 +0900 Subject: [PATCH 06/82] Fix #6157: boosting own private toots (#7200) * Fix boosting own private toots. * Run yarn manage:translations and update Japanese translations. --- app/javascript/mastodon/components/dropdown_menu.js | 2 +- app/javascript/mastodon/components/status_action_bar.js | 4 +++- .../mastodon/features/status/components/action_bar.js | 4 +++- app/javascript/mastodon/locales/en.json | 1 + app/javascript/mastodon/locales/ja.json | 9 +++++---- 5 files changed, 13 insertions(+), 7 deletions(-) diff --git a/app/javascript/mastodon/components/dropdown_menu.js b/app/javascript/mastodon/components/dropdown_menu.js index c5c6f73b33..982d34718e 100644 --- a/app/javascript/mastodon/components/dropdown_menu.js +++ b/app/javascript/mastodon/components/dropdown_menu.js @@ -63,7 +63,7 @@ class DropdownMenu extends React.PureComponent { if (typeof action === 'function') { e.preventDefault(); - action(); + action(e); } else if (to) { e.preventDefault(); this.context.router.history.push(to); diff --git a/app/javascript/mastodon/components/status_action_bar.js b/app/javascript/mastodon/components/status_action_bar.js index e58625582e..d605dbc8a9 100644 --- a/app/javascript/mastodon/components/status_action_bar.js +++ b/app/javascript/mastodon/components/status_action_bar.js @@ -153,7 +153,9 @@ export default class StatusActionBar extends ImmutablePureComponent { if (publicStatus) { menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick }); } else { - menu.push({ text: intl.formatMessage(status.get('reblog') ? messages.reblog_private : messages.cancel_reblog_private), action: this.handleReblogClick }); + if (status.get('visibility') === 'private') { + menu.push({ text: intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleReblogClick }); + } } menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js index fc34c8cdc8..bb9b75505b 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.js +++ b/app/javascript/mastodon/features/status/components/action_bar.js @@ -123,7 +123,9 @@ export default class ActionBar extends React.PureComponent { if (publicStatus) { menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick }); } else { - menu.push({ text: intl.formatMessage(status.get('reblog') ? messages.reblog_private : messages.cancel_reblog_private), action: this.handleReblogClick }); + if (status.get('visibility') === 'private') { + menu.push({ text: intl.formatMessage(status.get('reblogged') ? messages.cancel_reblog_private : messages.reblog_private), action: this.handleReblogClick }); + } } menu.push(null); diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index d8bd7e3e02..98d6c0dd71 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "to mention author", "keyboard_shortcuts.reply": "to reply", "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toggle_hidden": "to show/hide text behind CW", "keyboard_shortcuts.toot": "to start a brand new toot", "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", "keyboard_shortcuts.up": "to move up in the list", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index a4aa06a06a..23223cac36 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -101,7 +101,7 @@ "emoji_button.symbols": "記号", "emoji_button.travel": "旅行と場所", "empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!", - "empty_column.direct": "You don't have any direct messages yet. When you send or receive one, it will show up here.", + "empty_column.direct": "ダイレクトメッセージはまだありません。ダイレクトメッセージをやりとりすると、ここに表示されます。", "empty_column.hashtag": "このハッシュタグはまだ使われていません。", "empty_column.home": "まだ誰もフォローしていません。{public}を見に行くか、検索を使って他のユーザーを見つけましょう。", "empty_column.home.public_timeline": "連合タイムライン", @@ -135,6 +135,7 @@ "keyboard_shortcuts.mention": "メンション", "keyboard_shortcuts.reply": "返信", "keyboard_shortcuts.search": "検索欄に移動", + "keyboard_shortcuts.toggle_hidden": "CWで隠れた文を見る/隠す", "keyboard_shortcuts.toot": "新規トゥート", "keyboard_shortcuts.unfocus": "トゥート入力欄・検索欄から離れる", "keyboard_shortcuts.up": "カラム内一つ上に移動", @@ -156,7 +157,7 @@ "mute_modal.hide_notifications": "このユーザーからの通知を隠しますか?", "navigation_bar.blocks": "ブロックしたユーザー", "navigation_bar.community_timeline": "ローカルタイムライン", - "navigation_bar.direct": "Direct messages", + "navigation_bar.direct": "ダイレクトメッセージ", "navigation_bar.domain_blocks": "非表示にしたドメイン", "navigation_bar.edit_profile": "プロフィールを編集", "navigation_bar.favourites": "お気に入り", @@ -241,7 +242,7 @@ "search_results.total": "{count, number}件の結果", "standalone.public_title": "今こんな話をしています...", "status.block": "@{name}さんをブロック", - "status.cancel_reblog_private": "Unboost", + "status.cancel_reblog_private": "ブースト解除", "status.cannot_reblog": "この投稿はブーストできません", "status.delete": "削除", "status.direct": "@{name}さんにダイレクトメッセージ", @@ -257,7 +258,7 @@ "status.pin": "プロフィールに固定表示", "status.pinned": "固定されたトゥート", "status.reblog": "ブースト", - "status.reblog_private": "Boost to original audience", + "status.reblog_private": "ブースト", "status.reblogged_by": "{name}さんがブースト", "status.reply": "返信", "status.replyAll": "全員に返信", From 23106844a10606dd0e04c8382281d5ff80d4bdd9 Mon Sep 17 00:00:00 2001 From: TakesxiSximada Date: Sat, 21 Apr 2018 01:14:21 +0900 Subject: [PATCH 07/82] Fix the hot key (j, k) does not function correctly when there is a pinned toot in account timeline. (#7202) * Fix the hot key (j, k) does not function correctly when there is a pinned toot in account timeline. * Fix typo * Add custom attribute prefix --- app/javascript/mastodon/components/status.js | 10 +++++----- .../mastodon/components/status_list.js | 20 +++++++++++++++---- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/app/javascript/mastodon/components/status.js b/app/javascript/mastodon/components/status.js index e5f7c9399b..402d558c4c 100644 --- a/app/javascript/mastodon/components/status.js +++ b/app/javascript/mastodon/components/status.js @@ -114,12 +114,12 @@ export default class Status extends ImmutablePureComponent { this.context.router.history.push(`/accounts/${this._properStatus().getIn(['account', 'id'])}`); } - handleHotkeyMoveUp = () => { - this.props.onMoveUp(this.props.status.get('id')); + handleHotkeyMoveUp = e => { + this.props.onMoveUp(this.props.status.get('id'), e.target.getAttribute('data-featured')); } - handleHotkeyMoveDown = () => { - this.props.onMoveDown(this.props.status.get('id')); + handleHotkeyMoveDown = e => { + this.props.onMoveDown(this.props.status.get('id'), e.target.getAttribute('data-featured')); } handleHotkeyToggleHidden = () => { @@ -233,7 +233,7 @@ export default class Status extends ImmutablePureComponent { return ( -
+
{prepend}
diff --git a/app/javascript/mastodon/components/status_list.js b/app/javascript/mastodon/components/status_list.js index c98d4564e5..0c971ceb00 100644 --- a/app/javascript/mastodon/components/status_list.js +++ b/app/javascript/mastodon/components/status_list.js @@ -30,13 +30,25 @@ export default class StatusList extends ImmutablePureComponent { trackScroll: true, }; - handleMoveUp = id => { - const elementIndex = this.props.statusIds.indexOf(id) - 1; + getFeaturedStatusCount = () => { + return this.props.featuredStatusIds ? this.props.featuredStatusIds.size : 0; + } + + getCurrentStatusIndex = (id, featured) => { + if (featured) { + return this.props.featuredStatusIds.indexOf(id); + } else { + return this.props.statusIds.indexOf(id) + this.getFeaturedStatusCount(); + } + } + + handleMoveUp = (id, featured) => { + const elementIndex = this.getCurrentStatusIndex(id, featured) - 1; this._selectChild(elementIndex); } - handleMoveDown = id => { - const elementIndex = this.props.statusIds.indexOf(id) + 1; + handleMoveDown = (id, featured) => { + const elementIndex = this.getCurrentStatusIndex(id, featured) + 1; this._selectChild(elementIndex); } From 87e3f0a41d2a2d223c663365a199c01989afc8ed Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sat, 21 Apr 2018 01:14:31 +0900 Subject: [PATCH 08/82] Fix spec for sr-Latn (#7203) --- app/controllers/concerns/localized.rb | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/controllers/concerns/localized.rb b/app/controllers/concerns/localized.rb index abd85ea27a..145549bcd2 100644 --- a/app/controllers/concerns/localized.rb +++ b/app/controllers/concerns/localized.rb @@ -29,10 +29,14 @@ module Localized end def preferred_locale - http_accept_language.preferred_language_from(I18n.available_locales) + http_accept_language.preferred_language_from(available_locales) end def compatible_locale - http_accept_language.compatible_language_from(I18n.available_locales) + http_accept_language.compatible_language_from(available_locales) + end + + def available_locales + I18n.available_locales.reverse end end From 84214b864c63aee08357a719ab386b8e4ed5b901 Mon Sep 17 00:00:00 2001 From: unarist Date: Sat, 21 Apr 2018 01:36:52 +0900 Subject: [PATCH 09/82] Ignore keyevents during text composition (#7205) KeyboardEvent.key may be physical key name (Escape, Tab, etc.) even in text composition and it causes hotkeys or suggestion selection. So we need to check e.which or e.isComposing. Checking e.which also allows us to avoid Esc key on compositionend in Safari. --- app/javascript/mastodon/components/autosuggest_textarea.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/javascript/mastodon/components/autosuggest_textarea.js b/app/javascript/mastodon/components/autosuggest_textarea.js index 5474771c99..a4f5cf50c6 100644 --- a/app/javascript/mastodon/components/autosuggest_textarea.js +++ b/app/javascript/mastodon/components/autosuggest_textarea.js @@ -84,6 +84,12 @@ export default class AutosuggestTextarea extends ImmutablePureComponent { return; } + if (e.which === 229 || e.isComposing) { + // Ignore key events during text composition + // e.key may be a name of the physical key even in this case (e.x. Safari / Chrome on Mac) + return; + } + switch(e.key) { case 'Escape': if (suggestions.size === 0 || suggestionsHidden) { From b4382247515728521275002643e4d1b7360bf7fb Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sat, 21 Apr 2018 02:31:30 +0900 Subject: [PATCH 10/82] Introduce rspec-retry (#7206) --- Gemfile | 1 + Gemfile.lock | 3 +++ spec/spec_helper.rb | 8 ++++++++ 3 files changed, 12 insertions(+) diff --git a/Gemfile b/Gemfile index efafe295ce..8055f1561f 100644 --- a/Gemfile +++ b/Gemfile @@ -111,6 +111,7 @@ group :test do gem 'microformats', '~> 4.0' gem 'rails-controller-testing', '~> 1.0' gem 'rspec-sidekiq', '~> 3.0' + gem 'rspec-retry', '~> 0.5', require: false gem 'simplecov', '~> 0.14', require: false gem 'webmock', '~> 3.3' gem 'parallel_tests', '~> 2.21' diff --git a/Gemfile.lock b/Gemfile.lock index e799533ae0..eeb4bf17e7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -495,6 +495,8 @@ GEM rspec-expectations (~> 3.7.0) rspec-mocks (~> 3.7.0) rspec-support (~> 3.7.0) + rspec-retry (0.5.7) + rspec-core (> 3.3) rspec-sidekiq (3.0.3) rspec-core (~> 3.0, >= 3.0.0) sidekiq (>= 2.4.0) @@ -715,6 +717,7 @@ DEPENDENCIES redis-rails (~> 5.0) rqrcode (~> 0.10) rspec-rails (~> 3.7) + rspec-retry (~> 0.5) rspec-sidekiq (~> 3.0) rubocop ruby-oembed (~> 0.12) diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index a0466dd4bf..66ac75ee0e 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,3 +1,4 @@ +require 'rspec/retry' require 'simplecov' GC.disable @@ -11,6 +12,9 @@ end gc_counter = -1 RSpec.configure do |config| + config.verbose_retry = true + config.display_try_failure_messages = true + config.expect_with :rspec do |expectations| expectations.include_chain_clauses_in_custom_matcher_descriptions = true end @@ -25,6 +29,10 @@ RSpec.configure do |config| end end + config.around :each do |ex| + ex.run_with_retry retry: 3 + end + config.before :suite do Chewy.strategy(:bypass) end From 1a27f9f46fb751fa4b3bff9aff3d712bb04b1481 Mon Sep 17 00:00:00 2001 From: goofy-bz Date: Sat, 21 Apr 2018 20:07:25 +0200 Subject: [PATCH 11/82] one grammar fix (#7212) --- app/javascript/mastodon/locales/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index 98c1c43d28..9a9e8ab79f 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -105,7 +105,7 @@ "empty_column.hashtag": "Il n’y a encore aucun contenu associé à ce hashtag.", "empty_column.home": "Vous ne suivez encore personne. Visitez {public} ou bien utilisez la recherche pour vous connecter à d’autres personnes.", "empty_column.home.public_timeline": "le fil public", - "empty_column.list": "Il n'y a rien dans cette liste pour l'instant. Dès que des personnes de cette liste publierons de nouveaux statuts, ils apparaîtront ici.", + "empty_column.list": "Il n'y a rien dans cette liste pour l'instant. Dès que des personnes de cette liste publieront de nouveaux statuts, ils apparaîtront ici.", "empty_column.notifications": "Vous n’avez pas encore de notification. Interagissez avec d’autres personnes pour débuter la conversation.", "empty_column.public": "Il n’y a rien ici ! Écrivez quelque chose publiquement, ou bien suivez manuellement des personnes d’autres instances pour remplir le fil public", "follow_request.authorize": "Accepter", From bfe26ef67b6ee2ce4a4ca05565cd383074de3a8b Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Sat, 21 Apr 2018 21:34:36 +0200 Subject: [PATCH 12/82] Force convert to JPG for preview card thumbnails to avoid animations (#7109) * Force convert to JPG for preview card thumbnails to avoid animations Fix #7093 * Conditionally convert to JPG only if original is GIF Coalesce and strip on all formats to ensure no animated APNGs --- app/models/preview_card.rb | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb index 0c82f06ce0..0ffa6b10fd 100644 --- a/app/models/preview_card.rb +++ b/app/models/preview_card.rb @@ -34,7 +34,7 @@ class PreviewCard < ApplicationRecord has_and_belongs_to_many :statuses - has_attached_file :image, styles: { original: { geometry: '400x400>', file_geometry_parser: FastGeometryParser } }, convert_options: { all: '-quality 80 -strip' } + has_attached_file :image, styles: ->(f) { image_styles(f) }, convert_options: { all: '-quality 80 -strip' } include Attachmentable @@ -52,6 +52,23 @@ class PreviewCard < ApplicationRecord save! end + class << self + private + + def image_styles(f) + styles = { + original: { + geometry: '400x400>', + file_geometry_parser: FastGeometryParser, + convert_options: '-coalesce -strip', + }, + } + + styles[:original][:format] = 'jpg' if f.instance.image_content_type == 'image/gif' + styles + end + end + private def extract_dimensions From d10447c3a82d771f8ab61837128b011254894694 Mon Sep 17 00:00:00 2001 From: Yamagishi Kazutoshi Date: Sun, 22 Apr 2018 04:35:07 +0900 Subject: [PATCH 13/82] Use raw status code on have_http_status (#7214) --- spec/controllers/about_controller_spec.rb | 6 +++--- spec/controllers/accounts_controller_spec.rb | 2 +- .../activitypub/outboxes_controller_spec.rb | 2 +- .../admin/accounts_controller_spec.rb | 4 ++-- .../admin/change_email_controller_spec.rb | 2 +- .../admin/confirmations_controller_spec.rb | 4 ++-- .../admin/domain_blocks_controller_spec.rb | 6 +++--- .../email_domain_blocks_controller_spec.rb | 4 ++-- .../admin/instances_controller_spec.rb | 2 +- .../reported_statuses_controller_spec.rb | 2 +- .../admin/reports_controller_spec.rb | 8 ++++---- .../admin/settings_controller_spec.rb | 2 +- .../admin/statuses_controller_spec.rb | 6 +++--- .../admin/subscriptions_controller_spec.rb | 2 +- spec/controllers/api/base_controller_spec.rb | 2 +- .../controllers/api/oembed_controller_spec.rb | 2 +- spec/controllers/api/push_controller_spec.rb | 4 ++-- .../controllers/api/salmon_controller_spec.rb | 2 +- .../api/subscriptions_controller_spec.rb | 6 +++--- .../accounts/credentials_controller_spec.rb | 4 ++-- .../follower_accounts_controller_spec.rb | 2 +- .../following_accounts_controller_spec.rb | 2 +- .../api/v1/accounts/lists_controller_spec.rb | 2 +- .../accounts/relationships_controller_spec.rb | 4 ++-- .../api/v1/accounts/search_controller_spec.rb | 2 +- .../v1/accounts/statuses_controller_spec.rb | 8 ++++---- .../api/v1/accounts_controller_spec.rb | 18 ++++++++--------- .../v1/apps/credentials_controller_spec.rb | 2 +- .../api/v1/apps_controller_spec.rb | 2 +- .../api/v1/blocks_controller_spec.rb | 2 +- .../api/v1/custom_emojis_controller_spec.rb | 2 +- .../api/v1/domain_blocks_controller_spec.rb | 6 +++--- .../api/v1/follow_requests_controller_spec.rb | 6 +++--- .../api/v1/follows_controller_spec.rb | 4 ++-- .../api/v1/instances_controller_spec.rb | 2 +- .../api/v1/lists/accounts_controller_spec.rb | 6 +++--- .../api/v1/lists_controller_spec.rb | 10 +++++----- .../api/v1/media_controller_spec.rb | 8 ++++---- .../api/v1/mutes_controller_spec.rb | 2 +- .../api/v1/notifications_controller_spec.rb | 10 +++++----- .../api/v1/reports_controller_spec.rb | 4 ++-- .../api/v1/search_controller_spec.rb | 2 +- .../favourited_by_accounts_controller_spec.rb | 6 +++--- .../v1/statuses/favourites_controller_spec.rb | 4 ++-- .../api/v1/statuses/mutes_controller_spec.rb | 4 ++-- .../api/v1/statuses/pins_controller_spec.rb | 4 ++-- .../reblogged_by_accounts_controller_spec.rb | 6 +++--- .../v1/statuses/reblogs_controller_spec.rb | 4 ++-- .../api/v1/statuses_controller_spec.rb | 20 +++++++++---------- .../api/v1/timelines/home_controller_spec.rb | 2 +- .../api/v1/timelines/list_controller_spec.rb | 2 +- .../v1/timelines/public_controller_spec.rb | 6 +++--- .../api/v1/timelines/tag_controller_spec.rb | 4 ++-- .../api/web/settings_controller_spec.rb | 2 +- .../application_controller_spec.rb | 6 +++--- .../auth/confirmations_controller_spec.rb | 2 +- .../auth/passwords_controller_spec.rb | 4 ++-- .../auth/registrations_controller_spec.rb | 6 +++--- .../auth/sessions_controller_spec.rb | 2 +- .../authorize_follows_controller_spec.rb | 4 ++-- .../account_controller_concern_spec.rb | 2 +- .../export_controller_concern_spec.rb | 2 +- .../follower_accounts_controller_spec.rb | 2 +- .../following_accounts_controller_spec.rb | 2 +- spec/controllers/manifests_controller_spec.rb | 2 +- spec/controllers/media_controller_spec.rb | 6 +++--- .../oauth/authorizations_controller_spec.rb | 2 +- ...authorized_applications_controller_spec.rb | 2 +- .../remote_follow_controller_spec.rb | 4 ++-- .../settings/applications_controller_spec.rb | 10 +++++----- .../settings/deletes_controller_spec.rb | 2 +- .../settings/exports_controller_spec.rb | 2 +- .../follower_domains_controller_spec.rb | 2 +- .../settings/imports_controller_spec.rb | 2 +- .../settings/notifications_controller_spec.rb | 2 +- .../settings/preferences_controller_spec.rb | 2 +- .../settings/profiles_controller_spec.rb | 2 +- .../confirmations_controller_spec.rb | 4 ++-- .../recovery_codes_controller_spec.rb | 2 +- ..._factor_authentications_controller_spec.rb | 4 ++-- spec/controllers/statuses_controller_spec.rb | 2 +- .../stream_entries_controller_spec.rb | 2 +- spec/controllers/tags_controller_spec.rb | 4 ++-- .../well_known/host_meta_controller_spec.rb | 2 +- .../well_known/webfinger_controller_spec.rb | 6 +++--- spec/requests/host_meta_request_spec.rb | 2 +- spec/requests/webfinger_request_spec.rb | 10 +++++----- 87 files changed, 176 insertions(+), 176 deletions(-) diff --git a/spec/controllers/about_controller_spec.rb b/spec/controllers/about_controller_spec.rb index c2c34d34ad..2089b3b162 100644 --- a/spec/controllers/about_controller_spec.rb +++ b/spec/controllers/about_controller_spec.rb @@ -17,7 +17,7 @@ RSpec.describe AboutController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -35,7 +35,7 @@ RSpec.describe AboutController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -49,7 +49,7 @@ RSpec.describe AboutController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/accounts_controller_spec.rb b/spec/controllers/accounts_controller_spec.rb index a8ade790cd..18c249c07e 100644 --- a/spec/controllers/accounts_controller_spec.rb +++ b/spec/controllers/accounts_controller_spec.rb @@ -40,7 +40,7 @@ RSpec.describe AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns correct format' do diff --git a/spec/controllers/activitypub/outboxes_controller_spec.rb b/spec/controllers/activitypub/outboxes_controller_spec.rb index a259980216..47460b22ce 100644 --- a/spec/controllers/activitypub/outboxes_controller_spec.rb +++ b/spec/controllers/activitypub/outboxes_controller_spec.rb @@ -13,7 +13,7 @@ RSpec.describe ActivityPub::OutboxesController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns application/activity+json' do diff --git a/spec/controllers/admin/accounts_controller_spec.rb b/spec/controllers/admin/accounts_controller_spec.rb index 8be27d8668..ff9dbbfb84 100644 --- a/spec/controllers/admin/accounts_controller_spec.rb +++ b/spec/controllers/admin/accounts_controller_spec.rb @@ -63,7 +63,7 @@ RSpec.describe Admin::AccountsController, type: :controller do it 'returns http success' do get :index - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -72,7 +72,7 @@ RSpec.describe Admin::AccountsController, type: :controller do it 'returns http success' do get :show, params: { id: account.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/admin/change_email_controller_spec.rb b/spec/controllers/admin/change_email_controller_spec.rb index 50f94f8357..31df0f0fce 100644 --- a/spec/controllers/admin/change_email_controller_spec.rb +++ b/spec/controllers/admin/change_email_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Admin::ChangeEmailsController, type: :controller do get :show, params: { account_id: account.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/admin/confirmations_controller_spec.rb b/spec/controllers/admin/confirmations_controller_spec.rb index 3f2b28c0e5..7c80349646 100644 --- a/spec/controllers/admin/confirmations_controller_spec.rb +++ b/spec/controllers/admin/confirmations_controller_spec.rb @@ -20,14 +20,14 @@ RSpec.describe Admin::ConfirmationsController, type: :controller do it 'raises an error when there is no account' do post :create, params: { account_id: 'fake' } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end it 'raises an error when there is no user' do account = Fabricate(:account, user: nil) post :create, params: { account_id: account.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/admin/domain_blocks_controller_spec.rb b/spec/controllers/admin/domain_blocks_controller_spec.rb index b9e73c04b1..79e7fea423 100644 --- a/spec/controllers/admin/domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/domain_blocks_controller_spec.rb @@ -23,7 +23,7 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do assigned = assigns(:domain_blocks) expect(assigned.count).to eq 1 expect(assigned.klass).to be DomainBlock - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -32,7 +32,7 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do get :new expect(assigns(:domain_block)).to be_instance_of(DomainBlock) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -41,7 +41,7 @@ RSpec.describe Admin::DomainBlocksController, type: :controller do domain_block = Fabricate(:domain_block) get :show, params: { id: domain_block.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/admin/email_domain_blocks_controller_spec.rb b/spec/controllers/admin/email_domain_blocks_controller_spec.rb index 295de9073a..133d38ff10 100644 --- a/spec/controllers/admin/email_domain_blocks_controller_spec.rb +++ b/spec/controllers/admin/email_domain_blocks_controller_spec.rb @@ -25,7 +25,7 @@ RSpec.describe Admin::EmailDomainBlocksController, type: :controller do assigned = assigns(:email_domain_blocks) expect(assigned.count).to eq 1 expect(assigned.klass).to be EmailDomainBlock - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -34,7 +34,7 @@ RSpec.describe Admin::EmailDomainBlocksController, type: :controller do get :new expect(assigns(:email_domain_block)).to be_instance_of(EmailDomainBlock) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/admin/instances_controller_spec.rb b/spec/controllers/admin/instances_controller_spec.rb index f57e3fa97d..412b814439 100644 --- a/spec/controllers/admin/instances_controller_spec.rb +++ b/spec/controllers/admin/instances_controller_spec.rb @@ -26,7 +26,7 @@ RSpec.describe Admin::InstancesController, type: :controller do expect(instances.size).to eq 1 expect(instances[0].domain).to eq 'less.popular' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/admin/reported_statuses_controller_spec.rb b/spec/controllers/admin/reported_statuses_controller_spec.rb index 41e032fc77..29957ed37e 100644 --- a/spec/controllers/admin/reported_statuses_controller_spec.rb +++ b/spec/controllers/admin/reported_statuses_controller_spec.rb @@ -84,7 +84,7 @@ describe Admin::ReportedStatusesController do allow(RemovalWorker).to receive(:perform_async) delete :destroy, params: { report_id: report, id: status } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(RemovalWorker). to have_received(:perform_async).with(status.id) end diff --git a/spec/controllers/admin/reports_controller_spec.rb b/spec/controllers/admin/reports_controller_spec.rb index 9be298df6a..e50c02a729 100644 --- a/spec/controllers/admin/reports_controller_spec.rb +++ b/spec/controllers/admin/reports_controller_spec.rb @@ -18,7 +18,7 @@ describe Admin::ReportsController do reports = assigns(:reports).to_a expect(reports.size).to eq 1 expect(reports[0]).to eq specified - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns http success with resolved filter' do @@ -31,7 +31,7 @@ describe Admin::ReportsController do expect(reports.size).to eq 1 expect(reports[0]).to eq specified - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -42,7 +42,7 @@ describe Admin::ReportsController do get :show, params: { id: report } expect(assigns(:report)).to eq report - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -52,7 +52,7 @@ describe Admin::ReportsController do report = Fabricate(:report) put :update, params: { id: report, outcome: 'unknown' } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end diff --git a/spec/controllers/admin/settings_controller_spec.rb b/spec/controllers/admin/settings_controller_spec.rb index 609bc762b8..eaf99679a1 100644 --- a/spec/controllers/admin/settings_controller_spec.rb +++ b/spec/controllers/admin/settings_controller_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Admin::SettingsController, type: :controller do it 'returns http success' do get :edit - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/admin/statuses_controller_spec.rb b/spec/controllers/admin/statuses_controller_spec.rb index 1515e299b8..cbaf397865 100644 --- a/spec/controllers/admin/statuses_controller_spec.rb +++ b/spec/controllers/admin/statuses_controller_spec.rb @@ -20,7 +20,7 @@ describe Admin::StatusesController do statuses = assigns(:statuses).to_a expect(statuses.size).to eq 2 - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns http success with media' do @@ -28,7 +28,7 @@ describe Admin::StatusesController do statuses = assigns(:statuses).to_a expect(statuses.size).to eq 1 - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -99,7 +99,7 @@ describe Admin::StatusesController do allow(RemovalWorker).to receive(:perform_async) delete :destroy, params: { account_id: account.id, id: status } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(RemovalWorker). to have_received(:perform_async).with(status.id) end diff --git a/spec/controllers/admin/subscriptions_controller_spec.rb b/spec/controllers/admin/subscriptions_controller_spec.rb index eb6f12b16e..967152abe3 100644 --- a/spec/controllers/admin/subscriptions_controller_spec.rb +++ b/spec/controllers/admin/subscriptions_controller_spec.rb @@ -26,7 +26,7 @@ RSpec.describe Admin::SubscriptionsController, type: :controller do expect(subscriptions.count).to eq 1 expect(subscriptions[0]).to eq specified - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/base_controller_spec.rb b/spec/controllers/api/base_controller_spec.rb index 0c7ca8990d..750ccc8cf6 100644 --- a/spec/controllers/api/base_controller_spec.rb +++ b/spec/controllers/api/base_controller_spec.rb @@ -23,7 +23,7 @@ describe Api::BaseController do it 'does not protect from forgery' do ActionController::Base.allow_forgery_protection = true post 'success' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/api/oembed_controller_spec.rb b/spec/controllers/api/oembed_controller_spec.rb index 7af4a6a5be..7fee15a353 100644 --- a/spec/controllers/api/oembed_controller_spec.rb +++ b/spec/controllers/api/oembed_controller_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Api::OEmbedController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/push_controller_spec.rb b/spec/controllers/api/push_controller_spec.rb index 647698bd19..d769d8554f 100644 --- a/spec/controllers/api/push_controller_spec.rb +++ b/spec/controllers/api/push_controller_spec.rb @@ -23,7 +23,7 @@ RSpec.describe Api::PushController, type: :controller do '3600', nil ) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(202) end end @@ -43,7 +43,7 @@ RSpec.describe Api::PushController, type: :controller do account, 'https://callback.host/api', ) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(202) end end diff --git a/spec/controllers/api/salmon_controller_spec.rb b/spec/controllers/api/salmon_controller_spec.rb index 8af8b83a89..5f01f80735 100644 --- a/spec/controllers/api/salmon_controller_spec.rb +++ b/spec/controllers/api/salmon_controller_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Api::SalmonController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(202) end it 'creates remote account' do diff --git a/spec/controllers/api/subscriptions_controller_spec.rb b/spec/controllers/api/subscriptions_controller_spec.rb index d90da9e324..48eb1fc64c 100644 --- a/spec/controllers/api/subscriptions_controller_spec.rb +++ b/spec/controllers/api/subscriptions_controller_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Api::SubscriptionsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'echoes back the challenge' do @@ -27,7 +27,7 @@ RSpec.describe Api::SubscriptionsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end @@ -59,7 +59,7 @@ RSpec.describe Api::SubscriptionsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates statuses for feed' do diff --git a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb index 87fce64eb3..9a52fd14c5 100644 --- a/spec/controllers/api/v1/accounts/credentials_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/credentials_controller_spec.rb @@ -14,7 +14,7 @@ describe Api::V1::Accounts::CredentialsController do describe 'GET #show' do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -36,7 +36,7 @@ describe Api::V1::Accounts::CredentialsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates account info' do diff --git a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb index 33982cb8f6..b47af49631 100644 --- a/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/follower_accounts_controller_spec.rb @@ -15,7 +15,7 @@ describe Api::V1::Accounts::FollowerAccountsController do it 'returns http success' do get :index, params: { account_id: user.account.id, limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb index e22f54a31d..29fd7cd5b7 100644 --- a/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/following_accounts_controller_spec.rb @@ -15,7 +15,7 @@ describe Api::V1::Accounts::FollowingAccountsController do it 'returns http success' do get :index, params: { account_id: user.account.id, limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/accounts/lists_controller_spec.rb b/spec/controllers/api/v1/accounts/lists_controller_spec.rb index 0a372f65b7..df9fe0e34c 100644 --- a/spec/controllers/api/v1/accounts/lists_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/lists_controller_spec.rb @@ -17,7 +17,7 @@ describe Api::V1::Accounts::ListsController do describe 'GET #index' do it 'returns http success' do get :index, params: { account_id: account.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/accounts/relationships_controller_spec.rb b/spec/controllers/api/v1/accounts/relationships_controller_spec.rb index e0de790c83..7e350da7e5 100644 --- a/spec/controllers/api/v1/accounts/relationships_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/relationships_controller_spec.rb @@ -25,7 +25,7 @@ describe Api::V1::Accounts::RelationshipsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns JSON with correct data' do @@ -43,7 +43,7 @@ describe Api::V1::Accounts::RelationshipsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns JSON with correct data' do diff --git a/spec/controllers/api/v1/accounts/search_controller_spec.rb b/spec/controllers/api/v1/accounts/search_controller_spec.rb index 42cc3f64de..dbc4b9f3e6 100644 --- a/spec/controllers/api/v1/accounts/search_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/search_controller_spec.rb @@ -14,7 +14,7 @@ RSpec.describe Api::V1::Accounts::SearchController, type: :controller do it 'returns http success' do get :show, params: { q: 'query' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb index c49a77ac34..09bb469373 100644 --- a/spec/controllers/api/v1/accounts/statuses_controller_spec.rb +++ b/spec/controllers/api/v1/accounts/statuses_controller_spec.rb @@ -15,7 +15,7 @@ describe Api::V1::Accounts::StatusesController do it 'returns http success' do get :index, params: { account_id: user.account.id, limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end @@ -23,7 +23,7 @@ describe Api::V1::Accounts::StatusesController do it 'returns http success' do get :index, params: { account_id: user.account.id, only_media: true } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -35,7 +35,7 @@ describe Api::V1::Accounts::StatusesController do it 'returns http success' do get :index, params: { account_id: user.account.id, exclude_replies: true } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -47,7 +47,7 @@ describe Api::V1::Accounts::StatusesController do it 'returns http success' do get :index, params: { account_id: user.account.id, pinned: true } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/accounts_controller_spec.rb b/spec/controllers/api/v1/accounts_controller_spec.rb index 053c53e5af..7a9e0f8e41 100644 --- a/spec/controllers/api/v1/accounts_controller_spec.rb +++ b/spec/controllers/api/v1/accounts_controller_spec.rb @@ -13,7 +13,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do describe 'GET #show' do it 'returns http success' do get :show, params: { id: user.account.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -28,7 +28,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do let(:locked) { false } it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns JSON with following=true and requested=false' do @@ -47,7 +47,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do let(:locked) { true } it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns JSON with following=false and requested=true' do @@ -72,7 +72,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes the following relation between user and target user' do @@ -89,7 +89,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes the following relation between user and target user' do @@ -110,7 +110,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes the blocking relation between user and target user' do @@ -127,7 +127,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'does not remove the following relation between user and target user' do @@ -152,7 +152,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'does not remove the following relation between user and target user' do @@ -177,7 +177,7 @@ RSpec.describe Api::V1::AccountsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes the muting relation between user and target user' do diff --git a/spec/controllers/api/v1/apps/credentials_controller_spec.rb b/spec/controllers/api/v1/apps/credentials_controller_spec.rb index 38f2a4e102..0f811d5f37 100644 --- a/spec/controllers/api/v1/apps/credentials_controller_spec.rb +++ b/spec/controllers/api/v1/apps/credentials_controller_spec.rb @@ -16,7 +16,7 @@ describe Api::V1::Apps::CredentialsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'does not contain client credentials' do diff --git a/spec/controllers/api/v1/apps_controller_spec.rb b/spec/controllers/api/v1/apps_controller_spec.rb index 1ad9d63831..60a4c3b41c 100644 --- a/spec/controllers/api/v1/apps_controller_spec.rb +++ b/spec/controllers/api/v1/apps_controller_spec.rb @@ -9,7 +9,7 @@ RSpec.describe Api::V1::AppsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates an OAuth app' do diff --git a/spec/controllers/api/v1/blocks_controller_spec.rb b/spec/controllers/api/v1/blocks_controller_spec.rb index 9b2bbdf0e0..eff5fb9daa 100644 --- a/spec/controllers/api/v1/blocks_controller_spec.rb +++ b/spec/controllers/api/v1/blocks_controller_spec.rb @@ -47,7 +47,7 @@ RSpec.describe Api::V1::BlocksController, type: :controller do it 'returns http success' do get :index - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/custom_emojis_controller_spec.rb b/spec/controllers/api/v1/custom_emojis_controller_spec.rb index 9f3522812b..fe8daa7c5a 100644 --- a/spec/controllers/api/v1/custom_emojis_controller_spec.rb +++ b/spec/controllers/api/v1/custom_emojis_controller_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Api::V1::CustomEmojisController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/domain_blocks_controller_spec.rb b/spec/controllers/api/v1/domain_blocks_controller_spec.rb index 3713931dc6..bae4612a28 100644 --- a/spec/controllers/api/v1/domain_blocks_controller_spec.rb +++ b/spec/controllers/api/v1/domain_blocks_controller_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Api::V1::DomainBlocksController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns blocked domains' do @@ -31,7 +31,7 @@ RSpec.describe Api::V1::DomainBlocksController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates a domain block' do @@ -45,7 +45,7 @@ RSpec.describe Api::V1::DomainBlocksController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'deletes a domain block' do diff --git a/spec/controllers/api/v1/follow_requests_controller_spec.rb b/spec/controllers/api/v1/follow_requests_controller_spec.rb index 51df006a22..3c0b84af8b 100644 --- a/spec/controllers/api/v1/follow_requests_controller_spec.rb +++ b/spec/controllers/api/v1/follow_requests_controller_spec.rb @@ -18,7 +18,7 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -28,7 +28,7 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'allows follower to follow' do @@ -42,7 +42,7 @@ RSpec.describe Api::V1::FollowRequestsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes follow request' do diff --git a/spec/controllers/api/v1/follows_controller_spec.rb b/spec/controllers/api/v1/follows_controller_spec.rb index ea9e76d686..38badb80ae 100644 --- a/spec/controllers/api/v1/follows_controller_spec.rb +++ b/spec/controllers/api/v1/follows_controller_spec.rb @@ -24,7 +24,7 @@ RSpec.describe Api::V1::FollowsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates account for remote user' do @@ -45,7 +45,7 @@ RSpec.describe Api::V1::FollowsController, type: :controller do it 'returns http success if already following, too' do post :create, params: { uri: 'gargron@quitter.no' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/instances_controller_spec.rb b/spec/controllers/api/v1/instances_controller_spec.rb index eba233b053..7397d25d6b 100644 --- a/spec/controllers/api/v1/instances_controller_spec.rb +++ b/spec/controllers/api/v1/instances_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Api::V1::InstancesController, type: :controller do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/lists/accounts_controller_spec.rb b/spec/controllers/api/v1/lists/accounts_controller_spec.rb index 953e5909d0..c37a481d6d 100644 --- a/spec/controllers/api/v1/lists/accounts_controller_spec.rb +++ b/spec/controllers/api/v1/lists/accounts_controller_spec.rb @@ -17,7 +17,7 @@ describe Api::V1::Lists::AccountsController do it 'returns http success' do get :show, params: { list_id: list.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -30,7 +30,7 @@ describe Api::V1::Lists::AccountsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'adds account to the list' do @@ -44,7 +44,7 @@ describe Api::V1::Lists::AccountsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes account from the list' do diff --git a/spec/controllers/api/v1/lists_controller_spec.rb b/spec/controllers/api/v1/lists_controller_spec.rb index be08c221fe..2134295815 100644 --- a/spec/controllers/api/v1/lists_controller_spec.rb +++ b/spec/controllers/api/v1/lists_controller_spec.rb @@ -12,14 +12,14 @@ RSpec.describe Api::V1::ListsController, type: :controller do describe 'GET #index' do it 'returns http success' do get :index - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end describe 'GET #show' do it 'returns http success' do get :show, params: { id: list.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -29,7 +29,7 @@ RSpec.describe Api::V1::ListsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates list' do @@ -44,7 +44,7 @@ RSpec.describe Api::V1::ListsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the list' do @@ -58,7 +58,7 @@ RSpec.describe Api::V1::ListsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'deletes the list' do diff --git a/spec/controllers/api/v1/media_controller_spec.rb b/spec/controllers/api/v1/media_controller_spec.rb index 0e494638f2..ce260eb90a 100644 --- a/spec/controllers/api/v1/media_controller_spec.rb +++ b/spec/controllers/api/v1/media_controller_spec.rb @@ -30,7 +30,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do end it 'returns http 422' do - expect(response).to have_http_status(:error) + expect(response).to have_http_status(500) end end end @@ -41,7 +41,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates a media attachment' do @@ -63,7 +63,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates a media attachment' do @@ -85,7 +85,7 @@ RSpec.describe Api::V1::MediaController, type: :controller do end xit 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end xit 'creates a media attachment' do diff --git a/spec/controllers/api/v1/mutes_controller_spec.rb b/spec/controllers/api/v1/mutes_controller_spec.rb index 97d6c27738..dc4a9753a0 100644 --- a/spec/controllers/api/v1/mutes_controller_spec.rb +++ b/spec/controllers/api/v1/mutes_controller_spec.rb @@ -15,7 +15,7 @@ RSpec.describe Api::V1::MutesController, type: :controller do it 'returns http success' do get :index, params: { limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/notifications_controller_spec.rb b/spec/controllers/api/v1/notifications_controller_spec.rb index f493d0d38e..2e6163fcd6 100644 --- a/spec/controllers/api/v1/notifications_controller_spec.rb +++ b/spec/controllers/api/v1/notifications_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do notification = Fabricate(:notification, account: user.account) get :show, params: { id: notification.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -25,7 +25,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do notification = Fabricate(:notification, account: user.account) post :dismiss, params: { id: notification.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect { notification.reload }.to raise_error(ActiveRecord::RecordNotFound) end end @@ -36,7 +36,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do post :clear expect(notification.account.reload.notifications).to be_empty - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -56,7 +56,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'includes reblog' do @@ -82,7 +82,7 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'includes reblog' do diff --git a/spec/controllers/api/v1/reports_controller_spec.rb b/spec/controllers/api/v1/reports_controller_spec.rb index 1eb5a43534..1e1ef9308b 100644 --- a/spec/controllers/api/v1/reports_controller_spec.rb +++ b/spec/controllers/api/v1/reports_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Api::V1::ReportsController, type: :controller do it 'returns http success' do get :index - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -31,7 +31,7 @@ RSpec.describe Api::V1::ReportsController, type: :controller do it 'creates a report' do expect(status.reload.account.targeted_reports).not_to be_empty - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'sends e-mails to admins' do diff --git a/spec/controllers/api/v1/search_controller_spec.rb b/spec/controllers/api/v1/search_controller_spec.rb index ff0c254b1f..0247038675 100644 --- a/spec/controllers/api/v1/search_controller_spec.rb +++ b/spec/controllers/api/v1/search_controller_spec.rb @@ -16,7 +16,7 @@ RSpec.describe Api::V1::SearchController, type: :controller do it 'returns http success' do get :index, params: { q: 'test' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb b/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb index 556731d578..c873e05dd8 100644 --- a/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/favourited_by_accounts_controller_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control it 'returns http success' do get :index, params: { status_id: status.id, limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end @@ -43,7 +43,7 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control it 'returns http unautharized' do get :index, params: { status_id: status.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end @@ -58,7 +58,7 @@ RSpec.describe Api::V1::Statuses::FavouritedByAccountsController, type: :control it 'returns http success' do get :index, params: { status_id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/statuses/favourites_controller_spec.rb b/spec/controllers/api/v1/statuses/favourites_controller_spec.rb index aba7cd4588..53f602616d 100644 --- a/spec/controllers/api/v1/statuses/favourites_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/favourites_controller_spec.rb @@ -22,7 +22,7 @@ describe Api::V1::Statuses::FavouritesController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the favourites count' do @@ -51,7 +51,7 @@ describe Api::V1::Statuses::FavouritesController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the favourites count' do diff --git a/spec/controllers/api/v1/statuses/mutes_controller_spec.rb b/spec/controllers/api/v1/statuses/mutes_controller_spec.rb index 54c594e92c..13b4625d19 100644 --- a/spec/controllers/api/v1/statuses/mutes_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/mutes_controller_spec.rb @@ -22,7 +22,7 @@ describe Api::V1::Statuses::MutesController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'creates a conversation mute' do @@ -39,7 +39,7 @@ describe Api::V1::Statuses::MutesController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'destroys the conversation mute' do diff --git a/spec/controllers/api/v1/statuses/pins_controller_spec.rb b/spec/controllers/api/v1/statuses/pins_controller_spec.rb index 79005c9dec..8f5b0800b0 100644 --- a/spec/controllers/api/v1/statuses/pins_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/pins_controller_spec.rb @@ -22,7 +22,7 @@ describe Api::V1::Statuses::PinsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the pinned attribute' do @@ -46,7 +46,7 @@ describe Api::V1::Statuses::PinsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the pinned attribute' do diff --git a/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb b/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb index ba022a96e0..9c0c2b60cb 100644 --- a/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/reblogged_by_accounts_controller_spec.rb @@ -21,7 +21,7 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll it 'returns http success' do get :index, params: { status_id: status.id, limit: 1 } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end @@ -42,7 +42,7 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll it 'returns http unautharized' do get :index, params: { status_id: status.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end @@ -57,7 +57,7 @@ RSpec.describe Api::V1::Statuses::RebloggedByAccountsController, type: :controll it 'returns http success' do get :index, params: { status_id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb b/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb index 7417ff672f..e60f8da2a4 100644 --- a/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb +++ b/spec/controllers/api/v1/statuses/reblogs_controller_spec.rb @@ -22,7 +22,7 @@ describe Api::V1::Statuses::ReblogsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the reblogs count' do @@ -51,7 +51,7 @@ describe Api::V1::Statuses::ReblogsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'updates the reblogs count' do diff --git a/spec/controllers/api/v1/statuses_controller_spec.rb b/spec/controllers/api/v1/statuses_controller_spec.rb index a36265395b..27e4f4eb24 100644 --- a/spec/controllers/api/v1/statuses_controller_spec.rb +++ b/spec/controllers/api/v1/statuses_controller_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do it 'returns http success' do get :show, params: { id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -30,7 +30,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do it 'returns http success' do get :context, params: { id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -40,7 +40,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -52,7 +52,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'removes the status' do @@ -72,7 +72,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do describe 'GET #show' do it 'returns http unautharized' do get :show, params: { id: status.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end @@ -83,14 +83,14 @@ RSpec.describe Api::V1::StatusesController, type: :controller do it 'returns http unautharized' do get :context, params: { id: status.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end describe 'GET #card' do it 'returns http unautharized' do get :card, params: { id: status.id } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end @@ -101,7 +101,7 @@ RSpec.describe Api::V1::StatusesController, type: :controller do describe 'GET #show' do it 'returns http success' do get :show, params: { id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -112,14 +112,14 @@ RSpec.describe Api::V1::StatusesController, type: :controller do it 'returns http success' do get :context, params: { id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end describe 'GET #card' do it 'returns http success' do get :card, params: { id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/timelines/home_controller_spec.rb b/spec/controllers/api/v1/timelines/home_controller_spec.rb index 4d45235209..85b0316418 100644 --- a/spec/controllers/api/v1/timelines/home_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/home_controller_spec.rb @@ -23,7 +23,7 @@ describe Api::V1::Timelines::HomeController do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end diff --git a/spec/controllers/api/v1/timelines/list_controller_spec.rb b/spec/controllers/api/v1/timelines/list_controller_spec.rb index 07eba955af..1729217c91 100644 --- a/spec/controllers/api/v1/timelines/list_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/list_controller_spec.rb @@ -24,7 +24,7 @@ describe Api::V1::Timelines::ListController do it 'returns http success' do get :show, params: { id: list.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/api/v1/timelines/public_controller_spec.rb b/spec/controllers/api/v1/timelines/public_controller_spec.rb index 3acf2e2678..68d87bbcbd 100644 --- a/spec/controllers/api/v1/timelines/public_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/public_controller_spec.rb @@ -22,7 +22,7 @@ describe Api::V1::Timelines::PublicController do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end @@ -35,7 +35,7 @@ describe Api::V1::Timelines::PublicController do it 'returns http success' do get :show, params: { local: true } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end @@ -48,7 +48,7 @@ describe Api::V1::Timelines::PublicController do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link']).to be_nil end end diff --git a/spec/controllers/api/v1/timelines/tag_controller_spec.rb b/spec/controllers/api/v1/timelines/tag_controller_spec.rb index 6c66ee58e4..472779f545 100644 --- a/spec/controllers/api/v1/timelines/tag_controller_spec.rb +++ b/spec/controllers/api/v1/timelines/tag_controller_spec.rb @@ -21,7 +21,7 @@ describe Api::V1::Timelines::TagController do it 'returns http success' do get :show, params: { id: 'test' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link'].links.size).to eq(2) end end @@ -33,7 +33,7 @@ describe Api::V1::Timelines::TagController do describe 'GET #show' do it 'returns http success' do get :show, params: { id: 'test' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.headers['Link']).to be_nil end end diff --git a/spec/controllers/api/web/settings_controller_spec.rb b/spec/controllers/api/web/settings_controller_spec.rb index ff211c7b1a..815da04c47 100644 --- a/spec/controllers/api/web/settings_controller_spec.rb +++ b/spec/controllers/api/web/settings_controller_spec.rb @@ -13,7 +13,7 @@ describe Api::Web::SettingsController do patch :update, format: :json, params: { data: { 'onboarded' => true } } user.reload - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(user_web_setting.data['onboarded']).to eq('true') end diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb index 3e4d27e055..c6c78d3f71 100644 --- a/spec/controllers/application_controller_spec.rb +++ b/spec/controllers/application_controller_spec.rb @@ -51,7 +51,7 @@ describe ApplicationController, type: :controller do routes.draw { get 'success' => 'anonymous#success' } allow(Rails.env).to receive(:production?).and_return(false) get 'success' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it "forces ssl if Rails.env.production? is 'true'" do @@ -145,13 +145,13 @@ describe ApplicationController, type: :controller do it 'does nothing if not signed in' do get 'success' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'does nothing if user who signed in is not suspended' do sign_in(Fabricate(:user, account: Fabricate(:account, suspended: false))) get 'success' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'returns http 403 if user who signed in is suspended' do diff --git a/spec/controllers/auth/confirmations_controller_spec.rb b/spec/controllers/auth/confirmations_controller_spec.rb index 80a06c43ab..b3af5e0ec8 100644 --- a/spec/controllers/auth/confirmations_controller_spec.rb +++ b/spec/controllers/auth/confirmations_controller_spec.rb @@ -7,7 +7,7 @@ describe Auth::ConfirmationsController, type: :controller do it 'returns http success' do @request.env['devise.mapping'] = Devise.mappings[:user] get :new - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/auth/passwords_controller_spec.rb b/spec/controllers/auth/passwords_controller_spec.rb index 992d2e29d2..dcfdebb173 100644 --- a/spec/controllers/auth/passwords_controller_spec.rb +++ b/spec/controllers/auth/passwords_controller_spec.rb @@ -9,7 +9,7 @@ describe Auth::PasswordsController, type: :controller do it 'returns http success' do @request.env['devise.mapping'] = Devise.mappings[:user] get :new - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -24,7 +24,7 @@ describe Auth::PasswordsController, type: :controller do context 'with valid reset_password_token' do it 'returns http success' do get :edit, params: { reset_password_token: @token } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/auth/registrations_controller_spec.rb b/spec/controllers/auth/registrations_controller_spec.rb index 97d2c53df7..9cfbd481f8 100644 --- a/spec/controllers/auth/registrations_controller_spec.rb +++ b/spec/controllers/auth/registrations_controller_spec.rb @@ -35,7 +35,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do request.env["devise.mapping"] = Devise.mappings[:user] sign_in(Fabricate(:user)) get :edit - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -44,7 +44,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do request.env["devise.mapping"] = Devise.mappings[:user] sign_in(Fabricate(:user), scope: :user) post :update - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -63,7 +63,7 @@ RSpec.describe Auth::RegistrationsController, type: :controller do it 'returns http success' do Setting.open_registrations = true get :new - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index d5fed17d63..97719a606a 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -12,7 +12,7 @@ RSpec.describe Auth::SessionsController, type: :controller do it 'returns http success' do get :new - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/authorize_follows_controller_spec.rb b/spec/controllers/authorize_follows_controller_spec.rb index b1cbef7ea9..52971c7247 100644 --- a/spec/controllers/authorize_follows_controller_spec.rb +++ b/spec/controllers/authorize_follows_controller_spec.rb @@ -47,7 +47,7 @@ describe AuthorizeFollowsController do get :show, params: { acct: 'http://example.com' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(assigns(:account)).to eq account end @@ -59,7 +59,7 @@ describe AuthorizeFollowsController do get :show, params: { acct: 'acct:found@hostname' } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(assigns(:account)).to eq account end end diff --git a/spec/controllers/concerns/account_controller_concern_spec.rb b/spec/controllers/concerns/account_controller_concern_spec.rb index ae46f9ba66..93685103fc 100644 --- a/spec/controllers/concerns/account_controller_concern_spec.rb +++ b/spec/controllers/concerns/account_controller_concern_spec.rb @@ -39,7 +39,7 @@ describe ApplicationController, type: :controller do it 'returns http success' do account = Fabricate(:account) get 'success', params: { account_username: account.username } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/concerns/export_controller_concern_spec.rb b/spec/controllers/concerns/export_controller_concern_spec.rb index 9d6f782b99..6a13db69d6 100644 --- a/spec/controllers/concerns/export_controller_concern_spec.rb +++ b/spec/controllers/concerns/export_controller_concern_spec.rb @@ -19,7 +19,7 @@ describe ApplicationController, type: :controller do sign_in user get :index, format: :csv - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'text/csv' expect(response.headers['Content-Disposition']).to eq 'attachment; filename="anonymous.csv"' expect(response.body).to eq user.account.username diff --git a/spec/controllers/follower_accounts_controller_spec.rb b/spec/controllers/follower_accounts_controller_spec.rb index b9b7fef73e..3a42a6e182 100644 --- a/spec/controllers/follower_accounts_controller_spec.rb +++ b/spec/controllers/follower_accounts_controller_spec.rb @@ -19,7 +19,7 @@ describe FollowerAccountsController do expect(assigned[0]).to eq follow1 expect(assigned[1]).to eq follow0 - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/following_accounts_controller_spec.rb b/spec/controllers/following_accounts_controller_spec.rb index 55e7265c76..33376365d1 100644 --- a/spec/controllers/following_accounts_controller_spec.rb +++ b/spec/controllers/following_accounts_controller_spec.rb @@ -19,7 +19,7 @@ describe FollowingAccountsController do expect(assigned[0]).to eq follow1 expect(assigned[1]).to eq follow0 - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/manifests_controller_spec.rb b/spec/controllers/manifests_controller_spec.rb index 71967e4f09..a549adef3f 100644 --- a/spec/controllers/manifests_controller_spec.rb +++ b/spec/controllers/manifests_controller_spec.rb @@ -9,7 +9,7 @@ describe ManifestsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/media_controller_spec.rb b/spec/controllers/media_controller_spec.rb index 5b03899e4c..ac44a76f20 100644 --- a/spec/controllers/media_controller_spec.rb +++ b/spec/controllers/media_controller_spec.rb @@ -18,13 +18,13 @@ describe MediaController do media_attachment = Fabricate(:media_attachment, status: nil) get :show, params: { id: media_attachment.to_param } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end it 'raises when shortcode cant be found' do get :show, params: { id: 'missing' } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end it 'raises when not permitted to view' do @@ -33,7 +33,7 @@ describe MediaController do allow_any_instance_of(MediaController).to receive(:authorize).and_raise(ActiveRecord::RecordNotFound) get :show, params: { id: media_attachment.to_param } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/oauth/authorizations_controller_spec.rb b/spec/controllers/oauth/authorizations_controller_spec.rb index 5c2a62b48c..91c2d03ef0 100644 --- a/spec/controllers/oauth/authorizations_controller_spec.rb +++ b/spec/controllers/oauth/authorizations_controller_spec.rb @@ -26,7 +26,7 @@ RSpec.describe Oauth::AuthorizationsController, type: :controller do it 'returns http success' do subject - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'gives options to authorize and deny' do diff --git a/spec/controllers/oauth/authorized_applications_controller_spec.rb b/spec/controllers/oauth/authorized_applications_controller_spec.rb index 2a2b92283f..f967b507f0 100644 --- a/spec/controllers/oauth/authorized_applications_controller_spec.rb +++ b/spec/controllers/oauth/authorized_applications_controller_spec.rb @@ -24,7 +24,7 @@ describe Oauth::AuthorizedApplicationsController do it 'returns http success' do subject - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end include_examples 'stores location for user' diff --git a/spec/controllers/remote_follow_controller_spec.rb b/spec/controllers/remote_follow_controller_spec.rb index 86b1eb8d0f..5088c2e656 100644 --- a/spec/controllers/remote_follow_controller_spec.rb +++ b/spec/controllers/remote_follow_controller_spec.rb @@ -10,7 +10,7 @@ describe RemoteFollowController do account = Fabricate(:account) get :new, params: { account_username: account.to_param } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response).to render_template(:new) expect(assigns(:remote_follow).acct).to be_nil end @@ -20,7 +20,7 @@ describe RemoteFollowController do account = Fabricate(:account) get :new, params: { account_username: account.to_param } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response).to render_template(:new) expect(assigns(:remote_follow).acct).to eq 'user@example.com' end diff --git a/spec/controllers/settings/applications_controller_spec.rb b/spec/controllers/settings/applications_controller_spec.rb index 90e6a63d5c..f87107695f 100644 --- a/spec/controllers/settings/applications_controller_spec.rb +++ b/spec/controllers/settings/applications_controller_spec.rb @@ -15,7 +15,7 @@ describe Settings::ApplicationsController do it 'shows apps' do get :index - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(assigns(:applications)).to include(app) expect(assigns(:applications)).to_not include(other_app) end @@ -25,7 +25,7 @@ describe Settings::ApplicationsController do describe 'GET #show' do it 'returns http success' do get :show, params: { id: app.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(assigns[:application]).to eql(app) end @@ -40,7 +40,7 @@ describe Settings::ApplicationsController do describe 'GET #new' do it 'works' do get :new - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -102,7 +102,7 @@ describe Settings::ApplicationsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'renders form again' do @@ -151,7 +151,7 @@ describe Settings::ApplicationsController do end it 'returns http success' do - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'renders form again' do diff --git a/spec/controllers/settings/deletes_controller_spec.rb b/spec/controllers/settings/deletes_controller_spec.rb index 9b55090df2..35fd64e9b9 100644 --- a/spec/controllers/settings/deletes_controller_spec.rb +++ b/spec/controllers/settings/deletes_controller_spec.rb @@ -13,7 +13,7 @@ describe Settings::DeletesController do it 'renders confirmation page' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/exports_controller_spec.rb b/spec/controllers/settings/exports_controller_spec.rb index 19cb0abdae..b7cab4d8f2 100644 --- a/spec/controllers/settings/exports_controller_spec.rb +++ b/spec/controllers/settings/exports_controller_spec.rb @@ -17,7 +17,7 @@ describe Settings::ExportsController do export = assigns(:export) expect(export).to be_instance_of Export expect(export.account).to eq user.account - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/follower_domains_controller_spec.rb b/spec/controllers/settings/follower_domains_controller_spec.rb index 333223c619..6d415a6549 100644 --- a/spec/controllers/settings/follower_domains_controller_spec.rb +++ b/spec/controllers/settings/follower_domains_controller_spec.rb @@ -36,7 +36,7 @@ describe Settings::FollowerDomainsController do it 'returns http success' do sign_in user, scope: :user subject - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end include_examples 'authenticate user' diff --git a/spec/controllers/settings/imports_controller_spec.rb b/spec/controllers/settings/imports_controller_spec.rb index 59b10e0da2..7a9b021957 100644 --- a/spec/controllers/settings/imports_controller_spec.rb +++ b/spec/controllers/settings/imports_controller_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Settings::ImportsController, type: :controller do describe "GET #show" do it "returns http success" do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/notifications_controller_spec.rb b/spec/controllers/settings/notifications_controller_spec.rb index 0bd9934486..981ef674ec 100644 --- a/spec/controllers/settings/notifications_controller_spec.rb +++ b/spec/controllers/settings/notifications_controller_spec.rb @@ -12,7 +12,7 @@ describe Settings::NotificationsController do describe 'GET #show' do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/preferences_controller_spec.rb b/spec/controllers/settings/preferences_controller_spec.rb index 0f94316737..7877c73621 100644 --- a/spec/controllers/settings/preferences_controller_spec.rb +++ b/spec/controllers/settings/preferences_controller_spec.rb @@ -12,7 +12,7 @@ describe Settings::PreferencesController do describe 'GET #show' do it 'returns http success' do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/profiles_controller_spec.rb b/spec/controllers/settings/profiles_controller_spec.rb index ee3315be62..a453200af6 100644 --- a/spec/controllers/settings/profiles_controller_spec.rb +++ b/spec/controllers/settings/profiles_controller_spec.rb @@ -11,7 +11,7 @@ RSpec.describe Settings::ProfilesController, type: :controller do describe "GET #show" do it "returns http success" do get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb index aee82a3d85..7612bf90ec 100644 --- a/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/confirmations_controller_spec.rb @@ -15,7 +15,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do expect(assigns(:confirmation)).to be_instance_of Form::TwoFactorConfirmation expect(assigns(:provision_url)).to eq 'otpauth://totp/local-part@domain?secret=thisisasecretforthespecofnewview&issuer=cb6e6126.ngrok.io' expect(assigns(:qrcode)).to be_instance_of RQRCode::QRCode - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response).to render_template(:new) end end @@ -71,7 +71,7 @@ describe Settings::TwoFactorAuthentication::ConfirmationsController do expect(assigns(:recovery_codes)).to eq otp_backup_codes expect(flash[:notice]).to eq 'Two-factor authentication successfully enabled' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response).to render_template('settings/two_factor_authentication/recovery_codes/index') end end diff --git a/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb b/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb index aa28cdf3f8..c04760e535 100644 --- a/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentication/recovery_codes_controller_spec.rb @@ -19,7 +19,7 @@ describe Settings::TwoFactorAuthentication::RecoveryCodesController do expect(assigns(:recovery_codes)).to eq otp_backup_codes expect(flash[:notice]).to eq 'Recovery codes successfully regenerated' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response).to render_template(:index) end diff --git a/spec/controllers/settings/two_factor_authentications_controller_spec.rb b/spec/controllers/settings/two_factor_authentications_controller_spec.rb index 6c49f6f0dd..9f27222ad3 100644 --- a/spec/controllers/settings/two_factor_authentications_controller_spec.rb +++ b/spec/controllers/settings/two_factor_authentications_controller_spec.rb @@ -18,7 +18,7 @@ describe Settings::TwoFactorAuthenticationsController do user.update(otp_required_for_login: true) get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end @@ -27,7 +27,7 @@ describe Settings::TwoFactorAuthenticationsController do user.update(otp_required_for_login: false) get :show - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end end diff --git a/spec/controllers/statuses_controller_spec.rb b/spec/controllers/statuses_controller_spec.rb index 95fb4d5945..89af55688f 100644 --- a/spec/controllers/statuses_controller_spec.rb +++ b/spec/controllers/statuses_controller_spec.rb @@ -85,7 +85,7 @@ describe StatusesController do it 'returns a success' do status = Fabricate(:status) get :show, params: { account_username: status.account.username, id: status.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'renders stream_entries/show' do diff --git a/spec/controllers/stream_entries_controller_spec.rb b/spec/controllers/stream_entries_controller_spec.rb index 665c5b7474..534bc393dc 100644 --- a/spec/controllers/stream_entries_controller_spec.rb +++ b/spec/controllers/stream_entries_controller_spec.rb @@ -77,7 +77,7 @@ RSpec.describe StreamEntriesController, type: :controller do it 'returns http success with Atom' do status = Fabricate(:status) get :show, params: { account_username: status.account.username, id: status.stream_entry.id }, format: 'atom' - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end end diff --git a/spec/controllers/tags_controller_spec.rb b/spec/controllers/tags_controller_spec.rb index b04666c0ff..33ccaed61c 100644 --- a/spec/controllers/tags_controller_spec.rb +++ b/spec/controllers/tags_controller_spec.rb @@ -12,7 +12,7 @@ RSpec.describe TagsController, type: :controller do context 'when tag exists' do it 'returns http success' do get :show, params: { id: 'test', max_id: late.id } - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) end it 'renders application layout' do @@ -25,7 +25,7 @@ RSpec.describe TagsController, type: :controller do it 'returns http missing for non-existent tag' do get :show, params: { id: 'none' } - expect(response).to have_http_status(:missing) + expect(response).to have_http_status(404) end end end diff --git a/spec/controllers/well_known/host_meta_controller_spec.rb b/spec/controllers/well_known/host_meta_controller_spec.rb index 87c1485ed5..8147e8f618 100644 --- a/spec/controllers/well_known/host_meta_controller_spec.rb +++ b/spec/controllers/well_known/host_meta_controller_spec.rb @@ -7,7 +7,7 @@ describe WellKnown::HostMetaController, type: :controller do it 'returns http success' do get :show, format: :xml - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/xrd+xml' expect(response.body).to eq < diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb index 466f87c45c..b05745ea3b 100644 --- a/spec/controllers/well_known/webfinger_controller_spec.rb +++ b/spec/controllers/well_known/webfinger_controller_spec.rb @@ -50,7 +50,7 @@ PEM json = body_as_json - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/jrd+json' expect(json[:subject]).to eq 'acct:alice@cb6e6126.ngrok.io' expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice') @@ -61,7 +61,7 @@ PEM xml = Nokogiri::XML(response.body) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/xrd+xml' expect(xml.at_xpath('//xmlns:Subject').content).to eq 'acct:alice@cb6e6126.ngrok.io' expect(xml.xpath('//xmlns:Alias').map(&:content)).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice') @@ -81,7 +81,7 @@ PEM json = body_as_json - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/jrd+json' expect(json[:subject]).to eq 'acct:alice@cb6e6126.ngrok.io' expect(json[:aliases]).to include('https://cb6e6126.ngrok.io/@alice', 'https://cb6e6126.ngrok.io/users/alice') diff --git a/spec/requests/host_meta_request_spec.rb b/spec/requests/host_meta_request_spec.rb index 0c51b5f484..beb33a859e 100644 --- a/spec/requests/host_meta_request_spec.rb +++ b/spec/requests/host_meta_request_spec.rb @@ -5,7 +5,7 @@ describe "The host_meta route" do it "returns an xml response" do get host_meta_url - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq "application/xrd+xml" end end diff --git a/spec/requests/webfinger_request_spec.rb b/spec/requests/webfinger_request_spec.rb index a17d6cc22e..7f9e1162e9 100644 --- a/spec/requests/webfinger_request_spec.rb +++ b/spec/requests/webfinger_request_spec.rb @@ -7,7 +7,7 @@ describe 'The webfinger route' do it 'returns a json response' do get webfinger_url(resource: alice.to_webfinger_s) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/jrd+json' end end @@ -16,7 +16,7 @@ describe 'The webfinger route' do it 'returns an xml response for xml format' do get webfinger_url(resource: alice.to_webfinger_s, format: :xml) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/xrd+xml' end @@ -24,7 +24,7 @@ describe 'The webfinger route' do headers = { 'HTTP_ACCEPT' => 'application/xrd+xml' } get webfinger_url(resource: alice.to_webfinger_s), headers: headers - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/xrd+xml' end end @@ -33,7 +33,7 @@ describe 'The webfinger route' do it 'returns a json response for json format' do get webfinger_url(resource: alice.to_webfinger_s, format: :json) - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/jrd+json' end @@ -41,7 +41,7 @@ describe 'The webfinger route' do headers = { 'HTTP_ACCEPT' => 'application/jrd+json' } get webfinger_url(resource: alice.to_webfinger_s), headers: headers - expect(response).to have_http_status(:success) + expect(response).to have_http_status(200) expect(response.content_type).to eq 'application/jrd+json' end end From a4a36d994b0949aff134a2c1ba4efc361b7fb358 Mon Sep 17 00:00:00 2001 From: Lynx Kotoura Date: Sun, 22 Apr 2018 04:35:55 +0900 Subject: [PATCH 14/82] Separate high contrast theme (#7213) --- app/javascript/styles/contrast.scss | 3 + app/javascript/styles/contrast/diff.scss | 14 ++ app/javascript/styles/contrast/variables.scss | 22 +++ app/javascript/styles/mastodon/about.scss | 32 ++-- app/javascript/styles/mastodon/accounts.scss | 22 +-- app/javascript/styles/mastodon/admin.scss | 26 +-- .../styles/mastodon/compact_header.scss | 4 +- .../styles/mastodon/components.scss | 150 +++++++++--------- .../styles/mastodon/containers.scss | 2 +- .../styles/mastodon/emoji_picker.scss | 4 +- app/javascript/styles/mastodon/forms.scss | 10 +- .../styles/mastodon/landing_strip.scss | 2 +- .../styles/mastodon/stream_entries.scss | 14 +- app/javascript/styles/mastodon/variables.scss | 20 ++- config/locales/en.yml | 1 + config/themes.yml | 1 + 16 files changed, 187 insertions(+), 140 deletions(-) create mode 100644 app/javascript/styles/contrast.scss create mode 100644 app/javascript/styles/contrast/diff.scss create mode 100644 app/javascript/styles/contrast/variables.scss diff --git a/app/javascript/styles/contrast.scss b/app/javascript/styles/contrast.scss new file mode 100644 index 0000000000..5b43aecbe7 --- /dev/null +++ b/app/javascript/styles/contrast.scss @@ -0,0 +1,3 @@ +@import 'contrast/variables'; +@import 'application'; +@import 'contrast/diff'; diff --git a/app/javascript/styles/contrast/diff.scss b/app/javascript/styles/contrast/diff.scss new file mode 100644 index 0000000000..eee9ecc3ef --- /dev/null +++ b/app/javascript/styles/contrast/diff.scss @@ -0,0 +1,14 @@ +// components.scss +.compose-form { + .compose-form__modifiers { + .compose-form__upload { + &-description { + input { + &::placeholder { + opacity: 1.0; + } + } + } + } + } +} diff --git a/app/javascript/styles/contrast/variables.scss b/app/javascript/styles/contrast/variables.scss new file mode 100644 index 0000000000..35d11060eb --- /dev/null +++ b/app/javascript/styles/contrast/variables.scss @@ -0,0 +1,22 @@ +// Dependent colors +$black: #000000; + +$classic-base-color: #282c37; +$classic-primary-color: #9baec8; +$classic-secondary-color: #d9e1e8; + +$ui-base-color: $classic-base-color !default; +$ui-primary-color: $classic-primary-color !default; +$ui-secondary-color: $classic-secondary-color !default; + +// Differences +$ui-highlight-color: #2b5fd9; + +$darker-text-color: lighten($ui-primary-color, 20%) !default; +$dark-text-color: lighten($ui-primary-color, 12%) !default; +$secondary-text-color: lighten($ui-secondary-color, 6%) !default; +$action-button-color: #8d9ac2; + +$inverted-text-color: $black !default; +$lighter-text-color: darken($ui-base-color,6%) !default; +$light-text-color: darken($ui-primary-color, 40%) !default; diff --git a/app/javascript/styles/mastodon/about.scss b/app/javascript/styles/mastodon/about.scss index 0a09a38d24..c9c0e3081a 100644 --- a/app/javascript/styles/mastodon/about.scss +++ b/app/javascript/styles/mastodon/about.scss @@ -225,7 +225,7 @@ $small-breakpoint: 960px; font-family: inherit; font-size: inherit; line-height: inherit; - color: transparentize($darker-text-color, 0.1); + color: lighten($darker-text-color, 10%); } h1 { @@ -234,14 +234,14 @@ $small-breakpoint: 960px; line-height: 30px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; small { font-family: 'mastodon-font-sans-serif', sans-serif; display: block; font-size: 18px; font-weight: 400; - color: opacify($darker-text-color, 0.1); + color: lighten($darker-text-color, 10%); } } @@ -251,7 +251,7 @@ $small-breakpoint: 960px; line-height: 26px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; } h3 { @@ -260,7 +260,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; } h4 { @@ -269,7 +269,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; } h5 { @@ -278,7 +278,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; } h6 { @@ -287,7 +287,7 @@ $small-breakpoint: 960px; line-height: 24px; font-weight: 500; margin-bottom: 20px; - color: $primary-text-color; + color: $secondary-text-color; } ul, @@ -405,7 +405,7 @@ $small-breakpoint: 960px; font-size: 14px; &:hover { - color: $darker-text-color; + color: $secondary-text-color; } } @@ -517,7 +517,7 @@ $small-breakpoint: 960px; span { &:last-child { - color: $darker-text-color; + color: $secondary-text-color; } } @@ -559,7 +559,7 @@ $small-breakpoint: 960px; a, span { font-weight: 400; - color: opacify($darker-text-color, 0.1); + color: darken($darker-text-color, 10%); } a { @@ -775,7 +775,7 @@ $small-breakpoint: 960px; } p a { - color: $darker-text-color; + color: $secondary-text-color; } h1 { @@ -787,7 +787,7 @@ $small-breakpoint: 960px; color: $darker-text-color; span { - color: $darker-text-color; + color: $secondary-text-color; } } } @@ -896,7 +896,7 @@ $small-breakpoint: 960px; } a { - color: $darker-text-color; + color: $secondary-text-color; text-decoration: none; } } @@ -980,7 +980,7 @@ $small-breakpoint: 960px; .footer-links { padding-bottom: 50px; text-align: right; - color: $darker-text-color; + color: $dark-text-color; p { font-size: 14px; @@ -995,7 +995,7 @@ $small-breakpoint: 960px; &__footer { margin-top: 10px; text-align: center; - color: $darker-text-color; + color: $dark-text-color; p { font-size: 14px; diff --git a/app/javascript/styles/mastodon/accounts.scss b/app/javascript/styles/mastodon/accounts.scss index f9af6f2888..c2d0de4b99 100644 --- a/app/javascript/styles/mastodon/accounts.scss +++ b/app/javascript/styles/mastodon/accounts.scss @@ -178,7 +178,7 @@ font-size: 14px; line-height: 18px; padding: 0 15px; - color: $darker-text-color; + color: $secondary-text-color; } @media screen and (max-width: 480px) { @@ -256,7 +256,7 @@ .current { background: $simple-background-color; border-radius: 100px; - color: $lighter-text-color; + color: $inverted-text-color; cursor: default; margin: 0 10px; } @@ -268,7 +268,7 @@ .older, .newer { text-transform: uppercase; - color: $primary-text-color; + color: $secondary-text-color; } .older { @@ -293,7 +293,7 @@ .disabled { cursor: default; - color: opacify($lighter-text-color, 0.1); + color: lighten($inverted-text-color, 10%); } @media screen and (max-width: 700px) { @@ -332,7 +332,7 @@ width: 335px; background: $simple-background-color; border-radius: 4px; - color: $lighter-text-color; + color: $inverted-text-color; margin: 0 5px 10px; position: relative; @@ -344,7 +344,7 @@ overflow: hidden; height: 100px; border-radius: 4px 4px 0 0; - background-color: opacify($lighter-text-color, 0.04); + background-color: lighten($inverted-text-color, 4%); background-size: cover; background-position: center; position: relative; @@ -422,7 +422,7 @@ .account__header__content { padding: 10px 15px; padding-top: 15px; - color: transparentize($lighter-text-color, 0.1); + color: $lighter-text-color; word-wrap: break-word; overflow: hidden; text-overflow: ellipsis; @@ -434,7 +434,7 @@ .nothing-here { width: 100%; display: block; - color: $lighter-text-color; + color: $light-text-color; font-size: 14px; font-weight: 500; text-align: center; @@ -493,7 +493,7 @@ span { font-size: 14px; - color: $inverted-text-color; + color: $light-text-color; } } @@ -508,7 +508,7 @@ .account__header__content { font-size: 14px; - color: $darker-text-color; + color: $inverted-text-color; } } @@ -586,7 +586,7 @@ font-weight: 500; text-align: center; width: 94px; - color: opacify($darker-text-color, 0.1); + color: $secondary-text-color; background: rgba(darken($ui-base-color, 8%), 0.5); } diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index a0f69449a7..a6cc8b62ba 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -90,7 +90,7 @@ padding-left: 25px; h2 { - color: $primary-text-color; + color: $secondary-text-color; font-size: 24px; line-height: 28px; font-weight: 400; @@ -98,7 +98,7 @@ } h3 { - color: $primary-text-color; + color: $secondary-text-color; font-size: 20px; line-height: 28px; font-weight: 400; @@ -109,7 +109,7 @@ text-transform: uppercase; font-size: 13px; font-weight: 500; - color: $primary-text-color; + color: $darker-text-color; padding-bottom: 8px; margin-bottom: 8px; border-bottom: 1px solid lighten($ui-base-color, 8%); @@ -117,7 +117,7 @@ h6 { font-size: 16px; - color: $primary-text-color; + color: $secondary-text-color; line-height: 28px; font-weight: 400; } @@ -125,7 +125,7 @@ & > p { font-size: 14px; line-height: 18px; - color: $darker-text-color; + color: $secondary-text-color; margin-bottom: 20px; strong { @@ -292,7 +292,7 @@ font-weight: 500; font-size: 14px; line-height: 18px; - color: $primary-text-color; + color: $secondary-text-color; @each $lang in $cjk-langs { &:lang(#{$lang}) { @@ -420,7 +420,7 @@ } &__timestamp { - color: $darker-text-color; + color: $dark-text-color; } &__extras { @@ -437,7 +437,7 @@ &__icon { font-size: 28px; margin-right: 10px; - color: $darker-text-color; + color: $dark-text-color; } &__icon__overlay { @@ -464,7 +464,7 @@ a, .username, .target { - color: $primary-text-color; + color: $secondary-text-color; text-decoration: none; font-weight: 500; } @@ -474,7 +474,7 @@ } .diff-neutral { - color: $darker-text-color; + color: $secondary-text-color; } .diff-new { @@ -487,7 +487,7 @@ a.name-tag, display: flex; align-items: center; text-decoration: none; - color: $ui-secondary-color; + color: $secondary-text-color; .avatar { display: block; @@ -535,7 +535,7 @@ a.name-tag, font-weight: 500; a { - color: $ui-primary-color; + color: $darker-text-color; } } @@ -545,6 +545,6 @@ a.name-tag, } time { - color: $darker-text-color; + color: $dark-text-color; } } diff --git a/app/javascript/styles/mastodon/compact_header.scss b/app/javascript/styles/mastodon/compact_header.scss index 83ac7a8d04..4980ab5f1a 100644 --- a/app/javascript/styles/mastodon/compact_header.scss +++ b/app/javascript/styles/mastodon/compact_header.scss @@ -2,7 +2,7 @@ h1 { font-size: 24px; line-height: 28px; - color: $primary-text-color; + color: $darker-text-color; font-weight: 500; margin-bottom: 20px; padding: 0 10px; @@ -20,7 +20,7 @@ small { font-weight: 400; - color: $darker-text-color; + color: $secondary-text-color; } img { diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index 908fa8a072..1d0a7e9455 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -31,7 +31,7 @@ &:active, &:focus, &:hover { - background-color: lighten($ui-highlight-color, 4%); + background-color: lighten($ui-highlight-color, 10%); transition: all 200ms ease-out; } @@ -83,7 +83,7 @@ } &.button-secondary { - color: $ui-primary-color; + color: $darker-text-color; background: transparent; padding: 3px 15px; border: 1px solid $ui-primary-color; @@ -92,7 +92,7 @@ &:focus, &:hover { border-color: lighten($ui-primary-color, 4%); - color: lighten($ui-primary-color, 4%); + color: lighten($darker-text-color, 4%); } } @@ -149,18 +149,18 @@ &:hover, &:active, &:focus { - color: transparentize($lighter-text-color, 0.07); + color: darken($lighter-text-color, 7%); } &.disabled { - color: opacify($lighter-text-color, 0.07); + color: lighten($lighter-text-color, 7%); } &.active { color: $highlight-text-color; &.disabled { - color: opacify($lighter-text-color, 0.13); + color: lighten($highlight-text-color, 13%); } } } @@ -193,12 +193,12 @@ &:hover, &:active, &:focus { - color: opacify($lighter-text-color, 0.07); + color: darken($lighter-text-color, 7%); transition: color 200ms ease-out; } &.disabled { - color: transparentize($lighter-text-color, 0.2); + color: lighten($lighter-text-color, 20%); cursor: default; } @@ -349,7 +349,7 @@ box-shadow: 4px 4px 6px rgba($base-shadow-color, 0.4); background: $ui-secondary-color; border-radius: 0 0 4px 4px; - color: $lighter-text-color; + color: $inverted-text-color; font-size: 14px; padding: 6px; @@ -457,7 +457,7 @@ input { background: transparent; - color: $primary-text-color; + color: $secondary-text-color; border: 0; padding: 0; margin: 0; @@ -471,8 +471,8 @@ } &::placeholder { - opacity: 0.54; - color: $darker-text-color; + opacity: 0.75; + color: $secondary-text-color; } } @@ -588,7 +588,7 @@ } .reply-indicator__display-name { - color: $lighter-text-color; + color: $inverted-text-color; display: block; max-width: 100%; line-height: 24px; @@ -643,14 +643,14 @@ } a { - color: $ui-secondary-color; + color: $secondary-text-color; text-decoration: none; &:hover { text-decoration: underline; .fa { - color: lighten($action-button-color, 7%); + color: lighten($dark-text-color, 7%); } } @@ -665,7 +665,7 @@ } .fa { - color: $action-button-color; + color: $dark-text-color; } } @@ -769,7 +769,7 @@ &.light { .status__relative-time { - color: $lighter-text-color; + color: $light-text-color; } .status__display-name { @@ -782,7 +782,7 @@ } span { - color: $lighter-text-color; + color: $light-text-color; } } @@ -816,13 +816,13 @@ } .status__relative-time { - color: $darker-text-color; + color: $dark-text-color; float: right; font-size: 14px; } .status__display-name { - color: $darker-text-color; + color: $dark-text-color; } .status__info .status__display-name { @@ -873,14 +873,14 @@ .status__prepend { margin-left: 68px; - color: $darker-text-color; + color: $dark-text-color; padding: 8px 0; padding-bottom: 2px; font-size: 14px; position: relative; .status__display-name strong { - color: $darker-text-color; + color: $dark-text-color; } > span { @@ -942,7 +942,7 @@ .detailed-status__meta { margin-top: 15px; - color: $darker-text-color; + color: $dark-text-color; font-size: 14px; line-height: 18px; } @@ -1091,7 +1091,7 @@ a .account__avatar { } .account__header__username { - color: $darker-text-color; + color: $secondary-text-color; } } @@ -1101,7 +1101,7 @@ a .account__avatar { } .account__header__content { - color: $darker-text-color; + color: $secondary-text-color; } .account__header__display-name { @@ -1129,7 +1129,7 @@ a .account__avatar { .account__disclaimer { padding: 10px; border-top: 1px solid lighten($ui-base-color, 8%); - color: $darker-text-color; + color: $dark-text-color; strong { font-weight: 500; @@ -1316,7 +1316,7 @@ a.account__display-name { } .detailed-status__display-name { - color: $darker-text-color; + color: $secondary-text-color; display: block; line-height: 24px; margin-bottom: 15px; @@ -1351,11 +1351,11 @@ a.account__display-name { .muted { .status__content p, .status__content a { - color: $darker-text-color; + color: $dark-text-color; } .status__display-name strong { - color: $darker-text-color; + color: $dark-text-color; } .status__avatar { @@ -1363,11 +1363,11 @@ a.account__display-name { } a.status__content__spoiler-link { - background: $darker-text-color; - color: lighten($ui-base-color, 4%); + background: $ui-base-lighter-color; + color: $inverted-text-color; &:hover { - background: transparentize($darker-text-color, 0.07); + background: lighten($ui-base-lighter-color, 7%); text-decoration: none; } } @@ -1378,7 +1378,7 @@ a.account__display-name { padding: 8px 0; padding-bottom: 0; cursor: default; - color: $ui-primary-color; + color: $darker-text-color; font-size: 15px; position: relative; @@ -1489,7 +1489,7 @@ a.account__display-name { color: $darker-text-color; strong { - color: $primary-text-color; + color: $secondary-text-color; } a { @@ -1603,7 +1603,7 @@ a.account__display-name { &:hover, &:active { background: $ui-highlight-color; - color: $primary-text-color; + color: $secondary-text-color; outline: 0; } } @@ -1656,7 +1656,7 @@ a.account__display-name { &:hover { background: $ui-highlight-color; - color: $primary-text-color; + color: $secondary-text-color; } } } @@ -1668,7 +1668,7 @@ a.account__display-name { .static-content { padding: 10px; padding-top: 20px; - color: $darker-text-color; + color: $dark-text-color; h1 { font-size: 16px; @@ -1755,7 +1755,7 @@ a.account__display-name { display: block; flex: 1 1 auto; padding: 15px 5px 13px; - color: $ui-primary-color; + color: $darker-text-color; text-decoration: none; text-align: center; font-size: 16px; @@ -2167,7 +2167,7 @@ a.account__display-name { .column-subheading { background: $ui-base-color; - color: $darker-text-color; + color: $dark-text-color; padding: 8px 20px; font-size: 12px; font-weight: 500; @@ -2190,11 +2190,11 @@ a.account__display-name { flex: 1 0 auto; p { - color: $darker-text-color; + color: $secondary-text-color; } a { - color: opacify($darker-text-color, 0.07); + color: $dark-text-color; } } @@ -2275,7 +2275,7 @@ a.account__display-name { font-size: 14px; border: 1px solid lighten($ui-base-color, 8%); border-radius: 4px; - color: $darker-text-color; + color: $dark-text-color; margin-top: 14px; text-decoration: none; overflow: hidden; @@ -2355,7 +2355,7 @@ a.status-card { display: block; font-weight: 500; margin-bottom: 5px; - color: $ui-primary-color; + color: $darker-text-color; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -2369,7 +2369,7 @@ a.status-card { } .status-card__description { - color: $ui-primary-color; + color: $darker-text-color; } .status-card__host { @@ -2413,7 +2413,7 @@ a.status-card { .load-more { display: block; - color: $darker-text-color; + color: $dark-text-color; background-color: transparent; border: 0; font-size: inherit; @@ -2437,7 +2437,7 @@ a.status-card { text-align: center; font-size: 16px; font-weight: 500; - color: opacify($darker-text-color, 0.07); + color: $dark-text-color; background: $ui-base-color; cursor: default; display: flex; @@ -2477,7 +2477,7 @@ a.status-card { strong { display: block; margin-bottom: 10px; - color: $darker-text-color; + color: $dark-text-color; } span { @@ -2565,13 +2565,13 @@ a.status-card { .column-header__button { background: lighten($ui-base-color, 4%); border: 0; - color: $ui-primary-color; + color: $darker-text-color; cursor: pointer; font-size: 16px; padding: 0 15px; &:hover { - color: lighten($ui-primary-color, 7%); + color: lighten($darker-text-color, 7%); } &.active { @@ -2652,7 +2652,7 @@ a.status-card { } .loading-indicator { - color: $darker-text-color; + color: $dark-text-color; font-size: 12px; font-weight: 400; text-transform: uppercase; @@ -2749,7 +2749,7 @@ a.status-card { &:active, &:focus { padding: 0; - color: transparentize($darker-text-color, 0.07); + color: lighten($darker-text-color, 8%); } } @@ -2873,7 +2873,7 @@ a.status-card { .empty-column-indicator, .error-column { - color: $darker-text-color; + color: $dark-text-color; background: $ui-base-color; text-align: center; padding: 20px; @@ -3075,7 +3075,7 @@ a.status-card { display: flex; align-items: center; justify-content: center; - color: $primary-text-color; + color: $secondary-text-color; font-size: 18px; font-weight: 500; border: 2px dashed $ui-base-lighter-color; @@ -3173,7 +3173,7 @@ a.status-card { } .privacy-dropdown__option { - color: $lighter-text-color; + color: $inverted-text-color; padding: 10px; cursor: pointer; display: flex; @@ -3295,7 +3295,7 @@ a.status-card { font-size: 18px; width: 18px; height: 18px; - color: $ui-secondary-color; + color: $secondary-text-color; cursor: default; pointer-events: none; @@ -3331,7 +3331,7 @@ a.status-card { } .search-results__header { - color: $darker-text-color; + color: $dark-text-color; background: lighten($ui-base-color, 2%); border-bottom: 1px solid darken($ui-base-color, 4%); padding: 15px 10px; @@ -3379,13 +3379,13 @@ a.status-card { .search-results__hashtag { display: block; padding: 10px; - color: darken($primary-text-color, 4%); + color: $secondary-text-color; text-decoration: none; &:hover, &:active, &:focus { - color: $primary-text-color; + color: lighten($secondary-text-color, 4%); text-decoration: underline; } } @@ -3650,7 +3650,7 @@ a.status-card { &:hover, &:focus, &:active { - color: transparentize($lighter-text-color, 0.04); + color: darken($lighter-text-color, 4%); background-color: darken($ui-secondary-color, 16%); } @@ -3744,7 +3744,7 @@ a.status-card { strong { font-weight: 500; background: $ui-base-color; - color: $primary-text-color; + color: $secondary-text-color; border-radius: 4px; font-size: 14px; padding: 3px 6px; @@ -3804,7 +3804,7 @@ a.status-card { &__case { background: $ui-base-color; - color: $primary-text-color; + color: $secondary-text-color; font-weight: 500; padding: 10px; border-radius: 4px; @@ -3821,7 +3821,7 @@ a.status-card { .figure { background: darken($ui-base-color, 8%); - color: $darker-text-color; + color: $secondary-text-color; margin-bottom: 20px; border-radius: 4px; padding: 10px; @@ -3933,7 +3933,7 @@ a.status-card { } .status__content__spoiler-link { - color: lighten($ui-secondary-color, 8%); + color: lighten($secondary-text-color, 8%); } } @@ -4163,7 +4163,7 @@ a.status-card { &:hover, &:focus, &:active { - color: transparentize($lighter-text-color, 0.04); + color: darken($lighter-text-color, 4%); } } } @@ -4244,7 +4244,7 @@ a.status-card { &__icon { flex: 0 0 auto; - color: $darker-text-color; + color: $dark-text-color; padding: 8px 18px; cursor: default; border-right: 1px solid lighten($ui-base-color, 8%); @@ -4274,7 +4274,7 @@ a.status-card { a { text-decoration: none; - color: $darker-text-color; + color: $dark-text-color; font-weight: 500; &:hover { @@ -4293,7 +4293,7 @@ a.status-card { } .fa { - color: $darker-text-color; + color: $dark-text-color; } } } @@ -4329,7 +4329,7 @@ a.status-card { cursor: zoom-in; display: block; text-decoration: none; - color: $ui-secondary-color; + color: $secondary-text-color; line-height: 0; &, @@ -4500,7 +4500,7 @@ a.status-card { &:hover, &:active, &:focus { - color: transparentize($darker-text-color, 0.07); + color: lighten($darker-text-color, 7%); } } @@ -4705,7 +4705,7 @@ a.status-card { &:active, &:focus { outline: 0; - color: transparentize($darker-text-color, 0.07); + color: $secondary-text-color; &::before { content: ""; @@ -4745,7 +4745,7 @@ a.status-card { position: relative; &.active { - color: transparentize($darker-text-color, 0.07); + color: $secondary-text-color; &::before, &::after { @@ -4780,12 +4780,12 @@ a.status-card { padding: 10px 14px; padding-bottom: 14px; margin-top: 10px; - color: $lighter-text-color; + color: $light-text-color; box-shadow: 2px 4px 15px rgba($base-shadow-color, 0.4); h4 { text-transform: uppercase; - color: $lighter-text-color; + color: $light-text-color; font-size: 13px; font-weight: 500; margin-bottom: 10px; @@ -4817,7 +4817,7 @@ noscript { div { font-size: 14px; margin: 30px auto; - color: $primary-text-color; + color: $secondary-text-color; max-width: 400px; a { @@ -4970,7 +4970,7 @@ noscript { &__message { position: relative; margin-left: 58px; - color: $darker-text-color; + color: $dark-text-color; padding: 8px 0; padding-top: 0; padding-bottom: 4px; diff --git a/app/javascript/styles/mastodon/containers.scss b/app/javascript/styles/mastodon/containers.scss index 8df2902d23..9d5ab66a48 100644 --- a/app/javascript/styles/mastodon/containers.scss +++ b/app/javascript/styles/mastodon/containers.scss @@ -100,7 +100,7 @@ .name { flex: 1 1 auto; - color: $darker-text-color; + color: $secondary-text-color; width: calc(100% - 88px); .username { diff --git a/app/javascript/styles/mastodon/emoji_picker.scss b/app/javascript/styles/mastodon/emoji_picker.scss index 3620a6f54d..cf9547586d 100644 --- a/app/javascript/styles/mastodon/emoji_picker.scss +++ b/app/javascript/styles/mastodon/emoji_picker.scss @@ -50,7 +50,7 @@ cursor: pointer; &:hover { - color: opacify($lighter-text-color, 0.04); + color: darken($lighter-text-color, 4%); } } @@ -184,7 +184,7 @@ font-size: 14px; text-align: center; padding-top: 70px; - color: $lighter-text-color; + color: $light-text-color; .emoji-mart-category-label { display: none; diff --git a/app/javascript/styles/mastodon/forms.scss b/app/javascript/styles/mastodon/forms.scss index 3a3b4c3269..f978901870 100644 --- a/app/javascript/styles/mastodon/forms.scss +++ b/app/javascript/styles/mastodon/forms.scss @@ -248,7 +248,7 @@ code { } &:required:valid { - border-bottom-color: lighten($error-red, 12%); + border-bottom-color: $valid-value-color; } &:active, @@ -266,7 +266,7 @@ code { input[type=text], input[type=email], input[type=password] { - border-bottom-color: lighten($error-red, 12%); + border-bottom-color: $valid-value-color; } .error { @@ -356,7 +356,7 @@ code { padding: 7px 4px; padding-bottom: 9px; font-size: 16px; - color: $darker-text-color; + color: $dark-text-color; font-family: inherit; pointer-events: none; cursor: default; @@ -446,7 +446,7 @@ code { } strong { - color: $primary-text-color; + color: $secondary-text-color; font-weight: 500; @each $lang in $cjk-langs { @@ -483,7 +483,7 @@ code { .qr-alternative { margin-bottom: 20px; - color: $darker-text-color; + color: $secondary-text-color; flex: 150px; samp { diff --git a/app/javascript/styles/mastodon/landing_strip.scss b/app/javascript/styles/mastodon/landing_strip.scss index 651c06ced7..86614b89bc 100644 --- a/app/javascript/styles/mastodon/landing_strip.scss +++ b/app/javascript/styles/mastodon/landing_strip.scss @@ -45,7 +45,7 @@ padding: 14px; border-radius: 4px; background: rgba(darken($ui-base-color, 7%), 0.8); - color: $darker-text-color; + color: $secondary-text-color; font-weight: 400; margin-bottom: 20px; diff --git a/app/javascript/styles/mastodon/stream_entries.scss b/app/javascript/styles/mastodon/stream_entries.scss index c39163ba8d..281cbaf83a 100644 --- a/app/javascript/styles/mastodon/stream_entries.scss +++ b/app/javascript/styles/mastodon/stream_entries.scss @@ -93,7 +93,7 @@ display: block; max-width: 100%; padding-right: 25px; - color: $lighter-text-color; + color: $inverted-text-color; } .status__avatar { @@ -134,7 +134,7 @@ span { font-size: 14px; - color: $inverted-text-color; + color: $light-text-color; } } @@ -191,7 +191,7 @@ span { font-size: 14px; - color: $lighter-text-color; + color: $light-text-color; } } } @@ -225,7 +225,7 @@ .detailed-status__meta { margin-top: 15px; - color: $lighter-text-color; + color: $light-text-color; font-size: 14px; line-height: 18px; @@ -270,7 +270,7 @@ padding-left: (48px + 14px * 2); padding-bottom: 0; margin-bottom: -4px; - color: $lighter-text-color; + color: $light-text-color; font-size: 14px; position: relative; @@ -280,7 +280,7 @@ } .status__display-name.muted strong { - color: $lighter-text-color; + color: $light-text-color; } } @@ -293,7 +293,7 @@ } .more { - color: $classic-primary-color; + color: $darker-text-color; display: block; padding: 14px; text-align: center; diff --git a/app/javascript/styles/mastodon/variables.scss b/app/javascript/styles/mastodon/variables.scss index dc4e72a2ea..cbefe35b4d 100644 --- a/app/javascript/styles/mastodon/variables.scss +++ b/app/javascript/styles/mastodon/variables.scss @@ -17,12 +17,6 @@ $base-shadow-color: $black !default; $base-overlay-background: $black !default; $base-border-color: $white !default; $simple-background-color: $white !default; -$primary-text-color: $white !default; -$darker-text-color: rgba($primary-text-color, 0.7) !default; -$highlight-text-color: $classic-highlight-color !default; -$inverted-text-color: $black !default; -$lighter-text-color: rgba($inverted-text-color, 0.7) !default; -$action-button-color: #8d9ac2; $valid-value-color: $success-green !default; $error-value-color: $error-red !default; @@ -31,7 +25,19 @@ $ui-base-color: $classic-base-color !default; // Darkest $ui-base-lighter-color: lighten($ui-base-color, 26%) !default; // Lighter darkest $ui-primary-color: $classic-primary-color !default; // Lighter $ui-secondary-color: $classic-secondary-color !default; // Lightest -$ui-highlight-color: #2b5fd9; +$ui-highlight-color: $classic-highlight-color !default; + +// Variables for texts +$primary-text-color: $white !default; +$darker-text-color: $ui-primary-color !default; +$dark-text-color: $ui-base-lighter-color !default; +$secondary-text-color: $ui-secondary-color !default; +$highlight-text-color: $ui-highlight-color !default; +$action-button-color: $ui-base-lighter-color !default; +// For texts on inverted backgrounds +$inverted-text-color: $ui-base-color !default; +$lighter-text-color: $ui-base-lighter-color !default; +$light-text-color: $ui-primary-color !default; // Language codes that uses CJK fonts $cjk-langs: ja, ko, zh-CN, zh-HK, zh-TW; diff --git a/config/locales/en.yml b/config/locales/en.yml index 53b64a1003..8b66b91ec4 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -785,6 +785,7 @@ en:

Originally adapted from the Discourse privacy policy.

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

Privacy Policy

+

Quina informació recollim?

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

Per a què utilitzem la teva informació?

+ +

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

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

Com protegim la teva informació

+ +

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

+ +
+ +

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

+ +

Farem un esforç de bona fe per:

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

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

+ +

Pots eliminar el compte de forma irreversible en qualsevol moment.

+ +
+ +

Utilitzem cookies?

+ +

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

+ +

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

+ +
+ +

Revelem informació a terceres parts?

+ +

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

+ +

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

+ +

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

+ +
+ +

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

+ +

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

+ +
+ +

Canvis a la nostra política de privacitat

+ +

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

+ +

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

+ +

Originalment adaptat des del Discourse privacy policy.

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

Privacy Policy

+

What information do we collect?

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

What do we use your information for?

+ +

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

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

How do we protect your information?

+ +

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

+ +
+ +

What is our data retention policy?

+ +

We will make a good faith effort to:

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

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

+ +

You may irreversibly delete your account at any time.

+ +
+ +

Do we use cookies?

+ +

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

+ +

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

+ +
+ +

Do we disclose any information to outside parties?

+ +

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

+ +

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

+ +

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

+ +
+ +

Children's Online Privacy Protection Act Compliance

+ +

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

+ +
+ +

Changes to our Privacy Policy

+ +

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

+ +

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

+ +

Originally adapted from the Discourse privacy policy.

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

En bra plats för regler

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

@bob

' + is_expected.to eq '

@bob

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

Originalment adaptat des del Discourse privacy policy.

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

Intimidade

+

Qué información recollemos?

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

De qué xeito utilizamos os seus datos?

+ +

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

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

Cómo proxetemos os seus datos?

+ +

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

+ +
+ +

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

+ +

Faremos un sincero esforzo en:

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

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

+ +

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

+ +
+ +

Utilizamos testemuños?

+ +

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

+ +

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

+ +
+ +

Entregamos algunha información a terceiras alleas?

+ +

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

+ +

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

+ +

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

+ +
+ +

Children's Online Privacy Protection Act Compliance

+ +

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

+ +
+ +

Cambios na nosa política de intimidade

+ +

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

+ +

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

+ +

Adaptado do orixinal Discourse privacy policy.

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

Originally adapted from the Discourse privacy policy.

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

Bazowano na polityce prywatności Discourse.

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