Path: blob/master/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php
12242 views
<?php12final class PHUIDiffInlineCommentDetailView3extends PHUIDiffInlineCommentView {45private $handles;6private $markupEngine;7private $editable;8private $preview;9private $allowReply;10private $canMarkDone;11private $objectOwnerPHID;1213public function isHidden() {14return $this->getInlineComment()->isHidden();15}1617public function setHandles(array $handles) {18assert_instances_of($handles, 'PhabricatorObjectHandle');19$this->handles = $handles;20return $this;21}2223public function setMarkupEngine(PhabricatorMarkupEngine $engine) {24$this->markupEngine = $engine;25return $this;26}2728public function setEditable($editable) {29$this->editable = $editable;30return $this;31}3233public function setPreview($preview) {34$this->preview = $preview;35return $this;36}3738public function setAllowReply($allow_reply) {39$this->allowReply = $allow_reply;40return $this;41}4243public function setCanMarkDone($can_mark_done) {44$this->canMarkDone = $can_mark_done;45return $this;46}4748public function getCanMarkDone() {49return $this->canMarkDone;50}5152public function setObjectOwnerPHID($phid) {53$this->objectOwnerPHID = $phid;54return $this;55}5657public function getObjectOwnerPHID() {58return $this->objectOwnerPHID;59}6061public function getAnchorName() {62$inline = $this->getInlineComment();63if ($inline->getID()) {64return 'inline-'.$inline->getID();65}66return null;67}6869public function getScaffoldCellID() {70$anchor = $this->getAnchorName();71if ($anchor) {72return 'anchor-'.$anchor;73}74return null;75}7677public function render() {78require_celerity_resource('phui-inline-comment-view-css');79$inline = $this->getInlineComment();8081$is_synthetic = false;82if ($inline->getSyntheticAuthor()) {83$is_synthetic = true;84}8586$is_preview = $this->preview;8788$metadata = $this->getInlineCommentMetadata();8990$classes = array(91'differential-inline-comment',92);9394$sigil = 'differential-inline-comment';95if ($is_preview) {96$sigil = $sigil.' differential-inline-comment-preview';9798$classes[] = 'inline-comment-preview';99} else {100$classes[] = 'inline-comment-element';101}102103$handles = $this->handles;104105$links = array();106107$draft_text = null;108if (!$is_synthetic) {109// This display is controlled by CSS110$draft_text = id(new PHUITagView())111->setType(PHUITagView::TYPE_SHADE)112->setName(pht('Unsubmitted'))113->setSlimShady(true)114->setColor(PHUITagView::COLOR_RED)115->addClass('mml inline-draft-text');116}117118$ghost_tag = null;119$ghost = $inline->getIsGhost();120$ghost_id = null;121if ($ghost) {122if ($ghost['new']) {123$ghosticon = 'fa-fast-forward';124$reason = pht('View on forward revision');125} else {126$ghosticon = 'fa-fast-backward';127$reason = pht('View on previous revision');128}129130$ghost_icon = id(new PHUIIconView())131->setIcon($ghosticon)132->addSigil('has-tooltip')133->setMetadata(134array(135'tip' => $reason,136'size' => 300,137));138$ghost_tag = phutil_tag(139'a',140array(141'class' => 'ghost-icon',142'href' => $ghost['href'],143'target' => '_blank',144),145$ghost_icon);146$classes[] = 'inline-comment-ghost';147}148149if ($inline->getReplyToCommentPHID()) {150$classes[] = 'inline-comment-is-reply';151}152153$viewer_phid = $this->getUser()->getPHID();154$owner_phid = $this->getObjectOwnerPHID();155156if ($viewer_phid) {157if ($viewer_phid == $owner_phid) {158$classes[] = 'viewer-is-object-owner';159}160}161162$anchor_name = $this->getAnchorName();163164$action_buttons = array();165$menu_items = array();166167if ($this->editable && !$is_preview) {168$menu_items[] = array(169'label' => pht('Edit Comment'),170'icon' => 'fa-pencil',171'action' => 'edit',172'key' => 'e',173);174} else if ($is_preview) {175$links[] = javelin_tag(176'a',177array(178'class' => 'inline-button-divider pml msl',179'meta' => array(180'inlineCommentID' => $inline->getID(),181),182'sigil' => 'differential-inline-preview-jump',183),184pht('View'));185186$action_buttons[] = id(new PHUIButtonView())187->setTag('a')188->setTooltip(pht('Delete'))189->setIcon('fa-trash-o')190->addSigil('differential-inline-delete')191->setMustCapture(true)192->setAuralLabel(pht('Delete'));193}194195if (!$is_preview && $this->canHide()) {196$menu_items[] = array(197'label' => pht('Collapse'),198'icon' => 'fa-times',199'action' => 'collapse',200'key' => 'q',201);202}203204$can_reply =205(!$this->editable) &&206(!$is_preview) &&207($this->allowReply) &&208209// NOTE: No product reason why you can't reply to synthetic comments,210// but the reply mechanism currently sends the inline comment ID to the211// server, not file/line information, and synthetic comments don't have212// an inline comment ID.213(!$is_synthetic);214215if ($can_reply) {216$menu_items[] = array(217'label' => pht('Reply to Comment'),218'icon' => 'fa-reply',219'action' => 'reply',220'key' => 'r',221);222223$menu_items[] = array(224'label' => pht('Quote Comment'),225'icon' => 'fa-quote-left',226'action' => 'quote',227'key' => 'R',228);229}230231if (!$is_preview) {232$xaction_phid = $inline->getTransactionPHID();233$storage = $inline->getStorageObject();234235if ($xaction_phid) {236$menu_items[] = array(237'label' => pht('View Raw Remarkup'),238'icon' => 'fa-code',239'action' => 'raw',240'uri' => $storage->getRawRemarkupURI(),241);242}243}244245if ($this->editable && !$is_preview) {246$menu_items[] = array(247'label' => pht('Delete Comment'),248'icon' => 'fa-trash-o',249'action' => 'delete',250);251}252253$done_button = null;254255$mark_done = $this->getCanMarkDone();256257// Allow users to mark their own draft inlines as "Done".258if ($viewer_phid == $inline->getAuthorPHID()) {259if ($inline->isDraft()) {260$mark_done = true;261}262}263264if (!$is_synthetic) {265$draft_state = false;266switch ($inline->getFixedState()) {267case PhabricatorInlineComment::STATE_DRAFT:268$is_done = $mark_done;269$draft_state = true;270break;271case PhabricatorInlineComment::STATE_UNDRAFT:272$is_done = !$mark_done;273$draft_state = true;274break;275case PhabricatorInlineComment::STATE_DONE:276$is_done = true;277break;278default:279case PhabricatorInlineComment::STATE_UNDONE:280$is_done = false;281break;282}283284// If you don't have permission to mark the comment as "Done", you also285// can not see the draft state.286if (!$mark_done) {287$draft_state = false;288}289290if ($is_done) {291$classes[] = 'inline-is-done';292}293294if ($draft_state) {295$classes[] = 'inline-state-is-draft';296}297298if ($mark_done && !$is_preview) {299$done_input = javelin_tag(300'input',301array(302'type' => 'checkbox',303'checked' => ($is_done ? 'checked' : null),304'class' => 'differential-inline-done',305'sigil' => 'differential-inline-done',306));307$done_button = phutil_tag(308'label',309array(310'class' => 'differential-inline-done-label ',311),312array(313$done_input,314pht('Done'),315));316} else {317if ($is_done) {318$icon = id(new PHUIIconView())->setIcon('fa-check sky msr');319$label = pht('Done');320$class = 'button-done';321} else {322$icon = null;323$label = pht('Not Done');324$class = 'button-not-done';325}326$done_button = phutil_tag(327'div',328array(329'class' => 'done-label '.$class,330),331array(332$icon,333$label,334));335}336}337338$content = $this->markupEngine->getOutput(339$inline,340PhabricatorInlineComment::MARKUP_FIELD_BODY);341342if ($is_preview) {343$anchor = null;344} else {345$anchor = phutil_tag(346'a',347array(348'name' => $anchor_name,349'id' => $anchor_name,350'class' => 'differential-inline-comment-anchor',351),352'');353}354355if ($inline->isDraft() && !$is_synthetic) {356$classes[] = 'inline-state-is-draft';357}358if ($is_synthetic) {359$classes[] = 'differential-inline-comment-synthetic';360}361$classes = implode(' ', $classes);362363$author_owner = null;364if ($is_synthetic) {365$author = $inline->getSyntheticAuthor();366} else {367$author = $handles[$inline->getAuthorPHID()]->getName();368if ($inline->getAuthorPHID() == $this->objectOwnerPHID) {369$author_owner = id(new PHUITagView())370->setType(PHUITagView::TYPE_SHADE)371->setName(pht('Author'))372->setSlimShady(true)373->setColor(PHUITagView::COLOR_YELLOW)374->addClass('mml');375}376}377378$actions = null;379if ($action_buttons || $menu_items) {380$actions = new PHUIButtonBarView();381$actions->setBorderless(true);382$actions->addClass('inline-button-divider');383foreach ($action_buttons as $button) {384$actions->addButton($button);385}386387if (!$is_preview) {388$menu_button = id(new PHUIButtonView())389->setTag('a')390->setColor(PHUIButtonView::GREY)391->setDropdown(true)392->setAuralLabel(pht('Inline Actions'))393->addSigil('inline-action-dropdown');394395$actions->addButton($menu_button);396}397}398399$group_left = phutil_tag(400'div',401array(402'class' => 'inline-head-left',403),404array(405$author,406$author_owner,407$draft_text,408$ghost_tag,409));410411$group_right = phutil_tag(412'div',413array(414'class' => 'inline-head-right',415),416array(417$done_button,418$links,419$actions,420));421422$snippet = id(new PhutilUTF8StringTruncator())423->setMaximumGlyphs(96)424->truncateString($inline->getContent());425$metadata['snippet'] = pht('%s: %s', $author, $snippet);426427$metadata['menuItems'] = $menu_items;428429$suggestion_content = $this->newSuggestionView($inline);430431$inline_content = phutil_tag(432'div',433array(434'class' => 'phabricator-remarkup',435),436$content);437438$markup = javelin_tag(439'div',440array(441'class' => $classes,442'sigil' => $sigil,443'meta' => $metadata,444),445array(446javelin_tag(447'div',448array(449'class' => 'differential-inline-comment-head grouped',450'sigil' => 'differential-inline-header',451),452array(453$group_left,454$group_right,455)),456phutil_tag(457'div',458array(459'class' => 'differential-inline-comment-content',460),461array(462$suggestion_content,463$inline_content,464)),465));466467$summary = phutil_tag(468'div',469array(470'class' => 'differential-inline-summary',471),472array(473phutil_tag('strong', array(), pht('%s:', $author)),474' ',475$snippet,476));477478return array(479$anchor,480$markup,481$summary,482);483}484485private function canHide() {486$inline = $this->getInlineComment();487488if ($inline->isDraft()) {489return false;490}491492if (!$inline->getID()) {493return false;494}495496$viewer = $this->getUser();497if (!$viewer->isLoggedIn()) {498return false;499}500501if (!$inline->supportsHiding()) {502return false;503}504505return true;506}507508private function newSuggestionView(PhabricatorInlineComment $inline) {509$content_state = $inline->getContentState();510if (!$content_state->getContentHasSuggestion()) {511return null;512}513514$context = $inline->getInlineContext();515if (!$context) {516return null;517}518519$head_lines = $context->getHeadLines();520$head_lines = implode('', $head_lines);521522$tail_lines = $context->getTailLines();523$tail_lines = implode('', $tail_lines);524525$old_lines = $context->getBodyLines();526$old_lines = implode('', $old_lines);527$old_lines = $head_lines.$old_lines.$tail_lines;528if (strlen($old_lines) && !preg_match('/\n\z/', $old_lines)) {529$old_lines .= "\n";530}531532$new_lines = $content_state->getContentSuggestionText();533$new_lines = $head_lines.$new_lines.$tail_lines;534if (strlen($new_lines) && !preg_match('/\n\z/', $new_lines)) {535$new_lines .= "\n";536}537538if ($old_lines === $new_lines) {539return null;540}541542$viewer = $this->getViewer();543544$changeset = id(new PhabricatorDifferenceEngine())545->generateChangesetFromFileContent($old_lines, $new_lines);546547$changeset->setFilename($context->getFilename());548549$viewstate = new PhabricatorChangesetViewState();550551$parser = id(new DifferentialChangesetParser())552->setViewer($viewer)553->setViewstate($viewstate)554->setChangeset($changeset);555556$fragment = $inline->getInlineCommentCacheFragment();557if ($fragment !== null) {558$cache_key = sprintf(559'%s.suggestion-view(v1, %s)',560$fragment,561PhabricatorHash::digestForIndex($new_lines));562$parser->setRenderCacheKey($cache_key);563}564565$renderer = new DifferentialChangesetOneUpRenderer();566$renderer->setSimpleMode(true);567568$parser->setRenderer($renderer);569570// See PHI1896. If a user leaves an inline on a very long range with571// suggestions at the beginning and end, we'll hide context in the middle572// by default. We don't want to do this in the context of an inline573// suggestion, so build a mask to force display of all lines.574575// (We don't know exactly how many lines the diff has, we just know that576// it can't have more lines than the old file plus the new file, so we're577// using that as an upper bound.)578579$min = 0;580581$old_len = count(phutil_split_lines($old_lines));582$new_len = count(phutil_split_lines($new_lines));583$max = ($old_len + $new_len);584585$mask = array_fill($min, ($max - $min), true);586587$diff_view = $parser->render($min, ($max - $min), $mask);588589$view = phutil_tag(590'div',591array(592'class' => 'inline-suggestion-view PhabricatorMonospaced',593),594$diff_view);595596return $view;597}598}599600601