Page MenuHomePhabricator

D19271.diff
No OneTemporary

D19271.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -4020,6 +4020,7 @@
'PhabricatorRepositoryManagementRefsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementRefsWorkflow.php',
'PhabricatorRepositoryManagementReparseWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementReparseWorkflow.php',
'PhabricatorRepositoryManagementThawWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementThawWorkflow.php',
+ 'PhabricatorRepositoryManagementUnpublishWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementUnpublishWorkflow.php',
'PhabricatorRepositoryManagementUpdateWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementUpdateWorkflow.php',
'PhabricatorRepositoryManagementWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementWorkflow.php',
'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'applications/repository/worker/commitchangeparser/PhabricatorRepositoryMercurialCommitChangeParserWorker.php',
@@ -8563,7 +8564,10 @@
'PhabricatorPolicyInterface',
'PhabricatorMarkupInterface',
),
- 'PhabricatorFeedStoryData' => 'PhabricatorFeedDAO',
+ 'PhabricatorFeedStoryData' => array(
+ 'PhabricatorFeedDAO',
+ 'PhabricatorDestructibleInterface',
+ ),
'PhabricatorFeedStoryNotification' => 'PhabricatorFeedDAO',
'PhabricatorFeedStoryPublisher' => 'Phobject',
'PhabricatorFeedStoryReference' => 'PhabricatorFeedDAO',
@@ -9814,6 +9818,7 @@
'PhabricatorRepositoryManagementRefsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementReparseWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementThawWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
+ 'PhabricatorRepositoryManagementUnpublishWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementUpdateWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
'PhabricatorRepositoryManagementWorkflow' => 'PhabricatorManagementWorkflow',
'PhabricatorRepositoryMercurialCommitChangeParserWorker' => 'PhabricatorRepositoryCommitChangeParserWorker',
diff --git a/src/applications/feed/storage/PhabricatorFeedStoryData.php b/src/applications/feed/storage/PhabricatorFeedStoryData.php
--- a/src/applications/feed/storage/PhabricatorFeedStoryData.php
+++ b/src/applications/feed/storage/PhabricatorFeedStoryData.php
@@ -1,6 +1,8 @@
<?php
-final class PhabricatorFeedStoryData extends PhabricatorFeedDAO {
+final class PhabricatorFeedStoryData
+ extends PhabricatorFeedDAO
+ implements PhabricatorDestructibleInterface {
protected $phid;
@@ -66,4 +68,30 @@
return idx($this->storyData, $key, $default);
}
+
+/* -( PhabricatorDestructibleInterface )----------------------------------- */
+
+
+ public function destroyObjectPermanently(
+ PhabricatorDestructionEngine $engine) {
+
+ $this->openTransaction();
+ $conn = $this->establishConnection('w');
+
+ queryfx(
+ $conn,
+ 'DELETE FROM %T WHERE chronologicalKey = %s',
+ id(new PhabricatorFeedStoryNotification())->getTableName(),
+ $this->getChronologicalKey());
+
+ queryfx(
+ $conn,
+ 'DELETE FROM %T WHERE chronologicalKey = %s',
+ id(new PhabricatorFeedStoryReference())->getTableName(),
+ $this->getChronologicalKey());
+
+ $this->delete();
+ $this->saveTransaction();
+ }
+
}
diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementUnpublishWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementUnpublishWorkflow.php
new file mode 100644
--- /dev/null
+++ b/src/applications/repository/management/PhabricatorRepositoryManagementUnpublishWorkflow.php
@@ -0,0 +1,273 @@
+<?php
+
+final class PhabricatorRepositoryManagementUnpublishWorkflow
+ extends PhabricatorRepositoryManagementWorkflow {
+
+ protected function didConstruct() {
+ $this
+ ->setName('unpublish')
+ ->setExamples(
+ '**unpublish** [__options__] __repository__')
+ ->setSynopsis(
+ pht(
+ 'Unpublish all feed stories and notifications that a repository '.
+ 'has generated. Keep expectations low; can not rewind time.'))
+ ->setArguments(
+ array(
+ array(
+ 'name' => 'force',
+ 'help' => pht('Do not prompt for confirmation.'),
+ ),
+ array(
+ 'name' => 'dry-run',
+ 'help' => pht('Do not perform any writes.'),
+ ),
+ array(
+ 'name' => 'repositories',
+ 'wildcard' => true,
+ ),
+ ));
+ }
+
+ public function execute(PhutilArgumentParser $args) {
+ $viewer = $this->getViewer();
+ $is_force = $args->getArg('force');
+ $is_dry_run = $args->getArg('dry-run');
+
+ $repositories = $this->loadLocalRepositories($args, 'repositories');
+ if (count($repositories) !== 1) {
+ throw new PhutilArgumentUsageException(
+ pht('Specify exactly one repository to unpublish.'));
+ }
+ $repository = head($repositories);
+
+ if (!$is_force) {
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'This script will unpublish all feed stories and notifications '.
+ 'which a repository generated during import. This action can not '.
+ 'be undone.'));
+
+ $prompt = pht(
+ 'Permanently unpublish "%s"?',
+ $repository->getDisplayName());
+ if (!phutil_console_confirm($prompt)) {
+ throw new PhutilArgumentUsageException(
+ pht('User aborted workflow.'));
+ }
+ }
+
+ $commits = id(new DiffusionCommitQuery())
+ ->setViewer($viewer)
+ ->withRepositoryPHIDs(array($repository->getPHID()))
+ ->execute();
+
+ echo pht("Will unpublish %s commits.\n", count($commits));
+
+ foreach ($commits as $commit) {
+ $this->unpublishCommit($commit, $is_dry_run);
+ }
+
+ return 0;
+ }
+
+ private function unpublishCommit(
+ PhabricatorRepositoryCommit $commit,
+ $is_dry_run) {
+ $viewer = $this->getViewer();
+
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'Unpublishing commit "%s".',
+ $commit->getMonogram()));
+
+ $stories = id(new PhabricatorFeedQuery())
+ ->setViewer($viewer)
+ ->withFilterPHIDs(array($commit->getPHID()))
+ ->execute();
+
+ if ($stories) {
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'Found %s feed storie(s).',
+ count($stories)));
+
+ if (!$is_dry_run) {
+ $engine = new PhabricatorDestructionEngine();
+ foreach ($stories as $story) {
+ $story_data = $story->getStoryData();
+ $engine->destroyObject($story_data);
+ }
+
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'Destroyed %s feed storie(s).',
+ count($stories)));
+ }
+ }
+
+ $edge_types = array(
+ PhabricatorObjectMentionsObjectEdgeType::EDGECONST => true,
+ DiffusionCommitHasTaskEdgeType::EDGECONST => true,
+ DiffusionCommitHasRevisionEdgeType::EDGECONST => true,
+ DiffusionCommitRevertsCommitEdgeType::EDGECONST => true,
+ );
+
+ $query = id(new PhabricatorEdgeQuery())
+ ->withSourcePHIDs(array($commit->getPHID()))
+ ->withEdgeTypes(array_keys($edge_types));
+ $edges = $query->execute();
+
+ foreach ($edges[$commit->getPHID()] as $type => $edge_list) {
+ foreach ($edge_list as $edge) {
+ $dst = $edge['dst'];
+
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'Commit "%s" has edge of type "%s" to object "%s".',
+ $commit->getMonogram(),
+ $type,
+ $dst));
+
+ $object = id(new PhabricatorObjectQuery())
+ ->setViewer($viewer)
+ ->withPHIDs(array($dst))
+ ->executeOne();
+ if ($object) {
+ if ($object instanceof PhabricatorApplicationTransactionInterface) {
+ $this->unpublishEdgeTransaction(
+ $commit,
+ $type,
+ $object,
+ $is_dry_run);
+ }
+ }
+ }
+ }
+ }
+
+ private function unpublishEdgeTransaction(
+ $src,
+ $type,
+ PhabricatorApplicationTransactionInterface $dst,
+ $is_dry_run) {
+ $viewer = $this->getViewer();
+
+ $query = PhabricatorApplicationTransactionQuery::newQueryForObject($dst)
+ ->setViewer($viewer)
+ ->withObjectPHIDs(array($dst->getPHID()));
+
+ $xactions = id(clone $query)
+ ->withTransactionTypes(
+ array(
+ PhabricatorTransactions::TYPE_EDGE,
+ ))
+ ->execute();
+
+ $type_obj = PhabricatorEdgeType::getByConstant($type);
+ $inverse_type = $type_obj->getInverseEdgeConstant();
+
+ $engine = new PhabricatorDestructionEngine();
+ foreach ($xactions as $xaction) {
+ $edge_type = $xaction->getMetadataValue('edge:type');
+ if ($edge_type != $inverse_type) {
+ // Some other type of edge was edited.
+ continue;
+ }
+
+ $record = PhabricatorEdgeChangeRecord::newFromTransaction($xaction);
+ $changed = $record->getChangedPHIDs();
+ if ($changed !== array($src->getPHID())) {
+ // Affected objects were not just the object we're unpublishing.
+ continue;
+ }
+
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'Found edge transaction "%s" on object "%s" for type "%s".',
+ $xaction->getPHID(),
+ $dst->getPHID(),
+ $type));
+
+ if (!$is_dry_run) {
+ $engine->destroyObject($xaction);
+
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'Destroyed transaction "%s" on object "%s".',
+ $xaction->getPHID(),
+ $dst->getPHID()));
+ }
+ }
+
+ if ($type === DiffusionCommitHasTaskEdgeType::EDGECONST) {
+ $xactions = id(clone $query)
+ ->withTransactionTypes(
+ array(
+ ManiphestTaskStatusTransaction::TRANSACTIONTYPE,
+ ))
+ ->execute();
+
+ if ($xactions) {
+ foreach ($xactions as $xaction) {
+ $metadata = $xaction->getMetadata();
+ if (idx($metadata, 'commitPHID') === $src->getPHID()) {
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'MANUAL Task "%s" was likely closed improperly by "%s".',
+ $dst->getMonogram(),
+ $src->getMonogram()));
+ }
+ }
+ }
+ }
+
+ if ($type === DiffusionCommitHasRevisionEdgeType::EDGECONST) {
+ $xactions = id(clone $query)
+ ->withTransactionTypes(
+ array(
+ DifferentialRevisionCloseTransaction::TRANSACTIONTYPE,
+ ))
+ ->execute();
+
+ if ($xactions) {
+ foreach ($xactions as $xaction) {
+ $metadata = $xaction->getMetadata();
+ if (idx($metadata, 'isCommitClose')) {
+ if (idx($metadata, 'commitPHID') === $src->getPHID()) {
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'MANUAL Revision "%s" was likely closed improperly by "%s".',
+ $dst->getMonogram(),
+ $src->getMonogram()));
+ }
+ }
+ }
+ }
+ }
+
+ if (!$is_dry_run) {
+ id(new PhabricatorEdgeEditor())
+ ->removeEdge($src->getPHID(), $type, $dst->getPHID())
+ ->save();
+ echo tsprintf(
+ "%s\n",
+ pht(
+ 'Destroyed edge of type "%s" between "%s" and "%s".',
+ $type,
+ $src->getPHID(),
+ $dst->getPHID()));
+ }
+ }
+
+
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Mar 31, 9:39 AM (3 d, 9 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7708770
Default Alt Text
D19271.diff (11 KB)

Event Timeline