Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/notification/builder/PhabricatorNotificationBuilder.php b/src/applications/notification/builder/PhabricatorNotificationBuilder.php
index a8af4ff00e..12e4b57bfb 100644
--- a/src/applications/notification/builder/PhabricatorNotificationBuilder.php
+++ b/src/applications/notification/builder/PhabricatorNotificationBuilder.php
@@ -1,168 +1,181 @@
<?php
final class PhabricatorNotificationBuilder extends Phobject {
private $stories;
private $parsedStories;
private $user = null;
+ private $showTimestamps = true;
public function __construct(array $stories) {
assert_instances_of($stories, 'PhabricatorFeedStory');
$this->stories = $stories;
}
public function setUser($user) {
$this->user = $user;
return $this;
}
+ public function setShowTimestamps($show_timestamps) {
+ $this->showTimestamps = $show_timestamps;
+ return $this;
+ }
+
+ public function getShowTimestamps() {
+ return $this->showTimestamps;
+ }
+
private function parseStories() {
if ($this->parsedStories) {
return $this->parsedStories;
}
$stories = $this->stories;
$stories = mpull($stories, null, 'getChronologicalKey');
// Aggregate notifications. Generally, we can aggregate notifications only
// by object, e.g. "a updated T123" and "b updated T123" can become
// "a and b updated T123", but we can't combine "a updated T123" and
// "a updated T234" into "a updated T123 and T234" because there would be
// nowhere sensible for the notification to link to, and no reasonable way
// to unambiguously clear it.
// Build up a map of all the possible aggregations.
$chronokey_map = array();
$aggregation_map = array();
$agg_types = array();
foreach ($stories as $chronokey => $story) {
$chronokey_map[$chronokey] = $story->getNotificationAggregations();
foreach ($chronokey_map[$chronokey] as $key => $type) {
$agg_types[$key] = $type;
$aggregation_map[$key]['keys'][$chronokey] = true;
}
}
// Repeatedly select the largest available aggregation until none remain.
$aggregated_stories = array();
while ($aggregation_map) {
// Count the size of each aggregation, removing any which will consume
// fewer than 2 stories.
foreach ($aggregation_map as $key => $dict) {
$size = count($dict['keys']);
if ($size > 1) {
$aggregation_map[$key]['size'] = $size;
} else {
unset($aggregation_map[$key]);
}
}
// If we're out of aggregations, break out.
if (!$aggregation_map) {
break;
}
// Select the aggregation we're going to make, and remove it from the
// map.
$aggregation_map = isort($aggregation_map, 'size');
$agg_info = idx(last($aggregation_map), 'keys');
$agg_key = last_key($aggregation_map);
unset($aggregation_map[$agg_key]);
// Select all the stories it aggregates, and remove them from the master
// list of stories and from all other possible aggregations.
$sub_stories = array();
foreach ($agg_info as $chronokey => $ignored) {
$sub_stories[$chronokey] = $stories[$chronokey];
unset($stories[$chronokey]);
foreach ($chronokey_map[$chronokey] as $key => $type) {
unset($aggregation_map[$key]['keys'][$chronokey]);
}
unset($chronokey_map[$chronokey]);
}
// Build the aggregate story.
krsort($sub_stories);
$story_class = $agg_types[$agg_key];
$conv = array(head($sub_stories)->getStoryData());
$new_story = newv($story_class, $conv);
$new_story->setAggregateStories($sub_stories);
$aggregated_stories[] = $new_story;
}
// Combine the aggregate stories back into the list of stories.
$stories = array_merge($stories, $aggregated_stories);
$stories = mpull($stories, null, 'getChronologicalKey');
krsort($stories);
$this->parsedStories = $stories;
return $stories;
}
public function buildView() {
$stories = $this->parseStories();
$null_view = new AphrontNullView();
foreach ($stories as $story) {
try {
$view = $story->renderView();
} catch (Exception $ex) {
// TODO: Render a nice debuggable notice instead?
continue;
}
+
+ $view->setShowTimestamp($this->getShowTimestamps());
+
$null_view->appendChild($view->renderNotification($this->user));
}
return $null_view;
}
public function buildDict() {
$stories = $this->parseStories();
$dict = array();
$viewer = $this->user;
$desktop_key = PhabricatorDesktopNotificationsSetting::SETTINGKEY;
$desktop_enabled = $viewer->getUserSetting($desktop_key);
foreach ($stories as $story) {
if ($story instanceof PhabricatorApplicationTransactionFeedStory) {
$dict[] = array(
'desktopReady' => $desktop_enabled,
'title' => $story->renderText(),
'body' => $story->renderTextBody(),
'href' => $story->getURI(),
'icon' => $story->getImageURI(),
);
} else if ($story instanceof PhabricatorNotificationTestFeedStory) {
$dict[] = array(
'desktopReady' => $desktop_enabled,
'title' => pht('Test Notification'),
'body' => $story->renderText(),
'href' => null,
'icon' => PhabricatorUser::getDefaultProfileImageURI(),
);
} else {
$dict[] = array(
'desktopReady' => false,
'title' => null,
'body' => null,
'href' => null,
'icon' => null,
);
}
}
return $dict;
}
}
diff --git a/src/applications/notification/controller/PhabricatorNotificationIndividualController.php b/src/applications/notification/controller/PhabricatorNotificationIndividualController.php
index bfaebaf606..41dade2747 100644
--- a/src/applications/notification/controller/PhabricatorNotificationIndividualController.php
+++ b/src/applications/notification/controller/PhabricatorNotificationIndividualController.php
@@ -1,61 +1,62 @@
<?php
final class PhabricatorNotificationIndividualController
extends PhabricatorNotificationController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$stories = id(new PhabricatorNotificationQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->withKeys(array($request->getStr('key')))
->execute();
if (!$stories) {
return $this->buildEmptyResponse();
}
$story = head($stories);
if ($story->getAuthorPHID() === $viewer->getPHID()) {
// Don't show the user individual notifications about their own
// actions. Primarily, this stops pages from showing notifications
// immediately after you click "Submit" on a comment form if the
// notification server returns faster than the web server.
// TODO: It would be nice to retain the "page updated" bubble on copies
// of the page that are open in other tabs, but there isn't an obvious
// way to do this easily.
return $this->buildEmptyResponse();
}
$builder = id(new PhabricatorNotificationBuilder(array($story)))
- ->setUser($viewer);
+ ->setUser($viewer)
+ ->setShowTimestamps(false);
$content = $builder->buildView()->render();
$dict = $builder->buildDict();
$data = $dict[0];
$response = array(
'pertinent' => true,
'primaryObjectPHID' => $story->getPrimaryObjectPHID(),
'desktopReady' => $data['desktopReady'],
'href' => $data['href'],
'icon' => $data['icon'],
'title' => $data['title'],
'body' => $data['body'],
'content' => hsprintf('%s', $content),
);
return id(new AphrontAjaxResponse())->setContent($response);
}
private function buildEmptyResponse() {
return id(new AphrontAjaxResponse())->setContent(
array(
'pertinent' => false,
));
}
}
diff --git a/src/view/phui/PHUIFeedStoryView.php b/src/view/phui/PHUIFeedStoryView.php
index 8267fa0197..fbba4a3f46 100644
--- a/src/view/phui/PHUIFeedStoryView.php
+++ b/src/view/phui/PHUIFeedStoryView.php
@@ -1,297 +1,312 @@
<?php
final class PHUIFeedStoryView extends AphrontView {
private $title;
private $image;
private $imageHref;
private $appIcon;
private $phid;
private $epoch;
private $viewed;
private $href;
private $pontification = null;
private $tokenBar = array();
private $projects = array();
private $actions = array();
private $chronologicalKey;
private $tags;
private $authorIcon;
+ private $showTimestamp = true;
public function setTags($tags) {
$this->tags = $tags;
return $this;
}
public function getTags() {
return $this->tags;
}
public function setChronologicalKey($chronological_key) {
$this->chronologicalKey = $chronological_key;
return $this;
}
public function getChronologicalKey() {
return $this->chronologicalKey;
}
public function setTitle($title) {
$this->title = $title;
return $this;
}
public function getTitle() {
return $this->title;
}
public function setEpoch($epoch) {
$this->epoch = $epoch;
return $this;
}
public function setImage($image) {
$this->image = $image;
return $this;
}
public function getImage() {
return $this->image;
}
public function setImageHref($image_href) {
$this->imageHref = $image_href;
return $this;
}
public function setAppIcon($icon) {
$this->appIcon = $icon;
return $this;
}
public function setViewed($viewed) {
$this->viewed = $viewed;
return $this;
}
public function getViewed() {
return $this->viewed;
}
public function setHref($href) {
$this->href = $href;
return $this;
}
public function setAuthorIcon($author_icon) {
$this->authorIcon = $author_icon;
return $this;
}
public function getAuthorIcon() {
return $this->authorIcon;
}
public function setTokenBar(array $tokens) {
$this->tokenBar = $tokens;
return $this;
}
+ public function setShowTimestamp($show_timestamp) {
+ $this->showTimestamp = $show_timestamp;
+ return $this;
+ }
+
+ public function getShowTimestamp() {
+ return $this->showTimestamp;
+ }
+
public function addProject($project) {
$this->projects[] = $project;
return $this;
}
public function addAction(PHUIIconView $action) {
$this->actions[] = $action;
return $this;
}
public function setPontification($text, $title = null) {
if ($title) {
$title = phutil_tag('h3', array(), $title);
}
$copy = phutil_tag(
'div',
array(
'class' => 'phui-feed-story-bigtext-post',
),
array(
$title,
$text,
));
$this->appendChild($copy);
return $this;
}
public function getHref() {
return $this->href;
}
public function renderNotification($user) {
$classes = array(
'phabricator-notification',
);
if (!$this->viewed) {
$classes[] = 'phabricator-notification-unread';
}
- if ($this->epoch) {
- if ($user) {
- $foot = phabricator_datetime($this->epoch, $user);
- $foot = phutil_tag(
- 'span',
- array(
- 'class' => 'phabricator-notification-date',
- ),
- $foot);
+
+ if ($this->getShowTimestamp()) {
+ if ($this->epoch) {
+ if ($user) {
+ $foot = phabricator_datetime($this->epoch, $user);
+ $foot = phutil_tag(
+ 'span',
+ array(
+ 'class' => 'phabricator-notification-date',
+ ),
+ $foot);
+ } else {
+ $foot = null;
+ }
} else {
- $foot = null;
+ $foot = pht('No time specified.');
}
} else {
- $foot = pht('No time specified.');
+ $foot = null;
}
return javelin_tag(
'div',
array(
'class' => implode(' ', $classes),
'sigil' => 'notification',
'meta' => array(
'href' => $this->getHref(),
),
),
array($this->title, $foot));
}
public function render() {
require_celerity_resource('phui-feed-story-css');
Javelin::initBehavior('phui-hovercards');
$body = null;
$foot = null;
$actor = new PHUIIconView();
$actor->addClass('phui-feed-story-actor');
$author_icon = $this->getAuthorIcon();
if ($this->image) {
$actor->addClass('phui-feed-story-actor-image');
$actor->setImage($this->image);
} else if ($author_icon) {
$actor->addClass('phui-feed-story-actor-icon');
$actor->setIcon($author_icon);
}
if ($this->imageHref) {
$actor->setHref($this->imageHref);
}
if ($this->epoch) {
// TODO: This is really bad; when rendering through Conduit and via
// renderText() we don't have a user.
if ($this->hasViewer()) {
$foot = phabricator_datetime($this->epoch, $this->getViewer());
} else {
$foot = null;
}
} else {
$foot = pht('No time specified.');
}
if ($this->chronologicalKey) {
$foot = phutil_tag(
'a',
array(
'href' => '/feed/'.$this->chronologicalKey.'/',
),
$foot);
}
$icon = null;
if ($this->appIcon) {
$icon = id(new PHUIIconView())
->setIcon($this->appIcon);
}
$action_list = array();
$icons = null;
foreach ($this->actions as $action) {
$action_list[] = phutil_tag(
'li',
array(
'class' => 'phui-feed-story-action-item',
),
$action);
}
if (!empty($action_list)) {
$icons = phutil_tag(
'ul',
array(
'class' => 'phui-feed-story-action-list',
),
$action_list);
}
$head = phutil_tag(
'div',
array(
'class' => 'phui-feed-story-head',
),
array(
$actor,
nonempty($this->title, pht('Untitled Story')),
$icons,
));
if (!empty($this->tokenBar)) {
$tokenview = phutil_tag(
'div',
array(
'class' => 'phui-feed-token-bar',
),
$this->tokenBar);
$this->appendChild($tokenview);
}
$body_content = $this->renderChildren();
if ($body_content) {
$body = phutil_tag(
'div',
array(
'class' => 'phui-feed-story-body phabricator-remarkup',
),
$body_content);
}
$tags = null;
if ($this->tags) {
$tags = array(
" \xC2\xB7 ",
$this->tags,
);
}
$foot = phutil_tag(
'div',
array(
'class' => 'phui-feed-story-foot',
),
array(
$icon,
$foot,
$tags,
));
$classes = array('phui-feed-story');
return id(new PHUIBoxView())
->addClass(implode(' ', $classes))
->setBorder(true)
->addMargin(PHUI::MARGIN_MEDIUM_BOTTOM)
->appendChild(array($head, $body, $foot));
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jul 2, 4:21 AM (1 d, 16 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
164672
Default Alt Text
(15 KB)

Event Timeline