diff --git a/src/applications/phriction/controller/PhrictionDocumentController.php b/src/applications/phriction/controller/PhrictionDocumentController.php --- a/src/applications/phriction/controller/PhrictionDocumentController.php +++ b/src/applications/phriction/controller/PhrictionDocumentController.php @@ -1,8 +1,5 @@ setViewer($user) ->withIDs(array($new_doc_id)) - ->exectueOne(); + ->executeOne(); $slug_uri = PhrictionDocument::getSlugURI($new_doc->getSlug()); diff --git a/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php b/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php --- a/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php +++ b/src/applications/phriction/phid/PhrictionPHIDTypeDocument.php @@ -25,8 +25,8 @@ array $phids) { return id(new PhrictionDocumentQuery()) - ->needContent(true) - ->withPHIDs($phids); + ->withPHIDs($phids) + ->needContent(true); } public function loadHandles( diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php --- a/src/applications/phriction/query/PhrictionDocumentQuery.php +++ b/src/applications/phriction/query/PhrictionDocumentQuery.php @@ -49,21 +49,97 @@ } protected function loadPage() { - $document = new PhrictionDocument(); - $conn_r = $document->establishConnection('r'); + $table = new PhrictionDocument(); + $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', - $document->getTableName(), + $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); - return $document->loadAllFromArray($rows); + $documents = $table->loadAllFromArray($rows); + + if ($documents) { + $ancestor_slugs = array(); + foreach ($documents as $key => $document) { + $document_slug = $document->getSlug(); + foreach (PhabricatorSlug::getAncestry($document_slug) as $ancestor) { + $ancestor_slugs[$ancestor][] = $key; + } + } + + if ($ancestor_slugs) { + $ancestors = queryfx_all( + $conn_r, + 'SELECT * FROM %T WHERE slug IN (%Ls)', + $document->getTableName(), + array_keys($ancestor_slugs)); + $ancestors = $table->loadAllFromArray($ancestors); + $ancestors = mpull($ancestors, null, 'getSlug'); + + foreach ($ancestor_slugs as $ancestor_slug => $document_keys) { + $ancestor = idx($ancestors, $ancestor_slug); + foreach ($document_keys as $document_key) { + $documents[$document_key]->attachAncestor( + $ancestor_slug, + $ancestor); + } + } + } + } + + return $documents; } protected function willFilterPage(array $documents) { + // To view a Phriction document, you must also be able to view all of the + // ancestor documents. Filter out documents which have ancestors that are + // not visible. + + $document_map = array(); + foreach ($documents as $document) { + $document_map[$document->getSlug()] = $document; + foreach ($document->getAncestors() as $key => $ancestor) { + if ($ancestor) { + $document_map[$key] = $ancestor; + } + } + } + + $filtered_map = $this->applyPolicyFilter( + $document_map, + array(PhabricatorPolicyCapability::CAN_VIEW)); + + // Filter all of the documents where a parent is not visible. + foreach ($documents as $document_key => $document) { + // If the document itself is not visible, filter it. + if (!isset($filtered_map[$document->getSlug()])) { + $this->didRejectResult($documents[$document_key]); + unset($documents[$document_key]); + continue; + } + + // If an ancestor exists but is not visible, filter the document. + foreach ($document->getAncestors() as $ancestor_key => $ancestor) { + if (!$ancestor) { + continue; + } + + if (!isset($filtered_map[$ancestor_key])) { + $this->didRejectResult($documents[$document_key]); + unset($documents[$document_key]); + break; + } + } + } + + if (!$documents) { + return $documents; + } + if ($this->needContent) { $contents = id(new PhrictionContent())->loadAllWhere( 'id IN (%Ld)', diff --git a/src/applications/phriction/storage/PhrictionDocument.php b/src/applications/phriction/storage/PhrictionDocument.php --- a/src/applications/phriction/storage/PhrictionDocument.php +++ b/src/applications/phriction/storage/PhrictionDocument.php @@ -1,8 +1,5 @@ getProject(); } + public function getAncestors() { + return $this->ancestors; + } + + public function getAncestor($slug) { + return $this->assertAttachedKey($this->ancestors, $slug); + } + + public function attachAncestor($slug, $ancestor) { + $this->ancestors[$slug] = $ancestor; + return $this; + } + public static function isProjectSlug($slug) { $slug = PhabricatorSlug::normalize($slug); $prefix = 'projects/'; @@ -119,6 +130,7 @@ if ($this->hasProject()) { return $this->getProject()->getPolicy($capability); } + return PhabricatorPolicies::POLICY_USER; } @@ -134,6 +146,14 @@ return pht( "This is a project wiki page, and inherits the project's policies."); } + + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return pht( + 'To view a wiki document, you must also be able to view all '. + 'of its parents.'); + } + return null; } diff --git a/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php --- a/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php +++ b/src/infrastructure/query/policy/PhabricatorPolicyAwareQuery.php @@ -302,19 +302,32 @@ private function getPolicyFilter() { $filter = new PhabricatorPolicyFilter(); $filter->setViewer($this->viewer); - if (!$this->capabilities) { - $capabilities = array( - PhabricatorPolicyCapability::CAN_VIEW, - ); - } else { - $capabilities = $this->capabilities; - } + $capabilities = $this->getRequiredCapabilities(); $filter->requireCapabilities($capabilities); $filter->raisePolicyExceptions($this->shouldRaisePolicyExceptions()); return $filter; } + protected function getRequiredCapabilities() { + if ($this->capabilities) { + return $this->capabilities; + } + + return array( + PhabricatorPolicyCapability::CAN_VIEW, + ); + } + + protected function applyPolicyFilter(array $objects, array $capabilities) { + if ($this->shouldDisablePolicyFiltering()) { + return $objects; + } + $filter = $this->getPolicyFilter(); + $filter->requireCapabilities($capabilities); + return $filter->apply($objects); + } + protected function didRejectResult(PhabricatorPolicyInterface $object) { $this->getPolicyFilter()->rejectObject( $object,