Version bump

This commit is contained in:
Neil Lalonde 2015-08-13 10:41:37 -04:00
commit fbd5325031
304 changed files with 5119 additions and 2392 deletions

View File

@ -145,7 +145,7 @@ GEM
thor (~> 0.15)
libv8 (3.16.14.7)
listen (0.7.3)
logster (0.8.4.1.pre)
logster (0.8.4.5.pre)
lru_redux (1.1.0)
mail (2.5.4)
mime-types (~> 1.16)

View File

@ -0,0 +1,2 @@
import CustomizationBase from 'admin/adapters/customization-base';
export default CustomizationBase;

View File

@ -0,0 +1,2 @@
import CustomizationBase from 'admin/adapters/customization-base';
export default CustomizationBase;

View File

@ -0,0 +1,2 @@
import CustomizationBase from 'admin/adapters/customization-base';
export default CustomizationBase;

View File

@ -1,13 +1,14 @@
import { bufferedProperty } from 'discourse/mixins/buffered-content';
import UserField from 'admin/models/user-field';
import { bufferedProperty } from 'discourse/mixins/buffered-content';
import { popupAjaxError } from 'discourse/lib/ajax-error';
import { propertyEqual } from 'discourse/lib/computed';
export default Ember.Component.extend(bufferedProperty('userField'), {
editing: Ember.computed.empty('userField.id'),
classNameBindings: [':user-field'],
cantMoveUp: Discourse.computed.propertyEqual('userField', 'firstField'),
cantMoveDown: Discourse.computed.propertyEqual('userField', 'lastField'),
cantMoveUp: propertyEqual('userField', 'firstField'),
cantMoveDown: propertyEqual('userField', 'lastField'),
userFieldsDescription: function() {
return I18n.t('admin.user_fields.description');

View File

@ -0,0 +1,10 @@
export default Ember.Component.extend({
router: function() {
return this.container.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')
});

View File

@ -0,0 +1,9 @@
export default Ember.Component.extend({
classNames: ["item"],
actions: {
remove() {
this.sendAction('removeAction', this.get('member'));
}
}
});

View File

@ -1,20 +1,21 @@
import BufferedContent from 'discourse/mixins/buffered-content';
import ScrollTop from 'discourse/mixins/scroll-top';
import SiteSetting from 'admin/models/site-setting';
import { propertyNotEqual } from 'discourse/lib/computed';
const CustomTypes = ['bool', 'enum', 'list', 'url_list', 'host_list'];
export default Ember.Component.extend(BufferedContent, ScrollTop, {
classNameBindings: [':row', ':setting', 'setting.overridden', 'typeClass'],
content: Ember.computed.alias('setting'),
dirty: Discourse.computed.propertyNotEqual('buffered.value', 'setting.value'),
dirty: propertyNotEqual('buffered.value', 'setting.value'),
validationMessage: null,
preview: function() {
const preview = this.get('setting.preview');
if (preview) {
return new Handlebars.SafeString("<div class='preview'>" +
preview.replace("{{value}}", this.get('buffered.value')) +
preview.replace(/\{\{value\}\}/g, this.get('buffered.value')) +
"</div>");
}
}.property('buffered.value'),

View File

@ -1,20 +1,20 @@
export default Ember.ArrayController.extend({
needs: ["adminBackups"],
status: Em.computed.alias("controllers.adminBackups"),
isOperationRunning: Em.computed.alias("status.isOperationRunning"),
restoreDisabled: Em.computed.alias("status.restoreDisabled"),
isOperationRunning: Ember.computed.alias("status.model.isOperationRunning"),
restoreDisabled: Ember.computed.alias("status.model.restoreDisabled"),
uploadLabel: function() { return I18n.t("admin.backups.upload.label"); }.property(),
restoreTitle: function() {
if (!this.get('status.allowRestore')) {
if (!this.get('status.model.allowRestore')) {
return "admin.backups.operations.restore.is_disabled";
} else if (this.get("status.isOperationRunning")) {
} else if (this.get("status.model.isOperationRunning")) {
return "admin.backups.operations.is_running";
} else {
return "admin.backups.operations.restore.title";
}
}.property("status.{allowRestore,isOperationRunning}"),
}.property("status.model.{allowRestore,isOperationRunning}"),
actions: {

View File

@ -1,5 +1,5 @@
export default Ember.ObjectController.extend({
noOperationIsRunning: Em.computed.not("isOperationRunning"),
rollbackEnabled: Em.computed.and("canRollback", "restoreEnabled", "noOperationIsRunning"),
rollbackDisabled: Em.computed.not("rollbackEnabled")
noOperationIsRunning: Ember.computed.not("model.isOperationRunning"),
rollbackEnabled: Ember.computed.and("model.canRollback", "model.restoreEnabled", "noOperationIsRunning"),
rollbackDisabled: Ember.computed.not("rollbackEnabled")
});

View File

@ -1,5 +1,6 @@
import { popupAjaxError } from 'discourse/lib/ajax-error';
import BufferedContent from 'discourse/mixins/buffered-content';
import { propertyNotEqual } from 'discourse/lib/computed';
export default Ember.ObjectController.extend(BufferedContent, {
needs: ['admin-badges'],
@ -12,7 +13,7 @@ export default Ember.ObjectController.extend(BufferedContent, {
protectedSystemFields: Em.computed.alias('controllers.admin-badges.protectedSystemFields'),
readOnly: Ember.computed.alias('buffered.system'),
showDisplayName: Discourse.computed.propertyNotEqual('name', 'displayName'),
showDisplayName: propertyNotEqual('name', 'displayName'),
canEditDescription: Em.computed.none('buffered.translatedDescription'),
_resetSaving: function() {

View File

@ -1,13 +1,4 @@
/**
This controller supports interface for creating custom CSS skins in Discourse.
@class AdminCustomizeColorsController
@extends Ember.Controller
@namespace Discourse
@module Discourse
**/
export default Ember.ArrayController.extend({
onlyOverridden: false,
baseColorScheme: function() {

View File

@ -0,0 +1,79 @@
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'),
needs: ['adminCustomizeCssHtml'],
undoPreviewUrl: url('/?preview-style='),
defaultStyleUrl: url('/?preview-style=default'),
actions: {
save() {
this.get('model').saveChanges();
},
destroy() {
const self = this;
return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
if (result) {
const model = self.get('model');
model.destroyRecord().then(function() {
self.get('controllers.adminCustomizeCssHtml').get('model').removeObject(model);
self.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);
}
}
});

View File

@ -1,77 +0,0 @@
import showModal from 'discourse/lib/show-modal';
/**
This controller supports interface for creating custom CSS skins in Discourse.
@class AdminCustomizeCssHtmlController
@extends Ember.Controller
@namespace Discourse
@module Discourse
**/
export default Ember.ArrayController.extend({
undoPreviewUrl: function() {
return Discourse.getURL("/?preview-style=");
}.property(),
defaultStyleUrl: function() {
return Discourse.getURL("/?preview-style=default");
}.property(),
actions: {
/**
Create a new customization style
@method newCustomization
**/
newCustomization: function() {
var item = Discourse.SiteCustomization.create({name: I18n.t("admin.customize.new_style")});
this.pushObject(item);
this.set('selectedItem', item);
},
importModal: function() {
showModal('upload-customization');
},
/**
Select a given style
@method selectStyle
@param {Discourse.SiteCustomization} style The style we are selecting
**/
selectStyle: function(style) {
this.set('selectedItem', style);
},
/**
Save the current customization
@method save
**/
save: function() {
this.get('selectedItem').save();
},
/**
Destroy the current customization
@method destroy
**/
destroy: function() {
var _this = this;
return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
var selected;
if (result) {
selected = _this.get('selectedItem');
selected.destroy();
_this.set('selectedItem', null);
return _this.removeObject(selected);
}
});
}
}
});

View File

@ -1,17 +1,12 @@
/**
This controller supports the default interface when you enter the admin section.
import { setting } from 'discourse/lib/computed';
@class AdminDashboardController
@extends Ember.Controller
@namespace Discourse
@module Discourse
**/
// This controller supports the default interface when you enter the admin section.
export default Ember.Controller.extend({
loading: true,
versionCheck: null,
problemsCheckMinutes: 1,
showVersionChecks: Discourse.computed.setting('version_checks'),
showVersionChecks: setting('version_checks'),
foundProblems: function() {
return(Discourse.User.currentProp('admin') && this.get('problems') && this.get('problems').length > 0);

View File

@ -1,21 +1,22 @@
import { popupAjaxError } from 'discourse/lib/ajax-error';
import { propertyEqual } from 'discourse/lib/computed';
export default Em.ObjectController.extend({
needs: ['adminGroupsType'],
disableSave: false,
currentPage: function() {
if (this.get("user_count") === 0) { return 0; }
return Math.floor(this.get("offset") / this.get("limit")) + 1;
}.property("limit", "offset", "user_count"),
if (this.get("model.user_count") === 0) { return 0; }
return Math.floor(this.get("model.offset") / this.get("model.limit")) + 1;
}.property("model.limit", "model.offset", "model.user_count"),
totalPages: function() {
if (this.get("user_count") === 0) { return 0; }
return Math.floor(this.get("user_count") / this.get("limit")) + 1;
}.property("limit", "user_count"),
if (this.get("model.user_count") === 0) { return 0; }
return Math.floor(this.get("model.user_count") / this.get("model.limit")) + 1;
}.property("model.limit", "model.user_count"),
showingFirst: Em.computed.lte("currentPage", 1),
showingLast: Discourse.computed.propertyEqual("currentPage", "totalPages"),
showingLast: propertyEqual("currentPage", "totalPages"),
aliasLevelOptions: function() {
return [
@ -31,7 +32,7 @@ export default Em.ObjectController.extend({
if (this.get("showingLast")) { return; }
const group = this.get("model"),
offset = Math.min(group.get("offset") + group.get("limit"), group.get("user_count"));
offset = Math.min(group.get("offset") + group.get("model.limit"), group.get("user_count"));
group.set("offset", offset);
@ -42,7 +43,7 @@ export default Em.ObjectController.extend({
if (this.get("showingFirst")) { return; }
const group = this.get("model"),
offset = Math.max(group.get("offset") - group.get("limit"), 0);
offset = Math.max(group.get("offset") - group.get("model.limit"), 0);
group.set("offset", offset);
@ -51,7 +52,7 @@ export default Em.ObjectController.extend({
removeMember(member) {
const self = this,
message = I18n.t("admin.groups.delete_member_confirm", { username: member.get("username"), group: this.get("name") });
message = I18n.t("admin.groups.delete_member_confirm", { username: member.get("username"), group: this.get("model.name") });
return bootbox.confirm(message, I18n.t("no_value"), I18n.t("yes_value"), function(confirm) {
if (confirm) {
self.get("model").removeMember(member);
@ -60,10 +61,9 @@ export default Em.ObjectController.extend({
},
addMembers() {
if (Em.isEmpty(this.get("usernames"))) { return; }
this.get("model").addMembers(this.get("usernames"));
// clear the user selector
this.set("usernames", null);
if (Em.isEmpty(this.get("model.usernames"))) { return; }
this.get("model").addMembers(this.get("model.usernames")).catch(popupAjaxError);
this.set("model.usernames", null);
},
save() {

View File

@ -1,20 +1,16 @@
export default Ember.ObjectController.extend({
saving: false,
export default Ember.Controller.extend({
saved: false,
saveDisabled: function() {
if (this.get('saving')) { return true; }
if ((!this.get('allow_blank')) && Ember.isEmpty(this.get('value'))) { return true; }
if (this.get('model.isSaving')) { return true; }
if ((!this.get('allow_blank')) && Ember.isEmpty(this.get('model.value'))) { return true; }
return false;
}.property('saving', 'value'),
}.property('model.iSaving', 'model.value'),
actions: {
saveChanges: function() {
var self = this;
self.setProperties({saving: true, saved: false});
self.get('model').save().then(function () {
self.setProperties({saving: false, saved: true});
});
saveChanges() {
const model = this.get('model');
model.save(model.getProperties('value')).then(() => this.set('saved', true));
}
}
});

View File

@ -1,15 +1,16 @@
import ObjectController from 'discourse/controllers/object';
import CanCheckEmails from 'discourse/mixins/can-check-emails';
import { propertyNotEqual, setting } from 'discourse/lib/computed';
export default ObjectController.extend(CanCheckEmails, {
editingTitle: false,
originalPrimaryGroupId: null,
availableGroups: null,
showApproval: Discourse.computed.setting('must_approve_users'),
showBadges: Discourse.computed.setting('enable_badges'),
showApproval: setting('must_approve_users'),
showBadges: setting('enable_badges'),
primaryGroupDirty: Discourse.computed.propertyNotEqual('originalPrimaryGroupId', 'model.primary_group_id'),
primaryGroupDirty: propertyNotEqual('originalPrimaryGroupId', 'model.primary_group_id'),
automaticGroups: function() {
return this.get("model.automaticGroups").map((g) => g.name).join(", ");

View File

@ -1,3 +1,5 @@
import { i18n } from 'discourse/lib/computed';
export default Ember.ArrayController.extend({
query: null,
showEmails: false,
@ -9,7 +11,7 @@ export default Ember.ArrayController.extend({
queryPending: Em.computed.equal('query', 'pending'),
queryHasApproval: Em.computed.or('queryNew', 'queryPending'),
showApproval: Em.computed.and('siteSettings.must_approve_users', 'queryHasApproval'),
searchHint: Discourse.computed.i18n('search_hint'),
searchHint: i18n('search_hint'),
hasSelection: Em.computed.gt('selectedCount', 0),
selectedCount: function() {

View File

@ -1,3 +1,4 @@
import { propertyNotEqual } from 'discourse/lib/computed';
import { popupAjaxError } from 'discourse/lib/ajax-error';
const AdminUser = Discourse.User.extend({
@ -144,7 +145,7 @@ const AdminUser = Discourse.User.extend({
this.set('originalTrustLevel', this.get('trust_level'));
},
dirty: Discourse.computed.propertyNotEqual('originalTrustLevel', 'trustLevel.id'),
dirty: propertyNotEqual('originalTrustLevel', 'trustLevel.id'),
saveTrustLevel() {
return Discourse.ajax("/admin/users/" + this.id + "/trust_level", {

View File

@ -1,7 +1,8 @@
import round from "discourse/lib/round";
import { fmt } from 'discourse/lib/computed';
const Report = Discourse.Model.extend({
reportUrl: Discourse.computed.fmt("type", "/admin/reports/%@"),
reportUrl: fmt("type", "/admin/reports/%@"),
valueAt(numDaysAgo) {
if (this.data) {
@ -26,7 +27,7 @@ const Report = Discourse.Model.extend({
count++;
}
});
if (this.get("method") === "average") { sum /= count; }
if (this.get("method") === "average" && count > 0) { sum /= count; }
return round(sum, -2);
}
},

View File

@ -0,0 +1,31 @@
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;

View File

@ -0,0 +1,2 @@
import RestModel from 'discourse/models/rest';
export default RestModel.extend();

View File

@ -0,0 +1,8 @@
import RestModel from 'discourse/models/rest';
export default RestModel.extend({
markdown: Em.computed.equal('format', 'markdown'),
plainText: Em.computed.equal('format', 'plain'),
html: Em.computed.equal('format', 'html'),
css: Em.computed.equal('format', 'css'),
});

View File

@ -1,116 +0,0 @@
/**
Our data model for interacting with site customizations.
@class SiteCustomization
@extends Discourse.Model
@namespace Discourse
@module Discourse
**/
Discourse.SiteCustomization = Discourse.Model.extend({
trackedProperties: [
'enabled', 'name',
'stylesheet', 'header', 'top', 'footer',
'mobile_stylesheet', 'mobile_header', 'mobile_top', 'mobile_footer',
'head_tag', 'body_tag'
],
description: function() {
return "" + this.name + (this.enabled ? ' (*)' : '');
}.property('selected', 'name', 'enabled'),
changed: function() {
var self = this;
if (!this.originals) { return false; }
var changed = _.some(this.trackedProperties, function (p) {
return self.originals[p] !== self.get(p);
});
if (changed) { this.set('savingStatus', ''); }
return changed;
}.property('enabled', 'name', 'originals',
'stylesheet', 'header', 'top', 'footer',
'mobile_stylesheet', 'mobile_header', 'mobile_top', 'mobile_footer',
'head_tag', 'body_tag'),
startTrackingChanges: function() {
var self = this;
var originals = {};
_.each(this.trackedProperties, function (prop) {
originals[prop] = self.get(prop);
});
this.set('originals', originals);
}.on('init'),
previewUrl: function() { return Discourse.getURL("/?preview-style=" + this.get('key')); }.property('key'),
disableSave: function() { return !this.get('changed') || this.get('saving'); }.property('changed'),
save: function() {
this.set('savingStatus', I18n.t('saving'));
this.set('saving',true);
var data = {
name: this.name,
enabled: this.enabled,
stylesheet: this.stylesheet,
header: this.header,
top: this.top,
footer: this.footer,
mobile_stylesheet: this.mobile_stylesheet,
mobile_header: this.mobile_header,
mobile_top: this.mobile_top,
mobile_footer: this.mobile_footer,
head_tag: this.head_tag,
body_tag: this.body_tag
};
var siteCustomization = this;
return Discourse.ajax("/admin/site_customizations" + (this.id ? '/' + this.id : ''), {
data: { site_customization: data },
type: this.id ? 'PUT' : 'POST'
}).then(function (result) {
if (!siteCustomization.id) {
siteCustomization.set('id', result.id);
siteCustomization.set('key', result.key);
}
siteCustomization.set('savingStatus', I18n.t('saved'));
siteCustomization.set('saving',false);
siteCustomization.startTrackingChanges();
return siteCustomization;
});
},
destroy: function() {
if (!this.id) return;
return Discourse.ajax("/admin/site_customizations/" + this.id, { type: 'DELETE' });
},
download_url: function() {
return Discourse.getURL('/admin/site_customizations/' + this.id);
}.property('id')
});
var SiteCustomizations = Ember.ArrayProxy.extend({
selectedItemChanged: function() {
var selected = this.get('selectedItem');
_.each(this.get('content'), function (i) {
i.set('selected', selected === i);
});
}.observes('selectedItem')
});
Discourse.SiteCustomization.reopenClass({
findAll: function() {
return Discourse.ajax("/admin/site_customizations").then(function (data) {
var content = [];
if (data) {
content = data.site_customizations.map(function(c) {
return Discourse.SiteCustomization.create(c);
});
}
return SiteCustomizations.create({ content: content });
});
}
});

View File

@ -1,21 +0,0 @@
Discourse.SiteText = Discourse.Model.extend({
markdown: Em.computed.equal('format', 'markdown'),
plainText: Em.computed.equal('format', 'plain'),
html: Em.computed.equal('format', 'html'),
css: Em.computed.equal('format', 'css'),
save: function() {
return Discourse.ajax("/admin/customize/site_text/" + this.get('text_type'), {
type: 'PUT',
data: {value: this.get('value')}
});
}
});
Discourse.SiteText.reopenClass({
find: function(type) {
return Discourse.ajax("/admin/customize/site_text/" + type).then(function (data) {
return Discourse.SiteText.create(data.site_text);
});
}
});

View File

@ -1,11 +0,0 @@
Discourse.SiteTextType = Discourse.Model.extend();
Discourse.SiteTextType.reopenClass({
findAll: function() {
return Discourse.ajax("/admin/customize/site_text_types").then(function(data) {
return data.map(function(ct) {
return Discourse.SiteTextType.create(ct);
});
});
}
});

View File

@ -1,9 +1,10 @@
import RestModel from 'discourse/models/rest';
import { i18n } from 'discourse/lib/computed';
const UserField = RestModel.extend();
const UserFieldType = Ember.Object.extend({
name: Discourse.computed.i18n('id', 'admin.user_fields.field_types.%@')
name: i18n('id', 'admin.user_fields.field_types.%@')
});
UserField.reopenClass({

View File

@ -0,0 +1,5 @@
export default Ember.Route.extend({
model() {
return Discourse.ApiKey.find();
}
});

View File

@ -0,0 +1,5 @@
export default Ember.Route.extend({
model() {
return Discourse.Backup.find();
}
});

View File

@ -1,15 +1,15 @@
Discourse.AdminBackupsLogsRoute = Discourse.Route.extend({
export default Ember.Route.extend({
// since the logs are pushed via the message bus
// we only want to preload them (hence the beforeModel hook)
beforeModel: function() {
var logsController = this.controllerFor("adminBackupsLogs");
beforeModel() {
const logsController = this.controllerFor("adminBackupsLogs");
// preload the logs if any
PreloadStore.getAndRemove("logs").then(function (preloadedLogs) {
if (preloadedLogs && preloadedLogs.length) {
// we need to filter out message like: "[SUCCESS]"
// and convert POJOs to Ember Objects
var logs = _.chain(preloadedLogs)
const logs = _.chain(preloadedLogs)
.reject(function (log) { return log.message.length === 0 || log.message[0] === "["; })
.map(function (log) { return Em.Object.create(log); })
.value();
@ -18,6 +18,6 @@ Discourse.AdminBackupsLogsRoute = Discourse.Route.extend({
});
},
setupController: function() { /* prevent default behavior */ }
setupController() { /* prevent default behavior */ }
});

View File

@ -10,14 +10,14 @@ export default Discourse.Route.extend({
_processLogMessage(log) {
if (log.message === "[STARTED]") {
this.controllerFor("adminBackups").set("isOperationRunning", true);
this.controllerFor("adminBackups").set("model.isOperationRunning", true);
this.controllerFor("adminBackupsLogs").clear();
} else if (log.message === "[FAILED]") {
this.controllerFor("adminBackups").set("isOperationRunning", false);
this.controllerFor("adminBackups").set("model.isOperationRunning", false);
bootbox.alert(I18n.t("admin.backups.operations.failed", { operation: log.operation }));
} else if (log.message === "[SUCCESS]") {
Discourse.User.currentProp("hideReadOnlyAlert", false);
this.controllerFor("adminBackups").set("isOperationRunning", false);
this.controllerFor("adminBackups").set("model.isOperationRunning", false);
if (log.operation === "restore") {
// redirect to homepage when the restore is done (session might be lost)
window.location.pathname = Discourse.getURL("/");
@ -30,7 +30,7 @@ export default Discourse.Route.extend({
model() {
return PreloadStore.getAndRemove("operations_status", function() {
return Discourse.ajax("/admin/backups/status.json");
}).then(function (status) {
}).then(status => {
return Discourse.BackupStatus.create({
isOperationRunning: status.is_operation_running,
canRollback: status.can_rollback,
@ -82,7 +82,7 @@ export default Discourse.Route.extend({
Discourse.User.currentProp("hideReadOnlyAlert", true);
backup.restore().then(function() {
self.controllerFor("adminBackupsLogs").clear();
self.modelFor("adminBackups").set("isOperationRunning", true);
self.modelFor("adminBackups").set("model.isOperationRunning", true);
self.transitionTo("admin.backups.logs");
});
}
@ -99,7 +99,7 @@ export default Discourse.Route.extend({
function(confirmed) {
if (confirmed) {
Discourse.Backup.cancel().then(function() {
self.controllerFor("adminBackups").set("isOperationRunning", false);
self.controllerFor("adminBackups").set("model.isOperationRunning", false);
});
}
}

View File

@ -0,0 +1,12 @@
export default Ember.Route.extend({
model() {
return Discourse.ColorScheme.findAll();
},
deactivate() {
this._super();
this.controllerFor('adminCustomizeColors').set('selectedItem', null);
},
});

View File

@ -0,0 +1,11 @@
export default Ember.Route.extend({
model(params) {
const all = this.modelFor('adminCustomizeCssHtml');
const model = all.findProperty('id', parseInt(params.site_customization_id));
return model ? { model, section: params.section } : this.replaceWith('adminCustomizeCssHtml.index');
},
setupController(controller, hash) {
controller.setProperties(hash);
}
});

View File

@ -0,0 +1,26 @@
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);
}
}
});

View File

@ -0,0 +1,5 @@
export default Ember.Route.extend({
beforeModel() {
this.replaceWith('adminCustomize.colors');
}
});

View File

@ -11,9 +11,7 @@ export default Discourse.Route.extend({
setupController: function(controller, model) {
controller.set("model", model);
// clear the user selector
controller.set("usernames", null);
// load the members of the group
controller.set("model.usernames", null);
model.findMembers();
}

View File

@ -16,8 +16,12 @@ export default {
this.resource('adminCustomize', { path: '/customize' } ,function() {
this.route('colors');
this.route('css_html');
this.resource('adminSiteText', { path: '/site_text' }, function() {
this.resource('adminCustomizeCssHtml', { path: 'css_html' }, function() {
this.route('show', {path: '/:site_customization_id/:section'});
});
this.resource('adminSiteText', { path: '/site_texts' }, function() {
this.route('edit', {path: '/:text_type'});
});
this.resource('adminUserFields', { path: '/user_fields' });

View File

@ -1,5 +1,5 @@
export default Discourse.Route.extend({
model: function(params) {
return Discourse.SiteText.find(params.text_type);
model(params) {
return this.store.find('site-text', params.text_type);
}
});

View File

@ -1,5 +1,5 @@
export default Discourse.Route.extend({
model: function() {
return Discourse.SiteTextType.findAll();
model() {
return this.store.findAll('site-text-type');
}
});

View File

@ -0,0 +1,16 @@
export default Discourse.Route.extend({
titleToken() {
return I18n.t('admin_title');
},
activate() {
this.controllerFor("application").setProperties({
showTop: false,
showFooter: false,
});
},
deactivate() {
this.controllerFor("application").set("showTop", true);
}
});

View File

@ -1,15 +0,0 @@
/**
Handles routes related to api
@class AdminApiRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminApiRoute = Discourse.Route.extend({
model: function() {
return Discourse.ApiKey.find();
}
});

View File

@ -1,7 +0,0 @@
Discourse.AdminBackupsIndexRoute = Discourse.Route.extend({
model: function() {
return Discourse.Backup.find();
}
});

View File

@ -1,20 +0,0 @@
/**
Handles routes related to colors customization
@class AdminCustomizeColorsRoute
@extends Discourse.Route
@namespace Discourse
@module Discourse
**/
Discourse.AdminCustomizeColorsRoute = Discourse.Route.extend({
model: function() {
return Discourse.ColorScheme.findAll();
},
deactivate: function() {
this._super();
this.controllerFor('adminCustomizeColors').set('selectedItem', null);
},
});

View File

@ -1,5 +0,0 @@
Discourse.AdminCustomizeCssHtmlRoute = Discourse.Route.extend({
model: function() {
return Discourse.SiteCustomization.findAll();
}
});

View File

@ -1,5 +0,0 @@
Discourse.AdminCustomizeIndexRoute = Discourse.Route.extend({
beforeModel: function() {
this.replaceWith('adminCustomize.colors');
}
});

View File

@ -1,5 +0,0 @@
Discourse.AdminRoute = Discourse.Route.extend({
titleToken: function() {
return I18n.t('admin_title');
}
});

View File

@ -6,7 +6,7 @@
</ul>
</div>
<div class="pull-right">
{{#if canRollback}}
{{#if model.canRollback}}
{{d-button action="rollback"
class="btn-rollback"
label="admin.backups.operations.rollback.label"
@ -14,7 +14,7 @@
icon="ambulance"
disabled=rollbackDisabled}}
{{/if}}
{{#if isOperationRunning}}
{{#if model.isOperationRunning}}
{{d-button action="cancelOperation"
class="btn-danger"
title="admin.backups.operations.cancel.title"

View File

@ -6,9 +6,9 @@
<div class="pull-right">
{{resumable-upload target="/admin/backups/upload" success="uploadSuccess" error="uploadError" uploadText=uploadLabel title="admin.backups.upload.title"}}
{{#if site.isReadOnly}}
{{d-button icon="eye" action="toggleReadOnlyMode" disabled=isOperationRunning title="admin.backups.read_only.disable.title" label="admin.backups.read_only.disable.label"}}
{{d-button icon="eye" action="toggleReadOnlyMode" disabled=model.isOperationRunning title="admin.backups.read_only.disable.title" label="admin.backups.read_only.disable.label"}}
{{else}}
{{d-button icon="eye" action="toggleReadOnlyMode" disabled=isOperationRunning title="admin.backups.read_only.enable.title" label="admin.backups.read_only.enable.label"}}
{{d-button icon="eye" action="toggleReadOnlyMode" disabled=model.isOperationRunning title="admin.backups.read_only.enable.title" label="admin.backups.read_only.enable.label"}}
{{/if}}
</div>
</th>
@ -20,12 +20,12 @@
<td>
<div class="pull-right">
<a {{bind-attr href="backup.link"}} class="btn download" title="{{i18n 'admin.backups.operations.download.title'}}">{{fa-icon "download"}}{{i18n 'admin.backups.operations.download.label'}}</a>
{{#if isOperationRunning}}
{{#if model.isOperationRunning}}
{{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger" disabled="true" title="admin.backups.operations.is_running"}}
{{d-button icon="play" action="startRestore" actionParam=backup disabled=restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}}
{{d-button icon="play" action="startRestore" actionParam=backup disabled=model.restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}}
{{else}}
{{d-button icon="trash-o" action="destroyBackup" actionParam=backup class="btn-danger" title="admin.backups.operations.destroy.title"}}
{{d-button icon="play" action="startRestore" actionParam=backup disabled=restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}}
{{d-button icon="play" action="startRestore" actionParam=backup disabled=model.restoreDisabled title=restoreTitle label="admin.backups.operations.restore.label"}}
{{/if}}
</div>
</td>

View File

@ -0,0 +1,5 @@
<li>
<a href="/admin/customize/css_html/{{customization.id}}/css" class="{{if active 'active'}}">
{{customization.description}}
</a>
</li>

View File

@ -0,0 +1 @@
<a href={{member.adminPath}}>{{avatar member imageSize="small"}}</a> {{member.username}} {{#unless automatic}}<a class='remove' {{action "remove"}}>{{fa-icon "times"}}</a>{{/unless}}

View File

@ -0,0 +1 @@
<p class="about">{{i18n 'admin.customize.about'}}</p>

View File

@ -0,0 +1,75 @@
<div class="current-style {{if maximized 'maximized'}}">
<div class='wrapper'>
{{text-field class="style-name" value=model.name}}
<a class="btn export" download 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"}}&nbsp;{{i18n 'admin.customize.head_tag.text'}}
{{/link-to}}
</li>
<li>
{{#link-to 'adminCustomizeCssHtml.show' model.id 'body-tag'}}
{{fa-icon "file-text-o"}}&nbsp;{{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 {{bind-attr class="mobile:active"}} {{action "toggleMobile"}}>{{fa-icon "mobile"}}</a>
</li>
<li class='toggle-maximize'>
<a {{action "toggleMaximize"}}>
{{fa-icon-bound maximizeIcon}}
</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>

View File

@ -0,0 +1,13 @@
<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}}

View File

@ -1,6 +1,6 @@
{{#admin-nav}}
{{nav-item route='adminCustomize.colors' label='admin.customize.colors.title'}}
{{nav-item route='adminCustomize.css_html' label='admin.customize.css_html.title'}}
{{nav-item route='adminCustomizeCssHtml.index' label='admin.customize.css_html.title'}}
{{nav-item route='adminSiteText' label='admin.site_text.title'}}
{{nav-item route='adminUserFields' label='admin.user_fields.title'}}
{{nav-item route='adminEmojis' label='admin.emoji.title'}}

View File

@ -1,84 +0,0 @@
<div class='content-list span6'>
<h3>{{i18n 'admin.customize.css_html.long_title'}}</h3>
<ul>
{{#each style in model}}
<li><a {{action "selectStyle" style}} {{bind-attr class="style.selected:active"}}>{{style.description}}</a></li>
{{/each}}
</ul>
<button {{action "newCustomization"}} class='btn'>
{{fa-icon "plus"}}{{i18n 'admin.customize.new'}}
</button>
{{d-button action="importModal" icon="upload" label="admin.customize.import"}}
</div>
{{#if selectedItem}}
<div {{bind-attr class=":current-style view.maximized:maximized"}}>
<div class='wrapper'>
{{text-field class="style-name" value=selectedItem.name}}
<a class="btn export" download target="_blank" href={{selectedItem.download_url}}>{{fa-icon "download"}} {{i18n 'admin.export_json.button_text'}}</a>
<div class='admin-controls'>
<ul class="nav nav-pills">
{{#if view.mobile}}
<li><a {{bind-attr class="view.mobileStylesheetActive:active"}} {{action "select" "mobile_stylesheet" target="view"}}>{{fa-icon "mobile"}}&nbsp;{{i18n 'admin.customize.css'}}</a></li>
<li><a {{bind-attr class="view.mobileHeaderActive:active"}} {{action "select" "mobile_header" target="view"}}>{{fa-icon "mobile"}}&nbsp;{{i18n 'admin.customize.header'}}</a></li>
<li><a {{bind-attr class="view.mobileTopActive:active"}} {{action "select" "mobile_top" target="view"}}>{{fa-icon "mobile"}}&nbsp;{{i18n 'admin.customize.top'}}</a></li>
<li><a {{bind-attr class="view.mobileFooterActive:active"}} {{action "select" "mobile_footer" target="view"}}>{{fa-icon "mobile"}}&nbsp;{{i18n 'admin.customize.footer'}}</a></li>
{{else}}
<li><a {{bind-attr class="view.stylesheetActive:active"}} {{action "select" "stylesheet" target="view"}}>{{i18n 'admin.customize.css'}}</a></li>
<li><a {{bind-attr class="view.headerActive:active"}} {{action "select" "header" target="view"}}>{{i18n 'admin.customize.header'}}</a></li>
<li><a {{bind-attr class="view.topActive:active"}} {{action "select" "top" target="view"}}>{{i18n 'admin.customize.top'}}</a></li>
<li><a {{bind-attr class="view.footerActive:active"}} {{action "select" "footer" target="view"}}>{{i18n 'admin.customize.footer'}}</a></li>
<li><a {{bind-attr class="view.headTagActive:active"}} {{action "select" "head_tag" target="view"}} title="{{i18n 'admin.customize.head_tag.title'}}">{{fa-icon "file-text-o"}}&nbsp;{{i18n 'admin.customize.head_tag.text'}}</a></li>
<li><a {{bind-attr class="view.bodyTagActive:active"}} {{action "select" "body_tag" target="view"}} title="{{i18n 'admin.customize.body_tag.title'}}">{{fa-icon "file-text-o"}}&nbsp;{{i18n 'admin.customize.body_tag.text'}}</a></li>
{{/if}}
<li class='toggle-mobile'>
<a {{bind-attr class="view.mobile:active"}} {{action "toggleMobile" target="view"}}>{{fa-icon "mobile"}}</a>
</li>
<li class='toggle-maximize'>
<a {{action "toggleMaximize" target="view"}}>
{{#if view.maximized}}
{{fa-icon "compress"}}
{{else}}
{{fa-icon "expand"}}
{{/if}}
</a>
</li>
</ul>
</div>
<div class="admin-container">
{{#if view.stylesheetActive}}{{ace-editor content=selectedItem.stylesheet mode="scss"}}{{/if}}
{{#if view.headerActive}}{{ace-editor content=selectedItem.header mode="html"}}{{/if}}
{{#if view.topActive}}{{ace-editor content=selectedItem.top mode="html"}}{{/if}}
{{#if view.footerActive}}{{ace-editor content=selectedItem.footer mode="html"}}{{/if}}
{{#if view.headTagActive}}{{ace-editor content=selectedItem.head_tag mode="html"}}{{/if}}
{{#if view.bodyTagActive}}{{ace-editor content=selectedItem.body_tag mode="html"}}{{/if}}
{{#if view.mobileStylesheetActive}}{{ace-editor content=selectedItem.mobile_stylesheet mode="scss"}}{{/if}}
{{#if view.mobileHeaderActive}}{{ace-editor content=selectedItem.mobile_header mode="html"}}{{/if}}
{{#if view.mobileTopActive}}{{ace-editor content=selectedItem.mobile_top mode="html"}}{{/if}}
{{#if view.mobileFooterActive}}{{ace-editor content=selectedItem.mobile_footer mode="html"}}{{/if}}
</div>
<div class='admin-footer'>
<div class='status-actions'>
<span>{{i18n 'admin.customize.enabled'}} {{input type="checkbox" checked=selectedItem.enabled}}</span>
{{#unless selectedItem.changed}}
<a class='preview-link' {{bind-attr href="selectedItem.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'>
<button {{action "save"}} {{bind-attr disabled="selectedItem.disableSave"}} class='btn'>{{i18n 'admin.customize.save'}}</button>
<span class='saving'>{{selectedItem.savingStatus}}</span>
<a {{action "destroy"}} class='delete-link'>{{i18n 'admin.customize.delete'}}</a>
</div>
</div>
</div>
</div>
{{else}}
<p class="about">{{i18n 'admin.customize.about'}}</p>
{{/if}}

View File

@ -1,31 +1,33 @@
<form class="form-horizontal">
<div>
{{#if automatic}}
<h3>{{name}}</h3>
{{#if model.automatic}}
<h3>{{model.name}}</h3>
{{else}}
<label for="name">{{i18n 'admin.groups.name'}}</label>
{{text-field name="name" value=name placeholderKey="admin.groups.name_placeholder"}}
{{text-field name="name" value=model.name placeholderKey="admin.groups.name_placeholder"}}
{{/if}}
</div>
{{#if id}}
{{#if model.id}}
<div>
<label>{{i18n 'admin.groups.group_members'}} ({{user_count}})</label>
<label>{{i18n 'admin.groups.group_members'}} ({{model.user_count}})</label>
<div>
<a {{bind-attr class=":previous showingFirst:disabled"}} {{action "previous"}}>{{fa-icon "fast-backward"}}</a>
{{currentPage}}/{{totalPages}}
<a {{bind-attr class=":next showingLast:disabled"}} {{action "next"}}>{{fa-icon "fast-forward"}}</a>
</div>
<div class="ac-wrap clearfix">
{{each member in members itemView="group-member"}}
{{#each model.members as |member|}}
{{group-member member=member automatic=model.automatic removeAction="removeMember"}}
{{/each}}
</div>
</div>
{{#unless automatic}}
{{#unless model.automatic}}
<div>
<label for="user-selector">{{i18n 'admin.groups.add_members'}}</label>
{{user-selector usernames=usernames placeholderKey="admin.groups.selector_placeholder" id="user-selector"}}
{{user-selector usernames=model.usernames placeholderKey="admin.groups.selector_placeholder" id="user-selector"}}
<button {{action "addMembers"}} class='btn add'>{{fa-icon "plus"}} {{i18n 'admin.groups.add'}}</button>
</div>
{{/unless}}
@ -33,15 +35,15 @@
<div>
<label>
{{input type="checkbox" checked=visible}}
{{input type="checkbox" checked=model.visible}}
{{i18n 'groups.visible'}}
</label>
</div>
{{#unless automatic}}
{{#unless model.automatic}}
<div>
<label for="primary_group">
{{input type="checkbox" checked=primary_group}}
{{input type="checkbox" checked=model.primary_group}}
{{i18n 'admin.groups.primary_group'}}
</label>
</div>
@ -49,15 +51,15 @@
<div>
<label for="alias">{{i18n 'groups.alias_levels.title'}}</label>
{{combo-box name="alias" valueAttribute="value" value=alias_level content=aliasLevelOptions}}
{{combo-box name="alias" valueAttribute="value" value=model.alias_level content=aliasLevelOptions}}
</div>
{{#unless automatic}}
{{#unless model.automatic}}
<div>
<label for="automatic_membership">{{i18n 'admin.groups.automatic_membership_email_domains'}}</label>
{{list-setting name="automatic_membership" settingValue=emailDomains}}
{{list-setting name="automatic_membership" settingValue=model.emailDomains}}
<label>
{{input type="checkbox" checked=automatic_membership_retroactive}}
{{input type="checkbox" checked=model.automatic_membership_retroactive}}
{{i18n 'admin.groups.automatic_membership_retroactive'}}
</label>
</div>
@ -66,13 +68,13 @@
<label for="title">
{{i18n 'admin.groups.default_title'}}
</label>
{{input value=title}}
{{input value=model.title}}
</div>
{{/unless}}
<div class='buttons'>
<button {{action "save"}} {{bind-attr disabled="disableSave"}} class='btn btn-primary'>{{i18n 'admin.customize.save'}}</button>
{{#unless automatic}}
{{#unless model.automatic}}
<button {{action "destroy"}} class='btn btn-danger'>{{fa-icon "trash-o"}}{{i18n 'admin.customize.delete'}}</button>
{{/unless}}
</div>

View File

@ -1 +0,0 @@
<a href='{{unbound member.adminPath}}'>{{avatar member imageSize="small"}}</a> {{member.username}} {{#unless automatic}}<a class='remove' {{action "removeMember" member}}>{{fa-icon "times"}}</a>{{/unless}}

View File

@ -0,0 +1,26 @@
<h3>{{model.title}}</h3>
<p class='description'>{{model.description}}</p>
{{#if model.markdown}}
{{pagedown-editor value=model.value}}
{{/if}}
{{#if model.plainText}}
{{textarea value=model.value class="plain"}}
{{/if}}
{{#if model.html}}
{{ace-editor content=model.value mode="html"}}
{{/if}}
{{#if model.css}}
{{ace-editor content=model.value mode="css"}}
{{/if}}
<div class='controls'>
<button class='btn' {{action "saveChanges"}} disabled={{saveDisabled}}>
{{#if model.isSaving}}
{{i18n 'saving'}}
{{else}}
{{i18n 'save'}}
{{/if}}
</button>
{{#if saved}}{{i18n 'saved'}}{{/if}}
</div>

View File

@ -1,26 +0,0 @@
<h3>{{title}}</h3>
<p class='description'>{{description}}</p>
{{#if markdown}}
{{pagedown-editor value=value}}
{{/if}}
{{#if plainText}}
{{textarea value=value class="plain"}}
{{/if}}
{{#if html}}
{{ace-editor content=value mode="html"}}
{{/if}}
{{#if css}}
{{ace-editor content=value mode="css"}}
{{/if}}
<div class='controls'>
<button class='btn' {{action "saveChanges"}} {{bind-attr disabled="saveDisabled"}}>
{{#if saving}}
{{i18n 'saving'}}
{{else}}
{{i18n 'save'}}
{{/if}}
</button>
{{#if saved}}{{i18n 'saved'}}{{/if}}
</div>

View File

@ -5,20 +5,20 @@ export default Discourse.View.extend({
_initialize: function() { this._reset(); }.on("init"),
_reset: function() {
_reset() {
this.setProperties({ formattedLogs: "", index: 0 });
},
_updateFormattedLogs: Discourse.debounce(function() {
var logs = this.get("controller.model");
const logs = this.get("controller.model");
if (logs.length === 0) {
this._reset(); // reset the cached logs whenever the model is reset
} else {
// do the log formatting only once for HELLish performance
var formattedLogs = this.get("formattedLogs");
for (var i = this.get("index"), length = logs.length; i < length; i++) {
var date = logs[i].get("timestamp"),
message = Handlebars.Utils.escapeExpression(logs[i].get("message"));
let formattedLogs = this.get("formattedLogs");
for (let i = this.get("index"), length = logs.length; i < length; i++) {
const date = logs[i].get("timestamp"),
message = Handlebars.Utils.escapeExpression(logs[i].get("message"));
formattedLogs += "[" + date + "] " + message + "\n";
}
// update the formatted logs & cache index
@ -28,8 +28,8 @@ export default Discourse.View.extend({
}
}, 150).observes("controller.model.@each"),
render: function(buffer) {
var formattedLogs = this.get("formattedLogs");
render(buffer) {
const formattedLogs = this.get("formattedLogs");
if (formattedLogs && formattedLogs.length > 0) {
buffer.push("<pre>");
buffer.push(formattedLogs);
@ -38,13 +38,13 @@ export default Discourse.View.extend({
buffer.push("<p>" + I18n.t("admin.backups.logs.none") + "</p>");
}
// add a loading indicator
if (this.get("controller.status.isOperationRunning")) {
if (this.get("controller.status.model.isOperationRunning")) {
buffer.push(renderSpinner('small'));
}
},
_forceScrollToBottom: function() {
var $div = this.$()[0];
const $div = this.$()[0];
$div.scrollTop = $div.scrollHeight;
}.on("didInsertElement")

View File

@ -0,0 +1,18 @@
/*global Mousetrap:true */
export default Ember.View.extend({
classNames: ['customize'],
_init: function() {
var controller = this.get('controller');
Mousetrap.bindGlobal('mod+s', function() {
controller.send("save");
return false;
});
}.on("didInsertElement"),
_cleanUp: function() {
Mousetrap.unbindGlobal('mod+s');
}.on("willDestroyElement")
});

View File

@ -1,68 +0,0 @@
/*global Mousetrap:true */
/**
A view to handle site customizations
@class AdminCustomizeView
@extends Discourse.View
@namespace Discourse
@module Discourse
**/
Discourse.AdminCustomizeView = Discourse.View.extend({
templateName: 'admin/templates/customize',
classNames: ['customize'],
selected: 'stylesheet',
mobile: false,
stylesheetActive: Em.computed.equal('selected', 'stylesheet'),
headerActive: Em.computed.equal('selected', 'header'),
topActive: Em.computed.equal('selected', 'top'),
footerActive: Em.computed.equal('selected', 'footer'),
headTagActive: Em.computed.equal('selected', 'head_tag'),
bodyTagActive: Em.computed.equal('selected', 'body_tag'),
mobileStylesheetActive: Em.computed.equal('selected', 'mobile_stylesheet'),
mobileHeaderActive: Em.computed.equal('selected', 'mobile_header'),
mobileTopActive: Em.computed.equal('selected', 'mobile_top'),
mobileFooterActive: Em.computed.equal('selected', 'mobile_footer'),
actions: {
toggleMobile: function() {
// auto-select best tab
var tab = this.get("selected");
if (/_tag$/.test(tab)) { tab = "stylesheet"; }
if (this.get("mobile")) { tab = tab.replace("mobile_", ""); }
else { tab = "mobile_" + tab; }
this.set("selected", tab);
// toggle mobile
this.toggleProperty("mobile");
},
select: function(tab) {
this.set('selected', tab);
},
toggleMaximize: function() {
this.set("maximized", !this.get("maximized"));
Em.run.scheduleOnce('afterRender', this, function(){
$('.ace-wrapper').each(function(){
$(this).data("editor").resize();
});
});
},
},
_init: function() {
var controller = this.get('controller');
Mousetrap.bindGlobal('mod+s', function() {
controller.send("save");
return false;
});
}.on("didInsertElement"),
_cleanUp: function() {
Mousetrap.unbindGlobal('mod+s');
}.on("willDestroyElement")
});

View File

@ -1,4 +0,0 @@
export default Discourse.View.extend({
classNames: ["item"],
templateName: "admin/templates/group_member"
});

View File

@ -140,3 +140,18 @@ window.Discourse = Ember.Application.createWithMixins(Discourse.Ajax, {
// TODO: Remove this, it is in for backwards compatibiltiy with plugins
Discourse.HasCurrentUser = {};
function proxyDep(propName, moduleFunc, msg) {
if (Discourse.hasOwnProperty(propName)) { return; }
Object.defineProperty(Discourse, propName, {
get: function() {
msg = msg || "import the module";
Ember.warn("DEPRECATION: `Discourse." + propName + "` is deprecated, " + msg + ".");
return moduleFunc();
}
});
}
proxyDep('computed', function() { return require('discourse/lib/computed') });
proxyDep('Formatter', function() { return require('discourse/lib/formatter') });
proxyDep('PageTracker', function() { return require('discourse/lib/page-tracker').default });

View File

@ -1,4 +1,4 @@
const ADMIN_MODELS = ['plugin'];
const ADMIN_MODELS = ['plugin', 'site-customization'];
export function Result(payload, responseJson) {
this.payload = payload;

View File

@ -1,5 +1,6 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse/helpers/fa-icon';
import { autoUpdatingRelativeAge } from 'discourse/lib/formatter';
export default Ember.Component.extend(StringBuffer, {
tagName: 'section',
@ -57,7 +58,7 @@ export default Ember.Component.extend(StringBuffer, {
buffer.push("<div class='post-action'>" +
iconHTML('fa-trash-o') + '&nbsp;' +
Discourse.Utilities.tinyAvatar(post.get('postDeletedBy.avatar_template'), {title: post.get('postDeletedBy.username')}) +
Discourse.Formatter.autoUpdatingRelativeAge(new Date(post.get('postDeletedAt'))) +
autoUpdatingRelativeAge(new Date(post.get('postDeletedAt'))) +
"</div>");
}
},

View File

@ -1,8 +1,9 @@
import { setting } from 'discourse/lib/computed';
var get = Ember.get;
export default Ember.Component.extend({
classNameBindings: ['category::no-category', 'categories:has-drop','categoryStyle'],
categoryStyle: Discourse.computed.setting('category_style'),
categoryStyle: setting('category_style'),
tagName: 'li',

View File

@ -0,0 +1,30 @@
/* global Pikaday:true */
import loadScript from "discourse/lib/load-script";
export default Em.Component.extend({
tagName: "input",
classNames: ["date-picker"],
_picker: null,
_loadDatePicker: function() {
const self = this,
input = this.$()[0];
loadScript("/javascripts/pikaday.js").then(function() {
self._picker = new Pikaday({
field: input,
format: "YYYY-MM-DD",
defaultDate: moment().add(1, "day").toDate(),
minDate: new Date(),
onSelect: function(date) {
self.set("value", moment(date).format("YYYY-MM-DD"));
},
});
});
}.on("didInsertElement"),
_destroy: function() {
this._picker = null;
}.on("willDestroyElement"),
});

View File

@ -24,8 +24,11 @@ export default Ember.Component.extend(StringBuffer, {
}.on('willDestroyElement'),
renderString(buffer) {
const title = this.get('title');
if (title) {
buffer.push("<h4 class='title'>" + title + "</h4>");
}
buffer.push("<h4 class='title'>" + this.get('title') + "</h4>");
buffer.push("<button class='btn standard dropdown-toggle' data-toggle='dropdown'>");
buffer.push(this.get('text'));
buffer.push("</button>");

View File

@ -1,6 +1,7 @@
import { setting } from 'discourse/lib/computed';
import { buildCategoryPanel } from 'discourse/components/edit-category-panel';
export default buildCategoryPanel('settings', {
emailInEnabled: Discourse.computed.setting('email_in'),
showPositionInput: Discourse.computed.setting('fixed_category_positions'),
emailInEnabled: setting('email_in'),
showPositionInput: setting('fixed_category_positions'),
});

View File

@ -1,3 +1,5 @@
import { propertyEqual } from 'discourse/lib/computed';
export default Em.Component.extend({
tagName: 'li',
classNameBindings: ['active', 'tabClassName'],
@ -6,7 +8,7 @@ export default Em.Component.extend({
return 'edit-category-' + this.get('tab');
}.property('tab'),
active: Discourse.computed.propertyEqual('selectedTab', 'tab'),
active: propertyEqual('selectedTab', 'tab'),
title: function() {
return I18n.t('category.' + this.get('tab').replace('-', '_'));

View File

@ -1,3 +1,5 @@
import { setting } from 'discourse/lib/computed';
export default Ember.Component.extend({
classNames: ["title"],
@ -13,10 +15,10 @@ export default Ember.Component.extend({
return Discourse.Mobile.mobileView && !Ember.isBlank(this.get('mobileBigLogoUrl'));
}.property(),
smallLogoUrl: Discourse.computed.setting('logo_small_url'),
bigLogoUrl: Discourse.computed.setting('logo_url'),
mobileBigLogoUrl: Discourse.computed.setting('mobile_logo_url'),
title: Discourse.computed.setting('title'),
smallLogoUrl: setting('logo_small_url'),
bigLogoUrl: setting('logo_url'),
mobileBigLogoUrl: setting('mobile_logo_url'),
title: setting('title'),
click: function(e) {
// if they want to open in a new tab, let it so

View File

@ -30,9 +30,9 @@ export default Ember.Component.extend({
}
if (it.get('notification_type') === INVITED_TYPE) {
return Discourse.getURL('/my/invited');
return Discourse.getURL('/users/' + it.get('data.display_username'));
}
}.property("notification.data.{badge_id,badge_name}", "model.slug", "model.topic_id", "model.post_number"),
}.property("notification.data.{badge_id,badge_name,display_username}", "model.slug", "model.topic_id", "model.post_number"),
description: function() {
const badgeName = this.get("notification.data.badge_name");

View File

@ -1,6 +1,8 @@
import { setting } from 'discourse/lib/computed';
const PosterNameComponent = Em.Component.extend({
classNames: ['names', 'trigger-user-card'],
displayNameOnPosts: Discourse.computed.setting('display_name_on_posts'),
displayNameOnPosts: setting('display_name_on_posts'),
// sanitize name for comparison
sanitizeName(name){

View File

@ -1,3 +1,5 @@
import { relativeAge } from 'discourse/lib/formatter';
const icons = {
'closed.enabled': 'lock',
'closed.disabled': 'unlock-alt',
@ -19,7 +21,7 @@ export function actionDescription(actionCode, createdAt) {
const ac = this.get(actionCode);
if (ac) {
const dt = new Date(this.get(createdAt));
const when = Discourse.Formatter.relativeAge(dt, {format: 'medium-with-ago'});
const when = relativeAge(dt, {format: 'medium-with-ago'});
return I18n.t(`action_codes.${ac}`, {when}).htmlSafe();
}
}.property(actionCode, createdAt);

View File

@ -1,8 +1,9 @@
import { propertyEqual } from 'discourse/lib/computed';
import { actionDescription } from "discourse/components/small-action";
export default Ember.Component.extend({
classNameBindings: [":item", "item.hidden", "item.deleted", "moderatorAction"],
moderatorAction: Discourse.computed.propertyEqual("item.post_type", "site.post_types.moderator_action"),
moderatorAction: propertyEqual("item.post_type", "site.post_types.moderator_action"),
actionDescription: actionDescription("item.action_code", "item.created_at"),
actions: {

View File

@ -1,6 +1,8 @@
import { fmt } from 'discourse/lib/computed';
export default Ember.Component.extend({
classNameBindings: [':user-field', 'field.field_type'],
layoutName: Discourse.computed.fmt('field.field_type', 'components/user-fields/%@'),
layoutName: fmt('field.field_type', 'components/user-fields/%@'),
noneLabel: function() {
if (!this.get('field.required')) {

View File

@ -1,7 +1,9 @@
import { url } from 'discourse/lib/computed';
export default Ember.Component.extend({
classNames: ['user-small'],
userPath: Discourse.computed.url('user.username', '/users/%@'),
userPath: url('user.username', '/users/%@'),
name: function() {
const name = this.get('user.name');

View File

@ -1,4 +1,5 @@
export default Ember.Controller.extend({
showTop: true,
showFooter: false,
styleCategory: null,

View File

@ -1,3 +1,4 @@
import { setting } from 'discourse/lib/computed';
import Presence from 'discourse/mixins/presence';
export default Ember.ObjectController.extend(Presence, {
@ -8,7 +9,7 @@ export default Ember.ObjectController.extend(Presence, {
showEditReason: false,
editReason: null,
maxTitleLength: Discourse.computed.setting('max_topic_title_length'),
maxTitleLength: setting('max_topic_title_length'),
scopedCategoryId: null,
similarTopics: null,
similarTopicsMessage: null,

View File

@ -1,5 +1,6 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import DiscourseController from 'discourse/controllers/controller';
import { setting } from 'discourse/lib/computed';
export default DiscourseController.extend(ModalFunctionality, {
needs: ['login'],
@ -16,10 +17,10 @@ export default DiscourseController.extend(ModalFunctionality, {
userFields: null,
hasAuthOptions: Em.computed.notEmpty('authOptions'),
canCreateLocal: Discourse.computed.setting('enable_local_logins'),
canCreateLocal: setting('enable_local_logins'),
showCreateForm: Em.computed.or('hasAuthOptions', 'canCreateLocal'),
maxUsernameLength: Discourse.computed.setting('max_username_length'),
minUsernameLength: Discourse.computed.setting('min_username_length'),
maxUsernameLength: setting('max_username_length'),
minUsernameLength: setting('min_username_length'),
resetForm() {
// We wrap the fields in a structure so we can assign a value

View File

@ -1,3 +1,5 @@
import { propertyEqual } from 'discourse/lib/computed';
export default Ember.Controller.extend({
me: Discourse.computed.propertyEqual('model.user.id', 'currentUser.id')
me: propertyEqual('model.user.id', 'currentUser.id')
});

View File

@ -1,6 +1,7 @@
import DiscoveryController from 'discourse/controllers/discovery';
import { queryParams } from 'discourse/controllers/discovery-sortable';
import BulkTopicSelection from 'discourse/mixins/bulk-topic-selection';
import { endWith } from 'discourse/lib/computed';
const controllerOpts = {
needs: ['discovery'],
@ -102,8 +103,8 @@ const controllerOpts = {
hasTopics: Em.computed.gt('model.topics.length', 0),
allLoaded: Em.computed.empty('model.more_topics_url'),
latest: Discourse.computed.endWith('model.filter', 'latest'),
new: Discourse.computed.endWith('model.filter', 'new'),
latest: endWith('model.filter', 'latest'),
new: endWith('model.filter', 'new'),
top: Em.computed.notEmpty('period'),
yearly: Em.computed.equal('period', 'yearly'),
quarterly: Em.computed.equal('period', 'quarterly'),

View File

@ -1,7 +1,4 @@
import ObjectController from 'discourse/controllers/object';
// The basic controller for a group
export default ObjectController.extend({
export default Ember.Controller.extend({
counts: null,
showing: null,
@ -10,4 +7,3 @@ export default ObjectController.extend({
showingIndex: Em.computed.equal('showing', 'index'),
showingMembers: Em.computed.equal('showing', 'members')
});

View File

@ -1,11 +1,13 @@
export default Ember.ObjectController.extend({
export default Ember.Controller.extend({
loading: false,
limit: null,
offset: null,
actions: {
loadMore() {
if (this.get("loading")) { return; }
// we've reached the end
if (this.get("model.members.length") >= this.get("user_count")) { return; }
if (this.get("model.members.length") >= this.get("model.user_count")) { return; }
this.set("loading", true);

View File

@ -14,7 +14,7 @@ export default ObjectController.extend(Presence, ModalFunctionality, {
}.property(),
disabled: function() {
if (this.get('saving')) return true;
if (this.get('model.saving')) return true;
if (this.blank('emailOrUsername')) return true;
const emailOrUsername = this.get('emailOrUsername').trim();
// when inviting to forum, email must be valid
@ -22,14 +22,14 @@ export default ObjectController.extend(Presence, ModalFunctionality, {
// normal users (not admin) can't invite users to private topic via email
if (!this.get('isAdmin') && this.get('isPrivateTopic') && Discourse.Utilities.emailValid(emailOrUsername)) return true;
// when invting to private topic via email, group name must be specified
if (this.get('isPrivateTopic') && this.blank('groupNames') && Discourse.Utilities.emailValid(emailOrUsername)) return true;
if (this.get('isPrivateTopic') && this.blank('model.groupNames') && Discourse.Utilities.emailValid(emailOrUsername)) return true;
if (this.get('model.details.can_invite_to')) return false;
return false;
}.property('isAdmin', 'emailOrUsername', 'invitingToTopic', 'isPrivateTopic', 'groupNames', 'saving'),
}.property('isAdmin', 'emailOrUsername', 'invitingToTopic', 'isPrivateTopic', 'model.groupNames', 'model.saving'),
buttonTitle: function() {
return this.get('saving') ? I18n.t('topic.inviting') : I18n.t('topic.invite_reply.action');
}.property('saving'),
return this.get('model.saving') ? I18n.t('topic.inviting') : I18n.t('topic.invite_reply.action');
}.property('model.saving'),
// We are inviting to a topic if the model isn't the current user.
// The current user would mean we are inviting to the forum in general.
@ -117,8 +117,8 @@ export default ObjectController.extend(Presence, ModalFunctionality, {
// Reset the modal to allow a new user to be invited.
reset() {
this.setProperties({
emailOrUsername: null,
this.set('emailOrUsername', null);
this.get('model').setProperties({
groupNames: null,
error: false,
saving: false,
@ -131,13 +131,14 @@ export default ObjectController.extend(Presence, ModalFunctionality, {
createInvite() {
if (this.get('disabled')) { return; }
const groupNames = this.get('groupNames'),
userInvitedController = this.get('controllers.user-invited-show');
const groupNames = this.get('model.groupNames'),
userInvitedController = this.get('controllers.user-invited-show'),
model = this.get('model');
this.setProperties({ saving: true, error: false });
model.setProperties({ saving: true, error: false });
return this.get('model').createInvite(this.get('emailOrUsername').trim(), groupNames).then(result => {
this.setProperties({ saving: false, finished: true });
model.setProperties({ saving: false, finished: true });
if (!this.get('invitingToTopic')) {
Discourse.Invite.findInvitedBy(this.currentUser, userInvitedController.get('filter')).then(invite_model => {
userInvitedController.set('model', invite_model);
@ -146,7 +147,7 @@ export default ObjectController.extend(Presence, ModalFunctionality, {
} else if (this.get('isMessage') && result && result.user) {
this.get('model.details.allowed_users').pushObject(result.user);
}
}).catch(() => this.setProperties({ saving: false, error: true }));
}).catch(() => model.setProperties({ saving: false, error: true }));
}
}

View File

@ -1,6 +1,7 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import DiscourseController from 'discourse/controllers/controller';
import showModal from 'discourse/lib/show-modal';
import { setting } from 'discourse/lib/computed';
// This is happening outside of the app via popup
const AuthErrors =
@ -13,7 +14,7 @@ export default DiscourseController.extend(ModalFunctionality, {
loggingIn: false,
loggedIn: false,
canLoginLocal: Discourse.computed.setting('enable_local_logins'),
canLoginLocal: setting('enable_local_logins'),
loginRequired: Em.computed.alias('controllers.application.loginRequired'),
resetForm: function() {

View File

@ -1,7 +1,8 @@
import NavigationDefaultController from 'discourse/controllers/navigation/default';
import { setting } from 'discourse/lib/computed';
export default NavigationDefaultController.extend({
subcategoryListSetting: Discourse.computed.setting('show_subcategory_list'),
subcategoryListSetting: setting('show_subcategory_list'),
showingParentCategory: Em.computed.none('category.parentCategory'),
showingSubcategoryList: Em.computed.and('subcategoryListSetting', 'showingParentCategory'),

View File

@ -1,5 +1,7 @@
import { url } from 'discourse/lib/computed';
export default Ember.ArrayController.extend({
needs: ['header'],
loadingNotifications: Em.computed.alias('controllers.header.loadingNotifications'),
myNotificationsUrl: Discourse.computed.url('/my/notifications')
myNotificationsUrl: url('/my/notifications')
});

View File

@ -1,14 +1,15 @@
import { setting } from 'discourse/lib/computed';
import ObjectController from 'discourse/controllers/object';
import CanCheckEmails from 'discourse/mixins/can-check-emails';
import { popupAjaxError } from 'discourse/lib/ajax-error';
export default ObjectController.extend(CanCheckEmails, {
allowAvatarUpload: Discourse.computed.setting('allow_uploaded_avatars'),
allowUserLocale: Discourse.computed.setting('allow_user_locale'),
ssoOverridesAvatar: Discourse.computed.setting('sso_overrides_avatar'),
allowBackgrounds: Discourse.computed.setting('allow_profile_backgrounds'),
editHistoryVisible: Discourse.computed.setting('edit_history_visible_to_public'),
allowAvatarUpload: setting('allow_uploaded_avatars'),
allowUserLocale: setting('allow_user_locale'),
ssoOverridesAvatar: setting('sso_overrides_avatar'),
allowBackgrounds: setting('allow_profile_backgrounds'),
editHistoryVisible: setting('edit_history_visible_to_public'),
selectedCategories: function(){
return [].concat(this.get("model.watchedCategories"),
@ -40,7 +41,7 @@ export default ObjectController.extend(CanCheckEmails, {
cannotDeleteAccount: Em.computed.not('can_delete_account'),
deleteDisabled: Em.computed.or('saving', 'deleting', 'cannotDeleteAccount'),
canEditName: Discourse.computed.setting('enable_names'),
canEditName: setting('enable_names'),
nameInstructions: function() {
return I18n.t(Discourse.SiteSettings.full_name_required ? 'user.name.instructions_required' : 'user.name.instructions');

View File

@ -1,13 +1,6 @@
import { propertyEqual } from 'discourse/lib/computed';
import ObjectController from 'discourse/controllers/object';
/**
This controller supports actions related to updating one's email address
@class PreferencesEmailController
@extends ObjectController
@namespace Discourse
@module Discourse
**/
export default ObjectController.extend({
taken: false,
saving: false,
@ -17,7 +10,7 @@ export default ObjectController.extend({
newEmailEmpty: Em.computed.empty('newEmail'),
saveDisabled: Em.computed.or('saving', 'newEmailEmpty', 'taken', 'unchanged'),
unchanged: Discourse.computed.propertyEqual('newEmailLower', 'email'),
unchanged: propertyEqual('newEmailLower', 'email'),
newEmailLower: function() {
return this.get('newEmail').toLowerCase();

View File

@ -1,3 +1,4 @@
import { setting, propertyEqual } from 'discourse/lib/computed';
import Presence from 'discourse/mixins/presence';
import ObjectController from 'discourse/controllers/object';
@ -8,11 +9,11 @@ export default ObjectController.extend(Presence, {
errorMessage: null,
newUsername: null,
maxLength: Discourse.computed.setting('max_username_length'),
minLength: Discourse.computed.setting('min_username_length'),
maxLength: setting('max_username_length'),
minLength: setting('min_username_length'),
newUsernameEmpty: Em.computed.empty('newUsername'),
saveDisabled: Em.computed.or('saving', 'newUsernameEmpty', 'taken', 'unchanged', 'errorMessage'),
unchanged: Discourse.computed.propertyEqual('newUsername', 'username'),
unchanged: propertyEqual('newUsername', 'username'),
checkTaken: function() {
if( this.get('newUsername') && this.get('newUsername').length < this.get('minLength') ) {

View File

@ -1,3 +1,4 @@
import { propertyEqual } from 'discourse/lib/computed';
import BufferedContent from 'discourse/mixins/buffered-content';
import { popupAjaxError } from 'discourse/lib/ajax-error';
@ -21,7 +22,7 @@ export default Ember.Controller.extend(BufferedContent, {
post: Ember.computed.alias('model'),
currentlyEditing: Ember.computed.alias('controllers.queued-posts.editing'),
editing: Discourse.computed.propertyEqual('model', 'currentlyEditing'),
editing: propertyEqual('model', 'currentlyEditing'),
_confirmDelete: updateState('rejected', {deleteUser: true}),

View File

@ -1,14 +1,15 @@
import Presence from 'discourse/mixins/presence';
import searchForTerm from 'discourse/lib/search-for-term';
var _dontSearch = false;
let _dontSearch = false;
export default Em.Controller.extend(Presence, {
typeFilter: null,
contextType: function(key, value){
if(arguments.length > 1) {
// a bit hacky, consider cleaning this up, need to work through all observers though
var context = $.extend({}, this.get('searchContext'));
const context = $.extend({}, this.get('searchContext'));
context.type = value;
this.set('searchContext', context);
}
@ -29,8 +30,8 @@ export default Em.Controller.extend(Presence, {
return null;
}
var url = '/search?q=' + encodeURIComponent(this.get('term'));
var searchContext = this.get('searchContext');
let url = '/search?q=' + encodeURIComponent(this.get('term'));
const searchContext = this.get('searchContext');
if (this.get('searchContextEnabled') && searchContext) {
url += encodeURIComponent(" " + searchContext.type + ":" + searchContext.id);
@ -41,14 +42,14 @@ export default Em.Controller.extend(Presence, {
}.property('searchContext','term','searchContextEnabled'),
fullSearchUrl: function(){
var url = this.get('fullSearchUrlRelative');
const url = this.get('fullSearchUrlRelative');
if (url) {
return Discourse.getURL(url);
}
}.property('fullSearchUrlRelative'),
searchContextDescription: function(){
var ctx = this.get('searchContext');
const ctx = this.get('searchContext');
if (ctx) {
switch(Em.get(ctx, 'type')) {
case 'topic':
@ -71,7 +72,7 @@ export default Em.Controller.extend(Presence, {
// If we need to perform another search
newSearchNeeded: function() {
this.set('noResults', false);
var term = (this.get('term') || '').trim();
const term = (this.get('term') || '').trim();
if (term.length >= Discourse.SiteSettings.min_search_term_length) {
this.set('loading', true);
@ -82,23 +83,32 @@ export default Em.Controller.extend(Presence, {
this.set('selectedIndex', 0);
}.observes('term', 'typeFilter'),
searchTerm: function(term, typeFilter) {
var self = this;
searchTerm(term, typeFilter) {
const self = this;
var context;
if(this.get('searchContextEnabled')){
context = this.get('searchContext');
// for cancelling debounced search
if (this._cancelSearch){
this._cancelSearch = null;
return;
}
searchForTerm(term, {
typeFilter: typeFilter,
searchContext: context,
if (this._search) {
this._search.abort();
}
const searchContext = this.get('searchContextEnabled') ? this.get('searchContext') : null;
this._search = searchForTerm(term, {
typeFilter,
searchContext,
fullSearchUrl: this.get('fullSearchUrl')
}).then(function(results) {
});
this._search.then(function(results) {
self.setProperties({ noResults: !results, content: results });
}).finally(function() {
self.set('loading', false);
}).catch(function() {
self.set('loading', false);
self._search = null;
});
},
@ -112,22 +122,36 @@ export default Em.Controller.extend(Presence, {
}.observes('term'),
actions: {
fullSearch: function() {
var url = this.get('fullSearchUrlRelative');
fullSearch() {
const self = this;
if (this._search) {
this._search.abort();
}
// maybe we are debounced and delayed
// stop that as well
this._cancelSearch = true;
Em.run.later(function(){
self._cancelSearch = false;
}, 400);
const url = this.get('fullSearchUrlRelative');
if (url) {
Discourse.URL.routeTo(url);
}
},
moreOfType: function(type) {
moreOfType(type) {
this.set('typeFilter', type);
},
cancelType: function() {
cancelType() {
this.cancelTypeFilter();
}
},
cancelTypeFilter: function() {
cancelTypeFilter() {
this.set('typeFilter', null);
}
});

View File

@ -1,11 +1,12 @@
import Sharing from 'discourse/lib/sharing';
import { longDateNoYear } from 'discourse/lib/formatter';
export default Ember.Controller.extend({
needs: ['topic'],
title: Ember.computed.alias('controllers.topic.model.title'),
displayDate: function() {
return Discourse.Formatter.longDateNoYear(new Date(this.get('date')));
return longDateNoYear(new Date(this.get('date')));
}.property('date'),
// Close the share controller

Some files were not shown because too many files have changed in this diff Show More