Path: blob/master/src/applications/owners/xaction/PhabricatorOwnersPackagePathsTransaction.php
12256 views
<?php12final class PhabricatorOwnersPackagePathsTransaction3extends PhabricatorOwnersPackageTransactionType {45const TRANSACTIONTYPE = 'owners.paths';67public function generateOldValue($object) {8$paths = $object->getPaths();9return mpull($paths, 'getRef');10}1112public function generateNewValue($object, $value) {13$new = $value;1415foreach ($new as $key => $info) {16$info['excluded'] = (int)idx($info, 'excluded');1718// The input has one "path" key with the display path.19// Move it to "display", then normalize the value in "path".2021$display_path = $info['path'];22$raw_path = rtrim($display_path, '/').'/';2324$info['path'] = $raw_path;25$info['display'] = $display_path;2627$new[$key] = $info;28}2930return $new;31}3233public function getTransactionHasEffect($object, $old, $new) {34list($add, $rem) = PhabricatorOwnersPath::getTransactionValueChanges(35$old,36$new);3738return ($add || $rem);39}4041public function validateTransactions($object, array $xactions) {42$errors = array();4344if (!$xactions) {45return $errors;46}4748$old = mpull($object->getPaths(), 'getRef');49foreach ($xactions as $xaction) {50$new = $xaction->getNewValue();5152// Check that we have a list of paths.53if (!is_array($new)) {54$errors[] = $this->newInvalidError(55pht('Path specification must be a list of paths.'),56$xaction);57continue;58}5960// Check that each item in the list is formatted properly.61$type_exception = null;62foreach ($new as $key => $value) {63try {64PhutilTypeSpec::checkMap(65$value,66array(67'repositoryPHID' => 'string',68'path' => 'string',69'excluded' => 'optional wild',70));71} catch (PhutilTypeCheckException $ex) {72$errors[] = $this->newInvalidError(73pht(74'Path specification list contains invalid value '.75'in key "%s": %s.',76$key,77$ex->getMessage()),78$xaction);79$type_exception = $ex;80}81}8283if ($type_exception) {84continue;85}8687// Check that any new paths reference legitimate repositories which88// the viewer has permission to see.89list($rem, $add) = PhabricatorOwnersPath::getTransactionValueChanges(90$old,91$new);9293if ($add) {94$repository_phids = ipull($add, 'repositoryPHID');9596$repositories = id(new PhabricatorRepositoryQuery())97->setViewer($this->getActor())98->withPHIDs($repository_phids)99->execute();100$repositories = mpull($repositories, null, 'getPHID');101102foreach ($add as $ref) {103$repository_phid = $ref['repositoryPHID'];104if (isset($repositories[$repository_phid])) {105continue;106}107108$errors[] = $this->newInvalidError(109pht(110'Path specification list references repository PHID "%s", '.111'but that is not a valid, visible repository.',112$repository_phid));113}114}115}116117return $errors;118}119120public function applyExternalEffects($object, $value) {121$old = $this->generateOldValue($object);122$new = $value;123124$paths = $object->getPaths();125126// We store paths in a normalized format with a trailing slash, regardless127// of whether the user enters "path/to/file.c" or "src/backend/". Normalize128// paths now.129130$display_map = array();131$seen_map = array();132foreach ($new as $key => $spec) {133$raw_path = $spec['path'];134$display_path = $spec['display'];135136// If the user entered two paths in the same repository which normalize137// to the same value (like "src/main.c" and "src/main.c/"), discard the138// duplicates.139$repository_phid = $spec['repositoryPHID'];140if (isset($seen_map[$repository_phid][$raw_path])) {141unset($new[$key]);142continue;143}144145$new[$key]['path'] = $raw_path;146$display_map[$raw_path] = $display_path;147$seen_map[$repository_phid][$raw_path] = true;148}149150$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);151list($rem, $add) = $diffs;152153$set = PhabricatorOwnersPath::getSetFromTransactionValue($rem);154foreach ($paths as $path) {155$ref = $path->getRef();156if (PhabricatorOwnersPath::isRefInSet($ref, $set)) {157$path->delete();158continue;159}160161// If the user has changed the display value for a path but the raw162// storage value hasn't changed, update the display value.163164if (isset($display_map[$path->getPath()])) {165$path166->setPathDisplay($display_map[$path->getPath()])167->save();168continue;169}170}171172foreach ($add as $ref) {173$path = PhabricatorOwnersPath::newFromRef($ref)174->setPackageID($object->getID())175->setPathDisplay($display_map[$ref['path']])176->save();177}178}179180public function getTitle() {181// TODO: Flesh this out.182return pht(183'%s updated paths for this package.',184$this->renderAuthor());185}186187public function hasChangeDetailView() {188return true;189}190191public function newChangeDetailView() {192$old = $this->getOldValue();193$new = $this->getNewValue();194195$diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new);196list($rem, $add) = $diffs;197198$rows = array();199foreach ($rem as $ref) {200$rows[] = array(201'class' => 'diff-removed',202'change' => '-',203) + $ref;204}205206foreach ($add as $ref) {207$rows[] = array(208'class' => 'diff-added',209'change' => '+',210) + $ref;211}212213$rowc = array();214foreach ($rows as $key => $row) {215$rowc[] = $row['class'];216217if (array_key_exists('display', $row)) {218$display_path = $row['display'];219} else {220$display_path = $row['path'];221}222223$rows[$key] = array(224$row['change'],225$row['excluded'] ? pht('Exclude') : pht('Include'),226$this->renderHandle($row['repositoryPHID']),227$display_path,228);229}230231$table = id(new AphrontTableView($rows))232->setViewer($this->getViewer())233->setRowClasses($rowc)234->setHeaders(235array(236null,237pht('Type'),238pht('Repository'),239pht('Path'),240))241->setColumnClasses(242array(243null,244null,245null,246'wide',247));248249return $table;250}251252}253254255