@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.)
hq.recaptime.dev/wiki/Phorge
phorge
phabricator
1<?php
2
3final class PHUIObjectItemView extends AphrontTagView {
4
5 private $objectName;
6 private $header;
7 private $subhead;
8 private $href;
9 private $attributes = array();
10 private $icons = array();
11 private $barColor;
12 private $object;
13 private $effect;
14 private $statusIcon;
15 private $handleIcons = array();
16 private $bylines = array();
17 private $grippable;
18 private $actions = array();
19 private $headIcons = array();
20 private $disabled;
21 private $imageURI;
22 private $imageHref;
23 private $imageIcon;
24 private $hasSpriteImage;
25 private $titleText;
26 private $badge;
27 private $countdownNum;
28 private $countdownNoun;
29 private $sideColumn;
30 private $coverImage;
31 private $description;
32 private $clickable;
33 private $mapViews = array();
34 private $menu;
35
36 private $selectableName;
37 private $selectableValue;
38 private $isSelected;
39 private $isForbidden;
40
41 public function setDisabled($disabled) {
42 $this->disabled = $disabled;
43 return $this;
44 }
45
46 public function getDisabled() {
47 return $this->disabled;
48 }
49
50 public function addHeadIcon($icon) {
51 $this->headIcons[] = $icon;
52 return $this;
53 }
54
55 public function setObjectName($name) {
56 $this->objectName = $name;
57 return $this;
58 }
59
60 public function setGrippable($grippable) {
61 $this->grippable = $grippable;
62 return $this;
63 }
64
65 public function getGrippable() {
66 return $this->grippable;
67 }
68
69 public function setEffect($effect) {
70 $this->effect = $effect;
71 return $this;
72 }
73
74 public function getEffect() {
75 return $this->effect;
76 }
77
78 public function setObject($object) {
79 $this->object = $object;
80 return $this;
81 }
82
83 public function getObject() {
84 return $this->object;
85 }
86
87 /**
88 * Set the href attribute
89 *
90 * @param string|PhutilURI|null $href
91 * @return self
92 */
93 public function setHref($href) {
94 PhutilURI::checkHrefType($href);
95
96 $this->href = $href;
97 return $this;
98 }
99
100 /**
101 * Get the href attribute
102 *
103 * @see PHUIObjectItemView::setHref()
104 * @return string|PhutilURI|null
105 */
106 public function getHref() {
107 return $this->href;
108 }
109
110 public function setHeader($header) {
111 $this->header = $header;
112 return $this;
113 }
114
115 public function setSubHead($subhead) {
116 $this->subhead = $subhead;
117 return $this;
118 }
119
120 public function setBadge(PHUIBadgeMiniView $badge) {
121 $this->badge = $badge;
122 return $this;
123 }
124
125 public function setCountdown($num, $noun) {
126 $this->countdownNum = $num;
127 $this->countdownNoun = $noun;
128 return $this;
129 }
130
131 public function setTitleText($title_text) {
132 $this->titleText = $title_text;
133 return $this;
134 }
135
136 public function getTitleText() {
137 return $this->titleText;
138 }
139
140 public function getHeader() {
141 return $this->header;
142 }
143
144 public function addByline($byline) {
145 $this->bylines[] = $byline;
146 return $this;
147 }
148
149 public function setImageURI($image_uri) {
150 $this->imageURI = $image_uri;
151 return $this;
152 }
153
154 /**
155 * Set the image href attribute
156 *
157 * @param string|PhutilURI|null $image_href
158 * @return self
159 */
160 public function setImageHref($image_href) {
161 PhutilURI::checkHrefType($image_href);
162
163 $this->imageHref = $image_href;
164 return $this;
165 }
166
167 public function getImageURI() {
168 return $this->imageURI;
169 }
170
171 public function setImageIcon($image_icon) {
172 if (!$image_icon instanceof PHUIIconView) {
173 $image_icon = id(new PHUIIconView())
174 ->setIcon($image_icon);
175 }
176 $this->imageIcon = $image_icon;
177 return $this;
178 }
179
180 public function getImageIcon() {
181 return $this->imageIcon;
182 }
183
184 /**
185 * @param bool $sprite
186 */
187 public function setHasSpriteImage($sprite) {
188 $this->hasSpriteImage = $sprite;
189 return $this;
190 }
191
192 /**
193 * @return bool
194 */
195 public function getHasSpriteImage() {
196 return $this->hasSpriteImage;
197 }
198
199 public function setCoverImage($image) {
200 $this->coverImage = $image;
201 return $this;
202 }
203
204 public function setDescription($description) {
205 $this->description = $description;
206 return $this;
207 }
208
209 public function setSelectable(
210 $name,
211 $value,
212 $is_selected,
213 $is_forbidden = false) {
214
215 $this->selectableName = $name;
216 $this->selectableValue = $value;
217 $this->isSelected = $is_selected;
218 $this->isForbidden = $is_forbidden;
219
220 return $this;
221 }
222
223 public function setClickable($clickable) {
224 $this->clickable = $clickable;
225 return $this;
226 }
227
228 public function getClickable() {
229 return $this->clickable;
230 }
231
232 public function setEpoch($epoch) {
233 $date = phabricator_dual_datetime($epoch, $this->getUser());
234 $this->addIcon('none', $date);
235 return $this;
236 }
237
238 public function addAction(PHUIListItemView $action) {
239 if (count($this->actions) >= 3) {
240 throw new Exception(pht('Limit 3 actions per item.'));
241 }
242 $this->actions[] = $action;
243 return $this;
244 }
245
246 public function addIcon($icon, $label = null, $attributes = array()) {
247 $this->icons[] = array(
248 'icon' => $icon,
249 'label' => $label,
250 'attributes' => $attributes,
251 );
252 return $this;
253 }
254
255 public function newMenuItem() {
256 if (!$this->menu) {
257 $this->menu = new FuelMenuView();
258 }
259
260 return $this->menu->newItem();
261 }
262
263 public function newMapView() {
264 $list = id(new FuelMapView())
265 ->addClass('fuel-map-property-list');
266 $this->mapViews[] = $list;
267 return $list;
268 }
269
270 /**
271 * This method has been deprecated, use @{method:setImageIcon} instead.
272 *
273 * @deprecated
274 */
275 public function setIcon($icon) {
276 phlog(
277 pht('Deprecated call to setIcon(), use setImageIcon() instead.'));
278
279 return $this->setImageIcon($icon);
280 }
281
282 public function setStatusIcon($icon, $label = null) {
283 $this->statusIcon = array(
284 'icon' => $icon,
285 'label' => $label,
286 );
287 return $this;
288 }
289
290 public function addHandleIcon(
291 PhabricatorObjectHandle $handle,
292 $label = null) {
293 $this->handleIcons[] = array(
294 'icon' => $handle,
295 'label' => $label,
296 );
297 return $this;
298 }
299
300 public function setBarColor($bar_color) {
301 $this->barColor = $bar_color;
302 return $this;
303 }
304
305 public function getBarColor() {
306 return $this->barColor;
307 }
308
309 public function addAttribute($attribute) {
310 if (!empty($attribute)) {
311 $this->attributes[] = $attribute;
312 }
313 return $this;
314 }
315
316 public function setSideColumn($column) {
317 $this->sideColumn = $column;
318 return $this;
319 }
320
321 protected function getTagName() {
322 return 'li';
323 }
324
325 protected function getTagAttributes() {
326 $sigils = array();
327
328 $item_classes = array();
329 $item_classes[] = 'phui-oi';
330
331 if ($this->icons) {
332 $item_classes[] = 'phui-oi-with-icons';
333 }
334
335 if ($this->attributes) {
336 $item_classes[] = 'phui-oi-with-attrs';
337 }
338
339 if ($this->handleIcons) {
340 $item_classes[] = 'phui-oi-with-handle-icons';
341 }
342
343 if ($this->barColor) {
344 $item_classes[] = 'phui-oi-bar-color-'.$this->barColor;
345 } else {
346 $item_classes[] = 'phui-oi-no-bar';
347 }
348
349 if ($this->actions) {
350 $n = count($this->actions);
351 $item_classes[] = 'phui-oi-with-actions';
352 $item_classes[] = 'phui-oi-with-'.$n.'-actions';
353 }
354
355 if ($this->disabled) {
356 $item_classes[] = 'phui-oi-disabled';
357 } else {
358 $item_classes[] = 'phui-oi-enabled';
359 }
360
361 switch ($this->effect) {
362 case 'highlighted':
363 $item_classes[] = 'phui-oi-highlighted';
364 break;
365 case 'selected':
366 $item_classes[] = 'phui-oi-selected';
367 break;
368 case 'visited':
369 $item_classes[] = 'phui-oi-visited';
370 break;
371 case null:
372 break;
373 default:
374 throw new Exception(pht('Invalid effect!'));
375 }
376
377 if ($this->isForbidden) {
378 $item_classes[] = 'phui-oi-forbidden';
379 } else if ($this->isSelected) {
380 $item_classes[] = 'phui-oi-selected';
381 }
382
383 if ($this->selectableName !== null && !$this->isForbidden) {
384 $item_classes[] = 'phui-oi-selectable';
385 $sigils[] = 'phui-oi-selectable';
386
387 Javelin::initBehavior('phui-selectable-list');
388 }
389
390 $is_grippable = $this->getGrippable();
391 if ($is_grippable !== null) {
392 $item_classes[] = 'phui-oi-has-grip';
393 if ($is_grippable) {
394 $item_classes[] = 'phui-oi-grippable';
395 } else {
396 $item_classes[] = 'phui-oi-ungrippable';
397 }
398 }
399
400 if ($this->getImageURI()) {
401 $item_classes[] = 'phui-oi-with-image';
402 }
403
404 if ($this->getImageIcon()) {
405 $item_classes[] = 'phui-oi-with-image-icon';
406 }
407
408 if ($this->getClickable()) {
409 Javelin::initBehavior('linked-container');
410
411 $item_classes[] = 'phui-oi-linked-container';
412 $sigils[] = 'linked-container';
413 }
414
415 return array(
416 'class' => $item_classes,
417 'sigil' => $sigils,
418 );
419 }
420
421 protected function getTagContent() {
422 $viewer = $this->getUser();
423
424 $content_classes = array();
425 $content_classes[] = 'phui-oi-content';
426
427 $header_name = array();
428
429 if ($viewer) {
430 $header_name[] = id(new PHUISpacesNamespaceContextView())
431 ->setViewer($viewer)
432 ->setObject($this->object);
433 }
434
435 if ($this->objectName) {
436 $header_name[] = array(
437 javelin_tag(
438 'span',
439 array(
440 'class' => 'phui-oi-objname',
441 'sigil' => 'ungrabbable',
442 ),
443 $this->objectName),
444 ' ',
445 );
446 }
447
448 $title_text = null;
449 if ($this->titleText) {
450 $title_text = $this->titleText;
451 } else if ($this->href) {
452 $title_text = $this->header;
453 }
454
455 $header_link = phutil_tag(
456 $this->href ? 'a' : 'div',
457 array(
458 'href' => $this->href,
459 'class' => 'phui-oi-link',
460 'title' => $title_text,
461 ),
462 $this->header);
463
464 $description_tag = null;
465 if ($this->description) {
466 $decription_id = celerity_generate_unique_node_id();
467 $description_tag = id(new PHUITagView())
468 ->setIcon('fa-ellipsis-h')
469 ->addClass('phui-oi-description-tag')
470 ->setType(PHUITagView::TYPE_SHADE)
471 ->setColor(PHUITagView::COLOR_GREY)
472 ->addSigil('jx-toggle-class')
473 ->setSlimShady(true)
474 ->setMetaData(array(
475 'map' => array(
476 $decription_id => 'phui-oi-description-reveal',
477 ),
478 ));
479 }
480
481 $header = phutil_tag(
482 'div',
483 array(
484 'class' => 'phui-oi-name',
485 ),
486 array(
487 $this->headIcons,
488 $header_name,
489 $header_link,
490 $description_tag,
491 ));
492
493 $icons = array();
494 if ($this->icons) {
495 $icon_list = array();
496 foreach ($this->icons as $spec) {
497 $icon = $spec['icon'];
498 $icon = id(new PHUIIconView())
499 ->setIcon($icon)
500 ->addClass('phui-oi-icon-image');
501
502 if (isset($spec['attributes']['tip'])) {
503 $sigil = 'has-tooltip';
504 $meta = array(
505 'tip' => $spec['attributes']['tip'],
506 'align' => 'W',
507 );
508 $icon->addSigil($sigil);
509 $icon->setMetadata($meta);
510 }
511
512 $label = phutil_tag(
513 'span',
514 array(
515 'class' => 'phui-oi-icon-label',
516 ),
517 $spec['label']);
518
519 $classes = array();
520 $classes[] = 'phui-oi-icon';
521 if (isset($spec['attributes']['class'])) {
522 $classes[] = $spec['attributes']['class'];
523 }
524
525 $icon_list[] = javelin_tag(
526 'li',
527 array(
528 'class' => implode(' ', $classes),
529 ),
530 array(
531 $icon,
532 $label,
533 ));
534 }
535
536 $icons[] = phutil_tag(
537 'ul',
538 array(
539 'class' => 'phui-oi-icons',
540 ),
541 $icon_list);
542 }
543
544 $handle_bar = null;
545 if ($this->handleIcons) {
546 $handle_bar = array();
547 foreach ($this->handleIcons as $handleicon) {
548 $handle_bar[] =
549 $this->renderHandleIcon($handleicon['icon'], $handleicon['label']);
550 }
551 $handle_bar = phutil_tag(
552 'li',
553 array(
554 'class' => 'phui-oi-handle-icons',
555 ),
556 $handle_bar);
557 }
558
559 $bylines = array();
560 if ($this->bylines) {
561 foreach ($this->bylines as $byline) {
562 $bylines[] = phutil_tag(
563 'div',
564 array(
565 'class' => 'phui-oi-byline',
566 ),
567 $byline);
568 }
569 $bylines = phutil_tag(
570 'div',
571 array(
572 'class' => 'phui-oi-bylines',
573 ),
574 $bylines);
575 }
576
577 $subhead = null;
578 if ($this->subhead) {
579 $subhead = phutil_tag(
580 'div',
581 array(
582 'class' => 'phui-oi-subhead',
583 ),
584 $this->subhead);
585 }
586
587 if ($this->description) {
588 $subhead = phutil_tag(
589 'div',
590 array(
591 'class' => 'phui-oi-subhead phui-oi-description',
592 'id' => $decription_id,
593 ),
594 $this->description);
595 }
596
597 if ($icons) {
598 $icons = phutil_tag(
599 'div',
600 array(
601 'class' => 'phui-object-icon-pane',
602 ),
603 $icons);
604 }
605
606 $attrs = null;
607 if ($this->attributes || $handle_bar) {
608 $attrs = array();
609 $spacer = phutil_tag(
610 'span',
611 array(
612 'class' => 'phui-oi-attribute-spacer',
613 ),
614 "\xC2\xB7");
615 $first = true;
616 foreach ($this->attributes as $attribute) {
617 $attrs[] = phutil_tag(
618 'li',
619 array(
620 'class' => 'phui-oi-attribute',
621 ),
622 array(
623 ($first ? null : $spacer),
624 $attribute,
625 ));
626 $first = false;
627 }
628
629 $attrs = phutil_tag(
630 'ul',
631 array(
632 'class' => 'phui-oi-attributes',
633 ),
634 array(
635 $handle_bar,
636 $attrs,
637 ));
638 }
639
640 $status = null;
641 if ($this->statusIcon) {
642 $icon = $this->statusIcon;
643 $status = $this->renderStatusIcon($icon['icon'], $icon['label']);
644 }
645
646 $grippable = null;
647 if ($this->getGrippable() !== null) {
648 $grippable = phutil_tag(
649 'div',
650 array(
651 'class' => 'phui-oi-grip',
652 ),
653 '');
654 }
655
656 $map_views = null;
657 if ($this->mapViews) {
658 $grid = id(new FuelGridView())
659 ->addClass('fuel-grid-property-list');
660
661 $row = $grid->newRow();
662 foreach ($this->mapViews as $map_view) {
663 $row->newCell()
664 ->setContent($map_view);
665 }
666
667 $map_views = $grid;
668 }
669
670 $content = phutil_tag(
671 'div',
672 array(
673 'class' => implode(' ', $content_classes),
674 ),
675 array(
676 $subhead,
677 $attrs,
678 $map_views,
679 $this->renderChildren(),
680 ));
681
682 $image = null;
683 if ($this->getImageURI()) {
684 $image = phutil_tag(
685 'div',
686 array(
687 'class' => 'phui-oi-image',
688 'style' => 'background-image: url('.$this->getImageURI().')',
689 ),
690 '');
691 } else if ($this->getImageIcon()) {
692 $item_class = 'phui-oi-image-icon';
693 if ($this->getHasSpriteImage()) {
694 $item_class = $item_class.' phui-oi-with-sprite-image';
695 }
696 $image = phutil_tag(
697 'div',
698 array(
699 'class' => $item_class,
700 ),
701 $this->getImageIcon());
702 }
703
704 if ($image && (phutil_nonempty_stringlike($this->href) ||
705 phutil_nonempty_stringlike($this->imageHref))) {
706 $image_href = ($this->imageHref) ? $this->imageHref : $this->href;
707 $image = phutil_tag(
708 'a',
709 array(
710 'href' => $image_href,
711 'title' => $title_text,
712 ),
713 $image);
714 }
715
716 /* Build a fake table */
717 $column0 = null;
718 if ($status) {
719 $column0 = phutil_tag(
720 'div',
721 array(
722 'class' => 'phui-oi-col0',
723 ),
724 $status);
725 }
726
727 if ($this->badge) {
728 $column0 = phutil_tag(
729 'div',
730 array(
731 'class' => 'phui-oi-col0 phui-oi-badge',
732 ),
733 $this->badge);
734 }
735
736 if ($this->countdownNum) {
737 $countdown = phutil_tag(
738 'div',
739 array(
740 'class' => 'phui-oi-countdown-number',
741 ),
742 array(
743 phutil_tag_div('', $this->countdownNum),
744 phutil_tag_div('', $this->countdownNoun),
745 ));
746 $column0 = phutil_tag(
747 'div',
748 array(
749 'class' => 'phui-oi-col0 phui-oi-countdown',
750 ),
751 $countdown);
752 }
753
754 if ($this->selectableName !== null) {
755 if (!$this->isForbidden) {
756 $checkbox = phutil_tag(
757 'input',
758 array(
759 'type' => 'checkbox',
760 'name' => $this->selectableName,
761 'value' => $this->selectableValue,
762 'checked' => ($this->isSelected ? 'checked' : null),
763 ));
764 } else {
765 $checkbox = null;
766 }
767
768 $column0 = phutil_tag(
769 'div',
770 array(
771 'class' => 'phui-oi-col0 phui-oi-checkbox',
772 ),
773 $checkbox);
774 }
775
776 $column1 = phutil_tag(
777 'div',
778 array(
779 'class' => 'phui-oi-col1',
780 ),
781 array(
782 $header,
783 $content,
784 ));
785
786 $column2 = null;
787 if ($icons || $bylines) {
788 $column2 = phutil_tag(
789 'div',
790 array(
791 'class' => 'phui-oi-col2',
792 ),
793 array(
794 $icons,
795 $bylines,
796 ));
797 }
798
799 /* Fixed width, right column container. */
800 $column3 = null;
801 if ($this->sideColumn) {
802 $column3 = phutil_tag(
803 'div',
804 array(
805 'class' => 'phui-oi-col2 phui-oi-side-column',
806 ),
807 array(
808 $this->sideColumn,
809 ));
810 }
811
812 $table = phutil_tag(
813 'div',
814 array(
815 'class' => 'phui-oi-table',
816 ),
817 phutil_tag_div(
818 'phui-oi-table-row',
819 array(
820 $column0,
821 $column1,
822 $column2,
823 $column3,
824 )));
825
826 $box = phutil_tag(
827 'div',
828 array(
829 'class' => 'phui-oi-content-box',
830 ),
831 array(
832 $grippable,
833 $table,
834 ));
835
836 $actions = array();
837 if ($this->actions) {
838 Javelin::initBehavior('phabricator-tooltips');
839
840 foreach (array_reverse($this->actions) as $action) {
841 $action->setRenderNameAsTooltip(true);
842 $actions[] = $action;
843 }
844 $actions = phutil_tag(
845 'ul',
846 array(
847 'class' => 'phui-oi-actions',
848 ),
849 $actions);
850 }
851
852 $frame_content = phutil_tag(
853 'div',
854 array(
855 'class' => 'phui-oi-frame-content',
856 ),
857 array(
858 $actions,
859 $image,
860 $box,
861 ));
862
863 if ($this->menu) {
864 $grid_view = id(new FuelGridView())
865 ->addClass('fuel-grid-tablet');
866 $grid_row = $grid_view->newRow();
867
868 $grid_row->newCell()
869 ->setContent($frame_content);
870
871 $menu = $this->menu;
872
873 $grid_row->newCell()
874 ->addClass('phui-oi-menu')
875 ->setContent($menu);
876
877 $frame_content = $grid_view;
878 }
879
880 $frame_cover = null;
881 if ($this->coverImage) {
882 $cover_image = phutil_tag(
883 'img',
884 array(
885 'src' => $this->coverImage,
886 'class' => 'phui-oi-cover-image',
887 ));
888
889 $frame_cover = phutil_tag(
890 'div',
891 array(
892 'class' => 'phui-oi-frame-cover',
893 ),
894 $cover_image);
895 }
896
897 $frame = phutil_tag(
898 'div',
899 array(
900 'class' => 'phui-oi-frame',
901 ),
902 array(
903 $frame_cover,
904 $frame_content,
905 ));
906
907 return $frame;
908 }
909
910 private function renderStatusIcon($icon, $label) {
911 Javelin::initBehavior('phabricator-tooltips');
912
913 $icon = id(new PHUIIconView())
914 ->setIcon($icon);
915
916 $options = array(
917 'class' => 'phui-oi-status-icon',
918 );
919
920 if (phutil_nonempty_string($label)) {
921 $options['sigil'] = 'has-tooltip';
922 $options['meta'] = array('tip' => $label, 'size' => 300);
923 }
924
925 return javelin_tag('div', $options, $icon);
926 }
927
928
929 private function renderHandleIcon(PhabricatorObjectHandle $handle, $label) {
930 Javelin::initBehavior('phabricator-tooltips');
931
932 $options = array(
933 'class' => 'phui-oi-handle-icon',
934 'style' => 'background-image: url('.$handle->getImageURI().')',
935 );
936
937 if (strlen($label)) {
938 $options['sigil'] = 'has-tooltip';
939 $options['meta'] = array('tip' => $label, 'align' => 'E');
940 }
941
942 return javelin_tag('span', $options, '');
943 }
944
945}