diff --git a/src/applications/phriction/query/PhrictionDocumentQuery.php b/src/applications/phriction/query/PhrictionDocumentQuery.php index 8d35430d64..35b4ee1675 100644 --- a/src/applications/phriction/query/PhrictionDocumentQuery.php +++ b/src/applications/phriction/query/PhrictionDocumentQuery.php @@ -1,274 +1,242 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withSlugs(array $slugs) { $this->slugs = $slugs; return $this; } public function withStatus($status) { $this->status = $status; return $this; } public function needContent($need_content) { $this->needContent = $need_content; return $this; } public function setOrder($order) { $this->order = $order; return $this; } protected function loadPage() { $table = new PhrictionDocument(); $conn_r = $table->establishConnection('r'); $rows = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); $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)', mpull($documents, 'getContentID')); foreach ($documents as $key => $document) { $content_id = $document->getContentID(); if (empty($contents[$content_id])) { unset($documents[$key]); continue; } $document->attachContent($contents[$content_id]); } } - foreach ($documents as $document) { - $document->attachProject(null); - } - - $project_slugs = array(); - foreach ($documents as $key => $document) { - $slug = $document->getSlug(); - if (!PhrictionDocument::isProjectSlug($slug)) { - continue; - } - $project_slugs[$key] = PhrictionDocument::getProjectSlugIdentifier($slug); - } - - if ($project_slugs) { - $projects = id(new PhabricatorProjectQuery()) - ->setViewer($this->getViewer()) - ->withPhrictionSlugs($project_slugs) - ->execute(); - $projects = mpull($projects, null, 'getPhrictionSlug'); - foreach ($documents as $key => $document) { - $slug = idx($project_slugs, $key); - if ($slug) { - $project = idx($projects, $slug); - if (!$project) { - unset($documents[$key]); - continue; - } - $document->attachProject($project); - } - } - } - return $documents; } protected function buildWhereClause(AphrontDatabaseConnection $conn) { $where = array(); if ($this->ids) { $where[] = qsprintf( $conn, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn, 'phid IN (%Ls)', $this->phids); } if ($this->slugs) { $where[] = qsprintf( $conn, 'slug IN (%Ls)', $this->slugs); } switch ($this->status) { case self::STATUS_OPEN: $where[] = qsprintf( $conn, 'status NOT IN (%Ld)', array( PhrictionDocumentStatus::STATUS_DELETED, PhrictionDocumentStatus::STATUS_MOVED, PhrictionDocumentStatus::STATUS_STUB, )); break; case self::STATUS_NONSTUB: $where[] = qsprintf( $conn, 'status NOT IN (%Ld)', array( PhrictionDocumentStatus::STATUS_MOVED, PhrictionDocumentStatus::STATUS_STUB, )); break; case self::STATUS_ANY: break; default: throw new Exception("Unknown status '{$this->status}'!"); } $where[] = $this->buildPagingClause($conn); return $this->formatWhereClause($where); } protected function getPagingColumn() { switch ($this->order) { case self::ORDER_CREATED: return 'id'; case self::ORDER_UPDATED: return 'contentID'; default: throw new Exception("Unknown order '{$this->order}'!"); } } protected function getPagingValue($result) { switch ($this->order) { case self::ORDER_CREATED: return $result->getID(); case self::ORDER_UPDATED: return $result->getContentID(); default: throw new Exception("Unknown order '{$this->order}'!"); } } public function getQueryApplicationClass() { return 'PhabricatorPhrictionApplication'; } } diff --git a/src/applications/phriction/query/PhrictionSearchEngine.php b/src/applications/phriction/query/PhrictionSearchEngine.php index 9e9a64d2d4..8b3f174f16 100644 --- a/src/applications/phriction/query/PhrictionSearchEngine.php +++ b/src/applications/phriction/query/PhrictionSearchEngine.php @@ -1,188 +1,180 @@ setParameter('status', $request->getArr('status')); $saved->setParameter('order', $request->getArr('order')); return $saved; } public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) { $query = id(new PhrictionDocumentQuery()) ->needContent(true) ->withStatus(PhrictionDocumentQuery::STATUS_NONSTUB); $status = $saved->getParameter('status'); $status = idx($this->getStatusValues(), $status); if ($status) { $query->withStatus($status); } $order = $saved->getParameter('order'); $order = idx($this->getOrderValues(), $order); if ($order) { $query->setOrder($order); } return $query; } public function buildSearchForm( AphrontFormView $form, PhabricatorSavedQuery $saved_query) { $form ->appendChild( id(new AphrontFormSelectControl()) ->setLabel(pht('Status')) ->setName('status') ->setOptions($this->getStatusOptions()) ->setValue($saved_query->getParameter('status'))) ->appendChild( id(new AphrontFormSelectControl()) ->setLabel(pht('Order')) ->setName('order') ->setOptions($this->getOrderOptions()) ->setValue($saved_query->getParameter('order'))); } protected function getURI($path) { return '/phriction/'.$path; } public function getBuiltinQueryNames() { $names = array( 'active' => pht('Active'), 'updated' => pht('Updated'), 'all' => pht('All'), ); return $names; } public function buildSavedQueryFromBuiltin($query_key) { $query = $this->newSavedQuery(); $query->setQueryKey($query_key); switch ($query_key) { case 'active': return $query->setParameter('status', 'active'); case 'all': return $query; case 'updated': return $query->setParameter('order', 'updated'); } return parent::buildSavedQueryFromBuiltin($query_key); } private function getStatusOptions() { return array( 'active' => pht('Show Active Documents'), 'all' => pht('Show All Documents'), ); } private function getStatusValues() { return array( 'active' => PhrictionDocumentQuery::STATUS_OPEN, 'all' => PhrictionDocumentQuery::STATUS_NONSTUB, ); } private function getOrderOptions() { return array( 'created' => pht('Date Created'), 'updated' => pht('Date Updated'), ); } private function getOrderValues() { return array( 'created' => PhrictionDocumentQuery::ORDER_CREATED, 'updated' => PhrictionDocumentQuery::ORDER_UPDATED, ); } protected function getRequiredHandlePHIDsForResultList( array $documents, PhabricatorSavedQuery $query) { $phids = array(); foreach ($documents as $document) { $content = $document->getContent(); - if ($document->hasProject()) { - $phids[] = $document->getProject()->getPHID(); - } $phids[] = $content->getAuthorPHID(); } return $phids; } protected function renderResultList( array $documents, PhabricatorSavedQuery $query, array $handles) { assert_instances_of($documents, 'PhrictionDocument'); $viewer = $this->requireViewer(); $list = new PHUIObjectItemListView(); $list->setUser($viewer); foreach ($documents as $document) { $content = $document->getContent(); $slug = $document->getSlug(); $author_phid = $content->getAuthorPHID(); $slug_uri = PhrictionDocument::getSlugURI($slug); $byline = pht( 'Edited by %s', $handles[$author_phid]->renderLink()); $updated = phabricator_datetime( $content->getDateCreated(), $viewer); $item = id(new PHUIObjectItemView()) ->setHeader($content->getTitle()) ->setHref($slug_uri) ->addByline($byline) ->addIcon('none', $updated); - if ($document->hasProject()) { - $item->addAttribute( - $handles[$document->getProject()->getPHID()]->renderLink()); - } - $item->addAttribute($slug_uri); switch ($document->getStatus()) { case PhrictionDocumentStatus::STATUS_DELETED: $item->setDisabled(true); $item->addIcon('delete', pht('Deleted')); break; case PhrictionDocumentStatus::STATUS_MOVED: $item->setDisabled(true); $item->addIcon('arrow-right', pht('Moved Away')); break; } $list->addItem($item); } return $list; } } diff --git a/src/applications/phriction/storage/PhrictionDocument.php b/src/applications/phriction/storage/PhrictionDocument.php index 070b3106ce..40606fc768 100644 --- a/src/applications/phriction/storage/PhrictionDocument.php +++ b/src/applications/phriction/storage/PhrictionDocument.php @@ -1,266 +1,252 @@ true, self::CONFIG_TIMESTAMPS => false, self::CONFIG_COLUMN_SCHEMA => array( 'slug' => 'sort128', 'depth' => 'uint32', 'contentID' => 'id?', 'status' => 'uint32', 'mailKey' => 'bytes20', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, 'phid' => array( 'columns' => array('phid'), 'unique' => true, ), 'slug' => array( 'columns' => array('slug'), 'unique' => true, ), 'depth' => array( 'columns' => array('depth', 'slug'), 'unique' => true, ), ), ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhrictionDocumentPHIDType::TYPECONST); } public static function initializeNewDocument(PhabricatorUser $actor, $slug) { $document = new PhrictionDocument(); $document->setSlug($slug); $content = new PhrictionContent(); $content->setSlug($slug); $default_title = PhabricatorSlug::getDefaultTitle($slug); $content->setTitle($default_title); $document->attachContent($content); $parent_doc = null; $ancestral_slugs = PhabricatorSlug::getAncestry($slug); if ($ancestral_slugs) { $parent = end($ancestral_slugs); $parent_doc = id(new PhrictionDocumentQuery()) ->setViewer($actor) ->withSlugs(array($parent)) ->executeOne(); } if ($parent_doc) { $document->setViewPolicy($parent_doc->getViewPolicy()); $document->setEditPolicy($parent_doc->getEditPolicy()); } else { $default_view_policy = PhabricatorPolicies::getMostOpenPolicy(); $document->setViewPolicy($default_view_policy); $document->setEditPolicy(PhabricatorPolicies::POLICY_USER); } return $document; } public function save() { if (!$this->getMailKey()) { $this->setMailKey(Filesystem::readRandomCharacters(20)); } return parent::save(); } public static function getSlugURI($slug, $type = 'document') { static $types = array( 'document' => '/w/', 'history' => '/phriction/history/', ); if (empty($types[$type])) { throw new Exception("Unknown URI type '{$type}'!"); } $prefix = $types[$type]; if ($slug == '/') { return $prefix; } else { // NOTE: The effect here is to escape non-latin characters, since modern // browsers deal with escaped UTF8 characters in a reasonable way (showing // the user a readable URI) but older programs may not. $slug = phutil_escape_uri($slug); return $prefix.$slug; } } public function setSlug($slug) { $this->slug = PhabricatorSlug::normalize($slug); $this->depth = PhabricatorSlug::getDepth($slug); return $this; } public function attachContent(PhrictionContent $content) { $this->contentObject = $content; return $this; } public function getContent() { return $this->assertAttached($this->contentObject); } - public function getProject() { - return $this->assertAttached($this->project); - } - - public function attachProject(PhabricatorProject $project = null) { - $this->project = $project; - return $this; - } - - public function hasProject() { - return (bool)$this->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/'; if ($slug == $prefix) { // The 'projects/' document is not itself a project slug. return false; } return !strncmp($slug, $prefix, strlen($prefix)); } public static function getProjectSlugIdentifier($slug) { if (!self::isProjectSlug($slug)) { throw new Exception("Slug '{$slug}' is not a project slug!"); } $slug = PhabricatorSlug::normalize($slug); $parts = explode('/', $slug); return $parts[1].'/'; } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $user) { return false; } public function describeAutomaticCapability($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return pht( 'To view a wiki document, you must also be able to view all '. 'of its parents.'); case PhabricatorPolicyCapability::CAN_EDIT: return pht( 'To edit a wiki document, you must also be able to view all '. 'of its parents.'); } return null; } /* -( PhabricatorSubscribableInterface )----------------------------------- */ public function isAutomaticallySubscribed($phid) { return false; } public function shouldShowSubscribersProperty() { return true; } public function shouldAllowSubscription($phid) { return true; } /* -( PhabricatorTokenReceiverInterface )---------------------------------- */ public function getUsersToNotifyOfTokenGiven() { return PhabricatorSubscribersQuery::loadSubscribersForPHID($this->phid); } /* -( PhabricatorDestructibleInterface )----------------------------------- */ public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { $this->openTransaction(); $this->delete(); $contents = id(new PhrictionContent())->loadAllWhere( 'documentID = %d', $this->getID()); foreach ($contents as $content) { $content->delete(); } $this->saveTransaction(); } }