Changeset View
Changeset View
Standalone View
Standalone View
src/infrastructure/diff/PhabricatorInlineCommentController.php
<?php | <?php | ||||
abstract class PhabricatorInlineCommentController | abstract class PhabricatorInlineCommentController | ||||
extends PhabricatorController { | extends PhabricatorController { | ||||
private $containerObject; | |||||
abstract protected function createComment(); | abstract protected function createComment(); | ||||
abstract protected function newInlineCommentQuery(); | abstract protected function newInlineCommentQuery(); | ||||
abstract protected function loadCommentForDone($id); | abstract protected function loadCommentForDone($id); | ||||
abstract protected function loadObjectOwnerPHID( | abstract protected function loadObjectOwnerPHID( | ||||
PhabricatorInlineComment $inline); | PhabricatorInlineComment $inline); | ||||
abstract protected function deleteComment( | abstract protected function newContainerObject(); | ||||
PhabricatorInlineComment $inline); | |||||
abstract protected function undeleteComment( | final protected function getContainerObject() { | ||||
PhabricatorInlineComment $inline); | if ($this->containerObject === null) { | ||||
abstract protected function saveComment( | $object = $this->newContainerObject(); | ||||
PhabricatorInlineComment $inline); | if (!$object) { | ||||
throw new Exception( | |||||
pht( | |||||
'Failed to load container object for inline comment.')); | |||||
} | |||||
$this->containerObject = $object; | |||||
} | |||||
return $this->containerObject; | |||||
} | |||||
protected function hideComments(array $ids) { | protected function hideComments(array $ids) { | ||||
throw new PhutilMethodNotImplementedException(); | throw new PhutilMethodNotImplementedException(); | ||||
} | } | ||||
protected function showComments(array $ids) { | protected function showComments(array $ids) { | ||||
throw new PhutilMethodNotImplementedException(); | throw new PhutilMethodNotImplementedException(); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 143 Lines • ▼ Show 20 Lines | switch ($op) { | ||||
} | } | ||||
} | } | ||||
$is_delete = ($op == 'delete' || $op == 'refdelete'); | $is_delete = ($op == 'delete' || $op == 'refdelete'); | ||||
$inline = $this->loadCommentByIDForEdit($this->getCommentID()); | $inline = $this->loadCommentByIDForEdit($this->getCommentID()); | ||||
if ($is_delete) { | if ($is_delete) { | ||||
$this->deleteComment($inline); | $inline->setIsDeleted(1); | ||||
} else { | } else { | ||||
$this->undeleteComment($inline); | $inline->setIsDeleted(0); | ||||
} | } | ||||
$this->saveComment($inline); | |||||
return $this->buildEmptyResponse(); | return $this->buildEmptyResponse(); | ||||
case 'edit': | case 'edit': | ||||
$inline = $this->loadCommentByIDForEdit($this->getCommentID()); | $inline = $this->loadCommentByIDForEdit($this->getCommentID()); | ||||
$text = $this->getCommentText(); | $text = $this->getCommentText(); | ||||
if ($request->isFormPost()) { | if ($request->isFormPost()) { | ||||
if (strlen($text)) { | if (strlen($text)) { | ||||
$inline | $inline | ||||
->setContent($text) | ->setContent($text) | ||||
->setIsEditing(false); | ->setIsEditing(false); | ||||
$this->saveComment($inline); | $this->saveComment($inline); | ||||
$this->purgeVersionedDrafts($inline); | |||||
return $this->buildRenderedCommentResponse( | return $this->buildRenderedCommentResponse( | ||||
$inline, | $inline, | ||||
$this->getIsOnRight()); | $this->getIsOnRight()); | ||||
} else { | } else { | ||||
$this->deleteComment($inline); | $inline->setIsDeleted(1); | ||||
$this->purgeVersionedDrafts($inline); | |||||
$this->saveComment($inline); | |||||
return $this->buildEmptyResponse(); | return $this->buildEmptyResponse(); | ||||
} | } | ||||
} else { | } else { | ||||
$inline->setIsEditing(true); | // NOTE: At time of writing, the "editing" state of inlines is | ||||
// preserved by simluating a click on "Edit" when the inline loads. | |||||
if (strlen($text)) { | // In this case, we don't want to "saveComment()", because it | ||||
$inline->setContent($text); | // recalculates object drafts and purges versioned drafts. | ||||
} | |||||
$this->saveComment($inline); | // The recalculation is merely unnecessary (state doesn't change) | ||||
// but purging drafts means that loading a page and then closing it | |||||
// discards your drafts. | |||||
if (strlen($text)) { | // To avoid the purge, only invoke "saveComment()" if we actually | ||||
$this->purgeVersionedDrafts($inline); | // have changes to apply. | ||||
$is_dirty = false; | |||||
if (!$inline->getIsEditing()) { | |||||
$inline->setIsEditing(true); | |||||
$is_dirty = true; | |||||
} | } | ||||
if (strlen($text)) { | |||||
$inline->setContent($text); | |||||
$is_dirty = true; | |||||
} else { | |||||
PhabricatorInlineComment::loadAndAttachVersionedDrafts( | PhabricatorInlineComment::loadAndAttachVersionedDrafts( | ||||
$viewer, | $viewer, | ||||
array($inline)); | array($inline)); | ||||
} | } | ||||
if ($is_dirty) { | |||||
$this->saveComment($inline); | |||||
} | |||||
} | |||||
$edit_dialog = $this->buildEditDialog($inline) | $edit_dialog = $this->buildEditDialog($inline) | ||||
->setTitle(pht('Edit Inline Comment')); | ->setTitle(pht('Edit Inline Comment')); | ||||
$view = $this->buildScaffoldForView($edit_dialog); | $view = $this->buildScaffoldForView($edit_dialog); | ||||
return $this->newInlineResponse($inline, $view); | return $this->newInlineResponse($inline, $view); | ||||
case 'cancel': | case 'cancel': | ||||
$inline = $this->loadCommentByIDForEdit($this->getCommentID()); | $inline = $this->loadCommentByIDForEdit($this->getCommentID()); | ||||
$inline->setIsEditing(false); | $inline->setIsEditing(false); | ||||
// If the user uses "Undo" to get into an edited state ("AB"), then | // If the user uses "Undo" to get into an edited state ("AB"), then | ||||
// clicks cancel to return to the previous state ("A"), we also want | // clicks cancel to return to the previous state ("A"), we also want | ||||
// to set the stored state back to "A". | // to set the stored state back to "A". | ||||
$text = $this->getCommentText(); | $text = $this->getCommentText(); | ||||
if (strlen($text)) { | if (strlen($text)) { | ||||
$inline->setContent($text); | $inline->setContent($text); | ||||
} | } | ||||
$content = $inline->getContent(); | $content = $inline->getContent(); | ||||
if (!strlen($content)) { | if (!strlen($content)) { | ||||
$this->deleteComment($inline); | $inline->setIsDeleted(1); | ||||
} else { | |||||
$this->saveComment($inline); | |||||
} | } | ||||
$this->purgeVersionedDrafts($inline); | $this->saveComment($inline); | ||||
return $this->buildEmptyResponse(); | return $this->buildEmptyResponse(); | ||||
case 'draft': | case 'draft': | ||||
$inline = $this->loadCommentByIDForEdit($this->getCommentID()); | $inline = $this->loadCommentByIDForEdit($this->getCommentID()); | ||||
$versioned_draft = PhabricatorVersionedDraft::loadOrCreateDraft( | $versioned_draft = PhabricatorVersionedDraft::loadOrCreateDraft( | ||||
$inline->getPHID(), | $inline->getPHID(), | ||||
$viewer->getPHID(), | $viewer->getPHID(), | ||||
$inline->getID()); | $inline->getID()); | ||||
$text = $this->getCommentText(); | $text = $this->getCommentText(); | ||||
$versioned_draft | $versioned_draft | ||||
->setProperty('inline.text', $text) | ->setProperty('inline.text', $text) | ||||
->save(); | ->save(); | ||||
// We have to synchronize the draft engine after saving a versioned | |||||
// draft, because taking an inline comment from "no text, no draft" | |||||
// to "no text, text in a draft" marks the container object as having | |||||
// a draft. | |||||
$draft_engine = $this->newDraftEngine(); | |||||
if ($draft_engine) { | |||||
$draft_engine->synchronize(); | |||||
} else { | |||||
phlog('no draft engine'); | |||||
} | |||||
return $this->buildEmptyResponse(); | return $this->buildEmptyResponse(); | ||||
case 'new': | case 'new': | ||||
case 'reply': | case 'reply': | ||||
default: | default: | ||||
// NOTE: We read the values from the client (the display values), not | // NOTE: We read the values from the client (the display values), not | ||||
// the values from the database (the original values) when replying. | // the values from the database (the original values) when replying. | ||||
// In particular, when replying to a ghost comment which was moved | // In particular, when replying to a ghost comment which was moved | ||||
// across diffs and then moved backward to the most recent visible | // across diffs and then moved backward to the most recent visible | ||||
▲ Show 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | $response = array( | ||||
), | ), | ||||
'view' => hsprintf('%s', $view), | 'view' => hsprintf('%s', $view), | ||||
); | ); | ||||
return id(new AphrontAjaxResponse()) | return id(new AphrontAjaxResponse()) | ||||
->setContent($response); | ->setContent($response); | ||||
} | } | ||||
private function purgeVersionedDrafts( | |||||
PhabricatorInlineComment $inline) { | |||||
$viewer = $this->getViewer(); | |||||
PhabricatorVersionedDraft::purgeDrafts( | |||||
$inline->getPHID(), | |||||
$viewer->getPHID()); | |||||
} | |||||
final protected function loadCommentByID($id) { | final protected function loadCommentByID($id) { | ||||
$query = $this->newInlineCommentQuery() | $query = $this->newInlineCommentQuery() | ||||
->withIDs(array($id)); | ->withIDs(array($id)); | ||||
return $this->loadCommentByQuery($query); | return $this->loadCommentByQuery($query); | ||||
} | } | ||||
final protected function loadCommentByPHID($phid) { | final protected function loadCommentByPHID($phid) { | ||||
Show All 38 Lines | private function loadCommentByQuery( | ||||
if ($inline) { | if ($inline) { | ||||
$inline = $inline->newInlineCommentObject(); | $inline = $inline->newInlineCommentObject(); | ||||
} | } | ||||
return $inline; | return $inline; | ||||
} | } | ||||
private function saveComment(PhabricatorInlineComment $inline) { | |||||
$viewer = $this->getViewer(); | |||||
$draft_engine = $this->newDraftEngine(); | |||||
$inline->openTransaction(); | |||||
$inline->save(); | |||||
PhabricatorVersionedDraft::purgeDrafts( | |||||
$inline->getPHID(), | |||||
$viewer->getPHID()); | |||||
if ($draft_engine) { | |||||
$draft_engine->synchronize(); | |||||
} | |||||
$inline->saveTransaction(); | |||||
} | |||||
private function newDraftEngine() { | |||||
$viewer = $this->getViewer(); | |||||
$object = $this->getContainerObject(); | |||||
if (!($object instanceof PhabricatorDraftInterface)) { | |||||
return null; | |||||
} | |||||
return $object->newDraftEngine() | |||||
->setObject($object) | |||||
->setViewer($viewer); | |||||
} | |||||
} | } |