mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-18 19:40:55 +01:00
Store more datetime information on Calendar transactions and improve rendering behaviors
Summary: Fixes T11805. Depends on D16785. This generally tries to smooth out transactions: - All-day stuff now says "Nov 3" instead of "Nov 3 12:00:00 AM". - Fewer weird bugs / extra transactions. - No more silly extra "yeah, you definitely set that event time" transaction on create. Test Plan: Edited events; changed from all-day to not-all-day and back again, viewed transaction log. Reviewers: chad Reviewed By: chad Maniphest Tasks: T11805 Differential Revision: https://secure.phabricator.com/D16786
This commit is contained in:
parent
6b16f930c4
commit
3e15e0b980
7 changed files with 136 additions and 31 deletions
|
@ -3,6 +3,9 @@
|
||||||
final class PhabricatorCalendarEventEditor
|
final class PhabricatorCalendarEventEditor
|
||||||
extends PhabricatorApplicationTransactionEditor {
|
extends PhabricatorApplicationTransactionEditor {
|
||||||
|
|
||||||
|
private $oldIsAllDay;
|
||||||
|
private $newIsAllDay;
|
||||||
|
|
||||||
public function getEditorApplicationClass() {
|
public function getEditorApplicationClass() {
|
||||||
return 'PhabricatorCalendarApplication';
|
return 'PhabricatorCalendarApplication';
|
||||||
}
|
}
|
||||||
|
@ -19,13 +22,20 @@ final class PhabricatorCalendarEventEditor
|
||||||
return pht('%s created %s.', $author, $object);
|
return pht('%s created %s.', $author, $object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
protected function shouldApplyInitialEffects(
|
protected function shouldApplyInitialEffects(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions) {
|
array $xactions) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getOldIsAllDay() {
|
||||||
|
return $this->oldIsAllDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getNewIsAllDay() {
|
||||||
|
return $this->newIsAllDay;
|
||||||
|
}
|
||||||
|
|
||||||
protected function applyInitialEffects(
|
protected function applyInitialEffects(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions) {
|
array $xactions) {
|
||||||
|
@ -34,6 +44,22 @@ final class PhabricatorCalendarEventEditor
|
||||||
if ($object->getIsStub()) {
|
if ($object->getIsStub()) {
|
||||||
$this->materializeStub($object);
|
$this->materializeStub($object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Before doing anything, figure out if the event will be an all day event
|
||||||
|
// or not after the edit. This affects how we store datetime values, and
|
||||||
|
// whether we render times or not.
|
||||||
|
$old_allday = $object->getIsAllDay();
|
||||||
|
$new_allday = $old_allday;
|
||||||
|
$type_allday = PhabricatorCalendarEventAllDayTransaction::TRANSACTIONTYPE;
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
if ($xaction->getTransactionType() != $type_allday) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$target_alllday = (bool)$xaction->getNewValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->oldIsAllDay = $old_allday;
|
||||||
|
$this->newIsAllDay = $new_allday;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function materializeStub(PhabricatorCalendarEvent $event) {
|
private function materializeStub(PhabricatorCalendarEvent $event) {
|
||||||
|
|
|
@ -6,7 +6,11 @@ abstract class PhabricatorCalendarEventDateTransaction
|
||||||
abstract protected function getInvalidDateMessage();
|
abstract protected function getInvalidDateMessage();
|
||||||
|
|
||||||
public function generateNewValue($object, $value) {
|
public function generateNewValue($object, $value) {
|
||||||
return $value->getEpoch();
|
$editor = $this->getEditor();
|
||||||
|
return $value->newPhutilDateTime()
|
||||||
|
->setIsAllDay($editor->getNewIsAllDay())
|
||||||
|
->newAbsoluteDateTime()
|
||||||
|
->toDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validateTransactions($object, array $xactions) {
|
public function validateTransactions($object, array $xactions) {
|
||||||
|
|
|
@ -6,20 +6,32 @@ final class PhabricatorCalendarEventEndDateTransaction
|
||||||
const TRANSACTIONTYPE = 'calendar.enddate';
|
const TRANSACTIONTYPE = 'calendar.enddate';
|
||||||
|
|
||||||
public function generateOldValue($object) {
|
public function generateOldValue($object) {
|
||||||
// TODO: Upgrade this.
|
$editor = $this->getEditor();
|
||||||
return $object->newEndDateTimeForEdit()->getEpoch();
|
|
||||||
|
return $object->newEndDateTimeForEdit()
|
||||||
|
->newAbsoluteDateTime()
|
||||||
|
->setIsAllDay($editor->getOldIsAllDay())
|
||||||
|
->toDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applyInternalEffects($object, $value) {
|
public function applyInternalEffects($object, $value) {
|
||||||
$actor = $this->getActor();
|
$actor = $this->getActor();
|
||||||
|
$editor = $this->getEditor();
|
||||||
|
|
||||||
|
$datetime = PhutilCalendarAbsoluteDateTime::newFromDictionary($value);
|
||||||
|
$datetime->setIsAllDay($editor->getNewIsAllDay());
|
||||||
|
|
||||||
$datetime = PhutilCalendarAbsoluteDateTime::newFromEpoch(
|
|
||||||
$value,
|
|
||||||
$actor->getTimezoneIdentifier());
|
|
||||||
$datetime->setIsAllDay($object->getIsAllDay());
|
|
||||||
$object->setEndDateTime($datetime);
|
$object->setEndDateTime($datetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function shouldHide() {
|
||||||
|
if ($this->isCreateTransaction()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function getTitle() {
|
public function getTitle() {
|
||||||
return pht(
|
return pht(
|
||||||
'%s changed the end date for this event from %s to %s.',
|
'%s changed the end date for this event from %s to %s.',
|
||||||
|
|
|
@ -6,20 +6,32 @@ final class PhabricatorCalendarEventStartDateTransaction
|
||||||
const TRANSACTIONTYPE = 'calendar.startdate';
|
const TRANSACTIONTYPE = 'calendar.startdate';
|
||||||
|
|
||||||
public function generateOldValue($object) {
|
public function generateOldValue($object) {
|
||||||
// TODO: Upgrade this.
|
$editor = $this->getEditor();
|
||||||
return $object->getStartDateTimeEpoch();
|
|
||||||
|
return $object->newStartDateTime()
|
||||||
|
->newAbsoluteDateTime()
|
||||||
|
->setIsAllDay($editor->getOldIsAllDay())
|
||||||
|
->toDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applyInternalEffects($object, $value) {
|
public function applyInternalEffects($object, $value) {
|
||||||
$actor = $this->getActor();
|
$actor = $this->getActor();
|
||||||
|
$editor = $this->getEditor();
|
||||||
|
|
||||||
|
$datetime = PhutilCalendarAbsoluteDateTime::newFromDictionary($value);
|
||||||
|
$datetime->setIsAllDay($editor->getNewIsAllDay());
|
||||||
|
|
||||||
$datetime = PhutilCalendarAbsoluteDateTime::newFromEpoch(
|
|
||||||
$value,
|
|
||||||
$actor->getTimezoneIdentifier());
|
|
||||||
$datetime->setIsAllDay($object->getIsAllDay());
|
|
||||||
$object->setStartDateTime($datetime);
|
$object->setStartDateTime($datetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function shouldHide() {
|
||||||
|
if ($this->isCreateTransaction()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public function getTitle() {
|
public function getTitle() {
|
||||||
return pht(
|
return pht(
|
||||||
'%s changed the start date for this event from %s to %s.',
|
'%s changed the start date for this event from %s to %s.',
|
||||||
|
|
|
@ -6,20 +6,24 @@ final class PhabricatorCalendarEventUntilDateTransaction
|
||||||
const TRANSACTIONTYPE = 'calendar.recurrenceenddate';
|
const TRANSACTIONTYPE = 'calendar.recurrenceenddate';
|
||||||
|
|
||||||
public function generateOldValue($object) {
|
public function generateOldValue($object) {
|
||||||
// TODO: Upgrade this.
|
$editor = $this->getEditor();
|
||||||
return $object->getUntilDateTimeEpoch();
|
|
||||||
|
return $object->newUntilDateTime()
|
||||||
|
->newAbsoluteDateTime()
|
||||||
|
->setIsAllDay($editor->getOldIsAllDay())
|
||||||
|
->toDictionary();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applyInternalEffects($object, $value) {
|
public function applyInternalEffects($object, $value) {
|
||||||
$actor = $this->getActor();
|
$actor = $this->getActor();
|
||||||
|
$editor = $this->getEditor();
|
||||||
|
|
||||||
// TODO: DEPRECATED.
|
// TODO: DEPRECATED.
|
||||||
$object->setRecurrenceEndDate($value);
|
$object->setRecurrenceEndDate($value);
|
||||||
|
|
||||||
$datetime = PhutilCalendarAbsoluteDateTime::newFromEpoch(
|
$datetime = PhutilCalendarAbsoluteDateTime::newFromDictionary($value);
|
||||||
$value,
|
$datetime->setIsAllDay($editor->getNewIsAllDay());
|
||||||
$actor->getTimezoneIdentifier());
|
|
||||||
$datetime->setIsAllDay($object->getIsAllDay());
|
|
||||||
$object->setUntilDateTime($datetime);
|
$object->setUntilDateTime($datetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -196,19 +196,36 @@ abstract class PhabricatorModularTransactionType
|
||||||
final protected function renderDate($epoch) {
|
final protected function renderDate($epoch) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$display = phabricator_datetime($epoch, $viewer);
|
// We accept either epoch timestamps or dictionaries describing a
|
||||||
|
// PhutilCalendarDateTime.
|
||||||
|
if (is_array($epoch)) {
|
||||||
|
$datetime = PhutilCalendarAbsoluteDateTime::newFromDictionary($epoch)
|
||||||
|
->setViewerTimezone($viewer->getTimezoneIdentifier());
|
||||||
|
|
||||||
// When rendering to text, we explicitly render the offset from UTC to
|
$all_day = $datetime->getIsAllDay();
|
||||||
// provide context to the date: the mail may be generating with the
|
|
||||||
// server's settings, or the user may later refer back to it after changing
|
|
||||||
// timezones.
|
|
||||||
|
|
||||||
if ($this->isTextMode()) {
|
$epoch = $datetime->getEpoch();
|
||||||
$offset = $viewer->getTimeZoneOffsetInHours();
|
} else {
|
||||||
if ($offset >= 0) {
|
$all_day = false;
|
||||||
$display = pht('%s (UTC+%d)', $display, $offset);
|
}
|
||||||
} else {
|
|
||||||
$display = pht('%s (UTC-%d)', $display, abs($offset));
|
if ($all_day) {
|
||||||
|
$display = phabricator_date($epoch, $viewer);
|
||||||
|
} else {
|
||||||
|
$display = phabricator_datetime($epoch, $viewer);
|
||||||
|
|
||||||
|
// When rendering to text, we explicitly render the offset from UTC to
|
||||||
|
// provide context to the date: the mail may be generating with the
|
||||||
|
// server's settings, or the user may later refer back to it after
|
||||||
|
// changing timezones.
|
||||||
|
|
||||||
|
if ($this->isTextMode()) {
|
||||||
|
$offset = $viewer->getTimeZoneOffsetInHours();
|
||||||
|
if ($offset >= 0) {
|
||||||
|
$display = pht('%s (UTC+%d)', $display, $offset);
|
||||||
|
} else {
|
||||||
|
$display = pht('%s (UTC-%d)', $display, abs($offset));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,4 +279,8 @@ abstract class PhabricatorModularTransactionType
|
||||||
->setTransaction($this->getStorage());
|
->setTransaction($this->getStorage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final protected function isCreateTransaction() {
|
||||||
|
return $this->getStorage()->getIsCreateTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -231,6 +231,32 @@ final class AphrontFormDateControlValue extends Phobject {
|
||||||
return $datetime;
|
return $datetime;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function newPhutilDateTime() {
|
||||||
|
$datetime = $this->getDateTime();
|
||||||
|
if (!$datetime) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$all_day = !strlen($this->valueTime);
|
||||||
|
$zone_identifier = $this->viewer->getTimezoneIdentifier();
|
||||||
|
|
||||||
|
$result = id(new PhutilCalendarAbsoluteDateTime())
|
||||||
|
->setYear((int)$datetime->format('Y'))
|
||||||
|
->setMonth((int)$datetime->format('m'))
|
||||||
|
->setDay((int)$datetime->format('d'))
|
||||||
|
->setHour((int)$datetime->format('G'))
|
||||||
|
->setMinute((int)$datetime->format('i'))
|
||||||
|
->setSecond((int)$datetime->format('s'))
|
||||||
|
->setTimezone($zone_identifier);
|
||||||
|
|
||||||
|
if ($all_day) {
|
||||||
|
$result->setIsAllDay(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private function getFormattedDateFromParts(
|
private function getFormattedDateFromParts(
|
||||||
$year,
|
$year,
|
||||||
$month,
|
$month,
|
||||||
|
|
Loading…
Reference in a new issue