@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Support arbitary event invitees when importing events

Summary:
Ref T10747. When we import a ".ics" file, represent any attendees as simple external references.

For consistency with other areas of the product, I've avoided disclosing email addresses. We'll try to get a real name if we can.

(We store addresses and could expose or use them later, or do some kind of masking junk like "epr...ley@g...l.com" which is utterly impossible to figure out.)

Test Plan: {F1888367}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10747

Differential Revision: https://secure.phabricator.com/D16759

+313 -2
+12
resources/sql/autopatches/20161027.calendar.01.externalinvitee.sql
··· 1 + CREATE TABLE {$NAMESPACE}_calendar.calendar_externalinvitee ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + name LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, 5 + nameIndex BINARY(12) NOT NULL, 6 + uri LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, 7 + parameters LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, 8 + sourcePHID VARBINARY(64) NOT NULL, 9 + dateCreated INT UNSIGNED NOT NULL, 10 + dateModified INT UNSIGNED NOT NULL, 11 + UNIQUE KEY `key_name` (`nameIndex`) 12 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+9
src/__phutil_library_map__.php
··· 2098 2098 'PhabricatorCalendarExportTransactionQuery' => 'applications/calendar/query/PhabricatorCalendarExportTransactionQuery.php', 2099 2099 'PhabricatorCalendarExportTransactionType' => 'applications/calendar/xaction/PhabricatorCalendarExportTransactionType.php', 2100 2100 'PhabricatorCalendarExportViewController' => 'applications/calendar/controller/PhabricatorCalendarExportViewController.php', 2101 + 'PhabricatorCalendarExternalInvitee' => 'applications/calendar/storage/PhabricatorCalendarExternalInvitee.php', 2102 + 'PhabricatorCalendarExternalInviteePHIDType' => 'applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php', 2103 + 'PhabricatorCalendarExternalInviteeQuery' => 'applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php', 2101 2104 'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php', 2102 2105 'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php', 2103 2106 'PhabricatorCalendarICSFileImportEngine' => 'applications/calendar/import/PhabricatorCalendarICSFileImportEngine.php', ··· 6939 6942 'PhabricatorCalendarExportTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 6940 6943 'PhabricatorCalendarExportTransactionType' => 'PhabricatorModularTransactionType', 6941 6944 'PhabricatorCalendarExportViewController' => 'PhabricatorCalendarController', 6945 + 'PhabricatorCalendarExternalInvitee' => array( 6946 + 'PhabricatorCalendarDAO', 6947 + 'PhabricatorPolicyInterface', 6948 + ), 6949 + 'PhabricatorCalendarExternalInviteePHIDType' => 'PhabricatorPHIDType', 6950 + 'PhabricatorCalendarExternalInviteeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 6942 6951 'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO', 6943 6952 'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase', 6944 6953 'PhabricatorCalendarICSFileImportEngine' => 'PhabricatorCalendarICSImportEngine',
+107
src/applications/calendar/import/PhabricatorCalendarImportEngine.php
··· 207 207 208 208 $xactions = array(); 209 209 $update_map = array(); 210 + $invitee_map = array(); 211 + $attendee_map = array(); 210 212 foreach ($node_map as $full_uid => $node) { 211 213 $event = idx($events, $full_uid); 212 214 if (!$event) { ··· 222 224 $this->updateEventFromNode($viewer, $event, $node); 223 225 $xactions[$full_uid] = $this->newUpdateTransactions($event, $node); 224 226 $update_map[$full_uid] = $event; 227 + 228 + $attendees = $node->getAttendees(); 229 + $private_index = 1; 230 + foreach ($attendees as $attendee) { 231 + // Generate a "name" for this attendee which is not an email address. 232 + // We avoid disclosing email addresses to be consistent with the rest 233 + // of the product. 234 + $name = $attendee->getName(); 235 + if (preg_match('/@/', $name)) { 236 + $name = new PhutilEmailAddress($name); 237 + $name = $name->getDisplayName(); 238 + } 239 + 240 + // If we don't have a name or the name still looks like it's an 241 + // email address, give them a dummy placeholder name. 242 + if (!strlen($name) || preg_match('/@/', $name)) { 243 + $name = pht('Private User %d', $private_index); 244 + $private_index++; 245 + } 246 + 247 + $attendee_map[$full_uid][$name] = $attendee; 248 + } 249 + } 250 + 251 + $attendee_names = array(); 252 + foreach ($attendee_map as $full_uid => $event_attendees) { 253 + foreach ($event_attendees as $name => $attendee) { 254 + $attendee_names[$name] = $attendee; 255 + } 256 + } 257 + 258 + if ($attendee_names) { 259 + $external_invitees = id(new PhabricatorCalendarExternalInviteeQuery()) 260 + ->setViewer($viewer) 261 + ->withNames($attendee_names) 262 + ->execute(); 263 + $external_invitees = mpull($external_invitees, null, 'getName'); 264 + 265 + foreach ($attendee_names as $name => $attendee) { 266 + if (isset($external_invitees[$name])) { 267 + continue; 268 + } 269 + 270 + $external_invitee = id(new PhabricatorCalendarExternalInvitee()) 271 + ->setName($name) 272 + ->setURI($attendee->getURI()) 273 + ->setSourcePHID($import->getPHID()); 274 + 275 + try { 276 + $external_invitee->save(); 277 + } catch (AphrontDuplicateKeyQueryException $ex) { 278 + $external_invitee = 279 + id(new PhabricatorCalendarExternalInviteeQuery()) 280 + ->setViewer($viewer) 281 + ->withNames(array($name)) 282 + ->executeOne(); 283 + } 284 + 285 + $external_invitees[$name] = $external_invitee; 286 + } 225 287 } 226 288 227 289 // Reorder events so we create parents first. This allows us to populate ··· 287 349 $is_new = !$event->getID(); 288 350 289 351 $editor->applyTransactions($event, $event_xactions); 352 + 353 + // We're just forcing attendees to the correct values here because 354 + // transactions intentionally don't let you RSVP for other users. This 355 + // might need to be turned into a special type of transaction eventually. 356 + $attendees = $attendee_map[$full_uid]; 357 + $old_map = $event->getInvitees(); 358 + $old_map = mpull($old_map, null, 'getInviteePHID'); 359 + 360 + $new_map = array(); 361 + foreach ($attendees as $name => $attendee) { 362 + $phid = $external_invitees[$name]->getPHID(); 363 + 364 + $invitee = idx($old_map, $phid); 365 + if (!$invitee) { 366 + $invitee = id(new PhabricatorCalendarEventInvitee()) 367 + ->setEventPHID($event->getPHID()) 368 + ->setInviteePHID($phid) 369 + ->setInviterPHID($import->getPHID()); 370 + } 371 + 372 + switch ($attendee->getStatus()) { 373 + case PhutilCalendarUserNode::STATUS_ACCEPTED: 374 + $status = PhabricatorCalendarEventInvitee::STATUS_ATTENDING; 375 + break; 376 + case PhutilCalendarUserNode::STATUS_DECLINED: 377 + $status = PhabricatorCalendarEventInvitee::STATUS_DECLINED; 378 + break; 379 + case PhutilCalendarUserNode::STATUS_INVITED: 380 + default: 381 + $status = PhabricatorCalendarEventInvitee::STATUS_INVITED; 382 + break; 383 + } 384 + $invitee->setStatus($status); 385 + $invitee->save(); 386 + 387 + $new_map[$phid] = $invitee; 388 + } 389 + 390 + foreach ($old_map as $phid => $invitee) { 391 + if (empty($new_map[$phid])) { 392 + $invitee->delete(); 393 + } 394 + } 395 + 396 + $event->attachInvitees($new_map); 290 397 291 398 $import->newLogMessage( 292 399 PhabricatorCalendarImportUpdateLogType::LOGTYPE,
+40
src/applications/calendar/phid/PhabricatorCalendarExternalInviteePHIDType.php
··· 1 + <?php 2 + 3 + final class PhabricatorCalendarExternalInviteePHIDType 4 + extends PhabricatorPHIDType { 5 + 6 + const TYPECONST = 'CXNV'; 7 + 8 + public function getTypeName() { 9 + return pht('External Invitee'); 10 + } 11 + 12 + public function newObject() { 13 + return new PhabricatorCalendarExternalInvitee(); 14 + } 15 + 16 + public function getPHIDTypeApplicationClass() { 17 + return 'PhabricatorCalendarApplication'; 18 + } 19 + 20 + protected function buildQueryForObjects( 21 + PhabricatorObjectQuery $query, 22 + array $phids) { 23 + 24 + return id(new PhabricatorCalendarExternalInviteeQuery()) 25 + ->withPHIDs($phids); 26 + } 27 + 28 + public function loadHandles( 29 + PhabricatorHandleQuery $query, 30 + array $handles, 31 + array $objects) { 32 + 33 + foreach ($handles as $phid => $handle) { 34 + $invitee = $objects[$phid]; 35 + 36 + $name = $invitee->getName(); 37 + $handle->setName($name); 38 + } 39 + } 40 + }
+68
src/applications/calendar/query/PhabricatorCalendarExternalInviteeQuery.php
··· 1 + <?php 2 + 3 + final class PhabricatorCalendarExternalInviteeQuery 4 + extends PhabricatorCursorPagedPolicyAwareQuery { 5 + 6 + private $ids; 7 + private $phids; 8 + private $names; 9 + 10 + public function withIDs(array $ids) { 11 + $this->ids = $ids; 12 + return $this; 13 + } 14 + 15 + public function withPHIDs(array $phids) { 16 + $this->phids = $phids; 17 + return $this; 18 + } 19 + 20 + public function withNames(array $names) { 21 + $this->names = $names; 22 + return $this; 23 + } 24 + 25 + public function newResultObject() { 26 + return new PhabricatorCalendarExternalInvitee(); 27 + } 28 + 29 + protected function loadPage() { 30 + return $this->loadStandardPage($this->newResultObject()); 31 + } 32 + 33 + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { 34 + $where = parent::buildWhereClauseParts($conn); 35 + 36 + if ($this->ids !== null) { 37 + $where[] = qsprintf( 38 + $conn, 39 + 'id IN (%Ld)', 40 + $this->ids); 41 + } 42 + 43 + if ($this->phids !== null) { 44 + $where[] = qsprintf( 45 + $conn, 46 + 'phid IN (%Ls)', 47 + $this->phids); 48 + } 49 + 50 + if ($this->names !== null) { 51 + $name_indexes = array(); 52 + foreach ($this->names as $name) { 53 + $name_indexes[] = PhabricatorHash::digestForIndex($name); 54 + } 55 + $where[] = qsprintf( 56 + $conn, 57 + 'nameIndex IN (%Ls)', 58 + $name_indexes); 59 + } 60 + 61 + return $where; 62 + } 63 + 64 + public function getQueryApplicationClass() { 65 + return 'PhabricatorCalendarApplication'; 66 + } 67 + 68 + }
+74
src/applications/calendar/storage/PhabricatorCalendarExternalInvitee.php
··· 1 + <?php 2 + 3 + final class PhabricatorCalendarExternalInvitee 4 + extends PhabricatorCalendarDAO 5 + implements PhabricatorPolicyInterface { 6 + 7 + protected $name; 8 + protected $nameIndex; 9 + protected $uri; 10 + protected $parameters = array(); 11 + protected $sourcePHID; 12 + 13 + public static function initializeNewCalendarEventInvitee( 14 + PhabricatorUser $actor, $event) { 15 + return id(new PhabricatorCalendarEventInvitee()) 16 + ->setInviterPHID($actor->getPHID()) 17 + ->setStatus(self::STATUS_INVITED) 18 + ->setEventPHID($event->getPHID()); 19 + } 20 + 21 + protected function getConfiguration() { 22 + return array( 23 + self::CONFIG_AUX_PHID => true, 24 + self::CONFIG_SERIALIZATION => array( 25 + 'parameters' => self::SERIALIZATION_JSON, 26 + ), 27 + self::CONFIG_COLUMN_SCHEMA => array( 28 + 'name' => 'text', 29 + 'nameIndex' => 'bytes12', 30 + 'uri' => 'text', 31 + ), 32 + self::CONFIG_KEY_SCHEMA => array( 33 + 'key_name' => array( 34 + 'columns' => array('nameIndex'), 35 + 'unique' => true, 36 + ), 37 + ), 38 + ) + parent::getConfiguration(); 39 + } 40 + 41 + public function getPHIDType() { 42 + return PhabricatorCalendarExternalInviteePHIDType::TYPECONST; 43 + } 44 + 45 + public function save() { 46 + $this->nameIndex = PhabricatorHash::digestForIndex($this->getName()); 47 + return parent::save(); 48 + } 49 + 50 + 51 + /* -( PhabricatorPolicyInterface )----------------------------------------- */ 52 + 53 + 54 + public function getCapabilities() { 55 + return array( 56 + PhabricatorPolicyCapability::CAN_VIEW, 57 + ); 58 + } 59 + 60 + public function getPolicy($capability) { 61 + switch ($capability) { 62 + case PhabricatorPolicyCapability::CAN_VIEW: 63 + return PhabricatorPolicies::getMostOpenPolicy(); 64 + } 65 + } 66 + 67 + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { 68 + return false; 69 + } 70 + 71 + public function describeAutomaticCapability($capability) { 72 + return null; 73 + } 74 + }
+3 -2
src/applications/calendar/xaction/PhabricatorCalendarEventInviteTransaction.php
··· 30 30 31 31 $map = array(); 32 32 foreach ($add as $phid) { 33 - $map[$phid] = $status_invited; 33 + $map[$phid] = $status_invited; 34 34 } 35 + 35 36 foreach ($rem as $phid) { 36 - $map[$phid] = $status_uninvited; 37 + $map[$phid] = $status_uninvited; 37 38 } 38 39 39 40 // If we're creating this event and the actor is inviting themselves,