# 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 ::TopicAssigner def self.backfill_auto_assign staff_mention = User.where('moderator OR admin') .pluck('username') .map{|name| "p.cooked ILIKE '%mention%@#{name}%'"} .join(' OR ') sql = < 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 'topic_list_item_serializer' class ::TopicListItemSerializer 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 assigned_to_user_id && 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? # subtle but need to catch cases where stuff is not assigned object.topic.custom_fields.keys.include?("assigned_to_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 on(:post_created) do |post| ::TopicAssigner.auto_assign(post, force: true) end on(:post_edited) do |post, topic_changed| ::TopicAssigner.auto_assign(post, force: true) end on(:move_to_inbox) do |info| if SiteSetting.unassign_on_group_archive && info[:group] if topic = info[:topic] if user_id = topic.custom_fields["prev_assigned_to_id"] if user = User.find_by(id: user_id.to_i) assigner = TopicAssigner.new(topic, Discourse.system_user) assigner.assign(user, silent: true) end end end end end on(:archive_message) do |info| if SiteSetting.unassign_on_group_archive && info[:group] topic = info[:topic] if user_id = topic.custom_fields["assigned_to_id"] if user = User.find_by(id: user_id.to_i) topic.custom_fields["prev_assigned_to_id"] = user.id topic.save assigner = TopicAssigner.new(topic, Discourse.system_user) assigner.unassign(silent: true) end end end end end