From 03ea0bfe2282937bb680d71133a419d7524f2782 Mon Sep 17 00:00:00 2001 From: Sam Date: Wed, 23 Dec 2015 11:09:17 +1100 Subject: [PATCH] FEATURE: allow users to archive messages Messages are now in 3 buckets - Inbox for all new messages - Sent for all sent messages - Archive for all messages you are done with You can select messages from your Inbox or Sent and move them to your Archive, you can move messages out of your Archive similarly Similar concept applied to group messages, except that archiving and unarchiving will apply to all group members --- .../components/basic-topic-list.js.es6 | 1 + .../controllers/user-private-messages.js.es6 | 63 +++++++++++++++---- .../controllers/user-topics-list.js.es6 | 2 +- .../discourse/helpers/capitalize.js.es6 | 3 + .../discourse/routes/app-route-map.js.es6 | 5 +- .../build-private-messages-route.js.es6 | 7 ++- ...6 => user-private-messages-archive.js.es6} | 2 +- ...user-private-messages-group-archive.js.es6 | 18 ++++++ .../routes/user-private-messages-group.js.es6 | 12 +--- ....es6 => user-private-messages-sent.js.es6} | 2 +- .../templates/components/basic-topic-list.hbs | 5 ++ .../discourse/templates/user-topics-list.hbs | 3 + .../discourse/templates/user/messages.hbs | 63 +++++++++++++------ app/assets/javascripts/main_include.js | 1 + app/assets/stylesheets/common/base/user.scss | 6 ++ app/controllers/list_controller.rb | 2 +- app/controllers/topics_controller.rb | 2 +- app/models/group.rb | 2 + app/models/group_archived_message.rb | 4 ++ app/models/topic.rb | 3 + app/models/user.rb | 2 + app/models/user_archived_message.rb | 4 ++ config/locales/client.en.yml | 9 ++- config/routes.rb | 9 +++ ...chived_messages_group_archived_messages.rb | 19 ++++++ lib/post_creator.rb | 8 +++ lib/topic_query.rb | 39 +++++++++++- lib/topics_bulk_action.rb | 44 ++++++++++++- spec/components/post_creator_spec.rb | 5 ++ spec/controllers/topics_controller_spec.rb | 2 +- 30 files changed, 290 insertions(+), 57 deletions(-) create mode 100644 app/assets/javascripts/discourse/helpers/capitalize.js.es6 rename app/assets/javascripts/discourse/routes/{user-private-messages-unread.js.es6 => user-private-messages-archive.js.es6} (52%) create mode 100644 app/assets/javascripts/discourse/routes/user-private-messages-group-archive.js.es6 rename app/assets/javascripts/discourse/routes/{user-private-messages-mine.js.es6 => user-private-messages-sent.js.es6} (54%) create mode 100644 app/models/group_archived_message.rb create mode 100644 app/models/user_archived_message.rb create mode 100644 db/migrate/20151220232725_add_user_archived_messages_group_archived_messages.rb diff --git a/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 b/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 index 448deec2c0f..2e0907449e4 100644 --- a/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 +++ b/app/assets/javascripts/discourse/components/basic-topic-list.js.es6 @@ -1,4 +1,5 @@ export default Ember.Component.extend({ + loadingMore: Ember.computed.alias('topicList.loadingMore'), loading: Ember.computed.not('loaded'), loaded: function() { diff --git a/app/assets/javascripts/discourse/controllers/user-private-messages.js.es6 b/app/assets/javascripts/discourse/controllers/user-private-messages.js.es6 index 7b7664e7574..c51bf407297 100644 --- a/app/assets/javascripts/discourse/controllers/user-private-messages.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-private-messages.js.es6 @@ -1,25 +1,62 @@ import computed from 'ember-addons/ember-computed-decorators'; +import Topic from 'discourse/models/topic'; export default Ember.Controller.extend({ + needs: ["user-topics-list"], pmView: false, - privateMessagesActive: Em.computed.equal('pmView', 'index'), - privateMessagesMineActive: Em.computed.equal('pmView', 'mine'), - privateMessagesUnreadActive: Em.computed.equal('pmView', 'unread'), isGroup: Em.computed.equal('pmView', 'groups'), + selected: Em.computed.alias('controllers.user-topics-list.selected'), + bulkSelectEnabled: Em.computed.alias('controllers.user-topics-list.bulkSelectEnabled'), - @computed('model.groups', 'groupFilter', 'pmView') - groupPMStats(groups, filter, pmView) { - if (groups) { - return groups.filter(group => group.has_messages) - .map(g => { - return { - name: g.name, - active: (g.name === filter && pmView === "groups") - }; - }); + @computed('selected.@each', 'bulkSelectEnabled') + hasSelection(selected, bulkSelectEnabled){ + return bulkSelectEnabled && selected && selected.length > 0; + }, + + @computed('hasSelection', 'pmView', 'archive') + canMoveToInbox(hasSelection, pmView, archive){ + return hasSelection && (pmView === "archive" || archive); + }, + + @computed('hasSelection', 'pmView', 'archive') + canArchive(hasSelection, pmView, archive){ + return hasSelection && pmView !== "archive" && !archive; + }, + + + bulkOperation(operation) { + const selected = this.get('selected'); + var params = {type: operation}; + if (this.get('isGroup')) { + params.group = this.get('groupFilter'); + } + + Topic.bulkOperation(selected,params).then(() => { + const model = this.get('controllers.user-topics-list.model'); + const topics = model.get('topics'); + topics.removeObjects(selected); + selected.clear(); + model.loadMore(); + }, () => { + bootbox.alert(I18n.t("user.messages.failed_to_move")); + }); + }, + + actions: { + archive() { + this.bulkOperation("archive_messages"); + }, + toInbox() { + this.bulkOperation("move_messages_to_inbox"); + }, + toggleBulkSelect(){ + this.toggleProperty("bulkSelectEnabled"); + }, + selectAll() { + $('input.bulk-select:not(checked)').click(); } } }); diff --git a/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 b/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 index a0073b7c0e7..a0bcdada168 100644 --- a/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 +++ b/app/assets/javascripts/discourse/controllers/user-topics-list.js.es6 @@ -17,6 +17,6 @@ export default Ember.Controller.extend({ showNewPM: function(){ return this.get('controllers.user.viewingSelf') && Discourse.User.currentProp('can_send_private_messages'); - }.property('controllers.user.viewingSelf'), + }.property('controllers.user.viewingSelf') }); diff --git a/app/assets/javascripts/discourse/helpers/capitalize.js.es6 b/app/assets/javascripts/discourse/helpers/capitalize.js.es6 new file mode 100644 index 00000000000..97765d0c586 --- /dev/null +++ b/app/assets/javascripts/discourse/helpers/capitalize.js.es6 @@ -0,0 +1,3 @@ +Ember.Handlebars.registerBoundHelper("capitalize", function(str) { + return str[0].toUpperCase() + str.slice(1); +}); diff --git a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 b/app/assets/javascripts/discourse/routes/app-route-map.js.es6 index 39bee49123f..6aa420a88c4 100644 --- a/app/assets/javascripts/discourse/routes/app-route-map.js.es6 +++ b/app/assets/javascripts/discourse/routes/app-route-map.js.es6 @@ -76,9 +76,10 @@ export default function() { this.route('deletedPosts', { path: '/deleted-posts' }); this.resource('userPrivateMessages', { path: '/messages' }, function() { - this.route('mine'); - this.route('unread'); + this.route('sent'); + this.route('archive'); this.route('group', { path: 'group/:name'}); + this.route('groupArchive', { path: 'group/:name/archive'}); }); this.resource('preferences', function() { diff --git a/app/assets/javascripts/discourse/routes/build-private-messages-route.js.es6 b/app/assets/javascripts/discourse/routes/build-private-messages-route.js.es6 index cef72ed152d..4d50c3918be 100644 --- a/app/assets/javascripts/discourse/routes/build-private-messages-route.js.es6 +++ b/app/assets/javascripts/discourse/routes/build-private-messages-route.js.es6 @@ -21,10 +21,13 @@ export default (viewName, path) => { this.controllerFor("user-topics-list").setProperties({ hideCategory: true, - showParticipants: true + showParticipants: true, + canBulkSelect: true, + selected: [] }); - this.controllerFor("userPrivateMessages").set("pmView", viewName); + this.controllerFor("user-private-messages").set("archive", false); + this.controllerFor("user-private-messages").set("pmView", viewName); this.searchService.set('contextType', 'private_messages'); }, diff --git a/app/assets/javascripts/discourse/routes/user-private-messages-unread.js.es6 b/app/assets/javascripts/discourse/routes/user-private-messages-archive.js.es6 similarity index 52% rename from app/assets/javascripts/discourse/routes/user-private-messages-unread.js.es6 rename to app/assets/javascripts/discourse/routes/user-private-messages-archive.js.es6 index 062ef732894..7ebf279b290 100644 --- a/app/assets/javascripts/discourse/routes/user-private-messages-unread.js.es6 +++ b/app/assets/javascripts/discourse/routes/user-private-messages-archive.js.es6 @@ -1,3 +1,3 @@ import createPMRoute from "discourse/routes/build-private-messages-route"; -export default createPMRoute('unread', 'private-messages-unread'); +export default createPMRoute('archive', 'private-messages-archive'); diff --git a/app/assets/javascripts/discourse/routes/user-private-messages-group-archive.js.es6 b/app/assets/javascripts/discourse/routes/user-private-messages-group-archive.js.es6 new file mode 100644 index 00000000000..c4f3a3d9132 --- /dev/null +++ b/app/assets/javascripts/discourse/routes/user-private-messages-group-archive.js.es6 @@ -0,0 +1,18 @@ +import createPMRoute from "discourse/routes/build-private-messages-route"; + +export default createPMRoute('groups', 'private-messages-groups').extend({ + model(params) { + const username = this.modelFor("user").get("username_lower"); + return this.store.findFiltered("topicList", { + filter: `topics/private-messages-group/${username}/${params.name}/archive` + }); + }, + + setupController(controller, model) { + this._super.apply(this, arguments); + const split = model.get("filter").split('/'); + const group = split[split.length-2]; + this.controllerFor("user-private-messages").set("groupFilter", group); + this.controllerFor("user-private-messages").set("archive", true); + } +}); diff --git a/app/assets/javascripts/discourse/routes/user-private-messages-group.js.es6 b/app/assets/javascripts/discourse/routes/user-private-messages-group.js.es6 index 7e962ee8646..ded798bb2d6 100644 --- a/app/assets/javascripts/discourse/routes/user-private-messages-group.js.es6 +++ b/app/assets/javascripts/discourse/routes/user-private-messages-group.js.es6 @@ -1,4 +1,3 @@ -import Group from 'discourse/models/group'; import createPMRoute from "discourse/routes/build-private-messages-route"; export default createPMRoute('groups', 'private-messages-groups').extend({ @@ -9,17 +8,10 @@ export default createPMRoute('groups', 'private-messages-groups').extend({ }); }, - afterModel(model) { - const groupName = _.last(model.get("filter").split('/')); - Group.findAll().then(groups => { - const group = _.first(groups.filterBy("name", groupName)); - this.controllerFor("user-private-messages").set("group", group); - }); - }, - setupController(controller, model) { this._super.apply(this, arguments); const group = _.last(model.get("filter").split('/')); - this.controllerFor("userPrivateMessages").set("groupFilter", group); + this.controllerFor("user-private-messages").set("groupFilter", group); + this.controllerFor("user-private-messages").set("archive", false); } }); diff --git a/app/assets/javascripts/discourse/routes/user-private-messages-mine.js.es6 b/app/assets/javascripts/discourse/routes/user-private-messages-sent.js.es6 similarity index 54% rename from app/assets/javascripts/discourse/routes/user-private-messages-mine.js.es6 rename to app/assets/javascripts/discourse/routes/user-private-messages-sent.js.es6 index b2cf029e5d4..b31c2f4c9fe 100644 --- a/app/assets/javascripts/discourse/routes/user-private-messages-mine.js.es6 +++ b/app/assets/javascripts/discourse/routes/user-private-messages-sent.js.es6 @@ -1,3 +1,3 @@ import createPMRoute from "discourse/routes/build-private-messages-route"; -export default createPMRoute('mine', 'private-messages-sent'); +export default createPMRoute('sent', 'private-messages-sent'); diff --git a/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs b/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs index 6d323d7aabe..59b095c6872 100644 --- a/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs +++ b/app/assets/javascripts/discourse/templates/components/basic-topic-list.hbs @@ -5,10 +5,15 @@ hideCategory=hideCategory topics=topics expandExcerpts=expandExcerpts + bulkSelectEnabled=bulkSelectEnabled + canBulkSelect=canBulkSelect + selected=selected }} {{else}} + {{#unless loadingMore}}
{{i18n 'choose_topic.none_found'}}
+ {{/unless}} {{/if}} {{/conditional-loading-spinner}} diff --git a/app/assets/javascripts/discourse/templates/user-topics-list.hbs b/app/assets/javascripts/discourse/templates/user-topics-list.hbs index c9462573047..0195f9c08e8 100644 --- a/app/assets/javascripts/discourse/templates/user-topics-list.hbs +++ b/app/assets/javascripts/discourse/templates/user-topics-list.hbs @@ -2,4 +2,7 @@ {{basic-topic-list topicList=model hideCategory=hideCategory showParticipants=showParticipants + canBulkSelect=canBulkSelect + bulkSelectEnabled=bulkSelectEnabled + selected=selected postsAction="showTopicEntrance"}} diff --git a/app/assets/javascripts/discourse/templates/user/messages.hbs b/app/assets/javascripts/discourse/templates/user/messages.hbs index 5e404cb1728..9f8a49a94e0 100644 --- a/app/assets/javascripts/discourse/templates/user/messages.hbs +++ b/app/assets/javascripts/discourse/templates/user/messages.hbs @@ -1,30 +1,34 @@
@@ -33,11 +37,34 @@
-{{#if isGroup}} -
- {{group-notifications-button group=group}} +
+ + + {{#if canArchive}} + + {{/if}} + + {{#if canMoveToInbox}} + + {{/if}} + + + {{#if bulkSelectEnabled}} + + {{/if}} + + {{#if isGroup}} + {{group-notifications-button group=group}} + {{/if}}
-{{/if}} {{outlet}}
diff --git a/app/assets/javascripts/main_include.js b/app/assets/javascripts/main_include.js index 4858e420194..43379cc09c9 100644 --- a/app/assets/javascripts/main_include.js +++ b/app/assets/javascripts/main_include.js @@ -92,6 +92,7 @@ //= require ./discourse/components/conditional-loading-spinner //= require ./discourse/helpers/user-avatar //= require ./discourse/helpers/cold-age-class +//= require ./discourse/helpers/capitalize //= require ./discourse/helpers/loading-spinner //= require ./discourse/helpers/category-link //= require ./discourse/lib/export-result diff --git a/app/assets/stylesheets/common/base/user.scss b/app/assets/stylesheets/common/base/user.scss index d532078bfaa..1ca49d089ff 100644 --- a/app/assets/stylesheets/common/base/user.scss +++ b/app/assets/stylesheets/common/base/user.scss @@ -162,3 +162,9 @@ } } +.user-right .list-actions { + margin-bottom: 10px; + .btn { + margin-right: 10px; + } +} diff --git a/app/controllers/list_controller.rb b/app/controllers/list_controller.rb index 1e7a3245ff4..07fec8d6a3b 100644 --- a/app/controllers/list_controller.rb +++ b/app/controllers/list_controller.rb @@ -102,7 +102,7 @@ class ListController < ApplicationController end end - [:topics_by, :private_messages, :private_messages_sent, :private_messages_unread, :private_messages_group].each do |action| + [:topics_by, :private_messages, :private_messages_sent, :private_messages_unread, :private_messages_archive, :private_messages_group, :private_messages_group_archive].each do |action| define_method("#{action}") do list_opts = build_topic_list_options target_user = fetch_user_from_params(include_inactive: current_user.try(:staff?)) diff --git a/app/controllers/topics_controller.rb b/app/controllers/topics_controller.rb index 1a61e740d0b..1803f4b39e5 100644 --- a/app/controllers/topics_controller.rb +++ b/app/controllers/topics_controller.rb @@ -451,7 +451,7 @@ class TopicsController < ApplicationController operation = params.require(:operation).symbolize_keys raise ActionController::ParameterMissing.new(:operation_type) if operation[:type].blank? - operator = TopicsBulkAction.new(current_user, topic_ids, operation) + operator = TopicsBulkAction.new(current_user, topic_ids, operation, group: operation[:group]) changed_topic_ids = operator.perform! render_json_dump topic_ids: changed_topic_ids end diff --git a/app/models/group.rb b/app/models/group.rb index 9748090a3b0..925c3d70966 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -5,6 +5,8 @@ class Group < ActiveRecord::Base has_many :group_users, dependent: :destroy has_many :group_mentions, dependent: :destroy + has_many :group_archived_messages, dependent: :destroy + has_many :categories, through: :category_groups has_many :users, through: :group_users diff --git a/app/models/group_archived_message.rb b/app/models/group_archived_message.rb new file mode 100644 index 00000000000..7c346518e4f --- /dev/null +++ b/app/models/group_archived_message.rb @@ -0,0 +1,4 @@ +class GroupArchivedMessage < ActiveRecord::Base + belongs_to :user + belongs_to :topic +end diff --git a/app/models/topic.rb b/app/models/topic.rb index ace0481d2cb..d2440a57cf6 100644 --- a/app/models/topic.rb +++ b/app/models/topic.rb @@ -83,6 +83,9 @@ class Topic < ActiveRecord::Base has_many :topic_allowed_users has_many :topic_allowed_groups + has_many :group_archived_messages, dependent: :destroy + has_many :user_archived_messages, dependent: :destroy + has_many :allowed_group_users, through: :allowed_groups, source: :users has_many :allowed_groups, through: :topic_allowed_groups, source: :group has_many :allowed_users, through: :topic_allowed_users, source: :user diff --git a/app/models/user.rb b/app/models/user.rb index a9784dd6f9b..0c2d9989613 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -35,6 +35,8 @@ class User < ActiveRecord::Base has_many :topic_links, dependent: :destroy has_many :uploads has_many :warnings + has_many :user_archived_messages, dependent: :destroy + has_one :user_avatar, dependent: :destroy has_one :facebook_user_info, dependent: :destroy diff --git a/app/models/user_archived_message.rb b/app/models/user_archived_message.rb new file mode 100644 index 00000000000..b24c26b54a9 --- /dev/null +++ b/app/models/user_archived_message.rb @@ -0,0 +1,4 @@ +class UserArchivedMessage < ActiveRecord::Base + belongs_to :user + belongs_to :topic +end diff --git a/config/locales/client.en.yml b/config/locales/client.en.yml index 68cb3b42fbc..5a3da438085 100644 --- a/config/locales/client.en.yml +++ b/config/locales/client.en.yml @@ -504,9 +504,14 @@ en: messages: all: "All" - mine: "Mine" - unread: "Unread" + inbox: "Inbox" + sent: "Sent" + archive: "Archive" groups: "My Groups" + bulk_select: "Select messages" + move_to_inbox: "Move to Inbox" + failed_to_move: "Failed to move selected messages (perhaps your network is down)" + select_all: "Select All" change_password: success: "(email sent)" diff --git a/config/routes.rb b/config/routes.rb index 3f7f8c8f2d6..6f1c3180bf0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -279,6 +279,9 @@ Discourse::Application.routes.draw do get "users/:username/messages" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/messages/:filter" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} get "users/:username/messages/group/:group_name" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT, group_name: USERNAME_ROUTE_FORMAT} + + get "users/:username/messages/group/:group_name/archive" => "user_actions#private_messages", constraints: {username: USERNAME_ROUTE_FORMAT, group_name: USERNAME_ROUTE_FORMAT} + get "users/:username.json" => "users#show", constraints: {username: USERNAME_ROUTE_FORMAT}, defaults: {format: :json} get "users/:username" => "users#show", as: 'user', constraints: {username: USERNAME_ROUTE_FORMAT} put "users/:username" => "users#update", constraints: {username: USERNAME_ROUTE_FORMAT} @@ -468,12 +471,18 @@ Discourse::Application.routes.draw do get "topics/created-by/:username" => "list#topics_by", as: "topics_by", constraints: {username: USERNAME_ROUTE_FORMAT} get "topics/private-messages/:username" => "list#private_messages", as: "topics_private_messages", constraints: {username: USERNAME_ROUTE_FORMAT} get "topics/private-messages-sent/:username" => "list#private_messages_sent", as: "topics_private_messages_sent", constraints: {username: USERNAME_ROUTE_FORMAT} + get "topics/private-messages-archive/:username" => "list#private_messages_archive", as: "topics_private_messages_archive", constraints: {username: USERNAME_ROUTE_FORMAT} get "topics/private-messages-unread/:username" => "list#private_messages_unread", as: "topics_private_messages_unread", constraints: {username: USERNAME_ROUTE_FORMAT} get "topics/private-messages-group/:username/:group_name.json" => "list#private_messages_group", as: "topics_private_messages_group", constraints: { username: USERNAME_ROUTE_FORMAT, group_name: USERNAME_ROUTE_FORMAT } + get "topics/private-messages-group/:username/:group_name/archive.json" => "list#private_messages_group_archive", as: "topics_private_messages_group_archive", constraints: { + username: USERNAME_ROUTE_FORMAT, + group_name: USERNAME_ROUTE_FORMAT + } + get 'embed/comments' => 'embed#comments' get 'embed/count' => 'embed#count' get 'embed/info' => 'embed#info' diff --git a/db/migrate/20151220232725_add_user_archived_messages_group_archived_messages.rb b/db/migrate/20151220232725_add_user_archived_messages_group_archived_messages.rb new file mode 100644 index 00000000000..9ec99718711 --- /dev/null +++ b/db/migrate/20151220232725_add_user_archived_messages_group_archived_messages.rb @@ -0,0 +1,19 @@ +class AddUserArchivedMessagesGroupArchivedMessages < ActiveRecord::Migration + def change + create_table :user_archived_messages do |t| + t.integer :user_id, null: false + t.integer :topic_id, null: false + t.timestamps + end + + add_index :user_archived_messages, [:user_id, :topic_id], unique: true + + create_table :group_archived_messages do |t| + t.integer :group_id, null: false + t.integer :topic_id, null: false + t.timestamps + end + + add_index :group_archived_messages, [:group_id, :topic_id], unique: true + end +end diff --git a/lib/post_creator.rb b/lib/post_creator.rb index 76dd6b9a58f..4d2b158b87f 100644 --- a/lib/post_creator.rb +++ b/lib/post_creator.rb @@ -134,6 +134,7 @@ class PostCreator create_embedded_topic ensure_in_allowed_users if guardian.is_staff? + unarchive_message @post.advance_draft_sequence @post.save_reply_relationships end @@ -268,6 +269,13 @@ class PostCreator end end + def unarchive_message + return unless @topic.private_message? && @topic.id + + UserArchivedMessage.where(topic_id: @topic.id).destroy_all + GroupArchivedMessage.where(topic_id: @topic.id).destroy_all + end + private def create_topic diff --git a/lib/topic_query.rb b/lib/topic_query.rb index d34f9c52e2a..cde31fffd9c 100644 --- a/lib/topic_query.rb +++ b/lib/topic_query.rb @@ -117,14 +117,35 @@ class TopicQuery end end + def not_archived(list, user) + list.joins("LEFT JOIN user_archived_messages um + ON um.user_id = #{user.id.to_i} AND um.topic_id = topics.id") + .where('um.user_id IS NULL') + end + def list_private_messages(user) list = private_messages_for(user, :user) + + list = not_archived(list, user) + .where('NOT (topics.participant_count = 1 AND topics.user_id = ?)', user.id) + + create_list(:private_messages, {}, list) + end + + def list_private_messages_archive(user) + list = private_messages_for(user, :user) + list = list.joins(:user_archived_messages).where('user_archived_messages.user_id = ?', user.id) create_list(:private_messages, {}, list) end def list_private_messages_sent(user) list = private_messages_for(user, :user) - list = list.where(user_id: user.id) + list = list.where('EXISTS ( + SELECT 1 FROM posts + WHERE posts.topic_id = topics.id AND + posts.user_id = ? + )', user.id) + list = not_archived(list, user) create_list(:private_messages, {}, list) end @@ -136,6 +157,18 @@ class TopicQuery def list_private_messages_group(user) list = private_messages_for(user, :group) + group_id = Group.where('name ilike ?', @options[:group_name]).pluck(:id).first + list = list.joins("LEFT JOIN group_archived_messages gm ON gm.topic_id = topics.id AND + gm.group_id = #{group_id.to_i}") + list = list.where("gm.id IS NULL") + create_list(:private_messages, {}, list) + end + + def list_private_messages_group_archive(user) + list = private_messages_for(user, :group) + group_id = Group.where('name ilike ?', @options[:group_name]).pluck(:id).first + list = list.joins("JOIN group_archived_messages gm ON gm.topic_id = topics.id AND + gm.group_id = #{group_id.to_i}") create_list(:private_messages, {}, list) end @@ -195,7 +228,9 @@ class TopicQuery topics = yield(topics) if block_given? options = options.merge(@options) - if ["activity","default"].include?(options[:order] || "activity") && !options[:unordered] + if ["activity","default"].include?(options[:order] || "activity") && + !options[:unordered] && + filter != :private_messages topics = prioritize_pinned_topics(topics, options) end diff --git a/lib/topics_bulk_action.rb b/lib/topics_bulk_action.rb index 1a54ae9e3c8..0fcdf8b713b 100644 --- a/lib/topics_bulk_action.rb +++ b/lib/topics_bulk_action.rb @@ -1,14 +1,17 @@ class TopicsBulkAction - def initialize(user, topic_ids, operation) + def initialize(user, topic_ids, operation, options={}) @user = user @topic_ids = topic_ids @operation = operation @changed_ids = [] + @options = options end def self.operations - @operations ||= %w(change_category close archive change_notification_level reset_read dismiss_posts delete unlist) + @operations ||= %w(change_category close archive change_notification_level + reset_read dismiss_posts delete unlist archive_messages + move_messages_to_inbox) end def self.register_operation(name, &block) @@ -24,6 +27,43 @@ class TopicsBulkAction private + def find_group + return unless @options[:group] + + group = Group.where('name ilike ?', @options[:group]).first + raise Discourse::InvalidParameters.new(:group) unless group + unless group.group_users.where(user_id: @user.id).exists? + raise Discourse::InvalidParameters.new(:group) + end + group + end + + def move_messages_to_inbox + group = find_group + topics.each do |t| + if guardian.can_see?(t) && t.private_message? + if group + GroupArchivedMessage.where(group_id: group.id, topic_id: t.id).destroy_all + else + UserArchivedMessage.where(user_id: @user.id, topic_id: t.id).destroy_all + end + end + end + end + + def archive_messages + group = find_group + topics.each do |t| + if guardian.can_see?(t) && t.private_message? + if group + GroupArchivedMessage.create!(group_id: group.id, topic_id: t.id) + else + UserArchivedMessage.create!(user_id: @user.id, topic_id: t.id) + end + end + end + end + def dismiss_posts sql = " UPDATE topic_users tu diff --git a/spec/components/post_creator_spec.rb b/spec/components/post_creator_spec.rb index 5bbada846e4..2cdef4aef8f 100644 --- a/spec/components/post_creator_spec.rb +++ b/spec/components/post_creator_spec.rb @@ -478,6 +478,9 @@ describe PostCreator do expect(unrelated.notifications.count).to eq(0) expect(post.topic.subtype).to eq(TopicSubtype.user_to_user) + # archive this message and ensure archive is cleared for all users on reply + UserArchivedMessage.create(user_id: target_user2.id, topic_id: post.topic_id) + # if an admin replies they should be added to the allowed user list admin = Fabricate(:admin) PostCreator.create(admin, raw: 'hi there welcome topic, I am a mod', @@ -485,6 +488,8 @@ describe PostCreator do post.topic.reload expect(post.topic.topic_allowed_users.where(user_id: admin.id).count).to eq(1) + + expect(UserArchivedMessage.where(user_id: target_user2.id, topic_id: post.topic_id).count).to eq(0) end end diff --git a/spec/controllers/topics_controller_spec.rb b/spec/controllers/topics_controller_spec.rb index c0d18be282b..b92a14817b0 100644 --- a/spec/controllers/topics_controller_spec.rb +++ b/spec/controllers/topics_controller_spec.rb @@ -1148,7 +1148,7 @@ describe TopicsController do it "delegates work to `TopicsBulkAction`" do topics_bulk_action = mock - TopicsBulkAction.expects(:new).with(user, topic_ids, operation).returns(topics_bulk_action) + TopicsBulkAction.expects(:new).with(user, topic_ids, operation, group: nil).returns(topics_bulk_action) topics_bulk_action.expects(:perform!) xhr :put, :bulk, topic_ids: topic_ids, operation: operation end