1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 22:10:55 +01:00

Phriction - consolidate edit business logic into Editor

Summary:
Ref T4029. Some business logic lives outside the editor. This revision moves that logic from the edit controller into the editor proper. This makes re-using that business logic across other endpoints - say like a conduit end point - possible. This is also part of the general modernization quest for phriction I am on.

This diff also restores the functionality where you can delete a document by wiping out the content and saving.

Test Plan: tried to make a document with no title or content and saw errors. opened a document for edit with user 1, then made edits with user 2, then saw an error when i made the edit with user 1. clicking "overwrite changes" then worked.  deleted a document by wiping out the body and clicking save.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T4029

Differential Revision: https://secure.phabricator.com/D10795
This commit is contained in:
Bob Trahan 2014-11-06 14:02:52 -08:00
parent 9f8dde9cf6
commit 37bc0474fe
2 changed files with 223 additions and 123 deletions

View file

@ -93,123 +93,9 @@ final class PhrictionEditController
$draft_key); $draft_key);
} }
require_celerity_resource('phriction-document-css');
$e_title = true;
$notes = null;
$errors = array();
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');
$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);
if ($draft) {
$draft->delete();
}
$uri = PhrictionDocument::getSlugURI($document->getSlug());
return id(new AphrontRedirectResponse())->setURI($uri);
}
}
if ($document->getID()) {
$panel_header = pht('Edit Phriction Document');
$submit_button = pht('Save Changes');
} else {
$panel_header = pht('Create New Phriction Document');
$submit_button = pht('Create Document');
}
$uri = $document->getSlug();
$uri = PhrictionDocument::getSlugURI($uri);
$uri = PhabricatorEnv::getProductionURI($uri);
$cancel_uri = PhrictionDocument::getSlugURI($document->getSlug());
if ($draft && if ($draft &&
strlen($draft->getDraft()) && strlen($draft->getDraft()) &&
($draft->getDraft() != $content->getContent())) { ($draft->getDraft() != $content->getContent())) {
$content_text = $draft->getDraft(); $content_text = $draft->getDraft();
$discard = phutil_tag( $discard = phutil_tag(
@ -230,17 +116,94 @@ final class PhrictionEditController
$draft_note = null; $draft_note = null;
} }
require_celerity_resource('phriction-document-css');
$e_title = true;
$e_content = true;
$validation_exception = null;
$notes = null;
$title = $content->getTitle();
$overwrite = false;
if ($request->isFormPost()) {
$title = $request->getStr('title');
$content_text = $request->getStr('content');
$notes = $request->getStr('description');
$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');
$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());
$form = id(new AphrontFormView()) $form = id(new AphrontFormView())
->setUser($user) ->setUser($user)
->setWorkflow(true)
->setAction($request->getRequestURI()->getPath())
->addHiddenInput('slug', $document->getSlug()) ->addHiddenInput('slug', $document->getSlug())
->addHiddenInput('nodraft', $request->getBool('nodraft')) ->addHiddenInput('nodraft', $request->getBool('nodraft'))
->addHiddenInput('contentVersion', $current_version) ->addHiddenInput('contentVersion', $current_version)
->addHiddenInput('overwrite', $overwrite)
->appendChild( ->appendChild(
id(new AphrontFormTextControl()) id(new AphrontFormTextControl())
->setLabel(pht('Title')) ->setLabel(pht('Title'))
->setValue($content->getTitle()) ->setValue($title)
->setError($e_title) ->setError($e_title)
->setName('title')) ->setName('title'))
->appendChild( ->appendChild(
@ -251,6 +214,7 @@ final class PhrictionEditController
id(new PhabricatorRemarkupControl()) id(new PhabricatorRemarkupControl())
->setLabel(pht('Content')) ->setLabel(pht('Content'))
->setValue($content_text) ->setValue($content_text)
->setError($e_content)
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
->setName('content') ->setName('content')
->setID('document-textarea') ->setID('document-textarea')
@ -267,8 +231,8 @@ final class PhrictionEditController
->setValue($submit_button)); ->setValue($submit_button));
$form_box = id(new PHUIObjectBoxView()) $form_box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Edit Document')) ->setHeaderText($panel_header)
->setFormErrors($errors) ->setValidationException($validation_exception)
->setForm($form); ->setForm($form);
$preview = id(new PHUIRemarkupPreviewPanel()) $preview = id(new PHUIRemarkupPreviewPanel())
@ -295,7 +259,7 @@ final class PhrictionEditController
$preview, $preview,
), ),
array( array(
'title' => pht('Edit Document'), 'title' => $page_title,
)); ));
} }

View file

@ -8,6 +8,8 @@ final class PhrictionTransactionEditor
private $newContent; private $newContent;
private $moveAwayDocument; private $moveAwayDocument;
private $skipAncestorCheck; private $skipAncestorCheck;
private $contentVersion;
private $processContentVersionError = true;
public function setDescription($description) { public function setDescription($description) {
$this->description = $description; $this->description = $description;
@ -45,6 +47,24 @@ final class PhrictionTransactionEditor
return $this->skipAncestorCheck; 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() { public function getEditorApplicationClass() {
return 'PhabricatorPhrictionApplication'; return 'PhabricatorPhrictionApplication';
} }
@ -162,6 +182,30 @@ final class PhrictionTransactionEditor
} }
} }
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( protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) { PhabricatorApplicationTransaction $xaction) {
@ -251,7 +295,8 @@ final class PhrictionTransactionEditor
->setMetadataValue('stub:create:phid', $object->getPHID()); ->setMetadataValue('stub:create:phid', $object->getPHID());
$stub_xactions[] = id(new PhrictionTransaction()) $stub_xactions[] = id(new PhrictionTransaction())
->setTransactionType(PhrictionTransaction::TYPE_CONTENT) ->setTransactionType(PhrictionTransaction::TYPE_CONTENT)
->setNewValue(''); ->setNewValue('')
->setMetadataValue('stub:create:phid', $object->getPHID());
$sub_editor = id(new PhrictionTransactionEditor()) $sub_editor = id(new PhrictionTransactionEditor())
->setActor($this->getActor()) ->setActor($this->getActor())
->setContentSource($this->getContentSource()) ->setContentSource($this->getContentSource())
@ -366,6 +411,97 @@ final class PhrictionTransactionEditor
return $phids; 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() { protected function supportsSearch() {
return true; return true;
} }