Merge remote-tracking branch 'parent/main' into upstream-20240124
This commit is contained in:
commit
54f5113b46
106 changed files with 1396 additions and 1071 deletions
|
@ -24,7 +24,7 @@ class ActivityPub::FollowersSynchronizationsController < ActivityPub::BaseContro
|
|||
end
|
||||
|
||||
def set_items
|
||||
@items = @account.followers.where(Account.arel_table[:uri].matches("#{Account.sanitize_sql_like(uri_prefix)}/%", false, true)).or(@account.followers.where(uri: uri_prefix)).pluck(:uri)
|
||||
@items = @account.followers.matches_uri_prefix(uri_prefix).pluck(:uri)
|
||||
end
|
||||
|
||||
def collection_presenter
|
||||
|
|
|
@ -30,7 +30,7 @@ class Api::V1::Accounts::FollowerAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:active_relationships, :account_stat).references(:active_relationships)
|
||||
Account.includes(:active_relationships, :account_stat, :user).references(:active_relationships)
|
||||
end
|
||||
|
||||
def paginated_follows
|
||||
|
|
|
@ -30,7 +30,7 @@ class Api::V1::Accounts::FollowingAccountsController < Api::BaseController
|
|||
end
|
||||
|
||||
def default_accounts
|
||||
Account.includes(:passive_relationships, :account_stat).references(:passive_relationships)
|
||||
Account.includes(:passive_relationships, :account_stat, :user).references(:passive_relationships)
|
||||
end
|
||||
|
||||
def paginated_follows
|
||||
|
|
|
@ -17,7 +17,7 @@ class Api::V1::BlocksController < Api::BaseController
|
|||
end
|
||||
|
||||
def paginated_blocks
|
||||
@paginated_blocks ||= Block.eager_load(target_account: :account_stat)
|
||||
@paginated_blocks ||= Block.eager_load(target_account: [:account_stat, :user])
|
||||
.joins(:target_account)
|
||||
.merge(Account.without_suspended)
|
||||
.where(account: current_account)
|
||||
|
|
|
@ -27,7 +27,7 @@ class Api::V1::DirectoriesController < Api::BaseController
|
|||
scope.merge!(local_account_scope) if local_accounts?
|
||||
scope.merge!(account_exclusion_scope) if current_account
|
||||
scope.merge!(account_domain_block_scope) if current_account && !local_accounts?
|
||||
end
|
||||
end.includes(:account_stat, user: :role)
|
||||
end
|
||||
|
||||
def local_accounts?
|
||||
|
|
|
@ -25,7 +25,7 @@ class Api::V1::EndorsementsController < Api::BaseController
|
|||
end
|
||||
|
||||
def endorsed_accounts
|
||||
current_account.endorsed_accounts.includes(:account_stat).without_suspended
|
||||
current_account.endorsed_accounts.includes(:account_stat, :user).without_suspended
|
||||
end
|
||||
|
||||
def insert_pagination_headers
|
||||
|
|
|
@ -37,7 +37,7 @@ class Api::V1::FollowRequestsController < Api::BaseController
|
|||
end
|
||||
|
||||
def default_accounts
|
||||
Account.without_suspended.includes(:follow_requests, :account_stat).references(:follow_requests)
|
||||
Account.without_suspended.includes(:follow_requests, :account_stat, :user).references(:follow_requests)
|
||||
end
|
||||
|
||||
def paginated_follow_requests
|
||||
|
|
|
@ -37,9 +37,9 @@ class Api::V1::Lists::AccountsController < Api::BaseController
|
|||
|
||||
def load_accounts
|
||||
if unlimited?
|
||||
@list.accounts.without_suspended.includes(:account_stat).all
|
||||
@list.accounts.without_suspended.includes(:account_stat, :user).all
|
||||
else
|
||||
@list.accounts.without_suspended.includes(:account_stat).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
@list.accounts.without_suspended.includes(:account_stat, :user).paginate_by_max_id(limit_param(DEFAULT_ACCOUNTS_LIMIT), params[:max_id], params[:since_id])
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ class Api::V1::MutesController < Api::BaseController
|
|||
end
|
||||
|
||||
def paginated_mutes
|
||||
@paginated_mutes ||= Mute.eager_load(:target_account)
|
||||
@paginated_mutes ||= Mute.eager_load(target_account: [:account_stat, :user])
|
||||
.joins(:target_account)
|
||||
.merge(Account.without_suspended)
|
||||
.where(account: current_account)
|
||||
|
|
|
@ -14,14 +14,14 @@ class Api::V1::Statuses::FavouritedByAccountsController < Api::V1::Statuses::Bas
|
|||
|
||||
def load_accounts
|
||||
scope = default_accounts
|
||||
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
|
||||
scope = scope.not_excluded_by_account(current_account) unless current_account.nil?
|
||||
scope.merge(paginated_favourites).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account
|
||||
.without_suspended
|
||||
.includes(:favourites, :account_stat)
|
||||
.includes(:favourites, :account_stat, :user)
|
||||
.references(:favourites)
|
||||
.where(favourites: { status_id: @status.id })
|
||||
end
|
||||
|
|
|
@ -14,12 +14,12 @@ class Api::V1::Statuses::RebloggedByAccountsController < Api::V1::Statuses::Base
|
|||
|
||||
def load_accounts
|
||||
scope = default_accounts
|
||||
scope = scope.where.not(id: current_account.excluded_from_timeline_account_ids) unless current_account.nil?
|
||||
scope = scope.not_excluded_by_account(current_account) unless current_account.nil?
|
||||
scope.merge(paginated_statuses).to_a
|
||||
end
|
||||
|
||||
def default_accounts
|
||||
Account.without_suspended.includes(:statuses, :account_stat).references(:statuses)
|
||||
Account.without_suspended.includes(:statuses, :account_stat, :user).references(:statuses)
|
||||
end
|
||||
|
||||
def paginated_statuses
|
||||
|
|
|
@ -35,7 +35,7 @@ class Api::V2::FiltersController < Api::BaseController
|
|||
private
|
||||
|
||||
def set_filters
|
||||
@filters = current_account.custom_filters.includes(:keywords)
|
||||
@filters = current_account.custom_filters.includes(:keywords, :statuses)
|
||||
end
|
||||
|
||||
def set_filter
|
||||
|
|
|
@ -181,6 +181,11 @@ class Auth::SessionsController < Devise::SessionsController
|
|||
ip: request.remote_ip,
|
||||
user_agent: request.user_agent
|
||||
)
|
||||
|
||||
# Only send a notification email every hour at most
|
||||
return if redis.set("2fa_failure_notification:#{user.id}", '1', ex: 1.hour, get: true).present?
|
||||
|
||||
UserMailer.failed_2fa(user, request.remote_ip, request.user_agent, Time.now.utc).deliver_later!
|
||||
end
|
||||
|
||||
def second_factor_attempts_key(user)
|
||||
|
|
|
@ -116,7 +116,6 @@
|
|||
"compose_form.publish_form": "Artículu nuevu",
|
||||
"compose_form.publish_loud": "¡{publish}!",
|
||||
"compose_form.save_changes": "Guardar los cambeos",
|
||||
"compose_form.spoiler.unmarked": "Text is not hidden",
|
||||
"confirmation_modal.cancel": "Encaboxar",
|
||||
"confirmations.block.block_and_report": "Bloquiar ya informar",
|
||||
"confirmations.block.confirm": "Bloquiar",
|
||||
|
@ -146,6 +145,7 @@
|
|||
"dismissable_banner.community_timeline": "Esta seición contién los artículos públicos más actuales de los perfiles agospiaos nel dominiu {domain}.",
|
||||
"dismissable_banner.dismiss": "Escartar",
|
||||
"dismissable_banner.explore_tags": "Esta seición contién les etiquetes del fediversu que tán ganando popularidá güei. Les etiquetes más usaes polos perfiles apaecen no cimero.",
|
||||
"dismissable_banner.public_timeline": "Esta seición contién los artículos más nuevos de les persones na web social que les persones de {domain} siguen.",
|
||||
"embed.instructions": "Empotra esti artículu nel to sitiu web pente la copia del códigu d'abaxo.",
|
||||
"embed.preview": "Va apaecer asina:",
|
||||
"emoji_button.activity": "Actividá",
|
||||
|
@ -155,6 +155,7 @@
|
|||
"emoji_button.not_found": "Nun s'atoparon fustaxes que concasen",
|
||||
"emoji_button.objects": "Oxetos",
|
||||
"emoji_button.people": "Persones",
|
||||
"emoji_button.recent": "D'usu frecuente",
|
||||
"emoji_button.search": "Buscar…",
|
||||
"emoji_button.search_results": "Resultaos de la busca",
|
||||
"emoji_button.symbols": "Símbolos",
|
||||
|
@ -217,7 +218,6 @@
|
|||
"hashtag.column_header.tag_mode.any": "o {additional}",
|
||||
"hashtag.column_header.tag_mode.none": "ensin {additional}",
|
||||
"hashtag.column_settings.select.no_options_message": "Nun s'atopó nenguna suxerencia",
|
||||
"hashtag.column_settings.tag_toggle": "Include additional tags in this column",
|
||||
"hashtag.counter_by_accounts": "{count, plural, one {{counter} participante} other {{counter} participantes}}",
|
||||
"hashtag.follow": "Siguir a la etiqueta",
|
||||
"hashtag.unfollow": "Dexar de siguir a la etiqueta",
|
||||
|
@ -259,7 +259,6 @@
|
|||
"keyboard_shortcuts.reply": "Responder a un artículu",
|
||||
"keyboard_shortcuts.requests": "Abrir la llista de solicitúes de siguimientu",
|
||||
"keyboard_shortcuts.search": "Enfocar la barra de busca",
|
||||
"keyboard_shortcuts.spoilers": "to show/hide CW field",
|
||||
"keyboard_shortcuts.start": "Abrir la columna «Entamar»",
|
||||
"keyboard_shortcuts.toggle_sensitivity": "Amosar/anubrir el conteníu multimedia",
|
||||
"keyboard_shortcuts.toot": "Comenzar un artículu nuevu",
|
||||
|
@ -412,12 +411,16 @@
|
|||
"search.quick_action.go_to_hashtag": "Dir a la etiqueta {x}",
|
||||
"search.quick_action.status_search": "Artículos que concasen con {x}",
|
||||
"search.search_or_paste": "Busca o apiega una URL",
|
||||
"search_popout.language_code": "códigu de llingua ISO",
|
||||
"search_popout.quick_actions": "Aiciones rápides",
|
||||
"search_popout.recent": "Busques de recién",
|
||||
"search_popout.specific_date": "data específica",
|
||||
"search_popout.user": "perfil",
|
||||
"search_results.accounts": "Perfiles",
|
||||
"search_results.all": "Too",
|
||||
"search_results.hashtags": "Etiquetes",
|
||||
"search_results.nothing_found": "Nun se pudo atopar nada con esos términos de busca",
|
||||
"search_results.see_all": "Ver too",
|
||||
"search_results.statuses": "Artículos",
|
||||
"search_results.title": "Busca de: {q}",
|
||||
"server_banner.introduction": "{domain} ye parte de la rede social descentralizada que tien la teunoloxía de {mastodon}.",
|
||||
|
@ -460,6 +463,7 @@
|
|||
"status.replied_to": "En rempuesta a {name}",
|
||||
"status.reply": "Responder",
|
||||
"status.replyAll": "Responder al filu",
|
||||
"status.report": "Informar de @{name}",
|
||||
"status.sensitive_warning": "Conteníu sensible",
|
||||
"status.show_filter_reason": "Amosar de toes toes",
|
||||
"status.show_less": "Amosar menos",
|
||||
|
|
|
@ -150,7 +150,7 @@
|
|||
"compose_form.poll.duration": "Durada de l'enquesta",
|
||||
"compose_form.poll.option_placeholder": "Opció {number}",
|
||||
"compose_form.poll.remove_option": "Elimina aquesta opció",
|
||||
"compose_form.poll.switch_to_multiple": "Canvia l’enquesta per a permetre diverses opcions",
|
||||
"compose_form.poll.switch_to_multiple": "Canvia l’enquesta per a permetre múltiples opcions",
|
||||
"compose_form.poll.switch_to_single": "Canvia l’enquesta per a permetre una única opció",
|
||||
"compose_form.publish": "Tut",
|
||||
"compose_form.publish_form": "Nou tut",
|
||||
|
@ -607,7 +607,7 @@
|
|||
"search.quick_action.status_search": "Tuts coincidint amb {x}",
|
||||
"search.search_or_paste": "Cerca o escriu l'URL",
|
||||
"search_popout.full_text_search_disabled_message": "No disponible a {domain}.",
|
||||
"search_popout.full_text_search_logged_out_message": "Només disponible en iniciar la sessió.",
|
||||
"search_popout.full_text_search_logged_out_message": "Només disponible amb la sessió iniciada.",
|
||||
"search_popout.language_code": "Codi de llengua ISO",
|
||||
"search_popout.options": "Opcions de cerca",
|
||||
"search_popout.quick_actions": "Accions ràpides",
|
||||
|
|
|
@ -683,7 +683,7 @@
|
|||
"status.show_more": "펼치기",
|
||||
"status.show_more_all": "모두 펼치기",
|
||||
"status.show_original": "원본 보기",
|
||||
"status.title.with_attachments": "{user} 님이 {attachmentCount, plural, one {첨부} other {{attachmentCount}개 첨부}}하여 게시",
|
||||
"status.title.with_attachments": "{user} 님이 {attachmentCount, plural, one {첨부파일} other {{attachmentCount}개의 첨부파일}}과 함께 게시함",
|
||||
"status.translate": "번역",
|
||||
"status.translated_from_with": "{provider}에 의해 {lang}에서 번역됨",
|
||||
"status.uncached_media_warning": "마리보기 허용되지 않음",
|
||||
|
|
|
@ -328,6 +328,7 @@
|
|||
"interaction_modal.on_another_server": "En otro sirvidor",
|
||||
"interaction_modal.on_this_server": "En este sirvidor",
|
||||
"interaction_modal.sign_in": "No estas konektado kon este sirvidor. Ande tyenes tu kuento?",
|
||||
"interaction_modal.sign_in_hint": "Konsejo: Akel es el sitio adonde te enrejistrates. Si no lo akodras, bushka el mesaj de posta elektronika de bienvenida en tu kuti de arivo. Tambien puedes eskrivir tu nombre de utilizador kompleto (por enshemplo @Mastodon@mastodon.social)",
|
||||
"interaction_modal.title.favourite": "Endika ke te plaze publikasyon de {name}",
|
||||
"interaction_modal.title.follow": "Sige a {name}",
|
||||
"interaction_modal.title.reblog": "Repartaja publikasyon de {name}",
|
||||
|
@ -478,6 +479,7 @@
|
|||
"onboarding.actions.go_to_explore": "Va a los trendes",
|
||||
"onboarding.actions.go_to_home": "Va a tu linya prinsipala",
|
||||
"onboarding.compose.template": "Ke haber, #Mastodon?",
|
||||
"onboarding.follows.empty": "Malorozamente, no se pueden amostrar rezultados en este momento. Puedes aprovar uzar la bushkeda o navigar por la pajina de eksplorasyon para topar personas a las que segir, o aprovarlo de muevo mas tadre.",
|
||||
"onboarding.follows.title": "Personaliza tu linya prinsipala",
|
||||
"onboarding.profile.discoverable": "Faz ke mi profil apareska en bushkedas",
|
||||
"onboarding.profile.display_name": "Nombre amostrado",
|
||||
|
@ -497,7 +499,9 @@
|
|||
"onboarding.start.title": "Lo logrates!",
|
||||
"onboarding.steps.follow_people.body": "El buto de Mastodon es segir a djente interesante.",
|
||||
"onboarding.steps.follow_people.title": "Personaliza tu linya prinsipala",
|
||||
"onboarding.steps.publish_status.body": "Puedes introdusirte al mundo con teksto, fotos, videos o anketas {emoji}",
|
||||
"onboarding.steps.publish_status.title": "Eskrive tu primera publikasyon",
|
||||
"onboarding.steps.setup_profile.body": "Kompleta tu profil para aumentar tus enteraksyones.",
|
||||
"onboarding.steps.setup_profile.title": "Personaliza tu profil",
|
||||
"onboarding.steps.share_profile.body": "Informe a tus amigos komo toparte en Mastodon",
|
||||
"onboarding.steps.share_profile.title": "Partaja tu profil de Mastodon",
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"account.featured_tags.last_status_never": "Sem publicações",
|
||||
"account.featured_tags.title": "Hashtags em destaque de {name}",
|
||||
"account.follow": "Seguir",
|
||||
"account.follow_back": "Seguir de volta",
|
||||
"account.followers": "Seguidores",
|
||||
"account.followers.empty": "Nada aqui.",
|
||||
"account.followers_counter": "{count, plural, one {{counter} seguidor} other {{counter} seguidores}}",
|
||||
|
@ -52,6 +53,7 @@
|
|||
"account.mute_notifications_short": "Silenciar notificações",
|
||||
"account.mute_short": "Silenciar",
|
||||
"account.muted": "Silenciado",
|
||||
"account.mutual": "Mútuo",
|
||||
"account.no_bio": "Nenhuma descrição fornecida.",
|
||||
"account.open_original_page": "Abrir a página original",
|
||||
"account.posts": "Toots",
|
||||
|
|
|
@ -314,7 +314,7 @@
|
|||
"home.explore_prompt.body": "ฟีดหน้าแรกของคุณจะมีการผสมผสานของโพสต์จากแฮชแท็กที่คุณได้เลือกติดตาม, ผู้คนที่คุณได้เลือกติดตาม และโพสต์ที่เขาดัน หากนั่นรู้สึกเงียบเกินไป คุณอาจต้องการ:",
|
||||
"home.explore_prompt.title": "นี่คือฐานหน้าแรกของคุณภายใน Mastodon",
|
||||
"home.hide_announcements": "ซ่อนประกาศ",
|
||||
"home.pending_critical_update.body": "โปรดอัปเดตเซิร์ฟเวอร์ Mastodon ของคุณโดยเร็วที่สุดเท่าที่จะทำได้!",
|
||||
"home.pending_critical_update.body": "โปรดอัปเดตเซิร์ฟเวอร์ Mastodon ของคุณโดยเร็วที่สุดเท่าที่จะเป็นไปได้!",
|
||||
"home.pending_critical_update.link": "ดูการอัปเดต",
|
||||
"home.pending_critical_update.title": "มีการอัปเดตความปลอดภัยสำคัญพร้อมใช้งาน!",
|
||||
"home.show_announcements": "แสดงประกาศ",
|
||||
|
|
|
@ -191,6 +191,18 @@ class UserMailer < Devise::Mailer
|
|||
end
|
||||
end
|
||||
|
||||
def failed_2fa(user, remote_ip, user_agent, timestamp)
|
||||
@resource = user
|
||||
@remote_ip = remote_ip
|
||||
@user_agent = user_agent
|
||||
@detection = Browser.new(user_agent)
|
||||
@timestamp = timestamp.to_time.utc
|
||||
|
||||
I18n.with_locale(locale) do
|
||||
mail subject: default_i18n_subject
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def default_devise_subject
|
||||
|
|
|
@ -130,6 +130,7 @@ class Account < ApplicationRecord
|
|||
scope :bots, -> { where(actor_type: %w(Application Service)) }
|
||||
scope :groups, -> { where(actor_type: 'Group') }
|
||||
scope :alphabetic, -> { order(domain: :asc, username: :asc) }
|
||||
scope :matches_uri_prefix, ->(value) { where(arel_table[:uri].matches("#{sanitize_sql_like(value)}/%", false, true)).or(where(uri: value)) }
|
||||
scope :matches_username, ->(value) { where('lower((username)::text) LIKE lower(?)', "#{value}%") }
|
||||
scope :matches_display_name, ->(value) { where(arel_table[:display_name].matches("#{value}%")) }
|
||||
scope :without_unapproved, -> { left_outer_joins(:user).merge(User.approved.confirmed).or(remote) }
|
||||
|
|
|
@ -29,7 +29,7 @@ class AccountSuggestions
|
|||
# a complicated query on this end.
|
||||
|
||||
account_ids = account_ids_with_sources[offset, limit]
|
||||
accounts_map = Account.where(id: account_ids.map(&:first)).includes(:account_stat).index_by(&:id)
|
||||
accounts_map = Account.where(id: account_ids.map(&:first)).includes(:account_stat, :user).index_by(&:id)
|
||||
|
||||
account_ids.filter_map do |(account_id, source)|
|
||||
next unless accounts_map.key?(account_id)
|
||||
|
|
|
@ -41,7 +41,7 @@ class Report < ApplicationRecord
|
|||
|
||||
scope :unresolved, -> { where(action_taken_at: nil) }
|
||||
scope :resolved, -> { where.not(action_taken_at: nil) }
|
||||
scope :with_accounts, -> { includes([:account, :target_account, :action_taken_by_account, :assigned_account].index_with({ user: [:invite_request, :invite] })) }
|
||||
scope :with_accounts, -> { includes([:account, :target_account, :action_taken_by_account, :assigned_account].index_with([:account_stat, { user: [:invite_request, :invite, :ips] }])) }
|
||||
|
||||
# A report is considered local if the reporter is local
|
||||
delegate :local?, to: :account
|
||||
|
|
|
@ -41,6 +41,8 @@ class Tag < ApplicationRecord
|
|||
HASHTAG_NAME_RE = /\A(#{HASHTAG_NAME_PAT})\z/i
|
||||
HASHTAG_INVALID_CHARS_RE = /[^[:alnum:]\u0E47-\u0E4E#{HASHTAG_SEPARATORS}]/
|
||||
|
||||
RECENT_STATUS_LIMIT = 1000
|
||||
|
||||
validates :name, presence: true, format: { with: HASHTAG_NAME_RE }
|
||||
validates :display_name, format: { with: HASHTAG_NAME_RE }
|
||||
validate :validate_name_change, if: -> { !new_record? && name_changed? }
|
||||
|
@ -55,7 +57,7 @@ class Tag < ApplicationRecord
|
|||
scope :not_trendable, -> { where(trendable: false) }
|
||||
scope :recently_used, lambda { |account|
|
||||
joins(:statuses)
|
||||
.where(statuses: { id: account.statuses.select(:id).limit(1000) })
|
||||
.where(statuses: { id: account.statuses.select(:id).limit(RECENT_STATUS_LIMIT) })
|
||||
.group(:id).order(Arel.sql('count(*) desc'))
|
||||
}
|
||||
scope :matches_name, ->(term) { where(arel_table[:name].lower.matches(arel_table.lower("#{sanitize_sql_like(Tag.normalize(term))}%"), nil, true)) } # Search with case-sensitive to use B-tree index
|
||||
|
|
|
@ -44,7 +44,7 @@ class ActivityPub::FetchRemoteStatusService < BaseService
|
|||
|
||||
# If we fetched a status that already exists, then we need to treat the
|
||||
# activity as an update rather than create
|
||||
activity_json['type'] = 'Update' if equals_or_includes_any?(activity_json['type'], %w(Create)) && Status.where(uri: object_uri, account_id: actor.id).exists?
|
||||
activity_json['type'] = 'Update' if equals_or_includes_any?(activity_json['type'], %w(Create)) && Status.exists?(uri: object_uri, account_id: actor.id)
|
||||
|
||||
with_redis do |redis|
|
||||
discoveries = redis.incr("status_discovery_per_request:#{@request_id}")
|
||||
|
|
|
@ -19,7 +19,7 @@ class VoteService < BaseService
|
|||
already_voted = true
|
||||
|
||||
with_redis_lock("vote:#{@poll.id}:#{@account.id}") do
|
||||
already_voted = @poll.votes.where(account: @account).exists?
|
||||
already_voted = @poll.votes.exists?(account: @account)
|
||||
|
||||
ApplicationRecord.transaction do
|
||||
@choices.each do |choice|
|
||||
|
|
|
@ -19,7 +19,7 @@ class ReactionValidator < ActiveModel::Validator
|
|||
end
|
||||
|
||||
def new_reaction?(reaction)
|
||||
!reaction.announcement.announcement_reactions.where(name: reaction.name).exists?
|
||||
!reaction.announcement.announcement_reactions.exists?(name: reaction.name)
|
||||
end
|
||||
|
||||
def limit_reached?(reaction)
|
||||
|
|
|
@ -35,7 +35,7 @@ class VoteValidator < ActiveModel::Validator
|
|||
if vote.persisted?
|
||||
account_votes_on_same_poll(vote).where(choice: vote.choice).where.not(poll_votes: { id: vote }).exists?
|
||||
else
|
||||
account_votes_on_same_poll(vote).where(choice: vote.choice).exists?
|
||||
account_votes_on_same_poll(vote).exists?(choice: vote.choice)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
24
app/views/user_mailer/failed_2fa.html.haml
Normal file
24
app/views/user_mailer/failed_2fa.html.haml
Normal file
|
@ -0,0 +1,24 @@
|
|||
= content_for :heading do
|
||||
= render 'application/mailer/heading', heading_title: t('user_mailer.failed_2fa.title'), heading_subtitle: t('user_mailer.failed_2fa.explanation'), heading_image_url: frontend_asset_url('images/mailer-new/heading/login.png')
|
||||
%table.email-w-full{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }
|
||||
%tr
|
||||
%td.email-body-padding-td
|
||||
%table.email-inner-card-table{ cellspacing: 0, cellpadding: 0, border: 0, role: 'presentation' }
|
||||
%tr
|
||||
%td.email-inner-card-td.email-prose
|
||||
%p= t 'user_mailer.failed_2fa.details'
|
||||
%p
|
||||
%strong #{t('sessions.ip')}:
|
||||
= @remote_ip
|
||||
%br/
|
||||
%strong #{t('sessions.browser')}:
|
||||
%span{ title: @user_agent }
|
||||
= t 'sessions.description',
|
||||
browser: t("sessions.browsers.#{@detection.id}", default: @detection.id.to_s),
|
||||
platform: t("sessions.platforms.#{@detection.platform.id}", default: @detection.platform.id.to_s)
|
||||
%br/
|
||||
%strong #{t('sessions.date')}:
|
||||
= l(@timestamp.in_time_zone(@resource.time_zone.presence), format: :with_time_zone)
|
||||
= render 'application/mailer/button', text: t('settings.account_settings'), url: edit_user_registration_url
|
||||
%p= t 'user_mailer.failed_2fa.further_actions_html',
|
||||
action: link_to(t('user_mailer.suspicious_sign_in.change_password'), edit_user_registration_url)
|
15
app/views/user_mailer/failed_2fa.text.erb
Normal file
15
app/views/user_mailer/failed_2fa.text.erb
Normal file
|
@ -0,0 +1,15 @@
|
|||
<%= t 'user_mailer.failed_2fa.title' %>
|
||||
|
||||
===
|
||||
|
||||
<%= t 'user_mailer.failed_2fa.explanation' %>
|
||||
|
||||
<%= t 'user_mailer.failed_2fa.details' %>
|
||||
|
||||
<%= t('sessions.ip') %>: <%= @remote_ip %>
|
||||
<%= t('sessions.browser') %>: <%= t('sessions.description', browser: t("sessions.browsers.#{@detection.id}", default: "#{@detection.id}"), platform: t("sessions.platforms.#{@detection.platform.id}", default: "#{@detection.platform.id}")) %>
|
||||
<%= l(@timestamp.in_time_zone(@resource.time_zone.presence), format: :with_time_zone) %>
|
||||
|
||||
<%= t 'user_mailer.failed_2fa.further_actions_html', action: t('user_mailer.suspicious_sign_in.change_password') %>
|
||||
|
||||
=> <%= edit_user_registration_url %>
|
Loading…
Add table
Add a link
Reference in a new issue