From 5d710b1139b34f2ed6bd556f448fa7248354e758 Mon Sep 17 00:00:00 2001 From: Eugen Date: Sun, 16 Apr 2017 12:51:30 +0200 Subject: [PATCH 01/25] Make file attachment on MediaAttachment optional (#1865) Create MediaAttachment but without actual file download when domain is blocked with reject_media set to true Clean up old media files when creating a new domain block with reject_media set to true Return remote_url in media attachments API if local file is not present Undo domain block action in admin UI Ability to enable reject_media from admin UI --- .../admin/domain_blocks_controller.rb | 14 ++++++-- app/controllers/application_controller.rb | 4 ++- app/models/domain_block.rb | 2 ++ app/services/block_domain_service.rb | 32 ++++++++++++++++--- app/services/process_feed_service.rb | 10 ++++-- app/services/suspend_account_service.rb | 3 +- app/services/unblock_domain_service.rb | 15 +++++++++ app/views/admin/domain_blocks/index.html.haml | 9 +++++- app/views/admin/domain_blocks/new.html.haml | 3 ++ app/views/admin/domain_blocks/show.html.haml | 9 ++++++ app/views/api/v1/statuses/_media.rabl | 4 +-- config/locales/en.yml | 16 ++++++++++ config/routes.rb | 2 +- .../stream_entries/show.html.haml_spec.rb | 1 - 14 files changed, 106 insertions(+), 18 deletions(-) create mode 100644 app/services/unblock_domain_service.rb create mode 100644 app/views/admin/domain_blocks/show.html.haml diff --git a/app/controllers/admin/domain_blocks_controller.rb b/app/controllers/admin/domain_blocks_controller.rb index a8b56c085..5d146d946 100644 --- a/app/controllers/admin/domain_blocks_controller.rb +++ b/app/controllers/admin/domain_blocks_controller.rb @@ -15,16 +15,26 @@ module Admin if @domain_block.save DomainBlockWorker.perform_async(@domain_block.id) - redirect_to admin_domain_blocks_path, notice: 'Domain block is now being processed' + redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_block.created_msg') else render action: :new end end + def show + @domain_block = DomainBlock.find(params[:id]) + end + + def destroy + @domain_block = DomainBlock.find(params[:id]) + UnblockDomainService.new.call(@domain_block, resource_params[:retroactive]) + redirect_to admin_domain_blocks_path, notice: I18n.t('admin.domain_block.destroyed_msg') + end + private def resource_params - params.require(:domain_block).permit(:domain, :severity) + params.require(:domain_block).permit(:domain, :severity, :reject_media, :retroactive) end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0c320177d..e8d7de218 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -8,7 +8,9 @@ class ApplicationController < ActionController::Base force_ssl if: "Rails.env.production? && ENV['LOCAL_HTTPS'] == 'true'" include Localized - helper_method :current_account, :single_user_mode? + + helper_method :current_account + helper_method :single_user_mode? rescue_from ActionController::RoutingError, with: :not_found rescue_from ActiveRecord::RecordNotFound, with: :not_found diff --git a/app/models/domain_block.rb b/app/models/domain_block.rb index 3548ccd69..89c81f766 100644 --- a/app/models/domain_block.rb +++ b/app/models/domain_block.rb @@ -3,6 +3,8 @@ class DomainBlock < ApplicationRecord enum severity: [:silence, :suspend] + attr_accessor :retroactive + validates :domain, presence: true, uniqueness: true def self.blocked?(domain) diff --git a/app/services/block_domain_service.rb b/app/services/block_domain_service.rb index 6c131bd34..97d2ebcd7 100644 --- a/app/services/block_domain_service.rb +++ b/app/services/block_domain_service.rb @@ -3,12 +3,34 @@ class BlockDomainService < BaseService def call(domain_block) if domain_block.silence? - Account.where(domain: domain_block.domain).update_all(silenced: true) + silence_accounts!(domain_block.domain) + clear_media!(domain_block.domain) if domain_block.reject_media? else - Account.where(domain: domain_block.domain).find_each do |account| - account.subscription(api_subscription_url(account.id)).unsubscribe if account.subscribed? - SuspendAccountService.new.call(account) - end + suspend_accounts!(domain_block.domain) + end + end + + private + + def silence_accounts!(domain) + Account.where(domain: domain).update_all(silenced: true) + end + + def clear_media!(domain) + Account.where(domain: domain).find_each do |account| + account.avatar.destroy + account.header.destroy + end + + MediaAttachment.where(account: Account.where(domain: domain)).find_each do |attachment| + attachment.file.destroy + end + end + + def suspend_accounts!(domain) + Account.where(domain: domain).where(suspended: false).find_each do |account| + account.subscription(api_subscription_url(account.id)).unsubscribe if account.subscribed? + SuspendAccountService.new.call(account) end end end diff --git a/app/services/process_feed_service.rb b/app/services/process_feed_service.rb index 321f53f22..fa0633b27 100644 --- a/app/services/process_feed_service.rb +++ b/app/services/process_feed_service.rb @@ -179,12 +179,12 @@ class ProcessFeedService < BaseService end def hashtags_from_xml(parent, xml) - tags = xml.xpath('./xmlns:category', xmlns: TagManager::XMLNS).map { |category| category['term'] }.select { |t| !t.blank? } + tags = xml.xpath('./xmlns:category', xmlns: TagManager::XMLNS).map { |category| category['term'] }.select(&:present?) ProcessHashtagsService.new.call(parent, tags) end def media_from_xml(parent, xml) - return if DomainBlock.find_by(domain: parent.account.domain)&.reject_media? + do_not_download = DomainBlock.find_by(domain: parent.account.domain)&.reject_media? xml.xpath('./xmlns:link[@rel="enclosure"]', xmlns: TagManager::XMLNS).each do |link| next unless link['href'] @@ -192,7 +192,11 @@ class ProcessFeedService < BaseService media = MediaAttachment.where(status: parent, remote_url: link['href']).first_or_initialize(account: parent.account, status: parent, remote_url: link['href']) parsed_url = URI.parse(link['href']) - next if !%w(http https).include?(parsed_url.scheme) || parsed_url.host.empty? + next if !%w[http https].include?(parsed_url.scheme) || parsed_url.host.empty? + + media.save + + next if do_not_download begin media.file_remote_url = link['href'] diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb index 42ff4dcb7..66517470e 100644 --- a/app/services/suspend_account_service.rb +++ b/app/services/suspend_account_service.rb @@ -13,6 +13,7 @@ class SuspendAccountService < BaseService def purge_content @account.statuses.reorder(nil).find_each do |status| + # This federates out deletes to previous followers RemoveStatusService.new.call(status) end @@ -29,9 +30,7 @@ class SuspendAccountService < BaseService @account.display_name = '' @account.note = '' @account.avatar.destroy - @account.avatar.clear @account.header.destroy - @account.header.clear @account.save! end diff --git a/app/services/unblock_domain_service.rb b/app/services/unblock_domain_service.rb new file mode 100644 index 000000000..9794e439d --- /dev/null +++ b/app/services/unblock_domain_service.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +class UnblockDomainService < BaseService + def call(domain_block, retroactive) + if retroactive + if domain_block.silence? + Account.where(domain: domain_block.domain).update_all(silenced: false) + else + Account.where(domain: domain_block.domain).update_all(suspended: false) + end + end + + domain_block.destroy + end +end diff --git a/app/views/admin/domain_blocks/index.html.haml b/app/views/admin/domain_blocks/index.html.haml index 6f4ba9b57..da9a07bbc 100644 --- a/app/views/admin/domain_blocks/index.html.haml +++ b/app/views/admin/domain_blocks/index.html.haml @@ -6,12 +6,19 @@ %tr %th= t('admin.domain_block.domain') %th= t('admin.domain_block.severity') + %th= t('admin.domain_block.reject_media') + %th %tbody - @blocks.each do |block| %tr %td %samp= block.domain - %td= block.severity + %td= t("admin.domain_block.severities.#{block.severity}") + %td + - if block.reject_media? || block.suspend? + %i.fa.fa-check + %td + = table_link_to 'undo', t('admin.domain_block.undo'), admin_domain_block_path(block) = paginate @blocks = link_to t('admin.domain_block.add_new'), new_admin_domain_block_path, class: 'button' diff --git a/app/views/admin/domain_blocks/new.html.haml b/app/views/admin/domain_blocks/new.html.haml index 53aab21ff..603faeb55 100644 --- a/app/views/admin/domain_blocks/new.html.haml +++ b/app/views/admin/domain_blocks/new.html.haml @@ -10,5 +10,8 @@ = f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false, label_method: lambda { |type| I18n.t("admin.domain_block.new.severity.#{type}") } %p.hint= t('admin.domain_block.new.severity.desc_html') + + = f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_block.reject_media'), hint: I18n.t('admin.domain_block.reject_media_hint') + .actions = f.button :button, t('admin.domain_block.new.create'), type: :submit diff --git a/app/views/admin/domain_blocks/show.html.haml b/app/views/admin/domain_blocks/show.html.haml new file mode 100644 index 000000000..bf9011c52 --- /dev/null +++ b/app/views/admin/domain_blocks/show.html.haml @@ -0,0 +1,9 @@ +- content_for :page_title do + = t('admin.domain_block.show.title', domain: @domain_block.domain) + += simple_form_for @domain_block, url: admin_domain_block_path(@domain_block), method: :delete do |f| + + = f.input :retroactive, as: :boolean, wrapper: :with_label, label: I18n.t("admin.domain_block.show.retroactive.#{@domain_block.severity}"), hint: I18n.t('admin.domain_block.show.affected_accounts', count: Account.where(domain: @domain_block.domain).count) + + .actions + = f.button :button, t('admin.domain_block.show.undo'), type: :submit diff --git a/app/views/api/v1/statuses/_media.rabl b/app/views/api/v1/statuses/_media.rabl index 2f56c6d07..80d80ea05 100644 --- a/app/views/api/v1/statuses/_media.rabl +++ b/app/views/api/v1/statuses/_media.rabl @@ -1,5 +1,5 @@ attributes :id, :remote_url, :type -node(:url) { |media| full_asset_url(media.file.url(:original)) } -node(:preview_url) { |media| full_asset_url(media.file.url(:small)) } +node(:url) { |media| media.file.blank? ? media.remote_url : full_asset_url(media.file.url(:original)) } +node(:preview_url) { |media| media.file.blank? ? media.remote_url : full_asset_url(media.file.url(:small)) } node(:text_url) { |media| media.local? ? medium_url(media) : nil } diff --git a/config/locales/en.yml b/config/locales/en.yml index 474de3985..325df5045 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -81,6 +81,8 @@ en: web: Web domain_block: add_new: Add new + created_msg: Domain block is now being processed + destroyed_msg: Domain block has been undone domain: Domain new: create: Create block @@ -90,8 +92,22 @@ en: silence: Silence suspend: Suspend title: New domain block + reject_media: Reject media files + reject_media_hint: Removes locally stored media files and refuses to download any in the future. Irrelevant for suspensions + severities: + silence: Silence + suspend: Suspend severity: Severity + show: + affected_accounts: + one: One account in the database affected + other: "%{count} accounts in the database affected" + retroactive: + silence: Unsilence all existing accounts from this domain + suspend: Unsuspend all existing accounts from this domain + title: Undo domain block for %{domain} title: Domain Blocks + undo: Undo pubsubhubbub: callback_url: Callback URL confirmed: Confirmed diff --git a/config/routes.rb b/config/routes.rb index 31909a4f4..fd186c320 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -78,7 +78,7 @@ Rails.application.routes.draw do namespace :admin do resources :pubsubhubbub, only: [:index] - resources :domain_blocks, only: [:index, :new, :create] + resources :domain_blocks, only: [:index, :new, :create, :show, :destroy] resources :settings, only: [:index, :update] resources :reports, only: [:index, :show, :update] do diff --git a/spec/views/stream_entries/show.html.haml_spec.rb b/spec/views/stream_entries/show.html.haml_spec.rb index 5526d6780..acc0c39f5 100644 --- a/spec/views/stream_entries/show.html.haml_spec.rb +++ b/spec/views/stream_entries/show.html.haml_spec.rb @@ -61,5 +61,4 @@ describe 'stream_entries/show.html.haml' do expect(mf2.entry.in_reply_to.format.author.format.name.to_s).to eq alice.display_name expect(mf2.entry.in_reply_to.format.author.format.url.to_s).not_to be_empty end - end From 865cb39e9b27caf46c7592a5a36918afdf5e4104 Mon Sep 17 00:00:00 2001 From: Alex Dunn Date: Sun, 16 Apr 2017 03:52:18 -0700 Subject: [PATCH 02/25] lock capistrano to 3.8.0 (#1890) --- Gemfile | 2 +- Gemfile.lock | 4 ++-- config/deploy.rb | 5 ++++- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 57600ed9f..5d75c11ea 100644 --- a/Gemfile +++ b/Gemfile @@ -88,7 +88,7 @@ group :development do gem 'bullet' gem 'active_record_query_trace' - gem 'capistrano' + gem 'capistrano', '3.8.0' gem 'capistrano-rails' gem 'capistrano-rbenv' gem 'capistrano-yarn' diff --git a/Gemfile.lock b/Gemfile.lock index 2f55abec7..4b4dd105c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,7 +41,7 @@ GEM tzinfo (~> 1.1) addressable (2.5.1) public_suffix (~> 2.0, >= 2.0.2) - airbrussh (1.1.2) + airbrussh (1.2.0) sshkit (>= 1.6.1, != 1.7.0) arel (7.1.4) ast (2.3.0) @@ -469,7 +469,7 @@ DEPENDENCIES binding_of_caller browserify-rails bullet - capistrano + capistrano (= 3.8.0) capistrano-faster-assets (~> 1.0) capistrano-rails capistrano-rbenv diff --git a/config/deploy.rb b/config/deploy.rb index 9bcf907be..b1cade49d 100644 --- a/config/deploy.rb +++ b/config/deploy.rb @@ -1,4 +1,7 @@ -lock '3.7.2' +# frozen_string_literal: true + +lock '3.8.0' + set :repo_url, ENV.fetch('REPO', 'https://github.com/tootsuite/mastodon.git') set :branch, ENV.fetch('BRANCH', 'master') From e17f9d5e1ad63dc61085b4288f349f8a6f75361d Mon Sep 17 00:00:00 2001 From: Eugen Date: Sun, 16 Apr 2017 12:53:58 +0200 Subject: [PATCH 03/25] Unite all mandatory rake tasks in mastodon:daily (#1887) * Unite all mandatory rake tasks in mastodon:daily Add mastodon:media:remove_remote task Make mastodon:maintenance:add_static_avatars more resilient to exceptions * Fix typo in task description --- lib/tasks/mastodon.rake | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 54980634d..b47730274 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -1,6 +1,16 @@ # frozen_string_literal: true namespace :mastodon do + desc 'Execute daily tasks' + task :daily do + Rake::Task['mastodon:feeds:clear'].invoke + Rake::Task['mastodon:media:clear'].invoke + Rake::Task['mastodon:users:clear'].invoke + + Rake::Task['mastodon:push:refresh'].invoke + end + + desc 'Turn a user into an admin, identified by the USERNAME environment variable' task make_admin: :environment do include RoutingHelper @@ -13,12 +23,13 @@ namespace :mastodon do desc 'Manually confirms a user with associated user email address stored in USER_EMAIL environment variable.' task confirm_email: :environment do email = ENV.fetch('USER_EMAIL') - user = User.where(email: email).first + user = User.find_by(email: email) + if user user.update(confirmed_at: Time.now.utc) - puts "User #{email} confirmed." + puts "#{email} confirmed" else - abort "User #{email} not found." + abort "#{email} not found" end end @@ -32,6 +43,13 @@ namespace :mastodon do task remove_silenced: :environment do MediaAttachment.where(account: Account.silenced).find_each(&:destroy) end + + desc 'Remove cached remote media attachments that are older than a week' + task remove_remote: :environment do + MediaAttachment.where.not(remote_url: '').where('created_at < ?', 1.week.ago).find_each do |media| + media.file.destroy + end + end end namespace :push do @@ -60,7 +78,7 @@ namespace :mastodon do end end - desc 'Clears all timelines so that they would be regenerated on next hit' + desc 'Clears all timelines' task clear_all: :environment do Redis.current.keys('feed:*').each { |key| Redis.current.del(key) } end @@ -126,8 +144,13 @@ namespace :mastodon do Rails.logger.debug 'Generating static avatars/headers for GIF ones...' Account.unscoped.where(avatar_content_type: 'image/gif').or(Account.unscoped.where(header_content_type: 'image/gif')).find_each do |account| - account.avatar.reprocess! - account.header.reprocess! + begin + account.avatar.reprocess! + account.header.reprocess! + rescue StandardError => e + Rails.logger.error "Error while generating static avatars/headers for account #{account.id}: #{e}" + next + end end Rails.logger.debug 'Done!' From f2997c9715a54c28dc4a8669920237b34b2a475e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=96=B0=E9=83=BD=E5=BF=83=28Neet=20Shin=29?= Date: Sun, 16 Apr 2017 17:12:16 +0530 Subject: [PATCH 04/25] Update Japanese Translate (#1903) * [Update ja.jsx] Add Muted, Video_error and sorted * [Update ja] Added Recovery code's translation * [Update ja] Added Two-fact/Recovery's translation * Update ja.jsx --- .../javascripts/components/locales/ja.jsx | 200 +++++++++--------- config/locales/ja.yml | 7 +- config/locales/simple_form.ja.yml | 3 + 3 files changed, 111 insertions(+), 99 deletions(-) diff --git a/app/assets/javascripts/components/locales/ja.jsx b/app/assets/javascripts/components/locales/ja.jsx index c64d7ecc6..5d5cea5c3 100644 --- a/app/assets/javascripts/components/locales/ja.jsx +++ b/app/assets/javascripts/components/locales/ja.jsx @@ -1,121 +1,125 @@ const ja = { - "column_back_button.label": "戻る", - "lightbox.close": "閉じる", - "loading_indicator.label": "読み込み中...", - "status.mention": "@{name} さんへの返信", - "status.delete": "削除", - "status.reply": "返信", - "status.reblog": "ブースト", - "status.favourite": "お気に入り", - "status.reblogged_by": "{name} さんにブーストされました", - "status.sensitive_warning": "不適切なコンテンツ", - "status.sensitive_toggle": "クリックして表示", - "status.show_more": "もっと見る", - "status.load_more": "もっと見る", - "status.show_less": "隠す", - "status.open": "Expand this status", - "status.report": "@{name} さんを通報", - "status.media_hidden": "非表示のメデイア", - "video_player.toggle_sound": "音の切り替え", - "account.mention": "@{name} さんに返信", + "account.block": "@{name} さんをブロック", + "account.disclaimer": "このユーザーは他のインスタンスに所属しているため、数字が正確で無い場合があります。", "account.edit_profile": "プロフィールを編集", + "account.follow": "フォロー", + "account.followers": "フォロワー", + "account.follows": "フォロー", + "account.follows_you": "フォローされています", + "account.mention": "@{name} さんに返信", + "account.mute": "ミュート", + "account.posts": "投稿", + "account.report": "@{name}を通報する", + "account.requested": "承認待ち", "account.unblock": "@{name} さんのブロックを解除", "account.unfollow": "フォロー解除", - "account.block": "@{name} さんをブロック", - "account.mute": "ミュート", "account.unmute": "ミュート解除", - "account.follow": "フォロー", - "account.report": "@{name}を通報する", - "account.posts": "投稿", - "account.follows": "フォロー", - "account.followers": "フォロワー", - "account.follows_you": "フォローされています", - "account.requested": "承認待ち", - "follow_request.authorize": "許可", - "follow_request.reject": "拒否", - "getting_started.heading": "スタート", - "getting_started.about_addressing": "ドメインとユーザー名を知っているなら検索フォームに入力すればフォローできます。", - "getting_started.about_shortcuts": "対象のアカウントがあなたと同じドメインのユーザーならばユーザー名のみで検索できます。これは返信のときも一緒です。", - "getting_started.open_source_notice": "Mastodon はオープンソースソフトウェアです。誰でも GitHub({github})から開発に参加したり、問題を報告したりできます。 {apps}", - "getting_started.apps": "さまざまなアプリで利用できます。", - "column.home": "ホーム", + "boost_modal.combo": "次からは{combo}を押せば、これをスキップできます。" + "column.blocks": "ブロックしたユーザー", "column.community": "ローカルタイムライン", - "column.public": "連合タイムライン", - "column.notifications": "通知", "column.favourites": "お気に入り", - "tabs_bar.compose": "投稿", - "tabs_bar.home": "ホーム", - "tabs_bar.mentions": "返信", - "tabs_bar.local_timeline": "ローカル", - "tabs_bar.federated_timeline": "連合", - "tabs_bar.notifications": "通知", + "column.follow_requests": "フォローリクエスト", + "column.home": "ホーム", + "column.mutes": "ミュート済みユーザー", + "column.notifications": "通知", + "column.public": "連合タイムライン", + "column_back_button.label": "戻る", "compose_form.placeholder": "今なにしてる?", + "compose_form.privacy_disclaimer": "あなたの非公開トゥートは返信先のユーザー(at {domains})に公開されます。{domainsCount, plural, one {that server} other {those servers}}を信頼しますか?投稿のプライバシー保護はMastodonサーバー内でのみ有効です。 もし{domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}ならばあなたの投稿のプライバシーは保護されず、ブーストされたり予期しないユーザーに見られる可能性があります。", "compose_form.publish": "トゥート", "compose_form.sensitive": "メディアを不適切なコンテンツとしてマークする", "compose_form.spoiler": "テキストを隠す", - "compose_form.spoiler_placeholder": "内容注意メッセージ", - "compose_form.private": "非公開にする", - "compose_form.privacy_disclaimer": "あなたの非公開トゥートは返信先のユーザー(at {domains})に公開されます。{domainsCount, plural, one {that server} other {those servers}}を信頼しますか?投稿のプライバシー保護はMastodonサーバー内でのみ有効です。 もし{domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}ならばあなたの投稿のプライバシーは保護されず、ブーストされたり予期しないユーザーに見られる可能性があります。", - "compose_form.unlisted": "公開タイムラインに表示しない", - "privacy.public.short": "公開", - "privacy.public.long": "公開TLに投稿する", - "privacy.unlisted.short": "未収載", - "privacy.unlisted.long": "公開TLで表示しない", - "privacy.private.short": "非公開", - "privacy.private.long": "フォロワーだけに公開", - "privacy.direct.short": "ダイレクト", - "privacy.direct.long": "含んだユーザーだけに公開", - "privacy.change": "投稿のプライバシーを変更", - "report.heading": "新規通報", - "report.placeholder": "コメント", - "report.target": "問題のユーザー", - "report.submit": "通報する", - "navigation_bar.edit_profile": "プロフィールを編集", - "navigation_bar.preferences": "ユーザー設定", - "navigation_bar.community_timeline": "ローカルタイムライン", - "navigation_bar.public_timeline": "連合タイムライン", - "navigation_bar.logout": "ログアウト", - "navigation_bar.favourites": "お気に入り", - "navigation_bar.blocks": "ブロックしたユーザー", - "navigation_bar.info": "サーバー情報", - "reply_indicator.cancel": "キャンセル", - "search.placeholder": "検索", - "search.account": "アカウント", - "search.hashtag": "ハッシュタグ", - "search.status_by": "{uuuname}からの投稿", - "search_results.total": "{count} 件", - "upload_area.title": "ファイルをこちらにドラッグしてください", - "upload_button.label": "メディアを追加", - "upload_form.undo": "やり直す", - "notification.follow": "{name} さんにフォローされました", - "notification.favourite": "{name} さんがあなたのトゥートをお気に入りに登録しました", - "notification.reblog": "{name} さんがあなたのトゥートをブーストしました", - "notification.mention": "{name} さんがあなたに返信しました", - "notifications.clear": "通知を片付ける", - "notifications.clear_confirmation": "通知を全部片付けます。大丈夫ですか?", - "notifications.column_settings.alert": "デスクトップ通知", - "notifications.column_settings.show": "カラムに表示", - "notifications.column_settings.follow": "新しいフォロワー", - "notifications.column_settings.favourite": "お気に入り", - "notifications.column_settings.mention": "返信", - "notifications.column_settings.reblog": "ブースト", - "notifications.column_settings.sound": "通知音を再生", + "compose_form.spoiler_placeholder": "閲覧注意", + "emoji_button.label": "絵文字を追加", + "empty_column.community": "ローカルタイムラインはまだ使われていません。何か書いてみましょう!", + "empty_column.hashtag": "このハッシュタグはまだ使われていません。", "empty_column.home": "まだ誰もフォローしていません。{public}を見に行くか、検索を使って他のユーザーを見つけましょう。", "empty_column.home.public_timeline": "連合タイムライン", "empty_column.notifications": "まだ通知がありません。他の人とふれ合って会話を始めましょう。", "empty_column.public": "ここにはまだ何もありません!公開で何かを投稿したり、他のインスタンスのユーザーをフォローしたりしていっぱいにしましょう!", - "empty_column.hashtag": "このハッシュタグはまだ使っていません。", - "upload_progress.label": "アップロード中…", - "emoji_button.label": "絵文字を追加", + "follow_request.authorize": "許可", + "follow_request.reject": "拒否", + "getting_started.apps": "さまざまなアプリで利用できます。", + "getting_started.heading": "スタート", + "getting_started.open_source_notice": "Mastodon はオープンソースソフトウェアです。誰でも GitHub({github})から開発に参加したり、問題を報告したりできます。 {apps}", + "home.column_settings.advanced": "上級者向け", "home.column_settings.basic": "シンプル", - "home.column_settings.advanced": "エキスパート", + "home.column_settings.filter_regex": "正規表現でフィルター", "home.column_settings.show_reblogs": "ブースト表示", "home.column_settings.show_replies": "返信表示", - "home.column_settings.filter_regex": "正規表現でフィルター", "home.settings": "カラム設定", - "notifications.settings": "カラム設定", + "lightbox.close": "閉じる", + "loading_indicator.label": "読み込み中...", + "media_gallery.toggle_visible": "表示切り替え", "missing_indicator.label": "見つかりません", - "boost_modal.combo": "次は{combo}を押せば、これをスキップできます。" + "navigation_bar.blocks": "ブロックしたユーザー", + "navigation_bar.community_timeline": "ローカルタイムライン", + "navigation_bar.edit_profile": "プロフィールを編集", + "navigation_bar.favourites": "お気に入り", + "navigation_bar.follow_requests": "フォローリクエスト", + "navigation_bar.info": "サーバー情報", + "navigation_bar.logout": "ログアウト", + "navigation_bar.mutes": "ミュートしたユーザー", + "navigation_bar.preferences": "ユーザー設定", + "navigation_bar.public_timeline": "連合タイムライン", + "notification.favourite": "{name} さんがあなたのトゥートをお気に入りに登録しました", + "notification.follow": "{name} さんにフォローされました", + "notification.mention": "{name} さんがあなたに返信しました", + "notification.reblog": "{name} さんがあなたのトゥートをブーストしました", + "notifications.clear": "通知を消去", + "notifications.clear_confirmation": "本当に通知を消去しますか?", + "notifications.column_settings.alert": "デスクトップ通知", + "notifications.column_settings.favourite": "お気に入り", + "notifications.column_settings.follow": "新しいフォロワー", + "notifications.column_settings.mention": "返信", + "notifications.column_settings.reblog": "ブースト", + "notifications.column_settings.show": "カラムに表示", + "notifications.column_settings.sound": "通知音を再生", + "notifications.settings": "カラム設定", + "privacy.change": "投稿のプライバシーを変更", + "privacy.direct.long": "メンションしたユーザーだけに公開", + "privacy.direct.short": "ダイレクト", + "privacy.private.long": "フォロワーだけに公開", + "privacy.private.short": "非公開", + "privacy.public.long": "公開TLに投稿する", + "privacy.public.short": "公開", + "privacy.unlisted.long": "公開TLで表示しない", + "privacy.unlisted.short": "未収載", + "reply_indicator.cancel": "キャンセル", + "report.heading": "新規通報", + "report.placeholder": "コメント", + "report.submit": "通報する", + "report.target": "問題のユーザー", + "search.placeholder": "検索", + "search.status_by": "{name}からの投稿", + "search_results.total": "{count} {count, plural, one {result} other {results}} 件", + "status.delete": "削除", + "status.favourite": "お気に入り", + "status.load_more": "もっと見る", + "status.media_hidden": "非表示のメデイア", + "status.mention": "@{name} さんへの返信", + "status.open": "詳細を表示", + "status.reblog": "ブースト", + "status.reblogged_by": "{name} さんにブーストされました", + "status.reply": "返信", + "status.report": "@{name} さんを通報", + "status.sensitive_toggle": "クリックして表示", + "status.sensitive_warning": "不適切なコンテンツ", + "status.show_less": "隠す", + "status.show_more": "もっと見る", + "tabs_bar.compose": "投稿", + "tabs_bar.federated_timeline": "連合", + "tabs_bar.home": "ホーム", + "tabs_bar.local_timeline": "ローカル", + "tabs_bar.notifications": "通知", + "upload_area.title": "ドラッグ&ドロップでアップロード", + "upload_button.label": "メディアを追加", + "upload_form.undo": "やり直す", + "upload_progress.label": "アップロード中…", + "video_player.expand": "動画の詳細", + "video_player.toggle_sound": "音の切り替え", + "video_player.toggle_visible": "表示切り替え", + "video_player.video_error": "動画の再生に失敗しました", }; export default ja; diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 3c7b342e2..f5ee17f35 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -290,8 +290,13 @@ ja: disable: 無効 enable: 有効 enabled_success: 二段階認証が有効になりました + generate_recovery_codes: 復元コードを生成 instructions_html: "Google Authenticatorか、もしくはほかのTOTPアプリでこのQRコードをスキャンしてください。これ以降、ログインするときはそのアプリで生成されるコードが必要になります。" - manual_instructions: 'QRコードがスキャンできず、手動での登録を希望の場合はこのシークレットコードを利用してください。:' + lost_recovery_codes: リカバリコードを使用すると携帯電話を紛失した場合でもアカウントにアクセスできるようになります。 リカバリーコードを紛失した場合もここで再生成することができますが、古いリカバリコードは無効になります。 + manual_instructions: QRコードがスキャンできず、手動での登録を希望の場合はこのシークレットコードを利用してください。: + recovery_codes: リカバリーコード + recovery_codes_regenerated: リカバリーコードが再生成されました。 + recovery_instructions: 携帯電話を紛失した場合、以下の内どれかのリカバリコードを使用してアカウントへアクセスすることができます。 リカバリコードは印刷して安全に保管してください。 setup: 初期設定 warning: 現在認証アプリを設定できない場合、無効に設定して、有効にしないでください。 wrong_code: コードが間違っています。サーバー上の時間とデバイス上の時間が一致していることを確認してください。 diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 103001b7e..812526765 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -10,6 +10,8 @@ ja: note: プロフィールは160文字まで設定することができます。 imports: data: 他の Mastodon サーバーからエクスポートしたCSVファイルを選択して下さい + sessions: + otp: 携帯電話に表示された2段階認証コードを入力するか、生成したリカバリーコードを使用してください。 labels: defaults: avatar: アイコン @@ -28,6 +30,7 @@ ja: password: パスワード setting_boost_modal: ブーストする前に確認ダイアログを表示する setting_default_privacy: 投稿の公開範囲 + severity: 重大性 type: インポートする項目 username: ユーザー名 interactions: From 13b11ddc8ca48ed44996b097e94f40f05a146fc1 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Sun, 16 Apr 2017 07:42:45 -0400 Subject: [PATCH 05/25] Add binstub for rspec from rspec-core (#1913) --- bin/rspec | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100755 bin/rspec diff --git a/bin/rspec b/bin/rspec new file mode 100755 index 000000000..d738b23c0 --- /dev/null +++ b/bin/rspec @@ -0,0 +1,17 @@ +#!/usr/bin/env ruby +# frozen_string_literal: true +# +# This file was generated by Bundler. +# +# The application 'rspec' is installed as part of a gem, and +# this file is here to facilitate running it. +# + +require "pathname" +ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile", + Pathname.new(__FILE__).realpath) + +require "rubygems" +require "bundler/setup" + +load Gem.bin_path("rspec-core", "rspec") From 6df63465b9b4ae409cad2303ec2476c6c479284c Mon Sep 17 00:00:00 2001 From: George Hattori Date: Sun, 16 Apr 2017 20:44:57 +0900 Subject: [PATCH 06/25] Improve Japanese translation (#1909) --- config/locales/simple_form.ja.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/locales/simple_form.ja.yml b/config/locales/simple_form.ja.yml index 812526765..b9257ec35 100644 --- a/config/locales/simple_form.ja.yml +++ b/config/locales/simple_form.ja.yml @@ -16,7 +16,7 @@ ja: defaults: avatar: アイコン confirm_new_password: 新しいパスワード(確認用) - confirm_password: 新しいパスワード + confirm_password: パスワード(確認用) current_password: 現在のパスワード data: データ display_name: 表示名 @@ -24,7 +24,7 @@ ja: header: ヘッダー locale: 言語 locked: 非公開アカウントにする - new_password: パスワード + new_password: 新しいパスワード note: プロフィール otp_attempt: 二段階認証コード password: パスワード From a8f45c0838c35c51120e6e327457d5114f86a300 Mon Sep 17 00:00:00 2001 From: Yuki Nakagawa Date: Sun, 16 Apr 2017 21:05:16 +0900 Subject: [PATCH 07/25] Enlarge font size to avoid autozooming of iPhone. (#1911) --- app/assets/stylesheets/components.scss | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/assets/stylesheets/components.scss b/app/assets/stylesheets/components.scss index d1c4d2bb2..8bd35819a 100644 --- a/app/assets/stylesheets/components.scss +++ b/app/assets/stylesheets/components.scss @@ -1203,6 +1203,10 @@ a.status__content__spoiler-link { &:focus { outline: 0; } + + @media screen and (max-width: 600px) { + font-size: 16px; + } } .spoiler-input__input { @@ -1267,6 +1271,10 @@ a.status__content__spoiler-link { color: $color5; border-bottom-color: $color4; } + + @media screen and (max-width: 600px) { + font-size: 16px; + } } @import 'boost'; @@ -1906,6 +1914,10 @@ button.icon-button.active i.fa-retweet { &:focus { background: lighten($color1, 4%); } + + @media screen and (max-width: 600px) { + font-size: 16px; + } } .search__icon { From c99fc08a0d951ec454a3defc5f99ed7b2212a8ac Mon Sep 17 00:00:00 2001 From: Erwan Leboucher Date: Sun, 16 Apr 2017 14:15:03 +0200 Subject: [PATCH 08/25] Add missing french translation. (#1906) * Add missing french translation. Causing this error: [React Intl] Missing message: "navigation_bar.mutes" for locale: "fr", * Update fr.jsx --- app/assets/javascripts/components/locales/fr.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/app/assets/javascripts/components/locales/fr.jsx b/app/assets/javascripts/components/locales/fr.jsx index 8838c264f..0a1dd38ae 100644 --- a/app/assets/javascripts/components/locales/fr.jsx +++ b/app/assets/javascripts/components/locales/fr.jsx @@ -75,6 +75,7 @@ const fr = { "navigation_bar.favourites": "Favoris", "navigation_bar.info": "Plus d'informations", "navigation_bar.logout": "Déconnexion", + "navigation_bar.mutes": "Utilisateurs muets", "navigation_bar.follow_requests": "Demandes de suivi", "reply_indicator.cancel": "Annuler", "search.placeholder": "Rechercher", From 24baaa17e847830d7b3f8e37eaa2921bdaa22298 Mon Sep 17 00:00:00 2001 From: Naouak Date: Sun, 16 Apr 2017 14:54:09 +0200 Subject: [PATCH 09/25] Syntax error in japanese localisation (#1920) assets:precompile was failling because of this missing comma. --- app/assets/javascripts/components/locales/ja.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/components/locales/ja.jsx b/app/assets/javascripts/components/locales/ja.jsx index 5d5cea5c3..cf79c134a 100644 --- a/app/assets/javascripts/components/locales/ja.jsx +++ b/app/assets/javascripts/components/locales/ja.jsx @@ -14,7 +14,7 @@ const ja = { "account.unblock": "@{name} さんのブロックを解除", "account.unfollow": "フォロー解除", "account.unmute": "ミュート解除", - "boost_modal.combo": "次からは{combo}を押せば、これをスキップできます。" + "boost_modal.combo": "次からは{combo}を押せば、これをスキップできます。", "column.blocks": "ブロックしたユーザー", "column.community": "ローカルタイムライン", "column.favourites": "お気に入り", From 75f416a4925c3cb60c5a4919cdafc45392b3be00 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Sun, 16 Apr 2017 08:55:04 -0400 Subject: [PATCH 10/25] Fix yaml issue in ja locale (#1916) * Fix yaml parse issue in ja.yml locale * Fix issue in locales/ja.jsx --- app/assets/javascripts/components/locales/ja.jsx | 2 +- config/locales/ja.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/components/locales/ja.jsx b/app/assets/javascripts/components/locales/ja.jsx index cf79c134a..fd6e47c41 100644 --- a/app/assets/javascripts/components/locales/ja.jsx +++ b/app/assets/javascripts/components/locales/ja.jsx @@ -92,7 +92,7 @@ const ja = { "report.target": "問題のユーザー", "search.placeholder": "検索", "search.status_by": "{name}からの投稿", - "search_results.total": "{count} {count, plural, one {result} other {results}} 件", + "search_results.total": "{count} {count, plural, one {result} other {results}} 件", "status.delete": "削除", "status.favourite": "お気に入り", "status.load_more": "もっと見る", diff --git a/config/locales/ja.yml b/config/locales/ja.yml index f5ee17f35..01f97b8c6 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -293,7 +293,7 @@ ja: generate_recovery_codes: 復元コードを生成 instructions_html: "Google Authenticatorか、もしくはほかのTOTPアプリでこのQRコードをスキャンしてください。これ以降、ログインするときはそのアプリで生成されるコードが必要になります。" lost_recovery_codes: リカバリコードを使用すると携帯電話を紛失した場合でもアカウントにアクセスできるようになります。 リカバリーコードを紛失した場合もここで再生成することができますが、古いリカバリコードは無効になります。 - manual_instructions: QRコードがスキャンできず、手動での登録を希望の場合はこのシークレットコードを利用してください。: + manual_instructions: "QRコードがスキャンできず、手動での登録を希望の場合はこのシークレットコードを利用してください。:" recovery_codes: リカバリーコード recovery_codes_regenerated: リカバリーコードが再生成されました。 recovery_instructions: 携帯電話を紛失した場合、以下の内どれかのリカバリコードを使用してアカウントへアクセスすることができます。 リカバリコードは印刷して安全に保管してください。 From 8f8319852ca8ff3b1b8f163b184e58122ec5b3fb Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Sun, 16 Apr 2017 21:55:43 +0900 Subject: [PATCH 11/25] Fixed NoMethodError in UnfollowService (#1918) --- app/services/unfollow_service.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/services/unfollow_service.rb b/app/services/unfollow_service.rb index 244c9b529..9b39f4945 100644 --- a/app/services/unfollow_service.rb +++ b/app/services/unfollow_service.rb @@ -6,6 +6,7 @@ class UnfollowService < BaseService # @param [Account] target_account Which to unfollow def call(source_account, target_account) follow = source_account.unfollow!(target_account) + return unless follow NotificationWorker.perform_async(build_xml(follow), source_account.id, target_account.id) unless target_account.local? UnmergeWorker.perform_async(target_account.id, source_account.id) end From da302a43cddff2ffef145289eda3f69cc8c2ebf0 Mon Sep 17 00:00:00 2001 From: Eduardo Elias Date: Sun, 16 Apr 2017 13:56:04 +0100 Subject: [PATCH 12/25] Improve PT locale on simple_form (#1917) * Improve PT locale on simple_form * Add missing keys to PT locale --- config/locales/pt.yml | 77 +++++++++++++++++++++++++++++-- config/locales/simple_form.pt.yml | 12 ++--- 2 files changed, 79 insertions(+), 10 deletions(-) diff --git a/config/locales/pt.yml b/config/locales/pt.yml index f2c7458f7..703c8467f 100644 --- a/config/locales/pt.yml +++ b/config/locales/pt.yml @@ -2,28 +2,97 @@ pt: about: about_mastodon: Mastodon é um servidor de rede social grátis, e open-source. Uma alternativa descentralizada ás plataformas comerciais, que evita o risco de uma única empresa monopolizar a sua comunicação. Qualquer um pode ter uma instância Mastodon e assim participar na rede social federada sem problemas. + about_this: Sobre essa instância get_started: Como começar + apps: Aplicações + business_email: 'Email comercial:' + closed_registrations: Registros estão fechadas para essa instância. + contact: Contato + description_headline: O que é %{domain}? + domain_count_after: outras instâncias + domain_count_before: Conectado a + features: + api: Aberto para API de aplicações e serviços + blocks: Bloqueos e ferramentas para mudar + characters: 500 caracteres por post + chronology: Timeline são cronologicas + ethics: 'Design ético: sem propaganda, sem tracking' + gifv: GIFV e vídeos curtos + privacy: Granular, privacidade setada por post + public: Timelines públicas + features_headline: O que torna Mastodon diferente + get_started: Comece aqui + links: Links source_code: Source code + other_instances: Outras instâncias terms: Termos + user_count_after: usuários + user_count_before: Lugar de accounts: follow: Seguir followers: Seguidores - following: Following + following: Seguindo nothing_here: Não há nada aqui! people_followed_by: Pessoas seguidas por %{name} people_who_follow: Pessoas que seguem %{name} posts: Posts + remote_follow: Acesso remoto unfollow: Unfollow + admin: + accounts: + are_you_sure: Você tem certeza? + display_name: Nome mostrado + domain: Domain + edit: Editar + email: E-mail + feed_url: URL do Feed + followers: Seguidores + follows: Seguindo + location: + all: Todos + local: Local + remote: Remoto + title: Local + media_attachments: Mídia anexadas + moderation: + all: Todos + silenced: Silenciado + suspended: Supenso + title: Moderação + most_recent_activity: Atividade mais recente + most_recent_ip: IP mais recente + not_subscribed: Não inscrito + order: + alphabetic: Alfabética + most_recent: Mais recente + title: Ordem + perform_full_suspension: Fazer suspensão completa + profile_url: URL do perfil + public: Público + push_subscription_expires: PuSH subscription expires + salmon_url: Salmon URL + silence: Silêncio + statuses: Status + title: Contas + undo_silenced: Desfazer silenciar + undo_suspension: Desfazer supensão + username: Usuário + web: Web + domain_block: + add_new: Adicionar nova + created_msg: Bloqueio do domínio está sendo processado + destroyed_msg: Bloqueio de domínio está sendo desfeito + domain: Domínio application_mailer: signature: notificações Mastodon de %{instance} auth: - change_password: Mudar password + change_password: Mudar senha didnt_get_confirmation: Não recebeu instruções de confirmação? - forgot_password: Esqueceu a password? + forgot_password: Esqueceu a senha? login: Entrar register: Registar resend_confirmation: Reenviar instruções de confirmação - reset_password: Reset password + reset_password: Resetar senha set_new_password: Editar password generic: changes_saved_msg: Mudanças guardadas! diff --git a/config/locales/simple_form.pt.yml b/config/locales/simple_form.pt.yml index 07099c1f0..e8b5e2d7f 100644 --- a/config/locales/simple_form.pt.yml +++ b/config/locales/simple_form.pt.yml @@ -4,17 +4,17 @@ pt: labels: defaults: avatar: Avatar - confirm_new_password: Confirme nova password - confirm_password: Confirme a password - current_password: Password atual + confirm_new_password: Confirme nova senha + confirm_password: Confirme a senha + current_password: Senha atual display_name: Nome email: Endereço de email header: Header locale: Linguagem - new_password: Nova password + new_password: Nova senha note: Biografia - password: Password - username: Username + password: Senha + username: Usuário interactions: must_be_follower: Bloquear notificações de não-seguidores must_be_following: Bloquear notificações de pessoas que você From 0611209141d1dd446fcf2345084cef00538f6ee7 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Sun, 16 Apr 2017 21:57:30 +0900 Subject: [PATCH 13/25] Relax Ruby version requirement (#1901) --- .travis.yml | 1 + Gemfile | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 45a71d83c..a91d70cf5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,6 +14,7 @@ addons: postgresql: 9.4 rvm: + - 2.3.4 - 2.4.1 services: diff --git a/Gemfile b/Gemfile index 5d75c11ea..0165219d3 100644 --- a/Gemfile +++ b/Gemfile @@ -1,7 +1,7 @@ # frozen_string_literal: true source 'https://rubygems.org' -ruby '2.4.1' +ruby '>= 2.3.0', '< 2.5.0' gem 'pkg-config' From c2762fa498a07c2aa8594f106495c43d3501e821 Mon Sep 17 00:00:00 2001 From: Kazuhiro NISHIYAMA Date: Sun, 16 Apr 2017 23:07:45 +0900 Subject: [PATCH 14/25] Add missing Japanese translations (#1923) And `i18n-tasks add-missing -l ja` changes some quotes. --- config/locales/ja.yml | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 01f97b8c6..5483e63b5 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -71,6 +71,7 @@ ja: profile_url: プロフィールURL public: パブリック push_subscription_expires: PuSH購読期限切れ + reset_password: パスワード再設定 salmon_url: Salmon URL silence: サイレンス statuses: トゥート数 @@ -81,6 +82,8 @@ ja: web: Web domain_block: add_new: 新規追加 + created_msg: ドメインブロック処理を完了しました + destroyed_msg: ドメインブロックを外しました domain: ドメイン new: create: ブロックを作成 @@ -90,8 +93,21 @@ ja: silence: サイレンス suspend: 停止 title: 新規ドメインブロック + reject_media: メディアファイルを拒否 + reject_media_hint: ローカルに保村されたメディアファイルを削除し、今後のダウンロードを拒否します。停止とは無関係です。 + severities: + silence: サイレンス + suspend: 停止 severity: 深刻度 + show: + affected_accounts: "データベース中の%{count}個のアカウントに影響します" + retroactive: + silence: このドメインからの存在するすべてのアカウントのサイレンスを戻す + suspend: このドメインからの存在するすべてのアカウントの停止を戻す + title: "%{domain}のドメインブロックを戻す" + undo: 元に戻す title: ドメインブロック + undo: 元に戻す pubsubhubbub: callback_url: コールバックURL confirmed: 確認済み @@ -106,7 +122,7 @@ ja: delete: 削除 id: ID mark_as_resolved: 解決済みとしてマーク - report: 'レポート#%{id}' + report: レポート#%{id} reported_account: 報告対象アカウント reported_by: 報告者 resolved: 解決済み @@ -293,7 +309,7 @@ ja: generate_recovery_codes: 復元コードを生成 instructions_html: "Google Authenticatorか、もしくはほかのTOTPアプリでこのQRコードをスキャンしてください。これ以降、ログインするときはそのアプリで生成されるコードが必要になります。" lost_recovery_codes: リカバリコードを使用すると携帯電話を紛失した場合でもアカウントにアクセスできるようになります。 リカバリーコードを紛失した場合もここで再生成することができますが、古いリカバリコードは無効になります。 - manual_instructions: "QRコードがスキャンできず、手動での登録を希望の場合はこのシークレットコードを利用してください。:" + manual_instructions: 'QRコードがスキャンできず、手動での登録を希望の場合はこのシークレットコードを利用してください。:' recovery_codes: リカバリーコード recovery_codes_regenerated: リカバリーコードが再生成されました。 recovery_instructions: 携帯電話を紛失した場合、以下の内どれかのリカバリコードを使用してアカウントへアクセスすることができます。 リカバリコードは印刷して安全に保管してください。 From 2c329f2b69fde308d4006482ee352d86c0d5efa9 Mon Sep 17 00:00:00 2001 From: mshrtkch Date: Sun, 16 Apr 2017 23:07:58 +0900 Subject: [PATCH 15/25] Fix translation related to "mute" (#1926) --- app/assets/javascripts/components/locales/ja.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/assets/javascripts/components/locales/ja.jsx b/app/assets/javascripts/components/locales/ja.jsx index fd6e47c41..79defd325 100644 --- a/app/assets/javascripts/components/locales/ja.jsx +++ b/app/assets/javascripts/components/locales/ja.jsx @@ -20,7 +20,7 @@ const ja = { "column.favourites": "お気に入り", "column.follow_requests": "フォローリクエスト", "column.home": "ホーム", - "column.mutes": "ミュート済みユーザー", + "column.mutes": "ミュートしたユーザー", "column.notifications": "通知", "column.public": "連合タイムライン", "column_back_button.label": "戻る", From 77d1447ac407395a87b758d35eda2b4de8ca9413 Mon Sep 17 00:00:00 2001 From: Alda Marteau-Hardi Date: Sun, 16 Apr 2017 16:08:11 +0200 Subject: [PATCH 16/25] Add the licence key in package.json (#1914) --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 6f448238b..0ced631a9 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,6 @@ { "name": "mastodon", + "license" : "AGPL-3.0", "scripts": { "start": "babel-node ./streaming/index.js --presets es2015,stage-2", "storybook": "start-storybook -p 9001 -c storybook", From de72db99fac56fa06fccdab443a1c4e44e8fb985 Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Sun, 16 Apr 2017 23:28:26 +0900 Subject: [PATCH 17/25] Add presence validation to Import (#1928) ``` *An* `ActiveRecord::StatementInvalid` *occurred while* `POST ` *was processed by* `imports#create` Exception ---------------- PG::NotNullViolation: ERROR: null value in column "type" violates not-null constraint ``` --- app/models/import.rb | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/models/import.rb b/app/models/import.rb index 3013bc50e..85f6ca4bd 100644 --- a/app/models/import.rb +++ b/app/models/import.rb @@ -1,13 +1,15 @@ # frozen_string_literal: true class Import < ApplicationRecord + FILE_TYPES = ['text/plain', 'text/csv'].freeze + self.inheritance_column = false + belongs_to :account, required: true + enum type: [:following, :blocking, :muting] - belongs_to :account - - FILE_TYPES = ['text/plain', 'text/csv'].freeze + validates :type, presence: true has_attached_file :data, url: '/system/:hash.:extension', hash_secret: ENV['PAPERCLIP_SECRET'] validates_attachment_content_type :data, content_type: FILE_TYPES From fc34e0e19145f0f681f19e003060f3aa4ef122d4 Mon Sep 17 00:00:00 2001 From: tackeyy Date: Sun, 16 Apr 2017 23:28:52 +0900 Subject: [PATCH 18/25] Remove .keep in models (#1892) --- app/models/concerns/.keep | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 app/models/concerns/.keep diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep deleted file mode 100644 index e69de29bb..000000000 From 90760eae4c044817728859e1adf6f57930d83b98 Mon Sep 17 00:00:00 2001 From: abcang Date: Sun, 16 Apr 2017 23:33:38 +0900 Subject: [PATCH 19/25] fix regex filter (#1845) * fix regex filter * fixed br to linebreak and, stlip tags. * change to send raw content * changed to unescape in reducer --- .../features/ui/containers/status_list_container.jsx | 6 ++++-- app/assets/javascripts/components/reducers/statuses.jsx | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx index f249240d8..4c33f2b61 100644 --- a/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx +++ b/app/assets/javascripts/components/features/ui/containers/status_list_container.jsx @@ -24,8 +24,10 @@ const makeGetStatusIds = () => createSelector([ if (columnSettings.getIn(['regex', 'body'], '').trim().length > 0) { try { - const regex = new RegExp(columnSettings.getIn(['regex', 'body']).trim(), 'i'); - showStatus = showStatus && !regex.test(statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'content']) : statusForId.get('content')); + if (showStatus) { + const regex = new RegExp(columnSettings.getIn(['regex', 'body']).trim(), 'i'); + showStatus = !regex.test(statusForId.get('reblog') ? statuses.getIn([statusForId.get('reblog'), 'unescaped_content']) : statusForId.get('unescaped_content')); + } } catch(e) { // Bad regex, don't affect filters } diff --git a/app/assets/javascripts/components/reducers/statuses.jsx b/app/assets/javascripts/components/reducers/statuses.jsx index ca8fa7a01..2002d2223 100644 --- a/app/assets/javascripts/components/reducers/statuses.jsx +++ b/app/assets/javascripts/components/reducers/statuses.jsx @@ -48,6 +48,9 @@ const normalizeStatus = (state, status) => { normalStatus.reblog = status.reblog.id; } + const linebreakComplemented = status.content.replace(/
/g, '\n').replace(/<\/p>

/g, '\n\n'); + normalStatus.unescaped_content = new DOMParser().parseFromString(linebreakComplemented, 'text/html').documentElement.textContent; + return state.update(status.id, Immutable.Map(), map => map.mergeDeep(Immutable.fromJS(normalStatus))); }; From 7efde22c3ab0464a0896abcaa8be277fd14bdee9 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Sun, 16 Apr 2017 10:37:49 -0400 Subject: [PATCH 20/25] Use local vars in partials (#1935) * Use local vars in accounts/header partial * Use local variable in 2fa recovery codes partial --- app/views/accounts/_header.html.haml | 40 +++++++++---------- app/views/accounts/followers.html.haml | 2 +- app/views/accounts/following.html.haml | 2 +- app/views/accounts/show.html.haml | 2 +- .../_recovery_codes.html.haml | 2 +- .../two_factor_auths/create.html.haml | 2 +- .../two_factor_auths/recovery_codes.html.haml | 2 +- 7 files changed, 26 insertions(+), 26 deletions(-) diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index beee96cd8..6538885a0 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -1,34 +1,34 @@ -.card.h-card.p-author{ style: "background-image: url(#{@account.header.url( :original)})" } - - if user_signed_in? && current_account.id != @account.id && !current_account.requested?(@account) +.card.h-card.p-author{ style: "background-image: url(#{account.header.url( :original)})" } + - if user_signed_in? && current_account.id != account.id && !current_account.requested?(account) .controls - - if current_account.following?(@account) - = link_to t('accounts.unfollow'), unfollow_account_path(@account), data: { method: :post }, class: 'button' + - if current_account.following?(account) + = link_to t('accounts.unfollow'), unfollow_account_path(account), data: { method: :post }, class: 'button' - else - = link_to t('accounts.follow'), follow_account_path(@account), data: { method: :post }, class: 'button' + = link_to t('accounts.follow'), follow_account_path(account), data: { method: :post }, class: 'button' - elsif !user_signed_in? .controls .remote-follow - = link_to t('accounts.remote_follow'), account_remote_follow_path(@account), class: 'button' - .avatar= image_tag @account.avatar.url(:original), class: 'u-photo' + = link_to t('accounts.remote_follow'), account_remote_follow_path(account), class: 'button' + .avatar= image_tag account.avatar.url(:original), class: 'u-photo' %h1.name - %span.p-name.emojify= display_name(@account) + %span.p-name.emojify= display_name(account) %small - %span= "@#{@account.username}" - = fa_icon('lock') if @account.locked? + %span= "@#{account.username}" + = fa_icon('lock') if account.locked? .details .bio - .account__header__content.p-note.emojify= Formatter.instance.simplified_format(@account) + .account__header__content.p-note.emojify= Formatter.instance.simplified_format(account) .details-counters - .counter{ class: active_nav_class(short_account_url(@account)) } - = link_to short_account_url(@account), class: 'u-url u-uid' do + .counter{ class: active_nav_class(short_account_url(account)) } + = link_to short_account_url(account), class: 'u-url u-uid' do %span.counter-label= t('accounts.posts') - %span.counter-number= number_with_delimiter @account.statuses_count - .counter{ class: active_nav_class(following_account_url(@account)) } - = link_to following_account_url(@account) do + %span.counter-number= number_with_delimiter account.statuses_count + .counter{ class: active_nav_class(following_account_url(account)) } + = link_to following_account_url(account) do %span.counter-label= t('accounts.following') - %span.counter-number= number_with_delimiter @account.following_count - .counter{ class: active_nav_class(followers_account_url(@account)) } - = link_to followers_account_url(@account) do + %span.counter-number= number_with_delimiter account.following_count + .counter{ class: active_nav_class(followers_account_url(account)) } + = link_to followers_account_url(account) do %span.counter-label= t('accounts.followers') - %span.counter-number= number_with_delimiter @account.followers_count + %span.counter-number= number_with_delimiter account.followers_count diff --git a/app/views/accounts/followers.html.haml b/app/views/accounts/followers.html.haml index fa5071f38..4b53aef0c 100644 --- a/app/views/accounts/followers.html.haml +++ b/app/views/accounts/followers.html.haml @@ -1,7 +1,7 @@ - content_for :page_title do = t('accounts.people_who_follow', name: display_name(@account)) -= render partial: 'header' += render 'header', account: @account .accounts-grid - if @followers.empty? diff --git a/app/views/accounts/following.html.haml b/app/views/accounts/following.html.haml index 987dcba1f..4711997d9 100644 --- a/app/views/accounts/following.html.haml +++ b/app/views/accounts/following.html.haml @@ -1,7 +1,7 @@ - content_for :page_title do = t('accounts.people_followed_by', name: display_name(@account)) -= render partial: 'header' += render 'header', account: @account .accounts-grid - if @following.empty? diff --git a/app/views/accounts/show.html.haml b/app/views/accounts/show.html.haml index fd7ff9653..9a70fd16f 100644 --- a/app/views/accounts/show.html.haml +++ b/app/views/accounts/show.html.haml @@ -20,7 +20,7 @@ .h-feed %data.p-name{ value: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/ - = render partial: 'header' + = render 'header', account: @account - if @statuses.empty? .accounts-grid diff --git a/app/views/settings/two_factor_auths/_recovery_codes.html.haml b/app/views/settings/two_factor_auths/_recovery_codes.html.haml index 719a1e01b..054588b97 100644 --- a/app/views/settings/two_factor_auths/_recovery_codes.html.haml +++ b/app/views/settings/two_factor_auths/_recovery_codes.html.haml @@ -1,6 +1,6 @@ %p.hint= t('two_factor_auth.recovery_instructions') %ol.recovery-codes - - @codes.each do |code| + - recovery_codes.each do |code| %li %samp= code diff --git a/app/views/settings/two_factor_auths/create.html.haml b/app/views/settings/two_factor_auths/create.html.haml index 8710b6e02..138a930fd 100644 --- a/app/views/settings/two_factor_auths/create.html.haml +++ b/app/views/settings/two_factor_auths/create.html.haml @@ -1,4 +1,4 @@ - content_for :page_title do = t('settings.two_factor_auth') -= render 'recovery_codes' += render partial: 'recovery_codes', object: @codes diff --git a/app/views/settings/two_factor_auths/recovery_codes.html.haml b/app/views/settings/two_factor_auths/recovery_codes.html.haml index 8710b6e02..138a930fd 100644 --- a/app/views/settings/two_factor_auths/recovery_codes.html.haml +++ b/app/views/settings/two_factor_auths/recovery_codes.html.haml @@ -1,4 +1,4 @@ - content_for :page_title do = t('settings.two_factor_auth') -= render 'recovery_codes' += render partial: 'recovery_codes', object: @codes From 73b0af5c938b49ef0a4a21921b41a3b1bcd4e05e Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Sun, 16 Apr 2017 10:38:02 -0400 Subject: [PATCH 21/25] Simplify the og:image and og:description code in stream_entries/show (#1934) --- app/models/status.rb | 4 ++++ app/views/stream_entries/_og_description.html.haml | 4 ++++ app/views/stream_entries/_og_image.html.haml | 6 ++++++ app/views/stream_entries/show.html.haml | 13 ++----------- 4 files changed, 16 insertions(+), 11 deletions(-) create mode 100644 app/views/stream_entries/_og_description.html.haml create mode 100644 app/views/stream_entries/_og_image.html.haml diff --git a/app/models/status.rb b/app/models/status.rb index 16cd4383f..c0a5d9d1b 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -110,6 +110,10 @@ class Status < ApplicationRecord results end + def non_sensitive_with_media? + !sensitive? && media_attachments.any? + end + class << self def as_home_timeline(account) where(account: [account] + account.following) diff --git a/app/views/stream_entries/_og_description.html.haml b/app/views/stream_entries/_og_description.html.haml new file mode 100644 index 000000000..5762aca04 --- /dev/null +++ b/app/views/stream_entries/_og_description.html.haml @@ -0,0 +1,4 @@ +- if activity.is_a?(Status) && activity.spoiler_text? + %meta{ property: 'og:description', content: activity.spoiler_text }/ +- else + %meta{ property: 'og:description', content: activity.content }/ diff --git a/app/views/stream_entries/_og_image.html.haml b/app/views/stream_entries/_og_image.html.haml new file mode 100644 index 000000000..f725209d8 --- /dev/null +++ b/app/views/stream_entries/_og_image.html.haml @@ -0,0 +1,6 @@ +- if activity.is_a?(Status) && activity.non_sensitive_with_media? + %meta{ property: 'og:image', content: full_asset_url(activity.media_attachments.first.file.url(:small)) }/ +- else + %meta{ property: 'og:image', content: full_asset_url(account.avatar.url(:original)) }/ + %meta{ property: 'og:image:width', content: '120' }/ + %meta{ property: 'og:image:height', content: '120' }/ diff --git a/app/views/stream_entries/show.html.haml b/app/views/stream_entries/show.html.haml index eb8387ccb..dea5e9d40 100644 --- a/app/views/stream_entries/show.html.haml +++ b/app/views/stream_entries/show.html.haml @@ -6,17 +6,8 @@ %meta{ property: 'og:type', content: 'article' }/ %meta{ property: 'og:title', content: "#{@account.username} on #{Rails.configuration.x.local_domain}" }/ - - if @stream_entry.activity.is_a?(Status) && !@stream_entry.activity.spoiler_text.blank? - %meta{ property: 'og:description', content: @stream_entry.activity.spoiler_text }/ - - else - %meta{ property: 'og:description', content: @stream_entry.activity.content }/ - - - if @stream_entry.activity.is_a?(Status) && !@stream_entry.activity.sensitive? && @stream_entry.activity.media_attachments.size > 0 - %meta{ property: 'og:image', content: full_asset_url(@stream_entry.activity.media_attachments.first.file.url(:small)) }/ - - else - %meta{ property: 'og:image', content: full_asset_url(@account.avatar.url(:original)) }/ - %meta{ property: 'og:image:width', content: '120' }/ - %meta{ property: 'og:image:height', content: '120' }/ + = render 'stream_entries/og_description', activity: @stream_entry.activity + = render 'stream_entries/og_image', activity: @stream_entry.activity, account: @account %meta{ property: 'twitter:card', content: 'summary' }/ From 26ec042f38eee5d5c0b2127cd199e27539e28cd0 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Sun, 16 Apr 2017 10:38:13 -0400 Subject: [PATCH 22/25] Remove trailing whitespace in terms.no.html (#1933) --- app/views/about/terms.no.html.haml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/about/terms.no.html.haml b/app/views/about/terms.no.html.haml index 5506cd863..32ec57ed1 100644 --- a/app/views/about/terms.no.html.haml +++ b/app/views/about/terms.no.html.haml @@ -71,6 +71,6 @@ %p Dette dokumentet er lisensiert under CC-BY-SA. De ble sist oppdatert 12. april 2017. %p - Dokumentet er en adoptert og endret versjon fra + Dokumentet er en adoptert og endret versjon fra = succeed '.' do = link_to 'Discourse privacy policy', 'https://github.com/discourse/discourse' From e0b5a94a4b98aa99369428af42216b308377c618 Mon Sep 17 00:00:00 2001 From: Matt Jankowski Date: Sun, 16 Apr 2017 10:38:29 -0400 Subject: [PATCH 23/25] Clean up check that account needs a webfinger update (#1932) --- app/services/follow_remote_account_service.rb | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/services/follow_remote_account_service.rb b/app/services/follow_remote_account_service.rb index dce712b40..14bc064d5 100644 --- a/app/services/follow_remote_account_service.rb +++ b/app/services/follow_remote_account_service.rb @@ -16,7 +16,7 @@ class FollowRemoteAccountService < BaseService return Account.find_local(username) if TagManager.instance.local_domain?(domain) account = Account.find_remote(username, domain) - return account unless account&.last_webfingered_at.nil? || 1.day.from_now(account.last_webfingered_at) < Time.now.utc + return account unless account_needs_webfinger_update?(account) Rails.logger.debug "Looking up webfinger for #{uri}" @@ -62,6 +62,10 @@ class FollowRemoteAccountService < BaseService private + def account_needs_webfinger_update?(account) + account&.last_webfingered_at.nil? || account.last_webfingered_at <= 1.day.ago + end + def get_feed(url) response = http_client.get(Addressable::URI.parse(url)) [response.to_s, Nokogiri::XML(response)] From 42c9d5111ad4e543a0ef32ee8a791f0fd19a9433 Mon Sep 17 00:00:00 2001 From: Eugen Date: Sun, 16 Apr 2017 16:42:46 +0200 Subject: [PATCH 24/25] Add README note about tagged releases (#1927) --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 0a28170b7..ea7b20a5c 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,14 @@ If you would like, you can [support the development of this project on Patreon][ - **Deployable via Docker** You don't need to mess with dependencies and configuration if you want to try Mastodon, if you have Docker and Docker Compose the deployment is extremely easy +## Checking out + +If you want a stable release for production use, you should use tagged releases. To checkout the latest available tagged version: + + git clone https://github.com/tootsuite/mastodon.git + cd mastodon + git checkout $(git describe --tags `git rev-list --tags --max-count=1`) + ## Configuration - `LOCAL_DOMAIN` should be the domain/hostname of your instance. This is **absolutely required** as it is used for generating unique IDs for everything federation-related From 99226aba9391535a20d3cc6dba0f45e5792c73d4 Mon Sep 17 00:00:00 2001 From: Ash Furrow Date: Sun, 16 Apr 2017 10:59:53 -0400 Subject: [PATCH 25/25] Adds note for instance admins. (#1925) * Adds note for instance admins. * Addresses feedback from #1925. --- ISSUE_TEMPLATE.md | 1 + 1 file changed, 1 insertion(+) diff --git a/ISSUE_TEMPLATE.md b/ISSUE_TEMPLATE.md index 8394b2424..c78bcb492 100644 --- a/ISSUE_TEMPLATE.md +++ b/ISSUE_TEMPLATE.md @@ -3,3 +3,4 @@ * * * * - [ ] I searched or browsed the repo’s other issues to ensure this is not a duplicate. +- [ ] This bug happens on a [tagged release](https://github.com/tootsuite/mastodon/releases) and not on `master` (If you're a user, don't worry about this).