diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php index 539bdffa62..f2cfc5151a 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php @@ -1,341 +1,345 @@ getTransactionType()) { case PhabricatorOwnersPackageTransaction::TYPE_NAME: return $object->getName(); case PhabricatorOwnersPackageTransaction::TYPE_OWNERS: $phids = mpull($object->getOwners(), 'getUserPHID'); $phids = array_values($phids); return $phids; case PhabricatorOwnersPackageTransaction::TYPE_AUDITING: return (int)$object->getAuditingEnabled(); case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION: return $object->getDescription(); case PhabricatorOwnersPackageTransaction::TYPE_PATHS: $paths = $object->getPaths(); return mpull($paths, 'getRef'); case PhabricatorOwnersPackageTransaction::TYPE_STATUS: return $object->getStatus(); } } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorOwnersPackageTransaction::TYPE_NAME: case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION: case PhabricatorOwnersPackageTransaction::TYPE_STATUS: return $xaction->getNewValue(); case PhabricatorOwnersPackageTransaction::TYPE_PATHS: $new = $xaction->getNewValue(); foreach ($new as $key => $info) { $new[$key]['excluded'] = (int)idx($info, 'excluded'); } return $new; case PhabricatorOwnersPackageTransaction::TYPE_AUDITING: return (int)$xaction->getNewValue(); case PhabricatorOwnersPackageTransaction::TYPE_OWNERS: $phids = $xaction->getNewValue(); $phids = array_unique($phids); $phids = array_values($phids); return $phids; } } protected function transactionHasEffect( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorOwnersPackageTransaction::TYPE_PATHS: $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new); list($rem, $add) = $diffs; return ($rem || $add); } return parent::transactionHasEffect($object, $xaction); } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorOwnersPackageTransaction::TYPE_NAME: $object->setName($xaction->getNewValue()); return; case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION: $object->setDescription($xaction->getNewValue()); return; case PhabricatorOwnersPackageTransaction::TYPE_AUDITING: $object->setAuditingEnabled($xaction->getNewValue()); return; case PhabricatorOwnersPackageTransaction::TYPE_OWNERS: case PhabricatorOwnersPackageTransaction::TYPE_PATHS: return; case PhabricatorOwnersPackageTransaction::TYPE_STATUS: $object->setStatus($xaction->getNewValue()); return; } return parent::applyCustomInternalTransaction($object, $xaction); } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorOwnersPackageTransaction::TYPE_NAME: case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION: case PhabricatorOwnersPackageTransaction::TYPE_AUDITING: case PhabricatorOwnersPackageTransaction::TYPE_STATUS: return; case PhabricatorOwnersPackageTransaction::TYPE_OWNERS: $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $owners = $object->getOwners(); $owners = mpull($owners, null, 'getUserPHID'); $rem = array_diff($old, $new); foreach ($rem as $phid) { if (isset($owners[$phid])) { $owners[$phid]->delete(); unset($owners[$phid]); } } $add = array_diff($new, $old); foreach ($add as $phid) { $owners[$phid] = id(new PhabricatorOwnersOwner()) ->setPackageID($object->getID()) ->setUserPHID($phid) ->save(); } // TODO: Attach owners here return; case PhabricatorOwnersPackageTransaction::TYPE_PATHS: $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $paths = $object->getPaths(); $diffs = PhabricatorOwnersPath::getTransactionValueChanges($old, $new); list($rem, $add) = $diffs; $set = PhabricatorOwnersPath::getSetFromTransactionValue($rem); foreach ($paths as $path) { $ref = $path->getRef(); if (PhabricatorOwnersPath::isRefInSet($ref, $set)) { $path->delete(); } } foreach ($add as $ref) { $path = PhabricatorOwnersPath::newFromRef($ref) ->setPackageID($object->getID()) ->save(); } return; } return parent::applyCustomExternalTransaction($object, $xaction); } protected function validateTransaction( PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorOwnersPackageTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField( $object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError( $type, pht('Required'), pht('Package name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; case PhabricatorOwnersPackageTransaction::TYPE_PATHS: + if (!$xactions) { + continue; + } + $old = mpull($object->getPaths(), 'getRef'); foreach ($xactions as $xaction) { $new = $xaction->getNewValue(); // Check that we have a list of paths. if (!is_array($new)) { $errors[] = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid'), pht('Path specification must be a list of paths.'), $xaction); continue; } // Check that each item in the list is formatted properly. $type_exception = null; foreach ($new as $key => $value) { try { PhutilTypeSpec::checkMap( $value, array( 'repositoryPHID' => 'string', 'path' => 'string', 'excluded' => 'optional wild', )); } catch (PhutilTypeCheckException $ex) { $errors[] = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid'), pht( 'Path specification list contains invalid value '. 'in key "%s": %s.', $key, $ex->getMessage()), $xaction); $type_exception = $ex; } } if ($type_exception) { continue; } // Check that any new paths reference legitimate repositories which // the viewer has permission to see. list($rem, $add) = PhabricatorOwnersPath::getTransactionValueChanges( $old, $new); if ($add) { $repository_phids = ipull($add, 'repositoryPHID'); $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($this->getActor()) ->withPHIDs($repository_phids) ->execute(); $repositories = mpull($repositories, null, 'getPHID'); foreach ($add as $ref) { $repository_phid = $ref['repositoryPHID']; if (isset($repositories[$repository_phid])) { continue; } $errors[] = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid'), pht( 'Path specification list references repository PHID "%s", '. 'but that is not a valid, visible repository.', $repository_phid)); } } } break; } return $errors; } protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { return true; } protected function getMailSubjectPrefix() { return PhabricatorEnv::getEnvConfig('metamta.package.subject-prefix'); } protected function getMailTo(PhabricatorLiskDAO $object) { return array( $this->requireActor()->getPHID(), ); } protected function getMailCC(PhabricatorLiskDAO $object) { return mpull($object->getOwners(), 'getUserPHID'); } protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new OwnersPackageReplyHandler()) ->setMailReceiver($object); } protected function buildMailTemplate(PhabricatorLiskDAO $object) { $id = $object->getID(); $name = $object->getName(); return id(new PhabricatorMetaMTAMail()) ->setSubject($name) ->addHeader('Thread-Topic', $object->getPHID()); } protected function buildMailBody( PhabricatorLiskDAO $object, array $xactions) { $body = parent::buildMailBody($object, $xactions); $detail_uri = PhabricatorEnv::getProductionURI( '/owners/package/'.$object->getID().'/'); $body->addLinkSection( pht('PACKAGE DETAIL'), $detail_uri); return $body; } protected function supportsSearch() { return true; } }