예제 #1
0
 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;
 }
예제 #2
0
 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;
 }
 public function testSlugAncestry()
 {
     $slugs = array('/' => array(), 'pokemon/' => array('/'), 'pokemon/squirtle/' => array('/', 'pokemon/'));
     foreach ($slugs as $slug => $ancestry) {
         $this->assertEqual($ancestry, PhabricatorSlug::getAncestry($slug), "Ancestry of '{$slug}'");
     }
 }
예제 #4
0
 public function renderBreadcrumbs($slug)
 {
     $ancestor_handles = array();
     $ancestral_slugs = PhabricatorSlug::getAncestry($slug);
     $ancestral_slugs[] = $slug;
     if ($ancestral_slugs) {
         $empty_slugs = array_fill_keys($ancestral_slugs, null);
         $ancestors = id(new PhrictionDocumentQuery())->setViewer($this->getRequest()->getUser())->withSlugs($ancestral_slugs)->execute();
         $ancestors = mpull($ancestors, null, 'getSlug');
         $ancestor_phids = mpull($ancestors, 'getPHID');
         $handles = array();
         if ($ancestor_phids) {
             $handles = $this->loadViewerHandles($ancestor_phids);
         }
         $ancestor_handles = array();
         foreach ($ancestral_slugs as $slug) {
             if (isset($ancestors[$slug])) {
                 $ancestor_handles[] = $handles[$ancestors[$slug]->getPHID()];
             } else {
                 $handle = new PhabricatorObjectHandle();
                 $handle->setName(PhabricatorSlug::getDefaultTitle($slug));
                 $handle->setURI(PhrictionDocument::getSlugURI($slug));
                 $ancestor_handles[] = $handle;
             }
         }
     }
     $breadcrumbs = array();
     foreach ($ancestor_handles as $ancestor_handle) {
         $breadcrumbs[] = id(new PHUICrumbView())->setName($ancestor_handle->getName())->setHref($ancestor_handle->getUri());
     }
     return $breadcrumbs;
 }
 private function renderDocumentChildren($slug)
 {
     $document_dao = new PhrictionDocument();
     $content_dao = new PhrictionContent();
     $conn = $document_dao->establishConnection('r');
     $limit = 250;
     $d_child = PhabricatorSlug::getDepth($slug) + 1;
     $d_grandchild = PhabricatorSlug::getDepth($slug) + 2;
     // Select children and grandchildren.
     $children = queryfx_all($conn, 'SELECT d.slug, d.depth, c.title FROM %T d JOIN %T c
     ON d.contentID = c.id
     WHERE d.slug LIKE %> AND d.depth IN (%d, %d)
       AND d.status IN (%Ld)
     ORDER BY d.depth, c.title LIMIT %d', $document_dao->getTableName(), $content_dao->getTableName(), $slug == '/' ? '' : $slug, $d_child, $d_grandchild, array(PhrictionDocumentStatus::STATUS_EXISTS, PhrictionDocumentStatus::STATUS_STUB), $limit);
     if (!$children) {
         return;
     }
     // We're going to render in one of three modes to try to accommodate
     // different information scales:
     //
     //  - If we found fewer than $limit rows, we know we have all the children
     //    and grandchildren and there aren't all that many. We can just render
     //    everything.
     //  - If we found $limit rows but the results included some grandchildren,
     //    we just throw them out and render only the children, as we know we
     //    have them all.
     //  - If we found $limit rows and the results have no grandchildren, we
     //    have a ton of children. Render them and then let the user know that
     //    this is not an exhaustive list.
     if (count($children) == $limit) {
         $more_children = true;
         foreach ($children as $child) {
             if ($child['depth'] == $d_grandchild) {
                 $more_children = false;
             }
         }
         $show_grandchildren = false;
     } else {
         $show_grandchildren = true;
         $more_children = false;
     }
     $grandchildren = array();
     foreach ($children as $key => $child) {
         if ($child['depth'] == $d_child) {
             continue;
         } else {
             unset($children[$key]);
             if ($show_grandchildren) {
                 $ancestors = PhabricatorSlug::getAncestry($child['slug']);
                 $grandchildren[end($ancestors)][] = $child;
             }
         }
     }
     // Fill in any missing children.
     $known_slugs = ipull($children, null, 'slug');
     foreach ($grandchildren as $slug => $ignored) {
         if (empty($known_slugs[$slug])) {
             $children[] = array('slug' => $slug, 'depth' => $d_child, 'title' => PhabricatorSlug::getDefaultTitle($slug), 'empty' => true);
         }
     }
     $children = isort($children, 'title');
     $list = array();
     foreach ($children as $child) {
         $list[] = hsprintf('<li>');
         $list[] = $this->renderChildDocumentLink($child);
         $grand = idx($grandchildren, $child['slug'], array());
         if ($grand) {
             $list[] = hsprintf('<ul>');
             foreach ($grand as $grandchild) {
                 $list[] = hsprintf('<li>');
                 $list[] = $this->renderChildDocumentLink($grandchild);
                 $list[] = hsprintf('</li>');
             }
             $list[] = hsprintf('</ul>');
         }
         $list[] = hsprintf('</li>');
     }
     if ($more_children) {
         $list[] = phutil_tag('li', array(), pht('More...'));
     }
     $content = array(phutil_tag('div', array('class' => 'phriction-children-header ' . 'sprite-gradient gradient-lightblue-header'), pht('Document Hierarchy')), phutil_tag('div', array('class' => 'phriction-children'), phutil_tag('ul', array(), $list)));
     return id(new PHUIDocumentView())->setOffset(true)->appendChild($content);
 }
 private function renderDocumentChildren($slug)
 {
     $d_child = PhabricatorSlug::getDepth($slug) + 1;
     $d_grandchild = PhabricatorSlug::getDepth($slug) + 2;
     $limit = 250;
     $query = id(new PhrictionDocumentQuery())->setViewer($this->getRequest()->getUser())->withDepths(array($d_child, $d_grandchild))->withSlugPrefix($slug == '/' ? '' : $slug)->withStatuses(array(PhrictionDocumentStatus::STATUS_EXISTS, PhrictionDocumentStatus::STATUS_STUB))->setLimit($limit)->setOrder(PhrictionDocumentQuery::ORDER_HIERARCHY)->needContent(true);
     $children = $query->execute();
     if (!$children) {
         return;
     }
     // We're going to render in one of three modes to try to accommodate
     // different information scales:
     //
     //  - If we found fewer than $limit rows, we know we have all the children
     //    and grandchildren and there aren't all that many. We can just render
     //    everything.
     //  - If we found $limit rows but the results included some grandchildren,
     //    we just throw them out and render only the children, as we know we
     //    have them all.
     //  - If we found $limit rows and the results have no grandchildren, we
     //    have a ton of children. Render them and then let the user know that
     //    this is not an exhaustive list.
     if (count($children) == $limit) {
         $more_children = true;
         foreach ($children as $child) {
             if ($child->getDepth() == $d_grandchild) {
                 $more_children = false;
             }
         }
         $show_grandchildren = false;
     } else {
         $show_grandchildren = true;
         $more_children = false;
     }
     $children_dicts = array();
     $grandchildren_dicts = array();
     foreach ($children as $key => $child) {
         $child_dict = array('slug' => $child->getSlug(), 'depth' => $child->getDepth(), 'title' => $child->getContent()->getTitle());
         if ($child->getDepth() == $d_child) {
             $children_dicts[] = $child_dict;
             continue;
         } else {
             unset($children[$key]);
             if ($show_grandchildren) {
                 $ancestors = PhabricatorSlug::getAncestry($child->getSlug());
                 $grandchildren_dicts[end($ancestors)][] = $child_dict;
             }
         }
     }
     // Fill in any missing children.
     $known_slugs = mpull($children, null, 'getSlug');
     foreach ($grandchildren_dicts as $slug => $ignored) {
         if (empty($known_slugs[$slug])) {
             $children_dicts[] = array('slug' => $slug, 'depth' => $d_child, 'title' => PhabricatorSlug::getDefaultTitle($slug), 'empty' => true);
         }
     }
     $children_dicts = isort($children_dicts, 'title');
     $list = array();
     foreach ($children_dicts as $child) {
         $list[] = hsprintf('<li class="remarkup-list-item">');
         $list[] = $this->renderChildDocumentLink($child);
         $grand = idx($grandchildren_dicts, $child['slug'], array());
         if ($grand) {
             $list[] = hsprintf('<ul class="remarkup-list">');
             foreach ($grand as $grandchild) {
                 $list[] = hsprintf('<li class="remarkup-list-item">');
                 $list[] = $this->renderChildDocumentLink($grandchild);
                 $list[] = hsprintf('</li>');
             }
             $list[] = hsprintf('</ul>');
         }
         $list[] = hsprintf('</li>');
     }
     if ($more_children) {
         $list[] = phutil_tag('li', array('class' => 'remarkup-list-item'), pht('More...'));
     }
     $header = id(new PHUIHeaderView())->setHeader(pht('Document Hierarchy'));
     $box = id(new PHUIObjectBoxView())->setHeader($header)->appendChild(phutil_tag('div', array('class' => 'phabricator-remarkup mlt mlb'), phutil_tag('ul', array('class' => 'remarkup-list'), $list)));
     return phutil_tag_div('phui-document-box', $box);
 }
 protected function requireCapabilities(PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction)
 {
     /*
      * New objects have a special case. If a user can't see
      *   x/y
      * then definitely don't let them make some
      *   x/y/z
      * We need to load the direct parent to handle this case.
      */
     if ($this->getIsNewObject()) {
         $actor = $this->requireActor();
         $parent_doc = null;
         $ancestral_slugs = PhabricatorSlug::getAncestry($object->getSlug());
         // No ancestral slugs is "/"; the first person gets to play with "/".
         if ($ancestral_slugs) {
             $parent = end($ancestral_slugs);
             $parent_doc = id(new PhrictionDocumentQuery())->setViewer($actor)->withSlugs(array($parent))->executeOne();
             // If the $actor can't see the $parent_doc then they can't create
             // the child $object; throw a policy exception.
             if (!$parent_doc) {
                 id(new PhabricatorPolicyFilter())->setViewer($actor)->raisePolicyExceptions(true)->rejectObject($object, $object->getEditPolicy(), PhabricatorPolicyCapability::CAN_EDIT);
             }
             // If the $actor can't edit the $parent_doc then they can't create
             // the child $object; throw a policy exception.
             if (!PhabricatorPolicyFilter::hasCapability($actor, $parent_doc, PhabricatorPolicyCapability::CAN_EDIT)) {
                 id(new PhabricatorPolicyFilter())->setViewer($actor)->raisePolicyExceptions(true)->rejectObject($object, $object->getEditPolicy(), PhabricatorPolicyCapability::CAN_EDIT);
             }
         }
     }
     return parent::requireCapabilities($object, $xaction);
 }
 private function updateDocument($document, $content, $new_content)
 {
     $is_new = false;
     if (!$document->getID()) {
         $is_new = true;
     }
     $new_content->setVersion($content->getVersion() + 1);
     $change_type = $new_content->getChangeType();
     switch ($change_type) {
         case PhrictionChangeType::CHANGE_EDIT:
             $doc_status = PhrictionDocumentStatus::STATUS_EXISTS;
             $feed_action = $is_new ? PhrictionActionConstants::ACTION_CREATE : PhrictionActionConstants::ACTION_EDIT;
             break;
         case PhrictionChangeType::CHANGE_DELETE:
             $doc_status = PhrictionDocumentStatus::STATUS_DELETED;
             $feed_action = PhrictionActionConstants::ACTION_DELETE;
             if ($is_new) {
                 throw new Exception("You can not delete a document which doesn't exist yet!");
             }
             break;
         case PhrictionChangeType::CHANGE_STUB:
             $doc_status = PhrictionDocumentStatus::STATUS_STUB;
             $feed_action = null;
             break;
         case PhrictionChangeType::CHANGE_MOVE_AWAY:
             $doc_status = PhrictionDocumentStatus::STATUS_MOVED;
             $feed_action = null;
             break;
         case PhrictionChangeType::CHANGE_MOVE_HERE:
             $doc_status = PhrictionDocumentStatus::STATUS_EXISTS;
             $feed_action = PhrictionActionConstants::ACTION_MOVE_HERE;
             break;
         default:
             throw new Exception("Unsupported content change type '{$change_type}'!");
     }
     $document->setStatus($doc_status);
     // TODO: This should be transactional.
     if ($is_new) {
         $document->save();
     }
     $new_content->setDocumentID($document->getID());
     $new_content->save();
     $document->setContentID($new_content->getID());
     $document->save();
     $document->attachContent($new_content);
     id(new PhabricatorSearchIndexer())->queueDocumentForIndexing($document->getPHID());
     // Stub out empty parent documents if they don't exist
     $ancestral_slugs = PhabricatorSlug::getAncestry($document->getSlug());
     if ($ancestral_slugs) {
         $ancestors = id(new PhrictionDocument())->loadAllWhere('slug IN (%Ls)', $ancestral_slugs);
         $ancestors = mpull($ancestors, null, 'getSlug');
         foreach ($ancestral_slugs as $slug) {
             // We check for change type to prevent near-infinite recursion
             if (!isset($ancestors[$slug]) && $new_content->getChangeType() != PhrictionChangeType::CHANGE_STUB) {
                 id(PhrictionDocumentEditor::newForSlug($slug))->setActor($this->getActor())->setTitle(PhabricatorSlug::getDefaultTitle($slug))->setContent('')->setDescription(pht('Empty Parent Document'))->stub();
             }
         }
     }
     $project_phid = null;
     $slug = $document->getSlug();
     if (PhrictionDocument::isProjectSlug($slug)) {
         $project = id(new PhabricatorProjectQuery())->setViewer($this->requireActor())->withPhrictionSlugs(array(PhrictionDocument::getProjectSlugIdentifier($slug)))->executeOne();
         if ($project) {
             $project_phid = $project->getPHID();
         }
     }
     $related_phids = array($document->getPHID(), $this->getActor()->getPHID());
     if ($project_phid) {
         $related_phids[] = $project_phid;
     }
     if ($this->fromDocumentPHID) {
         $related_phids[] = $this->fromDocumentPHID;
     }
     if ($feed_action) {
         $content_str = id(new PhutilUTF8StringTruncator())->setMaximumGlyphs(140)->truncateString($new_content->getContent());
         id(new PhabricatorFeedStoryPublisher())->setRelatedPHIDs($related_phids)->setStoryAuthorPHID($this->getActor()->getPHID())->setStoryTime(time())->setStoryType(PhabricatorFeedStoryTypeConstants::STORY_PHRICTION)->setStoryData(array('phid' => $document->getPHID(), 'action' => $feed_action, 'content' => $content_str, 'project' => $project_phid, 'movedFromPHID' => $this->fromDocumentPHID))->publish();
     }
     // TODO: Migrate to ApplicationTransactions fast, so we get rid of this code
     $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID($document->getPHID());
     $this->sendMailToSubscribers($subscribers, $content);
     return $this;
 }
 protected function willFilterPage(array $documents)
 {
     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) {
             $table = new PhrictionDocument();
             $conn_r = $table->establishConnection('r');
             $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);
                 }
             }
         }
     }
     // 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]);
         }
     }
     return $documents;
 }