From 5aabb434b14b9292b2491555dc9472b44150b88d Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Tue, 13 Jul 2021 19:20:28 +0200 Subject: [PATCH] Disable as Spam - admin-only option to handle spammers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/__phutil_library_map__.php | 6 ++ .../PhabricatorPeopleApplication.php | 4 + .../PeopleDisableSpamUsersCapability.php | 16 ++++ ...abricatorPeopleProfileManageController.php | 12 +++ .../PhabricatorPeopleSpamController.php | 90 +++++++++++++++++++ .../PhabricatorUserDisableSpamTransaction.php | 68 ++++++++++++++ 6 files changed, 196 insertions(+) create mode 100644 src/applications/people/capability/PeopleDisableSpamUsersCapability.php create mode 100644 src/applications/people/controller/PhabricatorPeopleSpamController.php create mode 100644 src/applications/people/xaction/PhabricatorUserDisableSpamTransaction.php diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index bbe89e01d4..98536ce171 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -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', diff --git a/src/applications/people/application/PhabricatorPeopleApplication.php b/src/applications/people/application/PhabricatorPeopleApplication.php index 86c03fdf4b..24e548c66f 100644 --- a/src/applications/people/application/PhabricatorPeopleApplication.php +++ b/src/applications/people/application/PhabricatorPeopleApplication.php @@ -58,6 +58,7 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication { => 'PhabricatorPeopleDisableController', '(?Pdisable)/(?P[1-9]\d*)/' => 'PhabricatorPeopleDisableController', + 'spam/(?P[1-9]\d*)/' => 'PhabricatorPeopleSpamController', 'empower/(?P[1-9]\d*)/' => 'PhabricatorPeopleEmpowerController', 'delete/(?P[1-9]\d*)/' => 'PhabricatorPeopleDeleteController', 'rename/(?P[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(), ); } diff --git a/src/applications/people/capability/PeopleDisableSpamUsersCapability.php b/src/applications/people/capability/PeopleDisableSpamUsersCapability.php new file mode 100644 index 0000000000..5cb8dbc026 --- /dev/null +++ b/src/applications/people/capability/PeopleDisableSpamUsersCapability.php @@ -0,0 +1,16 @@ +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') diff --git a/src/applications/people/controller/PhabricatorPeopleSpamController.php b/src/applications/people/controller/PhabricatorPeopleSpamController.php new file mode 100644 index 0000000000..2bcb9f609c --- /dev/null +++ b/src/applications/people/controller/PhabricatorPeopleSpamController.php @@ -0,0 +1,90 @@ +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?
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); + } + +} diff --git a/src/applications/people/xaction/PhabricatorUserDisableSpamTransaction.php b/src/applications/people/xaction/PhabricatorUserDisableSpamTransaction.php new file mode 100644 index 0000000000..9073696a8f --- /dev/null +++ b/src/applications/people/xaction/PhabricatorUserDisableSpamTransaction.php @@ -0,0 +1,68 @@ +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; + } +}