mirror of
https://github.com/discourse/discourse.git
synced 2025-01-22 16:50:45 +08:00
Add button to delete a spammer in the flag modal
Add SiteSettings: delete_user_max_age, delete_all_posts_max. Add delete spammer button to admin flags UI Moderators can delete users too
This commit is contained in:
parent
e076158789
commit
4fd5087f91
|
@ -56,6 +56,16 @@ Discourse.AdminFlagsController = Ember.ArrayController.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
Deletes a user and all posts and topics created by that user.
|
||||||
|
|
||||||
|
@method deleteSpammer
|
||||||
|
@param {Discourse.FlaggedPost} item The post to delete
|
||||||
|
**/
|
||||||
|
deleteSpammer: function(item) {
|
||||||
|
item.get('user').deleteAsSpammer(function() { window.location.reload(); });
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Are we viewing the 'old' view?
|
Are we viewing the 'old' view?
|
||||||
|
|
||||||
|
|
|
@ -264,6 +264,36 @@ Discourse.AdminUser = Discourse.User.extend({
|
||||||
bootbox.dialog(message, buttons, {"classes": "delete-user-modal"});
|
bootbox.dialog(message, buttons, {"classes": "delete-user-modal"});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
deleteAsSpammer: function(successCallback) {
|
||||||
|
var user = this;
|
||||||
|
var message = I18n.t('flagging.delete_confirm', {posts: user.get('post_count'), topics: user.get('topic_count'), email: user.get('email')});
|
||||||
|
var buttons = [{
|
||||||
|
"label": I18n.t("composer.cancel"),
|
||||||
|
"class": "cancel",
|
||||||
|
"link": true
|
||||||
|
}, {
|
||||||
|
"label": I18n.t("flagging.yes_delete_spammer"),
|
||||||
|
"class": "btn btn-danger",
|
||||||
|
"callback": function() {
|
||||||
|
Discourse.ajax("/admin/users/" + user.get('id') + '.json', {
|
||||||
|
type: 'DELETE',
|
||||||
|
data: {delete_posts: true, block_email: true}
|
||||||
|
}).then(function(data) {
|
||||||
|
if (data.deleted) {
|
||||||
|
bootbox.alert(I18n.t("admin.user.deleted"), function() {
|
||||||
|
if (successCallback) successCallback();
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||||
|
}
|
||||||
|
}, function(jqXHR, status, error) {
|
||||||
|
bootbox.alert(I18n.t("admin.user.delete_failed"));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
bootbox.dialog(message, buttons, {"classes": "flagging-delete-spammer"});
|
||||||
|
},
|
||||||
|
|
||||||
loadDetails: function() {
|
loadDetails: function() {
|
||||||
var model = this;
|
var model = this;
|
||||||
if (model.get('loadedDetails')) { return Ember.RSVP.resolve(model); }
|
if (model.get('loadedDetails')) { return Ember.RSVP.resolve(model); }
|
||||||
|
|
|
@ -57,6 +57,14 @@ Discourse.FlaggedPost = Discourse.Post.extend({
|
||||||
return !this.get('topic_visible');
|
return !this.get('topic_visible');
|
||||||
}.property('topic_hidden'),
|
}.property('topic_hidden'),
|
||||||
|
|
||||||
|
flaggedForSpam: function() {
|
||||||
|
return !_.every(this.get('post_actions'), function(action) { return action.name_key !== 'spam'; });
|
||||||
|
}.property('post_actions.@each.name_key'),
|
||||||
|
|
||||||
|
canDeleteAsSpammer: function() {
|
||||||
|
return (Discourse.User.current('staff') && this.get('flaggedForSpam') && this.get('user.can_delete_all_posts') && this.get('user.can_be_deleted'));
|
||||||
|
}.property('flaggedForSpam'),
|
||||||
|
|
||||||
deletePost: function() {
|
deletePost: function() {
|
||||||
if (this.get('post_number') === '1') {
|
if (this.get('post_number') === '1') {
|
||||||
return Discourse.ajax('/t/' + this.topic_id, { type: 'DELETE', cache: false });
|
return Discourse.ajax('/t/' + this.topic_id, { type: 'DELETE', cache: false });
|
||||||
|
|
|
@ -22,17 +22,17 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{{#each flag in content}}
|
{{#each flaggedPost in content}}
|
||||||
<tr {{bindAttr class="flag.extraClasses"}}>
|
<tr {{bindAttr class="flaggedPost.extraClasses"}}>
|
||||||
|
|
||||||
<td class='user'>{{#if flag.user}}{{#linkTo 'adminUser' flag.user}}{{avatar flag.user imageSize="small"}}{{/linkTo}}{{/if}}</td>
|
<td class='user'>{{#if flaggedPost.user}}{{#linkTo 'adminUser' flaggedPost.user}}{{avatar flaggedPost.user imageSize="small"}}{{/linkTo}}{{/if}}</td>
|
||||||
|
|
||||||
<td class='excerpt'>{{#if flag.topicHidden}}<i title='{{i18n topic_statuses.invisible.help}}' class='icon icon-eye-close'></i> {{/if}}<h3><a href='{{unbound flag.url}}'>{{flag.title}}</a></h3><br>{{{flag.excerpt}}}
|
<td class='excerpt'>{{#if flaggedPost.topicHidden}}<i title='{{i18n topic_statuses.invisible.help}}' class='icon icon-eye-close'></i> {{/if}}<h3><a href='{{unbound flaggedPost.url}}'>{{flaggedPost.title}}</a></h3><br>{{{flaggedPost.excerpt}}}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
<td class='flaggers'>
|
<td class='flaggers'>
|
||||||
<table>
|
<table>
|
||||||
{{#each flag.flaggers}}
|
{{#each flaggedPost.flaggers}}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
{{#linkTo 'adminUser' this.user}}{{avatar this.user imageSize="small"}} {{/linkTo}}
|
{{#linkTo 'adminUser' this.user}}{{avatar this.user imageSize="small"}} {{/linkTo}}
|
||||||
|
@ -50,7 +50,7 @@
|
||||||
|
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
{{#each flag.messages}}
|
{{#each flaggedPost.messages}}
|
||||||
<tr>
|
<tr>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td class='message'>
|
<td class='message'>
|
||||||
|
@ -64,14 +64,19 @@
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="action">
|
<td colspan="4" class="action">
|
||||||
{{#if adminActiveFlagsView}}
|
{{#if adminActiveFlagsView}}
|
||||||
{{#if flag.postHidden}}
|
{{#if flaggedPost.postHidden}}
|
||||||
<button title='{{i18n admin.flags.disagree_unhide_title}}' class='btn' {{action disagreeFlags flag}}><i class="icon-thumbs-down"></i> {{i18n admin.flags.disagree_unhide}}</button>
|
<button title='{{i18n admin.flags.disagree_unhide_title}}' class='btn' {{action disagreeFlags flaggedPost}}><i class="icon-thumbs-down"></i> {{i18n admin.flags.disagree_unhide}}</button>
|
||||||
<button title='{{i18n admin.flags.defer_title}}' class='btn' {{action deferFlags flag}}><i class="icon-external-link"></i> {{i18n admin.flags.defer}}</button>
|
<button title='{{i18n admin.flags.defer_title}}' class='btn' {{action deferFlags flaggedPost}}><i class="icon-external-link"></i> {{i18n admin.flags.defer}}</button>
|
||||||
{{else}}
|
{{else}}
|
||||||
<button title='{{i18n admin.flags.agree_hide_title}}' class='btn' {{action agreeFlags flag}}><i class="icon-thumbs-up"></i> {{i18n admin.flags.agree_hide}}</button>
|
<button title='{{i18n admin.flags.agree_hide_title}}' class='btn' {{action agreeFlags flaggedPost}}><i class="icon-thumbs-up"></i> {{i18n admin.flags.agree_hide}}</button>
|
||||||
<button title='{{i18n admin.flags.disagree_title}}' class='btn' {{action disagreeFlags flag}}><i class="icon-thumbs-down"></i> {{i18n admin.flags.disagree}}</button>
|
<button title='{{i18n admin.flags.disagree_title}}' class='btn' {{action disagreeFlags flaggedPost}}><i class="icon-thumbs-down"></i> {{i18n admin.flags.disagree}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<button title='{{i18n admin.flags.delete_post_title}}' class='btn' {{action deletePost flag}}><i class="icon-trash"></i> {{i18n admin.flags.delete_post}}</button>
|
|
||||||
|
{{#if flaggedPost.canDeleteAsSpammer}}
|
||||||
|
<button title='{{i18n admin.flags.delete_spammer_title}}' class="btn" {{action deleteSpammer flaggedPost}}><i class="icon icon-trash"></i> {{i18n flagging.delete_spammer}}</button>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<button title='{{i18n admin.flags.delete_post_title}}' class='btn' {{action deletePost flaggedPost}}><i class="icon-trash"></i> {{i18n admin.flags.delete_post}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -63,8 +63,33 @@ Discourse.FlagController = Discourse.ObjectController.extend(Discourse.ModalFunc
|
||||||
}, function(errors) {
|
}, function(errors) {
|
||||||
flagController.displayErrors(errors);
|
flagController.displayErrors(errors);
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
canDeleteSpammer: function() {
|
||||||
|
if (Discourse.User.current('staff') && this.get('selected.name_key') === 'spam') {
|
||||||
|
return this.get('userDetails.can_be_deleted') && this.get('userDetails.can_delete_all_posts');
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}.property('selected.name_key', 'userDetails.can_be_deleted', 'userDetails.can_delete_all_posts'),
|
||||||
|
|
||||||
|
deleteSpammer: function() {
|
||||||
|
this.send('closeModal');
|
||||||
|
this.get('userDetails').deleteAsSpammer(function() { window.location.reload(); });
|
||||||
|
},
|
||||||
|
|
||||||
|
usernameChanged: function() {
|
||||||
|
this.set('userDetails', null);
|
||||||
|
this.fetchUserDetails();
|
||||||
|
}.observes('username'),
|
||||||
|
|
||||||
|
fetchUserDetails: function() {
|
||||||
|
if( Discourse.User.current('staff') && this.get('username') ) {
|
||||||
|
var flagController = this;
|
||||||
|
Discourse.AdminUser.find(this.get('username')).then(function(user){
|
||||||
|
flagController.set('userDetails', user);
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,5 +26,9 @@
|
||||||
{{#if canTakeAction}}
|
{{#if canTakeAction}}
|
||||||
<button class='btn btn-danger' {{action takeAction}} {{bindAttr disabled="submitDisabled"}}>{{i18n flagging.take_action}}</button>
|
<button class='btn btn-danger' {{action takeAction}} {{bindAttr disabled="submitDisabled"}}>{{i18n flagging.take_action}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if canDeleteSpammer}}
|
||||||
|
<button class="btn btn-danger" {{action deleteSpammer}} {{bindAttr disabled="submitDisabled"}}><i class="icon icon-trash"></i> {{i18n flagging.delete_spammer}}</button>
|
||||||
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -316,6 +316,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.flagging-delete-spammer {
|
||||||
|
.modal-footer .cancel {
|
||||||
|
text-decoration: underline;
|
||||||
|
margin-left: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.permission-list{
|
.permission-list{
|
||||||
list-style:none;
|
list-style:none;
|
||||||
margin: 0 0 30px;
|
margin: 0 0 30px;
|
||||||
|
|
|
@ -7,7 +7,7 @@ class Admin::FlagsController < Admin::AdminController
|
||||||
if posts.blank?
|
if posts.blank?
|
||||||
render json: {users: [], posts: []}
|
render json: {users: [], posts: []}
|
||||||
else
|
else
|
||||||
render json: MultiJson.dump({users: serialize_data(users, BasicUserSerializer), posts: posts})
|
render json: MultiJson.dump({users: serialize_data(users, AdminDetailedUserSerializer), posts: posts})
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -334,6 +334,7 @@ class PostAction < ActiveRecord::Base
|
||||||
post = post_lookup[pa.post_id]
|
post = post_lookup[pa.post_id]
|
||||||
post.post_actions ||= []
|
post.post_actions ||= []
|
||||||
action = pa.attributes
|
action = pa.attributes
|
||||||
|
action[:name_key] = PostActionType.types.key(pa.post_action_type_id)
|
||||||
if (pa.related_post && pa.related_post.topic)
|
if (pa.related_post && pa.related_post.topic)
|
||||||
action.merge!(topic_id: pa.related_post.topic_id,
|
action.merge!(topic_id: pa.related_post.topic_id,
|
||||||
slug: pa.related_post.topic.slug,
|
slug: pa.related_post.topic.slug,
|
||||||
|
@ -345,7 +346,7 @@ class PostAction < ActiveRecord::Base
|
||||||
|
|
||||||
# TODO add serializer so we can skip this
|
# TODO add serializer so we can skip this
|
||||||
posts.map!(&:marshal_dump)
|
posts.map!(&:marshal_dump)
|
||||||
[posts, User.select([:id, :username, :email]).where(id: users.to_a).to_a]
|
[posts, User.where(id: users.to_a).to_a]
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
|
@ -235,6 +235,9 @@ class SiteSetting < ActiveRecord::Base
|
||||||
|
|
||||||
client_setting(:relative_date_duration, 14)
|
client_setting(:relative_date_duration, 14)
|
||||||
|
|
||||||
|
setting(:delete_user_max_age, 7)
|
||||||
|
setting(:delete_all_posts_max, 10)
|
||||||
|
|
||||||
def self.generate_api_key!
|
def self.generate_api_key!
|
||||||
self.api_key = SecureRandom.hex(32)
|
self.api_key = SecureRandom.hex(32)
|
||||||
end
|
end
|
||||||
|
|
|
@ -8,10 +8,12 @@ class AdminDetailedUserSerializer < AdminUserSerializer
|
||||||
:can_impersonate,
|
:can_impersonate,
|
||||||
:like_count,
|
:like_count,
|
||||||
:post_count,
|
:post_count,
|
||||||
|
:topic_count,
|
||||||
:flags_given_count,
|
:flags_given_count,
|
||||||
:flags_received_count,
|
:flags_received_count,
|
||||||
:private_topics_count,
|
:private_topics_count,
|
||||||
:can_delete_all_posts
|
:can_delete_all_posts,
|
||||||
|
:can_be_deleted
|
||||||
|
|
||||||
has_one :approved_by, serializer: BasicUserSerializer, embed: :objects
|
has_one :approved_by, serializer: BasicUserSerializer, embed: :objects
|
||||||
|
|
||||||
|
@ -35,8 +37,16 @@ class AdminDetailedUserSerializer < AdminUserSerializer
|
||||||
scope.can_delete_all_posts?(object)
|
scope.can_delete_all_posts?(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def can_be_deleted
|
||||||
|
scope.can_delete_user?(object)
|
||||||
|
end
|
||||||
|
|
||||||
def moderator
|
def moderator
|
||||||
object.moderator
|
object.moderator
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def topic_count
|
||||||
|
object.topics.count
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -925,6 +925,9 @@ en:
|
||||||
action: 'Flag Post'
|
action: 'Flag Post'
|
||||||
take_action: "Take Action"
|
take_action: "Take Action"
|
||||||
notify_action: 'Notify'
|
notify_action: 'Notify'
|
||||||
|
delete_spammer: "Delete Spammer"
|
||||||
|
delete_confirm: "You are about to delete <b>%{posts}</b> posts and <b>%{topics}</b> topics from this user, remove their account, and add their email address <b>%{email}</b> to a permanent block list. Are you sure this user is really a spammer?"
|
||||||
|
yes_delete_spammer: "Yes, Delete Spammer"
|
||||||
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."
|
||||||
custom_placeholder_notify_moderators: "Why does this post require moderator attention? Let us know specifically what you are concerned about, and provide relevant links where possible."
|
custom_placeholder_notify_moderators: "Why does this post require moderator attention? Let us know specifically what you are concerned about, and provide relevant links where possible."
|
||||||
|
@ -1072,6 +1075,7 @@ en:
|
||||||
disagree_unhide_title: "Remove any flags from this post and make the post visible again"
|
disagree_unhide_title: "Remove any flags from this post and make the post visible again"
|
||||||
disagree: "Disagree"
|
disagree: "Disagree"
|
||||||
disagree_title: "Disagree with flag, remove any flags from this post"
|
disagree_title: "Disagree with flag, remove any flags from this post"
|
||||||
|
delete_spammer_title: "Delete the user and all its posts and topics."
|
||||||
|
|
||||||
flagged_by: "Flagged by"
|
flagged_by: "Flagged by"
|
||||||
error: "Something went wrong"
|
error: "Something went wrong"
|
||||||
|
@ -1234,8 +1238,8 @@ en:
|
||||||
delete: "Delete User"
|
delete: "Delete User"
|
||||||
delete_forbidden: "This user can't be deleted because there are posts. Delete all this user's posts first."
|
delete_forbidden: "This user can't be deleted because there are posts. Delete all this user's posts first."
|
||||||
delete_confirm: "Are you SURE you want to permanently delete this user from the site? This action is permanent!"
|
delete_confirm: "Are you SURE you want to permanently delete this user from the site? This action is permanent!"
|
||||||
delete_and_block: "<strong>Yes</strong>, and <strong>block</strong> signups with the same email address"
|
delete_and_block: "<b>Yes</b>, and <b>block</b> signups with the same email address"
|
||||||
delete_dont_block: "<strong>Yes</strong>, and <strong>allow</strong> signups with the same email address"
|
delete_dont_block: "<b>Yes</b>, and <b>allow</b> signups with the same email address"
|
||||||
deleted: "The user was deleted."
|
deleted: "The user was deleted."
|
||||||
delete_failed: "There was an error deleting that user. Make sure all posts are deleted before trying to delete the user."
|
delete_failed: "There was an error deleting that user. Make sure all posts are deleted before trying to delete the user."
|
||||||
send_activation_email: "Send Activation Email"
|
send_activation_email: "Send Activation Email"
|
||||||
|
|
|
@ -659,6 +659,8 @@ en:
|
||||||
minimum_topics_similar: "How many topics need to exist in the database before similar topics are presented."
|
minimum_topics_similar: "How many topics need to exist in the database before similar topics are presented."
|
||||||
|
|
||||||
relative_date_duration: "Number of days after posting where post dates will be shown as relative instead of absolute. Examples: relative date: 7d, absolute date: 20 Feb"
|
relative_date_duration: "Number of days after posting where post dates will be shown as relative instead of absolute. Examples: relative date: 7d, absolute date: 20 Feb"
|
||||||
|
delete_user_max_age: "The maximum age of a user, in days, which can be deleted by an admin."
|
||||||
|
delete_all_posts_max: "The maximum number of posts that can be deleted at once with the Delete All Posts button. If a user has more than this many posts, the posts cannot all be deleted at once and the user can't be deleted."
|
||||||
|
|
||||||
|
|
||||||
notification_types:
|
notification_types:
|
||||||
|
|
|
@ -151,7 +151,7 @@ class Guardian
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_delete_user?(user)
|
def can_delete_user?(user)
|
||||||
can_administer?(user)
|
user && is_staff? && !user.admin? && user.created_at > SiteSetting.delete_user_max_age.to_i.days.ago
|
||||||
end
|
end
|
||||||
|
|
||||||
# Can we see who acted on a post in a particular way?
|
# Can we see who acted on a post in a particular way?
|
||||||
|
@ -203,7 +203,7 @@ class Guardian
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_delete_all_posts?(user)
|
def can_delete_all_posts?(user)
|
||||||
is_staff? && user.created_at >= 7.days.ago
|
is_staff? && user && !user.admin? && user.created_at >= 7.days.ago && user.post_count <= SiteSetting.delete_all_posts_max.to_i
|
||||||
end
|
end
|
||||||
|
|
||||||
def can_remove_allowed_users?(topic)
|
def can_remove_allowed_users?(topic)
|
||||||
|
|
|
@ -3,10 +3,10 @@ class UserDestroyer
|
||||||
|
|
||||||
class PostsExistError < RuntimeError; end
|
class PostsExistError < RuntimeError; end
|
||||||
|
|
||||||
def initialize(admin)
|
def initialize(staff)
|
||||||
@admin = admin
|
@staff = staff
|
||||||
raise Discourse::InvalidParameters.new('admin is nil') unless @admin and @admin.is_a?(User)
|
raise Discourse::InvalidParameters.new('staff user is nil') unless @staff and @staff.is_a?(User)
|
||||||
raise Discourse::InvalidAccess unless @admin.admin?
|
raise Discourse::InvalidAccess unless @staff.staff?
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns false if the user failed to be deleted.
|
# Returns false if the user failed to be deleted.
|
||||||
|
@ -17,7 +17,7 @@ class UserDestroyer
|
||||||
User.transaction do
|
User.transaction do
|
||||||
if opts[:delete_posts]
|
if opts[:delete_posts]
|
||||||
user.posts.each do |post|
|
user.posts.each do |post|
|
||||||
PostDestroyer.new(@admin, post).destroy
|
PostDestroyer.new(@staff, post).destroy
|
||||||
end
|
end
|
||||||
raise PostsExistError if user.reload.post_count != 0
|
raise PostsExistError if user.reload.post_count != 0
|
||||||
end
|
end
|
||||||
|
@ -28,7 +28,7 @@ class UserDestroyer
|
||||||
b.record_match! if b
|
b.record_match! if b
|
||||||
end
|
end
|
||||||
Post.with_deleted.where(user_id: user.id).update_all("nuked_user = true")
|
Post.with_deleted.where(user_id: user.id).update_all("nuked_user = true")
|
||||||
StaffActionLogger.new(@admin).log_user_deletion(user, opts.slice(:context))
|
StaffActionLogger.new(@staff).log_user_deletion(user, opts.slice(:context))
|
||||||
DiscourseHub.unregister_nickname(user.username) if SiteSetting.call_discourse_hub?
|
DiscourseHub.unregister_nickname(user.username) if SiteSetting.call_discourse_hub?
|
||||||
MessageBus.publish "/file-change", ["refresh"], user_ids: [user.id]
|
MessageBus.publish "/file-change", ["refresh"], user_ids: [user.id]
|
||||||
end
|
end
|
||||||
|
|
|
@ -990,7 +990,7 @@ describe Guardian do
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
context "can_delete_user?" do
|
describe "can_delete_user?" do
|
||||||
it "is false without a logged in user" do
|
it "is false without a logged in user" do
|
||||||
Guardian.new(nil).can_delete_user?(user).should be_false
|
Guardian.new(nil).can_delete_user?(user).should be_false
|
||||||
end
|
end
|
||||||
|
@ -1003,19 +1003,83 @@ describe Guardian do
|
||||||
Guardian.new(user).can_delete_user?(coding_horror).should be_false
|
Guardian.new(user).can_delete_user?(coding_horror).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is false for moderators" do
|
shared_examples "can_delete_user examples" do
|
||||||
Guardian.new(moderator).can_delete_user?(coding_horror).should be_false
|
let(:deletable_user) { Fabricate.build(:user, created_at: 5.minutes.ago) }
|
||||||
|
|
||||||
|
it "is true if user is not an admin and is not too old" do
|
||||||
|
Guardian.new(actor).can_delete_user?(deletable_user).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is false if user is an admin" do
|
||||||
|
Guardian.new(actor).can_delete_user?(another_admin).should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is false if user is too old" do
|
||||||
|
SiteSetting.stubs(:delete_user_max_age).returns(7)
|
||||||
|
Guardian.new(actor).can_delete_user?(Fabricate(:user, created_at: 8.days.ago)).should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "for moderators" do
|
||||||
|
let(:actor) { moderator }
|
||||||
|
include_examples "can_delete_user examples"
|
||||||
end
|
end
|
||||||
|
|
||||||
context "for admins" do
|
context "for admins" do
|
||||||
it "is true if user has posts" do # UserDestroyer is responsible for checking for posts
|
let(:actor) { admin }
|
||||||
user.stubs(:post_count).returns(1)
|
include_examples "can_delete_user examples"
|
||||||
Guardian.new(admin).can_delete_user?(user).should be_true
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it "is true if user has no posts" do
|
describe "can_delete_all_posts?" do
|
||||||
Guardian.new(admin).can_delete_user?(user).should be_true
|
it "is false without a logged in user" do
|
||||||
|
Guardian.new(nil).can_delete_all_posts?(user).should be_false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "is false without a user to look at" do
|
||||||
|
Guardian.new(admin).can_delete_all_posts?(nil).should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is false for regular users" do
|
||||||
|
Guardian.new(user).can_delete_all_posts?(coding_horror).should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
shared_examples "can_delete_all_posts examples" do
|
||||||
|
it "is true if user is newer than 7 days old" do
|
||||||
|
Guardian.new(actor).can_delete_all_posts?(Fabricate.build(:user, created_at: 6.days.ago)).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is false if user is older than 7 days old" do
|
||||||
|
Guardian.new(actor).can_delete_all_posts?(Fabricate.build(:user, created_at: 8.days.ago)).should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is false if user is an admin" do
|
||||||
|
Guardian.new(actor).can_delete_all_posts?(admin).should be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is true if number of posts is small" do
|
||||||
|
u = Fabricate.build(:user, created_at: 1.day.ago)
|
||||||
|
u.stubs(:post_count).returns(1)
|
||||||
|
SiteSetting.stubs(:delete_all_posts_max).returns(10)
|
||||||
|
Guardian.new(actor).can_delete_all_posts?(u).should be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is false if number of posts is not small" do
|
||||||
|
u = Fabricate.build(:user, created_at: 1.day.ago)
|
||||||
|
u.stubs(:post_count).returns(11)
|
||||||
|
SiteSetting.stubs(:delete_all_posts_max).returns(10)
|
||||||
|
Guardian.new(actor).can_delete_all_posts?(u).should be_false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "for moderators" do
|
||||||
|
let(:actor) { moderator }
|
||||||
|
include_examples "can_delete_all_posts examples"
|
||||||
|
end
|
||||||
|
|
||||||
|
context "for admins" do
|
||||||
|
let(:actor) { admin }
|
||||||
|
include_examples "can_delete_all_posts examples"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -20,8 +20,8 @@ describe UserDestroyer do
|
||||||
expect { UserDestroyer.new( Fabricate(:user) ) }.to raise_error(Discourse::InvalidAccess)
|
expect { UserDestroyer.new( Fabricate(:user) ) }.to raise_error(Discourse::InvalidAccess)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'raises an error when user is a moderator' do
|
it 'returns an instance of UserDestroyer when user is a moderator' do
|
||||||
expect { UserDestroyer.new( Fabricate(:moderator) ) }.to raise_error(Discourse::InvalidAccess)
|
UserDestroyer.new( Fabricate(:moderator) ).should be_a(UserDestroyer)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns an instance of UserDestroyer when user is an admin' do
|
it 'returns an instance of UserDestroyer when user is an admin' do
|
||||||
|
|
55
test/javascripts/controllers/flag_controller_test.js
Normal file
55
test/javascripts/controllers/flag_controller_test.js
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
var buildPost = function(args) {
|
||||||
|
return Discourse.Post.create(_.merge({
|
||||||
|
id: 1,
|
||||||
|
can_delete: true,
|
||||||
|
version: 1
|
||||||
|
}, args || {}));
|
||||||
|
};
|
||||||
|
|
||||||
|
var buildAdminUser = function(args) {
|
||||||
|
return Discourse.AdminUser.create(_.merge({
|
||||||
|
id: 11,
|
||||||
|
username: 'urist'
|
||||||
|
}, args || {}));
|
||||||
|
};
|
||||||
|
|
||||||
|
module("Discourse.FlagController canDeleteSpammer");
|
||||||
|
|
||||||
|
test("canDeleteSpammer not staff", function(){
|
||||||
|
var flagController = controllerFor('flag', buildPost());
|
||||||
|
this.stub(Discourse.User, 'current').returns(false); // Discourse.User.current('staff') returns false
|
||||||
|
flagController.set('selected', Discourse.PostActionType.create({name_key: 'spam'}));
|
||||||
|
equal(flagController.get('canDeleteSpammer'), false, 'false if current user is not staff');
|
||||||
|
});
|
||||||
|
|
||||||
|
var canDeleteSpammer = function(test, postActionType, expected, testName) {
|
||||||
|
test.flagController.set('selected', Discourse.PostActionType.create({name_key: postActionType}));
|
||||||
|
equal(test.flagController.get('canDeleteSpammer'), expected, testName);
|
||||||
|
};
|
||||||
|
|
||||||
|
test("canDeleteSpammer spam not selected", function(){
|
||||||
|
this.stub(Discourse.User, 'current').returns(true); // Discourse.User.current('staff') returns true
|
||||||
|
this.flagController = controllerFor('flag', buildPost());
|
||||||
|
this.flagController.set('userDetails', buildAdminUser({can_delete_all_posts: true, can_be_deleted: true}));
|
||||||
|
canDeleteSpammer(this, 'off_topic', false, 'false if current user is staff, but selected is off_topic');
|
||||||
|
canDeleteSpammer(this, 'inappropriate', false, 'false if current user is staff, but selected is inappropriate');
|
||||||
|
canDeleteSpammer(this, 'notify_user', false, 'false if current user is staff, but selected is notify_user');
|
||||||
|
canDeleteSpammer(this, 'notify_moderators', false, 'false if current user is staff, but selected is notify_moderators');
|
||||||
|
});
|
||||||
|
|
||||||
|
test("canDeleteSpammer spam selected", function(){
|
||||||
|
this.stub(Discourse.User, 'current').returns(true);
|
||||||
|
this.flagController = controllerFor('flag', buildPost());
|
||||||
|
|
||||||
|
this.flagController.set('userDetails', buildAdminUser({can_delete_all_posts: true, can_be_deleted: true}));
|
||||||
|
canDeleteSpammer(this, 'spam', true, 'true if current user is staff, selected is spam, posts and user can be deleted');
|
||||||
|
|
||||||
|
this.flagController.set('userDetails', buildAdminUser({can_delete_all_posts: false, can_be_deleted: true}));
|
||||||
|
canDeleteSpammer(this, 'spam', false, 'false if current user is staff, selected is spam, posts cannot be deleted');
|
||||||
|
|
||||||
|
this.flagController.set('userDetails', buildAdminUser({can_delete_all_posts: true, can_be_deleted: false}));
|
||||||
|
canDeleteSpammer(this, 'spam', false, 'false if current user is staff, selected is spam, user cannot be deleted');
|
||||||
|
|
||||||
|
this.flagController.set('userDetails', buildAdminUser({can_delete_all_posts: false, can_be_deleted: false}));
|
||||||
|
canDeleteSpammer(this, 'spam', false, 'false if current user is staff, selected is spam, user cannot be deleted');
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user