mirror of
https://github.com/discourse/discourse.git
synced 2025-06-03 02:48:28 +08:00
FEATURE: Allow staffs to tag PMs
This commit is contained in:
@ -1,2 +1,7 @@
|
|||||||
// Injections don't occur without a class
|
import computed from 'ember-addons/ember-computed-decorators';
|
||||||
export default Ember.Component.extend();
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
|
||||||
|
@computed('topic.isPrivateMessage')
|
||||||
|
|
||||||
|
});
|
||||||
|
@ -140,7 +140,8 @@ export default Ember.Controller.extend({
|
|||||||
return !this.site.mobileView &&
|
return !this.site.mobileView &&
|
||||||
this.site.get('can_tag_topics') &&
|
this.site.get('can_tag_topics') &&
|
||||||
canEditTitle &&
|
canEditTitle &&
|
||||||
!creatingPrivateMessage;
|
!creatingPrivateMessage &&
|
||||||
|
(!this.get('model.topic.isPrivateMessage') || this.site.get('can_tag_pms'));
|
||||||
},
|
},
|
||||||
|
|
||||||
@computed('model.whisper', 'model.unlistTopic')
|
@computed('model.whisper', 'model.unlistTopic')
|
||||||
|
@ -104,7 +104,7 @@ export default Ember.Controller.extend(BufferedContent, {
|
|||||||
|
|
||||||
@computed('model.isPrivateMessage')
|
@computed('model.isPrivateMessage')
|
||||||
canEditTags(isPrivateMessage) {
|
canEditTags(isPrivateMessage) {
|
||||||
return !isPrivateMessage && this.site.get('can_tag_topics');
|
return this.site.get('can_tag_topics') && (!isPrivateMessage || this.site.get('can_tag_pms'));
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
@ -3,7 +3,12 @@ export default function renderTag(tag, params) {
|
|||||||
tag = Handlebars.Utils.escapeExpression(tag);
|
tag = Handlebars.Utils.escapeExpression(tag);
|
||||||
const classes = ['tag-' + tag, 'discourse-tag'];
|
const classes = ['tag-' + tag, 'discourse-tag'];
|
||||||
const tagName = params.tagName || "a";
|
const tagName = params.tagName || "a";
|
||||||
const href = (tagName === "a" && !params.noHref) ? " href='" + Discourse.getURL("/tags/" + tag) + "' " : "";
|
let path;
|
||||||
|
if (tagName === "a" && !params.noHref) {
|
||||||
|
const current_user = Discourse.User.current();
|
||||||
|
path = params.isPrivateMessage ? `/u/${current_user.username}/messages/tag/${tag}` : `/tags/${tag}`;
|
||||||
|
}
|
||||||
|
const href = path ? ` href='${Discourse.getURL(path)}' ` : "";
|
||||||
|
|
||||||
if (Discourse.SiteSettings.tag_style || params.style) {
|
if (Discourse.SiteSettings.tag_style || params.style) {
|
||||||
classes.push(params.style || Discourse.SiteSettings.tag_style);
|
classes.push(params.style || Discourse.SiteSettings.tag_style);
|
||||||
|
@ -20,6 +20,7 @@ export function addTagsHtmlCallback(callback, options) {
|
|||||||
export default function(topic, params){
|
export default function(topic, params){
|
||||||
let tags = topic.tags;
|
let tags = topic.tags;
|
||||||
let buffer = "";
|
let buffer = "";
|
||||||
|
const isPrivateMessage = topic.get('isPrivateMessage');
|
||||||
|
|
||||||
if (params && params.mode === "list") {
|
if (params && params.mode === "list") {
|
||||||
tags = topic.get("visibleListTags");
|
tags = topic.get("visibleListTags");
|
||||||
@ -43,7 +44,7 @@ export default function(topic, params){
|
|||||||
buffer = "<div class='discourse-tags'>";
|
buffer = "<div class='discourse-tags'>";
|
||||||
if (tags) {
|
if (tags) {
|
||||||
for(let i=0; i<tags.length; i++){
|
for(let i=0; i<tags.length; i++){
|
||||||
buffer += renderTag(tags[i]) + ' ';
|
buffer += renderTag(tags[i], { isPrivateMessage }) + ' ';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,6 +96,7 @@ export default function() {
|
|||||||
this.route('archive');
|
this.route('archive');
|
||||||
this.route('group', { path: 'group/:name'});
|
this.route('group', { path: 'group/:name'});
|
||||||
this.route('groupArchive', { path: 'group/:name/archive'});
|
this.route('groupArchive', { path: 'group/:name/archive'});
|
||||||
|
this.route('tag', { path: 'tag/:id'});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.route('preferences', { resetNamespace: true }, function() {
|
this.route('preferences', { resetNamespace: true }, function() {
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
import createPMRoute from "discourse/routes/build-private-messages-route";
|
||||||
|
|
||||||
|
export default createPMRoute('tags', 'private-messages-tags').extend({
|
||||||
|
model(params) {
|
||||||
|
const username = this.modelFor("user").get("username_lower");
|
||||||
|
return this.store.findFiltered("topicList", {
|
||||||
|
filter: `topics/private-messages-tag/${username}/${params.id}`
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
@ -1,13 +1,13 @@
|
|||||||
{{#if topic.category.parentCategory}}
|
{{#unless topic.isPrivateMessage}}
|
||||||
|
{{#if topic.category.parentCategory}}
|
||||||
{{bound-category-link topic.category.parentCategory}}
|
{{bound-category-link topic.category.parentCategory}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{bound-category-link topic.category hideParent=true}}
|
{{bound-category-link topic.category hideParent=true}}
|
||||||
|
{{/unless}}
|
||||||
<div class="topic-header-extra">
|
<div class="topic-header-extra">
|
||||||
{{#if siteSettings.tagging_enabled}}
|
{{#if siteSettings.tagging_enabled}}
|
||||||
<div class="list-tags">
|
<div class="list-tags">
|
||||||
{{#each topic.tags as |t|}}
|
{{discourse-tags topic mode="list"}}
|
||||||
{{discourse-tag t}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if siteSettings.topic_featured_link_enabled}}
|
{{#if siteSettings.topic_featured_link_enabled}}
|
||||||
|
@ -63,9 +63,7 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
{{#unless model.isPrivateMessage}}
|
|
||||||
{{topic-category topic=model class="topic-category"}}
|
{{topic-category topic=model class="topic-category"}}
|
||||||
{{/unless}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/topic-title}}
|
{{/topic-title}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
@ -189,7 +189,7 @@
|
|||||||
.category-input {
|
.category-input {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: 1 0 35%;
|
flex: 1 0 35%;
|
||||||
margin: 0 0 5px 10px;
|
margin: 0 5px 5px 10px;
|
||||||
@media screen and (max-width: 955px) {
|
@media screen and (max-width: 955px) {
|
||||||
flex: 1 0 100%;
|
flex: 1 0 100%;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
@ -223,7 +223,7 @@
|
|||||||
|
|
||||||
.mini-tag-chooser {
|
.mini-tag-chooser {
|
||||||
flex: 1 1 25%;
|
flex: 1 1 25%;
|
||||||
margin: 0 0 5px 5px;
|
margin: 0 0 5px 0;
|
||||||
background: $secondary;
|
background: $secondary;
|
||||||
@media all and (max-width: 900px) {
|
@media all and (max-width: 900px) {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -122,6 +122,10 @@ a.badge-category {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.archetype-private_message #topic-title .edit-topic-title .tag-chooser {
|
||||||
|
margin-left: 19px;
|
||||||
|
}
|
||||||
|
|
||||||
.private_message {
|
.private_message {
|
||||||
#topic-title {
|
#topic-title {
|
||||||
.edit-topic-title {
|
.edit-topic-title {
|
||||||
|
@ -151,6 +151,7 @@ class ListController < ApplicationController
|
|||||||
private_messages_archive
|
private_messages_archive
|
||||||
private_messages_group
|
private_messages_group
|
||||||
private_messages_group_archive
|
private_messages_group_archive
|
||||||
|
private_messages_tag
|
||||||
}.each do |action|
|
}.each do |action|
|
||||||
generate_message_route(action)
|
generate_message_route(action)
|
||||||
end
|
end
|
||||||
@ -332,6 +333,7 @@ class ListController < ApplicationController
|
|||||||
def build_topic_list_options
|
def build_topic_list_options
|
||||||
options = {}
|
options = {}
|
||||||
params[:page] = params[:page].to_i rescue 1
|
params[:page] = params[:page].to_i rescue 1
|
||||||
|
params[:tags] = [params[:tag_id]] if params[:tag_id].present? && guardian.can_tag_pms?
|
||||||
|
|
||||||
TopicQuery.public_valid_options.each do |key|
|
TopicQuery.public_valid_options.each do |key|
|
||||||
options[key] = params[key]
|
options[key] = params[key]
|
||||||
|
@ -16,21 +16,6 @@ class Tag < ActiveRecord::Base
|
|||||||
|
|
||||||
after_save :index_search
|
after_save :index_search
|
||||||
|
|
||||||
COUNT_ARG = "topics.id"
|
|
||||||
|
|
||||||
# Apply more activerecord filters to the tags_by_count_query, and then
|
|
||||||
# fetch the result with .count(Tag::COUNT_ARG).
|
|
||||||
#
|
|
||||||
# e.g., Tag.tags_by_count_query.where("topics.category_id = ?", category.id).count(Tag::COUNT_ARG)
|
|
||||||
def self.tags_by_count_query(opts = {})
|
|
||||||
q = Tag.joins("LEFT JOIN topic_tags ON tags.id = topic_tags.tag_id")
|
|
||||||
.joins("LEFT JOIN topics ON topics.id = topic_tags.topic_id AND topics.deleted_at IS NULL")
|
|
||||||
.group("tags.id, tags.name")
|
|
||||||
.order('count_topics_id DESC')
|
|
||||||
q = q.limit(opts[:limit]) if opts[:limit]
|
|
||||||
q
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.ensure_consistency!
|
def self.ensure_consistency!
|
||||||
update_topic_counts # topic_count counter cache can miscount
|
update_topic_counts # topic_count counter cache can miscount
|
||||||
end
|
end
|
||||||
@ -43,7 +28,7 @@ class Tag < ActiveRecord::Base
|
|||||||
SELECT COUNT(topics.id) AS topic_count, tags.id AS tag_id
|
SELECT COUNT(topics.id) AS topic_count, tags.id AS tag_id
|
||||||
FROM tags
|
FROM tags
|
||||||
LEFT JOIN topic_tags ON tags.id = topic_tags.tag_id
|
LEFT JOIN topic_tags ON tags.id = topic_tags.tag_id
|
||||||
LEFT JOIN topics ON topics.id = topic_tags.topic_id AND topics.deleted_at IS NULL
|
LEFT JOIN topics ON topics.id = topic_tags.topic_id AND topics.deleted_at IS NULL AND topics.archetype != "private_message"
|
||||||
GROUP BY tags.id
|
GROUP BY tags.id
|
||||||
) x
|
) x
|
||||||
WHERE x.tag_id = t.id
|
WHERE x.tag_id = t.id
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
class TopicTag < ActiveRecord::Base
|
class TopicTag < ActiveRecord::Base
|
||||||
belongs_to :topic
|
belongs_to :topic
|
||||||
belongs_to :tag, counter_cache: "topic_count"
|
belongs_to :tag
|
||||||
|
|
||||||
after_create do
|
after_create do
|
||||||
|
if topic.archetype != Archetype.private_message
|
||||||
|
tag.increment!(:topic_count)
|
||||||
|
|
||||||
if topic.category_id
|
if topic.category_id
|
||||||
if stat = CategoryTagStat.where(tag_id: tag_id, category_id: topic.category_id).first
|
if stat = CategoryTagStat.where(tag_id: tag_id, category_id: topic.category_id).first
|
||||||
stat.increment!(:topic_count)
|
stat.increment!(:topic_count)
|
||||||
@ -11,12 +14,15 @@ class TopicTag < ActiveRecord::Base
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
after_destroy do
|
after_destroy do
|
||||||
if topic.category_id
|
if topic.archetype != Archetype.private_message
|
||||||
if stat = CategoryTagStat.where(tag_id: tag_id, category: topic.category_id).first
|
if topic.category_id && stat = CategoryTagStat.where(tag_id: tag_id, category: topic.category_id).first
|
||||||
stat.topic_count == 1 ? stat.destroy : stat.decrement!(:topic_count)
|
stat.topic_count == 1 ? stat.destroy : stat.decrement!(:topic_count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
tag.decrement!(:topic_count)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -164,7 +164,7 @@ class PostRevisionSerializer < ApplicationSerializer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def include_tags_changes?
|
def include_tags_changes?
|
||||||
SiteSetting.tagging_enabled && previous["tags"] != current["tags"]
|
SiteSetting.tagging_enabled && previous["tags"] != current["tags"] && (!topic.private_message? || scope.can_tag_pms?)
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
@ -206,7 +206,7 @@ class PostRevisionSerializer < ApplicationSerializer
|
|||||||
latest_modifications["featured_link"] = [post.topic.featured_link]
|
latest_modifications["featured_link"] = [post.topic.featured_link]
|
||||||
end
|
end
|
||||||
|
|
||||||
if SiteSetting.tagging_enabled
|
if SiteSetting.tagging_enabled && (!topic.private_message? || scope.can_tag_pms?)
|
||||||
latest_modifications["tags"] = [post.topic.tags.map(&:name)]
|
latest_modifications["tags"] = [post.topic.tags.map(&:name)]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -1,12 +1,5 @@
|
|||||||
class SearchTopicListItemSerializer < ListableTopicSerializer
|
class SearchTopicListItemSerializer < ListableTopicSerializer
|
||||||
attributes :tags,
|
include TopicTagsMixin
|
||||||
:category_id
|
|
||||||
|
|
||||||
def include_tags?
|
attributes :category_id
|
||||||
SiteSetting.tagging_enabled
|
|
||||||
end
|
|
||||||
|
|
||||||
def tags
|
|
||||||
object.tags.map(&:name)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
@ -21,6 +21,7 @@ class SiteSerializer < ApplicationSerializer
|
|||||||
:topic_flag_types,
|
:topic_flag_types,
|
||||||
:can_create_tag,
|
:can_create_tag,
|
||||||
:can_tag_topics,
|
:can_tag_topics,
|
||||||
|
:can_tag_pms,
|
||||||
:tags_filter_regexp,
|
:tags_filter_regexp,
|
||||||
:top_tags,
|
:top_tags,
|
||||||
:wizard_required,
|
:wizard_required,
|
||||||
@ -106,11 +107,15 @@ class SiteSerializer < ApplicationSerializer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def can_create_tag
|
def can_create_tag
|
||||||
SiteSetting.tagging_enabled && scope.can_create_tag?
|
scope.can_create_tag?
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_tag_topics
|
def can_tag_topics
|
||||||
SiteSetting.tagging_enabled && scope.can_tag_topics?
|
scope.can_tag_topics?
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_tag_pms
|
||||||
|
scope.can_tag_pms?
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_tags_filter_regexp?
|
def include_tags_filter_regexp?
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
class SuggestedTopicSerializer < ListableTopicSerializer
|
class SuggestedTopicSerializer < ListableTopicSerializer
|
||||||
|
include TopicTagsMixin
|
||||||
|
|
||||||
# need to embed so we have users
|
# need to embed so we have users
|
||||||
# front page json gets away without embedding
|
# front page json gets away without embedding
|
||||||
@ -7,21 +8,13 @@ class SuggestedTopicSerializer < ListableTopicSerializer
|
|||||||
has_one :user, serializer: BasicUserSerializer, embed: :objects
|
has_one :user, serializer: BasicUserSerializer, embed: :objects
|
||||||
end
|
end
|
||||||
|
|
||||||
attributes :archetype, :like_count, :views, :category_id, :tags, :featured_link, :featured_link_root_domain
|
attributes :archetype, :like_count, :views, :category_id, :featured_link, :featured_link_root_domain
|
||||||
has_many :posters, serializer: SuggestedPosterSerializer, embed: :objects
|
has_many :posters, serializer: SuggestedPosterSerializer, embed: :objects
|
||||||
|
|
||||||
def posters
|
def posters
|
||||||
object.posters || []
|
object.posters || []
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_tags?
|
|
||||||
SiteSetting.tagging_enabled
|
|
||||||
end
|
|
||||||
|
|
||||||
def tags
|
|
||||||
object.tags.map(&:name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_featured_link?
|
def include_featured_link?
|
||||||
SiteSetting.topic_featured_link_enabled
|
SiteSetting.topic_featured_link_enabled
|
||||||
end
|
end
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
class TopicListItemSerializer < ListableTopicSerializer
|
class TopicListItemSerializer < ListableTopicSerializer
|
||||||
|
include TopicTagsMixin
|
||||||
|
|
||||||
attributes :views,
|
attributes :views,
|
||||||
:like_count,
|
:like_count,
|
||||||
@ -10,7 +11,6 @@ class TopicListItemSerializer < ListableTopicSerializer
|
|||||||
:pinned_globally,
|
:pinned_globally,
|
||||||
:bookmarked_post_numbers,
|
:bookmarked_post_numbers,
|
||||||
:liked_post_numbers,
|
:liked_post_numbers,
|
||||||
:tags,
|
|
||||||
:featured_link,
|
:featured_link,
|
||||||
:featured_link_root_domain
|
:featured_link_root_domain
|
||||||
|
|
||||||
@ -66,14 +66,6 @@ class TopicListItemSerializer < ListableTopicSerializer
|
|||||||
object.association(:first_post).loaded?
|
object.association(:first_post).loaded?
|
||||||
end
|
end
|
||||||
|
|
||||||
def include_tags?
|
|
||||||
SiteSetting.tagging_enabled
|
|
||||||
end
|
|
||||||
|
|
||||||
def tags
|
|
||||||
object.tags.map(&:name)
|
|
||||||
end
|
|
||||||
|
|
||||||
def include_featured_link?
|
def include_featured_link?
|
||||||
SiteSetting.topic_featured_link_enabled
|
SiteSetting.topic_featured_link_enabled
|
||||||
end
|
end
|
||||||
|
13
app/serializers/topic_tags_mixin.rb
Normal file
13
app/serializers/topic_tags_mixin.rb
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
module TopicTagsMixin
|
||||||
|
def self.included(klass)
|
||||||
|
klass.attributes :tags
|
||||||
|
end
|
||||||
|
|
||||||
|
def include_tags?
|
||||||
|
SiteSetting.tagging_enabled && (!object.private_message? || scope.can_tag_pms?)
|
||||||
|
end
|
||||||
|
|
||||||
|
def tags
|
||||||
|
object.tags.pluck(:name)
|
||||||
|
end
|
||||||
|
end
|
@ -239,7 +239,7 @@ class TopicViewSerializer < ApplicationSerializer
|
|||||||
end
|
end
|
||||||
|
|
||||||
def include_tags?
|
def include_tags?
|
||||||
SiteSetting.tagging_enabled
|
SiteSetting.tagging_enabled && (!object.topic.private_message? || scope.can_tag_pms?)
|
||||||
end
|
end
|
||||||
|
|
||||||
def topic_timer
|
def topic_timer
|
||||||
|
@ -1609,6 +1609,7 @@ en:
|
|||||||
tags_listed_by_group: "List tags by tag group on the Tags page (/tags)."
|
tags_listed_by_group: "List tags by tag group on the Tags page (/tags)."
|
||||||
tag_style: "Visual style for tag badges."
|
tag_style: "Visual style for tag badges."
|
||||||
staff_tags: "A list of tags that can only be applied by staff members"
|
staff_tags: "A list of tags that can only be applied by staff members"
|
||||||
|
allow_staff_to_tag_in_pm: "Allow staff members to tag any personal message"
|
||||||
min_trust_level_to_tag_topics: "Minimum trust level required to tag topics"
|
min_trust_level_to_tag_topics: "Minimum trust level required to tag topics"
|
||||||
suppress_overlapping_tags_in_list: "If tags match exact words in topic titles, don't show the tag"
|
suppress_overlapping_tags_in_list: "If tags match exact words in topic titles, don't show the tag"
|
||||||
remove_muted_tags_from_latest: "Don't show topics tagged with muted tags in the latest topic list."
|
remove_muted_tags_from_latest: "Don't show topics tagged with muted tags in the latest topic list."
|
||||||
|
@ -360,6 +360,7 @@ Discourse::Application.routes.draw do
|
|||||||
get "#{root_path}/:username/messages/:filter" => "user_actions#private_messages", constraints: { username: RouteFormat.username }
|
get "#{root_path}/:username/messages/:filter" => "user_actions#private_messages", constraints: { username: RouteFormat.username }
|
||||||
get "#{root_path}/:username/messages/group/:group_name" => "user_actions#private_messages", constraints: { username: RouteFormat.username, group_name: RouteFormat.username }
|
get "#{root_path}/:username/messages/group/:group_name" => "user_actions#private_messages", constraints: { username: RouteFormat.username, group_name: RouteFormat.username }
|
||||||
get "#{root_path}/:username/messages/group/:group_name/archive" => "user_actions#private_messages", constraints: { username: RouteFormat.username, group_name: RouteFormat.username }
|
get "#{root_path}/:username/messages/group/:group_name/archive" => "user_actions#private_messages", constraints: { username: RouteFormat.username, group_name: RouteFormat.username }
|
||||||
|
get "#{root_path}/:username/messages/tag/:tag_id" => "user_actions#private_messages", constraints: StaffConstraint.new
|
||||||
get "#{root_path}/:username.json" => "users#show", constraints: { username: RouteFormat.username }, defaults: { format: :json }
|
get "#{root_path}/:username.json" => "users#show", constraints: { username: RouteFormat.username }, defaults: { format: :json }
|
||||||
get({ "#{root_path}/:username" => "users#show", constraints: { username: RouteFormat.username, format: /(json|html)/ } }.merge(index == 1 ? { as: 'user' } : {}))
|
get({ "#{root_path}/:username" => "users#show", constraints: { username: RouteFormat.username, format: /(json|html)/ } }.merge(index == 1 ? { as: 'user' } : {}))
|
||||||
put "#{root_path}/:username" => "users#update", constraints: { username: RouteFormat.username }, defaults: { format: :json }
|
put "#{root_path}/:username" => "users#update", constraints: { username: RouteFormat.username }, defaults: { format: :json }
|
||||||
@ -589,20 +590,20 @@ Discourse::Application.routes.draw do
|
|||||||
resources :similar_topics
|
resources :similar_topics
|
||||||
|
|
||||||
get "topics/feature_stats"
|
get "topics/feature_stats"
|
||||||
get "topics/created-by/:username" => "list#topics_by", as: "topics_by", constraints: { username: RouteFormat.username }
|
|
||||||
get "topics/private-messages/:username" => "list#private_messages", as: "topics_private_messages", constraints: { username: RouteFormat.username }
|
|
||||||
get "topics/private-messages-sent/:username" => "list#private_messages_sent", as: "topics_private_messages_sent", constraints: { username: RouteFormat.username }
|
|
||||||
get "topics/private-messages-archive/:username" => "list#private_messages_archive", as: "topics_private_messages_archive", constraints: { username: RouteFormat.username }
|
|
||||||
get "topics/private-messages-unread/:username" => "list#private_messages_unread", as: "topics_private_messages_unread", constraints: { username: RouteFormat.username }
|
|
||||||
get "topics/private-messages-group/:username/:group_name.json" => "list#private_messages_group", as: "topics_private_messages_group", constraints: {
|
|
||||||
username: RouteFormat.username,
|
|
||||||
group_name: RouteFormat.username
|
|
||||||
}
|
|
||||||
|
|
||||||
get "topics/private-messages-group/:username/:group_name/archive.json" => "list#private_messages_group_archive", as: "topics_private_messages_group_archive", constraints: {
|
scope "/topics", username: RouteFormat.username do
|
||||||
username: RouteFormat.username,
|
get "created-by/:username" => "list#topics_by", as: "topics_by"
|
||||||
group_name: RouteFormat.username
|
get "private-messages/:username" => "list#private_messages", as: "topics_private_messages"
|
||||||
}
|
get "private-messages-sent/:username" => "list#private_messages_sent", as: "topics_private_messages_sent"
|
||||||
|
get "private-messages-archive/:username" => "list#private_messages_archive", as: "topics_private_messages_archive"
|
||||||
|
get "private-messages-unread/:username" => "list#private_messages_unread", as: "topics_private_messages_unread"
|
||||||
|
get "private-messages-tag/:username/:tag_id.json" => "list#private_messages_tag", as: "topics_private_messages_tag", constraints: StaffConstraint.new
|
||||||
|
|
||||||
|
scope "/private-messages-group/:username", group_name: RouteFormat.username do
|
||||||
|
get ":group_name.json" => "list#private_messages_group", as: "topics_private_messages_group"
|
||||||
|
get ":group_name/archive.json" => "list#private_messages_group_archive", as: "topics_private_messages_group_archive"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
get 'embed/comments' => 'embed#comments'
|
get 'embed/comments' => 'embed#comments'
|
||||||
get 'embed/count' => 'embed#count'
|
get 'embed/count' => 'embed#count'
|
||||||
|
@ -1560,6 +1560,8 @@ tags:
|
|||||||
type: list
|
type: list
|
||||||
client: true
|
client: true
|
||||||
default: ''
|
default: ''
|
||||||
|
allow_staff_to_tag_in_pm:
|
||||||
|
default: false
|
||||||
suppress_overlapping_tags_in_list:
|
suppress_overlapping_tags_in_list:
|
||||||
default: false
|
default: false
|
||||||
client: true
|
client: true
|
||||||
|
@ -4,10 +4,10 @@ module DiscourseTagging
|
|||||||
TAGS_FILTER_REGEXP = /[\/\?#\[\]@!\$&'\(\)\*\+,;=\.%\\`^\s|\{\}"<>]+/ # /?#[]@!$&'()*+,;=.%\`^|{}"<>
|
TAGS_FILTER_REGEXP = /[\/\?#\[\]@!\$&'\(\)\*\+,;=\.%\\`^\s|\{\}"<>]+/ # /?#[]@!$&'()*+,;=.%\`^|{}"<>
|
||||||
|
|
||||||
def self.tag_topic_by_names(topic, guardian, tag_names_arg, append: false)
|
def self.tag_topic_by_names(topic, guardian, tag_names_arg, append: false)
|
||||||
if SiteSetting.tagging_enabled
|
if can_tag?(topic)
|
||||||
tag_names = DiscourseTagging.tags_for_saving(tag_names_arg, guardian) || []
|
tag_names = DiscourseTagging.tags_for_saving(tag_names_arg, guardian) || []
|
||||||
|
|
||||||
old_tag_names = topic.tags.map(&:name) || []
|
old_tag_names = topic.tags.pluck(:name) || []
|
||||||
new_tag_names = tag_names - old_tag_names
|
new_tag_names = tag_names - old_tag_names
|
||||||
removed_tag_names = old_tag_names - tag_names
|
removed_tag_names = old_tag_names - tag_names
|
||||||
|
|
||||||
|
@ -129,6 +129,12 @@ class Guardian
|
|||||||
alias :can_see_flags? :can_moderate?
|
alias :can_see_flags? :can_moderate?
|
||||||
alias :can_close? :can_moderate?
|
alias :can_close? :can_moderate?
|
||||||
|
|
||||||
|
def can_tag?(obj)
|
||||||
|
return false unless obj && obj.is_a?(Topic)
|
||||||
|
|
||||||
|
obj.private_message? ? can_tag_pms? : can_tag_topics?
|
||||||
|
end
|
||||||
|
|
||||||
def can_send_activation_email?(user)
|
def can_send_activation_email?(user)
|
||||||
user && is_staff? && !SiteSetting.must_approve_users?
|
user && is_staff? && !SiteSetting.must_approve_users?
|
||||||
end
|
end
|
||||||
|
@ -5,7 +5,11 @@ module TagGuardian
|
|||||||
end
|
end
|
||||||
|
|
||||||
def can_tag_topics?
|
def can_tag_topics?
|
||||||
user && user.has_trust_level?(SiteSetting.min_trust_level_to_tag_topics.to_i)
|
user && SiteSetting.tagging_enabled && user.has_trust_level?(SiteSetting.min_trust_level_to_tag_topics.to_i)
|
||||||
|
end
|
||||||
|
|
||||||
|
def can_tag_pms?
|
||||||
|
is_staff? && SiteSetting.tagging_enabled && SiteSetting.allow_staff_to_tag_in_pm
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_admin_tags?
|
def can_admin_tags?
|
||||||
|
@ -269,6 +269,14 @@ class TopicQuery
|
|||||||
create_list(:private_messages, {}, list)
|
create_list(:private_messages, {}, list)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def list_private_messages_tag(user)
|
||||||
|
list = private_messages_for(user, :all)
|
||||||
|
tag_id = Tag.where('name ilike ?', @options[:tags][0]).pluck(:id).first
|
||||||
|
list = list.joins("JOIN topic_tags tt ON tt.topic_id = topics.id AND
|
||||||
|
tt.tag_id = #{tag_id}")
|
||||||
|
create_list(:private_messages, {}, list)
|
||||||
|
end
|
||||||
|
|
||||||
def list_category_topic_ids(category)
|
def list_category_topic_ids(category)
|
||||||
query = default_results(category: category.id)
|
query = default_results(category: category.id)
|
||||||
pinned_ids = query.where('pinned_at IS NOT NULL AND category_id = ?', category.id).limit(nil).order('pinned_at DESC').pluck(:id)
|
pinned_ids = query.where('pinned_at IS NOT NULL AND category_id = ?', category.id).limit(nil).order('pinned_at DESC').pluck(:id)
|
||||||
|
@ -15,51 +15,6 @@ describe Tag do
|
|||||||
SiteSetting.min_trust_level_to_tag_topics = 0
|
SiteSetting.min_trust_level_to_tag_topics = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
describe '#tags_by_count_query' do
|
|
||||||
it "returns empty hash if nothing is tagged" do
|
|
||||||
expect(described_class.tags_by_count_query.count(Tag::COUNT_ARG)).to eq({})
|
|
||||||
end
|
|
||||||
|
|
||||||
context "with some tagged topics" do
|
|
||||||
before do
|
|
||||||
@topics = []
|
|
||||||
3.times { @topics << Fabricate(:topic) }
|
|
||||||
make_some_tags(count: 2)
|
|
||||||
@topics[0].tags << @tags[0]
|
|
||||||
@topics[0].tags << @tags[1]
|
|
||||||
@topics[1].tags << @tags[0]
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns tag names with topic counts in a hash" do
|
|
||||||
counts = described_class.tags_by_count_query.count(Tag::COUNT_ARG)
|
|
||||||
expect(counts[@tags[0].name]).to eq(2)
|
|
||||||
expect(counts[@tags[1].name]).to eq(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "can be used to filter before doing the count" do
|
|
||||||
counts = described_class.tags_by_count_query.where("topics.id = ?", @topics[1].id).count(Tag::COUNT_ARG)
|
|
||||||
expect(counts).to eq(@tags[0].name => 1)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "returns unused tags too" do
|
|
||||||
unused = Fabricate(:tag)
|
|
||||||
counts = described_class.tags_by_count_query.count(Tag::COUNT_ARG)
|
|
||||||
expect(counts[unused.name]).to eq(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
it "doesn't include deleted topics in counts" do
|
|
||||||
deleted_topic_tag = Fabricate(:tag)
|
|
||||||
delete_topic = Fabricate(:topic)
|
|
||||||
post = Fabricate(:post, topic: delete_topic, user: delete_topic.user)
|
|
||||||
delete_topic.tags << deleted_topic_tag
|
|
||||||
PostDestroyer.new(Fabricate(:admin), post).destroy
|
|
||||||
|
|
||||||
counts = described_class.tags_by_count_query.count(Tag::COUNT_ARG)
|
|
||||||
expect(counts[deleted_topic_tag.name]).to eq(0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#top_tags' do
|
describe '#top_tags' do
|
||||||
it "returns nothing if nothing has been tagged" do
|
it "returns nothing if nothing has been tagged" do
|
||||||
make_some_tags(tag_a_topic: false)
|
make_some_tags(tag_a_topic: false)
|
||||||
|
@ -1,6 +1,11 @@
|
|||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
|
|
||||||
describe TopicViewSerializer do
|
describe TopicViewSerializer do
|
||||||
|
def serialize_topic(topic, user)
|
||||||
|
topic_view = TopicView.new(topic.id, user)
|
||||||
|
described_class.new(topic_view, scope: Guardian.new(user), root: false).as_json
|
||||||
|
end
|
||||||
|
|
||||||
let(:topic) { Fabricate(:topic) }
|
let(:topic) { Fabricate(:topic) }
|
||||||
let(:user) { Fabricate(:user) }
|
let(:user) { Fabricate(:user) }
|
||||||
|
|
||||||
@ -12,8 +17,7 @@ describe TopicViewSerializer do
|
|||||||
topic.update!(featured_link: featured_link)
|
topic.update!(featured_link: featured_link)
|
||||||
SiteSetting.topic_featured_link_enabled = false
|
SiteSetting.topic_featured_link_enabled = false
|
||||||
|
|
||||||
topic_view = TopicView.new(topic.id, user)
|
json = serialize_topic(topic, user)
|
||||||
json = described_class.new(topic_view, scope: Guardian.new(user), root: false).as_json
|
|
||||||
|
|
||||||
expect(json[:featured_link]).to eq(nil)
|
expect(json[:featured_link]).to eq(nil)
|
||||||
expect(json[:featured_link_root_domain]).to eq(nil)
|
expect(json[:featured_link_root_domain]).to eq(nil)
|
||||||
@ -24,8 +28,7 @@ describe TopicViewSerializer do
|
|||||||
it 'should return the right attributes' do
|
it 'should return the right attributes' do
|
||||||
topic.update!(featured_link: featured_link)
|
topic.update!(featured_link: featured_link)
|
||||||
|
|
||||||
topic_view = TopicView.new(topic.id, user)
|
json = serialize_topic(topic, user)
|
||||||
json = described_class.new(topic_view, scope: Guardian.new(user), root: false).as_json
|
|
||||||
|
|
||||||
expect(json[:featured_link]).to eq(featured_link)
|
expect(json[:featured_link]).to eq(featured_link)
|
||||||
expect(json[:featured_link_root_domain]).to eq('discourse.org')
|
expect(json[:featured_link_root_domain]).to eq('discourse.org')
|
||||||
@ -42,8 +45,7 @@ describe TopicViewSerializer do
|
|||||||
|
|
||||||
describe 'when loading last chunk' do
|
describe 'when loading last chunk' do
|
||||||
it 'should include suggested topics' do
|
it 'should include suggested topics' do
|
||||||
topic_view = TopicView.new(topic.id, user)
|
json = serialize_topic(topic, user)
|
||||||
json = described_class.new(topic_view, scope: Guardian.new(user), root: false).as_json
|
|
||||||
|
|
||||||
expect(json[:suggested_topics].first.id).to eq(topic2.id)
|
expect(json[:suggested_topics].first.id).to eq(topic2.id)
|
||||||
end
|
end
|
||||||
@ -64,4 +66,33 @@ describe TopicViewSerializer do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:moderator) { Fabricate(:moderator) }
|
||||||
|
let(:tag) { Fabricate(:tag) }
|
||||||
|
let(:pm) do
|
||||||
|
Fabricate(:private_message_topic, tags: [tag], topic_allowed_users: [
|
||||||
|
Fabricate.build(:topic_allowed_user, user: moderator),
|
||||||
|
Fabricate.build(:topic_allowed_user, user: user)
|
||||||
|
])
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when tags added to private message topics' do
|
||||||
|
before do
|
||||||
|
SiteSetting.tagging_enabled = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should not include the tag for normal users" do
|
||||||
|
json = serialize_topic(pm, user)
|
||||||
|
expect(json[:tags]).to eq(nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "should include the tag for staff users" do
|
||||||
|
json = serialize_topic(pm, moderator)
|
||||||
|
expect(json[:tags]).to eq([tag.name])
|
||||||
|
|
||||||
|
json = serialize_topic(pm, Fabricate(:admin))
|
||||||
|
expect(json[:tags]).to eq([tag.name])
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user