| @@ -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') | ||
| - | ||
| -}); |
| @@ -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") | ||
| }); |
| @@ -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") | ||
| }); |
| @@ -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); | ||
| - }) | ||
| -}); |
| @@ -1 +1 @@ | ||
| -export default Ember.ArrayController.extend(); | ||
| +export default Ember.Controller.extend(); |
| @@ -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'); | ||
| + }); | ||
| + } | ||
| + }); | ||
| + } | ||
| + } | ||
| +}); |
| @@ -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); | ||
| - } | ||
| - }); | ||
| - } | ||
| - | ||
| - } | ||
| - | ||
| -}); |
| @@ -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); | ||
| + } | ||
| + }); | ||
| + } | ||
| + } | ||
| +}); |
| @@ -0,0 +1,6 @@ | ||
| +export default Ember.Controller.extend({ | ||
| + titleSorting: ['title'], | ||
| + emailTemplates: null, | ||
| + | ||
| + sortedTemplates: Ember.computed.sort('emailTemplates', 'titleSorting') | ||
| +}); |
| @@ -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'); | ||
| + }); | ||
| + } | ||
| + } | ||
| +}); |
| @@ -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'); | ||
| + }); | ||
| + } | ||
| + }); | ||
| + }, | ||
| + | ||
| + } | ||
| + | ||
| +}); |
| @@ -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()]; | ||
| + }); | ||
| + } | ||
| +}); |
| @@ -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")); | ||
| - }); | ||
| - } | ||
| - | ||
| - } | ||
| - | ||
| -}); |
| @@ -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"); | ||
| - }); | ||
| - } | ||
| - } | ||
| -}); |
| @@ -1,3 +0,0 @@ | ||
| -import AdminEmailSkippedController from "admin/controllers/admin-email-skipped"; | ||
| - | ||
| -export default AdminEmailSkippedController.extend(); |
| @@ -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}") | ||
| +}); |
| @@ -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); | ||
| + }); | ||
| + } | ||
| + } | ||
| +}); |
| @@ -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); | ||
| + }); | ||
| + } | ||
| + } | ||
| +}); |
| @@ -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}") | ||
| +}); |
| @@ -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}") | ||
| +}); |
| @@ -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}") | ||
| }); |
| @@ -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}") | ||
| }); |
| @@ -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); | ||
| + } | ||
| + } | ||
| +}); |
| @@ -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); | ||
| + }); | ||
| + } | ||
| } | ||
| - }); | ||
| + ); | ||
| } | ||
| } | ||
| }); |
| @@ -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); | ||
| - }); | ||
| - } | ||
| - | ||
| -}); |
| @@ -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); | ||
| - }); | ||
| - } | ||
| - }); | ||
| - } | ||
| - } | ||
| -}); |
| @@ -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); | ||
| - }); | ||
| - }); | ||
| - } | ||
| - } | ||
| -}); |
| @@ -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})); | ||
| - }); | ||
| - } | ||
| - }); | ||
| - } | ||
| - } | ||
| -}); |
| @@ -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); | ||
| }); | ||
| } | ||
| }); |
| @@ -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); | ||
| } | ||
| } | ||
| }); |
| @@ -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")); | ||
| + }); | ||
| + } | ||
| + }); | ||
| + } | ||
| + } | ||
| +}); |
| @@ -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() | ||
| }); |
| @@ -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); | ||
| } | ||
| } | ||
| }); |
| @@ -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')} | ||
| + ] | ||
| +}); |
| @@ -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')} | ||
| + ] | ||
| +}); |
| @@ -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') | ||
| }); |
| @@ -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; | ||
| + } | ||
| + } | ||
| + } | ||
| +}); |
| @@ -1 +0,0 @@ | ||
| -export default Ember.ArrayController.extend(); |
| @@ -1,5 +0,0 @@ | ||
| -import ModalFunctionality from 'discourse/mixins/modal-functionality'; | ||
| - | ||
| -import ObjectController from 'discourse/controllers/object'; | ||
| - | ||
| -export default ObjectController.extend(ModalFunctionality); |
| @@ -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"); | ||
| - } | ||
| - | ||
| - } | ||
| - | ||
| -}); |
| @@ -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'); }); | ||
| - }); | ||
| - } | ||
| - } | ||
| - } | ||
| - | ||
| -}); |
| @@ -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); | ||
| - } | ||
| - } | ||
| - } | ||
| -}); |