FEATURE: Native theme support

This feature introduces the concept of themes. Themes are an evolution
of site customizations.

Themes introduce two very big conceptual changes:

- A theme may include other "child themes", children can include grand
children and so on.

- A theme may specify a color scheme

The change does away with the idea of "enabled" color schemes.

It also adds a bunch of big niceties like

- You can source a theme from a git repo

- History for themes is much improved

- You can only have a single enabled theme. Themes can be selected by
    users, if you opt for it.

On a technical level this change comes with a whole bunch of goodies

- All CSS is now compiled using a custom pipeline that uses libsass
    see /lib/stylesheet

- There is a single pipeline for css compilation (in the past we used
    one for customizations and another one for the rest of the app

- The stylesheet pipeline is now divorced of sprockets, there is no
   reliance on sprockets for CSS bundling

- CSS is generated with source maps everywhere (including themes) this
    makes debugging much easier

- Our "live reloader" is smarter and avoid a flash of unstyled content
   we run a file watcher in "puma" in dev so you no longer need to run
   rake autospec to watch for CSS changes
This commit is contained in:
Sam
2017-04-12 10:52:52 -04:00
parent 1a9afa976d
commit a3e8c3cd7b
163 changed files with 4415 additions and 2424 deletions

View File

@@ -9,63 +9,26 @@ class ColorSchemeRevisor
self.new(color_scheme, params).revise
end
def self.revert(color_scheme)
self.new(color_scheme).revert
end
def revise
ColorScheme.transaction do
if @params[:enabled]
ColorScheme.where('id != ?', @color_scheme.id).update_all enabled: false
end
@color_scheme.name = @params[:name] if @params.has_key?(:name)
@color_scheme.enabled = @params[:enabled] if @params.has_key?(:enabled)
@color_scheme.theme_id = @params[:theme_id] if @params.has_key?(:theme_id)
new_version = false
@color_scheme.base_scheme_id = @params[:base_scheme_id] if @params.has_key?(:base_scheme_id)
has_colors = @params[:colors]
if @params[:colors]
new_version = @params[:colors].any? do |c|
(existing = @color_scheme.colors_by_name[c[:name]]).nil? or existing.hex != c[:hex]
end
end
if new_version
ColorScheme.create(
name: @color_scheme.name,
enabled: false,
colors: @color_scheme.colors_hashes,
versioned_id: @color_scheme.id,
version: @color_scheme.version)
@color_scheme.version += 1
end
if @params[:colors]
if has_colors
@params[:colors].each do |c|
if existing = @color_scheme.colors_by_name[c[:name]]
existing.update_attributes(c)
else
@color_scheme.color_scheme_colors << ColorSchemeColor.new(name: c[:name], hex: c[:hex])
end
end
end
@color_scheme.save
@color_scheme.clear_colors_cache
end
@color_scheme
end
def revert
ColorScheme.transaction do
if prev = @color_scheme.previous_version
@color_scheme.version = prev.version
@color_scheme.colors.clear
prev.colors.update_all(color_scheme_id: @color_scheme.id)
prev.destroy
@color_scheme.save!
@color_scheme.clear_colors_cache
end
end
@color_scheme.save if has_colors || @color_scheme.name_changed? || @color_scheme.base_scheme_id_changed?
end
@color_scheme
end

View File

@@ -113,34 +113,49 @@ class StaffActionLogger
}))
end
SITE_CUSTOMIZATION_LOGGED_ATTRS = [
'stylesheet', 'mobile_stylesheet',
'header', 'mobile_header',
'top', 'mobile_top',
'footer', 'mobile_footer',
'head_tag',
'body_tag',
'position',
'enabled',
'key'
]
def theme_json(theme)
ThemeSerializer.new(theme, root:false).to_json
end
def strip_duplicates(old,cur)
return [old,cur] unless old && cur
old = JSON.parse(old)
cur = JSON.parse(cur)
old.each do |k, v|
next if k == "name"
next if k == "id"
if (v == cur[k])
cur.delete(k)
old.delete(k)
end
end
[old.to_json, cur.to_json]
end
def log_theme_change(old_json, new_theme, opts={})
raise Discourse::InvalidParameters.new(:new_theme) unless new_theme
new_json = theme_json(new_theme)
old_json,new_json = strip_duplicates(old_json,new_json)
def log_site_customization_change(old_record, site_customization_params, opts={})
raise Discourse::InvalidParameters.new(:site_customization_params) unless site_customization_params
UserHistory.create( params(opts).merge({
action: UserHistory.actions[:change_site_customization],
subject: site_customization_params[:name],
previous_value: old_record ? old_record.attributes.slice(*SITE_CUSTOMIZATION_LOGGED_ATTRS).to_json : nil,
new_value: site_customization_params.slice(*(SITE_CUSTOMIZATION_LOGGED_ATTRS.map(&:to_sym))).to_json
action: UserHistory.actions[:change_theme],
subject: new_theme.name,
previous_value: old_json,
new_value: new_json
}))
end
def log_site_customization_destroy(site_customization, opts={})
raise Discourse::InvalidParameters.new(:site_customization) unless site_customization
def log_theme_destroy(theme, opts={})
raise Discourse::InvalidParameters.new(:theme) unless theme
UserHistory.create( params(opts).merge({
action: UserHistory.actions[:delete_site_customization],
subject: site_customization.name,
previous_value: site_customization.attributes.slice(*SITE_CUSTOMIZATION_LOGGED_ATTRS).to_json
action: UserHistory.actions[:delete_theme],
subject: theme.name,
previous_value: theme_json(theme)
}))
end