diff --git a/app/assets/javascripts/admin/controllers/admin-agree-flag.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-agree-flag.js.es6
similarity index 100%
rename from app/assets/javascripts/admin/controllers/admin-agree-flag.js.es6
rename to app/assets/javascripts/admin/controllers/modals/admin-agree-flag.js.es6
diff --git a/app/assets/javascripts/admin/controllers/admin-badge-preview.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-badge-preview.js.es6
similarity index 100%
rename from app/assets/javascripts/admin/controllers/admin-badge-preview.js.es6
rename to app/assets/javascripts/admin/controllers/modals/admin-badge-preview.js.es6
diff --git a/app/assets/javascripts/admin/controllers/admin-delete-flag.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-delete-flag.js.es6
similarity index 100%
rename from app/assets/javascripts/admin/controllers/admin-delete-flag.js.es6
rename to app/assets/javascripts/admin/controllers/modals/admin-delete-flag.js.es6
diff --git a/app/assets/javascripts/admin/controllers/admin-edit-badge-groupings.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6
similarity index 100%
rename from app/assets/javascripts/admin/controllers/admin-edit-badge-groupings.js.es6
rename to app/assets/javascripts/admin/controllers/modals/admin-edit-badge-groupings.js.es6
diff --git a/app/assets/javascripts/admin/controllers/admin-staff-action-log-details.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-staff-action-log-details.js.es6
similarity index 100%
rename from app/assets/javascripts/admin/controllers/admin-staff-action-log-details.js.es6
rename to app/assets/javascripts/admin/controllers/modals/admin-staff-action-log-details.js.es6
diff --git a/app/assets/javascripts/admin/controllers/admin-suspend-user.js.es6 b/app/assets/javascripts/admin/controllers/modals/admin-suspend-user.js.es6
similarity index 99%
rename from app/assets/javascripts/admin/controllers/admin-suspend-user.js.es6
rename to app/assets/javascripts/admin/controllers/modals/admin-suspend-user.js.es6
index b2d30f96d..49999aa09 100644
--- a/app/assets/javascripts/admin/controllers/admin-suspend-user.js.es6
+++ b/app/assets/javascripts/admin/controllers/modals/admin-suspend-user.js.es6
@@ -1,5 +1,4 @@
import ModalFunctionality from 'discourse/mixins/modal-functionality';
-
import ObjectController from 'discourse/controllers/object';
export default ObjectController.extend(ModalFunctionality, {
diff --git a/app/assets/javascripts/admin/controllers/change-site-customization-details.js.es6 b/app/assets/javascripts/admin/controllers/modals/change-site-customization-details.js.es6
similarity index 100%
rename from app/assets/javascripts/admin/controllers/change-site-customization-details.js.es6
rename to app/assets/javascripts/admin/controllers/modals/change-site-customization-details.js.es6
diff --git a/app/assets/javascripts/admin/controllers/delete-site-customization-details.js.es6 b/app/assets/javascripts/admin/controllers/modals/delete-site-customization-details.js.es6
similarity index 65%
rename from app/assets/javascripts/admin/controllers/delete-site-customization-details.js.es6
rename to app/assets/javascripts/admin/controllers/modals/delete-site-customization-details.js.es6
index 57e3c9652..d38c396ce 100644
--- a/app/assets/javascripts/admin/controllers/delete-site-customization-details.js.es6
+++ b/app/assets/javascripts/admin/controllers/modals/delete-site-customization-details.js.es6
@@ -1,7 +1,7 @@
-import ChangeSiteCustomizationDetailsController from "admin/controllers/change-site-customization-details";
+import ChangeSiteCustomizationDetailsController from "admin/controllers/modals/change-site-customization-details";
export default ChangeSiteCustomizationDetailsController.extend({
onShow: function() {
- this.selectPrevious();
+ this.send("selectPrevious");
}
});
diff --git a/app/assets/javascripts/admin/routes/admin-badges-show.js.es6 b/app/assets/javascripts/admin/routes/admin-badges-show.js.es6
index 3d2ac3445..56d0f93da 100644
--- a/app/assets/javascripts/admin/routes/admin-badges-show.js.es6
+++ b/app/assets/javascripts/admin/routes/admin-badges-show.js.es6
@@ -25,7 +25,7 @@ export default Ember.Route.extend({
editGroupings() {
const groupings = this.controllerFor('admin-badges').get('badgeGroupings');
- showModal('admin_edit_badge_groupings', groupings);
+ showModal('modals/admin-edit-badge-groupings', groupings);
},
preview(badge, explain) {
@@ -40,7 +40,7 @@ export default Ember.Route.extend({
}
}).then(function(json) {
badge.set('preview_loading', false);
- showModal('admin_badge_preview', json);
+ showModal('modals/admin-badge-preview', json);
}).catch(function(error) {
badge.set('preview_loading', false);
Em.Logger.error(error);
diff --git a/app/assets/javascripts/admin/routes/admin-flags-list.js.es6 b/app/assets/javascripts/admin/routes/admin-flags-list.js.es6
index afe3d8ab4..99ceba771 100644
--- a/app/assets/javascripts/admin/routes/admin-flags-list.js.es6
+++ b/app/assets/javascripts/admin/routes/admin-flags-list.js.es6
@@ -13,12 +13,12 @@ export default Discourse.Route.extend({
actions: {
showAgreeFlagModal(flaggedPost) {
- showModal('admin_agree_flag', flaggedPost);
+ showModal('modals/admin-agree-flag', flaggedPost);
this.controllerFor('modal').set('modalClass', 'agree-flag-modal');
},
showDeleteFlagModal(flaggedPost) {
- showModal('admin_delete_flag', flaggedPost);
+ showModal('modals/admin-delete-flag', flaggedPost);
this.controllerFor('modal').set('modalClass', 'delete-flag-modal');
}
diff --git a/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6 b/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6
index 29a40ed77..f0f08ab2f 100644
--- a/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6
+++ b/app/assets/javascripts/admin/routes/admin-logs-staff-action-logs.js.es6
@@ -13,12 +13,13 @@ export default Discourse.Route.extend({
actions: {
showDetailsModal(logRecord) {
- showModal('admin_staff_action_log_details', logRecord);
+ showModal('modals/admin-staff-action-log-details', logRecord);
this.controllerFor('modal').set('modalClass', 'log-details-modal');
},
showCustomDetailsModal(logRecord) {
- showModal(logRecord.action_name + '_details', logRecord);
+ const modalName = "modals/" + (logRecord.action_name + '_details').replace("_", "-");
+ showModal(modalName, logRecord);
this.controllerFor('modal').set('modalClass', 'tabbed-modal log-details-modal');
}
}
diff --git a/app/assets/javascripts/admin/routes/admin-user-index.js.es6 b/app/assets/javascripts/admin/routes/admin-user-index.js.es6
index e9c084ab3..07cbfa8fc 100644
--- a/app/assets/javascripts/admin/routes/admin-user-index.js.es6
+++ b/app/assets/javascripts/admin/routes/admin-user-index.js.es6
@@ -25,7 +25,7 @@ export default Discourse.Route.extend({
actions: {
showSuspendModal(user) {
- showModal('admin_suspend_user', user);
+ showModal('modals/admin-suspend-user', user);
this.controllerFor('modal').set('modalClass', 'suspend-user-modal');
}
}
diff --git a/app/assets/javascripts/admin/views/modals/admin_agree_flag_view.js b/app/assets/javascripts/admin/views/modals/admin-agree-flag.js.es6
similarity index 54%
rename from app/assets/javascripts/admin/views/modals/admin_agree_flag_view.js
rename to app/assets/javascripts/admin/views/modals/admin-agree-flag.js.es6
index cabb3c7fd..0a4aba513 100644
--- a/app/assets/javascripts/admin/views/modals/admin_agree_flag_view.js
+++ b/app/assets/javascripts/admin/views/modals/admin-agree-flag.js.es6
@@ -1,4 +1,6 @@
-Discourse.AdminAgreeFlagView = Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'admin/templates/modal/admin_agree_flag',
title: I18n.t('admin.flags.agree_flag_modal_title')
});
diff --git a/app/assets/javascripts/admin/views/modals/admin_badge_preview_view.js b/app/assets/javascripts/admin/views/modals/admin-badge-preview.js.es6
similarity index 55%
rename from app/assets/javascripts/admin/views/modals/admin_badge_preview_view.js
rename to app/assets/javascripts/admin/views/modals/admin-badge-preview.js.es6
index 0d890b3d6..6431ba936 100644
--- a/app/assets/javascripts/admin/views/modals/admin_badge_preview_view.js
+++ b/app/assets/javascripts/admin/views/modals/admin-badge-preview.js.es6
@@ -1,5 +1,6 @@
+import ModalBodyView from "discourse/views/modal-body";
-Discourse.AdminBadgePreviewView = Discourse.ModalBodyView.extend({
+export default ModalBodyView.extend({
templateName: 'admin/templates/modal/admin_badge_preview',
title: I18n.t('admin.badges.preview.modal_title')
});
diff --git a/app/assets/javascripts/admin/views/modals/admin-delete-flag.js.es6 b/app/assets/javascripts/admin/views/modals/admin-delete-flag.js.es6
new file mode 100644
index 000000000..379b2ef77
--- /dev/null
+++ b/app/assets/javascripts/admin/views/modals/admin-delete-flag.js.es6
@@ -0,0 +1,6 @@
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
+ templateName: 'admin/templates/modal/admin_delete_flag',
+ title: I18n.t('admin.flags.delete_flag_modal_title')
+});
diff --git a/app/assets/javascripts/admin/views/modals/admin_edit_badge_groupings_view.js b/app/assets/javascripts/admin/views/modals/admin-edit-badge-groupings.js.es6
similarity index 58%
rename from app/assets/javascripts/admin/views/modals/admin_edit_badge_groupings_view.js
rename to app/assets/javascripts/admin/views/modals/admin-edit-badge-groupings.js.es6
index cf2227f35..ccc5f49a3 100644
--- a/app/assets/javascripts/admin/views/modals/admin_edit_badge_groupings_view.js
+++ b/app/assets/javascripts/admin/views/modals/admin-edit-badge-groupings.js.es6
@@ -1,5 +1,6 @@
+import ModalBodyView from "discourse/views/modal-body";
-Discourse.AdminEditBadgeGroupingsView = Discourse.ModalBodyView.extend({
+export default ModalBodyView.extend({
templateName: 'admin/templates/modal/admin_edit_badge_groupings',
title: I18n.t('admin.badges.badge_groupings.modal_title')
});
diff --git a/app/assets/javascripts/admin/views/modals/admin-staff-action-log-details.js.es6 b/app/assets/javascripts/admin/views/modals/admin-staff-action-log-details.js.es6
new file mode 100644
index 000000000..a31a61e0c
--- /dev/null
+++ b/app/assets/javascripts/admin/views/modals/admin-staff-action-log-details.js.es6
@@ -0,0 +1,6 @@
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
+ templateName: 'admin/templates/logs/details_modal',
+ title: I18n.t('admin.logs.staff_actions.modal_title')
+});
diff --git a/app/assets/javascripts/admin/views/modals/admin_start_backup_view.js b/app/assets/javascripts/admin/views/modals/admin-start-backup.js.es6
similarity index 56%
rename from app/assets/javascripts/admin/views/modals/admin_start_backup_view.js
rename to app/assets/javascripts/admin/views/modals/admin-start-backup.js.es6
index b59207f26..e363ee1a1 100644
--- a/app/assets/javascripts/admin/views/modals/admin_start_backup_view.js
+++ b/app/assets/javascripts/admin/views/modals/admin-start-backup.js.es6
@@ -1,4 +1,6 @@
-Discourse.AdminStartBackupView = Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'admin/templates/modal/admin_start_backup',
title: I18n.t('admin.backups.operations.backup.confirm')
});
diff --git a/app/assets/javascripts/admin/views/modals/admin-suspend-user.js.es6 b/app/assets/javascripts/admin/views/modals/admin-suspend-user.js.es6
new file mode 100644
index 000000000..9f3dcb163
--- /dev/null
+++ b/app/assets/javascripts/admin/views/modals/admin-suspend-user.js.es6
@@ -0,0 +1,6 @@
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
+ templateName: 'admin/templates/modal/admin_suspend_user',
+ title: I18n.t('admin.user.suspend_modal_title')
+});
diff --git a/app/assets/javascripts/admin/views/modals/admin_delete_flag_view.js b/app/assets/javascripts/admin/views/modals/admin_delete_flag_view.js
deleted file mode 100644
index 204e7b382..000000000
--- a/app/assets/javascripts/admin/views/modals/admin_delete_flag_view.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- A modal view for deleting a flag.
-
- @class AdminDeleteFlagView
- @extends Discourse.ModalBodyView
- @namespace Discourse
- @module Discourse
-**/
-Discourse.AdminDeleteFlagView = Discourse.ModalBodyView.extend({
- templateName: 'admin/templates/modal/admin_delete_flag',
- title: I18n.t('admin.flags.delete_flag_modal_title')
-});
diff --git a/app/assets/javascripts/admin/views/modals/admin_staff_action_log_details_view.js b/app/assets/javascripts/admin/views/modals/admin_staff_action_log_details_view.js
deleted file mode 100644
index 7e6e623e3..000000000
--- a/app/assets/javascripts/admin/views/modals/admin_staff_action_log_details_view.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- A modal view for details of a staff action log record in a modal.
-
- @class AdminStaffActionLogDetailsView
- @extends Discourse.ModalBodyView
- @namespace Discourse
- @module Discourse
-**/
-Discourse.AdminStaffActionLogDetailsView = Discourse.ModalBodyView.extend({
- templateName: 'admin/templates/logs/details_modal',
- title: I18n.t('admin.logs.staff_actions.modal_title')
-});
diff --git a/app/assets/javascripts/admin/views/modals/admin_suspend_user_view.js b/app/assets/javascripts/admin/views/modals/admin_suspend_user_view.js
deleted file mode 100644
index 102a5e765..000000000
--- a/app/assets/javascripts/admin/views/modals/admin_suspend_user_view.js
+++ /dev/null
@@ -1,12 +0,0 @@
-/**
- A modal view for suspending a user.
-
- @class AdminSuspendUserView
- @extends Discourse.ModalBodyView
- @namespace Discourse
- @module Discourse
-**/
-Discourse.AdminSuspendUserView = Discourse.ModalBodyView.extend({
- templateName: 'admin/templates/modal/admin_suspend_user',
- title: I18n.t('admin.user.suspend_modal_title')
-});
diff --git a/app/assets/javascripts/admin/views/modals/change-site-customization-details.js.es6 b/app/assets/javascripts/admin/views/modals/change-site-customization-details.js.es6
new file mode 100644
index 000000000..28cc323e4
--- /dev/null
+++ b/app/assets/javascripts/admin/views/modals/change-site-customization-details.js.es6
@@ -0,0 +1,6 @@
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
+ templateName: 'admin/templates/logs/site_customization_change_modal',
+ title: I18n.t('admin.logs.staff_actions.modal_title')
+});
diff --git a/app/assets/javascripts/admin/views/modals/change_site_customization_details_view.js b/app/assets/javascripts/admin/views/modals/change_site_customization_details_view.js
deleted file mode 100644
index 7728bf777..000000000
--- a/app/assets/javascripts/admin/views/modals/change_site_customization_details_view.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- A modal view for details of a staff action log record in a modal
- for when a site customization is created or changed.
-
- @class ChangeSiteCustomizationDetailsView
- @extends Discourse.ModalBodyView
- @namespace Discourse
- @module Discourse
-**/
-Discourse.ChangeSiteCustomizationDetailsView = Discourse.ModalBodyView.extend({
- templateName: 'admin/templates/logs/site_customization_change_modal',
- title: I18n.t('admin.logs.staff_actions.modal_title')
-});
diff --git a/app/assets/javascripts/admin/views/modals/delete-site-customization-details.js.es6 b/app/assets/javascripts/admin/views/modals/delete-site-customization-details.js.es6
new file mode 100644
index 000000000..28cc323e4
--- /dev/null
+++ b/app/assets/javascripts/admin/views/modals/delete-site-customization-details.js.es6
@@ -0,0 +1,6 @@
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
+ templateName: 'admin/templates/logs/site_customization_change_modal',
+ title: I18n.t('admin.logs.staff_actions.modal_title')
+});
diff --git a/app/assets/javascripts/admin/views/modals/delete_site_customization_details_view.js b/app/assets/javascripts/admin/views/modals/delete_site_customization_details_view.js
deleted file mode 100644
index a4670c697..000000000
--- a/app/assets/javascripts/admin/views/modals/delete_site_customization_details_view.js
+++ /dev/null
@@ -1,13 +0,0 @@
-/**
- A modal view for details of a staff action log record in a modal
- for when a site customization is deleted.
-
- @class DeleteSiteCustomizationDetailsView
- @extends Discourse.ModalBodyView
- @namespace Discourse
- @module Discourse
-**/
-Discourse.DeleteSiteCustomizationDetailsView = Discourse.ModalBodyView.extend({
- templateName: 'admin/templates/logs/site_customization_change_modal',
- title: I18n.t('admin.logs.staff_actions.modal_title')
-});
diff --git a/app/assets/javascripts/discourse/controllers/feature-topic.js.es6 b/app/assets/javascripts/discourse/controllers/feature-topic.js.es6
new file mode 100644
index 000000000..f7ae397e8
--- /dev/null
+++ b/app/assets/javascripts/discourse/controllers/feature-topic.js.es6
@@ -0,0 +1,77 @@
+import ModalFunctionality from 'discourse/mixins/modal-functionality';
+import ObjectController from 'discourse/controllers/object';
+import { categoryLinkHTML } from 'discourse/helpers/category-link';
+
+export default ObjectController.extend(ModalFunctionality, {
+ needs: ["topic"],
+
+ loading: true,
+ pinnedInCategoryCount: 0,
+ pinnedGloballyCount: 0,
+ bannerCount: 0,
+
+ categoryLink: function() {
+ return categoryLinkHTML(this.get("category"), { allowUncategorized: true });
+ }.property("category"),
+
+ unPinMessage: function() {
+ return this.get("pinned_globally") ?
+ I18n.t("topic.feature_topic.unpin_globally") :
+ I18n.t("topic.feature_topic.unpin", { categoryLink: this.get("categoryLink") });
+ }.property("categoryLink", "pinned_globally"),
+
+ pinMessage: function() {
+ return I18n.t("topic.feature_topic.pin", { categoryLink: this.get("categoryLink") });
+ }.property("categoryLink"),
+
+ alreadyPinnedMessage: function() {
+ return I18n.t("topic.feature_topic.already_pinned", { categoryLink: this.get("categoryLink"), count: this.get("pinnedInCategoryCount") });
+ }.property("categoryLink", "pinnedInCategoryCount"),
+
+ onShow() {
+ const self = this;
+
+ this.set("loading", true);
+
+ return Discourse.ajax("/topics/feature_stats.json", {
+ data: { category_id: this.get("category.id") }
+ }).then(function(result) {
+ if (result) {
+ self.setProperties({
+ pinnedInCategoryCount: result.pinned_in_category_count,
+ pinnedGloballyCount: result.pinned_globally_count,
+ bannerCount: result.banner_count,
+ });
+ }
+ }).finally(function() {
+ self.set("loading", false);
+ });
+ },
+
+ _forwardAction(name) {
+ this.get("controllers.topic").send(name);
+ this.send("closeModal");
+ },
+
+ _confirmBeforePinning(count, name, action) {
+ if (count < 4) {
+ this._forwardAction(action);
+ } else {
+ this.send("hideModal");
+ const message = I18n.t("topic.feature_topic.confirm_" + name, { count: count });
+ bootbox.confirm(
+ message, I18n.t("no_value"), I18n.t("yes_value"),
+ (confirmed) => confirmed ? this._forwardAction(action) : this.send("reopenModal")
+ );
+ }
+ },
+
+ actions: {
+ pin() { this._confirmBeforePinning(this.get("pinnedInCategoryCount"), "pin", "togglePinned"); },
+ pinGlobally() { this._confirmBeforePinning(this.get("pinnedGloballyCount"), "pin_globally", "pinGlobally"); },
+ unpin() { this._forwardAction("togglePinned"); },
+ makeBanner() { this._forwardAction("makeBanner"); },
+ removeBanner() { this._forwardAction("removeBanner"); },
+ }
+
+});
diff --git a/app/assets/javascripts/discourse/controllers/topic.js.es6 b/app/assets/javascripts/discourse/controllers/topic.js.es6
index e0da67fe6..c99a98655 100644
--- a/app/assets/javascripts/discourse/controllers/topic.js.es6
+++ b/app/assets/javascripts/discourse/controllers/topic.js.es6
@@ -94,6 +94,19 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
this.set('selectedReplies', []);
}.on('init'),
+ _togglePinnedStates(property) {
+ const value = this.get('pinned_at') ? false : true,
+ topic = this.get('content');
+
+ // optimistic update
+ topic.setProperties({
+ pinned_at: value,
+ pinned_globally: value
+ });
+
+ return topic.saveStatus(property, value);
+ },
+
actions: {
deleteTopic() {
this.deleteTopic();
@@ -352,13 +365,28 @@ export default ObjectController.extend(Discourse.SelectedPostsCount, BufferedCon
},
togglePinned() {
- // Note that this is different than clearPin
- this.get('content').setStatus('pinned', this.get('pinned_at') ? false : true);
+ const value = this.get('pinned_at') ? false : true,
+ topic = this.get('content');
+
+ // optimistic update
+ topic.setProperties({
+ pinned_at: value ? moment() : null,
+ pinned_globally: false
+ });
+
+ return topic.saveStatus("pinned", value);
},
- togglePinnedGlobally() {
- // Note that this is different than clearPin
- this.get('content').setStatus('pinned_globally', this.get('pinned_at') ? false : true);
+ pinGlobally() {
+ const topic = this.get('content');
+
+ // optimistic update
+ topic.setProperties({
+ pinned_at: moment(),
+ pinned_globally: true
+ });
+
+ return topic.saveStatus("pinned_globally", true);
},
toggleArchived() {
diff --git a/app/assets/javascripts/discourse/lib/show-modal.js.es6 b/app/assets/javascripts/discourse/lib/show-modal.js.es6
index f12435c49..667a344dd 100644
--- a/app/assets/javascripts/discourse/lib/show-modal.js.es6
+++ b/app/assets/javascripts/discourse/lib/show-modal.js.es6
@@ -1,19 +1,15 @@
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'});
+ 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();
- }
+ if (model) { controller.set('model', model); }
+ if (controller.onShow) { controller.onShow(); }
controller.set('flashMessage', null);
}
return controller;
diff --git a/app/assets/javascripts/discourse/models/topic.js.es6 b/app/assets/javascripts/discourse/models/topic.js.es6
index 5ae5ee7ec..93ff2850d 100644
--- a/app/assets/javascripts/discourse/models/topic.js.es6
+++ b/app/assets/javascripts/discourse/models/topic.js.es6
@@ -145,24 +145,16 @@ const Topic = Discourse.Model.extend({
toggleStatus(property) {
this.toggleProperty(property);
- this.saveStatus(property, this.get(property) ? true : false);
- },
-
- setStatus(property, value) {
- this.set(property, value);
- this.saveStatus(property, value);
+ this.saveStatus(property, !this.get(property));
},
saveStatus(property, value) {
if (property === 'closed' && value === true) {
this.set('details.auto_close_at', null);
}
- if (property === 'pinned') {
- this.set('pinned_at', value ? moment() : null);
- }
return Discourse.ajax(this.get('url') + "/status", {
type: 'PUT',
- data: {status: property, enabled: value ? 'true' : 'false' }
+ data: { status: property, enabled: !!value }
});
},
diff --git a/app/assets/javascripts/discourse/routes/application.js.es6 b/app/assets/javascripts/discourse/routes/application.js.es6
index 681a097cb..3ba76b649 100644
--- a/app/assets/javascripts/discourse/routes/application.js.es6
+++ b/app/assets/javascripts/discourse/routes/application.js.es6
@@ -102,7 +102,7 @@ const ApplicationRoute = Discourse.Route.extend({
// Close the current modal, and destroy its state.
closeModal() {
- this.render('hide-modal', {into: 'modal', outlet: 'modalBody'});
+ this.render('hide-modal', { into: 'modal', outlet: 'modalBody' });
},
/**
diff --git a/app/assets/javascripts/discourse/routes/topic.js.es6 b/app/assets/javascripts/discourse/routes/topic.js.es6
index 1b1474286..d7948ce3a 100644
--- a/app/assets/javascripts/discourse/routes/topic.js.es6
+++ b/app/assets/javascripts/discourse/routes/topic.js.es6
@@ -59,6 +59,11 @@ const TopicRoute = Discourse.Route.extend(ShowFooter, {
this.controllerFor('modal').set('modalClass', 'edit-auto-close-modal');
},
+ showFeatureTopic() {
+ showModal('featureTopic', this.modelFor('topic'));
+ this.controllerFor('modal').set('modalClass', 'feature-topic-modal');
+ },
+
showInvite() {
showModal('invite', this.modelFor('topic'));
this.controllerFor('invite').reset();
diff --git a/app/assets/javascripts/discourse/templates/modal/feature-topic.hbs b/app/assets/javascripts/discourse/templates/modal/feature-topic.hbs
new file mode 100644
index 000000000..b517a3762
--- /dev/null
+++ b/app/assets/javascripts/discourse/templates/modal/feature-topic.hbs
@@ -0,0 +1,62 @@
+
+ {{#if pinned_at}}
+
+ {{d-button action="unpin" icon="thumb-tack" label="topic.actions.unpin" class="btn-primary btn-small"}}
+
{{{unPinMessage}}}
+ {{#if pinned_globally}}
+
{{i18n "topic.feature_topic.global_pin_note"}}
+
+ {{#loading-spinner size="small" condition=loading}}
+ {{{i18n "topic.feature_topic.already_pinned_globally" count=pinnedGloballyCount}}}
+ {{/loading-spinner}}
+
+ {{else}}
+
{{i18n "topic.feature_topic.pin_note"}}
+
+ {{#loading-spinner size="small" condition=loading}}
+ {{{alreadyPinnedMessage}}}
+ {{/loading-spinner}}
+
+ {{/if}}
+
+ {{else}}
+
+ {{d-button action="pin" icon="thumb-tack" label="topic.actions.pin" class="btn-primary btn-small"}}
+
{{{pinMessage}}}
+
{{i18n "topic.feature_topic.pin_note"}}
+
+ {{#loading-spinner size="small" condition=loading}}
+ {{{alreadyPinnedMessage}}}
+ {{/loading-spinner}}
+
+
+
+ {{d-button action="pinGlobally" icon="thumb-tack" label="topic.actions.pin_globally" class="btn-primary btn-small"}}
+
{{i18n "topic.feature_topic.pin_globally"}}
+
{{i18n "topic.feature_topic.global_pin_note"}}
+
+ {{#loading-spinner size="small" condition=loading}}
+ {{{i18n "topic.feature_topic.already_pinned_globally" count=pinnedGloballyCount}}}
+ {{/loading-spinner}}
+
+
+ {{/if}}
+
+ {{#if isBanner}}
+ {{d-button action="removeBanner" icon="bullhorn" label="topic.actions.remove_banner" class="btn-primary btn-small"}}
+
{{i18n "topic.feature_topic.remove_banner"}}
+ {{else}}
+ {{d-button action="makeBanner" icon="bullhorn" label="topic.actions.make_banner" class="btn-primary btn-small"}}
+
{{i18n "topic.feature_topic.make_banner"}}
+ {{/if}}
+
{{i18n "topic.feature_topic.banner_note"}}
+
+ {{#loading-spinner size="small" condition=loading}}
+ {{{i18n "topic.feature_topic.already_banner" count=bannerCount}}}
+ {{/loading-spinner}}
+
+
+
+
diff --git a/app/assets/javascripts/discourse/templates/topic-admin-menu.hbs b/app/assets/javascripts/discourse/templates/topic-admin-menu.hbs
index ef204205e..0840dfc34 100644
--- a/app/assets/javascripts/discourse/templates/topic-admin-menu.hbs
+++ b/app/assets/javascripts/discourse/templates/topic-admin-menu.hbs
@@ -27,26 +27,11 @@
{{#unless isPrivateMessage}}
-
- {{#if isBanner}}
- {{d-button action="removeBanner" icon="bullhorn" label="topic.actions.remove_banner" class="btn-admin"}}
- {{else}}
- {{#if visible}}
- {{d-button action="makeBanner" icon="bullhorn" label="topic.actions.make_banner" class="btn-admin"}}
- {{/if}}
- {{/if}}
-
-
-
- {{#if pinned_at}}
- {{d-button action="togglePinned" icon="thumb-tack" label="topic.actions.unpin" class="btn-admin"}}
- {{else}}
- {{#if visible}}
- {{d-button action="togglePinned" icon="thumb-tack" label="topic.actions.pin" class="btn-admin"}}
- {{d-button action="togglePinnedGlobally" icon="thumb-tack" label="topic.actions.pin_globally" class="btn-admin"}}
- {{/if}}
- {{/if}}
-
+ {{#if visible}}
+
+ {{d-button action="showFeatureTopic" icon="bullhorn" label="topic.actions.feature" class="btn-admin"}}
+
+ {{/if}}
{{/unless}}
diff --git a/app/assets/javascripts/discourse/views/archetype-options-modal.js.es6 b/app/assets/javascripts/discourse/views/archetype-options-modal.js.es6
index 2daa63756..d6ebb0c27 100644
--- a/app/assets/javascripts/discourse/views/archetype-options-modal.js.es6
+++ b/app/assets/javascripts/discourse/views/archetype-options-modal.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/archetype_options',
title: I18n.t('topic.options')
});
diff --git a/app/assets/javascripts/discourse/views/avatar-selector.js.es6 b/app/assets/javascripts/discourse/views/avatar-selector.js.es6
index c87e91fcd..40414660a 100644
--- a/app/assets/javascripts/discourse/views/avatar-selector.js.es6
+++ b/app/assets/javascripts/discourse/views/avatar-selector.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/avatar_selector',
classNames: ['avatar-selector'],
title: I18n.t('user.change_avatar.title'),
@@ -9,10 +11,6 @@ export default Discourse.ModalBodyView.extend({
// *HACK* used to select the proper radio button, cause {{action}}
// stops the default behavior
selectedChanged: function() {
- var self = this;
- Em.run.next(function() {
- var value = self.get('controller.selected');
- $('input:radio[name="avatar"]').val([value]);
- });
+ Em.run.next(() => $('input:radio[name="avatar"]').val([this.get('controller.selected')]) );
}.observes('controller.selected')
});
diff --git a/app/assets/javascripts/discourse/views/change-owner.js.es6 b/app/assets/javascripts/discourse/views/change-owner.js.es6
index afce839d6..604dfbe56 100644
--- a/app/assets/javascripts/discourse/views/change-owner.js.es6
+++ b/app/assets/javascripts/discourse/views/change-owner.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/change_owner',
title: I18n.t('topic.change_owner.title')
});
diff --git a/app/assets/javascripts/discourse/views/create-account.js.es6 b/app/assets/javascripts/discourse/views/create-account.js.es6
index 8de0a1864..da0e28158 100644
--- a/app/assets/javascripts/discourse/views/create-account.js.es6
+++ b/app/assets/javascripts/discourse/views/create-account.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/create-account',
title: I18n.t('create_account.title'),
classNames: ['create-account'],
@@ -6,7 +8,7 @@ export default Discourse.ModalBodyView.extend({
_setup: function() {
// allows the submission the form when pressing 'ENTER' on *any* text input field
// but only when the submit button is enabled
- var createAccountController = this.get('controller');
+ const createAccountController = this.get('controller');
Em.run.schedule('afterRender', function() {
$("input[type='text'], input[type='password']").keydown(function(e) {
if (createAccountController.get('submitDisabled') === false && e.keyCode === 13) {
diff --git a/app/assets/javascripts/discourse/views/edit-category.js.es6 b/app/assets/javascripts/discourse/views/edit-category.js.es6
index 6d0fb05a5..ea93cc99b 100644
--- a/app/assets/javascripts/discourse/views/edit-category.js.es6
+++ b/app/assets/javascripts/discourse/views/edit-category.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/edit-category',
_initializePanels: function() {
diff --git a/app/assets/javascripts/discourse/views/edit-topic-auto-close.js.es6 b/app/assets/javascripts/discourse/views/edit-topic-auto-close.js.es6
index 57d7e55f6..b5b5c3328 100644
--- a/app/assets/javascripts/discourse/views/edit-topic-auto-close.js.es6
+++ b/app/assets/javascripts/discourse/views/edit-topic-auto-close.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/auto_close',
title: I18n.t('topic.auto_close_title')
});
diff --git a/app/assets/javascripts/discourse/views/feature-topic.js.es6 b/app/assets/javascripts/discourse/views/feature-topic.js.es6
new file mode 100644
index 000000000..2a106541e
--- /dev/null
+++ b/app/assets/javascripts/discourse/views/feature-topic.js.es6
@@ -0,0 +1,6 @@
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
+ templateName: 'modal/feature-topic',
+ title: I18n.t('topic.feature_topic.title')
+});
diff --git a/app/assets/javascripts/discourse/views/flag.js.es6 b/app/assets/javascripts/discourse/views/flag.js.es6
index a819d7efb..b948c35f0 100644
--- a/app/assets/javascripts/discourse/views/flag.js.es6
+++ b/app/assets/javascripts/discourse/views/flag.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/flag',
title: function() {
@@ -6,12 +8,13 @@ export default Discourse.ModalBodyView.extend({
}.property('controller.flagTopic'),
selectedChanged: function() {
- var self = this;
+ const self = this;
+
Em.run.next(function() {
self.$("input[type='radio']").prop('checked', false);
- var nameKey = self.get('controller.selected.name_key');
- if (!nameKey) return;
+ const nameKey = self.get('controller.selected.name_key');
+ if (!nameKey) { return; }
self.$('#radio_' + nameKey).prop('checked', 'true');
});
diff --git a/app/assets/javascripts/discourse/views/forgot-password.js.es6 b/app/assets/javascripts/discourse/views/forgot-password.js.es6
index 5bb6b40ee..40f4094b0 100644
--- a/app/assets/javascripts/discourse/views/forgot-password.js.es6
+++ b/app/assets/javascripts/discourse/views/forgot-password.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/forgot_password',
title: I18n.t('forgot_password.title'),
});
diff --git a/app/assets/javascripts/discourse/views/history.js.es6 b/app/assets/javascripts/discourse/views/history.js.es6
index f88b3f178..7bec4d846 100644
--- a/app/assets/javascripts/discourse/views/history.js.es6
+++ b/app/assets/javascripts/discourse/views/history.js.es6
@@ -1,9 +1,11 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/history',
title: I18n.t('history'),
resizeModal: function(){
- var viewPortHeight = $(window).height();
+ const viewPortHeight = $(window).height();
this.$(".modal-body").css("max-height", Math.floor(0.8 * viewPortHeight) + "px");
}.on("didInsertElement")
});
diff --git a/app/assets/javascripts/discourse/views/invite-private.js.es6 b/app/assets/javascripts/discourse/views/invite-private.js.es6
index 0b827d29b..461a6e102 100644
--- a/app/assets/javascripts/discourse/views/invite-private.js.es6
+++ b/app/assets/javascripts/discourse/views/invite-private.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/invite_private',
title: I18n.t('topic.invite_private.title')
});
diff --git a/app/assets/javascripts/discourse/views/invite.js.es6 b/app/assets/javascripts/discourse/views/invite.js.es6
index 6f8a2e8f1..03f4a72c4 100644
--- a/app/assets/javascripts/discourse/views/invite.js.es6
+++ b/app/assets/javascripts/discourse/views/invite.js.es6
@@ -1,10 +1,12 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/invite',
+
title: function() {
- if (this.get('controller.invitingToTopic')) {
- return I18n.t('topic.invite_reply.title');
- } else {
- return I18n.t('user.invited.create');
- }
+ return this.get('controller.invitingToTopic') ?
+ I18n.t('topic.invite_reply.title') :
+ I18n.t('user.invited.create');
}.property('controller.invitingToTopic')
+
});
diff --git a/app/assets/javascripts/discourse/views/keyboard-shortcuts-help.js.es6 b/app/assets/javascripts/discourse/views/keyboard-shortcuts-help.js.es6
index 33ed36151..eeb174f62 100644
--- a/app/assets/javascripts/discourse/views/keyboard-shortcuts-help.js.es6
+++ b/app/assets/javascripts/discourse/views/keyboard-shortcuts-help.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/keyboard_shortcuts_help',
title: I18n.t('keyboard_shortcuts_help.title')
});
diff --git a/app/assets/javascripts/discourse/views/login.js.es6 b/app/assets/javascripts/discourse/views/login.js.es6
index 334e91774..f2b61efb5 100644
--- a/app/assets/javascripts/discourse/views/login.js.es6
+++ b/app/assets/javascripts/discourse/views/login.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/login',
title: I18n.t('login.title'),
classNames: ['login-modal'],
@@ -9,7 +11,7 @@ export default Discourse.ModalBodyView.extend({
},
_setup: function() {
- var loginController = this.get('controller');
+ const loginController = this.get('controller');
// Get username and password from the browser's password manager,
// if it filled the hidden static login form:
@@ -18,10 +20,8 @@ export default Discourse.ModalBodyView.extend({
Em.run.schedule('afterRender', function() {
$('#login-account-password, #login-account-name').keydown(function(e) {
- if (e.keyCode === 13) {
- if (!loginController.get('loginDisabled')) {
- loginController.send('login');
- }
+ if (e.keyCode === 13 && !loginController.get('loginDisabled')) {
+ loginController.send('login');
}
});
});
diff --git a/app/assets/javascripts/discourse/views/merge-topic.js.es6 b/app/assets/javascripts/discourse/views/merge-topic.js.es6
index b4394c57b..bf31e0060 100644
--- a/app/assets/javascripts/discourse/views/merge-topic.js.es6
+++ b/app/assets/javascripts/discourse/views/merge-topic.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/merge_topic',
title: I18n.t('topic.merge_topic.title')
});
diff --git a/app/assets/javascripts/discourse/views/modal-body.js.es6 b/app/assets/javascripts/discourse/views/modal-body.js.es6
new file mode 100644
index 000000000..720e95516
--- /dev/null
+++ b/app/assets/javascripts/discourse/views/modal-body.js.es6
@@ -0,0 +1,30 @@
+export default Discourse.View.extend({
+ focusInput: true,
+
+ _setupModal: function() {
+ $('#modal-alert').hide();
+ $('#discourse-modal').modal('show');
+
+ // Focus on first element
+ if (!Discourse.Mobile.mobileView && this.get('focusInput')) {
+ Em.run.schedule('afterRender', () => this.$('input:first').focus() );
+ }
+
+ const title = this.get('title');
+ if (title) {
+ this.set('controller.controllers.modal.title', title);
+ }
+ }.on('didInsertElement'),
+
+ flashMessageChanged: function() {
+ const flashMessage = this.get('controller.flashMessage');
+ if (flashMessage) {
+ const messageClass = flashMessage.get('messageClass') || 'success';
+ $('#modal-alert').hide()
+ .removeClass('alert-error', 'alert-success')
+ .addClass("alert alert-" + messageClass).html(flashMessage.get('message'))
+ .fadeIn();
+ }
+ }.observes('controller.flashMessage')
+
+});
diff --git a/app/assets/javascripts/discourse/views/modal.js.es6 b/app/assets/javascripts/discourse/views/modal.js.es6
index 8da41fcc3..4621445dc 100644
--- a/app/assets/javascripts/discourse/views/modal.js.es6
+++ b/app/assets/javascripts/discourse/views/modal.js.es6
@@ -4,13 +4,13 @@ export default Ember.View.extend({
classNameBindings: [':modal', ':hidden', 'controller.modalClass'],
click: function(e) {
- var $target = $(e.target);
+ const $target = $(e.target);
if ($target.hasClass("modal-middle-container") ||
$target.hasClass("modal-outer-container")) {
- // Delegate click to modal backdrop if clicked outside. We do this
- // because some CSS of ours seems to cover the backdrop and makes it
- // unclickable.
- $('.modal-backdrop').click();
+ // Delegate click to modal close if clicked outside.
+ // We do this because some CSS of ours seems to cover
+ // the backdrop and makes it unclickable.
+ $('.modal-header a.close').click();
}
}
});
diff --git a/app/assets/javascripts/discourse/views/modal_body_view.js b/app/assets/javascripts/discourse/views/modal_body_view.js
deleted file mode 100644
index 10eec8f6c..000000000
--- a/app/assets/javascripts/discourse/views/modal_body_view.js
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- A base class for helping us display modal content
-
- @class ModalBodyView
- @extends Discourse.View
- @namespace Discourse
- @module Discourse
-**/
-Discourse.ModalBodyView = Discourse.View.extend({
- focusInput: true,
-
- _setupModal: function() {
- var self = this,
- $discourseModal = $('#discourse-modal');
-
- $discourseModal.modal('show');
- $discourseModal.one("hide", function () {
- self.get("controller").send("closeModal");
- });
-
- $('#modal-alert').hide();
-
- // Focus on first element
- if (!Discourse.Mobile.mobileView && self.get('focusInput')) {
- Em.run.schedule('afterRender', function() {
- self.$('input:first').focus();
- });
- }
-
- var title = this.get('title');
- if (title) {
- this.set('controller.controllers.modal.title', title);
- }
- }.on('didInsertElement'),
-
- flashMessageChanged: function() {
- var flashMessage = this.get('controller.flashMessage');
- if (flashMessage) {
- var messageClass = flashMessage.get('messageClass') || 'success';
- var $alert = $('#modal-alert').hide().removeClass('alert-error', 'alert-success');
- $alert.addClass("alert alert-" + messageClass).html(flashMessage.get('message'));
- $alert.fadeIn();
- }
- }.observes('controller.flashMessage')
-
-});
-
-
diff --git a/app/assets/javascripts/discourse/views/not-activated.js.es6 b/app/assets/javascripts/discourse/views/not-activated.js.es6
index 30f4da117..86e942fc3 100644
--- a/app/assets/javascripts/discourse/views/not-activated.js.es6
+++ b/app/assets/javascripts/discourse/views/not-activated.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/not_activated',
title: I18n.t('log_in')
});
diff --git a/app/assets/javascripts/discourse/views/raw-email.es6 b/app/assets/javascripts/discourse/views/raw-email.es6
index d529b1063..c14d201f3 100644
--- a/app/assets/javascripts/discourse/views/raw-email.es6
+++ b/app/assets/javascripts/discourse/views/raw-email.es6
@@ -1,9 +1,11 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/raw_email',
title: I18n.t('raw_email.title'),
resizeModal: function(){
- var viewPortHeight = $(window).height();
+ const viewPortHeight = $(window).height();
this.$(".modal-body").css("max-height", Math.floor(0.8 * viewPortHeight) + "px");
}.on("didInsertElement")
});
diff --git a/app/assets/javascripts/discourse/views/search-help.js.es6 b/app/assets/javascripts/discourse/views/search-help.js.es6
index 888c48bb4..d16b2fc79 100644
--- a/app/assets/javascripts/discourse/views/search-help.js.es6
+++ b/app/assets/javascripts/discourse/views/search-help.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/search_help',
title: I18n.t('search_help.title'),
focusInput: false
diff --git a/app/assets/javascripts/discourse/views/split-topic.js.es6 b/app/assets/javascripts/discourse/views/split-topic.js.es6
index b4be70f88..1c5be40e9 100644
--- a/app/assets/javascripts/discourse/views/split-topic.js.es6
+++ b/app/assets/javascripts/discourse/views/split-topic.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend(Discourse.SelectedPostsCount, {
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend(Discourse.SelectedPostsCount, {
templateName: 'modal/split_topic',
title: I18n.t('topic.split_topic.title')
});
diff --git a/app/assets/javascripts/discourse/views/topic-bulk-actions.js.es6 b/app/assets/javascripts/discourse/views/topic-bulk-actions.js.es6
index dd6490a71..8b8b693fb 100644
--- a/app/assets/javascripts/discourse/views/topic-bulk-actions.js.es6
+++ b/app/assets/javascripts/discourse/views/topic-bulk-actions.js.es6
@@ -1,4 +1,6 @@
-export default Discourse.ModalBodyView.extend({
+import ModalBodyView from "discourse/views/modal-body";
+
+export default ModalBodyView.extend({
templateName: 'modal/topic-bulk-actions',
title: I18n.t('topics.bulk.actions')
});
diff --git a/app/assets/javascripts/discourse/views/upload-selector.js.es6 b/app/assets/javascripts/discourse/views/upload-selector.js.es6
index 50b46b696..2a4697333 100644
--- a/app/assets/javascripts/discourse/views/upload-selector.js.es6
+++ b/app/assets/javascripts/discourse/views/upload-selector.js.es6
@@ -1,10 +1,12 @@
+import ModalBodyView from "discourse/views/modal-body";
+
function uploadTranslate(key, options) {
- var opts = options || {};
+ const opts = options || {};
if (Discourse.Utilities.allowsAttachments()) { key += "_with_attachments"; }
return I18n.t("upload_selector." + key, opts);
}
-export default Discourse.ModalBodyView.extend({
+export default ModalBodyView.extend({
templateName: 'modal/upload_selector',
classNames: ['upload-selector'],
@@ -12,17 +14,16 @@ export default Discourse.ModalBodyView.extend({
uploadIcon: function() { return Discourse.Utilities.allowsAttachments() ? "fa-upload" : "fa-picture-o"; }.property(),
tip: function() {
- var source = this.get("controller.local") ? "local" : "remote";
- var opts = { authorized_extensions: Discourse.Utilities.authorizedExtensions() };
+ const source = this.get("controller.local") ? "local" : "remote",
+ opts = { authorized_extensions: Discourse.Utilities.authorizedExtensions() };
return uploadTranslate(source + "_tip", opts);
}.property("controller.local"),
hint: function() {
// cf. http://stackoverflow.com/a/9851769/11983
- var isChrome = !!window.chrome && !(!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0);
- var isFirefox = typeof InstallTrigger !== 'undefined';
- var isSupported = isChrome || isFirefox;
-
+ const isChrome = !!window.chrome && !(!!window.opera || navigator.userAgent.indexOf(' OPR/') >= 0),
+ isFirefox = typeof InstallTrigger !== 'undefined',
+ isSupported = isChrome || isFirefox;
// chrome is the only browser that support copy & paste of images.
return I18n.t("upload_selector.hint" + (isSupported ? "_for_supported_browsers" : ""));
}.property(),
@@ -32,7 +33,7 @@ export default Discourse.ModalBodyView.extend({
}.on('didInsertElement'),
selectedChanged: function() {
- var self = this;
+ const self = this;
Em.run.next(function() {
// *HACK* to select the proper radio button
var value = self.get('controller.local') ? 'local' : 'remote';
@@ -47,9 +48,9 @@ export default Discourse.ModalBodyView.extend({
if (this.get("controller.local")) {
$('#reply-control').fileupload('add', { fileInput: $('#filename-input') });
} else {
- var imageUrl = $('#fileurl-input').val();
- var imageLink = $('#link-input').val();
- var composerView = this.get('controller.composerView');
+ const imageUrl = $('#fileurl-input').val(),
+ imageLink = $('#link-input').val(),
+ composerView = this.get('controller.composerView');
if (this.get("controller.showMore") && imageLink.length > 3) {
composerView.addMarkdown("[![](" + imageUrl +")](" + imageLink + ")");
} else {
diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js
index b6dad34de..489ed3c13 100644
--- a/app/assets/javascripts/main_include.js
+++ b/app/assets/javascripts/main_include.js
@@ -36,7 +36,7 @@
//= require ./discourse/controllers/navigation/default
//= require ./discourse/views/view
//= require ./discourse/views/container
-//= require ./discourse/views/modal_body_view
+//= require ./discourse/views/modal-body
//= require ./discourse/views/flag
//= require ./discourse/views/combo-box
//= require ./discourse/views/button
diff --git a/app/assets/javascripts/main_include_admin.js b/app/assets/javascripts/main_include_admin.js
index 9a66a6965..6ba3cb51e 100644
--- a/app/assets/javascripts/main_include_admin.js
+++ b/app/assets/javascripts/main_include_admin.js
@@ -2,7 +2,6 @@
//= require admin/models/user-field
//= require admin/models/site-setting
//= require admin/controllers/admin-email-skipped
-//= require admin/controllers/change-site-customization-details
//= require discourse/lib/export-result
//= require_tree ./admin
diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb
index 0f76977cc..cdc7a2819 100644
--- a/app/controllers/topics_controller.rb
+++ b/app/controllers/topics_controller.rb
@@ -158,6 +158,19 @@ class TopicsController < ApplicationController
render_serialized(topics, BasicTopicSerializer)
end
+ def feature_stats
+ params.require(:category_id)
+ category_id = params[:category_id].to_i
+
+ topics = Topic.listable_topics.visible
+
+ render json: {
+ pinned_in_category_count: topics.where(category_id: category_id).where(pinned_globally: false).where.not(pinned_at: nil).count,
+ pinned_globally_count: topics.where(pinned_globally: true).where.not(pinned_at: nil).count,
+ banner_count: topics.where(archetype: Archetype.banner).count,
+ }
+ end
+
def status
params.require(:status)
params.require(:enabled)
@@ -492,7 +505,7 @@ class TopicsController < ApplicationController
end
def check_for_status_presence(key, attr)
- invalid_param(key) unless %w(pinned_globally visible closed pinned archived).include?(attr)
+ invalid_param(key) unless %w(pinned pinned_globally visible closed archived).include?(attr)
end
def invalid_param(key)
diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml
index 71609c40e..07738bf14 100644
--- a/config/locales/client.en.yml
+++ b/config/locales/client.en.yml
@@ -973,10 +973,11 @@ en:
open: "Open Topic"
close: "Close Topic"
auto_close: "Auto Close"
+ feature: "Feature Topic"
make_banner: "Banner Topic"
remove_banner: "Remove Banner Topic"
- unpin: "Un-Pin Topic"
pin: "Pin Topic"
+ unpin: "Un-Pin Topic"
pin_globally: "Pin Topic Globally"
unarchive: "Unarchive Topic"
archive: "Archive Topic"
@@ -1002,6 +1003,31 @@ en:
help: 'privately flag this topic for attention or send a private notification about it'
success_message: 'You successfully flagged this topic.'
+ feature_topic:
+ title: "Feature this topic"
+ pin: "Make this topic appear at the top of the {{categoryLink}} category."
+ confirm_pin: "Are you sure? You already have {{count}} pinned topics -- too many pinned topics can obscure other active topics."
+ unpin: "Remove this topic from the topic of the {{categoryLink}} category."
+ pin_note: "Users can unpin the topic individually for themselves."
+ already_pinned:
+ zero: "No topic currently pinned in {{categoryLink}}."
+ one: "Topic currently pinned in {{categoryLink}}: 1."
+ other: "Topics currently pinned in {{categoryLink}}: {{count}}."
+ pin_globally: "Make this topic appear at the top of all topic lists, until a staff member unpins it."
+ confirm_pin_globally: "Are you sure? You already have {{count}} globally pinned topics -- too many pinned topics can obscure other active topics."
+ unpin_globally: "Remove this topic from the top of all topic lists."
+ global_pin_note: "Users can unpin the topic individually for themselves."
+ already_pinned_globally:
+ zero: "No topic currently pinned globally."
+ one: "Topic currently pinned globally: 1."
+ other: "Topics currently pinned globally: {{count}}."
+ make_banner: "Make this topic into a banner that appears at the top of all pages."
+ remove_banner: "Remove the banner that appears at the top of all pages."
+ banner_note: "Users can dismiss the banner by closing it. Only one topic can be bannered at any given time."
+ already_banner:
+ zero: "There is currently no banner topic."
+ one: "There is currently a banner topic."
+
inviting: "Inviting..."
automatically_add_to_groups_optional: "This invite also includes access to these groups: (optional, admin only)"
automatically_add_to_groups_required: "This invite also includes access to these groups: (Required, admin only)"
diff --git a/config/routes.rb b/config/routes.rb
index c4b3851b2..427a31960 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -392,6 +392,7 @@ Discourse::Application.routes.draw do
put "topics/reset-new" => 'topics#reset_new'
post "topics/timings"
get "topics/similar_to"
+ get "topics/feature_stats"
get "topics/created-by/:username" => "list#topics_by", as: "topics_by", constraints: {username: USERNAME_ROUTE_FORMAT}
get "topics/private-messages/:username" => "list#private_messages", as: "topics_private_messages", constraints: {username: USERNAME_ROUTE_FORMAT}
get "topics/private-messages-sent/:username" => "list#private_messages_sent", as: "topics_private_messages_sent", constraints: {username: USERNAME_ROUTE_FORMAT}
diff --git a/test/javascripts/integration/modal-test.js.es6 b/test/javascripts/integration/modal-test.js.es6
index 0a7fc9288..e89dce2b9 100644
--- a/test/javascripts/integration/modal-test.js.es6
+++ b/test/javascripts/integration/modal-test.js.es6
@@ -19,7 +19,7 @@ test("modal", function() {
click('.login-button');
andThen(function() {
- ok(find('#discourse-modal:visible').length === 1, 'modal should appear');
+ ok(find('#discourse-modal:visible').length === 1, 'modal should reappear');
});
keyEvent('#main-outlet', 'keyup', 27);