1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 19:40:55 +01:00

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
This commit is contained in:
lkassianik 2015-05-01 13:26:07 -07:00
parent 59416a13e7
commit aa68cc8830
9 changed files with 187 additions and 31 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
ADD mailKey binary(20) NOT NULL;

View file

@ -0,0 +1,21 @@
<?php
echo "Adding mailkeys to events.\n";
$table = new PhabricatorCalendarEvent();
$conn_w = $table->establishConnection('w');
$iterator = new LiskMigrationIterator($table);
foreach ($iterator as $event) {
$id = $event->getID();
echo "Populating event {$id}...\n";
queryfx(
$conn_w,
'UPDATE %T SET mailKey = %s WHERE id = %d',
$table->getTableName(),
Filesystem::readRandomCharacters(20),
$id);
}
echo "Done.\n";

View file

@ -1502,6 +1502,7 @@ phutil_register_library_map(array(
'PhabricatorCalendarEventInviteeQuery' => 'applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php', 'PhabricatorCalendarEventInviteeQuery' => 'applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php',
'PhabricatorCalendarEventJoinController' => 'applications/calendar/controller/PhabricatorCalendarEventJoinController.php', 'PhabricatorCalendarEventJoinController' => 'applications/calendar/controller/PhabricatorCalendarEventJoinController.php',
'PhabricatorCalendarEventListController' => 'applications/calendar/controller/PhabricatorCalendarEventListController.php', 'PhabricatorCalendarEventListController' => 'applications/calendar/controller/PhabricatorCalendarEventListController.php',
'PhabricatorCalendarEventMailReceiver' => 'applications/calendar/mail/PhabricatorCalendarEventMailReceiver.php',
'PhabricatorCalendarEventPHIDType' => 'applications/calendar/phid/PhabricatorCalendarEventPHIDType.php', 'PhabricatorCalendarEventPHIDType' => 'applications/calendar/phid/PhabricatorCalendarEventPHIDType.php',
'PhabricatorCalendarEventQuery' => 'applications/calendar/query/PhabricatorCalendarEventQuery.php', 'PhabricatorCalendarEventQuery' => 'applications/calendar/query/PhabricatorCalendarEventQuery.php',
'PhabricatorCalendarEventSearchEngine' => 'applications/calendar/query/PhabricatorCalendarEventSearchEngine.php', 'PhabricatorCalendarEventSearchEngine' => 'applications/calendar/query/PhabricatorCalendarEventSearchEngine.php',
@ -1513,6 +1514,7 @@ phutil_register_library_map(array(
'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php', 'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php',
'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php', 'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php',
'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php', 'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php',
'PhabricatorCalendarReplyHandler' => 'applications/calendar/mail/PhabricatorCalendarReplyHandler.php',
'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php', 'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php',
'PhabricatorCalendarViewController' => 'applications/calendar/controller/PhabricatorCalendarViewController.php', 'PhabricatorCalendarViewController' => 'applications/calendar/controller/PhabricatorCalendarViewController.php',
'PhabricatorCampfireProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php', 'PhabricatorCampfireProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php',
@ -4852,6 +4854,7 @@ phutil_register_library_map(array(
'PhabricatorCalendarEventInviteeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorCalendarEventInviteeQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorCalendarEventJoinController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventJoinController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventListController' => 'PhabricatorCalendarController', 'PhabricatorCalendarEventListController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventMailReceiver' => 'PhabricatorObjectMailReceiver',
'PhabricatorCalendarEventPHIDType' => 'PhabricatorPHIDType', 'PhabricatorCalendarEventPHIDType' => 'PhabricatorPHIDType',
'PhabricatorCalendarEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorCalendarEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorCalendarEventSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorCalendarEventSearchEngine' => 'PhabricatorApplicationSearchEngine',
@ -4863,6 +4866,7 @@ phutil_register_library_map(array(
'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO', 'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO',
'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase', 'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',
'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule',
'PhabricatorCalendarReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'PhabricatorCalendarViewController' => 'PhabricatorCalendarController', 'PhabricatorCalendarViewController' => 'PhabricatorCalendarController',
'PhabricatorCampfireProtocolAdapter' => 'PhabricatorBotBaseStreamingProtocolAdapter', 'PhabricatorCampfireProtocolAdapter' => 'PhabricatorBotBaseStreamingProtocolAdapter',

View file

@ -74,4 +74,18 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication {
return $items; return $items;
} }
public function getMailCommandObjects() {
return array(
'event' => array(
'name' => pht('Email Commands: Events'),
'header' => pht('Interacting with Calendar Events'),
'object' => new PhabricatorCalendarEvent(),
'summary' => pht(
'This page documents the commands you can use to interact with '.
'events in Calendar. These commands work when creating new tasks '.
'via email and when replying to existing tasks.'),
),
);
}
} }

View file

@ -209,10 +209,6 @@ final class PhabricatorCalendarEventEditor
return $errors; return $errors;
} }
protected function getMailTo(PhabricatorLiskDAO $object) {
return array($object->getUserPHID());
}
protected function shouldPublishFeedStory( protected function shouldPublishFeedStory(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
array $xactions) { array $xactions) {
@ -222,4 +218,88 @@ final class PhabricatorCalendarEventEditor
protected function supportsSearch() { protected function supportsSearch() {
return true; return true;
} }
protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {
$xactions = mfilter($xactions, 'shouldHide', true);
return $xactions;
}
protected function getMailSubjectPrefix() {
return pht('[Calendar]');
}
protected function getMailTo(PhabricatorLiskDAO $object) {
$phids = array();
if ($object->getUserPHID()) {
$phids[] = $object->getUserPHID();
}
$phids[] = $this->getActingAsPHID();
$invitees = $object->getInvitees();
foreach ($invitees as $phid => $status) {
if ($status === PhabricatorCalendarEventInvitee::STATUS_ATTENDING
|| $status === PhabricatorCalendarEventInvitee::STATUS_INVITED) {
$phids[] = $phid;
}
}
$phids = array_unique($phids);
return $phids;
}
public function getMailTagsMap() {
return array(
PhabricatorCalendarEventTransaction::MAILTAG_CONTENT =>
pht(
"An event's name, status, invite list, ".
"and description changes."),
PhabricatorCalendarEventTransaction::MAILTAG_RESCHEDULE =>
pht(
"An event's start and end date ".
"and cancellation status changes."),
PhabricatorCalendarEventTransaction::MAILTAG_OTHER =>
pht('Other event activity not listed above occurs.'),
);
}
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
return id(new PhabricatorCalendarReplyHandler())
->setMailReceiver($object);
}
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
$id = $object->getID();
$name = $object->getName();
return id(new PhabricatorMetaMTAMail())
->setSubject("E{$id}: {$name}")
->addHeader('Thread-Topic', "E{$id}: ".$object->getName());
}
protected function buildMailBody(
PhabricatorLiskDAO $object,
array $xactions) {
$description = $object->getDescription();
$body = parent::buildMailBody($object, $xactions);
if (strlen($description)) {
$body->addTextSection(
pht('EVENT DESCRIPTION'),
$object->getDescription());
}
$body->addLinkSection(
pht('EVENT DETAIL'),
PhabricatorEnv::getProductionURI('/E'.$object->getID()));
return $body;
}
} }

View file

@ -0,0 +1,28 @@
<?php
final class PhabricatorCalendarEventMailReceiver
extends PhabricatorObjectMailReceiver {
public function isEnabled() {
$app_class = 'PhabricatorCalendarApplication';
return PhabricatorApplication::isClassInstalled($app_class);
}
protected function getObjectPattern() {
return 'E[1-9]\d*';
}
protected function loadObject($pattern, PhabricatorUser $viewer) {
$id = (int)trim($pattern, 'E');
return id(new PhabricatorCalendarEventQuery())
->setViewer($viewer)
->withIDs(array($id))
->executeOne();
}
protected function getTransactionReplyHandler() {
return new PhabricatorCalendarReplyHandler();
}
}

View file

@ -0,0 +1,15 @@
<?php
final class PhabricatorCalendarReplyHandler
extends PhabricatorApplicationTransactionReplyHandler {
public function validateMailReceiver($mail_receiver) {
if (!($mail_receiver instanceof PhabricatorCalendarEvent)) {
throw new Exception('Mail receiver is not a PhabricatorCalendarEvent!');
}
}
public function getObjectPrefix() {
return 'E';
}
}

View file

@ -17,6 +17,7 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
protected $status; protected $status;
protected $description; protected $description;
protected $isCancelled; protected $isCancelled;
protected $mailKey;
protected $viewPolicy; protected $viewPolicy;
protected $editPolicy; protected $editPolicy;
@ -40,6 +41,17 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
->attachInvitees(array()); ->attachInvitees(array());
} }
public function save() {
if ($this->getDateTo() <= $this->getDateFrom()) {
throw new PhabricatorCalendarEventInvalidEpochException();
}
if (!$this->mailKey) {
$this->mailKey = Filesystem::readRandomCharacters(20);
}
return parent::save();
}
private static $statusTexts = array( private static $statusTexts = array(
self::STATUS_AWAY => 'away', self::STATUS_AWAY => 'away',
self::STATUS_SPORADIC => 'sporadic', self::STATUS_SPORADIC => 'sporadic',
@ -76,6 +88,7 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
'status' => 'uint32', 'status' => 'uint32',
'description' => 'text', 'description' => 'text',
'isCancelled' => 'bool', 'isCancelled' => 'bool',
'mailKey' => 'bytes20',
), ),
self::CONFIG_KEY_SCHEMA => array( self::CONFIG_KEY_SCHEMA => array(
'userPHID_dateFrom' => array( 'userPHID_dateFrom' => array(
@ -156,19 +169,6 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
return $is_attending; return $is_attending;
} }
/**
* Validates data and throws exceptions for non-sensical status
* windows
*/
public function save() {
if ($this->getDateTo() <= $this->getDateFrom()) {
throw new PhabricatorCalendarEventInvalidEpochException();
}
return parent::save();
}
/* -( Markup Interface )--------------------------------------------------- */ /* -( Markup Interface )--------------------------------------------------- */

View file

@ -11,6 +11,8 @@ final class PhabricatorCalendarEventTransaction
const TYPE_CANCEL = 'calendar.cancel'; const TYPE_CANCEL = 'calendar.cancel';
const TYPE_INVITE = 'calendar.invite'; const TYPE_INVITE = 'calendar.invite';
const MAILTAG_RESCHEDULE = 'calendar-reschedule';
const MAILTAG_CONTENT = 'calendar-content'; const MAILTAG_CONTENT = 'calendar-content';
const MAILTAG_OTHER = 'calendar-other'; const MAILTAG_OTHER = 'calendar-other';
@ -442,25 +444,15 @@ final class PhabricatorCalendarEventTransaction
$tags = array(); $tags = array();
switch ($this->getTransactionType()) { switch ($this->getTransactionType()) {
case self::TYPE_NAME: case self::TYPE_NAME:
case self::TYPE_STATUS:
case self::TYPE_DESCRIPTION:
case self::TYPE_INVITE:
$tags[] = self::MAILTAG_CONTENT; $tags[] = self::MAILTAG_CONTENT;
break; break;
case self::TYPE_START_DATE: case self::TYPE_START_DATE:
$tags[] = self::MAILTAG_CONTENT;
break;
case self::TYPE_END_DATE: case self::TYPE_END_DATE:
$tags[] = self::MAILTAG_CONTENT;
break;
case self::TYPE_STATUS:
$tags[] = self::MAILTAG_OTHER;
break;
case self::TYPE_DESCRIPTION:
$tags[] = self::MAILTAG_CONTENT;
break;
case self::TYPE_CANCEL: case self::TYPE_CANCEL:
$tags[] = self::MAILTAG_CONTENT; $tags[] = self::MAILTAG_RESCHEDULE;
break;
case self::TYPE_INVITE:
$tags[] = self::MAILTAG_CONTENT;
break; break;
} }
return $tags; return $tags;