Path: blob/master/src/applications/audit/management/PhabricatorAuditManagementDeleteWorkflow.php
12256 views
<?php12final class PhabricatorAuditManagementDeleteWorkflow3extends PhabricatorAuditManagementWorkflow {45protected function didConstruct() {6$this7->setName('delete')8->setExamples('**delete** [--dry-run] ...')9->setSynopsis(pht('Delete audit requests matching parameters.'))10->setArguments(11array(12array(13'name' => 'dry-run',14'help' => pht(15'Show what would be deleted, but do not actually delete '.16'anything.'),17),18array(19'name' => 'users',20'param' => 'names',21'help' => pht('Select only audits by a given list of users.'),22),23array(24'name' => 'repositories',25'param' => 'repos',26'help' => pht(27'Select only audits in a given list of repositories.'),28),29array(30'name' => 'commits',31'param' => 'commits',32'help' => pht('Select only audits for the given commits.'),33),34array(35'name' => 'min-commit-date',36'param' => 'date',37'help' => pht(38'Select only audits for commits on or after the given date.'),39),40array(41'name' => 'max-commit-date',42'param' => 'date',43'help' => pht(44'Select only audits for commits on or before the given date.'),45),46array(47'name' => 'status',48'param' => 'status',49'help' => pht(50'Select only audits in the given status. By default, '.51'only open audits are selected.'),52),53array(54'name' => 'ids',55'param' => 'ids',56'help' => pht('Select only audits with the given IDs.'),57),58));59}6061public function execute(PhutilArgumentParser $args) {62$viewer = $this->getViewer();63$users = $this->loadUsers($args->getArg('users'));64$repos = $this->loadRepos($args->getArg('repositories'));65$commits = $this->loadCommits($args->getArg('commits'));66$ids = $this->parseList($args->getArg('ids'));6768$status = $args->getArg('status');6970$min_date = $this->loadDate($args->getArg('min-commit-date'));71$max_date = $this->loadDate($args->getArg('max-commit-date'));72if ($min_date && $max_date && ($min_date > $max_date)) {73throw new PhutilArgumentUsageException(74pht('Specified maximum date must come after specified minimum date.'));75}7677$is_dry_run = $args->getArg('dry-run');7879$query = id(new DiffusionCommitQuery())80->setViewer($this->getViewer())81->needAuditRequests(true);8283if ($status) {84$query->withStatuses(array($status));85}8687$id_map = array();88if ($ids) {89$id_map = array_fuse($ids);90$query->withAuditIDs($ids);91}9293if ($repos) {94$query->withRepositoryIDs(mpull($repos, 'getID'));9596// See T13457. If we're iterating over commits in a single large97// repository, the lack of a "<repositoryID, [id]>" key can slow things98// down. Iterate in a specific order to use a key which is present99// on the table ("<repositoryID, epoch, [id]>").100$query->setOrderVector(array('-epoch', '-id'));101}102103$auditor_map = array();104if ($users) {105$auditor_map = array_fuse(mpull($users, 'getPHID'));106$query->withAuditorPHIDs($auditor_map);107}108109if ($commits) {110$query->withPHIDs(mpull($commits, 'getPHID'));111}112113$commit_iterator = id(new PhabricatorQueryIterator($query));114115// See T13457. We may be examining many commits; each commit is small so116// we can safely increase the page size to improve performance a bit.117$commit_iterator->setPageSize(1000);118119$audits = array();120foreach ($commit_iterator as $commit) {121$commit_audits = $commit->getAudits();122foreach ($commit_audits as $key => $audit) {123if ($id_map && empty($id_map[$audit->getID()])) {124unset($commit_audits[$key]);125continue;126}127128if ($auditor_map && empty($auditor_map[$audit->getAuditorPHID()])) {129unset($commit_audits[$key]);130continue;131}132133if ($min_date && $commit->getEpoch() < $min_date) {134unset($commit_audits[$key]);135continue;136}137138if ($max_date && $commit->getEpoch() > $max_date) {139unset($commit_audits[$key]);140continue;141}142}143144if (!$commit_audits) {145continue;146}147148$handles = id(new PhabricatorHandleQuery())149->setViewer($viewer)150->withPHIDs(mpull($commit_audits, 'getAuditorPHID'))151->execute();152153foreach ($commit_audits as $audit) {154$audit_id = $audit->getID();155$status = $audit->getAuditRequestStatusObject();156157$description = sprintf(158'%10d %-16s %-16s %s: %s',159$audit_id,160$handles[$audit->getAuditorPHID()]->getName(),161$status->getStatusName(),162$commit->getRepository()->formatCommitName(163$commit->getCommitIdentifier()),164trim($commit->getSummary()));165166$audits[] = array(167'auditID' => $audit_id,168'commitPHID' => $commit->getPHID(),169'description' => $description,170);171}172}173174if (!$audits) {175echo tsprintf(176"%s\n",177pht('No audits match the query.'));178return 0;179}180181foreach ($audits as $audit_spec) {182echo tsprintf(183"%s\n",184$audit_spec['description']);185}186187if ($is_dry_run) {188echo tsprintf(189"%s\n",190pht('This is a dry run, so no changes will be made.'));191return 0;192}193194$message = pht(195'Really delete these %s audit(s)? They will be permanently deleted '.196'and can not be recovered.',197phutil_count($audits));198if (!phutil_console_confirm($message)) {199echo tsprintf(200"%s\n",201pht('User aborted the workflow.'));202return 1;203}204205$audits_by_commit = igroup($audits, 'commitPHID');206foreach ($audits_by_commit as $commit_phid => $audit_specs) {207$audit_ids = ipull($audit_specs, 'auditID');208209$audits = id(new PhabricatorRepositoryAuditRequest())->loadAllWhere(210'id IN (%Ld)',211$audit_ids);212213foreach ($audits as $audit) {214$id = $audit->getID();215216echo tsprintf(217"%s\n",218pht('Deleting audit %d...', $id));219220$audit->delete();221}222223$this->synchronizeCommitAuditState($commit_phid);224}225226return 0;227}228229private function loadUsers($users) {230$users = $this->parseList($users);231if (!$users) {232return null;233}234235$objects = id(new PhabricatorPeopleQuery())236->setViewer($this->getViewer())237->withUsernames($users)238->execute();239$objects = mpull($objects, null, 'getUsername');240241foreach ($users as $name) {242if (empty($objects[$name])) {243throw new PhutilArgumentUsageException(244pht('No such user with username "%s"!', $name));245}246}247248return $objects;249}250251private function parseList($list) {252$list = preg_split('/\s*,\s*/', $list);253254foreach ($list as $key => $item) {255$list[$key] = trim($item);256}257258foreach ($list as $key => $item) {259if (!strlen($item)) {260unset($list[$key]);261}262}263264return $list;265}266267private function loadRepos($identifiers) {268$identifiers = $this->parseList($identifiers);269if (!$identifiers) {270return null;271}272273$query = id(new PhabricatorRepositoryQuery())274->setViewer($this->getViewer())275->withIdentifiers($identifiers);276277$repos = $query->execute();278279$map = $query->getIdentifierMap();280foreach ($identifiers as $identifier) {281if (empty($map[$identifier])) {282throw new PhutilArgumentUsageException(283pht('No repository "%s" exists!', $identifier));284}285}286287return $repos;288}289290private function loadDate($date) {291if (!$date) {292return null;293}294295$epoch = strtotime($date);296if (!$epoch || $epoch < 1) {297throw new PhutilArgumentUsageException(298pht(299'Unable to parse date "%s". Use a format like "%s".',300$date,301'2000-01-01'));302}303304return $epoch;305}306307private function loadCommits($commits) {308$names = $this->parseList($commits);309if (!$names) {310return null;311}312313$query = id(new DiffusionCommitQuery())314->setViewer($this->getViewer())315->withIdentifiers($names);316317$commits = $query->execute();318319$map = $query->getIdentifierMap();320foreach ($names as $name) {321if (empty($map[$name])) {322throw new PhutilArgumentUsageException(323pht('No such commit "%s"!', $name));324}325}326327return $commits;328}329330}331332333