@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
at recaptime-dev/main 945 lines 20 kB view raw
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}