diff --git a/src/applications/uiexample/examples/PhabricatorTimelineExample.php b/src/applications/uiexample/examples/PhabricatorTimelineExample.php index 645ec347fc..68e6878999 100644 --- a/src/applications/uiexample/examples/PhabricatorTimelineExample.php +++ b/src/applications/uiexample/examples/PhabricatorTimelineExample.php @@ -1,126 +1,126 @@ PhabricatorTimelineView to comments and transactions.'; } public function renderExample() { $request = $this->getRequest(); $user = $request->getUser(); $handle = PhabricatorObjectHandleData::loadOneHandle( $user->getPHID(), $user); $events = array(); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('A major event.') ->appendChild('This is a major timeline event.'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('A minor event.'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->appendChild('A major event with no title.'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Another minor event.'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Major Red Event') ->setIcon('love') ->appendChild('This event is red!') ->setColor(PhabricatorTransactions::COLOR_RED); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Minor Red Event') ->setColor(PhabricatorTransactions::COLOR_RED); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Minor Not-Red Event'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Minor Red Event') ->setColor(PhabricatorTransactions::COLOR_RED); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Minor Not-Red Event'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Major Green Event') ->appendChild('This event is green!') ->setColor(PhabricatorTransactions::COLOR_GREEN); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle(str_repeat('Long Text Title ', 64)) ->appendChild(str_repeat('Long Text Body ', 64)) ->setColor(PhabricatorTransactions::COLOR_ORANGE); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle(str_repeat('LongTextEventNoSpaces', 1024)) ->appendChild(str_repeat('LongTextNoSpaces', 1024)) ->setColor(PhabricatorTransactions::COLOR_RED); $colors = array( PhabricatorTransactions::COLOR_RED, PhabricatorTransactions::COLOR_ORANGE, PhabricatorTransactions::COLOR_YELLOW, PhabricatorTransactions::COLOR_GREEN, PhabricatorTransactions::COLOR_SKY, PhabricatorTransactions::COLOR_BLUE, PhabricatorTransactions::COLOR_INDIGO, PhabricatorTransactions::COLOR_VIOLET, PhabricatorTransactions::COLOR_GREY, PhabricatorTransactions::COLOR_BLACK, ); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) - ->setTitle(phutil_escape_html("Colorless")) + ->setTitle("Colorless") ->setIcon('lock'); foreach ($colors as $color) { $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) - ->setTitle(phutil_escape_html("Color '{$color}'")) + ->setTitle("Color '{$color}'") ->setIcon('lock') ->setColor($color); } $anchor = 0; foreach ($events as $event) { $event->setUser($user); $event->setDateCreated(time() + ($anchor * 60 * 8)); $event->setAnchor(++$anchor); } $timeline = id(new PhabricatorTimelineView()); foreach ($events as $event) { $timeline->addEvent($event); } return $timeline; } } diff --git a/src/view/AphrontView.php b/src/view/AphrontView.php index e1bf757429..bfe6f9d624 100644 --- a/src/view/AphrontView.php +++ b/src/view/AphrontView.php @@ -1,79 +1,92 @@ user = $user; return $this; } protected function getUser() { return $this->user; } protected function canAppendChild() { return true; } final public function appendChild($child) { if (!$this->canAppendChild()) { $class = get_class($this); throw new Exception( "View '{$class}' does not support children."); } $this->children[] = $child; return $this; } final protected function renderChildren() { $out = array(); foreach ($this->children as $child) { $out[] = $this->renderSingleView($child); } return implode('', $out); } final protected function renderHTMLChildren() { $out = array(); foreach ($this->children as $child) { $out[] = $this->renderHTMLView($child); } return $out; } final protected function renderSingleView($child) { if ($child instanceof AphrontView) { return $child->render(); } else if (is_array($child)) { $out = array(); foreach ($child as $element) { $out[] = $this->renderSingleView($element); } return implode('', $out); } else { return $child; } } final protected function renderHTMLView($child) { if ($child instanceof AphrontView) { return phutil_safe_html($child->render()); } else if ($child instanceof PhutilSafeHTML) { return $child; } else if (is_array($child)) { $out = array(); foreach ($child as $element) { $out[] = $this->renderHTMLView($element); } return phutil_safe_html(implode('', $out)); } else { return phutil_safe_html(phutil_escape_html($child)); } } + final protected function isEmptyContent($content) { + if (is_array($content)) { + foreach ($content as $element) { + if (!$this->isEmptyContent($element)) { + return false; + } + } + return true; + } else { + return !strlen((string)$content); + } + } + abstract public function render(); } diff --git a/src/view/layout/PhabricatorAnchorView.php b/src/view/layout/PhabricatorAnchorView.php index 4650a91b98..44e8f33730 100644 --- a/src/view/layout/PhabricatorAnchorView.php +++ b/src/view/layout/PhabricatorAnchorView.php @@ -1,45 +1,45 @@ anchorName = $name; return $this; } public function setNavigationMarker($marker) { $this->navigationMarker = $marker; return $this; } public function render() { $marker = null; if ($this->navigationMarker) { $marker = javelin_tag( 'legend', array( 'class' => 'phabricator-anchor-navigation-marker', 'sigil' => 'marker', 'meta' => array( 'anchor' => $this->anchorName, ), ), ''); } $anchor = phutil_tag( 'a', array( 'name' => $this->anchorName, 'id' => $this->anchorName, 'class' => 'phabricator-anchor-view', ), ''); - return $marker.$anchor; + return $this->renderHTMLView(array($marker, $anchor)); } } diff --git a/src/view/layout/PhabricatorTimelineEventView.php b/src/view/layout/PhabricatorTimelineEventView.php index 792c71da92..97c1970d61 100644 --- a/src/view/layout/PhabricatorTimelineEventView.php +++ b/src/view/layout/PhabricatorTimelineEventView.php @@ -1,300 +1,305 @@ 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; } public function setIsPreview($is_preview) { $this->isPreview = $is_preview; return $this; } public function getIsPreview() { return $this->isPreview; } public function setIsEditable($is_editable) { $this->isEditable = $is_editable; return $this; } public function getIsEditable() { return $this->isEditable; } 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; } public function setUserHandle(PhabricatorObjectHandle $handle) { $this->userHandle = $handle; return $this; } public function setAnchor($anchor) { $this->anchor = $anchor; return $this; } public function setTitle($title) { $this->title = $title; return $this; } public function addClass($class) { $this->classes[] = $class; return $this; } public function setIcon($icon) { $this->icon = $icon; return $this; } public function setColor($color) { $this->color = $color; return $this; } public function render() { - $content = $this->renderChildren(); + $content = $this->renderHTMLChildren(); $title = $this->title; - if (($title === null) && !strlen($content)) { + if (($title === null) && $this->isEmptyContent($content)) { $title = ''; } $extra = $this->renderExtra(); if ($title !== null || $extra !== null) { $title_classes = array(); $title_classes[] = 'phabricator-timeline-title'; $icon = null; if ($this->icon) { $title_classes[] = 'phabricator-timeline-title-with-icon'; $icon = phutil_tag( 'span', array( 'class' => 'phabricator-timeline-icon-fill', ), phutil_tag( 'span', array( 'class' => 'phabricator-timeline-icon sprite-icon '. 'action-'.$this->icon.'-white', ), '')); } - $title = phutil_render_tag( + $title = phutil_tag( 'div', array( 'class' => implode(' ', $title_classes), ), - $title.$extra); + array($title, $extra)); - $title = $icon.$title; + $title = $this->renderHTMLView(array($icon, $title)); } $wedge = phutil_tag( 'div', array( 'class' => 'phabricator-timeline-wedge phabricator-timeline-border', ), ''); $image_uri = $this->userHandle->getImageURI(); $image = phutil_tag( 'div', array( 'style' => 'background-image: url('.$image_uri.')', 'class' => 'phabricator-timeline-image', ), ''); $content_classes = array(); $content_classes[] = 'phabricator-timeline-content'; $classes = array(); $classes[] = 'phabricator-timeline-event-view'; $classes[] = 'phabricator-timeline-border'; if ($content) { $classes[] = 'phabricator-timeline-major-event'; - $content = phutil_render_tag( + $content = phutil_tag( 'div', array( 'class' => implode(' ', $content_classes), ), - phutil_render_tag( + phutil_tag( 'div', array( 'class' => 'phabricator-timeline-inner-content', ), - $title. - phutil_render_tag( - 'div', - array( - 'class' => 'phabricator-timeline-core-content', - ), - $content))); - $content = $image.$wedge.$content; + array( + $title, + phutil_tag( + 'div', + array( + 'class' => 'phabricator-timeline-core-content', + ), + $content), + ))); + $content = array($image, $wedge, $content); } else { $classes[] = 'phabricator-timeline-minor-event'; - $content = phutil_render_tag( + $content = phutil_tag( 'div', array( 'class' => implode(' ', $content_classes), ), - $image.$wedge.$title); + array($image, $wedge, $title)); } $outer_classes = $this->classes; $outer_classes[] = 'phabricator-timeline-shell'; if ($this->color) { $outer_classes[] = 'phabricator-timeline-'.$this->color; } $sigil = null; $meta = null; if ($this->getTransactionPHID()) { $sigil = 'transaction'; $meta = array( 'phid' => $this->getTransactionPHID(), 'anchor' => $this->anchor, ); } - return javelin_render_tag( + return javelin_tag( 'div', array( 'class' => implode(' ', $outer_classes), 'id' => $this->anchor ? 'anchor-'.$this->anchor : null, 'sigil' => $sigil, 'meta' => $meta, ), - phutil_render_tag( + phutil_tag( 'div', array( 'class' => implode(' ', $classes), ), $content)); } private function renderExtra() { $extra = array(); if ($this->getIsPreview()) { $extra[] = pht('PREVIEW'); } else { $xaction_phid = $this->getTransactionPHID(); if ($this->getIsEdited()) { $extra[] = javelin_tag( 'a', array( 'href' => '/transactions/history/'.$xaction_phid.'/', 'sigil' => 'workflow', ), pht('Edited')); } if ($this->getIsEditable()) { $extra[] = javelin_tag( 'a', array( 'href' => '/transactions/edit/'.$xaction_phid.'/', 'sigil' => 'workflow transaction-edit', ), pht('Edit')); } $source = $this->getContentSource(); if ($source) { $extra[] = id(new PhabricatorContentSourceView()) ->setContentSource($source) ->setUser($this->getUser()) ->render(); } if ($this->getDateCreated()) { $date = phabricator_datetime( $this->getDateCreated(), $this->getUser()); if ($this->anchor) { Javelin::initBehavior('phabricator-watch-anchor'); $anchor = id(new PhabricatorAnchorView()) ->setAnchorName($this->anchor) ->render(); - $date = $anchor.phutil_tag( - 'a', - array( - 'href' => '#'.$this->anchor, - ), - $date); + $date = $this->renderHTMLView( + array( + $anchor, + phutil_tag( + 'a', + array( + 'href' => '#'.$this->anchor, + ), + $date), + )); } $extra[] = $date; } } - $extra = implode(' · ', $extra); if ($extra) { - $extra = phutil_render_tag( + $extra = phutil_tag( 'span', array( 'class' => 'phabricator-timeline-extra', ), - $extra); + array_interleave(" \xC2\xB7 ", $extra)); } return $extra; } }