diff --git a/resources/sql/autopatches/20150612.diviner.status.sql b/resources/sql/autopatches/20150612.diviner.status.sql new file mode 100644 --- /dev/null +++ b/resources/sql/autopatches/20150612.diviner.status.sql @@ -0,0 +1,6 @@ +ALTER TABLE {$NAMESPACE}_diviner.diviner_livebook + ADD COLUMN status VARCHAR(32) COLLATE utf8mb4_bin NOT NULL AFTER name; + +UPDATE {$NAMESPACE}_diviner.diviner_livebook + SET status = 0 + WHERE status IS NULL; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -644,12 +644,14 @@ 'DivinerAtomSearchIndexer' => 'applications/diviner/search/DivinerAtomSearchIndexer.php', 'DivinerAtomizeWorkflow' => 'applications/diviner/workflow/DivinerAtomizeWorkflow.php', 'DivinerAtomizer' => 'applications/diviner/atomizer/DivinerAtomizer.php', + 'DivinerBookArchiveController' => 'applications/diviner/controller/DivinerBookArchiveController.php', 'DivinerBookController' => 'applications/diviner/controller/DivinerBookController.php', 'DivinerBookEditController' => 'applications/diviner/controller/DivinerBookEditController.php', 'DivinerBookItemView' => 'applications/diviner/view/DivinerBookItemView.php', 'DivinerBookPHIDType' => 'applications/diviner/phid/DivinerBookPHIDType.php', 'DivinerBookQuery' => 'applications/diviner/query/DivinerBookQuery.php', 'DivinerBookSearchIndexer' => 'applications/diviner/search/DivinerBookSearchIndexer.php', + 'DivinerBookStatus' => 'applications/diviner/constants/DivinerBookStatus.php', 'DivinerController' => 'applications/diviner/controller/DivinerController.php', 'DivinerDAO' => 'applications/diviner/storage/DivinerDAO.php', 'DivinerDefaultEditCapability' => 'applications/diviner/capability/DivinerDefaultEditCapability.php', @@ -3920,6 +3922,7 @@ 'DivinerAtomSearchEngine' => 'PhabricatorApplicationSearchEngine', 'DivinerAtomSearchIndexer' => 'PhabricatorSearchDocumentIndexer', 'DivinerAtomizeWorkflow' => 'DivinerWorkflow', + 'DivinerBookArchiveController' => 'DivinerController', 'DivinerBookController' => 'DivinerController', 'DivinerBookEditController' => 'DivinerController', 'DivinerBookItemView' => 'AphrontTagView', @@ -3940,6 +3943,7 @@ 'PhabricatorPolicyInterface', 'PhabricatorProjectInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorApplicationTransactionInterface', ), 'DivinerLiveBookEditor' => 'PhabricatorApplicationTransactionEditor', 'DivinerLiveBookTransaction' => 'PhabricatorApplicationTransaction', diff --git a/src/applications/diviner/application/PhabricatorDivinerApplication.php b/src/applications/diviner/application/PhabricatorDivinerApplication.php --- a/src/applications/diviner/application/PhabricatorDivinerApplication.php +++ b/src/applications/diviner/application/PhabricatorDivinerApplication.php @@ -39,6 +39,7 @@ 'find/' => 'DivinerFindController', ), '/book/(?P[^/]+)/' => 'DivinerBookController', + '/book/(?P[^/]+)/archive/' => 'DivinerBookArchiveController', '/book/(?P[^/]+)/edit/' => 'DivinerBookEditController', '/book/'. '(?P[^/]+)/'. diff --git a/src/applications/diviner/constants/DivinerBookStatus.php b/src/applications/diviner/constants/DivinerBookStatus.php new file mode 100644 --- /dev/null +++ b/src/applications/diviner/constants/DivinerBookStatus.php @@ -0,0 +1,24 @@ + pht('Active'), + self::STATUS_ARCHIVED => pht('Archived'), + ); + + return idx($map, coalesce($status, '?'), pht('Unknown')); + } + + public static function getStatusMap() { + return array( + self::STATUS_ACTIVE => pht('Active'), + self::STATUS_ARCHIVED => pht('Archived'), + ); + } + +} diff --git a/src/applications/diviner/controller/DivinerBookArchiveController.php b/src/applications/diviner/controller/DivinerBookArchiveController.php new file mode 100644 --- /dev/null +++ b/src/applications/diviner/controller/DivinerBookArchiveController.php @@ -0,0 +1,67 @@ +getViewer(); + + $book = id(new DivinerBookQuery()) + ->setViewer($viewer) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + )) + ->withNames(array($request->getURIData('book'))) + ->executeOne(); + + if (!$book) { + return new Aphront404Response(); + } + + $view_uri = '/book/'.$book->getName().'/'; + + if ($request->isFormPost()) { + if ($book->isArchived()) { + $new_status = DivinerBookStatus::STATUS_ACTIVE; + } else { + $new_status = DivinerBookStatus::STATUS_ARCHIVED; + } + + $xactions = array(); + + $xactions[] = id(new DivinerLiveBookTransaction()) + ->setTransactionType(DivinerLiveBookTransaction::TYPE_STATUS) + ->setNewValue($new_status); + + id(new DivinerLiveBookEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($book, $xactions); + + return id(new AphrontRedirectResponse())->setURI($view_uri); + } + + if ($book->isArchived()) { + $title = pht('Really activate book?'); + $body = pht('This book will become active again.'); + $button = pht('Activate Book'); + } else { + $title = pht('Really archive book?'); + $body = pht('This book will be moved to the archive.'); + $button = pht('Archive Book'); + } + + $dialog = id(new AphrontDialogView()) + ->setUser($viewer) + ->setTitle($title) + ->appendChild($body) + ->addCancelButton($view_uri) + ->addSubmitButton($button); + + return id(new AphrontDialogResponse())->setDialog($dialog); + } + +} 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 @@ -41,6 +41,12 @@ ->setEpoch($book->getDateModified()) ->addActionLink($action_button); + if ($book->getStatus() == DivinerBookStatus::STATUS_ACTIVE) { + $header->setStatus('fa-check', 'bluegrey', pht('Active')); + } else { + $header->setStatus('fa-ban', 'red', pht('Archived')); + } + $document = new PHUIDocumentView(); $document->setHeader($header); $document->addClass('diviner-view'); @@ -124,6 +130,24 @@ ->setHref('/book/'.$book->getName().'/edit/') ->setDisabled(!$can_edit)); + if ($book->isArchived()) { + $action_view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Activate Book')) + ->setIcon('fa-check') + ->setHref('/book/'.$book->getName().'/archive/') + ->setDisabled(!$can_edit) + ->setWorkflow(true)); + } else { + $action_view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Archive Book')) + ->setIcon('fa-ban') + ->setHref('/book/'.$book->getName().'/archive/') + ->setDisabled(!$can_edit) + ->setWorkflow(true)); + } + return $action_view; } diff --git a/src/applications/diviner/editor/DivinerLiveBookEditor.php b/src/applications/diviner/editor/DivinerLiveBookEditor.php --- a/src/applications/diviner/editor/DivinerLiveBookEditor.php +++ b/src/applications/diviner/editor/DivinerLiveBookEditor.php @@ -17,7 +17,61 @@ $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; + $types[] = DivinerLiveBookTransaction::TYPE_STATUS; + return $types; } + protected function getCustomTransactionOldValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case DivinerLiveBookTransaction::TYPE_STATUS: + return $object->getStatus(); + default: + return parent::getCustomTransactionOldValue($object, $xaction); + } + } + + protected function getCustomTransactionNewValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case DivinerLiveBookTransaction::TYPE_STATUS: + return $xaction->getNewValue(); + default: + return parent::getCustomTransactionNewValue($object, $xaction); + } + } + + protected function applyCustomInternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case DivinerLiveBookTransaction::TYPE_STATUS: + $object->setStatus($xaction->getNewValue()); + return; + default: + return parent::applyCustomInternalTransaction($object, $xaction); + } + } + + protected function applyCustomExternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + $old = $xaction->getOldValue(); + $new = $xaction->getNewValue(); + + switch ($xaction->getTransactionType()) { + case DivinerLiveBookTransaction::TYPE_STATUS: + return; + default: + return parent::applyCustomExternalTransaction($object, $xaction); + } + } + } 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,6 +8,7 @@ PhabricatorApplicationTransactionInterface { protected $name; + protected $status = DivinerBookStatus::STATUS_ACTIVE; protected $viewPolicy; protected $editPolicy; protected $configurationData = array(); @@ -22,6 +23,7 @@ ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'text64', + 'status' => 'text32', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, @@ -77,6 +79,10 @@ return $this->assertAttached($this->projectPHIDs); } + public function isArchived() { + return ($this->getStatus() == PhabricatorProjectStatus::STATUS_ARCHIVED); + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/diviner/storage/DivinerLiveBookTransaction.php b/src/applications/diviner/storage/DivinerLiveBookTransaction.php --- a/src/applications/diviner/storage/DivinerLiveBookTransaction.php +++ b/src/applications/diviner/storage/DivinerLiveBookTransaction.php @@ -3,6 +3,8 @@ final class DivinerLiveBookTransaction extends PhabricatorApplicationTransaction { + const TYPE_STATUS = 'diviner:book:status'; + public function getApplicationName() { return 'diviner'; } @@ -15,4 +17,86 @@ return null; } + public function getColor() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + switch ($this->getTransactionType()) { + case self::TYPE_STATUS: + if ($old == DivinerBookStatus::STATUS_ACTIVE) { + return 'red'; + } else { + return 'green'; + } + default: + return parent::getColor(); + } + } + + public function getIcon() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + switch ($this->getTransactionType()) { + case self::TYPE_STATUS: + if ($old == DivinerBookStatus::STATUS_ACTIVE) { + return 'fa-ban'; + } else { + return 'fa-check'; + } + default: + return parent::getIcon(); + } + } + + public function getTitle() { + $old = $this->getOldValue(); + $new = $this->getNewValue(); + $author_handle = $this->renderHandleLink($this->getAuthorPHID()); + + switch ($this->getTransactionType()) { + case self::TYPE_STATUS: + if ($old == 0) { + return pht( + '%s archived this book.', + $author_handle); + } else { + return pht( + '%s activated this book.', + $author_handle); + } + break; + + default: + return parent::getTitle(); + } + } + + public function getTitleForFeed() { + $author_phid = $this->getAuthorPHID(); + $object_phid = $this->getObjectPHID(); + $author_handle = $this->renderHandleLink($author_phid); + $object_handle = $this->renderHandleLink($object_phid); + + $old = $this->getOldValue(); + $new = $this->getNewValue(); + + switch ($this->getTransactionType()) { + case self::TYPE_STATUS: + if ($old == 0) { + return pht( + '%s archived %s.', + $author_handle, + $object_handle); + } else { + return pht( + '%s activated %s.', + $author_handle, + $object_handle); + } + default: + return parent::getTitleForFeed(); + } + } + }