diff --git a/resources/sql/autopatches/20150616.divinerrepository.sql b/resources/sql/autopatches/20150616.divinerrepository.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20150616.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 @@ -43,6 +43,14 @@ ->setEpoch($book->getDateModified()) ->addActionLink($action_button); + 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/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,7 +4,7 @@ private $book; - private function loadBook() { + protected function getBook() { if (!$this->book) { $book_name = $this->getConfig('name'); @@ -20,7 +20,28 @@ ->save(); } - $book->setConfigurationData($this->getConfigurationData())->save(); + $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(); $this->book = $book; id(new PhabricatorSearchIndexer()) @@ -33,7 +54,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())) @@ -45,7 +66,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()) @@ -68,7 +90,7 @@ protected function loadAllPublishedHashes() { $symbols = id(new DivinerAtomQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withBookPHIDs(array($this->loadBook()->getPHID())) + ->withBookPHIDs(array($this->getBook()->getPHID())) ->withGhosts(false) ->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; @@ -163,4 +164,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; private $needProjectPHIDs; @@ -23,6 +24,11 @@ return $this; } + public function withRepositoryPHIDs(array $repository_phids) { + $this->repositoryPHIDs = $repository_phids; + return $this; + } + public function needProjectPHIDs($need_phids) { $this->needProjectPHIDs = $need_phids; return $this; @@ -44,6 +50,30 @@ } protected function didFilterPage(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); + } + if ($this->needProjectPHIDs) { $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(mpull($books, 'getPHID')) @@ -89,6 +119,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 @@ -8,11 +8,13 @@ PhabricatorApplicationTransactionInterface { protected $name; + protected $repositoryPHID; protected $viewPolicy; protected $editPolicy; protected $configurationData = array(); private $projectPHIDs = self::ATTACHABLE; + private $repository = self::ATTACHABLE; protected function getConfiguration() { return array( @@ -22,6 +24,7 @@ ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'text64', + 'repositoryPHID' => 'phid?', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, @@ -68,6 +71,15 @@ return idx($spec, 'name', $group); } + public function attachRepository(PhabricatorRepository $repository = null) { + $this->repository = $repository; + return $this; + } + + public function getRepository() { + return $this->assertAttached($this->repository); + } + public function attachProjectPHIDs(array $project_phids) { $this->projectPHIDs = $project_phids; return $this; @@ -98,11 +110,27 @@ } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + $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; } 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); } @@ -145,11 +157,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(); }