Merge tag 'v3.3.0' into instance_only_statuses

This commit is contained in:
Renato "Lond" Cerqueira
2020-12-27 11:00:43 +01:00
877 changed files with 35407 additions and 11128 deletions

View File

@ -7,6 +7,6 @@
- domain_blocks.each do |domain_block|
%tr
%td.nowrap
%span{ title: domain_block.domain }= domain_block.domain
%span{ title: "SHA-256: #{domain_block.domain_digest}" }= domain_block.public_domain
%td
= domain_block.public_comment if display_blocks_rationale?

View File

@ -1,22 +1,25 @@
.simple_form__overlay-area{ class: (closed_registrations? && @instance_presenter.closed_registrations_message.present?) ? 'simple_form__overlay-area__blurred' : '' }
= simple_form_for(new_user, url: user_registration_path, namespace: 'registration') do |f|
= simple_form_for(new_user, url: user_registration_path, namespace: 'registration', html: { novalidate: false }) do |f|
%p.lead= t('about.federation_hint_html', instance: content_tag(:strong, site_hostname))
.fields-group
= f.simple_fields_for :account do |account_fields|
= account_fields.input :username, wrapper: :with_label, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username') }, append: "@#{site_hostname}", hint: false, disabled: closed_registrations?
= account_fields.input :username, wrapper: :with_label, label: false, required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', placeholder: t('simple_form.labels.defaults.username'), pattern: '[a-zA-Z0-9_]+', maxlength: 30 }, append: "@#{site_hostname}", hint: false, disabled: closed_registrations?
= f.input :email, placeholder: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
= f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
= f.input :password, placeholder: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: false, disabled: closed_registrations?
= f.input :password_confirmation, placeholder: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
= f.input :confirm_password, as: :string, placeholder: t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
= f.input :website, as: :url, placeholder: t('simple_form.labels.defaults.honeypot', label: 'Website'), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: 'Website'), :autocomplete => 'off' }, hint: false, disabled: closed_registrations?
- if approved_registrations?
.fields-group
= f.simple_fields_for :invite_request do |invite_request_fields|
= invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: false
= invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: Setting.require_invite_text
.fields-group
= f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), disabled: closed_registrations?
= f.input :agreement, as: :boolean, wrapper: :with_label, label: t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true, disabled: closed_registrations?
.actions
= f.button :button, sign_up_message, type: :submit, class: 'button button-primary', disabled: closed_registrations?

View File

@ -2,7 +2,7 @@
= site_hostname
- content_for :header_tags do
= javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'public', crossorigin: 'anonymous'
= render partial: 'shared/og'
.grid-4
@ -17,11 +17,11 @@
.row__information-board
.information-board__section
%span= t 'about.user_count_before'
%strong= number_with_delimiter @instance_presenter.user_count
%strong= number_to_human @instance_presenter.user_count, strip_insignificant_zeros: true
%span= t 'about.user_count_after', count: @instance_presenter.user_count
.information-board__section
%span= t 'about.status_count_before'
%strong= number_with_delimiter @instance_presenter.status_count
%strong= number_to_human @instance_presenter.status_count, strip_insignificant_zeros: true
%span= t 'about.status_count_after', count: @instance_presenter.status_count
.row__mascot
.landing-page__mascot

View File

@ -39,12 +39,12 @@
= render partial: 'statuses/status', collection: @pinned_statuses, as: :status, locals: { pinned: true }
- if @newer_url
.entry= link_to_more @newer_url
.entry= link_to_newer @newer_url
= render partial: 'statuses/status', collection: @statuses, as: :status
- if @older_url
.entry= link_to_more @older_url
.entry= link_to_older @older_url
.column-1
- if @account.memorial?

View File

@ -1,6 +1,10 @@
- content_for :page_title do
= @account.acct
- if @account.instance_actor?
.flash-message.notice
%strong= t('accounts.instance_actor_flash')
= render 'application/card', account: @account
- account = @account
@ -56,19 +60,23 @@
= link_to admin_action_logs_path(target_account_id: @account.id) do
.dashboard__counters__text
- if @account.local? && @account.user.nil?
%span.neutral= t('admin.accounts.deleted')
= t('admin.accounts.deleted')
- elsif @account.memorial?
= t('admin.accounts.memorialized')
- elsif @account.suspended?
%span.red= t('admin.accounts.suspended')
= t('admin.accounts.suspended')
- elsif @account.silenced?
%span.red= t('admin.accounts.silenced')
= t('admin.accounts.silenced')
- elsif @account.local? && @account.user&.disabled?
%span.red= t('admin.accounts.disabled')
= t('admin.accounts.disabled')
- elsif @account.local? && !@account.user&.confirmed?
%span.neutral= t('admin.accounts.confirming')
= t('admin.accounts.confirming')
- elsif @account.local? && !@account.user_approved?
%span.neutral= t('admin.accounts.pending')
= t('admin.accounts.pending')
- elsif @account.sensitized?
= t('admin.accounts.sensitive')
- else
%span.neutral= t('admin.accounts.no_limits_imposed')
= t('admin.accounts.no_limits_imposed')
.dashboard__counters__label= t 'admin.accounts.login_status'
- unless @account.local? && @account.user.nil?
@ -122,19 +130,6 @@
= t('admin.accounts.confirming')
%td= table_link_to 'refresh', t('admin.accounts.resend_confirmation.send'), resend_admin_account_confirmation_path(@account.id), method: :post if can?(:confirm, @account.user)
%tr
%th= t('admin.accounts.login_status')
%td
- if @account.user&.disabled?
= t('admin.accounts.disabled')
- else
= t('admin.accounts.enabled')
%td
- if @account.user&.disabled?
= table_link_to 'unlock', t('admin.accounts.enable'), enable_admin_account_path(@account.id), method: :post if can?(:enable, @account.user)
- elsif @account.user_approved?
= table_link_to 'lock', t('admin.accounts.disable'), new_admin_account_action_path(@account.id, type: 'disable') if can?(:disable, @account.user)
%tr
%th= t('simple_form.labels.defaults.locale')
%td= @account.user_locale
@ -172,49 +167,67 @@
%td
= @account.inbox_url
= fa_icon DeliveryFailureTracker.available?(@account.inbox_url) ? 'check' : 'times'
%td
= table_link_to 'search', @domain_block.present? ? t('admin.domain_blocks.view') : t('admin.accounts.view_domain'), admin_instance_path(@account.domain)
%tr
%th= t('admin.accounts.shared_inbox_url')
%td
= @account.shared_inbox_url
= fa_icon DeliveryFailureTracker.available?(@account.shared_inbox_url) ? 'check': 'times'
%td
- if @domain_block.nil?
= table_link_to 'ban', t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @account.domain)
%div.action-buttons
%div
- if @account.local? && @account.user_approved?
= link_to t('admin.accounts.warn'), new_admin_account_action_path(@account.id, type: 'none'), class: 'button' if can?(:warn, @account)
- if @account.silenced?
= link_to t('admin.accounts.undo_silenced'), unsilence_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsilence, @account)
- elsif !@account.local? || @account.user_approved?
= link_to t('admin.accounts.silence'), new_admin_account_action_path(@account.id, type: 'silence'), class: 'button button--destructive' if can?(:silence, @account)
- if @account.suspended?
%hr.spacer/
- if @account.local?
- if @account.user_pending?
= link_to t('admin.accounts.approve'), approve_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' if can?(:approve, @account.user)
= link_to t('admin.accounts.reject'), reject_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:reject, @account.user)
%p.muted-hint= @deletion_request.present? ? t('admin.accounts.suspension_reversible_hint_html', date: content_tag(:strong, l(@deletion_request.due_at.to_date))) : t('admin.accounts.suspension_irreversible')
- unless @account.user_confirmed?
= link_to t('admin.accounts.confirm'), admin_account_confirmation_path(@account.id), method: :post, class: 'button' if can?(:confirm, @account.user)
= link_to t('admin.accounts.undo_suspension'), unsuspend_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsuspend, @account)
- if @account.suspended?
= link_to t('admin.accounts.undo_suspension'), unsuspend_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsuspend, @account)
- elsif !@account.local? || @account.user_approved?
= link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(@account.id, type: 'suspend'), class: 'button button--destructive' if can?(:suspend, @account)
- if @deletion_request.present?
= link_to t('admin.accounts.delete'), admin_account_path(@account.id), method: :delete, class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure') } if can?(:destroy, @account)
- else
%div.action-buttons
%div
- if @account.local? && @account.user_approved?
= link_to t('admin.accounts.warn'), new_admin_account_action_path(@account.id, type: 'none'), class: 'button' if can?(:warn, @account)
- unless @account.local?
- if DomainBlock.rule_for(@account.domain)
= link_to t('admin.domain_blocks.view'), admin_instance_path(@account.domain), class: 'button'
- if @account.user_disabled?
= link_to t('admin.accounts.enable'), enable_admin_account_path(@account.id), method: :post, class: 'button' if can?(:enable, @account.user)
- else
= link_to t('admin.accounts.disable'), new_admin_account_action_path(@account.id, type: 'disable'), class: 'button' if can?(:disable, @account.user)
- if @account.sensitized?
= link_to t('admin.accounts.undo_sensitized'), unsensitive_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsensitive, @account)
- elsif !@account.local? || @account.user_approved?
= link_to t('admin.accounts.sensitive'), new_admin_account_action_path(@account.id, type: 'sensitive'), class: 'button' if can?(:sensitive, @account)
- if @account.silenced?
= link_to t('admin.accounts.undo_silenced'), unsilence_admin_account_path(@account.id), method: :post, class: 'button' if can?(:unsilence, @account)
- elsif !@account.local? || @account.user_approved?
= link_to t('admin.accounts.silence'), new_admin_account_action_path(@account.id, type: 'silence'), class: 'button' if can?(:silence, @account)
- if @account.local?
- if @account.user_pending?
= link_to t('admin.accounts.approve'), approve_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button' if can?(:approve, @account.user)
= link_to t('admin.accounts.reject'), reject_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:reject, @account.user)
- unless @account.user_confirmed?
= link_to t('admin.accounts.confirm'), admin_account_confirmation_path(@account.id), method: :post, class: 'button' if can?(:confirm, @account.user)
- if !@account.local? || @account.user_approved?
= link_to t('admin.accounts.perform_full_suspension'), new_admin_account_action_path(@account.id, type: 'suspend'), class: 'button' if can?(:suspend, @account)
%div
- if @account.local?
= link_to t('admin.accounts.reset_password'), admin_account_reset_path(@account.id), method: :create, class: 'button' if can?(:reset_password, @account.user)
- if @account.user&.otp_required_for_login?
= link_to t('admin.accounts.disable_two_factor_authentication'), admin_user_two_factor_authentication_path(@account.user.id), method: :delete, class: 'button' if can?(:disable_2fa, @account.user)
- if !@account.memorial? && @account.user_approved?
= link_to t('admin.accounts.memorialize'), memorialize_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:memorialize, @account)
- else
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @account.domain), class: 'button button--destructive'
%div
- if @account.local?
= link_to t('admin.accounts.reset_password'), admin_account_reset_path(@account.id), method: :create, class: 'button' if can?(:reset_password, @account.user)
- if @account.user&.otp_required_for_login?
= link_to t('admin.accounts.disable_two_factor_authentication'), admin_user_two_factor_authentication_path(@account.user.id), method: :delete, class: 'button' if can?(:disable_2fa, @account.user)
- if !@account.memorial? && @account.user_approved?
= link_to t('admin.accounts.memorialize'), memorialize_admin_account_path(@account.id), method: :post, data: { confirm: t('admin.accounts.are_you_sure') }, class: 'button button--destructive' if can?(:memorialize, @account)
- else
= link_to t('admin.accounts.redownload'), redownload_admin_account_path(@account.id), method: :post, class: 'button' if can?(:redownload, @account)
= link_to t('admin.accounts.redownload'), redownload_admin_account_path(@account.id), method: :post, class: 'button' if can?(:redownload, @account)
%hr.spacer/
@ -233,3 +246,13 @@
.actions
= f.button :button, t('admin.account_moderation_notes.create'), type: :submit
%hr.spacer/
- if @account.user&.invite_request&.text&.present?
%div.speech-bubble
%div.speech-bubble__bubble
= @account.user&.invite_request&.text
%div.speech-bubble__owner
= admin_account_link_to @account
= t('admin.accounts.invite_request_text')

View File

@ -2,7 +2,7 @@
= t('admin.action_logs.title')
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
= form_tag admin_action_logs_url, method: 'GET', class: 'simple_form' do
= hidden_field_tag :target_account_id, params[:target_account_id] if params[:target_account_id].present?

View File

@ -2,7 +2,7 @@
= t('admin.custom_emojis.title')
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
- if can?(:create, :custom_emoji)
- content_for :heading_actions do

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
- content_for :page_title do
= t('admin.domain_allows.add_new')

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
- content_for :page_title do
= t('admin.domain_blocks.edit')
@ -20,6 +20,9 @@
.fields-group
= f.input :reject_reports, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reports'), hint: I18n.t('admin.domain_blocks.reject_reports_hint')
.fields-group
= f.input :obfuscate, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.obfuscate'), hint: I18n.t('admin.domain_blocks.obfuscate_hint')
.field-group
= f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), rows: 6

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
- content_for :page_title do
= t('.title')
@ -20,6 +20,9 @@
.fields-group
= f.input :reject_reports, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reports'), hint: I18n.t('admin.domain_blocks.reject_reports_hint')
.fields-group
= f.input :obfuscate, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.obfuscate'), hint: I18n.t('admin.domain_blocks.obfuscate_hint')
.field-group
= f.input :private_comment, wrapper: :with_label, label: I18n.t('admin.domain_blocks.private_comment'), hint: t('admin.domain_blocks.private_comment_hint'), rows: 6

View File

@ -0,0 +1,25 @@
.directory__tag
= link_to admin_instance_path(instance) do
%h4
= instance.domain
%small
- if instance.domain_block
- first_item = true
- if !instance.domain_block.noop?
= t("admin.domain_blocks.severity.#{instance.domain_block.severity}")
- first_item = false
- unless instance.domain_block.suspend?
- if instance.domain_block.reject_media?
- unless first_item
•
= t('admin.domain_blocks.rejecting_media')
- first_item = false
- if instance.domain_block.reject_reports?
- unless first_item
•
= t('admin.domain_blocks.rejecting_reports')
- elsif whitelist_mode?
= t('admin.accounts.whitelisted')
- else
= t('admin.accounts.no_limits_imposed')
.trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true

View File

@ -32,32 +32,10 @@
%hr.spacer/
- @instances.each do |instance|
.directory__tag
= link_to admin_instance_path(instance) do
%h4
= instance.domain
%small
- if instance.domain_block
- first_item = true
- if !instance.domain_block.noop?
= t("admin.domain_blocks.severity.#{instance.domain_block.severity}")
- first_item = false
- unless instance.domain_block.suspend?
- if instance.domain_block.reject_media?
- unless first_item
•
= t('admin.domain_blocks.rejecting_media')
- first_item = false
- if instance.domain_block.reject_reports?
- unless first_item
•
= t('admin.domain_blocks.rejecting_reports')
- elsif whitelist_mode?
= t('admin.accounts.whitelisted')
- else
= t('admin.accounts.no_limits_imposed')
- if instance.countable?
.trends__item__current{ title: t('admin.instances.known_accounts', count: instance.accounts_count) }= number_to_human instance.accounts_count, strip_insignificant_zeros: true
- if @instances.empty?
%div.muted-hint.center-text
= t 'admin.instances.empty'
- else
= render @instances
= paginate paginated_instances
= paginate @instances

View File

@ -3,57 +3,59 @@
.dashboard__counters
%div
%div
.dashboard__counters__num= number_with_delimiter @following_count
.dashboard__counters__label= t 'admin.instances.total_followed_by_them'
%div
%div
.dashboard__counters__num= number_with_delimiter @followers_count
.dashboard__counters__label= t 'admin.instances.total_followed_by_us'
%div
%div
.dashboard__counters__num= number_to_human_size @media_storage
.dashboard__counters__label= t 'admin.instances.total_storage'
%div
%div
.dashboard__counters__num= number_with_delimiter @blocks_count
.dashboard__counters__label= t 'admin.instances.total_blocked_by_us'
= link_to admin_accounts_path(remote: '1', by_domain: @instance.domain) do
.dashboard__counters__num= number_with_delimiter @instance.accounts_count
.dashboard__counters__label= t 'admin.accounts.title'
%div
= link_to admin_reports_path(by_target_domain: @instance.domain) do
.dashboard__counters__num= number_with_delimiter @reports_count
.dashboard__counters__num= number_with_delimiter @instance.reports_count
.dashboard__counters__label= t 'admin.instances.total_reported'
%div
%div
.dashboard__counters__num= number_to_human_size @instance.media_storage
.dashboard__counters__label= t 'admin.instances.total_storage'
%div
%div
.dashboard__counters__num= number_with_delimiter @instance.following_count
.dashboard__counters__label= t 'admin.instances.total_followed_by_them'
%div
%div
.dashboard__counters__num= number_with_delimiter @instance.followers_count
.dashboard__counters__label= t 'admin.instances.total_followed_by_us'
%div
%div
.dashboard__counters__num= number_with_delimiter @instance.blocks_count
.dashboard__counters__label= t 'admin.instances.total_blocked_by_us'
%div
%div
.dashboard__counters__num
- if @available
- if @instance.delivery_failure_tracker.available?
= fa_icon 'check'
- else
= fa_icon 'times'
.dashboard__counters__label= t 'admin.instances.delivery_available'
- if @private_comment.present?
- if @instance.private_comment.present?
.speech-bubble
.speech-bubble__bubble
= simple_format(h(@private_comment))
= simple_format(h(@instance.private_comment))
.speech-bubble__owner= t 'admin.instances.private_comment'
- if @public_comment.present?
- if @instance.public_comment.present?
.speech-bubble
.speech-bubble__bubble
= simple_format(h(@public_comment))
= simple_format(h(@instance.public_comment))
.speech-bubble__owner= t 'admin.instances.public_comment'
%hr.spacer/
%div.action-buttons
%div
= link_to t('admin.accounts.title'), admin_accounts_path(remote: '1', by_domain: @instance.domain), class: 'button'
%div
- if @domain_allow
= link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
- elsif @domain_block
= link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@domain_block), class: 'button'
= link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@domain_block), class: 'button'
- if @instance.domain_allow
= link_to t('admin.domain_allows.undo'), admin_domain_allow_path(@instance.domain_allow), class: 'button button--destructive', data: { confirm: t('admin.accounts.are_you_sure'), method: :delete }
- elsif @instance.domain_block
= link_to t('admin.domain_blocks.edit'), edit_admin_domain_block_path(@instance.domain_block), class: 'button'
= link_to t('admin.domain_blocks.undo'), admin_domain_block_path(@instance.domain_block), class: 'button'
- else
= link_to t('admin.domain_blocks.add_new'), new_admin_domain_block_path(_domain: @instance.domain), class: 'button'

View File

@ -0,0 +1,11 @@
.batch-table__row
%label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
= f.check_box :ip_block_ids, { multiple: true, include_hidden: false }, ip_block.id
.batch-table__row__content
.batch-table__row__content__text
%samp= "#{ip_block.ip}/#{ip_block.ip.prefix}"
- if ip_block.comment.present?
= ip_block.comment
%br/
= t("simple_form.labels.ip_block.severities.#{ip_block.severity}")

View File

@ -0,0 +1,28 @@
- content_for :page_title do
= t('admin.ip_blocks.title')
- content_for :header_tags do
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
- if can?(:create, :ip_block)
- content_for :heading_actions do
= link_to t('admin.ip_blocks.add_new'), new_admin_ip_block_path, class: 'button'
= form_for(@form, url: batch_admin_ip_blocks_path) do |f|
= hidden_field_tag :page, params[:page] || 1
.batch-table
.batch-table__toolbar
%label.batch-table__toolbar__select.batch-checkbox-all
= check_box_tag :batch_checkbox_all, nil, false
.batch-table__toolbar__actions
- if can?(:destroy, :ip_block)
= f.button safe_join([fa_icon('times'), t('admin.ip_blocks.delete')]), name: :delete, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') }
.batch-table__body
- if @ip_blocks.empty?
= nothing_here 'nothing-here--under-tabs'
- else
= render partial: 'ip_block', collection: @ip_blocks, locals: { f: f }
= paginate @ip_blocks

View File

@ -0,0 +1,20 @@
- content_for :page_title do
= t('.title')
= simple_form_for @ip_block, url: admin_ip_blocks_path do |f|
= render 'shared/error_messages', object: @ip_block
.fields-group
= f.input :ip, as: :string, wrapper: :with_block_label, input_html: { placeholder: '192.0.2.0/24' }
.fields-group
= f.input :expires_in, wrapper: :with_block_label, collection: [1.day, 2.weeks, 1.month, 6.months, 1.year, 3.years].map(&:to_i), label_method: lambda { |i| I18n.t("admin.ip_blocks.expires_in.#{i}") }, prompt: I18n.t('invites.expires_in_prompt')
.fields-group
= f.input :severity, as: :radio_buttons, collection: IpBlock.severities.keys, include_blank: false, wrapper: :with_block_label, label_method: lambda { |severity| safe_join([I18n.t("simple_form.labels.ip_block.severities.#{severity}"), content_tag(:span, I18n.t("simple_form.hints.ip_block.severities.#{severity}"), class: 'hint')]) }
.fields-group
= f.input :comment, as: :string, wrapper: :with_block_label
.actions
= f.button :button, t('admin.ip_blocks.add_new'), type: :submit

View File

@ -7,7 +7,7 @@
%strong= account.user_email
= "(@#{account.username})"
%br/
= account.user_current_sign_in_ip
%samp= account.user_current_sign_in_ip
= t 'admin.accounts.time_in_queue', time: time_ago_in_words(account.user&.created_at)

View File

@ -2,7 +2,7 @@
= t('admin.pending_accounts.title', count: User.pending.count)
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
= form_for(@form, url: batch_admin_pending_accounts_path) do |f|
= hidden_field_tag :page, params[:page] || 1

View File

@ -59,6 +59,10 @@
= fa_icon('camera')
= report.media_attachments.count
- if report.forwarded?
·
= t('admin.reports.forwarded_to', domain: target_account.domain)
.report-card__summary__item__assigned
- if report.assigned_account.present?
= admin_account_link_to report.assigned_account

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
- content_for :page_title do
= t('admin.reports.report', id: @report.id)
@ -46,6 +46,16 @@
%td{ colspan: 2 }
- if @report.action_taken?
= table_link_to 'envelope-open', t('admin.reports.reopen'), admin_report_path(@report, outcome: 'reopen'), method: :put
- unless @report.target_account.local?
%tr
%th= t('admin.reports.forwarded')
%td{ colspan: 3 }
- if @report.forwarded.nil?
\-
- elsif @report.forwarded?
= t('simple_form.yes')
- else
= t('simple_form.no')
- if !@report.action_taken_by_account.nil?
%tr
%th= t('admin.reports.action_taken_by')

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
- content_for :page_title do
= t('admin.settings.title')
@ -44,7 +44,14 @@
%hr.spacer/
.fields-group
= f.input :enable_bootstrap_timeline_accounts, as: :boolean, wrapper: :with_label, label: t('admin.settings.enable_bootstrap_timeline_accounts.title')
= f.input :require_invite_text, as: :boolean, wrapper: :with_label, label: t('admin.settings.registrations.require_invite_text.title'), hint: t('admin.settings.registrations.require_invite_text.desc_html'), disabled: !approved_registrations?
.fields-group
%hr.spacer/
.fields-group
= f.input :enable_bootstrap_timeline_accounts, as: :boolean, wrapper: :with_label, label: t('admin.settings.enable_bootstrap_timeline_accounts.title'), hint: t('admin.settings.enable_bootstrap_timeline_accounts.desc_html')
.fields-group
= f.input :bootstrap_timeline_accounts, wrapper: :with_block_label, label: t('admin.settings.bootstrap_timeline_accounts.title'), hint: t('admin.settings.bootstrap_timeline_accounts.desc_html'), disabled: !Setting.enable_bootstrap_timeline_accounts

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
- content_for :page_title do
= t('admin.statuses.title')

View File

@ -2,7 +2,7 @@
= t('admin.tags.title')
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
.filters
.filter-subset

View File

@ -1,14 +1,14 @@
- content_for :page_title do
= t('auth.set_new_password')
= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f|
= simple_form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put, novalidate: false }) do |f|
= render 'shared/error_messages', object: resource
- if !use_seamless_external_login? || resource.encrypted_password.present?
= f.input :reset_password_token, as: :hidden
.fields-group
= f.input :password, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, required: true
= f.input :password, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, required: true
.fields-group
= f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, required: true

View File

@ -27,5 +27,5 @@
- else
%time.time-ago{ datetime: session.updated_at.iso8601, title: l(session.updated_at) }= l(session.updated_at)
%td
- if current_session.session_id != session.session_id
- if current_session.session_id != session.session_id && !current_account.suspended?
= table_link_to 'times', t('sessions.revoke'), settings_session_path(session), method: :delete

View File

@ -5,7 +5,7 @@
%h3= t('auth.security')
= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'auth_edit' }) do |f|
= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put, class: 'auth_edit', novalidate: false }) do |f|
= render 'shared/error_messages', object: resource
- if !use_seamless_external_login? || resource.encrypted_password.present?
@ -17,7 +17,7 @@
.fields-row
.fields-row__column.fields-group.fields-row__column-6
= f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off' }, hint: t('simple_form.hints.defaults.password'), disabled: current_account.suspended?
= f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.new_password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }, hint: t('simple_form.hints.defaults.password'), disabled: current_account.suspended?
.fields-row__column.fields-group.fields-row__column-6
= f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_new_password'), input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_new_password'), :autocomplete => 'off' }, disabled: current_account.suspended?
@ -30,18 +30,19 @@
= render 'sessions'
%hr.spacer/
%h3= t('auth.migrate_account')
%p.muted-hint= t('auth.migrate_account_html', path: settings_migration_path)
%hr.spacer/
%h3= t('migrations.incoming_migrations')
%p.muted-hint= t('migrations.incoming_migrations_html', path: settings_aliases_path)
- if open_deletion? && !current_account.suspended?
- unless current_account.suspended?
%hr.spacer/
%h3= t('auth.delete_account')
%p.muted-hint= t('auth.delete_account_html', path: settings_delete_path)
%h3= t('auth.migrate_account')
%p.muted-hint= t('auth.migrate_account_html', path: settings_migration_path)
%hr.spacer/
%h3= t('migrations.incoming_migrations')
%p.muted-hint= t('migrations.incoming_migrations_html', path: settings_aliases_path)
- if open_deletion?
%hr.spacer/
%h3= t('auth.delete_account')
%p.muted-hint= t('auth.delete_account_html', path: settings_delete_path)

View File

@ -4,7 +4,7 @@
- content_for :header_tags do
= render partial: 'shared/og', locals: { description: description_for_sign_up }
= simple_form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f|
= simple_form_for(resource, as: resource_name, url: registration_path(resource_name), html: { novalidate: false }) do |f|
= render 'shared/error_messages', object: resource
- if @invite.present? && @invite.autofollow?
@ -14,26 +14,29 @@
= f.simple_fields_for :account do |ff|
.fields-group
= ff.input :username, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off' }, append: "@#{site_hostname}", hint: t('simple_form.hints.defaults.username', domain: site_hostname)
= ff.input :username, wrapper: :with_label, autofocus: true, label: t('simple_form.labels.defaults.username'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.username'), :autocomplete => 'off', pattern: '[a-zA-Z0-9_]+', maxlength: 30 }, append: "@#{site_hostname}", hint: t('simple_form.hints.defaults.username', domain: site_hostname)
.fields-group
= f.input :email, wrapper: :with_label, label: t('simple_form.labels.defaults.email'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.email'), :autocomplete => 'off' }
.fields-group
= f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off' }
= f.input :password, wrapper: :with_label, label: t('simple_form.labels.defaults.password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.password'), :autocomplete => 'off', :minlength => User.password_length.first, :maxlength => User.password_length.last }
.fields-group
= f.input :password_confirmation, wrapper: :with_label, label: t('simple_form.labels.defaults.confirm_password'), required: true, input_html: { 'aria-label' => t('simple_form.labels.defaults.confirm_password'), :autocomplete => 'off' }
= f.input :confirm_password, as: :string, wrapper: :with_label, label: t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: t('simple_form.labels.defaults.password')), :autocomplete => 'off' }
= f.input :website, as: :url, wrapper: :with_label, label: t('simple_form.labels.defaults.honeypot', label: 'Website'), required: false, input_html: { 'aria-label' => t('simple_form.labels.defaults.honeypot', label: 'Website'), :autocomplete => 'off' }
- if approved_registrations? && !@invite.present?
.fields-group
= f.simple_fields_for :invite_request, resource.invite_request || resource.build_invite_request do |invite_request_fields|
= invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: false
= invite_request_fields.input :text, as: :text, wrapper: :with_block_label, required: Setting.require_invite_text
= f.input :invite_code, as: :hidden
.fields-group
= f.input :agreement, as: :boolean, wrapper: :with_label, label: whitelist_mode? ? t('auth.checkbox_agreement_without_rules_html', terms_path: terms_path) : t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path)
= f.input :agreement, as: :boolean, wrapper: :with_label, label: whitelist_mode? ? t('auth.checkbox_agreement_without_rules_html', terms_path: terms_path) : t('auth.checkbox_agreement_html', rules_path: about_more_path, terms_path: terms_path), required: true
.actions
= f.button :button, @invite.present? ? t('auth.register') : sign_up_message, type: :submit

View File

@ -22,7 +22,6 @@
.actions
- resource_class.omniauth_providers.each do |provider|
= link_to omniauth_authorize_path(resource_name, provider), class: "button button-#{provider}" do
= t("auth.providers.#{provider}", default: provider.to_s.chomp("_oauth2").capitalize)
= link_to t("auth.providers.#{provider}", default: provider.to_s.chomp("_oauth2").capitalize), omniauth_authorize_path(resource_name, provider), class: "button button-#{provider}", method: :post
.form-footer= render 'auth/shared/links'

View File

@ -1,14 +1,9 @@
- content_for :page_title do
= t('auth.login')
= simple_form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f|
%p.hint.otp-hint= t('simple_form.hints.sessions.otp')
=javascript_pack_tag 'two_factor_authentication', crossorigin: 'anonymous'
.fields-group
= f.input :otp_attempt, type: :number, wrapper: :with_label, label: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, autofocus: true
- if @webauthn_enabled
= render partial: 'auth/sessions/two_factor/webauthn_form', locals: { hidden: @scheme_type != 'webauthn' }
.actions
= f.button :button, t('auth.login'), type: :submit
- if Setting.site_contact_email.present?
%p.hint.subtle-hint= t('users.otp_lost_help_html', email: mail_to(Setting.site_contact_email, nil))
= render partial: 'auth/sessions/two_factor/otp_authentication_form', locals: { hidden: @scheme_type != 'totp' }

View File

@ -0,0 +1,18 @@
= simple_form_for(resource,
as: resource_name,
url: session_path(resource_name),
html: { method: :post, id: 'otp-authentication-form' }.merge(hidden ? { class: 'hidden' } : {})) do |f|
%p.hint.authentication-hint= t('simple_form.hints.sessions.otp')
.fields-group
= f.input :otp_attempt, type: :number, wrapper: :with_label, label: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt'), :autocomplete => 'off' }, autofocus: true
.actions
= f.button :button, t('auth.login'), type: :submit
- if Setting.site_contact_email.present?
%p.hint.subtle-hint= t('users.otp_lost_help_html', email: mail_to(Setting.site_contact_email, nil))
- if @webauthn_enabled
.form-footer
= link_to(t('auth.link_to_webauth'), '#', id: 'link-to-webauthn')

View File

@ -0,0 +1,17 @@
%p.flash-message.hidden#unsupported-browser-message= t 'webauthn_credentials.not_supported'
%p.flash-message.alert.hidden#security-key-error-message= t 'webauthn_credentials.invalid_credential'
= simple_form_for(resource,
as: resource_name,
url: session_path(resource_name),
html: { method: :post, id: 'webauthn-form' }.merge(hidden ? { class: 'hidden' } : {})) do |f|
%h3.title= t('simple_form.title.sessions.webauthn')
%p.hint= t('simple_form.hints.sessions.webauthn')
.actions
= f.button :button, t('auth.use_security_key'), class: 'js-webauthn', type: :submit
.form-footer
%p= t('auth.dont_have_your_security_key')
= link_to(t('auth.link_to_otp'), '#', id: 'link-to-otp')

View File

@ -1,12 +1,12 @@
- content_for :header_tags do
= preload_link_tag asset_pack_path('features/getting_started.js'), crossorigin: 'anonymous'
= preload_link_tag asset_pack_path('features/compose.js'), crossorigin: 'anonymous'
= preload_link_tag asset_pack_path('features/home_timeline.js'), crossorigin: 'anonymous'
= preload_link_tag asset_pack_path('features/notifications.js'), crossorigin: 'anonymous'
= preload_pack_asset 'features/getting_started.js', crossorigin: 'anonymous'
= preload_pack_asset 'features/compose.js', crossorigin: 'anonymous'
= preload_pack_asset 'features/home_timeline.js', crossorigin: 'anonymous'
= preload_pack_asset 'features/notifications.js', crossorigin: 'anonymous'
%meta{name: 'applicationServerKey', content: Rails.configuration.x.vapid_public_key}
= render_initial_state
= javascript_pack_tag 'application', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'application', crossorigin: 'anonymous'
.app-holder#mastodon{ data: { props: Oj.dump(default_props) } }
%noscript

View File

@ -1,6 +1,6 @@
- content_for :header_tags do
= render_initial_state
= javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'public', crossorigin: 'anonymous'
- content_for :content do
.admin-wrapper

View File

@ -6,6 +6,7 @@
- if cdn_host?
%link{ rel: 'dns-prefetch', href: cdn_host }/
%meta{ name: 'cdn-host', content: cdn_host }/
- if storage_host?
%link{ rel: 'dns-prefetch', href: storage_host }/
@ -20,10 +21,10 @@
%title= content_for?(:page_title) ? safe_join([yield(:page_title).chomp.html_safe, title], ' - ') : title
= stylesheet_pack_tag 'common', media: 'all'
= stylesheet_pack_tag current_theme, media: 'all'
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous'
= stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous'
= stylesheet_pack_tag current_theme, media: 'all', crossorigin: 'anonymous'
= javascript_pack_tag 'common', crossorigin: 'anonymous'
= javascript_pack_tag "locale_#{I18n.locale}", crossorigin: 'anonymous'
= csrf_meta_tags
%meta{ name: 'style-nonce', content: request.content_security_policy_nonce }

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'public', crossorigin: 'anonymous'
- content_for :content do
.container-alt

View File

@ -6,12 +6,13 @@
- if cdn_host?
%link{ rel: 'dns-prefetch', href: cdn_host }/
%meta{ name: 'cdn-host', content: cdn_host }/
- if storage_host?
%link{ rel: 'dns-prefetch', href: storage_host }/
= stylesheet_pack_tag 'common', media: 'all'
= stylesheet_pack_tag Setting.default_settings['theme'], media: 'all'
= stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous'
= stylesheet_pack_tag Setting.default_settings['theme'], media: 'all', crossorigin: 'anonymous'
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag "locale_#{I18n.locale}", integrity: true, crossorigin: 'anonymous'
= render_initial_state

View File

@ -5,10 +5,10 @@
%meta{ charset: 'utf-8' }/
%title= safe_join([yield(:page_title), Setting.default_settings['site_title']], ' - ')
%meta{ content: 'width=device-width,initial-scale=1', name: 'viewport' }/
= stylesheet_pack_tag 'common', media: 'all'
= stylesheet_pack_tag Setting.default_settings['theme'], media: 'all'
= javascript_pack_tag 'common', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'error', integrity: true, crossorigin: 'anonymous'
= stylesheet_pack_tag 'common', media: 'all', crossorigin: 'anonymous'
= stylesheet_pack_tag Setting.default_settings['theme'], media: 'all', crossorigin: 'anonymous'
= javascript_pack_tag 'common', crossorigin: 'anonymous'
= javascript_pack_tag 'error', crossorigin: 'anonymous'
%body.error
.dialog
.dialog__illustration

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'public', crossorigin: 'anonymous'
- content_for :content do
- if user_signed_in? && !@hide_header

View File

@ -1,6 +1,6 @@
- content_for :header_tags do
= render_initial_state
= javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'public', crossorigin: 'anonymous'
- content_for :content do
.public-layout

View File

@ -1,6 +1,6 @@
- content_for :header_tags do
= render_initial_state
= javascript_pack_tag 'public', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'public', crossorigin: 'anonymous'
- if @media_attachment.video?
= react_component :video, src: @media_attachment.file.url(:original), preview: @media_attachment.thumbnail.present? ? @media_attachment.thumbnail.url : @media_attachment.file.url(:small), blurhash: @media_attachment.blurhash, width: 670, height: 380, editable: true, detailed: true, inline: true, alt: @media_attachment.description do

View File

@ -26,11 +26,11 @@
= "@#{status.account.acct}"
- if status.spoiler_text?
%div{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
%div.auto-dir
%p
= Formatter.instance.format_spoiler(status)
%div{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
%div.auto-dir
= Formatter.instance.format(status)
- if status.media_attachments.size > 0

View File

@ -20,5 +20,5 @@
%th!= application.scopes.map { |scope| t(scope, scope: [:doorkeeper, :scopes]) }.join(', ')
%td= l application.created_at
%td
- unless application.superapp?
- unless application.superapp? || current_account.suspended?
= table_link_to 'times', t('doorkeeper.authorized_applications.buttons.revoke'), oauth_authorized_application_path(application), method: :delete, data: { confirm: t('doorkeeper.authorized_applications.confirmations.revoke') }

View File

@ -3,7 +3,7 @@
- content_for :header_tags do
%meta{ name: 'robots', content: 'noindex' }/
= javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'about', crossorigin: 'anonymous'
.page-header
%h1= t('about.see_whats_happening')

View File

@ -5,6 +5,8 @@
%table.accounts-table
%tbody
%tr
%td.accounts-table__interrelationships
= interrelationships_icon(@relationships, account.id)
%td= account_link_to account
%td.accounts-table__count.optional
= number_to_human account.statuses_count, strip_insignificant_zeros: true

View File

@ -2,7 +2,7 @@
= t('settings.relationships')
- content_for :header_tags do
= javascript_pack_tag 'admin', integrity: true, async: true, crossorigin: 'anonymous'
= javascript_pack_tag 'admin', async: true, crossorigin: 'anonymous'
.filters
.filter-subset
@ -42,6 +42,8 @@
%label.batch-table__toolbar__select.batch-checkbox-all
= check_box_tag :batch_checkbox_all, nil, false
.batch-table__toolbar__actions
= f.button safe_join([fa_icon('user-plus'), t('relationships.follow_selected_followers')]), name: :follow, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } if followed_by_relationship? && !mutual_relationship?
= f.button safe_join([fa_icon('user-times'), t('relationships.remove_selected_follows')]), name: :unfollow, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } unless followed_by_relationship?
= f.button safe_join([fa_icon('trash'), t('relationships.remove_selected_followers')]), name: :remove_from_followers, class: 'table-action-link', type: :submit, data: { confirm: t('admin.reports.are_you_sure') } unless following_relationship?

View File

@ -36,6 +36,10 @@
%th= t('exports.domain_blocks')
%td= number_with_delimiter @export.total_domain_blocks
%td= table_link_to 'download', t('exports.csv'), settings_exports_domain_blocks_path(format: :csv)
%tr
%th= t('exports.bookmarks')
%td= number_with_delimiter @export.total_bookmarks
%td= table_link_to 'download', t('bookmarks.csv'), settings_exports_bookmarks_path(format: :csv)
%hr.spacer/

View File

@ -9,7 +9,7 @@
= render 'shared/error_messages', object: @featured_tag
.fields-group
= f.input :name, wrapper: :with_block_label, hint: safe_join([t('simple_form.hints.featured_tag.name'), safe_join(@most_used_tags.map { |tag| link_to("##{tag.name}", settings_featured_tags_path(featured_tag: { name: tag.name }), method: :post) }, ', ')], ' ')
= f.input :name, wrapper: :with_block_label, hint: safe_join([t('simple_form.hints.featured_tag.name'), safe_join(@recently_used_tags.map { |tag| link_to("##{tag.name}", settings_featured_tags_path(featured_tag: { name: tag.name }), method: :post) }, ', ')], ' ')
.actions
= f.button :button, t('featured_tags.add_new'), type: :submit

View File

@ -30,6 +30,7 @@
.fields-group
= f.input :setting_auto_play_gif, as: :boolean, wrapper: :with_label, recommended: true
= f.input :setting_reduce_motion, as: :boolean, wrapper: :with_label
= f.input :setting_disable_swiping, as: :boolean, wrapper: :with_label
= f.input :setting_system_font_ui, as: :boolean, wrapper: :with_label
%h4= t 'appearance.toot_layout'

View File

@ -2,17 +2,17 @@
= t('settings.two_factor_authentication')
= simple_form_for @confirmation, url: settings_two_factor_authentication_confirmation_path, method: :post do |f|
%p.hint= t('two_factor_authentication.instructions_html')
%p.hint= t('otp_authentication.instructions_html')
.qr-wrapper
.qr-code!= @qrcode.as_svg(padding: 0, module_size: 4)
.qr-alternative
%p.hint= t('two_factor_authentication.manual_instructions')
%samp.qr-alternative__code= current_user.otp_secret.scan(/.{4}/).join(' ')
%p.hint= t('otp_authentication.manual_instructions')
%samp.qr-alternative__code= @new_otp_secret.scan(/.{4}/).join(' ')
.fields-group
= f.input :otp_attempt, wrapper: :with_label, hint: t('two_factor_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true
= f.input :otp_attempt, wrapper: :with_label, hint: t('otp_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true
.actions
= f.button :button, t('two_factor_authentication.enable'), type: :submit
= f.button :button, t('otp_authentication.enable'), type: :submit

View File

@ -0,0 +1,9 @@
- content_for :page_title do
= t('settings.two_factor_authentication')
.simple_form
%p.hint= t('otp_authentication.description_html')
%hr.spacer/
= link_to t('otp_authentication.setup'), settings_otp_authentication_path, data: { method: :post }, class: 'block-button'

View File

@ -0,0 +1,17 @@
- content_for :page_title do
= t('settings.webauthn_authentication')
.table-wrapper
%table.table
%tbody
- current_user.webauthn_credentials.each do |credential|
%tr
%td= credential.nickname
%td= t('webauthn_credentials.registered_on', date: l(credential.created_at.to_date, format: :with_month_name))
%td
= table_link_to 'trash', t('webauthn_credentials.delete'), settings_webauthn_credential_path(credential.id), method: :delete, data: { confirm: t('webauthn_credentials.delete_confirmation') }
%hr.spacer/
.simple_form
= link_to t('webauthn_credentials.add'), new_settings_webauthn_credential_path, class: 'block-button'

View File

@ -0,0 +1,16 @@
- content_for :page_title do
= t('settings.webauthn_authentication')
= simple_form_for(:new_webauthn_credential, url: settings_webauthn_credentials_path, html: { id: :new_webauthn_credential }) do |f|
%p.flash-message.hidden#unsupported-browser-message= t 'webauthn_credentials.not_supported'
%p.flash-message.alert.hidden#security-key-error-message= t 'webauthn_credentials.invalid_credential'
%p.hint= t('webauthn_credentials.description_html')
.fields_group
= f.input :nickname, wrapper: :with_block_label, hint: t('webauthn_credentials.nickname_hint'), input_html: { :autocomplete => 'off' }, required: true
.actions
= f.button :button, t('webauthn_credentials.add'), class: 'js-webauthn', type: :submit
= javascript_pack_tag 'two_factor_authentication', crossorigin: 'anonymous'

View File

@ -0,0 +1,41 @@
- content_for :page_title do
= t('settings.two_factor_authentication')
- content_for :heading_actions do
= link_to t('two_factor_authentication.disable'), disable_settings_two_factor_authentication_methods_path, class: 'button button--destructive', method: :post
%p.hint
%span.positive-hint
= fa_icon 'check'
= ' '
= t 'two_factor_authentication.enabled'
.table-wrapper
%table.table
%thead
%tr
%th= t('two_factor_authentication.methods')
%th
%tbody
%tr
%td= t('two_factor_authentication.otp')
%td
= table_link_to 'pencil', t('two_factor_authentication.edit'), settings_otp_authentication_path, method: :post
%tr
%td= t('two_factor_authentication.webauthn')
- if current_user.webauthn_enabled?
%td
= table_link_to 'pencil', t('two_factor_authentication.edit'), settings_webauthn_credentials_path, method: :get
- else
%td
= table_link_to 'key', t('two_factor_authentication.add'), new_settings_webauthn_credential_path, method: :get
%hr.spacer/
%h3= t('two_factor_authentication.recovery_codes')
%p.muted-hint= t('two_factor_authentication.lost_recovery_codes')
%hr.spacer/
.simple_form
= link_to t('two_factor_authentication.generate_recovery_codes'), settings_two_factor_authentication_recovery_codes_path, data: { method: :post }, class: 'block-button'

View File

@ -1,36 +0,0 @@
- content_for :page_title do
= t('settings.two_factor_authentication')
- if current_user.otp_required_for_login
%p.hint
%span.positive-hint
= fa_icon 'check'
= ' '
= t 'two_factor_authentication.enabled'
%hr.spacer/
= simple_form_for @confirmation, url: settings_two_factor_authentication_path, method: :delete do |f|
.fields-group
= f.input :otp_attempt, wrapper: :with_block_label, hint: t('two_factor_authentication.code_hint'), label: t('simple_form.labels.defaults.otp_attempt'), input_html: { :autocomplete => 'off' }, required: true
.actions
= f.button :button, t('two_factor_authentication.disable'), type: :submit, class: 'negative'
%hr.spacer/
%h3= t('two_factor_authentication.recovery_codes')
%p.muted-hint= t('two_factor_authentication.lost_recovery_codes')
%hr.spacer/
.simple_form
= link_to t('two_factor_authentication.generate_recovery_codes'), settings_two_factor_authentication_recovery_codes_path, data: { method: :post }, class: 'block-button'
- else
.simple_form
%p.hint= t('two_factor_authentication.description_html')
%hr.spacer/
= link_to t('two_factor_authentication.setup'), settings_two_factor_authentication_path, data: { method: :post }, class: 'block-button'

View File

@ -1,3 +1,6 @@
- if object.errors.any?
.flash-message.alert#error_explanation
%strong= t('generic.validation_errors', count: object.errors.count)
- object.errors[:base].each do |error|
.flash-message.alert
%strong= error

View File

@ -1,5 +1,5 @@
- content_for :header_tags do
= render_initial_state
= javascript_pack_tag 'share', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'share', crossorigin: 'anonymous'
#mastodon-compose{ data: { props: Oj.dump(default_props) } }

View File

@ -20,7 +20,7 @@
%p<
%span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
%button.status__content__spoiler-link= t('statuses.show_more')
.e-content{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
.e-content
= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
- if status.preloadable_poll
= react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
@ -29,17 +29,17 @@
- if !status.media_attachments.empty?
- if status.media_attachments.first.video?
- video = status.media_attachments.first
= react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: status.sensitive?, width: 670, height: 380, detailed: true, inline: true, alt: video.description do
= react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: sensitized?(status, current_account), width: 670, height: 380, detailed: true, inline: true, alt: video.description do
= render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
- elsif status.media_attachments.first.audio?
- audio = status.media_attachments.first
= react_component :audio, src: full_asset_url(audio.file.url(:original)), poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), backgroundColor: audio.file.meta.dig('colors', 'background'), foregroundColor: audio.file.meta.dig('colors', 'foreground'), accentColor: audio.file.meta.dig('colors', 'accent'), width: 670, height: 380, alt: audio.description, duration: audio.file.meta.dig('original', 'duration') do
= render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
- else
= react_component :media_gallery, height: 380, sensitive: status.sensitive?, standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
= react_component :media_gallery, height: 380, sensitive: sensitized?(status, current_account), standalone: true, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
= render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
- elsif status.preview_card
= react_component :card, sensitive: status.sensitive?, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json
= react_component :card, sensitive: sensitized?(status, current_account), 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json
.detailed-status__meta
%data.dt-published{ value: status.created_at.to_time.iso8601 }

View File

@ -1,10 +1,13 @@
:ruby
hide_show_thread ||= false
.status{ class: "status-#{status.visibility}" }
.status__info
= link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', target: stream_link_target, rel: 'noopener noreferrer' do
%span.status__visibility-icon><
= visibility_icon status
%time.time-ago{ datetime: status.created_at.iso8601, title: l(status.created_at) }= l(status.created_at)
%data.dt-published{ value: status.created_at.to_time.iso8601 }
%span.status__visibility-icon
= visibility_icon status
.p-author.h-card
= link_to ActivityPub::TagManager.instance.url_for(status.account), class: 'status__display-name u-url', target: stream_link_target, rel: 'noopener noreferrer' do
@ -17,7 +20,7 @@
%span.display-name
%bdi
%strong.display-name__html.p-name.emojify= display_name(status.account, custom_emojify: true, autoplay: autoplay)
&nbsp;
= ' '
%span.display-name__account
= acct(status.account)
= fa_icon('lock') if status.account.locked?
@ -26,7 +29,7 @@
%p<
%span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: autoplay)}&nbsp;
%button.status__content__spoiler-link= t('statuses.show_more')
.e-content{ dir: rtl_status?(status) ? 'rtl' : 'ltr' }
.e-content
= Formatter.instance.format(status, custom_emojify: true, autoplay: autoplay)
- if status.preloadable_poll
= react_component :poll, disabled: true, poll: ActiveModelSerializers::SerializableResource.new(status.preloadable_poll, serializer: REST::PollSerializer, scope: current_user, scope_name: :current_user).as_json do
@ -35,30 +38,29 @@
- if !status.media_attachments.empty?
- if status.media_attachments.first.video?
- video = status.media_attachments.first
= react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: status.sensitive?, width: 610, height: 343, inline: true, alt: video.description do
= react_component :video, src: full_asset_url(video.file.url(:original)), preview: full_asset_url(video.thumbnail.present? ? video.thumbnail.url : video.file.url(:small)), blurhash: video.blurhash, sensitive: sensitized?(status, current_account), width: 610, height: 343, inline: true, alt: video.description do
= render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
- elsif status.media_attachments.first.audio?
- audio = status.media_attachments.first
= react_component :audio, src: full_asset_url(audio.file.url(:original)), poster: full_asset_url(audio.thumbnail.present? ? audio.thumbnail.url : status.account.avatar_static_url), backgroundColor: audio.file.meta.dig('colors', 'background'), foregroundColor: audio.file.meta.dig('colors', 'foreground'), accentColor: audio.file.meta.dig('colors', 'accent'), width: 610, height: 343, alt: audio.description, duration: audio.file.meta.dig('original', 'duration') do
= render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
- else
= react_component :media_gallery, height: 343, sensitive: status.sensitive?, autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
= react_component :media_gallery, height: 343, sensitive: sensitized?(status, current_account), autoplay: autoplay, media: status.media_attachments.map { |a| ActiveModelSerializers::SerializableResource.new(a, serializer: REST::MediaAttachmentSerializer).as_json } do
= render partial: 'statuses/attachment_list', locals: { attachments: status.media_attachments }
- elsif status.preview_card
= react_component :card, sensitive: status.sensitive?, 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json
= react_component :card, sensitive: sensitized?(status, current_account), 'maxDescription': 160, card: ActiveModelSerializers::SerializableResource.new(status.preview_card, serializer: REST::PreviewCardSerializer).as_json
- if !status.in_reply_to_id.nil? && status.in_reply_to_account_id == status.account.id
- if !status.in_reply_to_id.nil? && status.in_reply_to_account_id == status.account.id && !hide_show_thread
= link_to ActivityPub::TagManager.instance.url_for(status), class: 'status__content__read-more-button', target: stream_link_target, rel: 'noopener noreferrer' do
= t 'statuses.show_thread'
.status__action-bar
.status__action-bar__counter
= link_to remote_interaction_path(status, type: :reply), class: 'status__action-bar-button icon-button modal-button' do
- if status.in_reply_to_id.nil?
= fa_icon 'reply fw'
- else
= fa_icon 'reply-all fw'
.status__action-bar__counter__label= obscured_counter status.replies_count
= link_to remote_interaction_path(status, type: :reply), class: 'status__action-bar-button icon-button icon-button--with-counter modal-button' do
- if status.in_reply_to_id.nil?
= fa_icon 'reply fw'
- else
= fa_icon 'reply-all fw'
%span.icon-button__counter= obscured_counter status.replies_count
= link_to remote_interaction_path(status, type: :reblog), class: 'status__action-bar-button icon-button modal-button' do
- if status.distributable?
= fa_icon 'retweet fw'

View File

@ -17,7 +17,7 @@
- if status.reply? && include_threads
- if @next_ancestor
.entry{ class: entry_classes }
= link_to_more ActivityPub::TagManager.instance.url_for(@next_ancestor)
= link_to_older ActivityPub::TagManager.instance.url_for(@next_ancestor)
= render partial: 'statuses/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id }, autoplay: autoplay
@ -39,21 +39,21 @@
%span
= t('stream_entries.pinned')
= render (centered ? 'statuses/detailed_status' : 'statuses/simple_status'), status: status.proper, autoplay: autoplay
= render (centered ? 'statuses/detailed_status' : 'statuses/simple_status'), status: status.proper, autoplay: autoplay, hide_show_thread: is_predecessor || is_successor
- if include_threads
- if @since_descendant_thread_id
.entry{ class: entry_classes }
= link_to_more short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1)
= link_to_newer short_account_status_url(status.account.username, status, max_descendant_thread_id: @since_descendant_thread_id + 1)
- @descendant_threads.each do |thread|
= render partial: 'statuses/status', collection: thread[:statuses], as: :status, locals: { is_successor: true, parent_id: status.id }, autoplay: autoplay
- if thread[:next_status]
.entry{ class: entry_classes }
= link_to_more ActivityPub::TagManager.instance.url_for(thread[:next_status])
= link_to_newer ActivityPub::TagManager.instance.url_for(thread[:next_status])
- if @next_descendant_thread
.entry{ class: entry_classes }
= link_to_more short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1)
= link_to_newer short_account_status_url(status.account.username, status, since_descendant_thread_id: @max_descendant_thread_id - 1)
- if include_threads && !embedded_view? && !user_signed_in?
.entry{ class: entry_classes }

View File

@ -12,6 +12,7 @@
= opengraph 'og:type', 'article'
= opengraph 'og:title', "#{display_name(@account)} (#{acct(@account)})"
= opengraph 'og:url', short_account_status_url(@account, @status)
= opengraph 'og:published_time', @status.created_at.iso8601
= render 'og_description', activity: @status
= render 'og_image', activity: @status, account: @account

View File

@ -5,7 +5,7 @@
%meta{ name: 'robots', content: 'noindex' }/
%link{ rel: 'alternate', type: 'application/rss+xml', href: tag_url(@tag, format: 'rss') }/
= javascript_pack_tag 'about', integrity: true, crossorigin: 'anonymous'
= javascript_pack_tag 'about', crossorigin: 'anonymous'
= render 'og'
.page-header

View File

@ -0,0 +1,44 @@
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.hero
.email-row
.col-6
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.text-center.padded
%table.hero-icon{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td
= image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: ''
%h1= t 'devise.mailer.webauthn_credential.added.title'
%p.lead= "#{t 'devise.mailer.webauthn_credential.added.explanation' }:"
%p.lead= @webauthn_credential.nickname
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.content-start
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.button-cell
%table.button{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.button-primary
= link_to edit_user_registration_url do
%span= t('settings.account_settings')

View File

@ -0,0 +1,7 @@
<%= t 'devise.mailer.two_factor_enabled.title' %>
===
<%= t 'devise.mailer.two_factor_enabled.explanation' %>
=> <%= edit_user_registration_url %>

View File

@ -0,0 +1,44 @@
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.hero
.email-row
.col-6
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.text-center.padded
%table.hero-icon.alert-icon{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td
= image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: ''
%h1= t 'devise.mailer.webauthn_credential.deleted.title'
%p.lead= "#{t 'devise.mailer.webauthn_credential.deleted.explanation' }:"
%p.lead= @webauthn_credential.nickname
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.content-start
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.button-cell
%table.button{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.button-primary
= link_to edit_user_registration_url do
%span= t('settings.account_settings')

View File

@ -0,0 +1,7 @@
<%= t 'devise.mailer.webauthn_credential.deleted.title' %>
===
<%= t 'devise.mailer.webauthn_credential.deleted.explanation' %>
=> <%= edit_user_registration_url %>

View File

@ -0,0 +1,43 @@
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.hero
.email-row
.col-6
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.text-center.padded
%table.hero-icon.alert-icon{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td
= image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: ''
%h1= t 'devise.mailer.webauthn_disabled.title'
%p.lead= t 'devise.mailer.webauthn_disabled.explanation'
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.content-start
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.button-cell
%table.button{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.button-primary
= link_to edit_user_registration_url do
%span= t('settings.account_settings')

View File

@ -0,0 +1,7 @@
<%= t 'devise.mailer.webauthn_disabled.title' %>
===
<%= t 'devise.mailer.webauthn_disabled.explanation' %>
=> <%= edit_user_registration_url %>

View File

@ -0,0 +1,43 @@
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.hero
.email-row
.col-6
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.text-center.padded
%table.hero-icon{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td
= image_tag full_pack_url('media/images/mailer/icon_lock_open.png'), alt: ''
%h1= t 'devise.mailer.webauthn_enabled.title'
%p.lead= t 'devise.mailer.webauthn_enabled.explanation'
%table.email-table{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.email-body
.email-container
%table.content-section{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.content-cell.content-start
%table.column{ cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.column-cell.button-cell
%table.button{ align: 'center', cellspacing: 0, cellpadding: 0 }
%tbody
%tr
%td.button-primary
= link_to edit_user_registration_url do
%span= t('settings.account_settings')

View File

@ -0,0 +1,7 @@
<%= t 'devise.mailer.webauthn_credentia.added.title' %>
===
<%= t 'devise.mailer.webauthn_credentia.added.explanation' %>
=> <%= edit_user_registration_url %>