mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-27 09:36:19 -05:00
New Feature: Staff can choose to "Take Action" when flagging to immediately reach hiding
thresholds.
This commit is contained in:
parent
476ffcc627
commit
545dbfc07e
19 changed files with 194 additions and 135 deletions
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
Supports logic for flags in the modal
|
||||||
|
|
||||||
|
@class FlagActionTypeController
|
||||||
|
@extends Discourse.ObjectController
|
||||||
|
@namespace Discourse
|
||||||
|
@module Discourse
|
||||||
|
**/
|
||||||
|
Discourse.FlagActionTypeController = Discourse.ObjectController.extend({
|
||||||
|
needs: ['flag'],
|
||||||
|
|
||||||
|
message: Em.computed.alias('controllers.flag.message'),
|
||||||
|
|
||||||
|
customPlaceholder: function(){
|
||||||
|
return Em.String.i18n("flagging.custom_placeholder_" + this.get('name_key'));
|
||||||
|
}.property('name_key'),
|
||||||
|
|
||||||
|
formattedName: function(){
|
||||||
|
return this.get('name').replace("{{username}}", this.get('controllers.flag.username'));
|
||||||
|
}.property('name'),
|
||||||
|
|
||||||
|
selected: function() {
|
||||||
|
return this.get('model') === this.get('controllers.flag.selected');
|
||||||
|
}.property('controllers.flag.selected'),
|
||||||
|
|
||||||
|
showMessageInput: Em.computed.and('is_custom_flag', 'selected'),
|
||||||
|
showDescription: Em.computed.not('showMessageInput'),
|
||||||
|
|
||||||
|
customMessageLengthClasses: function() {
|
||||||
|
return (this.get('message.length') < Discourse.PostActionType.MIN_MESSAGE_LENGTH) ? "too-short" : "ok"
|
||||||
|
}.property('message.length'),
|
||||||
|
|
||||||
|
customMessageLength: function() {
|
||||||
|
var len = this.get('message.length') || 0;
|
||||||
|
var minLen = Discourse.PostActionType.MIN_MESSAGE_LENGTH;
|
||||||
|
if (len === 0) {
|
||||||
|
return Em.String.i18n("flagging.custom_message.at_least", { n: minLen });
|
||||||
|
} else if (len < minLen) {
|
||||||
|
return Em.String.i18n("flagging.custom_message.more", { n: minLen - len });
|
||||||
|
} else {
|
||||||
|
return Em.String.i18n("flagging.custom_message.left", {
|
||||||
|
n: Discourse.PostActionType.MAX_MESSAGE_LENGTH - len
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}.property('message.length')
|
||||||
|
|
||||||
|
});
|
||||||
|
|
|
@ -9,70 +9,58 @@
|
||||||
**/
|
**/
|
||||||
Discourse.FlagController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
|
Discourse.FlagController = Discourse.ObjectController.extend(Discourse.ModalFunctionality, {
|
||||||
|
|
||||||
// trick to bind user / post to flag
|
|
||||||
boundFlags: function() {
|
|
||||||
var _this = this;
|
|
||||||
var original = this.get('flagsAvailable');
|
|
||||||
if(original){
|
|
||||||
return $.map(original, function(v){
|
|
||||||
var b = Discourse.BoundPostActionType.create(v);
|
|
||||||
b.set('post', _this.get('model'));
|
|
||||||
return b;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}.property('flagsAvailable.@each'),
|
|
||||||
|
|
||||||
changePostActionType: function(action) {
|
changePostActionType: function(action) {
|
||||||
if (this.get('postActionTypeId') === action.id) return false;
|
|
||||||
|
|
||||||
this.get('boundFlags').setEach('selected', false);
|
|
||||||
action.set('selected', true);
|
|
||||||
|
|
||||||
this.set('postActionTypeId', action.id);
|
|
||||||
this.set('isCustomFlag', action.is_custom_flag);
|
|
||||||
this.set('selected', action);
|
this.set('selected', action);
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
showSubmit: function() {
|
submitEnabled: function() {
|
||||||
if (this.get('postActionTypeId')) {
|
var selected = this.get('selected');
|
||||||
if (this.get('isCustomFlag')) {
|
if (!selected) return false;
|
||||||
var m = this.get('selected.message');
|
|
||||||
return m && m.length >= 10 && m.length <= 500;
|
if (selected.get('is_custom_flag')) {
|
||||||
} else {
|
var len = this.get('message.length') || 0;
|
||||||
return true;
|
return len >= Discourse.PostActionType.MIN_MESSAGE_LENGTH &&
|
||||||
}
|
len <= Discourse.PostActionType.MAX_MESSAGE_LENGTH;
|
||||||
}
|
}
|
||||||
return false;
|
return true;
|
||||||
}.property('isCustomFlag', 'selected.customMessageLength', 'postActionTypeId'),
|
}.property('selected.is_custom_flag', 'message.length'),
|
||||||
|
|
||||||
|
submitDisabled: Em.computed.not('submitEnabled'),
|
||||||
|
|
||||||
|
// Staff accounts can "take action"
|
||||||
|
canTakeAction: function() {
|
||||||
|
// We can only take actions on non-custom flags
|
||||||
|
if (this.get('selected.is_custom_flag')) return false;
|
||||||
|
return Discourse.User.current('staff');
|
||||||
|
}.property('selected.is_custom_flag'),
|
||||||
|
|
||||||
submitText: function(){
|
submitText: function(){
|
||||||
var action = this.get('selected');
|
|
||||||
if (this.get('selected.is_custom_flag')) {
|
if (this.get('selected.is_custom_flag')) {
|
||||||
return Em.String.i18n("flagging.notify_action");
|
return Em.String.i18n("flagging.notify_action");
|
||||||
} else {
|
} else {
|
||||||
return Em.String.i18n("flagging.action");
|
return Em.String.i18n("flagging.action");
|
||||||
}
|
}
|
||||||
}.property('selected'),
|
}.property('selected.is_custom_flag'),
|
||||||
|
|
||||||
createFlag: function() {
|
takeAction: function() {
|
||||||
var _this = this;
|
this.createFlag({takeAction: true})
|
||||||
|
this.set('hidden', true);
|
||||||
|
},
|
||||||
|
|
||||||
var action = this.get('selected');
|
createFlag: function(opts) {
|
||||||
var postAction = this.get('actionByName.' + (action.get('name_key')));
|
var flagController = this;
|
||||||
|
var postAction = this.get('actionByName.' + this.get('selected.name_key'));
|
||||||
|
var params = this.get('selected.is_custom_flag') ? {message: this.get('message')} : {}
|
||||||
|
|
||||||
var actionType = Discourse.Site.instance().postActionTypeById(this.get('postActionTypeId'));
|
if (opts) params = $.extend(params, opts);
|
||||||
if (postAction) {
|
|
||||||
postAction.act({
|
postAction.act(params).then(function() {
|
||||||
message: action.get('message')
|
flagController.closeModal();
|
||||||
}).then(function() {
|
}, function(errors) {
|
||||||
return $('#discourse-modal').modal('hide');
|
flagController.displayErrors(errors);
|
||||||
}, function(errors) {
|
});
|
||||||
return _this.displayErrors(errors);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,16 @@
|
||||||
**/
|
**/
|
||||||
Discourse.ModalController = Discourse.Controller.extend({
|
Discourse.ModalController = Discourse.Controller.extend({
|
||||||
|
|
||||||
|
/**
|
||||||
|
Close the modal.
|
||||||
|
|
||||||
|
@method closeModal
|
||||||
|
**/
|
||||||
|
closeModal: function() {
|
||||||
|
// Currently uses jQuery to hide it.
|
||||||
|
$('#discourse-modal').modal('hide');
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,16 @@ Discourse.ModalFunctionality = Em.Mixin.create({
|
||||||
message: message,
|
message: message,
|
||||||
messageClass: messageClass
|
messageClass: messageClass
|
||||||
}));
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Close the modal.
|
||||||
|
|
||||||
|
@method closeModal
|
||||||
|
**/
|
||||||
|
closeModal: function() {
|
||||||
|
// Currently uses jQuery to hide it.
|
||||||
|
this.get('controllers.modal').closeModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -37,6 +37,8 @@ Discourse.ActionSummary = Discourse.Model.extend({
|
||||||
|
|
||||||
// Perform this action
|
// Perform this action
|
||||||
act: function(opts) {
|
act: function(opts) {
|
||||||
|
if (!opts) opts = {};
|
||||||
|
|
||||||
var action = this.get('actionType.name_key');
|
var action = this.get('actionType.name_key');
|
||||||
|
|
||||||
// Mark it as acted
|
// Mark it as acted
|
||||||
|
@ -63,7 +65,8 @@ Discourse.ActionSummary = Discourse.Model.extend({
|
||||||
data: {
|
data: {
|
||||||
id: this.get('post.id'),
|
id: this.get('post.id'),
|
||||||
post_action_type_id: this.get('id'),
|
post_action_type_id: this.get('id'),
|
||||||
message: (opts ? opts.message : void 0) || ""
|
message: opts.message,
|
||||||
|
take_action: opts.takeAction
|
||||||
}
|
}
|
||||||
}).then(null, function (error) {
|
}).then(null, function (error) {
|
||||||
actionSummary.removeAction();
|
actionSummary.removeAction();
|
||||||
|
|
|
@ -6,31 +6,9 @@
|
||||||
@namespace Discourse
|
@namespace Discourse
|
||||||
@module Discourse
|
@module Discourse
|
||||||
**/
|
**/
|
||||||
Discourse.PostActionType = Discourse.Model.extend({
|
Discourse.PostActionType = Discourse.Model.extend({});
|
||||||
});
|
|
||||||
|
|
||||||
Discourse.BoundPostActionType = Discourse.PostActionType.extend({
|
Discourse.PostActionType.reopenClass({
|
||||||
customPlaceholder: function(){
|
MIN_MESSAGE_LENGTH: 10,
|
||||||
return Em.String.i18n("flagging.custom_placeholder_" + this.get('name_key'));
|
MAX_MESSAGE_LENGTH: 500
|
||||||
}.property('name_key'),
|
})
|
||||||
|
|
||||||
formattedName: function(){
|
|
||||||
return this.get('name').replace("{{username}}", this.get('post.username'));
|
|
||||||
}.property('name'),
|
|
||||||
|
|
||||||
messageChanged: function() {
|
|
||||||
var len, message, minLen, _ref;
|
|
||||||
minLen = 10;
|
|
||||||
len = ((_ref = this.get('message')) ? _ref.length : void 0) || 0;
|
|
||||||
this.set("customMessageLengthClasses", "too-short custom-message-length");
|
|
||||||
if (len === 0) {
|
|
||||||
message = Em.String.i18n("flagging.custom_message.at_least", { n: minLen });
|
|
||||||
} else if (len < minLen) {
|
|
||||||
message = Em.String.i18n("flagging.custom_message.more", { n: minLen - len });
|
|
||||||
} else {
|
|
||||||
message = Em.String.i18n("flagging.custom_message.left", { n: 500 - len });
|
|
||||||
this.set("customMessageLengthClasses", "ok custom-message-length");
|
|
||||||
}
|
|
||||||
this.set("customMessageLength", message);
|
|
||||||
}.observes("message")
|
|
||||||
});
|
|
||||||
|
|
|
@ -13,9 +13,7 @@ Discourse.TopicRoute = Discourse.Route.extend({
|
||||||
|
|
||||||
showFlags: function(post) {
|
showFlags: function(post) {
|
||||||
Discourse.Route.showModal(this, 'flag', post);
|
Discourse.Route.showModal(this, 'flag', post);
|
||||||
this.controllerFor('flag').setProperties({
|
this.controllerFor('flag').setProperties({ selected: null });
|
||||||
postActionTypeId: null
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
|
|
||||||
showAutoClose: function() {
|
showAutoClose: function() {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<form>
|
<form>
|
||||||
{{view Discourse.ArchetypeOptionsView archetypeBinding="view.archetype"}}
|
{{view Discourse.ArchetypeOptionsView archetypeBinding="view.archetype"}}
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class='btn btn-primary' data-dismiss="modal">{{i18n post.archetypes.save}}</button>
|
<button class='btn btn-primary' {{action closeModal}}>{{i18n post.archetypes.save}}</button>
|
||||||
</div>
|
</div>
|
|
@ -1,36 +1,30 @@
|
||||||
<div class="modal-body flag-modal">
|
<div class="modal-body flag-modal">
|
||||||
{{#if flagsAvailable}}
|
|
||||||
<form>
|
<form>
|
||||||
{{#each boundFlags}}
|
{{#each flagsAvailable itemController="flagActionType"}}
|
||||||
<div class='controls'>
|
<div class='controls'>
|
||||||
<label class='radio'>
|
<label class='radio'>
|
||||||
<input type='radio' id="radio_{{unbound name_key}}" {{action changePostActionType this}} name='post_action_type_index'> <strong>{{formattedName}}</strong>
|
<input type='radio' id="radio_{{unbound name_key}}" {{action changePostActionType this}} name='post_action_type_index'> <strong>{{formattedName}}</strong>
|
||||||
{{#if is_custom_flag}}
|
{{#if showDescription}}
|
||||||
{{#unless selected}}
|
<div class='description'>{{{description}}}</div>
|
||||||
<div class='description'>{{{description}}}</div>
|
|
||||||
{{/unless}}
|
|
||||||
{{else}}
|
|
||||||
{{#if description}}
|
|
||||||
<div class='description'>{{{description}}}</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
|
||||||
</label>
|
|
||||||
{{#if is_custom_flag}}
|
|
||||||
{{#if selected}}
|
|
||||||
{{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}}
|
|
||||||
<div {{bindAttr class="customMessageLengthClasses"}}>{{customMessageLength}}</div>
|
|
||||||
{{/if}}
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</label>
|
||||||
{{/each}}
|
{{#if showMessageInput}}
|
||||||
</form>
|
{{textarea name="message" class="flag-message" placeholder=customPlaceholder value=message}}
|
||||||
{{else}}
|
<div {{bindAttr class=":custom-message-length customMessageLengthClasses"}}>{{customMessageLength}}</div>
|
||||||
{{i18n flagging.cant}}
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
{{i18n flagging.cant}}
|
||||||
|
{{/each}}
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button class='btn btn-primary' {{action createFlag}} {{bindAttr disabled="submitDisabled"}}>{{submitText}}</button>
|
||||||
|
|
||||||
|
{{#if canTakeAction}}
|
||||||
|
<button class='btn btn-danger' {{action takeAction}} {{bindAttr disabled="submitDisabled"}}>{{i18n flagging.take_action}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if showSubmit}}
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button class='btn btn-primary' {{action createFlag}}>{{submitText}}</button>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
{{#if finished}}
|
{{#if finished}}
|
||||||
<button class='btn btn-primary' data-dismiss="modal">{{i18n close}}</button>
|
<button class='btn btn-primary' {{action closeModal}}>{{i18n close}}</button>
|
||||||
{{else}}
|
{{else}}
|
||||||
<button class='btn btn-primary' {{bindAttr disabled="disabled"}} {{action createInvite}}>{{buttonTitle}}</button>
|
<button class='btn btn-primary' {{bindAttr disabled="disabled"}} {{action createInvite}}>{{buttonTitle}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
{{#if finished}}
|
{{#if finished}}
|
||||||
<button class='btn btn-primary' data-dismiss="modal">{{i18n close}}</button>
|
<button class='btn btn-primary' {{action closeModal}}>{{i18n close}}</button>
|
||||||
{{else}}
|
{{else}}
|
||||||
<button class='btn btn-primary' {{bindAttr disabled="disabled"}} {{action invite}}>{{buttonTitle}}</button>
|
<button class='btn btn-primary' {{bindAttr disabled="disabled"}} {{action invite}}>{{buttonTitle}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<a class="close" data-dismiss="modal"><i class='icon-remove icon'></i></a>
|
<a class="close" {{action closeModal}}><i class='icon-remove icon'></i></a>
|
||||||
<h3>{{title}}</h3>
|
<h3>{{title}}</h3>
|
||||||
</div>
|
</div>
|
||||||
<div id='modal-alert'></div>
|
<div id='modal-alert'></div>
|
||||||
|
|
|
@ -7,5 +7,5 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<button class='btn btn-primary' data-dismiss="modal">{{i18n close}}</button>
|
<button class='btn btn-primary' {{action closeModal}}>{{i18n close}}</button>
|
||||||
</div>
|
</div>
|
|
@ -9,7 +9,11 @@ class PostActionsController < ApplicationController
|
||||||
def create
|
def create
|
||||||
guardian.ensure_post_can_act!(@post, PostActionType.types[@post_action_type_id])
|
guardian.ensure_post_can_act!(@post, PostActionType.types[@post_action_type_id])
|
||||||
|
|
||||||
post_action = PostAction.act(current_user, @post, @post_action_type_id, params[:message])
|
args = {}
|
||||||
|
args[:message] = params[:message] if params[:message].present?
|
||||||
|
args[:take_action] = true if guardian.is_staff? and params[:take_action] == 'true'
|
||||||
|
|
||||||
|
post_action = PostAction.act(current_user, @post, @post_action_type_id, args)
|
||||||
|
|
||||||
if post_action.blank? || post_action.errors.present?
|
if post_action.blank? || post_action.errors.present?
|
||||||
render_json_error(post_action)
|
render_json_error(post_action)
|
||||||
|
|
|
@ -8,7 +8,7 @@ class PostAction < ActiveRecord::Base
|
||||||
include RateLimiter::OnCreateRecord
|
include RateLimiter::OnCreateRecord
|
||||||
include Trashable
|
include Trashable
|
||||||
|
|
||||||
attr_accessible :post_action_type_id, :post_id, :user_id, :post, :user, :post_action_type, :message, :related_post_id
|
attr_accessible :post_action_type_id, :post_id, :user_id, :post, :user, :post_action_type, :message, :related_post_id, :staff_took_action
|
||||||
|
|
||||||
belongs_to :post
|
belongs_to :post
|
||||||
belongs_to :user
|
belongs_to :user
|
||||||
|
@ -70,11 +70,11 @@ class PostAction < ActiveRecord::Base
|
||||||
update_flagged_posts_count
|
update_flagged_posts_count
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.act(user, post, post_action_type_id, message = nil)
|
def self.act(user, post, post_action_type_id, opts={})
|
||||||
begin
|
begin
|
||||||
title, target_usernames, target_group_names, subtype, body = nil
|
title, target_usernames, target_group_names, subtype, body = nil
|
||||||
|
|
||||||
if message
|
if opts[:message]
|
||||||
[:notify_moderators, :notify_user].each do |k|
|
[:notify_moderators, :notify_user].each do |k|
|
||||||
if post_action_type_id == PostActionType.types[k]
|
if post_action_type_id == PostActionType.types[k]
|
||||||
if k == :notify_moderators
|
if k == :notify_moderators
|
||||||
|
@ -85,7 +85,7 @@ class PostAction < ActiveRecord::Base
|
||||||
title = I18n.t("post_action_types.#{k}.email_title",
|
title = I18n.t("post_action_types.#{k}.email_title",
|
||||||
title: post.topic.title)
|
title: post.topic.title)
|
||||||
body = I18n.t("post_action_types.#{k}.email_body",
|
body = I18n.t("post_action_types.#{k}.email_body",
|
||||||
message: message,
|
message: opts[:message],
|
||||||
link: "#{Discourse.base_url}#{post.url}")
|
link: "#{Discourse.base_url}#{post.url}")
|
||||||
subtype = k == :notify_moderators ? TopicSubtype.notify_moderators : TopicSubtype.notify_user
|
subtype = k == :notify_moderators ? TopicSubtype.notify_moderators : TopicSubtype.notify_user
|
||||||
end
|
end
|
||||||
|
@ -103,10 +103,12 @@ class PostAction < ActiveRecord::Base
|
||||||
raw: body
|
raw: body
|
||||||
).create.id
|
).create.id
|
||||||
end
|
end
|
||||||
|
|
||||||
create( post_id: post.id,
|
create( post_id: post.id,
|
||||||
user_id: user.id,
|
user_id: user.id,
|
||||||
post_action_type_id: post_action_type_id,
|
post_action_type_id: post_action_type_id,
|
||||||
message: message,
|
message: opts[:message],
|
||||||
|
staff_took_action: opts[:take_action] || false,
|
||||||
related_post_id: related_post_id )
|
related_post_id: related_post_id )
|
||||||
rescue ActiveRecord::RecordNotUnique
|
rescue ActiveRecord::RecordNotUnique
|
||||||
# can happen despite being .create
|
# can happen despite being .create
|
||||||
|
@ -177,13 +179,13 @@ class PostAction < ActiveRecord::Base
|
||||||
# can weigh flags differently.
|
# can weigh flags differently.
|
||||||
def self.flag_counts_for(post_id)
|
def self.flag_counts_for(post_id)
|
||||||
flag_counts = exec_sql("SELECT SUM(CASE
|
flag_counts = exec_sql("SELECT SUM(CASE
|
||||||
WHEN pa.deleted_at IS NULL AND u.admin THEN :flags_required_to_hide_post
|
WHEN pa.deleted_at IS NULL AND pa.staff_took_action THEN :flags_required_to_hide_post
|
||||||
WHEN pa.deleted_at IS NULL AND (NOT u.admin) THEN 1
|
WHEN pa.deleted_at IS NULL AND (NOT pa.staff_took_action) THEN 1
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END) AS new_flags,
|
END) AS new_flags,
|
||||||
SUM(CASE
|
SUM(CASE
|
||||||
WHEN pa.deleted_at IS NOT NULL AND u.admin THEN :flags_required_to_hide_post
|
WHEN pa.deleted_at IS NOT NULL AND pa.staff_took_action THEN :flags_required_to_hide_post
|
||||||
WHEN pa.deleted_at IS NOT NULL AND (NOT u.admin) THEN 1
|
WHEN pa.deleted_at IS NOT NULL AND (NOT pa.staff_took_action) THEN 1
|
||||||
ELSE 0
|
ELSE 0
|
||||||
END) AS old_flags
|
END) AS old_flags
|
||||||
FROM post_actions AS pa
|
FROM post_actions AS pa
|
||||||
|
@ -234,7 +236,8 @@ class PostAction < ActiveRecord::Base
|
||||||
reason = old_flags > 0 ? Post.hidden_reasons[:flag_threshold_reached_again] : Post.hidden_reasons[:flag_threshold_reached]
|
reason = old_flags > 0 ? Post.hidden_reasons[:flag_threshold_reached_again] : Post.hidden_reasons[:flag_threshold_reached]
|
||||||
Post.update_all(["hidden = true, hidden_reason_id = COALESCE(hidden_reason_id, ?)", reason], id: post_id)
|
Post.update_all(["hidden = true, hidden_reason_id = COALESCE(hidden_reason_id, ?)", reason], id: post_id)
|
||||||
Topic.update_all({ visible: false },
|
Topic.update_all({ visible: false },
|
||||||
["id = :topic_id AND NOT EXISTS(SELECT 1 FROM POSTS WHERE topic_id = :topic_id AND NOT hidden)", topic_id: post.topic_id])
|
["id = :topic_id AND NOT EXISTS(SELECT 1 FROM POSTS WHERE topic_id = :topic_id AND NOT hidden)",
|
||||||
|
topic_id: post.topic_id])
|
||||||
|
|
||||||
# inform user
|
# inform user
|
||||||
if post.user
|
if post.user
|
||||||
|
|
|
@ -831,6 +831,7 @@ en:
|
||||||
flagging:
|
flagging:
|
||||||
title: 'Why are you flagging this post?'
|
title: 'Why are you flagging this post?'
|
||||||
action: 'Flag Post'
|
action: 'Flag Post'
|
||||||
|
take_action: "Take Action"
|
||||||
notify_action: 'Notify'
|
notify_action: 'Notify'
|
||||||
cant: "Sorry, you can't flag this post at this time."
|
cant: "Sorry, you can't flag this post at this time."
|
||||||
custom_placeholder_notify_user: "Why does this post require you to speak to this user directly and privately? Be specific, be constructive, and always be kind."
|
custom_placeholder_notify_user: "Why does this post require you to speak to this user directly and privately? Be specific, be constructive, and always be kind."
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
class AddStaffTookActionToPostActions < ActiveRecord::Migration
|
||||||
|
def change
|
||||||
|
add_column :post_actions, :staff_took_action, :boolean, default: false, null: false
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,7 +9,7 @@ describe PostActionsController do
|
||||||
|
|
||||||
describe 'logged in' do
|
describe 'logged in' do
|
||||||
before do
|
before do
|
||||||
@user = log_in
|
@user = log_in(:moderator)
|
||||||
@post = Fabricate(:post, user: Fabricate(:coding_horror))
|
@post = Fabricate(:post, user: Fabricate(:coding_horror))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -34,9 +34,26 @@ describe PostActionsController do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'allows us to create an post action on a post' do
|
it 'allows us to create an post action on a post' do
|
||||||
PostAction.expects(:act).once.with(@user, @post, PostActionType.types[:like], nil)
|
PostAction.expects(:act).once.with(@user, @post, PostActionType.types[:like], {})
|
||||||
xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like]
|
xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like]
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'passes the message through' do
|
||||||
|
PostAction.expects(:act).once.with(@user, @post, PostActionType.types[:like], {message: 'action message goes here'})
|
||||||
|
xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like], message: 'action message goes here'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'passes take_action through' do
|
||||||
|
PostAction.expects(:act).once.with(@user, @post, PostActionType.types[:like], {take_action: true})
|
||||||
|
xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like], take_action: 'true'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "doesn't pass take_action through if the user isn't staff" do
|
||||||
|
Guardian.any_instance.stubs(:is_staff?).returns(false)
|
||||||
|
PostAction.expects(:act).once.with(@user, @post, PostActionType.types[:like], {})
|
||||||
|
xhr :post, :create, id: @post.id, post_action_type_id: PostActionType.types[:like], take_action: 'true'
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -21,7 +21,7 @@ describe PostAction do
|
||||||
|
|
||||||
it "notify moderators integration test" do
|
it "notify moderators integration test" do
|
||||||
mod = moderator
|
mod = moderator
|
||||||
action = PostAction.act(codinghorror, post, PostActionType.types[:notify_moderators], "this is my special long message");
|
action = PostAction.act(codinghorror, post, PostActionType.types[:notify_moderators], message: "this is my special long message");
|
||||||
|
|
||||||
posts = Post.joins(:topic)
|
posts = Post.joins(:topic)
|
||||||
.select('posts.id, topics.subtype, posts.topic_id')
|
.select('posts.id, topics.subtype, posts.topic_id')
|
||||||
|
@ -50,7 +50,7 @@ describe PostAction do
|
||||||
it "sends an email to all moderators if selected" do
|
it "sends an email to all moderators if selected" do
|
||||||
post = build(:post, id: 1000)
|
post = build(:post, id: 1000)
|
||||||
PostCreator.any_instance.expects(:create).returns(post)
|
PostCreator.any_instance.expects(:create).returns(post)
|
||||||
PostAction.act(build(:user), build(:post), PostActionType.types[:notify_moderators], "this is my special message");
|
PostAction.act(build(:user), build(:post), PostActionType.types[:notify_moderators], message: "this is my special message");
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ describe PostAction do
|
||||||
|
|
||||||
it "sends an email to user if selected" do
|
it "sends an email to user if selected" do
|
||||||
PostCreator.any_instance.expects(:create).returns(build(:post))
|
PostCreator.any_instance.expects(:create).returns(build(:post))
|
||||||
PostAction.act(build(:user), post, PostActionType.types[:notify_user], "this is my special message");
|
PostAction.act(build(:user), post, PostActionType.types[:notify_user], message: "this is my special message");
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -179,9 +179,9 @@ describe PostAction do
|
||||||
flag = PostAction.act(Fabricate(:evil_trout), post, PostActionType.types[:spam])
|
flag = PostAction.act(Fabricate(:evil_trout), post, PostActionType.types[:spam])
|
||||||
PostAction.flag_counts_for(post.id).should == [0, 1]
|
PostAction.flag_counts_for(post.id).should == [0, 1]
|
||||||
|
|
||||||
# If an admin flags the post, it is counted higher
|
# If staff takes action, it is ranked higher
|
||||||
admin = Fabricate(:admin)
|
admin = Fabricate(:admin)
|
||||||
PostAction.act(admin, post, PostActionType.types[:spam])
|
pa = PostAction.act(admin, post, PostActionType.types[:spam], take_action: true)
|
||||||
PostAction.flag_counts_for(post.id).should == [0, 8]
|
PostAction.flag_counts_for(post.id).should == [0, 8]
|
||||||
|
|
||||||
# If a flag is dismissed
|
# If a flag is dismissed
|
||||||
|
|
Loading…
Reference in a new issue