user = $user; return $this; } public function addEvent(AphrontCalendarEventView $event) { $this->events[] = $event; return $this; } public function setHolidays(array $holidays) { assert_instances_of($holidays, 'PhabricatorCalendarHoliday'); $this->holidays = mpull($holidays, null, 'getDay'); return $this; } public function __construct($month, $year) { $this->month = $month; $this->year = $year; } public function render() { if (empty($this->user)) { throw new Exception("Call setUser() before render()!"); } $events = msort($this->events, 'getEpochStart'); $days = $this->getDatesInMonth(); require_celerity_resource('aphront-calendar-view-css'); $first = reset($days); $empty = $first->format('w'); $markup = array(); $empty_box = '
'. '
'; for ($ii = 0; $ii < $empty; $ii++) { $markup[] = $empty_box; } foreach ($days as $day) { $holiday = idx($this->holidays, $day->format('Y-m-d')); $class = 'aphront-calendar-day'; $weekday = $day->format('w'); if ($holiday || $weekday == 0 || $weekday == 6) { $class .= ' aphront-calendar-not-work-day'; } $day->setTime(0, 0, 0); $epoch_start = $day->format('U'); $day->setTime(23, 59, 59); $epoch_end = $day->format('U'); $show_events = array(); foreach ($events as $event) { if ($event->getEpochStart() > $epoch_end) { // This list is sorted, so we can stop looking. break; } if ($event->getEpochStart() <= $epoch_end && $event->getEpochEnd() >= $epoch_start) { $show_events[] = $this->renderEvent( $event, $day, $epoch_start, $epoch_end); } } $holiday_markup = null; if ($holiday) { $holiday_markup = '
'. phutil_escape_html($holiday->getName()). '
'; } $markup[] = '
'. $holiday_markup. '
'. $day->format('j'). '
'. implode("\n", $show_events). '
'; } $table = array(); $rows = array_chunk($markup, 7); foreach ($rows as $row) { $table[] = ''; while (count($row) < 7) { $row[] = $empty_box; } foreach ($row as $cell) { $table[] = ''.$cell.''; } $table[] = ''; } $table = ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. ''. implode("\n", $table). '
'.$first->format('F Y').'
SunMonTueWedThuFriSat
'; return $table; } /** * Return a DateTime object representing the first moment in each day in the * month, according to the user's locale. * * @return list List of DateTimes, one for each day. */ private function getDatesInMonth() { $user = $this->user; $timezone = new DateTimeZone($user->getTimezoneIdentifier()); $month = $this->month; $year = $this->year; // Find the year and month numbers of the following month, so we can // determine when this month ends. $next_year = $year; $next_month = $month + 1; if ($next_month == 13) { $next_year = $year + 1; $next_month = 1; } $end_date = new DateTime("{$next_year}-{$next_month}-01", $timezone); $end_epoch = $end_date->format('U'); $days = array(); for ($day = 1; $day <= 31; $day++) { $day_date = new DateTime("{$year}-{$month}-{$day}", $timezone); $day_epoch = $day_date->format('U'); if ($day_epoch >= $end_epoch) { break; } else { $days[] = $day_date; } } return $days; } private function renderEvent( AphrontCalendarEventView $event, DateTime $day, $epoch_start, $epoch_end) { $user = $this->user; $event_start = $event->getEpochStart(); $event_end = $event->getEpochEnd(); $classes = array(); $when = array(); $classes[] = 'aphront-calendar-event'; if ($event_start < $epoch_start) { $classes[] = 'aphront-calendar-event-continues-before'; $when[] = 'Started '.phabricator_datetime($event_start, $user); } else { $when[] = 'Starts at '.phabricator_time($event_start, $user); } if ($event_end > $epoch_end) { $classes[] = 'aphront-calendar-event-continues-after'; $when[] = 'Ends '.phabricator_datetime($event_start, $user); } else { $when[] = 'Ends at '.phabricator_time($event_end, $user); } Javelin::initBehavior('phabricator-tooltips'); $info = $event->getName(); if ($event->getDescription()) { $info .= "\n\n".$event->getDescription(); } $text_div = javelin_render_tag( 'div', array( 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => $info."\n\n".implode("\n", $when), 'size' => 240, ), 'class' => 'aphront-calendar-event-text', ), phutil_escape_html(phutil_utf8_shorten($event->getName(), 32))); return javelin_render_tag( 'div', array( 'class' => implode(' ', $classes), ), $text_div); } }