2016-06-22 17:04:14 +02:00
|
|
|
<?php
|
|
|
|
|
|
|
|
final class PhabricatorSearchRelationshipController
|
|
|
|
extends PhabricatorSearchBaseController {
|
|
|
|
|
|
|
|
public function handleRequest(AphrontRequest $request) {
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
|
|
|
|
$phid = $request->getURIData('sourcePHID');
|
|
|
|
$object = id(new PhabricatorObjectQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withPHIDs(array($phid))
|
|
|
|
->requireCapabilities(
|
|
|
|
array(
|
|
|
|
PhabricatorPolicyCapability::CAN_VIEW,
|
|
|
|
PhabricatorPolicyCapability::CAN_EDIT,
|
|
|
|
))
|
|
|
|
->executeOne();
|
|
|
|
if (!$object) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$list = PhabricatorObjectRelationshipList::newForObject(
|
|
|
|
$viewer,
|
|
|
|
$object);
|
|
|
|
|
|
|
|
$relationship_key = $request->getURIData('relationshipKey');
|
|
|
|
$relationship = $list->getRelationship($relationship_key);
|
|
|
|
if (!$relationship) {
|
|
|
|
return new Aphront404Response();
|
|
|
|
}
|
|
|
|
|
|
|
|
$src_phid = $object->getPHID();
|
|
|
|
$edge_type = $relationship->getEdgeConstant();
|
|
|
|
|
|
|
|
$dst_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
|
|
|
$src_phid,
|
|
|
|
$edge_type);
|
|
|
|
|
|
|
|
$all_phids = $dst_phids;
|
|
|
|
$all_phids[] = $src_phid;
|
|
|
|
|
|
|
|
$handles = $viewer->loadHandles($all_phids);
|
|
|
|
$src_handle = $handles[$src_phid];
|
|
|
|
|
|
|
|
$done_uri = $src_handle->getURI();
|
2016-06-22 17:20:50 +02:00
|
|
|
$initial_phids = $dst_phids;
|
2016-06-22 17:04:14 +02:00
|
|
|
|
|
|
|
if ($request->isFormPost()) {
|
|
|
|
$phids = explode(';', $request->getStr('phids'));
|
|
|
|
$phids = array_filter($phids);
|
|
|
|
$phids = array_values($phids);
|
|
|
|
|
2016-06-22 17:20:50 +02:00
|
|
|
$initial_phids = $request->getStrList('initialPHIDs');
|
2016-06-22 17:04:14 +02:00
|
|
|
|
2016-06-22 17:20:50 +02:00
|
|
|
// Apply the changes as adds and removes relative to the original state
|
|
|
|
// of the object when the dialog was rendered so that two users adding
|
|
|
|
// relationships at the same time don't race and overwrite one another.
|
|
|
|
$add_phids = array_diff($phids, $initial_phids);
|
|
|
|
$rem_phids = array_diff($initial_phids, $phids);
|
2016-06-22 17:04:14 +02:00
|
|
|
|
|
|
|
if ($add_phids) {
|
|
|
|
$dst_objects = id(new PhabricatorObjectQuery())
|
|
|
|
->setViewer($viewer)
|
|
|
|
->withPHIDs($phids)
|
|
|
|
->setRaisePolicyExceptions(true)
|
|
|
|
->execute();
|
|
|
|
$dst_objects = mpull($dst_objects, null, 'getPHID');
|
|
|
|
} else {
|
|
|
|
$dst_objects = array();
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
foreach ($add_phids as $add_phid) {
|
|
|
|
$dst_object = idx($dst_objects, $add_phid);
|
|
|
|
if (!$dst_object) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'You can not create a relationship to object "%s" because '.
|
|
|
|
'the object does not exist or could not be loaded.',
|
|
|
|
$add_phid));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$relationship->canRelateObjects($object, $dst_object)) {
|
|
|
|
throw new Exception(
|
|
|
|
pht(
|
|
|
|
'You can not create a relationship (of type "%s") to object '.
|
|
|
|
'"%s" because it is not the right type of object for this '.
|
|
|
|
'relationship.',
|
|
|
|
$relationship->getRelationshipConstant(),
|
|
|
|
$add_phid));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch (Exception $ex) {
|
|
|
|
return $this->newUnrelatableObjectResponse($ex, $done_uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
$editor = $object->getApplicationTransactionEditor()
|
|
|
|
->setActor($viewer)
|
|
|
|
->setContentSourceFromRequest($request)
|
|
|
|
->setContinueOnMissingFields(true)
|
|
|
|
->setContinueOnNoEffect(true);
|
|
|
|
|
|
|
|
$xactions = array();
|
|
|
|
$xactions[] = $object->getApplicationTransactionTemplate()
|
|
|
|
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
|
|
|
->setMetadataValue('edge:type', $edge_type)
|
|
|
|
->setNewValue(array(
|
|
|
|
'+' => array_fuse($add_phids),
|
|
|
|
'-' => array_fuse($rem_phids),
|
|
|
|
));
|
|
|
|
|
|
|
|
try {
|
|
|
|
$editor->applyTransactions($object, $xactions);
|
|
|
|
|
|
|
|
return id(new AphrontRedirectResponse())->setURI($done_uri);
|
|
|
|
} catch (PhabricatorEdgeCycleException $ex) {
|
|
|
|
return $this->newGraphCycleResponse($ex, $done_uri);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
$handles = iterator_to_array($handles);
|
|
|
|
$handles = array_select_keys($handles, $dst_phids);
|
|
|
|
|
|
|
|
// TODO: These are hard-coded for now.
|
|
|
|
$filters = array(
|
|
|
|
'assigned' => pht('Assigned to Me'),
|
|
|
|
'created' => pht('Created By Me'),
|
|
|
|
'open' => pht('All Open Objects'),
|
|
|
|
'all' => pht('All Objects'),
|
|
|
|
);
|
|
|
|
|
|
|
|
$dialog_title = $relationship->getDialogTitleText();
|
|
|
|
$dialog_header = $relationship->getDialogHeaderText();
|
|
|
|
$dialog_button = $relationship->getDialogButtonText();
|
|
|
|
$dialog_instructions = $relationship->getDialogInstructionsText();
|
|
|
|
|
|
|
|
// TODO: Remove this, this is just legacy support.
|
|
|
|
$legacy_kinds = array(
|
|
|
|
ManiphestTaskHasCommitEdgeType::EDGECONST => 'CMIT',
|
|
|
|
ManiphestTaskHasMockEdgeType::EDGECONST => 'MOCK',
|
|
|
|
ManiphestTaskHasRevisionEdgeType::EDGECONST => 'DREV',
|
|
|
|
);
|
|
|
|
|
|
|
|
$edge_type = $relationship->getEdgeConstant();
|
|
|
|
$legacy_kind = idx($legacy_kinds, $edge_type);
|
|
|
|
if (!$legacy_kind) {
|
|
|
|
throw new Exception(
|
|
|
|
pht('Only specific legacy relationships are supported!'));
|
|
|
|
}
|
|
|
|
|
|
|
|
return id(new PhabricatorObjectSelectorDialog())
|
|
|
|
->setUser($viewer)
|
2016-06-22 17:20:50 +02:00
|
|
|
->setInitialPHIDs($initial_phids)
|
2016-06-22 17:04:14 +02:00
|
|
|
->setHandles($handles)
|
|
|
|
->setFilters($filters)
|
|
|
|
->setSelectedFilter('created')
|
|
|
|
->setExcluded($phid)
|
|
|
|
->setCancelURI($done_uri)
|
|
|
|
->setSearchURI("/search/select/{$legacy_kind}/edge/")
|
|
|
|
->setTitle($dialog_title)
|
|
|
|
->setHeader($dialog_header)
|
|
|
|
->setButtonText($dialog_button)
|
|
|
|
->setInstructions($dialog_instructions)
|
|
|
|
->buildDialog();
|
|
|
|
}
|
|
|
|
|
|
|
|
private function newGraphCycleResponse(
|
|
|
|
PhabricatorEdgeCycleException $ex,
|
|
|
|
$done_uri) {
|
|
|
|
|
|
|
|
$viewer = $this->getViewer();
|
|
|
|
$cycle = $ex->getCycle();
|
|
|
|
|
|
|
|
$handles = $this->loadViewerHandles($cycle);
|
|
|
|
$names = array();
|
|
|
|
foreach ($cycle as $cycle_phid) {
|
|
|
|
$names[] = $handles[$cycle_phid]->getFullName();
|
|
|
|
}
|
|
|
|
|
|
|
|
$message = pht(
|
|
|
|
'You can not create that relationship because it would create a '.
|
|
|
|
'circular dependency: %s.',
|
|
|
|
implode(" \xE2\x86\x92 ", $names));
|
|
|
|
|
|
|
|
return $this->newDialog()
|
|
|
|
->setTitle(pht('Circular Dependency'))
|
|
|
|
->appendParagraph($message)
|
|
|
|
->addCancelButton($done_uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
private function newUnrelatableObjectResponse(Exception $ex, $done_uri) {
|
|
|
|
$message = $ex->getMessage();
|
|
|
|
|
|
|
|
return $this->newDialog()
|
|
|
|
->setTitle(pht('Invalid Relationship'))
|
|
|
|
->appendParagraph($message)
|
|
|
|
->addCancelButton($done_uri);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|