diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php --- a/src/applications/calendar/application/PhabricatorCalendarApplication.php +++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php @@ -42,7 +42,8 @@ return array( '/E(?P[1-9]\d*)' => 'PhabricatorCalendarEventViewController', '/calendar/' => array( - '(?:query/(?P[^/]+)/(?:(?P\d+)/(?P\d+)/)?)?' + '(?:query/(?P[^/]+)/(?:(?P\d+)/'. + '(?P\d+)/)?(?:(?P\d+)/)?)?' => 'PhabricatorCalendarEventListController', 'event/' => array( 'create/' diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventListController.php b/src/applications/calendar/controller/PhabricatorCalendarEventListController.php --- a/src/applications/calendar/controller/PhabricatorCalendarEventListController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventListController.php @@ -10,10 +10,11 @@ public function handleRequest(AphrontRequest $request) { $year = $request->getURIData('year'); $month = $request->getURIData('month'); + $day = $request->getURIData('day'); $engine = new PhabricatorCalendarEventSearchEngine(); if ($month && $year) { - $engine->setCalendarYearAndMonth($year, $month); + $engine->setCalendarYearAndMonthAndDay($year, $month, $day); } $controller = id(new PhabricatorApplicationSearchController()) diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php --- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php @@ -57,7 +57,8 @@ $max_range = $this->getDateTo($saved)->getEpoch(); if ($saved->getParameter('display') == 'month') { - list($start_month, $start_year) = $this->getDisplayMonthAndYear($saved); + list($start_year, $start_month) = + $this->getDisplayYearAndMonthAndDay($saved); $start_day = 1; $end_year = ($start_month == 12) ? $start_year + 1 : $start_year; @@ -226,9 +227,10 @@ return $names; } - public function setCalendarYearAndMonth($year, $month) { + public function setCalendarYearAndMonthAndDay($year, $month, $day = null) { $this->calendarYear = $year; $this->calendarMonth = $month; + $this->calendarDay = $day; return $this; } @@ -309,7 +311,8 @@ $viewer = $this->requireViewer(); $now = time(); - list($start_month, $start_year) = $this->getDisplayMonthAndYear($query); + list($start_year, $start_month) = + $this->getDisplayYearAndMonthAndDay($query); $now_year = phabricator_format_local_time($now, $viewer, 'Y'); $now_month = phabricator_format_local_time($now, $viewer, 'm'); @@ -373,12 +376,12 @@ PhabricatorSavedQuery $query, array $handles) { $viewer = $this->requireViewer(); - list($start_month, $start_year, $start_day) = - $this->getDisplayMonthAndYearAndDay($query); + list($start_year, $start_month, $start_day) = + $this->getDisplayYearAndMonthAndDay($query); $day_view = new PHUICalendarDayView( - $start_month, $start_year, + $start_month, $start_day); $day_view->setUser($viewer); @@ -395,39 +398,19 @@ $day_view->addEvent($event); } + $day_view->setBrowseURI( + $this->getURI('query/'.$query->getQueryKey().'/')); + return $day_view; } - private function getDisplayMonthAndYear( + private function getDisplayYearAndMonthAndDay( PhabricatorSavedQuery $query) { $viewer = $this->requireViewer(); - - // get month/year from url if ($this->calendarYear && $this->calendarMonth) { $start_year = $this->calendarYear; $start_month = $this->calendarMonth; - } else { - $epoch = $this->getDateFrom($query)->getEpoch(); - if (!$epoch) { - $epoch = $this->getDateTo($query)->getEpoch(); - if (!$epoch) { - $epoch = time(); - } - } - $start_year = phabricator_format_local_time($epoch, $viewer, 'Y'); - $start_month = phabricator_format_local_time($epoch, $viewer, 'm'); - } - - return array($start_month, $start_year); - } - - private function getDisplayMonthAndYearAndDay( - PhabricatorSavedQuery $query) { - $viewer = $this->requireViewer(); - if ($this->calendarYear && $this->calendarMonth && $this->calendarDay) { - $start_year = $this->calendarYear; - $start_month = $this->calendarMonth; - $start_day = $this->calendarDay; + $start_day = $this->calendarDay ? $this->calendarDay : 1; } else { $epoch = $this->getDateFrom($query)->getEpoch(); if (!$epoch) { diff --git a/src/view/phui/calendar/PHUICalendarDayView.php b/src/view/phui/calendar/PHUICalendarDayView.php --- a/src/view/phui/calendar/PHUICalendarDayView.php +++ b/src/view/phui/calendar/PHUICalendarDayView.php @@ -5,6 +5,7 @@ private $day; private $month; private $year; + private $browseURI; private $events = array(); public function addEvent(AphrontCalendarDayEventView $event) { @@ -12,6 +13,14 @@ return $this; } + public function setBrowseURI($browse_uri) { + $this->browseURI = $browse_uri; + return $this; + } + private function getBrowseURI() { + return $this->browseURI; + } + public function __construct($year, $month, $day = null) { $this->day = $day; $this->month = $month; @@ -21,11 +30,6 @@ public function render() { require_celerity_resource('phui-calendar-day-css'); - $day_box = new PHUIObjectBoxView(); - $day_of_week = $this->getDayOfWeek(); - $header_text = $this->getDateTime()->format('F j, Y'); - $header_text = $day_of_week.', '.$header_text; - $day_box->setHeaderText($header_text); $hours = $this->getHoursOfDay(); $hourly_events = array(); $rows = array(); @@ -112,9 +116,64 @@ $rows, )); - $day_box->appendChild($table); + $header = $this->renderDayViewHeader(); + + $day_box = (new PHUIObjectBoxView()) + ->setHeader($header) + ->appendChild($table); + return $day_box; + } + private function renderDayViewHeader() { + $button_bar = null; + + // check for a browseURI, which means we need "fancy" prev / next UI + $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); + + } + + $day_of_week = $this->getDayOfWeek(); + $header_text = $this->getDateTime()->format('F j, Y'); + $header_text = $day_of_week.', '.$header_text; + + $header = id(new PHUIHeaderView()) + ->setHeader($header_text); + + if ($button_bar) { + $header->setButtonBar($button_bar); + } + + return $header; } private function updateEventsFromCluster($cluster, $hourly_events) { @@ -194,6 +253,92 @@ return $included_datetimes; } + private function getNumberOfDaysInMonth($month, $year) { + $user = $this->user; + $timezone = new DateTimeZone($user->getTimezoneIdentifier()); + + list($next_year, $next_month) = $this->getNextYearAndMonth($month, $year); + + $end_date = new DateTime("{$next_year}-{$next_month}-01", $timezone); + $end_epoch = $end_date->format('U'); + + $days = 0; + 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++; + } + } + + return $days; + } + + private function getPrevDay() { + $day = $this->day; + $month = $this->month; + $year = $this->year; + + $prev_year = $year; + $prev_month = $month; + $prev_day = $day - 1; + if ($prev_day == 0) { + $prev_month--; + if ($prev_month == 0) { + $prev_year--; + $prev_month = 12; + } + $prev_day = $this->getNumberOfDaysInMonth($prev_month, $prev_year); + } + + return array($prev_year, $prev_month, $prev_day); + } + + private function getNextDay() { + $day = $this->day; + $month = $this->month; + $year = $this->year; + + $next_year = $year; + $next_month = $month; + $next_day = $day + 1; + $days_in_month = $this->getNumberOfDaysInMonth($month, $year); + if ($next_day > $days_in_month) { + $next_day = 1; + $next_month++; + } + if ($next_month == 13) { + $next_year++; + $next_month = 1; + } + + return array($next_year, $next_month, $next_day); + } + + private function getNextYearAndMonth($month, $year) { + $next_year = $year; + $next_month = $month + 1; + if ($next_month == 13) { + $next_year = $year + 1; + $next_month = 1; + } + + return array($next_year, $next_month); + } + + private function getPrevYearAndMonth($month, $year) { + $prev_year = $year; + $prev_month = $month - 1; + if ($prev_month == 0) { + $prev_year = $year - 1; + $prev_month = 12; + } + + return array($prev_year, $prev_month); + } + private function getDateTime() { $user = $this->user;