mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 00:42:41 +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:
parent
59416a13e7
commit
aa68cc8830
9 changed files with 187 additions and 31 deletions
2
resources/sql/autopatches/20150501.calendar.1.reply.sql
Normal file
2
resources/sql/autopatches/20150501.calendar.1.reply.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_calendar.calendar_event
|
||||||
|
ADD mailKey binary(20) NOT NULL;
|
21
resources/sql/autopatches/20150501.calendar.2.reply.php
Normal file
21
resources/sql/autopatches/20150501.calendar.2.reply.php
Normal 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";
|
|
@ -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',
|
||||||
|
|
|
@ -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.'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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';
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 )--------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in a new issue