FEATURE: custom colors for default letter avatars (#7167)

FEATURE: custom colors for default letter avatars (#7167)

diff --git a/app/models/user.rb b/app/models/user.rb
index 3b1633b..a238306 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -743,8 +743,21 @@ class User < ActiveRecord::Base
 
   def self.letter_avatar_color(username)
     username ||= ""
-    color = LetterAvatar::COLORS[Digest::MD5.hexdigest(username)[0...15].to_i(16) % LetterAvatar::COLORS.length]
-    color.map { |c| c.to_s(16).rjust(2, '0') }.join
+    if SiteSetting.restrict_letter_avatar_colors.present?
+      hex_length = 6
+      colors = SiteSetting.restrict_letter_avatar_colors
+      length = colors.count("|") + 1
+      num = color_index(username, length)
+      index = (num * hex_length) + num
+      colors[index, hex_length]
+    else
+      color = LetterAvatar::COLORS[color_index(username, LetterAvatar::COLORS.length)]
+      color.map { |c| c.to_s(16).rjust(2, '0') }.join
+    end
+  end
+
+  def self.color_index(username, length)
+    Digest::MD5.hexdigest(username)[0...15].to_i(16) % length
   end
 
   def avatar_template
diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml
index 967a228..f050e03 100644
--- a/config/locales/server.en.yml
+++ b/config/locales/server.en.yml
@@ -1570,6 +1570,7 @@ en:
 
     external_system_avatars_enabled: "Use external system avatars service."
     external_system_avatars_url: "URL of the external system avatars service. Allowed substitutions are {username} {first_letter} {color} {size}"
+    restrict_letter_avatar_colors: "A list of 6-digit hexadecimal color values to be used for letter avatar background."
 
     selectable_avatars_enabled: "Force users to choose an avatar from the list."
     selectable_avatars: "List of avatars users can choose from."
@@ -2023,6 +2024,7 @@ en:
       min_username_length_range: "You cannot set the minimum above the maximum."
       max_username_length_exists: "You cannot set the maximum username length below the longest username (%{username})."
       max_username_length_range: "You cannot set the maximum below the minimum."
+      invalid_hex_value: "Color values have to be 6-digit hexadecimal codes."
 
     placeholder:
       sso_provider_secrets:
diff --git a/config/site_settings.yml b/config/site_settings.yml
index f4127f3..aa87006 100644
--- a/config/site_settings.yml
+++ b/config/site_settings.yml
@@ -1106,6 +1106,11 @@ files:
     client: true
     regex: '^((https?:)?\/)?\/.+[^\/]'
     shadowed_by_global: true
+  restrict_letter_avatar_colors:
+    default: ''
+    type: list
+    list_type: compact
+    validator: "ColorListValidator"
   selectable_avatars_enabled:
     default: false
     client: true
diff --git a/lib/validators/color_list_validator.rb b/lib/validators/color_list_validator.rb
new file mode 100644
index 0000000..dce170b
--- /dev/null
+++ b/lib/validators/color_list_validator.rb
@@ -0,0 +1,14 @@
+class ColorListValidator
+  def initialize(opts = {})
+    @opts = opts
+  end
+
+  def valid_value?(value)
+    hex_regex = /\A\h{6}\z/
+    value.split("|").all? { |c| c =~ hex_regex }
+  end
+
+  def error_message
+    I18n.t('site_settings.errors.invalid_hex_value')
+  end
+end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 3d1c1b8..a227bcf 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -1138,6 +1138,20 @@ describe User do
 
   end
 
+  describe "#letter_avatar_color" do
+    before do
+      SiteSetting.restrict_letter_avatar_colors = "2F70AC|ED207B|AAAAAA|77FF33"
+    end
+
+    it "returns custom color if restrict_letter_avatar_colors site setting is set" do
+      colors = SiteSetting.restrict_letter_avatar_colors.split("|")
+      expect(User.letter_avatar_color("username_one")).to eq("2F70AC")
+      expect(User.letter_avatar_color("username_two")).to eq("ED207B")
+      expect(User.letter_avatar_color("username_three")).to eq("AAAAAA")
+      expect(User.letter_avatar_color("username_four")).to eq("77FF33")
+    end
+  end
+
   describe ".small_avatar_url" do
 
     let(:user) { build(:user, username: 'Sam') }

GitHub sha: 7e9afdac

This commit has been mentioned on Discourse Meta. There might be relevant details there: