Split out bulk operations modal and `Discourse.Route.showModal`

This makes it easier to share bulk topic operations, for example
from a plugin's custom topic list.
This commit is contained in:
Robin Ward 2015-03-10 15:01:15 -04:00
parent e9b04170c6
commit f50280a889
27 changed files with 313 additions and 307 deletions

View File

@ -19,7 +19,7 @@ export default ObjectController.extend(ModalFunctionality, {
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'); });
bootbox.alert(error, function() { self.send('reopenModal'); });
});
}
}

View File

@ -1,12 +1,14 @@
Discourse.AdminBackupsRoute = Discourse.Route.extend({
import showModal from 'discourse/lib/show-modal';
export default Discourse.Route.extend({
LOG_CHANNEL: "/admin/backups/logs",
activate: function() {
activate() {
Discourse.MessageBus.subscribe(this.LOG_CHANNEL, this._processLogMessage.bind(this));
},
_processLogMessage: function(log) {
_processLogMessage(log) {
if (log.message === "[STARTED]") {
this.controllerFor("adminBackups").set("isOperationRunning", true);
this.controllerFor("adminBackupsLogs").clear();
@ -25,7 +27,7 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
}
},
model: function() {
model() {
return PreloadStore.getAndRemove("operations_status", function() {
return Discourse.ajax("/admin/backups/status.json");
}).then(function (status) {
@ -37,35 +39,24 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
});
},
deactivate: function() {
deactivate() {
Discourse.MessageBus.unsubscribe(this.LOG_CHANNEL);
},
actions: {
/**
Starts a backup and redirect the user to the logs tab
@method startBackup
**/
startBackup: function() {
Discourse.Route.showModal(this, 'admin_start_backup');
startBackup() {
showModal('admin_start_backup');
this.controllerFor('modal').set('modalClass', 'start-backup-modal');
},
backupStarted: function () {
backupStarted() {
this.modelFor("adminBackups").set("isOperationRunning", true);
this.transitionTo("admin.backups.logs");
this.send("closeModal");
},
/**
Destroys a backup
@method destroyBackup
@param {Discourse.Backup} backup the backup to destroy
**/
destroyBackup: function(backup) {
var self = this;
destroyBackup(backup) {
const self = this;
bootbox.confirm(
I18n.t("admin.backups.operations.destroy.confirm"),
I18n.t("no_value"),
@ -80,14 +71,8 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
);
},
/**
Start a restore and redirect the user to the logs tab
@method startRestore
@param {Discourse.Backup} backup the backup to restore
**/
startRestore: function(backup) {
var self = this;
startRestore(backup) {
const self = this;
bootbox.confirm(
I18n.t("admin.backups.operations.restore.confirm"),
I18n.t("no_value"),
@ -105,13 +90,8 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
);
},
/**
Cancels the current operation
@method cancelOperation
**/
cancelOperation: function() {
var self = this;
cancelOperation() {
const self = this;
bootbox.confirm(
I18n.t("admin.backups.operations.cancel.confirm"),
I18n.t("no_value"),
@ -126,12 +106,7 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
);
},
/**
Rollback to previous working state
@method rollback
**/
rollback: function() {
rollback() {
bootbox.confirm(
I18n.t("admin.backups.operations.rollback.confirm"),
I18n.t("no_value"),
@ -142,8 +117,8 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
);
},
uploadSuccess: function(filename) {
var self = this;
uploadSuccess(filename) {
const self = this;
bootbox.alert(I18n.t("admin.backups.upload.success", { filename: filename }), function() {
Discourse.Backup.find().then(function (backups) {
self.controllerFor("adminBackupsIndex").set("model", backups);
@ -151,7 +126,7 @@ Discourse.AdminBackupsRoute = Discourse.Route.extend({
});
},
uploadError: function(filename, message) {
uploadError(filename, message) {
bootbox.alert(I18n.t("admin.backups.upload.error", { filename: filename, message: message }));
}
}

View File

@ -1,9 +1,11 @@
import showModal from 'discourse/lib/show-modal';
export default Ember.Route.extend({
serialize: function(m) {
serialize(m) {
return {badge_id: Em.get(m, 'id') || 'new'};
},
model: function(params) {
model(params) {
if (params.badge_id === "new") {
return Discourse.Badge.create({
name: I18n.t('admin.badges.new_badge')
@ -13,22 +15,20 @@ export default Ember.Route.extend({
},
actions: {
saveError: function(e) {
var msg = I18n.t("generic_error");
saveError(e) {
let 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);
},
editGroupings: function() {
var groupings = this.controllerFor('admin-badges').get('badgeGroupings');
Discourse.Route.showModal(this, 'admin_edit_badge_groupings', groupings);
editGroupings() {
const groupings = this.controllerFor('admin-badges').get('badgeGroupings');
showModal('admin_edit_badge_groupings', groupings);
},
preview: function(badge, explain) {
var self = this;
preview(badge, explain) {
badge.set('preview_loading', true);
Discourse.ajax('/admin/badges/preview.json', {
method: 'post',
@ -36,11 +36,11 @@ export default Ember.Route.extend({
sql: badge.get('query'),
target_posts: !!badge.get('target_posts'),
trigger: badge.get('trigger'),
explain: explain
explain
}
}).then(function(json) {
badge.set('preview_loading', false);
Discourse.Route.showModal(self, 'admin_badge_preview', json);
showModal('admin_badge_preview', json);
}).catch(function(error) {
badge.set('preview_loading', false);
Em.Logger.error(error);

View File

@ -1,22 +1,24 @@
import showModal from 'discourse/lib/show-modal';
export default Discourse.Route.extend({
model: function(params) {
model(params) {
this.filter = params.filter;
return Discourse.FlaggedPost.findAll(params.filter);
},
setupController: function(controller, model) {
setupController(controller, model) {
controller.set('model', model);
controller.set('query', this.filter);
},
actions: {
showAgreeFlagModal: function (flaggedPost) {
Discourse.Route.showModal(this, 'admin_agree_flag', flaggedPost);
showAgreeFlagModal(flaggedPost) {
showModal('admin_agree_flag', flaggedPost);
this.controllerFor('modal').set('modalClass', 'agree-flag-modal');
},
showDeleteFlagModal: function (flaggedPost) {
Discourse.Route.showModal(this, 'admin_delete_flag', flaggedPost);
showDeleteFlagModal(flaggedPost) {
showModal('admin_delete_flag', flaggedPost);
this.controllerFor('modal').set('modalClass', 'delete-flag-modal');
}

View File

@ -1,3 +1,5 @@
import showModal from 'discourse/lib/show-modal';
export default Discourse.Route.extend({
// TODO: make this automatic using an `{{outlet}}`
renderTemplate: function() {
@ -10,13 +12,13 @@ export default Discourse.Route.extend({
},
actions: {
showDetailsModal: function(logRecord) {
Discourse.Route.showModal(this, 'admin_staff_action_log_details', logRecord);
showDetailsModal(logRecord) {
showModal('admin_staff_action_log_details', logRecord);
this.controllerFor('modal').set('modalClass', 'log-details-modal');
},
showCustomDetailsModal: function(logRecord) {
Discourse.Route.showModal(this, logRecord.action_name + '_details', logRecord);
showCustomDetailsModal(logRecord) {
showModal(logRecord.action_name + '_details', logRecord);
this.controllerFor('modal').set('modalClass', 'tabbed-modal log-details-modal');
}
}

View File

@ -0,0 +1,32 @@
import showModal from 'discourse/lib/show-modal';
export default Discourse.Route.extend({
model() {
return this.modelFor('adminUser');
},
afterModel(model) {
if (this.currentUser.get('admin')) {
const self = this;
return Discourse.Group.findAll().then(function(groups){
self._availableGroups = groups.filterBy('automatic', false);
return model;
});
}
},
setupController(controller, model) {
controller.setProperties({
originalPrimaryGroupId: model.get('primary_group_id'),
availableGroups: this._availableGroups,
model
});
},
actions: {
showSuspendModal(user) {
showModal('admin_suspend_user', user);
this.controllerFor('modal').set('modalClass', 'suspend-user-modal');
}
}
});

View File

@ -0,0 +1,20 @@
export default Discourse.Route.extend({
serialize(model) {
return { username: model.get('username').toLowerCase() };
},
model(params) {
return Discourse.AdminUser.find(Em.get(params, 'username').toLowerCase());
},
renderTemplate() {
this.render({into: 'admin'});
},
afterModel(adminUser) {
return adminUser.loadDetails().then(function () {
adminUser.setOriginalTrustLevel();
return adminUser;
});
}
});

View File

@ -1,51 +0,0 @@
Discourse.AdminUserRoute = Discourse.Route.extend({
serialize: function(model) {
return { username: model.get('username').toLowerCase() };
},
model: function(params) {
return Discourse.AdminUser.find(Em.get(params, 'username').toLowerCase());
},
renderTemplate: function() {
this.render({into: 'admin'});
},
afterModel: function(adminUser) {
return adminUser.loadDetails().then(function () {
adminUser.setOriginalTrustLevel();
return adminUser;
});
}
});
Discourse.AdminUserIndexRoute = Discourse.Route.extend({
model: function() {
return this.modelFor('adminUser');
},
afterModel: function(model) {
if(Discourse.User.currentProp('admin')) {
var self = this;
return Discourse.Group.findAll().then(function(groups){
self._availableGroups = groups.filterBy('automatic', false);
return model;
});
}
},
setupController: function(controller, model) {
controller.setProperties({
originalPrimaryGroupId: model.get('primary_group_id'),
availableGroups: this._availableGroups,
model: model
});
},
actions: {
showSuspendModal: function(user) {
Discourse.Route.showModal(this, 'admin_suspend_user', user);
this.controllerFor('modal').set('modalClass', 'suspend-user-modal');
}
}
});

View File

@ -0,0 +1,10 @@
import showModal from 'discourse/lib/show-modal';
export default Ember.Component.extend({
actions: {
showBulkActions() {
const controller = showModal('topicBulkActions', this.get('selected'));
controller.set('refreshTarget', this.get('refreshTarget'));
}
}
});

View File

@ -1,11 +1,9 @@
import DiscoveryController from 'discourse/controllers/discovery';
import { queryParams } from 'discourse/controllers/discovery-sortable';
import NotificationLevels from 'discourse/lib/notification-levels';
import BulkTopicSelection from 'discourse/mixins/bulk-topic-selection';
var controllerOpts = {
needs: ['discovery'],
bulkSelectEnabled: false,
selected: [],
period: null,
canStar: Em.computed.alias('controllers.discovery/topics.currentUser.id'),
@ -53,7 +51,8 @@ var controllerOpts = {
Discourse.TopicList.find(filter).then(function(list) {
Discourse.TopicList.hideUniformCategory(list, self.get('category'));
self.setProperties({ model: list, selected: [] });
self.setProperties({ model: list });
self.resetSelected();
var tracking = Discourse.TopicTrackingState.current();
if (tracking) {
@ -64,10 +63,6 @@ var controllerOpts = {
});
},
toggleBulkSelect: function() {
this.toggleProperty('bulkSelectEnabled');
this.get('selected').clear();
},
resetNew: function() {
var self = this;
@ -76,40 +71,9 @@ var controllerOpts = {
Discourse.Topic.resetNew().then(function() {
self.send('refresh');
});
},
dismissRead: function(operationType) {
var self = this,
selected = this.get('selected'),
operation;
if(operationType === "posts"){
operation = { type: 'dismiss_posts' };
} else {
operation = { type: 'change_notification_level',
notification_level_id: NotificationLevels.REGULAR };
}
var promise;
if (selected.length > 0) {
promise = Discourse.Topic.bulkOperation(selected, operation);
} else {
promise = Discourse.Topic.bulkOperationByFilter('unread', operation, this.get('category.id'));
}
promise.then(function(result) {
if (result && result.topic_ids) {
var tracker = Discourse.TopicTrackingState.current();
result.topic_ids.forEach(function(t) {
tracker.removeTopic(t);
});
tracker.incrementMessageCount();
}
self.send('refresh');
});
}
},
topicTrackingState: function() {
return Discourse.TopicTrackingState.current();
}.property(),
@ -132,7 +96,6 @@ var controllerOpts = {
this.get('topics.length') >= 30;
}.property('filter', 'topics.length'),
canBulkSelect: Em.computed.alias('currentUser.staff'),
hasTopics: Em.computed.gt('topics.length', 0),
allLoaded: Em.computed.empty('more_topics_url'),
latest: Discourse.computed.endWith('filter', 'latest'),
@ -187,4 +150,4 @@ Ember.keys(queryParams).forEach(function(p) {
}
});
export default DiscoveryController.extend(controllerOpts);
export default DiscoveryController.extend(controllerOpts, BulkTopicSelection);

View File

@ -174,12 +174,12 @@ export default ObjectController.extend(ModalFunctionality, {
self.flash(I18n.t('generic_error'));
}
self.send('showModal');
self.send('reopenModal');
self.displayErrors([I18n.t("category.delete_error")]);
self.set('deleting', false);
});
} else {
self.send('showModal');
self.send('reopenModal');
self.set('deleting', false);
}
});

View File

@ -43,10 +43,10 @@ export default ObjectController.extend(ModalFunctionality, {
self.set('details.auto_close_at', result.auto_close_at);
self.set('details.auto_close_hours', result.auto_close_hours);
} else {
bootbox.alert(I18n.t('composer.auto_close.error'), function() { self.send('showModal'); } );
bootbox.alert(I18n.t('composer.auto_close.error'), function() { self.send('reopenModal'); } );
}
}, function () {
bootbox.alert(I18n.t('composer.auto_close.error'), function() { self.send('showModal'); } );
bootbox.alert(I18n.t('composer.auto_close.error'), function() { self.send('reopenModal'); } );
});
}

View File

@ -1,11 +1,8 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality';
import DiscourseController from 'discourse/controllers/controller';
import showModal from 'discourse/lib/show-modal';
// This is happening outside of the app via popup
function showModal(modal) {
const route = Discourse.__container__.lookup('route:application');
Discourse.Route.showModal(route, modal);
}
const AuthErrors =
['requires_invite', 'awaiting_approval', 'awaiting_confirmation', 'admin_not_allowed_from_ip_address',
'not_allowed_from_ip_address'];

View File

@ -16,7 +16,6 @@ addBulkButton('resetRead', 'reset_read');
// Modal for performing bulk actions on topics
export default Ember.ArrayController.extend(ModalFunctionality, {
needs: ['discovery/topics'],
buttonRows: null,
onShow: function() {
@ -34,6 +33,7 @@ export default Ember.ArrayController.extend(ModalFunctionality, {
if (row.length) { buttonRows.push(row); }
this.set('buttonRows', buttonRows);
this.send('changeBulkTemplate', 'modal/bulk_actions_buttons');
},
perform: function(operation) {
@ -66,9 +66,10 @@ export default Ember.ArrayController.extend(ModalFunctionality, {
},
performAndRefresh: function(operation) {
var self = this;
const self = this;
return this.perform(operation).then(function() {
self.get('controllers.discovery/topics').send('refresh');
const refreshTarget = self.get('refreshTarget');
if (refreshTarget) { refreshTarget.send('refresh'); }
self.send('closeModal');
});
},
@ -107,7 +108,8 @@ export default Ember.ArrayController.extend(ModalFunctionality, {
topics.forEach(function(t) {
t.set('category', category);
});
self.get('controllers.discovery/topics').send('refresh');
const refreshTarget = self.get('refreshTarget');
if (refreshTarget) { refreshTarget.send('refresh'); }
self.send('closeModal');
});
},

View File

@ -0,0 +1,20 @@
export default function showModal(name, model) {
// We use the container here because modals are like singletons
// in Discourse. Only one can be shown with a particular state.
const route = Discourse.__container__.lookup('route:application');
route.controllerFor('modal').set('modalClass', null);
route.render(name, {into: 'modal', outlet: 'modalBody'});
const controller = route.controllerFor(name);
if (controller) {
if (model) {
controller.set('model', model);
}
if (controller.onShow) {
controller.onShow();
}
controller.set('flashMessage', null);
}
return controller;
}

View File

@ -0,0 +1,49 @@
import NotificationLevels from 'discourse/lib/notification-levels';
export default Ember.Mixin.create({
bulkSelectEnabled: false,
selected: null,
canBulkSelect: Em.computed.alias('currentUser.staff'),
resetSelected: function() {
this.set('selected', []);
}.on('init'),
actions: {
toggleBulkSelect() {
this.toggleProperty('bulkSelectEnabled');
this.get('selected').clear();
},
dismissRead(operationType) {
const self = this,
selected = this.get('selected');
let operation;
if(operationType === "posts"){
operation = { type: 'dismiss_posts' };
} else {
operation = { type: 'change_notification_level',
notification_level_id: NotificationLevels.REGULAR };
}
let promise;
if (selected.length > 0) {
promise = Discourse.Topic.bulkOperation(selected, operation);
} else {
promise = Discourse.Topic.bulkOperationByFilter('unread', operation, this.get('category.id'));
}
promise.then(function(result) {
if (result && result.topic_ids) {
const tracker = Discourse.TopicTrackingState.current();
result.topic_ids.forEach(function(t) {
tracker.removeTopic(t);
});
tracker.incrementMessageCount();
}
self.send('refresh');
});
}
}
});

View File

@ -1,3 +1,5 @@
import showModal from 'discourse/lib/show-modal';
function unlessReadOnly(method) {
return function() {
if (this.site.get("isReadOnly")) {
@ -74,31 +76,28 @@ const ApplicationRoute = Discourse.Route.extend({
showCreateAccount: unlessReadOnly('handleShowCreateAccount'),
showForgotPassword() {
Discourse.Route.showModal(this, 'forgotPassword');
showModal('forgotPassword');
},
showNotActivated(props) {
Discourse.Route.showModal(this, 'notActivated');
showModal('notActivated');
this.controllerFor('notActivated').setProperties(props);
},
showUploadSelector(composerView) {
Discourse.Route.showModal(this, 'uploadSelector');
showModal('uploadSelector');
this.controllerFor('upload-selector').setProperties({ composerView: composerView });
},
showKeyboardShortcutsHelp() {
Discourse.Route.showModal(this, 'keyboardShortcutsHelp');
showModal('keyboardShortcutsHelp');
},
showSearchHelp() {
const self = this;
// TODO: @EvitTrout how do we get a loading indicator here?
Discourse.ajax("/static/search_help.html", { dataType: 'html' }).then(function(html){
Discourse.Route.showModal(self, 'searchHelp', html);
showModal('searchHelp', html);
});
},
// Close the current modal, and destroy its state.
@ -109,13 +108,13 @@ const ApplicationRoute = Discourse.Route.extend({
/**
Hide the modal, but keep it with all its state so that it can be shown again later.
This is useful if you want to prompt for confirmation. hideModal, ask "Are you sure?",
user clicks "No", showModal. If user clicks "Yes", be sure to call closeModal.
user clicks "No", reopenModal. If user clicks "Yes", be sure to call closeModal.
**/
hideModal() {
$('#discourse-modal').modal('hide');
},
showModal() {
reopenModal() {
$('#discourse-modal').modal('show');
},
@ -123,7 +122,7 @@ const ApplicationRoute = Discourse.Route.extend({
const self = this;
Discourse.Category.reloadById(category.get('id')).then(function (c) {
self.site.updateCategory(c);
Discourse.Route.showModal(self, 'editCategory', c);
showModal(self, 'editCategory', c);
self.controllerFor('editCategory').set('selectedTab', 'general');
});
},
@ -135,6 +134,13 @@ const ApplicationRoute = Discourse.Route.extend({
checkEmail: function (user) {
user.checkEmail();
},
changeBulkTemplate(w) {
const controllerName = w.replace('modal/', ''),
factory = this.container.lookupFactory('controller:' + controllerName);
this.render(w, {into: 'topicBulkActions', outlet: 'bulkOutlet', controller: factory ? controllerName : 'topic-bulk-actions'});
}
},
@ -164,7 +170,7 @@ const ApplicationRoute = Discourse.Route.extend({
if (!this.siteSettings.enable_local_logins && methods.length === 1) {
this.controllerFor('login').send('externalLogin', methods[0]);
} else {
Discourse.Route.showModal(this, modal);
showModal(modal);
if (notAuto) { notAuto(); }
}
},

View File

@ -1,24 +1,45 @@
/**
The base route for all routes on Discourse. Includes global enter functionality.
import showModal from 'discourse/lib/show-modal';
@class Route
@extends Em.Route
@namespace Discourse
@module Discourse
**/
Discourse.Route = Ember.Route.extend({
const DiscourseRoute = Ember.Route.extend({
/**
NOT called every time we enter a route on Discourse.
Only called the FIRST time we enter a route.
So, when going from one topic to another, activate will only be called on the
TopicRoute for the first topic.
@method activate
**/
activate: function() {
this._super();
Em.run.scheduleOnce('afterRender', Discourse.Route, 'cleanDOM');
Em.run.scheduleOnce('afterRender', Ember.Route, this._cleanDOM);
},
_cleanDOM() {
// Close mini profiler
$('.profiler-results .profiler-result').remove();
// Close some elements that may be open
$('.d-dropdown').hide();
$('header ul.icons li').removeClass('active');
$('[data-toggle="dropdown"]').parent().removeClass('open');
// close the lightbox
if ($.magnificPopup && $.magnificPopup.instance) {
$.magnificPopup.instance.close();
$('body').removeClass('mfp-zoom-out-cur');
}
// Remove any link focus
// NOTE: the '.not("body")' is here to prevent a bug in IE10 on Win7
// cf. https://stackoverflow.com/questions/5657371/ie9-window-loses-focus-due-to-jquery-mobile
$(document.activeElement).not("body").blur();
Discourse.set('notifyCount',0);
$('#discourse-modal').modal('hide');
var hideDropDownFunction = $('html').data('hide-dropdown');
if (hideDropDownFunction) { hideDropDownFunction(); }
// TODO: Avoid container lookup here
var appEvents = Discourse.__container__.lookup('app-events:main');
appEvents.trigger('dom:clean');
},
_refreshTitleOnce: function() {
@ -79,7 +100,7 @@ Discourse.Route = Ember.Route.extend({
var routeBuilder;
Discourse.Route.reopenClass({
DiscourseRoute.reopenClass({
buildRoutes: function(builder) {
var oldBuilder = routeBuilder;
@ -174,48 +195,11 @@ Discourse.Route.reopenClass({
});
},
cleanDOM: function() {
// Close mini profiler
$('.profiler-results .profiler-result').remove();
// Close some elements that may be open
$('.d-dropdown').hide();
$('header ul.icons li').removeClass('active');
$('[data-toggle="dropdown"]').parent().removeClass('open');
// close the lightbox
if ($.magnificPopup && $.magnificPopup.instance) {
$.magnificPopup.instance.close();
$('body').removeClass('mfp-zoom-out-cur');
}
// Remove any link focus
// NOTE: the '.not("body")' is here to prevent a bug in IE10 on Win7
// cf. https://stackoverflow.com/questions/5657371/ie9-window-loses-focus-due-to-jquery-mobile
$(document.activeElement).not("body").blur();
Discourse.set('notifyCount',0);
$('#discourse-modal').modal('hide');
var hideDropDownFunction = $('html').data('hide-dropdown');
if (hideDropDownFunction) { hideDropDownFunction(); }
// TODO: Avoid container lookup here
var appEvents = Discourse.__container__.lookup('app-events:main');
appEvents.trigger('dom:clean');
},
showModal: function(route, name, model) {
route.controllerFor('modal').set('modalClass', null);
route.render(name, {into: 'modal', outlet: 'modalBody'});
var controller = route.controllerFor(name);
if (controller) {
if (model) {
controller.set('model', model);
}
if(controller && controller.onShow) {
controller.onShow();
}
controller.set('flashMessage', null);
}
Ember.warn('DEPRECATED `Discourse.Route.showModal` - use `showModal` instead');
showModal(name, model);
}
});
export default DiscourseRoute;

View File

@ -1,4 +1,5 @@
import ShowFooter from "discourse/mixins/show-footer";
import ShowFooter from 'discourse/mixins/show-footer';
import showModal from 'discourse/lib/show-modal';
Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(Discourse.OpenComposer, ShowFooter, {
renderTemplate() {
@ -45,7 +46,7 @@ Discourse.DiscoveryCategoriesRoute = Discourse.Route.extend(Discourse.OpenCompos
const groups = this.site.groups,
everyoneName = groups.findBy('id', 0).name;
Discourse.Route.showModal(this, 'editCategory', Discourse.Category.create({
showModal('editCategory', Discourse.Category.create({
color: 'AB9364', text_color: 'FFFFFF', group_permissions: [{group_name: everyoneName, permission_type: 1}],
available_groups: groups.map(g => g.name),
allow_badges: true

View File

@ -5,7 +5,7 @@
import ShowFooter from "discourse/mixins/show-footer";
Discourse.DiscoveryRoute = Discourse.Route.extend(Discourse.ScrollTop, Discourse.OpenComposer, ShowFooter, {
const DiscoveryRoute = Discourse.Route.extend(Discourse.ScrollTop, Discourse.OpenComposer, ShowFooter, {
redirect: function() { return this.redirectIfLoginRequired(); },
beforeModel: function(transition) {
@ -42,22 +42,9 @@ Discourse.DiscoveryRoute = Discourse.Route.extend(Discourse.ScrollTop, Discourse
createTopic: function() {
this.openComposer(this.controllerFor('discovery/topics'));
},
changeBulkTemplate: function(w) {
var controllerName = w.replace('modal/', ''),
factory = this.container.lookupFactory('controller:' + controllerName);
this.render(w, {into: 'topicBulkActions', outlet: 'bulkOutlet', controller: factory ? controllerName : 'topic-bulk-actions'});
},
showBulkActions: function() {
var selected = this.controllerFor('discovery/topics').get('selected');
Discourse.Route.showModal(this, 'topicBulkActions', selected);
this.send('changeBulkTemplate', 'modal/bulk_actions_buttons');
}
}
});
export default Discourse.DiscoveryRoute;
export default DiscoveryRoute;

View File

@ -1,23 +1,24 @@
import ShowFooter from "discourse/mixins/show-footer";
import RestrictedUserRoute from "discourse/routes/restricted-user";
import showModal from 'discourse/lib/show-modal';
export default RestrictedUserRoute.extend(ShowFooter, {
model: function() {
model() {
return this.modelFor('user');
},
setupController: function(controller, user) {
setupController(controller, user) {
controller.setProperties({ model: user, newNameInput: user.get('name') });
},
actions: {
showAvatarSelector: function() {
Discourse.Route.showModal(this, 'avatar-selector');
showAvatarSelector() {
showModal('avatar-selector');
// all the properties needed for displaying the avatar selector modal
var controller = this.controllerFor('avatar-selector');
var user = this.modelFor('user');
var props = user.getProperties(
const controller = this.controllerFor('avatar-selector');
const user = this.modelFor('user');
const props = user.getProperties(
'username', 'email',
'uploaded_avatar_id',
'system_avatar_upload_id',
@ -40,8 +41,8 @@ export default RestrictedUserRoute.extend(ShowFooter, {
},
saveAvatarSelection: function() {
var user = this.modelFor('user');
var avatarSelector = this.controllerFor('avatar-selector');
const user = this.modelFor('user');
const avatarSelector = this.controllerFor('avatar-selector');
// sends the information to the server if it has changed
if (avatarSelector.get('selectedUploadId') !== user.get('uploaded_avatar_id')) {

View File

@ -1,12 +1,14 @@
var isTransitioning = false,
let isTransitioning = false,
scheduledReplace = null,
lastScrollPos = null,
SCROLL_DELAY = 500;
lastScrollPos = null;
const SCROLL_DELAY = 500;
import ShowFooter from "discourse/mixins/show-footer";
import Topic from 'discourse/models/topic';
import showModal from 'discourse/lib/show-modal';
var TopicRoute = Discourse.Route.extend(ShowFooter, {
const TopicRoute = Discourse.Route.extend(ShowFooter, {
redirect() { return this.redirectIfLoginRequired(); },
queryParams: {
@ -16,16 +18,16 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
},
titleToken() {
var model = this.modelFor('topic');
const model = this.modelFor('topic');
if (model) {
var result = model.get('title'),
cat = model.get('category');
const result = model.get('title'),
cat = model.get('category');
// Only display uncategorized in the title tag if it was renamed
if (cat && !(cat.get('isUncategorizedCategory') && cat.get('name').toLowerCase() === "uncategorized")) {
var catName = cat.get('name'),
parentCategory = cat.get('parentCategory');
let catName = cat.get('name');
const parentCategory = cat.get('parentCategory');
if (parentCategory) {
catName = parentCategory.get('name') + " / " + catName;
}
@ -43,27 +45,27 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
},
showFlags(post) {
Discourse.Route.showModal(this, 'flag', post);
showModal('flag', post);
this.controllerFor('flag').setProperties({ selected: null });
},
showFlagTopic(topic) {
Discourse.Route.showModal(this, 'flag', topic);
showModal('flag', topic);
this.controllerFor('flag').setProperties({ selected: null, flagTopic: true });
},
showAutoClose() {
Discourse.Route.showModal(this, 'editTopicAutoClose', this.modelFor('topic'));
showModal('editTopicAutoClose', this.modelFor('topic'));
this.controllerFor('modal').set('modalClass', 'edit-auto-close-modal');
},
showInvite() {
Discourse.Route.showModal(this, 'invite', this.modelFor('topic'));
showModal('invite', this.modelFor('topic'));
this.controllerFor('invite').reset();
},
showPrivateInvite() {
Discourse.Route.showModal(this, 'invitePrivate', this.modelFor('topic'));
showModal('invitePrivate', this.modelFor('topic'));
this.controllerFor('invitePrivate').setProperties({
email: null,
error: false,
@ -73,26 +75,26 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
},
showHistory(post) {
Discourse.Route.showModal(this, 'history', post);
showModal('history', post);
this.controllerFor('history').refresh(post.get("id"), "latest");
this.controllerFor('modal').set('modalClass', 'history-modal');
},
showRawEmail(post) {
Discourse.Route.showModal(this, 'raw-email', post);
showModal('raw-email', post);
this.controllerFor('raw_email').loadRawEmail(post.get("id"));
},
mergeTopic() {
Discourse.Route.showModal(this, 'mergeTopic', this.modelFor('topic'));
showModal('mergeTopic', this.modelFor('topic'));
},
splitTopic() {
Discourse.Route.showModal(this, 'split-topic', this.modelFor('topic'));
showModal('split-topic', this.modelFor('topic'));
},
changeOwner() {
Discourse.Route.showModal(this, 'changeOwner', this.modelFor('topic'));
showModal('changeOwner', this.modelFor('topic'));
},
// Use replaceState to update the URL once it changes
@ -100,9 +102,9 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
// do nothing if we are transitioning to another route
if (isTransitioning || Discourse.TopicRoute.disableReplaceState) { return; }
var topic = this.modelFor('topic');
const topic = this.modelFor('topic');
if (topic && currentPost) {
var postUrl = topic.get('url');
let postUrl = topic.get('url');
if (currentPost > 1) { postUrl += "/" + currentPost; }
Em.run.cancel(scheduledReplace);
@ -128,7 +130,7 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
// replaceState can be very slow on Android Chrome. This function debounces replaceState
// within a topic until scrolling stops
_replaceUnlessScrolling(url) {
var currentPos = parseInt($(document).scrollTop(), 10);
const currentPos = parseInt($(document).scrollTop(), 10);
if (currentPos === lastScrollPos) {
Discourse.URL.replaceState(url);
return;
@ -138,11 +140,11 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
},
setupParams(topic, params) {
var postStream = topic.get('postStream');
const postStream = topic.get('postStream');
postStream.set('summary', Em.get(params, 'filter') === 'summary');
postStream.set('show_deleted', !!Em.get(params, 'show_deleted'));
var usernames = Em.get(params, 'username_filters'),
const usernames = Em.get(params, 'username_filters'),
userFilters = postStream.get('userFilters');
userFilters.clear();
@ -154,9 +156,9 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
},
model(params, transition) {
var queryParams = transition.queryParams;
const queryParams = transition.queryParams;
var topic = this.modelFor('topic');
const topic = this.modelFor('topic');
if (topic && (topic.get('id') === parseInt(params.id, 10))) {
this.setupParams(topic, queryParams);
// If we have the existing model, refresh it
@ -172,7 +174,7 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
this._super();
isTransitioning = false;
var topic = this.modelFor('topic');
const topic = this.modelFor('topic');
this.session.set('lastTopicIdViewed', parseInt(topic.get('id'), 10));
this.controllerFor('search').set('searchContext', topic.get('searchContext'));
},
@ -184,7 +186,7 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
this.controllerFor('search').set('searchContext', null);
this.controllerFor('user-card').set('visible', false);
var topicController = this.controllerFor('topic'),
const topicController = this.controllerFor('topic'),
postStream = topicController.get('postStream');
postStream.cancelFilter();
@ -193,8 +195,8 @@ var TopicRoute = Discourse.Route.extend(ShowFooter, {
this.controllerFor('composer').set('topic', null);
Discourse.ScreenTrack.current().stop();
var headerController;
if (headerController = this.controllerFor('header')) {
const headerController = this.controllerFor('header');
if (headerController) {
headerController.set('topic', null);
headerController.set('showExtraInfo', false);
}

View File

@ -1,15 +1,16 @@
import ShowFooter from "discourse/mixins/show-footer";
import ShowFooter from 'discourse/mixins/show-footer';
import showModal from 'discourse/lib/show-modal';
export default Discourse.Route.extend(ShowFooter, {
renderTemplate: function() {
renderTemplate() {
this.render({ into: 'user' });
},
model: function() {
model() {
return Discourse.Invite.findInvitedBy(this.modelFor('user'));
},
setupController: function(controller, model) {
setupController(controller, model) {
controller.setProperties({
model: model,
user: this.controllerFor('user').get('model'),
@ -19,16 +20,16 @@ export default Discourse.Route.extend(ShowFooter, {
},
actions: {
showInvite: function() {
Discourse.Route.showModal(this, 'invite', Discourse.User.current());
showInvite() {
showModal('invite', Discourse.User.current());
this.controllerFor('invite').reset();
},
uploadSuccess: function(filename) {
uploadSuccess(filename) {
bootbox.alert(I18n.t("user.invited.bulk_invite.success", { filename: filename }));
},
uploadError: function(filename, message) {
uploadError(filename, message) {
bootbox.alert(I18n.t("user.invited.bulk_invite.error", { filename: filename, message: message }));
}
}

View File

@ -0,0 +1,5 @@
{{#if selected}}
<div id='bulk-select'>
{{d-button action="showBulkActions" icon="wrench" class="no-text"}}
</div>
{{/if}}

View File

@ -14,11 +14,7 @@
</div>
{{/if}}
{{#if selected}}
<div id='bulk-select'>
{{d-button action="showBulkActions" icon="wrench" class="no-text"}}
</div>
{{/if}}
{{bulk-select-button selected=selected refreshTarget=controller}}
<div class='contents'>
{{#if top}}

View File

@ -49,7 +49,8 @@
//= require ./discourse/components/notifications-button
//= require ./discourse/components/topic-notifications-button
//= require ./discourse/views/composer
//= require ./discourse/routes/discourse_route
//= require ./discourse/lib/show-modal
//= require ./discourse/routes/discourse
//= require ./discourse/routes/build-topic-route
//= require ./discourse/routes/restricted-user
//= require ./discourse/routes/user-topic-list

View File

@ -106,6 +106,7 @@ module Tilt
# HAX
result = "Controller" if result == "ControllerController"
result = "Route" if result == "DiscourseRoute"
result.gsub!(/Mixin$/, '')
result.gsub!(/Model$/, '')