@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+136 -31
+27 -1
src/applications/calendar/editor/PhabricatorCalendarEventEditor.php
··· 3 3 final class PhabricatorCalendarEventEditor 4 4 extends PhabricatorApplicationTransactionEditor { 5 5 6 + private $oldIsAllDay; 7 + private $newIsAllDay; 8 + 6 9 public function getEditorApplicationClass() { 7 10 return 'PhabricatorCalendarApplication'; 8 11 } ··· 19 22 return pht('%s created %s.', $author, $object); 20 23 } 21 24 22 - 23 25 protected function shouldApplyInitialEffects( 24 26 PhabricatorLiskDAO $object, 25 27 array $xactions) { 26 28 return true; 27 29 } 28 30 31 + public function getOldIsAllDay() { 32 + return $this->oldIsAllDay; 33 + } 34 + 35 + public function getNewIsAllDay() { 36 + return $this->newIsAllDay; 37 + } 38 + 29 39 protected function applyInitialEffects( 30 40 PhabricatorLiskDAO $object, 31 41 array $xactions) { ··· 34 44 if ($object->getIsStub()) { 35 45 $this->materializeStub($object); 36 46 } 47 + 48 + // Before doing anything, figure out if the event will be an all day event 49 + // or not after the edit. This affects how we store datetime values, and 50 + // whether we render times or not. 51 + $old_allday = $object->getIsAllDay(); 52 + $new_allday = $old_allday; 53 + $type_allday = PhabricatorCalendarEventAllDayTransaction::TRANSACTIONTYPE; 54 + foreach ($xactions as $xaction) { 55 + if ($xaction->getTransactionType() != $type_allday) { 56 + continue; 57 + } 58 + $target_alllday = (bool)$xaction->getNewValue(); 59 + } 60 + 61 + $this->oldIsAllDay = $old_allday; 62 + $this->newIsAllDay = $new_allday; 37 63 } 38 64 39 65 private function materializeStub(PhabricatorCalendarEvent $event) {
+5 -1
src/applications/calendar/xaction/PhabricatorCalendarEventDateTransaction.php
··· 6 6 abstract protected function getInvalidDateMessage(); 7 7 8 8 public function generateNewValue($object, $value) { 9 - return $value->getEpoch(); 9 + $editor = $this->getEditor(); 10 + return $value->newPhutilDateTime() 11 + ->setIsAllDay($editor->getNewIsAllDay()) 12 + ->newAbsoluteDateTime() 13 + ->toDictionary(); 10 14 } 11 15 12 16 public function validateTransactions($object, array $xactions) {
+18 -6
src/applications/calendar/xaction/PhabricatorCalendarEventEndDateTransaction.php
··· 6 6 const TRANSACTIONTYPE = 'calendar.enddate'; 7 7 8 8 public function generateOldValue($object) { 9 - // TODO: Upgrade this. 10 - return $object->newEndDateTimeForEdit()->getEpoch(); 9 + $editor = $this->getEditor(); 10 + 11 + return $object->newEndDateTimeForEdit() 12 + ->newAbsoluteDateTime() 13 + ->setIsAllDay($editor->getOldIsAllDay()) 14 + ->toDictionary(); 11 15 } 12 16 13 17 public function applyInternalEffects($object, $value) { 14 18 $actor = $this->getActor(); 19 + $editor = $this->getEditor(); 15 20 16 - $datetime = PhutilCalendarAbsoluteDateTime::newFromEpoch( 17 - $value, 18 - $actor->getTimezoneIdentifier()); 19 - $datetime->setIsAllDay($object->getIsAllDay()); 21 + $datetime = PhutilCalendarAbsoluteDateTime::newFromDictionary($value); 22 + $datetime->setIsAllDay($editor->getNewIsAllDay()); 23 + 20 24 $object->setEndDateTime($datetime); 25 + } 26 + 27 + public function shouldHide() { 28 + if ($this->isCreateTransaction()) { 29 + return true; 30 + } 31 + 32 + return false; 21 33 } 22 34 23 35 public function getTitle() {
+18 -6
src/applications/calendar/xaction/PhabricatorCalendarEventStartDateTransaction.php
··· 6 6 const TRANSACTIONTYPE = 'calendar.startdate'; 7 7 8 8 public function generateOldValue($object) { 9 - // TODO: Upgrade this. 10 - return $object->getStartDateTimeEpoch(); 9 + $editor = $this->getEditor(); 10 + 11 + return $object->newStartDateTime() 12 + ->newAbsoluteDateTime() 13 + ->setIsAllDay($editor->getOldIsAllDay()) 14 + ->toDictionary(); 11 15 } 12 16 13 17 public function applyInternalEffects($object, $value) { 14 18 $actor = $this->getActor(); 19 + $editor = $this->getEditor(); 15 20 16 - $datetime = PhutilCalendarAbsoluteDateTime::newFromEpoch( 17 - $value, 18 - $actor->getTimezoneIdentifier()); 19 - $datetime->setIsAllDay($object->getIsAllDay()); 21 + $datetime = PhutilCalendarAbsoluteDateTime::newFromDictionary($value); 22 + $datetime->setIsAllDay($editor->getNewIsAllDay()); 23 + 20 24 $object->setStartDateTime($datetime); 25 + } 26 + 27 + public function shouldHide() { 28 + if ($this->isCreateTransaction()) { 29 + return true; 30 + } 31 + 32 + return false; 21 33 } 22 34 23 35 public function getTitle() {
+10 -6
src/applications/calendar/xaction/PhabricatorCalendarEventUntilDateTransaction.php
··· 6 6 const TRANSACTIONTYPE = 'calendar.recurrenceenddate'; 7 7 8 8 public function generateOldValue($object) { 9 - // TODO: Upgrade this. 10 - return $object->getUntilDateTimeEpoch(); 9 + $editor = $this->getEditor(); 10 + 11 + return $object->newUntilDateTime() 12 + ->newAbsoluteDateTime() 13 + ->setIsAllDay($editor->getOldIsAllDay()) 14 + ->toDictionary(); 11 15 } 12 16 13 17 public function applyInternalEffects($object, $value) { 14 18 $actor = $this->getActor(); 19 + $editor = $this->getEditor(); 15 20 16 21 // TODO: DEPRECATED. 17 22 $object->setRecurrenceEndDate($value); 18 23 19 - $datetime = PhutilCalendarAbsoluteDateTime::newFromEpoch( 20 - $value, 21 - $actor->getTimezoneIdentifier()); 22 - $datetime->setIsAllDay($object->getIsAllDay()); 24 + $datetime = PhutilCalendarAbsoluteDateTime::newFromDictionary($value); 25 + $datetime->setIsAllDay($editor->getNewIsAllDay()); 26 + 23 27 $object->setUntilDateTime($datetime); 24 28 } 25 29
+32 -11
src/applications/transactions/storage/PhabricatorModularTransactionType.php
··· 196 196 final protected function renderDate($epoch) { 197 197 $viewer = $this->getViewer(); 198 198 199 - $display = phabricator_datetime($epoch, $viewer); 199 + // We accept either epoch timestamps or dictionaries describing a 200 + // PhutilCalendarDateTime. 201 + if (is_array($epoch)) { 202 + $datetime = PhutilCalendarAbsoluteDateTime::newFromDictionary($epoch) 203 + ->setViewerTimezone($viewer->getTimezoneIdentifier()); 200 204 201 - // When rendering to text, we explicitly render the offset from UTC to 202 - // provide context to the date: the mail may be generating with the 203 - // server's settings, or the user may later refer back to it after changing 204 - // timezones. 205 + $all_day = $datetime->getIsAllDay(); 206 + 207 + $epoch = $datetime->getEpoch(); 208 + } else { 209 + $all_day = false; 210 + } 211 + 212 + if ($all_day) { 213 + $display = phabricator_date($epoch, $viewer); 214 + } else { 215 + $display = phabricator_datetime($epoch, $viewer); 216 + 217 + // When rendering to text, we explicitly render the offset from UTC to 218 + // provide context to the date: the mail may be generating with the 219 + // server's settings, or the user may later refer back to it after 220 + // changing timezones. 205 221 206 - if ($this->isTextMode()) { 207 - $offset = $viewer->getTimeZoneOffsetInHours(); 208 - if ($offset >= 0) { 209 - $display = pht('%s (UTC+%d)', $display, $offset); 210 - } else { 211 - $display = pht('%s (UTC-%d)', $display, abs($offset)); 222 + if ($this->isTextMode()) { 223 + $offset = $viewer->getTimeZoneOffsetInHours(); 224 + if ($offset >= 0) { 225 + $display = pht('%s (UTC+%d)', $display, $offset); 226 + } else { 227 + $display = pht('%s (UTC-%d)', $display, abs($offset)); 228 + } 212 229 } 213 230 } 214 231 ··· 260 277 final protected function newRemarkupChange() { 261 278 return id(new PhabricatorTransactionRemarkupChange()) 262 279 ->setTransaction($this->getStorage()); 280 + } 281 + 282 + final protected function isCreateTransaction() { 283 + return $this->getStorage()->getIsCreateTransaction(); 263 284 } 264 285 265 286 }
+26
src/view/form/control/AphrontFormDateControlValue.php
··· 231 231 return $datetime; 232 232 } 233 233 234 + public function newPhutilDateTime() { 235 + $datetime = $this->getDateTime(); 236 + if (!$datetime) { 237 + return null; 238 + } 239 + 240 + $all_day = !strlen($this->valueTime); 241 + $zone_identifier = $this->viewer->getTimezoneIdentifier(); 242 + 243 + $result = id(new PhutilCalendarAbsoluteDateTime()) 244 + ->setYear((int)$datetime->format('Y')) 245 + ->setMonth((int)$datetime->format('m')) 246 + ->setDay((int)$datetime->format('d')) 247 + ->setHour((int)$datetime->format('G')) 248 + ->setMinute((int)$datetime->format('i')) 249 + ->setSecond((int)$datetime->format('s')) 250 + ->setTimezone($zone_identifier); 251 + 252 + if ($all_day) { 253 + $result->setIsAllDay(true); 254 + } 255 + 256 + return $result; 257 + } 258 + 259 + 234 260 private function getFormattedDateFromParts( 235 261 $year, 236 262 $month,