diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php --- a/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventViewController.php @@ -309,16 +309,48 @@ $status_declined => 'red', ); + $viewer_phid = $viewer->getPHID(); + $is_rsvp_invited = $event->isRSVPInvited($viewer_phid); + $type_user = PhabricatorPeopleUserPHIDType::TYPECONST; + + $head = array(); + $tail = array(); foreach ($invitees as $invitee) { $item = new PHUIStatusItemView(); $invitee_phid = $invitee->getInviteePHID(); $status = $invitee->getStatus(); $target = $viewer->renderHandle($invitee_phid); - $icon = $icon_map[$status]; - $icon_color = $icon_color_map[$status]; + $is_user = (phid_get_type($invitee_phid) == $type_user); + + if (!$is_user) { + $icon = 'fa-users'; + $icon_color = 'blue'; + } else { + $icon = $icon_map[$status]; + $icon_color = $icon_color_map[$status]; + } + + // Highlight invited groups which you're a member of if you have + // not RSVP'd to an event yet. + if ($is_rsvp_invited) { + if ($invitee_phid != $viewer_phid) { + if ($event->hasRSVPAuthority($viewer_phid, $invitee_phid)) { + $item->setHighlighted(true); + } + } + } $item->setIcon($icon, $icon_color) ->setTarget($target); + + if ($is_user) { + $tail[] = $item; + } else { + $head[] = $item; + } + } + + foreach (array_merge($head, $tail) as $item) { $invitee_list->addItem($item); } } else { @@ -511,6 +543,7 @@ $event = id(new PhabricatorCalendarEventQuery()) ->setViewer($viewer) ->withIDs(array($id)) + ->needRSVPs(array($viewer->getPHID())) ->executeOne(); if (!$event) { return null; @@ -586,10 +619,8 @@ $viewer = $this->getViewer(); $id = $event->getID(); - $invite_status = $event->getUserInviteStatus($viewer->getPHID()); - $status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED; - $is_invite_pending = ($invite_status == $status_invited); - if (!$is_invite_pending) { + $is_pending = $event->isRSVPInvited($viewer->getPHID()); + if (!$is_pending) { return array(); } diff --git a/src/applications/calendar/policyrule/PhabricatorCalendarEventInviteesPolicyRule.php b/src/applications/calendar/policyrule/PhabricatorCalendarEventInviteesPolicyRule.php --- a/src/applications/calendar/policyrule/PhabricatorCalendarEventInviteesPolicyRule.php +++ b/src/applications/calendar/policyrule/PhabricatorCalendarEventInviteesPolicyRule.php @@ -43,7 +43,7 @@ if (!isset($this->sourcePHIDs[$viewer_phid])) { $source_phids = PhabricatorEdgeQuery::loadDestinationPHIDs( $viewer_phid, - PhabricatorProjectMemberOfProjectEdgeType::EDGECONST); + PhabricatorProjectMaterializedMemberEdgeType::EDGECONST); $source_phids[] = $viewer_phid; $this->sourcePHIDs[$viewer_phid] = $source_phids; } diff --git a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php --- a/src/applications/calendar/query/PhabricatorCalendarEventQuery.php +++ b/src/applications/calendar/query/PhabricatorCalendarEventQuery.php @@ -20,6 +20,7 @@ private $utcInitialEpochMin; private $utcInitialEpochMax; private $isImported; + private $needRSVPs; private $generateGhosts = false; @@ -109,6 +110,11 @@ return $this; } + public function needRSVPs(array $phids) { + $this->needRSVPs = $phids; + return $this; + } + protected function getDefaultOrderVector() { return array('start', 'id'); } @@ -614,6 +620,70 @@ $events = msort($events, 'getStartDateTimeEpoch'); + if ($this->needRSVPs) { + $rsvp_phids = $this->needRSVPs; + $project_type = PhabricatorProjectProjectPHIDType::TYPECONST; + + $project_phids = array(); + foreach ($events as $event) { + foreach ($event->getInvitees() as $invitee) { + $invitee_phid = $invitee->getInviteePHID(); + if (phid_get_type($invitee_phid) == $project_type) { + $project_phids[] = $invitee_phid; + } + } + } + + if ($project_phids) { + $member_type = PhabricatorProjectMaterializedMemberEdgeType::EDGECONST; + + $query = id(new PhabricatorEdgeQuery()) + ->withSourcePHIDs($project_phids) + ->withEdgeTypes(array($member_type)) + ->withDestinationPHIDs($rsvp_phids); + + $edges = $query->execute(); + + $project_map = array(); + foreach ($edges as $src => $types) { + foreach ($types as $type => $dsts) { + foreach ($dsts as $dst => $edge) { + $project_map[$dst][] = $src; + } + } + } + } else { + $project_map = array(); + } + + $membership_map = array(); + foreach ($rsvp_phids as $rsvp_phid) { + $membership_map[$rsvp_phid] = array(); + $membership_map[$rsvp_phid][] = $rsvp_phid; + + $project_phids = idx($project_map, $rsvp_phid); + if ($project_phids) { + foreach ($project_phids as $project_phid) { + $membership_map[$rsvp_phid][] = $project_phid; + } + } + } + + foreach ($events as $event) { + $invitees = $event->getInvitees(); + $invitees = mpull($invitees, null, 'getInviteePHID'); + + $rsvp_map = array(); + foreach ($rsvp_phids as $rsvp_phid) { + $membership_phids = $membership_map[$rsvp_phid]; + $rsvps = array_select_keys($invitees, $membership_phids); + $rsvp_map[$rsvp_phid] = $rsvps; + } + + $event->attachRSVPs($rsvp_map); + } + } + return $events; } 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 @@ -16,7 +16,10 @@ } public function newQuery() { - return new PhabricatorCalendarEventQuery(); + $viewer = $this->requireViewer(); + + return id(new PhabricatorCalendarEventQuery()) + ->needRSVPs(array($viewer->getPHID())); } protected function shouldShowOrderField() { diff --git a/src/applications/calendar/storage/PhabricatorCalendarEvent.php b/src/applications/calendar/storage/PhabricatorCalendarEvent.php --- a/src/applications/calendar/storage/PhabricatorCalendarEvent.php +++ b/src/applications/calendar/storage/PhabricatorCalendarEvent.php @@ -50,6 +50,7 @@ private $parentEvent = self::ATTACHABLE; private $invitees = self::ATTACHABLE; private $importSource = self::ATTACHABLE; + private $rsvps = self::ATTACHABLE; private $viewerTimezone; @@ -643,14 +644,19 @@ } if ($viewer->isLoggedIn()) { - $status = $this->getUserInviteStatus($viewer->getPHID()); - switch ($status) { - case PhabricatorCalendarEventInvitee::STATUS_ATTENDING: - return 'fa-check-circle'; - case PhabricatorCalendarEventInvitee::STATUS_INVITED: - return 'fa-user-plus'; - case PhabricatorCalendarEventInvitee::STATUS_DECLINED: - return 'fa-times'; + $viewer_phid = $viewer->getPHID(); + if ($this->isRSVPInvited($viewer_phid)) { + return 'fa-users'; + } else { + $status = $this->getUserInviteStatus($viewer_phid); + switch ($status) { + case PhabricatorCalendarEventInvitee::STATUS_ATTENDING: + return 'fa-check-circle'; + case PhabricatorCalendarEventInvitee::STATUS_INVITED: + return 'fa-user-plus'; + case PhabricatorCalendarEventInvitee::STATUS_DECLINED: + return 'fa-times'; + } } } @@ -671,7 +677,12 @@ } if ($viewer->isLoggedIn()) { - $status = $this->getUserInviteStatus($viewer->getPHID()); + $viewer_phid = $viewer->getPHID(); + if ($this->isRSVPInvited($viewer_phid)) { + return 'green'; + } + + $status = $this->getUserInviteStatus($viewer_phid); switch ($status) { case PhabricatorCalendarEventInvitee::STATUS_ATTENDING: return 'green'; @@ -1121,6 +1132,52 @@ return $phids; } + public function getRSVPs($phid) { + return $this->assertAttachedKey($this->rsvps, $phid); + } + + public function attachRSVPs(array $rsvps) { + $this->rsvps = $rsvps; + return $this; + } + + public function isRSVPInvited($phid) { + $status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED; + return ($this->getRSVPStatus($phid) == $status_invited); + } + + public function hasRSVPAuthority($phid, $other_phid) { + foreach ($this->getRSVPs($phid) as $rsvp) { + if ($rsvp->getInviteePHID() == $other_phid) { + return true; + } + } + + return false; + } + + public function getRSVPStatus($phid) { + // Check for an individual invitee record first. + $invitees = $this->invitees; + $invitees = mpull($invitees, null, 'getInviteePHID'); + $invitee = idx($invitees, $phid); + if ($invitee) { + return $invitee->getStatus(); + } + + // If we don't have one, try to find an invited status for the user's + // projects. + $status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED; + foreach ($this->getRSVPs($phid) as $rsvp) { + if ($rsvp->getStatus() == $status_invited) { + return $status_invited; + } + } + + return PhabricatorCalendarEventInvitee::STATUS_UNINVITED; + } + + /* -( Markup Interface )--------------------------------------------------- */