mirror of
https://github.com/discourse/discourse.git
synced 2025-01-18 10:42:45 +08:00
FEATURE: wiki editors are allowed edit tags for wiki topics.
If a wiki editor's TL is greater than 'min trust level to tag topics' site setting then they can edit the tags for any wiki topic.
This commit is contained in:
parent
67ad8fbd1b
commit
31577b2131
|
@ -10,6 +10,7 @@ import afterTransition from "discourse/lib/after-transition";
|
|||
export default Ember.Component.extend({
|
||||
classNames: ["title-input"],
|
||||
watchForLink: Ember.computed.alias("composer.canEditTopicFeaturedLink"),
|
||||
disabled: Ember.computed.or("composer.loading", "composer.disableTitleInput"),
|
||||
|
||||
didInsertElement() {
|
||||
this._super(...arguments);
|
||||
|
|
|
@ -186,6 +186,9 @@ export default Ember.Controller.extend({
|
|||
);
|
||||
},
|
||||
|
||||
disableCategoryChooser: Ember.computed.not("model.topic.details.can_edit"),
|
||||
disableTagsChooser: Ember.computed.not("model.topic.canEditTags"),
|
||||
|
||||
isStaffUser: Ember.computed.reads("currentUser.staff"),
|
||||
|
||||
canUnlistTopic: Ember.computed.and("model.creatingTopic", "isStaffUser"),
|
||||
|
|
|
@ -141,6 +141,7 @@ const Composer = RestModel.extend({
|
|||
creatingPrivateMessage: Ember.computed.equal("action", PRIVATE_MESSAGE),
|
||||
notCreatingPrivateMessage: Ember.computed.not("creatingPrivateMessage"),
|
||||
notPrivateMessage: Ember.computed.not("privateMessage"),
|
||||
disableTitleInput: Ember.computed.not("topic.details.can_edit"),
|
||||
|
||||
@computed("privateMessage", "archetype.hasOptions")
|
||||
showCategoryChooser(isPrivateMessage, hasOptions) {
|
||||
|
@ -784,31 +785,31 @@ const Composer = RestModel.extend({
|
|||
let promise = Ember.RSVP.resolve();
|
||||
|
||||
// Update the topic if we're editing the first post
|
||||
if (
|
||||
this.title &&
|
||||
post.post_number === 1 &&
|
||||
this.get("topic.details.can_edit")
|
||||
) {
|
||||
const topicProps = this.getProperties(
|
||||
Object.keys(_edit_topic_serializer)
|
||||
);
|
||||
// frontend should have featuredLink but backend needs featured_link
|
||||
if (topicProps.featuredLink) {
|
||||
topicProps.featured_link = topicProps.featuredLink;
|
||||
delete topicProps.featuredLink;
|
||||
}
|
||||
|
||||
if (this.title && post.post_number === 1) {
|
||||
const topic = this.topic;
|
||||
|
||||
// If we're editing a shared draft, keep the original category
|
||||
if (this.action === EDIT_SHARED_DRAFT) {
|
||||
const destinationCategoryId = topicProps.categoryId;
|
||||
promise = promise.then(() =>
|
||||
topic.updateDestinationCategory(destinationCategoryId)
|
||||
if (topic.details.can_edit) {
|
||||
const topicProps = this.getProperties(
|
||||
Object.keys(_edit_topic_serializer)
|
||||
);
|
||||
topicProps.categoryId = topic.get("category.id");
|
||||
// frontend should have featuredLink but backend needs featured_link
|
||||
if (topicProps.featuredLink) {
|
||||
topicProps.featured_link = topicProps.featuredLink;
|
||||
delete topicProps.featuredLink;
|
||||
}
|
||||
|
||||
// If we're editing a shared draft, keep the original category
|
||||
if (this.action === EDIT_SHARED_DRAFT) {
|
||||
const destinationCategoryId = topicProps.categoryId;
|
||||
promise = promise.then(() =>
|
||||
topic.updateDestinationCategory(destinationCategoryId)
|
||||
);
|
||||
topicProps.categoryId = topic.get("category.id");
|
||||
}
|
||||
promise = promise.then(() => Topic.update(topic, topicProps));
|
||||
} else if (topic.details.can_edit_tags) {
|
||||
promise = promise.then(() => topic.updateTags(this.tags));
|
||||
}
|
||||
promise = promise.then(() => Topic.update(topic, topicProps));
|
||||
}
|
||||
|
||||
const props = {
|
||||
|
|
|
@ -546,6 +546,7 @@ const Topic = RestModel.extend({
|
|||
|
||||
readLastPost: propertyEqual("last_read_post_number", "highest_post_number"),
|
||||
canClearPin: Ember.computed.and("pinned", "readLastPost"),
|
||||
canEditTags: Ember.computed.or("details.can_edit", "details.can_edit_tags"),
|
||||
|
||||
archiveMessage() {
|
||||
this.set("archiving", true);
|
||||
|
@ -610,6 +611,17 @@ const Topic = RestModel.extend({
|
|||
return ajax(`/t/${this.id}/reset-bump-date`, { type: "PUT" }).catch(
|
||||
popupAjaxError
|
||||
);
|
||||
},
|
||||
|
||||
updateTags(tags) {
|
||||
if (!tags || tags.length === 0) {
|
||||
tags = [""];
|
||||
}
|
||||
|
||||
return ajax(`/t/${this.id}/tags`, {
|
||||
type: "PUT",
|
||||
data: { tags: tags }
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
id="reply-title"
|
||||
maxLength=titleMaxLength
|
||||
placeholderKey=composer.titlePlaceholder
|
||||
disabled=composer.loading
|
||||
disabled=disabled
|
||||
autocomplete="discourse"}}
|
||||
|
||||
{{popup-input-tip validation=validation}}
|
||||
|
|
|
@ -76,12 +76,13 @@
|
|||
fullWidthOnMobile=true
|
||||
value=model.categoryId
|
||||
scopedCategoryId=scopedCategoryId
|
||||
isDisabled=disableCategoryChooser
|
||||
tabindex="3"}}
|
||||
{{popup-input-tip validation=categoryValidation}}
|
||||
</div>
|
||||
{{/if}}
|
||||
{{#if canEditTags}}
|
||||
{{mini-tag-chooser tags=model.tags tabindex="4" categoryId=model.categoryId minimum=model.minimumRequiredTags}}
|
||||
{{mini-tag-chooser tags=model.tags tabindex="4" categoryId=model.categoryId minimum=model.minimumRequiredTags isDisabled=disableTagsChooser}}
|
||||
{{popup-input-tip validation=tagValidation}}
|
||||
{{/if}}
|
||||
</div>
|
||||
|
|
|
@ -374,6 +374,16 @@ class TopicsController < ApplicationController
|
|||
success ? render_serialized(topic, BasicTopicSerializer) : render_json_error(topic)
|
||||
end
|
||||
|
||||
def update_tags
|
||||
params.require(:tags)
|
||||
topic = Topic.find_by(id: params[:topic_id])
|
||||
guardian.ensure_can_edit_tags!(topic)
|
||||
|
||||
success = PostRevisor.new(topic.first_post, topic).revise!(current_user, { tags: params[:tags] }, validate_post: false)
|
||||
|
||||
success ? render_serialized(topic, BasicTopicSerializer) : render_json_error(topic)
|
||||
end
|
||||
|
||||
def feature_stats
|
||||
params.require(:category_id)
|
||||
category_id = params[:category_id].to_i
|
||||
|
|
|
@ -14,7 +14,8 @@ class TopicViewDetailsSerializer < ApplicationSerializer
|
|||
:can_reply_as_new_topic,
|
||||
:can_flag_topic,
|
||||
:can_convert_topic,
|
||||
:can_review_topic]
|
||||
:can_review_topic,
|
||||
:can_edit_tags]
|
||||
end
|
||||
|
||||
attributes(
|
||||
|
@ -128,6 +129,10 @@ class TopicViewDetailsSerializer < ApplicationSerializer
|
|||
scope.can_convert_topic?(object.topic)
|
||||
end
|
||||
|
||||
def include_can_edit_tags?
|
||||
!scope.can_edit?(object.topic) && scope.can_edit_tags?(object.topic)
|
||||
end
|
||||
|
||||
def allowed_users
|
||||
object.topic.allowed_users.reject { |user| object.group_allowed_user_ids.include?(user.id) }
|
||||
end
|
||||
|
|
|
@ -754,6 +754,7 @@ Discourse::Application.routes.draw do
|
|||
delete "t/:topic_id/timings" => "topics#destroy_timings", constraints: { topic_id: /\d+/ }
|
||||
put "t/:topic_id/bookmark" => "topics#bookmark", constraints: { topic_id: /\d+/ }
|
||||
put "t/:topic_id/remove_bookmarks" => "topics#remove_bookmarks", constraints: { topic_id: /\d+/ }
|
||||
put "t/:topic_id/tags" => "topics#update_tags", constraints: { topic_id: /\d+/ }
|
||||
|
||||
post "t/:topic_id/notifications" => "topics#set_notifications" , constraints: { topic_id: /\d+/ }
|
||||
|
||||
|
|
|
@ -184,4 +184,16 @@ module TopicGuardian
|
|||
def can_banner_topic?(topic)
|
||||
topic && authenticated? && !topic.private_message? && is_staff?
|
||||
end
|
||||
|
||||
def can_edit_tags?(topic)
|
||||
return false unless can_tag_topics?
|
||||
return false if topic.private_message? && !can_tag_pms?
|
||||
return true if can_edit_topic?(topic)
|
||||
|
||||
if topic&.first_post&.wiki && (@user.trust_level >= SiteSetting.min_trust_to_edit_wiki_post.to_i)
|
||||
return can_create_post?(topic)
|
||||
end
|
||||
|
||||
false
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1027,6 +1027,31 @@ RSpec.describe TopicsController do
|
|||
expect(topic.tags.pluck(:id)).to contain_exactly(tag.id)
|
||||
end
|
||||
|
||||
it "can add a tag to wiki topic" do
|
||||
SiteSetting.min_trust_to_edit_wiki_post = 2
|
||||
topic.first_post.update!(wiki: true)
|
||||
user = Fabricate(:user)
|
||||
sign_in(user)
|
||||
|
||||
expect do
|
||||
put "/t/#{topic.id}/tags.json", params: {
|
||||
tags: [tag.name]
|
||||
}
|
||||
end.not_to change { topic.reload.first_post.revisions.count }
|
||||
|
||||
expect(response.status).to eq(403)
|
||||
user.update!(trust_level: 2)
|
||||
|
||||
expect do
|
||||
put "/t/#{topic.id}/tags.json", params: {
|
||||
tags: [tag.name]
|
||||
}
|
||||
end.to change { topic.reload.first_post.revisions.count }.by(1)
|
||||
|
||||
expect(response.status).to eq(200)
|
||||
expect(topic.tags.pluck(:id)).to contain_exactly(tag.id)
|
||||
end
|
||||
|
||||
it 'does not remove tag if no params is given' do
|
||||
topic.tags << tag
|
||||
|
||||
|
|
|
@ -257,6 +257,26 @@ describe TopicViewSerializer do
|
|||
expect(details[:allowed_users].find { |au| au[:id] == pm.user_id }).to be_present
|
||||
expect(details[:allowed_groups].find { |ag| ag[:id] == group.id }).to be_present
|
||||
end
|
||||
|
||||
context "can_edit_tags" do
|
||||
before do
|
||||
SiteSetting.tagging_enabled = true
|
||||
SiteSetting.min_trust_to_edit_wiki_post = 2
|
||||
end
|
||||
|
||||
it "returns true when user can edit a wiki topic" do
|
||||
post = Fabricate(:post, wiki: true)
|
||||
topic = Fabricate(:topic, first_post: post)
|
||||
|
||||
json = serialize_topic(topic, user)
|
||||
expect(json[:details][:can_edit_tags]).to be_nil
|
||||
|
||||
user.update!(trust_level: 2)
|
||||
|
||||
json = serialize_topic(topic, user)
|
||||
expect(json[:details][:can_edit_tags]).to eq(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue
Block a user