Version bump

This commit is contained in:
Neil Lalonde 2016-11-02 13:48:12 -04:00
commit ad7dbae939
402 changed files with 4567 additions and 2684 deletions

4
.gitignore vendored
View File

@ -30,9 +30,9 @@ config/discourse.pill
config/discourse.conf
# Ignore the default SQLite database and db dumps
*.sql
*.sql.gz
/db/*.sqlite3
/dbs/*.sql
/dbs/*.sql.gz
/db/structure.sql
# Ignore all logfiles and tempfiles.

View File

@ -147,7 +147,7 @@ GEM
librarian (0.1.2)
highline
thor (~> 0.15)
libv8 (5.0.71.48.3)
libv8 (5.3.332.38.1)
listen (0.7.3)
logster (1.2.5)
loofah (2.0.3)
@ -162,8 +162,8 @@ GEM
method_source (0.8.2)
mime-types (2.99.2)
mini_portile2 (2.1.0)
mini_racer (0.1.3)
libv8 (~> 5.0)
mini_racer (0.1.7)
libv8 (~> 5.3)
minitest (5.9.1)
mocha (1.1.0)
metaclass (~> 0.0.1)
@ -501,4 +501,4 @@ DEPENDENCIES
unicorn
BUNDLED WITH
1.13.4
1.13.6

View File

@ -1,8 +1,9 @@
/* global ace:true */
import loadScript from 'discourse/lib/load-script';
import { escapeExpression } from 'discourse/lib/utilities';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend({
export default Ember.Component.extend(bufferedRender({
mode: 'css',
classNames: ['ace-wrapper'],
_editor: null,
@ -14,7 +15,7 @@ export default Ember.Component.extend({
}
}.observes('content'),
render(buffer) {
buildBuffer(buffer) {
buffer.push("<div class='ace'>");
if (this.get('content')) {
buffer.push(escapeExpression(this.get('content')));
@ -66,4 +67,4 @@ export default Ember.Component.extend({
});
}.on('didInsertElement')
});
}));

View File

@ -1,18 +1,27 @@
import debounce from 'discourse/lib/debounce';
import { renderSpinner } from 'discourse/helpers/loading-spinner';
import { escapeExpression } from 'discourse/lib/utilities';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.View.extend({
export default Ember.Component.extend(bufferedRender({
classNames: ["admin-backups-logs"],
_initialize: function() { this._reset(); }.on("init"),
init() {
this._super();
this._reset();
},
_reset() {
this.setProperties({ formattedLogs: "", index: 0 });
},
_scrollDown() {
const $div = this.$()[0];
$div.scrollTop = $div.scrollHeight;
},
_updateFormattedLogs: debounce(function() {
const logs = this.get("controller.model");
const logs = this.get("logs");
if (logs.length === 0) {
this._reset(); // reset the cached logs whenever the model is reset
} else {
@ -26,11 +35,12 @@ export default Ember.View.extend({
// update the formatted logs & cache index
this.setProperties({ formattedLogs: formattedLogs, index: logs.length });
// force rerender
this.rerender();
this.rerenderBuffer();
}
}, 150).observes("controller.model.[]"),
Ember.run.scheduleOnce('afterRender', this, this._scrollDown);
}, 150).observes("logs.[]").on('init'),
render(buffer) {
buildBuffer(buffer) {
const formattedLogs = this.get("formattedLogs");
if (formattedLogs && formattedLogs.length > 0) {
buffer.push("<pre>");
@ -40,14 +50,8 @@ export default Ember.View.extend({
buffer.push("<p>" + I18n.t("admin.backups.logs.none") + "</p>");
}
// add a loading indicator
if (this.get("controller.status.model.isOperationRunning")) {
if (this.get("status.isOperationRunning")) {
buffer.push(renderSpinner('small'));
}
},
_forceScrollToBottom: function() {
const $div = this.$()[0];
$div.scrollTop = $div.scrollHeight;
}.on("didInsertElement")
});
}
}));

View File

@ -1,8 +1,8 @@
import computed from 'ember-addons/ember-computed-decorators';
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
classes: ["text-muted", "text-danger", "text-successful"],
icons: ["circle-o", "times-circle", "circle"],
@ -21,8 +21,8 @@ export default Ember.Component.extend(StringBuffer, {
return classes[statusId - 1];
},
renderString(buffer) {
buildBuffer(buffer) {
buffer.push(iconHTML(this.get('icon'), { class: this.get('class') }));
buffer.push(I18n.t(`admin.web_hooks.delivery_status.${this.get('status.name')}`));
}
});
}));

View File

@ -1,3 +1,5 @@
import { bufferedRender } from 'discourse-common/lib/buffered-render';
/*global Resumable:true */
/**
@ -10,7 +12,7 @@
uploadText="UPLOAD"
}}
**/
const ResumableUploadComponent = Ember.Component.extend(Discourse.StringBuffer, {
export default Ember.Component.extend(bufferedRender({
tagName: "button",
classNames: ["btn", "ru"],
classNameBindings: ["isUploading"],
@ -36,9 +38,9 @@ const ResumableUploadComponent = Ember.Component.extend(Discourse.StringBuffer,
}
}.property("isUploading", "progress"),
renderString: function(buffer) {
var icon = this.get("isUploading") ? "times" : "upload";
buffer.push("<i class='fa fa-" + icon + "'></i>");
buildBuffer(buffer) {
const icon = this.get("isUploading") ? "times" : "upload";
buffer.push(`<i class="fa fa-${icon}"></i>`);
buffer.push("<span class='ru-label'>" + this.get("text") + "</span>");
buffer.push("<span class='ru-progress' style='width:" + this.get("progress") + "%'></span>");
},
@ -117,6 +119,4 @@ const ResumableUploadComponent = Ember.Component.extend(Discourse.StringBuffer,
}
}.on("willDestroyElement")
});
export default ResumableUploadComponent;
}));

View File

@ -1,66 +1,30 @@
import ApiKey from 'admin/models/api-key';
/**
This controller supports the interface for dealing with API keys
@class AdminApiController
@extends Ember.ArrayController
@namespace Discourse
@module Discourse
**/
export default Ember.ArrayController.extend({
export default Ember.Controller.extend({
actions: {
/**
Generates a master api key
@method generateMasterKey
**/
generateMasterKey: function() {
var self = this;
ApiKey.generateMasterKey().then(function (key) {
self.get('model').pushObject(key);
});
generateMasterKey() {
ApiKey.generateMasterKey().then(key => this.get('model').pushObject(key));
},
/**
Creates an API key instance with internal user object
@method regenerateKey
@param {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) {
regenerateKey(key) {
bootbox.confirm(I18n.t("admin.api.confirm_regen"), I18n.t("no_value"), I18n.t("yes_value"), result => {
if (result) {
key.regenerate();
}
});
},
/**
Revokes an API key
@method revokeKey
@param {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) {
revokeKey(key) {
bootbox.confirm(I18n.t("admin.api.confirm_revoke"), I18n.t("no_value"), I18n.t("yes_value"), result => {
if (result) {
key.revoke().then(function() {
self.get('model').removeObject(key);
});
key.revoke().then(() => this.get('model').removeObject(key));
}
});
}
},
/**
Has a master key already been generated?
@property hasMasterKey
@type {Boolean}
**/
// Has a master key already been generated?
hasMasterKey: function() {
return !!this.get('model').findBy('user', null);
}.property('model.[]')

View File

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

View File

@ -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 File

@ -3,14 +3,14 @@ import BufferedContent from 'discourse/mixins/buffered-content';
import { propertyNotEqual } from 'discourse/lib/computed';
export default Ember.Controller.extend(BufferedContent, {
needs: ['admin-badges'],
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: propertyNotEqual('name', 'displayName'),
@ -30,16 +30,15 @@ export default Ember.Controller.extend(BufferedContent, {
}.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',
'long_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');
@ -51,54 +50,55 @@ export default Ember.Controller.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) {
var adminBadgesController = self.get('controllers.admin-badges');
if (!adminBadgesController.contains(model)) adminBadgesController.pushObject(model);
self.transitionToRoute('adminBadges.show', model.get('id'));
const adminBadges = this.get('adminBadges.model');
if (!adminBadges.contains(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(popupAjaxError).finally(function() {
self.set('saving', false);
self.set('savingStatus', '');
}).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 File

@ -1 +1 @@
export default Ember.ArrayController.extend();
export default Ember.Controller.extend();

View File

@ -1,4 +1,4 @@
export default Ember.ArrayController.extend({
export default Ember.Controller.extend({
onlyOverridden: false,
baseColorScheme: function() {
@ -13,8 +13,8 @@ export default Ember.ArrayController.extend({
return baseColorsHash;
}.property('baseColorScheme'),
removeSelected: function() {
this.removeObject(this.get('selectedItem'));
removeSelected() {
this.get('model').removeObject(this.get('selectedItem'));
this.set('selectedItem', null);
},
@ -26,8 +26,7 @@ export default Ember.ArrayController.extend({
return;
}
var matches = Em.A();
const matches = [];
_.each(this.get('selectedItem.colors'), function(color){
if (color.get('overridden')) matches.pushObject(color);
});
@ -58,10 +57,10 @@ export default Ember.ArrayController.extend({
this.filterContent();
},
newColorScheme: function() {
var newColorScheme = Em.copy(this.get('baseColorScheme'), true);
newColorScheme() {
const newColorScheme = Em.copy(this.get('baseColorScheme'), true);
newColorScheme.set('name', I18n.t('admin.customize.colors.new_name'));
this.pushObject(newColorScheme);
this.get('model').pushObject(newColorScheme);
this.send('selectColorScheme', newColorScheme);
this.set('onlyOverridden', false);
},
@ -86,10 +85,10 @@ export default Ember.ArrayController.extend({
this.updateEnabled();
},
copy: function(colorScheme) {
copy(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.get('model').pushObject(newColorScheme);
this.send('selectColorScheme', newColorScheme);
},

View File

@ -33,7 +33,7 @@ export default Ember.Controller.extend(activeSections, {
return !this.get('model.changed') || this.get('model.isSaving');
}.property('model.changed', 'model.isSaving'),
needs: ['adminCustomizeCssHtml'],
adminCustomizeCssHtml: Ember.inject.controller(),
undoPreviewUrl: url('/?preview-style='),
defaultStyleUrl: url('/?preview-style=default'),
@ -44,13 +44,12 @@ export default Ember.Controller.extend(activeSections, {
},
destroy() {
const self = this;
return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
return bootbox.confirm(I18n.t("admin.customize.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
if (result) {
const model = self.get('model');
model.destroyRecord().then(function() {
self.get('controllers.adminCustomizeCssHtml').get('model').removeObject(model);
self.transitionToRoute('adminCustomizeCssHtml');
const model = this.get('model');
model.destroyRecord().then(() => {
this.get('adminCustomizeCssHtml').get('model').removeObject(model);
this.transitionToRoute('adminCustomizeCssHtml');
});
}
});

View File

@ -9,7 +9,7 @@ export default Ember.Controller.extend({
@computed('embedding.embeddable_hosts.@each.isCreated')
showSecondary() {
const hosts = this.get('embedding.embeddable_hosts');
return hosts.length && hosts.findProperty('isCreated');
return hosts.length && hosts.findBy('isCreated');
},
@computed('embedding.base_url')

View File

@ -1,23 +1,23 @@
import { ajax } from 'discourse/lib/ajax';
export default Ember.ArrayController.extend({
sortProperties: ["name"],
export default Ember.Controller.extend({
sortedEmojis: Ember.computed.sort('model', 'emojiSorting'),
emojiSorting: ['name'],
actions: {
emojiUploaded(emoji) {
emoji.url += "?t=" + new Date().getTime();
this.pushObject(Ember.Object.create(emoji));
this.get('model').pushObject(Ember.Object.create(emoji));
},
destroy(emoji) {
const self = this;
return bootbox.confirm(
I18n.t("admin.emoji.delete_confirm", { name: emoji.get("name") }),
I18n.t("no_value"),
I18n.t("yes_value"),
function(destroy) {
destroy => {
if (destroy) {
return ajax("/admin/customize/emojis/" + emoji.get("name"), { type: "DELETE" }).then(function() {
self.removeObject(emoji);
return ajax("/admin/customize/emojis/" + emoji.get("name"), { type: "DELETE" }).then(() => {
this.get('model').removeObject(emoji);
});
}
}

View File

@ -1,6 +1,6 @@
import FlaggedPost from 'admin/models/flagged-post';
export default Ember.ArrayController.extend({
export default Ember.Controller.extend({
query: null,
adminOldFlagsView: Em.computed.equal("query", "old"),
@ -8,18 +8,16 @@ export default Ember.ArrayController.extend({
actions: {
disagreeFlags(flaggedPost) {
var self = this;
flaggedPost.disagreeFlags().then(function () {
self.removeObject(flaggedPost);
flaggedPost.disagreeFlags().then(() => {
this.get('model').removeObject(flaggedPost);
}, function () {
bootbox.alert(I18n.t("admin.flags.error"));
});
},
deferFlags(flaggedPost) {
var self = this;
flaggedPost.deferFlags().then(function () {
self.removeObject(flaggedPost);
flaggedPost.deferFlags().then(() => {
this.get('model').removeObject(flaggedPost);
}, function () {
bootbox.alert(I18n.t("admin.flags.error"));
});
@ -29,7 +27,7 @@ export default Ember.ArrayController.extend({
this.send("disagreeFlags", item);
},
loadMore(){
loadMore() {
const flags = this.get('model');
return FlaggedPost.findAll(this.get('query'), flags.length+1).then(data => {
if (data.length===0) {

View File

@ -4,7 +4,7 @@ import { escapeExpression } from 'discourse/lib/utilities';
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Controller.extend({
needs: ['adminGroupsType'],
adminGroupsType: Ember.inject.controller(),
disableSave: false,
savingStatus: '',
@ -131,13 +131,13 @@ export default Ember.Controller.extend({
save() {
const group = this.get('model'),
groupsController = this.get("controllers.adminGroupsType"),
groupsController = this.get("adminGroupsType"),
groupType = groupsController.get("type");
this.set('disableSave', true);
this.set('savingStatus', I18n.t('saving'));
let promise = group.get("id") ? group.save() : group.create().then(() => groupsController.addObject(group));
let promise = group.get("id") ? group.save() : group.create().then(() => groupsController.get('model').addObject(group));
promise.then(() => {
this.transitionToRoute("adminGroup", groupType, group.get('name'));
@ -148,7 +148,7 @@ export default Ember.Controller.extend({
destroy() {
const group = this.get('model'),
groupsController = this.get('controllers.adminGroupsType'),
groupsController = this.get('adminGroupsType'),
self = this;
if (!group.get('id')) {

View File

@ -1,18 +1,18 @@
import { ajax } from 'discourse/lib/ajax';
export default Ember.ArrayController.extend({
sortProperties: ['name'],
export default Ember.Controller.extend({
sortedGroups: Ember.computed.sort('model', 'groupSorting'),
groupSorting: ['name'],
refreshingAutoGroups: false,
isAuto: function(){
return this.get('type') === 'automatic';
}.property('type'),
isAuto: Ember.computed.equal('type', 'automatic'),
actions: {
refreshAutoGroups: function(){
var self = this;
refreshAutoGroups() {
this.set('refreshingAutoGroups', true);
ajax('/admin/groups/refresh_automatic_groups', {type: 'POST'}).then(function() {
self.transitionToRoute("adminGroupsType", "automatic").then(function() {
self.set('refreshingAutoGroups', false);
ajax('/admin/groups/refresh_automatic_groups', {type: 'POST'}).then(() => {
this.transitionToRoute("adminGroupsType", "automatic").then(() => {
this.set('refreshingAutoGroups', false);
});
});
}

View File

@ -2,12 +2,12 @@ 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({
export default Ember.Controller.extend({
loading: false,
actions: {
clearBlock(row){
row.clearBlock().then(function(){
row.clearBlock().then(function() {
// feeling lazy
window.location.reload();
});
@ -19,11 +19,10 @@ export default Ember.ArrayController.extend({
},
show() {
var self = this;
self.set('loading', true);
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 File

@ -3,7 +3,7 @@ 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({
export default Ember.Controller.extend({
loading: false,
filter: null,
savedIpAddress: null,
@ -63,16 +63,15 @@ export default Ember.ArrayController.extend({
},
destroy(record) {
const 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) {
result => {
if (result) {
record.destroy().then(deleted => {
if (deleted) {
self.get("content").removeObject(record);
this.get("model").removeObject(record);
} else {
bootbox.alert(I18n.t("generic_error"));
}

View File

@ -2,15 +2,14 @@ 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({
export default Ember.Controller.extend({
loading: false,
show() {
const self = this;
self.set('loading', true);
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);
});
},

View File

@ -2,7 +2,7 @@ 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({
export default Ember.Controller.extend({
loading: false,
filters: null,

View File

@ -1,16 +1,14 @@
import debounce from 'discourse/lib/debounce';
import Permalink from 'admin/models/permalink';
export default Ember.ArrayController.extend({
export default Ember.Controller.extend({
loading: false,
filter: null,
show: debounce(function() {
var self = this;
self.set('loading', true);
Permalink.findAll(self.get("filter")).then(function(result) {
self.set('model', result);
self.set('loading', false);
Permalink.findAll(this.get("filter")).then(result => {
this.set('model', result);
this.set('loading', false);
});
}, 250).observes("filter"),
@ -20,12 +18,11 @@ export default Ember.ArrayController.extend({
},
destroy: function(record) {
const self = this;
return bootbox.confirm(I18n.t("admin.permalink.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), function(result) {
return bootbox.confirm(I18n.t("admin.permalink.delete_confirm"), I18n.t("no_value"), I18n.t("yes_value"), result => {
if (result) {
record.destroy().then(function(deleted) {
record.destroy().then(deleted => {
if (deleted) {
self.removeObject(record);
this.get('model').removeObject(record);
} else {
bootbox.alert(I18n.t("generic_error"));
}

View File

@ -1,10 +1,9 @@
export default Ember.ArrayController.extend({
export default Ember.Controller.extend({
adminRoutes: function() {
return this.get('model').map(function(p) {
if (p.get('enabled')) {
return p.admin_route;
}
return this.get('model').map(p => {
if (p.get('enabled')) {
return p.admin_route;
}
}).compact();
}.property()
});

View File

@ -1,16 +1,16 @@
export default Ember.Controller.extend({
categoryNameKey: null,
needs: ['adminSiteSettings'],
adminSiteSettings: Ember.inject.controller(),
filteredContent: function() {
if (!this.get('categoryNameKey')) { return []; }
const 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')
}.property('adminSiteSettings.model', 'categoryNameKey')
});

View File

@ -1,6 +1,6 @@
import debounce from 'discourse/lib/debounce';
export default Ember.ArrayController.extend({
export default Ember.Controller.extend({
filter: null,
onlyOverridden: false,
filtered: Ember.computed.notEmpty('filter'),

View File

@ -1,10 +1,11 @@
import UserBadge from 'discourse/models/user-badge';
export default Ember.ArrayController.extend({
needs: ["adminUser"],
user: Em.computed.alias('controllers.adminUser.model'),
sortProperties: ['granted_at'],
sortAscending: false,
export default Ember.Controller.extend({
adminUser: Ember.inject.controller(),
user: Ember.computed.alias('adminUser.model'),
sortedBadges: Ember.computed.sort('model', 'badgeSortOrder'),
badgeSortOrder: ['granted_at:desc'],
groupedBadges: function(){
const allBadges = this.get('model');
@ -38,8 +39,6 @@ export default Ember.ArrayController.extend({
});
return _(expanded).sortBy(group => group.granted_at).reverse().value();
}.property('model', 'model.[]', 'model.expandedBadges.[]'),
/**
@ -80,22 +79,15 @@ 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;
UserBadge.grant(badgeId, this.get('user.username'), this.get('badgeReason')).then(function(userBadge) {
self.set('badgeReason', '');
self.pushObject(userBadge);
Ember.run.next(function() {
grantBadge(badgeId) {
UserBadge.grant(badgeId, this.get('user.username'), this.get('badgeReason')).then(userBadge => {
this.set('badgeReason', '');
this.get('model').pushObject(userBadge);
Ember.run.next(() => {
// Update the selected badge ID after the combobox has re-rendered.
var newSelectedBadge = self.get('grantableBadges')[0];
const newSelectedBadge = this.get('grantableBadges')[0];
if (newSelectedBadge) {
self.set('selectedBadgeId', newSelectedBadge.get('id'));
this.set('selectedBadgeId', newSelectedBadge.get('id'));
}
});
}, function() {
@ -104,12 +96,11 @@ export default Ember.ArrayController.extend({
});
},
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 File

@ -2,7 +2,7 @@ import debounce from 'discourse/lib/debounce';
import { i18n } from 'discourse/lib/computed';
import AdminUser from 'admin/models/admin-user';
export default Ember.ArrayController.extend({
export default Ember.Controller.extend({
query: null,
showEmails: false,
refreshing: false,
@ -19,7 +19,7 @@ export default Ember.ArrayController.extend({
selectedCount: function() {
var model = this.get('model');
if (!model || !model.length) return 0;
return model.filterProperty('selected').length;
return model.filterBy('selected').length;
}.property('model.@each.selected'),
selectAllChanged: function() {
@ -52,14 +52,14 @@ export default Ember.ArrayController.extend({
actions: {
approveUsers: function() {
AdminUser.bulkApprove(this.get('model').filterProperty('selected'));
AdminUser.bulkApprove(this.get('model').filterBy('selected'));
this._refreshUsers();
},
rejectUsers: function() {
var maxPostAge = this.siteSettings.delete_user_max_post_age;
var controller = this;
AdminUser.bulkReject(this.get('model').filterProperty('selected')).then(function(result){
AdminUser.bulkReject(this.get('model').filterBy('selected')).then(function(result){
var message = I18n.t("admin.users.reject_successful", {count: result.success});
if (result.failed > 0) {
message += ' ' + I18n.t("admin.users.reject_failures", {count: result.failed});

View File

@ -4,10 +4,10 @@ import computed from 'ember-addons/ember-computed-decorators';
import InputValidation from 'discourse/models/input-validation';
export default Ember.Controller.extend({
needs: ['adminWebHooks'],
eventTypes: Em.computed.alias('controllers.adminWebHooks.eventTypes'),
defaultEventTypes: Em.computed.alias('controllers.adminWebHooks.defaultEventTypes'),
contentTypes: Em.computed.alias('controllers.adminWebHooks.contentTypes'),
adminWebHooks: Ember.inject.controller(),
eventTypes: Ember.computed.alias('adminWebHooks.eventTypes'),
defaultEventTypes: Ember.computed.alias('adminWebHooks.defaultEventTypes'),
contentTypes: Ember.computed.alias('adminWebHooks.contentTypes'),
@computed('model.isSaving', 'saved', 'saveButtonDisabled')
savingStatus(isSaving, saved, saveButtonDisabled) {
@ -68,7 +68,7 @@ export default Ember.Controller.extend({
const saveWebHook = () => {
return model.save().then(() => {
this.set('saved', true);
this.get('controllers.adminWebHooks').get('model').addObject(model);
this.get('adminWebHooks').get('model').addObject(model);
}).catch(popupAjaxError);
};
@ -88,7 +88,7 @@ export default Ember.Controller.extend({
if (result) {
const model = this.get('model');
model.destroyRecord().then(() => {
this.get('controllers.adminWebHooks').get('model').removeObject(model);
this.get('adminWebHooks').get('model').removeObject(model);
this.transitionToRoute('adminWebHooks');
}).catch(popupAjaxError);
}

View File

@ -1,16 +1,15 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality';
export default Ember.Controller.extend(ModalFunctionality, {
needs: ["admin-flags-list"],
adminFlagsList: Ember.inject.controller(),
_agreeFlag: function (actionOnPost) {
var adminFlagController = this.get("controllers.admin-flags-list");
var post = this.get("content");
var self = this;
const adminFlagController = this.get("adminFlagsList");
const post = this.get("content");
return post.agreeFlags(actionOnPost).then(function () {
adminFlagController.removeObject(post);
self.send("closeModal");
return post.agreeFlags(actionOnPost).then(() => {
adminFlagController.get('model').removeObject(post);
this.send("closeModal");
}, function () {
bootbox.alert(I18n.t("admin.flags.error"));
});

View File

@ -1,11 +1,9 @@
import { escapeExpression } from 'discourse/lib/utilities';
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'),
sample: Ember.computed.alias('model.sample'),
errors: Ember.computed.alias('model.errors'),
count: Ember.computed.alias('model.grant_count'),
count_warning: function() {
if (this.get('count') <= 10) {

View File

@ -1,36 +1,31 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality';
export default Ember.Controller.extend(ModalFunctionality, {
needs: ["admin-flags-list"],
adminFlagsList: Ember.inject.controller(),
actions: {
deletePostDeferFlag() {
const adminFlagController = this.get("adminFlagsList");
const post = this.get("content");
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");
return post.deferFlags(true).then(() => {
adminFlagController.get('model').removeObject(post);
this.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;
deletePostAgreeFlag() {
const adminFlagController = this.get("adminFlagsList");
const post = this.get("content");
return post.agreeFlags("delete").then(function () {
adminFlagController.removeObject(post);
self.send("closeModal");
return post.agreeFlags("delete").then(() => {
adminFlagController.get('model').removeObject(post);
this.send("closeModal");
}, function () {
bootbox.alert(I18n.t("admin.flags.error"));
});
}
}
});

View File

@ -1,6 +1,5 @@
import { ajax } from 'discourse/lib/ajax';
export default Ember.Controller.extend({
needs: ['modal'],
modelChanged: function(){
const model = this.get('model');

View File

@ -2,31 +2,27 @@ import ModalFunctionality from 'discourse/mixins/modal-functionality';
import Backup from 'admin/models/backup';
export default Ember.Controller.extend(ModalFunctionality, {
needs: ["adminBackupsLogs"],
adminBackupsLogs: Ember.inject.controller(),
_startBackup: function (withUploads) {
var self = this;
Discourse.User.currentProp("hideReadOnlyAlert", true);
Backup.start(withUploads).then(function() {
self.get("controllers.adminBackupsLogs").clear();
self.send("backupStarted");
_startBackup(withUploads) {
this.currentUser.set('hideReadOnlyAlert', true);
Backup.start(withUploads).then(() => {
this.get("adminBackupsLogs.logs").clear();
this.send("backupStarted");
});
},
actions: {
startBackup: function () {
startBackup() {
this._startBackup();
},
startBackupWithoutUpload: function () {
startBackupWithoutUpload() {
this._startBackup(false);
},
cancel: function () {
cancel() {
this.send("closeModal");
}
}
});

View File

@ -2,7 +2,6 @@ import { ajax } from 'discourse/lib/ajax';
import PreloadStore from 'preload-store';
const Backup = Discourse.Model.extend({
destroy() {
return ajax("/admin/backups/" + this.get("filename"), { type: "DELETE" });
},
@ -13,11 +12,9 @@ const Backup = Discourse.Model.extend({
data: { client_id: window.MessageBus.clientId }
});
}
});
Backup.reopenClass({
find() {
return PreloadStore.getAndRemove("backups", () => ajax("/admin/backups.json"))
.then(backups => backups.map(backup => Backup.create(backup)));

View File

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

View File

@ -15,7 +15,7 @@ export default Discourse.Route.extend({
_processLogMessage(log) {
if (log.message === "[STARTED]") {
this.controllerFor("adminBackups").set("model.isOperationRunning", true);
this.controllerFor("adminBackupsLogs").clear();
this.controllerFor("adminBackupsLogs").get('logs').clear();
} else if (log.message === "[FAILED]") {
this.controllerFor("adminBackups").set("model.isOperationRunning", false);
bootbox.alert(I18n.t("admin.backups.operations.failed", { operation: log.operation }));
@ -27,7 +27,7 @@ export default Discourse.Route.extend({
window.location.pathname = Discourse.getURL("/");
}
} else {
this.controllerFor("adminBackupsLogs").pushObject(Em.Object.create(log));
this.controllerFor("adminBackupsLogs").get('logs').pushObject(Em.Object.create(log));
}
},

View File

@ -13,7 +13,7 @@ export default Ember.Route.extend({
name: I18n.t('admin.badges.new_badge')
});
}
return this.modelFor('adminBadges').findProperty('id', parseInt(params.badge_id));
return this.modelFor('adminBadges').findBy('id', parseInt(params.badge_id));
},
actions: {

View File

@ -5,21 +5,20 @@ import BadgeGrouping from 'discourse/models/badge-grouping';
export default Discourse.Route.extend({
_json: null,
model: function() {
var self = this;
return ajax('/admin/badges.json').then(function(json) {
self._json = json;
model() {
return ajax('/admin/badges.json').then(json => {
this._json = json;
return Badge.createFromJson(json);
});
},
setupController: function(controller, model) {
var json = this._json,
triggers = [],
badgeGroupings = [];
setupController(controller, model) {
const json = this._json;
const badgeTriggers = [];
const badgeGroupings = [];
_.each(json.admin_badges.triggers,function(v,k){
triggers.push({id: v, name: I18n.t('admin.badges.trigger_type.'+k)});
badgeTriggers.push({id: v, name: I18n.t('admin.badges.trigger_type.'+k)});
});
json.badge_groupings.forEach(function(badgeGroupingJson) {
@ -30,8 +29,8 @@ export default Discourse.Route.extend({
badgeGroupings: badgeGroupings,
badgeTypes: json.badge_types,
protectedSystemFields: json.admin_badges.protected_system_fields,
badgeTriggers: triggers,
model: model
badgeTriggers,
model
});
}
});

View File

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

View File

@ -3,7 +3,7 @@ import { scrollTop } from 'discourse/mixins/scroll-top';
export default Ember.Route.extend({
model(params) {
const all = this.modelFor('adminCustomizeEmailTemplates');
return all.findProperty('id', params.id);
return all.findBy('id', params.id);
},
setupController(controller, emailTemplate) {

View File

@ -7,8 +7,7 @@ export default Discourse.Route.extend({
return Group.create({ automatic: false, visible: true });
}
const group = this.modelFor('adminGroupsType')
.findProperty('name', params.name);
const group = this.modelFor('adminGroupsType').findBy('name', params.name);
if (!group) { return this.transitionTo('adminGroups.index'); }

View File

@ -30,5 +30,4 @@
{{#unless hasMasterKey}}
<button class='btn' {{action "generateMasterKey"}}><i class="fa fa-key"></i>{{i18n 'admin.api.generate_master'}}</button>
{{/unless }}
{{/unless}}

View File

@ -0,0 +1 @@
{{admin-backups-logs logs=logs status=status}}

View File

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

View File

@ -5,7 +5,7 @@
<p>{{emoji-uploader done="emojiUploaded"}}</p>
{{#if model}}
{{#if sortedEmojis}}
<div class="span8">
<table id="custom_emoji">
<thead>
@ -16,7 +16,7 @@
</tr>
</thead>
<tbody>
{{#each model as |e|}}
{{#each sortedEmojis as |e|}}
<tr>
<th><img class="emoji" src="{{unbound e.url}}" title="{{unbound e.name}}"></th>
<th>:{{e.name}}:</th>

View File

@ -2,7 +2,7 @@
<div class='content-list span6'>
<h3>{{i18n 'admin.groups.edit'}}</h3>
<ul>
{{#each model as |group|}}
{{#each sortedGroups as |group|}}
<li>
{{#link-to "adminGroup" group.type group.name}}{{group.name}}
{{#if group.userCountDisplay}}

View File

@ -1,31 +1,7 @@
(function() {
var Discourse = require('discourse').default;
function deprecate(module, methods) {
var result = {};
methods.forEach(function(m) {
result[m] = function() {
Ember.warn("Discourse." + module + "." + m + " is deprecated. Export a setup() function instead");
};
});
Discourse[module] = result;
}
deprecate('Markdown', ['whiteListTag', 'whiteListIframe']);
deprecate('Dialect', ['inlineRegexp', 'inlineBetween', 'addPreProcessor', 'replaceBlock',
'inlineReplace', 'registerInline', 'registerEmoji']);
deprecate('BBCode', ['replaceBBCode', 'register', 'rawBBCode', 'replaceBBCodeParamsRaw']);
Discourse.dialect_deprecated = true;
Discourse.ajax = function() {
var ajax = require('discourse/lib/ajax').ajax;
Ember.warn("Discourse.ajax is deprecated. Import the module and use it instead");
return ajax.apply(this, arguments);
};
window.Discourse = Discourse;
})();

View File

@ -1,13 +1,14 @@
import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { on, observes } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
export default Ember.Component.extend(bufferedRender({
tagName: 'select',
attributeBindings: ['tabindex', 'disabled'],
classNames: ['combobox'],
valueAttribute: 'id',
nameProperty: 'name',
render(buffer) {
buildBuffer(buffer) {
const nameProperty = this.get('nameProperty');
const none = this.get('none');
@ -48,11 +49,11 @@ export default Ember.Component.extend({
@observes('content.[]')
_rerenderOnChange() {
this.rerender();
this.rerenderBuffer();
},
@on('didInsertElement')
_initializeCombo() {
didInsertElement() {
this._super();
// Workaround for https://github.com/emberjs/ember.js/issues/9813
// Can be removed when fixed. Without it, the wrong option is selected
@ -60,7 +61,7 @@ export default Ember.Component.extend({
// observer for item names changing (optional)
if (this.get('nameChanges')) {
this.addObserver('content.@each.' + this.get('nameProperty'), this.rerender);
this.addObserver('content.@each.' + this.get('nameProperty'), this.rerenderBuffer);
}
const $elem = this.$();
@ -80,7 +81,11 @@ export default Ember.Component.extend({
}
this.set('value', val);
});
$elem.trigger('change');
Ember.run.scheduleOnce('afterRender', this, this._triggerChange);
},
_triggerChange() {
this.$().trigger('change');
},
@on('willDestroyElement')
@ -88,4 +93,4 @@ export default Ember.Component.extend({
this.$().select2('destroy');
}
});
}));

View File

@ -0,0 +1,53 @@
// Ember 2.0 removes buffered rendering, but we can still implement it ourselves.
// In the long term we'll want to remove this.
const Mixin = {
__bufferTimeout: null,
_customRender() {
Ember.run.cancel(this.__bufferTimeout);
if (!this.element || this.isDestroying || this.isDestroyed) { return; }
const buffer = [];
this.buildBuffer(buffer);
this.element.innerHTML = buffer.join('');
},
rerenderBuffer() {
Ember.run.scheduleOnce('render', this, this._customRender);
}
};
export function bufferedRender(obj) {
if (!obj.buildBuffer) {
Ember.warn('Missing `buildBuffer` method');
return obj;
}
const caller = {};
// True in 1.13 or greater
if (Ember.Helper) {
caller.didRender = function() {
this._super();
this._customRender();
};
} else {
caller.didInsertElement = function() {
this._super();
this._customRender();
};
}
const triggers = obj.rerenderTriggers;
if (triggers) {
caller.init = function() {
this._super();
triggers.forEach(k => this.addObserver(k, this.rerenderBuffer));
};
}
delete obj.rerenderTriggers;
return Ember.Mixin.create(Mixin, caller, obj);
}

View File

@ -3,8 +3,9 @@ import { get } from 'discourse-common/lib/raw-handlebars';
// `Ember.Helper` is only available in versions after 1.12
export function htmlHelper(fn) {
if (Ember.Helper) {
return Ember.Helper.helper(function() {
return new Handlebars.SafeString(fn.apply(this, Array.prototype.slice.call(arguments)) || '');
return Ember.Helper.helper(function(...args) {
args = (args.length > 1) ? args[0].concat({ hash: args[args.length-1] }) : args;
return new Handlebars.SafeString(fn.apply(this, args) || '');
});
} else {
return Ember.Handlebars.makeBoundHelper(function() {
@ -13,8 +14,24 @@ export function htmlHelper(fn) {
}
}
const _helpers = {};
export function registerHelper(name, fn) {
Ember.HTMLBars._registerHelper(name, fn);
if (Ember.Helper) {
_helpers[name] = Ember.Helper.helper(fn);
} else {
return Ember.HTMLBars._registerHelper(name, fn);
}
}
export function findHelper(name) {
return _helpers[name] || _helpers[name.dasherize()];
}
export function registerHelpers(registry) {
Object.keys(_helpers).forEach(name => {
registry.register(`helper:${name}`, _helpers[name], { singleton: false });
});
}
function resolveParams(ctx, options) {
@ -39,6 +56,7 @@ function resolveParams(ctx, options) {
}
export function registerUnbound(name, fn) {
const func = function(property, options) {
if (options.types && (options.types[0] === "ID" || options.types[0] === "PathExpression")) {
property = get(this, property, options);
@ -47,6 +65,14 @@ export function registerUnbound(name, fn) {
return fn.call(this, property, resolveParams(this, options));
};
if (Ember.Helper) {
_helpers[name] = Ember.Helper.extend({
compute: (params, args) => fn(params[0], args)
});
Handlebars.registerHelper(name, func);
return;
}
Handlebars.registerHelper(name, func);
Ember.Handlebars.registerHelper(name, func);
}

View File

@ -1,5 +1,6 @@
/* global requirejs, require */
import { findHelper } from 'discourse-common/lib/helpers';
/* global requirejs, require */
var classify = Ember.String.classify;
var get = Ember.get;
@ -109,7 +110,9 @@ export function buildResolver(baseName) {
},
resolveHelper(parsedName) {
return this.customResolve(parsedName) || this._super(parsedName);
return findHelper(parsedName.fullNameWithoutType) ||
this.customResolve(parsedName) ||
this._super(parsedName);
},
resolveController(parsedName) {
@ -178,6 +181,7 @@ export function buildResolver(baseName) {
return this._super(parsedName) ||
templates[slashedType] ||
templates[withoutType] ||
templates[withoutType.replace(/\.raw$/, '')] ||
templates[dashed] ||
templates[decamelized.replace(/\./, '/')] ||
templates[decamelized.replace(/\_/, '/')] ||

View File

@ -135,15 +135,6 @@ const Discourse = Ember.Application.extend({
}
});
});
const utils = require('discourse/lib/utilities');
Discourse.Utilities = {};
Object.keys(utils).forEach(function(k) {
Discourse.Utilities[k] = function() {
Ember.warn('Discourse.Utilities is deprecated. Import it as a module');
return utils[k].apply(utils, arguments);
};
});
},
@computed('currentAssetVersion', 'desiredAssetVersion')

View File

@ -1,66 +0,0 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import UserAction from "discourse/models/user-action";
export default Ember.Component.extend(StringBuffer, {
tagName: 'li',
classNameBindings: ['active', 'noGlyph'],
rerenderTriggers: ['content.count', 'count'],
noGlyph: Em.computed.empty('icon'),
isIndexStream: function() {
return !this.get('content');
}.property('content.count'),
active: function() {
if (this.get('isIndexStream')) {
return !this.get('userActionType');
}
const content = this.get('content');
if (content) {
return parseInt(this.get('userActionType'), 10) === parseInt(Em.get(content, 'action_type'), 10);
}
}.property('userActionType', 'isIndexStream'),
activityCount: function() {
return this.get('content.count') || this.get('count') || 0;
}.property('content.count', 'count'),
typeKey: function() {
const actionType = this.get('content.action_type');
if (actionType === UserAction.TYPES.messages_received) { return ""; }
const result = UserAction.TYPES_INVERTED[actionType];
if (!result) { return ""; }
// We like our URLS to have hyphens, not underscores
return "/" + result.replace("_", "-");
}.property('content.action_type'),
url: function() {
return "/users/" + this.get('user.username_lower') + "/activity" + this.get('typeKey');
}.property('typeKey', 'user.username_lower'),
description: function() {
return this.get('content.description') || I18n.t("user.filters.all");
}.property('content.description'),
renderString(buffer) {
buffer.push("<a href='" + this.get('url') + "'>");
const icon = this.get('icon');
if (icon) {
buffer.push("<i class='glyph fa fa-" + icon + "'></i> ");
}
buffer.push(this.get('description') + " <span class='count'>(" + this.get('activityCount') + ")</span></a>");
},
icon: function() {
switch(parseInt(this.get('content.action_type'), 10)) {
case UserAction.TYPES.likes_received: return "heart";
case UserAction.TYPES.bookmarks: return "bookmark";
case UserAction.TYPES.edits: return "pencil";
case UserAction.TYPES.replies: return "reply";
case UserAction.TYPES.mentions: return "at";
}
}.property("content.action_type")
});

View File

@ -47,12 +47,10 @@ export default Ember.Component.extend({
target = target.find('a');
}
const topic = this.get('topics').findProperty('id', parseInt(topicId));
const topic = this.get('topics').findBy('id', parseInt(topicId));
this.sendAction('postsAction', {topic, position: target.offset()});
}
return false;
}
}
});

View File

@ -35,9 +35,4 @@ export default Ember.Component.extend({
});
}.property('firstCategory', 'hideSubcategories'),
render(buffer) {
if (this.get('hidden')) { return; }
this._super(buffer);
}
});

View File

@ -7,10 +7,5 @@ export default Ember.Component.extend({
@computed('checked')
status(checked) {
return checked ? 'status-checked' : 'status-unchecked';
},
render(buffer) {
const icon = this.get('checked') ? 'check' : 'times';
buffer.push(`<i class='fa fa-${icon}'></i>`);
}
});

View File

@ -0,0 +1,27 @@
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
tagName: 'button',
attributeBindings: ['style', 'title'],
classNameBindings: [':colorpicker', 'isUsed:used-color:unused-color'],
@computed('color', 'usedColors')
isUsed(color, usedColors) {
return (usedColors || []).indexOf(color.toUpperCase()) >= 0;
},
@computed('isUsed')
title(isUsed) {
return isUsed ? I18n.t("category.already_used") : null;
},
@computed('color')
style(color) {
return `background-color: #${color};`.htmlSafe();
},
click(e) {
e.preventDefault();
this.sendAction('selectColor', this.get('color'));
}
});

View File

@ -1,30 +1,9 @@
import DiscourseContainerView from 'discourse/views/container';
export default DiscourseContainerView.extend({
export default Ember.Component.extend({
classNames: 'colors-container',
_createButtons: function() {
var colors = this.get('colors'),
isUsed, usedColors = this.get('usedColors') || [];
if (!colors) return;
var self = this;
colors.forEach(function(color) {
isUsed = usedColors.indexOf(color.toUpperCase()) >= 0;
self.attachViewWithArgs({
tagName: 'button',
attributeBindings: ['style', 'title'],
classNames: ['colorpicker'].concat( isUsed ? ['used-color'] : ['unused-color'] ),
style: ('background-color: #' + color + ';').htmlSafe(),
title: isUsed ? I18n.t("category.already_used") : null,
click: function() {
self.set("value", color);
return false;
}
});
});
}.on('init')
actions: {
selectColor(color) {
this.set('value', color);
}
}
});

View File

@ -2,7 +2,7 @@ import userSearch from 'discourse/lib/user-search';
import { default as computed, on } from 'ember-addons/ember-computed-decorators';
import { linkSeenMentions, fetchUnseenMentions } from 'discourse/lib/link-mentions';
import { linkSeenCategoryHashtags, fetchUnseenCategoryHashtags } from 'discourse/lib/link-category-hashtags';
import { fetchUnseenTagHashtags, linkSeenTagHashtags } from 'discourse/lib/link-tag-hashtag';
import { linkSeenTagHashtags, fetchUnseenTagHashtags } from 'discourse/lib/link-tag-hashtag';
import { load } from 'pretty-text/oneboxer';
import { ajax } from 'discourse/lib/ajax';
import InputValidation from 'discourse/models/input-validation';
@ -41,22 +41,6 @@ export default Ember.Component.extend({
return showPreview ? I18n.t('composer.hide_preview') : I18n.t('composer.show_preview');
},
_renderUnseenTagHashtags($preview, unseen) {
fetchUnseenTagHashtags(unseen).then(() => {
linkSeenTagHashtags($preview);
});
},
@on('previewRefreshed')
paintTagHashtags($preview) {
if (!this.siteSettings.tagging_enabled) { return; }
const unseenTagHashtags = linkSeenTagHashtags($preview);
if (unseenTagHashtags.length) {
Ember.run.debounce(this, this._renderUnseenTagHashtags, $preview, unseenTagHashtags, 500);
}
},
@computed
markdownOptions() {
return {
@ -66,7 +50,7 @@ export default Ember.Component.extend({
const posts = topic.get('postStream.posts');
if (posts && topicId === topic.get('id')) {
const quotedPost = posts.findProperty("post_number", postNumber);
const quotedPost = posts.findBy("post_number", postNumber);
if (quotedPost) {
return tinyAvatar(quotedPost.get('avatar_template'));
}
@ -152,19 +136,38 @@ export default Ember.Component.extend({
$preview.scrollTop(desired + 50);
},
_renderUnseenMentions: function($preview, unseen) {
fetchUnseenMentions($preview, unseen).then(() => {
_renderUnseenMentions($preview, unseen) {
fetchUnseenMentions(unseen).then(() => {
linkSeenMentions($preview, this.siteSettings);
this._warnMentionedGroups($preview);
});
},
_renderUnseenCategoryHashtags: function($preview, unseen) {
_renderUnseenCategoryHashtags($preview, unseen) {
fetchUnseenCategoryHashtags(unseen).then(() => {
linkSeenCategoryHashtags($preview);
});
},
_renderUnseenTagHashtags($preview, unseen) {
fetchUnseenTagHashtags(unseen).then(() => {
linkSeenTagHashtags($preview);
});
},
_loadOneboxes($oneboxes) {
const post = this.get('composer.post');
let refresh = false;
// If we are editing a post, we'll refresh its contents once.
if (post && !post.get('refreshedPost')) {
refresh = true;
post.set('refreshedPost', true);
}
$oneboxes.each((_, o) => load(o, refresh, ajax));
},
_warnMentionedGroups($preview) {
Ember.run.scheduleOnce('afterRender', () => {
this._warnedMentions = this._warnedMentions || [];
@ -481,31 +484,33 @@ export default Ember.Component.extend({
previewUpdated($preview) {
// Paint mentions
const unseen = linkSeenMentions($preview, this.siteSettings);
if (unseen.length) {
Ember.run.debounce(this, this._renderUnseenMentions, $preview, unseen, 500);
const unseenMentions = linkSeenMentions($preview, this.siteSettings);
if (unseenMentions.length) {
Ember.run.debounce(this, this._renderUnseenMentions, $preview, unseenMentions, 450);
}
this._warnMentionedGroups($preview);
// Paint category hashtags
const unseenHashtags = linkSeenCategoryHashtags($preview);
if (unseenHashtags.length) {
Ember.run.debounce(this, this._renderUnseenCategoryHashtags, $preview, unseenHashtags, 500);
const unseenCategoryHashtags = linkSeenCategoryHashtags($preview);
if (unseenCategoryHashtags.length) {
Ember.run.debounce(this, this._renderUnseenCategoryHashtags, $preview, unseenCategoryHashtags, 450);
}
const post = this.get('composer.post');
let refresh = false;
// If we are editing a post, we'll refresh its contents once. This is a feature that
// allows a user to refresh its contents once.
if (post && !post.get('refreshedPost')) {
refresh = true;
post.set('refreshedPost', true);
// Paint tag hashtags
if (this.siteSettings.tagging_enabled) {
const unseenTagHashtags = linkSeenTagHashtags($preview);
if (unseenTagHashtags.length) {
Ember.run.debounce(this, this._renderUnseenTagHashtags, $preview, unseenTagHashtags, 450);
}
}
// Paint oneboxes
$('a.onebox', $preview).each((i, e) => load(e, refresh, ajax));
const $oneboxes = $('a.onebox', $preview);
if ($oneboxes.length > 0 && $oneboxes.length <= this.siteSettings.max_oneboxes_per_post) {
Ember.run.debounce(this, this._loadOneboxes, $oneboxes, 450);
}
this.trigger('previewRefreshed', $preview);
this.sendAction('afterRefresh', $preview);
},

View File

@ -15,12 +15,12 @@ export default Ember.Component.extend({
didInsertElement() {
this._super();
this.reset();
this.appEvents.on('composer:typed-reply', this, this._typedReply);
this.appEvents.on('composer:opened', this, this._findMessages);
this.appEvents.on('composer:find-similar', this, this._findSimilar);
this.appEvents.on('composer-messages:close', this, this._closeTop);
this.appEvents.on('composer-messages:create', this, this._create);
Ember.run.scheduleOnce('afterRender', this, this.reset);
},
willDestroyElement() {

View File

@ -1,19 +1,10 @@
import computed from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
classNameBindings: ['containerClass', 'condition:visible'],
containerClass: function() {
return this.get('size') === 'small' ? 'inline-spinner' : undefined;
}.property('size'),
render: function(buffer) {
if (this.get('condition')) {
buffer.push('<div class="spinner ' + this.get('size') + '"}}></div>');
} else {
return this._super(buffer);
}
},
_conditionChanged: function() {
this.rerender();
}.observes('condition')
@computed('size')
containerClass(size) {
return size === 'small' ? 'inline-spinner' : undefined;
}
});

View File

@ -1,8 +1,10 @@
export default Ember.Component.extend(Discourse.StringBuffer, {
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(bufferedRender({
tagName: 'span',
rerenderTriggers: ['count', 'suffix'],
renderString: function(buffer) {
buildBuffer(buffer) {
buffer.push(I18n.t(this.get('key') + (this.get('suffix') || ''), { count: this.get('count') }));
}
});
}));

View File

@ -1,7 +1,9 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
import { default as computed } from 'ember-addons/ember-computed-decorators';
export default Ember.Component.extend({
// subclasses need this
layoutName: 'components/d-button',
tagName: 'button',
classNameBindings: [':btn', 'noText'],
attributeBindings: ['disabled', 'translatedTitle:title'],
@ -18,24 +20,6 @@ export default Ember.Component.extend({
if (label) return I18n.t(label);
},
@observes('icon')
iconChanged() {
this.rerender();
},
render(buffer) {
const label = this.get('translatedLabel'),
icon = this.get('icon');
if (label || icon) {
if (icon) { buffer.push(iconHTML(icon) + ' '); }
if (label) { buffer.push(label); }
} else {
// If no label or icon is present, yield
return this._super(buffer);
}
},
click() {
this.sendAction("action", this.get("actionParam"));
return false;

View File

@ -123,7 +123,7 @@ class Toolbar {
}
addButton(button) {
const g = this.groups.findProperty('group', button.group);
const g = this.groups.findBy('group', button.group);
if (!g) {
throw `Couldn't find toolbar group ${button.group}`;
}
@ -201,18 +201,23 @@ export default Ember.Component.extend({
return null;
},
@on('didInsertElement')
_startUp() {
_readyNow() {
this.set('ready', true);
},
didInsertElement() {
this._super();
const container = this.get('container'),
$editorInput = this.$('.d-editor-input');
this._applyEmojiAutocomplete(container, $editorInput);
this._applyCategoryHashtagAutocomplete(container, $editorInput);
this.set('ready', true);
Ember.run.scheduleOnce('afterRender', this, this._readyNow);
const mouseTrap = Mousetrap(this.$('.d-editor-input')[0]);
const shortcuts = this.get('toolbar.shortcuts');
Object.keys(shortcuts).forEach(sc => {
const button = shortcuts[sc];
@ -232,7 +237,6 @@ export default Ember.Component.extend({
this.appEvents.on('composer:insert-text', text => this._addText(this._getSelected(), text));
this.appEvents.on('composer:replace-text', (oldVal, newVal) => this._replaceText(oldVal, newVal));
this._mouseTrap = mouseTrap;
},
@ -267,7 +271,6 @@ export default Ember.Component.extend({
if (this._state !== "inDOM") { return; }
const $preview = this.$('.d-editor-preview');
if ($preview.length === 0) return;
this.sendAction('previewUpdated', $preview);
});
},

View File

@ -1,69 +0,0 @@
import computed from 'ember-addons/ember-computed-decorators';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import interceptClick from 'discourse/lib/intercept-click';
export default Ember.Component.extend({
tagName: 'a',
classNames: ['d-link'],
attributeBindings: ['translatedTitle:title', 'translatedTitle:aria-title', 'href'],
@computed('path')
href(path) {
if (path) { return path; }
const route = this.get('route');
if (route) {
const router = this.container.lookup('router:main');
if (router && router.router) {
const params = [route];
const model = this.get('model');
if (model) {
params.push(model);
}
return Discourse.getURL(router.router.generate.apply(router.router, params));
}
}
return '';
},
@computed("title")
translatedTitle(title) {
if (title) return I18n.t(title);
},
click(e) {
const action = this.get('action');
if (action) {
this.sendAction('action');
return false;
}
return interceptClick(e);
},
render(buffer) {
if (!!this.get('template')) {
return this._super(buffer);
}
const icon = this.get('icon');
if (icon) {
buffer.push(iconHTML(icon));
}
const label = this.get('label');
if (label) {
if (icon) { buffer.push(" "); }
if (this.get('translateLabel') === "false") {
buffer.push(label);
} else {
const count = this.get('count');
buffer.push(I18n.t(label, { count }));
}
}
}
});

View File

@ -1,7 +1,7 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
tagName: 'th',
classNames: ['sortable'],
attributeBindings: ['title'],
@ -12,8 +12,7 @@ export default Ember.Component.extend(StringBuffer, {
return I18n.t(labelKey + '_long', { defaultValue: I18n.t(labelKey) });
}.property('field'),
renderString(buffer) {
buildBuffer(buffer) {
const icon = this.get('icon');
if (icon) {
buffer.push(iconHTML(icon));
@ -37,4 +36,4 @@ export default Ember.Component.extend(StringBuffer, {
this.setProperties({ order: field, asc: null });
}
}
});
}));

View File

@ -1,6 +1,4 @@
import VisibleComponent from "discourse/components/visible";
export default VisibleComponent.extend({
export default Ember.Component.extend({
visible: function () {
var bannerKey = this.get("banner.key"),
@ -14,7 +12,7 @@ export default VisibleComponent.extend({
}.property("user.dismissed_banner_key", "banner.key", "hide"),
actions: {
dismiss: function () {
dismiss() {
if (this.get("user")) {
this.get("user").dismissBanner(this.get("banner.key"));
} else {

View File

@ -1,6 +1,6 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
classNameBindings: [':btn-group', 'hidden'],
rerenderTriggers: ['text', 'longDescription'],
@ -23,7 +23,7 @@ export default Ember.Component.extend(StringBuffer, {
this.$().off('click.dropdown-button', 'ul li');
}.on('willDestroyElement'),
renderString(buffer) {
buildBuffer(buffer) {
const title = this.get('title');
if (title) {
buffer.push("<h4 class='title'>" + title + "</h4>");
@ -56,4 +56,4 @@ export default Ember.Component.extend(StringBuffer, {
buffer.push("</p>");
}
}
});
}));

View File

@ -1,7 +1,25 @@
import { setting } from 'discourse/lib/computed';
import { buildCategoryPanel } from 'discourse/components/edit-category-panel';
import computed from "ember-addons/ember-computed-decorators";
export default buildCategoryPanel('settings', {
emailInEnabled: setting('email_in'),
showPositionInput: setting('fixed_category_positions'),
isDefaultSortOrder: Em.computed.empty('category.sort_order'),
@computed
availableSorts() {
return ['likes', 'op_likes', 'views', 'posts', 'activity', 'posters', 'category', 'created']
.map(s => ({ name: I18n.t('category.sort_options.' + s), value: s }))
.sort((a,b) => { return a.name > b.name; });
},
@computed
sortAscendingOptions() {
return [
{name: I18n.t('category.sort_ascending'), value: 'true'},
{name: I18n.t('category.sort_descending'), value: 'false'}
];
}
});

View File

@ -14,9 +14,14 @@ export default Em.Component.extend({
return I18n.t('category.' + this.get('tab').replace('-', '_'));
}.property('tab'),
didInsertElement() {
this._super();
Ember.run.scheduleOnce('afterRender', this, this._addToCollection);
},
_addToCollection: function() {
this.get('panels').addObject(this.get('tabClassName'));
}.on('didInsertElement'),
},
actions: {
select: function() {

View File

@ -1,12 +1,12 @@
import { on } from 'ember-addons/ember-computed-decorators';
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import LogsNotice from 'discourse/services/logs-notice';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
rerenderTriggers: ['site.isReadOnly'],
renderString: function(buffer) {
buildBuffer(buffer) {
let notices = [];
if (this.site.get("isReadOnly")) {
@ -51,7 +51,7 @@ export default Ember.Component.extend(StringBuffer, {
@on('didInsertElement')
_setupLogsNotice() {
LogsNotice.current().addObserver('hidden', () => {
this.rerenderString();
this.rerenderBuffer();
});
this.$().on('click.global-notice', '.alert-logs-notice .close', () => {
@ -63,4 +63,4 @@ export default Ember.Component.extend(StringBuffer, {
_teardownLogsNotice() {
this.$().off('click.global-notice');
}
});
}));

View File

@ -6,7 +6,7 @@ export default Em.Component.extend(UploadMixin, {
@computed('imageUrl')
backgroundStyle(imageUrl) {
if (Em.isNone(imageUrl)) { return; }
if (Em.isNone(imageUrl)) { return "".htmlSafe(); }
return `background-image: url(${imageUrl})`.htmlSafe();
},

View File

@ -1,17 +1,17 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
classNameBindings: [':tip', 'good', 'bad'],
rerenderTriggers: ['validation'],
bad: Em.computed.alias('validation.failed'),
good: Em.computed.not('bad'),
renderString(buffer) {
buildBuffer(buffer) {
const reason = this.get('validation.reason');
if (reason) {
buffer.push(iconHTML(this.get('good') ? 'check' : 'times') + ' ' + reason);
}
}
});
}));

View File

@ -0,0 +1,7 @@
import Button from 'discourse/components/d-button';
export default Button.extend({
label: 'topic.reply.title',
icon: 'reply',
action: 'showLogin'
});

View File

@ -1,7 +1,7 @@
import computed from "ember-addons/ember-computed-decorators";
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
tagName: 'li',
classNameBindings: ['active', 'content.hasIcon:has-icon'],
attributeBindings: ['title'],
@ -26,7 +26,7 @@ export default Ember.Component.extend(StringBuffer, {
filterMode.indexOf(contentFilterMode) === 0;
},
renderString(buffer) {
buildBuffer(buffer) {
const content = this.get('content');
buffer.push("<a href='" + content.get('href') + "'>");
if (content.get('hasIcon')) {
@ -35,4 +35,4 @@ export default Ember.Component.extend(StringBuffer, {
buffer.push(this.get('content.displayName'));
buffer.push("</a>");
}
});
}));

View File

@ -1,8 +1,8 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import { default as computed, observes } from 'ember-addons/ember-computed-decorators';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
classNameBindings: [':popup-tip', 'good', 'bad', 'lastShownAt::hide'],
animateAttribute: null,
bouncePixels: 6,
@ -37,7 +37,7 @@ export default Ember.Component.extend(StringBuffer, {
}
},
renderString(buffer) {
buildBuffer(buffer) {
const reason = this.get('validation.reason');
if (!reason) { return; }
@ -55,4 +55,4 @@ export default Ember.Component.extend(StringBuffer, {
$elem.animate({ right: '-=' + this.bouncePixels }, this.bounceDelay).animate({ right: '+=' + this.bouncePixels }, this.bounceDelay);
}
}
});
}));

View File

@ -13,7 +13,6 @@ function updateState(state, opts) {
post.update(args).then(() => {
this.sendAction('removePost', post);
// this.get('controllers.queued-posts.model').removeObject(post);
}).catch(popupAjaxError);
};
}

View File

@ -88,7 +88,6 @@ export default Em.Component.extend({
return;
}
this.findSearchTerms();
this.setSearchedTermValue('searchedTerms.username', REGEXP_USERNAME_PREFIX);
this.setSearchedTermValueForCategory();
this.setSearchedTermValueForGroup();
@ -117,11 +116,11 @@ export default Em.Component.extend({
result.push(block);
});
this.set('searchTermBlocks', result);
return result;
},
filterBlocks(regexPrefix) {
const blocks = this.get('searchTermBlocks');
const blocks = this.findSearchTerms();
if (!blocks) return [];
let result = [];

View File

@ -17,10 +17,6 @@ export default Ember.Component.extend({
return "tag-" + this.get('tagId');
}.property('tagId'),
render(buffer) {
buffer.push(Handlebars.Utils.escapeExpression(this.get('tagId')));
},
click(e) {
e.preventDefault();
DiscourseURL.routeTo(this.get('href'));

View File

@ -1,12 +1,9 @@
export default Ember.Component.extend({
_parse: function() {
didInsertElement() {
this._super();
Ember.run.next(null, () => {
this.$().find('hr').remove();
this.$().ellipsis();
});
}.on('didInsertElement'),
render(buffer) {
buffer.push(this.get('text'));
}
});

View File

@ -1,6 +1,6 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
elementId: 'topic-closing-info',
delayedRerender: null,
@ -9,7 +9,7 @@ export default Ember.Component.extend(StringBuffer, {
'topic.details.auto_close_based_on_last_post',
'topic.details.auto_close_hours'],
renderString(buffer) {
buildBuffer(buffer) {
if (!!Ember.isEmpty(this.get('topic.details.auto_close_at'))) return;
if (this.get("topic.closed")) return;
@ -48,4 +48,4 @@ export default Ember.Component.extend(StringBuffer, {
Em.run.cancel(this.get('delayedRerender'));
}
}
});
}));

View File

@ -1,23 +1,52 @@
import ContainerView from 'discourse/views/container';
import computed from 'ember-addons/ember-computed-decorators';
export default ContainerView.extend({
export default Ember.Component.extend({
elementId: 'topic-footer-buttons',
// Allow us to extend it
layoutName: 'components/topic-footer-buttons',
init() {
this._super();
if (this.currentUser) {
const viewArgs = this.getProperties('topic', 'topicDelegated');
viewArgs.currentUser = this.currentUser;
this._actions = this._actions || {};
this.attachViewWithArgs(viewArgs, 'topic-footer-main-buttons');
this.attachViewWithArgs(viewArgs, 'pinned-button');
this.attachViewWithArgs(viewArgs, 'topic-notifications-button');
(this.get('topicDelegated') || []).forEach(m => {
this._actions[m] = function() {
this.sendAction(m);
};
this.set(m, m);
});
},
@computed('topic.details.can_invite_to')
canInviteTo(result) {
return !this.site.mobileView && result;
},
inviteDisabled: Ember.computed.or('topic.archived', 'topic.closed', 'topic.deleted'),
@computed
showAdminButton() {
return !this.site.mobileView && this.currentUser.get('canManageTopic');
},
@computed('topic.message_archived')
archiveIcon: archived => archived ? '' : 'folder',
@computed('topic.message_archived')
archiveTitle: archived => archived ? 'topic.move_to_inbox.help' : 'topic.archive_message.help',
@computed('topic.message_archived')
archiveLabel: archived => archived ? "topic.move_to_inbox.title" : "topic.archive_message.title",
@computed('topic.bookmarked')
bookmarkClass: bookmarked => bookmarked ? 'bookmark bookmarked' : 'bookmark',
@computed('topic.bookmarked')
bookmarkLabel: bookmarked => bookmarked ? 'bookmarked.clear_bookmarks' : 'bookmarked.title',
@computed('topic.bookmarked')
bookmarkTitle: bookmarked => bookmarked ? "bookmarked.help.unbookmark" : "bookmarked.help.bookmark",
this.trigger('additionalButtons', this);
} else {
// If not logged in give them a login control
this.attachViewClass('login-reply-button');
}
}
});

View File

@ -27,7 +27,7 @@ export default Combobox.extend({
}
this.comboTemplate = (item) => {
const contentItem = content.findProperty('id', item.id);
const contentItem = content.findBy('id', item.id);
if (!contentItem) { return item.text; }
return `${iconHTML(contentItem.icon)}&nbsp; ${item.text}`;
};
@ -59,7 +59,7 @@ export default Combobox.extend({
refresh();
break;
case 'flag':
controller.send('showFlagTopic', topic);
controller.send('showFlagTopic');
refresh();
break;
}

View File

@ -1,5 +1,5 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import computed from 'ember-addons/ember-computed-decorators';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
export function showEntrance(e) {
let target = $(e.target);
@ -16,17 +16,23 @@ export function showEntrance(e) {
}
}
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
rerenderTriggers: ['bulkSelectEnabled', 'topic.pinned'],
tagName: 'tr',
rawTemplate: 'list/topic-list-item.raw',
classNameBindings: [':topic-list-item', 'unboundClassNames'],
attributeBindings: ['data-topic-id'],
'data-topic-id': Em.computed.alias('topic.id'),
actions: {
toggleBookmark() {
this.get('topic').toggleBookmark().finally(() => this.rerender());
this.get('topic').toggleBookmark().finally(() => this.rerenderBuffer());
}
},
buildBuffer(buffer) {
const template = Discourse.__container__.lookup('template:list/topic-list-item.raw');
if (template) {
buffer.push(template(this));
}
},
@ -142,4 +148,4 @@ export default Ember.Component.extend(StringBuffer, {
}
}.on('didInsertElement')
});
}));

View File

@ -32,7 +32,7 @@ export default Ember.Component.extend({
return this.get('order') === "op_likes";
}.property('order'),
@observes('topics.@each')
@observes('topics.[]')
topicsAdded() {
// special case so we don't keep scanning huge lists
if (!this.get('lastVisitedTopic')) {

View File

@ -5,6 +5,8 @@ export default Ember.Component.extend({
info: Em.Object.create(),
_checkSize() {
if (!this.element || this.isDestroying || this.isDestroyed) { return; }
let info = this.get('info');
if (info.get('topicProgressExpanded')) {
@ -47,7 +49,7 @@ export default Ember.Component.extend({
if (this.get('info.topicProgressExpanded')) {
$(window).on('click.hide-fullscreen', (e) => {
if ( $(e.target).is('.topic-timeline') ||
!$(e.target).parents().is('.timeline-container, #topic-progress-wrapper')) {
!$(e.target).parents().is('#topic-progress-wrapper')) {
this._collapseFullscreen();
}
});
@ -110,7 +112,7 @@ export default Ember.Component.extend({
$('#reply-control').on('div-resized.discourse-topic-navigation', () => this._checkSize());
}
this._checkSize();
Ember.run.scheduleOnce('afterRender', this, this._checkSize);
},
willDestroyElement() {

View File

@ -1,4 +1,4 @@
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
// Creates a link
function link(buffer, prop, url, cssClass, i18nKey, text) {
@ -7,15 +7,15 @@ function link(buffer, prop, url, cssClass, i18nKey, text) {
buffer.push(`<a href="${url}" class="badge ${cssClass} badge-notification" title="${title}">${text || prop}</a>\n`);
}
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
tagName: 'span',
classNameBindings: [':topic-post-badges'],
rerenderTriggers: ['url', 'unread', 'newPosts', 'unseen'],
renderString(buffer) {
buildBuffer(buffer) {
const url = this.get('url');
link(buffer, this.get('unread'), url, 'unread', 'unread_posts');
link(buffer, this.get('newPosts'), url, 'new-posts', 'new_posts');
link(buffer, this.get('unseen'), url, 'new-topic', 'new', I18n.t('filters.new.lower_title'));
}
});
}));

View File

@ -1,8 +1,8 @@
import { iconHTML } from 'discourse-common/helpers/fa-icon';
import StringBuffer from 'discourse/mixins/string-buffer';
import { bufferedRender } from 'discourse-common/lib/buffered-render';
import { escapeExpression } from 'discourse/lib/utilities';
export default Ember.Component.extend(StringBuffer, {
export default Ember.Component.extend(bufferedRender({
classNames: ['topic-statuses'],
rerenderTriggers: ['topic.archived', 'topic.closed', 'topic.pinned', 'topic.visible', 'topic.unpinned', 'topic.is_warning'],
@ -26,7 +26,7 @@ export default Ember.Component.extend(StringBuffer, {
return Discourse.User.current() && !this.get('disableActions');
}.property('disableActions'),
renderString(buffer) {
buildBuffer(buffer) {
const self = this;
const renderIcon = function(name, key, actionable) {
@ -57,4 +57,4 @@ export default Ember.Component.extend(StringBuffer, {
renderIconIf('topic.unpinned', 'thumb-tack', 'unpinned', this.get("canAct"));
renderIconIf('topic.invisible', 'eye-slash', 'invisible');
}
});
}));

View File

@ -1,12 +0,0 @@
export default Ember.Component.extend({
visibleChanged: function(){
this.rerender();
}.observes("visible"),
render: function(buffer){
if (this._state !== 'inDOM' && this._state !== 'preRender' && this._state !== 'inBuffer') { return; }
if (!this.get("visible")) { return; }
return this._super(buffer);
}
});

View File

@ -5,7 +5,7 @@ export default Ember.Controller.extend({
queryParams: ['username'],
noMoreBadges: false,
userBadges: null,
needs: ["application"],
application: Ember.inject.controller(),
@computed('username')
user(username) {
@ -55,7 +55,7 @@ export default Ember.Controller.extend({
@observes('canLoadMore')
_showFooter() {
this.set("controllers.application.showFooter", !this.get("canLoadMore"));
this.set("application.showFooter", !this.get("canLoadMore"));
}
});

View File

@ -3,7 +3,7 @@ import { topicLevels } from 'discourse/lib/notification-levels';
// Support for changing the notification level of various topics
export default Ember.Controller.extend({
needs: ['topic-bulk-actions'],
topicBulkActions: Ember.inject.controller(),
notificationLevelId: null,
@computed
@ -21,7 +21,7 @@ export default Ember.Controller.extend({
actions: {
changeNotificationLevel() {
this.get('controllers.topic-bulk-actions').performAndRefresh({
this.get('topicBulkActions').performAndRefresh({
type: 'change_notification_level',
notification_level_id: this.get('notificationLevelId')
});

View File

@ -4,9 +4,7 @@ import DiscourseURL from 'discourse/lib/url';
// Modal related to changing the ownership of posts
export default Ember.Controller.extend(SelectedPostsCount, ModalFunctionality, {
needs: ['topic'],
topicController: Em.computed.alias('controllers.topic'),
topicController: Ember.inject.controller('topic'),
selectedPosts: Em.computed.alias('topicController.selectedPosts'),
saving: false,
new_user: null,

View File

@ -4,9 +4,8 @@ import DiscourseURL from 'discourse/lib/url';
// Modal related to changing the timestamp of posts
export default Ember.Controller.extend(ModalFunctionality, {
needs: ['topic'],
topicController: Em.computed.alias('controllers.topic'),
topicController: Ember.inject.controller('topic'),
saving: false,
date: '',
time: '',

View File

@ -51,7 +51,9 @@ export function addPopupMenuOptionsCallback(callback) {
}
export default Ember.Controller.extend({
needs: ['modal', 'topic', 'application'],
topicController: Ember.inject.controller('topic'),
application: Ember.inject.controller(),
replyAsNewTopicDraft: Em.computed.equal('model.draftKey', Composer.REPLY_AS_NEW_TOPIC_KEY),
checkedMessages: false,
messageCount: null,
@ -102,7 +104,7 @@ export default Ember.Controller.extend({
}
}),
topicModel: Ember.computed.alias('controllers.topic.model'),
topicModel: Ember.computed.alias('topicController.model'),
@computed('model.canEditTitle', 'model.creatingPrivateMessage')
canEditTags(canEditTitle, creatingPrivateMessage) {
@ -484,7 +486,7 @@ export default Ember.Controller.extend({
self.appEvents.one('composer:will-open', () => bootbox.alert(error));
});
if (this.get('controllers.application.currentRouteName').split('.')[0] === 'topic' &&
if (this.get('application.currentRouteName').split('.')[0] === 'topic' &&
composer.get('topic.id') === this.get('topicModel.id')) {
staged = composer.get('stagedPost');
}
@ -616,10 +618,10 @@ export default Ember.Controller.extend({
let category;
if (!splitCategory[1]) {
category = this.site.get('categories').findProperty('nameLower', splitCategory[0].toLowerCase());
category = this.site.get('categories').findBy('nameLower', splitCategory[0].toLowerCase());
} else {
const categories = Discourse.Category.list();
const mainCategory = categories.findProperty('nameLower', splitCategory[0].toLowerCase());
const mainCategory = categories.findBy('nameLower', splitCategory[0].toLowerCase());
category = categories.find(function(item) {
return item && item.get('nameLower') === splitCategory[1].toLowerCase() && item.get('parent_category_id') === mainCategory.id;
});

View File

@ -7,7 +7,7 @@ import { emailValid } from 'discourse/lib/utilities';
import InputValidation from 'discourse/models/input-validation';
export default Ember.Controller.extend(ModalFunctionality, {
needs: ['login'],
login: Ember.inject.controller(),
uniqueUsernameValidation: null,
globalNicknameExists: false,
@ -56,7 +56,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
// Validate required fields
let userFields = this.get('userFields');
if (userFields) { userFields = userFields.filterProperty('field.required'); }
if (userFields) { userFields = userFields.filterBy('field.required'); }
if (!Ember.isEmpty(userFields)) {
const anyEmpty = userFields.any(function(uf) {
const val = uf.get('value');
@ -345,7 +345,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
actions: {
externalLogin(provider) {
this.get('controllers.login').send('externalLogin', provider);
this.get('login').send('externalLogin', provider);
},
createAccount() {

View File

@ -1,5 +1,5 @@
// Just add query params here to have them automatically passed to topic list filters.
export var queryParams = {
export const queryParams = {
order: { replace: true, refreshModel: true },
ascending: { replace: true, refreshModel: true },
status: { replace: true, refreshModel: true },
@ -10,12 +10,12 @@ export var queryParams = {
};
// Basic controller options
var controllerOpts = {
needs: ['discovery/topics'],
const controllerOpts = {
discoveryTopics: Ember.inject.controller('discovery/topics'),
queryParams: Object.keys(queryParams),
};
// Aliases for the values
controllerOpts.queryParams.forEach(p => controllerOpts[p] = Em.computed.alias(`controllers.discovery/topics.${p}`));
controllerOpts.queryParams.forEach(p => controllerOpts[p] = Ember.computed.alias(`discoveryTopics.${p}`));
export default Ember.Controller.extend(controllerOpts);

View File

@ -1,16 +1,19 @@
import DiscourseURL from 'discourse/lib/url';
export default Ember.Controller.extend({
needs: ['navigation/category', 'discovery/topics', 'application'],
discoveryTopics: Ember.inject.controller('discovery/topics'),
navigationCategory: Ember.inject.controller('navigation/category'),
application: Ember.inject.controller(),
loading: false,
category: Em.computed.alias('controllers.navigation/category.category'),
noSubcategories: Em.computed.alias('controllers.navigation/category.noSubcategories'),
category: Em.computed.alias('navigationCategory.category'),
noSubcategories: Em.computed.alias('navigationCategory.noSubcategories'),
loadedAllItems: Em.computed.not("controllers.discovery/topics.model.canLoadMore"),
loadedAllItems: Em.computed.not("discoveryTopics.model.canLoadMore"),
_showFooter: function() {
this.set("controllers.application.showFooter", this.get("loadedAllItems"));
this.set("application.showFooter", this.get("loadedAllItems"));
}.observes("loadedAllItems"),
showMoreUrl(period) {

View File

@ -2,7 +2,7 @@ import computed from 'ember-addons/ember-computed-decorators';
import DiscoveryController from 'discourse/controllers/discovery';
export default DiscoveryController.extend({
needs: ['modal', 'discovery'],
discovery: Ember.inject.controller(),
// this makes sure the composer isn't scoping to a specific category
category: null,
@ -20,7 +20,10 @@ export default DiscoveryController.extend({
@computed("model.parentCategory")
categoryPageStyle(parentCategory) {
const style = this.siteSettings.desktop_category_page_style;
return parentCategory && style === "categories_and_latest_topics" ? "categories_only" : style;
const componentName = (parentCategory && style === "categories_and_latest_topics") ?
"categories_only" :
style;
return Ember.String.dasherize(componentName);
}
});

View File

@ -5,13 +5,14 @@ import { endWith } from 'discourse/lib/computed';
import showModal from 'discourse/lib/show-modal';
const controllerOpts = {
needs: ['discovery'],
discovery: Ember.inject.controller(),
discoveryTopics: Ember.inject.controller('discovery/topics'),
period: null,
canStar: Em.computed.alias('controllers.discovery/topics.currentUser.id'),
showTopicPostBadges: Em.computed.not('controllers.discovery/topics.new'),
redirectedReason: Em.computed.alias('currentUser.redirected_to_top.reason'),
canStar: Ember.computed.alias('currentUser.id'),
showTopicPostBadges: Ember.computed.not('discoveryTopics.new'),
redirectedReason: Ember.computed.alias('currentUser.redirected_to_top.reason'),
order: 'default',
ascending: false,
@ -46,12 +47,12 @@ const controllerOpts = {
this.setProperties({ order: "default", ascending: false });
// Don't refresh if we're still loading
if (this.get('controllers.discovery.loading')) { return; }
if (this.get('discovery.loading')) { return; }
// If we `send('loading')` here, due to returning true it bubbles up to the
// router and ember throws an error due to missing `handlerInfos`.
// Lesson learned: Don't call `loading` yourself.
this.set('controllers.discovery.loading', true);
this.set('discovery.loading', true);
this.store.findFiltered('topicList', {filter}).then(list => {
const TopicList = require('discourse/models/topic-list').default;

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