mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
Merge pull request #4790 from tgxworld/add_publish_to_topic_status_update
FEATURE: Allow admins to schedule a topic to be published in the future.
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
import computed from "ember-addons/ember-computed-decorators";
|
||||
import { observes } from "ember-addons/ember-computed-decorators";
|
||||
import { default as computed, observes } from "ember-addons/ember-computed-decorators";
|
||||
|
||||
export default Ember.Component.extend({
|
||||
limited: false,
|
||||
inputValid: false,
|
||||
|
||||
didInsertElement() {
|
||||
this._super();
|
||||
this._updateInputValid();
|
||||
},
|
||||
|
||||
@computed("limited")
|
||||
inputUnitsKey(limited) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import { categoryBadgeHTML } from 'discourse/helpers/category-link';
|
||||
import computed from 'ember-addons/ember-computed-decorators';
|
||||
import { observes, on } from 'ember-addons/ember-computed-decorators';
|
||||
import PermissionType from 'discourse/models/permission-type';
|
||||
import Category from 'discourse/models/category';
|
||||
|
||||
export default ComboboxView.extend({
|
||||
classNames: ['combobox category-combobox'],
|
||||
@@ -14,13 +15,16 @@ export default ComboboxView.extend({
|
||||
content(scopedCategoryId, categories) {
|
||||
// Always scope to the parent of a category, if present
|
||||
if (scopedCategoryId) {
|
||||
const scopedCat = Discourse.Category.findById(scopedCategoryId);
|
||||
const scopedCat = Category.findById(scopedCategoryId);
|
||||
scopedCategoryId = scopedCat.get('parent_category_id') || scopedCat.get('id');
|
||||
}
|
||||
|
||||
const excludeCategoryId = this.get('excludeCategoryId');
|
||||
|
||||
return categories.filter(c => {
|
||||
if (scopedCategoryId && c.get('id') !== scopedCategoryId && c.get('parent_category_id') !== scopedCategoryId) { return false; }
|
||||
if (c.get('isUncategorizedCategory')) { return false; }
|
||||
const categoryId = c.get('id');
|
||||
if (scopedCategoryId && categoryId !== scopedCategoryId && c.get('parent_category_id') !== scopedCategoryId) { return false; }
|
||||
if (c.get('isUncategorizedCategory') || excludeCategoryId === categoryId) { return false; }
|
||||
return c.get('permission') === PermissionType.FULL;
|
||||
});
|
||||
},
|
||||
@@ -30,8 +34,8 @@ export default ComboboxView.extend({
|
||||
_updateCategories() {
|
||||
if (!this.get('categories')) {
|
||||
const categories = Discourse.SiteSettings.fixed_category_positions_on_create ?
|
||||
Discourse.Category.list() :
|
||||
Discourse.Category.listByActivity();
|
||||
Category.list() :
|
||||
Category.listByActivity();
|
||||
this.set('categories', categories);
|
||||
}
|
||||
},
|
||||
@@ -42,7 +46,7 @@ export default ComboboxView.extend({
|
||||
if (rootNone) {
|
||||
return "category.none";
|
||||
} else {
|
||||
return Discourse.Category.findUncategorized();
|
||||
return Category.findUncategorized();
|
||||
}
|
||||
} else {
|
||||
return 'category.choose';
|
||||
@@ -54,12 +58,12 @@ export default ComboboxView.extend({
|
||||
|
||||
// If we have no id, but text with the uncategorized name, we can use that badge.
|
||||
if (Ember.isEmpty(item.id)) {
|
||||
const uncat = Discourse.Category.findUncategorized();
|
||||
const uncat = Category.findUncategorized();
|
||||
if (uncat && uncat.get('name') === item.text) {
|
||||
category = uncat;
|
||||
}
|
||||
} else {
|
||||
category = Discourse.Category.findById(parseInt(item.id,10));
|
||||
category = Category.findById(parseInt(item.id,10));
|
||||
}
|
||||
|
||||
if (!category) return item.text;
|
||||
@@ -67,7 +71,7 @@ export default ComboboxView.extend({
|
||||
const parentCategoryId = category.get('parent_category_id');
|
||||
|
||||
if (parentCategoryId) {
|
||||
result = categoryBadgeHTML(Discourse.Category.findById(parentCategoryId), {link: false}) + " " + result;
|
||||
result = categoryBadgeHTML(Category.findById(parentCategoryId), {link: false}) + " " + result;
|
||||
}
|
||||
|
||||
result += ` <span class='topic-count'>× ${category.get('topic_count')}</span>`;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { bufferedRender } from 'discourse-common/lib/buffered-render';
|
||||
import Category from 'discourse/models/category';
|
||||
|
||||
export default Ember.Component.extend(bufferedRender({
|
||||
elementId: 'topic-status-info',
|
||||
@@ -8,7 +9,8 @@ export default Ember.Component.extend(bufferedRender({
|
||||
'topic.topic_status_update',
|
||||
'topic.topic_status_update.execute_at',
|
||||
'topic.topic_status_update.based_on_last_post',
|
||||
'topic.topic_status_update.duration'
|
||||
'topic.topic_status_update.duration',
|
||||
'topic.topic_status_update.category_id',
|
||||
],
|
||||
|
||||
buildBuffer(buffer) {
|
||||
@@ -35,11 +37,23 @@ export default Ember.Component.extend(bufferedRender({
|
||||
|
||||
buffer.push('<h3><i class="fa fa-clock-o"></i> ');
|
||||
|
||||
buffer.push(I18n.t(this._noticeKey(), {
|
||||
let options = {
|
||||
timeLeft: duration.humanize(true),
|
||||
duration: moment.duration(autoCloseHours, "hours").humanize()
|
||||
}));
|
||||
duration: moment.duration(autoCloseHours, "hours").humanize(),
|
||||
};
|
||||
|
||||
const categoryId = this.get('topic.topic_status_update.category_id');
|
||||
|
||||
if (categoryId) {
|
||||
const category = Category.findById(categoryId);
|
||||
|
||||
options = _.assign({
|
||||
categoryName: category.get('slug'),
|
||||
categoryUrl: category.get('url')
|
||||
}, options);
|
||||
}
|
||||
|
||||
buffer.push(I18n.t(this._noticeKey(), options));
|
||||
buffer.push('</h3>');
|
||||
|
||||
// TODO Sam: concerned this can cause a heavy rerender loop
|
||||
|
||||
@@ -5,18 +5,36 @@ import { popupAjaxError } from 'discourse/lib/ajax-error';
|
||||
|
||||
const CLOSE_STATUS_TYPE = 'close';
|
||||
const OPEN_STATUS_TYPE = 'open';
|
||||
const PUBLISH_TO_CATEGORY_STATUS_TYPE = 'publish_to_category';
|
||||
|
||||
export default Ember.Controller.extend(ModalFunctionality, {
|
||||
closeStatusType: CLOSE_STATUS_TYPE,
|
||||
openStatusType: OPEN_STATUS_TYPE,
|
||||
publishToCategoryStatusType: PUBLISH_TO_CATEGORY_STATUS_TYPE,
|
||||
updateTimeValid: null,
|
||||
updateTimeInvalid: Em.computed.not('updateTimeValid'),
|
||||
loading: false,
|
||||
updateTime: null,
|
||||
topicStatusUpdate: Ember.computed.alias("model.topic_status_update"),
|
||||
selection: Ember.computed.alias('model.topic_status_update.status_type'),
|
||||
autoReopen: Ember.computed.equal('selection', OPEN_STATUS_TYPE),
|
||||
autoOpen: Ember.computed.equal('selection', OPEN_STATUS_TYPE),
|
||||
autoClose: Ember.computed.equal('selection', CLOSE_STATUS_TYPE),
|
||||
disableAutoReopen: Ember.computed.and('autoClose', 'updateTime'),
|
||||
disableAutoClose: Ember.computed.and('autoReopen', 'updateTime'),
|
||||
publishToCategory: Ember.computed.equal('selection', PUBLISH_TO_CATEGORY_STATUS_TYPE),
|
||||
|
||||
@computed('autoClose', 'updateTime')
|
||||
disableAutoClose(autoClose, updateTime) {
|
||||
return updateTime && !autoClose;
|
||||
},
|
||||
|
||||
@computed('autoOpen', 'updateTime')
|
||||
disableAutoOpen(autoOpen, updateTime) {
|
||||
return updateTime && !autoOpen;
|
||||
},
|
||||
|
||||
@computed('publishToCatgory', 'updateTime')
|
||||
disablePublishToCategory(publishToCatgory, updateTime) {
|
||||
return updateTime && !publishToCatgory;
|
||||
},
|
||||
|
||||
@computed('topicStatusUpdate.based_on_last_post', 'updateTime', 'model.last_posted_at')
|
||||
willCloseImmediately(basedOnLastPost, updateTime, lastPostedAt) {
|
||||
@@ -42,7 +60,7 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
},
|
||||
|
||||
@observes("topicStatusUpdate.execute_at", "topicStatusUpdate.duration")
|
||||
setAutoCloseTime() {
|
||||
_setUpdateTime() {
|
||||
let time = null;
|
||||
|
||||
if (this.get("topicStatusUpdate.based_on_last_post")) {
|
||||
@@ -65,12 +83,18 @@ export default Ember.Controller.extend(ModalFunctionality, {
|
||||
this.get('model.id'),
|
||||
time,
|
||||
this.get('topicStatusUpdate.based_on_last_post'),
|
||||
status_type
|
||||
status_type,
|
||||
this.get('categoryId')
|
||||
).then(result => {
|
||||
if (time) {
|
||||
this.send('closeModal');
|
||||
this.set('topicStatusUpdate.execute_at', result.execute_at);
|
||||
this.set('topicStatusUpdate.duration', result.duration);
|
||||
|
||||
this.get("topicStatusUpdate").setProperties({
|
||||
execute_at: result.execute_at,
|
||||
duration: result.duration,
|
||||
category_id: result.category_id
|
||||
});
|
||||
|
||||
this.set('model.closed', result.closed);
|
||||
} else {
|
||||
this.set('topicStatusUpdate', Ember.Object.create({}));
|
||||
|
||||
@@ -4,7 +4,7 @@ import RestModel from 'discourse/models/rest';
|
||||
const TopicStatusUpdate = RestModel.extend({});
|
||||
|
||||
TopicStatusUpdate.reopenClass({
|
||||
updateStatus(topicId, time, basedOnLastPost, statusType) {
|
||||
updateStatus(topicId, time, basedOnLastPost, statusType, categoryId) {
|
||||
let data = {
|
||||
time: time,
|
||||
timezone_offset: (new Date().getTimezoneOffset()),
|
||||
@@ -12,6 +12,7 @@ TopicStatusUpdate.reopenClass({
|
||||
};
|
||||
|
||||
if (basedOnLastPost) data.based_on_last_post = basedOnLastPost;
|
||||
if (categoryId) data.category_id = categoryId;
|
||||
|
||||
return ajax({
|
||||
url: `/t/${topicId}/status_update`,
|
||||
|
||||
@@ -52,7 +52,7 @@ const TopicRoute = Discourse.Route.extend({
|
||||
|
||||
showTopicStatusUpdate() {
|
||||
const model = this.modelFor('topic');
|
||||
if (!model.get('topic_status_update')) model.set('topic_status_update', Ember.Object.create());
|
||||
model.set('topic_status_update', Ember.Object.create(model.get('topic_status_update')));
|
||||
showModal('edit-topic-status-update', { model });
|
||||
this.controllerFor('modal').set('modalClass', 'topic-close-modal');
|
||||
},
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
<div class="auto-update-input">
|
||||
<div>
|
||||
<div class="control-group">
|
||||
<label>
|
||||
{{i18n inputLabelKey}}
|
||||
{{text-field value=input}}
|
||||
{{i18n inputUnitsKey}}
|
||||
</label>
|
||||
|
||||
{{#if inputExamplesKey}}
|
||||
<div class="examples">
|
||||
{{i18n inputExamplesKey}}
|
||||
</div>
|
||||
{{/if}}
|
||||
</div>
|
||||
|
||||
{{#if inputExamplesKey}}
|
||||
<div class="examples">
|
||||
{{i18n inputExamplesKey}}
|
||||
</div>
|
||||
{{/if}}
|
||||
|
||||
|
||||
{{#unless hideBasedOnLastPost}}
|
||||
<div>
|
||||
<div class="control-group">
|
||||
<label>
|
||||
{{input type="checkbox" checked=basedOnLastPost}}
|
||||
{{i18n 'topic.auto_close.based_on_last_post'}}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
disabled=disableAutoClose
|
||||
name="auto-close"
|
||||
id="auto-close"
|
||||
value="close"
|
||||
value=closeStatusType
|
||||
selection=selection}}
|
||||
|
||||
<label class="radio" for="auto-close">
|
||||
@@ -19,46 +19,70 @@
|
||||
</label>
|
||||
|
||||
{{radio-button
|
||||
disabled=disableAutoReopen
|
||||
disabled=disableAutoOpen
|
||||
name="auto-reopen"
|
||||
id="auto-reopen"
|
||||
value="open"
|
||||
value=openStatusType
|
||||
selection=selection}}
|
||||
|
||||
<label class="radio" for="auto-reopen">
|
||||
{{fa-icon "clock-o"}} {{fa-icon "unlock"}}
|
||||
|
||||
|
||||
{{#if model.closed}}
|
||||
{{i18n 'topic.auto_reopen.title'}}
|
||||
{{else}}
|
||||
{{i18n 'topic.temp_close.title'}}
|
||||
{{/if}}
|
||||
</label>
|
||||
|
||||
{{radio-button
|
||||
disabled=disablePublishToCategory
|
||||
name="publish-to-category"
|
||||
id="publish-to-category"
|
||||
value=publishToCategoryStatusType
|
||||
selection=selection}}
|
||||
|
||||
<label class="radio" for="publish-to-category">
|
||||
{{fa-icon "clock-o"}} {{i18n 'topic.publish_to_category.title'}}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
{{#if autoReopen}}
|
||||
{{auto-update-input
|
||||
inputLabelKey='topic.topic_status_update.time'
|
||||
input=updateTime
|
||||
inputValid=updateTimeValid
|
||||
hideBasedOnLastPost=true
|
||||
basedOnLastPost=false}}
|
||||
{{else if autoClose}}
|
||||
{{auto-update-input
|
||||
inputLabelKey='topic.topic_status_update.time'
|
||||
input=updateTime
|
||||
inputValid=updateTimeValid
|
||||
limited=topicStatusUpdate.based_on_last_post
|
||||
basedOnLastPost=topicStatusUpdate.based_on_last_post}}
|
||||
|
||||
{{#if willCloseImmediately}}
|
||||
<div class="warning">
|
||||
{{fa-icon "warning"}}
|
||||
{{willCloseI18n}}
|
||||
<div>
|
||||
{{#if autoOpen}}
|
||||
{{auto-update-input
|
||||
inputLabelKey='topic.topic_status_update.time'
|
||||
input=updateTime
|
||||
inputValid=updateTimeValid
|
||||
hideBasedOnLastPost=true
|
||||
basedOnLastPost=false}}
|
||||
{{else if publishToCategory}}
|
||||
<div class="control-group">
|
||||
<label>{{i18n 'topic.topic_status_update.publish_to'}}</label>
|
||||
{{category-chooser valueAttribute="id" value=categoryId excludeCategoryId=model.category_id}}
|
||||
</div>
|
||||
|
||||
{{auto-update-input
|
||||
inputLabelKey='topic.topic_status_update.time'
|
||||
input=updateTime
|
||||
inputValid=updateTimeValid
|
||||
hideBasedOnLastPost=true
|
||||
basedOnLastPost=false}}
|
||||
{{else if autoClose}}
|
||||
{{auto-update-input
|
||||
inputLabelKey='topic.topic_status_update.time'
|
||||
input=updateTime
|
||||
inputValid=updateTimeValid
|
||||
limited=topicStatusUpdate.based_on_last_post
|
||||
basedOnLastPost=topicStatusUpdate.based_on_last_post}}
|
||||
|
||||
{{#if willCloseImmediately}}
|
||||
<div class="warning">
|
||||
{{fa-icon "warning"}}
|
||||
{{willCloseI18n}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
{{/if}}
|
||||
</div>
|
||||
{{/d-modal-body}}
|
||||
|
||||
<div class="modal-footer">
|
||||
|
||||
Reference in New Issue
Block a user