diff --git a/assets/javascripts/discourse-assign/connectors/flagged-post-below-controls/show-not-assigned.hbs b/assets/javascripts/discourse-assign/connectors/flagged-post-below-controls/show-not-assigned.hbs new file mode 100644 index 0000000..af28b80 --- /dev/null +++ b/assets/javascripts/discourse-assign/connectors/flagged-post-below-controls/show-not-assigned.hbs @@ -0,0 +1,12 @@ +{{#unless canAct}} + {{#if flaggedPost.topic.assigned_to_user}} +
+
+ {{i18n "discourse_assign.cant_act"}} +
+
+ {{assigned-to user=flaggedPost.topic.assigned_to_user}} +
+
+ {{/if}} +{{/unless}} diff --git a/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6 b/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6 index 1fec4ff..0b5c417 100644 --- a/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6 +++ b/assets/javascripts/discourse-assign/initializers/extend-for-assigns.js.es6 @@ -1,25 +1,50 @@ import { withPluginApi } from 'discourse/lib/plugin-api'; -import { observes } from 'ember-addons/ember-computed-decorators'; - +import { default as computed, observes } from 'ember-addons/ember-computed-decorators'; // should this be in API ? -import Topic from 'discourse/models/topic'; -import TopicFooterDropdown from 'discourse/components/topic-footer-mobile-dropdown'; import showModal from 'discourse/lib/show-modal'; import { iconNode } from 'discourse-common/lib/icon-library'; import { h } from 'virtual-dom'; -function initialize(api, container) { +function initialize(api) { - const siteSettings = container.lookup('site-settings:main'); - const currentUser = container.lookup('current-user:main'); + // You can't act on flags claimed by another user + api.modifyClass('component:flagged-post', { + @computed('flaggedPost.topic.assigned_to_user_id', 'filter') + canAct(assignedToUserId, filter) { + if (assignedToUserId && this.currentUser.id !== assignedToUserId) { + return false; + } + + return this._super(filter); + }, + + didInsertElement() { + this._super(); + this.messageBus.subscribe("/staff/topic-assignment", data => { + + let flaggedPost = this.get('flaggedPost'); + if (data.topic_id === flaggedPost.get('topic.id')) { + flaggedPost.set('topic.assigned_to_user_id', + data.type === 'assigned' ? data.assigned_to.id : null + ); + flaggedPost.set('topic.assigned_to_user', data.assigned_to); + } + }); + }, + + willDestroyElement() { + this._super(); + this.messageBus.unsubscribe("/staff/topic-assignment"); + } + }, { ignoreMissing: true }); // doing this mess while we come up with a better API - TopicFooterDropdown.reopen({ + api.modifyClass('component:topic-footer-mobile-dropdown', { _createContent() { this._super(); - if (!this.get('currentUser.staff') || !siteSettings.assign_enabled) { + if (!this.get('currentUser.staff') || !this.siteSettings.assign_enabled) { return; } const content = this.get('content'); @@ -50,16 +75,20 @@ function initialize(api, container) { } }); - Topic.reopen({ - assignedToUserPath: function(){ - return siteSettings.assigns_user_url_path.replace("{username}", this.get("assigned_to_user.username")); - }.property('assigned_to_user') + api.modifyClass('model:topic', { + @computed('assigned_to_user') + assignedToUserPath(assignedToUser) { + return this.siteSettings.assigns_user_url_path.replace( + "{username}", + Ember.get(assignedToUser, 'username') + ); + } }); api.addPostSmallActionIcon('assigned','user-plus'); api.addPostSmallActionIcon('unassigned','user-times'); - api.addPostTransformCallback((transformed) => { + api.addPostTransformCallback(transformed => { if (transformed.actionCode === "assigned" || transformed.actionCode === "unassigned") { transformed.isSmallAction = true; transformed.canEdit = false; @@ -77,14 +106,45 @@ function initialize(api, container) { }); - if (currentUser && currentUser.get("staff") && siteSettings.assign_enabled) { - api.addUserMenuGlyph({ - label: 'discourse_assign.assigned', - className: 'assigned', - icon: 'user-plus', - href: `${currentUser.get("path")}/activity/assigned` - }); - } + api.addUserMenuGlyph(widget => { + return undefined; + + if (widget.currentUser && + widget.currentUser.get('staff') && + widget.siteSettings.assign_enabled) { + + return { + label: 'discourse_assign.assigned', + className: 'assigned', + icon: 'user-plus', + href: `${widget.currentUser.get("path")}/activity/assigned`, + }; + } + }); + + api.createWidget('assigned-to', { + html(attrs) { + let { assignedToUser, href } = attrs; + + return h('p.assigned-to', [ + iconNode('user-plus'), + h('span.assign-text', I18n.t('discourse_assign.assigned_to')), + h('a', { attributes: { class: 'assigned-to-username', href } }, assignedToUser.username) + ]); + } + }); + + api.createWidget('assigned-to', { + html(attrs) { + let { assignedToUser, href } = attrs; + + return h('p.assigned-to', [ + iconNode('user-plus'), + h('span.assign-text', I18n.t('discourse_assign.assigned_to')), + h('a', { attributes: { class: 'assigned-to-username', href } }, assignedToUser.username) + ]); + } + }); api.createWidget('assigned-to', { html(attrs) { @@ -113,19 +173,12 @@ function initialize(api, container) { } }); - + api.replaceIcon('notification.discourse_assign.assign_notification', 'user-plus'); }; export default { name: 'extend-for-assign', initialize(container) { - withPluginApi('0.8.5', api => { - initialize(api, container); - }); - - // Fix icons in new versions of discourse - withPluginApi('0.8.10', api => { - api.replaceIcon('notification.discourse_assign.assign_notification', 'user-plus'); - }); + withPluginApi('0.8.11', api => initialize(api, container)); } }; diff --git a/assets/javascripts/discourse/components/assigned-to.js.es6 b/assets/javascripts/discourse/components/assigned-to.js.es6 new file mode 100644 index 0000000..493a0f1 --- /dev/null +++ b/assets/javascripts/discourse/components/assigned-to.js.es6 @@ -0,0 +1,3 @@ +export default Ember.Component.extend({ + classNames: ['assigned-to-user'] +}); diff --git a/assets/javascripts/discourse/components/flagged-topic-listener.js.es6 b/assets/javascripts/discourse/components/flagged-topic-listener.js.es6 index 75b3e63..30651c7 100644 --- a/assets/javascripts/discourse/components/flagged-topic-listener.js.es6 +++ b/assets/javascripts/discourse/components/flagged-topic-listener.js.es6 @@ -22,4 +22,3 @@ export default Ember.Component.extend({ this.messageBus.unsubscribe("/staff/topic-assignment"); } }); - diff --git a/assets/javascripts/discourse/templates/components/assigned-to.hbs b/assets/javascripts/discourse/templates/components/assigned-to.hbs new file mode 100644 index 0000000..934a4e1 --- /dev/null +++ b/assets/javascripts/discourse/templates/components/assigned-to.hbs @@ -0,0 +1,5 @@ +{{avatar user imageSize="small"}} + + {{user.username}} + +{{yield}} diff --git a/assets/javascripts/discourse/templates/components/claim-topic.hbs b/assets/javascripts/discourse/templates/components/claim-topic.hbs index 956d23a..6d13469 100644 --- a/assets/javascripts/discourse/templates/components/claim-topic.hbs +++ b/assets/javascripts/discourse/templates/components/claim-topic.hbs @@ -1,16 +1,12 @@ {{#if topic.assigned_to_user}} -
- {{avatar topic.assigned_to_user imageSize="small"}} - - {{topic.assigned_to_user.username}} - + {{#assigned-to user=topic.assigned_to_user}} {{d-button icon="times" class="btn-small unassign" action=(action "unassign") disabled=unassigning title="discourse_assign.unassign.help"}} -
+ {{/assigned-to}} {{else}} {{d-button class="btn-small assign" icon="user-plus" diff --git a/assets/stylesheets/assigns.scss b/assets/stylesheets/assigns.scss index 414fdef..42e44b3 100644 --- a/assets/stylesheets/assigns.scss +++ b/assets/stylesheets/assigns.scss @@ -8,6 +8,16 @@ } } +.cant-act-flagged-post { + padding: 0.5em; + border: 1px dashed $primary-medium; + color: $primary; + + display: flex; + justify-content: space-between; + align-items: center; +} + .assigned-to-user { display: flex; align-items: center; diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 60117b8..cce266a 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -4,6 +4,7 @@ en: assigned: "assigned %{who} %{when}" unassigned: "unassigned %{who} %{when}" discourse_assign: + cant_act: "You cannot act on flags that have been assigned to other users" assigned: "Assigned" assigned_to: "Assigned to" assign_notification: "

{{username}} {{description}}

" diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 4275529..93f5453 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -13,3 +13,4 @@ en: assigned_to: "Topic assigned to @%{username}" unassigned: "Topic was unassigned" already_claimed: "That topic has already been claimed." + flag_assigned: "Sorry, flag's topic is assigned to another user" diff --git a/plugin.rb b/plugin.rb index 66ff9ee..dfcb737 100644 --- a/plugin.rb +++ b/plugin.rb @@ -17,6 +17,22 @@ end after_initialize do require 'topic_assigner' + # Raise an invalid access error if a user tries to act on something + # not assigned to them + DiscourseEvent.on(:before_staff_flag_action) do |args| + if custom_fields = args[:post].topic.custom_fields + if assigned_to_id = custom_fields['assigned_to_id'] + unless assigned_to_id.to_i == args[:user].id + raise Discourse::InvalidAccess.new( + "That flag has been assigned to another user", + nil, + custom_message: 'discourse_assign.flag_assigned' + ) + end + end + end + end + # We can remove this check once this method is stable if respond_to?(:add_preloaded_topic_list_custom_field) add_preloaded_topic_list_custom_field('assigned_to_id')