diff --git a/src/applications/maniphest/relationship/ManiphestTaskCloseAsDuplicateRelationship.php b/src/applications/maniphest/relationship/ManiphestTaskCloseAsDuplicateRelationship.php index 696ceb615e..6428c79b55 100644 --- a/src/applications/maniphest/relationship/ManiphestTaskCloseAsDuplicateRelationship.php +++ b/src/applications/maniphest/relationship/ManiphestTaskCloseAsDuplicateRelationship.php @@ -1,84 +1,85 @@ setSelectedFilter('open'); } public function getRequiredRelationshipCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function canUndoRelationship() { return false; } public function willUpdateRelationships($object, array $add, array $rem) { // TODO: Communicate this in the UI before users hit this error. if (count($add) > 1) { throw new Exception( pht( 'A task can only be closed as a duplicate of exactly one other '. 'task.')); } $task = head($add); return $this->newMergeIntoTransactions($task); } public function didUpdateRelationships($object, array $add, array $rem) { $viewer = $this->getViewer(); $content_source = $this->getContentSource(); $task = head($add); $xactions = $this->newMergeFromTransactions(array($object)); $task->getApplicationTransactionEditor() ->setActor($viewer) ->setContentSource($content_source) ->setContinueOnMissingFields(true) ->setContinueOnNoEffect(true) ->applyTransactions($task, $xactions); } } diff --git a/src/applications/maniphest/relationship/ManiphestTaskHasParentRelationship.php b/src/applications/maniphest/relationship/ManiphestTaskHasParentRelationship.php index 6210e3c4d4..6cfd11ff09 100644 --- a/src/applications/maniphest/relationship/ManiphestTaskHasParentRelationship.php +++ b/src/applications/maniphest/relationship/ManiphestTaskHasParentRelationship.php @@ -1,44 +1,45 @@ setSelectedFilter('open'); } } diff --git a/src/applications/maniphest/relationship/ManiphestTaskHasSubtaskRelationship.php b/src/applications/maniphest/relationship/ManiphestTaskHasSubtaskRelationship.php index 335ac03d42..2fb69e90fb 100644 --- a/src/applications/maniphest/relationship/ManiphestTaskHasSubtaskRelationship.php +++ b/src/applications/maniphest/relationship/ManiphestTaskHasSubtaskRelationship.php @@ -1,44 +1,45 @@ setSelectedFilter('open'); } } diff --git a/src/applications/maniphest/relationship/ManiphestTaskMergeInRelationship.php b/src/applications/maniphest/relationship/ManiphestTaskMergeInRelationship.php index 5bf45b7911..c8442c428f 100644 --- a/src/applications/maniphest/relationship/ManiphestTaskMergeInRelationship.php +++ b/src/applications/maniphest/relationship/ManiphestTaskMergeInRelationship.php @@ -1,75 +1,76 @@ setSelectedFilter('open'); } public function getRequiredRelationshipCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function canUndoRelationship() { return false; } public function willUpdateRelationships($object, array $add, array $rem) { return $this->newMergeFromTransactions($add); } public function didUpdateRelationships($object, array $add, array $rem) { $viewer = $this->getViewer(); $content_source = $this->getContentSource(); foreach ($add as $task) { $xactions = $this->newMergeIntoTransactions($object); $task->getApplicationTransactionEditor() ->setActor($viewer) ->setContentSource($content_source) ->setContinueOnMissingFields(true) ->setContinueOnNoEffect(true) ->applyTransactions($task, $xactions); } } } diff --git a/src/applications/search/controller/PhabricatorSearchRelationshipController.php b/src/applications/search/controller/PhabricatorSearchRelationshipController.php index 767c756813..14c705ac62 100644 --- a/src/applications/search/controller/PhabricatorSearchRelationshipController.php +++ b/src/applications/search/controller/PhabricatorSearchRelationshipController.php @@ -1,219 +1,216 @@ getViewer(); $object = $this->loadRelationshipObject(); if (!$object) { return new Aphront404Response(); } $relationship = $this->loadRelationship($object); if (!$relationship) { return new Aphront404Response(); } $src_phid = $object->getPHID(); $edge_type = $relationship->getEdgeConstant(); // If this is a normal relationship, users can remove related objects. If // it's a special relationship like a merge, we can't undo it, so we won't // prefill the current related objects. if ($relationship->canUndoRelationship()) { $dst_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( $src_phid, $edge_type); } else { $dst_phids = array(); } $all_phids = $dst_phids; $all_phids[] = $src_phid; $handles = $viewer->loadHandles($all_phids); $src_handle = $handles[$src_phid]; $done_uri = $src_handle->getURI(); $initial_phids = $dst_phids; if ($request->isFormPost()) { $phids = explode(';', $request->getStr('phids')); $phids = array_filter($phids); $phids = array_values($phids); $initial_phids = $request->getStrList('initialPHIDs'); // 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); $all_phids = array_merge($add_phids, $rem_phids); $capabilities = $relationship->getRequiredRelationshipCapabilities(); if ($all_phids) { $dst_objects = id(new PhabricatorObjectQuery()) ->setViewer($viewer) ->withPHIDs($all_phids) ->setRaisePolicyExceptions(true) ->requireCapabilities($capabilities) ->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 ($add_phid == $src_phid) { throw new Exception( pht( 'You can not create a relationship to object "%s" because '. 'objects can not be related to themselves.', $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); } $content_source = PhabricatorContentSource::newFromRequest($request); $relationship->setContentSource($content_source); $editor = $object->getApplicationTransactionEditor() ->setActor($viewer) ->setContentSource($content_source) ->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), )); $add_objects = array_select_keys($dst_objects, $add_phids); $rem_objects = array_select_keys($dst_objects, $rem_phids); if ($add_objects || $rem_objects) { $more_xactions = $relationship->willUpdateRelationships( $object, $add_objects, $rem_objects); foreach ($more_xactions as $xaction) { $xactions[] = $xaction; } } try { $editor->applyTransactions($object, $xactions); if ($add_objects || $rem_objects) { $relationship->didUpdateRelationships( $object, $add_objects, $rem_objects); } 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(); $source_uri = $relationship->getSourceURI($object); + $source = $relationship->newSource(); + + $filters = $source->getFilters(); + $selected_filter = $source->getSelectedFilter(); + return id(new PhabricatorObjectSelectorDialog()) ->setUser($viewer) ->setInitialPHIDs($initial_phids) ->setHandles($handles) ->setFilters($filters) - ->setSelectedFilter('created') + ->setSelectedFilter($selected_filter) ->setExcluded($src_phid) ->setCancelURI($done_uri) ->setSearchURI($source_uri) ->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:'); $list = implode(" \xE2\x86\x92 ", $names); return $this->newDialog() ->setTitle(pht('Circular Dependency')) ->appendParagraph($message) ->appendParagraph($list) ->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); } } diff --git a/src/applications/search/relationship/DiffusionCommitRelationshipSource.php b/src/applications/search/relationship/DiffusionCommitRelationshipSource.php index 31fc918011..25c799caf4 100644 --- a/src/applications/search/relationship/DiffusionCommitRelationshipSource.php +++ b/src/applications/search/relationship/DiffusionCommitRelationshipSource.php @@ -1,20 +1,26 @@ getViewer(); return PhabricatorApplication::isClassInstalledForViewer( 'PhabricatorDiffusionApplication', $viewer); } public function getResultPHIDTypes() { return array( PhabricatorRepositoryCommitPHIDType::TYPECONST, ); } + public function getFilters() { + $filters = parent::getFilters(); + unset($filters['assigned']); + return $filters; + } + } diff --git a/src/applications/search/relationship/PhabricatorObjectRelationshipSource.php b/src/applications/search/relationship/PhabricatorObjectRelationshipSource.php index a1fc9a6370..9990740b4f 100644 --- a/src/applications/search/relationship/PhabricatorObjectRelationshipSource.php +++ b/src/applications/search/relationship/PhabricatorObjectRelationshipSource.php @@ -1,19 +1,48 @@ viewer = $viewer; return $this; } final public function getViewer() { return $this->viewer; } abstract public function isEnabledForObject($object); abstract public function getResultPHIDTypes(); + protected function getDefaultFilter() { + return 'created'; + } + + final public function setSelectedFilter($selected_filter) { + $this->selectedFilter = $selected_filter; + return $this; + } + + final public function getSelectedFilter() { + if ($this->selectedFilter === null) { + return $this->getDefaultFilter(); + } + + return $this->selectedFilter; + } + + public function getFilters() { + // TODO: These are hard-coded for now, and all of this will probably be + // rewritten when we move to ApplicationSearch. + return array( + 'assigned' => pht('Assigned to Me'), + 'created' => pht('Created By Me'), + 'open' => pht('All Open Objects'), + 'all' => pht('All Objects'), + ); + } + } diff --git a/src/applications/search/relationship/PholioMockRelationshipSource.php b/src/applications/search/relationship/PholioMockRelationshipSource.php index b378f8ef41..b21fd6624b 100644 --- a/src/applications/search/relationship/PholioMockRelationshipSource.php +++ b/src/applications/search/relationship/PholioMockRelationshipSource.php @@ -1,20 +1,26 @@ getViewer(); return PhabricatorApplication::isClassInstalledForViewer( 'PhabricatorPholioApplication', $viewer); } public function getResultPHIDTypes() { return array( PholioMockPHIDType::TYPECONST, ); } + public function getFilters() { + $filters = parent::getFilters(); + unset($filters['assigned']); + return $filters; + } + } diff --git a/src/applications/search/storage/document/PhabricatorSearchDocument.php b/src/applications/search/storage/document/PhabricatorSearchDocument.php index 161e791316..3e177c9813 100644 --- a/src/applications/search/storage/document/PhabricatorSearchDocument.php +++ b/src/applications/search/storage/document/PhabricatorSearchDocument.php @@ -1,37 +1,40 @@ false, self::CONFIG_IDS => self::IDS_MANUAL, self::CONFIG_COLUMN_SCHEMA => array( 'documentType' => 'text4', 'documentTitle' => 'text255', 'documentCreated' => 'epoch', 'documentModified' => 'epoch', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, 'PRIMARY' => array( 'columns' => array('phid'), 'unique' => true, ), 'documentCreated' => array( 'columns' => array('documentCreated'), ), + 'key_type' => array( + 'columns' => array('documentType', 'documentCreated'), + ), ), ) + parent::getConfiguration(); } public function getIDKey() { return 'phid'; } }