View
@@ -1,66 +0,0 @@
-/**
- This controller supports the interface for dealing with API keys
-
- @class AdminApiController
- @extends Ember.ArrayController
- @namespace Discourse
- @module Discourse
-**/
-export default Ember.ArrayController.extend({
-
- actions: {
- /**
- Generates a master api key
-
- @method generateMasterKey
- **/
- generateMasterKey: function() {
- var self = this;
- Discourse.ApiKey.generateMasterKey().then(function (key) {
- self.get('model').pushObject(key);
- });
- },
-
- /**
- Creates an API key instance with internal user object
-
- @method regenerateKey
- @param {Discourse.ApiKey} key the key to regenerate
- **/
- regenerateKey: function(key) {
- bootbox.confirm(I18n.t("admin.api.confirm_regen"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
- if (result) {
- key.regenerate();
- }
- });
- },
-
- /**
- Revokes an API key
-
- @method revokeKey
- @param {Discourse.ApiKey} key the key to revoke
- **/
- revokeKey: function(key) {
- var self = this;
- bootbox.confirm(I18n.t("admin.api.confirm_revoke"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
- if (result) {
- key.revoke().then(function() {
- self.get('model').removeObject(key);
- });
- }
- });
- }
- },
-
- /**
- Has a master key already been generated?
-
- @property hasMasterKey
- @type {Boolean}
- **/
- hasMasterKey: function() {
- return !!this.get('model').findBy('user', null);
- }.property('model.@each')
-
-});
View
@@ -1,49 +1,24 @@
-export default Ember.ArrayController.extend({
- needs: ["adminBackups"],
- status: Em.computed.alias("controllers.adminBackups"),
+import { ajax } from 'discourse/lib/ajax';
- uploadText: function() { return I18n.t("admin.backups.upload.text"); }.property(),
+export default Ember.Controller.extend({
+ adminBackups: Ember.inject.controller(),
+ status: Ember.computed.alias('adminBackups.model'),
- readOnlyModeDisabled: Em.computed.alias("status.isOperationRunning"),
-
- restoreDisabled: Em.computed.alias("status.restoreDisabled"),
+ uploadLabel: function() { return I18n.t("admin.backups.upload.label"); }.property(),
restoreTitle: function() {
if (!this.get('status.allowRestore')) {
- return I18n.t("admin.backups.operations.restore.is_disabled");
+ return "admin.backups.operations.restore.is_disabled";
} else if (this.get("status.isOperationRunning")) {
- return I18n.t("admin.backups.operation_already_running");
- } else {
- return I18n.t("admin.backups.operations.restore.title");
- }
- }.property("status.isOperationRunning"),
-
- destroyDisabled: Em.computed.alias("status.isOperationRunning"),
-
- destroyTitle: function() {
- if (this.get("status.isOperationRunning")) {
- return I18n.t("admin.backups.operation_already_running");
+ return "admin.backups.operations.is_running";
} else {
- return I18n.t("admin.backups.operations.destroy.title");
+ return "admin.backups.operations.restore.title";
}
- }.property("status.isOperationRunning"),
-
- readOnlyModeTitle: function() { return this._readOnlyModeI18n("title"); }.property("site.isReadOnly"),
- readOnlyModeText: function() { return this._readOnlyModeI18n("text"); }.property("site.isReadOnly"),
-
- _readOnlyModeI18n: function(value) {
- var action = this.site.get("isReadOnly") ? "disable" : "enable";
- return I18n.t("admin.backups.read_only." + action + "." + value);
- },
+ }.property("status.{allowRestore,isOperationRunning}"),
actions: {
- /**
- Toggle read-only mode
-
- @method toggleReadOnlyMode
- **/
- toggleReadOnlyMode: function() {
+ toggleReadOnlyMode() {
var self = this;
if (!this.site.get("isReadOnly")) {
bootbox.confirm(
@@ -60,16 +35,23 @@ export default Ember.ArrayController.extend({
} else {
this._toggleReadOnlyMode(false);
}
+ },
+
+ download(backup) {
+ let link = backup.get('filename');
+ ajax("/admin/backups/" + link, { type: "PUT" })
+ .then(() => {
+ bootbox.alert(I18n.t("admin.backups.operations.download.alert"));
+ });
}
-
},
- _toggleReadOnlyMode: function(enable) {
+ _toggleReadOnlyMode(enable) {
var site = this.site;
- Discourse.ajax("/admin/backups/readonly", {
+ ajax("/admin/backups/readonly", {
type: "PUT",
data: { enable: enable }
- }).then(function() {
+ }).then(() => {
site.set("isReadOnly", enable);
});
}
View
@@ -1,4 +1,5 @@
-export default Ember.ArrayController.extend({
- needs: ["adminBackups"],
- status: Em.computed.alias("controllers.adminBackups")
+export default Ember.Controller.extend({
+ logs: [],
+ adminBackups: Ember.inject.controller(),
+ status: Em.computed.alias("adminBackups.model")
});
View
@@ -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")
+export default Ember.Controller.extend({
+ noOperationIsRunning: Ember.computed.not("model.isOperationRunning"),
+ rollbackEnabled: Ember.computed.and("model.canRollback", "model.restoreEnabled", "noOperationIsRunning"),
+ rollbackDisabled: Ember.computed.not("rollbackEnabled")
});
View
@@ -1,49 +0,0 @@
-export default Ember.Controller.extend({
- needs: ['modal'],
-
- sample: Em.computed.alias('model.sample'),
- errors: Em.computed.alias('model.errors'),
- count: Em.computed.alias('model.grant_count'),
-
- count_warning: function() {
- if (this.get('count') <= 10) {
- return this.get('sample.length') !== this.get('count');
- } else {
- return this.get('sample.length') !== 10;
- }
- }.property('count', 'sample.length'),
-
- has_query_plan: function() {
- return !!this.get('model.query_plan');
- }.property('model.query_plan'),
-
- query_plan_html: function() {
- var raw = this.get('model.query_plan'),
- returned = "<pre class='badge-query-plan'>";
-
- _.each(raw, function(linehash) {
- returned += Handlebars.Utils.escapeExpression(linehash["QUERY PLAN"]);
- returned += "<br>";
- });
-
- returned += "</pre>";
- return returned;
- }.property('model.query_plan'),
-
- processed_sample: Ember.computed.map('model.sample', function(grant) {
- var i18nKey = 'admin.badges.preview.grant.with',
- i18nParams = { username: Handlebars.Utils.escapeExpression(grant.username) };
-
- if (grant.post_id) {
- i18nKey += "_post";
- i18nParams.link = "<a href='/p/" + grant.post_id + "' data-auto-route='true'>" + Handlebars.Utils.escapeExpression(grant.title) + "</a>";
- }
-
- if (grant.granted_at) {
- i18nKey += "_time";
- i18nParams.time = Handlebars.Utils.escapeExpression(moment(grant.granted_at).format(I18n.t('dates.long_with_year')));
- }
-
- return I18n.t(i18nKey, i18nParams);
- })
-});
View
@@ -1,34 +1,44 @@
+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'],
+export default Ember.Controller.extend(BufferedContent, {
+ adminBadges: Ember.inject.controller(),
saving: false,
savingStatus: '',
- badgeTypes: Em.computed.alias('controllers.admin-badges.badgeTypes'),
- badgeGroupings: Em.computed.alias('controllers.admin-badges.badgeGroupings'),
- badgeTriggers: Em.computed.alias('controllers.admin-badges.badgeTriggers'),
- protectedSystemFields: Em.computed.alias('controllers.admin-badges.protectedSystemFields'),
+ badgeTypes: Ember.computed.alias('adminBadges.badgeTypes'),
+ badgeGroupings: Ember.computed.alias('adminBadges.badgeGroupings'),
+ badgeTriggers: Ember.computed.alias('adminBadges.badgeTriggers'),
+ protectedSystemFields: Ember.computed.alias('adminBadges.protectedSystemFields'),
readOnly: Ember.computed.alias('buffered.system'),
- showDisplayName: Discourse.computed.propertyNotEqual('name', 'displayName'),
- canEditDescription: Em.computed.none('buffered.translatedDescription'),
+ showDisplayName: propertyNotEqual('name', 'displayName'),
+
+ hasQuery: function() {
+ const bQuery = this.get('buffered.query');
+ if (bQuery) {
+ return bQuery.trim().length > 0;
+ }
+ const mQuery = this.get('model.query');
+ return mQuery && mQuery.trim().length > 0;
+ }.property('model.query', 'buffered.query'),
_resetSaving: function() {
this.set('saving', false);
this.set('savingStatus', '');
}.observes('model.id'),
actions: {
- save: function() {
+ save() {
if (!this.get('saving')) {
- var fields = ['allow_title', 'multiple_grant',
- 'listable', 'auto_revoke',
- 'enabled', 'show_posts',
- 'target_posts', 'name', 'description',
- 'icon', 'image', 'query', 'badge_grouping_id',
- 'trigger', 'badge_type_id'],
- self = this;
+ let fields = ['allow_title', 'multiple_grant',
+ 'listable', 'auto_revoke',
+ 'enabled', 'show_posts',
+ 'target_posts', 'name', 'description',
+ 'long_description',
+ 'icon', 'image', 'query', 'badge_grouping_id',
+ 'trigger', 'badge_type_id'];
if (this.get('buffered.system')){
var protectedFields = this.get('protectedSystemFields');
@@ -40,55 +50,55 @@ export default Ember.ObjectController.extend(BufferedContent, {
this.set('saving', true);
this.set('savingStatus', I18n.t('saving'));
- var boolFields = ['allow_title', 'multiple_grant',
- 'listable', 'auto_revoke',
- 'enabled', 'show_posts',
- 'target_posts' ];
+ const boolFields = ['allow_title', 'multiple_grant',
+ 'listable', 'auto_revoke',
+ 'enabled', 'show_posts',
+ 'target_posts' ];
- var data = {},
- buffered = this.get('buffered');
+ const data = {};
+ const buffered = this.get('buffered');
fields.forEach(function(field){
var d = buffered.get(field);
if (_.include(boolFields, field)) { d = !!d; }
data[field] = d;
});
- var newBadge = !this.get('id'),
- model = this.get('model');
- this.get('model').save(data).then(function() {
+ const newBadge = !this.get('id');
+ const model = this.get('model');
+ this.get('model').save(data).then(() => {
if (newBadge) {
- self.get('controllers.admin-badges').pushObject(model);
- self.transitionToRoute('adminBadges.show', model.get('id'));
+ const adminBadges = this.get('adminBadges.model');
+ if (!adminBadges.includes(model)) {
+ adminBadges.pushObject(model);
+ }
+ this.transitionToRoute('adminBadges.show', model.get('id'));
} else {
- self.commitBuffer();
- self.set('savingStatus', I18n.t('saved'));
+ this.commitBuffer();
+ this.set('savingStatus', I18n.t('saved'));
}
- }).catch(function(error) {
- self.set('savingStatus', I18n.t('failed'));
- self.send('saveError', error);
- }).finally(function() {
- self.set('saving', false);
+ }).catch(popupAjaxError).finally(() => {
+ this.set('saving', false);
+ this.set('savingStatus', '');
});
}
},
- destroy: function() {
- var self = this,
- adminBadgesController = this.get('controllers.admin-badges'),
- model = this.get('model');
+ destroy() {
+ const adminBadges = this.get('adminBadges.model');
+ const model = this.get('model');
if (!model.get('id')) {
- self.transitionToRoute('adminBadges.index');
+ this.transitionToRoute('adminBadges.index');
return;
}
- return bootbox.confirm(I18n.t("admin.badges.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
+ return bootbox.confirm(I18n.t("admin.badges.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
if (result) {
- model.destroy().then(function() {
- adminBadgesController.removeObject(model);
- self.transitionToRoute('adminBadges.index');
- }).catch(function() {
+ model.destroy().then(() => {
+ adminBadges.removeObject(model);
+ this.transitionToRoute('adminBadges.index');
+ }).catch(() => {
bootbox.alert(I18n.t('generic_error'));
});
}
View
@@ -1 +1 @@
-export default Ember.ArrayController.extend();
+export default Ember.Controller.extend();
View
@@ -0,0 +1,74 @@
+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();
+ },
+
+ copyToClipboard() {
+ $(".table.colors").hide();
+ let area = $("<textarea id='copy-range'></textarea>");
+ $(".table.colors").after(area);
+ area.text(this.get("model").schemeJson());
+ let range = document.createRange();
+ range.selectNode(area[0]);
+ window.getSelection().addRange(range);
+ let successful = document.execCommand('copy');
+ if (successful) {
+ this.set("model.savingStatus", I18n.t("admin.customize.copied_to_clipboard"));
+ } else {
+ this.set("model.savingStatus", I18n.t("admin.customize.copy_to_clipboard_error"));
+ }
+
+ setTimeout(()=>{
+ this.set("model.savingStatus", null);
+ }, 2000);
+
+ window.getSelection().removeAllRanges();
+
+ $(".table.colors").show();
+ $(area).remove();
+ },
+
+ 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() {
+
+ const model = this.get('model');
+ return bootbox.confirm(I18n.t("admin.customize.colors.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
+ if (result) {
+ model.destroy().then(()=>{
+ this.get('allColors').removeObject(model);
+ this.replaceRoute('adminCustomize.colors');
+ });
+ }
+ });
+ }
+ }
+});
View
@@ -1,19 +1,14 @@
-/**
- 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,
+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){
@@ -22,100 +17,25 @@ export default Ember.ArrayController.extend({
return baseColorsHash;
}.property('baseColorScheme'),
- removeSelected: function() {
- this.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;
- }
-
- var matches = Em.A();
-
- _.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();
- },
- newColorScheme: function() {
- var newColorScheme = Em.copy(this.get('baseColorScheme'), true);
+ 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'));
- this.pushObject(newColorScheme);
- this.send('selectColorScheme', newColorScheme);
- this.set('onlyOverridden', false);
- },
-
- 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();
+ 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);
+ });
},
- copy: function(colorScheme) {
- var newColorScheme = Em.copy(colorScheme, true);
- newColorScheme.set('name', I18n.t('admin.customize.colors.copy_name_prefix') + ' ' + colorScheme.get('name'));
- this.pushObject(newColorScheme);
- this.send('selectColorScheme', newColorScheme);
+ newColorScheme() {
+ showModal('admin-color-scheme-select-base', { model: this.get('baseColorSchemes'), admin: true});
},
- 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(); });
- }
- }
- });
- }
}
});
View
@@ -1,63 +0,0 @@
-/**
- 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({
-
- 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);
- },
-
- /**
- 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
@@ -0,0 +1,38 @@
+import { popupAjaxError } from 'discourse/lib/ajax-error';
+import { bufferedProperty } from 'discourse/mixins/buffered-content';
+
+export default Ember.Controller.extend(bufferedProperty('emailTemplate'), {
+ saved: false,
+
+ hasMultipleSubjects: function() {
+ const buffered = this.get('buffered');
+ if (buffered.getProperties('subject')['subject']) {
+ return false;
+ } else {
+ return buffered.getProperties('id')['id'];
+ }
+ }.property("buffered"),
+
+ actions: {
+ saveChanges() {
+ this.set('saved', false);
+ const buffered = this.get('buffered');
+ this.get('emailTemplate').save(buffered.getProperties('subject', 'body')).then(() => {
+ this.set('saved', true);
+ }).catch(popupAjaxError);
+ },
+
+ revertChanges() {
+ this.set('saved', false);
+ bootbox.confirm(I18n.t('admin.customize.email_templates.revert_confirm'), result => {
+ if (result) {
+ this.get('emailTemplate').revert().then(props => {
+ const buffered = this.get('buffered');
+ buffered.setProperties(props);
+ this.commitBuffer();
+ }).catch(popupAjaxError);
+ }
+ });
+ }
+ }
+});
View
@@ -0,0 +1,6 @@
+export default Ember.Controller.extend({
+ titleSorting: ['title'],
+ emailTemplates: null,
+
+ sortedTemplates: Ember.computed.sort('emailTemplates', 'titleSorting')
+});
View
@@ -0,0 +1,162 @@
+import { url } from 'discourse/lib/computed';
+import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
+
+export default Ember.Controller.extend({
+ maximized: false,
+ section: null,
+
+ editRouteName: 'adminCustomizeThemes.edit',
+
+ targets: [
+ { id: 0, name: 'common' },
+ { id: 1, name: 'desktop' },
+ { id: 2, name: 'mobile' },
+ { id: 3, name: 'settings' }
+ ],
+
+ fieldsForTarget: function (target) {
+ const common = ["scss", "head_tag", "header", "after_header", "body_tag", "footer"];
+ switch(target) {
+ case "common": return [...common, "embedded_scss"];
+ case "desktop": return common;
+ case "mobile": return common;
+ case "settings": return ["yaml"];
+ }
+ },
+
+ @computed('onlyOverridden')
+ showCommon() {
+ return this.shouldShow('common');
+ },
+
+ @computed('onlyOverridden')
+ showDesktop() {
+ return this.shouldShow('desktop');
+ },
+
+ @computed('onlyOverridden')
+ showMobile() {
+ return this.shouldShow('mobile');
+ },
+
+ @computed('onlyOverridden', 'model.remote_theme')
+ showSettings() {
+ return false;
+ },
+
+ @observes('onlyOverridden')
+ onlyOverriddenChanged() {
+ if (this.get('onlyOverridden')) {
+ if (!this.get('model').hasEdited(this.get('currentTargetName'), this.get('fieldName'))) {
+ let target = (this.get('showCommon') && 'common') ||
+ (this.get('showDesktop') && 'desktop') ||
+ (this.get('showMobile') && 'mobile');
+
+ let fields = this.get('model.theme_fields');
+ let field = fields && fields.find(f => (f.target === target));
+ this.replaceRoute(this.get('editRouteName'), this.get('model.id'), target, field && field.name);
+ }
+ }
+ },
+
+ shouldShow(target){
+ if(!this.get("onlyOverridden")) {
+ return true;
+ }
+ return this.get("model").hasEdited(target);
+ },
+
+ currentTarget: 0,
+
+ setTargetName: function(name) {
+ const target = this.get('targets').find(t => t.name === name);
+ this.set("currentTarget", target && target.id);
+ },
+
+ @computed("currentTarget")
+ currentTargetName(id) {
+ const target = this.get('targets').find(t => t.id === parseInt(id, 10));
+ return target && target.name;
+ },
+
+ @computed("fieldName")
+ activeSectionMode(fieldName) {
+ if (fieldName === "yaml") return "yaml";
+ return fieldName && fieldName.indexOf("scss") > -1 ? "scss" : "html";
+ },
+
+ @computed("currentTargetName", "fieldName", "saving")
+ error(target, fieldName) {
+ return this.get('model').getError(target, fieldName);
+ },
+
+ @computed("fieldName", "currentTargetName")
+ editorId(fieldName, currentTarget) {
+ return fieldName + "|" + currentTarget;
+ },
+
+ @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("currentTargetName", "onlyOverridden")
+ fields(target, onlyOverridden) {
+ let fields = this.fieldsForTarget(target);
+
+ if (onlyOverridden) {
+ const model = this.get("model");
+ const targetName = this.get("currentTargetName");
+ fields = fields.filter(name => model.hasEdited(targetName, name));
+ }
+
+ 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.id', '/admin/themes/%@/preview'),
+
+ 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'),
+
+ actions: {
+ save() {
+ this.set('saving', true);
+ this.get('model').saveChanges("theme_fields").finally(()=>{this.set('saving', false);});
+ },
+
+ toggleMaximize: function() {
+ this.toggleProperty('maximized');
+ Em.run.next(()=>{
+ this.appEvents.trigger('ace:resize');
+ });
+ }
+ }
+});
View
@@ -0,0 +1,212 @@
+import { default as computed } from 'ember-addons/ember-computed-decorators';
+import { url } from 'discourse/lib/computed';
+import { popupAjaxError } from 'discourse/lib/ajax-error';
+import showModal from 'discourse/lib/show-modal';
+import ThemeSettings from 'admin/models/theme-settings';
+
+const THEME_UPLOAD_VAR = 2;
+
+export default Ember.Controller.extend({
+
+ editRouteName: 'adminCustomizeThemes.edit',
+
+ @computed("model", "allThemes")
+ parentThemes(model, allThemes) {
+ let parents = allThemes.filter(theme =>
+ _.contains(theme.get("childThemes"), model));
+ return parents.length === 0 ? null : parents;
+ },
+
+ @computed("model.theme_fields.@each")
+ hasEditedFields(fields) {
+ return fields.any(f=>!Em.isBlank(f.value));
+ },
+
+ @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));
+ },
+
+ previewUrl: url('model.id', '/admin/themes/%@/preview'),
+
+ @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;
+ },
+
+ @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;
+ },
+
+ @computed("model.settings")
+ settings(settings) {
+ return settings.map(setting => ThemeSettings.create(setting));
+ },
+
+ @computed("settings")
+ hasSettings(settings) {
+ return settings.length > 0;
+ },
+
+ downloadUrl: url('model.id', '/admin/themes/%@'),
+
+ actions: {
+
+ updateToLatest() {
+ this.set("updatingRemote", true);
+ this.get("model").updateToLatest()
+ .catch(popupAjaxError)
+ .finally(()=>{
+ this.set("updatingRemote", false);
+ });
+ },
+
+ checkForThemeUpdates() {
+ this.set("updatingRemote", true);
+ this.get("model").checkForUpdates()
+ .catch(popupAjaxError)
+ .finally(()=>{
+ this.set("updatingRemote", false);
+ });
+ },
+
+ addUploadModal() {
+ showModal('admin-add-upload', {admin: true, name: ''});
+ },
+
+ addUpload(info) {
+ let model = this.get("model");
+ model.setField('common', info.name, '', info.upload_id, THEME_UPLOAD_VAR);
+ model.saveChanges('theme_fields').catch(e => popupAjaxError(e));
+ },
+
+ 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(this.get('editRouteName'), this.get('model.id'), 'common', 'scss');
+
+ 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);
+ },
+
+ removeUpload(upload) {
+ return bootbox.confirm(
+ I18n.t("admin.customize.theme.delete_upload_confirm"),
+ I18n.t("no_value"),
+ I18n.t("yes_value"), result => {
+ if (result) {
+ this.get("model").removeField(upload);
+ }
+ });
+ },
+
+ 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');
+ });
+ }
+ });
+ },
+
+ }
+
+});
View
@@ -0,0 +1,10 @@
+import { default as computed } from 'ember-addons/ember-computed-decorators';
+
+export default Ember.Controller.extend({
+ @computed('model', 'model.@each')
+ sortedThemes(themes) {
+ return _.sortBy(themes.content, t => {
+ return [!t.get("default"), !t.get("user_selectable"), t.get("name").toLowerCase()];
+ });
+ }
+});
View
@@ -1,60 +1,100 @@
-/**
- This controller supports the default interface when you enter the admin section.
-
- @class AdminDashboardController
- @extends Ember.Controller
- @namespace Discourse
- @module Discourse
-**/
+import { setting } from 'discourse/lib/computed';
+import AdminDashboard from 'admin/models/admin-dashboard';
+import VersionCheck from 'admin/models/version-check';
+import Report from 'admin/models/report';
+import AdminUser from 'admin/models/admin-user';
+import computed from 'ember-addons/ember-computed-decorators';
+
+const PROBLEMS_CHECK_MINUTES = 1;
+
+const ATTRIBUTES = [ 'disk_space','admins', 'moderators', 'silenced', 'suspended', 'top_traffic_sources',
+ 'top_referred_topics', 'updated_at'];
+
+const REPORTS = [ 'global_reports', 'page_view_reports', 'private_message_reports', 'http_reports',
+ 'user_reports', 'mobile_reports'];
+
+// This controller supports the default interface when you enter the admin section.
export default Ember.Controller.extend({
- loading: true,
+ loading: null,
versionCheck: null,
- problemsCheckMinutes: 1,
+ dashboardFetchedAt: null,
+ showVersionChecks: setting('version_checks'),
+ exceptionController: Ember.inject.controller('exception'),
- showVersionChecks: Discourse.computed.setting('version_checks'),
+ @computed('problems.length')
+ foundProblems(problemsLength) {
+ return this.currentUser.get('admin') && (problemsLength || 0) > 0;
+ },
- foundProblems: function() {
- return(Discourse.User.currentProp('admin') && this.get('problems') && this.get('problems').length > 0);
- }.property('problems'),
+ @computed('foundProblems')
+ thereWereProblems(foundProblems) {
+ if (!this.currentUser.get('admin')) { return false; }
- thereWereProblems: function() {
- if(!Discourse.User.currentProp('admin')) { return false }
- if( this.get('foundProblems') ) {
+ if (foundProblems) {
this.set('hadProblems', true);
return true;
} else {
return this.get('hadProblems') || false;
}
- }.property('foundProblems'),
+ },
+
+ fetchDashboard() {
+ if (!this.get('dashboardFetchedAt') || moment().subtract(30, 'minutes').toDate() > this.get('dashboardFetchedAt')) {
+ this.set('loading', true);
+ const versionChecks = this.siteSettings.version_checks;
+ AdminDashboard.find().then(d => {
+ this.set('dashboardFetchedAt', new Date());
+ if (versionChecks) {
+ this.set('versionCheck', VersionCheck.create(d.version_check));
+ }
+
+ REPORTS.forEach(name => this.set(name, d[name].map(r => Report.create(r))));
+
+ const topReferrers = d.top_referrers;
+ if (topReferrers && topReferrers.data) {
+ d.top_referrers.data = topReferrers.data.map(user => AdminUser.create(user));
+ this.set('top_referrers', topReferrers);
+ }
- loadProblems: function() {
+ ATTRIBUTES.forEach(a => this.set(a, d[a]));
+ }).catch(e => {
+ this.get('exceptionController').set('thrown', e.jqXHR);
+ this.replaceRoute('exception');
+ }).finally(() => {
+ this.set('loading', false);
+ });
+ }
+
+ if (!this.get('problemsFetchedAt') || moment().subtract(PROBLEMS_CHECK_MINUTES, 'minutes').toDate() > this.get('problemsFetchedAt')) {
+ this.loadProblems();
+ }
+ },
+
+ loadProblems() {
this.set('loadingProblems', true);
this.set('problemsFetchedAt', new Date());
- var c = this;
- Discourse.AdminDashboard.fetchProblems().then(function(d) {
- c.set('problems', d.problems);
- c.set('loadingProblems', false);
- if( d.problems && d.problems.length > 0 ) {
- c.problemsCheckInterval = 1;
- } else {
- c.problemsCheckInterval = 10;
- }
+ AdminDashboard.fetchProblems().then(d => {
+ this.set('problems', d.problems);
+ }).finally(() => {
+ this.set('loadingProblems', false);
});
},
- problemsTimestamp: function() {
- return moment(this.get('problemsFetchedAt')).format('LLL');
- }.property('problemsFetchedAt'),
+ @computed('problemsFetchedAt')
+ problemsTimestamp(problemsFetchedAt) {
+ return moment(problemsFetchedAt).format('LLL');
+ },
- updatedTimestamp: function() {
- return moment(this.get('updated_at')).format('LLL');
- }.property('updated_at'),
+ @computed('updated_at')
+ updatedTimestamp(updatedAt) {
+ return moment(updatedAt).format('LLL');
+ },
actions: {
- refreshProblems: function() {
+ refreshProblems() {
this.loadProblems();
},
- showTrafficReport: function() {
+ showTrafficReport() {
this.set("showTrafficReport", true);
}
}
View
@@ -1,38 +0,0 @@
-import ModalFunctionality from 'discourse/mixins/modal-functionality';
-
-import ObjectController from 'discourse/controllers/object';
-
-export default ObjectController.extend(ModalFunctionality, {
- needs: ["admin-flags-list"],
-
- actions: {
-
- deletePostDeferFlag: function () {
- var adminFlagController = this.get("controllers.admin-flags-list");
- var post = this.get("content");
- var self = this;
-
- return post.deferFlags(true).then(function () {
- adminFlagController.removeObject(post);
- self.send("closeModal");
- }, function () {
- bootbox.alert(I18n.t("admin.flags.error"));
- });
- },
-
- deletePostAgreeFlag: function () {
- var adminFlagController = this.get("controllers.admin-flags-list");
- var post = this.get("content");
- var self = this;
-
- return post.agreeFlags("delete").then(function () {
- adminFlagController.removeObject(post);
- self.send("closeModal");
- }, function () {
- bootbox.alert(I18n.t("admin.flags.error"));
- });
- }
-
- }
-
-});
View
@@ -1,80 +0,0 @@
-export default Ember.Controller.extend({
- needs: ['modal'],
-
- modelChanged: function(){
-
- var grouping = Em.Object.extend({});
-
- var model = this.get('model');
- var copy = Em.A();
-
- if(model){
- model.forEach(function(o){
- copy.pushObject(grouping.create(o));
- });
- }
-
- this.set('workingCopy', copy);
- }.observes('model'),
-
- moveItem: function(item, delta){
- var copy = this.get('workingCopy');
- var index = copy.indexOf(item);
- if (index + delta < 0 || index + delta >= copy.length){
- return;
- }
-
- copy.removeAt(index);
- copy.insertAt(index+delta, item);
- },
-
- actions: {
- up: function(item){
- this.moveItem(item, -1);
- },
- down: function(item){
- this.moveItem(item, 1);
- },
- "delete": function(item){
- this.get('workingCopy').removeObject(item);
- },
- cancel: function(){
- this.set('model', null);
- this.set('workingCopy', null);
- this.send('closeModal');
- },
- edit: function(item){
- item.set("editing", true);
- },
- save: function(item){
- item.set("editing", false);
- },
- add: function(){
- var obj = Em.Object.create({editing: true, name: "Enter Name"});
- this.get('workingCopy').pushObject(obj);
- },
- saveAll: function(){
- var self = this;
- var items = this.get('workingCopy');
- var groupIds = items.map(function(i){return i.get("id") || -1});
- var names = items.map(function(i){return i.get("name")});
-
- Discourse.ajax('/admin/badges/badge_groupings',{
- data: {ids: groupIds, names: names},
- method: 'POST'
- }).then(function(data){
- items = self.get("model");
- items.clear();
- data.badge_groupings.forEach(function(g){
- items.pushObject(Em.Object.create(g));
- });
- self.set('model', null);
- self.set('workingCopy', null);
- self.send('closeModal');
- },function(){
- // TODO we can do better
- bootbox.alert("Something went wrong");
- });
- }
- }
-});
View
@@ -1,3 +0,0 @@
-import AdminEmailSkippedController from "admin/controllers/admin-email-skipped";
-
-export default AdminEmailSkippedController.extend();
View
@@ -0,0 +1,9 @@
+import AdminEmailLogsController from 'admin/controllers/admin-email-logs';
+import debounce from 'discourse/lib/debounce';
+import EmailLog from 'admin/models/email-log';
+
+export default AdminEmailLogsController.extend({
+ filterEmailLogs: debounce(function() {
+ EmailLog.findAll(this.get("filter")).then(logs => this.set("model", logs));
+ }, 250).observes("filter.{user,address,type,skipped_reason}")
+});
View
@@ -0,0 +1,21 @@
+import IncomingEmail from 'admin/models/incoming-email';
+
+export default Ember.Controller.extend({
+ loading: false,
+
+ actions: {
+
+ loadMore() {
+ if (this.get("loading") || this.get("model.allLoaded")) { return; }
+ this.set('loading', true);
+
+ IncomingEmail.findAll(this.get("filter"), this.get("model.length"))
+ .then(incoming => {
+ if (incoming.length < 50) { this.get("model").set("allLoaded", true); }
+ this.get("model").addObjects(incoming);
+ }).finally(() => {
+ this.set('loading', false);
+ });
+ }
+ }
+});
View
@@ -1,6 +1,5 @@
-import DiscourseController from 'discourse/controllers/controller';
-
-export default DiscourseController.extend({
+import { ajax } from 'discourse/lib/ajax';
+export default Ember.Controller.extend({
/**
Is the "send test email" button disabled?
@@ -31,7 +30,7 @@ export default DiscourseController.extend({
});
var self = this;
- Discourse.ajax("/admin/email/test", {
+ ajax("/admin/email/test", {
type: 'POST',
data: { email_address: this.get('testEmailAddress') }
}).then(function () {
View
@@ -0,0 +1,20 @@
+import EmailLog from 'admin/models/email-log';
+
+export default Ember.Controller.extend({
+ loading: false,
+
+ actions: {
+ loadMore() {
+ if (this.get("loading") || this.get("model.allLoaded")) { return; }
+
+ this.set('loading', true);
+ return EmailLog.findAll(this.get("filter"), this.get("model.length"))
+ .then(logs => {
+ if (logs.length < 50) { this.get("model").set("allLoaded", true); }
+ this.get("model").addObjects(logs);
+ }).finally(() => {
+ this.set('loading', false);
+ });
+ }
+ }
+});
View
@@ -1,21 +1,51 @@
-import ObjectController from 'discourse/controllers/object';
+import EmailPreview from 'admin/models/email-preview';
+import { popupAjaxError } from 'discourse/lib/ajax-error';
-export default ObjectController.extend({
+export default Ember.Controller.extend({
+ username: null,
+ lastSeen: null,
+
+ emailEmpty: Ember.computed.empty('email'),
+ sendEmailDisabled: Ember.computed.or('emailEmpty', 'sendingEmail'),
+ showSendEmailForm: Ember.computed.notEmpty('model.html_content'),
+ htmlEmpty: Ember.computed.empty('model.html_content'),
actions: {
- refresh: function() {
- var model = this.get('model'),
- self = this;
+ refresh() {
+ const model = this.get('model');
+
+ this.set('loading', true);
+ this.set('sentEmail', false);
- self.set('loading', true);
- Discourse.EmailPreview.findDigest(this.get('lastSeen')).then(function (email) {
+ let username = this.get('username');
+ if (!username) {
+ username = this.currentUser.get('username');
+ this.set('username', username);
+ }
+
+ EmailPreview.findDigest(username, this.get('lastSeen')).then(email => {
model.setProperties(email.getProperties('html_content', 'text_content'));
- self.set('loading', false);
+ this.set('loading', false);
});
},
- toggleShowHtml: function() {
+ toggleShowHtml() {
this.toggleProperty('showHtml');
+ },
+
+ sendEmail() {
+ this.set('sendingEmail', true);
+ this.set('sentEmail', false);
+
+ EmailPreview.sendDigest(this.get('username'), this.get('lastSeen'), this.get('email')).then(result => {
+ if (result.errors) {
+ bootbox.alert(result.errors);
+ } else {
+ this.set('sentEmail', true);
+ }
+ }).catch(popupAjaxError).finally(() => {
+ this.set('sendingEmail', false);
+ });
}
}
View
@@ -0,0 +1,9 @@
+import AdminEmailIncomingsController from 'admin/controllers/admin-email-incomings';
+import debounce from 'discourse/lib/debounce';
+import IncomingEmail from 'admin/models/incoming-email';
+
+export default AdminEmailIncomingsController.extend({
+ filterIncomingEmails: debounce(function() {
+ IncomingEmail.findAll(this.get("filter")).then(incomings => this.set("model", incomings));
+ }, 250).observes("filter.{from,to,subject}")
+});
View
@@ -0,0 +1,9 @@
+import AdminEmailIncomingsController from 'admin/controllers/admin-email-incomings';
+import debounce from 'discourse/lib/debounce';
+import IncomingEmail from 'admin/models/incoming-email';
+
+export default AdminEmailIncomingsController.extend({
+ filterIncomingEmails: debounce(function() {
+ IncomingEmail.findAll(this.get("filter")).then(incomings => this.set("model", incomings));
+ }, 250).observes("filter.{from,to,subject,error}")
+});
View
@@ -1,11 +1,9 @@
-import DiscourseController from 'discourse/controllers/controller';
+import AdminEmailLogsController from 'admin/controllers/admin-email-logs';
+import debounce from 'discourse/lib/debounce';
+import EmailLog from 'admin/models/email-log';
-export default DiscourseController.extend({
-
- filterEmailLogs: Discourse.debounce(function() {
- var self = this;
- Discourse.EmailLog.findAll(this.get("filter")).then(function(logs) {
- self.set("model", logs);
- });
- }, 250).observes("filter.user", "filter.address", "filter.type", "filter.reply_key")
+export default AdminEmailLogsController.extend({
+ filterEmailLogs: debounce(function() {
+ EmailLog.findAll(this.get("filter")).then(logs => this.set("model", logs));
+ }, 250).observes("filter.{user,address,type,reply_key}")
});
View
@@ -1,10 +1,9 @@
-import DiscourseController from 'discourse/controllers/controller';
+import AdminEmailLogsController from 'admin/controllers/admin-email-logs';
+import debounce from 'discourse/lib/debounce';
+import EmailLog from 'admin/models/email-log';
-export default DiscourseController.extend({
- filterEmailLogs: Discourse.debounce(function() {
- var self = this;
- Discourse.EmailLog.findAll(this.get("filter")).then(function(logs) {
- self.set("model", logs);
- });
- }, 250).observes("filter.user", "filter.address", "filter.type", "filter.skipped_reason")
+export default AdminEmailLogsController.extend({
+ filterEmailLogs: debounce(function() {
+ EmailLog.findAll(this.get("filter")).then(logs => this.set("model", logs));
+ }, 250).observes("filter.{user,address,type,skipped_reason}")
});
View
@@ -0,0 +1,53 @@
+import computed from 'ember-addons/ember-computed-decorators';
+import { popupAjaxError } from 'discourse/lib/ajax-error';
+
+export default Ember.Controller.extend({
+ saved: false,
+ embedding: null,
+
+ // show settings if we have at least one created host
+ @computed('embedding.embeddable_hosts.@each.isCreated')
+ showSecondary() {
+ const hosts = this.get('embedding.embeddable_hosts');
+ return hosts.length && hosts.findBy('isCreated');
+ },
+
+ @computed('embedding.base_url')
+ embeddingCode(baseUrl) {
+
+ const html =
+`<div id='discourse-comments'></div>
+
+<script type="text/javascript">
+ DiscourseEmbed = { discourseUrl: '${baseUrl}/',
+ discourseEmbedUrl: 'REPLACE_ME' };
+
+ (function() {
+ var d = document.createElement('script'); d.type = 'text/javascript'; d.async = true;
+ d.src = DiscourseEmbed.discourseUrl + 'javascripts/embed.js';
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(d);
+ })();
+</script>`;
+
+ return html;
+ },
+
+ actions: {
+ saveChanges() {
+ const embedding = this.get('embedding');
+ const updates = embedding.getProperties(embedding.get('fields'));
+
+ this.set('saved', false);
+ this.get('embedding').update(updates).then(() => this.set('saved', true)).catch(popupAjaxError);
+ },
+
+ addHost() {
+ const host = this.store.createRecord('embeddable-host');
+ this.get('embedding.embeddable_hosts').pushObject(host);
+ },
+
+ deleteHost(host) {
+ this.get('embedding.embeddable_hosts').removeObject(host);
+ }
+ }
+});
View
@@ -1,20 +1,27 @@
-export default Ember.ArrayController.extend({
- sortProperties: ["name"],
+import { ajax } from 'discourse/lib/ajax';
+export default Ember.Controller.extend({
+ sortedEmojis: Ember.computed.sort('model', 'emojiSorting'),
+ emojiSorting: ['name'],
actions: {
- emojiUploaded: function (emoji) {
- this.pushObject(emoji);
+ emojiUploaded(emoji) {
+ emoji.url += "?t=" + new Date().getTime();
+ this.get('model').pushObject(Ember.Object.create(emoji));
},
- destroy: function(emoji) {
- var self = this;
- return bootbox.confirm(I18n.t("admin.emoji.delete_confirm", { name: emoji.name }), I18n.t("no_value"), I18n.t("yes_value"), function (destroy) {
- if (destroy) {
- return Discourse.ajax("/admin/customize/emojis/" + emoji.name, { type: "DELETE" }).then(function() {
- self.removeObject(emoji);
- });
+ destroy(emoji) {
+ return bootbox.confirm(
+ I18n.t("admin.emoji.delete_confirm", { name: emoji.get("name") }),
+ I18n.t("no_value"),
+ I18n.t("yes_value"),
+ destroy => {
+ if (destroy) {
+ return ajax("/admin/customize/emojis/" + emoji.get("name"), { type: "DELETE" }).then(() => {
+ this.get('model').removeObject(emoji);
+ });
+ }
}
- });
+ );
}
}
});
View
@@ -1,41 +0,0 @@
-export default Ember.ArrayController.extend({
- query: null,
-
- adminOldFlagsView: Em.computed.equal("query", "old"),
- adminActiveFlagsView: Em.computed.equal("query", "active"),
-
- actions: {
- disagreeFlags(flaggedPost) {
- var self = this;
- flaggedPost.disagreeFlags().then(function () {
- self.removeObject(flaggedPost);
- }, function () {
- bootbox.alert(I18n.t("admin.flags.error"));
- });
- },
-
- deferFlags(flaggedPost) {
- var self = this;
- flaggedPost.deferFlags().then(function () {
- self.removeObject(flaggedPost);
- }, function () {
- bootbox.alert(I18n.t("admin.flags.error"));
- });
- },
-
- doneTopicFlags(item) {
- this.send("disagreeFlags", item);
- },
- },
-
- loadMore(){
- var flags = this.get("model");
- return Discourse.FlaggedPost.findAll(this.get("query"),flags.length+1).then(function(data){
- if(data.length===0){
- flags.set("allLoaded",true);
- }
- flags.addObjects(data);
- });
- }
-
-});
View
@@ -1,113 +0,0 @@
-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"),
-
- 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"),
-
- showingFirst: Em.computed.lte("currentPage", 1),
- showingLast: Discourse.computed.propertyEqual("currentPage", "totalPages"),
-
- aliasLevelOptions: function() {
- return [
- { name: I18n.t("groups.alias_levels.nobody"), value: 0 },
- { name: I18n.t("groups.alias_levels.mods_and_admins"), value: 2 },
- { name: I18n.t("groups.alias_levels.members_mods_and_admins"), value: 3 },
- { name: I18n.t("groups.alias_levels.everyone"), value: 99 }
- ];
- }.property(),
-
- actions: {
- next: function() {
- if (this.get("showingLast")) { return; }
-
- var group = this.get("model"),
- offset = Math.min(group.get("offset") + group.get("limit"), group.get("user_count"));
-
- group.set("offset", offset);
-
- return group.findMembers();
- },
-
- previous: function() {
- if (this.get("showingFirst")) { return; }
-
- var group = this.get("model"),
- offset = Math.max(group.get("offset") - group.get("limit"), 0);
-
- group.set("offset", offset);
-
- return group.findMembers();
- },
-
- removeMember: function(member) {
- var self = this,
- message = I18n.t("admin.groups.delete_member_confirm", { username: member.get("username"), group: this.get("name") });
- return bootbox.confirm(message, I18n.t("no_value"), I18n.t("yes_value"), function(confirm) {
- if (confirm) {
- self.get("model").removeMember(member);
- }
- });
- },
-
- addMembers: function() {
- if (Em.isEmpty(this.get("usernames"))) { return; }
- this.get("model").addMembers(this.get("usernames"));
- // clear the user selector
- this.set("usernames", null);
- },
-
- save: function() {
- var self = this,
- group = this.get('model'),
- groupsController = this.get("controllers.adminGroupsType");
-
- this.set('disableSave', true);
-
- var promise;
- if (group.get("id")) {
- promise = group.save();
- } else {
- promise = group.create().then(function() {
- groupsController.addObject(group);
- });
- }
- promise.then(function() {
- self.transitionToRoute("adminGroup", group);
- }, function(e) {
- var message = $.parseJSON(e.responseText).errors;
- bootbox.alert(message);
- }).finally(function() {
- self.set('disableSave', false);
- });
- },
-
- destroy: function() {
- var group = this.get('model'),
- groupsController = this.get('controllers.adminGroupsType'),
- self = this;
-
- this.set('disableSave', true);
-
- bootbox.confirm(I18n.t("admin.groups.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(confirmed) {
- if (confirmed) {
- group.destroy().then(function() {
- groupsController.get('model').removeObject(group);
- self.transitionToRoute('adminGroups.index');
- }, function() {
- bootbox.alert(I18n.t("admin.groups.delete_failed"));
- }).finally(function() {
- self.set('disableSave', false);
- });
- }
- });
- }
- }
-});
View
@@ -1,16 +0,0 @@
-export default Ember.ArrayController.extend({
- sortProperties: ['name'],
- refreshingAutoGroups: false,
-
- actions: {
- refreshAutoGroups: function(){
- var self = this;
- this.set('refreshingAutoGroups', true);
- Discourse.ajax('/admin/groups/refresh_automatic_groups', {type: 'POST'}).then(function() {
- self.transitionToRoute("adminGroupsType", "automatic").then(function() {
- self.set('refreshingAutoGroups', false);
- });
- });
- }
- }
-});
View
@@ -1,72 +0,0 @@
-export default Ember.ObjectController.extend({
- editing: false,
- savedIpAddress: null,
-
- isRange: function() {
- return this.get("ip_address").indexOf("/") > 0;
- }.property("ip_address"),
-
- actions: {
- allow: function(record) {
- record.set('action_name', 'do_nothing');
- this.send('save', record);
- },
-
- block: function(record) {
- record.set('action_name', 'block');
- this.send('save', record);
- },
-
- edit: function() {
- if (!this.get('editing')) {
- this.savedIpAddress = this.get('ip_address');
- }
- this.set('editing', true);
- },
-
- cancel: function() {
- if (this.get('savedIpAddress') && this.get('editing')) {
- this.set('ip_address', this.get('savedIpAddress'));
- }
- this.set('editing', false);
- },
-
- save: function(record) {
- var self = this;
- var wasEditing = this.get('editing');
- this.set('editing', false);
- record.save().then(function(saved){
- if (saved.success) {
- self.set('savedIpAddress', null);
- } else {
- bootbox.alert(saved.errors);
- if (wasEditing) self.set('editing', true);
- }
- }, function(e){
- if (e.responseJSON && e.responseJSON.errors) {
- bootbox.alert(I18n.t("generic_error_with_reason", {error: e.responseJSON.errors.join('. ')}));
- } else {
- bootbox.alert(I18n.t("generic_error"));
- }
- if (wasEditing) self.set('editing', true);
- });
- },
-
- destroy: function(record) {
- var self = this;
- return bootbox.confirm(I18n.t("admin.logs.screened_ips.delete_confirm", {ip_address: record.get('ip_address')}), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
- if (result) {
- record.destroy().then(function(deleted) {
- if (deleted) {
- self.get("parentController.content").removeObject(record);
- } else {
- bootbox.alert(I18n.t("generic_error"));
- }
- }, function(e){
- bootbox.alert(I18n.t("generic_error_with_reason", {error: "http: " + e.status + " - " + e.body}));
- });
- }
- });
- }
- }
-});
View
@@ -1,27 +1,28 @@
+import { exportEntity } from 'discourse/lib/export-csv';
import { outputExportResult } from 'discourse/lib/export-result';
+import ScreenedEmail from 'admin/models/screened-email';
-export default Ember.ArrayController.extend(Discourse.Presence, {
+export default Ember.Controller.extend({
loading: false,
actions: {
clearBlock(row){
- row.clearBlock().then(function(){
+ row.clearBlock().then(function() {
// feeling lazy
window.location.reload();
});
},
exportScreenedEmailList() {
- Discourse.ExportCsv.exportScreenedEmailList().then(outputExportResult);
+ exportEntity('screened_email').then(outputExportResult);
}
},
show() {
- var self = this;
- self.set('loading', true);
- Discourse.ScreenedEmail.findAll().then(function(result) {
- self.set('model', result);
- self.set('loading', false);
+ this.set('loading', true);
+ ScreenedEmail.findAll().then(result => {
+ this.set('model', result);
+ this.set('loading', false);
});
}
});
View
@@ -1,20 +1,88 @@
+import debounce from 'discourse/lib/debounce';
import { outputExportResult } from 'discourse/lib/export-result';
+import { exportEntity } from 'discourse/lib/export-csv';
+import ScreenedIpAddress from 'admin/models/screened-ip-address';
-export default Ember.ArrayController.extend(Discourse.Presence, {
+export default Ember.Controller.extend({
loading: false,
- itemController: 'admin-log-screened-ip-address',
filter: null,
+ savedIpAddress: null,
- show: Discourse.debounce(function() {
- var self = this;
- self.set('loading', true);
- Discourse.ScreenedIpAddress.findAll(this.get("filter")).then(function(result) {
- self.set('model', result);
- self.set('loading', false);
- });
+ show: debounce(function() {
+ this.set('loading', true);
+ ScreenedIpAddress.findAll(this.get("filter"))
+ .then(result => {
+ this.set('model', result);
+ this.set('loading', false);
+ });
}, 250).observes("filter"),
actions: {
+ allow(record) {
+ record.set('action_name', 'do_nothing');
+ record.save();
+ },
+
+ block(record) {
+ record.set('action_name', 'block');
+ record.save();
+ },
+
+ edit(record) {
+ if (!record.get('editing')) {
+ this.set("savedIpAddress", record.get('ip_address'));
+ }
+ record.set('editing', true);
+ },
+
+ cancel(record) {
+ if (this.get('savedIpAddress') && record.get('editing')) {
+ record.set('ip_address', this.get('savedIpAddress'));
+ }
+ record.set('editing', false);
+ },
+
+ save(record) {
+ const wasEditing = record.get('editing');
+ record.set('editing', false);
+ record.save().then(saved => {
+ if (saved.success) {
+ this.set('savedIpAddress', null);
+ } else {
+ bootbox.alert(saved.errors);
+ if (wasEditing) record.set('editing', true);
+ }
+ }).catch(e => {
+ if (e.responseJSON && e.responseJSON.errors) {
+ bootbox.alert(I18n.t("generic_error_with_reason", {error: e.responseJSON.errors.join('. ')}));
+ } else {
+ bootbox.alert(I18n.t("generic_error"));
+ }
+ if (wasEditing) record.set('editing', true);
+ });
+ },
+
+ destroy(record) {
+ return bootbox.confirm(
+ I18n.t("admin.logs.screened_ips.delete_confirm", { ip_address: record.get('ip_address') }),
+ I18n.t("no_value"),
+ I18n.t("yes_value"),
+ result => {
+ if (result) {
+ record.destroy().then(deleted => {
+ if (deleted) {
+ this.get("model").removeObject(record);
+ } else {
+ bootbox.alert(I18n.t("generic_error"));
+ }
+ }).catch(e => {
+ bootbox.alert(I18n.t("generic_error_with_reason", {error: "http: " + e.status + " - " + e.body}));
+ });
+ }
+ }
+ );
+ },
+
recordAdded(arg) {
this.get("model").unshiftObject(arg);
},
@@ -24,7 +92,7 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
return bootbox.confirm(I18n.t("admin.logs.screened_ips.roll_up_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function (confirmed) {
if (confirmed) {
self.set("loading", true);
- return Discourse.ScreenedIpAddress.rollUp().then(function(results) {
+ return ScreenedIpAddress.rollUp().then(function(results) {
if (results && results.subnets) {
if (results.subnets.length > 0) {
self.send("show");
@@ -40,7 +108,7 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
},
exportScreenedIpList() {
- Discourse.ExportCsv.exportScreenedIpList().then(outputExportResult);
+ exportEntity('screened_ip').then(outputExportResult);
}
}
});
View
@@ -1,20 +1,21 @@
+import { exportEntity } from 'discourse/lib/export-csv';
import { outputExportResult } from 'discourse/lib/export-result';
+import ScreenedUrl from 'admin/models/screened-url';
-export default Ember.ArrayController.extend(Discourse.Presence, {
+export default Ember.Controller.extend({
loading: false,
show() {
- const self = this;
- self.set('loading', true);
- Discourse.ScreenedUrl.findAll().then(function(result) {
- self.set('model', result);
- self.set('loading', false);
+ this.set('loading', true);
+ ScreenedUrl.findAll().then(result => {
+ this.set('model', result);
+ this.set('loading', false);
});
},
actions: {
exportScreenedUrlList() {
- Discourse.ExportCsv.exportScreenedUrlList().then(outputExportResult);
+ exportEntity('screened_url').then(outputExportResult);
}
}
});
View
@@ -1,11 +1,24 @@
+import { exportEntity } from 'discourse/lib/export-csv';
import { outputExportResult } from 'discourse/lib/export-result';
+import StaffActionLog from 'admin/models/staff-action-log';
-export default Ember.ArrayController.extend(Discourse.Presence, {
+export default Ember.Controller.extend({
loading: false,
filters: null,
+ userHistoryActions: [],
filtersExists: Ember.computed.gt('filterCount', 0),
+ filterActionIdChanged: function(){
+ const filterActionId = this.get('filterActionId');
+ if (filterActionId) {
+ this._changeFilters({
+ action_name: this.get('userHistoryActions').findBy("id", parseInt(filterActionId,10)).name_raw,
+ action_id: filterActionId
+ });
+ }
+ }.observes('filterActionId'),
+
actionFilter: function() {
var name = this.get('filters.action_name');
if (name) {
@@ -17,8 +30,7 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
showInstructions: Ember.computed.gt('model.length', 0),
- refresh: function() {
- var self = this;
+ _refresh() {
this.set('loading', true);
var filters = this.get('filters'),
@@ -35,21 +47,36 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
});
this.set('filterCount', count);
- Discourse.StaffActionLog.findAll(params).then(function(result) {
- self.set('model', result);
- }).finally(function() {
- self.set('loading', false);
+ StaffActionLog.findAll(params).then((result) => {
+ this.set('model', result.staff_action_logs);
+ if (this.get('userHistoryActions').length === 0) {
+ let actionTypes = result.user_history_actions.map(pair => {
+ return {
+ id: pair.id,
+ name: I18n.t("admin.logs.staff_actions.actions." + pair.name),
+ name_raw: pair.name
+ };
+ });
+ actionTypes = _.sortBy(actionTypes, row => row.name);
+ this.set('userHistoryActions', actionTypes);
+ }
+ }).finally(()=>{
+ this.set('loading', false);
});
},
+ scheduleRefresh() {
+ Ember.run.scheduleOnce('afterRender', this, this._refresh);
+ },
+
resetFilters: function() {
this.set('filters', Ember.Object.create());
- this.refresh();
+ this.scheduleRefresh();
}.on('init'),
_changeFilters: function(props) {
this.get('filters').setProperties(props);
- this.refresh();
+ this.scheduleRefresh();
},
actions: {
@@ -61,13 +88,15 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
changed.action_name = null;
changed.action_id = null;
changed.custom_type = null;
+ this.set("filterActionId", null);
} else {
changed[key] = null;
}
this._changeFilters(changed);
},
- clearAllFilters: function() {
+ clearAllFilters() {
+ this.set("filterActionId", null);
this.resetFilters();
},
@@ -92,7 +121,7 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
},
exportStaffActionLogs: function() {
- Discourse.ExportCsv.exportStaffActionLogs().then(outputExportResult);
+ exportEntity('staff_action').then(outputExportResult);
}
}
});
View
@@ -0,0 +1,36 @@
+import debounce from 'discourse/lib/debounce';
+import Permalink from 'admin/models/permalink';
+
+export default Ember.Controller.extend({
+ loading: false,
+ filter: null,
+
+ show: debounce(function() {
+ Permalink.findAll(this.get("filter")).then(result => {
+ this.set('model', result);
+ this.set('loading', false);
+ });
+ }, 250).observes("filter"),
+
+ actions: {
+ recordAdded(arg) {
+ this.get("model").unshiftObject(arg);
+ },
+
+ destroy: function(record) {
+ return bootbox.confirm(I18n.t("admin.permalink.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
+ if (result) {
+ record.destroy().then(deleted => {
+ if (deleted) {
+ this.get('model').removeObject(record);
+ } else {
+ bootbox.alert(I18n.t("generic_error"));
+ }
+ }, function(){
+ bootbox.alert(I18n.t("generic_error"));
+ });
+ }
+ });
+ }
+ }
+});
View
@@ -1,6 +1,9 @@
-export default Ember.ArrayController.extend({
-
+export default Ember.Controller.extend({
adminRoutes: function() {
- return this.get('model').map(p => p.admin_route).compact();
+ return this.get('model').map(p => {
+ if (p.get('enabled')) {
+ return p.admin_route;
+ }
+ }).compact();
}.property()
});
View
@@ -1,28 +1,84 @@
-export default Ember.ObjectController.extend({
- viewMode: 'table',
+import { exportEntity } from 'discourse/lib/export-csv';
+import { outputExportResult } from 'discourse/lib/export-result';
+import Report from 'admin/models/report';
+import computed from 'ember-addons/ember-computed-decorators';
+
+export default Ember.Controller.extend({
+ queryParams: ["mode", "start_date", "end_date", "category_id", "group_id"],
+ viewMode: 'graph',
viewingTable: Em.computed.equal('viewMode', 'table'),
- viewingBarChart: Em.computed.equal('viewMode', 'barChart'),
+ viewingGraph: Em.computed.equal('viewMode', 'graph'),
startDate: null,
endDate: null,
+ categoryId: null,
+ groupId: null,
refreshing: false,
+ @computed()
+ categoryOptions() {
+ const arr = [{name: I18n.t('category.all'), value: 'all'}];
+ return arr.concat(Discourse.Site.currentProp('sortedCategories').map((i) => {return {name: i.get('name'), value: i.get('id')};}));
+ },
+
+ @computed()
+ groupOptions() {
+ const arr = [{name: I18n.t('admin.dashboard.reports.groups'), value: 'all'}];
+ return arr.concat(this.site.groups.map((i) => {return {name: i['name'], value: i['id']};}));
+ },
+
+ @computed('model.type')
+ showCategoryOptions(modelType) {
+ return [
+ 'topics',
+ 'posts',
+ 'time_to_first_response_total',
+ 'topics_with_no_response',
+ 'flags',
+ 'likes',
+ 'bookmarks'
+ ].includes(modelType);
+ },
+
+ @computed('model.type')
+ showGroupOptions(modelType) {
+ return modelType === "visits" || modelType === "signups" || modelType === "profile_views";
+ },
+
actions: {
- refreshReport: function() {
- var self = this;
- this.set('refreshing', true);
- Discourse.Report.find(this.get('type'), this.get('startDate'), this.get('endDate')).then(function(r) {
- self.set('model', r);
- }).finally(function() {
- self.set('refreshing', false);
+ refreshReport() {
+ var q;
+ this.set("refreshing", true);
+
+ this.setProperties({
+ 'start_date': this.get('startDate'),
+ 'end_date': this.get('endDate'),
+ 'category_id': this.get('categoryId'),
});
+
+ if (this.get('groupId')){
+ this.set('group_id', this.get('groupId'));
+ }
+
+ q = Report.find(this.get("model.type"), this.get("startDate"), this.get("endDate"), this.get("categoryId"), this.get("groupId"));
+ q.then(m => this.set("model", m)).finally(() => this.set("refreshing", false));
},
- viewAsTable: function() {
+ viewAsTable() {
this.set('viewMode', 'table');
},
- viewAsBarChart: function() {
- this.set('viewMode', 'barChart');
+ viewAsGraph() {
+ this.set('viewMode', 'graph');
+ },
+
+ exportCsv() {
+ exportEntity('report', {
+ name: this.get("model.type"),
+ start_date: this.get('startDate'),
+ end_date: this.get('endDate'),
+ category_id: this.get('categoryId') === 'all' ? undefined : this.get('categoryId'),
+ group_id: this.get('groupId') === 'all' ? undefined : this.get('groupId')
+ }).then(outputExportResult);
}
}
});
View
@@ -0,0 +1,11 @@
+export default Ember.Controller.extend({
+ loading: false,
+ period: "all",
+ searchType: "all",
+
+ searchTypeOptions: [
+ {id: 'all', name: I18n.t('admin.logs.search_logs.types.all_search_types')},
+ {id: 'header', name: I18n.t('admin.logs.search_logs.types.header')},
+ {id: 'full_page', name: I18n.t('admin.logs.search_logs.types.full_page')}
+ ]
+});
View
@@ -0,0 +1,13 @@
+export default Ember.Controller.extend({
+ loading: false,
+ term: null,
+ period: "quarterly",
+ searchType: "all",
+
+ searchTypeOptions: [
+ {id: 'all', name: I18n.t('admin.logs.search_logs.types.all_search_types')},
+ {id: 'header', name: I18n.t('admin.logs.search_logs.types.header')},
+ {id: 'full_page', name: I18n.t('admin.logs.search_logs.types.full_page')},
+ {id: 'click_through_only', name: I18n.t('admin.logs.search_logs.types.click_through_only')}
+ ]
+});
View
@@ -1,50 +1,16 @@
-export default Ember.ObjectController.extend({
+export default Ember.Controller.extend({
categoryNameKey: null,
- needs: ['adminSiteSettings'],
+ adminSiteSettings: Ember.inject.controller(),
filteredContent: function() {
if (!this.get('categoryNameKey')) { return []; }
- var category = this.get('controllers.adminSiteSettings.content').findProperty('nameKey', this.get('categoryNameKey'));
+ const category = (this.get('adminSiteSettings.model') || []).findBy('nameKey', this.get('categoryNameKey'));
if (category) {
return category.siteSettings;
} else {
return [];
}
- }.property('controllers.adminSiteSettings.content', 'categoryNameKey'),
-
- actions: {
-
- /**
- Reset a setting to its default value
-
- @method resetDefault
- @param {Discourse.SiteSetting} setting The setting we want to revert
- **/
- resetDefault: function(setting) {
- setting.set('value', setting.get('default'));
- setting.save();
- },
-
- /**
- Save changes to a site setting
-
- @method save
- @param {Discourse.SiteSetting} setting The setting we've changed
- **/
- save: function(setting) {
- setting.save();
- },
-
- /**
- Cancel changes to a site setting
-
- @method cancel
- @param {Discourse.SiteSetting} setting The setting we've changed but want to revert
- **/
- cancel: function(setting) {
- setting.resetValue();
- }
- }
+ }.property('adminSiteSettings.model', 'categoryNameKey')
});
View
@@ -1,19 +1,15 @@
-export default Ember.ArrayController.extend(Discourse.Presence, {
+import debounce from 'discourse/lib/debounce';
+
+export default Ember.Controller.extend({
filter: null,
onlyOverridden: false,
filtered: Ember.computed.notEmpty('filter'),
- /**
- The list of settings based on the current filters
-
- @property filterContent
- **/
- filterContent: Discourse.debounce(function() {
-
+ filterContentNow(category) {
// If we have no content, don't bother filtering anything
- if (!this.present('allSiteSettings')) return;
+ if (!!Ember.isEmpty(this.get('allSiteSettings'))) return;
- var filter;
+ let filter;
if (this.get('filter')) {
filter = this.get('filter').toLowerCase();
}
@@ -24,38 +20,56 @@ export default Ember.ArrayController.extend(Discourse.Presence, {
return;
}
- var self = this,
- matches,
- matchesGroupedByCategory = Em.A([{nameKey: 'all_results', name: I18n.t('admin.site_settings.categories.all_results'), siteSettings: []}]);
+ const all = {nameKey: 'all_results', name: I18n.t('admin.site_settings.categories.all_results'), siteSettings: []};
+ const matchesGroupedByCategory = [all];
- _.each(this.get('allSiteSettings'), function(settingsCategory) {
- matches = settingsCategory.siteSettings.filter(function(item) {
- if (self.get('onlyOverridden') && !item.get('overridden')) return false;
+ const matches = [];
+ this.get('allSiteSettings').forEach(settingsCategory => {
+ const siteSettings = settingsCategory.siteSettings.filter(item => {
+ if (this.get('onlyOverridden') && !item.get('overridden')) return false;
if (filter) {
if (item.get('setting').toLowerCase().indexOf(filter) > -1) return true;
if (item.get('setting').toLowerCase().replace(/_/g, ' ').indexOf(filter) > -1) return true;
if (item.get('description').toLowerCase().indexOf(filter) > -1) return true;
- if (item.get('value').toLowerCase().indexOf(filter) > -1) return true;
+ if ((item.get('value') || '').toLowerCase().indexOf(filter) > -1) return true;
return false;
} else {
return true;
}
});
- if (matches.length > 0) {
- matchesGroupedByCategory[0].siteSettings.pushObjects(matches);
+ if (siteSettings.length > 0) {
+ matches.pushObjects(siteSettings);
+ matchesGroupedByCategory.pushObject({
+ nameKey: settingsCategory.nameKey,
+ name: I18n.t('admin.site_settings.categories.' + settingsCategory.nameKey),
+ siteSettings,
+ count: siteSettings.length
+ });
}
});
+ all.siteSettings.pushObjects(matches.slice(0, 30));
+ all.count = matches.length;
+
this.set('model', matchesGroupedByCategory);
- this.transitionToRoute("adminSiteSettingsCategory", "all_results");
+ this.transitionToRoute("adminSiteSettingsCategory", category || "all_results");
+ },
+
+ filterContent: debounce(function() {
+ if (this.get("_skipBounce")) {
+ this.set("_skipBounce", false);
+ } else {
+ this.filterContentNow();
+ }
}, 250).observes('filter', 'onlyOverridden'),
actions: {
- clearFilter: function() {
- this.setProperties({
- filter: '',
- onlyOverridden: false
- });
+ clearFilter() {
+ this.setProperties({ filter: '', onlyOverridden: false });
+ },
+
+ toggleMenu() {
+ $('.admin-detail').toggleClass('mobile-closed mobile-open');
}
}
View
@@ -1,19 +1,28 @@
-export default Ember.ObjectController.extend({
- saving: false,
- saved: false,
+import { popupAjaxError } from 'discourse/lib/ajax-error';
+import { bufferedProperty } from 'discourse/mixins/buffered-content';
- saveDisabled: function() {
- if (this.get('saving')) { return true; }
- if ((!this.get('allow_blank')) && Ember.isEmpty(this.get('value'))) { return true; }
- return false;
- }.property('saving', 'value'),
+export default Ember.Controller.extend(bufferedProperty('siteText'), {
+ saved: false,
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 buffered = this.get('buffered');
+ this.get('siteText').save(buffered.getProperties('value')).then(() => {
+ this.commitBuffer();
+ this.set('saved', true);
+ }).catch(popupAjaxError);
+ },
+
+ revertChanges() {
+ this.set('saved', false);
+ bootbox.confirm(I18n.t('admin.site_text.revert_confirm'), result => {
+ if (result) {
+ this.get('siteText').revert().then(props => {
+ const buffered = this.get('buffered');
+ buffered.setProperties(props);
+ this.commitBuffer();
+ }).catch(popupAjaxError);
+ }
});
}
}
View
@@ -0,0 +1,36 @@
+let lastSearch;
+let lastOverridden;
+
+export default Ember.Controller.extend({
+ searching: false,
+ siteTexts: null,
+ preferred: false,
+ queryParams: ['q', 'overridden'],
+
+ q: null,
+ overridden: null,
+
+ _performSearch() {
+ this.store.find('site-text', this.getProperties('q', 'overridden')).then(results => {
+ this.set('siteTexts', results);
+ }).finally(() => this.set('searching', false));
+ },
+
+ actions: {
+ edit(siteText) {
+ this.transitionToRoute('adminSiteText.edit', siteText.get('id'));
+ },
+
+ search(overridden) {
+ this.set('overridden', overridden);
+
+ const q = this.get('q');
+ if (q !== lastSearch || overridden !== lastOverridden) {
+ this.set('searching', true);
+ Ember.run.debounce(this, this._performSearch, 400);
+ lastSearch = q;
+ lastOverridden = overridden;
+ }
+ }
+ }
+});
View
@@ -1 +0,0 @@
-export default Ember.ArrayController.extend();
View
@@ -1,5 +0,0 @@
-import ModalFunctionality from 'discourse/mixins/modal-functionality';
-
-import ObjectController from 'discourse/controllers/object';
-
-export default ObjectController.extend(ModalFunctionality);
View
@@ -1,33 +0,0 @@
-import ModalFunctionality from 'discourse/mixins/modal-functionality';
-import Controller from 'discourse/controllers/controller';
-
-export default Controller.extend(ModalFunctionality, {
-
- needs: ["adminBackupsLogs"],
-
- _startBackup: function (withUploads) {
- var self = this;
- Discourse.User.currentProp("hideReadOnlyAlert", true);
- Discourse.Backup.start(withUploads).then(function() {
- self.get("controllers.adminBackupsLogs").clear();
- self.send("backupStarted");
- });
- },
-
- actions: {
-
- startBackup: function () {
- return this._startBackup();
- },
-
- startBackupWithoutUpload: function () {
- return this._startBackup(false);
- },
-
- cancel: function () {
- this.send("closeModal");
- }
-
- }
-
-});
View
@@ -1,28 +0,0 @@
-import ModalFunctionality from 'discourse/mixins/modal-functionality';
-
-import ObjectController from 'discourse/controllers/object';
-
-export default ObjectController.extend(ModalFunctionality, {
-
- submitDisabled: function() {
- return (!this.get('reason') || this.get('reason').length < 1);
- }.property('reason'),
-
- actions: {
- suspend: function() {
- if (this.get('submitDisabled')) return;
- var duration = parseInt(this.get('duration'), 10);
- if (duration > 0) {
- var self = this;
- this.send('hideModal');
- this.get('model').suspend(duration, this.get('reason')).then(function() {
- window.location.reload();
- }, function(e) {
- var error = I18n.t('admin.user.suspend_failed', { error: "http: " + e.status + " - " + e.body });
- bootbox.alert(error, function() { self.send('showModal'); });
- });
- }
- }
- }
-
-});
View
@@ -1,25 +1,21 @@
-/**
- This controller supports the interface for granting and revoking badges from
- individual users.
-
- @class AdminUserBadgesController
- @extends Ember.ArrayController
- @namespace Discourse
- @module Discourse
-**/
-export default Ember.ArrayController.extend({
- needs: ["adminUser"],
- user: Em.computed.alias('controllers.adminUser'),
- sortProperties: ['granted_at'],
- sortAscending: false,
+import GrantBadgeController from "discourse/mixins/grant-badge-controller";
+
+export default Ember.Controller.extend(GrantBadgeController, {
+ adminUser: Ember.inject.controller(),
+ user: Ember.computed.alias('adminUser.model'),
+ userBadges: Ember.computed.alias('model'),
+ allBadges: Ember.computed.alias('badges'),
+
+ sortedBadges: Ember.computed.sort('model', 'badgeSortOrder'),
+ badgeSortOrder: ['granted_at:desc'],
groupedBadges: function(){
- const badges = this.get('model');
+ const allBadges = this.get('model');
- var grouped = _.groupBy(badges, badge => badge.badge_id);
+ var grouped = _.groupBy(allBadges, badge => badge.badge_id);
var expanded = [];
- const expandedBadges = badges.get('expandedBadges');
+ const expandedBadges = allBadges.get('expandedBadges');
_(grouped).each(function(badges){
var lastGranted = badges[0].granted_at;
@@ -45,39 +41,7 @@ export default Ember.ArrayController.extend({
});
return _(expanded).sortBy(group => group.granted_at).reverse().value();
-
-
- }.property('model', 'model.@each', 'model.expandedBadges.@each'),
-
- /**
- Array of badges that have not been granted to this user.
-
- @property grantableBadges
- @type {Boolean}
- **/
- grantableBadges: function() {
- var granted = {};
- this.get('model').forEach(function(userBadge) {
- granted[userBadge.get('badge_id')] = true;
- });
-
- var badges = [];
- this.get('badges').forEach(function(badge) {
- if (badge.get('multiple_grant') || !granted[badge.get('id')]) {
- badges.push(badge);
- }
- });
-
- return _.sortBy(badges, "name");
- }.property('badges.@each', 'model.@each'),
-
- /**
- Whether there are any badges that can be granted.
-
- @property noBadges
- @type {Boolean}
- **/
- noBadges: Em.computed.empty('grantableBadges'),
+ }.property('model', 'model.[]', 'model.expandedBadges.[]'),
actions: {
@@ -87,42 +51,28 @@ export default Ember.ArrayController.extend({
model.get('expandedBadges').pushObject(userBadge.badge.id);
},
- /**
- Grant the selected badge to the user.
-
- @method grantBadge
- @param {Integer} badgeId id of the badge we want to grant.
- **/
- grantBadge: function(badgeId) {
- var self = this;
- Discourse.UserBadge.grant(badgeId, this.get('user.username'), this.get('badgeReason')).then(function(userBadge) {
- self.set('badgeReason', '');
- self.pushObject(userBadge);
- Ember.run.next(function() {
- // Update the selected badge ID after the combobox has re-rendered.
- var newSelectedBadge = self.get('grantableBadges')[0];
- if (newSelectedBadge) {
- self.set('selectedBadgeId', newSelectedBadge.get('id'));
- }
+ grantBadge() {
+ this.grantBadge(this.get('selectedBadgeId'), this.get('user.username'), this.get('badgeReason'))
+ .then(() => {
+ this.set('badgeReason', '');
+ Ember.run.next(() => {
+ // Update the selected badge ID after the combobox has re-rendered.
+ const newSelectedBadge = this.get('grantableBadges')[0];
+ if (newSelectedBadge) {
+ this.set('selectedBadgeId', newSelectedBadge.get('id'));
+ }
+ });
+ }, function() {
+ // Failure
+ bootbox.alert(I18n.t('generic_error'));
});
- }, function() {
- // Failure
- bootbox.alert(I18n.t('generic_error'));
- });
},
- /**
- Revoke the selected userBadge.
-
- @method revokeBadge
- @param {Discourse.UserBadge} userBadge the `Discourse.UserBadge` instance that needs to be revoked.
- **/
- revokeBadge: function(userBadge) {
- var self = this;
- return bootbox.confirm(I18n.t("admin.badges.revoke_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
+ revokeBadge(userBadge) {
+ return bootbox.confirm(I18n.t("admin.badges.revoke_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
if (result) {
- userBadge.revoke().then(function() {
- self.get('model').removeObject(userBadge);
+ userBadge.revoke().then(() => {
+ this.get('model').removeObject(userBadge);
});
}
});
View
@@ -1,64 +0,0 @@
-import UserField from 'admin/models/user-field';
-import BufferedContent from 'discourse/mixins/buffered-content';
-
-export default Ember.ObjectController.extend(BufferedContent, {
- needs: ['admin-user-fields'],
- editing: Ember.computed.empty('id'),
-
- fieldName: function() {
- return UserField.fieldTypeById(this.get('field_type')).get('name');
- }.property('field_type'),
-
- flags: function() {
- var ret = [];
- if (this.get('editable')) {
- ret.push(I18n.t('admin.user_fields.editable.enabled'));
- }
- if (this.get('required')) {
- ret.push(I18n.t('admin.user_fields.required.enabled'));
- }
- if (this.get('show_on_profile')) {
- ret.push(I18n.t('admin.user_fields.show_on_profile.enabled'));
- }
-
- return ret.join(', ');
- }.property('editable', 'required', 'show_on_profile'),
-
- actions: {
- save: function() {
- var self = this;
-
- var attrs = this.get('buffered').getProperties('name', 'description', 'field_type', 'editable', 'required', 'show_on_profile');
-
- this.get('model').save(attrs).then(function(res) {
- self.set('model.id', res.user_field.id);
- self.set('editing', false);
- self.commitBuffer();
- }).catch(function(e) {
- var msg = I18n.t("generic_error");
- if (e.responseJSON && e.responseJSON.errors) {
- msg = I18n.t("generic_error_with_reason", {error: e.responseJSON.errors.join('. ')});
- }
- bootbox.alert(msg);
- });
- },
-
- edit: function() {
- this.set('editing', true);
- },
-
- destroy: function() {
- this.get('controllers.admin-user-fields').send('destroy', this.get('model'));
- },
-
- cancel: function() {
- var id = this.get('id');
- if (Ember.isEmpty(id)) {
- this.get('controllers.admin-user-fields').send('destroy', this.get('model'));
- } else {
- this.rollbackBuffer();
- this.set('editing', false);
- }
- }
- }
-});
View
@@ -1,39 +1,56 @@
-import UserField from 'admin/models/user-field';
+import { popupAjaxError } from 'discourse/lib/ajax-error';
-export default Ember.ArrayController.extend({
+const MAX_FIELDS = 20;
+
+export default Ember.Controller.extend({
fieldTypes: null,
- createDisabled: Em.computed.gte('model.length', 3),
+ createDisabled: Em.computed.gte('model.length', MAX_FIELDS),
- userFieldsDescription: function() {
- return I18n.t('admin.user_fields.description');
- }.property(),
+ fieldSortOrder: ['position'],
+ sortedFields: Ember.computed.sort('model', 'fieldSortOrder'),
- userFieldsName: function() {
- return I18n.t('admin.user_fields.name');
- }.property(),
+ actions: {
+ createField() {
+ const f = this.store.createRecord('user-field', { field_type: 'text', position: MAX_FIELDS });
+ this.get('model').pushObject(f);
+ },
- _performDestroy: function(f, model) {
- return f.destroy().then(function() {
- model.removeObject(f);
- });
- },
+ moveUp(f) {
+ const idx = this.get('sortedFields').indexOf(f);
+ if (idx) {
+ const prev = this.get('sortedFields').objectAt(idx-1);
+ const prevPos = prev.get('position');
- actions: {
- createField: function() {
- this.pushObject(UserField.create({ field_type: 'text' }));
+ prev.update({ position: f.get('position') });
+ f.update({ position: prevPos });
+ }
+ },
+
+ moveDown(f) {
+ const idx = this.get('sortedFields').indexOf(f);
+ if (idx > -1) {
+ const next = this.get('sortedFields').objectAt(idx+1);
+ const nextPos = next.get('position');
+
+ next.update({ position: f.get('position') });
+ f.update({ position: nextPos });
+ }
},
- destroy: function(f) {
- var model = this.get('model'),
- self = this;
+ destroy(f) {
+ const model = this.get('model');
// Only confirm if we already been saved
if (f.get('id')) {
bootbox.confirm(I18n.t("admin.user_fields.delete_confirm"), function(result) {
- if (result) { self._performDestroy(f, model); }
+ if (result) {
+ f.destroyRecord().then(function() {
+ model.removeObject(f);
+ }).catch(popupAjaxError);
+ }
});
} else {
- self._performDestroy(f, model);
+ model.removeObject(f);
}
}
}
Oops, something went wrong.