Path: blob/master/src/view/phui/PHUITimelineEventView.php
12249 views
<?php12final class PHUITimelineEventView extends AphrontView {34const DELIMITER = " \xC2\xB7 ";56private $userHandle;7private $title;8private $icon;9private $color;10private $classes = array();11private $contentSource;12private $dateCreated;13private $anchor;14private $isEditable;15private $isEdited;16private $isRemovable;17private $transactionPHID;18private $isPreview;19private $eventGroup = array();20private $hideByDefault;21private $token;22private $tokenRemoved;23private $quoteTargetID;24private $isNormalComment;25private $quoteRef;26private $reallyMajorEvent;27private $hideCommentOptions = false;28private $authorPHID;29private $badges = array();30private $pinboardItems = array();31private $isSilent;32private $isMFA;33private $isLockOverride;34private $canInteract;3536public function setAuthorPHID($author_phid) {37$this->authorPHID = $author_phid;38return $this;39}4041public function getAuthorPHID() {42return $this->authorPHID;43}4445public function setQuoteRef($quote_ref) {46$this->quoteRef = $quote_ref;47return $this;48}4950public function getQuoteRef() {51return $this->quoteRef;52}5354public function setQuoteTargetID($quote_target_id) {55$this->quoteTargetID = $quote_target_id;56return $this;57}5859public function getQuoteTargetID() {60return $this->quoteTargetID;61}6263public function setIsNormalComment($is_normal_comment) {64$this->isNormalComment = $is_normal_comment;65return $this;66}6768public function getIsNormalComment() {69return $this->isNormalComment;70}7172public function setHideByDefault($hide_by_default) {73$this->hideByDefault = $hide_by_default;74return $this;75}7677public function getHideByDefault() {78return $this->hideByDefault;79}8081public function setTransactionPHID($transaction_phid) {82$this->transactionPHID = $transaction_phid;83return $this;84}8586public function getTransactionPHID() {87return $this->transactionPHID;88}8990public function setIsEdited($is_edited) {91$this->isEdited = $is_edited;92return $this;93}9495public function getIsEdited() {96return $this->isEdited;97}9899public function setIsPreview($is_preview) {100$this->isPreview = $is_preview;101return $this;102}103104public function getIsPreview() {105return $this->isPreview;106}107108public function setIsEditable($is_editable) {109$this->isEditable = $is_editable;110return $this;111}112113public function getIsEditable() {114return $this->isEditable;115}116117public function setCanInteract($can_interact) {118$this->canInteract = $can_interact;119return $this;120}121122public function getCanInteract() {123return $this->canInteract;124}125126public function setIsRemovable($is_removable) {127$this->isRemovable = $is_removable;128return $this;129}130131public function getIsRemovable() {132return $this->isRemovable;133}134135public function setDateCreated($date_created) {136$this->dateCreated = $date_created;137return $this;138}139140public function getDateCreated() {141return $this->dateCreated;142}143144public function setContentSource(PhabricatorContentSource $content_source) {145$this->contentSource = $content_source;146return $this;147}148149public function getContentSource() {150return $this->contentSource;151}152153public function setUserHandle(PhabricatorObjectHandle $handle) {154$this->userHandle = $handle;155return $this;156}157158public function setAnchor($anchor) {159$this->anchor = $anchor;160return $this;161}162163public function getAnchor() {164return $this->anchor;165}166167public function setTitle($title) {168$this->title = $title;169return $this;170}171172public function addClass($class) {173$this->classes[] = $class;174return $this;175}176177public function addBadge(PHUIBadgeMiniView $badge) {178$this->badges[] = $badge;179return $this;180}181182public function setIcon($icon) {183$this->icon = $icon;184return $this;185}186187public function setColor($color) {188$this->color = $color;189return $this;190}191192public function setIsSilent($is_silent) {193$this->isSilent = $is_silent;194return $this;195}196197public function getIsSilent() {198return $this->isSilent;199}200201public function setIsMFA($is_mfa) {202$this->isMFA = $is_mfa;203return $this;204}205206public function getIsMFA() {207return $this->isMFA;208}209210public function setIsLockOverride($is_override) {211$this->isLockOverride = $is_override;212return $this;213}214215public function getIsLockOverride() {216return $this->isLockOverride;217}218219public function setReallyMajorEvent($me) {220$this->reallyMajorEvent = $me;221return $this;222}223224public function setHideCommentOptions($hide_comment_options) {225$this->hideCommentOptions = $hide_comment_options;226return $this;227}228229public function getHideCommentOptions() {230return $this->hideCommentOptions;231}232233public function addPinboardItem(PHUIPinboardItemView $item) {234$this->pinboardItems[] = $item;235return $this;236}237238public function setToken($token, $removed = false) {239$this->token = $token;240$this->tokenRemoved = $removed;241return $this;242}243244public function getEventGroup() {245return array_merge(array($this), $this->eventGroup);246}247248public function addEventToGroup(PHUITimelineEventView $event) {249$this->eventGroup[] = $event;250return $this;251}252253protected function shouldRenderEventTitle() {254if ($this->title === null) {255return false;256}257258return true;259}260261protected function renderEventTitle($force_icon, $has_menu, $extra) {262$title = $this->title;263264$title_classes = array();265$title_classes[] = 'phui-timeline-title';266267$icon = null;268if ($this->icon || $force_icon) {269$title_classes[] = 'phui-timeline-title-with-icon';270}271272if ($has_menu) {273$title_classes[] = 'phui-timeline-title-with-menu';274}275276if ($this->icon) {277$fill_classes = array();278$fill_classes[] = 'phui-timeline-icon-fill';279if ($this->color) {280$fill_classes[] = 'fill-has-color';281$fill_classes[] = 'phui-timeline-icon-fill-'.$this->color;282}283284$icon = id(new PHUIIconView())285->setIcon($this->icon)286->addClass('phui-timeline-icon');287288$icon = phutil_tag(289'span',290array(291'class' => implode(' ', $fill_classes),292),293$icon);294}295296$token = null;297if ($this->token) {298$token = id(new PHUIIconView())299->addClass('phui-timeline-token')300->setSpriteSheet(PHUIIconView::SPRITE_TOKENS)301->setSpriteIcon($this->token);302if ($this->tokenRemoved) {303$token->addClass('strikethrough');304}305}306307$title = phutil_tag(308'div',309array(310'class' => implode(' ', $title_classes),311),312array($icon, $token, $title, $extra));313314return $title;315}316317public function render() {318319$events = $this->getEventGroup();320321// Move events with icons first.322$icon_keys = array();323foreach ($this->getEventGroup() as $key => $event) {324if ($event->icon) {325$icon_keys[] = $key;326}327}328$events = array_select_keys($events, $icon_keys) + $events;329$force_icon = (bool)$icon_keys;330331$menu = null;332$items = array();333if (!$this->getIsPreview() && !$this->getHideCommentOptions()) {334foreach ($this->getEventGroup() as $event) {335$items[] = $event->getMenuItems($this->anchor);336}337$items = array_mergev($items);338}339340if ($items) {341$icon = id(new PHUIIconView())342->setIcon('fa-caret-down');343$aural = javelin_tag(344'span',345array(346'aural' => true,347),348pht('Comment Actions'));349350if ($items) {351$sigil = 'phui-dropdown-menu';352Javelin::initBehavior('phui-dropdown-menu');353} else {354$sigil = null;355}356357$action_list = id(new PhabricatorActionListView())358->setUser($this->getUser());359foreach ($items as $item) {360$action_list->addAction($item);361}362363$menu = javelin_tag(364$items ? 'a' : 'span',365array(366'href' => '#',367'class' => 'phui-timeline-menu',368'sigil' => $sigil,369'aria-haspopup' => 'true',370'aria-expanded' => 'false',371'meta' => $action_list->getDropdownMenuMetadata(),372),373array(374$aural,375$icon,376));377378$has_menu = true;379} else {380$has_menu = false;381}382383// Render "extra" information (timestamp, etc).384$extra = $this->renderExtra($events);385386$show_badges = false;387388$group_titles = array();389$group_items = array();390$group_children = array();391foreach ($events as $event) {392if ($event->shouldRenderEventTitle()) {393394// Render the group anchor here, outside the title box. If we render395// it inside the title box it ends up completely hidden and Chrome 55396// refuses to jump to it. See T11997 for discussion.397398if ($extra && $this->anchor) {399$group_titles[] = id(new PhabricatorAnchorView())400->setAnchorName($this->anchor)401->render();402}403404$group_titles[] = $event->renderEventTitle(405$force_icon,406$has_menu,407$extra);408409// Don't render this information more than once.410$extra = null;411}412413if ($event->hasChildren()) {414$group_children[] = $event->renderChildren();415$show_badges = true;416}417}418419$image_uri = $this->userHandle->getImageURI();420421$wedge = phutil_tag(422'div',423array(424'class' => 'phui-timeline-wedge',425'style' => (nonempty($image_uri)) ? '' : 'display: none;',426),427'');428429$image = null;430$badges = null;431if ($image_uri) {432$image = javelin_tag(433($this->userHandle->getURI()) ? 'a' : 'div',434array(435'style' => 'background-image: url('.$image_uri.')',436'class' => 'phui-timeline-image',437'href' => $this->userHandle->getURI(),438'aural' => false,439),440'');441if ($this->badges && $show_badges) {442$flex = new PHUIBadgeBoxView();443$flex->addItems($this->badges);444$flex->setCollapsed(true);445$badges = phutil_tag(446'div',447array(448'class' => 'phui-timeline-badges',449),450$flex);451}452}453454$content_classes = array();455$content_classes[] = 'phui-timeline-content';456457$classes = array();458$classes[] = 'phui-timeline-event-view';459if ($group_children) {460$classes[] = 'phui-timeline-major-event';461$content = phutil_tag(462'div',463array(464'class' => 'phui-timeline-inner-content',465),466array(467$group_titles,468$menu,469phutil_tag(470'div',471array(472'class' => 'phui-timeline-core-content',473),474$group_children),475));476} else {477$classes[] = 'phui-timeline-minor-event';478$content = $group_titles;479}480481$content = phutil_tag(482'div',483array(484'class' => 'phui-timeline-group',485),486$content);487488// Image Events489$pinboard = null;490if ($this->pinboardItems) {491$pinboard = new PHUIPinboardView();492foreach ($this->pinboardItems as $item) {493$pinboard->addItem($item);494}495}496497$content = phutil_tag(498'div',499array(500'class' => implode(' ', $content_classes),501),502array($image, $badges, $wedge, $content, $pinboard));503504$outer_classes = $this->classes;505$outer_classes[] = 'phui-timeline-shell';506$color = null;507foreach ($this->getEventGroup() as $event) {508if ($event->color) {509$color = $event->color;510break;511}512}513514if ($color) {515$outer_classes[] = 'phui-timeline-'.$color;516}517518$sigils = array();519$meta = null;520if ($this->getTransactionPHID()) {521$sigils[] = 'transaction';522$meta = array(523'phid' => $this->getTransactionPHID(),524'anchor' => $this->anchor,525);526}527528$major_event = null;529if ($this->reallyMajorEvent) {530$major_event = phutil_tag(531'div',532array(533'class' => 'phui-timeline-event-view '.534'phui-timeline-spacer '.535'phui-timeline-spacer-bold',536));537}538539$sigils[] = 'anchor-container';540541return array(542javelin_tag(543'div',544array(545'class' => implode(' ', $outer_classes),546'sigil' => implode(' ', $sigils),547'meta' => $meta,548),549phutil_tag(550'div',551array(552'class' => implode(' ', $classes),553),554$content)),555$major_event,556);557}558559private function renderExtra(array $events) {560$extra = array();561562if ($this->getIsPreview()) {563$extra[] = pht('PREVIEW');564} else {565foreach ($events as $event) {566if ($event->getIsEdited()) {567$extra[] = pht('Edited');568break;569}570}571572$source = $this->getContentSource();573$content_source = null;574if ($source) {575$content_source = id(new PhabricatorContentSourceView())576->setContentSource($source)577->setUser($this->getUser());578$content_source = pht('Via %s', $content_source->getSourceName());579}580581$date_created = null;582foreach ($events as $event) {583if ($event->getDateCreated()) {584if ($date_created === null) {585$date_created = $event->getDateCreated();586} else {587$date_created = min($event->getDateCreated(), $date_created);588}589}590}591592if ($date_created) {593$date = phabricator_dual_datetime(594$date_created,595$this->getUser());596if ($this->anchor) {597Javelin::initBehavior('phabricator-watch-anchor');598Javelin::initBehavior('phabricator-tooltips');599600$date = array(601javelin_tag(602'a',603array(604'href' => '#'.$this->anchor,605'sigil' => 'has-tooltip',606'meta' => array(607'tip' => $content_source,608),609),610$date),611);612}613614$extra[] = $date;615}616617// If this edit was applied silently, give user a hint that they should618// not expect to have received any mail or notifications.619if ($this->getIsSilent()) {620$extra[] = id(new PHUIIconView())621->setIcon('fa-bell-slash', 'white')622->setEmblemColor('red')623->setTooltip(pht('Silent Edit'));624}625626// If this edit was applied while the actor was in high-security mode,627// provide a hint that it was extra authentic.628if ($this->getIsMFA()) {629$extra[] = id(new PHUIIconView())630->setIcon('fa-vcard', 'white')631->setEmblemColor('pink')632->setTooltip(pht('MFA Authenticated'));633}634635if ($this->getIsLockOverride()) {636$extra[] = id(new PHUIIconView())637->setIcon('fa-chain-broken', 'white')638->setEmblemColor('violet')639->setTooltip(pht('Lock Overridden'));640}641}642643$extra = javelin_tag(644'span',645array(646'class' => 'phui-timeline-extra',647),648phutil_implode_html(649javelin_tag(650'span',651array(652'aural' => false,653),654self::DELIMITER),655$extra));656657return $extra;658}659660private function getMenuItems($anchor) {661$xaction_phid = $this->getTransactionPHID();662663$can_interact = $this->getCanInteract();664$viewer = $this->getViewer();665$is_admin = $viewer->getIsAdmin();666667$items = array();668669if ($this->getIsEditable()) {670$items[] = id(new PhabricatorActionView())671->setIcon('fa-pencil')672->setHref('/transactions/edit/'.$xaction_phid.'/')673->setName(pht('Edit Comment'))674->addSigil('transaction-edit')675->setDisabled(!$can_interact)676->setMetadata(677array(678'anchor' => $anchor,679));680}681682if ($this->getQuoteTargetID()) {683$ref = null;684if ($this->getQuoteRef()) {685$ref = $this->getQuoteRef();686if ($anchor) {687$ref = $ref.'#'.$anchor;688}689}690691$items[] = id(new PhabricatorActionView())692->setIcon('fa-quote-left')693->setName(pht('Quote Comment'))694->setHref('#')695->addSigil('transaction-quote')696->setMetadata(697array(698'targetID' => $this->getQuoteTargetID(),699'uri' => '/transactions/quote/'.$xaction_phid.'/',700'ref' => $ref,701));702}703704if ($this->getIsNormalComment()) {705$items[] = id(new PhabricatorActionView())706->setIcon('fa-code')707->setHref('/transactions/raw/'.$xaction_phid.'/')708->setName(pht('View Raw Remarkup'))709->addSigil('transaction-raw')710->setMetadata(711array(712'anchor' => $anchor,713));714715$content_source = $this->getContentSource();716$source_email = PhabricatorEmailContentSource::SOURCECONST;717if ($content_source->getSource() == $source_email) {718$source_id = $content_source->getContentSourceParameter('id');719if ($source_id) {720$items[] = id(new PhabricatorActionView())721->setIcon('fa-envelope-o')722->setHref('/transactions/raw/'.$xaction_phid.'/?email')723->setName(pht('View Email Body'))724->addSigil('transaction-raw')725->setMetadata(726array(727'anchor' => $anchor,728));729}730}731}732733if ($this->getIsEdited()) {734$items[] = id(new PhabricatorActionView())735->setIcon('fa-list')736->setHref('/transactions/history/'.$xaction_phid.'/')737->setName(pht('View Edit History'))738->setWorkflow(true);739}740741if ($this->getIsRemovable()) {742$items[] = id(new PhabricatorActionView())743->setType(PhabricatorActionView::TYPE_DIVIDER);744745$remove_item = id(new PhabricatorActionView())746->setIcon('fa-trash-o')747->setHref('/transactions/remove/'.$xaction_phid.'/')748->setName(pht('Remove Comment'))749->addSigil('transaction-remove')750->setMetadata(751array(752'anchor' => $anchor,753));754755if (!$is_admin && !$can_interact) {756$remove_item->setDisabled(!$is_admin && !$can_interact);757} else {758$remove_item->setColor(PhabricatorActionView::RED);759}760761$items[] = $remove_item;762}763764return $items;765}766767}768769770