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 @@ -5029,6 +5029,7 @@ 'PhrictionDocumentPHIDType' => 'applications/phriction/phid/PhrictionDocumentPHIDType.php', 'PhrictionDocumentPathHeraldField' => 'applications/phriction/herald/PhrictionDocumentPathHeraldField.php', 'PhrictionDocumentPolicyCodex' => 'applications/phriction/codex/PhrictionDocumentPolicyCodex.php', + 'PhrictionDocumentPublishTransaction' => 'applications/phriction/xaction/PhrictionDocumentPublishTransaction.php', 'PhrictionDocumentQuery' => 'applications/phriction/query/PhrictionDocumentQuery.php', 'PhrictionDocumentSearchConduitAPIMethod' => 'applications/phriction/conduit/PhrictionDocumentSearchConduitAPIMethod.php', 'PhrictionDocumentSearchEngine' => 'applications/phriction/query/PhrictionDocumentSearchEngine.php', @@ -5046,6 +5047,7 @@ 'PhrictionMarkupPreviewController' => 'applications/phriction/controller/PhrictionMarkupPreviewController.php', 'PhrictionMoveController' => 'applications/phriction/controller/PhrictionMoveController.php', 'PhrictionNewController' => 'applications/phriction/controller/PhrictionNewController.php', + 'PhrictionPublishController' => 'applications/phriction/controller/PhrictionPublishController.php', 'PhrictionRemarkupRule' => 'applications/phriction/markup/PhrictionRemarkupRule.php', 'PhrictionReplyHandler' => 'applications/phriction/mail/PhrictionReplyHandler.php', 'PhrictionSchemaSpec' => 'applications/phriction/storage/PhrictionSchemaSpec.php', @@ -11144,6 +11146,7 @@ 'PhrictionDocumentPHIDType' => 'PhabricatorPHIDType', 'PhrictionDocumentPathHeraldField' => 'PhrictionDocumentHeraldField', 'PhrictionDocumentPolicyCodex' => 'PhabricatorPolicyCodex', + 'PhrictionDocumentPublishTransaction' => 'PhrictionDocumentTransactionType', 'PhrictionDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhrictionDocumentSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod', 'PhrictionDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine', @@ -11161,6 +11164,7 @@ 'PhrictionMarkupPreviewController' => 'PhabricatorController', 'PhrictionMoveController' => 'PhrictionController', 'PhrictionNewController' => 'PhrictionController', + 'PhrictionPublishController' => 'PhrictionController', 'PhrictionRemarkupRule' => 'PhutilRemarkupRule', 'PhrictionReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhrictionSchemaSpec' => 'PhabricatorConfigSchemaSpec', diff --git a/src/applications/phriction/application/PhabricatorPhrictionApplication.php b/src/applications/phriction/application/PhabricatorPhrictionApplication.php --- a/src/applications/phriction/application/PhabricatorPhrictionApplication.php +++ b/src/applications/phriction/application/PhabricatorPhrictionApplication.php @@ -56,6 +56,8 @@ 'edit/(?:(?P[1-9]\d*)/)?' => 'PhrictionEditController', 'delete/(?P[1-9]\d*)/' => 'PhrictionDeleteController', + 'publish/(?P[1-9]\d*)/(?P[1-9]\d*)/' + => 'PhrictionPublishController', 'new/' => 'PhrictionNewController', 'move/(?P[1-9]\d*)/' => 'PhrictionMoveController', diff --git a/src/applications/phriction/controller/PhrictionDocumentController.php b/src/applications/phriction/controller/PhrictionDocumentController.php --- a/src/applications/phriction/controller/PhrictionDocumentController.php +++ b/src/applications/phriction/controller/PhrictionDocumentController.php @@ -203,7 +203,7 @@ $curtain = null; if ($document->getID()) { - $curtain = $this->buildCurtain($document); + $curtain = $this->buildCurtain($document, $content); } $crumbs = $this->buildApplicationCrumbs(); @@ -230,11 +230,11 @@ $prop_list = phutil_tag_div('phui-document-view-pro-box', $prop_list); $page_content = id(new PHUIDocumentView()) + ->setBanner($version_note) ->setHeader($header) ->setToc($toc) ->appendChild( array( - $version_note, $move_notice, $core_content, )); @@ -277,7 +277,9 @@ return $view; } - private function buildCurtain(PhrictionDocument $document) { + private function buildCurtain( + PhrictionDocument $document, + PhrictionContent $content) { $viewer = $this->getViewer(); $can_edit = PhabricatorPolicyFilter::hasCapability( @@ -286,6 +288,7 @@ PhabricatorPolicyCapability::CAN_EDIT); $slug = PhabricatorSlug::normalize($this->slug); + $id = $document->getID(); $curtain = $this->newCurtainView($document); @@ -296,6 +299,26 @@ ->setIcon('fa-pencil') ->setHref('/phriction/edit/'.$document->getID().'/')); + $is_current = false; + $content_id = null; + if ($content) { + if ($content->getPHID() == $document->getContentPHID()) { + $is_current = true; + } + $content_id = $content->getID(); + } + $can_publish = ($can_edit && $content && !$is_current); + + $publish_uri = "/phriction/publish/{$id}/{$content_id}/"; + + $curtain->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Publish')) + ->setIcon('fa-upload') + ->setDisabled(!$can_publish) + ->setWorkflow(true) + ->setHref($publish_uri)); + if ($document->getStatus() == PhrictionDocumentStatus::STATUS_EXISTS) { $curtain->addAction( id(new PhabricatorActionView()) diff --git a/src/applications/phriction/controller/PhrictionPublishController.php b/src/applications/phriction/controller/PhrictionPublishController.php new file mode 100644 --- /dev/null +++ b/src/applications/phriction/controller/PhrictionPublishController.php @@ -0,0 +1,86 @@ +getViewer(); + $id = $request->getURIData('documentID'); + $content_id = $request->getURIData('contentID'); + + $document = id(new PhrictionDocumentQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->needContent(true) + ->requireCapabilities( + array( + PhabricatorPolicyCapability::CAN_EDIT, + PhabricatorPolicyCapability::CAN_VIEW, + )) + ->executeOne(); + if (!$document) { + return new Aphront404Response(); + } + + $document_uri = $document->getURI(); + + $content = id(new PhrictionContentQuery()) + ->setViewer($viewer) + ->withIDs(array($content_id)) + ->executeOne(); + if (!$content) { + return new Aphront404Response(); + } + + if ($content->getPHID() == $document->getContentPHID()) { + return $this->newDialog() + ->setTitle(pht('Already Published')) + ->appendChild( + pht( + 'This version of the document is already the published '. + 'version.')) + ->addCancelButton($document_uri); + } + + $content_uri = $document_uri.'?v='.$content->getVersion(); + + if ($request->isFormPost()) { + $xactions = array(); + + $xactions[] = id(new PhrictionTransaction()) + ->setTransactionType( + PhrictionDocumentPublishTransaction::TRANSACTIONTYPE) + ->setNewValue($content->getPHID()); + + id(new PhrictionTransactionEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($document, $xactions); + + return id(new AphrontRedirectResponse())->setURI($document_uri); + } + + if ($content->getVersion() < $document->getContent()->getVersion()) { + $title = pht('Revert Document?'); + $body = pht( + 'Revert the published version of this document to an older '. + 'version?'); + $button = pht('Revert'); + } else { + $title = pht('Publish Draft?'); + $body = pht( + 'Update the published version of this document to this newer '. + 'version?'); + $button = pht('Publish'); + } + + return $this->newDialog() + ->setTitle($title) + ->appendChild($body) + ->addSubmitButton($button) + ->addCancelButton($content_uri); + } + +} diff --git a/src/applications/phriction/xaction/PhrictionDocumentPublishTransaction.php b/src/applications/phriction/xaction/PhrictionDocumentPublishTransaction.php new file mode 100644 --- /dev/null +++ b/src/applications/phriction/xaction/PhrictionDocumentPublishTransaction.php @@ -0,0 +1,71 @@ +getContentPHID(); + } + + public function applyInternalEffects($object, $value) { + $object->setContentPHID($value); + } + + public function getActionName() { + return pht('Published'); + } + + public function getTitle() { + return pht( + '%s published a new version of this document.', + $this->renderAuthor()); + } + + public function getTitleForFeed() { + return pht( + '%s published a new version of %s.', + $this->renderAuthor(), + $this->renderObject()); + } + + public function validateTransactions($object, array $xactions) { + $actor = $this->getActor(); + $errors = array(); + + foreach ($xactions as $xaction) { + $content_phid = $xaction->getNewValue(); + + // If this isn't changing anything, skip it. + if ($content_phid === $object->getContentPHID()) { + continue; + } + + $content = id(new PhrictionContentQuery()) + ->setViewer($actor) + ->withPHIDs(array($content_phid)) + ->executeOne(); + if (!$content) { + $errors[] = $this->newInvalidError( + pht( + 'Unable to load Content object with PHID "%s".', + $content_phid), + $xaction); + continue; + } + + if ($content->getDocumentPHID() !== $object->getPHID()) { + $errors[] = $this->newInvalidError( + pht( + 'Content object "%s" can not be published because it belongs '. + 'to a different document.', + $content_phid)); + continue; + } + } + + return $errors; + } + +} diff --git a/src/view/phui/PHUIDocumentView.php b/src/view/phui/PHUIDocumentView.php --- a/src/view/phui/PHUIDocumentView.php +++ b/src/view/phui/PHUIDocumentView.php @@ -9,6 +9,7 @@ private $toc; private $foot; private $curtain; + private $banner; public function setHeader(PHUIHeaderView $header) { $header->setTall(true); @@ -46,6 +47,15 @@ return $this->curtain; } + public function setBanner($banner) { + $this->banner = $banner; + return $this; + } + + public function getBanner() { + return $this->banner; + } + protected function getTagAttributes() { $classes = array(); @@ -160,6 +170,7 @@ array( $table_of_contents, $this->header, + $this->banner, array( $curtain, $main_content,