Add admin UI for managing email subscriptions (#38741)
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
This commit is contained in:
parent
46ccfa6e8d
commit
ee88da4511
@ -0,0 +1,9 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::EmailSubscriptions::AdditionalFooterTextsController < Admin::SettingsController
|
||||
private
|
||||
|
||||
def after_update_redirect_path
|
||||
admin_email_subscriptions_path
|
||||
end
|
||||
end
|
||||
@ -0,0 +1,34 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::EmailSubscriptions::SetupsController < Admin::BaseController
|
||||
before_action :require_enabled!
|
||||
|
||||
def show
|
||||
authorize :email_subscription, :enable?
|
||||
|
||||
@form = Form::EmailSubscriptionsConfirmation.new
|
||||
end
|
||||
|
||||
def create
|
||||
authorize :email_subscription, :enable?
|
||||
|
||||
@form = Form::EmailSubscriptionsConfirmation.new(resource_params)
|
||||
|
||||
if @form.valid?
|
||||
Setting.email_subscriptions = true
|
||||
redirect_to admin_email_subscriptions_path
|
||||
else
|
||||
render :show
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def require_enabled!
|
||||
raise ActionController::RoutingError, 'Feature disabled' unless Rails.application.config.x.email_subscriptions
|
||||
end
|
||||
|
||||
def resource_params
|
||||
params.expect(form_email_subscriptions_confirmation: [:agreement_email_volume, :agreement_privacy_and_terms])
|
||||
end
|
||||
end
|
||||
23
app/controllers/admin/email_subscriptions_controller.rb
Normal file
23
app/controllers/admin/email_subscriptions_controller.rb
Normal file
@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::EmailSubscriptionsController < Admin::BaseController
|
||||
def index
|
||||
authorize :email_subscription, :index?
|
||||
|
||||
@enabled = Setting.email_subscriptions
|
||||
@roles = UserRole.where('permissions & ? != 0', UserRole::FLAGS[:manage_email_subscriptions] | UserRole::FLAGS[:administrator])
|
||||
@accounts = Account.local.joins(:email_subscriptions).where.associated(:email_subscriptions).includes(:user)
|
||||
end
|
||||
|
||||
def disable
|
||||
authorize :email_subscription, :disable?
|
||||
Setting.email_subscriptions = false
|
||||
redirect_to admin_email_subscriptions_path, notice: I18n.t('admin.email_subscriptions.disabled_msg')
|
||||
end
|
||||
|
||||
def purge
|
||||
authorize :email_subscription, :purge?
|
||||
Admin::EmailSubscriptionsPurgeWorker.perform_async
|
||||
redirect_to admin_email_subscriptions_path, notice: I18n.t('admin.email_subscriptions.purged_msg')
|
||||
end
|
||||
end
|
||||
@ -19,7 +19,7 @@ class Api::V1::Accounts::EmailSubscriptionsController < Api::BaseController
|
||||
end
|
||||
|
||||
def require_feature_enabled!
|
||||
head 404 unless Mastodon::Feature.email_subscriptions_enabled?
|
||||
head 404 unless Rails.application.config.x.email_subscriptions && Setting.email_subscriptions
|
||||
end
|
||||
|
||||
def require_account_permissions!
|
||||
|
||||
@ -127,6 +127,10 @@ module ApplicationHelper
|
||||
)
|
||||
end
|
||||
|
||||
def emptyphaunt
|
||||
inline_svg_tag 'elephant_ui.svg'
|
||||
end
|
||||
|
||||
def check_icon
|
||||
inline_svg_tag 'check.svg'
|
||||
end
|
||||
|
||||
@ -2223,3 +2223,117 @@ a.sparkline {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.subheading-with-action {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16px;
|
||||
|
||||
&__subheading {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
.actions-list {
|
||||
&__item {
|
||||
border-bottom: 1px solid var(--color-border-primary);
|
||||
padding: 12px 0;
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: center;
|
||||
|
||||
&__label {
|
||||
flex-grow: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
|
||||
&__label {
|
||||
font-weight: 600;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
&__hint {
|
||||
font-size: 13px;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
&__action {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
border: 1px solid var(--color-border-primary);
|
||||
border-radius: 12px;
|
||||
padding: 24px 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
|
||||
&__title-and-description {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
line-height: 21px;
|
||||
font-size: 15px;
|
||||
text-align: center;
|
||||
|
||||
&__title {
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
// FIXME: This is duplicated with empty_message.module.scss
|
||||
[data-color-scheme='dark'] & svg {
|
||||
--color-skin-1: #3a3a50;
|
||||
--color-skin-2: #67678e;
|
||||
--color-skin-3: #44445f;
|
||||
--color-outline: #21212c;
|
||||
--color-shadow: #181820;
|
||||
--color-highlight: #b2b1c8;
|
||||
}
|
||||
}
|
||||
|
||||
.heading-with-lead {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.lead {
|
||||
font-size: 18px;
|
||||
color: var(--color-text-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
display: inline-flex;
|
||||
padding: 3px 4px;
|
||||
border-radius: 8px;
|
||||
background: var(--color-bg-success-softest);
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
@ -71,6 +71,24 @@ code {
|
||||
}
|
||||
|
||||
.simple_form {
|
||||
h3 {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.numbered-list {
|
||||
list-style: decimal;
|
||||
font-size: 16px;
|
||||
line-height: 24px;
|
||||
margin-bottom: 16px;
|
||||
margin-inline-start: 28px;
|
||||
|
||||
li {
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
&.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@ -21,6 +21,24 @@
|
||||
}
|
||||
}
|
||||
|
||||
.align-end {
|
||||
text-align: end;
|
||||
}
|
||||
|
||||
.valign-middle {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.avatar-column {
|
||||
width: 24px;
|
||||
|
||||
.avatar {
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--color-border-primary);
|
||||
background: var(--color-background-secondary);
|
||||
}
|
||||
}
|
||||
|
||||
& > thead > tr > th {
|
||||
vertical-align: bottom;
|
||||
font-weight: 500;
|
||||
@ -170,6 +188,14 @@ a.table-action-link {
|
||||
}
|
||||
}
|
||||
|
||||
.table-icon-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 4px;
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.batch-table {
|
||||
&--no-toolbar {
|
||||
.batch-table__toolbar {
|
||||
|
||||
@ -46,6 +46,7 @@ class Form::AdminSettings
|
||||
remote_topic_feed_access
|
||||
landing_page
|
||||
wrapstodon
|
||||
email_footer_text
|
||||
).freeze
|
||||
|
||||
INTEGER_KEYS = %i(
|
||||
|
||||
11
app/models/form/email_subscriptions_confirmation.rb
Normal file
11
app/models/form/email_subscriptions_confirmation.rb
Normal file
@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Form::EmailSubscriptionsConfirmation
|
||||
include ActiveModel::Model
|
||||
include ActiveModel::Attributes
|
||||
|
||||
attribute :agreement_email_volume, :boolean
|
||||
attribute :agreement_privacy_and_terms, :boolean
|
||||
|
||||
validates :agreement_email_volume, :agreement_privacy_and_terms, acceptance: true
|
||||
end
|
||||
13
app/policies/email_subscription_policy.rb
Normal file
13
app/policies/email_subscription_policy.rb
Normal file
@ -0,0 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class EmailSubscriptionPolicy < ApplicationPolicy
|
||||
def index?
|
||||
role.can?(:manage_settings)
|
||||
end
|
||||
|
||||
alias enable? index?
|
||||
|
||||
alias disable? index?
|
||||
|
||||
alias purge? index?
|
||||
end
|
||||
@ -22,7 +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? }
|
||||
attribute :email_subscriptions, if: -> { Rails.application.config.x.email_subscriptions && Setting.email_subscriptions }
|
||||
|
||||
class AccountDecorator < SimpleDelegator
|
||||
def self.model_name
|
||||
|
||||
@ -171,7 +171,7 @@ class PostStatusService < BaseService
|
||||
end
|
||||
|
||||
def process_email_subscriptions!
|
||||
return unless Mastodon::Feature.email_subscriptions_enabled? &&
|
||||
return unless Rails.application.config.x.email_subscriptions && Setting.email_subscriptions &&
|
||||
@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?
|
||||
|
||||
44
app/views/admin/email_subscriptions/_accounts.html.haml
Normal file
44
app/views/admin/email_subscriptions/_accounts.html.haml
Normal file
@ -0,0 +1,44 @@
|
||||
.subheading-with-action
|
||||
.subheading-with-action__subheading
|
||||
%h3= t('.title')
|
||||
%p= t('.lead')
|
||||
|
||||
.table-wrapper
|
||||
- if accounts.empty?
|
||||
.empty-state
|
||||
= emptyphaunt
|
||||
|
||||
.empty-state__title-and-description
|
||||
.empty-state__title-and-description__title
|
||||
= t('.empty.no_lists_yet')
|
||||
.empty-state__title-and-description__description
|
||||
= t('.empty.hint')
|
||||
- else
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th.avatar-column
|
||||
%th= t('.account')
|
||||
%th= t('.status')
|
||||
%th= t('.subscribers')
|
||||
%th= t('.last_email')
|
||||
%th
|
||||
%tbody
|
||||
- accounts.each do |account|
|
||||
%tr
|
||||
%td.valign-middle.avatar-column
|
||||
= image_tag(full_asset_url(account.avatar_original_url), width: 24, height: 24, alt: '', class: 'avatar')
|
||||
%td.valign-middle
|
||||
%strong
|
||||
%bdi= display_name(account)
|
||||
%td.valign-middle
|
||||
- if account.user_can?(:manage_email_subscriptions) && account.user_email_subscriptions_enabled?
|
||||
%span.status-badge.positive= t('.active')
|
||||
- else
|
||||
%span.status-badge.negative= t('.inactive')
|
||||
%td.valign-middle
|
||||
= account.email_subscriptions.count
|
||||
%td.valign-middle
|
||||
= l account.last_status_at
|
||||
%td.valign-middle.align-end
|
||||
= link_to material_symbol('chevron_right'), admin_account_path(account.id), class: 'table-icon-link'
|
||||
@ -0,0 +1,22 @@
|
||||
.subheading-with-action
|
||||
.subheading-with-action__subheading
|
||||
%h3= t('.title')
|
||||
%p= t('.lead')
|
||||
|
||||
.actions-list
|
||||
.actions-list__item
|
||||
.actions-list__item__label
|
||||
.actions-list__item__label__label
|
||||
= t('.privacy_policy.title')
|
||||
.actions-list__item__label__hint
|
||||
= t('.privacy_policy.hint')
|
||||
.actions-list__item__action
|
||||
= link_to t('.privacy_policy.action'), admin_settings_about_path, class: 'button button-secondary'
|
||||
.actions-list__item
|
||||
.actions-list__item__label
|
||||
.actions-list__item__label__label
|
||||
= t('.additional_footer_text.title')
|
||||
.actions-list__item__label__hint
|
||||
= t('.additional_footer_text.hint')
|
||||
.actions-list__item__action
|
||||
= link_to t('.additional_footer_text.action'), admin_email_subscriptions_additional_footer_text_path, class: 'button button-secondary'
|
||||
22
app/views/admin/email_subscriptions/_danger_zone.html.haml
Normal file
22
app/views/admin/email_subscriptions/_danger_zone.html.haml
Normal file
@ -0,0 +1,22 @@
|
||||
.subheading-with-action
|
||||
.subheading-with-action__subheading
|
||||
%h3= t('.title')
|
||||
|
||||
.actions-list
|
||||
.actions-list__item
|
||||
.actions-list__item__label
|
||||
.actions-list__item__label__label
|
||||
= t('.disable_feature.title')
|
||||
.actions-list__item__label__hint
|
||||
= t('.disable_feature.hint')
|
||||
.actions-list__item__action
|
||||
= link_to t('.disable_feature.action'), disable_admin_email_subscriptions_path, class: 'button button-secondary button--destructive', data: { method: 'post', confirm: t('admin.accounts.are_you_sure') }
|
||||
- unless accounts.empty?
|
||||
.actions-list__item
|
||||
.actions-list__item__label
|
||||
.actions-list__item__label__label
|
||||
= t('.erase_all_data.title')
|
||||
.actions-list__item__label__hint
|
||||
= t('.erase_all_data.hint')
|
||||
.actions-list__item__action
|
||||
= link_to t('.erase_all_data.action'), purge_admin_email_subscriptions_path, class: 'button button-secondary button--destructive', data: { method: 'post', confirm: t('admin.accounts.are_you_sure') }
|
||||
36
app/views/admin/email_subscriptions/_roles.html.haml
Normal file
36
app/views/admin/email_subscriptions/_roles.html.haml
Normal file
@ -0,0 +1,36 @@
|
||||
.subheading-with-action
|
||||
.subheading-with-action__subheading
|
||||
%h3= t('.title')
|
||||
%p= t('.lead')
|
||||
- unless roles.empty?
|
||||
.subheading-with-action__action
|
||||
= link_to t('.manage_roles'), admin_roles_path, class: 'button button-secondary'
|
||||
|
||||
.table-wrapper
|
||||
- if roles.empty?
|
||||
.empty-state
|
||||
= emptyphaunt
|
||||
|
||||
.empty-state__title-and-description
|
||||
.empty-state__title-and-description__title
|
||||
= t('.empty.no_roles_added')
|
||||
.empty-state__title-and-description__description
|
||||
= t('.empty.hint')
|
||||
.empty-state__action
|
||||
= link_to t('.manage_roles'), admin_roles_path, class: 'button'
|
||||
- else
|
||||
%table.table
|
||||
%thead
|
||||
%tr
|
||||
%th= t('.role_name')
|
||||
%th= t('.accounts')
|
||||
%th
|
||||
%tbody
|
||||
- roles.each do |role|
|
||||
%tr
|
||||
%td
|
||||
= role.name
|
||||
%td
|
||||
= role.users.count
|
||||
%td.align-end
|
||||
= link_to material_symbol('edit'), edit_admin_role_path(role), title: t('.edit_role'), class: 'table-icon-link' if can?(:update, role)
|
||||
@ -0,0 +1,16 @@
|
||||
- content_for :page_title do
|
||||
= t('.title')
|
||||
|
||||
= simple_form_for @admin_settings, url: admin_email_subscriptions_additional_footer_text_path do |form|
|
||||
= render 'shared/error_messages', object: @admin_settings
|
||||
|
||||
.fields-group
|
||||
= form.input :email_footer_text,
|
||||
as: :text,
|
||||
input_html: { rows: 8 },
|
||||
wrapper: :with_block_label
|
||||
|
||||
.actions
|
||||
= form.button :button,
|
||||
t('generic.save_changes'),
|
||||
type: :submit
|
||||
28
app/views/admin/email_subscriptions/index.html.haml
Normal file
28
app/views/admin/email_subscriptions/index.html.haml
Normal file
@ -0,0 +1,28 @@
|
||||
- content_for :page_title do
|
||||
= t('.title')
|
||||
|
||||
- content_for :heading do
|
||||
.heading-with-lead
|
||||
%h1= t('.title')
|
||||
%p.lead= t('.lead')
|
||||
|
||||
%hr.spacer/
|
||||
|
||||
- if @enabled
|
||||
= render 'roles', roles: @roles
|
||||
%hr.spacer/
|
||||
= render 'accounts', accounts: @accounts
|
||||
%hr.spacer/
|
||||
= render 'compliance_settings'
|
||||
%hr.spacer/
|
||||
= render 'danger_zone', accounts: @accounts
|
||||
- else
|
||||
.simple_form
|
||||
%p.lead= t('.disabled.description')
|
||||
|
||||
- if Rails.application.config.x.email_subscriptions
|
||||
= link_to t('.disabled.get_started'), admin_email_subscriptions_setup_path, class: 'button'
|
||||
- else
|
||||
%aside.callout
|
||||
.content
|
||||
.body= t('.disabled.cannot_be_enabled')
|
||||
25
app/views/admin/email_subscriptions/setups/show.html.haml
Normal file
25
app/views/admin/email_subscriptions/setups/show.html.haml
Normal file
@ -0,0 +1,25 @@
|
||||
- content_for :page_title do
|
||||
= t('admin.email_subscriptions.index.title')
|
||||
|
||||
- content_for :heading do
|
||||
.heading-with-lead
|
||||
%h1= t('admin.email_subscriptions.index.title')
|
||||
%p.lead= t('admin.email_subscriptions.index.lead')
|
||||
|
||||
= simple_form_for @form, url: admin_email_subscriptions_setup_path, method: :post do |f|
|
||||
= render 'shared/error_messages', object: @form
|
||||
|
||||
%h3= t('.important_information')
|
||||
|
||||
%ol.numbered-list
|
||||
%li= t('.list.1_permission_explanation')
|
||||
%li= t('.list.2_feature_explanation')
|
||||
%li= t('.list.3_privacy_policy_warning')
|
||||
%li= t('.list.4_cost_warning')
|
||||
|
||||
.fields-group
|
||||
= f.input :agreement_email_volume, as: :boolean, wrapper: :with_label
|
||||
= f.input :agreement_privacy_and_terms, as: :boolean, wrapper: :with_label
|
||||
|
||||
.actions
|
||||
= f.button :button, t('.enable_feature'), type: :submit
|
||||
@ -18,3 +18,5 @@
|
||||
- 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)
|
||||
- if Setting.email_footer_text.present?
|
||||
%p.email-footer-p= Setting.email_footer_text
|
||||
|
||||
@ -34,3 +34,5 @@
|
||||
- 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)
|
||||
- if Setting.email_footer_text.present?
|
||||
%p.email-footer-p= Setting.email_footer_text
|
||||
|
||||
@ -38,7 +38,7 @@
|
||||
.fields-group
|
||||
= ff.input :show_application, wrapper: :with_label
|
||||
|
||||
- if Mastodon::Feature.email_subscriptions_enabled? && current_user.can?(:manage_email_subscriptions)
|
||||
- if Rails.application.config.x.email_subscriptions && Setting.email_subscriptions && current_user.can?(:manage_email_subscriptions)
|
||||
%h2= t('privacy.email_subscriptions')
|
||||
|
||||
%p.lead= t('privacy.email_subscriptions_hint_html')
|
||||
|
||||
11
app/workers/admin/email_subscriptions_purge_worker.rb
Normal file
11
app/workers/admin/email_subscriptions_purge_worker.rb
Normal file
@ -0,0 +1,11 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::EmailSubscriptionsPurgeWorker
|
||||
include Sidekiq::Worker
|
||||
|
||||
sidekiq_options lock: :until_executed, lock_ttl: 1.week.to_i
|
||||
|
||||
def perform
|
||||
EmailSubscription.in_batches.delete_all
|
||||
end
|
||||
end
|
||||
@ -7,7 +7,7 @@ class EmailDistributionWorker
|
||||
sidekiq_options lock: :until_executed, lock_ttl: 1.day.to_i
|
||||
|
||||
def perform(account_id)
|
||||
return unless Mastodon::Feature.email_subscriptions_enabled?
|
||||
return unless Rails.application.config.x.email_subscriptions && Setting.email_subscriptions
|
||||
|
||||
@account = Account.find(account_id)
|
||||
|
||||
|
||||
5
config/initializers/email_subscriptions.rb
Normal file
5
config/initializers/email_subscriptions.rb
Normal file
@ -0,0 +1,5 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
Rails.application.configure do
|
||||
config.x.email_subscriptions = ENV['DISABLE_EMAIL_SUBSCRIPTIONS'] != 'true'
|
||||
end
|
||||
@ -487,6 +487,71 @@ en:
|
||||
resolved_dns_records_hint_html: The domain name resolves to the following MX domains, which are ultimately responsible for accepting email. Blocking an MX domain will block sign-ups from any email address which uses the same MX domain, even if the visible domain name is different. <strong>Be careful not to block major email providers.</strong>
|
||||
resolved_through_html: Resolved through %{domain}
|
||||
title: Blocked email domains
|
||||
email_subscriptions:
|
||||
accounts:
|
||||
account: Account
|
||||
active: Active
|
||||
empty:
|
||||
hint: No accounts have subscribers yet.
|
||||
no_lists_yet: No lists yet
|
||||
inactive: Inactive
|
||||
last_email: Last email
|
||||
lead: Accounts who have enabled the feature and have subscribers will show below.
|
||||
status: Status
|
||||
subscribers: Subscribers
|
||||
title: Mailing lists
|
||||
additional_footer_texts:
|
||||
show:
|
||||
title: Additional footer text
|
||||
compliance_settings:
|
||||
additional_footer_text:
|
||||
action: Manage
|
||||
hint: Optional text that appears in the footer of newsletter emails only
|
||||
title: Additional footer text
|
||||
lead: Email newsletters may be considered marketing emails, depending on the jurisdictions where you operate.
|
||||
privacy_policy:
|
||||
action: Manage
|
||||
hint: This policy is linked in the footer of every email
|
||||
title: Privacy policy
|
||||
title: Compliance settings
|
||||
danger_zone:
|
||||
disable_feature:
|
||||
action: Disable
|
||||
hint: Turn off feature for all accounts
|
||||
title: Disable feature
|
||||
erase_all_data:
|
||||
action: Erase data
|
||||
hint: Permanently deletes all emails in all mailing lists
|
||||
title: Erase all data
|
||||
title: Danger zone
|
||||
disabled_msg: Email subscriptions have been successfully disabled.
|
||||
index:
|
||||
disabled:
|
||||
cannot_be_enabled: Your technical provider has not enabled this feature for your server.
|
||||
description: This feature allows specified accounts to add a widget to their profiles, enabling visitors without a Mastodon account to receive their posts via email.
|
||||
get_started: Get started
|
||||
lead: Allow visitors to receive posts via email from dedicated accounts on this server.
|
||||
title: Email newsletters
|
||||
purged_msg: All email subscriptions data is being erased.
|
||||
roles:
|
||||
accounts: Accounts
|
||||
edit_role: Edit role
|
||||
empty:
|
||||
hint: No one has permission to use this feature.
|
||||
no_roles_added: No roles added
|
||||
lead: Accounts with the following roles can enable this feature on their profiles.
|
||||
manage_roles: Manage roles
|
||||
role_name: Role name
|
||||
title: Roles
|
||||
setups:
|
||||
show:
|
||||
enable_feature: Enable feature
|
||||
important_information: Important information
|
||||
list:
|
||||
1_permission_explanation: When this feature is enabled, accounts with the designated permissions can add an email collection form to their profiles.
|
||||
2_feature_explanation: When visitors sign up on an account’s profile page and confirm their subscription, they’ll begin to receive email updates when the account creates new public posts.
|
||||
3_privacy_policy_warning: Server admins will have access to PII (email addresses) collected. As such, the privacy policy and Terms of Service for the server must be updated before using this feature.
|
||||
4_cost_warning: Emails may incur a fee depending on hosting setup. Discuss with your hosting provider before enabling, as this feature could drastically increase the amount of emails sent from your server.
|
||||
export_domain_allows:
|
||||
new:
|
||||
title: Import domain allows
|
||||
@ -792,7 +857,7 @@ en:
|
||||
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_email_subscriptions_description: Allow users with this permission to enable the email newsletter feature for their account
|
||||
manage_federation: Manage Federation
|
||||
manage_federation_description: Allows users to block or allow federation with other domains, and control deliverability
|
||||
manage_invites: Manage Invites
|
||||
|
||||
@ -92,6 +92,7 @@ en:
|
||||
closed_registrations_message: Displayed when sign-ups are closed
|
||||
content_cache_retention_period: All posts from other servers (including boosts and replies) will be deleted after the specified number of days, without regard to any local user interaction with those posts. This includes posts where a local user has marked it as bookmarks or favorites. Private mentions between users from different instances will also be lost and impossible to restore. Use of this setting is intended for special purpose instances and breaks many user expectations when implemented for general purpose use.
|
||||
custom_css: You can apply custom styles on the web version of Mastodon.
|
||||
email_footer_text: Optional text that appears in the footer of newsletter emails only.
|
||||
favicon: WEBP, PNG, GIF or JPG. Overrides the default Mastodon favicon with a custom icon.
|
||||
landing_page: Selects what page new visitors see when they first arrive on your server. If you select "Trends", then trends needs to be enabled in the Discovery Settings. If you select "Local feed", then "Access to live feeds featuring local posts" needs to be set to "Everyone" in the Discovery Settings.
|
||||
mascot: Overrides the illustration in the advanced web interface.
|
||||
@ -294,6 +295,7 @@ en:
|
||||
closed_registrations_message: Custom message when sign-ups are not available
|
||||
content_cache_retention_period: Remote content retention period
|
||||
custom_css: Custom CSS
|
||||
email_footer_text: Additional footer text
|
||||
favicon: Favicon
|
||||
landing_page: Landing page for new visitors
|
||||
local_live_feed_access: Access to live feeds featuring local posts
|
||||
@ -322,6 +324,9 @@ en:
|
||||
trendable_by_default: Allow trends without prior review
|
||||
trends: Enable trends
|
||||
wrapstodon: Enable Wrapstodon
|
||||
form_email_subscriptions_confirmation:
|
||||
agreement_email_volume: I understand that enabling this feature may significantly increase the volume of emails sent from the server and that I am solely responsible for any costs incurred.
|
||||
agreement_privacy_and_terms: I have updated the Privacy Policy and Terms of Service.
|
||||
interactions:
|
||||
must_be_follower: Block notifications from non-followers
|
||||
must_be_following: Block notifications from people you don't follow
|
||||
|
||||
@ -74,6 +74,7 @@ SimpleNavigation::Configuration.run do |navigation|
|
||||
s.item :warning_presets, safe_join([material_symbol('warning'), t('admin.warning_presets.title')]), admin_warning_presets_path, highlights_on: %r{/admin/warning_presets}, if: -> { current_user.can?(:manage_settings) }
|
||||
s.item :roles, safe_join([material_symbol('contact_mail'), t('admin.roles.title')]), admin_roles_path, highlights_on: %r{/admin/roles}, if: -> { current_user.can?(:manage_roles) }
|
||||
s.item :announcements, safe_join([material_symbol('campaign'), t('admin.announcements.title')]), admin_announcements_path, highlights_on: %r{/admin/announcements}, if: -> { current_user.can?(:manage_announcements) }
|
||||
s.item :email_subscriptions, safe_join([material_symbol('mail'), t('admin.email_subscriptions.index.title')]), admin_email_subscriptions_path, highlights_on: %r{/admin/email_subscriptions}, if: -> { current_user.can?(:manage_settings) }
|
||||
s.item :custom_emojis, safe_join([material_symbol('mood'), t('admin.custom_emojis.title')]), admin_custom_emojis_path, highlights_on: %r{/admin/custom_emojis}, if: -> { current_user.can?(:manage_custom_emojis) }
|
||||
s.item :webhooks, safe_join([material_symbol('inbox'), t('admin.webhooks.title')]), admin_webhooks_path, highlights_on: %r{/admin/webhooks}, if: -> { current_user.can?(:manage_webhooks) }
|
||||
s.item :fasp, safe_join([material_symbol('extension'), t('admin.fasp.title')]), admin_fasp_providers_path, highlights_on: %r{/admin/fasp}, if: -> { current_user.can?(:manage_federation) } if Mastodon::Feature.fasp_enabled?
|
||||
|
||||
@ -26,6 +26,18 @@ namespace :admin do
|
||||
|
||||
resources :email_domain_blocks, only: [:index, :new, :create], concerns: :batch
|
||||
|
||||
resources :email_subscriptions, only: [:index] do
|
||||
collection do
|
||||
post :purge
|
||||
post :disable
|
||||
end
|
||||
end
|
||||
|
||||
namespace :email_subscriptions do
|
||||
resource :setup, only: [:show, :create]
|
||||
resource :additional_footer_text, only: [:show, :update]
|
||||
end
|
||||
|
||||
resources :action_logs, only: [:index]
|
||||
resources :warning_presets, except: [:new, :show]
|
||||
|
||||
|
||||
@ -2,9 +2,13 @@
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Accounts Email Subscriptions API', feature: :email_subscriptions do
|
||||
RSpec.describe 'Accounts Email Subscriptions API' do
|
||||
let(:account) { Fabricate(:user).account }
|
||||
|
||||
before do
|
||||
Setting.email_subscriptions = true
|
||||
end
|
||||
|
||||
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]) }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user