Disable as Spam - admin-only option to handle spammers

This implements a convenient way to handle spam users, mimicking the steps the
admins already do manually:

Performed tasks:
* Disable Account
* Real Name -> "spam"
* Title -> ""
* Icon → ""
* Blurb → ""
* Profile Picture → default

This is disabling the account using the same functionality as the "Disable User"
button. And on top of that it replaces the account real name with "spam" and
wipe the other personal information.

Note that this doesn't delete any posts from the user.

Reviewers: sergey

https://developer.blender.org/D11904
This commit is contained in:
2021-07-13 19:20:28 +02:00
parent eced78a591
commit 5aabb434b1
6 changed files with 196 additions and 0 deletions

View File

@@ -2166,6 +2166,7 @@ phutil_register_library_map(array(
'PasteSearchConduitAPIMethod' => 'applications/paste/conduit/PasteSearchConduitAPIMethod.php',
'PeopleBrowseUserDirectoryCapability' => 'applications/people/capability/PeopleBrowseUserDirectoryCapability.php',
'PeopleCreateUsersCapability' => 'applications/people/capability/PeopleCreateUsersCapability.php',
'PeopleDisableSpamUsersCapability' => 'applications/people/capability/PeopleDisableSpamUsersCapability.php',
'PeopleDisableUsersCapability' => 'applications/people/capability/PeopleDisableUsersCapability.php',
'PeopleHovercardEngineExtension' => 'applications/people/engineextension/PeopleHovercardEngineExtension.php',
'PeopleMainMenuBarExtension' => 'applications/people/engineextension/PeopleMainMenuBarExtension.php',
@@ -4167,6 +4168,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleRenameController' => 'applications/people/controller/PhabricatorPeopleRenameController.php',
'PhabricatorPeopleRevisionsProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleRevisionsProfileMenuItem.php',
'PhabricatorPeopleSearchEngine' => 'applications/people/query/PhabricatorPeopleSearchEngine.php',
'PhabricatorPeopleSpamController' => 'applications/people/controller/PhabricatorPeopleSpamController.php',
'PhabricatorPeopleTasksProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleTasksProfileMenuItem.php',
'PhabricatorPeopleTestDataGenerator' => 'applications/people/lipsum/PhabricatorPeopleTestDataGenerator.php',
'PhabricatorPeopleTransactionQuery' => 'applications/people/query/PhabricatorPeopleTransactionQuery.php',
@@ -5059,6 +5061,7 @@ phutil_register_library_map(array(
'PhabricatorUserCustomFieldNumericIndex' => 'applications/people/storage/PhabricatorUserCustomFieldNumericIndex.php',
'PhabricatorUserCustomFieldStringIndex' => 'applications/people/storage/PhabricatorUserCustomFieldStringIndex.php',
'PhabricatorUserDAO' => 'applications/people/storage/PhabricatorUserDAO.php',
'PhabricatorUserDisableSpamTransaction' => 'applications/people/xaction/PhabricatorUserDisableSpamTransaction.php',
'PhabricatorUserDisableTransaction' => 'applications/people/xaction/PhabricatorUserDisableTransaction.php',
'PhabricatorUserEditEngine' => 'applications/people/editor/PhabricatorUserEditEngine.php',
'PhabricatorUserEditor' => 'applications/people/editor/PhabricatorUserEditor.php',
@@ -8473,6 +8476,7 @@ phutil_register_library_map(array(
'PasteSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'PeopleBrowseUserDirectoryCapability' => 'PhabricatorPolicyCapability',
'PeopleCreateUsersCapability' => 'PhabricatorPolicyCapability',
'PeopleDisableSpamUsersCapability' => 'PhabricatorPolicyCapability',
'PeopleDisableUsersCapability' => 'PhabricatorPolicyCapability',
'PeopleHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
'PeopleMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
@@ -10790,6 +10794,7 @@ phutil_register_library_map(array(
'PhabricatorPeopleRenameController' => 'PhabricatorPeopleController',
'PhabricatorPeopleRevisionsProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorPeopleSpamController' => 'PhabricatorPeopleController',
'PhabricatorPeopleTasksProfileMenuItem' => 'PhabricatorProfileMenuItem',
'PhabricatorPeopleTestDataGenerator' => 'PhabricatorTestDataGenerator',
'PhabricatorPeopleTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
@@ -11860,6 +11865,7 @@ phutil_register_library_map(array(
'PhabricatorUserCustomFieldNumericIndex' => 'PhabricatorCustomFieldNumericIndexStorage',
'PhabricatorUserCustomFieldStringIndex' => 'PhabricatorCustomFieldStringIndexStorage',
'PhabricatorUserDAO' => 'PhabricatorLiskDAO',
'PhabricatorUserDisableSpamTransaction' => 'PhabricatorUserTransactionType',
'PhabricatorUserDisableTransaction' => 'PhabricatorUserTransactionType',
'PhabricatorUserEditEngine' => 'PhabricatorEditEngine',
'PhabricatorUserEditor' => 'PhabricatorEditor',

View File

@@ -58,6 +58,7 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication {
=> 'PhabricatorPeopleDisableController',
'(?P<via>disable)/(?P<id>[1-9]\d*)/'
=> 'PhabricatorPeopleDisableController',
'spam/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleSpamController',
'empower/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleEmpowerController',
'delete/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleDeleteController',
'rename/(?P<id>[1-9]\d*)/' => 'PhabricatorPeopleRenameController',
@@ -99,6 +100,9 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication {
PeopleDisableUsersCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
PeopleDisableSpamUsersCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
PeopleBrowseUserDirectoryCapability::CAPABILITY => array(),
);
}

View File

@@ -0,0 +1,16 @@
<?php
final class PeopleDisableSpamUsersCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'people.disable_spam.users';
public function getCapabilityName() {
return pht('Can Disable Spammers');
}
public function describeCapabilityRejection() {
return pht('You do not have permission to handle spam users.');
}
}

View File

@@ -93,6 +93,10 @@ final class PhabricatorPeopleProfileManageController
PeopleDisableUsersCapability::CAPABILITY);
$can_disable = ($has_disable && !$is_self);
$has_disable_spam_capability = $this->hasApplicationCapability(
PeopleDisableSpamUsersCapability::CAPABILITY);
$can_disable_spam = ($has_disable_spam_capability && !$is_self);
$id = $user->getID();
$welcome_engine = id(new PhabricatorPeopleWelcomeMailEngine())
@@ -193,6 +197,14 @@ final class PhabricatorPeopleProfileManageController
->setWorkflow(true)
->setHref($this->getApplicationURI('disable/'.$id.'/')));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-shield')
->setName(pht('Disable as Spam'))
->setDisabled(!$can_disable_spam)
->setWorkflow(true)
->setHref($this->getApplicationURI('spam/'.$id.'/')));
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-times')

View File

@@ -0,0 +1,90 @@
<?php
final class PhabricatorPeopleSpamController
extends PhabricatorPeopleController {
public function shouldRequireAdmin() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
if (!$user) {
return new Aphront404Response();
}
$this->requireApplicationCapability(
PeopleDisableUsersCapability::CAPABILITY);
$actor = $viewer;
$done_uri = $this->getApplicationURI("manage/{$id}/");
if ($viewer->getPHID() == $user->getPHID()) {
return $this->newDialog()
->setTitle(pht('Something Stays Your Hand'))
->appendParagraph(
pht(
'Try as you might, you find you can not disable your own account.'))
->addCancelButton($done_uri, pht('Curses!'));
}
if ($request->isFormPost()) {
// Disable the account.
if (!$user->getIsDisabled()) {
$xactions = array();
$xactions[] = id(new PhabricatorUserTransaction())
->setTransactionType(PhabricatorUserDisableTransaction::TRANSACTIONTYPE)
->setNewValue(true);
id(new PhabricatorUserTransactionEditor())
->setActor($actor)
->setActingAsPHID($viewer->getPHID())
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->applyTransactions($user, $xactions);
}
// Set profile info to spam and blank to everything else.
{
$xactions = array();
$xactions[] = id(new PhabricatorUserTransaction())
->setTransactionType(PhabricatorUserDisableSpamTransaction::TRANSACTIONTYPE);
id(new PhabricatorUserTransactionEditor())
->setActor($actor)
->setActingAsPHID($viewer->getPHID())
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->applyTransactions($user, $xactions);
}
return id(new AphrontRedirectResponse())->setURI($done_uri);
}
$title = pht('Disable as Spam?');
$short_title = pht('Disable as Spam');
$body = pht(
'Is %s\'s profile spam?<br />All the profile info will be erased and '.
'they will no longer be able to access Phabricator.',
phutil_tag('strong', array(), $user->getUsername()));
$submit = pht('Disable as Spam');
return $this->newDialog()
->setTitle($title)
->setShortTitle($short_title)
->appendParagraph($body)
->addCancelButton($done_uri)
->addSubmitButton($submit);
}
}

View File

@@ -0,0 +1,68 @@
<?php
final class PhabricatorUserDisableSpamTransaction
extends PhabricatorUserTransactionType {
const TRANSACTIONTYPE = 'user.disable_spam';
public function generateOldValue($object) {
$user = $object;
return (string)$user->getRealName();
}
public function generateNewValue($object, $value) {
// Deliberately not using 'spam'.
// This way we can use this button even for accounts that
// have been already manually renamed to 'spam'.
// Otherwise when the name clash with the existing name
// none of the changes happens.
return 'disabled_spam';
}
public function applyInternalEffects($object, $value) {
$user = $object;
$user->setRealName('spam');
$profile = $user->loadUserProfile();
$profile->setBlurb('');
$profile->setTitle('');
$profile->setIcon('');
$file = PhabricatorFile::loadBuiltin($user, 'profile.png');
$user->setProfileImagePHID($file->getPHID());
}
public function getTitle() {
return pht(
'%s set this user as spam.',
$this->renderAuthor());
}
public function shouldHideForFeed() {
// Don't publish feed stories about handling spam.
return true;
}
public function validateTransactions($object, array $xactions) {
$errors = array();
foreach ($xactions as $xaction) {
// You must have the "Can Disable Spam Users" permission to disable a user as spam.
$this->requireApplicationCapability(
PeopleDisableSpamUsersCapability::CAPABILITY);
if ($this->getActingAsPHID() === $object->getPHID()) {
$errors[] = $this->newInvalidError(
pht('You can not disable your own account as spam.'));
}
}
return $errors;
}
public function getRequiredCapabilities(
$object,
PhabricatorApplicationTransaction $xaction) {
return null;
}
}