diff --git a/src/applications/phriction/controller/PhrictionEditController.php b/src/applications/phriction/controller/PhrictionEditController.php index 850e5f47fc..e85b7bc788 100644 --- a/src/applications/phriction/controller/PhrictionEditController.php +++ b/src/applications/phriction/controller/PhrictionEditController.php @@ -1,302 +1,266 @@ id = idx($data, 'id'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $current_version = null; if ($this->id) { $document = id(new PhrictionDocumentQuery()) ->setViewer($user) ->withIDs(array($this->id)) ->needContent(true) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$document) { return new Aphront404Response(); } $current_version = $document->getContent()->getVersion(); $revert = $request->getInt('revert'); if ($revert) { $content = id(new PhrictionContent())->loadOneWhere( 'documentID = %d AND version = %d', $document->getID(), $revert); if (!$content) { return new Aphront404Response(); } } else { $content = $document->getContent(); } } else { $slug = $request->getStr('slug'); $slug = PhabricatorSlug::normalize($slug); if (!$slug) { return new Aphront404Response(); } $document = id(new PhrictionDocumentQuery()) ->setViewer($user) ->withSlugs(array($slug)) ->needContent(true) ->executeOne(); if ($document) { $content = $document->getContent(); $current_version = $content->getVersion(); } else { if (PhrictionDocument::isProjectSlug($slug)) { $project = id(new PhabricatorProjectQuery()) ->setViewer($user) ->withPhrictionSlugs(array( PhrictionDocument::getProjectSlugIdentifier($slug), )) ->executeOne(); if (!$project) { return new Aphront404Response(); } } $document = PhrictionDocument::initializeNewDocument($user, $slug); $content = $document->getContent(); } } if ($request->getBool('nodraft')) { $draft = null; $draft_key = null; } else { if ($document->getPHID()) { $draft_key = $document->getPHID().':'.$content->getVersion(); } else { $draft_key = 'phriction:'.$content->getSlug(); } $draft = id(new PhabricatorDraft())->loadOneWhere( 'authorPHID = %s AND draftKey = %s', $user->getPHID(), $draft_key); } + if ($draft && + strlen($draft->getDraft()) && + ($draft->getDraft() != $content->getContent())) { + $content_text = $draft->getDraft(); + + $discard = phutil_tag( + 'a', + array( + 'href' => $request->getRequestURI()->alter('nodraft', true), + ), + pht('discard this draft')); + + $draft_note = new AphrontErrorView(); + $draft_note->setSeverity(AphrontErrorView::SEVERITY_NOTICE); + $draft_note->setTitle('Recovered Draft'); + $draft_note->appendChild(hsprintf( + '

Showing a saved draft of your edits, you can %s.

', + $discard)); + } else { + $content_text = $content->getContent(); + $draft_note = null; + } + require_celerity_resource('phriction-document-css'); $e_title = true; + $e_content = true; + $validation_exception = null; $notes = null; - $errors = array(); + $title = $content->getTitle(); + $overwrite = false; if ($request->isFormPost()) { - $overwrite = $request->getBool('overwrite'); - if (!$overwrite) { - $edit_version = $request->getStr('contentVersion'); - if ($edit_version != $current_version) { - $dialog = $this->newDialog() - ->setTitle(pht('Edit Conflict!')) - ->appendParagraph( - pht( - 'Another user made changes to this document after you began '. - 'editing it. Do you want to overwrite their changes?')) - ->appendParagraph( - pht( - 'If you choose to overwrite their changes, you should review '. - 'the document edit history to see what you overwrote, and '. - 'then make another edit to merge the changes if necessary.')) - ->addSubmitButton(pht('Overwrite Changes')) - ->addCancelButton($request->getRequestURI()); - - $dialog->addHiddenInput('overwrite', 'true'); - foreach ($request->getPassthroughRequestData() as $key => $value) { - $dialog->addHiddenInput($key, $value); - } - - return $dialog; - } - } - - $title = $request->getStr('title'); + $content_text = $request->getStr('content'); $notes = $request->getStr('description'); - - if (!strlen($title)) { - $e_title = pht('Required'); - $errors[] = pht('Document title is required.'); - } else { - $e_title = null; - } - - if ($document->getID()) { - if ($content->getTitle() == $title && - $content->getContent() == $request->getStr('content')) { - - $dialog = new AphrontDialogView(); - $dialog->setUser($user); - $dialog->setTitle(pht('No Edits')); - $dialog->appendChild(phutil_tag('p', array(), pht( - 'You did not make any changes to the document.'))); - $dialog->addCancelButton($request->getRequestURI()); - - return id(new AphrontDialogResponse())->setDialog($dialog); - } - } else if (!strlen($request->getStr('content'))) { - - // We trigger this only for new pages. For existing pages, deleting - // all the content counts as deleting the page. - - $dialog = new AphrontDialogView(); - $dialog->setUser($user); - $dialog->setTitle(pht('Empty Page')); - $dialog->appendChild(phutil_tag('p', array(), pht( - 'You can not create an empty document.'))); - $dialog->addCancelButton($request->getRequestURI()); - - return id(new AphrontDialogResponse())->setDialog($dialog); - } - - if (!count($errors)) { - - $xactions = array(); - $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_TITLE) - ->setNewValue($title); - $xactions[] = id(new PhrictionTransaction()) - ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) - ->setNewValue($request->getStr('content')); - - $editor = id(new PhrictionTransactionEditor()) - ->setActor($user) - ->setContentSourceFromRequest($request) - ->setContinueOnNoEffect(true) - ->setDescription($notes) - ->applyTransactions($document, $xactions); + $current_version = $request->getInt('contentVersion'); + + $xactions = array(); + $xactions[] = id(new PhrictionTransaction()) + ->setTransactionType(PhrictionTransaction::TYPE_TITLE) + ->setNewValue($title); + $xactions[] = id(new PhrictionTransaction()) + ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) + ->setNewValue($content_text); + + $editor = id(new PhrictionTransactionEditor()) + ->setActor($user) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setDescription($notes) + ->setProcessContentVersionError(!$request->getBool('overwrite')) + ->setContentVersion($current_version); + + try { + $editor->applyTransactions($document, $xactions); if ($draft) { $draft->delete(); } $uri = PhrictionDocument::getSlugURI($document->getSlug()); return id(new AphrontRedirectResponse())->setURI($uri); + } catch (PhabricatorApplicationTransactionValidationException $ex) { + $validation_exception = $ex; + $e_title = $ex->getShortMessage( + PhrictionTransaction::TYPE_TITLE); + $e_content = $ex->getShortMessage( + PhrictionTransaction::TYPE_CONTENT); + + // if we're not supposed to process the content version error, then + // overwrite that content...! + if (!$editor->getProcessContentVersionError()) { + $overwrite = true; + } + + // TODO - remember to set policy to what the user tried to set it to } } if ($document->getID()) { $panel_header = pht('Edit Phriction Document'); - $submit_button = pht('Save Changes'); + $page_title = pht('Edit Document'); + if ($overwrite) { + $submit_button = pht('Overwrite Changes'); + } else { + $submit_button = pht('Save Changes'); + } } else { $panel_header = pht('Create New Phriction Document'); $submit_button = pht('Create Document'); + $page_title = pht('Create Document'); } $uri = $document->getSlug(); $uri = PhrictionDocument::getSlugURI($uri); $uri = PhabricatorEnv::getProductionURI($uri); $cancel_uri = PhrictionDocument::getSlugURI($document->getSlug()); - if ($draft && - strlen($draft->getDraft()) && - ($draft->getDraft() != $content->getContent())) { - $content_text = $draft->getDraft(); - - $discard = phutil_tag( - 'a', - array( - 'href' => $request->getRequestURI()->alter('nodraft', true), - ), - pht('discard this draft')); - - $draft_note = new AphrontErrorView(); - $draft_note->setSeverity(AphrontErrorView::SEVERITY_NOTICE); - $draft_note->setTitle('Recovered Draft'); - $draft_note->appendChild(hsprintf( - '

Showing a saved draft of your edits, you can %s.

', - $discard)); - } else { - $content_text = $content->getContent(); - $draft_note = null; - } - $form = id(new AphrontFormView()) ->setUser($user) - ->setWorkflow(true) - ->setAction($request->getRequestURI()->getPath()) ->addHiddenInput('slug', $document->getSlug()) ->addHiddenInput('nodraft', $request->getBool('nodraft')) ->addHiddenInput('contentVersion', $current_version) + ->addHiddenInput('overwrite', $overwrite) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Title')) - ->setValue($content->getTitle()) + ->setValue($title) ->setError($e_title) ->setName('title')) ->appendChild( id(new AphrontFormStaticControl()) ->setLabel(pht('URI')) ->setValue($uri)) ->appendChild( id(new PhabricatorRemarkupControl()) ->setLabel(pht('Content')) ->setValue($content_text) + ->setError($e_content) ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) ->setName('content') ->setID('document-textarea') ->setUser($user)) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Edit Notes')) ->setValue($notes) ->setError(null) ->setName('description')) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($cancel_uri) ->setValue($submit_button)); $form_box = id(new PHUIObjectBoxView()) - ->setHeaderText(pht('Edit Document')) - ->setFormErrors($errors) + ->setHeaderText($panel_header) + ->setValidationException($validation_exception) ->setForm($form); $preview = id(new PHUIRemarkupPreviewPanel()) ->setHeader(pht('Document Preview')) ->setPreviewURI('/phriction/preview/') ->setControlID('document-textarea') ->setSkin('document'); $crumbs = $this->buildApplicationCrumbs(); if ($document->getID()) { $crumbs->addTextCrumb( $content->getTitle(), PhrictionDocument::getSlugURI($document->getSlug())); $crumbs->addTextCrumb(pht('Edit')); } else { $crumbs->addTextCrumb(pht('Create')); } return $this->buildApplicationPage( array( $crumbs, $draft_note, $form_box, $preview, ), array( - 'title' => pht('Edit Document'), + 'title' => $page_title, )); } } diff --git a/src/applications/phriction/editor/PhrictionTransactionEditor.php b/src/applications/phriction/editor/PhrictionTransactionEditor.php index d97b9e0844..c8bd4096de 100644 --- a/src/applications/phriction/editor/PhrictionTransactionEditor.php +++ b/src/applications/phriction/editor/PhrictionTransactionEditor.php @@ -1,396 +1,532 @@ description = $description; return $this; } private function getDescription() { return $this->description; } private function setOldContent(PhrictionContent $content) { $this->oldContent = $content; return $this; } private function getOldContent() { return $this->oldContent; } private function setNewContent(PhrictionContent $content) { $this->newContent = $content; return $this; } private function getNewContent() { return $this->newContent; } public function setSkipAncestorCheck($bool) { $this->skipAncestorCheck = $bool; return $this; } public function getSkipAncestorCheck() { return $this->skipAncestorCheck; } + public function setContentVersion($version) { + $this->contentVersion = $version; + return $this; + } + + public function getContentVersion() { + return $this->contentVersion; + } + + public function setProcessContentVersionError($process) { + $this->processContentVersionError = $process; + return $this; + } + + public function getProcessContentVersionError() { + return $this->processContentVersionError; + } + public function getEditorApplicationClass() { return 'PhabricatorPhrictionApplication'; } public function getEditorObjectsDescription() { return pht('Phriction Documents'); } public function getTransactionTypes() { $types = parent::getTransactionTypes(); $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; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; */ return $types; } protected function getCustomTransactionOldValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhrictionTransaction::TYPE_TITLE: if ($this->getIsNewObject()) { return null; } return $this->getOldContent()->getTitle(); case PhrictionTransaction::TYPE_CONTENT: if ($this->getIsNewObject()) { return null; } return $this->getOldContent()->getContent(); case PhrictionTransaction::TYPE_DELETE: case PhrictionTransaction::TYPE_MOVE_TO: case PhrictionTransaction::TYPE_MOVE_AWAY: return null; } } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { 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; } } protected function shouldApplyInitialEffects( PhabricatorLiskDAO $object, array $xactions) { foreach ($xactions as $xaction) { 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; } } return parent::shouldApplyInitialEffects($object, $xactions); } protected function applyInitialEffects( PhabricatorLiskDAO $object, array $xactions) { $this->setOldContent($object->getContent()); $this->setNewContent($this->buildNewContentTemplate($object)); } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { 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; } } + protected function expandTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + $xactions = parent::expandTransaction($object, $xaction); + switch ($xaction->getTransactionType()) { + case PhrictionTransaction::TYPE_CONTENT: + if ($this->getIsNewObject()) { + break; + } + $content = $xaction->getNewValue(); + if ($content === '') { + $xactions[] = id(new PhrictionTransaction()) + ->setTransactionType(PhrictionTransaction::TYPE_DELETE) + ->setNewValue(true); + } + break; + default: + break; + } + + return $xactions; + } + protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhrictionTransaction::TYPE_TITLE: $this->getNewContent()->setTitle($xaction->getNewValue()); break; 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; } } protected function applyFinalEffects( PhabricatorLiskDAO $object, array $xactions) { $save_content = false; foreach ($xactions as $xaction) { 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: break; } } if ($save_content) { $content = $this->getNewContent(); $content->setDocumentID($object->getID()); $content->save(); $object->setContentID($content->getID()); $object->save(); $object->attachContent($content); } if ($this->getIsNewObject() && !$this->getSkipAncestorCheck()) { // Stub out empty parent documents if they don't exist $ancestral_slugs = PhabricatorSlug::getAncestry($object->getSlug()); if ($ancestral_slugs) { $ancestors = id(new PhrictionDocumentQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withSlugs($ancestral_slugs) ->needContent(true) ->execute(); $ancestors = mpull($ancestors, null, 'getSlug'); $stub_type = PhrictionChangeType::CHANGE_STUB; foreach ($ancestral_slugs as $slug) { $ancestor_doc = idx($ancestors, $slug); // We check for change type to prevent near-infinite recursion if (!$ancestor_doc && $content->getChangeType() != $stub_type) { $ancestor_doc = PhrictionDocument::initializeNewDocument( $this->getActor(), $slug); $stub_xactions = array(); $stub_xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhrictionTransaction::TYPE_TITLE) ->setNewValue(PhabricatorSlug::getDefaultTitle($slug)) ->setMetadataValue('stub:create:phid', $object->getPHID()); $stub_xactions[] = id(new PhrictionTransaction()) ->setTransactionType(PhrictionTransaction::TYPE_CONTENT) - ->setNewValue(''); + ->setNewValue('') + ->setMetadataValue('stub:create:phid', $object->getPHID()); $sub_editor = id(new PhrictionTransactionEditor()) ->setActor($this->getActor()) ->setContentSource($this->getContentSource()) ->setContinueOnNoEffect($this->getContinueOnNoEffect()) ->setSkipAncestorCheck(true) ->setDescription(pht('Empty Parent Document')) ->applyTransactions($ancestor_doc, $stub_xactions); } } } } 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) { return true; } protected function getMailSubjectPrefix() { return '[Phriction]'; } protected function getMailTo(PhabricatorLiskDAO $object) { return array( $object->getContent()->getAuthorPHID(), $this->getActingAsPHID(), ); } public function getMailTagsMap() { return array( PhrictionTransaction::MAILTAG_TITLE => pht("A document's title changes."), PhrictionTransaction::MAILTAG_CONTENT => pht("A document's content changes."), PhrictionTransaction::MAILTAG_DELETE => pht('A document is deleted.'), ); } protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new PhrictionReplyHandler()) ->setMailReceiver($object); } protected function buildMailTemplate(PhabricatorLiskDAO $object) { $id = $object->getID(); $title = $object->getContent()->getTitle(); return id(new PhabricatorMetaMTAMail()) ->setSubject($title) ->addHeader('Thread-Topic', $object->getPHID()); } protected function buildMailBody( PhabricatorLiskDAO $object, array $xactions) { $body = parent::buildMailBody($object, $xactions); if ($this->getIsNewObject()) { $body->addTextSection( pht('DOCUMENT CONTENT'), $object->getContent()->getContent()); } $body->addLinkSection( pht('DOCUMENT DETAIL'), PhabricatorEnv::getProductionURI( PhrictionDocument::getSlugURI($object->getSlug()))); return $body; } protected function shouldPublishFeedStory( PhabricatorLiskDAO $object, array $xactions) { return $this->shouldSendMail($object, $xactions); } protected function getFeedRelatedPHIDs( PhabricatorLiskDAO $object, array $xactions) { $phids = parent::getFeedRelatedPHIDs($object, $xactions); foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case PhrictionTransaction::TYPE_MOVE_TO: $dict = $xaction->getNewValue(); $phids[] = $dict['phid']; break; } } return $phids; } + protected function validateTransaction( + PhabricatorLiskDAO $object, + $type, + array $xactions) { + + $errors = parent::validateTransaction($object, $type, $xactions); + + foreach ($xactions as $xaction) { + switch ($type) { + case PhrictionTransaction::TYPE_TITLE: + $title = $object->getContent()->getTitle(); + $missing = $this->validateIsEmptyTextField( + $title, + $xactions); + + if ($missing) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht('Document title is required.'), + nonempty(last($xactions), null)); + + $error->setIsMissingFieldError(true); + $errors[] = $error; + } else if ($this->getProcessContentVersionError()) { + $error = $this->validateContentVersion($object, $type, $xaction); + if ($error) { + $this->setProcessContentVersionError(false); + $errors[] = $error; + } + } + break; + + case PhrictionTransaction::TYPE_CONTENT: + if ($xaction->getMetadataValue('stub:create:phid')) { + continue; + } + + $missing = false; + if ($this->getIsNewObject()) { + $content = $object->getContent()->getContent(); + $missing = $this->validateIsEmptyTextField( + $content, + $xactions); + } + + if ($missing) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht('Document content is required.'), + nonempty(last($xactions), null)); + + $error->setIsMissingFieldError(true); + $errors[] = $error; + } else if ($this->getProcessContentVersionError()) { + $error = $this->validateContentVersion($object, $type, $xaction); + if ($error) { + $this->setProcessContentVersionError(false); + $errors[] = $error; + } + } + break; + } + } + + return $errors; + } + + private function validateContentVersion( + PhabricatorLiskDAO $object, + $type, + PhabricatorApplicationTransaction $xaction) { + + $error = null; + if ($this->getContentVersion() && + ($object->getContent()->getVersion() != $this->getContentVersion())) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Edit Conflict'), + pht( + 'Another user made changes to this document after you began '. + 'editing it. Do you want to overwrite their changes? '. + '(If you choose to overwrite their changes, you should review '. + 'the document edit history to see what you overwrote, and '. + 'then make another edit to merge the changes if necessary.)'), + $xaction); + } + return $error; + } + protected function supportsSearch() { return true; } protected function shouldApplyHeraldRules( PhabricatorLiskDAO $object, array $xactions) { return false; } private function buildNewContentTemplate( PhrictionDocument $document) { $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()); } $new_content->setVersion($this->getOldContent()->getVersion() + 1); return $new_content; } }