diff --git a/src/applications/phriction/controller/PhrictionDeleteController.php b/src/applications/phriction/controller/PhrictionDeleteController.php --- a/src/applications/phriction/controller/PhrictionDeleteController.php +++ b/src/applications/phriction/controller/PhrictionDeleteController.php @@ -15,6 +15,7 @@ $document = id(new PhrictionDocumentQuery()) ->setViewer($user) ->withIDs(array($this->id)) + ->needContent(true) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_EDIT, @@ -32,15 +33,24 @@ PhrictionDocumentStatus::STATUS_STUB => true, // How could they? ); if (isset($disallowed_states[$document->getStatus()])) { - $e_text = pht('An already moved or deleted document can not be deleted'); + $e_text = pht( + 'An already moved or deleted document can not be deleted.'); } $document_uri = PhrictionDocument::getSlugURI($document->getSlug()); if (!$e_text && $request->isFormPost()) { - $editor = id(PhrictionDocumentEditor::newForSlug($document->getSlug())) + $xactions = array(); + $xactions[] = id(new PhrictionTransaction()) + ->setTransactionType(PhrictionTransaction::TYPE_DELETE) + ->setNewValue(true); + + $editor = id(new PhrictionTransactionEditor()) ->setActor($user) - ->delete(); + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->applyTransactions($document, $xactions); + return id(new AphrontRedirectResponse())->setURI($document_uri); } diff --git a/src/applications/phriction/controller/PhrictionMoveController.php b/src/applications/phriction/controller/PhrictionMoveController.php --- a/src/applications/phriction/controller/PhrictionMoveController.php +++ b/src/applications/phriction/controller/PhrictionMoveController.php @@ -16,6 +16,7 @@ $document = id(new PhrictionDocumentQuery()) ->setViewer($user) ->withIDs(array($this->id)) + ->needContent(true) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, @@ -32,6 +33,7 @@ $document = id(new PhrictionDocumentQuery()) ->setViewer($user) ->withSlugs(array($slug)) + ->needContent(true) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, @@ -74,50 +76,46 @@ return id(new AphrontDialogResponse())->setDialog($error_dialog); } - $content = id(new PhrictionContent())->load($document->getContentID()); + $content = $document->getContent(); if ($request->isFormPost() && !count($errors)) { - if (!count($errors)) { // First check if the target document exists - // NOTE: We use the ominpotent user because we can't let users overwrite - // documents even if they can't see them. - $target_document = id(new PhrictionDocumentQuery()) - ->setViewer(PhabricatorUser::getOmnipotentUser()) - ->withSlugs(array($target_slug)) - ->executeOne(); - - // Considering to overwrite existing docs? Nuke this! - if ($target_document && $target_document->getStatus() == - PhrictionDocumentStatus::STATUS_EXISTS) { + // NOTE: We use the ominpotent user because we can't let users overwrite + // documents even if they can't see them. + $target_document = id(new PhrictionDocumentQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withSlugs(array($target_slug)) + ->needContent(true) + ->executeOne(); - $errors[] = pht('Can not overwrite existing target document.'); - $e_url = pht('Already exists.'); - } + // Considering to overwrite existing docs? Nuke this! + $exists = PhrictionDocumentStatus::STATUS_EXISTS; + if ($target_document && $target_document->getStatus() == $exists) { + $errors[] = pht('Can not overwrite existing target document.'); + $e_url = pht('Already exists.'); } - if (!count($errors)) { // I like to move it, move it! - $from_editor = id(PhrictionDocumentEditor::newForSlug($slug)) - ->setActor($user) - ->setTitle($content->getTitle()) - ->setContent($content->getContent()) - ->setDescription($content->getDescription()); + if (!count($errors)) { - $target_editor = id(PhrictionDocumentEditor::newForSlug( - $target_slug)) + $editor = id(new PhrictionTransactionEditor()) ->setActor($user) - ->setTitle($content->getTitle()) - ->setContent($content->getContent()) - ->setDescription($content->getDescription()); - - // Move it! - $target_editor->moveHere($document->getID(), $document->getPHID()); - - // Retrieve the target doc directly from the editor - // No need to load it per Sql again - $target_document = $target_editor->getDocument(); - $from_editor->moveAway($target_document->getID()); + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setDescription($request->getStr('description')); + + $xactions = array(); + $xactions[] = id(new PhrictionTransaction()) + ->setTransactionType(PhrictionTransaction::TYPE_MOVE_TO) + ->setNewValue($document); + if (!$target_document) { + $target_document = PhrictionDocument::initializeNewDocument( + $user, + $target_slug); + } + $editor->applyTransactions($target_document, $xactions); - $redir_uri = PhrictionDocument::getSlugURI($target_document->getSlug()); + $redir_uri = PhrictionDocument::getSlugURI( + $target_document->getSlug()); return id(new AphrontRedirectResponse())->setURI($redir_uri); } } @@ -131,21 +129,21 @@ ->setUser($user) ->appendChild( id(new AphrontFormStaticControl()) - ->setLabel(pht('Title')) - ->setValue($content->getTitle())) + ->setLabel(pht('Title')) + ->setValue($content->getTitle())) ->appendChild( id(new AphrontFormTextControl()) - ->setLabel(pht('New URI')) - ->setValue($target_slug) - ->setError($e_url) - ->setName('new-slug') - ->setCaption(pht('The new location of the document.'))) + ->setLabel(pht('New URI')) + ->setValue($target_slug) + ->setError($e_url) + ->setName('new-slug') + ->setCaption(pht('The new location of the document.'))) ->appendChild( id(new AphrontFormTextControl()) - ->setLabel(pht('Edit Notes')) - ->setValue($content->getDescription()) - ->setError(null) - ->setName('description')); + ->setLabel(pht('Edit Notes')) + ->setValue($content->getDescription()) + ->setError(null) + ->setName('description')); $dialog = id(new AphrontDialogView()) ->setUser($user) diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -6,6 +6,7 @@ private $description; private $oldContent; private $newContent; + private $moveAwayDocument; public function setDescription($description) { $this->description = $description; @@ -48,6 +49,9 @@ $types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhrictionTransaction::TYPE_TITLE; $types[] = PhrictionTransaction::TYPE_CONTENT; + $types[] = PhrictionTransaction::TYPE_DELETE; + $types[] = PhrictionTransaction::TYPE_MOVE_TO; + $types[] = PhrictionTransaction::TYPE_MOVE_AWAY; /* TODO $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; @@ -72,6 +76,10 @@ return null; } return $this->getOldContent()->getContent(); + case PhrictionTransaction::TYPE_DELETE: + case PhrictionTransaction::TYPE_MOVE_TO: + case PhrictionTransaction::TYPE_MOVE_AWAY: + return null; } } @@ -82,7 +90,24 @@ switch ($xaction->getTransactionType()) { case PhrictionTransaction::TYPE_TITLE: case PhrictionTransaction::TYPE_CONTENT: + case PhrictionTransaction::TYPE_DELETE: return $xaction->getNewValue(); + case PhrictionTransaction::TYPE_MOVE_TO: + $document = $xaction->getNewValue(); + // grab the real object now for the sub-editor to come + $this->moveAwayDocument = $document; + $dict = array( + 'id' => $document->getID(), + 'phid' => $document->getPHID(), + 'content' => $document->getContent()->getContent(),); + return $dict; + case PhrictionTransaction::TYPE_MOVE_AWAY: + $document = $xaction->getNewValue(); + $dict = array( + 'id' => $document->getID(), + 'phid' => $document->getPHID(), + 'content' => $document->getContent()->getContent(),); + return $dict; } } @@ -94,6 +119,9 @@ switch ($xaction->getTransactionType()) { case PhrictionTransaction::TYPE_TITLE: case PhrictionTransaction::TYPE_CONTENT: + case PhrictionTransaction::TYPE_DELETE: + case PhrictionTransaction::TYPE_MOVE_TO: + case PhrictionTransaction::TYPE_MOVE_AWAY: return true; } } @@ -115,8 +143,12 @@ switch ($xaction->getTransactionType()) { case PhrictionTransaction::TYPE_TITLE: case PhrictionTransaction::TYPE_CONTENT: + case PhrictionTransaction::TYPE_MOVE_TO: $object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS); return; + case PhrictionTransaction::TYPE_MOVE_AWAY: + $object->setStatus(PhrictionDocumentStatus::STATUS_MOVED); + return; } } @@ -131,6 +163,25 @@ case PhrictionTransaction::TYPE_CONTENT: $this->getNewContent()->setContent($xaction->getNewValue()); break; + case PhrictionTransaction::TYPE_DELETE: + $this->getNewContent()->setContent(''); + $this->getNewContent()->setChangeType( + PhrictionChangeType::CHANGE_DELETE); + break; + case PhrictionTransaction::TYPE_MOVE_TO: + $dict = $xaction->getNewValue(); + $this->getNewContent()->setContent($dict['content']); + $this->getNewContent()->setChangeType( + PhrictionChangeType::CHANGE_MOVE_HERE); + $this->getNewContent()->setChangeRef($dict['id']); + break; + case PhrictionTransaction::TYPE_MOVE_AWAY: + $dict = $xaction->getNewValue(); + $this->getNewContent()->setContent(''); + $this->getNewContent()->setChangeType( + PhrictionChangeType::CHANGE_MOVE_AWAY); + $this->getNewContent()->setChangeRef($dict['id']); + break; default: break; } @@ -145,6 +196,9 @@ switch ($xaction->getTransactionType()) { case PhrictionTransaction::TYPE_TITLE: case PhrictionTransaction::TYPE_CONTENT: + case PhrictionTransaction::TYPE_DELETE: + case PhrictionTransaction::TYPE_MOVE_AWAY: + case PhrictionTransaction::TYPE_MOVE_TO: $save_content = true; break; default: @@ -185,15 +239,27 @@ } } } + + if ($this->moveAwayDocument !== null) { + $move_away_xactions = array(); + $move_away_xactions[] = id(new PhrictionTransaction()) + ->setTransactionType(PhrictionTransaction::TYPE_MOVE_AWAY) + ->setNewValue($object); + $sub_editor = id(new PhrictionTransactionEditor()) + ->setActor($this->getActor()) + ->setContentSource($this->getContentSource()) + ->setContinueOnNoEffect($this->getContinueOnNoEffect()) + ->setDescription($this->getDescription()) + ->applyTransactions($this->moveAwayDocument, $move_away_xactions); + } + return $xactions; } protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { - - $xactions = mfilter($xactions, 'shouldHide', true); - return $xactions; + return true; } protected function getMailSubjectPrefix() { @@ -213,6 +279,8 @@ pht("A document's title changes."), PhrictionTransaction::MAILTAG_CONTENT => pht("A document's content changes."), + PhrictionTransaction::MAILTAG_DELETE => + pht('A document is deleted.'), ); } @@ -261,8 +329,16 @@ array $xactions) { $phids = parent::getFeedRelatedPHIDs($object, $xactions); - // TODO - once the editor supports moves, we'll need to surface the - // "from document phid" to related phids. + + foreach ($xactions as $xaction) { + switch ($xaction->getTransactionType()) { + case PhrictionTransaction::TYPE_MOVE_TO: + $dict = $xaction->getNewValue(); + $phids[] = $dict['phid']; + break; + } + } + return $phids; } @@ -279,14 +355,12 @@ private function buildNewContentTemplate( PhrictionDocument $document) { - $new_content = new PhrictionContent(); - $new_content->setSlug($document->getSlug()); - $new_content->setAuthorPHID($this->getActor()->getPHID()); - $new_content->setChangeType(PhrictionChangeType::CHANGE_EDIT); - - $new_content->setTitle($this->getOldContent()->getTitle()); - $new_content->setContent($this->getOldContent()->getContent()); - + $new_content = id(new PhrictionContent()) + ->setSlug($document->getSlug()) + ->setAuthorPHID($this->getActor()->getPHID()) + ->setChangeType(PhrictionChangeType::CHANGE_EDIT) + ->setTitle($this->getOldContent()->getTitle()) + ->setContent($this->getOldContent()->getContent()); if (strlen($this->getDescription())) { $new_content->setDescription($this->getDescription()); } diff --git a/src/applications/phriction/storage/PhrictionTransaction.php b/src/applications/phriction/storage/PhrictionTransaction.php --- a/src/applications/phriction/storage/PhrictionTransaction.php +++ b/src/applications/phriction/storage/PhrictionTransaction.php @@ -5,9 +5,13 @@ const TYPE_TITLE = 'title'; const TYPE_CONTENT = 'content'; + const TYPE_DELETE = 'delete'; + const TYPE_MOVE_TO = 'move-to'; + const TYPE_MOVE_AWAY = 'move-away'; const MAILTAG_TITLE = 'phriction-title'; const MAILTAG_CONTENT = 'phriction-content'; + const MAILTAG_DELETE = 'phriction-delete'; public function getApplicationName() { return 'phriction'; @@ -21,6 +25,20 @@ return new PhrictionTransactionComment(); } + public function getRequiredHandlePHIDs() { + $phids = parent::getRequiredHandlePHIDs(); + $new = $this->getNewValue(); + switch ($this->getTransactionType()) { + case self::TYPE_MOVE_TO: + case self::TYPE_MOVE_AWAY: + $phids[] = $new['phid']; + break; + } + + + return $phids; + } + public function getRemarkupBlocks() { $blocks = parent::getRemarkupBlocks(); @@ -47,12 +65,35 @@ return parent::shouldHide(); } + public function shouldHideForMail(array $xactions) { + switch ($this->getTransactionType()) { + case self::TYPE_MOVE_TO: + case self::TYPE_MOVE_AWAY: + return true; + } + return parent::shouldHideForMail($xactions); + } + + public function shouldHideForFeed() { + switch ($this->getTransactionType()) { + case self::TYPE_MOVE_TO: + case self::TYPE_MOVE_AWAY: + return true; + } + return parent::shouldHideForFeed(); + } + public function getActionStrength() { switch ($this->getTransactionType()) { case self::TYPE_TITLE: return 1.4; case self::TYPE_CONTENT: return 1.3; + case self::TYPE_DELETE: + return 1.2; + case self::TYPE_MOVE_TO: + case self::TYPE_MOVE_AWAY: + return 1.0; } return parent::getActionStrength(); @@ -73,6 +114,15 @@ case self::TYPE_CONTENT: return pht('Edited'); + case self::TYPE_DELETE: + return pht('Deleted'); + + case self::TYPE_MOVE_TO: + return pht('Moved'); + + case self::TYPE_MOVE_AWAY: + return pht('Moved Away'); + } return parent::getActionName(); @@ -86,13 +136,17 @@ case self::TYPE_TITLE: case self::TYPE_CONTENT: return 'fa-pencil'; + case self::TYPE_DELETE: + return 'fa-times'; + case self::TYPE_MOVE_TO: + case self::TYPE_MOVE_AWAY: + return 'fa-arrows'; } return parent::getIcon(); } - public function getTitle() { $author_phid = $this->getAuthorPHID(); @@ -117,6 +171,23 @@ '%s edited the document content.', $this->renderHandleLink($author_phid)); + case self::TYPE_DELETE: + return pht( + '%s deleted this document.', + $this->renderHandleLink($author_phid)); + + case self::TYPE_MOVE_TO: + return pht( + '%s moved this document from %s', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($new['phid'])); + + case self::TYPE_MOVE_AWAY: + return pht( + '%s moved this document to %s', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($new['phid'])); + } return parent::getTitle(); @@ -151,6 +222,12 @@ $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); + case self::TYPE_DELETE: + return pht( + '%s deleted %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } return parent::getTitleForFeed($story); } @@ -179,6 +256,10 @@ case self::TYPE_CONTENT: $tags[] = self::MAILTAG_CONTENT; break; + case self::TYPE_DELETE: + $tags[] = self::MAILTAG_DELETE; + break; + } return $tags; }