diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 703a0067f6..06e401616b 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -94,7 +94,7 @@ return array( 'rsrc/css/application/ponder/feed.css' => 'e62615b6', 'rsrc/css/application/ponder/post.css' => 'ebab8a70', 'rsrc/css/application/ponder/vote.css' => '8ed6ed8b', - 'rsrc/css/application/profile/profile-view.css' => '3a7e04ca', + 'rsrc/css/application/profile/profile-view.css' => '9bdb9804', 'rsrc/css/application/projects/phabricator-object-list-view.css' => '1a1ea560', 'rsrc/css/application/projects/project-tag.css' => '095c9404', 'rsrc/css/application/releeph/releeph-branch.css' => 'b8821d2d', @@ -123,9 +123,12 @@ return array( 'rsrc/css/layout/phabricator-hovercard-view.css' => '67c12b16', 'rsrc/css/layout/phabricator-side-menu-view.css' => '503699d0', 'rsrc/css/layout/phabricator-source-code-view.css' => '62a99814', - 'rsrc/css/phui/phui-box.css' => '1a82a4ae', + 'rsrc/css/phui/calendar/phui-calendar-day.css' => 'de035c8a', + 'rsrc/css/phui/calendar/phui-calendar-list.css' => 'c1d0ca59', + 'rsrc/css/phui/calendar/phui-calendar-month.css' => '5e762971', + 'rsrc/css/phui/calendar/phui-calendar.css' => '5e1ad989', + 'rsrc/css/phui/phui-box.css' => 'a36cf3a5', 'rsrc/css/phui/phui-button.css' => '8784a966', - 'rsrc/css/phui/phui-calendar-month.css' => '3474d15a', 'rsrc/css/phui/phui-document.css' => '143b2ac8', 'rsrc/css/phui/phui-feed-story.css' => '3a59c2cf', 'rsrc/css/phui/phui-form-view.css' => '0efd3326', @@ -134,7 +137,7 @@ return array( 'rsrc/css/phui/phui-icon.css' => 'fcb145a7', 'rsrc/css/phui/phui-info-panel.css' => '27ea50a1', 'rsrc/css/phui/phui-list.css' => '2edb76cf', - 'rsrc/css/phui/phui-object-box.css' => '95767d08', + 'rsrc/css/phui/phui-object-box.css' => 'ce92d8ec', 'rsrc/css/phui/phui-object-item-list-view.css' => 'eb579d6c', 'rsrc/css/phui/phui-pinboard-view.css' => '4b346c2a', 'rsrc/css/phui/phui-property-list-view.css' => 'dbf53b12', @@ -702,7 +705,7 @@ return array( 'phabricator-object-selector-css' => '029a133d', 'phabricator-phtize' => 'd254d646', 'phabricator-prefab' => '0326e5d0', - 'phabricator-profile-css' => '3a7e04ca', + 'phabricator-profile-css' => '9bdb9804', 'phabricator-project-tag-css' => '095c9404', 'phabricator-remarkup-css' => 'ca7f2265', 'phabricator-search-results-css' => 'f240504c', @@ -735,9 +738,12 @@ return array( 'phortune-credit-card-form-css' => 'b25b4beb', 'phrequent-css' => 'ffc185ad', 'phriction-document-css' => 'b0309d8e', - 'phui-box-css' => '1a82a4ae', + 'phui-box-css' => 'a36cf3a5', 'phui-button-css' => '8784a966', - 'phui-calendar-month-css' => '3474d15a', + 'phui-calendar-css' => '5e1ad989', + 'phui-calendar-day-css' => 'de035c8a', + 'phui-calendar-list-css' => 'c1d0ca59', + 'phui-calendar-month-css' => '5e762971', 'phui-document-view-css' => '143b2ac8', 'phui-feed-story-css' => '3a59c2cf', 'phui-form-css' => 'b78ec020', @@ -746,7 +752,7 @@ return array( 'phui-icon-view-css' => 'fcb145a7', 'phui-info-panel-css' => '27ea50a1', 'phui-list-view-css' => '2edb76cf', - 'phui-object-box-css' => '95767d08', + 'phui-object-box-css' => 'ce92d8ec', 'phui-object-item-list-view-css' => 'eb579d6c', 'phui-pinboard-view-css' => '4b346c2a', 'phui-property-list-view-css' => 'dbf53b12', diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 07dc895b3f..88a8b3322f 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -93,6 +93,8 @@ phutil_register_library_map(array( 'AphrontWebpageResponse' => 'aphront/response/AphrontWebpageResponse.php', 'AuditActionMenuEventListener' => 'applications/audit/events/AuditActionMenuEventListener.php', 'BuildStepImplementation' => 'applications/harbormaster/step/BuildStepImplementation.php', + 'CalendarColors' => 'applications/calendar/constants/CalendarColors.php', + 'CalendarConstants' => 'applications/calendar/constants/CalendarConstants.php', 'CelerityAPI' => 'infrastructure/celerity/CelerityAPI.php', 'CelerityManagementMapWorkflow' => 'infrastructure/celerity/management/CelerityManagementMapWorkflow.php', 'CelerityManagementWorkflow' => 'infrastructure/celerity/management/CelerityManagementWorkflow.php', @@ -1000,7 +1002,9 @@ phutil_register_library_map(array( 'PHUIButtonBarView' => 'view/phui/PHUIButtonBarView.php', 'PHUIButtonExample' => 'applications/uiexample/examples/PHUIButtonExample.php', 'PHUIButtonView' => 'view/phui/PHUIButtonView.php', - 'PHUICalendarMonthView' => 'applications/calendar/view/PHUICalendarMonthView.php', + 'PHUICalendarListView' => 'view/phui/calendar/PHUICalendarListView.php', + 'PHUICalendarMonthView' => 'view/phui/calendar/PHUICalendarMonthView.php', + 'PHUICalendarWidgetView' => 'view/phui/calendar/PHUICalendarWidgetView.php', 'PHUIColorPalletteExample' => 'applications/uiexample/examples/PHUIColorPalletteExample.php', 'PHUIDocumentExample' => 'applications/uiexample/examples/PHUIDocumentExample.php', 'PHUIDocumentView' => 'view/phui/PHUIDocumentView.php', @@ -1291,7 +1295,6 @@ phutil_register_library_map(array( 'PhabricatorCalendarEventEditController' => 'applications/calendar/controller/PhabricatorCalendarEventEditController.php', 'PhabricatorCalendarEventInvalidEpochException' => 'applications/calendar/exception/PhabricatorCalendarEventInvalidEpochException.php', 'PhabricatorCalendarEventListController' => 'applications/calendar/controller/PhabricatorCalendarEventListController.php', - 'PhabricatorCalendarEventOverlapException' => 'applications/calendar/exception/PhabricatorCalendarEventOverlapException.php', 'PhabricatorCalendarEventQuery' => 'applications/calendar/query/PhabricatorCalendarEventQuery.php', 'PhabricatorCalendarEventSearchEngine' => 'applications/calendar/query/PhabricatorCalendarEventSearchEngine.php', 'PhabricatorCalendarEventViewController' => 'applications/calendar/controller/PhabricatorCalendarEventViewController.php', @@ -1800,6 +1803,7 @@ phutil_register_library_map(array( 'PhabricatorPasteTransactionQuery' => 'applications/paste/query/PhabricatorPasteTransactionQuery.php', 'PhabricatorPasteViewController' => 'applications/paste/controller/PhabricatorPasteViewController.php', 'PhabricatorPeopleApproveController' => 'applications/people/controller/PhabricatorPeopleApproveController.php', + 'PhabricatorPeopleCalendarController' => 'applications/people/controller/PhabricatorPeopleCalendarController.php', 'PhabricatorPeopleController' => 'applications/people/controller/PhabricatorPeopleController.php', 'PhabricatorPeopleDisableController' => 'applications/people/controller/PhabricatorPeopleDisableController.php', 'PhabricatorPeopleEditController' => 'applications/people/controller/PhabricatorPeopleEditController.php', @@ -2652,6 +2656,7 @@ phutil_register_library_map(array( ), 'AphrontWebpageResponse' => 'AphrontHTMLResponse', 'AuditActionMenuEventListener' => 'PhabricatorEventListener', + 'CalendarColors' => 'CalendarConstants', 'CelerityManagementMapWorkflow' => 'CelerityManagementWorkflow', 'CelerityManagementWorkflow' => 'PhabricatorManagementWorkflow', 'CelerityPhabricatorResourceController' => 'CelerityResourceController', @@ -3666,7 +3671,9 @@ phutil_register_library_map(array( 'PHUIButtonBarView' => 'AphrontTagView', 'PHUIButtonExample' => 'PhabricatorUIExample', 'PHUIButtonView' => 'AphrontTagView', + 'PHUICalendarListView' => 'AphrontTagView', 'PHUICalendarMonthView' => 'AphrontView', + 'PHUICalendarWidgetView' => 'AphrontTagView', 'PHUIColorPalletteExample' => 'PhabricatorUIExample', 'PHUIDocumentExample' => 'PhabricatorUIExample', 'PHUIDocumentView' => 'AphrontTagView', @@ -3987,7 +3994,6 @@ phutil_register_library_map(array( 0 => 'PhabricatorCalendarController', 1 => 'PhabricatorApplicationSearchResultsControllerInterface', ), - 'PhabricatorCalendarEventOverlapException' => 'Exception', 'PhabricatorCalendarEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorCalendarEventSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorCalendarEventViewController' => 'PhabricatorCalendarController', @@ -4563,6 +4569,7 @@ phutil_register_library_map(array( 'PhabricatorPasteTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorPasteViewController' => 'PhabricatorPasteController', 'PhabricatorPeopleApproveController' => 'PhabricatorPeopleController', + 'PhabricatorPeopleCalendarController' => 'PhabricatorPeopleController', 'PhabricatorPeopleController' => 'PhabricatorController', 'PhabricatorPeopleDisableController' => 'PhabricatorPeopleController', 'PhabricatorPeopleEditController' => 'PhabricatorPeopleController', diff --git a/src/applications/calendar/constants/CalendarColors.php b/src/applications/calendar/constants/CalendarColors.php new file mode 100644 index 0000000000..3f0b03b4ed --- /dev/null +++ b/src/applications/calendar/constants/CalendarColors.php @@ -0,0 +1,30 @@ +loadViewerHandles($phids); + /* Assign Colors */ + $unique = array_unique($phids); + $allblue = false; + $calcolors = CalendarColors::getColors(); + if (count($unique) > count($calcolors)) { + $allblue = true; + } + $i = 0; + $eventcolor = array(); + foreach ($unique as $phid) { + if ($allblue) { + $eventcolor[$phid] = CalendarColors::COLOR_SKY; + } else { + $eventcolor[$phid] = $calcolors[$i]; + } + $i++; + } + foreach ($statuses as $status) { $event = new AphrontCalendarEventView(); $event->setEpochRange($status->getDateFrom(), $status->getDateTo()); @@ -46,14 +64,10 @@ final class PhabricatorCalendarBrowseController $name_text = $handles[$status->getUserPHID()]->getName(); $status_text = $status->getHumanStatus(); $event->setUserPHID($status->getUserPHID()); - $event->setName("{$name_text} ({$status_text})"); - $details = ''; - if ($status->getDescription()) { - $details = "\n\n".rtrim($status->getDescription()); - } - $event->setDescription( - $status->getTerseSummary($user).$details); + $event->setDescription(pht('%s (%s)', $name_text, $status_text)); + $event->setName($status_text); $event->setEventID($status->getID()); + $event->setColor($eventcolor[$status->getUserPHID()]); $month_view->addEvent($event); } diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php index 53ef1fc567..8209756a32 100644 --- a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php @@ -73,9 +73,6 @@ final class PhabricatorCalendarEventEditController ->save(); } catch (PhabricatorCalendarEventInvalidEpochException $e) { $errors[] = pht('Start must be before end.'); - } catch (PhabricatorCalendarEventOverlapException $e) { - $errors[] = pht('There is already a status within the specified '. - 'timeframe. Edit or delete this existing status.'); } if (!$errors) { diff --git a/src/applications/calendar/exception/PhabricatorCalendarEventOverlapException.php b/src/applications/calendar/exception/PhabricatorCalendarEventOverlapException.php deleted file mode 100644 index 9efed052f0..0000000000 --- a/src/applications/calendar/exception/PhabricatorCalendarEventOverlapException.php +++ /dev/null @@ -1,4 +0,0 @@ -openTransaction(); - $this->beginWriteLocking(); - - if ($this->shouldInsertWhenSaved()) { - - $overlap = $this->loadAllWhere( - 'userPHID = %s AND dateFrom < %d AND dateTo > %d', - $this->getUserPHID(), - $this->getDateTo(), - $this->getDateFrom()); - - if ($overlap) { - $this->endWriteLocking(); - $this->killTransaction(); - throw new PhabricatorCalendarEventOverlapException(); - } - } - - parent::save(); - - $this->endWriteLocking(); - return $this->saveTransaction(); + return parent::save(); } diff --git a/src/applications/calendar/view/AphrontCalendarEventView.php b/src/applications/calendar/view/AphrontCalendarEventView.php index 9fef7d4f1b..f82c89faea 100644 --- a/src/applications/calendar/view/AphrontCalendarEventView.php +++ b/src/applications/calendar/view/AphrontCalendarEventView.php @@ -8,6 +8,7 @@ final class AphrontCalendarEventView extends AphrontView { private $epochEnd; private $description; private $eventID; + private $color; public function setEventID($event_id) { $this->eventID = $event_id; @@ -58,6 +59,18 @@ final class AphrontCalendarEventView extends AphrontView { return $this->description; } + public function setColor($color) { + $this->color = $color; + return $this; + } + public function getColor() { + if ($this->color) { + return $this->color; + } else { + return CalendarColors::COLOR_SKY; + } + } + public function render() { throw new Exception("Events are only rendered indirectly."); } diff --git a/src/applications/people/application/PhabricatorApplicationPeople.php b/src/applications/people/application/PhabricatorApplicationPeople.php index 8ea84fd95d..e937ca751b 100644 --- a/src/applications/people/application/PhabricatorApplicationPeople.php +++ b/src/applications/people/application/PhabricatorApplicationPeople.php @@ -3,7 +3,7 @@ final class PhabricatorApplicationPeople extends PhabricatorApplication { public function getShortDescription() { - return 'User Accounts'; + return pht('User Accounts'); } public function getBaseURI() { @@ -53,6 +53,8 @@ final class PhabricatorApplicationPeople extends PhabricatorApplication { ), '/p/(?P[\w._-]+)/' => 'PhabricatorPeopleProfileController', + '/p/(?P[\w._-]+)/calendar/' + => 'PhabricatorPeopleCalendarController', ); } diff --git a/src/applications/people/conduit/ConduitAPI_user_addstatus_Method.php b/src/applications/people/conduit/ConduitAPI_user_addstatus_Method.php index 17266d14e0..3938c4be72 100644 --- a/src/applications/people/conduit/ConduitAPI_user_addstatus_Method.php +++ b/src/applications/people/conduit/ConduitAPI_user_addstatus_Method.php @@ -51,8 +51,6 @@ final class ConduitAPI_user_addstatus_Method extends ConduitAPI_user_Method { ->save(); } catch (PhabricatorCalendarEventInvalidEpochException $e) { throw new ConduitException('ERR-BAD-EPOCH'); - } catch (PhabricatorCalendarEventOverlapException $e) { - throw new ConduitException('ERR-OVERLAP'); } } diff --git a/src/applications/people/controller/PhabricatorPeopleCalendarController.php b/src/applications/people/controller/PhabricatorPeopleCalendarController.php new file mode 100644 index 0000000000..174e71cc29 --- /dev/null +++ b/src/applications/people/controller/PhabricatorPeopleCalendarController.php @@ -0,0 +1,92 @@ +username = idx($data, 'username'); + } + + public function processRequest() { + $viewer = $this->getRequest()->getUser(); + $user = id(new PhabricatorPeopleQuery()) + ->setViewer($viewer) + ->withUsernames(array($this->username)) + ->needProfileImage(true) + ->executeOne(); + + if (!$user) { + return new Aphront404Response(); + } + + $picture = $user->loadProfileImageURI(); + + $now = time(); + $request = $this->getRequest(); + $year_d = phabricator_format_local_time($now, $user, 'Y'); + $year = $request->getInt('year', $year_d); + $month_d = phabricator_format_local_time($now, $user, 'm'); + $month = $request->getInt('month', $month_d); + $day = phabricator_format_local_time($now, $user, 'j'); + + + $holidays = id(new PhabricatorCalendarHoliday())->loadAllWhere( + 'day BETWEEN %s AND %s', + "{$year}-{$month}-01", + "{$year}-{$month}-31"); + + $statuses = id(new PhabricatorCalendarEventQuery()) + ->setViewer($user) + ->withInvitedPHIDs(array($user->getPHID())) + ->withDateRange( + strtotime("{$year}-{$month}-01"), + strtotime("{$year}-{$month}-01 next month")) + ->execute(); + + if ($month == $month_d && $year == $year_d) { + $month_view = new PHUICalendarMonthView($month, $year, $day); + } else { + $month_view = new PHUICalendarMonthView($month, $year); + } + + $month_view->setBrowseURI($request->getRequestURI()); + $month_view->setUser($user); + $month_view->setHolidays($holidays); + $month_view->setImage($picture); + + $phids = mpull($statuses, 'getUserPHID'); + $handles = $this->loadViewerHandles($phids); + + foreach ($statuses as $status) { + $event = new AphrontCalendarEventView(); + $event->setEpochRange($status->getDateFrom(), $status->getDateTo()); + $event->setUserPHID($status->getUserPHID()); + $event->setName($status->getHumanStatus()); + $event->setDescription($status->getDescription()); + $event->setEventID($status->getID()); + $month_view->addEvent($event); + } + + $date = new DateTime("{$year}-{$month}-01"); + $crumbs = $this->buildApplicationCrumbs(); + $crumbs->addTextCrumb( + $user->getUsername(), + '/p/'.$user->getUsername().'/'); + $crumbs->addTextCrumb($date->format('F Y')); + + return $this->buildApplicationPage( + array( + $crumbs, + $month_view), + array( + 'title' => pht('Calendar'), + 'device' => true, + )); + } +} diff --git a/src/applications/people/controller/PhabricatorPeopleProfileController.php b/src/applications/people/controller/PhabricatorPeopleProfileController.php index 9bb4225e5e..a715342d71 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileController.php @@ -73,6 +73,16 @@ final class PhabricatorPeopleProfileController $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($user->getUsername()); $feed = $this->renderUserFeed($user); + $calendar = $this->renderUserCalendar($user); + $activity = phutil_tag( + 'div', + array( + 'class' => 'profile-activity-view grouped' + ), + array( + $calendar, + $feed + )); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) @@ -82,7 +92,7 @@ final class PhabricatorPeopleProfileController array( $crumbs, $object_box, - $feed, + $activity, ), array( 'title' => $user->getUsername(), @@ -129,4 +139,72 @@ final class PhabricatorPeopleProfileController 'profile-feed', $view->render()); } + + private function renderUserCalendar(PhabricatorUser $user) { + $now = time(); + $year = phabricator_format_local_time($now, $user, 'Y'); + $month = phabricator_format_local_time($now, $user, 'm'); + $day = phabricator_format_local_time($now, $user, 'j'); + $statuses = id(new PhabricatorCalendarEventQuery()) + ->setViewer($user) + ->withInvitedPHIDs(array($user->getPHID())) + ->withDateRange( + strtotime("{$year}-{$month}-{$day}"), + strtotime("{$year}-{$month}-{$day} +7 days")) + ->execute(); + + $events = array(); + foreach ($statuses as $status) { + $event = new AphrontCalendarEventView(); + $event->setEpochRange($status->getDateFrom(), $status->getDateTo()); + + $status_text = $status->getHumanStatus(); + $event->setUserPHID($status->getUserPHID()); + $event->setName($status_text); + $event->setDescription($status->getDescription()); + $event->setEventID($status->getID()); + $key = date('Y-m-d', $event->getEpochStart()); + $events[$key][] = $event; + // Populate multiday events + // Better means? + $next_day = strtotime("{$key} +1 day"); + if ($event->getEpochEnd() >= $next_day) { + $nextkey = date('Y-m-d', $next_day); + $events[$nextkey][] = $event; + } + } + + $i = 0; + $week = array(); + for ($i = 0;$i <= 6;$i++) { + $datetime = strtotime("{$year}-{$month}-{$day} +{$i} days"); + $headertext = phabricator_format_local_time($datetime, $user, 'l, M d'); + $this_day = date('Y-m-d', $datetime); + + $list = new PHUICalendarListView(); + $list->setUser($user); + $list->showBlankState(true); + if (isset($events[$this_day])) { + foreach ($events[$this_day] as $event) { + $list->addEvent($event); + } + } + + $header = phutil_tag( + 'a', + array( + 'href' => $this->getRequest()->getRequestURI().'calendar/' + ), + $headertext); + + $calendar = new PHUICalendarWidgetView(); + $calendar->setHeader($header); + $calendar->setCalendarList($list); + $week[] = $calendar; + } + + return phutil_tag_div( + 'profile-calendar', + $week); + } } diff --git a/src/view/phui/calendar/PHUICalendarListView.php b/src/view/phui/calendar/PHUICalendarListView.php new file mode 100644 index 0000000000..cd6b41192a --- /dev/null +++ b/src/view/phui/calendar/PHUICalendarListView.php @@ -0,0 +1,138 @@ +events[] = $event; + return $this; + } + + public function setUser($user) { + $this->user = $user; + return $this; + } + + public function showBlankState($state) { + $this->blankState = $state; + return $this; + } + + public function getTagName() { + return 'div'; + } + + public function getTagAttributes() { + require_celerity_resource('phui-calendar-css'); + require_celerity_resource('phui-calendar-list-css'); + return array('class' => 'phui-calendar-day-list'); + } + + protected function getTagContent() { + if (!$this->blankState && empty($this->events)) { + return ''; + } + + $events = msort($this->events, 'getEpochStart'); + + // All Day Event (well, 23 hours, 59 minutes worth) + $timespan = ((3600 * 24) - 60); + + $singletons = array(); + $allday = false; + foreach ($events as $event) { + $color = $event->getColor(); + + $length = ($event->getEpochEnd() - $event->getEpochStart()); + if ($length >= $timespan) { + $timelabel = pht('All Day'); + } else { + $timelabel = phabricator_time($event->getEpochStart(), $this->user); + } + + $dot = phutil_tag( + 'span', + array( + 'class' => 'phui-calendar-list-dot'), + ''); + $title = phutil_tag( + 'span', + array( + 'class' => 'phui-calendar-list-title'), + $this->renderEventLink($event, $allday)); + $time = phutil_tag( + 'span', + array( + 'class' => 'phui-calendar-list-time'), + $timelabel); + + $singletons[] = phutil_tag( + 'li', + array( + 'class' => 'phui-calendar-list-item phui-calendar-'.$color + ), + array( + $dot, + $title, + $time)); + } + + if (empty($singletons)) { + $singletons[] = phutil_tag( + 'li', + array( + 'class' => 'phui-calendar-list-item-empty' + ), + pht('Clear sailing ahead.')); + } + + $list = phutil_tag( + 'ul', + array( + 'class' => 'phui-calendar-list' + ), + $singletons); + + return $list; + } + + private function renderEventLink($event) { + + Javelin::initBehavior('phabricator-tooltips'); + + // Multiple Days + $timespan = ((3600 * 24) + 60); + $length = ($event->getEpochEnd() - $event->getEpochStart()); + if ($length >= $timespan) { + $tip = pht('%s, Until: %s', $event->getName(), + phabricator_date($event->getEpochEnd(), $this->user)); + } else { + $tip = pht('%s, Until: %s', $event->getName(), + phabricator_time($event->getEpochEnd(), $this->user)); + } + + $description = $event->getDescription(); + if (strlen($description) == 0) { + $description = pht('(%s)', $event->getName()); + } + + $anchor = javelin_tag( + 'a', + array( + 'sigil' => 'has-tooltip', + 'class' => 'phui-calendar-item-link', + 'href' => '/calendar/event/view/'.$event->getEventID().'/', + 'meta' => array( + 'tip' => $tip, + 'size' => 200, + ), + ), + $description); + + return $anchor; + } +} diff --git a/src/applications/calendar/view/PHUICalendarMonthView.php b/src/view/phui/calendar/PHUICalendarMonthView.php similarity index 81% rename from src/applications/calendar/view/PHUICalendarMonthView.php rename to src/view/phui/calendar/PHUICalendarMonthView.php index 8c2a7f1308..46ad47f1bd 100644 --- a/src/applications/calendar/view/PHUICalendarMonthView.php +++ b/src/view/phui/calendar/PHUICalendarMonthView.php @@ -8,6 +8,7 @@ final class PHUICalendarMonthView extends AphrontView { private $holidays = array(); private $events = array(); private $browseURI; + private $image; public function setBrowseURI($browse_uri) { $this->browseURI = $browse_uri; @@ -22,6 +23,11 @@ final class PHUICalendarMonthView extends AphrontView { return $this; } + public function setImage($uri) { + $this->image = $uri; + return $this; + } + public function setHolidays(array $holidays) { assert_instances_of($holidays, 'PhabricatorCalendarHoliday'); $this->holidays = mpull($holidays, null, 'getDay'); @@ -92,6 +98,7 @@ final class PHUICalendarMonthView extends AphrontView { "\xC2\xA0")); //   } + $list_events = array(); foreach ($events as $event) { if ($event->getEpochStart() >= $epoch_end) { // This list is sorted, so we can stop looking. @@ -99,13 +106,16 @@ final class PHUICalendarMonthView extends AphrontView { } if ($event->getEpochStart() < $epoch_end && $event->getEpochEnd() > $epoch_start) { - $show_events[$event->getUserPHID()] = $this->renderEvent( - $event, - $epoch_start, - $epoch_end); + $list_events[] = $event; } } + $list = new PHUICalendarListView(); + $list->setUser($this->user); + foreach ($list_events as $item) { + $list->addEvent($item); + } + $holiday_markup = null; if ($holiday) { $name = $holiday->getName(); @@ -123,7 +133,7 @@ final class PHUICalendarMonthView extends AphrontView { array( phutil_tag_div('phui-calendar-date-number', $day_number), $holiday_markup, - phutil_implode_html("\n", $show_events), + $list, )); } @@ -227,6 +237,10 @@ final class PHUICalendarMonthView extends AphrontView { $header->setButtonBar($button_bar); } + if ($this->image) { + $header->setImage($this->image); + } + return $header; } @@ -293,60 +307,4 @@ final class PHUICalendarMonthView extends AphrontView { return $days; } - private function renderEvent( - AphrontCalendarEventView $event, - $epoch_start, - $epoch_end) { - - $user = $this->user; - - $event_start = $event->getEpochStart(); - $event_end = $event->getEpochEnd(); - - $classes = array(); - $when = array(); - - $classes[] = 'phui-calendar-event'; - if ($event_start < $epoch_start) { - $classes[] = 'phui-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[] = 'phui-calendar-event-continues-after'; - $when[] = 'Ends '.phabricator_datetime($event_end, $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_tag( - 'a', - array( - 'sigil' => 'has-tooltip', - 'meta' => array( - 'tip' => $info."\n\n".implode("\n", $when), - 'size' => 240, - ), - 'class' => 'phui-calendar-event-text', - 'href' => '/calendar/event/view/'.$event->getEventID().'/', - ), - phutil_utf8_shorten($event->getName(), 32)); - - return javelin_tag( - 'div', - array( - 'class' => implode(' ', $classes), - ), - $text_div); - } - } diff --git a/src/view/phui/calendar/PHUICalendarWidgetView.php b/src/view/phui/calendar/PHUICalendarWidgetView.php new file mode 100644 index 0000000000..1f294abebd --- /dev/null +++ b/src/view/phui/calendar/PHUICalendarWidgetView.php @@ -0,0 +1,39 @@ +header = $date; + return $this; + } + + public function setCalendarList(PHUICalendarListView $list) { + $this->list = $list; + return $this; + } + + public function getTagName() { + return 'div'; + } + + public function getTagAttributes() { + require_celerity_resource('phui-calendar-list-css'); + return array('class' => 'phui-calendar-list-container'); + } + + protected function getTagContent() { + + $header = id(new PHUIHeaderView()) + ->setHeader($this->header); + + $box = id(new PHUIObjectBoxView()) + ->setHeader($header) + ->setFlush(true) + ->appendChild($this->list); + + return $box; + } +} diff --git a/webroot/rsrc/css/application/profile/profile-view.css b/webroot/rsrc/css/application/profile/profile-view.css index 162aa14ca5..f2a999414b 100644 --- a/webroot/rsrc/css/application/profile/profile-view.css +++ b/webroot/rsrc/css/application/profile/profile-view.css @@ -4,8 +4,7 @@ .device-desktop .profile-feed, .device-tablet .profile-feed { - max-width: 640px; - padding: 12px 16px; + padding: 0 16px 16px 0; } .device-phone .profile-feed { @@ -16,3 +15,26 @@ font-size: 16px; margin-bottom: 5px; } + +.profile-activity-view { + padding-top: 16px; +} + +.profile-activity-view .profile-calendar { + float: left; + margin: 0 16px; +} + +.profile-activity-view .profile-feed { + margin-left: 332px; +} + +.device-phone .profile-activity-view .profile-calendar { + float: none; + margin: 0; +} + +.device-phone .profile-activity-view .profile-feed { + float: none; + margin: 0 8px; +} diff --git a/webroot/rsrc/css/phui/calendar/phui-calendar-day.css b/webroot/rsrc/css/phui/calendar/phui-calendar-day.css new file mode 100644 index 0000000000..9c8dd62331 --- /dev/null +++ b/webroot/rsrc/css/phui/calendar/phui-calendar-day.css @@ -0,0 +1,3 @@ +/** + * @provides phui-calendar-day-css + */ diff --git a/webroot/rsrc/css/phui/calendar/phui-calendar-list.css b/webroot/rsrc/css/phui/calendar/phui-calendar-list.css new file mode 100644 index 0000000000..bf92ee4b09 --- /dev/null +++ b/webroot/rsrc/css/phui/calendar/phui-calendar-list.css @@ -0,0 +1,64 @@ +/** + * @provides phui-calendar-list-css + */ + +.phui-calendar-list-container { + width: 300px; +} + +.device-phone .phui-calendar-list-container { + width: auto; +} + +.phui-calendar-list-container .phui-object-box { + border-bottom: none; + margin: 0; +} + +.phui-calendar-list-container:last-child .phui-object-box { + border-bottom: 1px solid {$blueborder}; +} + +.phui-calendar-list-container .phui-object-box .phui-header-shell h1 { + padding: 6px 0; +} + +.phui-calendar-list { + padding: 16px 12px; +} + +.phui-calendar-list-item { + position: relative; +} + +.phui-calendar-list-dot { + position: relative; + display: inline-block; + width: 5px; + height: 5px; + margin-right: 6px; + top: -5px; + border-radius: 10px; + border: 1px solid transparent; +} + +.phui-calendar-list-title { + width: 200px; + display: inline-block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.phui-calendar-list-item .phui-calendar-list-time { + position: absolute; + width: 60px; + right: 0; + top: 0; + color: {$lightgreytext}; + text-align: right; +} + +.phui-calendar-list-item-empty { + color: {$lightgreytext}; +} diff --git a/webroot/rsrc/css/phui/phui-calendar-month.css b/webroot/rsrc/css/phui/calendar/phui-calendar-month.css similarity index 67% rename from webroot/rsrc/css/phui/phui-calendar-month.css rename to webroot/rsrc/css/phui/calendar/phui-calendar-month.css index 2fabca87ca..252b3382cd 100644 --- a/webroot/rsrc/css/phui/phui-calendar-month.css +++ b/webroot/rsrc/css/phui/calendar/phui-calendar-month.css @@ -23,6 +23,7 @@ table.phui-calendar-view td { table.phui-calendar-view td div.phui-calendar-day { min-height: 125px; + position: relative; } .phui-calendar-holiday { @@ -43,12 +44,14 @@ table.phui-calendar-view td.phui-calendar-month-weekstart { border-color: {$thinblueborder}; border-style: solid; border-width: 0 0 1px 1px; - float: right; + position: absolute; background: #ffffff; width: 16px; height: 16px; text-align: center; - margin-bottom: 3px; + top: 0; + right: 0; + z-index: 10; } .phui-calendar-not-work-day { @@ -63,40 +66,36 @@ table.phui-calendar-view td.phui-calendar-month-weekstart { background-color: {$greybackground}; } -.phui-calendar-event { - clear: both; - background: {$sky}; - font-size: 11px; - margin: 2px 0; - border-radius: 3px; - padding: 3px 5%; - width: 90%; - overflow: hidden; -} - -.phui-calendar-event a:link { - color: #fff; -} - .phui-calendar-event-empty { border-color: transparent; background: transparent; } -.phui-calendar-event-text { - color: #fff; - overflow: hidden; - white-space: nowrap; +.phui-calendar-view .phui-calendar-list { + padding: 8px; } -.phui-calendar-event-continues-before { - border-top-left-radius: 0px; - border-bottom-left-radius: 0px; - border-left-width: 0px; +.phui-calendar-view .phui-calendar-list li:first-child { + margin-right: 16px; } -.phui-calendar-event-continues-after { - border-top-right-radius: 0px; - border-bottom-right-radius: 0px; - border-right-width: 0px; +.phui-calendar-view .phui-calendar-list-dot { + width: 3px; + height: 3px; + margin-right: 4px; + border-radius: 10px; + position: absolute; + top: 5px; + left: 0; +} + +.phui-calendar-view .phui-calendar-list-title { + width: auto; + margin-left: 10px; + white-space: normal; + word-break: break-word; +} + +.phui-calendar-view .phui-calendar-list-time { + display: none; } diff --git a/webroot/rsrc/css/phui/calendar/phui-calendar.css b/webroot/rsrc/css/phui/calendar/phui-calendar.css new file mode 100644 index 0000000000..e338366b3d --- /dev/null +++ b/webroot/rsrc/css/phui/calendar/phui-calendar.css @@ -0,0 +1,107 @@ +/** + * @provides phui-calendar-css + */ + +.phui-calendar-red a { + color: {$red}; +} + +.phui-calendar-orange a { + color: {$orange}; +} + +.phui-calendar-yellow a { + color: {$yellow}; +} + +.phui-calendar-green a { + color: {$green} +} + +.phui-calendar-blue a { + color: {$blue}; +} + +.phui-calendar-sky a { + color: {$sky}; +} + +.phui-calendar-indigo a { + color: {$indigo}; +} + +.phui-calendar-violet a { + color: {$violet}; +} + +.phui-calendar-bg-red { + background-color: {$lightred}; +} + +.phui-calendar-bg-orange { + background-color: {$lightorange}; +} + +.phui-calendar-bg-yellow { + background-color: {$lightyellow}; +} + +.phui-calendar-bg-green { + background-color: {$lightgreen} +} + +.phui-calendar-bg-blue { + background-color: {$lightblue}; +} + +.phui-calendar-bg-sky { + background-color: {$lightsky}; +} + +.phui-calendar-bg-indigo { + background-color: {$lightindigo}; +} + +.phui-calendar-bg-violet { + background-color: {$lightviolet}; +} + +.phui-calendar-red .phui-calendar-list-dot { + background-color: {$red}; + border-color: {$red}; +} + +.phui-calendar-orange .phui-calendar-list-dot { + background-color: {$orange}; + border-color: {$orange}; +} + +.phui-calendar-yellow .phui-calendar-list-dot { + background-color: {$orange}; + border-color: {$orange}; +} + +.phui-calendar-green .phui-calendar-list-dot { + background-color: {$green}; + border-color: {$green}; +} + +.phui-calendar-blue .phui-calendar-list-dot { + background-color: {$blue}; + border-color: {$blue}; +} + +.phui-calendar-sky .phui-calendar-list-dot { + background-color: {$sky}; + border-color: {$sky}; +} + +.phui-calendar-indigo .phui-calendar-list-dot { + background-color: {$indigo}; + border-color: {$indigo}; +} + +.phui-calendar-violet .phui-calendar-list-dot { + background-color: {$violet}; + border-color: {$violet}; +} diff --git a/webroot/rsrc/css/phui/phui-box.css b/webroot/rsrc/css/phui/phui-box.css index bf96793454..4c2b83dc5d 100644 --- a/webroot/rsrc/css/phui/phui-box.css +++ b/webroot/rsrc/css/phui/phui-box.css @@ -7,10 +7,3 @@ border-bottom: 1px solid {$blueborder}; background-color: #fff; } - -.device-phone .phui-box { - border-left: none; - border-right: none; - margin-left: 0; - margin-right: 0; -} diff --git a/webroot/rsrc/css/phui/phui-object-box.css b/webroot/rsrc/css/phui/phui-object-box.css index 155461724e..162dc7efa4 100644 --- a/webroot/rsrc/css/phui/phui-object-box.css +++ b/webroot/rsrc/css/phui/phui-object-box.css @@ -2,7 +2,7 @@ * @provides phui-object-box-css */ -.phui-object-box.phui-object-box-flush { +div.phui-object-box.phui-object-box-flush { margin-top: 0; } @@ -29,9 +29,5 @@ } .device-phone .phui-object-box { - margin-top: 0; -} - -.device-phone .phui-object-box + .phui-object-box { - border-top: none; + margin: 8px 8px 0 8px; }