diff --git a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php index fc4161d6cf..704f161e3e 100644 --- a/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventSearchEngine.php @@ -1,623 +1,625 @@ requireViewer(); return id(new PhabricatorCalendarEventQuery()) ->needRSVPs(array($viewer->getPHID())); } protected function shouldShowOrderField() { return false; } protected function buildCustomSearchFields() { return array( id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Hosts')) ->setKey('hostPHIDs') ->setAliases(array('host', 'hostPHID', 'hosts')) ->setDatasource(new PhabricatorPeopleUserFunctionDatasource()), id(new PhabricatorSearchDatasourceField()) ->setLabel(pht('Invited')) ->setKey('invitedPHIDs') ->setDatasource(new PhabricatorCalendarInviteeDatasource()), id(new PhabricatorSearchDateControlField()) ->setLabel(pht('Occurs After')) ->setKey('rangeStart'), id(new PhabricatorSearchDateControlField()) ->setLabel(pht('Occurs Before')) ->setKey('rangeEnd') ->setAliases(array('rangeEnd')), id(new PhabricatorSearchCheckboxesField()) ->setKey('upcoming') ->setOptions(array( 'upcoming' => pht('Show only upcoming events.'), )), id(new PhabricatorSearchSelectField()) ->setLabel(pht('Cancelled Events')) ->setKey('isCancelled') ->setOptions($this->getCancelledOptions()) ->setDefault('active'), id(new PhabricatorPHIDsSearchField()) ->setLabel(pht('Import Sources')) ->setKey('importSourcePHIDs') ->setAliases(array('importSourcePHID')), id(new PhabricatorSearchSelectField()) ->setLabel(pht('Display Options')) ->setKey('display') ->setOptions($this->getViewOptions()) ->setDefault('month'), ); } private function getCancelledOptions() { return array( 'active' => pht('Active Events Only'), 'cancelled' => pht('Cancelled Events Only'), 'both' => pht('Both Cancelled and Active Events'), ); } private function getViewOptions() { return array( 'month' => pht('Month View'), 'day' => pht('Day View'), 'list' => pht('List View'), ); } protected function buildQueryFromParameters(array $map) { $query = $this->newQuery(); $viewer = $this->requireViewer(); if ($map['hostPHIDs']) { $query->withHostPHIDs($map['hostPHIDs']); } if ($map['invitedPHIDs']) { $query->withInvitedPHIDs($map['invitedPHIDs']); } $range_start = $map['rangeStart']; $range_end = $map['rangeEnd']; $display = $map['display']; if ($map['upcoming'] && $map['upcoming'][0] == 'upcoming') { $upcoming = true; } else { $upcoming = false; } list($range_start, $range_end) = $this->getQueryDateRange( $range_start, $range_end, $display, $upcoming); $query->withDateRange($range_start, $range_end); switch ($map['isCancelled']) { case 'active': $query->withIsCancelled(false); break; case 'cancelled': $query->withIsCancelled(true); break; } if ($map['importSourcePHIDs']) { $query->withImportSourcePHIDs($map['importSourcePHIDs']); } // Generate ghosts (and ignore stub events) if we aren't querying for // specific events or exporting. if (!empty($map['export'])) { // This is a specific mode enabled by event exports. $query ->withIsStub(false); } else if (!$map['ids'] && !$map['phids']) { $query ->withIsStub(false) ->setGenerateGhosts(true); } return $query; } private function getQueryDateRange( $start_date_wild, $end_date_wild, $display, $upcoming) { $start_date_value = $this->getSafeDate($start_date_wild); $end_date_value = $this->getSafeDate($end_date_wild); $viewer = $this->requireViewer(); $timezone = new DateTimeZone($viewer->getTimezoneIdentifier()); $min_range = null; $max_range = null; $min_range = $start_date_value->getEpoch(); $max_range = $end_date_value->getEpoch(); if ($display == 'month' || $display == 'day') { list($start_year, $start_month, $start_day) = $this->getDisplayYearAndMonthAndDay($min_range, $max_range, $display); $start_day = new DateTime( "{$start_year}-{$start_month}-{$start_day}", $timezone); $next = clone $start_day; if ($display == 'month') { $next->modify('+1 month'); } else if ($display == 'day') { $next->modify('+7 day'); } $display_start = $start_day->format('U'); $display_end = $next->format('U'); $start_of_week = $viewer->getUserSetting( PhabricatorWeekStartDaySetting::SETTINGKEY); $end_of_week = ($start_of_week + 6) % 7; $first_of_month = $start_day->format('w'); $last_of_month = id(clone $next)->modify('-1 day')->format('w'); if (!$min_range || ($min_range < $display_start)) { $min_range = $display_start; if ($display == 'month' && $first_of_month !== $start_of_week) { $interim_day_num = ($first_of_month + 7 - $start_of_week) % 7; $min_range = id(clone $start_day) ->modify('-'.$interim_day_num.' days') ->format('U'); } } if (!$max_range || ($max_range > $display_end)) { $max_range = $display_end; if ($display == 'month' && $last_of_month !== $end_of_week) { $interim_day_num = ($end_of_week + 7 - $last_of_month) % 7; $max_range = id(clone $next) ->modify('+'.$interim_day_num.' days') ->format('U'); } } } if ($upcoming) { $now = PhabricatorTime::getNow(); if ($min_range) { $min_range = max($now, $min_range); } else { $min_range = $now; } } return array($min_range, $max_range); } protected function getURI($path) { return '/calendar/'.$path; } protected function getBuiltinQueryNames() { $names = array( 'month' => pht('Month View'), 'day' => pht('Day View'), 'upcoming' => pht('Upcoming Events'), 'all' => pht('All Events'), ); return $names; } public function setCalendarYearAndMonthAndDay($year, $month, $day = null) { $this->calendarYear = $year; $this->calendarMonth = $month; $this->calendarDay = $day; return $this; } public function buildSavedQueryFromBuiltin($query_key) { $query = $this->newSavedQuery(); $query->setQueryKey($query_key); switch ($query_key) { case 'month': return $query->setParameter('display', 'month'); case 'day': return $query->setParameter('display', 'day'); case 'upcoming': return $query ->setParameter('display', 'list') ->setParameter('upcoming', array( 0 => 'upcoming', )); case 'all': return $query; } return parent::buildSavedQueryFromBuiltin($query_key); } protected function renderResultList( array $events, PhabricatorSavedQuery $query, array $handles) { if ($this->isMonthView($query)) { $result = $this->buildCalendarMonthView($events, $query); } else if ($this->isDayView($query)) { $result = $this->buildCalendarDayView($events, $query); } else { $result = $this->buildCalendarListView($events, $query); } return $result; } private function buildCalendarListView( array $events, PhabricatorSavedQuery $query) { assert_instances_of($events, 'PhabricatorCalendarEvent'); $viewer = $this->requireViewer(); $list = new PHUIObjectItemListView(); foreach ($events as $event) { if ($event->getIsGhostEvent()) { $monogram = $event->getParentEvent()->getMonogram(); $index = $event->getSequenceIndex(); $monogram = "{$monogram}/{$index}"; } else { $monogram = $event->getMonogram(); } $item = id(new PHUIObjectItemView()) ->setUser($viewer) ->setObject($event) ->setObjectName($monogram) ->setHeader($event->getName()) ->setHref($event->getURI()); $item->addAttribute($event->renderEventDate($viewer, false)); if ($event->getIsCancelled()) { $item->setDisabled(true); } $status_icon = $event->getDisplayIcon($viewer); $status_color = $event->getDisplayIconColor($viewer); $status_label = $event->getDisplayIconLabel($viewer); $item->setStatusIcon("{$status_icon} {$status_color}", $status_label); $host = pht( 'Hosted by %s', $viewer->renderHandle($event->getHostPHID())); $item->addByline($host); $list->addItem($item); } return $this->newResultView() ->setObjectList($list) ->setNoDataString(pht('No events found.')); } private function buildCalendarMonthView( array $events, PhabricatorSavedQuery $query) { assert_instances_of($events, 'PhabricatorCalendarEvent'); $viewer = $this->requireViewer(); $now = PhabricatorTime::getNow(); list($start_year, $start_month) = $this->getDisplayYearAndMonthAndDay( $this->getQueryDateFrom($query)->getEpoch(), $this->getQueryDateTo($query)->getEpoch(), $query->getParameter('display')); $now_year = phabricator_format_local_time($now, $viewer, 'Y'); $now_month = phabricator_format_local_time($now, $viewer, 'm'); $now_day = phabricator_format_local_time($now, $viewer, 'j'); if ($start_month == $now_month && $start_year == $now_year) { $month_view = new PHUICalendarMonthView( $this->getQueryDateFrom($query), $this->getQueryDateTo($query), $start_month, $start_year, $now_day); } else { $month_view = new PHUICalendarMonthView( $this->getQueryDateFrom($query), $this->getQueryDateTo($query), $start_month, $start_year); } $month_view->setUser($viewer); foreach ($events as $event) { $epoch_min = $event->getStartDateTimeEpoch(); $epoch_max = $event->getEndDateTimeEpoch(); $is_invited = $event->isRSVPInvited($viewer->getPHID()); $event_view = id(new AphrontCalendarEventView()) ->setHostPHID($event->getHostPHID()) ->setEpochRange($epoch_min, $epoch_max) ->setIsCancelled($event->getIsCancelled()) ->setName($event->getName()) ->setURI($event->getURI()) ->setIsAllDay($event->getIsAllDay()) ->setIcon($event->getDisplayIcon($viewer)) ->setViewerIsInvited($is_invited) + ->setDatetimeSummary($event->renderEventDate($viewer, true)) ->setIconColor($event->getDisplayIconColor($viewer)); $month_view->addEvent($event_view); } $month_view->setBrowseURI( $this->getURI('query/'.$query->getQueryKey().'/')); $from = $this->getQueryDateFrom($query)->getDateTime(); $crumbs = array(); $crumbs[] = id(new PHUICrumbView()) ->setName($from->format('F Y')); $header = id(new PHUIHeaderView()) ->setProfileHeader(true) ->setHeader($from->format('F Y')); return $this->newResultView($month_view) ->setCrumbs($crumbs) ->setHeader($header); } private function buildCalendarDayView( array $events, PhabricatorSavedQuery $query) { $viewer = $this->requireViewer(); list($start_year, $start_month, $start_day) = $this->getDisplayYearAndMonthAndDay( $this->getQueryDateFrom($query)->getEpoch(), $this->getQueryDateTo($query)->getEpoch(), $query->getParameter('display')); $day_view = id(new PHUICalendarDayView( $this->getQueryDateFrom($query), $this->getQueryDateTo($query), $start_year, $start_month, $start_day)) ->setQuery($query->getQueryKey()); $day_view->setUser($viewer); $phids = mpull($events, 'getHostPHID'); foreach ($events as $event) { $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $event, PhabricatorPolicyCapability::CAN_EDIT); $epoch_min = $event->getStartDateTimeEpoch(); $epoch_max = $event->getEndDateTimeEpoch(); $status_icon = $event->getDisplayIcon($viewer); $status_color = $event->getDisplayIconColor($viewer); $event_view = id(new AphrontCalendarEventView()) ->setCanEdit($can_edit) ->setEventID($event->getID()) ->setEpochRange($epoch_min, $epoch_max) ->setIsAllDay($event->getIsAllDay()) ->setIcon($status_icon) ->setIconColor($status_color) ->setName($event->getName()) ->setURI($event->getURI()) + ->setDatetimeSummary($event->renderEventDate($viewer, true)) ->setIsCancelled($event->getIsCancelled()); $day_view->addEvent($event_view); } $browse_uri = $this->getURI('query/'.$query->getQueryKey().'/'); $day_view->setBrowseURI($browse_uri); $from = $this->getQueryDateFrom($query)->getDateTime(); $month_uri = $browse_uri.$from->format('Y/m/'); $crumbs = array( id(new PHUICrumbView()) ->setName($from->format('F Y')) ->setHref($month_uri), id(new PHUICrumbView()) ->setName($from->format('D jS')), ); $header = id(new PHUIHeaderView()) ->setProfileHeader(true) ->setHeader($from->format('D, F jS')); return $this->newResultView($day_view) ->setCrumbs($crumbs) ->setHeader($header); } private function getDisplayYearAndMonthAndDay( $range_start, $range_end, $display) { $viewer = $this->requireViewer(); $epoch = null; if ($this->calendarYear && $this->calendarMonth) { $start_year = $this->calendarYear; $start_month = $this->calendarMonth; $start_day = $this->calendarDay ? $this->calendarDay : 1; } else { if ($range_start) { $epoch = $range_start; } else if ($range_end) { $epoch = $range_end; } else { $epoch = time(); } if ($display == 'month') { $day = 1; } else { $day = phabricator_format_local_time($epoch, $viewer, 'd'); } $start_year = phabricator_format_local_time($epoch, $viewer, 'Y'); $start_month = phabricator_format_local_time($epoch, $viewer, 'm'); $start_day = $day; } return array($start_year, $start_month, $start_day); } public function getPageSize(PhabricatorSavedQuery $saved) { if ($this->isMonthView($saved) || $this->isDayView($saved)) { return $saved->getParameter('limit', 1000); } else { return $saved->getParameter('limit', 100); } } private function getQueryDateFrom(PhabricatorSavedQuery $saved) { if ($this->calendarYear && $this->calendarMonth) { $viewer = $this->requireViewer(); $start_year = $this->calendarYear; $start_month = $this->calendarMonth; $start_day = $this->calendarDay ? $this->calendarDay : 1; return AphrontFormDateControlValue::newFromDictionary( $viewer, array( 'd' => "{$start_year}-{$start_month}-{$start_day}", )); } return $this->getQueryDate($saved, 'rangeStart'); } private function getQueryDateTo(PhabricatorSavedQuery $saved) { return $this->getQueryDate($saved, 'rangeEnd'); } private function getQueryDate(PhabricatorSavedQuery $saved, $key) { $viewer = $this->requireViewer(); $wild = $saved->getParameter($key); return $this->getSafeDate($wild); } private function getSafeDate($value) { $viewer = $this->requireViewer(); if ($value) { // ideally this would be consistent and always pass in the same type if ($value instanceof AphrontFormDateControlValue) { return $value; } else { $value = AphrontFormDateControlValue::newFromWild($viewer, $value); } } else { $value = AphrontFormDateControlValue::newFromEpoch( $viewer, PhabricatorTime::getTodayMidnightDateTime($viewer)->format('U')); $value->setEnabled(false); } $value->setOptional(true); return $value; } private function isMonthView(PhabricatorSavedQuery $query) { if ($this->isDayView($query)) { return false; } if ($query->getParameter('display') == 'month') { return true; } } private function isDayView(PhabricatorSavedQuery $query) { if ($query->getParameter('display') == 'day') { return true; } if ($this->calendarDay) { return true; } return false; } public function newUseResultsActions(PhabricatorSavedQuery $saved) { $viewer = $this->requireViewer(); $can_export = $viewer->isLoggedIn(); return array( id(new PhabricatorActionView()) ->setIcon('fa-download') ->setName(pht('Export Query as .ics')) ->setDisabled(!$can_export) ->setHref('/calendar/export/edit/?queryKey='.$saved->getQueryKey()), ); } private function newResultView($content = null) { // If we aren't rendering a dashboard panel, activate global drag-and-drop // so you can import ".ics" files by dropping them directly onto the // calendar. if (!$this->isPanelContext()) { $drop_upload = id(new PhabricatorGlobalUploadTargetView()) ->setViewer($this->requireViewer()) ->setHintText("\xE2\x87\xAA ".pht('Drop .ics Files to Import')) ->setSubmitURI('/calendar/import/drop/') ->setViewPolicy(PhabricatorPolicies::POLICY_NOONE); $content = array( $drop_upload, $content, ); } return id(new PhabricatorApplicationSearchResultView()) ->setContent($content); } } diff --git a/src/applications/calendar/view/AphrontCalendarEventView.php b/src/applications/calendar/view/AphrontCalendarEventView.php index 0dc9231e6f..d25196bdd1 100644 --- a/src/applications/calendar/view/AphrontCalendarEventView.php +++ b/src/applications/calendar/view/AphrontCalendarEventView.php @@ -1,142 +1,152 @@ iconColor = $icon_color; return $this; } public function getIconColor() { return $this->iconColor; } public function setIsCancelled($is_cancelled) { $this->isCancelled = $is_cancelled; return $this; } public function getIsCancelled() { return $this->isCancelled; } public function setURI($uri) { $this->uri = $uri; return $this; } public function getURI() { return $this->uri; } public function setEventID($event_id) { $this->eventID = $event_id; return $this; } public function getEventID() { return $this->eventID; } public function setViewerIsInvited($viewer_is_invited) { $this->viewerIsInvited = $viewer_is_invited; return $this; } public function getViewerIsInvited() { return $this->viewerIsInvited; } public function setHostPHID($host_phid) { $this->hostPHID = $host_phid; return $this; } public function getHostPHID() { return $this->hostPHID; } public function setName($name) { $this->name = $name; return $this; } public function setEpochRange($start, $end) { $this->epochStart = $start; $this->epochEnd = $end; return $this; } public function getEpochStart() { return $this->epochStart; } public function getEpochEnd() { return $this->epochEnd; } public function getName() { return $this->name; } public function setDescription($description) { $this->description = $description; return $this; } public function getDescription() { return $this->description; } public function setIsAllDay($is_all_day) { $this->isAllDay = $is_all_day; return $this; } public function getIsAllDay() { return $this->isAllDay; } public function setIcon($icon) { $this->icon = $icon; return $this; } public function getIcon() { return $this->icon; } public function setCanEdit($can_edit) { $this->canEdit = $can_edit; return $this; } public function getCanEdit() { return $this->canEdit; } public function getMultiDay() { $nextday = strtotime('12:00 AM Tomorrow', $this->getEpochStart()); if ($this->getEpochEnd() > $nextday) { return true; } return false; } + public function setDatetimeSummary($datetime_summary) { + $this->datetimeSummary = $datetime_summary; + return $this; + } + + public function getDatetimeSummary() { + return $this->datetimeSummary; + } + public function render() { throw new Exception(pht('Events are only rendered indirectly.')); } } diff --git a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php index 28e6e464a3..79134de017 100644 --- a/src/applications/people/controller/PhabricatorPeopleProfileViewController.php +++ b/src/applications/people/controller/PhabricatorPeopleProfileViewController.php @@ -1,377 +1,378 @@ getViewer(); $username = $request->getURIData('username'); $user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) ->withUsernames(array($username)) ->needBadges(true) ->needProfileImage(true) ->needAvailability(true) ->executeOne(); if (!$user) { return new Aphront404Response(); } $this->setUser($user); $profile = $user->loadUserProfile(); $picture = $user->getProfileImageURI(); $profile_icon = PhabricatorPeopleIconSet::getIconIcon($profile->getIcon()); $profile_icon = id(new PHUIIconView()) ->setIcon($profile_icon); $profile_title = $profile->getDisplayTitle(); $header = id(new PHUIHeaderView()) ->setHeader($user->getFullName()) ->setSubheader(array($profile_icon, $profile_title)) ->setImage($picture) ->setProfileHeader(true); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $user, PhabricatorPolicyCapability::CAN_EDIT); if ($can_edit) { $id = $user->getID(); $header->setImageEditURL($this->getApplicationURI("picture/{$id}/")); } $properties = $this->buildPropertyView($user); $name = $user->getUsername(); $feed = $this->buildPeopleFeed($user, $viewer); $feed = id(new PHUIObjectBoxView()) ->setHeaderText(pht('Recent Activity')) ->addClass('project-view-feed') ->appendChild($feed); $projects = $this->buildProjectsView($user); $calendar = $this->buildCalendarDayView($user); $badges = $this->buildBadgesView($user); require_celerity_resource('project-view-css'); $home = id(new PHUITwoColumnView()) ->setHeader($header) ->addClass('project-view-home') ->setMainColumn( array( $properties, $feed, )) ->setSideColumn( array( $projects, $calendar, $badges, )); $nav = $this->getProfileMenu(); $nav->selectFilter(PhabricatorPeopleProfilePanelEngine::PANEL_PROFILE); $crumbs = $this->buildApplicationCrumbs(); $crumbs->setBorder(true); return $this->newPage() ->setTitle($user->getUsername()) ->setNavigation($nav) ->setCrumbs($crumbs) ->appendChild( array( $home, )); } private function buildPropertyView( PhabricatorUser $user) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView()) ->setUser($viewer) ->setObject($user); $field_list = PhabricatorCustomField::getObjectFields( $user, PhabricatorCustomField::ROLE_VIEW); $field_list->appendFieldsToPropertyList($user, $viewer, $view); if (!$view->hasAnyProperties()) { return null; } $view = id(new PHUIObjectBoxView()) ->appendChild($view) ->addClass('project-view-properties'); return $view; } private function buildProjectsView( PhabricatorUser $user) { $viewer = $this->getViewer(); $projects = id(new PhabricatorProjectQuery()) ->setViewer($viewer) ->withMemberPHIDs(array($user->getPHID())) ->needImages(true) ->withStatuses( array( PhabricatorProjectStatus::STATUS_ACTIVE, )) ->execute(); $header = id(new PHUIHeaderView()) ->setHeader(pht('Projects')); if (!empty($projects)) { $limit = 5; $render_phids = array_slice($projects, 0, $limit); $list = id(new PhabricatorProjectListView()) ->setUser($viewer) ->setProjects($render_phids); if (count($projects) > $limit) { $header_text = pht( 'Projects (%s)', phutil_count($projects)); $header = id(new PHUIHeaderView()) ->setHeader($header_text) ->addActionLink( id(new PHUIButtonView()) ->setTag('a') ->setIcon('fa-list-ul') ->setText(pht('View All')) ->setHref('/project/?member='.$user->getPHID())); } } else { $error = id(new PHUIBoxView()) ->addClass('mlb') ->appendChild(pht('User does not belong to any projects.')); $list = id(new PHUIInfoView()) ->setSeverity(PHUIInfoView::SEVERITY_NODATA) ->appendChild($error); } $box = id(new PHUIObjectBoxView()) ->setHeader($header) ->appendChild($list) ->setBackground(PHUIObjectBoxView::GREY); return $box; } private function buildCalendarDayView(PhabricatorUser $user) { $viewer = $this->getViewer(); $class = 'PhabricatorCalendarApplication'; if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { return null; } $midnight = PhabricatorTime::getTodayMidnightDateTime($viewer); $week_end = clone $midnight; $week_end = $week_end->modify('+3 days'); $range_start = $midnight->format('U'); $range_end = $week_end->format('U'); $events = id(new PhabricatorCalendarEventQuery()) ->setViewer($viewer) ->withDateRange($range_start, $range_end) ->withInvitedPHIDs(array($user->getPHID())) ->withIsCancelled(false) ->needRSVPs(array($viewer->getPHID())) ->execute(); $event_views = array(); foreach ($events as $event) { $viewer_is_invited = $event->isRSVPInvited($viewer->getPHID()); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $event, PhabricatorPolicyCapability::CAN_EDIT); $epoch_min = $event->getStartDateTimeEpoch(); $epoch_max = $event->getEndDateTimeEpoch(); $event_view = id(new AphrontCalendarEventView()) ->setCanEdit($can_edit) ->setEventID($event->getID()) ->setEpochRange($epoch_min, $epoch_max) ->setIsAllDay($event->getIsAllDay()) ->setIcon($event->getIcon()) ->setViewerIsInvited($viewer_is_invited) ->setName($event->getName()) + ->setDatetimeSummary($event->renderEventDate($viewer, true)) ->setURI($event->getURI()); $event_views[] = $event_view; } $event_views = msort($event_views, 'getEpochStart'); $day_view = id(new PHUICalendarWeekView()) ->setViewer($viewer) ->setView('week') ->setEvents($event_views) ->setWeekLength(3) ->render(); $header = id(new PHUIHeaderView()) ->setHeader(pht('Calendar')) ->setHref( urisprintf( '/calendar/?invited=%s#R', $user->getUsername())); $box = id(new PHUIObjectBoxView()) ->setHeader($header) ->appendChild($day_view) ->addClass('calendar-profile-box') ->setBackground(PHUIObjectBoxView::GREY); return $box; } private function buildBadgesView(PhabricatorUser $user) { $viewer = $this->getViewer(); $class = 'PhabricatorBadgesApplication'; if (!PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { return null; } $awards = array(); $badges = array(); if ($user->getBadgePHIDs()) { $awards = id(new PhabricatorBadgesAwardQuery()) ->setViewer($viewer) ->withRecipientPHIDs(array($user->getPHID())) ->execute(); $awards = mpull($awards, null, 'getBadgePHID'); $badges = array(); foreach ($awards as $award) { $badge = $award->getBadge(); if ($badge->getStatus() == PhabricatorBadgesBadge::STATUS_ACTIVE) { $badges[$award->getBadgePHID()] = $badge; } } } if (count($badges)) { $flex = new PHUIBadgeBoxView(); foreach ($badges as $badge) { if ($badge) { $awarder_info = array(); $award = idx($awards, $badge->getPHID(), null); $awarder_phid = $award->getAwarderPHID(); $awarder_handle = $viewer->renderHandle($awarder_phid); $awarder_info = pht( 'Awarded by %s', $awarder_handle->render()); $item = id(new PHUIBadgeView()) ->setIcon($badge->getIcon()) ->setHeader($badge->getName()) ->setSubhead($badge->getFlavor()) ->setQuality($badge->getQuality()) ->setHref($badge->getViewURI()) ->addByLine($awarder_info); $flex->addItem($item); } } } else { $error = id(new PHUIBoxView()) ->addClass('mlb') ->appendChild(pht('User does not have any badges.')); $flex = id(new PHUIInfoView()) ->setSeverity(PHUIInfoView::SEVERITY_NODATA) ->appendChild($error); } // Best option? $badges = id(new PhabricatorBadgesQuery()) ->setViewer($viewer) ->withStatuses(array( PhabricatorBadgesBadge::STATUS_ACTIVE, )) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->execute(); $button = id(new PHUIButtonView()) ->setTag('a') ->setIcon('fa-plus') ->setText(pht('Award')) ->setWorkflow(true) ->setHref('/badges/award/'.$user->getID().'/'); $can_award = false; if (count($badges)) { $can_award = true; } $header = id(new PHUIHeaderView()) ->setHeader(pht('Badges')); if (count($badges)) { $header->addActionLink($button); } $box = id(new PHUIObjectBoxView()) ->setHeader($header) ->addClass('project-view-badges') ->appendChild($flex) ->setBackground(PHUIObjectBoxView::GREY); return $box; } private function buildPeopleFeed( PhabricatorUser $user, $viewer) { $query = new PhabricatorFeedQuery(); $query->setFilterPHIDs( array( $user->getPHID(), )); $query->setLimit(100); $query->setViewer($viewer); $stories = $query->execute(); $builder = new PhabricatorFeedBuilder($stories); $builder->setUser($viewer); $builder->setShowHovercards(true); $builder->setNoDataString(pht('To begin on such a grand journey, '. 'requires but just a single step.')); $view = $builder->buildView(); return $view->render(); } } diff --git a/src/view/phui/calendar/PHUICalendarListView.php b/src/view/phui/calendar/PHUICalendarListView.php index be167fc023..bf4726796c 100644 --- a/src/view/phui/calendar/PHUICalendarListView.php +++ b/src/view/phui/calendar/PHUICalendarListView.php @@ -1,240 +1,196 @@ moreLink = $more_link; return $this; } public function getMoreLink() { return $this->moreLink; } private function getView() { return $this->view; } public function setView($view) { $this->view = $view; return $this; } public function addEvent(AphrontCalendarEventView $event) { $this->events[] = $event; return $this; } public function showBlankState($state) { $this->blankState = $state; return $this; } protected function getTagName() { return 'div'; } protected function getTagAttributes() { require_celerity_resource('phui-calendar-css'); require_celerity_resource('phui-calendar-list-css'); return array( 'sigil' => 'calendar-event-list', 'class' => 'phui-calendar-event-list', ); } protected function getTagContent() { if (!$this->blankState && empty($this->events)) { return ''; } + Javelin::initBehavior('phabricator-tooltips'); + $singletons = array(); $allday = false; foreach ($this->events as $event) { $start_epoch = $event->getEpochStart(); if ($event->getIsAllDay()) { $timelabel = pht('All Day'); } else { $timelabel = phabricator_time( $event->getEpochStart(), $this->getUser()); } $icon_icon = $event->getIcon(); $icon_color = $event->getIconColor(); $icon = id(new PHUIIconView()) ->setIcon($icon_icon, $icon_color) ->addClass('phui-calendar-list-item-icon'); $title = phutil_tag( 'span', array( 'class' => 'phui-calendar-list-title', ), $this->getEventTitle($event, $allday)); $time = phutil_tag( 'span', array( 'class' => 'phui-calendar-list-time', ), $timelabel); $event_classes = array(); $event_classes[] = 'phui-calendar-list-item'; if ($event->getIsAllDay()) { $event_classes[] = 'all-day'; } if ($event->getIsCancelled()) { $event_classes[] = 'event-cancelled'; } - $tip = $this->getEventTooltip($event); + $tip = $event->getDateTimeSummary(); if ($this->getView() == 'day') { $tip_align = 'E'; } else if ($this->getView() == 'month') { $tip_align = 'N'; } else { $tip_align = 'W'; } $content = javelin_tag( 'a', array( 'href' => $event->getURI(), 'sigil' => 'has-tooltip', 'meta' => array( 'tip' => $tip, 'size' => 200, 'align' => $tip_align, ), ), array( $icon, $time, $title, )); $singletons[] = phutil_tag( 'li', array( 'class' => implode(' ', $event_classes), ), $content); } if ($this->moreLink) { $singletons[] = phutil_tag( 'li', array( 'class' => 'phui-calendar-list-item', ), phutil_tag( 'a', array( 'href' => $this->moreLink, 'class' => 'phui-calendar-list-more', ), array( id(new PHUIIconView())->setIcon('fa-ellipsis-h grey'), phutil_tag( 'span', array( 'class' => 'phui-calendar-list-title', ), pht('View More...')), ))); } 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 getEventTitle($event) { $class = 'phui-calendar-item'; return phutil_tag( 'span', array( 'class' => $class, ), $event->getName()); } - private function getEventTooltip(AphrontCalendarEventView $event) { - $viewer = $this->getViewer(); - $time_key = PhabricatorTimeFormatSetting::SETTINGKEY; - $time_pref = $viewer->getUserSetting($time_key); - - Javelin::initBehavior('phabricator-tooltips'); - - $start = id(AphrontFormDateControlValue::newFromEpoch( - $viewer, - $event->getEpochStart())); - - $end = id(AphrontFormDateControlValue::newFromEpoch( - $viewer, - $event->getEpochEnd())); - - $end_date = $end->getDateTime(); - $end_date = $end_date->modify('-1 second'); - - $start_date = $start->getDateTime()->format('m d Y'); - $end_date = $end_date->format('m d Y'); - - if ($event->getIsAllDay()) { - if ($start_date == $end_date) { - $tip = pht('All day'); - } else { - $tip = pht( - 'All day, %s - %s', - $start->getValueAsFormat('M j, Y'), - $end->getValueAsFormat('M j, Y')); - } - } else { - if ($start->getValueDate() == $end->getValueDate()) { - $tip = pht( - '%s - %s', - $start->getValueAsFormat($time_pref), - $end->getValueAsFormat($time_pref)); - } else { - $tip = pht( - '%s - %s', - $start->getValueAsFormat('M j, Y, '.$time_pref), - $end->getValueAsFormat('M j, Y, '.$time_pref)); - } - } - return $tip; - } - public function getIsViewerInvitedOnList() { foreach ($this->events as $event) { if ($event->getViewerIsInvited()) { return true; } } return false; } }