mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-19 13:22:42 +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:
parent
6ff1354ac1
commit
df5a031b54
13 changed files with 465 additions and 156 deletions
|
@ -688,6 +688,7 @@ phutil_register_library_map(array(
|
||||||
'DiffusionRepositoryDefaultController' => 'applications/diffusion/controller/DiffusionRepositoryDefaultController.php',
|
'DiffusionRepositoryDefaultController' => 'applications/diffusion/controller/DiffusionRepositoryDefaultController.php',
|
||||||
'DiffusionRepositoryEditActionsController' => 'applications/diffusion/controller/DiffusionRepositoryEditActionsController.php',
|
'DiffusionRepositoryEditActionsController' => 'applications/diffusion/controller/DiffusionRepositoryEditActionsController.php',
|
||||||
'DiffusionRepositoryEditActivateController' => 'applications/diffusion/controller/DiffusionRepositoryEditActivateController.php',
|
'DiffusionRepositoryEditActivateController' => 'applications/diffusion/controller/DiffusionRepositoryEditActivateController.php',
|
||||||
|
'DiffusionRepositoryEditAutomationController' => 'applications/diffusion/controller/DiffusionRepositoryEditAutomationController.php',
|
||||||
'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php',
|
'DiffusionRepositoryEditBasicController' => 'applications/diffusion/controller/DiffusionRepositoryEditBasicController.php',
|
||||||
'DiffusionRepositoryEditBranchesController' => 'applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php',
|
'DiffusionRepositoryEditBranchesController' => 'applications/diffusion/controller/DiffusionRepositoryEditBranchesController.php',
|
||||||
'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php',
|
'DiffusionRepositoryEditController' => 'applications/diffusion/controller/DiffusionRepositoryEditController.php',
|
||||||
|
@ -875,6 +876,7 @@ phutil_register_library_map(array(
|
||||||
'DrydockManagementUpdateLeaseWorkflow' => 'applications/drydock/management/DrydockManagementUpdateLeaseWorkflow.php',
|
'DrydockManagementUpdateLeaseWorkflow' => 'applications/drydock/management/DrydockManagementUpdateLeaseWorkflow.php',
|
||||||
'DrydockManagementUpdateResourceWorkflow' => 'applications/drydock/management/DrydockManagementUpdateResourceWorkflow.php',
|
'DrydockManagementUpdateResourceWorkflow' => 'applications/drydock/management/DrydockManagementUpdateResourceWorkflow.php',
|
||||||
'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
|
'DrydockManagementWorkflow' => 'applications/drydock/management/DrydockManagementWorkflow.php',
|
||||||
|
'DrydockObjectAuthorizationView' => 'applications/drydock/view/DrydockObjectAuthorizationView.php',
|
||||||
'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
|
'DrydockQuery' => 'applications/drydock/query/DrydockQuery.php',
|
||||||
'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
|
'DrydockResource' => 'applications/drydock/storage/DrydockResource.php',
|
||||||
'DrydockResourceActivationFailureLogType' => 'applications/drydock/logtype/DrydockResourceActivationFailureLogType.php',
|
'DrydockResourceActivationFailureLogType' => 'applications/drydock/logtype/DrydockResourceActivationFailureLogType.php',
|
||||||
|
@ -4424,6 +4426,7 @@ phutil_register_library_map(array(
|
||||||
'DiffusionRepositoryDefaultController' => 'DiffusionController',
|
'DiffusionRepositoryDefaultController' => 'DiffusionController',
|
||||||
'DiffusionRepositoryEditActionsController' => 'DiffusionRepositoryEditController',
|
'DiffusionRepositoryEditActionsController' => 'DiffusionRepositoryEditController',
|
||||||
'DiffusionRepositoryEditActivateController' => 'DiffusionRepositoryEditController',
|
'DiffusionRepositoryEditActivateController' => 'DiffusionRepositoryEditController',
|
||||||
|
'DiffusionRepositoryEditAutomationController' => 'DiffusionRepositoryEditController',
|
||||||
'DiffusionRepositoryEditBasicController' => 'DiffusionRepositoryEditController',
|
'DiffusionRepositoryEditBasicController' => 'DiffusionRepositoryEditController',
|
||||||
'DiffusionRepositoryEditBranchesController' => 'DiffusionRepositoryEditController',
|
'DiffusionRepositoryEditBranchesController' => 'DiffusionRepositoryEditController',
|
||||||
'DiffusionRepositoryEditController' => 'DiffusionController',
|
'DiffusionRepositoryEditController' => 'DiffusionController',
|
||||||
|
@ -4645,6 +4648,7 @@ phutil_register_library_map(array(
|
||||||
'DrydockManagementUpdateLeaseWorkflow' => 'DrydockManagementWorkflow',
|
'DrydockManagementUpdateLeaseWorkflow' => 'DrydockManagementWorkflow',
|
||||||
'DrydockManagementUpdateResourceWorkflow' => 'DrydockManagementWorkflow',
|
'DrydockManagementUpdateResourceWorkflow' => 'DrydockManagementWorkflow',
|
||||||
'DrydockManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
'DrydockManagementWorkflow' => 'PhabricatorManagementWorkflow',
|
||||||
|
'DrydockObjectAuthorizationView' => 'AphrontView',
|
||||||
'DrydockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'DrydockQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'DrydockResource' => array(
|
'DrydockResource' => array(
|
||||||
'DrydockDAO',
|
'DrydockDAO',
|
||||||
|
|
|
@ -102,6 +102,7 @@ final class PhabricatorDiffusionApplication extends PhabricatorApplication {
|
||||||
'update/' => 'DiffusionRepositoryEditUpdateController',
|
'update/' => 'DiffusionRepositoryEditUpdateController',
|
||||||
'symbol/' => 'DiffusionRepositorySymbolsController',
|
'symbol/' => 'DiffusionRepositorySymbolsController',
|
||||||
'staging/' => 'DiffusionRepositoryEditStagingController',
|
'staging/' => 'DiffusionRepositoryEditStagingController',
|
||||||
|
'automation/' => 'DiffusionRepositoryEditAutomationController',
|
||||||
),
|
),
|
||||||
'pathtree/(?P<dblob>.*)' => 'DiffusionPathTreeController',
|
'pathtree/(?P<dblob>.*)' => 'DiffusionPathTreeController',
|
||||||
'mirror/' => array(
|
'mirror/' => array(
|
||||||
|
|
|
@ -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,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -31,6 +31,7 @@ final class DiffusionRepositoryEditMainController
|
||||||
$has_branches = ($is_git || $is_hg);
|
$has_branches = ($is_git || $is_hg);
|
||||||
$has_local = $repository->usesLocalWorkingCopy();
|
$has_local = $repository->usesLocalWorkingCopy();
|
||||||
$supports_staging = $repository->supportsStaging();
|
$supports_staging = $repository->supportsStaging();
|
||||||
|
$supports_automation = $repository->supportsAutomation();
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs($is_main = true);
|
$crumbs = $this->buildApplicationCrumbs($is_main = true);
|
||||||
|
|
||||||
|
@ -100,6 +101,13 @@ final class DiffusionRepositoryEditMainController
|
||||||
$this->buildStagingActions($repository));
|
$this->buildStagingActions($repository));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$automation_properties = null;
|
||||||
|
if ($supports_automation) {
|
||||||
|
$automation_properties = $this->buildAutomationProperties(
|
||||||
|
$repository,
|
||||||
|
$this->buildAutomationActions($repository));
|
||||||
|
}
|
||||||
|
|
||||||
$actions_properties = $this->buildActionsProperties(
|
$actions_properties = $this->buildActionsProperties(
|
||||||
$repository,
|
$repository,
|
||||||
$this->buildActionsActions($repository));
|
$this->buildActionsActions($repository));
|
||||||
|
@ -171,6 +179,12 @@ final class DiffusionRepositoryEditMainController
|
||||||
->addPropertyList($staging_properties);
|
->addPropertyList($staging_properties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($automation_properties) {
|
||||||
|
$boxes[] = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Automation'))
|
||||||
|
->addPropertyList($automation_properties);
|
||||||
|
}
|
||||||
|
|
||||||
$boxes[] = id(new PHUIObjectBoxView())
|
$boxes[] = id(new PHUIObjectBoxView())
|
||||||
->setHeaderText(pht('Text Encoding'))
|
->setHeaderText(pht('Text Encoding'))
|
||||||
->addPropertyList($encoding_properties);
|
->addPropertyList($encoding_properties);
|
||||||
|
@ -622,7 +636,6 @@ final class DiffusionRepositoryEditMainController
|
||||||
return $view;
|
return $view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private function buildStagingActions(PhabricatorRepository $repository) {
|
private function buildStagingActions(PhabricatorRepository $repository) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
@ -661,6 +674,47 @@ final class DiffusionRepositoryEditMainController
|
||||||
return $view;
|
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) {
|
private function buildHostingActions(PhabricatorRepository $repository) {
|
||||||
$user = $this->getRequest()->getUser();
|
$user = $this->getRequest()->getUser();
|
||||||
|
|
||||||
|
|
|
@ -93,6 +93,86 @@ final class DrydockAuthorization extends DrydockDAO
|
||||||
return idx($map, $state, pht('<Unknown: %s>', $state));
|
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 )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -178,4 +178,40 @@ final class PhabricatorObjectQuery
|
||||||
return null;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ final class PhabricatorRepositoryEditor
|
||||||
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE;
|
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE;
|
||||||
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES;
|
$types[] = PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES;
|
||||||
$types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI;
|
$types[] = PhabricatorRepositoryTransaction::TYPE_STAGING_URI;
|
||||||
|
$types[] = PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS;
|
||||||
|
|
||||||
$types[] = PhabricatorTransactions::TYPE_EDGE;
|
$types[] = PhabricatorTransactions::TYPE_EDGE;
|
||||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||||
|
@ -107,6 +108,8 @@ final class PhabricatorRepositoryEditor
|
||||||
return $object->getSymbolSources();
|
return $object->getSymbolSources();
|
||||||
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
|
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
|
||||||
return $object->getDetail('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_LANGUAGE:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
|
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
|
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
|
||||||
|
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
|
||||||
return $xaction->getNewValue();
|
return $xaction->getNewValue();
|
||||||
case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
|
case PhabricatorRepositoryTransaction::TYPE_NOTIFY:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
|
case PhabricatorRepositoryTransaction::TYPE_AUTOCLOSE:
|
||||||
|
@ -226,6 +230,11 @@ final class PhabricatorRepositoryEditor
|
||||||
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
|
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
|
||||||
$object->setDetail('staging-uri', $xaction->getNewValue());
|
$object->setDetail('staging-uri', $xaction->getNewValue());
|
||||||
return;
|
return;
|
||||||
|
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
|
||||||
|
$object->setDetail(
|
||||||
|
'automation.blueprintPHIDs',
|
||||||
|
$xaction->getNewValue());
|
||||||
|
return;
|
||||||
case PhabricatorRepositoryTransaction::TYPE_ENCODING:
|
case PhabricatorRepositoryTransaction::TYPE_ENCODING:
|
||||||
// Make sure the encoding is valid by converting to UTF-8. This tests
|
// 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
|
// that the user has mbstring installed, and also that they didn't type
|
||||||
|
@ -276,33 +285,17 @@ final class PhabricatorRepositoryEditor
|
||||||
|
|
||||||
$editor->save();
|
$editor->save();
|
||||||
break;
|
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(
|
protected function requireCapabilities(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
@ -338,6 +331,7 @@ final class PhabricatorRepositoryEditor
|
||||||
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
|
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_SOURCES:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
|
case PhabricatorRepositoryTransaction::TYPE_SYMBOLS_LANGUAGE:
|
||||||
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
|
case PhabricatorRepositoryTransaction::TYPE_STAGING_URI:
|
||||||
|
case PhabricatorRepositoryTransaction::TYPE_AUTOMATION_BLUEPRINTS:
|
||||||
PhabricatorPolicyFilter::requireCapability(
|
PhabricatorPolicyFilter::requireCapability(
|
||||||
$this->requireActor(),
|
$this->requireActor(),
|
||||||
$object,
|
$object,
|
||||||
|
@ -431,6 +425,29 @@ final class PhabricatorRepositoryEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
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;
|
return $errors;
|
||||||
|
|
|
@ -1799,7 +1799,7 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Staging )-------------------------------------------------------------*/
|
/* -( Staging )------------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
public function supportsStaging() {
|
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 )------------------------- */
|
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ final class PhabricatorRepositoryTransaction
|
||||||
const TYPE_SYMBOLS_SOURCES = 'repo:symbol-source';
|
const TYPE_SYMBOLS_SOURCES = 'repo:symbol-source';
|
||||||
const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language';
|
const TYPE_SYMBOLS_LANGUAGE = 'repo:symbol-language';
|
||||||
const TYPE_STAGING_URI = 'repo:staging-uri';
|
const TYPE_STAGING_URI = 'repo:staging-uri';
|
||||||
|
const TYPE_AUTOMATION_BLUEPRINTS = 'repo:automation-blueprints';
|
||||||
|
|
||||||
// TODO: Clean up these legacy transaction types.
|
// TODO: Clean up these legacy transaction types.
|
||||||
const TYPE_SSH_LOGIN = 'repo:ssh-login';
|
const TYPE_SSH_LOGIN = 'repo:ssh-login';
|
||||||
|
@ -65,6 +66,7 @@ final class PhabricatorRepositoryTransaction
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case self::TYPE_SYMBOLS_SOURCES:
|
case self::TYPE_SYMBOLS_SOURCES:
|
||||||
|
case self::TYPE_AUTOMATION_BLUEPRINTS:
|
||||||
if ($old) {
|
if ($old) {
|
||||||
$phids = array_merge($phids, $old);
|
$phids = array_merge($phids, $old);
|
||||||
}
|
}
|
||||||
|
@ -436,6 +438,34 @@ final class PhabricatorRepositoryTransaction
|
||||||
$old,
|
$old,
|
||||||
$new);
|
$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();
|
return parent::getTitle();
|
||||||
|
|
|
@ -14,75 +14,14 @@ final class PhabricatorStandardCustomFieldBlueprints
|
||||||
public function applyApplicationTransactionExternalEffects(
|
public function applyApplicationTransactionExternalEffects(
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
PhabricatorApplicationTransaction $xaction) {
|
||||||
|
|
||||||
$object_phid = $xaction->getObjectPHID();
|
|
||||||
|
|
||||||
$old = $this->decodeValue($xaction->getOldValue());
|
$old = $this->decodeValue($xaction->getOldValue());
|
||||||
$new = $this->decodeValue($xaction->getNewValue());
|
$new = $this->decodeValue($xaction->getNewValue());
|
||||||
|
|
||||||
$old_phids = array_fuse($old);
|
DrydockAuthorization::applyAuthorizationChanges(
|
||||||
$new_phids = array_fuse($new);
|
$this->getViewer(),
|
||||||
|
$xaction->getObjectPHID(),
|
||||||
$rem_phids = array_diff_key($old_phids, $new_phids);
|
$old,
|
||||||
$add_phids = array_diff_key($new_phids, $old_phids);
|
$new);
|
||||||
|
|
||||||
$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();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function renderPropertyViewValue(array $handles) {
|
public function renderPropertyViewValue(array $handles) {
|
||||||
|
@ -91,55 +30,10 @@ final class PhabricatorStandardCustomFieldBlueprints
|
||||||
return phutil_tag('em', array(), pht('No authorized blueprints.'));
|
return phutil_tag('em', array(), pht('No authorized blueprints.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$object = $this->getObject();
|
return id(new DrydockObjectAuthorizationView())
|
||||||
$object_phid = $object->getPHID();
|
->setUser($this->getViewer())
|
||||||
|
->setObjectPHID($this->getObject()->getPHID())
|
||||||
// NOTE: We're intentionally letting you see the authorization state on
|
->setBlueprintPHIDs($value);
|
||||||
// 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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -158,22 +158,9 @@ abstract class PhabricatorStandardCustomFieldPHIDs
|
||||||
|
|
||||||
$add = array_diff($new, $old);
|
$add = array_diff($new, $old);
|
||||||
|
|
||||||
if (!$add) {
|
$invalid = PhabricatorObjectQuery::loadInvalidPHIDsForViewer(
|
||||||
continue;
|
$editor->getActor(),
|
||||||
}
|
$add);
|
||||||
|
|
||||||
$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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($invalid) {
|
if ($invalid) {
|
||||||
$error = new PhabricatorApplicationTransactionValidationError(
|
$error = new PhabricatorApplicationTransactionValidationError(
|
||||||
|
|
|
@ -1403,6 +1403,23 @@ final class PhabricatorUSEnglishTranslation
|
||||||
'Waiting %s seconds for lease to activate.',
|
'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.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue