@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.

Calendar event transaction emails without reply handling

Summary: Ref T7957, Calendar event transaction emails without reply handling.

Test Plan: Create event, invitees should get email.

Reviewers: epriestley, #blessed_reviewers

Reviewed By: epriestley, #blessed_reviewers

Subscribers: Korvin, epriestley

Maniphest Tasks: T7957

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

+187 -31
+2
resources/sql/autopatches/20150501.calendar.1.reply.sql
··· 1 + ALTER TABLE {$NAMESPACE}_calendar.calendar_event 2 + ADD mailKey binary(20) NOT NULL;
+21
resources/sql/autopatches/20150501.calendar.2.reply.php
··· 1 + <?php 2 + 3 + echo "Adding mailkeys to events.\n"; 4 + 5 + $table = new PhabricatorCalendarEvent(); 6 + $conn_w = $table->establishConnection('w'); 7 + $iterator = new LiskMigrationIterator($table); 8 + foreach ($iterator as $event) { 9 + $id = $event->getID(); 10 + 11 + echo "Populating event {$id}...\n"; 12 + 13 + queryfx( 14 + $conn_w, 15 + 'UPDATE %T SET mailKey = %s WHERE id = %d', 16 + $table->getTableName(), 17 + Filesystem::readRandomCharacters(20), 18 + $id); 19 + } 20 + 21 + echo "Done.\n";
+4
src/__phutil_library_map__.php
··· 1502 1502 'PhabricatorCalendarEventInviteeQuery' => 'applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php', 1503 1503 'PhabricatorCalendarEventJoinController' => 'applications/calendar/controller/PhabricatorCalendarEventJoinController.php', 1504 1504 'PhabricatorCalendarEventListController' => 'applications/calendar/controller/PhabricatorCalendarEventListController.php', 1505 + 'PhabricatorCalendarEventMailReceiver' => 'applications/calendar/mail/PhabricatorCalendarEventMailReceiver.php', 1505 1506 'PhabricatorCalendarEventPHIDType' => 'applications/calendar/phid/PhabricatorCalendarEventPHIDType.php', 1506 1507 'PhabricatorCalendarEventQuery' => 'applications/calendar/query/PhabricatorCalendarEventQuery.php', 1507 1508 'PhabricatorCalendarEventSearchEngine' => 'applications/calendar/query/PhabricatorCalendarEventSearchEngine.php', ··· 1513 1514 'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php', 1514 1515 'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php', 1515 1516 'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php', 1517 + 'PhabricatorCalendarReplyHandler' => 'applications/calendar/mail/PhabricatorCalendarReplyHandler.php', 1516 1518 'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php', 1517 1519 'PhabricatorCalendarViewController' => 'applications/calendar/controller/PhabricatorCalendarViewController.php', 1518 1520 'PhabricatorCampfireProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php', ··· 4852 4854 'PhabricatorCalendarEventInviteeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4853 4855 'PhabricatorCalendarEventJoinController' => 'PhabricatorCalendarController', 4854 4856 'PhabricatorCalendarEventListController' => 'PhabricatorCalendarController', 4857 + 'PhabricatorCalendarEventMailReceiver' => 'PhabricatorObjectMailReceiver', 4855 4858 'PhabricatorCalendarEventPHIDType' => 'PhabricatorPHIDType', 4856 4859 'PhabricatorCalendarEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4857 4860 'PhabricatorCalendarEventSearchEngine' => 'PhabricatorApplicationSearchEngine', ··· 4863 4866 'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO', 4864 4867 'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase', 4865 4868 'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule', 4869 + 'PhabricatorCalendarReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 4866 4870 'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec', 4867 4871 'PhabricatorCalendarViewController' => 'PhabricatorCalendarController', 4868 4872 'PhabricatorCampfireProtocolAdapter' => 'PhabricatorBotBaseStreamingProtocolAdapter',
+14
src/applications/calendar/application/PhabricatorCalendarApplication.php
··· 74 74 return $items; 75 75 } 76 76 77 + public function getMailCommandObjects() { 78 + return array( 79 + 'event' => array( 80 + 'name' => pht('Email Commands: Events'), 81 + 'header' => pht('Interacting with Calendar Events'), 82 + 'object' => new PhabricatorCalendarEvent(), 83 + 'summary' => pht( 84 + 'This page documents the commands you can use to interact with '. 85 + 'events in Calendar. These commands work when creating new tasks '. 86 + 'via email and when replying to existing tasks.'), 87 + ), 88 + ); 89 + } 90 + 77 91 }
+84 -4
src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
··· 209 209 return $errors; 210 210 } 211 211 212 - protected function getMailTo(PhabricatorLiskDAO $object) { 213 - return array($object->getUserPHID()); 214 - } 215 - 216 212 protected function shouldPublishFeedStory( 217 213 PhabricatorLiskDAO $object, 218 214 array $xactions) { ··· 222 218 protected function supportsSearch() { 223 219 return true; 224 220 } 221 + 222 + protected function shouldSendMail( 223 + PhabricatorLiskDAO $object, 224 + array $xactions) { 225 + 226 + $xactions = mfilter($xactions, 'shouldHide', true); 227 + return $xactions; 228 + } 229 + 230 + protected function getMailSubjectPrefix() { 231 + return pht('[Calendar]'); 232 + } 233 + 234 + protected function getMailTo(PhabricatorLiskDAO $object) { 235 + $phids = array(); 236 + 237 + if ($object->getUserPHID()) { 238 + $phids[] = $object->getUserPHID(); 239 + } 240 + $phids[] = $this->getActingAsPHID(); 241 + 242 + $invitees = $object->getInvitees(); 243 + foreach ($invitees as $phid => $status) { 244 + if ($status === PhabricatorCalendarEventInvitee::STATUS_ATTENDING 245 + || $status === PhabricatorCalendarEventInvitee::STATUS_INVITED) { 246 + $phids[] = $phid; 247 + } 248 + } 249 + 250 + $phids = array_unique($phids); 251 + return $phids; 252 + } 253 + 254 + public function getMailTagsMap() { 255 + return array( 256 + PhabricatorCalendarEventTransaction::MAILTAG_CONTENT => 257 + pht( 258 + "An event's name, status, invite list, ". 259 + "and description changes."), 260 + PhabricatorCalendarEventTransaction::MAILTAG_RESCHEDULE => 261 + pht( 262 + "An event's start and end date ". 263 + "and cancellation status changes."), 264 + PhabricatorCalendarEventTransaction::MAILTAG_OTHER => 265 + pht('Other event activity not listed above occurs.'), 266 + ); 267 + } 268 + 269 + protected function buildReplyHandler(PhabricatorLiskDAO $object) { 270 + return id(new PhabricatorCalendarReplyHandler()) 271 + ->setMailReceiver($object); 272 + } 273 + 274 + protected function buildMailTemplate(PhabricatorLiskDAO $object) { 275 + $id = $object->getID(); 276 + $name = $object->getName(); 277 + 278 + return id(new PhabricatorMetaMTAMail()) 279 + ->setSubject("E{$id}: {$name}") 280 + ->addHeader('Thread-Topic', "E{$id}: ".$object->getName()); 281 + } 282 + 283 + protected function buildMailBody( 284 + PhabricatorLiskDAO $object, 285 + array $xactions) { 286 + 287 + $description = $object->getDescription(); 288 + $body = parent::buildMailBody($object, $xactions); 289 + 290 + if (strlen($description)) { 291 + $body->addTextSection( 292 + pht('EVENT DESCRIPTION'), 293 + $object->getDescription()); 294 + } 295 + 296 + $body->addLinkSection( 297 + pht('EVENT DETAIL'), 298 + PhabricatorEnv::getProductionURI('/E'.$object->getID())); 299 + 300 + 301 + return $body; 302 + } 303 + 304 + 225 305 }
+28
src/applications/calendar/mail/PhabricatorCalendarEventMailReceiver.php
··· 1 + <?php 2 + 3 + final class PhabricatorCalendarEventMailReceiver 4 + extends PhabricatorObjectMailReceiver { 5 + 6 + public function isEnabled() { 7 + $app_class = 'PhabricatorCalendarApplication'; 8 + return PhabricatorApplication::isClassInstalled($app_class); 9 + } 10 + 11 + protected function getObjectPattern() { 12 + return 'E[1-9]\d*'; 13 + } 14 + 15 + protected function loadObject($pattern, PhabricatorUser $viewer) { 16 + $id = (int)trim($pattern, 'E'); 17 + 18 + return id(new PhabricatorCalendarEventQuery()) 19 + ->setViewer($viewer) 20 + ->withIDs(array($id)) 21 + ->executeOne(); 22 + } 23 + 24 + protected function getTransactionReplyHandler() { 25 + return new PhabricatorCalendarReplyHandler(); 26 + } 27 + 28 + }
+15
src/applications/calendar/mail/PhabricatorCalendarReplyHandler.php
··· 1 + <?php 2 + 3 + final class PhabricatorCalendarReplyHandler 4 + extends PhabricatorApplicationTransactionReplyHandler { 5 + 6 + public function validateMailReceiver($mail_receiver) { 7 + if (!($mail_receiver instanceof PhabricatorCalendarEvent)) { 8 + throw new Exception('Mail receiver is not a PhabricatorCalendarEvent!'); 9 + } 10 + } 11 + 12 + public function getObjectPrefix() { 13 + return 'E'; 14 + } 15 + }
+13 -13
src/applications/calendar/storage/PhabricatorCalendarEvent.php
··· 17 17 protected $status; 18 18 protected $description; 19 19 protected $isCancelled; 20 + protected $mailKey; 20 21 21 22 protected $viewPolicy; 22 23 protected $editPolicy; ··· 38 39 ->setViewPolicy($actor->getPHID()) 39 40 ->setEditPolicy($actor->getPHID()) 40 41 ->attachInvitees(array()); 42 + } 43 + 44 + public function save() { 45 + if ($this->getDateTo() <= $this->getDateFrom()) { 46 + throw new PhabricatorCalendarEventInvalidEpochException(); 47 + } 48 + 49 + if (!$this->mailKey) { 50 + $this->mailKey = Filesystem::readRandomCharacters(20); 51 + } 52 + return parent::save(); 41 53 } 42 54 43 55 private static $statusTexts = array( ··· 76 88 'status' => 'uint32', 77 89 'description' => 'text', 78 90 'isCancelled' => 'bool', 91 + 'mailKey' => 'bytes20', 79 92 ), 80 93 self::CONFIG_KEY_SCHEMA => array( 81 94 'userPHID_dateFrom' => array( ··· 154 167 $is_attending = ($old_status == $attending_status); 155 168 156 169 return $is_attending; 157 - } 158 - 159 - /** 160 - * Validates data and throws exceptions for non-sensical status 161 - * windows 162 - */ 163 - public function save() { 164 - 165 - if ($this->getDateTo() <= $this->getDateFrom()) { 166 - throw new PhabricatorCalendarEventInvalidEpochException(); 167 - } 168 - 169 - return parent::save(); 170 170 } 171 171 172 172 /* -( Markup Interface )--------------------------------------------------- */
+6 -14
src/applications/calendar/storage/PhabricatorCalendarEventTransaction.php
··· 11 11 const TYPE_CANCEL = 'calendar.cancel'; 12 12 const TYPE_INVITE = 'calendar.invite'; 13 13 14 + 15 + const MAILTAG_RESCHEDULE = 'calendar-reschedule'; 14 16 const MAILTAG_CONTENT = 'calendar-content'; 15 17 const MAILTAG_OTHER = 'calendar-other'; 16 18 ··· 442 444 $tags = array(); 443 445 switch ($this->getTransactionType()) { 444 446 case self::TYPE_NAME: 445 - $tags[] = self::MAILTAG_CONTENT; 446 - break; 447 - case self::TYPE_START_DATE: 448 - $tags[] = self::MAILTAG_CONTENT; 449 - break; 450 - case self::TYPE_END_DATE: 451 - $tags[] = self::MAILTAG_CONTENT; 452 - break; 453 447 case self::TYPE_STATUS: 454 - $tags[] = self::MAILTAG_OTHER; 455 - break; 456 448 case self::TYPE_DESCRIPTION: 449 + case self::TYPE_INVITE: 457 450 $tags[] = self::MAILTAG_CONTENT; 458 451 break; 452 + case self::TYPE_START_DATE: 453 + case self::TYPE_END_DATE: 459 454 case self::TYPE_CANCEL: 460 - $tags[] = self::MAILTAG_CONTENT; 461 - break; 462 - case self::TYPE_INVITE: 463 - $tags[] = self::MAILTAG_CONTENT; 455 + $tags[] = self::MAILTAG_RESCHEDULE; 464 456 break; 465 457 } 466 458 return $tags;