Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/infrastructure/diff/PhabricatorInlineCommentController.php b/src/infrastructure/diff/PhabricatorInlineCommentController.php
index e1b8dc7264..693f66066f 100644
--- a/src/infrastructure/diff/PhabricatorInlineCommentController.php
+++ b/src/infrastructure/diff/PhabricatorInlineCommentController.php
@@ -1,591 +1,596 @@
<?php
abstract class PhabricatorInlineCommentController
extends PhabricatorController {
private $containerObject;
abstract protected function createComment();
abstract protected function newInlineCommentQuery();
abstract protected function loadCommentForDone($id);
abstract protected function loadObjectOwnerPHID(
PhabricatorInlineComment $inline);
abstract protected function newContainerObject();
final protected function getContainerObject() {
if ($this->containerObject === null) {
$object = $this->newContainerObject();
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) {
throw new PhutilMethodNotImplementedException();
}
protected function showComments(array $ids) {
throw new PhutilMethodNotImplementedException();
}
private $changesetID;
private $isNewFile;
private $isOnRight;
private $lineNumber;
private $lineLength;
private $operation;
private $commentID;
private $renderer;
private $replyToCommentPHID;
public function getCommentID() {
return $this->commentID;
}
public function getOperation() {
return $this->operation;
}
public function getLineLength() {
return $this->lineLength;
}
public function getLineNumber() {
return $this->lineNumber;
}
public function getIsOnRight() {
return $this->isOnRight;
}
public function getChangesetID() {
return $this->changesetID;
}
public function getIsNewFile() {
return $this->isNewFile;
}
public function setRenderer($renderer) {
$this->renderer = $renderer;
return $this;
}
public function getRenderer() {
return $this->renderer;
}
public function setReplyToCommentPHID($phid) {
$this->replyToCommentPHID = $phid;
return $this;
}
public function getReplyToCommentPHID() {
return $this->replyToCommentPHID;
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $this->getViewer();
if (!$request->validateCSRF()) {
return new Aphront404Response();
}
$this->readRequestParameters();
$op = $this->getOperation();
switch ($op) {
case 'hide':
case 'show':
$ids = $request->getStrList('ids');
if ($ids) {
if ($op == 'hide') {
$this->hideComments($ids);
} else {
$this->showComments($ids);
}
}
return id(new AphrontAjaxResponse())->setContent(array());
case 'done':
$inline = $this->loadCommentForDone($this->getCommentID());
$is_draft_state = false;
$is_checked = false;
switch ($inline->getFixedState()) {
case PhabricatorInlineComment::STATE_DRAFT:
$next_state = PhabricatorInlineComment::STATE_UNDONE;
break;
case PhabricatorInlineComment::STATE_UNDRAFT:
$next_state = PhabricatorInlineComment::STATE_DONE;
$is_checked = true;
break;
case PhabricatorInlineComment::STATE_DONE:
$next_state = PhabricatorInlineComment::STATE_UNDRAFT;
$is_draft_state = true;
break;
default:
case PhabricatorInlineComment::STATE_UNDONE:
$next_state = PhabricatorInlineComment::STATE_DRAFT;
$is_draft_state = true;
$is_checked = true;
break;
}
$inline->setFixedState($next_state)->save();
return id(new AphrontAjaxResponse())
->setContent(
array(
'isChecked' => $is_checked,
'draftState' => $is_draft_state,
));
case 'delete':
case 'undelete':
case 'refdelete':
// NOTE: For normal deletes, we just process the delete immediately
// and show an "Undo" action. For deletes by reference from the
// preview ("refdelete"), we prompt first (because the "Undo" may
// not draw, or may not be easy to locate).
if ($op == 'refdelete') {
if (!$request->isFormPost()) {
return $this->newDialog()
->setTitle(pht('Really delete comment?'))
->addHiddenInput('id', $this->getCommentID())
->addHiddenInput('op', $op)
->appendParagraph(pht('Delete this inline comment?'))
->addCancelButton('#')
->addSubmitButton(pht('Delete'));
}
}
$is_delete = ($op == 'delete' || $op == 'refdelete');
$inline = $this->loadCommentByIDForEdit($this->getCommentID());
if ($is_delete) {
$inline->setIsDeleted(1);
} else {
$inline->setIsDeleted(0);
}
$this->saveComment($inline);
return $this->buildEmptyResponse();
case 'edit':
case 'save':
$inline = $this->loadCommentByIDForEdit($this->getCommentID());
if ($op === 'save') {
$this->updateCommentContentState($inline);
$inline->setIsEditing(false);
if (!$inline->isVoidComment($viewer)) {
+ $inline->setIsDeleted(0);
+
$this->saveComment($inline);
return $this->buildRenderedCommentResponse(
$inline,
$this->getIsOnRight());
} else {
$inline->setIsDeleted(1);
$this->saveComment($inline);
return $this->buildEmptyResponse();
}
} else {
// NOTE: At time of writing, the "editing" state of inlines is
// preserved by simulating a click on "Edit" when the inline loads.
// In this case, we don't want to "saveComment()", because it
// recalculates object drafts and purges versioned drafts.
// The recalculation is merely unnecessary (state doesn't change)
// but purging drafts means that loading a page and then closing it
// discards your drafts.
// To avoid the purge, only invoke "saveComment()" if we actually
// have changes to apply.
$is_dirty = false;
if (!$inline->getIsEditing()) {
- $inline->setIsEditing(true);
+ $inline
+ ->setIsDeleted(0)
+ ->setIsEditing(true);
+
$is_dirty = true;
}
if ($this->hasContentState()) {
$this->updateCommentContentState($inline);
$is_dirty = true;
} else {
PhabricatorInlineComment::loadAndAttachVersionedDrafts(
$viewer,
array($inline));
}
if ($is_dirty) {
$this->saveComment($inline);
}
}
$edit_dialog = $this->buildEditDialog($inline)
->setTitle(pht('Edit Inline Comment'));
$view = $this->buildScaffoldForView($edit_dialog);
return $this->newInlineResponse($inline, $view, true);
case 'cancel':
$inline = $this->loadCommentByIDForEdit($this->getCommentID());
$inline->setIsEditing(false);
// 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
// to set the stored state back to "A".
$this->updateCommentContentState($inline);
if ($inline->isVoidComment($viewer)) {
$inline->setIsDeleted(1);
}
$this->saveComment($inline);
return $this->buildEmptyResponse();
case 'draft':
$inline = $this->loadCommentByIDForEdit($this->getCommentID());
$versioned_draft = PhabricatorVersionedDraft::loadOrCreateDraft(
$inline->getPHID(),
$viewer->getPHID(),
$inline->getID());
$map = $this->newRequestContentState($inline)->newStorageMap();
$versioned_draft->setProperty('inline.state', $map);
$versioned_draft->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();
}
return $this->buildEmptyResponse();
case 'new':
case 'reply':
default:
// NOTE: We read the values from the client (the display values), not
// the values from the database (the original values) when replying.
// In particular, when replying to a ghost comment which was moved
// across diffs and then moved backward to the most recent visible
// line, we want to reply on the display line (which exists), not on
// the comment's original line (which may not exist in this changeset).
$is_new = $this->getIsNewFile();
$number = $this->getLineNumber();
$length = $this->getLineLength();
$inline = $this->createComment()
->setChangesetID($this->getChangesetID())
->setAuthorPHID($viewer->getPHID())
->setIsNewFile($is_new)
->setLineNumber($number)
->setLineLength($length)
->setReplyToCommentPHID($this->getReplyToCommentPHID())
->setIsEditing(true)
->setStartOffset($request->getInt('startOffset'))
->setEndOffset($request->getInt('endOffset'))
->setContent('');
$document_engine_key = $request->getStr('documentEngineKey');
if ($document_engine_key !== null) {
$inline->setDocumentEngineKey($document_engine_key);
}
// If you own this object, mark your own inlines as "Done" by default.
$owner_phid = $this->loadObjectOwnerPHID($inline);
if ($owner_phid) {
if ($viewer->getPHID() == $owner_phid) {
$fixed_state = PhabricatorInlineComment::STATE_DRAFT;
$inline->setFixedState($fixed_state);
}
}
if ($this->hasContentState()) {
$this->updateCommentContentState($inline);
}
$this->saveComment($inline);
// Reload the inline to attach context.
$inline = $this->loadCommentByIDForEdit($inline->getID());
$edit_dialog = $this->buildEditDialog($inline);
if ($this->getOperation() == 'reply') {
$edit_dialog->setTitle(pht('Reply to Inline Comment'));
} else {
$edit_dialog->setTitle(pht('New Inline Comment'));
}
$view = $this->buildScaffoldForView($edit_dialog);
return $this->newInlineResponse($inline, $view, true);
}
}
private function readRequestParameters() {
$request = $this->getRequest();
// NOTE: This isn't necessarily a DifferentialChangeset ID, just an
// application identifier for the changeset. In Diffusion, it's a Path ID.
$this->changesetID = $request->getInt('changesetID');
$this->isNewFile = (int)$request->getBool('is_new');
$this->isOnRight = $request->getBool('on_right');
$this->lineNumber = $request->getInt('number');
$this->lineLength = $request->getInt('length');
$this->commentID = $request->getInt('id');
$this->operation = $request->getStr('op');
$this->renderer = $request->getStr('renderer');
$this->replyToCommentPHID = $request->getStr('replyToCommentPHID');
if ($this->getReplyToCommentPHID()) {
$reply_phid = $this->getReplyToCommentPHID();
$reply_comment = $this->loadCommentByPHID($reply_phid);
if (!$reply_comment) {
throw new Exception(
pht('Failed to load comment "%s".', $reply_phid));
}
// When replying, force the new comment into the same location as the
// old comment. If we don't do this, replying to a ghost comment from
// diff A while viewing diff B can end up placing the two comments in
// different places while viewing diff C, because the porting algorithm
// makes a different decision. Forcing the comments to bind to the same
// place makes sure they stick together no matter which diff is being
// viewed. See T10562 for discussion.
$this->changesetID = $reply_comment->getChangesetID();
$this->isNewFile = $reply_comment->getIsNewFile();
$this->lineNumber = $reply_comment->getLineNumber();
$this->lineLength = $reply_comment->getLineLength();
}
}
private function buildEditDialog(PhabricatorInlineComment $inline) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$edit_dialog = id(new PHUIDiffInlineCommentEditView())
->setViewer($viewer)
->setInlineComment($inline)
->setIsOnRight($this->getIsOnRight())
->setRenderer($this->getRenderer());
return $edit_dialog;
}
private function buildEmptyResponse() {
return id(new AphrontAjaxResponse())
->setContent(
array(
'inline' => array(),
'view' => null,
));
}
private function buildRenderedCommentResponse(
PhabricatorInlineComment $inline,
$on_right) {
$request = $this->getRequest();
$viewer = $this->getViewer();
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($viewer);
$engine->addObject(
$inline,
PhabricatorInlineComment::MARKUP_FIELD_BODY);
$engine->process();
$phids = array($viewer->getPHID());
$handles = $this->loadViewerHandles($phids);
$object_owner_phid = $this->loadObjectOwnerPHID($inline);
$view = id(new PHUIDiffInlineCommentDetailView())
->setUser($viewer)
->setInlineComment($inline)
->setIsOnRight($on_right)
->setMarkupEngine($engine)
->setHandles($handles)
->setEditable(true)
->setCanMarkDone(false)
->setObjectOwnerPHID($object_owner_phid);
$view = $this->buildScaffoldForView($view);
return $this->newInlineResponse($inline, $view, false);
}
private function buildScaffoldForView(PHUIDiffInlineCommentView $view) {
$renderer = DifferentialChangesetHTMLRenderer::getHTMLRendererByKey(
$this->getRenderer());
$view = $renderer->getRowScaffoldForInline($view);
return id(new PHUIDiffInlineCommentTableScaffold())
->addRowScaffold($view);
}
private function newInlineResponse(
PhabricatorInlineComment $inline,
$view,
$is_edit) {
if ($inline->getReplyToCommentPHID()) {
$can_suggest = false;
} else {
$can_suggest = (bool)$inline->getInlineContext();
}
if ($is_edit) {
$viewer = $this->getViewer();
$content_state = $inline->getContentStateForEdit($viewer);
} else {
$content_state = $inline->getContentState();
}
$state_map = $content_state->newStorageMap();
$response = array(
'inline' => array(
'id' => $inline->getID(),
'contentState' => $state_map,
'canSuggestEdit' => $can_suggest,
),
'view' => hsprintf('%s', $view),
);
return id(new AphrontAjaxResponse())
->setContent($response);
}
final protected function loadCommentByID($id) {
$query = $this->newInlineCommentQuery()
->withIDs(array($id));
return $this->loadCommentByQuery($query);
}
final protected function loadCommentByPHID($phid) {
$query = $this->newInlineCommentQuery()
->withPHIDs(array($phid));
return $this->loadCommentByQuery($query);
}
final protected function loadCommentByIDForEdit($id) {
$viewer = $this->getViewer();
$query = $this->newInlineCommentQuery()
->withIDs(array($id))
->needInlineContext(true);
$inline = $this->loadCommentByQuery($query);
if (!$inline) {
throw new Exception(
pht(
'Unable to load inline "%s".',
$id));
}
if (!$this->canEditInlineComment($viewer, $inline)) {
throw new Exception(
pht(
'Inline comment "%s" is not editable.',
$id));
}
return $inline;
}
private function loadCommentByQuery(
PhabricatorDiffInlineCommentQuery $query) {
$viewer = $this->getViewer();
$inline = $query
->setViewer($viewer)
->executeOne();
if ($inline) {
$inline = $inline->newInlineCommentObject();
}
return $inline;
}
private function hasContentState() {
$request = $this->getRequest();
return (bool)$request->getBool('hasContentState');
}
private function newRequestContentState($inline) {
$request = $this->getRequest();
return $inline->newContentStateFromRequest($request);
}
private function updateCommentContentState(PhabricatorInlineComment $inline) {
if (!$this->hasContentState()) {
throw new Exception(
pht(
'Attempting to update comment content state, but request has no '.
'content state.'));
}
$state = $this->newRequestContentState($inline);
$inline->setContentState($state);
}
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);
}
}

File Metadata

Mime Type
text/x-diff
Expires
Sun, Nov 24, 8:11 AM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
993
Default Alt Text
(18 KB)

Event Timeline