2012-11-22 02:24:56 +01:00
|
|
|
<?php
|
|
|
|
|
2014-02-12 18:02:05 +01:00
|
|
|
final class PHUITimelineEventView extends AphrontView {
|
2012-11-22 02:24:56 +01:00
|
|
|
|
2014-04-04 21:23:22 +02:00
|
|
|
const DELIMITER = " \xC2\xB7 ";
|
|
|
|
|
2012-11-22 02:24:56 +01:00
|
|
|
private $userHandle;
|
|
|
|
private $title;
|
2012-12-11 22:59:35 +01:00
|
|
|
private $icon;
|
|
|
|
private $color;
|
2012-11-22 02:24:56 +01:00
|
|
|
private $classes = array();
|
2012-12-11 22:59:51 +01:00
|
|
|
private $contentSource;
|
|
|
|
private $dateCreated;
|
|
|
|
private $anchor;
|
2012-12-11 23:01:51 +01:00
|
|
|
private $isEditable;
|
|
|
|
private $isEdited;
|
|
|
|
private $transactionPHID;
|
2012-12-21 14:51:33 +01:00
|
|
|
private $isPreview;
|
2013-09-24 23:35:35 +02:00
|
|
|
private $eventGroup = array();
|
2014-02-14 19:23:07 +01:00
|
|
|
private $hideByDefault;
|
2014-03-27 22:24:31 +01:00
|
|
|
private $token;
|
|
|
|
private $tokenRemoved;
|
2014-02-14 19:23:07 +01:00
|
|
|
|
|
|
|
public function setHideByDefault($hide_by_default) {
|
|
|
|
$this->hideByDefault = $hide_by_default;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getHideByDefault() {
|
|
|
|
return $this->hideByDefault;
|
|
|
|
}
|
2012-12-11 23:01:51 +01:00
|
|
|
|
|
|
|
public function setTransactionPHID($transaction_phid) {
|
|
|
|
$this->transactionPHID = $transaction_phid;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getTransactionPHID() {
|
|
|
|
return $this->transactionPHID;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setIsEdited($is_edited) {
|
|
|
|
$this->isEdited = $is_edited;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getIsEdited() {
|
|
|
|
return $this->isEdited;
|
|
|
|
}
|
|
|
|
|
2012-12-21 14:51:33 +01:00
|
|
|
public function setIsPreview($is_preview) {
|
|
|
|
$this->isPreview = $is_preview;
|
2012-12-11 23:01:51 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-12-21 14:51:33 +01:00
|
|
|
public function getIsPreview() {
|
|
|
|
return $this->isPreview;
|
2012-12-11 23:01:51 +01:00
|
|
|
}
|
2012-12-11 22:59:51 +01:00
|
|
|
|
2012-12-21 14:51:33 +01:00
|
|
|
public function setIsEditable($is_editable) {
|
|
|
|
$this->isEditable = $is_editable;
|
2012-12-11 22:59:51 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-12-21 14:51:33 +01:00
|
|
|
public function getIsEditable() {
|
|
|
|
return $this->isEditable;
|
2012-12-11 22:59:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public function setDateCreated($date_created) {
|
|
|
|
$this->dateCreated = $date_created;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getDateCreated() {
|
|
|
|
return $this->dateCreated;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function setContentSource(PhabricatorContentSource $content_source) {
|
|
|
|
$this->contentSource = $content_source;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getContentSource() {
|
|
|
|
return $this->contentSource;
|
|
|
|
}
|
2012-11-22 02:24:56 +01:00
|
|
|
|
|
|
|
public function setUserHandle(PhabricatorObjectHandle $handle) {
|
|
|
|
$this->userHandle = $handle;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-12-11 22:59:51 +01:00
|
|
|
public function setAnchor($anchor) {
|
|
|
|
$this->anchor = $anchor;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2014-02-14 19:23:07 +01:00
|
|
|
public function getAnchor() {
|
|
|
|
return $this->anchor;
|
|
|
|
}
|
|
|
|
|
2012-11-22 02:24:56 +01:00
|
|
|
public function setTitle($title) {
|
|
|
|
$this->title = $title;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function addClass($class) {
|
|
|
|
$this->classes[] = $class;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-12-11 22:59:35 +01:00
|
|
|
public function setIcon($icon) {
|
|
|
|
$this->icon = $icon;
|
2012-11-22 02:24:56 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2012-12-11 22:59:35 +01:00
|
|
|
public function setColor($color) {
|
|
|
|
$this->color = $color;
|
2012-11-22 02:24:56 +01:00
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2014-03-27 22:24:31 +01:00
|
|
|
public function setToken($token, $removed=false) {
|
|
|
|
$this->token = $token;
|
|
|
|
$this->tokenRemoved = $removed;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-09-24 23:35:35 +02:00
|
|
|
public function getEventGroup() {
|
|
|
|
return array_merge(array($this), $this->eventGroup);
|
|
|
|
}
|
|
|
|
|
2014-02-12 18:02:05 +01:00
|
|
|
public function addEventToGroup(PHUITimelineEventView $event) {
|
2013-09-24 23:35:35 +02:00
|
|
|
$this->eventGroup[] = $event;
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
2013-12-27 04:24:04 +01:00
|
|
|
protected function renderEventTitle($is_first_event, $force_icon) {
|
2012-11-22 02:24:56 +01:00
|
|
|
$title = $this->title;
|
Provide hasChildren() to replace isEmptyContent()
Summary:
Fixes T3698. Sometimes views need to render differently depending on whether they contain content or not. The existing approach for this is `isEmptyContent()`, which doesn't work well and is sort of hacky (it implies double-rendering content, which is not always free or side-effect free).
Instead, provide a test for an element without children. This test is powerful enough to catch the easy cases of `null`, etc., and just do the expected thing, but will not catch a View which is reduced upon rendering. Since this is rare and we have no actual need for it today, just accept that as a limitation.
Test Plan:
Viewed Timeline and Feed UI examples. Viewed Feed (feed), Pholio (timelineview), and Differential (old transactionview).
{F53915}
Reviewers: chad, btrahan
Reviewed By: chad
CC: aran
Maniphest Tasks: T3698
Differential Revision: https://secure.phabricator.com/D6718
2013-08-12 16:51:01 +02:00
|
|
|
if (($title === null) && !$this->hasChildren()) {
|
2012-11-22 02:24:56 +01:00
|
|
|
$title = '';
|
|
|
|
}
|
|
|
|
|
2013-12-27 04:24:04 +01:00
|
|
|
if ($is_first_event) {
|
|
|
|
$extra = array();
|
|
|
|
$is_first_extra = true;
|
|
|
|
foreach ($this->getEventGroup() as $event) {
|
2013-12-27 14:51:15 +01:00
|
|
|
$extra[] = $event->renderExtra($is_first_extra);
|
2013-12-27 04:24:04 +01:00
|
|
|
$is_first_extra = false;
|
|
|
|
}
|
2013-12-27 14:51:15 +01:00
|
|
|
$extra = array_reverse($extra);
|
|
|
|
$extra = array_mergev($extra);
|
2014-04-04 21:23:22 +02:00
|
|
|
$extra = javelin_tag(
|
2013-12-27 04:24:04 +01:00
|
|
|
'span',
|
|
|
|
array(
|
2014-04-04 21:23:22 +02:00
|
|
|
'sigil' => 'timeline-extra',
|
2014-02-12 18:02:05 +01:00
|
|
|
'class' => 'phui-timeline-extra',
|
2013-12-27 04:24:04 +01:00
|
|
|
),
|
2014-04-04 21:23:22 +02:00
|
|
|
phutil_implode_html(self::DELIMITER, $extra));
|
2013-12-27 04:24:04 +01:00
|
|
|
} else {
|
|
|
|
$extra = null;
|
|
|
|
}
|
2012-12-11 22:59:51 +01:00
|
|
|
|
2013-12-27 04:24:04 +01:00
|
|
|
if ($title !== null || $extra) {
|
2012-11-22 02:24:56 +01:00
|
|
|
$title_classes = array();
|
2014-02-12 18:02:05 +01:00
|
|
|
$title_classes[] = 'phui-timeline-title';
|
2012-12-11 22:59:35 +01:00
|
|
|
|
|
|
|
$icon = null;
|
2013-12-27 04:24:04 +01:00
|
|
|
if ($this->icon || $force_icon) {
|
2014-02-12 18:02:05 +01:00
|
|
|
$title_classes[] = 'phui-timeline-title-with-icon';
|
2013-12-27 04:24:04 +01:00
|
|
|
}
|
2012-12-11 22:59:35 +01:00
|
|
|
|
2013-12-27 04:24:04 +01:00
|
|
|
if ($this->icon) {
|
2013-09-24 23:35:35 +02:00
|
|
|
$fill_classes = array();
|
2014-02-12 18:02:05 +01:00
|
|
|
$fill_classes[] = 'phui-timeline-icon-fill';
|
2013-09-24 23:35:35 +02:00
|
|
|
if ($this->color) {
|
2014-02-12 18:02:05 +01:00
|
|
|
$fill_classes[] = 'phui-timeline-icon-fill-'.$this->color;
|
2013-09-24 23:35:35 +02:00
|
|
|
}
|
|
|
|
|
2014-04-22 17:25:54 +02:00
|
|
|
$icon = id(new PHUIIconView())
|
|
|
|
->setIconFont($this->icon.' white')
|
|
|
|
->addClass('phui-timeline-icon');
|
|
|
|
|
2013-01-18 03:47:13 +01:00
|
|
|
$icon = phutil_tag(
|
2012-12-11 22:59:35 +01:00
|
|
|
'span',
|
|
|
|
array(
|
2013-09-24 23:35:35 +02:00
|
|
|
'class' => implode(' ', $fill_classes),
|
2012-12-11 22:59:35 +01:00
|
|
|
),
|
2014-04-22 17:25:54 +02:00
|
|
|
$icon);
|
2012-11-22 02:24:56 +01:00
|
|
|
}
|
|
|
|
|
2014-03-27 22:24:31 +01:00
|
|
|
$token = null;
|
|
|
|
if ($this->token) {
|
|
|
|
$token = id(new PHUIIconView())
|
|
|
|
->addClass('phui-timeline-token')
|
|
|
|
->setSpriteSheet(PHUIIconView::SPRITE_TOKENS)
|
|
|
|
->setSpriteIcon($this->token);
|
|
|
|
if ($this->tokenRemoved) {
|
|
|
|
$token->addClass('strikethrough');
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-01-29 03:09:33 +01:00
|
|
|
$title = phutil_tag(
|
2012-11-22 02:24:56 +01:00
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => implode(' ', $title_classes),
|
|
|
|
),
|
2014-03-27 22:24:31 +01:00
|
|
|
array($icon, $token, $title, $extra));
|
2013-09-24 23:35:35 +02:00
|
|
|
}
|
2012-12-11 23:02:51 +01:00
|
|
|
|
2013-09-24 23:35:35 +02:00
|
|
|
return $title;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function render() {
|
|
|
|
|
2013-12-27 04:24:04 +01:00
|
|
|
$events = $this->getEventGroup();
|
|
|
|
|
|
|
|
// Move events with icons first.
|
|
|
|
$icon_keys = array();
|
|
|
|
foreach ($this->getEventGroup() as $key => $event) {
|
|
|
|
if ($event->icon) {
|
|
|
|
$icon_keys[] = $key;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
$events = array_select_keys($events, $icon_keys) + $events;
|
|
|
|
$force_icon = (bool)$icon_keys;
|
|
|
|
|
2013-09-24 23:35:35 +02:00
|
|
|
$group_titles = array();
|
|
|
|
$group_children = array();
|
2013-12-27 04:24:04 +01:00
|
|
|
$is_first_event = true;
|
|
|
|
foreach ($events as $event) {
|
|
|
|
$group_titles[] = $event->renderEventTitle($is_first_event, $force_icon);
|
|
|
|
$is_first_event = false;
|
2013-09-24 23:35:35 +02:00
|
|
|
if ($event->hasChildren()) {
|
|
|
|
$group_children[] = $event->renderChildren();
|
|
|
|
}
|
2012-11-22 02:24:56 +01:00
|
|
|
}
|
|
|
|
|
2014-04-29 19:11:40 +02:00
|
|
|
$image_uri = $this->userHandle->getImageURI();
|
|
|
|
|
2013-01-18 03:57:09 +01:00
|
|
|
$wedge = phutil_tag(
|
2012-11-22 02:24:56 +01:00
|
|
|
'div',
|
|
|
|
array(
|
2014-02-12 18:02:05 +01:00
|
|
|
'class' => 'phui-timeline-wedge phui-timeline-border',
|
2014-04-29 19:11:40 +02:00
|
|
|
'style' => (nonempty($image_uri)) ? '' : 'display: none;',
|
2012-11-22 02:24:56 +01:00
|
|
|
),
|
|
|
|
'');
|
|
|
|
|
2013-01-18 03:57:09 +01:00
|
|
|
$image = phutil_tag(
|
2012-11-22 02:24:56 +01:00
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'style' => 'background-image: url('.$image_uri.')',
|
2014-02-12 18:02:05 +01:00
|
|
|
'class' => 'phui-timeline-image',
|
2012-11-22 02:24:56 +01:00
|
|
|
),
|
|
|
|
'');
|
|
|
|
|
|
|
|
$content_classes = array();
|
2014-02-12 18:02:05 +01:00
|
|
|
$content_classes[] = 'phui-timeline-content';
|
2012-11-22 02:24:56 +01:00
|
|
|
|
|
|
|
$classes = array();
|
2014-02-12 18:02:05 +01:00
|
|
|
$classes[] = 'phui-timeline-event-view';
|
2013-09-24 23:35:35 +02:00
|
|
|
if ($group_children) {
|
2014-02-12 18:02:05 +01:00
|
|
|
$classes[] = 'phui-timeline-major-event';
|
2013-01-29 03:09:33 +01:00
|
|
|
$content = phutil_tag(
|
2012-11-22 02:24:56 +01:00
|
|
|
'div',
|
|
|
|
array(
|
2014-02-12 18:02:05 +01:00
|
|
|
'class' => 'phui-timeline-inner-content',
|
2012-11-22 02:24:56 +01:00
|
|
|
),
|
2013-09-24 23:35:35 +02:00
|
|
|
array(
|
|
|
|
$group_titles,
|
|
|
|
phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
2014-02-12 18:02:05 +01:00
|
|
|
'class' => 'phui-timeline-core-content',
|
2013-09-24 23:35:35 +02:00
|
|
|
),
|
|
|
|
$group_children),
|
|
|
|
));
|
2012-11-22 02:24:56 +01:00
|
|
|
} else {
|
2014-02-12 18:02:05 +01:00
|
|
|
$classes[] = 'phui-timeline-minor-event';
|
2013-09-24 23:35:35 +02:00
|
|
|
$content = $group_titles;
|
2012-11-22 02:24:56 +01:00
|
|
|
}
|
|
|
|
|
2013-09-24 23:35:35 +02:00
|
|
|
$content = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
2014-02-12 18:02:05 +01:00
|
|
|
'class' => 'phui-timeline-group phui-timeline-border',
|
2013-09-24 23:35:35 +02:00
|
|
|
),
|
|
|
|
$content);
|
|
|
|
|
|
|
|
$content = phutil_tag(
|
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => implode(' ', $content_classes),
|
|
|
|
),
|
|
|
|
array($image, $wedge, $content));
|
|
|
|
|
2012-12-11 22:59:35 +01:00
|
|
|
$outer_classes = $this->classes;
|
2014-02-12 18:02:05 +01:00
|
|
|
$outer_classes[] = 'phui-timeline-shell';
|
2013-09-24 23:35:35 +02:00
|
|
|
$color = null;
|
|
|
|
foreach ($this->getEventGroup() as $event) {
|
|
|
|
if ($event->color) {
|
|
|
|
$color = $event->color;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($color) {
|
2014-02-12 18:02:05 +01:00
|
|
|
$outer_classes[] = 'phui-timeline-'.$color;
|
2012-12-11 22:59:35 +01:00
|
|
|
}
|
|
|
|
|
2012-12-11 23:02:12 +01:00
|
|
|
$sigil = null;
|
|
|
|
$meta = null;
|
|
|
|
if ($this->getTransactionPHID()) {
|
|
|
|
$sigil = 'transaction';
|
|
|
|
$meta = array(
|
|
|
|
'phid' => $this->getTransactionPHID(),
|
|
|
|
'anchor' => $this->anchor,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-01-29 03:09:33 +01:00
|
|
|
return javelin_tag(
|
2012-11-22 02:24:56 +01:00
|
|
|
'div',
|
|
|
|
array(
|
2012-12-11 22:59:35 +01:00
|
|
|
'class' => implode(' ', $outer_classes),
|
2012-12-11 22:59:51 +01:00
|
|
|
'id' => $this->anchor ? 'anchor-'.$this->anchor : null,
|
2012-12-11 23:02:12 +01:00
|
|
|
'sigil' => $sigil,
|
|
|
|
'meta' => $meta,
|
2012-11-22 02:24:56 +01:00
|
|
|
),
|
2013-01-29 03:09:33 +01:00
|
|
|
phutil_tag(
|
2012-11-22 02:24:56 +01:00
|
|
|
'div',
|
|
|
|
array(
|
|
|
|
'class' => implode(' ', $classes),
|
|
|
|
),
|
|
|
|
$content));
|
|
|
|
}
|
|
|
|
|
2013-12-27 04:24:04 +01:00
|
|
|
private function renderExtra($is_first_extra) {
|
2012-12-21 14:51:33 +01:00
|
|
|
$extra = array();
|
|
|
|
|
|
|
|
if ($this->getIsPreview()) {
|
|
|
|
$extra[] = pht('PREVIEW');
|
|
|
|
} else {
|
|
|
|
$xaction_phid = $this->getTransactionPHID();
|
|
|
|
|
|
|
|
if ($this->getIsEdited()) {
|
2013-01-25 21:57:17 +01:00
|
|
|
$extra[] = javelin_tag(
|
2012-12-21 14:51:33 +01:00
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '/transactions/history/'.$xaction_phid.'/',
|
2014-04-04 21:23:22 +02:00
|
|
|
'sigil' => 'workflow transaction-edit-history',
|
2012-12-21 14:51:33 +01:00
|
|
|
),
|
|
|
|
pht('Edited'));
|
|
|
|
}
|
|
|
|
|
|
|
|
if ($this->getIsEditable()) {
|
2013-01-25 21:57:17 +01:00
|
|
|
$extra[] = javelin_tag(
|
2012-12-21 14:51:33 +01:00
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '/transactions/edit/'.$xaction_phid.'/',
|
|
|
|
'sigil' => 'workflow transaction-edit',
|
|
|
|
),
|
|
|
|
pht('Edit'));
|
|
|
|
}
|
|
|
|
|
2013-12-27 04:24:04 +01:00
|
|
|
if ($is_first_extra) {
|
|
|
|
$source = $this->getContentSource();
|
|
|
|
if ($source) {
|
|
|
|
$extra[] = id(new PhabricatorContentSourceView())
|
|
|
|
->setContentSource($source)
|
|
|
|
->setUser($this->getUser())
|
2012-12-21 14:51:33 +01:00
|
|
|
->render();
|
2013-12-27 04:24:04 +01:00
|
|
|
}
|
2012-12-21 14:51:33 +01:00
|
|
|
|
2013-12-27 04:24:04 +01:00
|
|
|
$date_created = null;
|
|
|
|
foreach ($this->getEventGroup() as $event) {
|
|
|
|
if ($event->getDateCreated()) {
|
|
|
|
if ($date_created === null) {
|
|
|
|
$date_created = $event->getDateCreated();
|
|
|
|
} else {
|
|
|
|
$date_created = min($event->getDateCreated(), $date_created);
|
|
|
|
}
|
|
|
|
}
|
2012-12-21 14:51:33 +01:00
|
|
|
}
|
|
|
|
|
2013-12-27 04:24:04 +01:00
|
|
|
if ($date_created) {
|
|
|
|
$date = phabricator_datetime(
|
|
|
|
$this->getDateCreated(),
|
|
|
|
$this->getUser());
|
|
|
|
if ($this->anchor) {
|
|
|
|
Javelin::initBehavior('phabricator-watch-anchor');
|
|
|
|
|
|
|
|
$anchor = id(new PhabricatorAnchorView())
|
|
|
|
->setAnchorName($this->anchor)
|
|
|
|
->render();
|
|
|
|
|
|
|
|
$date = array(
|
|
|
|
$anchor,
|
|
|
|
phutil_tag(
|
|
|
|
'a',
|
|
|
|
array(
|
|
|
|
'href' => '#'.$this->anchor,
|
|
|
|
),
|
|
|
|
$date),
|
|
|
|
);
|
|
|
|
}
|
|
|
|
$extra[] = $date;
|
|
|
|
}
|
|
|
|
}
|
2012-12-21 14:51:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $extra;
|
|
|
|
}
|
|
|
|
|
2012-11-22 02:24:56 +01:00
|
|
|
}
|