diff --git a/app/assets/javascripts/admin/adapters/flagged-post.js.es6 b/app/assets/javascripts/admin/adapters/flagged-post.js.es6 new file mode 100644 index 00000000000..ed5237588c6 --- /dev/null +++ b/app/assets/javascripts/admin/adapters/flagged-post.js.es6 @@ -0,0 +1,36 @@ +import RestAdapter from 'discourse/adapters/rest'; + +export default RestAdapter.extend({ + pathFor(store, type, findArgs) { + return `/admin/flags/${findArgs.filter}.json?rest_api=true`; + }, + + afterFindAll(results, helper) { + results.forEach(flag => { + let conversations = []; + flag.post_actions.forEach(pa => { + if (pa.conversation) { + let conversation = { + permalink: pa.permalink, + hasMore: pa.conversation.has_more, + response: { + excerpt: pa.conversation.response.excerpt, + user: helper.lookup('user', pa.conversation.response.user_id) + } + }; + + if (pa.conversation.reply) { + conversation.reply = { + excerpt: pa.conversation.reply.excerpt, + user: helper.lookup('user', pa.conversation.reply.user_id) + }; + } + conversations.push(conversation); + } + }); + flag.set('conversations', conversations); + }); + + return results; + } +}); diff --git a/app/assets/javascripts/admin/helpers/disposition-icon.js.es6 b/app/assets/javascripts/admin/helpers/disposition-icon.js.es6 new file mode 100644 index 00000000000..60473dc18b0 --- /dev/null +++ b/app/assets/javascripts/admin/helpers/disposition-icon.js.es6 @@ -0,0 +1,15 @@ +import { iconHTML } from 'discourse-common/lib/icon-library'; + +export default Ember.Helper.extend({ + compute([disposition]) { + if (!disposition) { return null; } + let icon; + let title = 'admin.flags.dispositions.' + disposition; + switch (disposition) { + case "deferred": { icon = "external-link"; break; } + case "agreed": { icon = "thumbs-o-up"; break; } + case "disagreed": { icon = "thumbs-o-down"; break; } + } + return iconHTML(icon, { title }).htmlSafe(); + } +}); diff --git a/app/assets/javascripts/admin/models/flagged-post.js.es6 b/app/assets/javascripts/admin/models/flagged-post.js.es6 index 14af903c220..73e513f12c4 100644 --- a/app/assets/javascripts/admin/models/flagged-post.js.es6 +++ b/app/assets/javascripts/admin/models/flagged-post.js.es6 @@ -1,11 +1,8 @@ import { ajax } from 'discourse/lib/ajax'; -import AdminUser from 'admin/models/admin-user'; -import Topic from 'discourse/models/topic'; import Post from 'discourse/models/post'; -import { iconHTML } from 'discourse-common/lib/icon-library'; import computed from 'ember-addons/ember-computed-decorators'; -const FlaggedPost = Post.extend({ +export default Post.extend({ @computed summary() { @@ -15,34 +12,6 @@ const FlaggedPost = Post.extend({ .join(','); }, - @computed - flaggers() { - return this.post_actions.map(postAction => { - return { - user: this.userLookup[postAction.user_id], - topic: this.topicLookup[postAction.topic_id], - flagType: I18n.t('admin.flags.summary.action_type_' + postAction.post_action_type_id, { count: 1 }), - flaggedAt: postAction.created_at, - disposedBy: postAction.disposed_by_id ? this.userLookup[postAction.disposed_by_id] : null, - disposedAt: postAction.disposed_at, - dispositionIcon: this.dispositionIcon(postAction.disposition), - tookAction: postAction.staff_took_action - }; - }); - }, - - dispositionIcon(disposition) { - if (!disposition) { return null; } - let icon; - let title = 'admin.flags.dispositions.' + disposition; - switch (disposition) { - case "deferred": { icon = "external-link"; break; } - case "agreed": { icon = "thumbs-o-up"; break; } - case "disagreed": { icon = "thumbs-o-down"; break; } - } - return iconHTML(icon, { title }); - }, - @computed('last_revised_at', 'post_actions.@each.created_at') wasEdited(lastRevisedAt) { if (Ember.isEmpty(this.get("last_revised_at"))) { return false; } @@ -52,44 +21,6 @@ const FlaggedPost = Post.extend({ }); }, - @computed - conversations() { - let conversations = []; - - this.post_actions.forEach(postAction => { - if (postAction.conversation) { - let conversation = { - permalink: postAction.permalink, - hasMore: postAction.conversation.has_more, - response: { - excerpt: postAction.conversation.response.excerpt, - user: this.userLookup[postAction.conversation.response.user_id] - } - }; - - if (postAction.conversation.reply) { - conversation.reply = { - excerpt: postAction.conversation.reply.excerpt, - user: this.userLookup[postAction.conversation.reply.user_id] - }; - } - conversations.push(conversation); - } - }); - - return conversations; - }, - - @computed - user() { - return this.userLookup[this.user_id]; - }, - - @computed - topic() { - return this.topicLookup[this.topic_id]; - }, - @computed('post_actions.@each.name_key') flaggedForSpam() { return this.get('post_actions').every(action => action.name_key === 'spam'); @@ -136,43 +67,3 @@ const FlaggedPost = Post.extend({ deleted: Ember.computed.or('deleted_at', 'topic_deleted_at'), }); - -FlaggedPost.reopenClass({ - findAll(args) { - let { filter } = args; - - let result = []; - result.set('loading', true); - - let data = {}; - if (args.topic_id) { - data.topic_id = args.topic_id; - } - if (args.offset) { - data.offset = args.offset; - } - - return ajax(`/admin/flags/${filter}.json`, { data }).then(response => { - // users - let userLookup = {}; - response.users.forEach(user => userLookup[user.id] = AdminUser.create(user)); - - // topics - let topicLookup = {}; - response.topics.forEach(topic => topicLookup[topic.id] = Topic.create(topic)); - - // posts - response.posts.forEach(post => { - let f = FlaggedPost.create(post); - f.userLookup = userLookup; - f.topicLookup = topicLookup; - result.pushObject(f); - }); - - result.set('loading', false); - return result; - }); - } -}); - -export default FlaggedPost; diff --git a/app/assets/javascripts/admin/routes/admin-flags-posts-active.js.es6 b/app/assets/javascripts/admin/routes/admin-flags-posts-active.js.es6 index c1d2dd21017..6c8b797603a 100644 --- a/app/assets/javascripts/admin/routes/admin-flags-posts-active.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-flags-posts-active.js.es6 @@ -1,7 +1,5 @@ -import FlaggedPost from 'admin/models/flagged-post'; - export default Discourse.Route.extend({ model() { - return FlaggedPost.findAll({ filter: 'active' }); + return this.store.findAll('flagged-post', { filter: 'active' }); } }); diff --git a/app/assets/javascripts/admin/routes/admin-flags-posts-old.js.es6 b/app/assets/javascripts/admin/routes/admin-flags-posts-old.js.es6 index ff3c1305984..f6743f4857a 100644 --- a/app/assets/javascripts/admin/routes/admin-flags-posts-old.js.es6 +++ b/app/assets/javascripts/admin/routes/admin-flags-posts-old.js.es6 @@ -1,7 +1,5 @@ -import FlaggedPost from 'admin/models/flagged-post'; - export default Discourse.Route.extend({ model() { - return FlaggedPost.findAll({ filter: 'old' }); + return this.store.findAll('flagged-post', { filter: 'old' }); }, }); diff --git a/app/assets/javascripts/admin/templates/components/flagged-post-response.hbs b/app/assets/javascripts/admin/templates/components/flagged-post-response.hbs index 1cac2cc51be..be71bef0793 100644 --- a/app/assets/javascripts/admin/templates/components/flagged-post-response.hbs +++ b/app/assets/javascripts/admin/templates/components/flagged-post-response.hbs @@ -1,4 +1,4 @@ -{{#link-to 'adminUser' response.user class="response-avatar"}} +{{#link-to 'adminUser' response.user.id response.user.username class="response-avatar"}} {{avatar response.user imageSize="medium"}} {{/link-to}}
{{{response.excerpt}}}
diff --git a/app/assets/javascripts/admin/templates/components/flagged-post.hbs b/app/assets/javascripts/admin/templates/components/flagged-post.hbs index 2f20803255a..38cc482f1da 100644 --- a/app/assets/javascripts/admin/templates/components/flagged-post.hbs +++ b/app/assets/javascripts/admin/templates/components/flagged-post.hbs @@ -2,7 +2,9 @@
{{#if flaggedPost.postAuthorFlagged}} {{#if flaggedPost.user}} - {{#link-to 'adminUser' flaggedPost.user}}{{avatar flaggedPost.user imageSize="large"}}{{/link-to}} + {{#link-to 'adminUser' flaggedPost.user.id flaggedPost.user.username}} + {{avatar flaggedPost.user imageSize="large"}} + {{/link-to}} {{#if flaggedPost.wasEdited}} {{d-icon "pencil" title="admin.flags.was_edited"}} {{/if}} @@ -37,22 +39,22 @@
{{/if}} - {{#each flaggedPost.flaggers as |flagger|}} + {{#each flaggedPost.post_actions as |postAction|}}
- {{#link-to 'adminUser' flagger.user class='flagger-avatar'}} - {{avatar flagger.user imageSize="medium"}} + {{#link-to 'adminUser' postAction.user.id postAction.user.username class='flagger-avatar'}} + {{avatar postAction.user imageSize="medium"}} {{/link-to}}
- {{#link-to 'adminUser' flagger.user}} - {{flagger.user.username}} + {{#link-to 'adminUser' postAction.user.id postAction.user.username}} + {{postAction.user.username}} {{/link-to}}
- {{format-age flagger.flaggedAt}} + {{format-age postAction.created_at}}
- {{flagger.flagType}} + {{i18n (concat "admin.flags.summary.action_type_" postAction.post_action_type_id) count=1}}
@@ -61,15 +63,19 @@ {{#if showResolvedBy}}
- {{#each flaggedPost.flaggers as |flagger|}} + {{#each flaggedPost.post_actions as |postAction|}}
- {{#link-to 'adminUser' flagger.disposedBy class="disposer-avatar"}} - {{avatar flagger.disposedBy imageSize="medium"}} + {{#link-to + 'adminUser' + postAction.disposed_by.id + postAction.disposed_by.username + class="disposer-avatar"}} + {{avatar postAction.disposed_by imageSize="medium"}} {{/link-to}}
- {{format-age flagger.disposedAt}} - {{{flagger.dispositionIcon}}} - {{#if flagger.tookAction}} + {{format-age postAction.disposed_at}} + {{disposition-icon postAction.disposition}} + {{#if postAction.staff_took_action}} {{d-icon "gavel" title="admin.flags.took_action"}} {{/if}}
diff --git a/app/assets/javascripts/discourse/controllers/flag.js.es6 b/app/assets/javascripts/discourse/controllers/flag.js.es6 index b8ab2e8845b..84377f8892f 100644 --- a/app/assets/javascripts/discourse/controllers/flag.js.es6 +++ b/app/assets/javascripts/discourse/controllers/flag.js.es6 @@ -139,8 +139,9 @@ export default Ember.Controller.extend(ModalFunctionality, { canDeleteSpammer: function() { if (this.get("flagTopic")) return false; - if (Discourse.User.currentProp('staff') && this.get('selected.name_key') === 'spam') { - return this.get('userDetails.can_be_deleted') && this.get('userDetails.can_delete_all_posts'); + if (this.currentUser.get('staff') && this.get('selected.name_key') === 'spam') { + return this.get('userDetails.can_be_deleted') && + this.get('userDetails.can_delete_all_posts'); } else { return false; } diff --git a/app/assets/javascripts/discourse/models/store.js.es6 b/app/assets/javascripts/discourse/models/store.js.es6 index 96086f068f9..e5138109b69 100644 --- a/app/assets/javascripts/discourse/models/store.js.es6 +++ b/app/assets/javascripts/discourse/models/store.js.es6 @@ -57,10 +57,19 @@ export default Ember.Object.extend({ findAll(type, findArgs) { const adapter = this.adapterFor(type); - return adapter.findAll(this, type, findArgs).then((result) => { + + let store = this; + return adapter.findAll(this, type, findArgs).then(result => { let results = this._resultSet(type, result); if (adapter.afterFindAll) { - results = adapter.afterFindAll(results); + results = adapter.afterFindAll( + results, + { + lookup(subType, id) { + return store._lookupSubType(subType, type, id, result); + } + } + ); } return results; }); @@ -231,6 +240,10 @@ export default Ember.Object.extend({ return Discourse.Category.findById(id); } + if (root.meta && root.meta.types) { + subType = root.meta.types[subType] || subType; + } + const pluralType = this.pluralize(subType); const collection = root[this.pluralize(subType)]; if (collection) { diff --git a/app/assets/javascripts/discourse/routes/topic.js.es6 b/app/assets/javascripts/discourse/routes/topic.js.es6 index 5a2590d2792..5f920b58fd6 100644 --- a/app/assets/javascripts/discourse/routes/topic.js.es6 +++ b/app/assets/javascripts/discourse/routes/topic.js.es6 @@ -40,14 +40,14 @@ const TopicRoute = Discourse.Route.extend({ actions: { showFlags(model) { - showModal('flag', { model }); - this.controllerFor('flag').setProperties({ selected: null, flagTopic: false }); + let controller = showModal('flag', { model }); + controller.setProperties({ selected: null, flagTopic: false }); }, showFlagTopic() { const model = this.modelFor('topic'); - showModal('flag', { model }); - this.controllerFor('flag').setProperties({ selected: null, flagTopic: true }); + let controller = showModal('flag', { model }); + controller.setProperties({ selected: null, flagTopic: true }); }, showTopicStatusUpdate() { diff --git a/app/controllers/admin/flags_controller.rb b/app/controllers/admin/flags_controller.rb index 9c0fcc3f01e..16d4b03fb69 100644 --- a/app/controllers/admin/flags_controller.rb +++ b/app/controllers/admin/flags_controller.rb @@ -10,22 +10,40 @@ class Admin::FlagsController < Admin::AdminController # we may get out of sync, fix it here PostAction.update_flagged_posts_count - posts, topics, users = FlagQuery.flagged_posts_report( + posts, topics, users, post_actions = FlagQuery.flagged_posts_report( current_user, filter: params[:filter], offset: params[:offset].to_i, topic_id: params[:topic_id], - per_page: Admin::FlagsController.flags_per_page + per_page: Admin::FlagsController.flags_per_page, + rest_api: params[:rest_api].present? ) if posts.blank? render json: { posts: [], topics: [], users: [] } else - render json: MultiJson.dump( - posts: posts, - topics: serialize_data(topics, FlaggedTopicSerializer), - users: serialize_data(users, FlaggedUserSerializer) - ) + if params[:rest_api] + render_json_dump( + { + flagged_posts: posts, + topics: serialize_data(topics, FlaggedTopicSerializer), + users: serialize_data(users, FlaggedUserSerializer), + post_actions: post_actions + }, + rest_serializer: true, + meta: { + types: { + disposed_by: 'user' + } + } + ) + else + render_json_dump( + posts: posts, + topics: serialize_data(topics, FlaggedTopicSerializer), + users: serialize_data(users, FlaggedUserSerializer) + ) + end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 50a36187232..fb587cd83d9 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -327,6 +327,7 @@ class ApplicationController < ActionController::Base end obj['extras'] = opts[:extras] if opts[:extras] + obj['meta'] = opts[:meta] if opts[:meta] end render json: MultiJson.dump(obj), status: opts[:status] || 200 diff --git a/lib/flag_query.rb b/lib/flag_query.rb index be64f9966aa..dbbdd7964ee 100644 --- a/lib/flag_query.rb +++ b/lib/flag_query.rb @@ -60,9 +60,17 @@ module FlagQuery .includes(related_post: { topic: { ordered_posts: :user } }) .where(post_id: post_ids) + all_post_actions = [] + post_actions.each do |pa| post = post_lookup[pa.post_id] - post.post_actions ||= [] + + if opts[:rest_api] + post.post_action_ids ||= [] + else + post.post_actions ||= [] + end + # TODO: add serializer so we can skip this action = { id: pa.id, @@ -101,7 +109,12 @@ module FlagQuery action.merge!(permalink: related_topic.relative_url, conversation: conversation) end - post.post_actions << action + if opts[:rest_api] + post.post_action_ids << action[:id] + all_post_actions << action + else + post.post_actions << action + end user_ids << pa.user_id user_ids << pa.disposed_by_id if pa.disposed_by_id @@ -115,7 +128,8 @@ module FlagQuery [ posts, Topic.with_deleted.where(id: topic_ids.to_a).to_a, - User.includes(:user_stat).where(id: user_ids.to_a).to_a + User.includes(:user_stat).where(id: user_ids.to_a).to_a, + all_post_actions ] end diff --git a/test/javascripts/helpers/flag-pretender.js.es6 b/test/javascripts/helpers/flag-pretender.js.es6 index 5ce060aacf7..68ffd308664 100644 --- a/test/javascripts/helpers/flag-pretender.js.es6 +++ b/test/javascripts/helpers/flag-pretender.js.es6 @@ -36,19 +36,22 @@ export default function(helpers) { this.get('/admin/flags/active.json', () => { return response(200, { - posts: [ + flagged_posts: [ { id: 1, user_id: sam.id, - post_actions: [{ - user_id: eviltrout.id, - post_action_type_id: 8, - name_key: 'spam' - }] + post_action_ids: [1] } ], users: [eviltrout, sam], topics: [], + post_actions: [{ + id: 1, + user_id: eviltrout.id, + post_action_type_id: 8, + name_key: 'spam' + }], + "__rest_serializer": "1" }); });