diff --git a/app/controllers/api/v1/accounts/email_subscriptions_controller.rb b/app/controllers/api/v1/accounts/email_subscriptions_controller.rb new file mode 100644 index 0000000000..dcdd41f6db --- /dev/null +++ b/app/controllers/api/v1/accounts/email_subscriptions_controller.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class Api::V1::Accounts::EmailSubscriptionsController < Api::BaseController + before_action :set_account + before_action :require_feature_enabled! + before_action :require_account_permissions! + + def create + @account.email_subscriptions.create!(email: params[:email], locale: I18n.locale) + render_empty + end + + private + + def set_account + @account = Account.local.find(params[:account_id]) + end + + def require_feature_enabled! + head 404 unless Mastodon::Feature.email_subscriptions_enabled? + end + + def require_account_permissions! + head 404 if @account.unavailable? || !@account.user_can?(:manage_email_subscriptions) || !@account.user_email_subscriptions_enabled? + end +end diff --git a/app/controllers/email_subscriptions/confirmations_controller.rb b/app/controllers/email_subscriptions/confirmations_controller.rb new file mode 100644 index 0000000000..2750b68d4f --- /dev/null +++ b/app/controllers/email_subscriptions/confirmations_controller.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class EmailSubscriptions::ConfirmationsController < ApplicationController + layout 'auth' + + before_action :set_email_subscription + + def show + @email_subscription.confirm! unless @email_subscription.confirmed? + end + + private + + def set_email_subscription + @email_subscription = EmailSubscription.find_by!(confirmation_token: params[:confirmation_token]) + end +end diff --git a/app/controllers/mail_subscriptions_controller.rb b/app/controllers/mail_subscriptions_controller.rb deleted file mode 100644 index 34df75f63a..0000000000 --- a/app/controllers/mail_subscriptions_controller.rb +++ /dev/null @@ -1,39 +0,0 @@ -# frozen_string_literal: true - -class MailSubscriptionsController < ApplicationController - layout 'auth' - - skip_before_action :require_functional! - - before_action :set_user - before_action :set_type - - protect_from_forgery with: :null_session - - def show; end - - def create - @user.settings[email_type_from_param] = false - @user.save! - end - - private - - def set_user - @user = GlobalID::Locator.locate_signed(params[:token], for: 'unsubscribe') - not_found unless @user - end - - def set_type - @type = email_type_from_param - end - - def email_type_from_param - case params[:type] - when 'follow', 'reblog', 'favourite', 'mention', 'follow_request' - "notification_emails.#{params[:type]}" - else - not_found - end - end -end diff --git a/app/controllers/settings/privacy_controller.rb b/app/controllers/settings/privacy_controller.rb index 96efa03ccf..2716fce806 100644 --- a/app/controllers/settings/privacy_controller.rb +++ b/app/controllers/settings/privacy_controller.rb @@ -2,6 +2,7 @@ class Settings::PrivacyController < Settings::BaseController before_action :set_account + before_action :set_email_subscriptions_count def show; end @@ -24,4 +25,8 @@ class Settings::PrivacyController < Settings::BaseController def set_account @account = current_account end + + def set_email_subscriptions_count + @email_subscriptions_count = with_read_replica { @account.email_subscriptions.confirmed.count } + end end diff --git a/app/controllers/unsubscriptions_controller.rb b/app/controllers/unsubscriptions_controller.rb new file mode 100644 index 0000000000..aac58a3806 --- /dev/null +++ b/app/controllers/unsubscriptions_controller.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +class UnsubscriptionsController < ApplicationController + layout 'auth' + + skip_before_action :require_functional! + + before_action :set_recipient + before_action :set_type + before_action :set_scope + before_action :require_type_if_user! + + protect_from_forgery with: :null_session + + def show; end + + def create + case @scope + when :user + @recipient.settings[@type] = false + @recipient.save! + when :email_subscription + @recipient.destroy! + end + end + + private + + def set_recipient + @recipient = GlobalID::Locator.locate_signed(params[:token], for: 'unsubscribe') + not_found unless @recipient + end + + def set_scope + if @recipient.is_a?(User) + @scope = :user + elsif @recipient.is_a?(EmailSubscription) + @scope = :email_subscription + else + not_found + end + end + + def set_type + @type = email_type_from_param + end + + def require_type_if_user! + not_found if @recipient.is_a?(User) && @type.blank? + end + + def email_type_from_param + case params[:type] + when 'follow', 'reblog', 'favourite', 'mention', 'follow_request' + "notification_emails.#{params[:type]}" + end + end +end diff --git a/app/javascript/styles/entrypoints/mailer.scss b/app/javascript/styles/entrypoints/mailer.scss index fcbbd66f4c..725f4e4de4 100644 --- a/app/javascript/styles/entrypoints/mailer.scss +++ b/app/javascript/styles/entrypoints/mailer.scss @@ -721,6 +721,52 @@ table + p { line-height: 24px; } +// Banner item +.email-banner-table { + border-radius: 12px; + background-color: #1b001f; + background-image: url('../../images/mailer-new/common/header-bg-start.png'); + background-position: left top; + background-repeat: repeat; +} + +.email-banner-td { + padding: 24px 24px 14px; +} + +.email-banner-text-td { + p { + margin: 0 0 12px; + color: #fff; + font-size: 14px; + font-weight: 600; + line-height: 16.8px; + } + + .email-desktop-flex { + align-items: center; + } + + .email-btn-table { + background-color: #fff; + } + + .email-btn-td { + mso-padding-alt: 10px; + } + + .email-btn-a { + color: #181820; + padding-left: 10px; + padding-right: 10px; + } + + div + div { + margin-inline-start: auto; + margin-bottom: 12px; + } +} + // Checklist item .email-checklist-wrapper-td { padding: 4px 0; diff --git a/app/javascript/styles/mastodon/tables.scss b/app/javascript/styles/mastodon/tables.scss index 8e303aff68..1088781417 100644 --- a/app/javascript/styles/mastodon/tables.scss +++ b/app/javascript/styles/mastodon/tables.scss @@ -93,6 +93,26 @@ } } + &.mini-table { + border-top: 1px solid var(--color-border-primary); + width: 50%; + + & > tbody > tr > th, + & > tbody > tr > td { + padding: 12px 0; + } + + & > tbody > tr > th { + color: var(--color-text-secondary); + font-weight: 400; + } + + & > tbody > tr > td { + color: var(--color-text-primary); + font-weight: 600; + } + } + &.batch-table { & > thead > tr > th { background: var(--color-bg-primary); diff --git a/app/mailers/email_subscription_mailer.rb b/app/mailers/email_subscription_mailer.rb new file mode 100644 index 0000000000..35bd6da2f9 --- /dev/null +++ b/app/mailers/email_subscription_mailer.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +class EmailSubscriptionMailer < ApplicationMailer + include BulkMailSettingsConcern + include Redisable + + layout 'mailer' + + helper :accounts + helper :routing + helper :statuses + + before_action :set_subscription + before_action :set_unsubscribe_url + before_action :set_instance + before_action :set_skip_preferences_link + + after_action :use_bulk_mail_delivery_settings, except: [:confirmation] + after_action :set_list_headers + + default to: -> { @subscription.email } + + def confirmation + I18n.with_locale(locale) do + mail subject: default_i18n_subject + end + end + + def notification(statuses) + @statuses = statuses + + I18n.with_locale(locale) do + mail subject: default_i18n_subject(count: @statuses.size, name: @subscription.account.display_name, excerpt: @statuses.first.text.truncate(17)) + end + end + + private + + def set_list_headers + headers( + 'List-ID' => "<#{@subscription.account.username}.#{Rails.configuration.x.local_domain}>", + 'List-Unsubscribe-Post' => 'List-Unsubscribe=One-Click', + 'List-Unsubscribe' => "<#{@unsubscribe_url}>" + ) + end + + def set_subscription + @subscription = params[:subscription] + end + + def set_unsubscribe_url + @unsubscribe_url = unsubscribe_url(token: @subscription.to_sgid(for: 'unsubscribe').to_s) + end + + def set_instance + @instance = Rails.configuration.x.local_domain + end + + def set_skip_preferences_link + @skip_preferences_link = true + end + + def locale + @subscription.locale.presence || I18n.default_locale + end +end diff --git a/app/models/account.rb b/app/models/account.rb index ceb5f857a9..8c44db813a 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -193,8 +193,10 @@ class Account < ApplicationRecord :role, :locale, :shows_application?, + :email_subscriptions_enabled?, :prefers_noindex?, :time_zone, + :can?, to: :user, prefix: true, allow_nil: true diff --git a/app/models/concerns/account/associations.rb b/app/models/concerns/account/associations.rb index 089f08fc59..db2e996d0f 100644 --- a/app/models/concerns/account/associations.rb +++ b/app/models/concerns/account/associations.rb @@ -38,6 +38,7 @@ module Account::Associations has_many :status_pins has_many :statuses has_many :keypairs + has_many :email_subscriptions has_one :deletion_request, class_name: 'AccountDeletionRequest' has_one :follow_recommendation_suppression diff --git a/app/models/concerns/user/has_settings.rb b/app/models/concerns/user/has_settings.rb index f46a2ac53d..4fa8ab53fa 100644 --- a/app/models/concerns/user/has_settings.rb +++ b/app/models/concerns/user/has_settings.rb @@ -15,6 +15,10 @@ module User::HasSettings settings['noindex'] end + def email_subscriptions_enabled? + settings['email_subscriptions'] + end + def preferred_posting_language valid_locale_cascade(settings['default_language'], locale, I18n.locale) end diff --git a/app/models/email_subscription.rb b/app/models/email_subscription.rb new file mode 100644 index 0000000000..a965f06e31 --- /dev/null +++ b/app/models/email_subscription.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +# == Schema Information +# +# Table name: email_subscriptions +# +# id :bigint(8) not null, primary key +# confirmation_token :string +# confirmed_at :datetime +# email :string not null +# locale :string not null +# created_at :datetime not null +# updated_at :datetime not null +# account_id :bigint(8) not null +# + +class EmailSubscription < ApplicationRecord + belongs_to :account + + normalizes :email, with: ->(str) { str.squish.downcase } + + validates :email, presence: true, email_address: true, uniqueness: { scope: :account_id } + + scope :confirmed, -> { where.not(confirmed_at: nil) } + scope :unconfirmed, -> { where(confirmed_at: nil) } + + before_create :set_confirmation_token + + after_create_commit :send_confirmation_email + + def confirmed? + confirmed_at.present? + end + + def confirm! + touch(:confirmed_at) + end + + private + + def set_confirmation_token + self.confirmation_token = Devise.friendly_token unless confirmed? + end + + def send_confirmation_email + EmailSubscriptionMailer.with(subscription: self).confirmation.deliver_later + end +end diff --git a/app/models/user_role.rb b/app/models/user_role.rb index f2597e1c43..e98d9bc479 100644 --- a/app/models/user_role.rb +++ b/app/models/user_role.rb @@ -39,6 +39,7 @@ class UserRole < ApplicationRecord delete_user_data: (1 << 19), view_feeds: (1 << 20), invite_bypass_approval: (1 << 21), + manage_email_subscriptions: (1 << 22), }.freeze EVERYONE_ROLE_ID = -99 @@ -60,6 +61,10 @@ class UserRole < ApplicationRecord invite_bypass_approval ).freeze, + email: %i( + manage_email_subscriptions + ).freeze, + moderation: %i( view_dashboard view_audit_log diff --git a/app/models/user_settings.rb b/app/models/user_settings.rb index c5f02ae2e9..3a6e6f249b 100644 --- a/app/models/user_settings.rb +++ b/app/models/user_settings.rb @@ -16,6 +16,7 @@ class UserSettings setting :default_sensitive, default: false setting :default_privacy, default: nil, in: %w(public unlisted private) setting :default_quote_policy, default: 'public', in: %w(public followers nobody) + setting :email_subscriptions, default: false setting_inverse_alias :indexable, :noindex diff --git a/app/serializers/rest/account_serializer.rb b/app/serializers/rest/account_serializer.rb index e97baa8612..9fc5acee9d 100644 --- a/app/serializers/rest/account_serializer.rb +++ b/app/serializers/rest/account_serializer.rb @@ -22,6 +22,7 @@ class REST::AccountSerializer < ActiveModel::Serializer attribute :memorial, if: :memorial? attribute :feature_approval, if: -> { Mastodon::Feature.collections_enabled? } + attribute :email_subscriptions, if: -> { Mastodon::Feature.email_subscriptions_enabled? } class AccountDecorator < SimpleDelegator def self.model_name @@ -176,4 +177,8 @@ class REST::AccountSerializer < ActiveModel::Serializer current_user: object.feature_policy_for_account(current_user&.account), } end + + def email_subscriptions + object.user_can?(:manage_email_subscriptions) && object.user_email_subscriptions_enabled? + end end diff --git a/app/services/post_status_service.rb b/app/services/post_status_service.rb index e44ddda54c..3ef1502cd6 100644 --- a/app/services/post_status_service.rb +++ b/app/services/post_status_service.rb @@ -5,6 +5,12 @@ class PostStatusService < BaseService include Lockable include LanguagesHelper + # How much to delay sending an e-mail about a new post, to allow grouping multiple posts + EMAIL_DISTRIBUTION_DELAY = 5.minutes.freeze + + # If the job is not executed within this timeframe, it will lose its arguments + EMAIL_DISTRIBUTION_TTL = 1.hour.to_i + class UnexpectedMentionsError < StandardError attr_reader :accounts @@ -158,11 +164,26 @@ class PostStatusService < BaseService Trends.tags.register(@status) LinkCrawlWorker.perform_async(@status.id) DistributionWorker.perform_async(@status.id) + process_email_subscriptions! ActivityPub::DistributionWorker.perform_async(@status.id) PollExpirationNotifyWorker.perform_at(@status.poll.expires_at, @status.poll.id) if @status.poll ActivityPub::QuoteRequestWorker.perform_async(@status.quote.id) if @status.quote&.quoted_status.present? && !@status.quote&.quoted_status&.local? end + def process_email_subscriptions! + return unless Mastodon::Feature.email_subscriptions_enabled? && + @status.public_visibility? && (!@status.reply? || @status.in_reply_to_account_id == @status.account_id) && + @status.account.user_can?(:manage_email_subscriptions) && + @status.account.user_email_subscriptions_enabled? + + # To allow e-mail grouping, pass the arguments via a redis set and schedule + # a unique worker a few minutes in the future, in case the user makes subsequent + # posts within that time window + redis.sadd("email_subscriptions:#{@status.account_id}:next_batch", @status.id) + redis.expire("email_subscriptions:#{@status.account_id}:next_batch", EMAIL_DISTRIBUTION_TTL) + EmailDistributionWorker.perform_in(EMAIL_DISTRIBUTION_DELAY, @status.account_id) + end + def validate_media! if @options[:media_ids].blank? || !@options[:media_ids].is_a?(Enumerable) @media = [] diff --git a/app/views/email_subscription_mailer/confirmation.html.haml b/app/views/email_subscription_mailer/confirmation.html.haml new file mode 100644 index 0000000000..7d14d9ff59 --- /dev/null +++ b/app/views/email_subscription_mailer/confirmation.html.haml @@ -0,0 +1,20 @@ += content_for :heading do + = render 'application/mailer/heading', + image_url: full_asset_url(@subscription.account.avatar.url), + title: t('.title', name: display_name(@subscription.account)) + +%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } + %tr + %td.email-body-padding-td + %table.email-inner-card-table{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } + %tr + %td.email-inner-card-td.email-prose + %p= t '.instructions_to_confirm', name: display_name(@subscription.account), acct: "#{@subscription.account.username}@#{@instance}" + + = render 'application/mailer/button', text: t('.action'), url: email_subscriptions_confirmation_url(confirmation_token: @subscription.confirmation_token) + + %p= t '.instructions_to_ignore' + +- content_for :footer do + %p.email-footer-p= t('email_subscription_mailer.notification.footer.reason_for_email_html', name: display_name(@subscription.account), unsubscribe_path: @unsubscribe_url) + %p.email-footer-p= t('email_subscription_mailer.notification.footer.privacy_html', domain: @instance, privacy_policy_path: privacy_policy_path) diff --git a/app/views/email_subscription_mailer/confirmation.text.erb b/app/views/email_subscription_mailer/confirmation.text.erb new file mode 100644 index 0000000000..4d80bebc0d --- /dev/null +++ b/app/views/email_subscription_mailer/confirmation.text.erb @@ -0,0 +1,9 @@ +<%= t '.title', name: display_name(@subscription.account) %> + +=== + +<%= t '.instructions_to_confirm', name: display_name(@subscription.account), acct: "#{@subscription.account.username}@#{@instance}" %> + +=> <%= root_url(confirmation_token: @subscription.confirmation_token) %> + +<%= t '.instructions_to_ignore' %> diff --git a/app/views/email_subscription_mailer/notification.html.haml b/app/views/email_subscription_mailer/notification.html.haml new file mode 100644 index 0000000000..ad816ba460 --- /dev/null +++ b/app/views/email_subscription_mailer/notification.html.haml @@ -0,0 +1,36 @@ +%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } + - @statuses.each do |status| + %tr + %td.email-body-padding-td + %table.email-inner-card-table{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } + %tr + %td.email-inner-card-td + = render 'notification_mailer/status', status: status, time_zone: nil + +%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } + %tr + %td.email-body-padding-td + %table.email-w-full.email-checklist-wrapper-table{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } + %tr + %td.email-checklist-wrapper-td + %table.email-w-full.email-banner-table{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } + %tr + %td.email-banner-td + %table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } + %tr + %td.email-banner-text-td + .email-desktop-flex + /[if mso] +
+ %div + %p= t('.interact_with_this_post', count: @statuses.size) + /[if mso] + + %div + = render 'application/mailer/button', text: t('.create_account'), url: available_sign_up_path, has_arrow: false + /[if mso] +
+ +- content_for :footer do + %p.email-footer-p= t('.footer.reason_for_email_html', name: display_name(@subscription.account), unsubscribe_path: @unsubscribe_url) + %p.email-footer-p= t('.footer.privacy_html', domain: @instance, privacy_policy_path: privacy_policy_path) diff --git a/app/views/email_subscription_mailer/notification.text.erb b/app/views/email_subscription_mailer/notification.text.erb new file mode 100644 index 0000000000..7da5261b64 --- /dev/null +++ b/app/views/email_subscription_mailer/notification.text.erb @@ -0,0 +1,7 @@ +<%= t '.title', count: @statuses.size, name: display_name(@subscription.account), excerpt: truncate(@statuses.first.text, length: 17) %> + +=== + +<%- @statuses.each do |status| %> +<%= render 'notification_mailer/status', status: status %> +<%- end %> diff --git a/app/views/email_subscriptions/confirmations/show.html.haml b/app/views/email_subscriptions/confirmations/show.html.haml new file mode 100644 index 0000000000..a13504bb48 --- /dev/null +++ b/app/views/email_subscriptions/confirmations/show.html.haml @@ -0,0 +1,11 @@ +- content_for :page_title do + = t('.title') + +.simple_form + %h1.title + = t('.title') + %p.lead + = t('.success_html', name: content_tag(:strong, display_name(@email_subscription.account)), sender: content_tag(:strong, EmailSubscriptionMailer.default[:from])) + %p.lead + = t('.changed_your_mind') + = link_to t('.unsubscribe'), unsubscribe_url(token: @email_subscription.to_sgid(for: 'unsubscribe')) diff --git a/app/views/layouts/mailer.html.haml b/app/views/layouts/mailer.html.haml index 842312b853..5862f79712 100644 --- a/app/views/layouts/mailer.html.haml +++ b/app/views/layouts/mailer.html.haml @@ -12,6 +12,7 @@ %style{ 'data-premailer': 'ignore' } \.email a { color: inherit; text-decoration: none; } \.email-btn-hover:hover { background-color: #563acc !important; } + \.email-banner-text-td .email-btn-hover:hover { background-color: #fff !important; } /[if mso] @@ -73,15 +74,18 @@ %table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' } %tr %td.email-footer-td - %p.email-footer-p - = link_to root_url, class: 'email-footer-logo-a' do - = image_tag frontend_asset_url('images/mailer-new/common/logo-footer.png'), alt: 'Mastodon', width: 44, height: 44 - %p.email-footer-p - = t 'about.hosted_on', domain: site_hostname - %p.email-footer-p - = link_to t('application_mailer.notification_preferences'), settings_preferences_notifications_url - - if defined?(@unsubscribe_url) - · - = link_to t('application_mailer.unsubscribe'), @unsubscribe_url + - if content_for?(:footer) + = yield :footer + - else + %p.email-footer-p + = link_to root_url, class: 'email-footer-logo-a' do + = image_tag frontend_asset_url('images/mailer-new/common/logo-footer.png'), alt: 'Mastodon', width: 44, height: 44 + %p.email-footer-p + = t 'about.hosted_on', domain: site_hostname + %p.email-footer-p + = link_to t('application_mailer.notification_preferences'), settings_preferences_notifications_url + - if defined?(@unsubscribe_url) + · + = link_to t('application_mailer.unsubscribe'), @unsubscribe_url /[if mso] diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb index 87b0b2929c..1a78f33110 100644 --- a/app/views/layouts/mailer.text.erb +++ b/app/views/layouts/mailer.text.erb @@ -1,5 +1,9 @@ <%= yield %> --- -<%= t 'about.hosted_on', domain: site_hostname %> -<%= t('application_mailer.settings', link: settings_preferences_url) %> +<%- unless defined?(@skip_preferences_link) %> +<%= t('application_mailer.notification_preferences') %>: <%= settings_preferences_url %> +<%- end %> +<%- if defined?(@unsubscribe_url) %> +<%= t('application_mailer.unsubscribe') %>: <%= @unsubscribe_url %> +<%- end %> diff --git a/app/views/mail_subscriptions/create.html.haml b/app/views/mail_subscriptions/create.html.haml deleted file mode 100644 index 16ee486b00..0000000000 --- a/app/views/mail_subscriptions/create.html.haml +++ /dev/null @@ -1,9 +0,0 @@ -- content_for :page_title do - = t('mail_subscriptions.unsubscribe.title') - -.simple_form - %h1.title= t('mail_subscriptions.unsubscribe.complete') - %p.lead - = t('mail_subscriptions.unsubscribe.success_html', domain: content_tag(:strong, site_hostname), type: content_tag(:strong, I18n.t(@type, scope: 'mail_subscriptions.unsubscribe.emails')), email: content_tag(:strong, @user.email)) - %p.lead - = t('mail_subscriptions.unsubscribe.resubscribe_html', settings_path: settings_preferences_notifications_path) diff --git a/app/views/mail_subscriptions/show.html.haml b/app/views/mail_subscriptions/show.html.haml deleted file mode 100644 index 78de486457..0000000000 --- a/app/views/mail_subscriptions/show.html.haml +++ /dev/null @@ -1,20 +0,0 @@ -- content_for :page_title do - = t('mail_subscriptions.unsubscribe.title') - -.simple_form - %h1.title= t('mail_subscriptions.unsubscribe.title') - %p.lead - = t 'mail_subscriptions.unsubscribe.confirmation_html', - domain: content_tag(:strong, site_hostname), - type: content_tag(:strong, I18n.t(@type, scope: 'mail_subscriptions.unsubscribe.emails')), - email: content_tag(:strong, @user.email), - settings_path: settings_preferences_notifications_path - - = form_with url: unsubscribe_path do |form| - = form.hidden_field :token, - value: params[:token] - = form.hidden_field :type, - value: params[:type] - = form.button t('mail_subscriptions.unsubscribe.action'), - type: :submit, - class: 'btn' diff --git a/app/views/settings/privacy/show.html.haml b/app/views/settings/privacy/show.html.haml index 542d7b34a2..282f63f19e 100644 --- a/app/views/settings/privacy/show.html.haml +++ b/app/views/settings/privacy/show.html.haml @@ -41,5 +41,25 @@ .fields-group = ff.input :show_application, wrapper: :with_label + - if Mastodon::Feature.email_subscriptions_enabled? && current_user.can?(:manage_email_subscriptions) + %h2= t('privacy.email_subscriptions') + + %p.lead= t('privacy.email_subscriptions_hint_html') + + - if @email_subscriptions_count.positive? || @account.user_email_subscriptions_enabled? + .table-wrapper + %table.table.mini-table + %tbody + %tr + %th= t('email_subscriptions.status') + %td= @account.user_email_subscriptions_enabled? ? t('email_subscriptions.active') : t('email_subscriptions.inactive') + %tr + %th= t('email_subscriptions.subscribers') + %td= number_with_delimiter @email_subscriptions_count + + = f.simple_fields_for :settings, current_user.settings do |ff| + .fields-group + = ff.input :email_subscriptions, wrapper: :with_label + .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/unsubscriptions/create.html.haml b/app/views/unsubscriptions/create.html.haml new file mode 100644 index 0000000000..157cf26226 --- /dev/null +++ b/app/views/unsubscriptions/create.html.haml @@ -0,0 +1,12 @@ +- content_for :page_title do + = t('.title') + +.simple_form + %h1.title= t('.title') + %p.lead + - if @scope == :email_subscription + = t('.email_subscription.confirmation_html', name: display_name(@recipient.account)) + - elsif @scope == :user + = t('.user.confirmation_html', type: I18n.t(@type, scope: 'unsubscriptions.notification_emails'), domain: site_hostname) + + = link_to t('.action'), root_path, class: 'btn' diff --git a/app/views/unsubscriptions/show.html.haml b/app/views/unsubscriptions/show.html.haml new file mode 100644 index 0000000000..6f5f007612 --- /dev/null +++ b/app/views/unsubscriptions/show.html.haml @@ -0,0 +1,26 @@ +- content_for :page_title do + - if @scope == :user + = t('.user.title', type: I18n.t(@type, scope: 'unsubscriptions.notification_emails')) + - elsif @scope == :email_subscription + = t('.email_subscription.title', name: display_name(@recipient.account)) + +.simple_form + %h1.title + - if @scope == :user + = t('.user.title', type: I18n.t(@type, scope: 'unsubscriptions.notification_emails')) + - elsif @scope == :email_subscription + = t('.email_subscription.title', name: display_name(@recipient.account)) + %p.lead + - if @scope == :user + = t('.user.confirmation_html') + - elsif @scope == :email_subscription + = t('.email_subscription.confirmation_html') + + = form_with url: unsubscribe_path do |form| + = form.hidden_field :token, + value: params[:token] + = form.hidden_field :type, + value: params[:type] + = form.button t('.action'), + type: :submit, + class: 'btn' diff --git a/app/workers/email_distribution_worker.rb b/app/workers/email_distribution_worker.rb new file mode 100644 index 0000000000..41edcb932c --- /dev/null +++ b/app/workers/email_distribution_worker.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +class EmailDistributionWorker + include Sidekiq::Worker + include Redisable + + sidekiq_options lock: :until_executed, lock_ttl: 1.day.to_i + + def perform(account_id) + return unless Mastodon::Feature.email_subscriptions_enabled? + + @account = Account.find(account_id) + + return unless @account.user_can?(:manage_email_subscriptions) && @account.user_email_subscriptions_enabled? + + with_redis do |redis| + @status_ids = redis.smembers("email_subscriptions:#{account_id}:next_batch") + redis.srem("email_subscriptions:#{account_id}:next_batch", @status_ids) + end + + return if @account.email_subscriptions.confirmed.empty? || @status_ids.empty? + + statuses = Status.without_replies + .without_reblogs + .public_visibility + .where(id: @status_ids) + .to_a + + return if statuses.empty? + + @account.email_subscriptions.confirmed.find_each do |email_subscription| + EmailSubscriptionMailer.with(subscription: email_subscription).notification(statuses).deliver_later + end + rescue ActiveRecord::RecordNotFound + nil + end +end diff --git a/app/workers/scheduler/user_cleanup_scheduler.rb b/app/workers/scheduler/user_cleanup_scheduler.rb index 03544e2e98..20d895551a 100644 --- a/app/workers/scheduler/user_cleanup_scheduler.rb +++ b/app/workers/scheduler/user_cleanup_scheduler.rb @@ -11,6 +11,7 @@ class Scheduler::UserCleanupScheduler def perform clean_unconfirmed_accounts! clean_discarded_statuses! + clean_unconfirmed_email_subscriptions! end private @@ -32,4 +33,10 @@ class Scheduler::UserCleanupScheduler end end end + + def clean_unconfirmed_email_subscriptions! + EmailSubscription.unconfirmed.where(created_at: ..UNCONFIRMED_ACCOUNTS_MAX_AGE_DAYS.days.ago).find_in_batches do |batch| + EmailSubscription.where(id: batch.map(&:id)).delete_all + end + end end diff --git a/config/i18n-tasks.yml b/config/i18n-tasks.yml index b934696bda..49858b22d1 100644 --- a/config/i18n-tasks.yml +++ b/config/i18n-tasks.yml @@ -65,6 +65,7 @@ ignore_unused: - 'move_handler.carry_{mutes,blocks}_over_text' - 'admin_mailer.*.subject' - 'user_mailer.*.subject' + - 'email_subscription_mailer.*' - 'notification_mailer.*' - 'imports.overwrite_preambles.{following,blocking,muting,domain_blocking,bookmarks,lists}_html.*' - 'imports.preambles.{following,blocking,muting,domain_blocking,bookmarks,lists}_html.*' diff --git a/config/locales/ar.yml b/config/locales/ar.yml index f689f79007..218b12a065 100644 --- a/config/locales/ar.yml +++ b/config/locales/ar.yml @@ -1267,7 +1267,6 @@ ar: application_mailer: notification_preferences: تغيير تفضيلات البريد الإلكتروني salutation: "%{name}،" - settings: 'تغيير تفضيلات البريد الإلكتروني: %{link}' unsubscribe: إلغاء الاشتراك view: 'اعرض:' view_profile: اعرض الصفحة التعريفية @@ -1764,9 +1763,6 @@ ar: title: تاريخ المصادقة mail_subscriptions: unsubscribe: - action: نعم، ألغِ الاشتراك - complete: غير مشترك - confirmation_html: هل أنت متأكد أنك تريد إلغاء الاشتراك عن تلقي %{type} لماستدون على %{domain} إلى بريدك الإلكتروني %{email}؟ يمكنك دائمًا إعادة الاشتراك من إعدادات إشعارات البريد الإلكتروني. emails: notification_emails: favourite: إرسال إشعارات التفضيلات بالبريد الإلكتروني @@ -1774,9 +1770,6 @@ ar: follow_request: إرسال إشعارات الطلبات بالبريد الإلكتروني mention: إشعارات رسائل البريد عندما يَذكُرك أحدهم reblog: رسائل البريد الخاصة بالمنشورات المعاد نشرها - resubscribe_html: إذا قمت بإلغاء الاشتراك عن طريق الخطأ، يمكنك إعادة الاشتراك من إعدادات إشعارات البريد الإلكتروني. - success_html: لن تتلقّ بعد الآن %{type} لماستدون مِن %{domain} على بريدك الإلكتروني %{email}. - title: إلغاء الاشتراك media_attachments: validations: images_and_video: ليس بالإمكان إرفاق فيديو في منشور يحتوي مسبقا على صور diff --git a/config/locales/be.yml b/config/locales/be.yml index 9d06a26030..7a30f3a6ec 100644 --- a/config/locales/be.yml +++ b/config/locales/be.yml @@ -1273,7 +1273,6 @@ be: application_mailer: notification_preferences: Змяніць налады эл. пошты salutation: "%{name}," - settings: 'Змяніць налады эл. пошты: %{link}' unsubscribe: Адпісацца view: 'Паглядзець:' view_profile: Паглядзець профіль @@ -1737,9 +1736,6 @@ be: title: Гісторыя ўваходаў mail_subscriptions: unsubscribe: - action: Так, адпісацца - complete: Адпісаны - confirmation_html: Вы ўпэўнены, што жадаеце адмовіцца ад атрымання %{type} з Mastodon на дамене %{domain} на сваю электронную пошту %{email}? Вы заўсёды можаце паўторна падпісацца ў наладах апавяшчэнняў па электроннай пошце. emails: notification_emails: favourite: апавяшчэнні на пошту пра упадабанае @@ -1747,9 +1743,6 @@ be: follow_request: апавяшчэнні на пошту пра запыты на падпіску mention: апавяшчэнні на пошту пра згадванні reblog: апавяшчэнні на пошту пра пашырэнні - resubscribe_html: Калі вы адмовіліся ад падпіскі памылкова, вы можаце зноў падпісацца ў наладах апавяшчэнняў па электроннай пошце. - success_html: Вы больш не будзеце атрымліваць %{type} на сваю электронную пошту %{email} ад Mastodon на дамене %{domain}. - title: Адпісацца media_attachments: validations: images_and_video: Немагчыма далучыць відэа да допісу, які ўжо змяшчае выявы diff --git a/config/locales/bg.yml b/config/locales/bg.yml index 514cb2f6e9..35ef73d521 100644 --- a/config/locales/bg.yml +++ b/config/locales/bg.yml @@ -1186,7 +1186,6 @@ bg: application_mailer: notification_preferences: Промяна на предпочитанията за е-поща salutation: "%{name}," - settings: 'Промяна на предпочитанията за имейл: %{link}' unsubscribe: Стоп на абонамента view: 'Преглед:' view_profile: Преглед на профила @@ -1601,9 +1600,6 @@ bg: title: Историята на удостоверяване mail_subscriptions: unsubscribe: - action: Да, да се спре абонамента - complete: Спрян абонамент - confirmation_html: Наистина ли искате да спрете абонамента от получаването на %{type} за Mastodon в %{domain} към имейла си при %{email}? Може винаги пак да се абонирате от своите настройки за известяване по е-поща. emails: notification_emails: favourite: е-писма за известия с любими @@ -1611,9 +1607,6 @@ bg: follow_request: е-писма със заявки за следване mention: е-писма с известия за споменаване reblog: е-писма с известия за подсилване - resubscribe_html: Ако погрешка сте спрели абонамента, то може пак да се абонирате от своите настройки за известия по е-поща. - success_html: Повече няма да получавате %{type} за Mastodon на %{domain} към имейла си при %{email}. - title: Спиране на абонамента media_attachments: validations: images_and_video: Не мога да прикача видеоклип към публикация, която вече съдържа изображения diff --git a/config/locales/br.yml b/config/locales/br.yml index c1b2906548..850d7dc093 100644 --- a/config/locales/br.yml +++ b/config/locales/br.yml @@ -683,9 +683,6 @@ br: authentication_methods: password: ger-tremen webauthn: alc’hwezioù surentez - mail_subscriptions: - unsubscribe: - action: Ya, digoumanantiñ media_attachments: validations: images_and_video: N'haller stagañ ur video ouzh un embannadur a zo fotoioù gantañ dija diff --git a/config/locales/ca.yml b/config/locales/ca.yml index a7ef2e7c38..6ac65afd6f 100644 --- a/config/locales/ca.yml +++ b/config/locales/ca.yml @@ -1194,7 +1194,6 @@ ca: application_mailer: notification_preferences: Canviar les preferències de correu-e salutation: "%{name}," - settings: 'Canviar les preferències de correu-e: %{link}' unsubscribe: Cancel·la la subscripció view: 'Visualització:' view_profile: Mostra el perfil @@ -1616,9 +1615,6 @@ ca: title: Historial d'autenticació mail_subscriptions: unsubscribe: - action: Sí, canceŀla la subscripció - complete: Subscripció cancel·lada - confirmation_html: Segur que vols donar-te de baixa de rebre %{type} de Mastodon a %{domain} a %{email}? Sempre pots subscriure't de nou des de la configuració de les notificacions per correu electrònic. emails: notification_emails: favourite: notificacions dels favorits per correu electrònic @@ -1626,9 +1622,6 @@ ca: follow_request: correus electrònics de peticions de seguiment mention: correus electrònics de notificacions de mencions reblog: correus electrònics de notificacions d'impulsos - resubscribe_html: Si ets dones de baixa per error pots donar-te d'alta des de la configuració de les notificacions per correu electrònic. - success_html: Ja no rebràs %{type} de Mastodon a %{domain} a %{email}. - title: Cancel·la la subscripció media_attachments: validations: images_and_video: No es pot adjuntar un vídeo a una publicació que ja contingui imatges diff --git a/config/locales/cs.yml b/config/locales/cs.yml index 0d324d1453..6b2bfd4539 100644 --- a/config/locales/cs.yml +++ b/config/locales/cs.yml @@ -1267,7 +1267,6 @@ cs: application_mailer: notification_preferences: Změnit předvolby e-mailu salutation: "%{name}," - settings: 'Změnit předvolby e-mailu: %{link}' unsubscribe: Přestat odebírat view: 'Zobrazit:' view_profile: Zobrazit profil @@ -1729,9 +1728,6 @@ cs: title: Historie přihlášení mail_subscriptions: unsubscribe: - action: Ano, odeberte odběr - complete: Odběr byl odhlášen - confirmation_html: Jste si jisti, že chcete odhlásit odběr %{type} pro Mastodon na %{domain} na váš e-mail %{email}? Vždy se můžete znovu přihlásit ve svém nastavení e-mailových oznámení. emails: notification_emails: favourite: e-mailové oznámení při oblíbení @@ -1739,9 +1735,6 @@ cs: follow_request: e-mail při žádost o sledování mention: e-mailové oznámení při zmínění reblog: e-mailové oznámení při boostu - resubscribe_html: Pokud jste se odhlásili omylem, můžete se znovu přihlásit ve svých nastavení e-mailových oznámení. - success_html: Již nebudete dostávat %{type} pro Mastodon na %{domain} na vaši e-mailovou adresu %{email}. - title: Odhlásit odběr media_attachments: validations: images_and_video: K příspěvku, který již obsahuje obrázky, nelze připojit video diff --git a/config/locales/cy.yml b/config/locales/cy.yml index 560bc3e6f3..a81826d7c2 100644 --- a/config/locales/cy.yml +++ b/config/locales/cy.yml @@ -1313,7 +1313,6 @@ cy: application_mailer: notification_preferences: Newid dewisiadau e-bost salutation: "%{name}," - settings: 'Newid dewisiadau e-bost: %{link}' unsubscribe: Dad-danysgrifio view: 'Gweld:' view_profile: Gweld proffil @@ -1817,9 +1816,6 @@ cy: title: Hanes dilysu mail_subscriptions: unsubscribe: - action: Iawn, dad-danysgrifio - complete: Dad-danysgrifiwyd - confirmation_html: Ydych chi'n siŵr eich bod am ddad-danysgrifio rhag derbyn %{type} Mastodon ar %{domain} i'ch e-bost yn %{email}? Gallwch ail-danysgrifio o'ch gosodiadau hysbysu e-bost rhywbryd eto. emails: notification_emails: favourite: e-bost hysbysu hoffi @@ -1827,9 +1823,6 @@ cy: follow_request: e-byst ceisiadau dilyn mention: e-byst hysbysu crybwylliadau reblog: e-byst hysbysiadau hybu - resubscribe_html: Os ydych wedi dad-danysgrifio trwy gamgymeriad, gallwch ail-danysgrifio drwy'ch gosodiadau hysbysu e-bost. - success_html: Ni fyddwch bellach yn derbyn %{type} ar gyfer Mastodon ar %{domain} i'ch e-bost am %{email}. - title: Dad-danysgrifio media_attachments: validations: images_and_video: Methu atodi fideo i bostiad sydd eisoes yn cynnwys delweddau diff --git a/config/locales/da.yml b/config/locales/da.yml index c83750d58c..ca3d45aca3 100644 --- a/config/locales/da.yml +++ b/config/locales/da.yml @@ -1231,7 +1231,6 @@ da: application_mailer: notification_preferences: Skift e-mailpræferencer salutation: "%{name}" - settings: 'Skift e-mailpræferencer: %{link}' unsubscribe: Afmeld notifikationer view: 'Vis:' view_profile: Vis profil @@ -1655,9 +1654,6 @@ da: title: Godkendelseshistorik mail_subscriptions: unsubscribe: - action: Ja, afmeld - complete: Afmeldt - confirmation_html: Er du sikker på, at du vil afmelde modtagelse af %{type} for Mastodon på %{domain} til din e-mail på %{email}? Du kan altid tilmelde dig igen fra dine indstillinger for e-mail-notifikationer. emails: notification_emails: favourite: e-mailnotifikationer om favoritmarkeringer @@ -1665,9 +1661,6 @@ da: follow_request: e-mailnotifikationer om følgeanmodninger mention: e-mailnotifikationer om omtaler reblog: e-mailnotifikationer om fremhævelser - resubscribe_html: Har du afmeldt dig ved en fejl, kan du gentilmelde dig via indstillingerne for e-mail-notifikationer. - success_html: Du vil ikke længere modtage %{type} for Mastodon på %{domain} til din e-mail %{email}. - title: Opsig abonnement media_attachments: validations: images_and_video: En video kan ikke vedhæftes et indlæg med billedindhold diff --git a/config/locales/de.yml b/config/locales/de.yml index 4981257442..f95f2f7940 100644 --- a/config/locales/de.yml +++ b/config/locales/de.yml @@ -1231,7 +1231,6 @@ de: application_mailer: notification_preferences: E-Mail-Einstellungen ändern salutation: "%{name}," - settings: 'E-Mail-Einstellungen ändern: %{link}' unsubscribe: Abbestellen view: 'Siehe:' view_profile: Profil anzeigen @@ -1655,9 +1654,6 @@ de: title: Anmeldeverlauf mail_subscriptions: unsubscribe: - action: Ja, abbestellen - complete: Abbestellt - confirmation_html: Möchtest du %{type} für Mastodon auf %{domain} an deine E-Mail-Adresse %{email} wirklich abbestellen? Du kannst dies später in den Einstellungen Benachrichtigungen per E-Mail rückgängig machen. emails: notification_emails: favourite: E-Mail-Benachrichtigungen bei Favoriten @@ -1665,9 +1661,6 @@ de: follow_request: E-Mail-Benachrichtigungen bei Follower-Anfragen mention: E-Mail-Benachrichtigungen bei Erwähnungen reblog: E-Mail-Benachrichtigungen bei geteilten Beiträgen - resubscribe_html: Falls du etwas irrtümlich abbestellt hast, kannst du das in den Einstellungen Benachrichtigungen per E-Mail rückgängig machen. - success_html: Du wirst nicht länger %{type} für Mastodon auf %{domain} an deine E-Mail-Adresse %{email} erhalten. - title: Abbestellen media_attachments: validations: images_and_video: Es kann kein Video an einen Beitrag angehängt werden, der bereits Bilder enthält diff --git a/config/locales/el.yml b/config/locales/el.yml index de95a5b725..5b834356fb 100644 --- a/config/locales/el.yml +++ b/config/locales/el.yml @@ -1231,7 +1231,6 @@ el: application_mailer: notification_preferences: Αλλαγή προτιμήσεων email salutation: "%{name}," - settings: 'Αλλαγή προτιμήσεων email: %{link}' unsubscribe: Κατάργηση εγγραφής view: 'Προβολή:' view_profile: Προβολή προφίλ @@ -1655,9 +1654,6 @@ el: title: Ιστορικό ελέγχου ταυτότητας mail_subscriptions: unsubscribe: - action: Ναι, κατάργηση συνδρομής - complete: Η συνδρομή καταργήθηκε - confirmation_html: Σίγουρα θες να καταργήσεις την εγγραφή σου για %{type} για το Mastodon στο %{domain} στο email σου %{email}; Μπορείς πάντα να εγγραφείς ξανά από τις ρυθμίσεις ειδοποιήσεων email. emails: notification_emails: favourite: ειδοποιήσεις email για αγαπημένα @@ -1665,9 +1661,6 @@ el: follow_request: email για αιτήματα ακολούθησης mention: ειδοποιήσεις email για επισημάνσεις reblog: ειδοποιήσεις email για ενίσχυση - resubscribe_html: Αν έχεις καταργήσει την εγγραφή σου κατά λάθος, μπορείς να εγγραφείς εκ νέου από τις ρυθμίσεις ειδοποίησης email. - success_html: Δεν θα λαμβάνεις πλέον %{type} για το Mastodon στο %{domain} στο email σου στο %{email}. - title: Κατάργηση συνδρομής media_attachments: validations: images_and_video: Δεν γίνεται να προσθέσεις βίντεο σε ανάρτηση που ήδη περιέχει εικόνες diff --git a/config/locales/en-GB.yml b/config/locales/en-GB.yml index 815378fbdd..81843c2c4d 100644 --- a/config/locales/en-GB.yml +++ b/config/locales/en-GB.yml @@ -1229,7 +1229,6 @@ en-GB: application_mailer: notification_preferences: Change email preferences salutation: "%{name}," - settings: 'Change email preferences: %{link}' unsubscribe: Unsubscribe view: 'View:' view_profile: View profile @@ -1651,9 +1650,6 @@ en-GB: title: Authentication history mail_subscriptions: unsubscribe: - action: Yes, unsubscribe - complete: Unsubscribed - confirmation_html: Are you sure you want to unsubscribe from receiving %{type} for Mastodon on %{domain} to your email at %{email}? You can always re-subscribe from your email notification settings. emails: notification_emails: favourite: favourite notification emails @@ -1661,9 +1657,6 @@ en-GB: follow_request: follow request emails mention: mention notification emails reblog: boost notification emails - resubscribe_html: If you've unsubscribed by mistake, you can re-subscribe from your email notification settings. - success_html: You'll no longer receive %{type} for Mastodon on %{domain} to your email at %{email}. - title: Unsubscribe media_attachments: validations: images_and_video: Cannot attach a video to a post that already contains images diff --git a/config/locales/en.yml b/config/locales/en.yml index a745021dc1..97c4809aa3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -762,6 +762,7 @@ en: categories: administration: Administration devops: DevOps + email: Email invites: Invites moderation: Moderation special: Special @@ -790,6 +791,8 @@ en: manage_blocks_description: Allows users to block email providers and IP addresses manage_custom_emojis: Manage Custom Emojis manage_custom_emojis_description: Allows users to manage custom emojis on the server + manage_email_subscriptions: Manage Email Subscriptions + manage_email_subscriptions_description: Allow users to subscribe to users with this permission by email manage_federation: Manage Federation manage_federation_description: Allows users to block or allow federation with other domains, and control deliverability manage_invites: Manage Invites @@ -1231,7 +1234,6 @@ en: application_mailer: notification_preferences: Change email preferences salutation: "%{name}," - settings: 'Change email preferences: %{link}' unsubscribe: Unsubscribe view: 'View:' view_profile: View profile @@ -1419,6 +1421,38 @@ en: basic_information: Basic information hint_html: "Customize what people see on your public profile and next to your posts. Other people are more likely to follow you back and interact with you when you have a filled out profile and a profile picture." other: Other + email_subscription_mailer: + confirmation: + action: Confirm email address + instructions_to_confirm: Confirm you'd like to receive emails from %{name} (@%{acct}) when they publish new posts. + instructions_to_ignore: If you're not sure why you received this email, you can delete it. You will not be subscribed if you don't click on the link above. + subject: Confirm your email address + title: Get email updates from %{name}? + notification: + create_account: Create a Mastodon account + footer: + privacy_html: Emails are sent from %{domain}, a server powered by Mastodon. To understand how this server processes your personal data, refer to the Privacy Policy. + reason_for_email_html: You're receiving this email because you opted into email updates from %{name}. Don't want to receive these emails? Unsubscribe + interact_with_this_post: + one: Interact with this post and discover more like it. + other: Interact with these posts and discover more. + subject: + one: 'New post: "%{excerpt}"' + other: New posts from %{name} + title: + one: 'New post: "%{excerpt}"' + other: New posts from %{name} + email_subscriptions: + active: Active + confirmations: + show: + changed_your_mind: Changed your mind? + success_html: You'll now start receiving emails when %{name} publishes new posts. Add %{sender} to your contacts so these posts don't end up in your Spam folder. + title: You're signed up + unsubscribe: Unsubscribe + inactive: Inactive + status: Status + subscribers: Subscribers emoji_styles: auto: Auto native: Native @@ -1653,21 +1687,6 @@ en: failed_sign_in_html: Failed sign-in attempt with %{method} from %{ip} (%{browser}) successful_sign_in_html: Successful sign-in with %{method} from %{ip} (%{browser}) title: Authentication history - mail_subscriptions: - unsubscribe: - action: Yes, unsubscribe - complete: Unsubscribed - confirmation_html: Are you sure you want to unsubscribe from receiving %{type} for Mastodon on %{domain} to your email at %{email}? You can always re-subscribe from your email notification settings. - emails: - notification_emails: - favourite: favorite notification emails - follow: follow notification emails - follow_request: follow request emails - mention: mention notification emails - reblog: boost notification emails - resubscribe_html: If you've unsubscribed by mistake, you can re-subscribe from your email notification settings. - success_html: You'll no longer receive %{type} for Mastodon on %{domain} to your email at %{email}. - title: Unsubscribe media_attachments: validations: images_and_video: Cannot attach a video to a post that already contains images @@ -1806,6 +1825,8 @@ en: posting_defaults: Posting defaults public_timelines: Public timelines privacy: + email_subscriptions: Send posts via email + email_subscriptions_hint_html: Add an email sign-up form to your profile that appears for logged-out users. When visitors enter their email address and opt in, Mastodon will send email updates for your public posts. hint_html: "Customize how you want your profile and your posts to be found. A variety of features in Mastodon can help you reach a wider audience when enabled. Take a moment to review these settings to make sure they fit your use case." privacy: Privacy privacy_hint_html: Control how much you want to disclose for the benefit of others. People discover interesting profiles and cool apps by browsing other people's follows and seeing which apps they post from, but you may prefer to keep it hidden. @@ -2069,6 +2090,28 @@ en: resume_app_authorization: Resume application authorization role_requirement: "%{domain} requires you to set up Two-Factor Authentication before you can use Mastodon." webauthn: Security keys + unsubscriptions: + create: + action: Go to server homepage + email_subscription: + confirmation_html: You'll no longer receive emails from %{name}. + title: You are unsubscribed + user: + confirmation_html: You'll no longer receive %{type} from Mastodon on %{domain}. + notification_emails: + favourite: favorite notification emails + follow: follow notification emails + follow_request: follow request emails + mention: mention notification emails + reblog: boost notification emails + show: + action: Unsubscribe + email_subscription: + confirmation_html: You'll stop receiving emails when this account publishes new posts. + title: Unsubscribe from %{name}? + user: + confirmation_html: You'll stop receiving %{type} from Mastodon on %{domain}. + title: Unsubscribe from %{type}? user_mailer: announcement_published: description: 'The administrators of %{domain} are making an announcement:' diff --git a/config/locales/eo.yml b/config/locales/eo.yml index de46b0a7be..d9712beb53 100644 --- a/config/locales/eo.yml +++ b/config/locales/eo.yml @@ -1182,7 +1182,6 @@ eo: application_mailer: notification_preferences: Ŝanĝi retpoŝtajn preferojn salutation: "%{name}," - settings: 'Ŝanĝi retpoŝtajn preferojn: %{link}' unsubscribe: Malabonu view: 'Vidi:' view_profile: Vidi profilon @@ -1592,9 +1591,6 @@ eo: title: Aŭtentiga historio mail_subscriptions: unsubscribe: - action: Jes, malabonu - complete: Malabonita - confirmation_html: Ĉu vi certas, ke vi volas malaboni je ricevi %{type} por Mastodon ĉe %{domain} al via retpoŝto ĉe %{email}? Vi ĉiam povas reaboni de viaj retpoŝtaj sciigaj agordoj. emails: notification_emails: favourite: sciigoj retpoŝtaj de ŝatataj @@ -1602,9 +1598,6 @@ eo: follow_request: retpoŝtajn petoj de sekvado mention: sciigoj retpoŝtaj de mencioj reblog: sciigoj retpoŝtaj de diskonigoj - resubscribe_html: Se vi malabonis erare, vi povas reaboni de viaj retpoŝtaj sciigaj agordoj. - success_html: Vi ne plu ricevos %{type} por Mastodon ĉe %{domain} al via retpoŝto ĉe %{email}. - title: Malaboni media_attachments: validations: images_and_video: Aldoni videon al mesaĝo, kiu jam havas bildojn ne eblas diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml index e4c8e85622..248c3db979 100644 --- a/config/locales/es-AR.yml +++ b/config/locales/es-AR.yml @@ -1231,7 +1231,6 @@ es-AR: application_mailer: notification_preferences: Cambiar configuración de correo electrónico salutation: "%{name}:" - settings: 'Cambiar configuración de correo electrónico: %{link}' unsubscribe: Desuscribirse view: 'Visitá:' view_profile: Ver perfil @@ -1655,9 +1654,6 @@ es-AR: title: Historial de autenticación mail_subscriptions: unsubscribe: - action: Sí, desuscribir - complete: Desuscripto - confirmation_html: ¿Estás seguro que querés dejar de recibir %{type} de Mastodon en %{domain} a tu correo electrónico %{email}? Siempre podrás volver a suscribirte desde la configuración de notificaciones por correo electrónico.. emails: notification_emails: favourite: notificaciones de favoritos por correo electrónico @@ -1665,9 +1661,6 @@ es-AR: follow_request: notificaciones de solicitudes de seguimiento por correo electrónico mention: notificaciones de menciones por correo electrónico reblog: notificaciones de adhesiones por correo electrónico - resubscribe_html: Si te desuscribiste por error, podés resuscribirte desde la configuración de notificaciones por correo electrónico. - success_html: Ya no recibirás %{type} de mastodon en %{domain} a tu correo electrónico %{email}. - title: Desuscribirse media_attachments: validations: images_and_video: No se puede adjuntar un video a un mensaje que ya contenga imágenes diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml index 44677285a7..48e19919f0 100644 --- a/config/locales/es-MX.yml +++ b/config/locales/es-MX.yml @@ -1231,7 +1231,6 @@ es-MX: application_mailer: notification_preferences: Cambiar preferencias de correo electrónico salutation: "%{name}:" - settings: 'Cambiar preferencias de correo: %{link}' unsubscribe: Cancelar suscripción view: 'Vista:' view_profile: Ver perfil @@ -1655,9 +1654,6 @@ es-MX: title: Historial de autenticación mail_subscriptions: unsubscribe: - action: Sí, darse de baja - complete: Has cancelado tu suscripción - confirmation_html: ¿Estás seguro de que quieres dejar de recibir %{type} de Mastodon en %{domain} a tu correo %{email}? Siempre podrás volver a suscribirte de nuevo desde los ajustes de notificación por correo. emails: notification_emails: favourite: correos de notificación de favoritos @@ -1665,9 +1661,6 @@ es-MX: follow_request: correos electrónicos de solicitud de seguimiento mention: correos de notificación de menciones reblog: correos de notificación de impulsos - resubscribe_html: Si te has dado de baja por error, puedes volver a darte de alta desde tus ajustes de notificaciones por correo. - success_html: Ya no recibirás %{type} de Mastodon en %{domain} a tu correo %{email}. - title: Cancelar suscripción media_attachments: validations: images_and_video: No se puede adjuntar un video a una publicación que ya contenga imágenes diff --git a/config/locales/es.yml b/config/locales/es.yml index c7de434673..15c7d44ba5 100644 --- a/config/locales/es.yml +++ b/config/locales/es.yml @@ -1231,7 +1231,6 @@ es: application_mailer: notification_preferences: Cambiar preferencias de correo electrónico salutation: "%{name}:" - settings: 'Cambiar preferencias de correo: %{link}' unsubscribe: Cancelar suscripción view: 'Vista:' view_profile: Ver perfil @@ -1655,9 +1654,6 @@ es: title: Historial de autenticación mail_subscriptions: unsubscribe: - action: Sí, cancelar suscripción - complete: Has cancelado tu suscripción - confirmation_html: ¿Estás seguro de que quieres dejar de recibir %{type} de Mastodon en %{domain} a tu correo %{email}? Siempre podrás volver a suscribirte de nuevo desde los ajustes de notificación por correo. emails: notification_emails: favourite: correos de notificación de favoritos @@ -1665,9 +1661,6 @@ es: follow_request: correos de notificación de solicitud de seguidor mention: correos de notificación de menciones reblog: correos de notificación de impulsos - resubscribe_html: Si te has dado de baja por error, puedes volver a darte de alta desde tus ajustes de notificaciones por correo. - success_html: Ya no recibirás %{type} de Mastodon en %{domain} a tu correo %{email}. - title: Cancelar suscripición media_attachments: validations: images_and_video: No se puede adjuntar un video a una publicación que ya contenga imágenes diff --git a/config/locales/et.yml b/config/locales/et.yml index ddd0572cfc..91b227b5aa 100644 --- a/config/locales/et.yml +++ b/config/locales/et.yml @@ -1214,7 +1214,6 @@ et: application_mailer: notification_preferences: Muuda e-posti eelistusi salutation: "%{name}!" - settings: 'Muuda e-posti eelistusi: %{link}' unsubscribe: Loobu tellimisest view: 'Vaade:' view_profile: Vaata profiili @@ -1636,9 +1635,6 @@ et: title: Autentimise ajalugu mail_subscriptions: unsubscribe: - action: Jah, lõpeta tellimine - complete: Tellimine lõpetatud - confirmation_html: Kas oled kindel, et soovid loobuda %{type} tellimisest oma e-postiaadressile %{email} Mastodonist kohas %{domain}? Saad alati uuesti tellida oma e-posti teavituste seadetest. emails: notification_emails: favourite: lemmikuks märkimise teavituskirjade @@ -1646,9 +1642,6 @@ et: follow_request: jälgimistaotluste teavituskirjade mention: mainimiste teavituskirjade reblog: jagamiste teavituskirjade - resubscribe_html: Kui loobusid tellimisest ekslikult, saad uuesti tellida oma e-posti teavituste seadetest. - success_html: Sa ei saa enam %{type} teateid oma e-postile %{email} Mastodonist kohas %{domain}. - title: Loobu tellimisest media_attachments: validations: images_and_video: Ei saa lisada video postitusele, milles on juba pildid diff --git a/config/locales/eu.yml b/config/locales/eu.yml index 0102508797..41a6d57895 100644 --- a/config/locales/eu.yml +++ b/config/locales/eu.yml @@ -1171,7 +1171,6 @@ eu: application_mailer: notification_preferences: Posta elektronikoaren lehentasunak aldatu salutation: "%{name}," - settings: 'Posta elektronikoaren lehentasunak aldatu: %{link}' unsubscribe: Kendu harpidetza view: 'Ikusi:' view_profile: Ikusi profila @@ -1538,11 +1537,6 @@ eu: title: Autentifikazioen historia mail_subscriptions: unsubscribe: - action: Bai, kendu harpidetza - complete: Harpidetza kenduta - confirmation_html: |- - Ziur Mastodonen %{domain} zerbitzariko %{type} %{email} helbide elektronikoan jasotzeari utzi nahi diozula? - Beti harpidetu zaitezke berriro eposta jakinarazpenen hobespenetan. emails: notification_emails: favourite: zure argitalpena gogoko egin dutenaren jakinarazpen e-mailak @@ -1550,9 +1544,6 @@ eu: follow_request: jarraipen-eskaeren jakinarazpen e-mailak mention: aipamenen jakinarazpen e-mailak reblog: bultzaden jakinarazpen e-mailak - resubscribe_html: Nahi gabe utzi badiozu jakinarazpenak jasotzeari, berriro harpidetu zaitezke e-mail jakinarazpenen hobespenetan. - success_html: Ez duzu Mastodonen %{domain} zerbitzariko %{type} jasoko %{email} helbide elektronikoan. - title: Kendu harpidetza media_attachments: validations: images_and_video: Ezin da irudiak dituen bidalketa batean bideo bat erantsi diff --git a/config/locales/fa.yml b/config/locales/fa.yml index b166d1f304..1fd310cb8f 100644 --- a/config/locales/fa.yml +++ b/config/locales/fa.yml @@ -1210,7 +1210,6 @@ fa: application_mailer: notification_preferences: تغییر ترجیحات رایانامه salutation: "%{name}،" - settings: 'تغییر ترجیحات رایانامه: %{link}' unsubscribe: لغو اشتراک view: 'نمایش:' view_profile: دیدن نمایه @@ -1632,9 +1631,6 @@ fa: title: تاریخچهٔ تأیید هویت mail_subscriptions: unsubscribe: - action: بله. لغو اشتراک - complete: لغو اشتراک شد - confirmation_html: مطمئنید که می‌خواهید اشتراک %{type} را از ماستودون روی %{domain} برای رایانامهٔ %{email} لغو کنید؟ همواره می‌توانید از تنظیمات آگاهی رایانامه‌ای دوباره مشترک شوید. emails: notification_emails: favourite: رایانامه‌های آگاهی برگزیدن @@ -1642,9 +1638,6 @@ fa: follow_request: رایانامه‌های درخواست پی‌گیری mention: رایانامه‌های آگاهی اشاره reblog: رایانامه‌های آگاهی تقویت - resubscribe_html: اگر اشتراک را اشتباهی لغو کردید می‌توانید از تنظیمات آگاهی رایانامه‌ای دوباره مشترک شوید. - success_html: دیگر %{type} را از ماستودون روی %{domain} برای رایانامهٔ %{email} نخواهید گرفت. - title: لغو اشتراک media_attachments: validations: images_and_video: نمی‌توان برای نوشته‌ای که تصویر دارد ویدیو بارگذاری کرد diff --git a/config/locales/fi.yml b/config/locales/fi.yml index 77d938b6b7..782d59c315 100644 --- a/config/locales/fi.yml +++ b/config/locales/fi.yml @@ -1231,7 +1231,6 @@ fi: application_mailer: notification_preferences: Muuta sähköpostiasetuksia salutation: "%{name}" - settings: 'Muuta sähköpostiasetuksia: %{link}' unsubscribe: Lopeta tilaus view: 'Näytä:' view_profile: Näytä profiili @@ -1655,9 +1654,6 @@ fi: title: Todennushistoria mail_subscriptions: unsubscribe: - action: Kyllä, peru tilaus - complete: Tilaus lopetettiin - confirmation_html: Haluatko varmasti lopettaa Mastodonin sähköposti-ilmoitusten vastaanottamisen aiheesta %{type} palvelimelta %{domain} osoitteeseesi %{email}? Voit tilata ilmoitusviestejä milloin tahansa uudelleen sähköposti-ilmoitusten asetuksista. emails: notification_emails: favourite: sähköposti-ilmoituksia suosikkeihin lisäämisistä @@ -1665,9 +1661,6 @@ fi: follow_request: sähköposti-ilmoituksia seurantapyynnöistä mention: sähköposti-ilmoituksia maininnoista reblog: sähköposti-ilmoituksia tehostuksista - resubscribe_html: Jos olet perunut tilauksen erehdyksessä, voit tilata ilmoitusviestejä uudelleen sähköposti-ilmoitusten asetuksista. - success_html: Sinulle ei enää lähetetä Mastodonin %{type} palvelimelta %{domain} osoitteeseen %{email}. - title: Lopeta tilaus media_attachments: validations: images_and_video: Videota ei voi liittää tilapäivitykseen, jossa on jo kuvia diff --git a/config/locales/fo.yml b/config/locales/fo.yml index 36b2dc5c68..49e768f4a3 100644 --- a/config/locales/fo.yml +++ b/config/locales/fo.yml @@ -1229,7 +1229,6 @@ fo: application_mailer: notification_preferences: Broyt teldupostastillingar salutation: "%{name}" - settings: 'Broyt teldupostastillingar: %{link}' unsubscribe: Strika hald view: 'Vís:' view_profile: Vís vanga @@ -1651,9 +1650,6 @@ fo: title: Samgildissøga mail_subscriptions: unsubscribe: - action: Ja, strika hald - complete: Hald strikað - confirmation_html: Ert tú vís/ur í, at tú vil gevast at móttaka %{type} fyri Mastodon á %{domain} til tína teldupostadressu á %{email}? Tú kanst altíð gera haldið virkið aftur frá tínum teldupostfráboðanarstillingum. emails: notification_emails: favourite: yndisfráboðanarteldupostar @@ -1661,9 +1657,6 @@ fo: follow_request: fylg umbønir um teldupost mention: nevn fráboðanarteldupostar reblog: framhevja fráboðanarpostar - resubscribe_html: Um tú hevur strikað haldið av misgávum, so kanst tú tekna haldið av nýggjum í tínum teldupostfráboðarstillingum. - success_html: Tú fer ikki longur at móttaka %{type} fyri Mastodon á %{domain} til tín teldupost á %{email}. - title: Strika hald media_attachments: validations: images_and_video: Kann ikki viðfesta sjónfílu til ein post, sum longu inniheldur myndir diff --git a/config/locales/fr-CA.yml b/config/locales/fr-CA.yml index 14e7e5a3d8..dca8edc581 100644 --- a/config/locales/fr-CA.yml +++ b/config/locales/fr-CA.yml @@ -1234,7 +1234,6 @@ fr-CA: application_mailer: notification_preferences: Modification des préférences de la messagerie salutation: "%{name}," - settings: 'Modifier les préférences de la messagerie : %{link}' unsubscribe: Se désabonner view: 'Voir :' view_profile: Voir le profil @@ -1658,9 +1657,6 @@ fr-CA: title: Historique d'authentification mail_subscriptions: unsubscribe: - action: Oui, me désabonner - complete: Désabonné·e - confirmation_html: Êtes-vous sûr de vouloir vous désabonner de la réception de %{type} pour Mastodon sur %{domain} à votre adresse e-mail %{email} ? Vous pouvez toujours vous réabonner à partir de vos paramètres de notification par messagerie. emails: notification_emails: favourite: e-mails de notifications de favoris @@ -1668,9 +1664,6 @@ fr-CA: follow_request: e-mails de demandes d’abonnements mention: e-mails de notifications de mentions reblog: e-mails de notifications de partages - resubscribe_html: Si vous vous êtes désinscrit par erreur, vous pouvez vous réinscrire à partir de vos paramètres de notification par e-mail. - success_html: Vous ne recevrez plus de %{type} pour Mastodon sur %{domain} à votre adresse e-mail à %{email}. - title: Se désabonner media_attachments: validations: images_and_video: Impossible de joindre une vidéo à un message contenant déjà des images diff --git a/config/locales/fr.yml b/config/locales/fr.yml index 3407c4515b..7b7968b7ce 100644 --- a/config/locales/fr.yml +++ b/config/locales/fr.yml @@ -1234,7 +1234,6 @@ fr: application_mailer: notification_preferences: Modification des préférences de la messagerie salutation: "%{name}," - settings: 'Modifier les préférences de la messagerie : %{link}' unsubscribe: Se désabonner view: 'Voir :' view_profile: Voir le profil @@ -1658,9 +1657,6 @@ fr: title: Historique d'authentification mail_subscriptions: unsubscribe: - action: Oui, se désinscrire - complete: Désinscrit - confirmation_html: Êtes-vous sûr de vouloir vous désabonner de la réception de %{type} pour Mastodon sur %{domain} à votre adresse e-mail %{email} ? Vous pouvez toujours vous réabonner à partir de vos paramètres de notification par messagerie. emails: notification_emails: favourite: e-mails de notifications de favoris @@ -1668,9 +1664,6 @@ fr: follow_request: e-mails de demandes d’abonnements mention: e-mails de notifications de mentions reblog: e-mails de notifications de partages - resubscribe_html: Si vous vous êtes désinscrit par erreur, vous pouvez vous réinscrire à partir de vos paramètres de notification par e-mail. - success_html: Vous ne recevrez plus de %{type} pour Mastodon sur %{domain} à votre adresse e-mail à %{email}. - title: Se désinscrire media_attachments: validations: images_and_video: Impossible de joindre une vidéo à un message contenant déjà des images diff --git a/config/locales/fy.yml b/config/locales/fy.yml index 4959b1adaa..c017132e75 100644 --- a/config/locales/fy.yml +++ b/config/locales/fy.yml @@ -1189,7 +1189,6 @@ fy: application_mailer: notification_preferences: E-mailynstellingen wizigje salutation: "%{name}," - settings: 'E-mailfoarkarren wizigje: %{link}' unsubscribe: Ofmelde view: 'Besjoch:' view_profile: Profyl besjen @@ -1597,9 +1596,6 @@ fy: title: Oanmeldskiednis mail_subscriptions: unsubscribe: - action: Ja, ôfmelde - complete: Ofmelden - confirmation_html: Binne jo wis dat jo jo ôfmelde wolle foar it ûntfangen fan %{type} fan Mastodon op %{domain} op jo e-mailadres %{email}? Jo kinne jo altyd opnij abonnearje yn jo ynstellingen foar e-mailmeldingen. emails: notification_emails: favourite: e-mailmeldingen foar favoriten @@ -1607,9 +1603,6 @@ fy: follow_request: e-mailmeldingen foar folchfersiken mention: e-mailmeldingen foar fermeldingen reblog: e-mailmeldingen foar boosts - resubscribe_html: As jo jo mei fersin ôfmeld hawwe, kinne jo jo opnij abonnearje yn jo ynstellingen foar e-mailmeldingen. - success_html: Jo ûntfange net langer %{type} fan Mastodon op %{domain} op jo e-mailadres %{email}. - title: Ofmelde media_attachments: validations: images_and_video: In fideo kin net oan in berjocht mei ôfbyldingen keppele wurde diff --git a/config/locales/ga.yml b/config/locales/ga.yml index 1295a4efc5..d75af42526 100644 --- a/config/locales/ga.yml +++ b/config/locales/ga.yml @@ -1296,7 +1296,6 @@ ga: application_mailer: notification_preferences: Athraigh roghanna ríomhphoist salutation: "%{name}," - settings: 'Athraigh sainroghanna ríomhphoist: %{link}' unsubscribe: Díliostáil view: 'Amharc:' view_profile: Féach ar phróifíl @@ -1780,9 +1779,6 @@ ga: title: Stair fíordheimhnithe mail_subscriptions: unsubscribe: - action: Sea, díliostáil - complete: Gan liostáil - confirmation_html: An bhfuil tú cinnte gur mhaith leat díliostáil ó %{type} a fháil do Mastodon ar %{domain} chuig do ríomhphost ag %{email}? Is féidir leat liostáil arís i gcónaí ó do socruithe fógra ríomhphoist. emails: notification_emails: favourite: ríomhphoist fógra is fearr leat @@ -1790,9 +1786,6 @@ ga: follow_request: lean ríomhphoist iarratais mention: trácht ar ríomhphoist fógra reblog: ríomhphoist fógraí a threisiú - resubscribe_html: Má dhíliostáil tú de dhearmad, is féidir leat liostáil arís ó do socruithe fógra ríomhphoist. - success_html: Ní bhfaighidh tú %{type} le haghaidh Mastodon ar %{domain} chuig do ríomhphost ag %{email} a thuilleadh. - title: Díliostáil media_attachments: validations: images_and_video: Ní féidir físeán a cheangal le postáil a bhfuil íomhánna ann cheana féin diff --git a/config/locales/gd.yml b/config/locales/gd.yml index c619e7818b..fc11600ca4 100644 --- a/config/locales/gd.yml +++ b/config/locales/gd.yml @@ -1248,7 +1248,6 @@ gd: application_mailer: notification_preferences: Atharraich roghainnean a’ phuist-d salutation: "%{name}," - settings: 'Atharraich roghainnean a’ phuist-d: %{link}' unsubscribe: Cuir crìoch air an fho-sgrìobhadh view: 'Faic:' view_profile: Seall a’ phròifil @@ -1710,9 +1709,6 @@ gd: title: Eachdraidh an dearbhaidh mail_subscriptions: unsubscribe: - action: Tha, cuir crìoch air an fho-sgrìobhadh - complete: Chaidh crìoch a chur air an fho-sgrìobhadh - confirmation_html: A bheil thu cinnteach nach eil thu airson %{type} fhaighinn tuilleadh o Mhastodon air %{domain} dhan post-d agad aig %{email}? ’S urrainn dhut fo-sgrìobhadh a-rithist uair sam bith o roghainnean a’ puist-d agad. emails: notification_emails: favourite: puist-d le brathan mu annsachdan @@ -1720,9 +1716,6 @@ gd: follow_request: puist-d le brathan mu iarrtasan leantainn mention: puist-d le brathan mu iomraidhean reblog: puist-d le brathan mu bhrosnachaidhean - resubscribe_html: Ma chuir thu crìoch air an fho-sgrìobhadh le mearachd, ’s urrainn dhut fo-sgrìobhadh a-rithist o roghainnean a’ puist-d agad. - success_html: Chan fhaigh thu %{type} o Mhastodon air %{domain} dhan phost-d agad aig %{email} tuilleadh. - title: Cuir crìoch air an fho-sgrìobhadh media_attachments: validations: images_and_video: Chan urrainn dhut video a cheangal ri post sa bheil dealbh mu thràth diff --git a/config/locales/gl.yml b/config/locales/gl.yml index 469f7badd7..307029de98 100644 --- a/config/locales/gl.yml +++ b/config/locales/gl.yml @@ -1231,7 +1231,6 @@ gl: application_mailer: notification_preferences: Cambiar preferencias de correo salutation: "%{name}," - settings: 'Cambiar preferencias de correo: %{link}' unsubscribe: Anular subscrición view: 'Vista:' view_profile: Ver perfil @@ -1655,9 +1654,6 @@ gl: title: Historial de autenticación mail_subscriptions: unsubscribe: - action: Si, retirar subscrición - complete: Subscrición anulada - confirmation_html: Tes a certeza de querer retirar a subscrición a Mastodon en %{domain} para recibir %{type} no teu correo electrónico en %{email}? Poderás volver a subscribirte desde os axustes de notificacións por correo. emails: notification_emails: favourite: notificacións de favorecidas @@ -1665,9 +1661,6 @@ gl: follow_request: notificacións de solicitudes de seguimento mention: notificacións de mencións reblog: notificacións de promocións - resubscribe_html: Se por un erro eliminaches a subscrición, podes volver a subscribirte desde os axustes de notificacións por correo electrónico. - success_html: Non vas recibir %{type} de Mastodon en %{domain} no enderezo %{email}. - title: Anular subscrición media_attachments: validations: images_and_video: Non podes anexar un vídeo a unha publicación que xa contén imaxes diff --git a/config/locales/he.yml b/config/locales/he.yml index d18d44689d..2fe1446b4e 100644 --- a/config/locales/he.yml +++ b/config/locales/he.yml @@ -1273,7 +1273,6 @@ he: application_mailer: notification_preferences: שינוי העדפות דוא"ל salutation: "%{name}," - settings: 'שינוי הגדרות דוא"ל: %{link}' unsubscribe: בטל מנוי view: 'תצוגה:' view_profile: צפיה בפרופיל @@ -1737,9 +1736,6 @@ he: title: הסטוריית אימותים mail_subscriptions: unsubscribe: - action: כן, לבטל הרשמה - complete: הפסקת הרשמה - confirmation_html: יש לאשר את ביטול ההרשמה להודעות %{type} ממסטודון בשרת %{domain} לכתובת הדואל %{email}. תמיד אפשר להרשם מחדש בכיוונוני הודעות דואל. emails: notification_emails: favourite: הודעות דואל לגבי חיבובים @@ -1747,9 +1743,6 @@ he: follow_request: הודעות דואל לגבי בקשות מעקב mention: הודעות דואל לגבי איזכורים reblog: הודעות דואל לגבי הידהודים - resubscribe_html: אם ביטול ההרשמה היה בטעות, ניתן להרשם מחדש מתוך מסך הגדרות ההרשמה שלך. - success_html: לא יגיעו אליך יותר הודעות %{type} משרת מסטודון %{domain} לכתובת הדואל %{email}. - title: הפסקת הרשמה media_attachments: validations: images_and_video: לא ניתן להוסיף וידאו להודעה שכבר מכילה תמונות diff --git a/config/locales/hu.yml b/config/locales/hu.yml index f8cbce5cc3..921f05f2b8 100644 --- a/config/locales/hu.yml +++ b/config/locales/hu.yml @@ -1231,7 +1231,6 @@ hu: application_mailer: notification_preferences: E-mail-beállítások módosítása salutation: "%{name}!" - settings: 'E-mail-beállítások módosítása: %{link}' unsubscribe: Leiratkozás view: 'Megtekintés:' view_profile: Profil megtekintése @@ -1655,9 +1654,6 @@ hu: title: Hitelesítési történet mail_subscriptions: unsubscribe: - action: Igen, leiratkozás - complete: Leiratkozva - confirmation_html: 'Biztos, hogy leiratkozol arról, hogy %{type} típusú üzeneteket kapj a(z) %{domain} Mastodon-kiszolgálótól erre a címedre: %{email}? Bármikor újra feliratkozhatsz az e-mail-értesítési beállításokban.' emails: notification_emails: favourite: kedvencnek jelölés értesítő e-mailjei @@ -1665,9 +1661,6 @@ hu: follow_request: követési kérések e-mailjei mention: megemlítés értesítő e-mailjei reblog: megtolás értesítő e-mailjei - resubscribe_html: Ha tévedésből iratkoztál le, újra feliratkozhatsz az e-mail-értesítési beállításoknál. - success_html: 'Mostantól nem kapsz %{type} típusú üzeneket a(z) %{domain} Mastodon-kiszolgálón erre a címedre: %{email}.' - title: Leiratkozás media_attachments: validations: images_and_video: Nem csatolhatsz videót olyan bejegyzéshez, amelyhez már csatoltál képet diff --git a/config/locales/ia.yml b/config/locales/ia.yml index 3fea1ed91c..f090d52469 100644 --- a/config/locales/ia.yml +++ b/config/locales/ia.yml @@ -1207,7 +1207,6 @@ ia: application_mailer: notification_preferences: Cambiar preferentias de e-mail salutation: "%{name}," - settings: 'Cambiar preferentias de e-mail: %{link}' unsubscribe: Cancellar subscription view: 'Visita:' view_profile: Vider profilo @@ -1622,9 +1621,6 @@ ia: title: Historia de authentication mail_subscriptions: unsubscribe: - action: Si, cancellar subscription - complete: Desubscribite - confirmation_html: Es tu secur de voler cancellar le subscription al %{type} de Mastodon sur %{domain} pro tu adresse de e-mail %{email}? Tu pote sempre resubscriber te a partir del parametros de notification in e-mail. emails: notification_emails: favourite: notificationes de favorites in e-mail @@ -1632,9 +1628,6 @@ ia: follow_request: requestas de sequimento in e-mail mention: notificationes de mentiones in e-mail reblog: notificationes de impulsos in e-mail - resubscribe_html: Si tu ha cancellate le subscription in error, tu pote resubscriber te a partir del parametros de notification in e-mail. - success_html: Tu non recipera plus %{type} pro Mastodon sur %{domain} a tu adresse de e-mail %{email}. - title: Desubcriber media_attachments: validations: images_and_video: Impossibile annexar un video a un message que jam contine imagines diff --git a/config/locales/ie.yml b/config/locales/ie.yml index 1576504444..b28cbbb70b 100644 --- a/config/locales/ie.yml +++ b/config/locales/ie.yml @@ -1328,11 +1328,6 @@ ie: failed_sign_in_html: Fallit prova de apertion de session per %{method} de %{ip} (%{browser}) successful_sign_in_html: Successosi apertion de session per %{method} de %{ip} (%{browser}) title: Historie de autentication - mail_subscriptions: - unsubscribe: - action: Yes, desabonnar - complete: Desabonnat - title: Desabonnar media_attachments: validations: images_and_video: On ne posse atachar un video a un posta quel ja contene images diff --git a/config/locales/io.yml b/config/locales/io.yml index 58ae0f015d..85aa06234a 100644 --- a/config/locales/io.yml +++ b/config/locales/io.yml @@ -1104,7 +1104,6 @@ io: application_mailer: notification_preferences: Chanjar retpostopreferaji salutation: "%{name}," - settings: 'Chanjar retpostopreferaji: %{link}' unsubscribe: Desabonez view: 'Vidar:' view_profile: Videz profilo @@ -1461,11 +1460,6 @@ io: failed_sign_in_html: Falita enirprob per %{method} de %{ip} (%{browser}) successful_sign_in_html: Sucesoza eniro per %{method} de %{ip} (%{browser}) title: Yurizeshistorio - mail_subscriptions: - unsubscribe: - action: Yes, desabonez - complete: Desabonita - title: Desabonez media_attachments: validations: images_and_video: Ne povas addonar video ad afisho qua ja enhavas imaji diff --git a/config/locales/is.yml b/config/locales/is.yml index 1ea18a39d4..77a4feb512 100644 --- a/config/locales/is.yml +++ b/config/locales/is.yml @@ -1233,7 +1233,6 @@ is: application_mailer: notification_preferences: Breyta kjörstillingum tölvupósts salutation: "%{name}," - settings: 'Breyta kjörstillingum tölvupósts: %{link}' unsubscribe: Taka úr áskrift view: 'Skoða:' view_profile: Skoða notandasnið @@ -1659,9 +1658,6 @@ is: title: Auðkenningarferill mail_subscriptions: unsubscribe: - action: Já, hætta í áskrift - complete: Hætta í áskrift - confirmation_html: Ertu viss um að þú viljir hætta áskrift sendinga á %{type} fyrir Mastodon á %{domain} til póstfangsins þíns %{email}? Þú getur alltaf aftur gerst áskrifandi í stillingunum fyrir tilkynningar í tölvupósti. emails: notification_emails: favourite: tilkynningum í tölvupósti um eftirlæti @@ -1669,9 +1665,6 @@ is: follow_request: tilkynningum í tölvupósti um beiðnir um að fylgjast með mention: tilkynningum í tölvupósti um tilvísanir reblog: tilkynningum í tölvupósti um endurbirtingar - resubscribe_html: Ef þú hættir áskrift fyrir mistök, geturðu alltaf aftur gerst áskrifandi í stillingunum fyrir tilkynningar í tölvupósti. - success_html: Þú munt ekki lengur fá sendingar með %{type} fyrir Mastodon á %{domain} á póstfangið þitt %{email}. - title: Taka úr áskrift media_attachments: validations: images_and_video: Ekki er hægt að hengja myndskeið við færslu sem þegar inniheldur myndir diff --git a/config/locales/it.yml b/config/locales/it.yml index e01bed8b97..ed80d9b377 100644 --- a/config/locales/it.yml +++ b/config/locales/it.yml @@ -1231,7 +1231,6 @@ it: application_mailer: notification_preferences: Modifica le preferenze e-mail salutation: "%{name}," - settings: 'Modifica le preferenze e-mail: %{link}' unsubscribe: Disiscriviti view: 'Guarda:' view_profile: Mostra profilo @@ -1655,9 +1654,6 @@ it: title: Cronologia delle autenticazioni mail_subscriptions: unsubscribe: - action: Sì, annulla l'iscrizione - complete: Iscrizione annullata - confirmation_html: Si è sicuri di voler annullare l'iscrizione per non ricevere %{type} per Mastodon su %{domain} sulla tua e-mail %{email}? Puoi sempre reiscriverti dalle tue impostazioni di notifica e-mail. emails: notification_emails: favourite: e-mail di notifica preferite @@ -1665,9 +1661,6 @@ it: follow_request: segui le e-mail di richiesta mention: menziona le e-mail di notifica reblog: e-mail di notifica per le condivisioni - resubscribe_html: Se hai annullato l'iscrizione per errore, puoi reiscriverti tramite le impostazioni di notifica e-mail. - success_html: Non riceverai più %{type} per Mastodon su %{domain} al tuo indirizzo e-mail %{email}. - title: Disiscriviti media_attachments: validations: images_and_video: Impossibile allegare video a un post che contiene già immagini diff --git a/config/locales/ja.yml b/config/locales/ja.yml index b1e2fdbef7..df8967eebe 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -1169,7 +1169,6 @@ ja: application_mailer: notification_preferences: メール設定の変更 salutation: "%{name}さん" - settings: 'メール設定の変更: %{link}' unsubscribe: 購読解除 view: 'リンク:' view_profile: プロフィールを表示 @@ -1560,9 +1559,6 @@ ja: title: 認証履歴 mail_subscriptions: unsubscribe: - action: 購読を解除する - complete: 購読を解除しました - confirmation_html: Mastodon (%{domain}) による %{email} 宛の「%{type}」の配信を停止します。再度必要になった場合はメール通知の設定からいつでも再開できます。 emails: notification_emails: favourite: お気に入りの通知メール @@ -1570,9 +1566,6 @@ ja: follow_request: フォローリクエストの通知メール mention: 返信の通知メール reblog: ブーストの通知メール - resubscribe_html: 誤って解除した場合はメール通知の設定から再購読できます。 - success_html: Mastodon (%{domain}) から %{email} への「%{type}」の配信が停止されました。 - title: 購読の解除 media_attachments: validations: images_and_video: 既に画像が追加されているため、動画を追加することはできません diff --git a/config/locales/kab.yml b/config/locales/kab.yml index 35bffc2810..93dc72865c 100644 --- a/config/locales/kab.yml +++ b/config/locales/kab.yml @@ -567,7 +567,6 @@ kab: application_mailer: notification_preferences: Snifel imenyafen n imayl salutation: "%{name}," - settings: 'Snifel imenyafen n imayl: %{link}' view: 'Ẓaṛ:' view_profile: Ssken-d amaɣnu view_status: Ssken-d tasuffiɣt diff --git a/config/locales/ko.yml b/config/locales/ko.yml index a5e90f9894..ae7d13ff0f 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -1194,7 +1194,6 @@ ko: application_mailer: notification_preferences: 이메일 설정 변경 salutation: "%{name} 님," - settings: '이메일 설정 변경: %{link}' unsubscribe: 구독 해제 view: '보기:' view_profile: 프로필 보기 @@ -1596,9 +1595,6 @@ ko: title: 인증 이력 mail_subscriptions: unsubscribe: - action: 네, 구독 취소합니다 - complete: 구독 취소됨 - confirmation_html: 정말로 %{domain}에서 %{email}로 보내는 마스토돈의 %{type}에 대한 구독을 취소하시겠습니까? 언제든지 이메일 알림 설정에서 다시 구독할 수 있습니다. emails: notification_emails: favourite: 좋아요 알림 이메일 @@ -1606,9 +1602,6 @@ ko: follow_request: 팔로우 요청 이메일 mention: 멘션 알림 이메일 reblog: 부스트 알림 이메일 - resubscribe_html: 만약 실수로 구독 취소를 했다면 이메일 알림 설정에서 다시 구독할 수 있습니다. - success_html: 이제 더이상 %{domain}의 마스토돈에서 %{email}로 %{type} 알림을 보내지 않습니다. - title: 구독 취소 media_attachments: validations: images_and_video: 이미 사진이 첨부된 게시물엔 동영상을 첨부할 수 없습니다. diff --git a/config/locales/lad.yml b/config/locales/lad.yml index aeffb477be..78eadbb953 100644 --- a/config/locales/lad.yml +++ b/config/locales/lad.yml @@ -1135,7 +1135,6 @@ lad: application_mailer: notification_preferences: Troka preferensyas de posta salutation: "%{name}," - settings: 'Troka preferensyas de posta: %{link}' unsubscribe: Dezabona view: 'Mira:' view_profile: Ve profil @@ -1505,9 +1504,6 @@ lad: title: Estoria de autentifikasyon mail_subscriptions: unsubscribe: - action: Si, dezabona - complete: Dezabonado - confirmation_html: Estas siguro de ke ya no keres risivir %{type} de Mastodon en %{domain} a tu posta elektronika %{email}? Syempre podras reabonarte dizde las opsyones de avizos por posta.. emails: notification_emails: favourite: avizos de favoritos por posta @@ -1515,9 +1511,6 @@ lad: follow_request: avizos de solisitasyones de segimyento por posta mention: avizos de enmentaduras por posta reblog: avizos de repartajasyones por posta - resubscribe_html: Si tyenes deabonado por yerro, puedes reabonar en tus opsyones de avizos por posta elektronika. - success_html: Ya no risiviras %{type} de Mastodon en %{domain} a tu posta en %{email}. - title: Dezabona media_attachments: validations: images_and_video: No se puede adjuntar un video a un estado ke ya kontenga imajes diff --git a/config/locales/lt.yml b/config/locales/lt.yml index b807451ece..8ce2304a40 100644 --- a/config/locales/lt.yml +++ b/config/locales/lt.yml @@ -1078,7 +1078,6 @@ lt: emails: notification_emails: reblog: dalintis pranešimų el. pašto laiškais - success_html: Daugiau negausi %{type} „Mastodon“ domene %{domain} į savo el. paštą %{email}. media_attachments: validations: images_and_video: Negalima pridėti video prie statuso, kuris jau turi nuotrauką diff --git a/config/locales/lv.yml b/config/locales/lv.yml index 1a31034a59..fb96e9400a 100644 --- a/config/locales/lv.yml +++ b/config/locales/lv.yml @@ -1174,7 +1174,6 @@ lv: application_mailer: notification_preferences: Mainīt e-pasta uztādījumus salutation: "%{name}," - settings: 'Mainīt e-pasta uztādījumus: %{link}' unsubscribe: Atcelt abonēšanu view: 'Skatīt:' view_profile: Skatīt profilu @@ -1598,9 +1597,6 @@ lv: title: Autentifikācijas vēsture mail_subscriptions: unsubscribe: - action: Jā, atcelt abonēšanu - complete: Anulēts - confirmation_html: Vai tiešām atteikties no %{type} saņemšanas savā e-pasta adresē %{email} par %{domain} esošo Mastodon? Vienmēr var abonēt no jauna savos e-pasta paziņojumu iestatījumos. emails: notification_emails: favourite: izlases paziņojumu e-pasta ziņojumi @@ -1608,9 +1604,6 @@ lv: follow_request: sekošanas pieprasījumu e-pasta ziņojumi mention: pieminēšanas paziņojumu e-pasta ziņojumi reblog: pastiprinājumu paziņojumu e-pasta ziņojumi - resubscribe_html: Ja abonements tika atcelts kļūdas dēļ, abonēt no jauna var savos e-pasta paziņojumu iestatījumos. - success_html: Tu vairs savā e-pasta adresē %{email} nesaņemsi %{type} par %{domain} esošo Mastodon. - title: Atcelt abonēšanu media_attachments: validations: images_and_video: Nevar pievienot videoklipu tādai ziņai, kura jau satur attēlus diff --git a/config/locales/ms.yml b/config/locales/ms.yml index 6edddd4323..21470409e2 100644 --- a/config/locales/ms.yml +++ b/config/locales/ms.yml @@ -1280,12 +1280,9 @@ ms: title: Sejarah pengesahan mail_subscriptions: unsubscribe: - action: Ya, nyahlanggan - complete: Menyahlanggan emails: notification_emails: favourite: emel pemberitahuan sukaan - title: Hentikan langganan media_attachments: validations: images_and_video: Tidak boleh melampirkan video pada pos yang sudah mengandungi imej diff --git a/config/locales/my.yml b/config/locales/my.yml index f8f69586a9..fd0bbcb005 100644 --- a/config/locales/my.yml +++ b/config/locales/my.yml @@ -1275,11 +1275,6 @@ my: failed_sign_in_html: "%{ip} (%{browser}) မှ %{method} ဖြင့် အကောင့်ဝင်ရောက်ခြင်း မအောင်မြင်ပါ" successful_sign_in_html: "%{ip} (%{browser}) မှ %{method} ဖြင့် အကောင့်ဝင်၍ရပါပြီ" title: အထောက်အထားမှတ်တမ်း - mail_subscriptions: - unsubscribe: - action: ဟုတ်ကဲ့၊ စာရင်းမှ ဖြုတ်လိုက်ပါပြီ - complete: စာရင်းမှထွက်ရန် - title: စာရင်းမှထွက်ရန် media_attachments: validations: images_and_video: ရုပ်ပုံပါရှိပြီးသားပို့စ်တွင် ဗီဒီယို ပူးတွဲ၍မရပါ diff --git a/config/locales/nan-TW.yml b/config/locales/nan-TW.yml index dc471a9c2c..deba967257 100644 --- a/config/locales/nan-TW.yml +++ b/config/locales/nan-TW.yml @@ -1210,7 +1210,6 @@ nan-TW: application_mailer: notification_preferences: 改電子phue ê偏好 salutation: "%{name}、" - settings: 改電子phue ê偏好:%{link} unsubscribe: 取消訂 view: 檢視: view_profile: 看個人資料 @@ -1614,9 +1613,6 @@ nan-TW: title: 認證歷史 mail_subscriptions: unsubscribe: - action: Hennh,mài訂 - complete: 無訂ah - confirmation_html: Lí kám確定beh取消訂 %{domain} ê Mastodon 內底 ê %{type} kàu lí ê電子批 %{email}?Lí ē當隨時對lí ê電子批通知設定重訂。 emails: notification_emails: favourite: 收藏通知電子批 @@ -1624,9 +1620,6 @@ nan-TW: follow_request: 跟tuè請求電子批 mention: 提起通知電子批 reblog: 轉送通知電子批 - resubscribe_html: Nā出tshê取消訂,lí通重訂tuì lí ê電子批通知設定。 - success_html: Lí bē koh收著佇 %{domain} ê Mastodon內底ê %{type} kàu lí ê電子批 %{email}。 - title: 取消訂 media_attachments: validations: images_and_video: Bē當佇有影像ê PO文內底加影片 diff --git a/config/locales/nl.yml b/config/locales/nl.yml index 69ff56febd..8549e7e17c 100644 --- a/config/locales/nl.yml +++ b/config/locales/nl.yml @@ -1231,7 +1231,6 @@ nl: application_mailer: notification_preferences: E-mailvoorkeuren wijzigen salutation: "%{name}," - settings: 'E-mailvoorkeuren wijzigen: %{link}' unsubscribe: Afmelden view: 'Bekijk:' view_profile: Profiel bekijken @@ -1655,9 +1654,6 @@ nl: title: Inloggeschiedenis mail_subscriptions: unsubscribe: - action: Ja, afmelden - complete: Afgemeld - confirmation_html: Weet je zeker dat je je wilt afmelden voor het ontvangen van %{type} van Mastodon op %{domain} op je e-mailadres %{email}? Je kunt je altijd opnieuw abonneren in jouw instellingen voor e-mailmeldingen. emails: notification_emails: favourite: e-mailmeldingen voor favorieten @@ -1665,9 +1661,6 @@ nl: follow_request: e-mailmeldingen voor volgverzoeken mention: e-mailmeldingen voor vermeldingen reblog: e-mailmeldingen voor boosts - resubscribe_html: Als je je per ongeluk hebt afgemeld, kun je je opnieuw abonneren in jouw instellingen voor e-mailmeldingen. - success_html: Je ontvangt niet langer %{type} van Mastodon op %{domain} op je e-mailadres %{email}. - title: Afmelden media_attachments: validations: images_and_video: Een video kan niet aan een bericht met afbeeldingen worden gekoppeld diff --git a/config/locales/nn.yml b/config/locales/nn.yml index f59ba33334..04c021d5cc 100644 --- a/config/locales/nn.yml +++ b/config/locales/nn.yml @@ -1229,7 +1229,6 @@ nn: application_mailer: notification_preferences: Endre e-post-innstillingane salutation: Hei %{name}, - settings: 'Endre e-post-innstillingar: %{link}' unsubscribe: Meld av view: 'Sjå:' view_profile: Sjå profil @@ -1653,9 +1652,6 @@ nn: title: Autentiseringshistorikk mail_subscriptions: unsubscribe: - action: Ja, meld av - complete: Meldt av - confirmation_html: Er du sikker på at du ikkje lenger ynskjer å motta %{type} frå Mastodon på %{domain} til e-posten din %{email}? Du kan alltids gjera om på dette i innstillingar for e-postvarsling. emails: notification_emails: favourite: e-postar om favorittmarkeringar @@ -1663,9 +1659,6 @@ nn: follow_request: e-postar om fylgjeførespurnadar mention: e-postar om omtaler reblog: e-postar om framhevingar - resubscribe_html: Om du har avslutta abonnementet ved ein feil, kan du abonnera på nytt i innstillingar for e-postvarsling. - success_html: Du vil ikkje lenger få %{type} frå Mastodon på %{domain} til e-posten på %{email}. - title: Meld av media_attachments: validations: images_and_video: Kan ikkje leggja ved video til status som allereie inneheld bilete diff --git a/config/locales/no.yml b/config/locales/no.yml index f6dd1e9c8d..e2bf55a229 100644 --- a/config/locales/no.yml +++ b/config/locales/no.yml @@ -1352,11 +1352,6 @@ failed_sign_in_html: Mislykket innloggingsforsøk med %{method} fra %{ip} (%{browser}) successful_sign_in_html: Vellykket innlogging med %{method} fra %{ip} (%{browser}) title: Autentiseringshistorikk - mail_subscriptions: - unsubscribe: - action: Ja, avslutt abonnement - complete: Abonnement avsluttet - title: Avslutt abonnement media_attachments: validations: images_and_video: Kan ikke legge ved video på en status som allerede inneholder bilder diff --git a/config/locales/pl.yml b/config/locales/pl.yml index e0ae1f7106..af03c4f7cf 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -1230,7 +1230,6 @@ pl: application_mailer: notification_preferences: Zmień ustawienia e-maili salutation: "%{name}," - settings: 'Zmień ustawienia e-maili: %{link}' unsubscribe: Anuluj subskrypcję view: 'Zobacz:' view_profile: Wyświetl profil @@ -1689,9 +1688,6 @@ pl: title: Historia uwierzytelniania mail_subscriptions: unsubscribe: - action: Tak, wypisuję się - complete: Anulowano subskrypcję - confirmation_html: Czy na pewno chcesz wypisać się z otrzymywania %{type} z Mastodona na %{domain} na adres %{email}? Zawsze możesz zapisać się ponownie ze strony ustawień powiadomień mejlowych. emails: notification_emails: favourite: powiadomień mejlowych o polubieniach @@ -1699,9 +1695,6 @@ pl: follow_request: mejli o prośbach o możliwość obserwowania mention: powiadomień mejlowych o wspomnieniach reblog: powiadomień mejlowych o podbiciach - resubscribe_html: W przypadku przypadkowego wypisania możesz zapisać się ponownie z ustawień powiadomień mejlowych. - success_html: Już nie będziesz otrzymywać %{type} z Mastodona na %{domain} na adres %{email}. - title: Anuluj subskrypcję media_attachments: validations: images_and_video: Nie możesz załączyć pliku wideo do wpisu, który zawiera już zdjęcia diff --git a/config/locales/pt-BR.yml b/config/locales/pt-BR.yml index e8789d4236..2b4a820851 100644 --- a/config/locales/pt-BR.yml +++ b/config/locales/pt-BR.yml @@ -1229,7 +1229,6 @@ pt-BR: application_mailer: notification_preferences: Alterar preferências de e-mail salutation: "%{name}," - settings: 'Alterar preferências de e-mail: %{link}' unsubscribe: Desinscrever view: 'Ver:' view_profile: Ver perfil @@ -1651,9 +1650,6 @@ pt-BR: title: Histórico de autenticação mail_subscriptions: unsubscribe: - action: Sim, cancelar subscrição - complete: Desinscrito - confirmation_html: Tem certeza que deseja cancelar a assinatura de %{type} para Mastodon no %{domain} para o seu endereço de e-mail %{email}? Você sempre pode se inscrever novamente nas configurações de notificação de email. emails: notification_emails: favourite: emails de notificação favoritos @@ -1661,9 +1657,6 @@ pt-BR: follow_request: emails de seguidores pendentes mention: emails de notificação de menções reblog: emails de notificação de impulsos - resubscribe_html: Se você cancelou sua inscrição por engano, você pode se inscrever novamente em suas configurações de notificações por e-mail. - success_html: Você não mais receberá %{type} no Mastodon em %{domain} ao seu endereço de e-mail %{email}. - title: Cancelar inscrição media_attachments: validations: images_and_video: Não foi possível anexar um vídeo a uma publicação que já contém imagens diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml index c388a0a48e..21550b348d 100644 --- a/config/locales/pt-PT.yml +++ b/config/locales/pt-PT.yml @@ -1229,7 +1229,6 @@ pt-PT: application_mailer: notification_preferences: Alterar preferências de e-mail salutation: "%{name}," - settings: 'Alterar preferências de e-mail: %{link}' unsubscribe: Cancelar subscrição view: 'Ver:' view_profile: Ver perfil @@ -1651,9 +1650,6 @@ pt-PT: title: Histórico de autenticação mail_subscriptions: unsubscribe: - action: Sim, cancelar subscrição - complete: Subscrição cancelada - confirmation_html: Tens a certeza que desejas cancelar a subscrição para receber %{type} pelo Mastodon em %{domain} no teu e-mail em %{email}? Podes sempre subscrever novamente nas tuas definições de notificação por e-mail. emails: notification_emails: favourite: e-mails de notificação de favoritos @@ -1661,9 +1657,6 @@ pt-PT: follow_request: e-mails de pedido de seguidor mention: e-mails de notificação de menção reblog: e-mails de notificação de partilhas - resubscribe_html: Se tiveres anulado a subscrição por engano, podes voltar a subscrevê-la nas definições de notificação por e-mail. - success_html: Não receberás novamente %{type} do Mastodon em %{domain} para o teu e-mail em %{email}. - title: Cancelar subscrição media_attachments: validations: images_and_video: Não é possível anexar um vídeo a uma publicação que já contém imagens diff --git a/config/locales/ru.yml b/config/locales/ru.yml index 626870709d..c56574643b 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -1227,7 +1227,6 @@ ru: application_mailer: notification_preferences: Настроить оповещения по электронной почте salutation: Привет, %{name}! - settings: 'Настроить оповещения по электронной почте можно здесь: %{link}' unsubscribe: Отписаться view: 'Открыть в браузере:' view_profile: Перейти к профилю @@ -1675,9 +1674,6 @@ ru: title: История входов mail_subscriptions: unsubscribe: - action: Да, я хочу отписаться - complete: Подписка отменена - confirmation_html: Вы уверены в том, что хотите отписаться от всех %{type}, которые вы получаете на адрес %{email} для учётной записи на сервере Mastodon %{domain}? Вы всегда сможете подписаться снова в настройках уведомлений по электронной почте. emails: notification_emails: favourite: уведомлений о добавлении ваших постов в избранное @@ -1685,9 +1681,6 @@ ru: follow_request: уведомлений о новых запросах на подписку mention: уведомлений о новых упоминаниях reblog: уведомлений о продвижении ваших постов - resubscribe_html: Если вы отписались по ошибке и хотите подписаться снова, перейдите на страницу настройки уведомлений по электронной почте. - success_html: Вы отказались от %{type}, которые вы получали на адрес %{email} для вашей учётной записи на сервере Mastodon %{domain}. - title: Отписаться media_attachments: validations: images_and_video: Нельзя добавить видео к посту с изображениями diff --git a/config/locales/sc.yml b/config/locales/sc.yml index 6924ba67c4..2c94bc3a10 100644 --- a/config/locales/sc.yml +++ b/config/locales/sc.yml @@ -913,9 +913,6 @@ sc: authentication_methods: password: crae webauthn: craes de seguresa - mail_subscriptions: - unsubscribe: - title: Annulla sa sutiscritzione media_attachments: validations: images_and_video: Non si podet allegare unu vìdeu in una publicatzione chi cuntenet giai immàgines diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index 87334b14e7..be92d56a8a 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -134,6 +134,7 @@ en: otp: 'Enter the two-factor code generated by your phone app or use one of your recovery codes:' webauthn: If it's an USB key be sure to insert it and, if necessary, tap it. settings: + email_subscriptions: Disabling retains existing subscribers but stops sending emails. indexable: Your profile page may appear in search results on Google, Bing, and others. show_application: You will always be able to see which app published your post regardless. tag: @@ -356,6 +357,7 @@ en: hint: Additional information text: Rule settings: + email_subscriptions: Enable email sign-ups indexable: Include profile page in search engines show_application: Display from which app you sent a post tag: diff --git a/config/locales/sk.yml b/config/locales/sk.yml index 0a684eeb4c..25bc48a198 100644 --- a/config/locales/sk.yml +++ b/config/locales/sk.yml @@ -1085,7 +1085,6 @@ sk: emails: notification_emails: reblog: e-mailové upozornenia na zdieľania - title: Ukonči odber media_attachments: validations: images_and_video: K príspevku ktorý už obsahuje obrázky nemôžeš priložiť video diff --git a/config/locales/sl.yml b/config/locales/sl.yml index 6061b6fc8e..8e2372a4cc 100644 --- a/config/locales/sl.yml +++ b/config/locales/sl.yml @@ -1227,7 +1227,6 @@ sl: application_mailer: notification_preferences: Spremenite e-poštne nastavitve salutation: "%{name}," - settings: 'Spremenite e-poštne nastavitve: %{link}' unsubscribe: Odjavi od naročnine view: 'Pogled:' view_profile: Ogled profila @@ -1687,9 +1686,6 @@ sl: title: Zgodovina overjanja mail_subscriptions: unsubscribe: - action: Da, odjavi me - complete: Odjavljeni - confirmation_html: Ali se res želite odjaviti od prejemanja %{type} za Mastodon na %{domain} na svojo e-pošto %{email}? Kadarkoli se lahko znova prijavite iz svojih nastavitev e-poštnih obvestil. emails: notification_emails: favourite: e-sporočil z obvestili o priljubljenosti @@ -1697,9 +1693,6 @@ sl: follow_request: e-sporočil o zahtevah za sledenje mention: e-sporočil z obvestili o omembah reblog: e-sporočil z obvestili o izpostavljanju - resubscribe_html: Če ste se odjavili po pomoti, se lahko znova prijavite iz svojih nastavitev e-poštnih obvestil. - success_html: Nič več ne boste prejemali %{type} za Mastodon na %{domain} na svoj e-naslov %{email}. - title: Odjavi od naročnine media_attachments: validations: images_and_video: Videoposnetka ni mogoče priložiti objavi, ki že vsebuje slike diff --git a/config/locales/sq.yml b/config/locales/sq.yml index caf915df78..dc52f54b24 100644 --- a/config/locales/sq.yml +++ b/config/locales/sq.yml @@ -1220,7 +1220,6 @@ sq: application_mailer: notification_preferences: Ndryshoni parapëlqime rreth email-esh salutation: "%{name}," - settings: 'Ndryshoni parapëlqime rreth email-esh: %{link}' unsubscribe: Shpajtohuni view: 'Parje:' view_profile: Shihni profilin @@ -1640,9 +1639,6 @@ sq: title: Historik mirëfilltësimesh mail_subscriptions: unsubscribe: - action: Po, shpajtomëni - complete: U shpajtuat - confirmation_html: Jeni i sigurt se doni të shpajtoheni nga marrje %{type} për Mastodon te %{domain} në email-in tuaj %{email}? Mundeni përherë të ripajtoheni që nga rregullimet tuaja për njoftime me email. emails: notification_emails: favourite: email-e njoftimesh parapëlqimesh @@ -1650,9 +1646,6 @@ sq: follow_request: email-e kërkesash ndjekjeje mention: email-e njoftimesh përmendjesh reblog: email-e njoftimesh përforcimesh - resubscribe_html: Nëse u shpajtuat gabimisht, mund të ripajtoheni që nga rregullimet tuaja për njoftime me email. - success_html: S’do të merrni më %{type} për Mastodon te %{domain} në email-in tuaj te %{email}. - title: Shpajtohuni media_attachments: validations: images_and_video: S’mund të bashkëngjitet video te një gjendje që përmban figura tashmë diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml index 3f9eee3cae..d75f5f76d3 100644 --- a/config/locales/sr-Latn.yml +++ b/config/locales/sr-Latn.yml @@ -1357,11 +1357,6 @@ sr-Latn: failed_sign_in_html: Neuspešan pokušaj prijavljivanja putem %{method} sa %{ip} (%{browser}) successful_sign_in_html: Uspešan pokušaj prijavljivanja putem %{method} sa %{ip} (%{browser}) title: Istorija autentifikacije - mail_subscriptions: - unsubscribe: - action: Da, odjavi me - complete: Odjavljen - title: Odjavi se media_attachments: validations: images_and_video: Ne može da se prikači video na status koji već ima slike diff --git a/config/locales/sr.yml b/config/locales/sr.yml index 35d845bb1e..8bc89180d4 100644 --- a/config/locales/sr.yml +++ b/config/locales/sr.yml @@ -1387,11 +1387,6 @@ sr: failed_sign_in_html: Неуспешан покушај пријављивања путем %{method} са %{ip} (%{browser}) successful_sign_in_html: Успешан покушај пријављивања путем %{method} са %{ip} (%{browser}) title: Историја аутентификације - mail_subscriptions: - unsubscribe: - action: Да, одјави ме - complete: Одјављен - title: Одјави се media_attachments: validations: images_and_video: Не може да се прикачи видео на статус који већ има слике diff --git a/config/locales/sv.yml b/config/locales/sv.yml index a514556ba9..3180a0ba98 100644 --- a/config/locales/sv.yml +++ b/config/locales/sv.yml @@ -1228,7 +1228,6 @@ sv: application_mailer: notification_preferences: Ändra e-postpreferenser salutation: "%{name}," - settings: 'Ändra e-postinställningar: %{link}' unsubscribe: Avprenumerera view: 'Granska:' view_profile: Visa profil @@ -1652,9 +1651,6 @@ sv: title: Autentiseringshistorik mail_subscriptions: unsubscribe: - action: Ja, avsluta prenumerationen - complete: Prenumeration avslutad - confirmation_html: Är du säker på att du vill avregistrera dig från att ta emot %{type} för Mastodon på %{domain} med din e-post på %{email}? Du kan alltid återprenumerera bland dina e-postmeddelandeinställningar. emails: notification_emails: favourite: aviseringsmejl för favoriserade inlägg @@ -1662,9 +1658,6 @@ sv: follow_request: aviseringsmejl för följdförfrågningar mention: aviseringsmejl för inlägg där du nämns reblog: aviseringsmejl för förhöjda inlägg - resubscribe_html: Om du slutat prenumerera av misstag kan du återprenumerera i dina e-postaviseringsinställningar. - success_html: Du får inte längre %{type} för Mastodon på %{domain} till din e-post på %{email}. - title: Avsluta prenumeration media_attachments: validations: images_and_video: Det går inte att bifoga en video till ett inlägg som redan innehåller bilder diff --git a/config/locales/th.yml b/config/locales/th.yml index b3e2413235..a90f1a2555 100644 --- a/config/locales/th.yml +++ b/config/locales/th.yml @@ -1124,7 +1124,6 @@ th: application_mailer: notification_preferences: เปลี่ยนการกำหนดลักษณะอีเมล salutation: "%{name}," - settings: 'เปลี่ยนการกำหนดลักษณะอีเมล: %{link}' unsubscribe: เลิกบอกรับ view: 'ดู:' view_profile: ดูโปรไฟล์ @@ -1510,9 +1509,6 @@ th: title: ประวัติการรับรองความถูกต้อง mail_subscriptions: unsubscribe: - action: ใช่ เลิกบอกรับ - complete: เลิกบอกรับแล้ว - confirmation_html: คุณแน่ใจหรือไม่ว่าต้องการเลิกบอกรับจากการรับ %{type} สำหรับ Mastodon ใน %{domain} ไปยังอีเมลของคุณที่ %{email}? คุณสามารถบอกรับใหม่ได้เสมอจาก การตั้งค่าการแจ้งเตือนอีเมล ของคุณ emails: notification_emails: favourite: อีเมลการแจ้งเตือนการชื่นชอบ @@ -1520,9 +1516,6 @@ th: follow_request: อีเมลคำขอติดตาม mention: อีเมลการแจ้งเตือนการกล่าวถึง reblog: อีเมลการแจ้งเตือนการดัน - resubscribe_html: หากคุณได้เลิกบอกรับโดยไม่ได้ตั้งใจ คุณสามารถบอกรับใหม่ได้จาก การตั้งค่าการแจ้งเตือนอีเมล ของคุณ - success_html: คุณจะไม่ได้รับ %{type} สำหรับ Mastodon ใน %{domain} ไปยังอีเมลของคุณที่ %{email} อีกต่อไป - title: เลิกบอกรับ media_attachments: validations: images_and_video: ไม่สามารถแนบวิดีโอกับโพสต์ที่มีภาพอยู่แล้ว diff --git a/config/locales/tr.yml b/config/locales/tr.yml index d58aab2664..1ad87c39d3 100644 --- a/config/locales/tr.yml +++ b/config/locales/tr.yml @@ -1231,7 +1231,6 @@ tr: application_mailer: notification_preferences: E-posta tercihlerini değiştir salutation: "%{name}," - settings: 'E-posta tercihlerini değiştir: %{link}' unsubscribe: Abonelikten çık view: 'Görüntüle:' view_profile: Profili görüntüle @@ -1655,9 +1654,6 @@ tr: title: Kimlik doğrulama geçmişi mail_subscriptions: unsubscribe: - action: Evet, abonelikten çık - complete: Abonelikten çık - confirmation_html: '%{domain} üzerindeki Mastodon için %{type} almayı durdurarak %{email} adresinize aboneliğinizi iptal etmek istediğinizden emin misiniz? e-posta bildirim ayarlarınızdan her zaman yeniden abone olabilirsiniz.' emails: notification_emails: favourite: favori bildirim e-postaları @@ -1665,9 +1661,6 @@ tr: follow_request: takip isteği bildirim e-postaları mention: bahsetme bildirim e-postaları reblog: öne çıkanlar bildirim e-postaları - resubscribe_html: Abonelikten yanlışlıkla çıktıysanız, e-posta bildirim ayarlarınızdan yeniden abone olabilirsiniz. - success_html: Artık %{email} adresindeki e-postanıza %{domain} üzerindeki Mastodon için %{type} almayacaksınız. - title: Abonelikten çık media_attachments: validations: images_and_video: Zaten resim içeren bir duruma video eklenemez diff --git a/config/locales/uk.yml b/config/locales/uk.yml index 11dcf51c6e..e5ddc13b1f 100644 --- a/config/locales/uk.yml +++ b/config/locales/uk.yml @@ -1180,7 +1180,6 @@ uk: application_mailer: notification_preferences: Змінити налаштування електронної пошти salutation: "%{name}," - settings: 'Змінити налаштування електронної пошти: %{link}' unsubscribe: Відписатися view: 'Перегляд:' view_profile: Показати профіль @@ -1572,9 +1571,6 @@ uk: title: Історія входів mail_subscriptions: unsubscribe: - action: Так, відписатися - complete: Відписалися - confirmation_html: Ви впевнені, що хочете відписатися від отримання %{type} для Mastodon на %{domain} до своєї скриньки %{email}? Ви можете повторно підписатися у налаштуваннях сповіщень електронною поштою. emails: notification_emails: favourite: отримувати сповіщення про вподобання електронною поштою @@ -1582,9 +1578,6 @@ uk: follow_request: отримувати сповіщення про запити на стеження електронною поштою mention: отримувати сповіщення про згадки електронною поштою reblog: отримувати сповіщення про поширення електронною поштою - resubscribe_html: Якщо ви відписалися помилково, ви можете повторно підписатися в налаштуваннях сповіщень електронною поштою. - success_html: Ви більше не отримуватимете %{type} для Mastodon %{domain} на адресу %{email}. - title: Відписатися media_attachments: validations: images_and_video: Не можна додати відео до допису з зображеннями diff --git a/config/locales/vi.yml b/config/locales/vi.yml index f773dda729..02862dcd73 100644 --- a/config/locales/vi.yml +++ b/config/locales/vi.yml @@ -1210,7 +1210,6 @@ vi: application_mailer: notification_preferences: Thay đổi thiết lập email salutation: "%{name}," - settings: 'Thay đổi thiết lập email: %{link}' unsubscribe: Hủy đăng ký view: 'Chi tiết:' view_profile: Xem trang hồ sơ @@ -1614,9 +1613,6 @@ vi: title: Lịch sử đăng nhập mail_subscriptions: unsubscribe: - action: Đúng, hủy đăng ký - complete: Đã hủy đăng ký - confirmation_html: Bạn có chắc muốn hủy đăng ký %{type} Mastodon trên %{domain} tới %{email}? Bạn có thể đăng ký lại từ cài đặt thông báo email. emails: notification_emails: favourite: email thông báo lượt thích @@ -1624,9 +1620,6 @@ vi: follow_request: email thông báo yêu cầu theo dõi mention: email thông báo lượt nhắc đến reblog: email thông báo lượt đăng lại - resubscribe_html: Nếu đổi ý, bạn có thể đăng ký lại từ cài đặt thông báo email. - success_html: Bạn sẽ không còn nhận %{type} Mastodon trên %{domain} tới %{email}. - title: Hủy đăng ký media_attachments: validations: images_and_video: Không thể đính kèm video vào tút đã chứa hình ảnh diff --git a/config/locales/zh-CN.yml b/config/locales/zh-CN.yml index c161b99681..80ead9ee79 100644 --- a/config/locales/zh-CN.yml +++ b/config/locales/zh-CN.yml @@ -1210,7 +1210,6 @@ zh-CN: application_mailer: notification_preferences: 更改邮件偏好 salutation: "%{name}:" - settings: 更改邮件偏好: %{link} unsubscribe: 取消订阅 view: 点此链接查看详情: view_profile: 查看个人资料 @@ -1614,9 +1613,6 @@ zh-CN: title: 认证历史 mail_subscriptions: unsubscribe: - action: 是,取消订阅 - complete: 已取消订阅 - confirmation_html: 你确定要退订来自 %{domain} 上的 Mastodon 的 %{type} 到你的邮箱 %{email} 吗?你可以随时在邮件通知设置中重新订阅。 emails: notification_emails: favourite: 嘟文被喜欢邮件通知 @@ -1624,9 +1620,6 @@ zh-CN: follow_request: 关注请求邮件通知 mention: 账号被提及邮件通知 reblog: 嘟文被转嘟邮件通知 - resubscribe_html: 如果你不小心取消了订阅,可以在你的邮件通知设置中重新订阅。 - success_html: 你将不会在你的邮箱 %{email} 中收到 %{domain} 上的 Mastodon的 %{type} - title: 取消订阅 media_attachments: validations: images_and_video: 无法在嘟文中同时插入视频和图片 diff --git a/config/locales/zh-HK.yml b/config/locales/zh-HK.yml index ac26ce9af4..6caf362fa7 100644 --- a/config/locales/zh-HK.yml +++ b/config/locales/zh-HK.yml @@ -1341,11 +1341,6 @@ zh-HK: failed_sign_in_html: 以 %{method} 從 %{ip} (%{browser}) 登入失敗 successful_sign_in_html: 以 %{method} 從 %{ip} (%{browser}) 成功登入 title: 驗證操作歷史 - mail_subscriptions: - unsubscribe: - action: 沒錯,取消訂閱 - complete: 已取消訂閱 - title: 取消訂閱 media_attachments: validations: images_and_video: 不能在已有圖片的文章上加入影片 diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 4585b729bc..8d3cfc6ee6 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -1212,7 +1212,6 @@ zh-TW: application_mailer: notification_preferences: 變更電子郵件設定 salutation: "%{name}、" - settings: 變更電子郵件設定︰%{link} unsubscribe: 取消訂閱 view: 進入瀏覽: view_profile: 檢視個人檔案 @@ -1616,9 +1615,6 @@ zh-TW: title: 認證歷史紀錄 mail_subscriptions: unsubscribe: - action: 是的,取消訂閱 - complete: 取消訂閱 - confirmation_html: 您確定要取要取消訂閱自 Mastodon 上 %{domain} 之 %{type} 至您電子郵件 %{email} 嗎?您隨時可以自電子郵件通知設定重新訂閱。 emails: notification_emails: favourite: 最愛通知電子郵件 @@ -1626,9 +1622,6 @@ zh-TW: follow_request: 跟隨請求通知電子郵件 mention: 提及通知電子郵件 reblog: 轉嘟通知電子郵件 - resubscribe_html: 若您不慎錯誤地取消訂閱,您可以自電子郵件通知設定重新訂閱。 - success_html: 您將不再收到來自 Mastodon 上 %{domain} 之 %{type} 至您電子郵件 %{email}。 - title: 取消訂閱 media_attachments: validations: images_and_video: 無法於已有圖片之嘟文中加入影片 diff --git a/config/routes.rb b/config/routes.rb index 1bc32a2861..d31331a6c1 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -71,7 +71,7 @@ Rails.application.routes.draw do devise_scope :user do get '/invite/:invite_code', to: 'auth/registrations#new', as: :public_invite - resource :unsubscribe, only: [:show, :create], controller: :mail_subscriptions + resource :unsubscribe, only: [:show, :create], controller: :unsubscriptions namespace :auth do resource :setup, only: [:show, :update], controller: :setup @@ -188,6 +188,10 @@ Rails.application.routes.draw do resources :statuses, only: :show end + namespace :email_subscriptions do + resource :confirmation, only: :show + end + resources :media, only: [:show] do get :player end diff --git a/config/routes/api.rb b/config/routes/api.rb index 285b032d01..61d74f4cd9 100644 --- a/config/routes/api.rb +++ b/config/routes/api.rb @@ -221,6 +221,7 @@ namespace :api, format: false do resources :identity_proofs, only: :index resources :featured_tags, only: :index resources :endorsements, only: :index + resources :email_subscriptions, only: :create end member do diff --git a/db/migrate/20260311212130_create_email_subscriptions.rb b/db/migrate/20260311212130_create_email_subscriptions.rb new file mode 100644 index 0000000000..b750ccc7fc --- /dev/null +++ b/db/migrate/20260311212130_create_email_subscriptions.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class CreateEmailSubscriptions < ActiveRecord::Migration[8.1] + def change + create_table :email_subscriptions do |t| + t.references :account, null: false, foreign_key: { on_delete: :cascade } + t.string :email, null: false + t.string :locale, null: false + t.string :confirmation_token, index: { unique: true, where: 'confirmation_token is not null' } + t.datetime :confirmed_at + + t.timestamps + end + + add_index :email_subscriptions, [:account_id, :email], unique: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 69a9ef45da..1a839c2493 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -504,6 +504,19 @@ ActiveRecord::Schema[8.1].define(version: 2026_03_23_105645) do t.index ["domain"], name: "index_email_domain_blocks_on_domain", unique: true end + create_table "email_subscriptions", force: :cascade do |t| + t.bigint "account_id", null: false + t.string "confirmation_token" + t.datetime "confirmed_at" + t.datetime "created_at", null: false + t.string "email", null: false + t.string "locale", null: false + t.datetime "updated_at", null: false + t.index ["account_id", "email"], name: "index_email_subscriptions_on_account_id_and_email", unique: true + t.index ["account_id"], name: "index_email_subscriptions_on_account_id" + t.index ["confirmation_token"], name: "index_email_subscriptions_on_confirmation_token", unique: true, where: "(confirmation_token IS NOT NULL)" + end + create_table "fasp_backfill_requests", force: :cascade do |t| t.string "category", null: false t.datetime "created_at", null: false @@ -1486,6 +1499,7 @@ ActiveRecord::Schema[8.1].define(version: 2026_03_23_105645) do add_foreign_key "custom_filter_statuses", "statuses", on_delete: :cascade add_foreign_key "custom_filters", "accounts", on_delete: :cascade add_foreign_key "email_domain_blocks", "email_domain_blocks", column: "parent_id", on_delete: :cascade + add_foreign_key "email_subscriptions", "accounts", on_delete: :cascade add_foreign_key "fasp_backfill_requests", "fasp_providers" add_foreign_key "fasp_debug_callbacks", "fasp_providers" add_foreign_key "fasp_follow_recommendations", "accounts", column: "recommended_account_id" diff --git a/spec/fabricators/email_subscription_fabricator.rb b/spec/fabricators/email_subscription_fabricator.rb new file mode 100644 index 0000000000..8d61945564 --- /dev/null +++ b/spec/fabricators/email_subscription_fabricator.rb @@ -0,0 +1,7 @@ +# frozen_string_literal: true + +Fabricator(:email_subscription) do + account + email { sequence(:email) { |i| "#{i}#{Faker::Internet.email}" } } + locale 'en' +end diff --git a/spec/mailers/email_subscription_mailer_spec.rb b/spec/mailers/email_subscription_mailer_spec.rb new file mode 100644 index 0000000000..0d8ec6e66b --- /dev/null +++ b/spec/mailers/email_subscription_mailer_spec.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe EmailSubscriptionMailer do + describe '.confirmation' do + let(:email_subscription) { Fabricate(:email_subscription) } + let(:mail) { described_class.with(subscription: email_subscription).confirmation } + + it 'renders the email' do + expect { mail.deliver } + .to send_email( + to: email_subscription.email, + from: 'notifications@localhost', + subject: I18n.t('email_subscription_mailer.confirmation.subject') + ) + end + end + + describe '.notification' do + let(:email_subscription) { Fabricate(:email_subscription, confirmed_at: Time.now.utc) } + let(:statuses) { Fabricate.times(num_of_statuses, :status) } + let(:mail) { described_class.with(subscription: email_subscription).notification(statuses) } + + context 'with a single status' do + let(:num_of_statuses) { 1 } + + it 'renders the email' do + expect { mail.deliver } + .to send_email( + to: email_subscription.email, + from: 'notifications@localhost', + subject: I18n.t('email_subscription_mailer.notification.subject', count: statuses.size, name: email_subscription.account.display_name, excerpt: statuses.first.text.truncate(17)) + ) + end + end + + context 'with multiple statuses' do + let(:num_of_statuses) { 2 } + + it 'renders the email' do + expect { mail.deliver } + .to send_email( + to: email_subscription.email, + from: 'notifications@localhost', + subject: I18n.t('email_subscription_mailer.notification.subject', count: statuses.size, name: email_subscription.account.display_name, excerpt: ActionController::Base.helpers.truncate(statuses.first.text, length: 17)) + ) + end + end + end +end diff --git a/spec/mailers/previews/email_subscription_mailer_preview.rb b/spec/mailers/previews/email_subscription_mailer_preview.rb new file mode 100644 index 0000000000..436e24c390 --- /dev/null +++ b/spec/mailers/previews/email_subscription_mailer_preview.rb @@ -0,0 +1,15 @@ +# frozen_string_literal: true + +# Preview all emails at http://localhost:3000/rails/mailers/admin_mailer + +class EmailSubscriptionMailerPreview < ActionMailer::Preview + # Preview this email at http://localhost:3000/rails/mailers/email_subscription_mailer/confirmation + def confirmation + EmailSubscriptionMailer.with(subscription: EmailSubscription.last!).confirmation + end + + # Preview this email at http://localhost:3000/rails/mailers/email_subscription_mailer/notification + def notification + EmailSubscriptionMailer.with(subscription: EmailSubscription.last!).notification(Status.where(visibility: :public).without_replies.without_reblogs.limit(5)) + end +end diff --git a/spec/models/email_subscription_spec.rb b/spec/models/email_subscription_spec.rb new file mode 100644 index 0000000000..6e1a0483f8 --- /dev/null +++ b/spec/models/email_subscription_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe EmailSubscription do + describe '#confirmed?' do + it 'returns true when confirmed' do + subject.confirmed_at = Time.now.utc + expect(subject.confirmed?).to be true + end + + it 'returns false when not confirmed' do + subject.confirmed_at = nil + expect(subject.confirmed?).to be false + end + end + + describe '#confirm!' do + subject { Fabricate(:email_subscription) } + + it 'records confirmation time' do + subject.confirm! + expect(subject.confirmed_at).to_not be_nil + end + end + + describe 'Callbacks' do + subject { Fabricate(:email_subscription) } + + it 'generates token and delivers confirmation email', :inline_jobs do + emails = capture_emails { subject } + + expect(subject.confirmed_at).to be_nil + expect(subject.confirmation_token).to_not be_nil + expect(emails.size).to eq(1) + expect(emails.first) + .to have_attributes( + to: contain_exactly(subject.email), + subject: eq(I18n.t('email_subscription_mailer.confirmation.subject', name: subject.account.username, domain: Rails.configuration.x.local_domain)) + ) + end + end +end diff --git a/spec/requests/api/v1/accounts/email_subscriptions_spec.rb b/spec/requests/api/v1/accounts/email_subscriptions_spec.rb new file mode 100644 index 0000000000..ef7a31476a --- /dev/null +++ b/spec/requests/api/v1/accounts/email_subscriptions_spec.rb @@ -0,0 +1,48 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Accounts Email Subscriptions API', feature: :email_subscriptions do + let(:account) { Fabricate(:user).account } + + describe 'POST /api/v1/accounts/:id/email_subscriptions' do + context 'when the account has the permission' do + let(:role) { Fabricate(:user_role, permissions: UserRole::FLAGS[:manage_email_subscriptions]) } + + before do + account.user.update!(role: role) + end + + context 'when user has enabled the setting' do + before do + account.user.settings['email_subscriptions'] = true + account.user.save! + end + + it 'returns http success' do + post "/api/v1/accounts/#{account.id}/email_subscriptions", params: { email: 'test@example.com' } + + expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + end + end + + context 'when user has not enabled the setting' do + it 'returns http not found' do + post "/api/v1/accounts/#{account.id}/email_subscriptions", params: { email: 'test@example.com' } + + expect(response).to have_http_status(404) + end + end + end + + context 'when the account does not have the permission' do + it 'returns http not found' do + post "/api/v1/accounts/#{account.id}/email_subscriptions", params: { email: 'test@example.com' } + + expect(response).to have_http_status(404) + end + end + end +end diff --git a/spec/requests/email_subscriptions/confirmations_spec.rb b/spec/requests/email_subscriptions/confirmations_spec.rb new file mode 100644 index 0000000000..909aab1b77 --- /dev/null +++ b/spec/requests/email_subscriptions/confirmations_spec.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe 'Email Subscriptions Confirmation' do + describe 'GET /email_subscriptions/confirmation' do + context 'when email subscription is unconfirmed' do + let!(:email_subscription) { Fabricate(:email_subscription, confirmed_at: nil) } + + it 'renders success page and updates subscription as confirmed' do + get email_subscriptions_confirmation_path(confirmation_token: email_subscription.confirmation_token) + + expect(response) + .to have_http_status(200) + expect(email_subscription.reload.confirmed?) + .to be true + end + end + + context 'when email subscription is already confirmed' do + let!(:email_subscription) { Fabricate(:email_subscription, confirmed_at: Time.now.utc) } + + it 'renders success page' do + get email_subscriptions_confirmation_path(confirmation_token: email_subscription.confirmation_token) + + expect(response) + .to have_http_status(200) + expect(email_subscription.reload.confirmed?) + .to be true + end + end + end +end diff --git a/spec/requests/mail_subscriptions_spec.rb b/spec/requests/unsubscriptions_spec.rb similarity index 90% rename from spec/requests/mail_subscriptions_spec.rb rename to spec/requests/unsubscriptions_spec.rb index cc6557cab0..95a1499223 100644 --- a/spec/requests/mail_subscriptions_spec.rb +++ b/spec/requests/unsubscriptions_spec.rb @@ -2,7 +2,7 @@ require 'rails_helper' -RSpec.describe 'MailSubscriptionsController' do +RSpec.describe 'UnsubscriptionsController' do let(:user) { Fabricate(:user) } let(:token) { user.to_sgid(for: 'unsubscribe').to_s } let(:type) { 'follow' } @@ -39,9 +39,8 @@ RSpec.describe 'MailSubscriptionsController' do expect(response).to have_http_status(200) expect(response.body).to include( - I18n.t('mail_subscriptions.unsubscribe.action') + I18n.t('unsubscriptions.show.action') ) - expect(response.body).to include(user.email) end end @@ -60,9 +59,8 @@ RSpec.describe 'MailSubscriptionsController' do expect(response).to have_http_status(200) expect(response.body).to include( - I18n.t('mail_subscriptions.unsubscribe.complete') + I18n.t('unsubscriptions.create.title') ) - expect(response.body).to include(user.email) end it 'updates notification settings' do diff --git a/spec/services/bootstrap_timeline_service_spec.rb b/spec/services/bootstrap_timeline_service_spec.rb index c99813bceb..4c2d129e9e 100644 --- a/spec/services/bootstrap_timeline_service_spec.rb +++ b/spec/services/bootstrap_timeline_service_spec.rb @@ -3,35 +3,31 @@ require 'rails_helper' RSpec.describe BootstrapTimelineService do - subject { described_class.new } + subject { described_class.new.call(new_user.account) } + + let(:invite) { nil } + let(:new_user) { Fabricate(:user, invite_code: invite&.code) } context 'when the new user has registered from an invite' do - let(:service) { instance_double(FollowService) } let(:autofollow) { false } let(:inviter) { Fabricate(:user, confirmed_at: 2.days.ago) } let(:invite) { Fabricate(:invite, user: inviter, max_uses: nil, expires_at: 1.hour.from_now, autofollow: autofollow) } - let(:new_user) { Fabricate(:user, invite_code: invite.code) } - - before do - allow(FollowService).to receive(:new).and_return(service) - allow(service).to receive(:call) - end context 'when the invite has auto-follow enabled' do let(:autofollow) { true } - it 'calls FollowService to follow the inviter' do - subject.call(new_user.account) - expect(service).to have_received(:call).with(new_user.account, inviter.account) + it 'follows the inviter' do + subject + expect(new_user.account.following?(inviter.account)).to be true end end context 'when the invite does not have auto-follow enable' do let(:autofollow) { false } - it 'calls FollowService to follow the inviter' do - subject.call(new_user.account) - expect(service).to_not have_received(:call) + it 'does not follow the inviter' do + subject + expect(new_user.account.following?(inviter.account)).to be false end end end