Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2896262
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Advanced/Developer...
View Handle
View Hovercard
Size
21 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/src/view/phui/calendar/PHUICalendarDayView.php b/src/view/phui/calendar/PHUICalendarDayView.php
index cbf1023f61..7d32866cea 100644
--- a/src/view/phui/calendar/PHUICalendarDayView.php
+++ b/src/view/phui/calendar/PHUICalendarDayView.php
@@ -1,579 +1,594 @@
<?php
final class PHUICalendarDayView extends AphrontView {
private $rangeStart;
private $rangeEnd;
private $day;
private $month;
private $year;
private $browseURI;
private $events = array();
private $todayEvents = array();
private $jsTodayEvents = array();
private $allDayEvents = array();
public function addEvent(AphrontCalendarEventView $event) {
$this->events[] = $event;
return $this;
}
public function setBrowseURI($browse_uri) {
$this->browseURI = $browse_uri;
return $this;
}
private function getBrowseURI() {
return $this->browseURI;
}
public function __construct(
$range_start,
$range_end,
$year,
$month,
$day = null) {
$this->rangeStart = $range_start;
$this->rangeEnd = $range_end;
$this->day = $day;
$this->month = $month;
$this->year = $year;
}
public function render() {
require_celerity_resource('phui-calendar-day-css');
$hours = $this->getHoursOfDay();
+ $js_hours = array();
+
+ foreach ($hours as $hour) {
+ $js_hours[] = array(
+ 'hour' => $hour->format('G'),
+ 'hour_meridian' => $hour->format('g A'),
+ );
+ }
+
$js_hourly_events = array();
$hourly_events = array();
$first_event_hour = null;
$all_day_events = $this->getAllDayEvents();
$today_all_day_events = array();
$day_start = $this->getDateTime();
$day_end = id(clone $day_start)->modify('+1 day');
$day_start_epoch = $day_start->format('U');
$day_end_epoch = $day_end->format('U') - 1;
foreach ($all_day_events as $all_day_event) {
$all_day_start = $all_day_event->getEpochStart();
$all_day_end = $all_day_event->getEpochEnd();
if ($all_day_start < $day_end_epoch && $all_day_end > $day_start_epoch) {
$today_all_day_events[] = $all_day_event;
}
}
foreach ($hours as $hour) {
$current_hour_events = array();
$hour_start = $hour->format('U');
$hour_end = id(clone $hour)->modify('+1 hour')->format('U');
foreach ($this->events as $event) {
if ($event->getIsAllDay()) {
continue;
}
if (($hour == $day_start &&
$event->getEpochStart() <= $hour_start &&
$event->getEpochEnd() > $day_start_epoch) ||
($event->getEpochStart() >= $hour_start
&& $event->getEpochStart() < $hour_end)) {
$current_hour_events[] = $event;
$this->todayEvents[] = $event;
$this->jsTodayEvents[] = array(
'eventStartEpoch' => $event->getEpochStart(),
'eventEndEpoch' => $event->getEpochEnd(),
'eventName' => $event->getName(),
'eventID' => $event->getEventID(),
+ 'viewerIsInvited' => $event->getViewerIsInvited(),
+ 'uri' => $event->getURI(),
);
}
}
foreach ($current_hour_events as $event) {
$day_start_epoch = $this->getDateTime()->format('U');
$event_start = max($event->getEpochStart(), $day_start_epoch);
$event_end = min($event->getEpochEnd(), $day_end_epoch);
$top = (($event_start - $hour_start) / ($hour_end - $hour_start))
* 100;
$top = max(0, $top);
$height = (($event_end - $event_start) / ($hour_end - $hour_start))
* 100;
$height = min(2400, $height);
if ($first_event_hour === null) {
$first_event_hour = $hour;
}
$js_hourly_events[] = array(
'eventStartEpoch' => $event->getEpochStart(),
'eventEndEpoch' => $event->getEpochEnd(),
'eventName' => $event->getName(),
'eventID' => $event->getEventID(),
- 'hour' => $hour,
+ 'viewerIsInvited' => $event->getViewerIsInvited(),
+ 'uri' => $event->getURI(),
+ 'hour' => $hour->format('G'),
'offset' => '0',
'width' => '100%',
'top' => $top.'%',
'height' => $height.'%',
);
$hourly_events[$event->getEventID()] = array(
'hour' => $hour,
'event' => $event,
'offset' => '0',
'width' => '100%',
'top' => $top.'%',
'height' => $height.'%',
);
}
}
$clusters = $this->findTodayClusters();
foreach ($clusters as $cluster) {
$hourly_events = $this->updateEventsFromCluster(
$cluster,
$hourly_events);
}
$rows = array();
foreach ($hours as $hour) {
$early_hours = array(8);
if ($first_event_hour) {
$early_hours[] = $first_event_hour->format('G');
}
if ($hour->format('G') < min($early_hours)) {
continue;
}
$drawn_hourly_events = array();
$cell_time = phutil_tag(
'td',
array('class' => 'phui-calendar-day-hour'),
$hour->format('g A'));
foreach ($hourly_events as $hourly_event) {
if ($hourly_event['hour'] == $hour) {
$drawn_hourly_events[] = $this->drawEvent(
$hourly_event['event'],
$hourly_event['offset'],
$hourly_event['width'],
$hourly_event['top'],
$hourly_event['height']);
}
}
$cell_event = phutil_tag(
'td',
array('class' => 'phui-calendar-day-events'),
$drawn_hourly_events);
$row = phutil_tag(
'tr',
array(),
array($cell_time, $cell_event));
$rows[] = $row;
}
$table = phutil_tag(
'table',
array('class' => 'phui-calendar-day-view'),
$rows);
$all_day_event_box = new PHUIBoxView();
foreach ($today_all_day_events as $all_day_event) {
$all_day_event_box->appendChild(
$this->drawAllDayEvent($all_day_event));
}
$header = $this->renderDayViewHeader();
$sidebar = $this->renderSidebar();
$warnings = $this->getQueryRangeWarning();
Javelin::initBehavior(
'day-view',
array(
'todayEvents' => $this->jsTodayEvents,
'hourlyEvents' => $js_hourly_events,
+ 'hours' => $js_hours,
+ 'firstEventHour' => $first_event_hour->format('G'),
));
$table_box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($all_day_event_box)
->appendChild($table)
->setFormErrors($warnings)
->setFlush(true);
$layout = id(new AphrontMultiColumnView())
->addColumn($sidebar, 'third')
->addColumn($table_box, 'thirds phui-day-view-column')
->setFluidLayout(true)
->setGutter(AphrontMultiColumnView::GUTTER_MEDIUM);
return phutil_tag(
'div',
array(
'class' => 'ml',
),
$layout);
}
private function getAllDayEvents() {
$all_day_events = array();
foreach ($this->events as $event) {
if ($event->getIsAllDay()) {
$all_day_events[] = $event;
}
}
$all_day_events = array_values(msort($all_day_events, 'getEpochStart'));
return $all_day_events;
}
private function getQueryRangeWarning() {
$errors = array();
$range_start_epoch = $this->rangeStart->getEpoch();
$range_end_epoch = $this->rangeEnd->getEpoch();
$day_start = $this->getDateTime();
$day_end = id(clone $day_start)->modify('+1 day');
$day_start = $day_start->format('U');
$day_end = $day_end->format('U') - 1;
if (($range_start_epoch != null &&
$range_start_epoch < $day_end &&
$range_start_epoch > $day_start) ||
($range_end_epoch != null &&
$range_end_epoch < $day_end &&
$range_end_epoch > $day_start)) {
$errors[] = pht('Part of the day is out of range');
}
if (($this->rangeEnd->getEpoch() != null &&
$this->rangeEnd->getEpoch() < $day_start) ||
($this->rangeStart->getEpoch() != null &&
$this->rangeStart->getEpoch() > $day_end)) {
$errors[] = pht('Day is out of query range');
}
return $errors;
}
private function renderSidebar() {
$this->events = msort($this->events, 'getEpochStart');
$week_of_boxes = $this->getWeekOfBoxes();
$filled_boxes = array();
foreach ($week_of_boxes as $day_box) {
$box_start = $day_box['start'];
$box_end = id(clone $box_start)->modify('+1 day');
$box_start = $box_start->format('U');
$box_end = $box_end->format('U');
$box_events = array();
foreach ($this->events as $event) {
$event_start = $event->getEpochStart();
$event_end = $event->getEpochEnd();
if ($event_start < $box_end && $event_end > $box_start) {
$box_events[] = $event;
}
}
$filled_boxes[] = $this->renderSidebarBox(
$box_events,
$day_box['title']);
}
return $filled_boxes;
}
private function renderSidebarBox($events, $title) {
$widget = id(new PHUICalendarWidgetView())
->addClass('calendar-day-view-sidebar');
$list = id(new PHUICalendarListView())
->setUser($this->user)
->setView('day');
if (count($events) == 0) {
$list->showBlankState(true);
} else {
$sorted_events = msort($events, 'getEpochStart');
foreach ($sorted_events as $event) {
$list->addEvent($event);
}
}
$widget
->setCalendarList($list)
->setHeader($title);
return $widget;
}
private function getWeekOfBoxes() {
$sidebar_day_boxes = array();
$display_start_day = $this->getDateTime();
$display_end_day = id(clone $display_start_day)->modify('+6 day');
$box_start_time = clone $display_start_day;
$today_time = PhabricatorTime::getTodayMidnightDateTime($this->user);
$tomorrow_time = clone $today_time;
$tomorrow_time->modify('+1 day');
while ($box_start_time <= $display_end_day) {
if ($box_start_time == $today_time) {
$title = pht('Today');
} else if ($box_start_time == $tomorrow_time) {
$title = pht('Tomorrow');
} else {
$title = $box_start_time->format('l');
}
$sidebar_day_boxes[] = array(
'title' => $title,
'start' => clone $box_start_time,
);
$box_start_time->modify('+1 day');
}
return $sidebar_day_boxes;
}
private function renderDayViewHeader() {
$button_bar = null;
$uri = $this->getBrowseURI();
if ($uri) {
list($prev_year, $prev_month, $prev_day) = $this->getPrevDay();
$prev_uri = $uri.$prev_year.'/'.$prev_month.'/'.$prev_day.'/';
list($next_year, $next_month, $next_day) = $this->getNextDay();
$next_uri = $uri.$next_year.'/'.$next_month.'/'.$next_day.'/';
$button_bar = new PHUIButtonBarView();
$left_icon = id(new PHUIIconView())
->setIconFont('fa-chevron-left bluegrey');
$left = id(new PHUIButtonView())
->setTag('a')
->setColor(PHUIButtonView::GREY)
->setHref($prev_uri)
->setTitle(pht('Previous Day'))
->setIcon($left_icon);
$right_icon = id(new PHUIIconView())
->setIconFont('fa-chevron-right bluegrey');
$right = id(new PHUIButtonView())
->setTag('a')
->setColor(PHUIButtonView::GREY)
->setHref($next_uri)
->setTitle(pht('Next Day'))
->setIcon($right_icon);
$button_bar->addButton($left);
$button_bar->addButton($right);
}
$display_day = $this->getDateTime();
$header_text = $display_day->format('l, F j, Y');
$header = id(new PHUIHeaderView())
->setHeader($header_text);
if ($button_bar) {
$header->setButtonBar($button_bar);
}
return $header;
}
private function updateEventsFromCluster($cluster, $hourly_events) {
$cluster_size = count($cluster);
$n = 0;
foreach ($cluster as $cluster_member) {
$event_id = $cluster_member->getEventID();
$offset = (($n / $cluster_size) * 100).'%';
$width = ((1 / $cluster_size) * 100).'%';
if (isset($hourly_events[$event_id])) {
$hourly_events[$event_id]['offset'] = $offset;
$hourly_events[$event_id]['width'] = $width;
}
$n++;
}
return $hourly_events;
}
private function drawAllDayEvent(AphrontCalendarEventView $event) {
$class = 'day-view-all-day';
if ($event->getViewerIsInvited()) {
$class = $class.' viewer-invited-day-event';
}
$name = phutil_tag(
'a',
array(
'class' => $class,
'href' => $event->getURI(),
),
$event->getName());
$all_day_label = phutil_tag(
'span',
array(
'class' => 'phui-calendar-all-day-label',
),
pht('All Day'));
$div = phutil_tag(
'div',
array(
'class' => 'phui-calendar-day-event',
),
array(
$all_day_label,
$name,
));
return $div;
}
private function drawEvent(
AphrontCalendarEventView $event,
$offset,
$width,
$top,
$height) {
$class = 'phui-calendar-day-event-link';
if ($event->getViewerIsInvited()) {
$class = $class.' viewer-invited-day-event';
}
$name = phutil_tag(
'a',
array(
'class' => $class,
'href' => $event->getURI(),
),
$event->getName());
$div = phutil_tag(
'div',
array(
'class' => 'phui-calendar-day-event',
'style' => 'left: '.$offset
.'; width: '.$width
.'; top: '.$top
.'; height: '.$height
.';',
),
$name);
return $div;
}
// returns DateTime of each hour in the day
private function getHoursOfDay() {
$included_datetimes = array();
$day_datetime = $this->getDateTime();
$day_epoch = $day_datetime->format('U');
$day_datetime->modify('+1 day');
$next_day_epoch = $day_datetime->format('U');
$included_time = $day_epoch;
$included_datetime = $this->getDateTime();
while ($included_time < $next_day_epoch) {
$included_datetimes[] = clone $included_datetime;
$included_datetime->modify('+1 hour');
$included_time = $included_datetime->format('U');
}
return $included_datetimes;
}
private function getPrevDay() {
$prev = $this->getDateTime();
$prev->modify('-1 day');
return array(
$prev->format('Y'),
$prev->format('m'),
$prev->format('d'),
);
}
private function getNextDay() {
$next = $this->getDateTime();
$next->modify('+1 day');
return array(
$next->format('Y'),
$next->format('m'),
$next->format('d'),
);
}
private function getDateTime() {
$user = $this->user;
$timezone = new DateTimeZone($user->getTimezoneIdentifier());
$day = $this->day;
$month = $this->month;
$year = $this->year;
$date = new DateTime("{$year}-{$month}-{$day} ", $timezone);
return $date;
}
private function findTodayClusters() {
$events = msort($this->todayEvents, 'getEpochStart');
$clusters = array();
foreach ($events as $event) {
$destination_cluster_key = null;
$event_start = $event->getEpochStart() - (30 * 60);
$event_end = $event->getEpochEnd() + (30 * 60);
foreach ($clusters as $key => $cluster) {
foreach ($cluster as $clustered_event) {
$compare_event_start = $clustered_event->getEpochStart();
$compare_event_end = $clustered_event->getEpochEnd();
if ($event_start < $compare_event_end
&& $event_end > $compare_event_start) {
$destination_cluster_key = $key;
break;
}
}
}
if ($destination_cluster_key !== null) {
$clusters[$destination_cluster_key][] = $event;
} else {
$next_cluster = array();
$next_cluster[] = $event;
$clusters[] = $next_cluster;
}
}
return $clusters;
}
}
diff --git a/webroot/rsrc/js/application/calendar/behavior-day-view.js b/webroot/rsrc/js/application/calendar/behavior-day-view.js
index bcd87912cc..f37714c04f 100644
--- a/webroot/rsrc/js/application/calendar/behavior-day-view.js
+++ b/webroot/rsrc/js/application/calendar/behavior-day-view.js
@@ -1,85 +1,182 @@
/**
* @provides javelin-behavior-day-view
*/
JX.behavior('day-view', function(config) {
+ var hours = config.hours;
+ var first_event_hour = config.firstEventHour;
var hourly_events = config.hourlyEvents;
var today_events = config.todayEvents;
function findTodayClusters() {
var events = today_events.sort(function(x, y){
return (x.eventStartEpoch - y.eventStartEpoch);
});
var clusters = [];
for (var i=0; i < events.length; i++) {
var today_event = events[i];
var destination_cluster_index = null;
var event_start = today_event.eventStartEpoch - (30*60);
var event_end = today_event.eventEndEpoch + (30*60);
for (var j=0; j < clusters.length; j++) {
var cluster = clusters[j];
for(var k=0; k < cluster.length; k++) {
var clustered_event = cluster[k];
var compare_event_start = clustered_event.eventStartEpoch;
var compare_event_end = clustered_event.eventEndEpoch;
if (event_start < compare_event_end &&
event_end > compare_event_start) {
destination_cluster_index = j;
break;
}
}
if (destination_cluster_index !== null) {
break;
}
}
if (destination_cluster_index !== null) {
clusters[destination_cluster_index].push(today_event);
destination_cluster_index = null;
} else {
var next_cluster = [];
next_cluster.push(today_event);
clusters.push(next_cluster);
}
}
return clusters;
}
function updateEventsFromCluster(cluster, hourly_events) {
var cluster_size = cluster.length;
var n = 0;
for(var i=0; i < cluster.length; i++) {
var cluster_member = cluster[i];
var event_id = cluster_member.eventID;
var offset = ((n / cluster_size) * 100) + '%';
var width = ((1 / cluster_size) * 100) + '%';
for (var j=0; j < hourly_events.length; j++) {
if (hourly_events[j].eventID == event_id) {
hourly_events[j]['offset'] = offset;
hourly_events[j]['width'] = width;
}
}
n++;
}
return hourly_events;
}
+ function drawEvent(
+ start,
+ end,
+ name,
+ viewerIsInvited,
+ uri,
+ id,
+ offset,
+ width,
+ top,
+ height) {
+
+ var link_class = 'phui-calendar-day-event-link';
+ if (viewerIsInvited) {
+ link_class = link_class + ' viewer-invited-day-event';
+ }
+
+ var name_link = JX.$N(
+ 'a',
+ {
+ className : link_class,
+ href: uri
+ },
+ name);
+
+ var div = JX.$N(
+ 'div',
+ {
+ className: 'phui-calendar-day-event',
+ style: {
+ left: offset,
+ width: width,
+ top: top,
+ height: height
+ }
+ },
+ name_link);
+
+ return div;
+ }
+
+ function drawRows() {
+ var rows = [];
+ var early_hours = [8];
+ if (first_event_hour) {
+ early_hours.push(first_event_hour);
+ }
+ var min_early_hour = Math.min(early_hours[0], early_hours[1]);
+
+
+ for(var i=0; i < hours.length; i++) {
+ if (hours[i]['hour'] < min_early_hour) {
+ continue;
+ }
+ var drawn_hourly_events = [];
+ var cell_time = JX.$N(
+ 'td',
+ {className: 'phui-calendar-day-hour'},
+ hours[i]['hour_meridian']);
+
+ for (var j=0; j < hourly_events.length; j++) {
+ if (hourly_events[j]['hour'] == hours[i]['hour']) {
+ drawn_hourly_events.push(
+ drawEvent(
+ hourly_events[j]['eventStartEpoch'],
+ hourly_events[j]['eventEndEpoch'],
+ hourly_events[j]['eventName'],
+ hourly_events[j]['eventID'],
+ hourly_events[j]['viewerIsInvited'],
+ hourly_events[j]['hour'],
+ hourly_events[j]['offset'],
+ hourly_events[j]['width'],
+ hourly_events[j]['top'],
+ hourly_events[j]['height']
+ )
+ );
+ }
+ }
+
+ var cell_event = JX.$N(
+ 'td',
+ {
+ className: 'phui-calendar-day-events'
+ },
+ drawn_hourly_events);
+ var row = JX.$N(
+ 'tr',
+ {},
+ [cell_time, cell_event]);
+ rows.push(row);
+ }
+ return rows;
+ }
+
var today_clusters = findTodayClusters();
for(var i=0; i < today_clusters.length; i++) {
hourly_events = updateEventsFromCluster(today_clusters[i], hourly_events);
}
+ var rows = drawRows();
});
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Jan 19 2025, 22:50 (6 w, 3 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
1129560
Default Alt Text
(21 KB)
Attached To
Mode
rP Phorge
Attached
Detach File
Event Timeline
Log In to Comment