mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 02:19:27 +08:00
FEATURE: allow themes to share color schemes
This commit is contained in:
parent
1872a1714f
commit
5e3a0846f7
|
@ -3,6 +3,13 @@ import { url } from 'discourse/lib/computed';
|
|||
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
@computed("model", "allThemes")
|
||||
parentThemes(model, allThemes) {
|
||||
let parents = allThemes.filter(theme =>
|
||||
_.contains(theme.get("childThemes"), model));
|
||||
return parents.length === 0 ? null : parents;
|
||||
},
|
||||
|
||||
@computed("model.theme_fields.@each")
|
||||
hasEditedFields(fields) {
|
||||
return fields.any(f=>!Em.isBlank(f.value));
|
||||
|
@ -48,8 +55,6 @@ export default Ember.Controller.extend({
|
|||
return themes.length === 0 ? null : themes;
|
||||
},
|
||||
|
||||
showSchemes: Em.computed.or("model.default", "model.user_selectable"),
|
||||
|
||||
@computed("allThemes", "allThemes.length", "model")
|
||||
availableChildThemes(allThemes, count) {
|
||||
if (count === 1) {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
@computed('model', 'model.@each')
|
||||
sortedThemes(themes) {
|
||||
return _.sortBy(themes.content, t => {
|
||||
return [!t.get("default"), !t.get("user_selectable"), t.get("name")];
|
||||
});
|
||||
}
|
||||
});
|
|
@ -34,6 +34,7 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, {
|
|||
}.property('name', 'colors.@each.changed', 'saving'),
|
||||
|
||||
disableSave: function() {
|
||||
if (this.get('theme_id')) { return false; }
|
||||
return !this.get('changed') || this.get('saving') || _.any(this.get('colors'), function(c) { return !c.get('valid'); });
|
||||
}.property('changed'),
|
||||
|
||||
|
@ -100,6 +101,8 @@ ColorScheme.reopenClass({
|
|||
id: colorScheme.id,
|
||||
name: colorScheme.name,
|
||||
is_base: colorScheme.is_base,
|
||||
theme_id: colorScheme.theme_id,
|
||||
theme_name: colorScheme.theme_name,
|
||||
base_scheme_id: colorScheme.base_scheme_id,
|
||||
colors: colorScheme.colors.map(function(c) { return ColorSchemeColor.create({name: c.name, hex: c.hex, default_hex: c.default_hex}); })
|
||||
}));
|
||||
|
|
|
@ -1,11 +1,17 @@
|
|||
<div class="color-scheme show-current-style">
|
||||
<div class="admin-container">
|
||||
<h1>{{text-field class="style-name" value=model.name}}</h1>
|
||||
|
||||
<h1>{{#if model.theme_id}}{{model.name}}{{else}}{{text-field class="style-name" value=model.name}}{{/if}}</h1>
|
||||
<div class="controls">
|
||||
{{#unless model.theme_id}}
|
||||
<button {{action "save"}} disabled={{model.disableSave}} class='btn'>{{i18n 'admin.customize.save'}}</button>
|
||||
{{/unless}}
|
||||
<button {{action "copy" model}} class='btn'><i class="fa fa-copy"></i> {{i18n 'admin.customize.copy'}}</button>
|
||||
{{#if model.theme_id}}
|
||||
{{i18n "admin.customize.theme_owner"}}
|
||||
{{#link-to "adminCustomizeThemes.show" model.theme_id}}{{model.theme_name}}{{/link-to}}
|
||||
{{else}}
|
||||
<button {{action "destroy"}} class='btn btn-danger'><i class="fa fa-trash-o"></i> {{i18n 'admin.customize.delete'}}</button>
|
||||
{{/if}}
|
||||
<span class="saving {{unless model.savingStatus 'hidden'}}">{{model.savingStatus}}</span>
|
||||
</div>
|
||||
|
||||
|
@ -39,8 +45,10 @@
|
|||
</td>
|
||||
<td class="hex">{{color-input hexValue=c.hex brightnessValue=c.brightness valid=c.valid}}</td>
|
||||
<td class="actions">
|
||||
{{#unless model.theme_id}}
|
||||
<button class="btn revert {{unless c.savedIsOverriden 'invisible'}}" {{action "revert" c}} title="{{i18n 'admin.customize.colors.revert_title'}}">{{i18n 'revert'}}</button>
|
||||
<button class="btn undo {{unless c.changed 'invisible'}}" {{action "undo" c}} title="{{i18n 'admin.customize.colors.undo_title'}}">{{i18n 'undo'}}</button>
|
||||
{{/unless}}
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
|
|
|
@ -21,12 +21,19 @@
|
|||
{{/if}}
|
||||
|
||||
|
||||
{{#if parentThemes}}
|
||||
<h3>{{i18n "admin.customize.theme.component_of"}}</h3>
|
||||
<ul>
|
||||
{{#each parentThemes as |theme|}}
|
||||
<li>{{#link-to 'adminCustomizeThemes.show' theme replace=true}}{{theme.name}}{{/link-to}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<p>
|
||||
{{inline-edit-checkbox action="applyDefault" labelKey="admin.customize.theme.is_default" checked=model.default}}
|
||||
{{inline-edit-checkbox action="applyUserSelectable" labelKey="admin.customize.theme.user_selectable" checked=model.user_selectable}}
|
||||
</p>
|
||||
|
||||
{{#if showSchemes}}
|
||||
<h3>{{i18n "admin.customize.theme.color_scheme"}}</h3>
|
||||
<p>{{i18n "admin.customize.theme.color_scheme_select"}}</p>
|
||||
<p>{{combo-box content=colorSchemes
|
||||
|
@ -80,7 +87,7 @@
|
|||
</p>
|
||||
|
||||
{{#if availableChildThemes}}
|
||||
<h3>{{i18n "admin.customize.theme.included_themes"}}</h3>
|
||||
<h3>{{i18n "admin.customize.theme.theme_components"}}</h3>
|
||||
{{#unless model.childThemes.length}}
|
||||
<p>
|
||||
<label class='checkbox-label'>
|
||||
|
@ -91,7 +98,7 @@
|
|||
{{else}}
|
||||
<ul>
|
||||
{{#each model.childThemes as |child|}}
|
||||
<li>{{child.name}} {{d-button action="removeChildTheme" actionParam=child class="btn-small cancel-edit" icon="times"}}</li>
|
||||
<li>{{#link-to 'adminCustomizeThemes.show' child replace=true}}{{child.name}}{{/link-to}} {{d-button action="removeChildTheme" actionParam=child class="btn-small cancel-edit" icon="times"}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/unless}}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
<div class='content-list span6'>
|
||||
<h3>{{i18n 'admin.customize.theme.long_title'}}</h3>
|
||||
<ul>
|
||||
{{#each model as |theme|}}
|
||||
{{#each sortedThemes as |theme|}}
|
||||
<li>
|
||||
{{#link-to 'adminCustomizeThemes.show' theme replace=true}}
|
||||
{{theme.name}}
|
||||
|
|
|
@ -1,5 +1,11 @@
|
|||
// Customise area
|
||||
.customize {
|
||||
h1 {
|
||||
margin-bottom: 10px;
|
||||
input {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
.admin-container {
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
|
|
|
@ -52,6 +52,7 @@ class ColorScheme < ActiveRecord::Base
|
|||
after_save :publish_discourse_stylesheet
|
||||
after_save :dump_hex_cache
|
||||
after_destroy :dump_hex_cache
|
||||
belongs_to :theme
|
||||
|
||||
validates_associated :color_scheme_colors
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ class RemoteTheme < ActiveRecord::Base
|
|||
def update_from_remote(importer=nil)
|
||||
return unless remote_url
|
||||
cleanup = false
|
||||
|
||||
unless importer
|
||||
cleanup = true
|
||||
importer = GitImporter.new(remote_url)
|
||||
|
@ -61,12 +62,13 @@ class RemoteTheme < ActiveRecord::Base
|
|||
theme_info = JSON.parse(importer["about.json"])
|
||||
self.license_url ||= theme_info["license_url"]
|
||||
self.about_url ||= theme_info["about_url"]
|
||||
|
||||
self.remote_updated_at = Time.zone.now
|
||||
self.remote_version = importer.version
|
||||
self.local_version = importer.version
|
||||
self.commits_behind = 0
|
||||
|
||||
update_theme_color_schemes(theme, theme_info["color_schemes"])
|
||||
|
||||
self
|
||||
ensure
|
||||
begin
|
||||
|
@ -75,6 +77,39 @@ class RemoteTheme < ActiveRecord::Base
|
|||
Rails.logger.warn("Failed cleanup remote git #{e}")
|
||||
end
|
||||
end
|
||||
|
||||
def normalize_override(hex)
|
||||
return unless hex
|
||||
|
||||
override = hex.downcase
|
||||
if override !~ /[0-9a-f]{6}/
|
||||
override = nil
|
||||
end
|
||||
override
|
||||
end
|
||||
|
||||
def update_theme_color_schemes(theme, schemes)
|
||||
return if schemes.blank?
|
||||
|
||||
schemes.each do |name, colors|
|
||||
existing = theme.color_schemes.find_by(name: name)
|
||||
if existing
|
||||
existing.colors.each do |c|
|
||||
override = normalize_override(colors[c.name])
|
||||
if override && c.hex != override
|
||||
c.hex = override
|
||||
theme.notify_color_change(c)
|
||||
end
|
||||
end
|
||||
else
|
||||
scheme = theme.color_schemes.build(name: name)
|
||||
ColorScheme.base.colors_hashes.each do |color|
|
||||
override = normalize_override(colors[color[:name]])
|
||||
scheme.color_scheme_colors << ColorSchemeColor.new(name: color[:name], hex: override || color[:hex])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# == Schema Information
|
||||
|
|
|
@ -12,6 +12,7 @@ class Theme < ActiveRecord::Base
|
|||
has_many :theme_fields, dependent: :destroy
|
||||
has_many :child_theme_relation, class_name: 'ChildTheme', foreign_key: 'parent_theme_id', dependent: :destroy
|
||||
has_many :child_themes, through: :child_theme_relation, source: :child_theme
|
||||
has_many :color_schemes
|
||||
belongs_to :remote_theme
|
||||
|
||||
before_create do
|
||||
|
@ -19,7 +20,13 @@ class Theme < ActiveRecord::Base
|
|||
true
|
||||
end
|
||||
|
||||
def notify_color_change(color)
|
||||
changed_colors << color
|
||||
end
|
||||
|
||||
after_save do
|
||||
changed_colors.each(&:save!)
|
||||
changed_colors.clear
|
||||
changed_fields.each(&:save!)
|
||||
changed_fields.clear
|
||||
|
||||
|
@ -222,6 +229,10 @@ class Theme < ActiveRecord::Base
|
|||
@changed_fields ||= []
|
||||
end
|
||||
|
||||
def changed_colors
|
||||
@changed_colors ||= []
|
||||
end
|
||||
|
||||
def set_field(target, name, value)
|
||||
name = name.to_s
|
||||
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
class ColorSchemeSerializer < ApplicationSerializer
|
||||
attributes :id, :name, :is_base, :base_scheme_id
|
||||
attributes :id, :name, :is_base, :base_scheme_id, :theme_id, :theme_name
|
||||
has_many :colors, serializer: ColorSchemeColorSerializer, embed: :objects
|
||||
|
||||
def theme_name
|
||||
object.theme&.name
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2796,6 +2796,7 @@ en:
|
|||
color: "Color"
|
||||
opacity: "Opacity"
|
||||
copy: "Copy"
|
||||
theme_owner: "Not editable, owned by:"
|
||||
email_templates:
|
||||
title: "Email Templates"
|
||||
subject: "Subject"
|
||||
|
@ -2821,7 +2822,7 @@ en:
|
|||
color_scheme: "Color Scheme"
|
||||
color_scheme_select: "Select colors to be used by theme"
|
||||
custom_sections: "Custom sections:"
|
||||
included_themes: "Included Themes"
|
||||
theme_components: "Theme Components"
|
||||
child_themes_check: "Theme includes other child themes"
|
||||
css_html: "Custom CSS/HTML"
|
||||
edit_css_html: "Edit CSS/HTML"
|
||||
|
@ -2830,6 +2831,7 @@ en:
|
|||
import_file_tip: ".dcstyle.json file containing theme"
|
||||
about_theme: "About Theme"
|
||||
license: "License"
|
||||
component_of: "Theme is a component of:"
|
||||
update_to_latest: "Update to Latest"
|
||||
check_for_updates: "Check for Updates"
|
||||
updating: "Updating..."
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
class AddThemeIdToColorScheme < ActiveRecord::Migration
|
||||
def change
|
||||
add_column :color_schemes, :theme_id, :int
|
||||
end
|
||||
end
|
|
@ -18,13 +18,26 @@ describe RemoteTheme do
|
|||
repo_dir
|
||||
end
|
||||
|
||||
let :initial_repo do
|
||||
setup_git_repo(
|
||||
"about.json" => '{
|
||||
def about_json(options = {})
|
||||
options[:love] ||= "FAFAFA"
|
||||
|
||||
<<JSON
|
||||
{
|
||||
"name": "awesome theme",
|
||||
"about_url": "https://www.site.com/about",
|
||||
"license_url": "https://www.site.com/license"
|
||||
}',
|
||||
"license_url": "https://www.site.com/license",
|
||||
"color_schemes": {
|
||||
"Amazing": {
|
||||
"love": "#{options[:love]}"
|
||||
}
|
||||
}
|
||||
}
|
||||
JSON
|
||||
end
|
||||
|
||||
let :initial_repo do
|
||||
setup_git_repo(
|
||||
"about.json" => about_json,
|
||||
"desktop/desktop.scss" => "body {color: red;}",
|
||||
"common/header.html" => "I AM HEADER",
|
||||
"common/random.html" => "I AM SILLY",
|
||||
|
@ -62,9 +75,16 @@ describe RemoteTheme do
|
|||
|
||||
expect(remote.remote_updated_at).to eq(time)
|
||||
|
||||
scheme = ColorScheme.find_by(theme_id: @theme.id)
|
||||
expect(scheme.name).to eq("Amazing")
|
||||
expect(scheme.colors.find_by(name: 'love').hex).to eq('fafafa')
|
||||
|
||||
File.write("#{initial_repo}/common/header.html", "I AM UPDATED")
|
||||
File.write("#{initial_repo}/about.json", about_json(love: "EAEAEA"))
|
||||
|
||||
`cd #{initial_repo} && git commit -am "update"`
|
||||
|
||||
|
||||
time = Time.new('2001')
|
||||
freeze_time time
|
||||
|
||||
|
@ -77,6 +97,10 @@ describe RemoteTheme do
|
|||
@theme.save
|
||||
@theme.reload
|
||||
|
||||
scheme = ColorScheme.find_by(theme_id: @theme.id)
|
||||
expect(scheme.name).to eq("Amazing")
|
||||
expect(scheme.colors.find_by(name: 'love').hex).to eq('eaeaea')
|
||||
|
||||
mapped = Hash[*@theme.theme_fields.map{|f| ["#{f.target}-#{f.name}", f.value]}.flatten]
|
||||
|
||||
expect(mapped["0-header"]).to eq("I AM UPDATED")
|
||||
|
|
Loading…
Reference in New Issue
Block a user