diff --git a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php --- a/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php +++ b/src/applications/calendar/controller/PhabricatorCalendarEventEditController.php @@ -105,15 +105,7 @@ $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID( $event->getPHID()); - $invitees = array(); - foreach ($event->getInvitees() as $invitee) { - if ($invitee->isUninvited()) { - continue; - } else { - $invitees[] = $invitee->getInviteePHID(); - } - } - + $invitees = $event->getInviteePHIDsForEdit(); $cancel_uri = $event->getURI(); } @@ -172,14 +164,6 @@ $icon = $request->getStr('icon'); $invitees = $request->getArr('invitees'); - $new_invitees = $this->getNewInviteeList($invitees, $event); - $status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING; - if ($this->isCreate()) { - $status = idx($new_invitees, $viewer->getPHID()); - if ($status) { - $new_invitees[$viewer->getPHID()] = $status_attending; - } - } $xactions[] = id(new PhabricatorCalendarEventTransaction()) ->setTransactionType( @@ -236,7 +220,7 @@ $xactions[] = id(new PhabricatorCalendarEventTransaction()) ->setTransactionType( PhabricatorCalendarEventTransaction::TYPE_INVITE) - ->setNewValue($new_invitees); + ->setNewValue($invitees); $xactions[] = id(new PhabricatorCalendarEventTransaction()) ->setTransactionType( @@ -487,7 +471,6 @@ ->setUser($viewer) ->setDatasource(new PhabricatorMetaMTAMailableDatasource()); - $icon = id(new PHUIFormIconSetControl()) ->setLabel(pht('Icon')) ->setName('icon') @@ -574,32 +557,6 @@ } - public function getNewInviteeList(array $phids, $event) { - $invitees = $event->getInvitees(); - $invitees = mpull($invitees, null, 'getInviteePHID'); - $invited_status = PhabricatorCalendarEventInvitee::STATUS_INVITED; - $uninvited_status = PhabricatorCalendarEventInvitee::STATUS_UNINVITED; - $phids = array_fuse($phids); - - $new = array(); - foreach ($phids as $phid) { - $old_status = $event->getUserInviteStatus($phid); - if ($old_status != $uninvited_status) { - continue; - } - $new[$phid] = $invited_status; - } - - foreach ($invitees as $invitee) { - $deleted_invitee = !idx($phids, $invitee->getInviteePHID()); - if ($deleted_invitee) { - $new[$invitee->getInviteePHID()] = $uninvited_status; - } - } - - return $new; - } - private function getDefaultTimeValues($viewer) { $start = new DateTime('@'.time()); $start->setTimeZone($viewer->getTimeZone()); diff --git a/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php b/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php --- a/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEditEngine.php @@ -60,6 +60,14 @@ } protected function buildCustomEditFields($object) { + $viewer = $this->getViewer(); + + if ($this->getIsCreate()) { + $invitee_phids = array($viewer->getPHID()); + } else { + $invitee_phids = $object->getInviteePHIDsForEdit(); + } + $fields = array( id(new PhabricatorTextEditField()) ->setKey('name') @@ -90,6 +98,17 @@ ->setConduitDescription(pht('Cancel or restore the event.')) ->setConduitTypeDescription(pht('True to cancel the event.')) ->setValue($object->getIsCancelled()), + id(new PhabricatorDatasourceEditField()) + ->setKey('inviteePHIDs') + ->setAliases(array('invite', 'invitee', 'invitees', 'inviteePHID')) + ->setLabel(pht('Invitees')) + ->setDatasource(new PhabricatorMetaMTAMailableDatasource()) + ->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_INVITE) + ->setDescription(pht('Users invited to the event.')) + ->setConduitDescription(pht('Change invited users.')) + ->setConduitTypeDescription(pht('New event invitees.')) + ->setValue($invitee_phids) + ->setCommentActionLabel(pht('Change Invitees')), id(new PhabricatorIconSetEditField()) ->setKey('icon') ->setLabel(pht('Icon')) diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php @@ -109,20 +109,8 @@ $actor_phid = $this->getActingAsPHID(); return $object->getUserInviteStatus($actor_phid); case PhabricatorCalendarEventTransaction::TYPE_INVITE: - $map = $xaction->getNewValue(); - $phids = array_keys($map); - $invitees = mpull($object->getInvitees(), null, 'getInviteePHID'); - - $old = array(); - foreach ($phids as $phid) { - $invitee = idx($invitees, $phid); - if ($invitee) { - $old[$phid] = $invitee->getStatus(); - } else { - $old[$phid] = PhabricatorCalendarEventInvitee::STATUS_UNINVITED; - } - } - return $old; + $invitees = $object->getInvitees(); + return mpull($invitees, 'getStatus', 'getInviteePHID'); } return parent::getCustomTransactionOldValue($object, $xaction); @@ -137,7 +125,6 @@ case PhabricatorCalendarEventTransaction::TYPE_NAME: case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION: case PhabricatorCalendarEventTransaction::TYPE_CANCEL: - case PhabricatorCalendarEventTransaction::TYPE_INVITE: case PhabricatorCalendarEventTransaction::TYPE_ICON: return $xaction->getNewValue(); case PhabricatorCalendarEventTransaction::TYPE_ACCEPT: @@ -150,6 +137,45 @@ case PhabricatorCalendarEventTransaction::TYPE_START_DATE: case PhabricatorCalendarEventTransaction::TYPE_END_DATE: return $xaction->getNewValue(); + case PhabricatorCalendarEventTransaction::TYPE_INVITE: + $status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED; + $status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED; + $status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING; + + $invitees = $object->getInvitees(); + foreach ($invitees as $key => $invitee) { + if ($invitee->getStatus() == $status_uninvited) { + unset($invitees[$key]); + } + } + $invitees = mpull($invitees, null, 'getInviteePHID'); + + $new = $xaction->getNewValue(); + $new = array_fuse($new); + + $all = array_keys($invitees + $new); + $map = array(); + foreach ($all as $phid) { + $is_old = isset($invitees[$phid]); + $is_new = isset($new[$phid]); + + if ($is_old && !$is_new) { + $map[$phid] = $status_uninvited; + } else if (!$is_old && $is_new) { + $map[$phid] = $status_invited; + } + } + + // If we're creating this event and the actor is inviting themselves, + // mark them as attending. + if ($this->getIsNewObject()) { + $acting_phid = $this->getActingAsPHID(); + if (isset($map[$acting_phid])) { + $map[$acting_phid] = $status_attending; + } + } + + return $map; } return parent::getCustomTransactionNewValue($object, $xaction); @@ -400,6 +426,40 @@ $errors[] = $error; } break; + case PhabricatorCalendarEventTransaction::TYPE_INVITE: + $old = $object->getInvitees(); + $old = mpull($old, null, 'getInviteePHID'); + foreach ($xactions as $xaction) { + $new = $xaction->getNewValue(); + $new = array_fuse($new); + $add = array_diff_key($new, $old); + if (!$add) { + continue; + } + + // In the UI, we only allow you to invite mailable objects, but there + // is no definitive marker for "invitable object" today. Just allow + // any valid object to be invited. + $objects = id(new PhabricatorObjectQuery()) + ->setViewer($this->getActor()) + ->withPHIDs($add) + ->execute(); + $objects = mpull($objects, null, 'getPHID'); + foreach ($add as $phid) { + if (isset($objects[$phid])) { + continue; + } + + $errors[] = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Invalid'), + pht( + 'Invitee "%s" identifies an object that does not exist or '. + 'which you do not have permission to view.', + $phid)); + } + } + break; } return $errors; 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 @@ -329,6 +329,19 @@ return $this; } + public function getInviteePHIDsForEdit() { + $invitees = array(); + + foreach ($this->getInvitees() as $invitee) { + if ($invitee->isUninvited()) { + continue; + } + $invitees[] = $invitee->getInviteePHID(); + } + + return $invitees; + } + public function getUserInviteStatus($phid) { $invitees = $this->getInvitees(); $invitees = mpull($invitees, null, 'getInviteePHID'); diff --git a/src/applications/calendar/storage/PhabricatorCalendarEventTransaction.php b/src/applications/calendar/storage/PhabricatorCalendarEventTransaction.php --- a/src/applications/calendar/storage/PhabricatorCalendarEventTransaction.php +++ b/src/applications/calendar/storage/PhabricatorCalendarEventTransaction.php @@ -18,7 +18,6 @@ const TYPE_FREQUENCY = 'calendar.frequency'; const TYPE_RECURRENCE_END_DATE = 'calendar.recurrenceenddate'; - const MAILTAG_RESCHEDULE = 'calendar-reschedule'; const MAILTAG_CONTENT = 'calendar-content'; const MAILTAG_OTHER = 'calendar-other'; @@ -177,6 +176,11 @@ case self::TYPE_INVITE: $text = null; + // Fill in any new invitees as "uninvited" in the old data, to make + // some of the rendering logic a little easier. + $status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED; + $old = $old + array_fill_keys(array_keys($new), $status_uninvited); + if (count($old) === 1 && count($new) === 1 && isset($old[$author_phid])) { @@ -387,6 +391,9 @@ case self::TYPE_INVITE: $text = null; + $status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED; + $old = $old + array_fill_keys(array_keys($new), $status_uninvited); + if (count($old) === 1 && count($new) === 1 && isset($old[$author_phid])) {