diff --git a/resources/sql/autopatches/20150529.divinerrepository.sql b/resources/sql/autopatches/20150529.divinerrepository.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20150529.divinerrepository.sql @@ -0,0 +1,5 @@ +ALTER TABLE {$NAMESPACE}_diviner.diviner_livebook + ADD COLUMN repositoryPHID VARBINARY(64) AFTER name; + +ALTER TABLE {$NAMESPACE}_diviner.diviner_livesymbol + ADD COLUMN repositoryPHID VARBINARY(64) AFTER bookPHID; diff --git a/src/applications/diviner/controller/DivinerBookController.php b/src/applications/diviner/controller/DivinerBookController.php --- a/src/applications/diviner/controller/DivinerBookController.php +++ b/src/applications/diviner/controller/DivinerBookController.php @@ -38,6 +38,14 @@ ->setPolicyObject($book) ->setEpoch($book->getDateModified()); + if ($book->getRepositoryPHID()) { + $header->addTag( + id(new PHUITagView()) + ->setType(PHUITagView::TYPE_STATE) + ->setBackgroundColor(PHUITagView::COLOR_BLUE) + ->setName($book->getRepository()->getMonogram())); + } + $document = new PHUIDocumentView(); $document->setHeader($header); $document->addClass('diviner-view'); diff --git a/src/applications/diviner/controller/DivinerController.php b/src/applications/diviner/controller/DivinerController.php --- a/src/applications/diviner/controller/DivinerController.php +++ b/src/applications/diviner/controller/DivinerController.php @@ -28,8 +28,8 @@ $user = $request->getUser(); $list = array(); - foreach ($symbols as $symbol) { + foreach ($symbols as $symbol) { switch ($symbol->getType()) { case DivinerAtom::TYPE_FUNCTION: $title = $symbol->getTitle().'()'; @@ -43,8 +43,7 @@ ->setTitle($title) ->setHref($symbol->getURI()) ->setSubtitle($symbol->getSummary()) - ->setType(DivinerAtom::getAtomTypeNameString( - $symbol->getType())); + ->setType(DivinerAtom::getAtomTypeNameString($symbol->getType())); $list[] = $item; } diff --git a/src/applications/diviner/publisher/DivinerLivePublisher.php b/src/applications/diviner/publisher/DivinerLivePublisher.php --- a/src/applications/diviner/publisher/DivinerLivePublisher.php +++ b/src/applications/diviner/publisher/DivinerLivePublisher.php @@ -4,19 +4,37 @@ private $book; - private function loadBook() { + protected function getBook() { if (!$this->book) { $book_name = $this->getConfig('name'); $book = id(new DivinerLiveBook())->loadOneWhere('name = %s', $book_name); if (!$book) { - $book = id(new DivinerLiveBook()) - ->setName($book_name) - ->setViewPolicy(PhabricatorPolicies::POLICY_USER) - ->save(); + $book = id(new DivinerLiveBook())->setName($book_name); + } + + $repository = null; + $callsign = $this->getRepository(); + + if ($callsign) { + $repository = id(new PhabricatorRepositoryQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withCallsigns(array($callsign)) + ->executeOne(); + + if (!$repository) { + throw new Exception( + pht('No such repository "%s" found.', $callsign)); + } + + $book->setRepositoryPHID($repository->getPHID()); + } else { + $book->setRepositoryPHID(null); } - $book->setConfigurationData($this->getConfigurationData())->save(); + $book + ->setConfigurationData($this->getConfigurationData()) + ->save(); $this->book = $book; } @@ -26,7 +44,7 @@ private function loadSymbolForAtom(DivinerAtom $atom) { $symbol = id(new DivinerAtomQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withBookPHIDs(array($this->loadBook()->getPHID())) + ->withBookPHIDs(array($atom->getBook())) ->withTypes(array($atom->getType())) ->withNames(array($atom->getName())) ->withContexts(array($atom->getContext())) @@ -40,7 +58,8 @@ } return id(new DivinerLiveSymbol()) - ->setBookPHID($this->loadBook()->getPHID()) + ->setBookPHID($this->getBook()->getPHID()) + ->setRepositoryPHID($this->getBook()->getRepositoryPHID()) ->setType($atom->getType()) ->setName($atom->getName()) ->setContext($atom->getContext()) @@ -63,7 +82,7 @@ protected function loadAllPublishedHashes() { $symbols = id(new DivinerAtomQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withBookPHIDs(array($this->loadBook()->getPHID())) + ->withBookPHIDs(array($this->getBook()->getPHID())) ->withIncludeUndocumentable(true) ->execute(); diff --git a/src/applications/diviner/publisher/DivinerPublisher.php b/src/applications/diviner/publisher/DivinerPublisher.php --- a/src/applications/diviner/publisher/DivinerPublisher.php +++ b/src/applications/diviner/publisher/DivinerPublisher.php @@ -9,6 +9,7 @@ private $config; private $symbolReverseMap; private $dropCaches; + private $repository; final public function setDropCaches($drop_caches) { $this->dropCaches = $drop_caches; @@ -153,4 +154,15 @@ return true; } + final public function getRepository() { + return coalesce( + $this->repository, + $this->getConfig('repository')); + } + + final public function setRepository($callsign) { + $this->repository = $callsign; + return $this; + } + } diff --git a/src/applications/diviner/query/DivinerBookQuery.php b/src/applications/diviner/query/DivinerBookQuery.php --- a/src/applications/diviner/query/DivinerBookQuery.php +++ b/src/applications/diviner/query/DivinerBookQuery.php @@ -5,6 +5,7 @@ private $ids; private $phids; private $names; + private $repositoryPHIDs; public function withIDs(array $ids) { $this->ids = $ids; @@ -21,6 +22,11 @@ return $this; } + public function withRepositoryPHIDs(array $repository_phids) { + $this->repositoryPHIDs = $repository_phids; + return $this; + } + protected function loadPage() { $table = new DivinerLiveBook(); $conn_r = $table->establishConnection('r'); @@ -36,6 +42,34 @@ return $table->loadAllFromArray($data); } + protected function willFilterPage(array $books) { + assert_instances_of($books, 'DivinerLiveBook'); + + $repos = id(new PhabricatorRepositoryQuery()) + ->setViewer($this->getViewer()) + ->withPHIDs(mpull($books, 'getRepositoryPHID')) + ->execute(); + $repos = mpull($repos, null, 'getPHID'); + + foreach ($books as $key => $book) { + if ($book->getRepositoryPHID() === null) { + $book->attachRepository(null); + continue; + } + + $repository = idx($repos, $book->getRepositoryPHID()); + + if (!$repository) { + unset($books[$key]); + continue; + } + + $book->attachRepository($repository); + } + + return $books; + } + protected function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); @@ -60,6 +94,13 @@ $this->names); } + if ($this->repositoryPHIDs !== null) { + $where[] = qsprintf( + $conn_r, + 'repositoryPHID IN (%Ls)', + $this->repositoryPHIDs); + } + $where[] = $this->buildPagingClause($conn_r); return $this->formatWhereClause($where); diff --git a/src/applications/diviner/storage/DivinerLiveBook.php b/src/applications/diviner/storage/DivinerLiveBook.php --- a/src/applications/diviner/storage/DivinerLiveBook.php +++ b/src/applications/diviner/storage/DivinerLiveBook.php @@ -6,9 +6,12 @@ PhabricatorDestructibleInterface { protected $name; + protected $repositoryPHID; protected $viewPolicy; protected $configurationData = array(); + private $repository = self::ATTACHABLE; + protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, @@ -17,6 +20,7 @@ ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'text64', + 'repositoryPHID' => 'phid?', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, @@ -64,6 +68,15 @@ return idx($spec, 'name', $group); } + public function getRepository() { + return $this->assertAttached($this->repository); + } + + public function attachRepository(PhabricatorRepository $repository = null) { + $this->repository = $repository; + return $this; + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { @@ -77,11 +90,27 @@ } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { - return false; + $repository = $this->getRepository(); + + if (!$repository) { + return false; + } + + return $repository->hasAutomaticCapability($capability, $viewer); } public function describeAutomaticCapability($capability) { - return null; + $description = array(); + + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + $description[] = pht( + 'If a book belongs to a repository, other users must be able '. + 'to view the repository in order to view the book.'); + break; + } + + return $description; } /* -( PhabricatorDestructibleInterface )----------------------------------- */ diff --git a/src/applications/diviner/storage/DivinerLiveSymbol.php b/src/applications/diviner/storage/DivinerLiveSymbol.php --- a/src/applications/diviner/storage/DivinerLiveSymbol.php +++ b/src/applications/diviner/storage/DivinerLiveSymbol.php @@ -7,6 +7,7 @@ PhabricatorDestructibleInterface { protected $bookPHID; + protected $repositoryPHID; protected $context; protected $type; protected $name; @@ -22,6 +23,7 @@ protected $isDocumentable = 0; private $book = self::ATTACHABLE; + private $repository = self::ATTACHABLE; private $atom = self::ATTACHABLE; private $extends = self::ATTACHABLE; private $children = self::ATTACHABLE; @@ -31,6 +33,7 @@ self::CONFIG_AUX_PHID => true, self::CONFIG_TIMESTAMPS => false, self::CONFIG_COLUMN_SCHEMA => array( + 'repositoryPHID' => 'phid?', 'context' => 'text255?', 'type' => 'text32', 'name' => 'text255', @@ -94,6 +97,15 @@ return $this; } + public function getRepository() { + return $this->assertAttached($this->repository); + } + + public function attachRepository(PhabricatorRepository $repository) { + $this->repository = $repository; + return $this; + } + public function getAtom() { return $this->assertAttached($this->atom); } @@ -142,11 +154,12 @@ $this->identityHash = PhabricatorHash::digestForIndex( serialize( array( - 'bookPHID' => $this->getBookPHID(), - 'context' => $this->getContext(), - 'type' => $this->getType(), - 'name' => $this->getName(), - 'index' => $this->getAtomIndex(), + 'bookPHID' => $this->getBookPHID(), + 'repositoryPHID' => $this->getRepositoryPHID(), + 'context' => $this->getContext(), + 'type' => $this->getType(), + 'name' => $this->getName(), + 'index' => $this->getAtomIndex(), ))); } diff --git a/src/applications/diviner/workflow/DivinerWorkflow.php b/src/applications/diviner/workflow/DivinerWorkflow.php --- a/src/applications/diviner/workflow/DivinerWorkflow.php +++ b/src/applications/diviner/workflow/DivinerWorkflow.php @@ -32,6 +32,7 @@ $book, array( 'name' => 'string', + 'repository' => 'optional string', 'title' => 'optional string', 'short' => 'optional string', 'preface' => 'optional string', diff --git a/src/applications/repository/storage/PhabricatorRepository.php b/src/applications/repository/storage/PhabricatorRepository.php --- a/src/applications/repository/storage/PhabricatorRepository.php +++ b/src/applications/repository/storage/PhabricatorRepository.php @@ -1913,7 +1913,17 @@ PhabricatorDestructionEngine $engine) { $this->openTransaction(); - $this->delete(); + + $this->delete(); + + $books = id(new DivinerBookQuery()) + ->setViewer($engine->getViewer()) + ->withRepositoryPHIDs(array($this->getPHID())) + ->execute(); + foreach ($books as $book) { + $engine->destroyObject($book); + } + $this->saveTransaction(); }