diff --git a/.discourse-compatibility b/.discourse-compatibility index 2f18db7..5590fdd 100644 --- a/.discourse-compatibility +++ b/.discourse-compatibility @@ -1,4 +1,5 @@ -3.1.999: 0cbf10b8055370445bd36536e51986bf48bdc57e +< 3.2.0.beta2-dev: ac930c509e2a5b0c37b84bcea28d332e686add95 +3.1.999: a304cd2028ccf1f5b00f5137633aa7027a1fd334 3.1.0.beta3: 9c270cac9abc1c2b30574d8c655fb3a90546236b 2.9.0.beta8: 28bc8ab78a09551548c87f511ade3d64e1b04bc3 2.9.0.beta3: 46f200935dc9e5750c3f2740abd993e27a9b3f6c diff --git a/.eslintrc b/.eslintrc deleted file mode 100644 index 7898fbf..0000000 --- a/.eslintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "extends": "eslint-config-discourse" -} diff --git a/.eslintrc.cjs b/.eslintrc.cjs new file mode 100644 index 0000000..be1a9f3 --- /dev/null +++ b/.eslintrc.cjs @@ -0,0 +1 @@ +module.exports = require("@discourse/lint-configs/eslint"); diff --git a/.gitignore b/.gitignore index d45a64e..91bbf8e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -.bundle/ -auto_generated -.DS_Store -node_modules/ +node_modules +/gems +/auto_generated \ No newline at end of file diff --git a/.prettierrc b/.prettierrc deleted file mode 100644 index 0967ef4..0000000 --- a/.prettierrc +++ /dev/null @@ -1 +0,0 @@ -{} diff --git a/.prettierrc.cjs b/.prettierrc.cjs new file mode 100644 index 0000000..57f647b --- /dev/null +++ b/.prettierrc.cjs @@ -0,0 +1 @@ +module.exports = require("@discourse/lint-configs/prettier"); diff --git a/.template-lintrc.cjs b/.template-lintrc.cjs new file mode 100644 index 0000000..5355ea0 --- /dev/null +++ b/.template-lintrc.cjs @@ -0,0 +1 @@ +module.exports = require("@discourse/lint-configs/template-lint"); diff --git a/.template-lintrc.js b/.template-lintrc.js deleted file mode 100644 index a558b8e..0000000 --- a/.template-lintrc.js +++ /dev/null @@ -1,4 +0,0 @@ -module.exports = { - plugins: ["ember-template-lint-plugin-discourse"], - extends: "discourse:recommended", -}; diff --git a/Gemfile.lock b/Gemfile.lock index 28b6f70..23ad67d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -2,43 +2,45 @@ GEM remote: https://rubygems.org/ specs: ast (2.4.2) - json (2.6.3) - parallel (1.23.0) - parser (3.2.2.3) + json (2.7.1) + language_server-protocol (3.17.0.3) + parallel (1.24.0) + parser (3.3.0.3) ast (~> 2.4.1) racc prettier_print (1.2.1) - racc (1.7.1) + racc (1.7.3) rainbow (3.1.1) - regexp_parser (2.8.1) - rexml (3.2.5) - rubocop (1.52.1) + regexp_parser (2.9.0) + rexml (3.2.6) + rubocop (1.59.0) json (~> 2.3) + language_server-protocol (>= 3.17.0) parallel (~> 1.10) - parser (>= 3.2.2.3) + parser (>= 3.2.2.4) rainbow (>= 2.2.2, < 4.0) regexp_parser (>= 1.8, < 3.0) rexml (>= 3.2.5, < 4.0) - rubocop-ast (>= 1.28.0, < 2.0) + rubocop-ast (>= 1.30.0, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.29.0) + rubocop-ast (1.30.0) parser (>= 3.2.1.0) - rubocop-capybara (2.18.0) + rubocop-capybara (2.20.0) rubocop (~> 1.41) - rubocop-discourse (3.2.0) - rubocop (>= 1.1.0) - rubocop-rspec (>= 2.0.0) - rubocop-factory_bot (2.23.1) - rubocop (~> 1.33) - rubocop-rspec (2.22.0) - rubocop (~> 1.33) + rubocop-discourse (3.6.0) + rubocop (>= 1.59.0) + rubocop-rspec (>= 2.25.0) + rubocop-factory_bot (2.25.1) + rubocop (~> 1.41) + rubocop-rspec (2.26.1) + rubocop (~> 1.40) rubocop-capybara (~> 2.17) rubocop-factory_bot (~> 2.22) ruby-progressbar (1.13.0) - syntax_tree (6.1.1) + syntax_tree (6.2.0) prettier_print (>= 1.2.0) - unicode-display_width (2.4.2) + unicode-display_width (2.5.0) PLATFORMS ruby @@ -48,4 +50,4 @@ DEPENDENCIES syntax_tree BUNDLED WITH - 2.3.4 + 2.5.4 diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..baa5b89 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Civilized Discourse Construction Kit + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/app/controllers/discourse_assign/assign_controller.rb b/app/controllers/discourse_assign/assign_controller.rb index 842b318..846faff 100644 --- a/app/controllers/discourse_assign/assign_controller.rb +++ b/app/controllers/discourse_assign/assign_controller.rb @@ -159,43 +159,6 @@ module DiscourseAssign } end - def user_menu_assigns - assign_notifications = - Notification.unread_type(current_user, Notification.types[:assigned], user_menu_limit) - - if assign_notifications.size < user_menu_limit - opts = {} - ignored_assignment_ids = - assign_notifications.filter_map { |notification| notification.data_hash[:assignment_id] } - opts[:ignored_assignment_ids] = ignored_assignment_ids if ignored_assignment_ids.present? - - assigns_list = - TopicQuery.new( - current_user, - per_page: user_menu_limit - assign_notifications.size, - ).list_messages_assigned(current_user, ignored_assignment_ids) - end - - if assign_notifications.present? - serialized_notifications = - ActiveModel::ArraySerializer.new( - assign_notifications, - each_serializer: NotificationSerializer, - scope: guardian, - ) - end - - if assigns_list - serialized_assigns = - serialize_data(assigns_list, TopicListSerializer, scope: guardian, root: false)[:topics] - end - - render json: { - notifications: serialized_notifications || [], - topics: serialized_assigns || [], - } - end - private def translate_failure(reason, assign_to) @@ -261,10 +224,6 @@ module DiscourseAssign raise Discourse::InvalidAccess.new unless current_user.can_assign? end - def user_menu_limit - UsersController::USER_MENU_LIST_LIMIT - end - def recent_assignees User .where("users.id <> ?", current_user.id) diff --git a/app/jobs/regular/assign_notification.rb b/app/jobs/regular/assign_notification.rb index a2ada45..2bae14d 100644 --- a/app/jobs/regular/assign_notification.rb +++ b/app/jobs/regular/assign_notification.rb @@ -3,79 +3,8 @@ module Jobs class AssignNotification < ::Jobs::Base def execute(args) - raise Discourse::InvalidParameters.new(:topic_id) if args[:topic_id].nil? - raise Discourse::InvalidParameters.new(:post_id) if args[:post_id].nil? - raise Discourse::InvalidParameters.new(:assigned_to_id) if args[:assigned_to_id].nil? - raise Discourse::InvalidParameters.new(:assigned_to_type) if args[:assigned_to_type].nil? - raise Discourse::InvalidParameters.new(:assigned_by_id) if args[:assigned_by_id].nil? raise Discourse::InvalidParameters.new(:assignment_id) if args[:assignment_id].nil? - - if args[:skip_small_action_post].nil? - raise Discourse::InvalidParameters.new(:skip_small_action_post) - end - - topic = Topic.find(args[:topic_id]) - post = Post.find(args[:post_id]) - assigned_by = User.find(args[:assigned_by_id]) - assigned_to = - ( - if args[:assigned_to_type] == "User" - User.find(args[:assigned_to_id]) - else - Group.find(args[:assigned_to_id]) - end - ) - assigned_to_users = args[:assigned_to_type] == "User" ? [assigned_to] : assigned_to.users - - assigned_to_users.each do |user| - Assigner.publish_topic_tracking_state(topic, user.id) - - next if assigned_by == user - - assigned_to_user = args[:assigned_to_type] == "User" - - PostAlerter.new(post).create_notification_alert( - user: user, - post: post, - username: assigned_by.username, - notification_type: Notification.types[:assigned] || Notification.types[:custom], - excerpt: - I18n.t( - ( - if assigned_to_user - "discourse_assign.topic_assigned_excerpt" - else - "discourse_assign.topic_group_assigned_excerpt" - end - ), - title: topic.title, - group: assigned_to.name, - locale: user.effective_locale, - ), - ) - - next if args[:skip_small_action_post] - Notification.create!( - notification_type: Notification.types[:assigned] || Notification.types[:custom], - user_id: user.id, - topic_id: topic.id, - post_number: post.post_number, - high_priority: true, - data: { - message: - ( - if assigned_to_user - "discourse_assign.assign_notification" - else - "discourse_assign.assign_group_notification" - end - ), - display_username: assigned_to_user ? assigned_by.username : assigned_to.name, - topic_title: topic.title, - assignment_id: args[:assignment_id], - }.to_json, - ) - end + Assignment.find(args[:assignment_id]).create_missing_notifications! end end end diff --git a/app/jobs/regular/unassign_notification.rb b/app/jobs/regular/unassign_notification.rb index cdbe4cf..6db3f14 100644 --- a/app/jobs/regular/unassign_notification.rb +++ b/app/jobs/regular/unassign_notification.rb @@ -3,34 +3,18 @@ module Jobs class UnassignNotification < ::Jobs::Base def execute(args) - raise Discourse::InvalidParameters.new(:topic_id) if args[:topic_id].nil? - raise Discourse::InvalidParameters.new(:assigned_to_id) if args[:assigned_to_id].nil? - raise Discourse::InvalidParameters.new(:assigned_to_type) if args[:assigned_to_type].nil? - - topic = Topic.find(args[:topic_id]) - assigned_to_users = - ( - if args[:assigned_to_type] == "User" - [User.find(args[:assigned_to_id])] - else - Group.find(args[:assigned_to_id]).users - end - ) - - assigned_to_users.each do |user| - Assigner.publish_topic_tracking_state(topic, user.id) - - Notification - .where( - notification_type: Notification.types[:assigned] || Notification.types[:custom], - user_id: user.id, - topic_id: topic.id, - ) - .where( - "data like '%discourse_assign.assign_notification%' OR data like '%discourse_assign.assign_group_notification%'", - ) - .destroy_all + %i[topic_id assigned_to_id assigned_to_type assignment_id].each do |argument| + raise Discourse::InvalidParameters.new(argument) if args[argument].nil? end + + assignment = Assignment.new(args.slice(:topic_id, :assigned_to_id, :assigned_to_type)) + assignment.assigned_users.each do |user| + Assigner.publish_topic_tracking_state(assignment.topic, user.id) + end + Notification + .for_assignment(args[:assignment_id]) + .where(user: assignment.assigned_users, topic: assignment.topic) + .destroy_all end end end diff --git a/app/models/assignment.rb b/app/models/assignment.rb index 8d0b897..58479e5 100644 --- a/app/models/assignment.rb +++ b/app/models/assignment.rb @@ -9,13 +9,15 @@ class Assignment < ActiveRecord::Base belongs_to :target, polymorphic: true scope :joins_with_topics, - -> { + -> do joins( "INNER JOIN topics ON topics.id = assignments.target_id AND assignments.target_type = 'Topic' AND topics.deleted_at IS NULL", ) - } + end - scope :active_for_group, ->(group) { where(assigned_to: group, active: true) } + scope :active_for_group, ->(group) { active.where(assigned_to: group) } + scope :active, -> { where(active: true) } + scope :inactive, -> { where(active: false) } before_validation :default_status @@ -38,11 +40,31 @@ class Assignment < ActiveRecord::Base end def assigned_to_user? - assigned_to_type == "User" + assigned_to.is_a?(User) end def assigned_to_group? - assigned_to_type == "Group" + assigned_to.is_a?(Group) + end + + def assigned_users + Array.wrap(assigned_to.try(:users) || assigned_to) + end + + def post + return target.posts.find_by(post_number: 1) if target.is_a?(Topic) + target + end + + def create_missing_notifications! + assigned_users.each do |user| + next if user.notifications.for_assignment(self).exists? + DiscourseAssign::CreateNotification.call( + assignment: self, + user: user, + mark_as_read: assigned_by_user == user, + ) + end end private diff --git a/assets/javascripts/discourse/assigned-messages-route-map.js b/assets/javascripts/discourse/assigned-messages-route-map.js index 8de2d1c..3d465b0 100644 --- a/assets/javascripts/discourse/assigned-messages-route-map.js +++ b/assets/javascripts/discourse/assigned-messages-route-map.js @@ -2,7 +2,7 @@ export default { resource: "user.userPrivateMessages", map() { - this.route("assigned", { path: "/assigned" }, function () { + this.route("assigned", function () { this.route("index", { path: "/" }); }); }, diff --git a/assets/javascripts/discourse/components/assign-actions-dropdown.js b/assets/javascripts/discourse/components/assign-actions-dropdown.js index dd30bc4..2283049 100644 --- a/assets/javascripts/discourse/components/assign-actions-dropdown.js +++ b/assets/javascripts/discourse/components/assign-actions-dropdown.js @@ -1,6 +1,6 @@ +import { action } from "@ember/object"; import I18n from "I18n"; import DropdownSelectBoxComponent from "select-kit/components/dropdown-select-box"; -import { action } from "@ember/object"; export default DropdownSelectBoxComponent.extend({ classNames: ["assign-actions-dropdown"], diff --git a/assets/javascripts/discourse/components/assign-user-form.hbs b/assets/javascripts/discourse/components/assign-user-form.hbs index 2160f23..c9ced8f 100644 --- a/assets/javascripts/discourse/components/assign-user-form.hbs +++ b/assets/javascripts/discourse/components/assign-user-form.hbs @@ -36,7 +36,7 @@ @id="assign-status" @content={{this.availableStatuses}} @value={{this.status}} - @onChange={{action (mut @model.status)}} + @onChange={{fn (mut @model.status)}} /> {{/if}} @@ -51,7 +51,6 @@