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:
Guo Xiang Tan
2017-04-04 11:18:53 +08:00
committed by GitHub
23 changed files with 359 additions and 177 deletions

View File

@@ -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) {

View File

@@ -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'>&times; ${category.get('topic_count')}</span>`;

View File

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

View File

@@ -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({}));

View File

@@ -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`,

View File

@@ -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');
},

View File

@@ -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'}}

View File

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