Path: blob/master/src/applications/notification/builder/PhabricatorNotificationBuilder.php
12256 views
<?php12final class PhabricatorNotificationBuilder extends Phobject {34private $stories;5private $parsedStories;6private $user = null;7private $showTimestamps = true;89public function __construct(array $stories) {10assert_instances_of($stories, 'PhabricatorFeedStory');11$this->stories = $stories;12}1314public function setUser($user) {15$this->user = $user;16return $this;17}1819public function setShowTimestamps($show_timestamps) {20$this->showTimestamps = $show_timestamps;21return $this;22}2324public function getShowTimestamps() {25return $this->showTimestamps;26}2728private function parseStories() {2930if ($this->parsedStories) {31return $this->parsedStories;32}3334$stories = $this->stories;35$stories = mpull($stories, null, 'getChronologicalKey');3637// Aggregate notifications. Generally, we can aggregate notifications only38// by object, e.g. "a updated T123" and "b updated T123" can become39// "a and b updated T123", but we can't combine "a updated T123" and40// "a updated T234" into "a updated T123 and T234" because there would be41// nowhere sensible for the notification to link to, and no reasonable way42// to unambiguously clear it.4344// Build up a map of all the possible aggregations.4546$chronokey_map = array();47$aggregation_map = array();48$agg_types = array();49foreach ($stories as $chronokey => $story) {50$chronokey_map[$chronokey] = $story->getNotificationAggregations();51foreach ($chronokey_map[$chronokey] as $key => $type) {52$agg_types[$key] = $type;53$aggregation_map[$key]['keys'][$chronokey] = true;54}55}5657// Repeatedly select the largest available aggregation until none remain.5859$aggregated_stories = array();60while ($aggregation_map) {6162// Count the size of each aggregation, removing any which will consume63// fewer than 2 stories.6465foreach ($aggregation_map as $key => $dict) {66$size = count($dict['keys']);67if ($size > 1) {68$aggregation_map[$key]['size'] = $size;69} else {70unset($aggregation_map[$key]);71}72}7374// If we're out of aggregations, break out.7576if (!$aggregation_map) {77break;78}7980// Select the aggregation we're going to make, and remove it from the81// map.8283$aggregation_map = isort($aggregation_map, 'size');84$agg_info = idx(last($aggregation_map), 'keys');85$agg_key = last_key($aggregation_map);86unset($aggregation_map[$agg_key]);8788// Select all the stories it aggregates, and remove them from the master89// list of stories and from all other possible aggregations.9091$sub_stories = array();92foreach ($agg_info as $chronokey => $ignored) {93$sub_stories[$chronokey] = $stories[$chronokey];94unset($stories[$chronokey]);95foreach ($chronokey_map[$chronokey] as $key => $type) {96unset($aggregation_map[$key]['keys'][$chronokey]);97}98unset($chronokey_map[$chronokey]);99}100101// Build the aggregate story.102103krsort($sub_stories);104$story_class = $agg_types[$agg_key];105$conv = array(head($sub_stories)->getStoryData());106107$new_story = newv($story_class, $conv);108$new_story->setAggregateStories($sub_stories);109$aggregated_stories[] = $new_story;110}111112// Combine the aggregate stories back into the list of stories.113114$stories = array_merge($stories, $aggregated_stories);115$stories = mpull($stories, null, 'getChronologicalKey');116krsort($stories);117118$this->parsedStories = $stories;119return $stories;120}121122public function buildView() {123$stories = $this->parseStories();124$null_view = new AphrontNullView();125126foreach ($stories as $story) {127try {128$view = $story->renderView();129} catch (Exception $ex) {130// TODO: Render a nice debuggable notice instead?131continue;132}133134$view->setShowTimestamp($this->getShowTimestamps());135136$null_view->appendChild($view->renderNotification($this->user));137}138139return $null_view;140}141142public function buildDict() {143$stories = $this->parseStories();144$dict = array();145146$viewer = $this->user;147$key = PhabricatorNotificationsSetting::SETTINGKEY;148$setting = $viewer->getUserSetting($key);149$desktop_ready = PhabricatorNotificationsSetting::desktopReady($setting);150$web_ready = PhabricatorNotificationsSetting::webReady($setting);151152foreach ($stories as $story) {153if ($story instanceof PhabricatorApplicationTransactionFeedStory) {154$dict[] = array(155'showAnyNotification' => $web_ready,156'showDesktopNotification' => $desktop_ready,157'title' => $story->renderText(),158'body' => $story->renderTextBody(),159'href' => $story->getURI(),160'icon' => $story->getImageURI(),161);162} else {163$dict[] = array(164'showWebNotification' => false,165'showDesktopNotification' => false,166'title' => null,167'body' => null,168'href' => null,169'icon' => null,170);171}172}173174return $dict;175}176}177178179