Merge tag 'v4.4.0-rc.1' into chinwag-next

This commit is contained in:
Mike Barnes 2025-09-14 11:47:14 +10:00
commit fbbcaf4efd
2660 changed files with 83548 additions and 52192 deletions

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
class DateOfBirthValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add(attribute, :below_limit) if value.present? && value.to_date > min_age.ago
rescue Date::Error
record.errors.add(attribute, :invalid)
end
private
def min_age
Setting.min_age.to_i.years
end
end

View file

@ -4,23 +4,26 @@ class DomainValidator < ActiveModel::EachValidator
MAX_DOMAIN_LENGTH = 256
MIN_LABEL_LENGTH = 1
MAX_LABEL_LENGTH = 63
ALLOWED_CHARACTERS_RE = /^[a-z0-9\-]+$/i
ALLOWED_CHARACTERS_RE = /^[a-z0-9-]+$/i
def validate_each(record, attribute, value)
return if value.blank?
(options[:multiline] ? value.split : [value]).each do |domain|
_, domain = domain.split('@') if options[:acct]
Array.wrap(value).each do |domain|
if options[:acct]
_, domain = domain.split('@')
next if domain.blank?
end
next if domain.blank?
record.errors.add(attribute, options[:multiline] ? :invalid_domain_on_line : :invalid, value: domain) unless compliant?(domain)
record.errors.add(attribute, value.is_a?(Enumerable) ? :invalid_domain_on_line : :invalid, value: domain) unless compliant?(domain)
end
end
private
def compliant?(value)
return false if value.blank?
uri = Addressable::URI.new
uri.host = value
uri.normalized_host.size < MAX_DOMAIN_LENGTH && uri.normalized_host.split('.').all? { |label| label.size.between?(MIN_LABEL_LENGTH, MAX_LABEL_LENGTH) && label =~ ALLOWED_CHARACTERS_RE }

View file

@ -0,0 +1,15 @@
# frozen_string_literal: true
class EmptyProfileFieldNamesValidator < ActiveModel::Validator
def validate(account)
return if account.fields.empty?
account.errors.add(:fields, :fields_with_values_missing_labels) if fields_with_values_missing_names?(account)
end
private
def fields_with_values_missing_names?(account)
account.fields.any? { |field| field.name.blank? && field.value.present? }
end
end

View file

@ -1,9 +0,0 @@
# frozen_string_literal: true
class LinesValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
return if value.blank?
record.errors.add(attribute, :too_many_lines, limit: options[:maximum]) if options[:maximum].present? && value.split.size > options[:maximum]
end
end

View file

@ -8,7 +8,7 @@ class NoteLengthValidator < ActiveModel::EachValidator
private
def too_long?(value)
countable_text(value).mb_chars.grapheme_length > options[:maximum]
countable_text(value).each_grapheme_cluster.size > options[:maximum]
end
def countable_text(value)

View file

@ -5,9 +5,12 @@ class PollExpirationValidator < ActiveModel::Validator
MIN_EXPIRATION = 5.minutes.freeze
def validate(poll)
# We have a `presence: true` check for this attribute already
return if poll.expires_at.nil?
current_time = Time.now.utc
poll.errors.add(:expires_at, I18n.t('polls.errors.duration_too_long')) if poll.expires_at.nil? || poll.expires_at - current_time > MAX_EXPIRATION
poll.errors.add(:expires_at, I18n.t('polls.errors.duration_too_short')) if poll.expires_at.present? && (poll.expires_at - current_time).ceil < MIN_EXPIRATION
poll.errors.add(:expires_at, I18n.t('polls.errors.duration_too_long')) if poll.expires_at - current_time > MAX_EXPIRATION
poll.errors.add(:expires_at, I18n.t('polls.errors.duration_too_short')) if (poll.expires_at - current_time).ceil < MIN_EXPIRATION
end
end

View file

@ -7,7 +7,7 @@ class PollOptionsValidator < ActiveModel::Validator
def validate(poll)
poll.errors.add(:options, I18n.t('polls.errors.too_few_options')) unless poll.options.size > 1
poll.errors.add(:options, I18n.t('polls.errors.too_many_options', max: MAX_OPTIONS)) if poll.options.size > MAX_OPTIONS
poll.errors.add(:options, I18n.t('polls.errors.over_character_limit', max: MAX_OPTION_CHARS)) if poll.options.any? { |option| option.mb_chars.grapheme_length > MAX_OPTION_CHARS }
poll.errors.add(:options, I18n.t('polls.errors.over_character_limit', max: MAX_OPTION_CHARS)) if poll.options.any? { |option| option.each_grapheme_cluster.size > MAX_OPTION_CHARS }
poll.errors.add(:options, I18n.t('polls.errors.duplicate_options')) unless poll.options.uniq.size == poll.options.size
end
end

View file

@ -18,7 +18,7 @@ class StatusLengthValidator < ActiveModel::Validator
end
def countable_length(str)
str.mb_chars.grapheme_length
str.each_grapheme_cluster.size
end
def combined_text(status)