PageRenderTime 49ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php

http://github.com/facebook/phabricator
PHP | 468 lines | 393 code | 66 blank | 9 comment | 47 complexity | 2ade14b64f23933770a84a09f2172702 MD5 | raw file
Possible License(s): JSON, MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause, LGPL-2.0, MIT, LGPL-2.1, LGPL-3.0
  1. <?php
  2. final class PHUIDiffInlineCommentDetailView
  3. extends PHUIDiffInlineCommentView {
  4. private $handles;
  5. private $markupEngine;
  6. private $editable;
  7. private $preview;
  8. private $allowReply;
  9. private $canMarkDone;
  10. private $objectOwnerPHID;
  11. public function isHidden() {
  12. return $this->getInlineComment()->isHidden();
  13. }
  14. public function setHandles(array $handles) {
  15. assert_instances_of($handles, 'PhabricatorObjectHandle');
  16. $this->handles = $handles;
  17. return $this;
  18. }
  19. public function setMarkupEngine(PhabricatorMarkupEngine $engine) {
  20. $this->markupEngine = $engine;
  21. return $this;
  22. }
  23. public function setEditable($editable) {
  24. $this->editable = $editable;
  25. return $this;
  26. }
  27. public function setPreview($preview) {
  28. $this->preview = $preview;
  29. return $this;
  30. }
  31. public function setAllowReply($allow_reply) {
  32. $this->allowReply = $allow_reply;
  33. return $this;
  34. }
  35. public function setCanMarkDone($can_mark_done) {
  36. $this->canMarkDone = $can_mark_done;
  37. return $this;
  38. }
  39. public function getCanMarkDone() {
  40. return $this->canMarkDone;
  41. }
  42. public function setObjectOwnerPHID($phid) {
  43. $this->objectOwnerPHID = $phid;
  44. return $this;
  45. }
  46. public function getObjectOwnerPHID() {
  47. return $this->objectOwnerPHID;
  48. }
  49. public function getAnchorName() {
  50. $inline = $this->getInlineComment();
  51. if ($inline->getID()) {
  52. return 'inline-'.$inline->getID();
  53. }
  54. return null;
  55. }
  56. public function getScaffoldCellID() {
  57. $anchor = $this->getAnchorName();
  58. if ($anchor) {
  59. return 'anchor-'.$anchor;
  60. }
  61. return null;
  62. }
  63. public function render() {
  64. require_celerity_resource('phui-inline-comment-view-css');
  65. $inline = $this->getInlineComment();
  66. $classes = array(
  67. 'differential-inline-comment',
  68. );
  69. $is_synthetic = false;
  70. if ($inline->getSyntheticAuthor()) {
  71. $is_synthetic = true;
  72. }
  73. $metadata = $this->getInlineCommentMetadata();
  74. $sigil = 'differential-inline-comment';
  75. if ($this->preview) {
  76. $sigil = $sigil.' differential-inline-comment-preview';
  77. }
  78. $classes = array(
  79. 'differential-inline-comment',
  80. );
  81. $content = $inline->getContent();
  82. $handles = $this->handles;
  83. $links = array();
  84. $draft_text = null;
  85. if (!$is_synthetic) {
  86. // This display is controlled by CSS
  87. $draft_text = id(new PHUITagView())
  88. ->setType(PHUITagView::TYPE_SHADE)
  89. ->setName(pht('Unsubmitted'))
  90. ->setSlimShady(true)
  91. ->setColor(PHUITagView::COLOR_RED)
  92. ->addClass('mml inline-draft-text');
  93. }
  94. $ghost_tag = null;
  95. $ghost = $inline->getIsGhost();
  96. $ghost_id = null;
  97. if ($ghost) {
  98. if ($ghost['new']) {
  99. $ghosticon = 'fa-fast-forward';
  100. $reason = pht('View on forward revision');
  101. } else {
  102. $ghosticon = 'fa-fast-backward';
  103. $reason = pht('View on previous revision');
  104. }
  105. $ghost_icon = id(new PHUIIconView())
  106. ->setIcon($ghosticon)
  107. ->addSigil('has-tooltip')
  108. ->setMetadata(
  109. array(
  110. 'tip' => $reason,
  111. 'size' => 300,
  112. ));
  113. $ghost_tag = phutil_tag(
  114. 'a',
  115. array(
  116. 'class' => 'ghost-icon',
  117. 'href' => $ghost['href'],
  118. 'target' => '_blank',
  119. ),
  120. $ghost_icon);
  121. $classes[] = 'inline-comment-ghost';
  122. }
  123. // I think this is unused
  124. if ($inline->getHasReplies()) {
  125. $classes[] = 'inline-comment-has-reply';
  126. }
  127. if ($inline->getReplyToCommentPHID()) {
  128. $classes[] = 'inline-comment-is-reply';
  129. }
  130. $viewer_phid = $this->getUser()->getPHID();
  131. $owner_phid = $this->getObjectOwnerPHID();
  132. if ($viewer_phid) {
  133. if ($viewer_phid == $owner_phid) {
  134. $classes[] = 'viewer-is-object-owner';
  135. }
  136. }
  137. $anchor_name = $this->getAnchorName();
  138. $action_buttons = array();
  139. $can_reply =
  140. (!$this->editable) &&
  141. (!$this->preview) &&
  142. ($this->allowReply) &&
  143. // NOTE: No product reason why you can't reply to synthetic comments,
  144. // but the reply mechanism currently sends the inline comment ID to the
  145. // server, not file/line information, and synthetic comments don't have
  146. // an inline comment ID.
  147. (!$is_synthetic);
  148. if ($can_reply) {
  149. $action_buttons[] = id(new PHUIButtonView())
  150. ->setTag('a')
  151. ->setIcon('fa-reply')
  152. ->setTooltip(pht('Reply'))
  153. ->addSigil('differential-inline-reply')
  154. ->setMustCapture(true)
  155. ->setAuralLabel(pht('Reply'));
  156. }
  157. if ($this->editable && !$this->preview) {
  158. $action_buttons[] = id(new PHUIButtonView())
  159. ->setTag('a')
  160. ->setIcon('fa-pencil')
  161. ->setTooltip(pht('Edit'))
  162. ->addSigil('differential-inline-edit')
  163. ->setMustCapture(true)
  164. ->setAuralLabel(pht('Edit'));
  165. $action_buttons[] = id(new PHUIButtonView())
  166. ->setTag('a')
  167. ->setIcon('fa-trash-o')
  168. ->setTooltip(pht('Delete'))
  169. ->addSigil('differential-inline-delete')
  170. ->setMustCapture(true)
  171. ->setAuralLabel(pht('Delete'));
  172. } else if ($this->preview) {
  173. $links[] = javelin_tag(
  174. 'a',
  175. array(
  176. 'class' => 'inline-button-divider pml msl',
  177. 'meta' => array(
  178. 'inlineCommentID' => $inline->getID(),
  179. ),
  180. 'sigil' => 'differential-inline-preview-jump',
  181. ),
  182. pht('View'));
  183. $action_buttons[] = id(new PHUIButtonView())
  184. ->setTag('a')
  185. ->setTooltip(pht('Delete'))
  186. ->setIcon('fa-trash-o')
  187. ->addSigil('differential-inline-delete')
  188. ->setMustCapture(true)
  189. ->setAuralLabel(pht('Delete'));
  190. }
  191. if (!$this->preview && $this->canHide()) {
  192. $action_buttons[] = id(new PHUIButtonView())
  193. ->setTag('a')
  194. ->setTooltip(pht('Collapse'))
  195. ->setIcon('fa-times')
  196. ->addSigil('hide-inline')
  197. ->setMustCapture(true)
  198. ->setAuralLabel(pht('Collapse'));
  199. }
  200. $done_button = null;
  201. $mark_done = $this->getCanMarkDone();
  202. // Allow users to mark their own draft inlines as "Done".
  203. if ($viewer_phid == $inline->getAuthorPHID()) {
  204. if ($inline->isDraft()) {
  205. $mark_done = true;
  206. }
  207. }
  208. if (!$is_synthetic) {
  209. $draft_state = false;
  210. switch ($inline->getFixedState()) {
  211. case PhabricatorInlineComment::STATE_DRAFT:
  212. $is_done = $mark_done;
  213. $draft_state = true;
  214. break;
  215. case PhabricatorInlineComment::STATE_UNDRAFT:
  216. $is_done = !$mark_done;
  217. $draft_state = true;
  218. break;
  219. case PhabricatorInlineComment::STATE_DONE:
  220. $is_done = true;
  221. break;
  222. default:
  223. case PhabricatorInlineComment::STATE_UNDONE:
  224. $is_done = false;
  225. break;
  226. }
  227. // If you don't have permission to mark the comment as "Done", you also
  228. // can not see the draft state.
  229. if (!$mark_done) {
  230. $draft_state = false;
  231. }
  232. if ($is_done) {
  233. $classes[] = 'inline-is-done';
  234. }
  235. if ($draft_state) {
  236. $classes[] = 'inline-state-is-draft';
  237. }
  238. if ($mark_done && !$this->preview) {
  239. $done_input = javelin_tag(
  240. 'input',
  241. array(
  242. 'type' => 'checkbox',
  243. 'checked' => ($is_done ? 'checked' : null),
  244. 'class' => 'differential-inline-done',
  245. 'sigil' => 'differential-inline-done',
  246. ));
  247. $done_button = phutil_tag(
  248. 'label',
  249. array(
  250. 'class' => 'differential-inline-done-label ',
  251. ),
  252. array(
  253. $done_input,
  254. pht('Done'),
  255. ));
  256. } else {
  257. if ($is_done) {
  258. $icon = id(new PHUIIconView())->setIcon('fa-check sky msr');
  259. $label = pht('Done');
  260. $class = 'button-done';
  261. } else {
  262. $icon = null;
  263. $label = pht('Not Done');
  264. $class = 'button-not-done';
  265. }
  266. $done_button = phutil_tag(
  267. 'div',
  268. array(
  269. 'class' => 'done-label '.$class,
  270. ),
  271. array(
  272. $icon,
  273. $label,
  274. ));
  275. }
  276. }
  277. $content = $this->markupEngine->getOutput(
  278. $inline,
  279. PhabricatorInlineComment::MARKUP_FIELD_BODY);
  280. if ($this->preview) {
  281. $anchor = null;
  282. } else {
  283. $anchor = phutil_tag(
  284. 'a',
  285. array(
  286. 'name' => $anchor_name,
  287. 'id' => $anchor_name,
  288. 'class' => 'differential-inline-comment-anchor',
  289. ),
  290. '');
  291. }
  292. if ($inline->isDraft() && !$is_synthetic) {
  293. $classes[] = 'inline-state-is-draft';
  294. }
  295. if ($is_synthetic) {
  296. $classes[] = 'differential-inline-comment-synthetic';
  297. }
  298. $classes = implode(' ', $classes);
  299. $author_owner = null;
  300. if ($is_synthetic) {
  301. $author = $inline->getSyntheticAuthor();
  302. } else {
  303. $author = $handles[$inline->getAuthorPHID()]->getName();
  304. if ($inline->getAuthorPHID() == $this->objectOwnerPHID) {
  305. $author_owner = id(new PHUITagView())
  306. ->setType(PHUITagView::TYPE_SHADE)
  307. ->setName(pht('Author'))
  308. ->setSlimShady(true)
  309. ->setColor(PHUITagView::COLOR_YELLOW)
  310. ->addClass('mml');
  311. }
  312. }
  313. $actions = null;
  314. if ($action_buttons) {
  315. $actions = new PHUIButtonBarView();
  316. $actions->setBorderless(true);
  317. $actions->addClass('inline-button-divider');
  318. foreach ($action_buttons as $button) {
  319. $actions->addButton($button);
  320. }
  321. }
  322. $group_left = phutil_tag(
  323. 'div',
  324. array(
  325. 'class' => 'inline-head-left',
  326. ),
  327. array(
  328. $author,
  329. $author_owner,
  330. $draft_text,
  331. $ghost_tag,
  332. ));
  333. $group_right = phutil_tag(
  334. 'div',
  335. array(
  336. 'class' => 'inline-head-right',
  337. ),
  338. array(
  339. $done_button,
  340. $links,
  341. $actions,
  342. ));
  343. $snippet = id(new PhutilUTF8StringTruncator())
  344. ->setMaximumGlyphs(96)
  345. ->truncateString($inline->getContent());
  346. $metadata['snippet'] = pht('%s: %s', $author, $snippet);
  347. $markup = javelin_tag(
  348. 'div',
  349. array(
  350. 'class' => $classes,
  351. 'sigil' => $sigil,
  352. 'meta' => $metadata,
  353. ),
  354. array(
  355. javelin_tag(
  356. 'div',
  357. array(
  358. 'class' => 'differential-inline-comment-head grouped',
  359. 'sigil' => 'differential-inline-header',
  360. ),
  361. array(
  362. $group_left,
  363. $group_right,
  364. )),
  365. phutil_tag_div(
  366. 'differential-inline-comment-content',
  367. phutil_tag_div('phabricator-remarkup', $content)),
  368. ));
  369. $summary = phutil_tag(
  370. 'div',
  371. array(
  372. 'class' => 'differential-inline-summary',
  373. ),
  374. array(
  375. phutil_tag('strong', array(), pht('%s:', $author)),
  376. ' ',
  377. $snippet,
  378. ));
  379. return array(
  380. $anchor,
  381. $markup,
  382. $summary,
  383. );
  384. }
  385. private function canHide() {
  386. $inline = $this->getInlineComment();
  387. if ($inline->isDraft()) {
  388. return false;
  389. }
  390. if (!$inline->getID()) {
  391. return false;
  392. }
  393. $viewer = $this->getUser();
  394. if (!$viewer->isLoggedIn()) {
  395. return false;
  396. }
  397. if (!$inline->supportsHiding()) {
  398. return false;
  399. }
  400. return true;
  401. }
  402. }