2018-05-27 21:46:00 -04:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2014-04-16 09:49:06 -04:00
|
|
|
class ColorScheme < ActiveRecord::Base
|
|
|
|
|
2019-12-09 19:48:27 -05:00
|
|
|
# rubocop:disable Layout/HashAlignment
|
2018-12-04 04:48:16 -05:00
|
|
|
|
2017-04-12 10:52:52 -04:00
|
|
|
CUSTOM_SCHEMES = {
|
2018-07-24 09:00:20 -04:00
|
|
|
'Dark': {
|
2017-04-12 10:52:52 -04:00
|
|
|
"primary" => 'dddddd',
|
|
|
|
"secondary" => '222222',
|
|
|
|
"tertiary" => '0f82af',
|
|
|
|
"quaternary" => 'c14924',
|
|
|
|
"header_background" => '111111',
|
2017-10-03 13:01:05 -04:00
|
|
|
"header_primary" => 'dddddd',
|
2017-04-12 10:52:52 -04:00
|
|
|
"highlight" => 'a87137',
|
|
|
|
"danger" => 'e45735',
|
|
|
|
"success" => '1ca551',
|
|
|
|
"love" => 'fa6c8d'
|
2018-07-24 09:00:20 -04:00
|
|
|
},
|
|
|
|
# By @itsbhanusharma
|
|
|
|
'Neutral': {
|
|
|
|
"primary" => '000000',
|
|
|
|
"secondary" => 'ffffff',
|
|
|
|
"tertiary" => '51839b',
|
|
|
|
"quaternary" => 'b85e48',
|
|
|
|
"header_background" => '333333',
|
|
|
|
"header_primary" => 'f3f3f3',
|
|
|
|
"highlight" => 'ecec70',
|
|
|
|
"danger" => 'b85e48',
|
|
|
|
"success" => '518751',
|
|
|
|
"love" => 'fa6c8d'
|
|
|
|
},
|
|
|
|
# By @Flower_Child
|
|
|
|
'Grey Amber': {
|
|
|
|
"primary" => 'd9d9d9',
|
|
|
|
"secondary" => '3d4147',
|
|
|
|
"tertiary" => 'fdd459',
|
|
|
|
"quaternary" => 'fdd459',
|
|
|
|
"header_background" => '36393e',
|
|
|
|
"header_primary" => 'd9d9d9',
|
|
|
|
"highlight" => 'fdd459',
|
|
|
|
"danger" => 'e45735',
|
|
|
|
"success" => 'fdd459',
|
|
|
|
"love" => 'fdd459'
|
|
|
|
},
|
2018-08-08 16:01:21 -04:00
|
|
|
# By @rafafotes
|
2018-07-24 09:00:20 -04:00
|
|
|
'Shades of Blue': {
|
|
|
|
"primary" => '203243',
|
|
|
|
"secondary" => 'eef4f7',
|
|
|
|
"tertiary" => '416376',
|
|
|
|
"quaternary" => '5e99b9',
|
|
|
|
"header_background" => '86bddb',
|
|
|
|
"header_primary" => 'ffffff',
|
|
|
|
"highlight" => '86bddb',
|
|
|
|
"danger" => 'bf3c3c',
|
|
|
|
"success" => '70db82',
|
|
|
|
"love" => 'fc94cb'
|
|
|
|
},
|
|
|
|
# By @mikechristopher
|
|
|
|
'Latte': {
|
2018-07-25 15:12:01 -04:00
|
|
|
"primary" => 'f2e5d7',
|
2018-07-24 09:00:20 -04:00
|
|
|
"secondary" => '262322',
|
|
|
|
"tertiary" => 'f7f2ed',
|
|
|
|
"quaternary" => 'd7c9aa',
|
|
|
|
"header_background" => 'd7c9aa',
|
|
|
|
"header_primary" => '262322',
|
|
|
|
"highlight" => 'd7c9aa',
|
|
|
|
"danger" => 'db9584',
|
|
|
|
"success" => '78be78',
|
|
|
|
"love" => '8f6201'
|
|
|
|
},
|
|
|
|
# By @Flower_Child
|
|
|
|
'Summer': {
|
|
|
|
"primary" => '874342',
|
|
|
|
"secondary" => 'fffff4',
|
|
|
|
"tertiary" => 'fe9896',
|
|
|
|
"quaternary" => 'fcc9d0',
|
|
|
|
"header_background" => '96ccbf',
|
|
|
|
"header_primary" => 'fff1e7',
|
|
|
|
"highlight" => 'f3c07f',
|
|
|
|
"danger" => 'cfebdc',
|
|
|
|
"success" => 'fcb4b5',
|
|
|
|
"love" => 'f3c07f'
|
|
|
|
},
|
|
|
|
# By @Flower_Child
|
|
|
|
'Dark Rose': {
|
|
|
|
"primary" => 'ca9cb2',
|
|
|
|
"secondary" => '3a2a37',
|
|
|
|
"tertiary" => 'fdd459',
|
|
|
|
"quaternary" => '7e566a',
|
|
|
|
"header_background" => 'a97189',
|
|
|
|
"header_primary" => 'd9b2bb',
|
|
|
|
"highlight" => '6c3e63',
|
|
|
|
"danger" => '6c3e63',
|
|
|
|
"success" => 'd9b2bb',
|
|
|
|
"love" => 'd9b2bb'
|
2017-04-12 10:52:52 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-09 19:48:27 -05:00
|
|
|
# rubocop:enable Layout/HashAlignment
|
2018-12-04 04:48:16 -05:00
|
|
|
|
2019-05-09 05:22:28 -04:00
|
|
|
LIGHT_THEME_ID = 'Light'
|
|
|
|
|
2017-04-12 10:52:52 -04:00
|
|
|
def self.base_color_scheme_colors
|
2016-09-02 11:42:14 -04:00
|
|
|
base_with_hash = {}
|
2018-12-04 04:48:16 -05:00
|
|
|
|
2016-09-02 11:42:14 -04:00
|
|
|
base_colors.each do |name, color|
|
2017-04-12 10:52:52 -04:00
|
|
|
base_with_hash[name] = "#{color}"
|
2016-09-02 11:42:14 -04:00
|
|
|
end
|
|
|
|
|
2017-04-12 10:52:52 -04:00
|
|
|
list = [
|
2019-05-09 05:22:28 -04:00
|
|
|
{ id: LIGHT_THEME_ID, colors: base_with_hash }
|
2016-09-02 11:42:14 -04:00
|
|
|
]
|
2017-04-12 10:52:52 -04:00
|
|
|
|
|
|
|
CUSTOM_SCHEMES.each do |k, v|
|
|
|
|
list.push(id: k.to_s, colors: v)
|
|
|
|
end
|
2018-12-04 04:48:16 -05:00
|
|
|
|
2017-04-12 10:52:52 -04:00
|
|
|
list
|
2016-09-02 11:42:14 -04:00
|
|
|
end
|
|
|
|
|
2015-09-23 02:47:17 -04:00
|
|
|
def self.hex_cache
|
|
|
|
@hex_cache ||= DistributedCache.new("scheme_hex_for_name")
|
|
|
|
end
|
|
|
|
|
2014-05-02 17:46:03 -04:00
|
|
|
attr_accessor :is_base
|
|
|
|
|
2014-04-16 09:49:06 -04:00
|
|
|
has_many :color_scheme_colors, -> { order('id ASC') }, dependent: :destroy
|
|
|
|
|
|
|
|
alias_method :colors, :color_scheme_colors
|
|
|
|
|
2018-03-15 03:26:54 -04:00
|
|
|
before_save :bump_version
|
2014-11-10 23:32:44 -05:00
|
|
|
after_save :publish_discourse_stylesheet
|
2020-08-28 10:36:52 -04:00
|
|
|
after_save :dump_caches
|
|
|
|
after_destroy :dump_caches
|
2017-04-17 15:56:13 -04:00
|
|
|
belongs_to :theme
|
2014-04-16 09:49:06 -04:00
|
|
|
|
2014-05-02 17:46:03 -04:00
|
|
|
validates_associated :color_scheme_colors
|
|
|
|
|
|
|
|
BASE_COLORS_FILE = "#{Rails.root}/app/assets/stylesheets/common/foundation/colors.scss"
|
2019-08-12 06:02:38 -04:00
|
|
|
COLOR_TRANSFORMATION_FILE = "#{Rails.root}/app/assets/stylesheets/common/foundation/color_transformations.scss"
|
2014-05-02 17:46:03 -04:00
|
|
|
|
|
|
|
@mutex = Mutex.new
|
|
|
|
|
|
|
|
def self.base_colors
|
2019-02-26 09:22:02 -05:00
|
|
|
return @base_colors if @base_colors
|
2014-05-02 17:46:03 -04:00
|
|
|
@mutex.synchronize do
|
|
|
|
return @base_colors if @base_colors
|
2019-02-26 09:22:02 -05:00
|
|
|
base_colors = {}
|
2016-09-02 11:42:14 -04:00
|
|
|
File.readlines(BASE_COLORS_FILE).each do |line|
|
2014-05-02 17:46:03 -04:00
|
|
|
matches = /\$([\w]+):\s*#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})(?:[;]|\s)/.match(line.strip)
|
2019-02-26 09:22:02 -05:00
|
|
|
base_colors[matches[1]] = matches[2] if matches
|
2014-05-02 17:46:03 -04:00
|
|
|
end
|
2019-02-26 09:22:02 -05:00
|
|
|
@base_colors = base_colors
|
2014-05-02 17:46:03 -04:00
|
|
|
end
|
|
|
|
@base_colors
|
|
|
|
end
|
|
|
|
|
2019-08-12 06:02:38 -04:00
|
|
|
def self.color_transformation_variables
|
|
|
|
return @transformation_variables if @transformation_variables
|
|
|
|
@mutex.synchronize do
|
|
|
|
return @transformation_variables if @transformation_variables
|
|
|
|
transformation_variables = []
|
|
|
|
File.readlines(COLOR_TRANSFORMATION_FILE).each do |line|
|
|
|
|
matches = /\$([\w\-_]+):.*/.match(line.strip)
|
|
|
|
transformation_variables.append(matches[1]) if matches
|
|
|
|
end
|
|
|
|
@transformation_variables = transformation_variables
|
|
|
|
end
|
|
|
|
@transformation_variables
|
|
|
|
end
|
|
|
|
|
2017-04-12 10:52:52 -04:00
|
|
|
def self.base_color_schemes
|
|
|
|
base_color_scheme_colors.map do |hash|
|
2018-07-24 09:00:20 -04:00
|
|
|
scheme = new(name: I18n.t("color_schemes.#{hash[:id].downcase.gsub(' ', '_')}"), base_scheme_id: hash[:id])
|
2017-04-12 10:52:52 -04:00
|
|
|
scheme.colors = hash[:colors].map { |k, v| { name: k.to_s, hex: v.sub("#", "") } }
|
|
|
|
scheme.is_base = true
|
|
|
|
scheme
|
|
|
|
end
|
2014-04-16 09:49:06 -04:00
|
|
|
end
|
|
|
|
|
2014-05-02 17:46:03 -04:00
|
|
|
def self.base
|
2014-06-03 12:36:34 -04:00
|
|
|
return @base_color_scheme if @base_color_scheme
|
2017-04-12 10:52:52 -04:00
|
|
|
@base_color_scheme = new(name: I18n.t('color_schemes.base_theme_name'))
|
2014-06-03 12:36:34 -04:00
|
|
|
@base_color_scheme.colors = base_colors.map { |name, hex| { name: name, hex: hex } }
|
|
|
|
@base_color_scheme.is_base = true
|
|
|
|
@base_color_scheme
|
|
|
|
end
|
|
|
|
|
2019-10-25 08:29:51 -04:00
|
|
|
def self.is_base?(scheme_name)
|
|
|
|
base_color_scheme_colors.map { |c| c[:id] }.include?(scheme_name)
|
|
|
|
end
|
|
|
|
|
2014-06-03 12:36:34 -04:00
|
|
|
# create_from_base will create a new ColorScheme that overrides Discourse's base color scheme with the given colors.
|
|
|
|
def self.create_from_base(params)
|
|
|
|
new_color_scheme = new(name: params[:name])
|
2017-05-03 11:31:16 -04:00
|
|
|
new_color_scheme.via_wizard = true if params[:via_wizard]
|
|
|
|
new_color_scheme.base_scheme_id = params[:base_scheme_id]
|
|
|
|
|
|
|
|
colors = CUSTOM_SCHEMES[params[:base_scheme_id].to_sym]&.map do |name, hex|
|
|
|
|
{ name: name, hex: hex }
|
|
|
|
end if params[:base_scheme_id]
|
|
|
|
colors ||= base.colors_hashes
|
2014-06-03 12:36:34 -04:00
|
|
|
|
|
|
|
# Override base values
|
|
|
|
params[:colors].each do |name, hex|
|
|
|
|
c = colors.find { |x| x[:name].to_s == name.to_s }
|
|
|
|
c[:hex] = hex
|
2017-05-03 11:31:16 -04:00
|
|
|
end if params[:colors]
|
2014-06-03 12:36:34 -04:00
|
|
|
|
|
|
|
new_color_scheme.colors = colors
|
|
|
|
new_color_scheme.save
|
|
|
|
new_color_scheme
|
2014-04-16 09:49:06 -04:00
|
|
|
end
|
|
|
|
|
2018-09-11 21:04:58 -04:00
|
|
|
def self.lookup_hex_for_name(name, scheme_id = nil)
|
|
|
|
enabled_color_scheme = find_by(id: scheme_id) if scheme_id
|
|
|
|
enabled_color_scheme ||= Theme.where(id: SiteSetting.default_theme_id).first&.color_scheme
|
2018-05-27 21:46:00 -04:00
|
|
|
(enabled_color_scheme || base).colors.find { |c| c.name == name }.try(:hex) || "nil"
|
2017-01-12 15:43:09 -05:00
|
|
|
end
|
|
|
|
|
2018-09-11 21:04:58 -04:00
|
|
|
def self.hex_for_name(name, scheme_id = nil)
|
|
|
|
cache_key = scheme_id ? name + "_#{scheme_id}" : name
|
|
|
|
hex_cache[cache_key] ||= lookup_hex_for_name(name, scheme_id)
|
|
|
|
hex_cache[cache_key] == "nil" ? nil : hex_cache[cache_key]
|
2014-07-17 12:31:37 -04:00
|
|
|
end
|
2014-05-02 17:46:03 -04:00
|
|
|
|
2014-04-16 09:49:06 -04:00
|
|
|
def colors=(arr)
|
|
|
|
@colors_by_name = nil
|
|
|
|
arr.each do |c|
|
2014-05-02 17:46:03 -04:00
|
|
|
self.color_scheme_colors << ColorSchemeColor.new(name: c[:name], hex: c[:hex])
|
2014-04-16 09:49:06 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def colors_by_name
|
|
|
|
@colors_by_name ||= self.colors.inject({}) { |sum, c| sum[c.name] = c; sum; }
|
|
|
|
end
|
2018-12-04 04:48:16 -05:00
|
|
|
|
2014-04-16 09:49:06 -04:00
|
|
|
def clear_colors_cache
|
|
|
|
@colors_by_name = nil
|
|
|
|
end
|
|
|
|
|
|
|
|
def colors_hashes
|
|
|
|
color_scheme_colors.map do |c|
|
2014-05-02 17:46:03 -04:00
|
|
|
{ name: c.name, hex: c.hex }
|
2014-04-16 09:49:06 -04:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2017-04-12 10:52:52 -04:00
|
|
|
def base_colors
|
|
|
|
colors = nil
|
2018-07-24 09:00:20 -04:00
|
|
|
if base_scheme_id && base_scheme_id != "Light"
|
2017-04-12 10:52:52 -04:00
|
|
|
colors = CUSTOM_SCHEMES[base_scheme_id.to_sym]
|
|
|
|
end
|
|
|
|
colors || ColorScheme.base_colors
|
2014-04-16 09:49:06 -04:00
|
|
|
end
|
|
|
|
|
2017-04-12 10:52:52 -04:00
|
|
|
def resolved_colors
|
|
|
|
resolved = ColorScheme.base_colors.dup
|
2018-07-24 09:00:20 -04:00
|
|
|
if base_scheme_id && base_scheme_id != "Light"
|
2017-04-12 10:52:52 -04:00
|
|
|
if scheme = CUSTOM_SCHEMES[base_scheme_id.to_sym]
|
|
|
|
scheme.each do |name, value|
|
|
|
|
resolved[name] = value
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
colors.each do |c|
|
|
|
|
resolved[c.name] = c.hex
|
|
|
|
end
|
|
|
|
resolved
|
2014-04-16 09:49:06 -04:00
|
|
|
end
|
|
|
|
|
2014-11-10 23:32:44 -05:00
|
|
|
def publish_discourse_stylesheet
|
2017-04-12 10:52:52 -04:00
|
|
|
if self.id
|
2020-08-18 13:02:13 -04:00
|
|
|
Stylesheet::Manager.clear_color_scheme_cache!
|
2020-08-11 16:28:59 -04:00
|
|
|
|
2018-08-08 00:46:34 -04:00
|
|
|
theme_ids = Theme.where(color_scheme_id: self.id).pluck(:id)
|
|
|
|
if theme_ids.present?
|
2017-04-12 10:52:52 -04:00
|
|
|
Stylesheet::Manager.cache.clear
|
2018-08-08 00:46:34 -04:00
|
|
|
Theme.notify_theme_change(
|
|
|
|
theme_ids,
|
|
|
|
with_scheme: true,
|
|
|
|
clear_manager_cache: false,
|
|
|
|
all_themes: true
|
|
|
|
)
|
2017-04-12 10:52:52 -04:00
|
|
|
end
|
|
|
|
end
|
2014-11-10 23:32:44 -05:00
|
|
|
end
|
|
|
|
|
2020-08-28 10:36:52 -04:00
|
|
|
def dump_caches
|
2015-09-23 02:47:17 -04:00
|
|
|
self.class.hex_cache.clear
|
2020-08-28 10:36:52 -04:00
|
|
|
ApplicationSerializer.expire_cache_fragment!("user_color_schemes")
|
2015-09-23 02:47:17 -04:00
|
|
|
end
|
|
|
|
|
2018-03-15 03:26:54 -04:00
|
|
|
def bump_version
|
|
|
|
if self.id
|
|
|
|
self.version += 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-08-20 14:23:18 -04:00
|
|
|
def is_dark?
|
2020-08-28 10:36:52 -04:00
|
|
|
return if colors.empty?
|
|
|
|
|
2020-08-20 14:23:18 -04:00
|
|
|
primary_b = brightness(colors_by_name["primary"].hex)
|
|
|
|
secondary_b = brightness(colors_by_name["secondary"].hex)
|
|
|
|
|
|
|
|
primary_b > secondary_b
|
|
|
|
end
|
|
|
|
|
|
|
|
# Equivalent to dc-color-brightness() in variables.scss
|
|
|
|
def brightness(color)
|
|
|
|
rgb = color.scan(/../).map { |c| c.to_i(16) }
|
|
|
|
(rgb[0].to_i * 299 + rgb[1].to_i * 587 + rgb[2].to_i * 114) / 1000.0
|
|
|
|
end
|
2014-04-16 09:49:06 -04:00
|
|
|
end
|
2014-05-08 02:45:49 -04:00
|
|
|
|
|
|
|
# == Schema Information
|
|
|
|
#
|
|
|
|
# Table name: color_schemes
|
|
|
|
#
|
2020-09-01 00:52:32 -04:00
|
|
|
# id :integer not null, primary key
|
|
|
|
# name :string not null
|
|
|
|
# version :integer default(1), not null
|
|
|
|
# created_at :datetime not null
|
|
|
|
# updated_at :datetime not null
|
|
|
|
# via_wizard :boolean default(FALSE), not null
|
|
|
|
# base_scheme_id :string
|
|
|
|
# theme_id :integer
|
2014-05-08 02:45:49 -04:00
|
|
|
#
|