]> git.openstreetmap.org Git - rails.git/blobdiff - app/models/user.rb
Merge remote-tracking branch 'upstream/pull/4126'
[rails.git] / app / models / user.rb
index f804f4666b8c28a8e38f64a35837af2dcec65a29..7faf748cd8afdb5866af50915e8dcd033a8dd2c2 100644 (file)
 #
 # Indexes
 #
-#  users_auth_idx                (auth_provider,auth_uid) UNIQUE
-#  users_display_name_idx        (display_name) UNIQUE
-#  users_display_name_lower_idx  (lower((display_name)::text))
-#  users_email_idx               (email) UNIQUE
-#  users_email_lower_idx         (lower((email)::text))
-#  users_home_idx                (home_tile)
+#  users_auth_idx                    (auth_provider,auth_uid) UNIQUE
+#  users_display_name_canonical_idx  (lower(NORMALIZE(display_name, NFKC)))
+#  users_display_name_idx            (display_name) UNIQUE
+#  users_email_idx                   (email) UNIQUE
+#  users_email_lower_idx             (lower((email)::text))
+#  users_home_idx                    (home_tile)
 #
 
 class User < ApplicationRecord
@@ -51,9 +51,10 @@ class User < ApplicationRecord
   has_many :diary_comments, -> { order(:created_at => :desc) }, :inverse_of => :user
   has_many :diary_entry_subscriptions, :class_name => "DiaryEntrySubscription"
   has_many :diary_subscriptions, :through => :diary_entry_subscriptions, :source => :diary_entry
-  has_many :messages, -> { where(:to_user_visible => true).order(:sent_on => :desc).preload(:sender, :recipient) }, :foreign_key => :to_user_id
-  has_many :new_messages, -> { where(:to_user_visible => true, :message_read => false).order(:sent_on => :desc) }, :class_name => "Message", :foreign_key => :to_user_id
+  has_many :messages, -> { where(:to_user_visible => true, :muted => false).order(:sent_on => :desc).preload(:sender, :recipient) }, :foreign_key => :to_user_id
+  has_many :new_messages, -> { where(:to_user_visible => true, :muted => false, :message_read => false).order(:sent_on => :desc) }, :class_name => "Message", :foreign_key => :to_user_id
   has_many :sent_messages, -> { where(:from_user_visible => true).order(:sent_on => :desc).preload(:sender, :recipient) }, :class_name => "Message", :foreign_key => :from_user_id
+  has_many :muted_messages, -> { where(:to_user_visible => true, :muted => true).order(:sent_on => :desc).preload(:sender, :recipient) }, :class_name => "Message", :foreign_key => :to_user_id
   has_many :friendships, -> { joins(:befriendee).where(:users => { :status => %w[active confirmed] }) }
   has_many :friends, :through => :friendships, :source => :befriendee
   has_many :tokens, :class_name => "UserToken", :dependent => :destroy
@@ -75,6 +76,9 @@ class User < ApplicationRecord
   has_many :blocks_created, :class_name => "UserBlock", :foreign_key => :creator_id, :inverse_of => :creator
   has_many :blocks_revoked, :class_name => "UserBlock", :foreign_key => :revoker_id, :inverse_of => :revoker
 
+  has_many :mutes, -> { order(:created_at => :desc) }, :class_name => "UserMute", :foreign_key => :owner_id, :inverse_of => :owner
+  has_many :muted_users, :through => :mutes, :source => :subject
+
   has_many :roles, :class_name => "UserRole"
 
   has_many :issues, :class_name => "Issue", :foreign_key => :reported_user_id, :inverse_of => :reported_user
@@ -91,10 +95,11 @@ class User < ApplicationRecord
   validates :display_name, :presence => true, :length => 3..255,
                            :exclusion => %w[new terms save confirm confirm-email go_public reset-password forgot-password suspended]
   validates :display_name, :if => proc { |u| u.display_name_changed? },
-                           :uniqueness => { :case_sensitive => false }
+                           :normalized_uniqueness => { :case_sensitive => false }
   validates :display_name, :if => proc { |u| u.display_name_changed? },
                            :characters => { :url_safe => true },
                            :whitespace => { :leading => false, :trailing => false }
+  validate :display_name_cannot_be_user_id_with_other_id, :if => proc { |u| u.display_name_changed? }
   validates :email, :presence => true, :confirmation => true, :characters => true
   validates :email, :if => proc { |u| u.email_changed? },
                     :uniqueness => { :case_sensitive => false }
@@ -119,6 +124,12 @@ class User < ApplicationRecord
   before_save :update_tile
   after_save :spam_check
 
+  def display_name_cannot_be_user_id_with_other_id
+    display_name&.match(/^user_(\d+)$/i) do |m|
+      errors.add :display_name, I18n.t("activerecord.errors.messages.display_name_is_user_n") unless m[1].to_i == id
+    end
+  end
+
   def to_param
     display_name
   end
@@ -128,7 +139,7 @@ class User < ApplicationRecord
       user = find_by("email = ? OR display_name = ?", options[:username].strip, options[:username])
 
       if user.nil?
-        users = where("LOWER(email) = LOWER(?) OR LOWER(display_name) = LOWER(?)", options[:username].strip, options[:username])
+        users = where("LOWER(email) = LOWER(?) OR LOWER(NORMALIZE(display_name, NFKC)) = LOWER(NORMALIZE(?, NFKC))", options[:username].strip, options[:username])
 
         user = users.first if users.count == 1
       end
@@ -290,6 +301,12 @@ class User < ApplicationRecord
     role? "administrator"
   end
 
+  ##
+  # returns true if the user has the importer role, false otherwise
+  def importer?
+    role? "importer"
+  end
+
   ##
   # returns true if the user has the requested role
   def role?(role)
@@ -412,6 +429,18 @@ class User < ApplicationRecord
     end
   end
 
+  def deletion_allowed_at
+    unless Settings.user_account_deletion_delay.nil?
+      last_changeset = changesets.reorder(:closed_at => :desc).first
+      return last_changeset.closed_at.utc + Settings.user_account_deletion_delay.hours if last_changeset
+    end
+    creation_time.utc
+  end
+
+  def deletion_allowed?
+    deletion_allowed_at <= Time.now.utc
+  end
+
   private
 
   def encrypt_password