diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 02edc3b6dc..f845fc8ed6 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -505,6 +505,7 @@ phutil_register_library_map(array( 'DivinerArticleAtomizer' => 'applications/diviner/atomizer/DivinerArticleAtomizer.php', 'DivinerAtom' => 'applications/diviner/atom/DivinerAtom.php', 'DivinerAtomCache' => 'applications/diviner/cache/DivinerAtomCache.php', + 'DivinerAtomController' => 'applications/diviner/controller/DivinerAtomController.php', 'DivinerAtomListController' => 'applications/diviner/controller/DivinerAtomListController.php', 'DivinerAtomQuery' => 'applications/diviner/query/DivinerAtomQuery.php', 'DivinerAtomRef' => 'applications/diviner/atom/DivinerAtomRef.php', @@ -2326,6 +2327,7 @@ phutil_register_library_map(array( 'DiffusionView' => 'AphrontView', 'DivinerArticleAtomizer' => 'DivinerAtomizer', 'DivinerAtomCache' => 'DivinerDiskCache', + 'DivinerAtomController' => 'DivinerController', 'DivinerAtomListController' => array( 0 => 'DivinerController', diff --git a/src/applications/diviner/application/PhabricatorApplicationDiviner.php b/src/applications/diviner/application/PhabricatorApplicationDiviner.php index 5e0cf503ae..00c5bcae85 100644 --- a/src/applications/diviner/application/PhabricatorApplicationDiviner.php +++ b/src/applications/diviner/application/PhabricatorApplicationDiviner.php @@ -24,6 +24,13 @@ final class PhabricatorApplicationDiviner extends PhabricatorApplication { '' => 'DivinerLegacyController', 'query/((?[^/]+)/)?' => 'DivinerAtomListController', ), + '/docs/(?P[^/]+)/' => 'DivinerJumpController', + '/book/'. + '(?P[^/]+)/'. + '(?P[^/]+)/'. + '(?:(?P[^/]+)/)?'. + '(?P[^/]+)/'. + '(?:(?P\d+)/)?' => 'DivinerAtomController', ); } diff --git a/src/applications/diviner/controller/DivinerAtomController.php b/src/applications/diviner/controller/DivinerAtomController.php new file mode 100644 index 0000000000..21779c8a63 --- /dev/null +++ b/src/applications/diviner/controller/DivinerAtomController.php @@ -0,0 +1,84 @@ +bookName = $data['book']; + $this->atomType = $data['type']; + $this->atomName = $data['name']; + $this->atomContext = nonempty(idx($data, 'context'), null); + $this->atomIndex = nonempty(idx($data, 'index'), null); + } + + public function processRequest() { + $request = $this->getRequest(); + $viewer = $request->getUser(); + + $book = id(new DivinerBookQuery()) + ->setViewer($viewer) + ->withNames(array($this->bookName)) + ->executeOne(); + + if (!$book) { + return new Aphront404Response(); + } + + $atom = id(new DivinerAtomQuery()) + ->setViewer($viewer) + ->withBookPHIDs(array($book->getPHID())) + ->withTypes(array($this->atomType)) + ->withNames(array($this->atomName)) + ->withContexts(array($this->atomContext)) + ->withIndexes(array($this->atomIndex)) + ->needAtoms(true) + ->executeOne(); + + if (!$atom) { + return new Aphront404Response(); + } + + $crumbs = $this->buildApplicationCrumbs(); + + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName($book->getName()) + ->setHref('/book/'.$book->getName().'/')); + + $crumbs->addCrumb( + id(new PhabricatorCrumbView()) + ->setName($atom->getName())); + + $header = id(new PhabricatorHeaderView())->setHeader($atom->getName()); + + $document = id(new PHUIDocumentView()) + ->appendChild( + phutil_tag( + 'div', + array( + 'class' => 'phabricator-remarkup', + ), + phutil_safe_html($atom->getContent()))); + + return $this->buildApplicationPage( + array( + $crumbs, + $document, + ), + array( + 'title' => $atom->getName(), + 'dust' => true, + 'device' => true, + )); + } + +} diff --git a/src/applications/diviner/controller/DivinerAtomListController.php b/src/applications/diviner/controller/DivinerAtomListController.php index f43180ba0a..ce42535208 100644 --- a/src/applications/diviner/controller/DivinerAtomListController.php +++ b/src/applications/diviner/controller/DivinerAtomListController.php @@ -35,6 +35,7 @@ final class DivinerAtomListController extends DivinerController foreach ($symbols as $symbol) { $item = id(new PhabricatorObjectItemView()) ->setHeader($symbol->getName()) + ->setHref($symbol->getURI()) ->addIcon('none', $symbol->getType()); $list->addItem($item); diff --git a/src/applications/diviner/query/DivinerAtomQuery.php b/src/applications/diviner/query/DivinerAtomQuery.php index 8dfcf774ba..e59bc01e88 100644 --- a/src/applications/diviner/query/DivinerAtomQuery.php +++ b/src/applications/diviner/query/DivinerAtomQuery.php @@ -5,6 +5,13 @@ final class DivinerAtomQuery private $ids; private $phids; + private $bookPHIDs; + private $names; + private $types; + private $contexts; + private $indexes; + + private $needAtoms; public function withIDs(array $ids) { $this->ids = $ids; @@ -16,6 +23,36 @@ final class DivinerAtomQuery return $this; } + public function withBookPHIDs(array $phids) { + $this->bookPHIDs = $phids; + return $this; + } + + public function withTypes(array $types) { + $this->types = $types; + return $this; + } + + public function withNames(array $names) { + $this->names = $names; + return $this; + } + + public function withContexts(array $contexts) { + $this->contexts = $contexts; + return $this; + } + + public function withIndexes(array $indexes) { + $this->indexes = $indexes; + return $this; + } + + public function needAtoms($need) { + $this->needAtoms = $need; + return $this; + } + protected function loadPage() { $table = new DivinerLiveSymbol(); $conn_r = $table->establishConnection('r'); @@ -53,6 +90,22 @@ final class DivinerAtomQuery $atom->attachBook($book); } + if ($this->needAtoms) { + $atom_data = id(new DivinerLiveAtom())->loadAllWhere( + 'symbolPHID IN (%Ls)', + mpull($atoms, 'getPHID')); + $atom_data = mpull($atom_data, null, 'getSymbolPHID'); + + foreach ($atoms as $key => $atom) { + $data = idx($atom_data, $atom->getPHID()); + if (!$data) { + unset($atoms[$key]); + continue; + } + $atom->attachAtom($data); + } + } + return $atoms; } @@ -73,6 +126,62 @@ final class DivinerAtomQuery $this->phids); } + if ($this->bookPHIDs) { + $where[] = qsprintf( + $conn_r, + 'bookPHID IN (%Ls)', + $this->bookPHIDs); + } + + if ($this->types) { + $where[] = qsprintf( + $conn_r, + 'type IN (%Ls)', + $this->types); + } + + if ($this->names) { + $where[] = qsprintf( + $conn_r, + 'name IN (%Ls)', + $this->names); + } + + if ($this->contexts) { + $with_null = false; + $contexts = $this->contexts; + foreach ($contexts as $key => $value) { + if ($value === null) { + unset($contexts[$key]); + $with_null = true; + continue; + } + } + + if ($contexts && $with_null) { + $where[] = qsprintf( + $conn_r, + 'context IN (%Ls) OR context IS NULL', + $contexts); + } else if ($contexts) { + $where[] = qsprintf( + $conn_r, + 'context IN (%Ls)', + $contexts); + } else if ($with_null) { + $where[] = qsprintf( + $conn_r, + 'context IS NULL'); + } + } + + if ($this->indexes) { + $where[] = qsprintf( + $conn_r, + 'atomIndex IN (%Ld)', + $this->indexes); + } + $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); diff --git a/src/applications/diviner/query/DivinerBookQuery.php b/src/applications/diviner/query/DivinerBookQuery.php index 68dd85a424..af6fe1d717 100644 --- a/src/applications/diviner/query/DivinerBookQuery.php +++ b/src/applications/diviner/query/DivinerBookQuery.php @@ -5,6 +5,7 @@ final class DivinerBookQuery private $ids; private $phids; + private $names; public function withIDs(array $ids) { $this->ids = $ids; @@ -16,6 +17,11 @@ final class DivinerBookQuery return $this; } + public function withNames(array $names) { + $this->names = $names; + return $this; + } + protected function loadPage() { $table = new DivinerLiveBook(); $conn_r = $table->establishConnection('r'); @@ -48,6 +54,13 @@ final class DivinerBookQuery $this->phids); } + if ($this->names) { + $where[] = qsprintf( + $conn_r, + 'name IN (%Ls)', + $this->names); + } + $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); diff --git a/src/applications/diviner/storage/DivinerLiveSymbol.php b/src/applications/diviner/storage/DivinerLiveSymbol.php index 069e4437b6..a14db72be5 100644 --- a/src/applications/diviner/storage/DivinerLiveSymbol.php +++ b/src/applications/diviner/storage/DivinerLiveSymbol.php @@ -13,6 +13,8 @@ final class DivinerLiveSymbol extends DivinerDAO protected $identityHash; private $book; + private $content; + private $atom; public function getConfiguration() { return array( @@ -38,6 +40,46 @@ final class DivinerLiveSymbol extends DivinerDAO return $this; } + public function getContent() { + if ($this->content === null) { + throw new Exception("Call attachAtom() before getContent()!"); + } + return $this->content; + } + + public function getAtom() { + if ($this->atom === null) { + throw new Exception("Call attachAtom() before getAtom()!"); + } + return $this->atom; + } + + public function attachAtom(DivinerLiveAtom $atom) { + $this->content = $atom->getContent(); + $this->atom = DivinerAtom::newFromDictionary($atom->getAtomData()); + return $this; + } + + public function getURI() { + $parts = array( + 'book', + $this->getBook()->getName(), + $this->getType(), + ); + + if ($this->getContext()) { + $parts[] = $this->getContext(); + } + + $parts[] = $this->getName(); + + if ($this->getAtomIndex()) { + $parts[] = $this->getAtomIndex(); + } + + return '/'.implode('/', $parts).'/'; + } + public function save() { // NOTE: The identity hash is just a sanity check because the unique tuple