Page MenuHomePhabricator

D16759.diff
No OneTemporary

D16759.diff

diff --git a/resources/sql/autopatches/20161027.calendar.01.externalinvitee.sql b/resources/sql/autopatches/20161027.calendar.01.externalinvitee.sql
new file mode 100644
--- /dev/null
+++ b/resources/sql/autopatches/20161027.calendar.01.externalinvitee.sql
@@ -0,0 +1,12 @@
+CREATE TABLE {$NAMESPACE}_calendar.calendar_externalinvitee (
+ id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ phid VARBINARY(64) NOT NULL,
+ name LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
+ nameIndex BINARY(12) NOT NULL,
+ uri LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
+ parameters LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT},
+ sourcePHID VARBINARY(64) NOT NULL,
+ dateCreated INT UNSIGNED NOT NULL,
+ dateModified INT UNSIGNED NOT NULL,
+ UNIQUE KEY `key_name` (`nameIndex`)
+) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -2098,6 +2098,9 @@
'PhabricatorCalendarExportTransactionQuery' => 'applications/calendar/query/PhabricatorCalendarExportTransactionQuery.php',
'PhabricatorCalendarExportTransactionType' => 'applications/calendar/xaction/PhabricatorCalendarExportTransactionType.php',
'PhabricatorCalendarExportViewController' => 'applications/calendar/controller/PhabricatorCalendarExportViewController.php',
+ 'PhabricatorCalendarExternalInvitee' => 'applications/calendar/storage/PhabricatorCalendarExternalInvitee.php',
+ 'PhabricatorCalendarExternalInviteePHIDType' => 'applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php',
+ 'PhabricatorCalendarExternalInviteeQuery' => 'applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php',
'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php',
'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php',
'PhabricatorCalendarICSFileImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php',
@@ -6939,6 +6942,12 @@
'PhabricatorCalendarExportTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorCalendarExportTransactionType' => 'PhabricatorModularTransactionType',
'PhabricatorCalendarExportViewController' => 'PhabricatorCalendarController',
+ 'PhabricatorCalendarExternalInvitee' => array(
+ 'PhabricatorCalendarDAO',
+ 'PhabricatorPolicyInterface',
+ ),
+ 'PhabricatorCalendarExternalInviteePHIDType' => 'PhabricatorPHIDType',
+ 'PhabricatorCalendarExternalInviteeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO',
'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',
'PhabricatorCalendarICSFileImportEngine' => 'PhabricatorCalendarICSImportEngine',
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,6 +207,8 @@
$xactions = array();
$update_map = array();
+ $invitee_map = array();
+ $attendee_map = array();
foreach ($node_map as $full_uid => $node) {
$event = idx($events, $full_uid);
if (!$event) {
@@ -222,6 +224,66 @@
$this->updateEventFromNode($viewer, $event, $node);
$xactions[$full_uid] = $this->newUpdateTransactions($event, $node);
$update_map[$full_uid] = $event;
+
+ $attendees = $node->getAttendees();
+ $private_index = 1;
+ foreach ($attendees as $attendee) {
+ // Generate a "name" for this attendee which is not an email address.
+ // We avoid disclosing email addresses to be consistent with the rest
+ // of the product.
+ $name = $attendee->getName();
+ if (preg_match('/@/', $name)) {
+ $name = new PhutilEmailAddress($name);
+ $name = $name->getDisplayName();
+ }
+
+ // If we don't have a name or the name still looks like it's an
+ // email address, give them a dummy placeholder name.
+ if (!strlen($name) || preg_match('/@/', $name)) {
+ $name = pht('Private User %d', $private_index);
+ $private_index++;
+ }
+
+ $attendee_map[$full_uid][$name] = $attendee;
+ }
+ }
+
+ $attendee_names = array();
+ foreach ($attendee_map as $full_uid => $event_attendees) {
+ foreach ($event_attendees as $name => $attendee) {
+ $attendee_names[$name] = $attendee;
+ }
+ }
+
+ if ($attendee_names) {
+ $external_invitees = id(new PhabricatorCalendarExternalInviteeQuery())
+ ->setViewer($viewer)
+ ->withNames($attendee_names)
+ ->execute();
+ $external_invitees = mpull($external_invitees, null, 'getName');
+
+ foreach ($attendee_names as $name => $attendee) {
+ if (isset($external_invitees[$name])) {
+ continue;
+ }
+
+ $external_invitee = id(new PhabricatorCalendarExternalInvitee())
+ ->setName($name)
+ ->setURI($attendee->getURI())
+ ->setSourcePHID($import->getPHID());
+
+ try {
+ $external_invitee->save();
+ } catch (AphrontDuplicateKeyQueryException $ex) {
+ $external_invitee =
+ id(new PhabricatorCalendarExternalInviteeQuery())
+ ->setViewer($viewer)
+ ->withNames(array($name))
+ ->executeOne();
+ }
+
+ $external_invitees[$name] = $external_invitee;
+ }
}
// Reorder events so we create parents first. This allows us to populate
@@ -288,6 +350,51 @@
$editor->applyTransactions($event, $event_xactions);
+ // 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];
+ $old_map = $event->getInvitees();
+ $old_map = mpull($old_map, null, 'getInviteePHID');
+
+ $new_map = array();
+ foreach ($attendees as $name => $attendee) {
+ $phid = $external_invitees[$name]->getPHID();
+
+ $invitee = idx($old_map, $phid);
+ if (!$invitee) {
+ $invitee = id(new PhabricatorCalendarEventInvitee())
+ ->setEventPHID($event->getPHID())
+ ->setInviteePHID($phid)
+ ->setInviterPHID($import->getPHID());
+ }
+
+ switch ($attendee->getStatus()) {
+ case PhutilCalendarUserNode::STATUS_ACCEPTED:
+ $status = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
+ break;
+ case PhutilCalendarUserNode::STATUS_DECLINED:
+ $status = PhabricatorCalendarEventInvitee::STATUS_DECLINED;
+ break;
+ case PhutilCalendarUserNode::STATUS_INVITED:
+ default:
+ $status = PhabricatorCalendarEventInvitee::STATUS_INVITED;
+ break;
+ }
+ $invitee->setStatus($status);
+ $invitee->save();
+
+ $new_map[$phid] = $invitee;
+ }
+
+ foreach ($old_map as $phid => $invitee) {
+ if (empty($new_map[$phid])) {
+ $invitee->delete();
+ }
+ }
+
+ $event->attachInvitees($new_map);
+
$import->newLogMessage(
PhabricatorCalendarImportUpdateLogType::LOGTYPE,
array(
diff --git a/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php b/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php
@@ -0,0 +1,40 @@
+<?php
+
+final class PhabricatorCalendarExternalInviteePHIDType
+ extends PhabricatorPHIDType {
+
+ const TYPECONST = 'CXNV';
+
+ public function getTypeName() {
+ return pht('External Invitee');
+ }
+
+ public function newObject() {
+ return new PhabricatorCalendarExternalInvitee();
+ }
+
+ public function getPHIDTypeApplicationClass() {
+ return 'PhabricatorCalendarApplication';
+ }
+
+ protected function buildQueryForObjects(
+ PhabricatorObjectQuery $query,
+ array $phids) {
+
+ return id(new PhabricatorCalendarExternalInviteeQuery())
+ ->withPHIDs($phids);
+ }
+
+ public function loadHandles(
+ PhabricatorHandleQuery $query,
+ array $handles,
+ array $objects) {
+
+ foreach ($handles as $phid => $handle) {
+ $invitee = $objects[$phid];
+
+ $name = $invitee->getName();
+ $handle->setName($name);
+ }
+ }
+}
diff --git a/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php b/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php
@@ -0,0 +1,68 @@
+<?php
+
+final class PhabricatorCalendarExternalInviteeQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $ids;
+ private $phids;
+ private $names;
+
+ public function withIDs(array $ids) {
+ $this->ids = $ids;
+ return $this;
+ }
+
+ public function withPHIDs(array $phids) {
+ $this->phids = $phids;
+ return $this;
+ }
+
+ public function withNames(array $names) {
+ $this->names = $names;
+ return $this;
+ }
+
+ public function newResultObject() {
+ return new PhabricatorCalendarExternalInvitee();
+ }
+
+ protected function loadPage() {
+ return $this->loadStandardPage($this->newResultObject());
+ }
+
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $where = parent::buildWhereClauseParts($conn);
+
+ if ($this->ids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'id IN (%Ld)',
+ $this->ids);
+ }
+
+ if ($this->phids !== null) {
+ $where[] = qsprintf(
+ $conn,
+ 'phid IN (%Ls)',
+ $this->phids);
+ }
+
+ if ($this->names !== null) {
+ $name_indexes = array();
+ foreach ($this->names as $name) {
+ $name_indexes[] = PhabricatorHash::digestForIndex($name);
+ }
+ $where[] = qsprintf(
+ $conn,
+ 'nameIndex IN (%Ls)',
+ $name_indexes);
+ }
+
+ return $where;
+ }
+
+ public function getQueryApplicationClass() {
+ return 'PhabricatorCalendarApplication';
+ }
+
+}
diff --git a/src/applications/calendar/storage/PhabricatorCalendarExternalInvitee.php b/src/applications/calendar/storage/PhabricatorCalendarExternalInvitee.php
new file mode 100644
--- /dev/null
+++ b/src/applications/calendar/storage/PhabricatorCalendarExternalInvitee.php
@@ -0,0 +1,74 @@
+<?php
+
+final class PhabricatorCalendarExternalInvitee
+ extends PhabricatorCalendarDAO
+ implements PhabricatorPolicyInterface {
+
+ protected $name;
+ protected $nameIndex;
+ protected $uri;
+ protected $parameters = array();
+ protected $sourcePHID;
+
+ public static function initializeNewCalendarEventInvitee(
+ PhabricatorUser $actor, $event) {
+ return id(new PhabricatorCalendarEventInvitee())
+ ->setInviterPHID($actor->getPHID())
+ ->setStatus(self::STATUS_INVITED)
+ ->setEventPHID($event->getPHID());
+ }
+
+ protected function getConfiguration() {
+ return array(
+ self::CONFIG_AUX_PHID => true,
+ self::CONFIG_SERIALIZATION => array(
+ 'parameters' => self::SERIALIZATION_JSON,
+ ),
+ self::CONFIG_COLUMN_SCHEMA => array(
+ 'name' => 'text',
+ 'nameIndex' => 'bytes12',
+ 'uri' => 'text',
+ ),
+ self::CONFIG_KEY_SCHEMA => array(
+ 'key_name' => array(
+ 'columns' => array('nameIndex'),
+ 'unique' => true,
+ ),
+ ),
+ ) + parent::getConfiguration();
+ }
+
+ public function getPHIDType() {
+ return PhabricatorCalendarExternalInviteePHIDType::TYPECONST;
+ }
+
+ public function save() {
+ $this->nameIndex = PhabricatorHash::digestForIndex($this->getName());
+ return parent::save();
+ }
+
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ );
+ }
+
+ public function getPolicy($capability) {
+ switch ($capability) {
+ case PhabricatorPolicyCapability::CAN_VIEW:
+ return PhabricatorPolicies::getMostOpenPolicy();
+ }
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ return false;
+ }
+
+ public function describeAutomaticCapability($capability) {
+ return null;
+ }
+}
diff --git a/src/applications/calendar/xaction/PhabricatorCalendarEventInviteTransaction.php b/src/applications/calendar/xaction/PhabricatorCalendarEventInviteTransaction.php
--- a/src/applications/calendar/xaction/PhabricatorCalendarEventInviteTransaction.php
+++ b/src/applications/calendar/xaction/PhabricatorCalendarEventInviteTransaction.php
@@ -30,10 +30,11 @@
$map = array();
foreach ($add as $phid) {
- $map[$phid] = $status_invited;
+ $map[$phid] = $status_invited;
}
+
foreach ($rem as $phid) {
- $map[$phid] = $status_uninvited;
+ $map[$phid] = $status_uninvited;
}
// If we're creating this event and the actor is inviting themselves,

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 22, 9:36 PM (17 h, 29 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6776022
Default Alt Text
D16759.diff (13 KB)

Event Timeline