3 final class PHUICalendarDayView
extends AphrontView
{
12 private $events = array();
14 private $allDayEvents = array();
16 public function addEvent(AphrontCalendarEventView
$event) {
17 $this->events
[] = $event;
21 public function setBrowseURI($browse_uri) {
22 $this->browseURI
= $browse_uri;
25 private function getBrowseURI() {
26 return $this->browseURI
;
29 public function setQuery($query) {
30 $this->query
= $query;
33 private function getQuery() {
37 public function __construct(
44 $this->rangeStart
= $range_start;
45 $this->rangeEnd
= $range_end;
48 $this->month
= $month;
52 public function render() {
53 require_celerity_resource('phui-calendar-day-css');
55 $viewer = $this->getUser();
57 $hours = $this->getHoursOfDay();
59 $js_today_events = array();
61 foreach ($hours as $hour) {
63 'hour' => $hour->format('G'),
64 'displayTime' => phabricator_time($hour->format('U'), $viewer),
68 $first_event_hour = null;
69 $js_today_all_day_events = array();
70 $all_day_events = $this->getAllDayEvents();
72 $day_start = $this->getDateTime();
73 $day_end = id(clone $day_start)->modify('+1 day');
75 $day_start_epoch = $day_start->format('U');
76 $day_end_epoch = $day_end->format('U') - 1;
78 foreach ($all_day_events as $all_day_event) {
79 $all_day_start = $all_day_event->getEpochStart();
80 $all_day_end = $all_day_event->getEpochEnd();
82 if ($all_day_start < $day_end_epoch && $all_day_end > $day_start_epoch) {
83 $js_today_all_day_events[] = array(
84 'name' => $all_day_event->getName(),
85 'id' => $all_day_event->getEventID(),
86 'viewerIsInvited' => $all_day_event->getViewerIsInvited(),
87 'uri' => $all_day_event->getURI(),
88 'displayIcon' => $all_day_event->getIcon(),
89 'displayIconColor' => $all_day_event->getIconColor(),
94 $this->events
= msort($this->events
, 'getEpochStart');
95 $first_event_hour = $this->getDateTime()->setTime(8, 0, 0);
96 $midnight = $this->getDateTime()->setTime(0, 0, 0);
98 foreach ($this->events
as $event) {
99 if ($event->getIsAllDay()) {
102 if ($event->getEpochStart() <= $day_end_epoch &&
103 $event->getEpochEnd() > $day_start_epoch) {
105 if ($event->getEpochStart() < $midnight->format('U') &&
106 $event->getEpochEnd() > $midnight->format('U')) {
107 $first_event_hour = clone $midnight;
110 if ($event->getEpochStart() < $first_event_hour->format('U') &&
111 $event->getEpochStart() > $midnight->format('U')) {
112 $first_event_hour = PhabricatorTime
::getDateTimeFromEpoch(
113 $event->getEpochStart(),
115 $first_event_hour->setTime($first_event_hour->format('h'), 0, 0);
118 $event_start = max($event->getEpochStart(), $day_start_epoch);
119 $event_end = min($event->getEpochEnd(), $day_end_epoch);
121 $day_duration = ($day_end_epoch - $first_event_hour->format('U')) / 60;
123 $top = (($event_start - $first_event_hour->format('U'))
124 / ($day_end_epoch - $first_event_hour->format('U')))
128 $height = (($event_end - $event_start)
129 / ($day_end_epoch - $first_event_hour->format('U')))
131 $height = min($day_duration, $height);
133 $js_today_events[] = array(
134 'eventStartEpoch' => $event->getEpochStart(),
135 'eventEndEpoch' => $event->getEpochEnd(),
136 'eventName' => $event->getName(),
137 'eventID' => $event->getEventID(),
138 'viewerIsInvited' => $event->getViewerIsInvited(),
139 'uri' => $event->getURI(),
143 'height' => $height.'px',
144 'canEdit' => $event->getCanEdit(),
145 'displayIcon' => $event->getIcon(),
146 'displayIconColor' => $event->getIconColor(),
151 $header = $this->renderDayViewHeader();
152 $sidebar = id(new PHUICalendarWeekView())
153 ->setViewer($this->getViewer())
154 ->setEvents($this->events
)
155 ->setDateTime($this->getDateTime())
157 $warnings = $this->getQueryRangeWarning();
159 $table_id = celerity_generate_unique_node_id();
161 $table_wrapper = phutil_tag(
168 Javelin
::initBehavior(
171 'year' => $first_event_hour->format('Y'),
172 'month' => $first_event_hour->format('m'),
173 'day' => $first_event_hour->format('d'),
174 'query' => $this->getQuery(),
175 'allDayEvents' => $js_today_all_day_events,
176 'todayEvents' => $js_today_events,
177 'hours' => $js_hours,
178 'firstEventHour' => $first_event_hour->format('G'),
179 'firstEventHourEpoch' => $first_event_hour->format('U'),
180 'tableID' => $table_id,
183 $table_box = id(new PHUIObjectBoxView())
185 ->appendChild($table_wrapper)
186 ->setFormErrors($warnings)
189 $layout = id(new AphrontMultiColumnView())
190 ->addColumn($sidebar, 'third phui-day-view-upcoming')
191 ->addColumn($table_box, 'thirds phui-day-view-column')
192 ->setFluidLayout(true);
194 $layout = id(new PHUIBoxView())
195 ->appendChild($layout)
196 ->addClass('phui-calendar-box');
201 private function getAllDayEvents() {
202 $all_day_events = array();
204 foreach ($this->events
as $event) {
205 if ($event->getIsAllDay()) {
206 $all_day_events[] = $event;
210 $all_day_events = array_values(msort($all_day_events, 'getEpochStart'));
211 return $all_day_events;
214 private function getQueryRangeWarning() {
217 $range_start_epoch = null;
218 $range_end_epoch = null;
220 if ($this->rangeStart
) {
221 $range_start_epoch = $this->rangeStart
->getEpoch();
223 if ($this->rangeEnd
) {
224 $range_end_epoch = $this->rangeEnd
->getEpoch();
227 $day_start = $this->getDateTime();
228 $day_end = id(clone $day_start)->modify('+1 day');
230 $day_start = $day_start->format('U');
231 $day_end = $day_end->format('U') - 1;
233 if (($range_start_epoch != null &&
234 $range_start_epoch < $day_end &&
235 $range_start_epoch > $day_start) ||
236 ($range_end_epoch != null &&
237 $range_end_epoch < $day_end &&
238 $range_end_epoch > $day_start)) {
239 $errors[] = pht('Part of the day is out of range');
242 if (($range_end_epoch != null &&
243 $range_end_epoch < $day_start) ||
244 ($range_start_epoch != null &&
245 $range_start_epoch > $day_end)) {
246 $errors[] = pht('Day is out of query range');
251 private function renderDayViewHeader() {
253 $uri = $this->getBrowseURI();
255 list($prev_year, $prev_month, $prev_day) = $this->getPrevDay();
256 $prev_uri = $uri.$prev_year.'/'.$prev_month.'/'.$prev_day.'/';
258 list($next_year, $next_month, $next_day) = $this->getNextDay();
259 $next_uri = $uri.$next_year.'/'.$next_month.'/'.$next_day.'/';
261 $button_bar = new PHUIButtonBarView();
263 $left_icon = id(new PHUIIconView())
264 ->setIcon('fa-chevron-left bluegrey');
265 $left = id(new PHUIButtonView())
267 ->setColor(PHUIButtonView
::GREY
)
269 ->setTitle(pht('Previous Day'))
270 ->setIcon($left_icon);
272 $right_icon = id(new PHUIIconView())
273 ->setIcon('fa-chevron-right bluegrey');
274 $right = id(new PHUIButtonView())
276 ->setColor(PHUIButtonView
::GREY
)
278 ->setTitle(pht('Next Day'))
279 ->setIcon($right_icon);
281 $button_bar->addButton($left);
282 $button_bar->addButton($right);
286 $display_day = $this->getDateTime();
287 $header_text = $display_day->format('l, F j, Y');
289 $header = id(new PHUIHeaderView())
290 ->setHeader($header_text);
293 $header->setButtonBar($button_bar);
299 private function updateEventsFromCluster($cluster, $hourly_events) {
300 $cluster_size = count($cluster);
302 foreach ($cluster as $cluster_member) {
303 $event_id = $cluster_member->getEventID();
304 $offset = (($n / $cluster_size) * 100).'%';
305 $width = ((1 / $cluster_size) * 100).'%';
307 if (isset($hourly_events[$event_id])) {
308 $hourly_events[$event_id]['offset'] = $offset;
309 $hourly_events[$event_id]['width'] = $width;
314 return $hourly_events;
317 // returns DateTime of each hour in the day
318 private function getHoursOfDay() {
319 $included_datetimes = array();
321 $day_datetime = $this->getDateTime();
322 $day_epoch = $day_datetime->format('U');
324 $day_datetime->modify('+1 day');
325 $next_day_epoch = $day_datetime->format('U');
327 $included_time = $day_epoch;
328 $included_datetime = $this->getDateTime();
330 while ($included_time < $next_day_epoch) {
331 $included_datetimes[] = clone $included_datetime;
333 $included_datetime->modify('+1 hour');
334 $included_time = $included_datetime->format('U');
337 return $included_datetimes;
340 private function getPrevDay() {
341 $prev = $this->getDateTime();
342 $prev->modify('-1 day');
350 private function getNextDay() {
351 $next = $this->getDateTime();
352 $next->modify('+1 day');
360 private function getDateTime() {
361 $user = $this->getViewer();
362 $timezone = new DateTimeZone($user->getTimezoneIdentifier());
365 $month = $this->month
;
368 $date = new DateTime("{$year}-{$month}-{$day} ", $timezone);