mirror of
https://github.com/discourse/discourse.git
synced 2025-05-24 03:36:18 +08:00
FEATURE: add full editing access to queued posts (#5047)
For pending new topics: the body of the post, title, categories and the tags are editable. For pending new replies: only the body is applicable and thus editable DISCUSSION: https://meta.discourse.org/t/66754
This commit is contained in:
@ -17,10 +17,42 @@ function updateState(state, opts) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Ember.Component.extend(bufferedProperty('post'), {
|
export default Ember.Component.extend(bufferedProperty('editables'), {
|
||||||
editing: propertyEqual('post', 'currentlyEditing'),
|
editing: propertyEqual('post', 'currentlyEditing'),
|
||||||
|
editables: {},
|
||||||
_confirmDelete: updateState('rejected', {deleteUser: true}),
|
_confirmDelete: updateState('rejected', {deleteUser: true}),
|
||||||
|
|
||||||
|
_initEditables: function() {
|
||||||
|
const post = this.get('post');
|
||||||
|
const postOptions = post.get('post_options');
|
||||||
|
|
||||||
|
this.set('editables.raw', post.get('raw'));
|
||||||
|
this.set('editables.category', post.get('category'));
|
||||||
|
this.set('editables.category_id', post.get('category.id'));
|
||||||
|
this.set('editables.title', postOptions.title);
|
||||||
|
this.set('editables.tags', postOptions.tags);
|
||||||
|
}.on('init'),
|
||||||
|
|
||||||
|
_categoryChanged: function() {
|
||||||
|
this.set('buffered.category', Discourse.Category.findById(this.get('buffered.category_id')));
|
||||||
|
}.observes('buffered.category_id'),
|
||||||
|
|
||||||
|
editTitleAndCategory: function() {
|
||||||
|
return this.get('editing') && !this.get('post.topic');
|
||||||
|
}.property('editing'),
|
||||||
|
|
||||||
|
tags: function() {
|
||||||
|
return this.get('editables.tags') || this.get('post.topic.tags') || [];
|
||||||
|
}.property('editables.tags'),
|
||||||
|
|
||||||
|
showTags: function() {
|
||||||
|
return this.siteSettings.tagging_enabled && !this.get('editing') && this.get('tags').length > 0;
|
||||||
|
}.property('editing', 'tags'),
|
||||||
|
|
||||||
|
editTags: function() {
|
||||||
|
return this.siteSettings.tagging_enabled && this.get('editing') && !this.get('post.topic');
|
||||||
|
}.property('editing'),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
approve: updateState('approved'),
|
approve: updateState('approved'),
|
||||||
reject: updateState('rejected'),
|
reject: updateState('rejected'),
|
||||||
@ -38,7 +70,14 @@ export default Ember.Component.extend(bufferedProperty('post'), {
|
|||||||
},
|
},
|
||||||
|
|
||||||
confirmEdit() {
|
confirmEdit() {
|
||||||
this.get('post').update({ raw: this.get('buffered.raw') }).then(() => {
|
const buffered = this.get('buffered');
|
||||||
|
|
||||||
|
this.get('post').update(buffered.getProperties(
|
||||||
|
'raw',
|
||||||
|
'title',
|
||||||
|
'tags',
|
||||||
|
'category_id'
|
||||||
|
)).then(() => {
|
||||||
this.commitBuffer();
|
this.commitBuffer();
|
||||||
this.set('currentlyEditing', null);
|
this.set('currentlyEditing', null);
|
||||||
});
|
});
|
||||||
|
@ -20,24 +20,41 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class='clearfix'></div>
|
<div class='clearfix'></div>
|
||||||
|
|
||||||
|
{{#if editTitleAndCategory}}
|
||||||
|
<span class="edit-title">
|
||||||
|
{{text-field value=buffered.title maxlength=siteSettings.max_topic_title_length}}
|
||||||
|
</span>
|
||||||
|
{{category-chooser value=buffered.category_id}}
|
||||||
|
{{else}}
|
||||||
<span class='post-title'>
|
<span class='post-title'>
|
||||||
{{i18n "queue.topic"}}
|
{{i18n "queue.topic"}}
|
||||||
{{#if post.topic}}
|
{{#if post.topic}}
|
||||||
{{topic-link post.topic}}
|
{{topic-link post.topic}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{post.post_options.title}}
|
{{editables.title}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{category-badge post.category}}
|
{{category-badge editables.category}}
|
||||||
</span>
|
</span>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class='body'>
|
<div class='body'>
|
||||||
{{#if editing}}
|
{{#if editing}}
|
||||||
{{d-editor value=buffered.raw}}
|
{{d-editor value=buffered.raw}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{cook-text post.raw}}
|
{{cook-text editables.raw}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{#if showTags}}
|
||||||
|
<div class="list-tags">
|
||||||
|
{{#each tags as |t|}}
|
||||||
|
{{discourse-tag t}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
{{else if editTags}}
|
||||||
|
{{tag-chooser tags=buffered.tags categoryId=buffered.category_id width='100%'}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
<div class='queue-controls'>
|
<div class='queue-controls'>
|
||||||
{{#if editing}}
|
{{#if editing}}
|
||||||
{{d-button action="confirmEdit"
|
{{d-button action="confirmEdit"
|
||||||
|
@ -20,10 +20,17 @@ class QueuedPostsController < ApplicationController
|
|||||||
def update
|
def update
|
||||||
qp = QueuedPost.where(id: params[:id]).first
|
qp = QueuedPost.where(id: params[:id]).first
|
||||||
|
|
||||||
if params[:queued_post][:raw].present?
|
update_params = params[:queued_post]
|
||||||
qp.update_column(:raw, params[:queued_post][:raw])
|
|
||||||
|
qp.raw = update_params[:raw] if update_params[:raw].present?
|
||||||
|
unless qp.topic_id
|
||||||
|
qp.post_options['title'] = update_params[:title] if update_params[:title].present?
|
||||||
|
qp.post_options['category'] = update_params[:category_id].to_i if update_params[:category_id].present?
|
||||||
|
qp.post_options['tags'] = update_params[:tags] if update_params[:tags].present?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
qp.save(validate: false)
|
||||||
|
|
||||||
state = params[:queued_post][:state]
|
state = params[:queued_post][:state]
|
||||||
begin
|
begin
|
||||||
if state == 'approved'
|
if state == 'approved'
|
||||||
|
@ -27,7 +27,7 @@ describe QueuedPostsController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'update' do
|
describe '#update' do
|
||||||
let!(:user) { log_in(:moderator) }
|
let!(:user) { log_in(:moderator) }
|
||||||
let(:qp) { Fabricate(:queued_post) }
|
let(:qp) { Fabricate(:queued_post) }
|
||||||
|
|
||||||
@ -43,7 +43,7 @@ describe QueuedPostsController do
|
|||||||
end
|
end
|
||||||
|
|
||||||
context 'rejected' do
|
context 'rejected' do
|
||||||
it 'updates the post to approved' do
|
it 'updates the post to rejected' do
|
||||||
|
|
||||||
xhr :put, :update, id: qp.id, queued_post: { state: 'rejected' }
|
xhr :put, :update, id: qp.id, queued_post: { state: 'rejected' }
|
||||||
expect(response).to be_success
|
expect(response).to be_success
|
||||||
@ -53,5 +53,66 @@ describe QueuedPostsController do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'editing content' do
|
||||||
|
let(:changes) do
|
||||||
|
{
|
||||||
|
raw: 'new raw',
|
||||||
|
title: 'new title',
|
||||||
|
category_id: 10,
|
||||||
|
tags: ['new_tag']
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is a topic' do
|
||||||
|
let(:queued_topic) { Fabricate(:queued_topic) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
xhr :put, :update, id: queued_topic.id, queued_post: changes
|
||||||
|
expect(response).to be_success
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates raw' do
|
||||||
|
expect(queued_topic.reload.raw).to eq(changes[:raw])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the title' do
|
||||||
|
expect(queued_topic.reload.post_options['title']).to eq(changes[:title])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the category' do
|
||||||
|
expect(queued_topic.reload.post_options['category']).to eq(changes[:category_id])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates the tags' do
|
||||||
|
expect(queued_topic.reload.post_options['tags']).to eq(changes[:tags])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when it is a reply' do
|
||||||
|
let(:queued_reply) { Fabricate(:queued_post) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
xhr :put, :update, id: queued_reply.id, queued_post: changes
|
||||||
|
expect(response).to be_success
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'updates raw' do
|
||||||
|
expect(queued_reply.reload.raw).to eq(changes[:raw])
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not update the title' do
|
||||||
|
expect(queued_reply.reload.post_options['title']).to be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not update the category' do
|
||||||
|
original_category = queued_reply.post_options['category']
|
||||||
|
expect(queued_reply.reload.post_options['category']).to eq(original_category)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not update the tags' do
|
||||||
|
expect(queued_reply.reload.post_options['tags']).to be_nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -15,3 +15,12 @@ Fabricator(:queued_post) do
|
|||||||
image_sizes: { "http://foo.bar/image.png" => { "width" => 0, "height" => 222 } } }
|
image_sizes: { "http://foo.bar/image.png" => { "width" => 0, "height" => 222 } } }
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Fabricator(:queued_topic, from: :queued_post) do
|
||||||
|
topic nil
|
||||||
|
raw 'This post should be queued up, and more importantly, this is a new topic'
|
||||||
|
post_options do
|
||||||
|
{ category: 1,
|
||||||
|
title: 'This is a new topic' }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
47
test/javascripts/acceptance/queued-posts-test.js.es6
Normal file
47
test/javascripts/acceptance/queued-posts-test.js.es6
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { acceptance } from "helpers/qunit-helpers";
|
||||||
|
|
||||||
|
acceptance("Queued Posts", {
|
||||||
|
loggedIn: true,
|
||||||
|
settings: { tagging_enabled: true }
|
||||||
|
});
|
||||||
|
|
||||||
|
QUnit.test("For topics: body of post, title, category and tags are all editbale", assert => {
|
||||||
|
server.get("/queued_posts", () => { //eslint-disable-line no-undef
|
||||||
|
return [
|
||||||
|
200,
|
||||||
|
{"Content-Type": "application/json"},
|
||||||
|
{"users":[{"id":3,"username":"test_user","avatar_template":"/letter_avatar_proxy/v2/letter/t/eada6e/{size}.png","active":true,"admin":false,"moderator":false,"last_seen_at":"2017-08-11T20:48:05.405Z","last_emailed_at":null,"created_at":"2017-08-07T02:23:33.309Z","last_seen_age":"1d","last_emailed_age":null,"created_at_age":"6d","username_lower":"test_user","trust_level":0,"trust_level_locked":false,"flag_level":0,"title":null,"suspended_at":null,"suspended_till":null,"suspended":null,"blocked":false,"time_read":"19m","staged":false,"days_visited":4,"posts_read_count":12,"topics_entered":6,"post_count":2}],"queued_posts":[{"id":22,"queue":"default","user_id":3,"state":1,"topic_id":null,"approved_by_id":null,"rejected_by_id":null,"raw":"some content","post_options":{"archetype":"regular","category":"1","typing_duration_msecs":"3200","composer_open_duration_msecs":"19007","visible":true,"is_warning":false,"title":"a new topic that needs to be reviewed","ip_address":"172.17.0.1","first_post_checks":true,"is_poll":true},"created_at":"2017-08-11T20:43:41.115Z","category_id":1,"can_delete_user":true}],"__rest_serializer":"1","refresh_queued_posts":"/queued_posts?status=new"}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
visit("/queued-posts");
|
||||||
|
click(".queued-posts .queued-post button.edit");
|
||||||
|
|
||||||
|
andThen(() => {
|
||||||
|
assert.ok(exists(".d-editor-container"), "the body should be editable");
|
||||||
|
assert.ok(exists(".edit-title .ember-text-field"), "the title should be editable");
|
||||||
|
assert.ok(exists(".category-combobox"), "category should be editbale");
|
||||||
|
assert.ok(exists(".tag-chooser"), "tags should be editable");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
QUnit.test("For replies: only the body of post is editbale", assert => {
|
||||||
|
server.get("/queued_posts", () => { //eslint-disable-line no-undef
|
||||||
|
return [
|
||||||
|
200,
|
||||||
|
{"Content-Type": "application/json"},
|
||||||
|
{"users":[{"id":3,"username":"test_user","avatar_template":"/letter_avatar_proxy/v2/letter/t/eada6e/{size}.png","active":true,"admin":false,"moderator":false,"last_seen_at":"2017-08-11T20:48:05.405Z","last_emailed_at":null,"created_at":"2017-08-07T02:23:33.309Z","last_seen_age":"1d","last_emailed_age":null,"created_at_age":"6d","username_lower":"test_user","trust_level":0,"trust_level_locked":false,"flag_level":0,"title":null,"suspended_at":null,"suspended_till":null,"suspended":null,"blocked":false,"time_read":"19m","staged":false,"days_visited":4,"posts_read_count":12,"topics_entered":6,"post_count":2}],"topics":[{"id":11,"title":"This is a topic","fancy_title":"This is a topic","slug":"this-is-a-topic","posts_count":2}],"queued_posts":[{"id":4,"queue":"default","user_id":3,"state":1,"topic_id":11,"approved_by_id":null,"rejected_by_id":null,"raw":"edited haahaasdfasdfasdfasdf","post_options":{"archetype":"regular","category":"3","reply_to_post_number":"2","typing_duration_msecs":"1900","composer_open_duration_msecs":"12096","visible":true,"is_warning":false,"featured_link":"","ip_address":"172.17.0.1","first_post_checks":true,"is_poll":true},"created_at":"2017-08-07T19:11:52.018Z","category_id":3,"can_delete_user":true}],"__rest_serializer":"1","refresh_queued_posts":"/queued_posts?status=new"}
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
visit("/queued-posts");
|
||||||
|
click(".queued-posts .queued-post button.edit");
|
||||||
|
|
||||||
|
andThen(() => {
|
||||||
|
assert.ok(exists(".d-editor-container"), "the body should be editable");
|
||||||
|
assert.notOk(exists(".edit-title .ember-text-field"), "title should not be editbale");
|
||||||
|
assert.notOk(exists(".category-combobox"), "category should not be editable");
|
||||||
|
assert.notOk(exists("div.tag-chooser"), "tags should not be editable");
|
||||||
|
});
|
||||||
|
});
|
Reference in New Issue
Block a user