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:
@@ -0,0 +1,20 @@
|
||||
import RestAdapter from 'discourse/adapters/rest';
|
||||
|
||||
export default RestAdapter.extend({
|
||||
basePath() {
|
||||
return "/admin/";
|
||||
},
|
||||
|
||||
afterFindAll(results) {
|
||||
let map = {};
|
||||
results.forEach(theme => {map[theme.id] = theme;});
|
||||
results.forEach(theme => {
|
||||
let mapped = theme.get("child_themes") || [];
|
||||
mapped = mapped.map(t => map[t.id]);
|
||||
theme.set("childThemes", mapped);
|
||||
});
|
||||
return results;
|
||||
},
|
||||
|
||||
jsonMode: true
|
||||
});
|
||||
@@ -14,6 +14,13 @@ export default Ember.Component.extend({
|
||||
}
|
||||
},
|
||||
|
||||
@observes('mode')
|
||||
modeChanged() {
|
||||
if (this._editor && !this._skipContentChangeEvent) {
|
||||
this._editor.getSession().setMode("ace/mode/" + this.get('mode'));
|
||||
}
|
||||
},
|
||||
|
||||
_destroyEditor: function() {
|
||||
if (this._editor) {
|
||||
this._editor.destroy();
|
||||
@@ -41,6 +48,7 @@ export default Ember.Component.extend({
|
||||
|
||||
editor.setTheme("ace/theme/chrome");
|
||||
editor.setShowPrintMargin(false);
|
||||
editor.setOptions({fontSize: "14px"});
|
||||
editor.getSession().setMode("ace/mode/" + this.get('mode'));
|
||||
editor.on('change', () => {
|
||||
this._skipContentChangeEvent = true;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import {default as loadScript, loadCSS } from 'discourse/lib/load-script';
|
||||
|
||||
/**
|
||||
An input field for a color.
|
||||
|
||||
@@ -6,19 +8,36 @@
|
||||
@params valid is a boolean indicating if the input field is a valid color.
|
||||
**/
|
||||
export default Ember.Component.extend({
|
||||
classNames: ['color-picker'],
|
||||
hexValueChanged: function() {
|
||||
var hex = this.get('hexValue');
|
||||
let $text = this.$('input.hex-input');
|
||||
|
||||
if (this.get('valid')) {
|
||||
this.$('input').attr('style', 'color: ' + (this.get('brightnessValue') > 125 ? 'black' : 'white') + '; background-color: #' + hex + ';');
|
||||
$text.attr('style', 'color: ' + (this.get('brightnessValue') > 125 ? 'black' : 'white') + '; background-color: #' + hex + ';');
|
||||
|
||||
if (this.get('pickerLoaded')) {
|
||||
this.$('.picker').spectrum({color: "#" + this.get('hexValue')});
|
||||
}
|
||||
} else {
|
||||
this.$('input').attr('style', '');
|
||||
$text.attr('style', '');
|
||||
}
|
||||
}.observes('hexValue', 'brightnessValue', 'valid'),
|
||||
|
||||
_triggerHexChanged: function() {
|
||||
var self = this;
|
||||
Em.run.schedule('afterRender', function() {
|
||||
self.hexValueChanged();
|
||||
didInsertElement() {
|
||||
loadScript('/javascripts/spectrum.js').then(()=>{
|
||||
loadCSS('/javascripts/spectrum.css').then(()=>{
|
||||
Em.run.schedule('afterRender', ()=>{
|
||||
this.$('.picker').spectrum({color: "#" + this.get('hexValue')})
|
||||
.on("change.spectrum", (me, color)=>{
|
||||
this.set('hexValue', color.toHexString().replace("#",""));
|
||||
});
|
||||
this.set('pickerLoaded', true);
|
||||
});
|
||||
});
|
||||
});
|
||||
}.on('didInsertElement')
|
||||
Em.run.schedule('afterRender', ()=>{
|
||||
this.hexValueChanged();
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
import { getOwner } from 'discourse-common/lib/get-owner';
|
||||
|
||||
export default Ember.Component.extend({
|
||||
router: function() {
|
||||
return getOwner(this).lookup('router:main');
|
||||
}.property(),
|
||||
|
||||
active: function() {
|
||||
const id = this.get('customization.id');
|
||||
return this.get('router.url').indexOf(`/customize/css_html/${id}/css`) !== -1;
|
||||
}.property('router.url', 'customization.id')
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import {default as computed, observes} from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
init(){
|
||||
this._super();
|
||||
this.set("checkedInternal", this.get("checked"));
|
||||
},
|
||||
|
||||
classNames: ['inline-edit'],
|
||||
|
||||
@observes("checked")
|
||||
checkedChanged() {
|
||||
this.set("checkedInternal", this.get("checked"));
|
||||
},
|
||||
|
||||
@computed("labelKey")
|
||||
label(key) {
|
||||
return I18n.t(key);
|
||||
},
|
||||
|
||||
@computed("checked", "checkedInternal")
|
||||
changed(checked, checkedInternal) {
|
||||
return (!!checked) !== (!!checkedInternal);
|
||||
},
|
||||
|
||||
actions: {
|
||||
cancelled(){
|
||||
this.set("checkedInternal", this.get("checked"));
|
||||
},
|
||||
|
||||
finished(){
|
||||
this.set("checked", this.get("checkedInternal"));
|
||||
this.sendAction();
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
@computed("model.colors","onlyOverridden")
|
||||
colors(allColors, onlyOverridden) {
|
||||
if (onlyOverridden) {
|
||||
return allColors.filter(color => color.get("overridden"));
|
||||
} else {
|
||||
return allColors;
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
revert: function(color) {
|
||||
color.revert();
|
||||
},
|
||||
|
||||
undo: function(color) {
|
||||
color.undo();
|
||||
},
|
||||
|
||||
copy() {
|
||||
var newColorScheme = Em.copy(this.get('model'), true);
|
||||
newColorScheme.set('name', I18n.t('admin.customize.colors.copy_name_prefix') + ' ' + this.get('model.name'));
|
||||
newColorScheme.save().then(()=>{
|
||||
this.get('allColors').pushObject(newColorScheme);
|
||||
this.replaceRoute('adminCustomize.colors.show', newColorScheme);
|
||||
});
|
||||
},
|
||||
|
||||
save: function() {
|
||||
this.get('model').save();
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
|
||||
return bootbox.confirm(I18n.t("admin.customize.colors.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
|
||||
if (result) {
|
||||
this.get('model').destroy().then(()=>{
|
||||
this.get('allColors').removeObject(this.get('model'));
|
||||
this.replaceRoute('adminCustomize.colors');
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,10 +1,14 @@
|
||||
export default Ember.Controller.extend({
|
||||
onlyOverridden: false,
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
baseColorScheme: function() {
|
||||
return this.get('model').findBy('is_base', true);
|
||||
}.property('model.@each.id'),
|
||||
|
||||
baseColorSchemes: function() {
|
||||
return this.get('model').filterBy('is_base', true);
|
||||
}.property('model.@each.id'),
|
||||
|
||||
baseColors: function() {
|
||||
var baseColorsHash = Em.Object.create({});
|
||||
_.each(this.get('baseColorScheme.colors'), function(color){
|
||||
@@ -13,99 +17,25 @@ export default Ember.Controller.extend({
|
||||
return baseColorsHash;
|
||||
}.property('baseColorScheme'),
|
||||
|
||||
removeSelected() {
|
||||
this.get('model').removeObject(this.get('selectedItem'));
|
||||
this.set('selectedItem', null);
|
||||
},
|
||||
|
||||
filterContent: function() {
|
||||
if (!this.get('selectedItem')) { return; }
|
||||
|
||||
if (!this.get('onlyOverridden')) {
|
||||
this.set('colors', this.get('selectedItem.colors'));
|
||||
return;
|
||||
}
|
||||
|
||||
const matches = [];
|
||||
_.each(this.get('selectedItem.colors'), function(color){
|
||||
if (color.get('overridden')) matches.pushObject(color);
|
||||
});
|
||||
|
||||
this.set('colors', matches);
|
||||
}.observes('onlyOverridden'),
|
||||
|
||||
updateEnabled: function() {
|
||||
var selectedItem = this.get('selectedItem');
|
||||
if (selectedItem.get('enabled')) {
|
||||
this.get('model').forEach(function(c) {
|
||||
if (c !== selectedItem) {
|
||||
c.set('enabled', false);
|
||||
c.startTrackingChanges();
|
||||
c.notifyPropertyChange('description');
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
selectColorScheme: function(colorScheme) {
|
||||
if (this.get('selectedItem')) { this.get('selectedItem').set('selected', false); }
|
||||
this.set('selectedItem', colorScheme);
|
||||
this.set('colors', colorScheme.get('colors'));
|
||||
colorScheme.set('savingStatus', null);
|
||||
colorScheme.set('selected', true);
|
||||
this.filterContent();
|
||||
|
||||
newColorSchemeWithBase(baseKey) {
|
||||
const base = this.get('baseColorSchemes').findBy('base_scheme_id', baseKey);
|
||||
const newColorScheme = Em.copy(base, true);
|
||||
newColorScheme.set('name', I18n.t('admin.customize.colors.new_name'));
|
||||
newColorScheme.set('base_scheme_id', base.get('base_scheme_id'));
|
||||
newColorScheme.save().then(()=>{
|
||||
this.get('model').pushObject(newColorScheme);
|
||||
newColorScheme.set('savingStatus', null);
|
||||
this.replaceRoute('adminCustomize.colors.show', newColorScheme);
|
||||
});
|
||||
},
|
||||
|
||||
newColorScheme() {
|
||||
const newColorScheme = Em.copy(this.get('baseColorScheme'), true);
|
||||
newColorScheme.set('name', I18n.t('admin.customize.colors.new_name'));
|
||||
this.get('model').pushObject(newColorScheme);
|
||||
this.send('selectColorScheme', newColorScheme);
|
||||
this.set('onlyOverridden', false);
|
||||
showModal('admin-color-scheme-select-base', { model: this.get('baseColorSchemes'), admin: true});
|
||||
},
|
||||
|
||||
revert: function(color) {
|
||||
color.revert();
|
||||
},
|
||||
|
||||
undo: function(color) {
|
||||
color.undo();
|
||||
},
|
||||
|
||||
toggleEnabled: function() {
|
||||
var selectedItem = this.get('selectedItem');
|
||||
selectedItem.toggleProperty('enabled');
|
||||
selectedItem.save({enabledOnly: true});
|
||||
this.updateEnabled();
|
||||
},
|
||||
|
||||
save: function() {
|
||||
this.get('selectedItem').save();
|
||||
this.updateEnabled();
|
||||
},
|
||||
|
||||
copy(colorScheme) {
|
||||
var newColorScheme = Em.copy(colorScheme, true);
|
||||
newColorScheme.set('name', I18n.t('admin.customize.colors.copy_name_prefix') + ' ' + colorScheme.get('name'));
|
||||
this.get('model').pushObject(newColorScheme);
|
||||
this.send('selectColorScheme', newColorScheme);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
var self = this,
|
||||
item = self.get('selectedItem');
|
||||
|
||||
return bootbox.confirm(I18n.t("admin.customize.colors.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
|
||||
if (result) {
|
||||
if (item.get('newRecord')) {
|
||||
self.removeSelected();
|
||||
} else {
|
||||
item.destroy().then(function(){ self.removeSelected(); });
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
import { url } from 'discourse/lib/computed';
|
||||
|
||||
const sections = ['css', 'header', 'top', 'footer', 'head-tag', 'body-tag',
|
||||
'mobile-css', 'mobile-header', 'mobile-top', 'mobile-footer',
|
||||
'embedded-css'];
|
||||
|
||||
const activeSections = {};
|
||||
sections.forEach(function(s) {
|
||||
activeSections[Ember.String.camelize(s) + "Active"] = Ember.computed.equal('section', s);
|
||||
});
|
||||
|
||||
|
||||
export default Ember.Controller.extend(activeSections, {
|
||||
maximized: false,
|
||||
section: null,
|
||||
|
||||
previewUrl: url("model.key", "/?preview-style=%@"),
|
||||
downloadUrl: url('model.id', '/admin/site_customizations/%@'),
|
||||
|
||||
mobile: function() {
|
||||
return this.get('section').indexOf('mobile-') === 0;
|
||||
}.property('section'),
|
||||
|
||||
maximizeIcon: function() {
|
||||
return this.get('maximized') ? 'compress' : 'expand';
|
||||
}.property('maximized'),
|
||||
|
||||
saveButtonText: function() {
|
||||
return this.get('model.isSaving') ? I18n.t('saving') : I18n.t('admin.customize.save');
|
||||
}.property('model.isSaving'),
|
||||
|
||||
saveDisabled: function() {
|
||||
return !this.get('model.changed') || this.get('model.isSaving');
|
||||
}.property('model.changed', 'model.isSaving'),
|
||||
|
||||
adminCustomizeCssHtml: Ember.inject.controller(),
|
||||
|
||||
undoPreviewUrl: url('/?preview-style='),
|
||||
defaultStyleUrl: url('/?preview-style=default'),
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.get('model').saveChanges();
|
||||
},
|
||||
|
||||
destroy() {
|
||||
return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
|
||||
if (result) {
|
||||
const model = this.get('model');
|
||||
model.destroyRecord().then(() => {
|
||||
this.get('adminCustomizeCssHtml').get('model').removeObject(model);
|
||||
this.transitionToRoute('adminCustomizeCssHtml');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
toggleMaximize: function() {
|
||||
this.toggleProperty('maximized');
|
||||
},
|
||||
|
||||
toggleMobile: function() {
|
||||
const section = this.get('section');
|
||||
|
||||
// Try to send to the same tab as before
|
||||
let dest;
|
||||
if (this.get('mobile')) {
|
||||
dest = section.replace('mobile-', '');
|
||||
if (sections.indexOf(dest) === -1) { dest = 'css'; }
|
||||
} else {
|
||||
dest = 'mobile-' + section;
|
||||
if (sections.indexOf(dest) === -1) { dest = 'mobile-css'; }
|
||||
}
|
||||
this.replaceRoute('adminCustomizeCssHtml.show', this.get('model.id'), dest);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,106 @@
|
||||
import { url } from 'discourse/lib/computed';
|
||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
maximized: false,
|
||||
section: null,
|
||||
|
||||
targets: [
|
||||
{id: 0, name: I18n.t('admin.customize.theme.common')},
|
||||
{id: 1, name: I18n.t('admin.customize.theme.desktop')},
|
||||
{id: 2, name: I18n.t('admin.customize.theme.mobile')}
|
||||
],
|
||||
|
||||
currentTarget: 0,
|
||||
|
||||
setTargetName: function(name) {
|
||||
let target;
|
||||
switch(name) {
|
||||
case "common": target = 0; break;
|
||||
case "desktop": target = 1; break;
|
||||
case "mobile": target = 2; break;
|
||||
}
|
||||
|
||||
this.set("currentTarget", target);
|
||||
},
|
||||
|
||||
@computed("currentTarget")
|
||||
currentTargetName(target) {
|
||||
switch(parseInt(target)) {
|
||||
case 0: return "common";
|
||||
case 1: return "desktop";
|
||||
case 2: return "mobile";
|
||||
}
|
||||
},
|
||||
|
||||
@computed("fieldName")
|
||||
activeSectionMode(fieldName) {
|
||||
return fieldName && fieldName.indexOf("scss") > -1 ? "css" : "html";
|
||||
},
|
||||
|
||||
@computed("fieldName", "currentTargetName", "model")
|
||||
activeSection: {
|
||||
get(fieldName, target, model) {
|
||||
return model.getField(target, fieldName);
|
||||
},
|
||||
set(value, fieldName, target, model) {
|
||||
model.setField(target, fieldName, value);
|
||||
return value;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@computed("currentTarget")
|
||||
fields(target) {
|
||||
let fields = [
|
||||
"scss", "head_tag", "header", "after_header", "body_tag", "footer"
|
||||
];
|
||||
|
||||
if (parseInt(target) === 0) {
|
||||
fields.push("embedded_scss");
|
||||
}
|
||||
|
||||
return fields.map(name=>{
|
||||
let hash = {
|
||||
key: (`admin.customize.theme.${name}.text`),
|
||||
name: name
|
||||
};
|
||||
|
||||
if (name.indexOf("_tag") > 0) {
|
||||
hash.icon = "file-text-o";
|
||||
}
|
||||
|
||||
hash.title = I18n.t(`admin.customize.theme.${name}.title`);
|
||||
|
||||
return hash;
|
||||
});
|
||||
},
|
||||
|
||||
previewUrl: url('model.key', '/?preview-style=%@'),
|
||||
|
||||
maximizeIcon: function() {
|
||||
return this.get('maximized') ? 'compress' : 'expand';
|
||||
}.property('maximized'),
|
||||
|
||||
saveButtonText: function() {
|
||||
return this.get('model.isSaving') ? I18n.t('saving') : I18n.t('admin.customize.save');
|
||||
}.property('model.isSaving'),
|
||||
|
||||
saveDisabled: function() {
|
||||
return !this.get('model.changed') || this.get('model.isSaving');
|
||||
}.property('model.changed', 'model.isSaving'),
|
||||
|
||||
undoPreviewUrl: url('/?preview-style='),
|
||||
defaultStyleUrl: url('/?preview-style=default'),
|
||||
|
||||
actions: {
|
||||
save() {
|
||||
this.get('model').saveChanges("theme_fields");
|
||||
},
|
||||
|
||||
toggleMaximize: function() {
|
||||
this.toggleProperty('maximized');
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,163 @@
|
||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||
import { url } from 'discourse/lib/computed';
|
||||
|
||||
export default Ember.Controller.extend({
|
||||
|
||||
@computed("model.theme_fields.@each")
|
||||
hasEditedFields(fields) {
|
||||
return fields.any(f=>!Em.isBlank(f.value));
|
||||
},
|
||||
|
||||
@computed('model.theme_fields.@each')
|
||||
editedDescriptions(fields) {
|
||||
let descriptions = [];
|
||||
let description = target => {
|
||||
let current = fields.filter(field => field.target === target && !Em.isBlank(field.value));
|
||||
if (current.length > 0) {
|
||||
let text = I18n.t('admin.customize.theme.'+target);
|
||||
let localized = current.map(f=>I18n.t('admin.customize.theme.'+f.name + '.text'));
|
||||
return text + ": " + localized.join(" , ");
|
||||
}
|
||||
};
|
||||
['common','desktop','mobile'].forEach(target=> {
|
||||
descriptions.push(description(target));
|
||||
});
|
||||
return descriptions.reject(d=>Em.isBlank(d));
|
||||
},
|
||||
|
||||
@computed("colorSchemeId", "model.color_scheme_id")
|
||||
colorSchemeChanged(colorSchemeId, existingId) {
|
||||
colorSchemeId = colorSchemeId === null ? null : parseInt(colorSchemeId);
|
||||
return colorSchemeId !== existingId;
|
||||
},
|
||||
|
||||
@computed("availableChildThemes", "model.childThemes.@each", "model", "allowChildThemes")
|
||||
selectableChildThemes(available, childThemes, model, allowChildThemes) {
|
||||
if (!allowChildThemes && (!childThemes || childThemes.length === 0)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let themes = [];
|
||||
available.forEach(t=> {
|
||||
if (!childThemes || (childThemes.indexOf(t) === -1)) {
|
||||
themes.push(t);
|
||||
};
|
||||
});
|
||||
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) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let excludeIds = [this.get("model.id")];
|
||||
|
||||
let themes = [];
|
||||
allThemes.forEach(theme => {
|
||||
if (excludeIds.indexOf(theme.get("id")) === -1) {
|
||||
themes.push(theme);
|
||||
}
|
||||
});
|
||||
|
||||
return themes;
|
||||
},
|
||||
|
||||
downloadUrl: url('model.id', '/admin/themes/%@'),
|
||||
|
||||
actions: {
|
||||
|
||||
updateToLatest() {
|
||||
this.set("updatingRemote", true);
|
||||
this.get("model").updateToLatest().finally(()=>{
|
||||
this.set("updatingRemote", false);
|
||||
});
|
||||
},
|
||||
|
||||
checkForThemeUpdates() {
|
||||
this.set("updatingRemote", true);
|
||||
this.get("model").checkForUpdates().finally(()=>{
|
||||
this.set("updatingRemote", false);
|
||||
});
|
||||
},
|
||||
|
||||
cancelChangeScheme() {
|
||||
this.set("colorSchemeId", this.get("model.color_scheme_id"));
|
||||
},
|
||||
changeScheme(){
|
||||
let schemeId = this.get("colorSchemeId");
|
||||
this.set("model.color_scheme_id", schemeId === null ? null : parseInt(schemeId));
|
||||
this.get("model").saveChanges("color_scheme_id");
|
||||
},
|
||||
startEditingName() {
|
||||
this.set("oldName", this.get("model.name"));
|
||||
this.set("editingName", true);
|
||||
},
|
||||
cancelEditingName() {
|
||||
this.set("model.name", this.get("oldName"));
|
||||
this.set("editingName", false);
|
||||
},
|
||||
finishedEditingName() {
|
||||
this.get("model").saveChanges("name");
|
||||
this.set("editingName", false);
|
||||
},
|
||||
|
||||
editTheme() {
|
||||
let edit = ()=>this.transitionToRoute('adminCustomizeThemes.edit', {model: this.get('model')});
|
||||
|
||||
if (this.get("model.remote_theme")) {
|
||||
bootbox.confirm(I18n.t("admin.customize.theme.edit_confirm"), result => {
|
||||
if (result) {
|
||||
edit();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
edit();
|
||||
}
|
||||
},
|
||||
|
||||
applyDefault() {
|
||||
const model = this.get("model");
|
||||
model.saveChanges("default").then(()=>{
|
||||
if (model.get("default")) {
|
||||
this.get("allThemes").forEach(theme=>{
|
||||
if (theme !== model && theme.get('default')) {
|
||||
theme.set("default", false);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
applyUserSelectable() {
|
||||
this.get("model").saveChanges("user_selectable");
|
||||
},
|
||||
|
||||
addChildTheme() {
|
||||
let themeId = parseInt(this.get("selectedChildThemeId"));
|
||||
let theme = this.get("allThemes").findBy("id", themeId);
|
||||
this.get("model").addChildTheme(theme);
|
||||
},
|
||||
|
||||
removeChildTheme(theme) {
|
||||
this.get("model").removeChildTheme(theme);
|
||||
},
|
||||
|
||||
destroy() {
|
||||
return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
|
||||
if (result) {
|
||||
const model = this.get('model');
|
||||
model.destroyRecord().then(() => {
|
||||
this.get('allThemes').removeObject(model);
|
||||
this.transitionToRoute('adminCustomizeThemes');
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
|
||||
adminCustomizeColors: Ember.inject.controller(),
|
||||
|
||||
actions: {
|
||||
selectBase() {
|
||||
this.get('adminCustomizeColors')
|
||||
.send('newColorSchemeWithBase', this.get('selectedBaseThemeId'));
|
||||
this.send('closeModal');
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,35 @@
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
import { ajax } from 'discourse/lib/ajax';
|
||||
// import computed from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
local: Ember.computed.equal('selection', 'local'),
|
||||
remote: Ember.computed.equal('selection', 'remote'),
|
||||
selection: 'local',
|
||||
adminCustomizeThemes: Ember.inject.controller(),
|
||||
|
||||
actions: {
|
||||
importTheme() {
|
||||
|
||||
let options = {
|
||||
type: 'POST'
|
||||
};
|
||||
|
||||
if (this.get('local')) {
|
||||
options.processData = false;
|
||||
options.contentType = false;
|
||||
options.data = new FormData();
|
||||
options.data.append('theme', $('#file-input')[0].files[0]);
|
||||
} else {
|
||||
options.data = {remote: this.get('uploadUrl')};
|
||||
}
|
||||
|
||||
ajax('/admin/themes/import', options).then(result=>{
|
||||
const theme = this.store.createRecord('theme',result.theme);
|
||||
this.get('adminCustomizeThemes').send('addTheme', theme);
|
||||
this.send('closeModal');
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,13 @@
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
import { ajax } from 'discourse/lib/ajax';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
loadDiff() {
|
||||
this.set('loading', true);
|
||||
ajax('/admin/logs/staff_action_logs/' + this.get('model.id') + '/diff')
|
||||
.then(diff=>{
|
||||
this.set('loading', false);
|
||||
this.set('diff', diff.side_by_side);
|
||||
});
|
||||
}
|
||||
});
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
import ModalFunctionality from 'discourse/mixins/modal-functionality';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
previousSelected: Ember.computed.equal('selectedTab', 'previous'),
|
||||
newSelected: Ember.computed.equal('selectedTab', 'new'),
|
||||
|
||||
onShow: function() {
|
||||
this.send("selectNew");
|
||||
},
|
||||
|
||||
actions: {
|
||||
selectNew: function() {
|
||||
this.set('selectedTab', 'new');
|
||||
},
|
||||
|
||||
selectPrevious: function() {
|
||||
this.set('selectedTab', 'previous');
|
||||
}
|
||||
}
|
||||
});
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
import ChangeSiteCustomizationDetailsController from "admin/controllers/modals/change-site-customization-details";
|
||||
|
||||
export default ChangeSiteCustomizationDetailsController.extend({
|
||||
onShow() {
|
||||
this.send("selectPrevious");
|
||||
}
|
||||
});
|
||||
@@ -9,18 +9,17 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, {
|
||||
},
|
||||
|
||||
description: function() {
|
||||
return "" + this.name + (this.enabled ? ' (*)' : '');
|
||||
return "" + this.name;
|
||||
}.property(),
|
||||
|
||||
startTrackingChanges: function() {
|
||||
this.set('originals', {
|
||||
name: this.get('name'),
|
||||
enabled: this.get('enabled')
|
||||
name: this.get('name')
|
||||
});
|
||||
},
|
||||
|
||||
copy: function() {
|
||||
var newScheme = ColorScheme.create({name: this.get('name'), enabled: false, can_edit: true, colors: Em.A()});
|
||||
var newScheme = ColorScheme.create({name: this.get('name'), can_edit: true, colors: Em.A()});
|
||||
_.each(this.get('colors'), function(c){
|
||||
newScheme.colors.pushObject(ColorSchemeColor.create({name: c.get('name'), hex: c.get('hex'), default_hex: c.get('default_hex')}));
|
||||
});
|
||||
@@ -29,19 +28,15 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, {
|
||||
|
||||
changed: function() {
|
||||
if (!this.originals) return false;
|
||||
if (this.originals['name'] !== this.get('name') || this.originals['enabled'] !== this.get('enabled')) return true;
|
||||
if (this.originals['name'] !== this.get('name')) return true;
|
||||
if (_.any(this.get('colors'), function(c){ return c.get('changed'); })) return true;
|
||||
return false;
|
||||
}.property('name', 'enabled', 'colors.@each.changed', 'saving'),
|
||||
}.property('name', 'colors.@each.changed', 'saving'),
|
||||
|
||||
disableSave: function() {
|
||||
return !this.get('changed') || this.get('saving') || _.any(this.get('colors'), function(c) { return !c.get('valid'); });
|
||||
}.property('changed'),
|
||||
|
||||
disableEnable: function() {
|
||||
return !this.get('id') || this.get('saving');
|
||||
}.property('id', 'saving'),
|
||||
|
||||
newRecord: function() {
|
||||
return (!this.get('id'));
|
||||
}.property('id'),
|
||||
@@ -53,11 +48,11 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, {
|
||||
this.set('savingStatus', I18n.t('saving'));
|
||||
this.set('saving',true);
|
||||
|
||||
var data = { enabled: this.enabled };
|
||||
var data = {};
|
||||
|
||||
if (!opts || !opts.enabledOnly) {
|
||||
data.name = this.name;
|
||||
|
||||
data.base_scheme_id = this.get('base_scheme_id');
|
||||
data.colors = [];
|
||||
_.each(this.get('colors'), function(c) {
|
||||
if (!self.id || c.get('changed')) {
|
||||
@@ -78,8 +73,6 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, {
|
||||
_.each(self.get('colors'), function(c) {
|
||||
c.startTrackingChanges();
|
||||
});
|
||||
} else {
|
||||
self.set('originals.enabled', data.enabled);
|
||||
}
|
||||
self.set('savingStatus', I18n.t('saved'));
|
||||
self.set('saving', false);
|
||||
@@ -96,30 +89,23 @@ const ColorScheme = Discourse.Model.extend(Ember.Copyable, {
|
||||
});
|
||||
|
||||
var ColorSchemes = Ember.ArrayProxy.extend({
|
||||
selectedItemChanged: function() {
|
||||
var selected = this.get('selectedItem');
|
||||
_.each(this.get('content'),function(i) {
|
||||
return i.set('selected', selected === i);
|
||||
});
|
||||
}.observes('selectedItem')
|
||||
});
|
||||
|
||||
ColorScheme.reopenClass({
|
||||
findAll: function() {
|
||||
var colorSchemes = ColorSchemes.create({ content: [], loading: true });
|
||||
ajax('/admin/color_schemes').then(function(all) {
|
||||
return ajax('/admin/color_schemes').then(function(all) {
|
||||
_.each(all, function(colorScheme){
|
||||
colorSchemes.pushObject(ColorScheme.create({
|
||||
id: colorScheme.id,
|
||||
name: colorScheme.name,
|
||||
enabled: colorScheme.enabled,
|
||||
is_base: colorScheme.is_base,
|
||||
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}); })
|
||||
}));
|
||||
});
|
||||
colorSchemes.set('loading', false);
|
||||
return colorSchemes;
|
||||
});
|
||||
return colorSchemes;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
import RestModel from 'discourse/models/rest';
|
||||
|
||||
const trackedProperties = [
|
||||
'enabled', 'name', 'stylesheet', 'header', 'top', 'footer', 'mobile_stylesheet',
|
||||
'mobile_header', 'mobile_top', 'mobile_footer', 'head_tag', 'body_tag', 'embedded_css'
|
||||
];
|
||||
|
||||
function changed() {
|
||||
const originals = this.get('originals');
|
||||
if (!originals) { return false; }
|
||||
return _.some(trackedProperties, (p) => originals[p] !== this.get(p));
|
||||
}
|
||||
|
||||
const SiteCustomization = RestModel.extend({
|
||||
description: function() {
|
||||
return "" + this.name + (this.enabled ? ' (*)' : '');
|
||||
}.property('selected', 'name', 'enabled'),
|
||||
|
||||
changed: changed.property.apply(changed, trackedProperties.concat('originals')),
|
||||
|
||||
startTrackingChanges: function() {
|
||||
this.set('originals', this.getProperties(trackedProperties));
|
||||
}.on('init'),
|
||||
|
||||
saveChanges() {
|
||||
return this.save(this.getProperties(trackedProperties)).then(() => this.startTrackingChanges());
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default SiteCustomization;
|
||||
@@ -39,7 +39,7 @@ const StaffActionLog = Discourse.Model.extend({
|
||||
}.property('action_name'),
|
||||
|
||||
useCustomModalForDetails: function() {
|
||||
return _.contains(['change_site_customization', 'delete_site_customization'], this.get('action_name'));
|
||||
return _.contains(['change_theme', 'delete_theme'], this.get('action_name'));
|
||||
}.property('action_name')
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
import RestModel from 'discourse/models/rest';
|
||||
import { default as computed } from 'ember-addons/ember-computed-decorators';
|
||||
|
||||
const Theme = RestModel.extend({
|
||||
|
||||
@computed('theme_fields')
|
||||
themeFields(fields) {
|
||||
|
||||
if (!fields) {
|
||||
this.set('theme_fields', []);
|
||||
return {};
|
||||
}
|
||||
|
||||
let hash = {};
|
||||
if (fields) {
|
||||
fields.forEach(field=>{
|
||||
hash[field.target + " " + field.name] = field;
|
||||
});
|
||||
}
|
||||
return hash;
|
||||
},
|
||||
|
||||
getField(target, name) {
|
||||
let themeFields = this.get("themeFields");
|
||||
let key = target + " " + name;
|
||||
let field = themeFields[key];
|
||||
return field ? field.value : "";
|
||||
},
|
||||
|
||||
setField(target, name, value) {
|
||||
this.set("changed", true);
|
||||
|
||||
let themeFields = this.get("themeFields");
|
||||
let key = target + " " + name;
|
||||
let field = themeFields[key];
|
||||
if (!field) {
|
||||
field = {name, target, value};
|
||||
this.theme_fields.push(field);
|
||||
themeFields[key] = field;
|
||||
} else {
|
||||
field.value = value;
|
||||
}
|
||||
},
|
||||
|
||||
@computed("childThemes.@each")
|
||||
child_theme_ids(childThemes) {
|
||||
if (childThemes) {
|
||||
return childThemes.map(theme => Ember.get(theme, "id"));
|
||||
}
|
||||
},
|
||||
|
||||
removeChildTheme(theme) {
|
||||
const childThemes = this.get("childThemes");
|
||||
childThemes.removeObject(theme);
|
||||
return this.saveChanges("child_theme_ids");
|
||||
},
|
||||
|
||||
addChildTheme(theme){
|
||||
let childThemes = this.get("childThemes");
|
||||
childThemes.removeObject(theme);
|
||||
childThemes.pushObject(theme);
|
||||
return this.saveChanges("child_theme_ids");
|
||||
},
|
||||
|
||||
@computed('name', 'default')
|
||||
description: function(name, isDefault) {
|
||||
if (isDefault) {
|
||||
return I18n.t('admin.customize.theme.default_name', {name: name});
|
||||
} else {
|
||||
return name;
|
||||
}
|
||||
},
|
||||
|
||||
checkForUpdates() {
|
||||
return this.save({remote_check: true})
|
||||
.then(() => this.set("changed", false));
|
||||
},
|
||||
|
||||
updateToLatest() {
|
||||
return this.save({remote_update: true})
|
||||
.then(() => this.set("changed", false));
|
||||
},
|
||||
|
||||
changed: false,
|
||||
|
||||
saveChanges() {
|
||||
const hash = this.getProperties.apply(this, arguments);
|
||||
return this.save(hash)
|
||||
.then(() => this.set("changed", false));
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
export default Theme;
|
||||
@@ -0,0 +1,18 @@
|
||||
export default Ember.Route.extend({
|
||||
|
||||
model(params) {
|
||||
const all = this.modelFor('adminCustomize.colors');
|
||||
const model = all.findBy('id', parseInt(params.scheme_id));
|
||||
return model ? model : this.replaceWith('adminCustomize.colors.index');
|
||||
},
|
||||
|
||||
serialize(model) {
|
||||
return {scheme_id: model.get('id')};
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
controller.set('model', model);
|
||||
controller.set('allColors', this.modelFor('adminCustomize.colors'));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -6,9 +6,7 @@ export default Ember.Route.extend({
|
||||
return ColorScheme.findAll();
|
||||
},
|
||||
|
||||
deactivate() {
|
||||
this._super();
|
||||
this.controllerFor('adminCustomizeColors').set('selectedItem', null);
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
controller.set("model", model);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
export default Ember.Route.extend({
|
||||
model(params) {
|
||||
const all = this.modelFor('adminCustomizeCssHtml');
|
||||
const model = all.findBy('id', parseInt(params.site_customization_id));
|
||||
return model ? { model, section: params.section } : this.replaceWith('adminCustomizeCssHtml.index');
|
||||
},
|
||||
|
||||
setupController(controller, hash) {
|
||||
controller.setProperties(hash);
|
||||
}
|
||||
});
|
||||
@@ -1,26 +0,0 @@
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model() {
|
||||
return this.store.findAll('site-customization');
|
||||
},
|
||||
|
||||
actions: {
|
||||
importModal() {
|
||||
showModal('upload-customization');
|
||||
},
|
||||
|
||||
newCustomization(obj) {
|
||||
obj = obj || {name: I18n.t("admin.customize.new_style")};
|
||||
const item = this.store.createRecord('site-customization');
|
||||
|
||||
const all = this.modelFor('adminCustomizeCssHtml');
|
||||
const self = this;
|
||||
item.save(obj).then(function() {
|
||||
all.pushObject(item);
|
||||
self.transitionTo('adminCustomizeCssHtml.show', item.get('id'), 'css');
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -1,5 +1,5 @@
|
||||
export default Ember.Route.extend({
|
||||
beforeModel() {
|
||||
this.transitionTo('adminCustomize.colors');
|
||||
this.transitionTo('adminCustomizeThemes');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
export default Ember.Route.extend({
|
||||
model(params) {
|
||||
const all = this.modelFor('adminCustomizeThemes');
|
||||
const model = all.findBy('id', parseInt(params.theme_id));
|
||||
return model ? { model, target: params.target, field_name: params.field_name} : this.replaceWith('adminCustomizeThemes.index');
|
||||
},
|
||||
|
||||
serialize(wrapper) {
|
||||
return {
|
||||
model: wrapper.model,
|
||||
target: wrapper.target || "common",
|
||||
field_name: wrapper.field_name || "scss",
|
||||
theme_id: wrapper.model.get("id")
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
setupController(controller, wrapper) {
|
||||
controller.set("model", wrapper.model);
|
||||
controller.setTargetName(wrapper.target || "common");
|
||||
controller.set("fieldName", wrapper.field_name || "scss");
|
||||
this.controllerFor("adminCustomizeThemes").set("editingTheme", true);
|
||||
},
|
||||
|
||||
});
|
||||
@@ -0,0 +1,21 @@
|
||||
export default Ember.Route.extend({
|
||||
|
||||
serialize(model) {
|
||||
return {theme_id: model.get('id')};
|
||||
},
|
||||
|
||||
model(params) {
|
||||
const all = this.modelFor('adminCustomizeThemes');
|
||||
const model = all.findBy('id', parseInt(params.theme_id));
|
||||
return model ? model : this.replaceWith('adminCustomizeTheme.index');
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
controller.set("model", model);
|
||||
const parentController = this.controllerFor("adminCustomizeThemes");
|
||||
parentController.set("editingTheme", false);
|
||||
controller.set("allThemes", parentController.get("model"));
|
||||
controller.set("colorSchemes", parentController.get("model.extras.color_schemes"));
|
||||
controller.set("colorSchemeId", model.get("color_scheme_id"));
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,36 @@
|
||||
import showModal from 'discourse/lib/show-modal';
|
||||
import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
export default Ember.Route.extend({
|
||||
model() {
|
||||
return this.store.findAll('theme');
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
this._super(controller, model);
|
||||
// TODO ColorScheme to model
|
||||
controller.set("editingTheme", false);
|
||||
},
|
||||
|
||||
actions: {
|
||||
importModal() {
|
||||
showModal('admin-import-theme', {admin: true});
|
||||
},
|
||||
|
||||
addTheme(theme) {
|
||||
const all = this.modelFor('adminCustomizeThemes');
|
||||
all.pushObject(theme);
|
||||
this.transitionTo('adminCustomizeThemes.show', theme.get('id'));
|
||||
},
|
||||
|
||||
|
||||
newTheme(obj) {
|
||||
obj = obj || {name: I18n.t("admin.customize.new_style")};
|
||||
const item = this.store.createRecord('theme');
|
||||
|
||||
item.save(obj).then(() => {
|
||||
this.send('addTheme', item);
|
||||
}).catch(popupAjaxError);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -13,14 +13,9 @@ export default Discourse.Route.extend({
|
||||
},
|
||||
|
||||
showCustomDetailsModal(model) {
|
||||
const modalName = (model.action_name + '_details').replace(/\_/g, "-");
|
||||
|
||||
showModal(modalName, {
|
||||
model,
|
||||
admin: true,
|
||||
templateName: 'site-customization-change'
|
||||
});
|
||||
this.controllerFor('modal').set('modalClass', 'tabbed-modal log-details-modal');
|
||||
let modal = showModal('admin-theme-change', { model, admin: true});
|
||||
this.controllerFor('modal').set('modalClass', 'history-modal');
|
||||
modal.loadDiff();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -15,10 +15,14 @@ export default function() {
|
||||
});
|
||||
|
||||
this.route('adminCustomize', { path: '/customize', resetNamespace: true } ,function() {
|
||||
this.route('colors');
|
||||
|
||||
this.route('adminCustomizeCssHtml', { path: 'css_html', resetNamespace: true }, function() {
|
||||
this.route('show', {path: '/:site_customization_id/:section'});
|
||||
this.route('colors', function() {
|
||||
this.route('show', {path: '/:scheme_id'});
|
||||
});
|
||||
|
||||
this.route('adminCustomizeThemes', { path: 'themes', resetNamespace: true }, function() {
|
||||
this.route('show', {path: '/:theme_id'});
|
||||
this.route('edit', {path: '/:theme_id/:target/:field_name/edit'});
|
||||
});
|
||||
|
||||
this.route('adminSiteText', { path: '/site_texts', resetNamespace: true }, function() {
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
<li>
|
||||
<a href="/admin/customize/css_html/{{customization.id}}/css" class="{{if active 'active'}}">
|
||||
{{customization.description}}
|
||||
</a>
|
||||
</li>
|
||||
@@ -0,0 +1,8 @@
|
||||
<label class='checkbox-label'>
|
||||
{{input type="checkbox" disabled=disabled checked=checkedInternal}}
|
||||
{{label}}
|
||||
</label>
|
||||
{{#if changed}}
|
||||
{{d-button action="finished" class="btn-primary btn-small submit-edit" icon="check"}}
|
||||
{{d-button action="cancelled" class="btn-small cancel-edit" icon="times"}}
|
||||
{{/if}}
|
||||
@@ -0,0 +1 @@
|
||||
<p class="about">{{i18n 'admin.customize.colors.about'}}</p>
|
||||
@@ -0,0 +1,53 @@
|
||||
<div class="color-scheme show-current-style">
|
||||
<div class="admin-container">
|
||||
<h1>{{text-field class="style-name" value=model.name}}</h1>
|
||||
|
||||
<div class="controls">
|
||||
<button {{action "save"}} disabled={{model.disableSave}} class='btn'>{{i18n 'admin.customize.save'}}</button>
|
||||
<button {{action "copy" model}} class='btn'><i class="fa fa-copy"></i> {{i18n 'admin.customize.copy'}}</button>
|
||||
<button {{action "destroy"}} class='btn btn-danger'><i class="fa fa-trash-o"></i> {{i18n 'admin.customize.delete'}}</button>
|
||||
<span class="saving {{unless model.savingStatus 'hidden'}}">{{model.savingStatus}}</span>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class='admin-controls'>
|
||||
<div class='search controls'>
|
||||
<label>
|
||||
{{input type="checkbox" checked=onlyOverridden}}
|
||||
{{i18n 'admin.site_settings.show_overriden'}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if colors.length}}
|
||||
<table class="table colors">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="hex">{{i18n 'admin.customize.color'}}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each colors as |c|}}
|
||||
<tr class="{{if c.changed 'changed'}} {{if c.valid 'valid' 'invalid'}}">
|
||||
<td class="name" title={{c.name}}>
|
||||
<b>{{c.translatedName}}</b>
|
||||
<br/>
|
||||
<span class="description">{{c.description}}</span>
|
||||
</td>
|
||||
<td class="hex">{{color-input hexValue=c.hex brightnessValue=c.brightness valid=c.valid}}</td>
|
||||
<td class="actions">
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<p>{{i18n 'search.no_results'}}</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
@@ -3,76 +3,16 @@
|
||||
<ul>
|
||||
{{#each model as |scheme|}}
|
||||
{{#unless scheme.is_base}}
|
||||
<li><a {{action "selectColorScheme" scheme}} class="{{if scheme.selected 'active'}}">{{scheme.description}}</a></li>
|
||||
|
||||
<li>
|
||||
{{#link-to 'adminCustomize.colors.show' scheme replace=true}}{{scheme.description}}{{/link-to}}
|
||||
</li>
|
||||
{{/unless}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
<button {{action "newColorScheme"}} class='btn'><i class="fa fa-plus"></i>{{i18n 'admin.customize.new'}}</button>
|
||||
<button {{action "newColorScheme"}} class='btn'>{{fa-icon 'plus'}}{{i18n 'admin.customize.new'}}</button>
|
||||
</div>
|
||||
|
||||
{{#if selectedItem}}
|
||||
<div class="current-style color-scheme">
|
||||
<div class="admin-container">
|
||||
<h1>{{text-field class="style-name" value=selectedItem.name}}</h1>
|
||||
|
||||
<div class="controls">
|
||||
<button {{action "save"}} disabled={{selectedItem.disableSave}} class='btn'>{{i18n 'admin.customize.save'}}</button>
|
||||
<button {{action "toggleEnabled"}} disabled={{selectedItem.disableEnable}} class="btn">
|
||||
{{#if selectedItem.enabled}}
|
||||
{{i18n 'disable'}}
|
||||
{{else}}
|
||||
{{i18n 'enable'}}
|
||||
{{/if}}
|
||||
</button>
|
||||
<button {{action "copy" selectedItem}} class='btn'><i class="fa fa-copy"></i> {{i18n 'admin.customize.copy'}}</button>
|
||||
<button {{action "destroy"}} class='btn btn-danger'><i class="fa fa-trash-o"></i> {{i18n 'admin.customize.delete'}}</button>
|
||||
<span class="saving {{unless selectedItem.savingStatus 'hidden'}}">{{selectedItem.savingStatus}}</span>
|
||||
</div>
|
||||
|
||||
<br/>
|
||||
|
||||
<div class='admin-controls'>
|
||||
<div class='search controls'>
|
||||
<label>
|
||||
{{input type="checkbox" checked=onlyOverridden}}
|
||||
{{i18n 'admin.site_settings.show_overriden'}}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{#if colors.length}}
|
||||
<table class="table colors">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th class="hex">{{i18n 'admin.customize.color'}}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{#each colors as |c|}}
|
||||
<tr class="{{if c.changed 'changed'}} {{if c.valid 'valid' 'invalid'}}">
|
||||
<td class="name" title={{c.name}}>
|
||||
<b>{{c.translatedName}}</b>
|
||||
<br/>
|
||||
<span class="description">{{c.description}}</span>
|
||||
</td>
|
||||
<td class="hex">{{color-input hexValue=c.hex brightnessValue=c.brightness valid=c.valid}}</td>
|
||||
<td class="actions">
|
||||
<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>
|
||||
</td>
|
||||
</tr>
|
||||
{{/each}}
|
||||
</tbody>
|
||||
</table>
|
||||
{{else}}
|
||||
<p>{{i18n 'search.no_results'}}</p>
|
||||
{{/if}}
|
||||
</div>
|
||||
</div>
|
||||
{{else}}
|
||||
<p class="about">{{i18n 'admin.customize.colors.about'}}</p>
|
||||
{{/if}}
|
||||
{{outlet}}
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
@@ -1,75 +0,0 @@
|
||||
<div class="current-style {{if maximized 'maximized'}}">
|
||||
<div class='wrapper'>
|
||||
{{text-field class="style-name" value=model.name}}
|
||||
<a class="btn export" target="_blank" href={{downloadUrl}}>{{fa-icon "download"}} {{i18n 'admin.export_json.button_text'}}</a>
|
||||
|
||||
<div class='admin-controls'>
|
||||
<ul class="nav nav-pills">
|
||||
{{#if mobile}}
|
||||
<li>{{#link-to 'adminCustomizeCssHtml.show' model.id 'mobile-css' replace=true}}{{i18n "admin.customize.css"}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminCustomizeCssHtml.show' model.id 'mobile-header' replace=true}}{{i18n "admin.customize.header"}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminCustomizeCssHtml.show' model.id 'mobile-top' replace=true}}{{i18n "admin.customize.top"}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminCustomizeCssHtml.show' model.id 'mobile-footer' replace=true}}{{i18n "admin.customize.footer"}}{{/link-to}}</li>
|
||||
{{else}}
|
||||
<li>{{#link-to 'adminCustomizeCssHtml.show' model.id 'css' replace=true}}{{i18n "admin.customize.css"}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminCustomizeCssHtml.show' model.id 'header' replace=true}}{{i18n "admin.customize.header"}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminCustomizeCssHtml.show' model.id 'top' replace=true}}{{i18n "admin.customize.top"}}{{/link-to}}</li>
|
||||
<li>{{#link-to 'adminCustomizeCssHtml.show' model.id 'footer' replace=true}}{{i18n "admin.customize.footer"}}{{/link-to}}</li>
|
||||
<li>
|
||||
{{#link-to 'adminCustomizeCssHtml.show' model.id 'head-tag'}}
|
||||
{{fa-icon "file-text-o"}} {{i18n 'admin.customize.head_tag.text'}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li>
|
||||
{{#link-to 'adminCustomizeCssHtml.show' model.id 'body-tag'}}
|
||||
{{fa-icon "file-text-o"}} {{i18n 'admin.customize.body_tag.text'}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li>{{#link-to 'adminCustomizeCssHtml.show' model.id 'embedded-css' replace=true}}{{i18n "admin.customize.embedded_css"}}{{/link-to}}</li>
|
||||
{{/if}}
|
||||
<li class='toggle-mobile'>
|
||||
<a class="{{if mobile 'active'}}" {{action "toggleMobile"}}>{{fa-icon "mobile"}}</a>
|
||||
</li>
|
||||
<li class='toggle-maximize'>
|
||||
<a {{action "toggleMaximize"}}>
|
||||
<i class="fa fa-{{maximizeIcon}}"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="admin-container">
|
||||
{{#if cssActive}}{{ace-editor content=model.stylesheet mode="scss"}}{{/if}}
|
||||
{{#if headerActive}}{{ace-editor content=model.header mode="html"}}{{/if}}
|
||||
{{#if topActive}}{{ace-editor content=model.top mode="html"}}{{/if}}
|
||||
{{#if footerActive}}{{ace-editor content=model.footer mode="html"}}{{/if}}
|
||||
{{#if headTagActive}}{{ace-editor content=model.head_tag mode="html"}}{{/if}}
|
||||
{{#if bodyTagActive}}{{ace-editor content=model.body_tag mode="html"}}{{/if}}
|
||||
{{#if embeddedCssActive}}{{ace-editor content=model.embedded_css mode="css"}}{{/if}}
|
||||
{{#if mobileCssActive}}{{ace-editor content=model.mobile_stylesheet mode="scss"}}{{/if}}
|
||||
{{#if mobileHeaderActive}}{{ace-editor content=model.mobile_header mode="html"}}{{/if}}
|
||||
{{#if mobileTopActive}}{{ace-editor content=model.mobile_top mode="html"}}{{/if}}
|
||||
{{#if mobileFooterActive}}{{ace-editor content=model.mobile_footer mode="html"}}{{/if}}
|
||||
</div>
|
||||
|
||||
<div class='admin-footer'>
|
||||
<div class='status-actions'>
|
||||
<span>{{i18n 'admin.customize.enabled'}} {{input type="checkbox" checked=model.enabled}}</span>
|
||||
{{#unless model.changed}}
|
||||
<a class='preview-link' href={{previewUrl}} target='_blank' title="{{i18n 'admin.customize.explain_preview'}}">{{i18n 'admin.customize.preview'}}</a>
|
||||
|
|
||||
<a href={{undoPreviewUrl}} target='_blank' title="{{i18n 'admin.customize.explain_undo_preview'}}">{{i18n 'admin.customize.undo_preview'}}</a>
|
||||
|
|
||||
<a href={{defaultStyleUrl}} target='_blank' title="{{i18n 'admin.customize.explain_rescue_preview'}}">{{i18n 'admin.customize.rescue_preview'}}</a><br>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
<div class='buttons'>
|
||||
{{#d-button action="save" disabled=saveDisabled class='btn-primary'}}
|
||||
{{saveButtonText}}
|
||||
{{/d-button}}
|
||||
{{d-button action="destroy" label="admin.customize.delete" icon="trash" class="btn-danger"}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,13 +0,0 @@
|
||||
<div class='content-list span6'>
|
||||
<h3>{{i18n 'admin.customize.css_html.long_title'}}</h3>
|
||||
<ul>
|
||||
{{#each model as |c|}}
|
||||
{{customize-link customization=c}}
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
{{d-button label="admin.customize.new" icon="plus" action="newCustomization" class="btn-primary"}}
|
||||
{{d-button action="importModal" icon="upload" label="admin.customize.import"}}
|
||||
</div>
|
||||
|
||||
{{outlet}}
|
||||
@@ -0,0 +1,62 @@
|
||||
<div class="current-style {{if maximized 'maximized'}}">
|
||||
<div class='wrapper'>
|
||||
<h2>{{i18n 'admin.customize.theme.edit_css_html'}} {{#link-to 'adminCustomizeThemes.show' model.id replace=true}}{{model.name}}{{/link-to}}</h2>
|
||||
|
||||
<ul class='nav nav-pills target'>
|
||||
<li>
|
||||
{{#link-to 'adminCustomizeThemes.edit' model.id 'common' fieldName replace=true title=field.title}}
|
||||
{{i18n 'admin.customize.theme.common'}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li>
|
||||
{{#link-to 'adminCustomizeThemes.edit' model.id 'desktop' fieldName replace=true title=field.title}}
|
||||
{{i18n 'admin.customize.theme.desktop'}}
|
||||
{{fa-icon 'desktop'}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
<li>
|
||||
{{#link-to 'adminCustomizeThemes.edit' model.id 'mobile' fieldName replace=true title=field.title}}
|
||||
{{i18n 'admin.customize.theme.mobile'}}
|
||||
{{fa-icon 'mobile'}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class='admin-controls'>
|
||||
<ul class='nav nav-pills fields'>
|
||||
{{#each fields as |field|}}
|
||||
<li>
|
||||
{{#link-to 'adminCustomizeThemes.edit' model.id currentTargetName field.name replace=true title=field.title}}
|
||||
{{#if field.icon}}{{fa-icon field.icon}} {{/if}}
|
||||
{{i18n field.key}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{/each}}
|
||||
<li class='toggle-maximize'>
|
||||
<a {{action "toggleMaximize"}}>
|
||||
<i class="fa fa-{{maximizeIcon}}"></i>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class='custom-ace-gutter'></div>
|
||||
{{ace-editor content=activeSection mode=activeSectionMode}}
|
||||
</div>
|
||||
|
||||
<div class='admin-footer'>
|
||||
<div class='status-actions'>
|
||||
{{#unless model.changed}}
|
||||
<a class='preview-link' href={{previewUrl}} target='_blank' title="{{i18n 'admin.customize.explain_preview'}}">{{i18n 'admin.customize.preview'}}</a>
|
||||
{{/unless}}
|
||||
</div>
|
||||
|
||||
<div class='buttons'>
|
||||
{{#d-button action="save" disabled=saveDisabled class='btn-primary'}}
|
||||
{{saveButtonText}}
|
||||
{{/d-button}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,112 @@
|
||||
<div class="show-current-style">
|
||||
<h2>
|
||||
{{#if editingName}}
|
||||
{{text-field value=model.name autofocus="true"}}
|
||||
{{d-button action="finishedEditingName" class="btn-primary btn-small submit-edit" icon="check"}}
|
||||
{{d-button action="cancelEditingName" class="btn-small cancel-edit" icon="times"}}
|
||||
{{else}}
|
||||
{{model.name}} <a {{action "startEditingName"}}>{{fa-icon "pencil"}}</a>
|
||||
{{/if}}
|
||||
</h2>
|
||||
|
||||
{{#if model.remote_theme}}
|
||||
<p>
|
||||
<a href="{{model.remote_theme.about_url}}">{{i18n "admin.customize.theme.about_theme"}}</a>
|
||||
</p>
|
||||
{{#if model.remote_theme.license_url}}
|
||||
<p>
|
||||
<a href="{{model.remote_theme.license_url}}">{{i18n "admin.customize.theme.license"}} {{fa-icon "copyright"}}</a>
|
||||
</p>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
|
||||
<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
|
||||
nameProperty="name"
|
||||
value=colorSchemeId
|
||||
valueAttribute="id"}}
|
||||
{{#if colorSchemeChanged}}
|
||||
{{d-button action="changeScheme" class="btn-primary btn-small submit-edit" icon="check"}}
|
||||
{{d-button action="cancelChangeScheme" class="btn-small cancel-edit" icon="times"}}
|
||||
{{/if}}
|
||||
</p>
|
||||
{{#link-to 'adminCustomize.colors' class="btn edit"}}{{i18n 'admin.customize.colors.edit'}}{{/link-to}}
|
||||
{{/if}}
|
||||
|
||||
<h3>{{i18n "admin.customize.theme.css_html"}}</h3>
|
||||
{{#if hasEditedFields}}
|
||||
|
||||
<p>{{i18n "admin.customize.theme.custom_sections"}}</p>
|
||||
<ul>
|
||||
{{#each editedDescriptions as |desc|}}
|
||||
<li>{{desc}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{else}}
|
||||
<p>
|
||||
{{i18n "admin.customize.theme.edit_css_html_help"}}
|
||||
</p>
|
||||
{{/if}}
|
||||
<p>
|
||||
{{#if model.remote_theme}}
|
||||
{{#if model.remote_theme.commits_behind}}
|
||||
{{#d-button action="updateToLatest" icon="download"}}{{i18n "admin.customize.theme.update_to_latest"}}{{/d-button}}
|
||||
{{else}}
|
||||
{{#d-button action="checkForThemeUpdates" icon="refresh"}}{{i18n "admin.customize.theme.check_for_updates"}}{{/d-button}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{#d-button action="editTheme" class="btn edit"}}{{i18n 'admin.customize.theme.edit_css_html'}}{{/d-button}}
|
||||
{{#if model.remote_theme}}
|
||||
<span class='status-message'>
|
||||
{{#if updatingRemote}}
|
||||
{{i18n 'admin.customize.theme.updating'}}
|
||||
{{else}}
|
||||
{{#if model.remote_theme.commits_behind}}
|
||||
{{i18n 'admin.customize.theme.commits_behind' count=model.remote_theme.commits_behind}}
|
||||
{{else}}
|
||||
{{i18n 'admin.customize.theme.up_to_date'}} {{format-date model.remote_theme.updated_at leaveAgo="true"}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</span>
|
||||
{{/if}}
|
||||
</p>
|
||||
|
||||
{{#if availableChildThemes}}
|
||||
<h3>{{i18n "admin.customize.theme.included_themes"}}</h3>
|
||||
{{#unless model.childThemes.length}}
|
||||
<p>
|
||||
<label class='checkbox-label'>
|
||||
{{input type="checkbox" checked=allowChildThemes}}
|
||||
{{i18n "admin.customize.theme.child_themes_check"}}
|
||||
</label>
|
||||
</p>
|
||||
{{else}}
|
||||
<ul>
|
||||
{{#each model.childThemes as |child|}}
|
||||
<li>{{child.name}} {{d-button action="removeChildTheme" actionParam=child class="btn-small cancel-edit" icon="times"}}</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
{{/unless}}
|
||||
{{#if selectableChildThemes}}
|
||||
<p>{{combo-box content=selectableChildThemes
|
||||
nameProperty="name"
|
||||
value=selectedChildThemeId
|
||||
valueAttribute="id"}}
|
||||
|
||||
{{#d-button action="addChildTheme" icon="plus"}}{{i18n "admin.customize.theme.add"}}{{/d-button}}
|
||||
</p>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
|
||||
<a class="btn export" target="_blank" href={{downloadUrl}}>{{fa-icon "download"}} {{i18n 'admin.export_json.button_text'}}</a>
|
||||
|
||||
{{d-button action="destroy" label="admin.customize.delete" icon="trash" class="btn-danger"}}
|
||||
</div>
|
||||
@@ -0,0 +1,24 @@
|
||||
{{#unless editingTheme}}
|
||||
<div class='content-list span6'>
|
||||
<h3>{{i18n 'admin.customize.theme.long_title'}}</h3>
|
||||
<ul>
|
||||
{{#each model as |theme|}}
|
||||
<li>
|
||||
{{#link-to 'adminCustomizeThemes.show' theme replace=true}}
|
||||
{{theme.name}}
|
||||
{{#if theme.user_selectable}}
|
||||
{{fa-icon "user"}}
|
||||
{{/if}}
|
||||
{{#if theme.default}}
|
||||
{{fa-icon "asterisk"}}
|
||||
{{/if}}
|
||||
{{/link-to}}
|
||||
</li>
|
||||
{{/each}}
|
||||
</ul>
|
||||
|
||||
{{d-button label="admin.customize.new" icon="plus" action="newTheme" class="btn-primary"}}
|
||||
{{d-button action="importModal" icon="upload" label="admin.customize.import"}}
|
||||
</div>
|
||||
{{/unless}}
|
||||
{{outlet}}
|
||||
@@ -1,7 +1,7 @@
|
||||
<div class='customize'>
|
||||
{{#admin-nav}}
|
||||
{{nav-item route='adminCustomizeThemes' label='admin.customize.theme.title'}}
|
||||
{{nav-item route='adminCustomize.colors' label='admin.customize.colors.title'}}
|
||||
{{nav-item route='adminCustomizeCssHtml' label='admin.customize.css_html.title'}}
|
||||
{{nav-item route='adminSiteText' label='admin.site_text.title'}}
|
||||
{{nav-item route='adminCustomizeEmailTemplates' label='admin.customize.email_templates.title'}}
|
||||
{{nav-item route='adminUserFields' label='admin.user_fields.title'}}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<div>
|
||||
{{#d-modal-body title="admin.customize.colors.select_base.title"}}
|
||||
{{i18n "admin.customize.colors.select_base.description"}}
|
||||
{{combo-box content=model
|
||||
nameProperty="name"
|
||||
value=selectedBaseThemeId
|
||||
valueAttribute="base_scheme_id"}}
|
||||
{{/d-modal-body}}
|
||||
<div class="modal-footer">
|
||||
<button class='btn btn-primary' {{action "selectBase"}}>{{fa-icon 'plus'}}{{i18n 'admin.customize.new'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,27 @@
|
||||
{{#d-modal-body class='upload-selector' title="admin.customize.theme.import_theme"}}
|
||||
<div class="radios">
|
||||
{{radio-button name="upload" id="local" value="local" selection=selection}}
|
||||
<label class="radio" for="local">{{i18n 'upload_selector.from_my_computer'}}</label>
|
||||
{{#if local}}
|
||||
<div class="inputs">
|
||||
<input type="file" id="file-input" accept='.dcstyle.json'><br>
|
||||
<span class="description">{{i18n 'admin.customize.theme.import_file_tip'}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="radios">
|
||||
{{radio-button name="upload" id="remote" value="remote" selection=selection}}
|
||||
<label class="radio" for="remote">{{i18n 'upload_selector.from_the_web'}}</label>
|
||||
{{#if remote}}
|
||||
<div class="inputs">
|
||||
{{input value=uploadUrl placeholder="https://github.com/discourse/discourse/sample_theme"}}
|
||||
<span class="description">{{i18n 'admin.customize.theme.import_web_tip'}}</span>
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/d-modal-body}}
|
||||
|
||||
<div class="modal-footer">
|
||||
{{d-button action="importTheme" disabled=loading class='btn btn-primary' icon='upload' label='admin.customize.import'}}
|
||||
<a href {{action "closeModal"}}>{{i18n 'cancel'}}</a>
|
||||
</div>
|
||||
@@ -0,0 +1,8 @@
|
||||
<div>
|
||||
{{#d-modal-body title="admin.logs.staff_actions.modal_title"}}
|
||||
{{{diff}}}
|
||||
{{/d-modal-body}}
|
||||
<div class="modal-footer">
|
||||
<button class='btn btn-primary' {{action "closeModal"}}>{{i18n 'close'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,29 +0,0 @@
|
||||
<div>
|
||||
<ul class="nav nav-pills">
|
||||
<li class="{{if newSelected 'active'}}">
|
||||
<a href {{action "selectNew"}}>{{i18n 'admin.logs.staff_actions.new_value'}}</a>
|
||||
</li>
|
||||
<li class="{{if previousSelected 'active'}}">
|
||||
<a href {{action "selectPrevious"}}>{{i18n 'admin.logs.staff_actions.previous_value'}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{{#d-modal-body title="admin.logs.staff_actions.modal_title"}}
|
||||
<div class="modal-tab new-tab {{unless newSelected 'invisible'}}">
|
||||
{{#if model.new_value}}
|
||||
{{site-customization-change-details change=model.new_value}}
|
||||
{{else}}
|
||||
{{i18n 'admin.logs.staff_actions.deleted'}}
|
||||
{{/if}}
|
||||
</div>
|
||||
<div class="modal-tab previous-tab {{unless previousSelected 'invisible'}}">
|
||||
{{#if model.previous_value}}
|
||||
{{site-customization-change-details change=model.previous_value}}
|
||||
{{else}}
|
||||
{{i18n 'admin.logs.staff_actions.no_previous'}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/d-modal-body}}
|
||||
<div class="modal-footer">
|
||||
<button class='btn btn-primary' {{action "closeModal"}}>{{i18n 'close'}}</button>
|
||||
</div>
|
||||
</div>
|
||||
Reference in New Issue
Block a user