discourse-assign/plugin.rb

226 lines
7.1 KiB
Ruby

# name: discourse-assign
# about: Assign users to topics
# version: 0.1
# authors: Sam Saffron
register_asset 'stylesheets/assigns.scss'
after_initialize do
module ::DiscourseAssign
class Engine < ::Rails::Engine
engine_name "discourse_assign"
isolate_namespace DiscourseAssign
end
end
class ::DiscourseAssign::AssignController < Admin::AdminController
before_filter :ensure_logged_in
def unassign
topic_id = params.require(:topic_id)
topic = Topic.find(topic_id.to_i)
if assigned_to_id = topic.custom_fields["assigned_to_id"]
topic.custom_fields["assigned_to_id"] = nil
topic.custom_fields["assigned_by_id"] = nil
topic.save!
post = topic.posts.where(post_number: 1).first
post.publish_change_to_clients!(:revised, { reload_topic: true })
assigned_user = User.find_by(id: assigned_to_id)
UserAction.where(
action_type: UserAction::ASSIGNED,
target_post_id: post.id
).destroy_all
# yank notification
Notification.where(
notification_type: Notification.types[:custom],
user_id: assigned_user.try(:id),
topic_id: topic.id,
post_number: 1
).where("data like '%discourse_assign.assign_notification%'")
.destroy_all
post_type = SiteSetting.assigns_public ? Post.types[:small_action] : Post.types[:whisper]
topic.add_moderator_post(current_user,
I18n.t('discourse_assign.unassigned'),
{ bump: false,
post_type: post_type,
action_code: "assigned"})
end
render json: success_json
end
def assign
topic_id = params.require(:topic_id)
username = params.require(:username)
topic = Topic.find(topic_id.to_i)
assign_to = User.find_by(username_lower: username.downcase)
raise Discourse::NotFound unless assign_to
#Scheduler::Defer.later "assign topic" do
topic.custom_fields["assigned_to_id"] = assign_to.id
topic.custom_fields["assigned_by_id"] = current_user.id
topic.save!
topic.posts.first.publish_change_to_clients!(:revised, { reload_topic: true })
UserAction.log_action!(action_type: UserAction::ASSIGNED,
user_id: assign_to.id,
acting_user_id: current_user.id,
target_post_id: topic.posts.find_by(post_number: 1).id,
target_topic_id: topic.id)
post_type = SiteSetting.assigns_public ? Post.types[:small_action] : Post.types[:whisper]
topic.add_moderator_post(current_user,
I18n.t('discourse_assign.assigned_to',
username: assign_to.username),
{ bump: false,
post_type: post_type,
action_code: "assigned"})
unless current_user.id == assign_to.id
Notification.create!(notification_type: Notification.types[:custom],
user_id: assign_to.id,
topic_id: topic.id,
post_number: 1,
data: {
message: 'discourse_assign.assign_notification',
display_username: current_user.username,
topic_title: topic.title
}.to_json
)
end
render json: success_json
end
class ::Topic
def assigned_to_user
@assigned_to_user ||
if user_id = custom_fields["assigned_to_id"]
@assigned_to_user = User.find_by(id: user_id)
end
end
def preload_assigned_to_user(assigned_to_user)
@assigned_to_user = assigned_to_user
end
end
TopicList.preloaded_custom_fields << "assigned_to_id"
TopicList.on_preload do |topics, topic_list|
is_staff = topic_list.current_user && topic_list.current_user.staff?
allowed_access = SiteSetting.assigns_public || is_staff
if allowed_access && topics.length > 0
users = User.where("id in (
SELECT value::int
FROM topic_custom_fields
WHERE name = 'assigned_to_id' AND topic_id IN (?)
)", topics.map(&:id))
.select(:id, :email, :username, :uploaded_avatar_id)
map = {}
users.each{|u| map[u.id] = u}
topics.each do |t|
if id = t.custom_fields['assigned_to_id']
t.preload_assigned_to_user(map[id.to_i])
end
end
end
end
require_dependency 'topic_query'
TopicQuery.add_custom_filter(:assigned) do |results, topic_query|
if topic_query.guardian.is_staff? || SiteSetting.assigns_public
username = topic_query.options[:assigned]
user_id = User.where(username_lower: username.downcase).pluck(:id).first if username.present? && username != "*"
if user_id || username == "*"
if username == "*"
filter = "AND tc_assign.value IS NOT NULL"
else
filter = "AND tc_assign.value = '#{user_id.to_i.to_s}'"
end
results = results.joins("JOIN topic_custom_fields tc_assign ON
topics.id = tc_assign.topic_id AND
tc_assign.name = 'assigned_to_id'
#{filter}
")
end
end
results
end
require_dependency 'listable_topic_serializer'
class ::ListableTopicSerializer
has_one :assigned_to_user, serializer: BasicUserSerializer, embed: :objects
def include_assigned_to_user?
(SiteSetting.assigns_public || scope.is_staff?) && object.assigned_to_user
end
end
require_dependency 'topic_view_serializer'
class ::TopicViewSerializer
attributes :assigned_to_user
def assigned_to_user
if user = User.find_by(id: assigned_to_user_id)
assigned_at = TopicCustomField.where(
topic_id: object.topic.id,
name: "assigned_to_id"
).pluck(:created_at).first
{
username: user.username,
name: user.name,
avatar_template: user.avatar_template,
assigned_at: assigned_at
}
end
end
def include_assigned_to_user?
if SiteSetting.assigns_public || scope.is_staff?
assigned_to_user_id
end
end
def assigned_to_user_id
id = object.topic.custom_fields["assigned_to_id"]
# a bit messy but race conditions can give us an array here, avoid
id && id.to_i rescue nil
end
end
DiscourseAssign::Engine.routes.draw do
put "/assign" => "assign#assign"
put "/unassign" => "assign#unassign"
end
Discourse::Application.routes.append do
mount ::DiscourseAssign::Engine, at: "/assign"
end
end
end