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

Convert Calendar to Modular Transactions

Summary:
Ref T9275. Swaps Calendar over to modular transactions. Theoretically, this has almost no effect on anything.

Ref T10633. I didn't actually do anything here yet, but this gets us ready to put timestamps in email.

Test Plan: Created and edited a bunch of events, nothing seemed catastrophically broken.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9275, T10633

Differential Revision: https://secure.phabricator.com/D16286
This commit is contained in:
epriestley 2016-07-12 15:44:11 -07:00
parent 63fec9b97d
commit 7b09f5698f
28 changed files with 942 additions and 953 deletions

View file

@ -2021,24 +2021,40 @@ phutil_register_library_map(array(
'PhabricatorCalendarDAO' => 'applications/calendar/storage/PhabricatorCalendarDAO.php',
'PhabricatorCalendarEditEngine' => 'applications/calendar/editor/PhabricatorCalendarEditEngine.php',
'PhabricatorCalendarEvent' => 'applications/calendar/storage/PhabricatorCalendarEvent.php',
'PhabricatorCalendarEventAcceptTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventAcceptTransaction.php',
'PhabricatorCalendarEventAllDayTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventAllDayTransaction.php',
'PhabricatorCalendarEventCancelController' => 'applications/calendar/controller/PhabricatorCalendarEventCancelController.php',
'PhabricatorCalendarEventCancelTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventCancelTransaction.php',
'PhabricatorCalendarEventDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventDateTransaction.php',
'PhabricatorCalendarEventDeclineTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventDeclineTransaction.php',
'PhabricatorCalendarEventDescriptionTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventDescriptionTransaction.php',
'PhabricatorCalendarEventDragController' => 'applications/calendar/controller/PhabricatorCalendarEventDragController.php',
'PhabricatorCalendarEventEditController' => 'applications/calendar/controller/PhabricatorCalendarEventEditController.php',
'PhabricatorCalendarEventEditor' => 'applications/calendar/editor/PhabricatorCalendarEventEditor.php',
'PhabricatorCalendarEventEmailCommand' => 'applications/calendar/command/PhabricatorCalendarEventEmailCommand.php',
'PhabricatorCalendarEventEndDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventEndDateTransaction.php',
'PhabricatorCalendarEventFrequencyTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventFrequencyTransaction.php',
'PhabricatorCalendarEventFulltextEngine' => 'applications/calendar/search/PhabricatorCalendarEventFulltextEngine.php',
'PhabricatorCalendarEventIconTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventIconTransaction.php',
'PhabricatorCalendarEventInviteTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventInviteTransaction.php',
'PhabricatorCalendarEventInvitee' => 'applications/calendar/storage/PhabricatorCalendarEventInvitee.php',
'PhabricatorCalendarEventInviteeQuery' => 'applications/calendar/query/PhabricatorCalendarEventInviteeQuery.php',
'PhabricatorCalendarEventJoinController' => 'applications/calendar/controller/PhabricatorCalendarEventJoinController.php',
'PhabricatorCalendarEventListController' => 'applications/calendar/controller/PhabricatorCalendarEventListController.php',
'PhabricatorCalendarEventMailReceiver' => 'applications/calendar/mail/PhabricatorCalendarEventMailReceiver.php',
'PhabricatorCalendarEventNameTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventNameTransaction.php',
'PhabricatorCalendarEventPHIDType' => 'applications/calendar/phid/PhabricatorCalendarEventPHIDType.php',
'PhabricatorCalendarEventQuery' => 'applications/calendar/query/PhabricatorCalendarEventQuery.php',
'PhabricatorCalendarEventRSVPEmailCommand' => 'applications/calendar/command/PhabricatorCalendarEventRSVPEmailCommand.php',
'PhabricatorCalendarEventRecurringTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventRecurringTransaction.php',
'PhabricatorCalendarEventReplyTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventReplyTransaction.php',
'PhabricatorCalendarEventSearchEngine' => 'applications/calendar/query/PhabricatorCalendarEventSearchEngine.php',
'PhabricatorCalendarEventStartDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventStartDateTransaction.php',
'PhabricatorCalendarEventTransaction' => 'applications/calendar/storage/PhabricatorCalendarEventTransaction.php',
'PhabricatorCalendarEventTransactionComment' => 'applications/calendar/storage/PhabricatorCalendarEventTransactionComment.php',
'PhabricatorCalendarEventTransactionQuery' => 'applications/calendar/query/PhabricatorCalendarEventTransactionQuery.php',
'PhabricatorCalendarEventTransactionType' => 'applications/calendar/xaction/PhabricatorCalendarEventTransactionType.php',
'PhabricatorCalendarEventUntilDateTransaction' => 'applications/calendar/xaction/PhabricatorCalendarEventUntilDateTransaction.php',
'PhabricatorCalendarEventViewController' => 'applications/calendar/controller/PhabricatorCalendarEventViewController.php',
'PhabricatorCalendarHoliday' => 'applications/calendar/storage/PhabricatorCalendarHoliday.php',
'PhabricatorCalendarHolidayTestCase' => 'applications/calendar/storage/__tests__/PhabricatorCalendarHolidayTestCase.php',
@ -6629,12 +6645,22 @@ phutil_register_library_map(array(
'PhabricatorSpacesInterface',
'PhabricatorFulltextInterface',
),
'PhabricatorCalendarEventAcceptTransaction' => 'PhabricatorCalendarEventReplyTransaction',
'PhabricatorCalendarEventAllDayTransaction' => 'PhabricatorCalendarEventTransactionType',
'PhabricatorCalendarEventCancelController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventCancelTransaction' => 'PhabricatorCalendarEventTransactionType',
'PhabricatorCalendarEventDateTransaction' => 'PhabricatorCalendarEventTransactionType',
'PhabricatorCalendarEventDeclineTransaction' => 'PhabricatorCalendarEventReplyTransaction',
'PhabricatorCalendarEventDescriptionTransaction' => 'PhabricatorCalendarEventTransactionType',
'PhabricatorCalendarEventDragController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventEditor' => 'PhabricatorApplicationTransactionEditor',
'PhabricatorCalendarEventEmailCommand' => 'MetaMTAEmailTransactionCommand',
'PhabricatorCalendarEventEndDateTransaction' => 'PhabricatorCalendarEventDateTransaction',
'PhabricatorCalendarEventFrequencyTransaction' => 'PhabricatorCalendarEventTransactionType',
'PhabricatorCalendarEventFulltextEngine' => 'PhabricatorFulltextEngine',
'PhabricatorCalendarEventIconTransaction' => 'PhabricatorCalendarEventTransactionType',
'PhabricatorCalendarEventInviteTransaction' => 'PhabricatorCalendarEventTransactionType',
'PhabricatorCalendarEventInvitee' => array(
'PhabricatorCalendarDAO',
'PhabricatorPolicyInterface',
@ -6643,13 +6669,19 @@ phutil_register_library_map(array(
'PhabricatorCalendarEventJoinController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventListController' => 'PhabricatorCalendarController',
'PhabricatorCalendarEventMailReceiver' => 'PhabricatorObjectMailReceiver',
'PhabricatorCalendarEventNameTransaction' => 'PhabricatorCalendarEventTransactionType',
'PhabricatorCalendarEventPHIDType' => 'PhabricatorPHIDType',
'PhabricatorCalendarEventQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorCalendarEventRSVPEmailCommand' => 'PhabricatorCalendarEventEmailCommand',
'PhabricatorCalendarEventRecurringTransaction' => 'PhabricatorCalendarEventTransactionType',
'PhabricatorCalendarEventReplyTransaction' => 'PhabricatorCalendarEventTransactionType',
'PhabricatorCalendarEventSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhabricatorCalendarEventTransaction' => 'PhabricatorApplicationTransaction',
'PhabricatorCalendarEventStartDateTransaction' => 'PhabricatorCalendarEventDateTransaction',
'PhabricatorCalendarEventTransaction' => 'PhabricatorModularTransaction',
'PhabricatorCalendarEventTransactionComment' => 'PhabricatorApplicationTransactionComment',
'PhabricatorCalendarEventTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
'PhabricatorCalendarEventTransactionType' => 'PhabricatorModularTransactionType',
'PhabricatorCalendarEventUntilDateTransaction' => 'PhabricatorCalendarEventDateTransaction',
'PhabricatorCalendarEventViewController' => 'PhabricatorCalendarController',
'PhabricatorCalendarHoliday' => 'PhabricatorCalendarDAO',
'PhabricatorCalendarHolidayTestCase' => 'PhabricatorTestCase',

View file

@ -44,32 +44,24 @@ final class PhabricatorCalendarEventRSVPEmailCommand
PhabricatorMetaMTAReceivedMail $mail,
$command,
array $argv) {
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
$status_declined = PhabricatorCalendarEventInvitee::STATUS_DECLINED;
$xactions = array();
$target = phutil_utf8_strtolower(implode(' ', $argv));
$rsvp = null;
$yes_values = $this->getYesValues();
$no_values = $this->getNoValues();
if (in_array($target, $yes_values)) {
$rsvp = $status_attending;
$rsvp = PhabricatorCalendarEventAcceptTransaction::TRANSACTIONTYPE;
} else if (in_array($target, $no_values)) {
$rsvp = $status_declined;
$rsvp = PhabricatorCalendarEventDeclineTransaction::TRANSACTIONTYPE;
} else {
$rsvp = null;
}
if ($rsvp === null) {
return array();
}
$xactions = array();
$xactions[] = $object->getApplicationTransactionTemplate()
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_INVITE)
->setNewValue(array($viewer->getPHID() => $rsvp));
->setTransactionType($rsvp)
->setNewValue(true);
return $xactions;
}

View file

@ -38,7 +38,7 @@ final class PhabricatorCalendarEventCancelController
$xaction = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_CANCEL)
PhabricatorCalendarEventCancelTransaction::TRANSACTIONTYPE)
->setNewValue(!$is_cancelled);
$editor = id(new PhabricatorCalendarEventEditor())

View file

@ -41,13 +41,14 @@ final class PhabricatorCalendarEventDragController
$viewer,
$end));
$xactions[] = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_START_DATE)
->setTransactionType(
PhabricatorCalendarEventStartDateTransaction::TRANSACTIONTYPE)
->setNewValue($start_value);
$xactions[] = id(new PhabricatorCalendarEventTransaction())
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_END_DATE)
->setTransactionType(
PhabricatorCalendarEventEndDateTransaction::TRANSACTIONTYPE)
->setNewValue($end_value);
$editor = id(new PhabricatorCalendarEventEditor())

View file

@ -33,9 +33,11 @@ final class PhabricatorCalendarEventJoinController
$validation_exception = null;
if ($request->isFormPost()) {
if ($is_join) {
$xaction_type = PhabricatorCalendarEventTransaction::TYPE_ACCEPT;
$xaction_type =
PhabricatorCalendarEventAcceptTransaction::TRANSACTIONTYPE;
} else {
$xaction_type = PhabricatorCalendarEventTransaction::TYPE_DECLINE;
$xaction_type =
PhabricatorCalendarEventDeclineTransaction::TRANSACTIONTYPE;
}
$xaction = id(new PhabricatorCalendarEventTransaction())

View file

@ -120,7 +120,7 @@ final class PhabricatorCalendarEventViewController
if ($event->isChildEvent()) {
$edit_label = pht('Edit This Instance');
} else {
$edit_label = pht('Edit');
$edit_label = pht('Edit Event');
}
$curtain = $this->newCurtainView($event);

View file

@ -81,7 +81,8 @@ final class PhabricatorCalendarEditEngine
->setLabel(pht('Name'))
->setDescription(pht('Name of the event.'))
->setIsRequired(true)
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_NAME)
->setTransactionType(
PhabricatorCalendarEventNameTransaction::TRANSACTIONTYPE)
->setConduitDescription(pht('Rename the event.'))
->setConduitTypeDescription(pht('New event name.'))
->setValue($object->getName()),
@ -90,7 +91,7 @@ final class PhabricatorCalendarEditEngine
->setLabel(pht('Description'))
->setDescription(pht('Description of the event.'))
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION)
PhabricatorCalendarEventDescriptionTransaction::TRANSACTIONTYPE)
->setConduitDescription(pht('Update the event description.'))
->setConduitTypeDescription(pht('New event description.'))
->setValue($object->getDescription()),
@ -100,7 +101,7 @@ final class PhabricatorCalendarEditEngine
->setLabel(pht('Cancelled'))
->setDescription(pht('Cancel the event.'))
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_CANCEL)
PhabricatorCalendarEventCancelTransaction::TRANSACTIONTYPE)
->setIsConduitOnly(true)
->setConduitDescription(pht('Cancel or restore the event.'))
->setConduitTypeDescription(pht('True to cancel the event.'))
@ -110,7 +111,8 @@ final class PhabricatorCalendarEditEngine
->setAliases(array('invite', 'invitee', 'invitees', 'inviteePHID'))
->setLabel(pht('Invitees'))
->setDatasource(new PhabricatorMetaMTAMailableDatasource())
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_INVITE)
->setTransactionType(
PhabricatorCalendarEventInviteTransaction::TRANSACTIONTYPE)
->setDescription(pht('Users invited to the event.'))
->setConduitDescription(pht('Change invited users.'))
->setConduitTypeDescription(pht('New event invitees.'))
@ -124,7 +126,7 @@ final class PhabricatorCalendarEditEngine
->setLabel(pht('Recurring'))
->setOptions(pht('One-Time Event'), pht('Recurring Event'))
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_RECURRING)
PhabricatorCalendarEventRecurringTransaction::TRANSACTIONTYPE)
->setDescription(pht('One time or recurring event.'))
->setConduitDescription(pht('Make the event recurring.'))
->setConduitTypeDescription(pht('Mark the event as a recurring event.'))
@ -135,7 +137,7 @@ final class PhabricatorCalendarEditEngine
->setLabel(pht('Frequency'))
->setOptions($frequency_options)
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_FREQUENCY)
PhabricatorCalendarEventFrequencyTransaction::TRANSACTIONTYPE)
->setDescription(pht('Recurring event frequency.'))
->setConduitDescription(pht('Change the event frequency.'))
->setConduitTypeDescription(pht('New event frequency.'))
@ -148,7 +150,7 @@ final class PhabricatorCalendarEditEngine
->setKey('until')
->setLabel(pht('Repeat Until'))
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE)
PhabricatorCalendarEventUntilDateTransaction::TRANSACTIONTYPE)
->setDescription(pht('Last instance of the event.'))
->setConduitDescription(pht('Change when the event repeats until.'))
->setConduitTypeDescription(pht('New final event time.'))
@ -159,7 +161,8 @@ final class PhabricatorCalendarEditEngine
->setKey('isAllDay')
->setLabel(pht('All Day'))
->setOptions(pht('Normal Event'), pht('All Day Event'))
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_ALL_DAY)
->setTransactionType(
PhabricatorCalendarEventAllDayTransaction::TRANSACTIONTYPE)
->setDescription(pht('Marks this as an all day event.'))
->setConduitDescription(pht('Make the event an all day event.'))
->setConduitTypeDescription(pht('Mark the event as an all day event.'))
@ -169,7 +172,7 @@ final class PhabricatorCalendarEditEngine
->setKey('start')
->setLabel(pht('Start'))
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_START_DATE)
PhabricatorCalendarEventStartDateTransaction::TRANSACTIONTYPE)
->setDescription(pht('Start time of the event.'))
->setConduitDescription(pht('Change the start time of the event.'))
->setConduitTypeDescription(pht('New event start time.'))
@ -179,7 +182,7 @@ final class PhabricatorCalendarEditEngine
->setKey('end')
->setLabel(pht('End'))
->setTransactionType(
PhabricatorCalendarEventTransaction::TYPE_END_DATE)
PhabricatorCalendarEventEndDateTransaction::TRANSACTIONTYPE)
->setDescription(pht('End time of the event.'))
->setConduitDescription(pht('Change the end time of the event.'))
->setConduitTypeDescription(pht('New event end time.'))
@ -189,7 +192,8 @@ final class PhabricatorCalendarEditEngine
->setKey('icon')
->setLabel(pht('Icon'))
->setIconSet(new PhabricatorCalendarIconSet())
->setTransactionType(PhabricatorCalendarEventTransaction::TYPE_ICON)
->setTransactionType(
PhabricatorCalendarEventIconTransaction::TRANSACTIONTYPE)
->setDescription(pht('Event icon.'))
->setConduitDescription(pht('Change the event icon.'))
->setConduitTypeDescription(pht('New event icon.'))

View file

@ -58,21 +58,6 @@ final class PhabricatorCalendarEventEditor
public function getTransactionTypes() {
$types = parent::getTransactionTypes();
$types[] = PhabricatorCalendarEventTransaction::TYPE_NAME;
$types[] = PhabricatorCalendarEventTransaction::TYPE_START_DATE;
$types[] = PhabricatorCalendarEventTransaction::TYPE_END_DATE;
$types[] = PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION;
$types[] = PhabricatorCalendarEventTransaction::TYPE_CANCEL;
$types[] = PhabricatorCalendarEventTransaction::TYPE_INVITE;
$types[] = PhabricatorCalendarEventTransaction::TYPE_ALL_DAY;
$types[] = PhabricatorCalendarEventTransaction::TYPE_ICON;
$types[] = PhabricatorCalendarEventTransaction::TYPE_ACCEPT;
$types[] = PhabricatorCalendarEventTransaction::TYPE_DECLINE;
$types[] = PhabricatorCalendarEventTransaction::TYPE_RECURRING;
$types[] = PhabricatorCalendarEventTransaction::TYPE_FREQUENCY;
$types[] = PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
@ -80,217 +65,6 @@ final class PhabricatorCalendarEventEditor
return $types;
}
protected function getCustomTransactionOldValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
return (int)$object->getIsRecurring();
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
return $object->getFrequencyUnit();
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
return $object->getRecurrenceEndDate();
case PhabricatorCalendarEventTransaction::TYPE_NAME:
return $object->getName();
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
return $object->getDateFrom();
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
return $object->getDateTo();
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
return $object->getDescription();
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
return $object->getIsCancelled();
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
return (int)$object->getIsAllDay();
case PhabricatorCalendarEventTransaction::TYPE_ICON:
return $object->getIcon();
case PhabricatorCalendarEventTransaction::TYPE_ACCEPT:
case PhabricatorCalendarEventTransaction::TYPE_DECLINE:
$actor_phid = $this->getActingAsPHID();
return $object->getUserInviteStatus($actor_phid);
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
$invitees = $object->getInvitees();
return mpull($invitees, 'getStatus', 'getInviteePHID');
}
return parent::getCustomTransactionOldValue($object, $xaction);
}
protected function getCustomTransactionNewValue(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
case PhabricatorCalendarEventTransaction::TYPE_NAME:
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
case PhabricatorCalendarEventTransaction::TYPE_ICON:
return $xaction->getNewValue();
case PhabricatorCalendarEventTransaction::TYPE_ACCEPT:
return PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
case PhabricatorCalendarEventTransaction::TYPE_DECLINE:
return PhabricatorCalendarEventInvitee::STATUS_DECLINED;
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
return (int)$xaction->getNewValue();
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
return $xaction->getNewValue()->getEpoch();
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
$status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
$invitees = $object->getInvitees();
foreach ($invitees as $key => $invitee) {
if ($invitee->getStatus() == $status_uninvited) {
unset($invitees[$key]);
}
}
$invitees = mpull($invitees, null, 'getInviteePHID');
$new = $xaction->getNewValue();
$new = array_fuse($new);
$all = array_keys($invitees + $new);
$map = array();
foreach ($all as $phid) {
$is_old = isset($invitees[$phid]);
$is_new = isset($new[$phid]);
if ($is_old && !$is_new) {
$map[$phid] = $status_uninvited;
} else if (!$is_old && $is_new) {
$map[$phid] = $status_invited;
} else {
$map[$phid] = $invitees[$phid]->getStatus();
}
}
// If we're creating this event and the actor is inviting themselves,
// mark them as attending.
if ($this->getIsNewObject()) {
$acting_phid = $this->getActingAsPHID();
if (isset($map[$acting_phid])) {
$map[$acting_phid] = $status_attending;
}
}
return $map;
}
return parent::getCustomTransactionNewValue($object, $xaction);
}
protected function applyCustomInternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
return $object->setIsRecurring((int)$xaction->getNewValue());
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
return $object->setRecurrenceFrequency(
array(
'rule' => $xaction->getNewValue(),
));
case PhabricatorCalendarEventTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
$object->setDateFrom($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
$object->setDateTo($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
$object->setRecurrenceEndDate($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
$object->setDescription($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
$object->setIsCancelled((int)$xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
$object->setIsAllDay((int)$xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_ICON:
$object->setIcon($xaction->getNewValue());
return;
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
case PhabricatorCalendarEventTransaction::TYPE_ACCEPT:
case PhabricatorCalendarEventTransaction::TYPE_DECLINE:
return;
}
return parent::applyCustomInternalTransaction($object, $xaction);
}
protected function applyCustomExternalTransaction(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_NAME:
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_DESCRIPTION:
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
case PhabricatorCalendarEventTransaction::TYPE_ICON:
return;
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
$map = $xaction->getNewValue();
$phids = array_keys($map);
$invitees = $object->getInvitees();
$invitees = mpull($invitees, null, 'getInviteePHID');
foreach ($phids as $phid) {
$invitee = idx($invitees, $phid);
if (!$invitee) {
$invitee = id(new PhabricatorCalendarEventInvitee())
->setEventPHID($object->getPHID())
->setInviteePHID($phid)
->setInviterPHID($this->getActingAsPHID());
$invitees[] = $invitee;
}
$invitee->setStatus($map[$phid])
->save();
}
$object->attachInvitees($invitees);
return;
case PhabricatorCalendarEventTransaction::TYPE_ACCEPT:
case PhabricatorCalendarEventTransaction::TYPE_DECLINE:
$acting_phid = $this->getActingAsPHID();
$invitees = $object->getInvitees();
$invitees = mpull($invitees, null, 'getInviteePHID');
$invitee = idx($invitees, $acting_phid);
if (!$invitee) {
$invitee = id(new PhabricatorCalendarEventInvitee())
->setEventPHID($object->getPHID())
->setInviteePHID($acting_phid)
->setInviterPHID($acting_phid);
$invitees[$acting_phid] = $invitee;
}
$invitee
->setStatus($xaction->getNewValue())
->save();
$object->attachInvitees($invitees);
return;
}
return parent::applyCustomExternalTransaction($object, $xaction);
}
protected function applyFinalEffects(
PhabricatorLiskDAO $object,
array $xactions) {
@ -302,26 +76,21 @@ final class PhabricatorCalendarEventEditor
$invalidate_phids = array();
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorCalendarEventTransaction::TYPE_ICON:
break;
case PhabricatorCalendarEventTransaction::TYPE_RECURRING:
case PhabricatorCalendarEventTransaction::TYPE_FREQUENCY:
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_CANCEL:
case PhabricatorCalendarEventTransaction::TYPE_ALL_DAY:
case PhabricatorCalendarEventUntilDateTransaction::TRANSACTIONTYPE:
case PhabricatorCalendarEventStartDateTransaction::TRANSACTIONTYPE:
case PhabricatorCalendarEventEndDateTransaction::TRANSACTIONTYPE:
case PhabricatorCalendarEventCancelTransaction::TRANSACTIONTYPE:
case PhabricatorCalendarEventAllDayTransaction::TRANSACTIONTYPE:
// For these kinds of changes, we need to invalidate the availabilty
// caches for all attendees.
$invalidate_all = true;
break;
case PhabricatorCalendarEventTransaction::TYPE_ACCEPT:
case PhabricatorCalendarEventTransaction::TYPE_DECLINE:
case PhabricatorCalendarEventAcceptTransaction::TRANSACTIONTYPE:
case PhabricatorCalendarEventDeclineTransaction::TRANSACTIONTYPE:
$acting_phid = $this->getActingAsPHID();
$invalidate_phids[$acting_phid] = $acting_phid;
break;
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
case PhabricatorCalendarEventInviteTransaction::TRANSACTIONTYPE:
foreach ($xaction->getNewValue() as $phid => $ignored) {
$invalidate_phids[$phid] = $phid;
}
@ -357,14 +126,15 @@ final class PhabricatorCalendarEventEditor
protected function validateAllTransactions(
PhabricatorLiskDAO $object,
array $xactions) {
$start_date_xaction =
PhabricatorCalendarEventTransaction::TYPE_START_DATE;
PhabricatorCalendarEventStartDateTransaction::TRANSACTIONTYPE;
$end_date_xaction =
PhabricatorCalendarEventTransaction::TYPE_END_DATE;
PhabricatorCalendarEventEndDateTransaction::TRANSACTIONTYPE;
$is_recurrence_xaction =
PhabricatorCalendarEventTransaction::TYPE_RECURRING;
PhabricatorCalendarEventRecurringTransaction::TRANSACTIONTYPE;
$recurrence_end_xaction =
PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE;
PhabricatorCalendarEventUntilDateTransaction::TRANSACTIONTYPE;
$start_date = $object->getDateFrom();
$end_date = $object->getDateTo();
@ -386,19 +156,16 @@ final class PhabricatorCalendarEventEditor
}
if ($start_date > $end_date) {
$type = PhabricatorCalendarEventTransaction::TYPE_END_DATE;
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
$end_date_xaction,
pht('Invalid'),
pht('End date must be after start date.'),
null);
}
if ($recurrence_end && !$is_recurring) {
$type =
PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE;
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
$recurrence_end_xaction,
pht('Invalid'),
pht('Event must be recurring to have a recurrence end date.').
null);
@ -407,97 +174,6 @@ final class PhabricatorCalendarEventEditor
return $errors;
}
protected function validateTransaction(
PhabricatorLiskDAO $object,
$type,
array $xactions) {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case PhabricatorCalendarEventTransaction::TYPE_NAME:
$missing = $this->validateIsEmptyTextField(
$object->getName(),
$xactions);
if ($missing) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Event name is required.'),
nonempty(last($xactions), null));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
case PhabricatorCalendarEventTransaction::TYPE_INVITE:
$old = $object->getInvitees();
$old = mpull($old, null, 'getInviteePHID');
foreach ($xactions as $xaction) {
$new = $xaction->getNewValue();
$new = array_fuse($new);
$add = array_diff_key($new, $old);
if (!$add) {
continue;
}
// In the UI, we only allow you to invite mailable objects, but there
// is no definitive marker for "invitable object" today. Just allow
// any valid object to be invited.
$objects = id(new PhabricatorObjectQuery())
->setViewer($this->getActor())
->withPHIDs($add)
->execute();
$objects = mpull($objects, null, 'getPHID');
foreach ($add as $phid) {
if (isset($objects[$phid])) {
continue;
}
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
pht(
'Invitee "%s" identifies an object that does not exist or '.
'which you do not have permission to view.',
$phid));
}
}
break;
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
foreach ($xactions as $xaction) {
if ($xaction->getNewValue()->isValid()) {
continue;
}
switch ($type) {
case PhabricatorCalendarEventTransaction::TYPE_START_DATE:
$message = pht('Start date is invalid.');
break;
case PhabricatorCalendarEventTransaction::TYPE_END_DATE:
$message = pht('End date is invalid.');
break;
case PhabricatorCalendarEventTransaction::TYPE_RECURRENCE_END_DATE:
$message = pht('Repeat until date is invalid.');
break;
}
$errors[] = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Invalid'),
$message,
$xaction);
}
break;
}
return $errors;
}
protected function shouldPublishFeedStory(
PhabricatorLiskDAO $object,
array $xactions) {

View file

@ -1,22 +1,7 @@
<?php
final class PhabricatorCalendarEventTransaction
extends PhabricatorApplicationTransaction {
const TYPE_NAME = 'calendar.name';
const TYPE_START_DATE = 'calendar.startdate';
const TYPE_END_DATE = 'calendar.enddate';
const TYPE_DESCRIPTION = 'calendar.description';
const TYPE_CANCEL = 'calendar.cancel';
const TYPE_ALL_DAY = 'calendar.allday';
const TYPE_ICON = 'calendar.icon';
const TYPE_INVITE = 'calendar.invite';
const TYPE_ACCEPT = 'calendar.accept';
const TYPE_DECLINE = 'calendar.decline';
const TYPE_RECURRING = 'calendar.recurring';
const TYPE_FREQUENCY = 'calendar.frequency';
const TYPE_RECURRENCE_END_DATE = 'calendar.recurrenceenddate';
extends PhabricatorModularTransaction {
const MAILTAG_RESCHEDULE = 'calendar-reschedule';
const MAILTAG_CONTENT = 'calendar-content';
@ -34,580 +19,25 @@ final class PhabricatorCalendarEventTransaction
return new PhabricatorCalendarEventTransactionComment();
}
public function getRequiredHandlePHIDs() {
$phids = parent::getRequiredHandlePHIDs();
switch ($this->getTransactionType()) {
case self::TYPE_NAME:
case self::TYPE_START_DATE:
case self::TYPE_END_DATE:
case self::TYPE_DESCRIPTION:
case self::TYPE_CANCEL:
case self::TYPE_ALL_DAY:
case self::TYPE_RECURRING:
case self::TYPE_FREQUENCY:
case self::TYPE_RECURRENCE_END_DATE:
$phids[] = $this->getObjectPHID();
break;
case self::TYPE_INVITE:
$new = $this->getNewValue();
foreach ($new as $phid => $status) {
$phids[] = $phid;
}
break;
}
return $phids;
}
public function shouldHide() {
$old = $this->getOldValue();
switch ($this->getTransactionType()) {
case self::TYPE_START_DATE:
case self::TYPE_END_DATE:
case self::TYPE_DESCRIPTION:
case self::TYPE_CANCEL:
case self::TYPE_ALL_DAY:
case self::TYPE_INVITE:
case self::TYPE_RECURRING:
case self::TYPE_FREQUENCY:
case self::TYPE_RECURRENCE_END_DATE:
return ($old === null);
}
return parent::shouldHide();
}
public function getIcon() {
switch ($this->getTransactionType()) {
case self::TYPE_ICON:
return $this->getNewValue();
case self::TYPE_NAME:
case self::TYPE_START_DATE:
case self::TYPE_END_DATE:
case self::TYPE_DESCRIPTION:
case self::TYPE_ALL_DAY:
case self::TYPE_CANCEL:
case self::TYPE_RECURRING:
case self::TYPE_FREQUENCY:
case self::TYPE_RECURRENCE_END_DATE:
return 'fa-pencil';
break;
case self::TYPE_INVITE:
return 'fa-user-plus';
break;
}
return parent::getIcon();
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_NAME:
if ($old === null) {
return pht(
'%s created this event.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s changed the name of this event from %s to %s.',
$this->renderHandleLink($author_phid),
$old,
$new);
}
case self::TYPE_START_DATE:
if ($old) {
return pht(
'%s edited the start date of this event.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_END_DATE:
if ($old) {
return pht(
'%s edited the end date of this event.',
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_DESCRIPTION:
return pht(
"%s updated the event's description.",
$this->renderHandleLink($author_phid));
case self::TYPE_ALL_DAY:
if ($new) {
return pht(
'%s made this an all day event.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s converted this from an all day event.',
$this->renderHandleLink($author_phid));
}
case self::TYPE_ICON:
$set = new PhabricatorCalendarIconSet();
return pht(
'%s set this event\'s icon to %s.',
$this->renderHandleLink($author_phid),
$set->getIconLabel($new));
break;
case self::TYPE_CANCEL:
if ($new) {
return pht(
'%s cancelled this event.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s reinstated this event.',
$this->renderHandleLink($author_phid));
}
case self::TYPE_ACCEPT:
return pht(
'%s is attending this event.',
$this->renderHandleLink($author_phid));
case self::TYPE_DECLINE:
return pht(
'%s declined this event.',
$this->renderHandleLink($author_phid));
case self::TYPE_INVITE:
$text = null;
list($old, $new) = $this->getSimpleInvites($old, $new);
if (count($old) === 1
&& count($new) === 1
&& isset($old[$author_phid])) {
// user joined/declined/accepted event themself
$old_status = $old[$author_phid];
$new_status = $new[$author_phid];
if ($old_status !== $new_status) {
switch ($new_status) {
case PhabricatorCalendarEventInvitee::STATUS_INVITED:
$text = pht(
'%s has joined this event.',
$this->renderHandleLink($author_phid));
break;
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
$text = pht(
'%s is attending this event.',
$this->renderHandleLink($author_phid));
break;
case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
case PhabricatorCalendarEventInvitee::STATUS_UNINVITED:
$text = pht(
'%s has declined this event.',
$this->renderHandleLink($author_phid));
break;
default:
$text = pht(
'%s has changed their status for this event.',
$this->renderHandleLink($author_phid));
break;
}
}
} else {
// user changed status for many users
$self_joined = null;
$self_declined = null;
$added = array();
$uninvited = array();
foreach ($new as $phid => $status) {
if ($status == PhabricatorCalendarEventInvitee::STATUS_INVITED
|| $status == PhabricatorCalendarEventInvitee::STATUS_ATTENDING) {
// added users
$added[] = $phid;
} else if (
$status == PhabricatorCalendarEventInvitee::STATUS_DECLINED
|| $status == PhabricatorCalendarEventInvitee::STATUS_UNINVITED) {
$uninvited[] = $phid;
}
}
$count_added = count($added);
$count_uninvited = count($uninvited);
$added_text = null;
$uninvited_text = null;
if ($count_added > 0 && $count_uninvited == 0) {
$added_text = $this->renderHandleList($added);
$text = pht('%s invited %s.',
$this->renderHandleLink($author_phid),
$added_text);
} else if ($count_added > 0 && $count_uninvited > 0) {
$added_text = $this->renderHandleList($added);
$uninvited_text = $this->renderHandleList($uninvited);
$text = pht('%s invited %s and uninvited %s.',
$this->renderHandleLink($author_phid),
$added_text,
$uninvited_text);
} else if ($count_added == 0 && $count_uninvited > 0) {
$uninvited_text = $this->renderHandleList($uninvited);
$text = pht('%s uninvited %s.',
$this->renderHandleLink($author_phid),
$uninvited_text);
} else {
$text = pht('%s updated the invitee list.',
$this->renderHandleLink($author_phid));
}
}
return $text;
case self::TYPE_RECURRING:
$text = pht('%s made this event recurring.',
$this->renderHandleLink($author_phid));
return $text;
case self::TYPE_FREQUENCY:
$rule = $new;
if (is_array($rule)) {
$rule = idx($rule, 'rule');
}
$text = '';
switch ($rule) {
case PhabricatorCalendarEvent::FREQUENCY_DAILY:
$text = pht('%s set this event to repeat daily.',
$this->renderHandleLink($author_phid));
break;
case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
$text = pht('%s set this event to repeat weekly.',
$this->renderHandleLink($author_phid));
break;
case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
$text = pht('%s set this event to repeat monthly.',
$this->renderHandleLink($author_phid));
break;
case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
$text = pht('%s set this event to repeat yearly.',
$this->renderHandleLink($author_phid));
break;
}
return $text;
case self::TYPE_RECURRENCE_END_DATE:
$text = pht('%s has changed the recurrence end date of this event.',
$this->renderHandleLink($author_phid));
return $text;
}
return parent::getTitle();
}
public function getTitleForFeed() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
$viewer = $this->getViewer();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_NAME:
if ($old === null) {
return pht(
'%s created %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
} else {
return pht(
'%s changed the name of %s from %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid),
$old,
$new);
}
case self::TYPE_START_DATE:
if ($old) {
$old = phabricator_datetime($old, $viewer);
$new = phabricator_datetime($new, $viewer);
return pht(
'%s changed the start date of %s from %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid),
$old,
$new);
}
break;
case self::TYPE_END_DATE:
if ($old) {
$old = phabricator_datetime($old, $viewer);
$new = phabricator_datetime($new, $viewer);
return pht(
'%s edited the end date of %s from %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid),
$old,
$new);
}
break;
case self::TYPE_DESCRIPTION:
return pht(
'%s updated the description of %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
case self::TYPE_ALL_DAY:
if ($new) {
return pht(
'%s made %s an all day event.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
} else {
return pht(
'%s converted %s from an all day event.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
}
case self::TYPE_ICON:
$set = new PhabricatorCalendarIconSet();
return pht(
'%s set the icon for %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid),
$set->getIconLabel($new));
case self::TYPE_CANCEL:
if ($new) {
return pht(
'%s cancelled %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
} else {
return pht(
'%s reinstated %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
}
case self::TYPE_ACCEPT:
return pht(
'%s is attending %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
case self::TYPE_DECLINE:
return pht(
'%s declined %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
case self::TYPE_INVITE:
$text = null;
list($old, $new) = $this->getSimpleInvites($old, $new);
if (count($old) === 1
&& count($new) === 1
&& isset($old[$author_phid])) {
// user joined/declined/accepted event themself
$old_status = $old[$author_phid];
$new_status = $new[$author_phid];
if ($old_status !== $new_status) {
switch ($new_status) {
case PhabricatorCalendarEventInvitee::STATUS_INVITED:
$text = pht(
'%s has joined %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
case PhabricatorCalendarEventInvitee::STATUS_ATTENDING:
$text = pht(
'%s is attending %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
case PhabricatorCalendarEventInvitee::STATUS_DECLINED:
case PhabricatorCalendarEventInvitee::STATUS_UNINVITED:
$text = pht(
'%s has declined %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
default:
$text = pht(
'%s has changed their status of %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
}
}
} else {
// user changed status for many users
$self_joined = null;
$self_declined = null;
$added = array();
$uninvited = array();
foreach ($new as $phid => $status) {
if ($status == PhabricatorCalendarEventInvitee::STATUS_INVITED
|| $status == PhabricatorCalendarEventInvitee::STATUS_ATTENDING) {
// added users
$added[] = $phid;
} else if (
$status == PhabricatorCalendarEventInvitee::STATUS_DECLINED
|| $status == PhabricatorCalendarEventInvitee::STATUS_UNINVITED) {
$uninvited[] = $phid;
}
}
$count_added = count($added);
$count_uninvited = count($uninvited);
$added_text = null;
$uninvited_text = null;
if ($count_added > 0 && $count_uninvited == 0) {
$added_text = $this->renderHandleList($added);
$text = pht('%s invited %s to %s.',
$this->renderHandleLink($author_phid),
$added_text,
$this->renderHandleLink($object_phid));
} else if ($count_added > 0 && $count_uninvited > 0) {
$added_text = $this->renderHandleList($added);
$uninvited_text = $this->renderHandleList($uninvited);
$text = pht('%s invited %s and uninvited %s to %s.',
$this->renderHandleLink($author_phid),
$added_text,
$uninvited_text,
$this->renderHandleLink($object_phid));
} else if ($count_added == 0 && $count_uninvited > 0) {
$uninvited_text = $this->renderHandleList($uninvited);
$text = pht('%s uninvited %s to %s.',
$this->renderHandleLink($author_phid),
$uninvited_text,
$this->renderHandleLink($object_phid));
} else {
$text = pht('%s updated the invitee list of %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
}
}
return $text;
case self::TYPE_RECURRING:
$text = pht('%s made %s a recurring event.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
return $text;
case self::TYPE_FREQUENCY:
$rule = $new;
if (is_array($rule)) {
$rule = idx($rule, 'rule');
}
$text = '';
switch ($rule) {
case PhabricatorCalendarEvent::FREQUENCY_DAILY:
$text = pht('%s set %s to repeat daily.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
$text = pht('%s set %s to repeat weekly.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
$text = pht('%s set %s to repeat monthly.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
$text = pht('%s set %s to repeat yearly.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid));
break;
}
return $text;
case self::TYPE_RECURRENCE_END_DATE:
$text = pht('%s set the recurrence end date of %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($object_phid),
$new);
return $text;
}
return parent::getTitleForFeed();
}
public function getColor() {
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_NAME:
case self::TYPE_START_DATE:
case self::TYPE_END_DATE:
case self::TYPE_DESCRIPTION:
case self::TYPE_CANCEL:
case self::TYPE_INVITE:
return PhabricatorTransactions::COLOR_GREEN;
}
return parent::getColor();
}
public function hasChangeDetails() {
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
return ($this->getOldValue() !== null);
}
return parent::hasChangeDetails();
}
public function renderChangeDetails(PhabricatorUser $viewer) {
switch ($this->getTransactionType()) {
case self::TYPE_DESCRIPTION:
$old = $this->getOldValue();
$new = $this->getNewValue();
return $this->renderTextCorpusChangeDetails(
$viewer,
$old,
$new);
}
return parent::renderChangeDetails($viewer);
public function getBaseTransactionClass() {
return 'PhabricatorCalendarEventTransactionType';
}
public function getMailTags() {
$tags = array();
switch ($this->getTransactionType()) {
case self::TYPE_NAME:
case self::TYPE_DESCRIPTION:
case self::TYPE_INVITE:
case self::TYPE_ICON:
case PhabricatorCalendarEventNameTransaction::TRANSACTIONTYPE:
case PhabricatorCalendarEventDescriptionTransaction::TRANSACTIONTYPE:
case PhabricatorCalendarEventInviteTransaction::TRANSACTIONTYPE:
$tags[] = self::MAILTAG_CONTENT;
break;
case self::TYPE_START_DATE:
case self::TYPE_END_DATE:
case self::TYPE_CANCEL:
case PhabricatorCalendarEventStartDateTransaction::TRANSACTIONTYPE:
case PhabricatorCalendarEventEndDateTransaction::TRANSACTIONTYPE:
case PhabricatorCalendarEventCancelTransaction::TRANSACTIONTYPE:
$tags[] = self::MAILTAG_RESCHEDULE;
break;
}
return $tags;
}
private function getSimpleInvites(array $old, array $new) {
// Fill in any new invitees as "uninvited" in the old data, to make
// some of the rendering logic a little easier.
$status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
$old = $old + array_fill_keys(array_keys($new), $status_uninvited);
$all = $old + $new;
foreach (array_keys($all) as $key) {
// If the invitee exists in both the old and new lists with the same
// value, remove it from both.
if (isset($old[$key]) && isset($new[$key])) {
if ($old[$key] == $new[$key]) {
unset($old[$key]);
unset($new[$key]);
}
}
}
return array($old, $new);
}
}

View file

@ -0,0 +1,25 @@
<?php
final class PhabricatorCalendarEventAcceptTransaction
extends PhabricatorCalendarEventReplyTransaction {
const TRANSACTIONTYPE = 'calendar.accept';
public function generateNewValue($object, $value) {
return PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
}
public function getTitle() {
return pht(
'%s is attending this event.',
$this->renderAuthor());
}
public function getTitleForFeed() {
return pht(
'%s is attending %s.',
$this->renderAuthor(),
$this->renderObject());
}
}

View file

@ -0,0 +1,46 @@
<?php
final class PhabricatorCalendarEventAllDayTransaction
extends PhabricatorCalendarEventTransactionType {
const TRANSACTIONTYPE = 'calendar.allday';
public function generateOldValue($object) {
return (int)$object->getIsAllDay();
}
public function generateNewValue($object, $value) {
return (int)$value;
}
public function applyInternalEffects($object, $value) {
$object->setIsAllDay($value);
}
public function getTitle() {
if ($this->getNewValue()) {
return pht(
'%s changed this as an all day event.',
$this->renderAuthor());
} else {
return pht(
'%s conveted this from an all day event.',
$this->renderAuthor());
}
}
public function getTitleForFeed() {
if ($this->getNewValue()) {
return pht(
'%s changed %s to an all day event.',
$this->renderAuthor(),
$this->renderObject());
} else {
return pht(
'%s converted %s from an all day event.',
$this->renderAuthor(),
$this->renderObject());
}
}
}

View file

@ -0,0 +1,46 @@
<?php
final class PhabricatorCalendarEventCancelTransaction
extends PhabricatorCalendarEventTransactionType {
const TRANSACTIONTYPE = 'calendar.cancel';
public function generateOldValue($object) {
return (int)$object->getIsCancelled();
}
public function generateNewValue($object, $value) {
return (int)$value;
}
public function applyInternalEffects($object, $value) {
$object->setIsCancelled($value);
}
public function getTitle() {
if ($this->getNewValue()) {
return pht(
'%s cancelled this event.',
$this->renderAuthor());
} else {
return pht(
'%s reinstated this event.',
$this->renderAuthor());
}
}
public function getTitleForFeed() {
if ($this->getNewValue()) {
return pht(
'%s cancelled %s.',
$this->renderAuthor(),
$this->renderObject());
} else {
return pht(
'%s reinstated %s.',
$this->renderAuthor(),
$this->renderObject());
}
}
}

View file

@ -0,0 +1,27 @@
<?php
abstract class PhabricatorCalendarEventDateTransaction
extends PhabricatorCalendarEventTransactionType {
abstract protected function getInvalidDateMessage();
public function generateNewValue($object, $value) {
return $value->getEpoch();
}
public function validateTransactions($object, array $xactions) {
$errors = array();
foreach ($xactions as $xaction) {
if ($xaction->getNewValue()->isValid()) {
continue;
}
$message = $this->getInvalidDateMessage();
$errors[] = $this->newInvalidError($message, $xaction);
}
return $errors;
}
}

View file

@ -0,0 +1,25 @@
<?php
final class PhabricatorCalendarEventDeclineTransaction
extends PhabricatorCalendarEventReplyTransaction {
const TRANSACTIONTYPE = 'calendar.decline';
public function generateNewValue($object, $value) {
return PhabricatorCalendarEventInvitee::STATUS_DECLINED;
}
public function getTitle() {
return pht(
'%s declined this event.',
$this->renderAuthor());
}
public function getTitleForFeed() {
return pht(
'%s declined %s.',
$this->renderAuthor(),
$this->renderObject());
}
}

View file

@ -0,0 +1,42 @@
<?php
final class PhabricatorCalendarEventDescriptionTransaction
extends PhabricatorCalendarEventTransactionType {
const TRANSACTIONTYPE = 'calendar.description';
public function generateOldValue($object) {
return $object->getDescription();
}
public function applyInternalEffects($object, $value) {
$object->setDescription($value);
}
public function getTitle() {
return pht(
'%s updated the event description.',
$this->renderAuthor());
}
public function getTitleForFeed() {
return pht(
'%s updated the event description for %s.',
$this->renderAuthor(),
$this->renderObject());
}
public function hasChangeDetailView() {
return true;
}
public function newChangeDetailView() {
$viewer = $this->getViewer();
return id(new PhabricatorApplicationTransactionTextDiffDetailView())
->setViewer($viewer)
->setOldText($this->getOldValue())
->setNewText($this->getNewValue());
}
}

View file

@ -0,0 +1,37 @@
<?php
final class PhabricatorCalendarEventEndDateTransaction
extends PhabricatorCalendarEventDateTransaction {
const TRANSACTIONTYPE = 'calendar.enddate';
public function generateOldValue($object) {
return $object->getDateTo();
}
public function applyInternalEffects($object, $value) {
$object->setDateTo($value);
}
public function getTitle() {
return pht(
'%s changed the end date for this event from %s to %s.',
$this->renderAuthor(),
$this->renderOldDate(),
$this->renderNewDate());
}
public function getTitleForFeed() {
return pht(
'%s changed the end date for %s from %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderOldDate(),
$this->renderNewDate());
}
protected function getInvalidDateMessage() {
return pht('End date is invalid.');
}
}

View file

@ -0,0 +1,75 @@
<?php
final class PhabricatorCalendarEventFrequencyTransaction
extends PhabricatorCalendarEventTransactionType {
const TRANSACTIONTYPE = 'calendar.frequency';
public function generateOldValue($object) {
return $object->getFrequencyUnit();
}
public function applyInternalEffects($object, $value) {
$object->setRecurrenceFrequency(
array(
'rule' => $value,
));
}
public function getTitle() {
$frequency = $this->getFrequencyUnit($this->getNewValue());
switch ($frequency) {
case PhabricatorCalendarEvent::FREQUENCY_DAILY:
return pht(
'%s set this event to repeat daily.',
$this->renderAuthor());
case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
return pht(
'%s set this event to repeat weekly.',
$this->renderAuthor());
case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
return pht(
'%s set this event to repeat monthly.',
$this->renderAuthor());
case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
return pht(
'%s set this event to repeat yearly.',
$this->renderAuthor());
}
}
public function getTitleForFeed() {
$frequency = $this->getFrequencyUnit($this->getNewValue());
switch ($frequency) {
case PhabricatorCalendarEvent::FREQUENCY_DAILY:
return pht(
'%s set %s to repeat daily.',
$this->renderAuthor(),
$this->renderObject());
case PhabricatorCalendarEvent::FREQUENCY_WEEKLY:
return pht(
'%s set %s to repeat weekly.',
$this->renderAuthor(),
$this->renderObject());
case PhabricatorCalendarEvent::FREQUENCY_MONTHLY:
return pht(
'%s set %s to repeat monthly.',
$this->renderAuthor(),
$this->renderObject());
case PhabricatorCalendarEvent::FREQUENCY_YEARLY:
return pht(
'%s set %s to repeat yearly.',
$this->renderAuthor(),
$this->renderObject());
}
}
private function getFrequencyUnit($value) {
if (is_array($value)) {
$value = idx($value, 'rule');
} else {
return $value;
}
}
}

View file

@ -0,0 +1,44 @@
<?php
final class PhabricatorCalendarEventIconTransaction
extends PhabricatorCalendarEventTransactionType {
const TRANSACTIONTYPE = 'calendar.icon';
public function generateOldValue($object) {
return $object->getIcon();
}
public function applyInternalEffects($object, $value) {
$object->setIcon($value);
}
public function getTitle() {
$old = $this->getIconLabel($this->getOldValue());
$new = $this->getIconLabel($this->getNewValue());
return pht(
'%s changed the event icon from %s to %s.',
$this->renderAuthor(),
$this->renderValue($old),
$this->renderValue($new));
}
public function getTitleForFeed() {
$old = $this->getIconLabel($this->getOldValue());
$new = $this->getIconLabel($this->getNewValue());
return pht(
'%s changed the icon for %s from %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderValue($old),
$this->renderValue($new));
}
private function getIconLabel($icon) {
$set = new PhabricatorCalendarIconSet();
return $set->getIconLabel($icon);
}
}

View file

@ -0,0 +1,191 @@
<?php
final class PhabricatorCalendarEventInviteTransaction
extends PhabricatorCalendarEventTransactionType {
const TRANSACTIONTYPE = 'calendar.invite';
public function generateOldValue($object) {
$status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
$invitees = $object->getInvitees();
foreach ($invitees as $key => $invitee) {
if ($invitee->getStatus() == $status_uninvited) {
unset($invitees[$key]);
}
}
return mpull($invitees, 'getStatus', 'getInviteePHID');
}
public function generateNewValue($object, $value) {
$status_invited = PhabricatorCalendarEventInvitee::STATUS_INVITED;
$status_uninvited = PhabricatorCalendarEventInvitee::STATUS_UNINVITED;
$status_attending = PhabricatorCalendarEventInvitee::STATUS_ATTENDING;
$invitees = $this->generateOldValue($object);
$new = array_fuse($value);
$all = array_keys($invitees + $new);
$map = array();
foreach ($all as $phid) {
$is_old = isset($invitees[$phid]);
$is_new = isset($new[$phid]);
if ($is_old && !$is_new) {
$map[$phid] = $status_uninvited;
} else if (!$is_old && $is_new) {
$map[$phid] = $status_invited;
} else {
$map[$phid] = $invitees[$phid];
}
}
// If we're creating this event and the actor is inviting themselves,
// mark them as attending.
if ($this->isNewObject()) {
$acting_phid = $this->getActingAsPHID();
if (isset($map[$acting_phid])) {
$map[$acting_phid] = $status_attending;
}
}
return $map;
}
public function applyExternalEffects($object, $value) {
$phids = array_keys($value);
$invitees = $object->getInvitees();
$invitees = mpull($invitees, null, 'getInviteePHID');
foreach ($phids as $phid) {
$invitee = idx($invitees, $phid);
if (!$invitee) {
$invitee = id(new PhabricatorCalendarEventInvitee())
->setEventPHID($object->getPHID())
->setInviteePHID($phid)
->setInviterPHID($this->getActingAsPHID());
$invitees[] = $invitee;
}
$invitee->setStatus($value[$phid])
->save();
}
$object->attachInvitees($invitees);
}
public function validateTransactions($object, array $xactions) {
$actor = $this->getActor();
$errors = array();
$old = $object->getInvitees();
$old = mpull($old, null, 'getInviteePHID');
foreach ($xactions as $xaction) {
$new = $xaction->getNewValue();
$new = array_fuse($new);
$add = array_diff_key($new, $old);
if (!$add) {
continue;
}
// In the UI, we only allow you to invite mailable objects, but there
// is no definitive marker for "invitable object" today. Just allow
// any valid object to be invited.
$objects = id(new PhabricatorObjectQuery())
->setViewer($actor)
->withPHIDs($add)
->execute();
$objects = mpull($objects, null, 'getPHID');
foreach ($add as $phid) {
if (isset($objects[$phid])) {
continue;
}
$errors[] = $this->newInvalidError(
pht(
'Invitee "%s" identifies an object that does not exist or '.
'which you do not have permission to view.',
$phid),
$xaction);
}
}
return $errors;
}
public function getIcon() {
return 'fa-user-plus';
}
public function getTitle() {
list($add, $rem) = $this->getChanges();
if ($add && !$rem) {
return pht(
'%s invited %s attendee(s): %s.',
$this->renderAuthor(),
phutil_count($add),
$this->renderHandleList($add));
} else if (!$add && $rem) {
return pht(
'%s uninvited %s attendee(s): %s.',
$this->renderAuthor(),
phutil_count($rem),
$this->renderHandleList($rem));
} else {
return pht(
'%s invited %s attendee(s): %s; uninvinted %s attendee(s): %s.',
$this->renderAuthor(),
phutil_count($add),
$this->renderHandleList($add),
phutil_count($rem),
$this->renderHandleList($rem));
}
}
public function getTitleForFeed() {
list($add, $rem) = $this->getChanges();
if ($add && !$rem) {
return pht(
'%s invited %s attendee(s) to %s: %s.',
$this->renderAuthor(),
phutil_count($add),
$this->renderObject(),
$this->renderHandleList($add));
} else if (!$add && $rem) {
return pht(
'%s uninvited %s attendee(s) to %s: %s.',
$this->renderAuthor(),
phutil_count($rem),
$this->renderObject(),
$this->renderHandleList($rem));
} else {
return pht(
'%s updated the invite list for %s, invited %s: %s; '.
'uninvinted %s: %s.',
$this->renderAuthor(),
$this->renderObject(),
phutil_count($add),
$this->renderHandleList($add),
phutil_count($rem),
$this->renderHandleList($rem));
}
}
private function getChanges() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$add = array_diff_key($new, $old);
$rem = array_diff_key($old, $new);
$add = array_keys($add);
$rem = array_keys($rem);
return array(array_fuse($add), array_fuse($rem));
}
}

View file

@ -0,0 +1,44 @@
<?php
final class PhabricatorCalendarEventNameTransaction
extends PhabricatorCalendarEventTransactionType {
const TRANSACTIONTYPE = 'calendar.name';
public function generateOldValue($object) {
return $object->getName();
}
public function applyInternalEffects($object, $value) {
$object->setName($value);
}
public function getTitle() {
return pht(
'%s renamed this event from %s to %s.',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
}
public function getTitleForFeed() {
return pht(
'%s renamed %s from %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderOldValue(),
$this->renderNewValue());
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
$errors[] = $this->newRequiredError(
pht('Events must have a name.'));
}
return $errors;
}
}

View file

@ -0,0 +1,44 @@
<?php
final class PhabricatorCalendarEventRecurringTransaction
extends PhabricatorCalendarEventTransactionType {
const TRANSACTIONTYPE = 'calendar.recurring';
public function generateOldValue($object) {
return (int)$object->getIsRecurring();
}
public function generateNewValue($object, $value) {
return (int)$value;
}
public function applyInternalEffects($object, $value) {
$object->setIsRecurring($value);
}
public function validateTransactions($object, array $xactions) {
$errors = array();
$old = $object->getIsRecurring();
foreach ($xactions as $xaction) {
if ($this->getIsNewObject()) {
continue;
}
if ($xaction->getNewValue() == $old) {
continue;
}
$errors[] = $this->newInvalidError(
pht(
'An event can only be made recurring when it is created. '.
'You can not convert an existing event into a recurring '.
'event or vice versa.'),
$xaction);
}
return $errors;
}
}

View file

@ -0,0 +1,33 @@
<?php
abstract class PhabricatorCalendarEventReplyTransaction
extends PhabricatorCalendarEventTransactionType {
public function generateOldValue($object) {
$actor_phid = $this->getActingAsPHID();
return $object->getUserInviteStatus($actor_phid);
}
public function applyExternalEffects($object, $value) {
$acting_phid = $this->getActingAsPHID();
$invitees = $object->getInvitees();
$invitees = mpull($invitees, null, 'getInviteePHID');
$invitee = idx($invitees, $acting_phid);
if (!$invitee) {
$invitee = id(new PhabricatorCalendarEventInvitee())
->setEventPHID($object->getPHID())
->setInviteePHID($acting_phid)
->setInviterPHID($acting_phid);
$invitees[$acting_phid] = $invitee;
}
$invitee
->setStatus($value)
->save();
$object->attachInvitees($invitees);
}
}

View file

@ -0,0 +1,37 @@
<?php
final class PhabricatorCalendarEventStartDateTransaction
extends PhabricatorCalendarEventDateTransaction {
const TRANSACTIONTYPE = 'calendar.startdate';
public function generateOldValue($object) {
return $object->getDateFrom();
}
public function applyInternalEffects($object, $value) {
$object->setDateFrom($value);
}
public function getTitle() {
return pht(
'%s changed the start date for this event from %s to %s.',
$this->renderAuthor(),
$this->renderOldDate(),
$this->renderNewDate());
}
public function getTitleForFeed() {
return pht(
'%s changed the start date for %s from %s to %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderOldDate(),
$this->renderNewDate());
}
protected function getInvalidDateMessage() {
return pht('Start date is invalid.');
}
}

View file

@ -0,0 +1,4 @@
<?php
abstract class PhabricatorCalendarEventTransactionType
extends PhabricatorModularTransactionType {}

View file

@ -0,0 +1,35 @@
<?php
final class PhabricatorCalendarEventUntilDateTransaction
extends PhabricatorCalendarEventDateTransaction {
const TRANSACTIONTYPE = 'calendar.recurrenceenddate';
public function generateOldValue($object) {
return $object->getRecurrenceEndDate();
}
public function applyInternalEffects($object, $value) {
$object->setRecurrenceEndDate($value);
}
public function getTitle() {
return pht(
'%s changed this event to repeat until %s.',
$this->renderAuthor(),
$this->renderNewDate());
}
public function getTitleForFeed() {
return pht(
'%s changed %s to repeat until %s.',
$this->renderAuthor(),
$this->renderObject(),
$this->renderNewDate());
}
protected function getInvalidDateMessage() {
return pht('Repeat until date is invalid.');
}
}

View file

@ -12,6 +12,7 @@ final class PHUIHandleListView
private $handleList;
private $asInline;
private $asText;
public function setHandleList(PhabricatorHandleList $list) {
$this->handleList = $list;
@ -27,6 +28,15 @@ final class PHUIHandleListView
return $this->asInline;
}
public function setAsText($as_text) {
$this->asText = $as_text;
return $this;
}
public function getAsText() {
return $this->asText;
}
protected function getTagName() {
// TODO: It would be nice to render this with a proper <ul />, at least in
// block mode, but don't stir the waters up too much for now.
@ -37,9 +47,9 @@ final class PHUIHandleListView
$list = $this->handleList;
$items = array();
foreach ($list as $handle) {
$view = $list->renderHandle($handle->getPHID());
$view->setShowHovercard(true);
$view = $list->renderHandle($handle->getPHID())
->setShowHovercard(true)
->setAsText($this->getAsText());
$items[] = $view;
}

View file

@ -86,6 +86,14 @@ abstract class PhabricatorModularTransactionType
return $this->viewer;
}
final public function getActor() {
return $this->getEditor()->getActor();
}
final public function getActingAsPHID() {
return $this->getEditor()->getActingAsPHID();
}
final public function setEditor(
PhabricatorApplicationTransactionEditor $editor) {
$this->editor = $editor;
@ -141,6 +149,19 @@ abstract class PhabricatorModularTransactionType
return $display;
}
final protected function renderHandleList(array $phids) {
$viewer = $this->getViewer();
$display = $viewer->renderHandleList($phids)
->setAsInline(true);
$rendering_target = $this->getStorage()->getRenderingTarget();
if ($rendering_target == PhabricatorApplicationTransaction::TARGET_TEXT) {
$display->setAsText(true);
}
return $display;
}
final protected function renderValue($value) {
$rendering_target = $this->getStorage()->getRenderingTarget();
if ($rendering_target == PhabricatorApplicationTransaction::TARGET_TEXT) {
@ -155,6 +176,32 @@ abstract class PhabricatorModularTransactionType
$value);
}
final protected function renderOldValue() {
return $this->renderValue($this->getOldValue());
}
final protected function renderNewValue() {
return $this->renderValue($this->getNewValue());
}
final protected function renderDate($epoch) {
$viewer = $this->getViewer();
$display = phabricator_datetime($epoch, $viewer);
// TODO: When rendering for email, include the UTC offset. See T10633.
return $this->renderValue($display);
}
final protected function renderOldDate() {
return $this->renderDate($this->getOldValue());
}
final protected function renderNewDate() {
return $this->renderDate($this->getNewValue());
}
final protected function newError($title, $message, $xaction = null) {
return new PhabricatorApplicationTransactionValidationError(
$this->getTransactionTypeConstant(),
@ -163,4 +210,26 @@ abstract class PhabricatorModularTransactionType
$xaction);
}
final protected function newRequiredError($message, $xaction = null) {
return $this->newError(pht('Required'), $message, $xaction)
->setIsMissingFieldError(true);
}
final protected function newInvalidError($message, $xaction = null) {
return $this->newError(pht('Invalid'), $message, $xaction);
}
final protected function isNewObject() {
return $this->getEditor()->getIsNewObject();
}
final protected function isEmptyTextTransaction($value, array $xactions) {
foreach ($xactions as $xaction) {
$value = $xaction->getNewValue();
}
return !strlen($value);
}
}

View file

@ -1560,6 +1560,24 @@ final class PhabricatorUSEnglishTranslation
),
),
),
'%s invited %s attendee(s): %s.' =>
'%s invited: %3$s.',
'%s uninvited %s attendee(s): %s.' =>
'%s uninvited: %3$s.',
'%s invited %s attendee(s): %s; uninvinted %s attendee(s): %s.' =>
'%s invited: %3$s; uninvited: %5$s.',
'%s invited %s attendee(s) to %s: %s.' =>
'%s added invites for %3$s: %4$s.',
'%s uninvited %s attendee(s) to %s: %s.' =>
'%s removed invites for %3$s: %4$s.',
'%s updated the invite list for %s, invited %s: %s; uninvinted %s: %s.' =>
'%s updated the invite list for %s, invited: %4$s; uninvited: %6$s.',
);
}