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 @@ -166,6 +166,7 @@ } $availability_select->setDropdownMenu($dropdown); + $availability_select->setDisabled($event->isImportedEvent()); $header->addActionLink($availability_select); } @@ -629,6 +630,7 @@ ->setIcon('fa-times grey') ->setHref($this->getApplicationURI("/event/decline/{$id}/")) ->setWorkflow(true) + ->setDisabled($event->isImportedEvent()) ->setText(pht('Decline')); $accept_button = id(new PHUIButtonView()) @@ -636,6 +638,7 @@ ->setIcon('fa-check green') ->setHref($this->getApplicationURI("/event/accept/{$id}/")) ->setWorkflow(true) + ->setDisabled($event->isImportedEvent()) ->setText(pht('Accept')); return array($decline_button, $accept_button); diff --git a/src/applications/calendar/import/PhabricatorCalendarImportEngine.php b/src/applications/calendar/import/PhabricatorCalendarImportEngine.php --- a/src/applications/calendar/import/PhabricatorCalendarImportEngine.php +++ b/src/applications/calendar/import/PhabricatorCalendarImportEngine.php @@ -207,10 +207,23 @@ $events = null; } + // Verified emails of the Event Uploader, to be eventually matched. + // Phorge loves privacy, so emails are generally private. + // This just covers a corner case: yourself inviting yourself. + // NOTE: We are using the omnipotent user since we already have + // withUserPHIDs() limiting to a specific person (you). + $author_verified_emails = id(new PhabricatorPeopleUserEmailQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withUserPHIDs(array($import->getAuthorPHID())) + ->withIsVerified(true) + ->execute(); + $author_verified_emails = mpull($author_verified_emails, 'getAddress'); + $xactions = array(); $update_map = array(); $invitee_map = array(); - $attendee_map = array(); + $attendee_name_map = array(); // [eventUID][email from] = Attendee + $attendee_user_map = array(); // [eventUID][userPHID ] = Attendee foreach ($node_map as $full_uid => $node) { $event = idx($events, $full_uid); if (!$event) { @@ -227,7 +240,8 @@ $xactions[$full_uid] = $this->newUpdateTransactions($event, $node); $update_map[$full_uid] = $event; - $attendee_map[$full_uid] = array(); + $attendee_name_map[$full_uid] = array(); + $attendee_user_map[$full_uid] = array(); $attendees = $node->getAttendees(); $private_index = 1; foreach ($attendees as $attendee) { @@ -236,8 +250,18 @@ // of the product. $name = $attendee->getName(); if (preg_match('/@/', $name)) { - $name = new PhutilEmailAddress($name); - $name = $name->getDisplayName(); + $attendee_mail = new PhutilEmailAddress($name); + $name = $attendee_mail->getDisplayName(); + $address = $attendee_mail->getAddress(); + + // Skip creation of dummy "Private User" if it's me, the uploader. + if ($address) { + if (in_array($address, $author_verified_emails, true)) { + $attendee_user_map[$full_uid][$import->getAuthorPHID()] = + $attendee; + continue; + } + } } // If we don't have a name or the name still looks like it's an @@ -247,12 +271,12 @@ $private_index++; } - $attendee_map[$full_uid][$name] = $attendee; + $attendee_name_map[$full_uid][$name] = $attendee; } } $attendee_names = array(); - foreach ($attendee_map as $full_uid => $event_attendees) { + foreach ($attendee_name_map as $full_uid => $event_attendees) { foreach ($event_attendees as $name => $attendee) { $attendee_names[$name] = $attendee; } @@ -356,19 +380,28 @@ // We're just forcing attendees to the correct values here because // transactions intentionally don't let you RSVP for other users. This // might need to be turned into a special type of transaction eventually. - $attendees = $attendee_map[$full_uid]; + $attendees_name = $attendee_name_map[$full_uid]; + $attendees_user = $attendee_user_map[$full_uid]; $old_map = $event->getInvitees(); $old_map = mpull($old_map, null, 'getInviteePHID'); + $phid_invitees = array(); + foreach ($attendees_name as $name => $attendee) { + $attendee_phid = $external_invitees[$name]->getPHID(); + $phid_invitees[$attendee_phid] = $attendee; + } + foreach ($attendees_user as $phid_user_attendee => $attendee) { + $phid_invitees[$phid_user_attendee] = $attendee; + } + $new_map = array(); - foreach ($attendees as $name => $attendee) { - $phid = $external_invitees[$name]->getPHID(); + foreach ($phid_invitees as $phid_invitee => $attendee) { - $invitee = idx($old_map, $phid); + $invitee = idx($old_map, $phid_invitee); if (!$invitee) { $invitee = id(new PhabricatorCalendarEventInvitee()) ->setEventPHID($event->getPHID()) - ->setInviteePHID($phid) + ->setInviteePHID($phid_invitee) ->setInviterPHID($import->getPHID()); } @@ -381,15 +414,20 @@ break; case PhutilCalendarUserNode::STATUS_INVITED: default: - $status = PhabricatorCalendarEventInvitee::STATUS_INVITED; + if ($phid_invitee === $import->getAuthorPHID()) { + $status = PhabricatorCalendarEventInvitee::STATUS_ATTENDING; + } else { + $status = PhabricatorCalendarEventInvitee::STATUS_INVITED; + } break; } $invitee->setStatus($status); $invitee->save(); - $new_map[$phid] = $invitee; + $new_map[$phid_invitee] = $invitee; } + // Remove old Invitees if they are not invited anymore. foreach ($old_map as $phid => $invitee) { if (empty($new_map[$phid])) { $invitee->delete(); diff --git a/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php b/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php --- a/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php +++ b/src/applications/people/query/PhabricatorPeopleUserEmailQuery.php @@ -5,6 +5,8 @@ private $ids; private $phids; + private $userPhids; + private $isVerified; public function withIDs(array $ids) { $this->ids = $ids; @@ -16,6 +18,24 @@ return $this; } + /** + * With the specified User PHIDs. + * @param null|array $phids User PHIDs + */ + public function withUserPHIDs(array $phids) { + $this->userPhids = $phids; + return $this; + } + + /** + * With a verified email or not. + * @param bool|null $isVerified + */ + public function withIsVerified($verified) { + $this->isVerified = $verified; + return $this; + } + public function newResultObject() { return new PhabricatorUserEmail(); } @@ -41,6 +61,20 @@ $this->phids); } + if ($this->userPhids !== null) { + $where[] = qsprintf( + $conn, + 'email.userPHID IN (%Ls)', + $this->userPhids); + } + + if ($this->isVerified !== null) { + $where[] = qsprintf( + $conn, + 'email.isVerified = %d', + (int)$this->isVerified); + } + return $where; }