diff --git a/src/infrastructure/diff/PhabricatorInlineCommentController.php b/src/infrastructure/diff/PhabricatorInlineCommentController.php index 4aa0a7d6ee..749a66098b 100644 --- a/src/infrastructure/diff/PhabricatorInlineCommentController.php +++ b/src/infrastructure/diff/PhabricatorInlineCommentController.php @@ -1,303 +1,312 @@ commentID; } public function getOperation() { return $this->operation; } public function getCommentText() { return $this->commentText; } 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(); $user = $request->getUser(); $this->readRequestParameters(); switch ($this->getOperation()) { case 'delete': $inline = $this->loadCommentForEdit($this->getCommentID()); if ($request->isFormPost()) { $this->deleteComment($inline); return $this->buildEmptyResponse(); } $dialog = new AphrontDialogView(); $dialog->setUser($user); $dialog->setSubmitURI($request->getRequestURI()); $dialog->setTitle(pht('Really delete this comment?')); $dialog->addHiddenInput('id', $this->getCommentID()); $dialog->addHiddenInput('op', 'delete'); $dialog->appendChild( phutil_tag('p', array(), pht('Delete this inline comment?'))); $dialog->addCancelButton('#'); $dialog->addSubmitButton(pht('Delete')); return id(new AphrontDialogResponse())->setDialog($dialog); case 'edit': $inline = $this->loadCommentForEdit($this->getCommentID()); $text = $this->getCommentText(); if ($request->isFormPost()) { if (strlen($text)) { $inline->setContent($text); $this->saveComment($inline); return $this->buildRenderedCommentResponse( $inline, $this->getIsOnRight()); } else { $this->deleteComment($inline); return $this->buildEmptyResponse(); } } $edit_dialog = $this->buildEditDialog(); $edit_dialog->setTitle(pht('Edit Inline Comment')); $edit_dialog->addHiddenInput('id', $this->getCommentID()); $edit_dialog->addHiddenInput('op', 'edit'); $edit_dialog->appendChild( $this->renderTextArea( nonempty($text, $inline->getContent()))); + $view = $this->buildScaffoldForView($edit_dialog); + return id(new AphrontAjaxResponse()) - ->setContent($edit_dialog->render()); + ->setContent($view->render()); case 'create': $text = $this->getCommentText(); if (!$request->isFormPost() || !strlen($text)) { return $this->buildEmptyResponse(); } $inline = $this->createComment() ->setChangesetID($this->getChangesetID()) ->setAuthorPHID($user->getPHID()) ->setLineNumber($this->getLineNumber()) ->setLineLength($this->getLineLength()) ->setIsNewFile($this->getIsNewFile()) ->setContent($text); if ($this->getReplyToCommentPHID()) { $inline->setReplyToCommentPHID($this->getReplyToCommentPHID()); } $this->saveComment($inline); return $this->buildRenderedCommentResponse( $inline, $this->getIsOnRight()); case 'reply': default: $edit_dialog = $this->buildEditDialog(); if ($this->getOperation() == 'reply') { $inline = $this->loadComment($this->getCommentID()); $edit_dialog->setTitle(pht('Reply to Inline Comment')); $changeset = $inline->getChangesetID(); $is_new = $inline->getIsNewFile(); $number = $inline->getLineNumber(); $length = $inline->getLineLength(); } else { $edit_dialog->setTitle(pht('New Inline Comment')); $changeset = $this->getChangesetID(); $is_new = $this->getIsNewFile(); $number = $this->getLineNumber(); $length = $this->getLineLength(); } $edit_dialog->addHiddenInput('op', 'create'); $edit_dialog->addHiddenInput('is_new', $is_new); $edit_dialog->addHiddenInput('number', $number); $edit_dialog->addHiddenInput('length', $length); $text_area = $this->renderTextArea($this->getCommentText()); $edit_dialog->appendChild($text_area); + $view = $this->buildScaffoldForView($edit_dialog); + return id(new AphrontAjaxResponse()) - ->setContent($edit_dialog->render()); + ->setContent($view->render()); } } 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->commentText = $request->getStr('text'); $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)); } if ($reply_comment->getChangesetID() != $this->getChangesetID()) { throw new Exception( pht( 'Comment "%s" belongs to wrong changeset (%s vs %s).', $reply_phid, $reply_comment->getChangesetID(), $this->getChangesetID())); } } } private function buildEditDialog() { $request = $this->getRequest(); $user = $request->getUser(); $edit_dialog = id(new PHUIDiffInlineCommentEditView()) ->setUser($user) ->setSubmitURI($request->getRequestURI()) ->setOnRight($this->getIsOnRight()) ->setIsNewFile($this->getIsNewFile()) ->setNumber($this->getLineNumber()) ->setLength($this->getLineLength()) ->setRenderer($this->getRenderer()) ->setReplyToCommentPHID($this->getReplyToCommentPHID()) ->setChangesetID($this->getChangesetID()); return $edit_dialog; } private function buildEmptyResponse() { return id(new AphrontAjaxResponse()) ->setContent( array( 'markup' => '', )); } private function buildRenderedCommentResponse( PhabricatorInlineCommentInterface $inline, $on_right) { $request = $this->getRequest(); $user = $request->getUser(); $engine = new PhabricatorMarkupEngine(); $engine->setViewer($user); $engine->addObject( $inline, PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY); $engine->process(); $phids = array($user->getPHID()); $handles = $this->loadViewerHandles($phids); $view = id(new PHUIDiffInlineCommentDetailView()) ->setInlineComment($inline) ->setOnRight($on_right) ->setMarkupEngine($engine) ->setHandles($handles) ->setEditable(true); - $renderer = DifferentialChangesetHTMLRenderer::getHTMLRendererByKey( - $this->getRenderer()); - - $view = $renderer->getRowScaffoldForInline($view); - $view = id(new PHUIDiffInlineCommentTableScaffold()) - ->addRowScaffold($view); + $view = $this->buildScaffoldForView($view); return id(new AphrontAjaxResponse()) ->setContent( array( 'inlineCommentID' => $inline->getID(), 'markup' => $view->render(), )); } private function renderTextArea($text) { return id(new PhabricatorRemarkupControl()) ->setUser($this->getRequest()->getUser()) ->setSigil('differential-inline-comment-edit-textarea') ->setName('text') ->setValue($text) ->setDisableFullScreen(true); } + private function buildScaffoldForView(PHUIDiffInlineCommentView $view) { + $renderer = DifferentialChangesetHTMLRenderer::getHTMLRendererByKey( + $this->getRenderer()); + + $view = $renderer->getRowScaffoldForInline($view); + + return id(new PHUIDiffInlineCommentTableScaffold()) + ->addRowScaffold($view); + } + } diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentEditView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentEditView.php index 47710a85e0..da89f79079 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentEditView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentEditView.php @@ -1,214 +1,189 @@ isNewFile = $is_new_file; return $this; } public function getIsNewFile() { return $this->isNewFile; } public function getIsOnRight() { return $this->onRight; } public function setRenderer($renderer) { $this->renderer = $renderer; return $this; } public function getRenderer() { return $this->renderer; } public function addHiddenInput($key, $value) { $this->inputs[] = array($key, $value); return $this; } public function setSubmitURI($uri) { $this->uri = $uri; return $this; } public function setTitle($title) { $this->title = $title; return $this; } public function setReplyToCommentPHID($reply_to_phid) { $this->replyToCommentPHID = $reply_to_phid; return $this; } public function getReplyToCommentPHID() { return $this->replyToCommentPHID; } public function setChangesetID($changeset_id) { $this->changesetID = $changeset_id; return $this; } public function getChangesetID() { return $this->changesetID; } public function setOnRight($on_right) { $this->onRight = $on_right; return $this; } public function setNumber($number) { $this->number = $number; return $this; } public function setLength($length) { $this->length = $length; return $this; } public function render() { if (!$this->uri) { throw new Exception('Call setSubmitURI() before render()!'); } if (!$this->user) { throw new Exception('Call setUser() before render()!'); } $content = phabricator_form( $this->user, array( 'action' => $this->uri, 'method' => 'POST', 'sigil' => 'inline-edit-form', ), array( $this->renderInputs(), $this->renderBody(), )); - if ($this->renderer == '1up') { - $cells = array( - phutil_tag('th', array()), - phutil_tag('th', array()), - phutil_tag( - 'td', - array('colspan' => 3, 'class' => 'right3'), - $content), - ); - } else { - $cells = array( - phutil_tag('th', array()), - phutil_tag( - 'td', - array('class' => 'left'), - $this->onRight ? null : $content), - phutil_tag('th', array()), - phutil_tag( - 'td', - array('colspan' => 3, 'class' => 'right3'), - $this->onRight ? $content : null), - ); - } - - $row = phutil_tag('tr', array('class' => 'inline-comment-splint'), $cells); - return phutil_tag('table', array(), $row); + return $content; } private function renderInputs() { $inputs = $this->inputs; $out = array(); $inputs[] = array('on_right', (bool)$this->getIsOnRight()); $inputs[] = array('replyToCommentPHID', $this->getReplyToCommentPHID()); $inputs[] = array('renderer', $this->getRenderer()); $inputs[] = array('changesetID', $this->getChangesetID()); foreach ($inputs as $input) { list($name, $value) = $input; $out[] = phutil_tag( 'input', array( 'type' => 'hidden', 'name' => $name, 'value' => $value, )); } return $out; } private function renderBody() { $buttons = array(); $buttons[] = phutil_tag('button', array(), pht('Ready')); $buttons[] = javelin_tag( 'button', array( 'sigil' => 'inline-edit-cancel', 'class' => 'grey', ), pht('Cancel')); $title = phutil_tag( 'div', array( 'class' => 'differential-inline-comment-edit-title', ), $this->title); $body = phutil_tag( 'div', array( 'class' => 'differential-inline-comment-edit-body', ), $this->renderChildren()); $edit = phutil_tag( 'div', array( 'class' => 'differential-inline-comment-edit-buttons', ), array( $buttons, phutil_tag('div', array('style' => 'clear: both'), ''), )); return javelin_tag( 'div', array( 'class' => 'differential-inline-comment-edit', 'sigil' => 'differential-inline-comment', 'meta' => array( 'changesetID' => $this->getChangesetID(), 'on_right' => $this->getIsOnRight(), 'isNewFile' => (bool)$this->getIsNewFile(), 'number' => $this->number, 'length' => $this->length, 'replyToCommentPHID' => $this->getReplyToCommentPHID(), ), ), array( $title, $body, $edit, )); } } diff --git a/webroot/rsrc/css/application/differential/changeset-view.css b/webroot/rsrc/css/application/differential/changeset-view.css index 5c3c33e5c0..b302f8b23b 100644 --- a/webroot/rsrc/css/application/differential/changeset-view.css +++ b/webroot/rsrc/css/application/differential/changeset-view.css @@ -1,485 +1,478 @@ /** * @provides differential-changeset-view-css */ .differential-changeset { position: relative; margin: 0; padding-top: 32px; overflow-x: auto; } .device-phone .differential-changeset { overflow-x: scroll; -webkit-overflow-scrolling: touch; } .differential-diff { background: #fff; width: 100%; border-top: 1px solid {$lightblueborder}; border-bottom: 1px solid {$lightblueborder}; table-layout: fixed; } .differential-diff.diff-2up { min-width: 780px; } .differential-diff col.num { width: 45px; } .device .differential-diff.diff-1up col.num { width: 32px; } .differential-diff.diff-2up col.left, .differential-diff.diff-2up col.right { width: 49.25%; } .differential-diff.diff-1up col.unified { width: 99.5%; } .differential-diff col.copy { width: 0.5%; } .differential-diff col.cov { width: 1%; } .differential-diff td { vertical-align: top; white-space: pre-wrap; word-wrap: break-word; padding: 0 8px 1px; line-height: 16px; } .device .differential-diff td { padding: 0 4px 1px; } .differential-diff td .zwsp { position: absolute; width: 0; } .differential-diff th { text-align: right; padding: 2px 6px 0px 0px; vertical-align: top; background: {$lightbluebackground}; color: {$bluetext}; cursor: pointer; border-right: 1px solid {$thinblueborder}; overflow: hidden; -moz-user-select: -moz-none; -khtml-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none; } .differential-diff th.selected { background: {$hovergrey}; } .differential-changeset-immutable .differential-diff th { cursor: auto; } .differential-diff td.old { background: #ffd0d0; } .differential-diff td.new { background: #d0ffd0; } .differential-diff td.old-rebase { background: #ffeeee; } .differential-diff td.new-rebase { background: #eeffee; } .differential-diff td.old-full, .differential-diff td.old span.bright { background: #ffaaaa; } .differential-diff td.new-full, .differential-diff td.new span.bright { background: #aaffaa; } .differential-diff td.copy { min-width: 0.5%; width: 0.5%; padding: 0; } .differential-diff td.new-copy, .differential-diff td.new-copy span.bright { background: {$lightyellow}; } .differential-diff td.new-move, .differential-diff td.new-move span.bright { background: {$yellow}; } .differential-diff td.comment { background: #dddddd; } .differential-diff td.cov { padding: 0; } .diffusion-source td.cov { padding: 0 8px; } td.cov-U { background: #dd8866; } td.cov-C { background: #66bbff; } td.cov-N { background: #ddeeff; } td.cov-X { background: #aa00aa; } td.cov-I { background: {$lightgreybackground}; } .differential-diff td.source-cov-C, .differential-diff td.source-cov-C span.bright { background: #cceeff; } .differential-diff td.source-cov-U, .differential-diff td.source-cov-U span.bright { background: #ffbb99; } .differential-diff td.source-cov-N, .differential-diff td.source-cov-N span.bright { background: #f3f6ff; } .differential-diff td.show-more, .differential-diff th.show-context-line, .differential-diff td.show-context, .differential-diff td.differential-shield { background: {$lightbluebackground}; padding: 12px 0; border-top: 1px solid {$thinblueborder}; border-bottom: 1px solid {$thinblueborder}; } .device .differential-diff td.show-more, .device .differential-diff th.show-context-line, .device .differential-diff td.show-context, .device .differential-diff td.differential-shield { padding: 6px 0; } .differential-diff td.show-more, .differential-diff td.differential-shield { font: {$basefont}; font-size: 12px; white-space: normal; } .differential-diff td.show-more { text-align: center; color: {$bluetext}; } .differential-diff th.show-context-line { padding-right: 6px; } .differential-diff td.show-context { padding-left: 14px; } .differential-diff td.differential-shield { text-align: center; } .differential-diff td.differential-shield a { font-weight: bold; } .differential-diff .differential-image-diff { background-image: url(/rsrc/image/checker_light.png); } .differential-diff .differential-image-diff:hover { background-image: url(/rsrc/image/checker_dark.png); } .differential-diff .differential-image-diff td { padding: 8px; } .differential-image-stage { overflow: auto; } .differential-meta-notice { border-top: 1px solid {$yellow}; border-bottom: 1px solid {$yellow}; background-color: {$lightyellow}; padding: 12px; } .differential-meta-notice + .differential-diff { border-top: none; } .differential-changeset h1 { font-size: 15px; padding: 2px 0 12px 12px; line-height: 18px; } .device-phone .differential-changeset h1 { word-break: break-word; margin-right: 8px; } .differential-reticle { background: {$lightyellow}; border: 1px solid {$yellow}; position: absolute; opacity: 0.5; top: 0px; left: 0px; box-sizing: border-box; } .differential-inline-comment, .differential-inline-comment-edit { background: #ffffee; border: 1px solid #ccccaa; font: {$basefont}; font-size: 12px; margin: 6px 0px; padding: 8px 10px; width: 100%; -moz-box-sizing: border-box; -webkit-box-sizing: border-box; box-sizing: border-box; overflow: hidden; white-space: normal; } .differential-inline-comment-edit .aphront-form-control-textarea { padding: 0; } .differential-inline-comment-unsaved-draft { background: #f1f1f1; border: 1px dashed {$greytext}; } .differential-inline-comment-head { font-weight: bold; color: #333333; border-bottom: 1px solid rgba(204,204,170,0.37); padding-bottom: 4px; margin-bottom: 8px; } .differential-inline-comment-unsaved-draft .differential-inline-comment-head { border-bottom: 1px solid #aaaaaa; } .differential-inline-comment-synthetic { background: {$lightblue}; border: 1px solid {$blue}; } .differential-inline-comment-synthetic .differential-inline-comment-head { border-bottom: 1px solid {$blueborder}; } .differential-inline-comment-links, .differential-inline-comment-line { font-weight: normal; font-style: italic; color: {$greytext}; float: right; white-space: nowrap; } .differential-inline-comment-links { margin-left: 8px; font-style: normal; } .differential-inline-comment-edit-body .aphront-form-input { margin: 0; width: 100%; } .differential-changeset-buttons { float: right; margin-right: 16px; } .device-phone .differential-changeset-buttons { float: none; margin: 0 0 8px 4px; } .differential-changeset-buttons a.button { margin-left: 8px; } .differential-property-table { margin: 12px; background: {$lightgreybackground}; border: 1px solid {$lightblueborder}; border-bottom: 1px solid {$blueborder}; } .differential-property-table td em { color: {$lightgreytext}; } .differential-property-table td.oval { background: #ffd0d0; width: 50%; } .differential-property-table td.nval { background: #d0ffd0; width: 50%; } .differential-inline-undo { padding: 4px; text-align: center; background: #ffeeaa; margin: 3px 0 1px; font: 12px; color: 444444; } .differential-inline-undo a { font-weight: bold; } .differential-inline-comment-edit { padding: 10px; } .differential-inline-comment-edit-buttons { padding: 4px 0 0 0; } .differential-inline-comment-edit-buttons button { float: right; margin-left: 6px; } .differential-inline-comment-edit-title { font-weight: bold; color: #333333; padding-bottom: 2px; margin-bottom: 6px; } /* When the inline editor is active, disable all the other inline comment links on the page ("Edit", "Reply", "Delete", etc). The goal here is to prevent issues where you open up multiple editors and run into problems with assumptions about modalness. They are disabled explicitly by the JS, but render them in a disabled state as well. */ .inline-editor-active .differential-inline-comment-links a, .inline-editor-active .differential-inline-comment-links a:hover, .inline-editor-active .differential-inline-comment-links a:active { color: {$lightgreytext}; cursor: normal; text-decoration: none; } -/* Create a wide band of color behind the inline edit interface so it is easy - to find by skimming the page while scrolling. -*/ -tr.inline-comment-splint { - background: #f9f1d5; -} - tr.differential-inline-hidden { display: none; } tr.differential-inline-loading { opacity: 0.5; } /* In the document, the anchor is positioned inside the inline comment, but this makes the browser jump into the comment so the top isn't visible. Instead, artificially position it a bit above the comment so we'll jump a bit before the comment. This allows us to see the entire comment (and generally the commented-on lines, at least in the case of one or two-line comments) after the jump. */ .differential-inline-comment-anchor { position: absolute; display: block; margin-top: -72px; } .differential-loading { border-top: 1px solid {$yellow}; border-bottom: 1px solid {$yellow}; background-color: {$lightyellow}; padding: 12px; text-align: center; } .differential-collapse-undo { background: #FFE; color: #000; padding: 1em 0em; border: 1px solid #CCA; text-align: center; background-color: #FFE; } .differential-collapse-undo a { font-weight: bold; } .differential-file-icon-header .phui-icon-view { display: inline-block; margin: 0 6px 2px 0; vertical-align: middle; font-size: 14px; } .device-phone .differential-file-icon-header .phui-icon-view { display: none; }