1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-24 06:20:56 +01:00

Allow "Repository Automation" to be configured for repositories

Summary:
Ref T182. This allows you to assign blueprints that a repository can use to perform working copy operations. Eventually, this will support "merge this" in Differential, etc.

This is just UI for now, with no material effects.

Most of this diff is just taking logic that was in the existing "Blueprints" CustomField and putting it in more general places so Diffusion (which does not use CustomFields) can also access it.

Test Plan:
  - Configured repository automation for a repository.
  - Removed repository automation for a repository.

Reviewers: chad

Reviewed By: chad

Subscribers: avivey

Maniphest Tasks: T182

Differential Revision: https://secure.phabricator.com/D14259
This commit is contained in:
epriestley 2015-10-13 15:45:59 -07:00
parent 6ff1354ac1
commit df5a031b54
13 changed files with 465 additions and 156 deletions

View file

@ -688,6 +688,7 @@ phutil_register_library_map(array(
'DiffusionRepositoryDefaultController' => 'applications/diffusion/controller/DiffusionRepositoryDefaultController.php',
'DiffusionRepositoryEditActionsController' => 'applications/diffusion/controller/DiffusionRepositoryEditActionsController.php',
'DiffusionRepositoryEditActivateController' => 'applications/diffusion/controller/DiffusionRepositoryEditActivateController.php',
'DiffusionRepositoryEditAutomationController' => 'applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php',
'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php',
'DiffusionRepositoryEditBranchesController' => 'applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php',
'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php',
@ -875,6 +876,7 @@ phutil_register_library_map(array(
'DrydockManagementUpdateLeaseWorkflow' => 'applications/drydock/management/DrydockManagementUpdateLeaseWorkflow.php',
'DrydockManagementUpdateResourceWorkflow' => 'applications/drydock/management/DrydockManagementUpdateResourceWorkflow.php',
'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
'DrydockObjectAuthorizationView' => 'applications/drydock/view/DrydockObjectAuthorizationView.php',
'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
'DrydockResourceActivationFailureLogType' => 'applications/drydock/logtype/DrydockResourceActivationFailureLogType.php',
@ -4424,6 +4426,7 @@ phutil_register_library_map(array(
'DiffusionRepositoryDefaultController' => 'DiffusionController',
'DiffusionRepositoryEditActionsController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditActivateController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditAutomationController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditBasicController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditBranchesController' => 'DiffusionRepositoryEditController',
'DiffusionRepositoryEditController' => 'DiffusionController',
@ -4645,6 +4648,7 @@ phutil_register_library_map(array(
'DrydockManagementUpdateLeaseWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementUpdateResourceWorkflow' => 'DrydockManagementWorkflow',
'DrydockManagementWorkflow' => 'PhabricatorManagementWorkflow',
'DrydockObjectAuthorizationView' => 'AphrontView',
'DrydockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'DrydockResource' => array(
'DrydockDAO',

View file

@ -102,6 +102,7 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
'update/' => 'DiffusionRepositoryEditUpdateController',
'symbol/' => 'DiffusionRepositorySymbolsController',
'staging/' => 'DiffusionRepositoryEditStagingController',
'automation/' => 'DiffusionRepositoryEditAutomationController',
),
'pathtree/(?P<dblob>.*)' => 'DiffusionPathTreeController',
'mirror/' => array(

View file

@ -0,0 +1,94 @@
<?php
final class DiffusionRepositoryEditAutomationController
extends DiffusionRepositoryEditController {
protected function processDiffusionRequest(AphrontRequest $request) {
$viewer = $request->getUser();
$drequest = $this->diffusionRequest;
$repository = $drequest->getRepository();
$repository = id(new PhabricatorRepositoryQuery())
->setViewer($viewer)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->withIDs(array($repository->getID()))
->executeOne();
if (!$repository) {
return new Aphront404Response();
}
if (!$repository->supportsAutomation()) {
return new Aphront404Response();
}
$edit_uri = $this->getRepositoryControllerURI($repository, 'edit/');
$v_blueprints = $repository->getHumanReadableDetail(
'automation.blueprintPHIDs');
if ($request->isFormPost()) {
$v_blueprints = $request->getArr('blueprintPHIDs');
$xactions = array();
$template = id(new PhabricatorRepositoryTransaction());
$type_blueprints =
PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS;
$xactions[] = id(clone $template)
->setTransactionType($type_blueprints)
->setNewValue($v_blueprints);
id(new PhabricatorRepositoryEditor())
->setContinueOnNoEffect(true)
->setContentSourceFromRequest($request)
->setActor($viewer)
->applyTransactions($repository, $xactions);
return id(new AphrontRedirectResponse())->setURI($edit_uri);
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Edit Automation'));
$title = pht('Edit %s', $repository->getName());
$form = id(new AphrontFormView())
->setUser($viewer)
->appendRemarkupInstructions(
pht(
"Configure **Repository Automation** to allow Phabricator to ".
"write to this repository.".
"\n\n".
"IMPORTANT: This feature is new, experimental, and not supported. ".
"Use it at your own risk."))
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Use Blueprints'))
->setName('blueprintPHIDs')
->setValue($v_blueprints)
->setDatasource(new DrydockBlueprintDatasource()))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Save'))
->addCancelButton($edit_uri));
$object_box = id(new PHUIObjectBoxView())
->setHeaderText($title)
->setForm($form);
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
),
array(
'title' => $title,
));
}
}

View file

@ -31,6 +31,7 @@ final class DiffusionRepositoryEditMainController
$has_branches = ($is_git || $is_hg);
$has_local = $repository->usesLocalWorkingCopy();
$supports_staging = $repository->supportsStaging();
$supports_automation = $repository->supportsAutomation();
$crumbs = $this->buildApplicationCrumbs($is_main = true);
@ -100,6 +101,13 @@ final class DiffusionRepositoryEditMainController
$this->buildStagingActions($repository));
}
$automation_properties = null;
if ($supports_automation) {
$automation_properties = $this->buildAutomationProperties(
$repository,
$this->buildAutomationActions($repository));
}
$actions_properties = $this->buildActionsProperties(
$repository,
$this->buildActionsActions($repository));
@ -171,6 +179,12 @@ final class DiffusionRepositoryEditMainController
->addPropertyList($staging_properties);
}
if ($automation_properties) {
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Automation'))
->addPropertyList($automation_properties);
}
$boxes[] = id(new PHUIObjectBoxView())
->setHeaderText(pht('Text Encoding'))
->addPropertyList($encoding_properties);
@ -622,7 +636,6 @@ final class DiffusionRepositoryEditMainController
return $view;
}
private function buildStagingActions(PhabricatorRepository $repository) {
$viewer = $this->getViewer();
@ -661,6 +674,47 @@ final class DiffusionRepositoryEditMainController
return $view;
}
private function buildAutomationActions(PhabricatorRepository $repository) {
$viewer = $this->getViewer();
$view = id(new PhabricatorActionListView())
->setObjectURI($this->getRequest()->getRequestURI())
->setUser($viewer);
$edit = id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Automation'))
->setHref(
$this->getRepositoryControllerURI($repository, 'edit/automation/'));
$view->addAction($edit);
return $view;
}
private function buildAutomationProperties(
PhabricatorRepository $repository,
PhabricatorActionListView $actions) {
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setActionList($actions);
$blueprint_phids = $repository->getAutomationBlueprintPHIDs();
if (!$blueprint_phids) {
$blueprint_view = phutil_tag('em', array(), pht('Not Configured'));
} else {
$blueprint_view = id(new DrydockObjectAuthorizationView())
->setUser($viewer)
->setObjectPHID($repository->getPHID())
->setBlueprintPHIDs($blueprint_phids);
}
$view->addProperty(pht('Automation'), $blueprint_view);
return $view;
}
private function buildHostingActions(PhabricatorRepository $repository) {
$user = $this->getRequest()->getUser();

View file

@ -93,6 +93,86 @@ final class DrydockAuthorization extends DrydockDAO
return idx($map, $state, pht('<Unknown: %s>', $state));
}
/**
* Apply external authorization effects after a user chagnes the value of a
* blueprint selector control an object.
*
* @param PhabricatorUser User applying the change.
* @param phid Object PHID change is being applied to.
* @param list<phid> Old blueprint PHIDs.
* @param list<phid> New blueprint PHIDs.
* @return void
*/
public static function applyAuthorizationChanges(
PhabricatorUser $viewer,
$object_phid,
array $old,
array $new) {
$old_phids = array_fuse($old);
$new_phids = array_fuse($new);
$rem_phids = array_diff_key($old_phids, $new_phids);
$add_phids = array_diff_key($new_phids, $old_phids);
$altered_phids = $rem_phids + $add_phids;
if (!$altered_phids) {
return;
}
$authorizations = id(new DrydockAuthorizationQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withObjectPHIDs(array($object_phid))
->withBlueprintPHIDs($altered_phids)
->execute();
$authorizations = mpull($authorizations, null, 'getBlueprintPHID');
$state_active = self::OBJECTAUTH_ACTIVE;
$state_inactive = self::OBJECTAUTH_INACTIVE;
$state_requested = self::BLUEPRINTAUTH_REQUESTED;
// Disable the object side of the authorization for any existing
// authorizations.
foreach ($rem_phids as $rem_phid) {
$authorization = idx($authorizations, $rem_phid);
if (!$authorization) {
continue;
}
$authorization
->setObjectAuthorizationState($state_inactive)
->save();
}
// For new authorizations, either add them or reactivate them depending
// on the current state.
foreach ($add_phids as $add_phid) {
$needs_update = false;
$authorization = idx($authorizations, $add_phid);
if (!$authorization) {
$authorization = id(new DrydockAuthorization())
->setObjectPHID($object_phid)
->setObjectAuthorizationState($state_active)
->setBlueprintPHID($add_phid)
->setBlueprintAuthorizationState($state_requested);
$needs_update = true;
} else {
$current_state = $authorization->getObjectAuthorizationState();
if ($current_state != $state_active) {
$authorization->setObjectAuthorizationState($state_active);
$needs_update = true;
}
}
if ($needs_update) {
$authorization->save();
}
}
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */

View file

@ -0,0 +1,79 @@
<?php
final class DrydockObjectAuthorizationView extends AphrontView {
private $objectPHID;
private $blueprintPHIDs;
public function setObjectPHID($object_phid) {
$this->objectPHID = $object_phid;
return $this;
}
public function getObjectPHID() {
return $this->objectPHID;
}
public function setBlueprintPHIDs(array $blueprint_phids) {
$this->blueprintPHIDs = $blueprint_phids;
return $this;
}
public function getBlueprintPHIDs() {
return $this->blueprintPHIDs;
}
public function render() {
$viewer = $this->getUser();
$blueprint_phids = $this->getBlueprintPHIDs();
$object_phid = $this->getObjectPHID();
// NOTE: We're intentionally letting you see the authorization state on
// blueprints you can't see because this has a tremendous potential to
// be extremely confusing otherwise. You still can't see the blueprints
// themselves, but you can know if the object is authorized on something.
if ($blueprint_phids) {
$handles = $viewer->loadHandles($blueprint_phids);
$authorizations = id(new DrydockAuthorizationQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withObjectPHIDs(array($object_phid))
->withBlueprintPHIDs($blueprint_phids)
->execute();
$authorizations = mpull($authorizations, null, 'getBlueprintPHID');
} else {
$handles = array();
$authorizations = array();
}
$items = array();
foreach ($blueprint_phids as $phid) {
$authorization = idx($authorizations, $phid);
if (!$authorization) {
continue;
}
$handle = $handles[$phid];
$item = id(new PHUIStatusItemView())
->setTarget($handle->renderLink());
$state = $authorization->getBlueprintAuthorizationState();
$item->setIcon(
DrydockAuthorization::getBlueprintStateIcon($state),
null,
DrydockAuthorization::getBlueprintStateName($state));
$items[] = $item;
}
$status = new PHUIStatusListView();
foreach ($items as $item) {
$status->addItem($item);
}
return $status;
}
}

View file

@ -178,4 +178,40 @@ final class PhabricatorObjectQuery
return null;
}
/**
* Select invalid or restricted PHIDs from a list.
*
* PHIDs are invalid if their objects do not exist or can not be seen by the
* viewer. This method is generally used to validate that PHIDs affected by
* a transaction are valid.
*
* @param PhabricatorUser Viewer.
* @param list<phid> List of ostensibly valid PHIDs.
* @return list<phid> List of invalid or restricted PHIDs.
*/
public static function loadInvalidPHIDsForViewer(
PhabricatorUser $viewer,
array $phids) {
if (!$phids) {
return array();
}
$objects = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withPHIDs($phids)
->execute();
$objects = mpull($objects, null, 'getPHID');
$invalid = array();
foreach ($phids as $phid) {
if (empty($objects[$phid])) {
$invalid[] = $phid;
}
}
return $invalid;
}
}

View file

@ -44,6 +44,7 @@ final class PhabricatorRepositoryEditor
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE;
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES;
$types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI;
$types[] = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
@ -107,6 +108,8 @@ final class PhabricatorRepositoryEditor
return $object->getSymbolSources();
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
return $object->getDetail('staging-uri');
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
return $object->getDetail('automation.blueprintPHIDs', array());
}
}
@ -143,6 +146,7 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
return $xaction->getNewValue();
case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
@ -226,6 +230,11 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
$object->setDetail('staging-uri', $xaction->getNewValue());
return;
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
$object->setDetail(
'automation.blueprintPHIDs',
$xaction->getNewValue());
return;
case PhabricatorRepositoryTransaction::TYPE_ENCODING:
// Make sure the encoding is valid by converting to UTF-8. This tests
// that the user has mbstring installed, and also that they didn't type
@ -276,33 +285,17 @@ final class PhabricatorRepositoryEditor
$editor->save();
break;
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
DrydockAuthorization::applyAuthorizationChanges(
$this->getActor(),
$object->getPHID(),
$xaction->getOldValue(),
$xaction->getNewValue());
break;
}
}
protected function mergeTransactions(
PhabricatorApplicationTransaction $u,
PhabricatorApplicationTransaction $v) {
$type = $u->getTransactionType();
switch ($type) {}
return parent::mergeTransactions($u, $v);
}
protected function transactionHasEffect(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$type = $xaction->getTransactionType();
switch ($type) {}
return parent::transactionHasEffect($object, $xaction);
}
protected function requireCapabilities(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
@ -338,6 +331,7 @@ final class PhabricatorRepositoryEditor
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,
@ -431,6 +425,29 @@ final class PhabricatorRepositoryEditor
}
}
break;
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
foreach ($xactions as $xaction) {
$old = nonempty($xaction->getOldValue(), array());
$new = nonempty($xaction->getNewValue(), array());
$add = array_diff($new, $old);
$invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer(
$this->getActor(),
$add);
if ($invalid) {
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'Some of the selected automation blueprints are invalid '.
'or restricted: %s.',
implode(', ', $invalid)),
$xaction);
}
}
break;
}
return $errors;

View file

@ -1799,7 +1799,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
}
/* -( Staging )-------------------------------------------------------------*/
/* -( Staging )------------------------------------------------------------ */
public function supportsStaging() {
@ -1815,6 +1815,22 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
}
/* -( Automation )--------------------------------------------------------- */
public function supportsAutomation() {
return $this->isGit();
}
public function getAutomationBlueprintPHIDs() {
if (!$this->supportsAutomation()) {
return array();
}
return $this->getDetail('automation.blueprintPHIDs', array());
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */

View file

@ -28,6 +28,7 @@ final class PhabricatorRepositoryTransaction
const TYPE_SYMBOLS_SOURCES = 'repo:symbol-source';
const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language';
const TYPE_STAGING_URI = 'repo:staging-uri';
const TYPE_AUTOMATION_BLUEPRINTS = 'repo:automation-blueprints';
// TODO: Clean up these legacy transaction types.
const TYPE_SSH_LOGIN = 'repo:ssh-login';
@ -65,6 +66,7 @@ final class PhabricatorRepositoryTransaction
}
break;
case self::TYPE_SYMBOLS_SOURCES:
case self::TYPE_AUTOMATION_BLUEPRINTS:
if ($old) {
$phids = array_merge($phids, $old);
}
@ -436,6 +438,34 @@ final class PhabricatorRepositoryTransaction
$old,
$new);
}
case self::TYPE_AUTOMATION_BLUEPRINTS:
$add = array_diff($new, $old);
$rem = array_diff($old, $new);
if ($add && $rem) {
return pht(
'%s changed %s automation blueprint(s), '.
'added %s: %s; removed %s: %s.',
$this->renderHandleLink($author_phid),
new PhutilNumber(count($add) + count($rem)),
new PhutilNumber(count($add)),
$this->renderHandleList($add),
new PhutilNumber(count($rem)),
$this->renderHandleList($rem));
} else if ($add) {
return pht(
'%s added %s automation blueprint(s): %s.',
$this->renderHandleLink($author_phid),
new PhutilNumber(count($add)),
$this->renderHandleList($add));
} else {
return pht(
'%s removed %s automation blueprint(s): %s.',
$this->renderHandleLink($author_phid),
new PhutilNumber(count($rem)),
$this->renderHandleList($rem));
}
}
return parent::getTitle();

View file

@ -14,75 +14,14 @@ final class PhabricatorStandardCustomFieldBlueprints
public function applyApplicationTransactionExternalEffects(
PhabricatorApplicationTransaction $xaction) {
$object_phid = $xaction->getObjectPHID();
$old = $this->decodeValue($xaction->getOldValue());
$new = $this->decodeValue($xaction->getNewValue());
$old_phids = array_fuse($old);
$new_phids = array_fuse($new);
$rem_phids = array_diff_key($old_phids, $new_phids);
$add_phids = array_diff_key($new_phids, $old_phids);
$altered_phids = $rem_phids + $add_phids;
if (!$altered_phids) {
return;
}
$authorizations = id(new DrydockAuthorizationQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withObjectPHIDs(array($object_phid))
->withBlueprintPHIDs($altered_phids)
->execute();
$authorizations = mpull($authorizations, null, 'getBlueprintPHID');
$state_active = DrydockAuthorization::OBJECTAUTH_ACTIVE;
$state_inactive = DrydockAuthorization::OBJECTAUTH_INACTIVE;
$state_requested = DrydockAuthorization::BLUEPRINTAUTH_REQUESTED;
// Disable the object side of the authorization for any existing
// authorizations.
foreach ($rem_phids as $rem_phid) {
$authorization = idx($authorizations, $rem_phid);
if (!$authorization) {
continue;
}
$authorization
->setObjectAuthorizationState($state_inactive)
->save();
}
// For new authorizations, either add them or reactivate them depending
// on the current state.
foreach ($add_phids as $add_phid) {
$needs_update = false;
$authorization = idx($authorizations, $add_phid);
if (!$authorization) {
$authorization = id(new DrydockAuthorization())
->setObjectPHID($object_phid)
->setObjectAuthorizationState($state_active)
->setBlueprintPHID($add_phid)
->setBlueprintAuthorizationState($state_requested);
$needs_update = true;
} else {
$current_state = $authorization->getObjectAuthorizationState();
if ($current_state != $state_active) {
$authorization->setObjectAuthorizationState($state_active);
$needs_update = true;
}
}
if ($needs_update) {
$authorization->save();
}
}
DrydockAuthorization::applyAuthorizationChanges(
$this->getViewer(),
$xaction->getObjectPHID(),
$old,
$new);
}
public function renderPropertyViewValue(array $handles) {
@ -91,55 +30,10 @@ final class PhabricatorStandardCustomFieldBlueprints
return phutil_tag('em', array(), pht('No authorized blueprints.'));
}
$object = $this->getObject();
$object_phid = $object->getPHID();
// NOTE: We're intentionally letting you see the authorization state on
// blueprints you can't see because this has a tremendous potential to
// be extremely confusing otherwise. You still can't see the blueprints
// themselves, but you can know if the object is authorized on something.
if ($value) {
$handles = $this->getViewer()->loadHandles($value);
$authorizations = id(new DrydockAuthorizationQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withObjectPHIDs(array($object_phid))
->withBlueprintPHIDs($value)
->execute();
$authorizations = mpull($authorizations, null, 'getBlueprintPHID');
} else {
$handles = array();
$authorizations = array();
}
$items = array();
foreach ($value as $phid) {
$authorization = idx($authorizations, $phid);
if (!$authorization) {
continue;
}
$handle = $handles[$phid];
$item = id(new PHUIStatusItemView())
->setTarget($handle->renderLink());
$state = $authorization->getBlueprintAuthorizationState();
$item->setIcon(
DrydockAuthorization::getBlueprintStateIcon($state),
null,
DrydockAuthorization::getBlueprintStateName($state));
$items[] = $item;
}
$status = new PHUIStatusListView();
foreach ($items as $item) {
$status->addItem($item);
}
return $status;
return id(new DrydockObjectAuthorizationView())
->setUser($this->getViewer())
->setObjectPHID($this->getObject()->getPHID())
->setBlueprintPHIDs($value);
}

View file

@ -158,22 +158,9 @@ abstract class PhabricatorStandardCustomFieldPHIDs
$add = array_diff($new, $old);
if (!$add) {
continue;
}
$objects = id(new PhabricatorObjectQuery())
->setViewer($editor->getActor())
->withPHIDs($add)
->execute();
$objects = mpull($objects, null, 'getPHID');
$invalid = array();
foreach ($add as $phid) {
if (empty($objects[$phid])) {
$invalid[] = $phid;
}
}
$invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer(
$editor->getActor(),
$add);
if ($invalid) {
$error = new PhabricatorApplicationTransactionValidationError(

View file

@ -1403,6 +1403,23 @@ final class PhabricatorUSEnglishTranslation
'Waiting %s seconds for lease to activate.',
),
'%s changed %s automation blueprint(s), added %s: %s; removed %s: %s.' =>
'%s changed automation blueprints, added: %4$s; removed: %6$s.',
'%s added %s automation blueprint(s): %s.' => array(
array(
'%s added an automation blueprint: %3$s.',
'%s added automation blueprints: %3$s.',
),
),
'%s removed %s automation blueprint(s): %s.' => array(
array(
'%s removed an automation blueprint: %3$s.',
'%s removed automation blueprints: %3$s.',
),
),
);
}