diff --git a/src/applications/config/phid/PhabricatorConfigPHIDTypeConfig.php b/src/applications/config/phid/PhabricatorConfigPHIDTypeConfig.php index 04234c27b4..ae297edbef 100644 --- a/src/applications/config/phid/PhabricatorConfigPHIDTypeConfig.php +++ b/src/applications/config/phid/PhabricatorConfigPHIDTypeConfig.php @@ -1,48 +1,49 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $entry = $objects[$phid]; $key = $entry->getConfigKey(); $handle->setName($key); $handle->setURI("/config/edit/{$key}/"); } } public function canLoadNamedObject($name) { return false; } } diff --git a/src/applications/conpherence/phid/PhabricatorConpherencePHIDTypeThread.php b/src/applications/conpherence/phid/PhabricatorConpherencePHIDTypeThread.php index 5e360ad642..dba81f2642 100644 --- a/src/applications/conpherence/phid/PhabricatorConpherencePHIDTypeThread.php +++ b/src/applications/conpherence/phid/PhabricatorConpherencePHIDTypeThread.php @@ -1,49 +1,50 @@ withPHIDs($phids) ->setViewer($query->getViewer()) + ->setParentQuery($query) + ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $thread = $objects[$phid]; $name = $thread->getTitle(); if (!strlen($name)) { $name = pht('[No Title]'); } $handle->setName($name); $handle->setFullName($name); $handle->setURI('/conpherence/'.$thread->getID().'/'); } } } diff --git a/src/applications/countdown/phid/PhabricatorCountdownPHIDTypeCountdown.php b/src/applications/countdown/phid/PhabricatorCountdownPHIDTypeCountdown.php index c2661c02a3..c816a1054d 100644 --- a/src/applications/countdown/phid/PhabricatorCountdownPHIDTypeCountdown.php +++ b/src/applications/countdown/phid/PhabricatorCountdownPHIDTypeCountdown.php @@ -1,75 +1,76 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $countdown = $objects[$phid]; $name = $countdown->getTitle(); $id = $countdown->getID(); $handle->setName("C{$id}"); $handle->setFullName("C{$id}: {$name}"); $handle->setURI("/countdown/{$id}/"); } } public function canLoadNamedObject($name) { return preg_match('/^C\d*[1-9]\d*$/i', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $id_map = array(); foreach ($names as $name) { $id = (int)substr($name, 1); $id_map[$id][] = $name; } $objects = id(new PhabricatorCountdownQuery()) ->setViewer($query->getViewer()) ->withIDs(array_keys($id_map)) ->execute(); $results = array(); foreach ($objects as $id => $object) { foreach (idx($id_map, $id, array()) as $name) { $results[$name] = $object; } } return $results; } } diff --git a/src/applications/differential/phid/DifferentialPHIDTypeRevision.php b/src/applications/differential/phid/DifferentialPHIDTypeRevision.php index 3a2ff3647d..414afc7d0d 100644 --- a/src/applications/differential/phid/DifferentialPHIDTypeRevision.php +++ b/src/applications/differential/phid/DifferentialPHIDTypeRevision.php @@ -1,85 +1,86 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { static $closed_statuses = array( ArcanistDifferentialRevisionStatus::CLOSED => true, ArcanistDifferentialRevisionStatus::ABANDONED => true, ); foreach ($handles as $phid => $handle) { $revision = $objects[$phid]; $title = $revision->getTitle(); $id = $revision->getID(); $status = $revision->getStatus(); $handle->setName("D{$id}"); $handle->setURI("/D{$id}"); $handle->setFullName("D{$id}: {$title}"); if (isset($closed_statuses[$status])) { $handle->setStatus(PhabricatorObjectHandleStatus::STATUS_CLOSED); } } } public function canLoadNamedObject($name) { return preg_match('/^D\d*[1-9]\d*$/i', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $id_map = array(); foreach ($names as $name) { $id = (int)substr($name, 1); $id_map[$id][] = $name; } $objects = id(new DifferentialRevisionQuery()) ->setViewer($query->getViewer()) ->withIDs(array_keys($id_map)) ->execute(); $results = array(); foreach ($objects as $id => $object) { foreach (idx($id_map, $id, array()) as $name) { $results[$name] = $object; } } return $results; } } diff --git a/src/applications/diviner/phid/DivinerPHIDTypeAtom.php b/src/applications/diviner/phid/DivinerPHIDTypeAtom.php index 51f8c472c3..a563ec45c6 100644 --- a/src/applications/diviner/phid/DivinerPHIDTypeAtom.php +++ b/src/applications/diviner/phid/DivinerPHIDTypeAtom.php @@ -1,42 +1,43 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $atom = $objects[$phid]; $handle->setName($atom->getTitle()); $handle->setURI($atom->getName()); } } } diff --git a/src/applications/diviner/phid/DivinerPHIDTypeBook.php b/src/applications/diviner/phid/DivinerPHIDTypeBook.php index c36bb1249a..2d80555371 100644 --- a/src/applications/diviner/phid/DivinerPHIDTypeBook.php +++ b/src/applications/diviner/phid/DivinerPHIDTypeBook.php @@ -1,45 +1,46 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $book = $objects[$phid]; $name = $book->getName(); $handle->setName($book->getShortTitle()); $handle->setFullName($book->getTitle()); $handle->setURI("/diviner/book/{$name}/"); } } } diff --git a/src/applications/files/phid/PhabricatorFilePHIDTypeFile.php b/src/applications/files/phid/PhabricatorFilePHIDTypeFile.php index 04c11128e5..50586bee78 100644 --- a/src/applications/files/phid/PhabricatorFilePHIDTypeFile.php +++ b/src/applications/files/phid/PhabricatorFilePHIDTypeFile.php @@ -1,76 +1,77 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $file = $objects[$phid]; $id = $file->getID(); $name = $file->getName(); $uri = $file->getBestURI(); $handle->setName("F{$id}"); $handle->setFullName("F{$id}: {$name}"); $handle->setURI($uri); } } public function canLoadNamedObject($name) { return preg_match('/^F\d*[1-9]\d*$/', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $id_map = array(); foreach ($names as $name) { $id = (int)substr($name, 1); $id_map[$id][] = $name; } $objects = id(new PhabricatorFileQuery()) ->setViewer($query->getViewer()) ->withIDs(array_keys($id_map)) ->execute(); $results = array(); foreach ($objects as $id => $object) { foreach (idx($id_map, $id, array()) as $name) { $results[$name] = $object; } } return $results; } } diff --git a/src/applications/herald/phid/HeraldPHIDTypeRule.php b/src/applications/herald/phid/HeraldPHIDTypeRule.php index 9e517cf61d..1c408cbb46 100644 --- a/src/applications/herald/phid/HeraldPHIDTypeRule.php +++ b/src/applications/herald/phid/HeraldPHIDTypeRule.php @@ -1,45 +1,46 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $rule = $objects[$phid]; $id = $rule->getID(); $name = $rule->getName(); $handle->setName($name); $handle->setURI("/herald/rule/{$id}/"); } } } diff --git a/src/applications/legalpad/phid/PhabricatorLegalpadPHIDTypeDocument.php b/src/applications/legalpad/phid/PhabricatorLegalpadPHIDTypeDocument.php index 345c8252e8..d1e1fcc8cc 100644 --- a/src/applications/legalpad/phid/PhabricatorLegalpadPHIDTypeDocument.php +++ b/src/applications/legalpad/phid/PhabricatorLegalpadPHIDTypeDocument.php @@ -1,76 +1,77 @@ needDocumentBodies(true) - ->withPHIDs($phids) ->setViewer($query->getViewer()) + ->setParentQuery($query) + ->withPHIDs($phids) + ->needDocumentBodies(true) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $document = $objects[$phid]; $name = $document->getDocumentBody()->getTitle(); $handle->setName($name); $handle->setFullName($name); $handle->setURI('/legalpad/view/'.$document->getID().'/'); } } public function canLoadNamedObject($name) { return preg_match('/^L\d*[1-9]\d*$/i', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $id_map = array(); foreach ($names as $name) { $id = (int)substr($name, 1); $id_map[$id][] = $name; } $objects = id(new LegalpadDocumentQuery()) ->setViewer($query->getViewer()) ->withIDs(array_keys($id_map)) ->execute(); $results = array(); foreach ($objects as $id => $object) { foreach (idx($id_map, $id, array()) as $name) { $results[$name] = $object; } } return $results; } } diff --git a/src/applications/macro/phid/PhabricatorMacroPHIDTypeMacro.php b/src/applications/macro/phid/PhabricatorMacroPHIDTypeMacro.php index 906ddcfafd..bc9b7a8d61 100644 --- a/src/applications/macro/phid/PhabricatorMacroPHIDTypeMacro.php +++ b/src/applications/macro/phid/PhabricatorMacroPHIDTypeMacro.php @@ -1,50 +1,51 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $macro = $objects[$phid]; $id = $macro->getID(); $name = $macro->getName(); $handle->setName($name); $handle->setFullName(pht('Image Macro "%s"', $name)); $handle->setURI("/macro/view/{$id}/"); } } public function canLoadNamedObject($name) { return false; } } diff --git a/src/applications/mailinglists/phid/PhabricatorMailingListPHIDTypeList.php b/src/applications/mailinglists/phid/PhabricatorMailingListPHIDTypeList.php index ec9462d97d..eee01d277a 100644 --- a/src/applications/mailinglists/phid/PhabricatorMailingListPHIDTypeList.php +++ b/src/applications/mailinglists/phid/PhabricatorMailingListPHIDTypeList.php @@ -1,46 +1,47 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $list = $objects[$phid]; $handle->setName($list->getName()); $handle->setURI($list->getURI()); } } public function canLoadNamedObject($name) { return false; } } diff --git a/src/applications/maniphest/phid/ManiphestPHIDTypeTask.php b/src/applications/maniphest/phid/ManiphestPHIDTypeTask.php index b81bc354cf..dd076bc511 100644 --- a/src/applications/maniphest/phid/ManiphestPHIDTypeTask.php +++ b/src/applications/maniphest/phid/ManiphestPHIDTypeTask.php @@ -1,78 +1,79 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $task = $objects[$phid]; $id = $task->getID(); $title = $task->getTitle(); $handle->setName("T{$id}"); $handle->setFullName("T{$id}: {$title}"); $handle->setURI("/T{$id}"); if ($task->getStatus() != ManiphestTaskStatus::STATUS_OPEN) { $handle->setStatus(PhabricatorObjectHandleStatus::STATUS_CLOSED); } } } public function canLoadNamedObject($name) { return preg_match('/^T\d*[1-9]\d*$/i', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $id_map = array(); foreach ($names as $name) { $id = (int)substr($name, 1); $id_map[$id][] = $name; } $objects = id(new ManiphestTaskQuery()) ->setViewer($query->getViewer()) ->withIDs(array_keys($id_map)) ->execute(); $results = array(); foreach ($objects as $id => $object) { foreach (idx($id_map, $id, array()) as $name) { $results[$name] = $object; } } return $results; } } diff --git a/src/applications/owners/phid/PhabricatorOwnersPHIDTypePackage.php b/src/applications/owners/phid/PhabricatorOwnersPHIDTypePackage.php index cf8ce0b667..af7976aad0 100644 --- a/src/applications/owners/phid/PhabricatorOwnersPHIDTypePackage.php +++ b/src/applications/owners/phid/PhabricatorOwnersPHIDTypePackage.php @@ -1,45 +1,46 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $package = $objects[$phid]; $name = $package->getName(); $id = $package->getID(); $handle->setName($name); $handle->setURI("/owners/package/{$id}/"); } } } diff --git a/src/applications/paste/phid/PhabricatorPastePHIDTypePaste.php b/src/applications/paste/phid/PhabricatorPastePHIDTypePaste.php index 8e068f23cb..024cdd9f16 100644 --- a/src/applications/paste/phid/PhabricatorPastePHIDTypePaste.php +++ b/src/applications/paste/phid/PhabricatorPastePHIDTypePaste.php @@ -1,75 +1,76 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $paste = $objects[$phid]; $id = $paste->getID(); $name = $paste->getFullName(); $handle->setName("P{$id}"); $handle->setFullName($name); $handle->setURI("/P{$id}"); } } public function canLoadNamedObject($name) { return preg_match('/^P\d*[1-9]\d*$/i', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $id_map = array(); foreach ($names as $name) { $id = (int)substr($name, 1); $id_map[$id][] = $name; } $objects = id(new PhabricatorPasteQuery()) ->setViewer($query->getViewer()) ->withIDs(array_keys($id_map)) ->execute(); $results = array(); foreach ($objects as $id => $object) { foreach (idx($id_map, $id, array()) as $name) { $results[$name] = $object; } } return $results; } } diff --git a/src/applications/people/phid/PhabricatorPeoplePHIDTypeExternal.php b/src/applications/people/phid/PhabricatorPeoplePHIDTypeExternal.php index 4332260034..b3c0044e60 100644 --- a/src/applications/people/phid/PhabricatorPeoplePHIDTypeExternal.php +++ b/src/applications/people/phid/PhabricatorPeoplePHIDTypeExternal.php @@ -1,46 +1,47 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $account = $objects[$phid]; $display_name = $account->getDisplayName(); $handle->setName($display_name); } } public function canLoadNamedObject($name) { return false; } } diff --git a/src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php b/src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php index b9dbc51422..4cf6e21f30 100644 --- a/src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php +++ b/src/applications/people/phid/PhabricatorPeoplePHIDTypeUser.php @@ -1,53 +1,54 @@ needProfileImage(true) - ->needStatus(true) ->setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) + ->needProfileImage(true) + ->needStatus(true) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $user = $objects[$phid]; $handle->setName($user->getUsername()); $handle->setURI('/p/'.$user->getUsername().'/'); $handle->setFullName( $user->getUsername().' ('.$user->getRealName().')'); $handle->setImageURI($user->loadProfileImageURI()); $handle->setDisabled($user->getIsDisabled()); if ($user->hasStatus()) { $status = $user->getStatus(); $handle->setStatus($status->getTextStatus()); $handle->setTitle($status->getTerseSummary($query->getViewer())); } } } } diff --git a/src/applications/phame/phid/PhabricatorPhamePHIDTypeBlog.php b/src/applications/phame/phid/PhabricatorPhamePHIDTypeBlog.php index e13d378a7c..fe24e757b5 100644 --- a/src/applications/phame/phid/PhabricatorPhamePHIDTypeBlog.php +++ b/src/applications/phame/phid/PhabricatorPhamePHIDTypeBlog.php @@ -1,45 +1,46 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $blog = $objects[$phid]; $handle->setName($blog->getName()); $handle->setFullName($blog->getName()); $handle->setURI('/phame/blog/view/'.$blog->getID().'/'); } } } diff --git a/src/applications/phame/phid/PhabricatorPhamePHIDTypePost.php b/src/applications/phame/phid/PhabricatorPhamePHIDTypePost.php index c9556c538f..790b1084f6 100644 --- a/src/applications/phame/phid/PhabricatorPhamePHIDTypePost.php +++ b/src/applications/phame/phid/PhabricatorPhamePHIDTypePost.php @@ -1,45 +1,46 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $post = $objects[$phid]; $handle->setName($post->getTitle()); $handle->setFullName($post->getTitle()); $handle->setURI('/phame/post/view/'.$post->getID().'/'); } } } diff --git a/src/applications/phlux/phid/PhluxPHIDTypeVariable.php b/src/applications/phlux/phid/PhluxPHIDTypeVariable.php index cfb479c329..e6dc1a5f66 100644 --- a/src/applications/phlux/phid/PhluxPHIDTypeVariable.php +++ b/src/applications/phlux/phid/PhluxPHIDTypeVariable.php @@ -1,49 +1,50 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $variable = $objects[$phid]; $key = $variable->getVariableKey(); $handle->setName($key); $handle->setFullName(pht('Variable "%s"', $key)); $handle->setURI("/phlux/view/{$key}/"); } } public function canLoadNamedObject($name) { return false; } } diff --git a/src/applications/pholio/phid/PholioPHIDTypeImage.php b/src/applications/pholio/phid/PholioPHIDTypeImage.php index a9696803b7..87c5dadf48 100644 --- a/src/applications/pholio/phid/PholioPHIDTypeImage.php +++ b/src/applications/pholio/phid/PholioPHIDTypeImage.php @@ -1,47 +1,48 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $image = $objects[$phid]; $id = $image->getID(); $mock_id = $image->getMockID(); $name = $image->getName(); $handle->setURI("/M{$mock_id}/{$id}/"); $handle->setName($name); $handle->setFullName($name); } } } diff --git a/src/applications/pholio/phid/PholioPHIDTypeMock.php b/src/applications/pholio/phid/PholioPHIDTypeMock.php index dfc4467688..ced9f5e9cf 100644 --- a/src/applications/pholio/phid/PholioPHIDTypeMock.php +++ b/src/applications/pholio/phid/PholioPHIDTypeMock.php @@ -1,75 +1,76 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $mock = $objects[$phid]; $id = $mock->getID(); $name = $mock->getName(); $handle->setURI("/M{$id}"); $handle->setName("M{$id}"); $handle->setFullName("M{$id}: {$name}"); } } public function canLoadNamedObject($name) { return preg_match('/^M\d*[1-9]\d*$/i', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $id_map = array(); foreach ($names as $name) { $id = (int)substr($name, 1); $id_map[$id][] = $name; } $objects = id(new PholioMockQuery()) ->setViewer($query->getViewer()) ->withIDs(array_keys($id_map)) ->execute(); $results = array(); foreach ($objects as $id => $object) { foreach (idx($id_map, $id, array()) as $name) { $results[$name] = $object; } } return $results; } } diff --git a/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php b/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php index 3f357d9f68..554457bba0 100644 --- a/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php +++ b/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php @@ -1,55 +1,56 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $document = $objects[$phid]; $content = $document->getContent(); $title = $content->getTitle(); $slug = $document->getSlug(); $status = $document->getStatus(); $handle->setName($title); $handle->setURI(PhrictionDocument::getSlugURI($slug)); if ($status != PhrictionDocumentStatus::STATUS_EXISTS) { $handle->setStatus(PhabricatorObjectHandleStatus::STATUS_CLOSED); } } } public function canLoadNamedObject($name) { return false; } } diff --git a/src/applications/ponder/phid/PonderPHIDTypeAnswer.php b/src/applications/ponder/phid/PonderPHIDTypeAnswer.php index 388677e156..b6d10c2d7a 100644 --- a/src/applications/ponder/phid/PonderPHIDTypeAnswer.php +++ b/src/applications/ponder/phid/PonderPHIDTypeAnswer.php @@ -1,45 +1,46 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $answer = $objects[$phid]; $id = $answer->getID(); $qid = $answer->getQuestionID(); $handle->setName("Answer {$id}"); $handle->setURI("/Q{$qid}#A{$id}"); } } } diff --git a/src/applications/ponder/phid/PonderPHIDTypeQuestion.php b/src/applications/ponder/phid/PonderPHIDTypeQuestion.php index aba99a254d..32a81a5d28 100644 --- a/src/applications/ponder/phid/PonderPHIDTypeQuestion.php +++ b/src/applications/ponder/phid/PonderPHIDTypeQuestion.php @@ -1,75 +1,76 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $question = $objects[$phid]; $id = $question->getID(); $title = $question->getTitle(); $handle->setName("Q{$id}"); $handle->setURI("/Q{$id}"); $handle->setFullName("Q{$id}: {$title}"); } } public function canLoadNamedObject($name) { return preg_match('/^Q\d*[1-9]\d*$/i', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $id_map = array(); foreach ($names as $name) { $id = (int)substr($name, 1); $id_map[$id][] = $name; } $objects = id(new PonderQuestionQuery()) ->setViewer($query->getViewer()) ->withIDs(array_keys($id_map)) ->execute(); $results = array(); foreach ($objects as $id => $object) { foreach (idx($id_map, $id, array()) as $name) { $results[$name] = $object; } } return $results; } } diff --git a/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php b/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php index 82b6936341..e160caebcb 100644 --- a/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php +++ b/src/applications/project/phid/PhabricatorProjectPHIDTypeProject.php @@ -1,50 +1,51 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $project = $objects[$phid]; $name = $project->getName(); $id = $project->getID(); $handle->setName($name); $handle->setURI("/project/view/{$id}/"); } } public function canLoadNamedObject($name) { // TODO: We should be able to load named projects by hashtag, e.g. "#yolo". return false; } } diff --git a/src/applications/releeph/phid/ReleephPHIDTypeBranch.php b/src/applications/releeph/phid/ReleephPHIDTypeBranch.php index b4a51a1399..7106dfc9a1 100644 --- a/src/applications/releeph/phid/ReleephPHIDTypeBranch.php +++ b/src/applications/releeph/phid/ReleephPHIDTypeBranch.php @@ -1,47 +1,48 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $branch = $objects[$phid]; $handle->setURI($branch->getURI()); $handle->setName($branch->getBasename()); $handle->setFullName($branch->getName()); } } public function canLoadNamedObject($name) { return false; } } diff --git a/src/applications/releeph/phid/ReleephPHIDTypeProject.php b/src/applications/releeph/phid/ReleephPHIDTypeProject.php index 753dff0315..f127b75849 100644 --- a/src/applications/releeph/phid/ReleephPHIDTypeProject.php +++ b/src/applications/releeph/phid/ReleephPHIDTypeProject.php @@ -1,46 +1,47 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $project = $objects[$phid]; $handle->setName($project->getName()); $handle->setURI($project->getURI()); } } public function canLoadNamedObject($name) { return false; } } diff --git a/src/applications/releeph/phid/ReleephPHIDTypeRequest.php b/src/applications/releeph/phid/ReleephPHIDTypeRequest.php index 116cda9503..a3529a612c 100644 --- a/src/applications/releeph/phid/ReleephPHIDTypeRequest.php +++ b/src/applications/releeph/phid/ReleephPHIDTypeRequest.php @@ -1,50 +1,51 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $request = $objects[$phid]; $id = $request->getID(); $title = $request->getSummaryForDisplay(); $handle->setURI("/RQ{$id}"); $handle->setName($title); $handle->setFullName("RQ{$id}: {$title}"); } } public function canLoadNamedObject($name) { return false; } } diff --git a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeArcanistProject.php b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeArcanistProject.php index 47f7df3f9a..49f24131a8 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeArcanistProject.php +++ b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeArcanistProject.php @@ -1,43 +1,45 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $project = $objects[$phid]; $handle->setName($project->getName()); } } } diff --git a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeCommit.php b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeCommit.php index 72ed350e22..34f4c4be57 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeCommit.php +++ b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeCommit.php @@ -1,86 +1,87 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $commit = $objects[$phid]; $repository = $commit->getRepository(); $callsign = $repository->getCallsign(); $commit_identifier = $commit->getCommitIdentifier(); $name = $repository->formatCommitName($commit_identifier); $summary = $commit->getSummary(); if (strlen($summary)) { $full_name = $name.': '.$summary; } else { $full_name = $name; } $handle->setName($name); $handle->setFullName($full_name); $handle->setURI('/r'.$callsign.$commit_identifier); $handle->setTimestamp($commit->getEpoch()); } } public static function getCommitObjectNamePattern() { $min_unqualified = PhabricatorRepository::MINIMUM_UNQUALIFIED_HASH; $min_qualified = PhabricatorRepository::MINIMUM_QUALIFIED_HASH; return 'r[A-Z]+[1-9]\d*'. '|'. 'r[A-Z]+[a-f0-9]{'.$min_qualified.',40}'. '|'. '[a-f0-9]{'.$min_unqualified.',40}'; } public function canLoadNamedObject($name) { $pattern = self::getCommitObjectNamePattern(); return preg_match('(^'.$pattern.'$)', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $query = id(new DiffusionCommitQuery()) ->setViewer($query->getViewer()) ->withIdentifiers($names); $query->execute(); return $query->getIdentifierMap(); } } diff --git a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeRepository.php b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeRepository.php index cfda831fd9..70e4c8bf48 100644 --- a/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeRepository.php +++ b/src/applications/repository/phid/PhabricatorRepositoryPHIDTypeRepository.php @@ -1,77 +1,78 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $repository = $objects[$phid]; $callsign = $repository->getCallsign(); $name = $repository->getName(); $handle->setName("r{$callsign}"); $handle->setFullName("r{$callsign} ({$name})"); $handle->setURI("/diffusion/{$callsign}/"); } } public function canLoadNamedObject($name) { return preg_match('/^r[A-Z]+$/', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $id_map = array(); foreach ($names as $name) { $id = substr($name, 1); $id_map[$id][] = $name; } $objects = id(new PhabricatorRepositoryQuery()) ->setViewer($query->getViewer()) ->withCallsigns(array_keys($id_map)) ->execute(); $results = array(); foreach ($objects as $object) { $callsign = $object->getCallsign(); foreach (idx($id_map, $callsign, array()) as $name) { $results[$name] = $object; } } return $results; } } diff --git a/src/applications/slowvote/phid/PhabricatorSlowvotePHIDTypePoll.php b/src/applications/slowvote/phid/PhabricatorSlowvotePHIDTypePoll.php index 6eaf8d3230..7f40a8a7ac 100644 --- a/src/applications/slowvote/phid/PhabricatorSlowvotePHIDTypePoll.php +++ b/src/applications/slowvote/phid/PhabricatorSlowvotePHIDTypePoll.php @@ -1,72 +1,73 @@ setViewer($query->getViewer()) + ->setParentQuery($query) ->withPHIDs($phids) ->execute(); } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { foreach ($handles as $phid => $handle) { $poll = $objects[$phid]; $handle->setName('V'.$poll->getID()); $handle->setFullName('V'.$poll->getID().': '.$poll->getQuestion()); $handle->setURI('/V'.$poll->getID()); } } public function canLoadNamedObject($name) { return preg_match('/^V\d*[1-9]\d*$/i', $name); } public function loadNamedObjects( PhabricatorObjectQuery $query, array $names) { $id_map = array(); foreach ($names as $name) { $id = (int)substr($name, 1); $id_map[$id][] = $name; } $objects = id(new PhabricatorSlowvoteQuery()) ->setViewer($query->getViewer()) ->withIDs(array_keys($id_map)) ->execute(); $results = array(); foreach ($objects as $id => $object) { foreach (idx($id_map, $id, array()) as $name) { $results[$name] = $object; } } return $results; } } diff --git a/src/applications/transactions/phid/PhabricatorApplicationTransactionPHIDTypeTransaction.php b/src/applications/transactions/phid/PhabricatorApplicationTransactionPHIDTypeTransaction.php index 4e59f7ce20..d818dad226 100644 --- a/src/applications/transactions/phid/PhabricatorApplicationTransactionPHIDTypeTransaction.php +++ b/src/applications/transactions/phid/PhabricatorApplicationTransactionPHIDTypeTransaction.php @@ -1,79 +1,80 @@ setAncestorClass('PhabricatorApplicationTransactionQuery') ->loadObjects(); $queries = array(); foreach ($objects as $object) { $type = $object ->getTemplateApplicationTransaction() ->getApplicationTransactionType(); $queries[$type] = $object; } } $phid_subtypes = array(); foreach ($phids as $phid) { $subtype = phid_get_subtype($phid); if ($subtype) { $phid_subtypes[$subtype][] = $phid; } } $results = array(); foreach ($phid_subtypes as $subtype => $subtype_phids) { $query = idx($queries, $subtype); if (!$query) { continue; } $xactions = id(clone $query) ->setViewer($object_query->getViewer()) + ->setParentQuery($object_query) ->withPHIDs($subtype_phids) ->execute(); $results += mpull($xactions, null, 'getPHID'); } return $results; } public function loadHandles( PhabricatorHandleQuery $query, array $handles, array $objects) { // NOTE: We don't produce meaningful handles here because they're // impractical to produce and no application uses them. } } diff --git a/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php index b16a47db73..a5e74c6a2b 100644 --- a/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php @@ -1,349 +1,401 @@ setViewer($user) * ->withConstraint($example) * ->execute(); * * Normally, you should extend @{class:PhabricatorCursorPagedPolicyAwareQuery}, * not this class. @{class:PhabricatorCursorPagedPolicyAwareQuery} provides a * more practical interface for building usable queries against most object * types. * * NOTE: Although this class extends @{class:PhabricatorOffsetPagedQuery}, * offset paging with policy filtering is not efficient. All results must be * loaded into the application and filtered here: skipping `N` rows via offset * is an `O(N)` operation with a large constant. Prefer cursor-based paging * with @{class:PhabricatorCursorPagedPolicyAwareQuery}, which can filter far * more efficiently in MySQL. * * @task config Query Configuration * @task exec Executing Queries * @task policyimpl Policy Query Implementation */ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery { private $viewer; private $raisePolicyExceptions; + private $parentQuery; private $rawResultLimit; private $capabilities; /* -( Query Configuration )------------------------------------------------ */ /** * Set the viewer who is executing the query. Results will be filtered * according to the viewer's capabilities. You must set a viewer to execute * a policy query. * * @param PhabricatorUser The viewing user. * @return this * @task config */ final public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; return $this; } /** * Get the query's viewer. * * @return PhabricatorUser The viewing user. * @task config */ final public function getViewer() { return $this->viewer; } + /** + * Set the parent query of this query. This is useful for nested queries so + * that configuration like whether or not to raise policy exceptions is + * seamlessly passed along to child queries. + * + * @return this + * @task config + */ + final public function setParentQuery(PhabricatorPolicyAwareQuery $query) { + $this->parentQuery = $query; + return $this; + } + + + /** + * Get the parent query. See @{method:setParentQuery} for discussion. + * + * @return PhabricatorPolicyAwareQuery The parent query. + * @task config + */ + final public function getParentQuery() { + return $this->parentQuery; + } + + + /** + * Hook to configure whether this query should raise policy exceptions. + * + * @return this + * @task config + */ + final public function setRaisePolicyExceptions($bool) { + $this->raisePolicyExceptions = $bool; + return $this; + } + + + /** + * @return bool + * @task config + */ + final public function shouldRaisePolicyExceptions() { + return (bool) $this->raisePolicyExceptions; + } + + /** * @task config */ final public function requireCapabilities(array $capabilities) { $this->capabilities = $capabilities; return $this; } /* -( Query Execution )---------------------------------------------------- */ /** * Execute the query, expecting a single result. This method simplifies * loading objects for detail pages or edit views. * * // Load one result by ID. * $obj = id(new ExampleQuery()) * ->setViewer($user) * ->withIDs(array($id)) * ->executeOne(); * if (!$obj) { * return new Aphront404Response(); * } * * If zero results match the query, this method returns `null`. - * * If one result matches the query, this method returns that result. * * If two or more results match the query, this method throws an exception. * You should use this method only when the query constraints guarantee at * most one match (e.g., selecting a specific ID or PHID). * * If one result matches the query but it is caught by the policy filter (for * example, the user is trying to view or edit an object which exists but * which they do not have permission to see) a policy exception is thrown. * * @return mixed Single result, or null. * @task exec */ final public function executeOne() { - $this->raisePolicyExceptions = true; + $this->setRaisePolicyExceptions(true); try { $results = $this->execute(); } catch (Exception $ex) { - $this->raisePolicyExceptions = false; + $this->setRaisePolicyExceptions(false); throw $ex; } if (count($results) > 1) { throw new Exception("Expected a single result!"); } if (!$results) { return null; } return head($results); } /** * Execute the query, loading all visible results. * * @return list Result objects. * @task exec */ final public function execute() { if (!$this->viewer) { throw new Exception("Call setViewer() before execute()!"); } + $parent_query = $this->getParentQuery(); + if ($parent_query) { + $this->setRaisePolicyExceptions( + $parent_query->shouldRaisePolicyExceptions()); + } + $results = array(); $filter = new PhabricatorPolicyFilter(); $filter->setViewer($this->viewer); if (!$this->capabilities) { $capabilities = array( PhabricatorPolicyCapability::CAN_VIEW, ); } else { $capabilities = $this->capabilities; } $filter->requireCapabilities($capabilities); - $filter->raisePolicyExceptions($this->raisePolicyExceptions); + $filter->raisePolicyExceptions($this->shouldRaisePolicyExceptions()); $offset = (int)$this->getOffset(); $limit = (int)$this->getLimit(); $count = 0; if ($limit) { $need = $offset + $limit; } else { $need = 0; } $this->willExecute(); do { if ($need) { $this->rawResultLimit = min($need - $count, 1024); } else { $this->rawResultLimit = 0; } try { $page = $this->loadPage(); } catch (PhabricatorEmptyQueryException $ex) { $page = array(); } if ($page) { $maybe_visible = $this->willFilterPage($page); } else { $maybe_visible = array(); } if ($this->shouldDisablePolicyFiltering()) { $visible = $maybe_visible; } else { $visible = $filter->apply($maybe_visible); } $removed = array(); foreach ($maybe_visible as $key => $object) { if (empty($visible[$key])) { $removed[$key] = $object; } } $this->didFilterResults($removed); foreach ($visible as $key => $result) { ++$count; // If we have an offset, we just ignore that many results and start // storing them only once we've hit the offset. This reduces memory // requirements for large offsets, compared to storing them all and // slicing them away later. if ($count > $offset) { $results[$key] = $result; } if ($need && ($count >= $need)) { // If we have all the rows we need, break out of the paging query. break 2; } } if (!$this->rawResultLimit) { // If we don't have a load count, we loaded all the results. We do // not need to load another page. break; } if (count($page) < $this->rawResultLimit) { // If we have a load count but the unfiltered results contained fewer // objects, we know this was the last page of objects; we do not need // to load another page because we can deduce it would be empty. break; } $this->nextPage($page); } while (true); $results = $this->didLoadResults($results); return $results; } /* -( Policy Query Implementation )---------------------------------------- */ /** * Get the number of results @{method:loadPage} should load. If the value is * 0, @{method:loadPage} should load all available results. * * @return int The number of results to load, or 0 for all results. * @task policyimpl */ final protected function getRawResultLimit() { return $this->rawResultLimit; } /** * Hook invoked before query execution. Generally, implementations should * reset any internal cursors. * * @return void * @task policyimpl */ protected function willExecute() { return; } /** * Load a raw page of results. Generally, implementations should load objects * from the database. They should attempt to return the number of results * hinted by @{method:getRawResultLimit}. * * @return list List of filterable policy objects. * @task policyimpl */ abstract protected function loadPage(); /** * Update internal state so that the next call to @{method:loadPage} will * return new results. Generally, you should adjust a cursor position based * on the provided result page. * * @param list The current page of results. * @return void * @task policyimpl */ abstract protected function nextPage(array $page); /** * Hook for applying a page filter prior to the privacy filter. This allows * you to drop some items from the result set without creating problems with * pagination or cursor updates. * * This method will only be called if data is available. Implementations * do not need to handle the case of no results specially. * * @param list Results from `loadPage()`. * @return list Objects for policy filtering. * @task policyimpl */ protected function willFilterPage(array $page) { return $page; } /** * Hook for removing filtered results from alternate result sets. This * hook will be called with any objects which were returned by the query but * filtered for policy reasons. The query should remove them from any cached * or partial result sets. * * @param list List of objects that should not be returned by alternate * result mechanisms. * @return void * @task policyimpl */ protected function didFilterResults(array $results) { return; } /** * Hook for applying final adjustments before results are returned. This is * used by @{class:PhabricatorCursorPagedPolicyAwareQuery} to reverse results * that are queried during reverse paging. * * @param list Query results. * @return list Final results. * @task policyimpl */ protected function didLoadResults(array $results) { return $results; } /** * Allows a subclass to disable policy filtering. This method is dangerous. * It should be used only if the query loads data which has already been * filtered (for example, because it wraps some other query which uses * normal policy filtering). * * @return bool True to disable all policy filtering. * @task policyimpl */ protected function shouldDisablePolicyFiltering() { return false; } }