@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

Convert Calendar Events to use RRULE frequency constants in various other places

Summary:
Ref T10747.

- Store recurrence as RRULEs internally.
- Use RRULE constants.
- Migrate existing rules to RRULEs.

Test Plan: Ran migration, nothing seemed broken?

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T10747

Differential Revision: https://secure.phabricator.com/D16670

+146 -68
+44
resources/sql/autopatches/20161005.cal.01.rrules.php
··· 1 + <?php 2 + 3 + $table = new PhabricatorCalendarEvent(); 4 + $conn = $table->establishConnection('w'); 5 + $table_name = 'calendar_event'; 6 + 7 + foreach (new LiskRawMigrationIterator($conn, $table_name) as $row) { 8 + $parameters = phutil_json_decode($row['parameters']); 9 + if (isset($parameters['recurrenceRule'])) { 10 + // This event has already been migrated. 11 + continue; 12 + } 13 + 14 + if (!$row['isRecurring']) { 15 + continue; 16 + } 17 + 18 + $old_rule = $row['recurrenceFrequency']; 19 + if (!$old_rule) { 20 + continue; 21 + } 22 + 23 + try { 24 + $frequency = phutil_json_decode($old_rule); 25 + if ($frequency) { 26 + $frequency_rule = $frequency['rule']; 27 + $frequency_rule = phutil_utf8_strtoupper($frequency_rule); 28 + 29 + $rrule = id(new PhutilCalendarRecurrenceRule()) 30 + ->setFrequency($frequency_rule); 31 + } 32 + } catch (Exception $ex) { 33 + continue; 34 + } 35 + 36 + $parameters['recurrenceRule'] = $rrule->toDictionary(); 37 + 38 + queryfx( 39 + $conn, 40 + 'UPDATE %T SET parameters = %s WHERE id = %d', 41 + $table_name, 42 + phutil_json_encode($parameters), 43 + $row['id']); 44 + }
+13 -6
src/applications/calendar/controller/PhabricatorCalendarEventViewController.php
··· 348 348 ->render(); 349 349 } 350 350 351 - $rule = $event->getFrequencyRule(); 352 - switch ($rule) { 353 - case PhabricatorCalendarEvent::FREQUENCY_DAILY: 351 + $rrule = $event->newRecurrenceRule(); 352 + 353 + if ($rrule) { 354 + $frequency = $rrule->getFrequency(); 355 + } else { 356 + $frequency = null; 357 + } 358 + 359 + switch ($frequency) { 360 + case PhutilCalendarRecurrenceRule::FREQUENCY_DAILY: 354 361 if ($is_parent) { 355 362 $message = pht('This event repeats every day.'); 356 363 } else { ··· 359 366 $parent_link); 360 367 } 361 368 break; 362 - case PhabricatorCalendarEvent::FREQUENCY_WEEKLY: 369 + case PhutilCalendarRecurrenceRule::FREQUENCY_WEEKLY: 363 370 if ($is_parent) { 364 371 $message = pht('This event repeats every week.'); 365 372 } else { ··· 368 375 $parent_link); 369 376 } 370 377 break; 371 - case PhabricatorCalendarEvent::FREQUENCY_MONTHLY: 378 + case PhutilCalendarRecurrenceRule::FREQUENCY_MONTHLY: 372 379 if ($is_parent) { 373 380 $message = pht('This event repeats every month.'); 374 381 } else { ··· 377 384 $parent_link); 378 385 } 379 386 break; 380 - case PhabricatorCalendarEvent::FREQUENCY_YEARLY: 387 + case PhutilCalendarRecurrenceRule::FREQUENCY_YEARLY: 381 388 if ($is_parent) { 382 389 $message = pht('This event repeats every year.'); 383 390 } else {
+13 -5
src/applications/calendar/editor/PhabricatorCalendarEventEditEngine.php
··· 68 68 } 69 69 70 70 $frequency_options = array( 71 - PhabricatorCalendarEvent::FREQUENCY_DAILY => pht('Daily'), 72 - PhabricatorCalendarEvent::FREQUENCY_WEEKLY => pht('Weekly'), 73 - PhabricatorCalendarEvent::FREQUENCY_MONTHLY => pht('Monthly'), 74 - PhabricatorCalendarEvent::FREQUENCY_YEARLY => pht('Yearly'), 71 + PhutilCalendarRecurrenceRule::FREQUENCY_DAILY => pht('Daily'), 72 + PhutilCalendarRecurrenceRule::FREQUENCY_WEEKLY => pht('Weekly'), 73 + PhutilCalendarRecurrenceRule::FREQUENCY_MONTHLY => pht('Monthly'), 74 + PhutilCalendarRecurrenceRule::FREQUENCY_YEARLY => pht('Yearly'), 75 75 ); 76 76 77 77 $fields = array( ··· 142 142 ->setConduitTypeDescription(pht('Mark the event as a recurring event.')) 143 143 ->setValue($object->getIsRecurring()); 144 144 145 + 146 + $rrule = $object->newRecurrenceRule(); 147 + if ($rrule) { 148 + $frequency = $rrule->getFrequency(); 149 + } else { 150 + $frequency = null; 151 + } 152 + 145 153 $fields[] = id(new PhabricatorSelectEditField()) 146 154 ->setKey('frequency') 147 155 ->setLabel(pht('Frequency')) ··· 151 159 ->setDescription(pht('Recurring event frequency.')) 152 160 ->setConduitDescription(pht('Change the event frequency.')) 153 161 ->setConduitTypeDescription(pht('New event frequency.')) 154 - ->setValue($object->getFrequencyRule()); 162 + ->setValue($frequency); 155 163 } 156 164 157 165 if ($this->getIsCreate() || $object->getIsRecurring()) {
+19 -40
src/applications/calendar/storage/PhabricatorCalendarEvent.php
··· 25 25 protected $isStub; 26 26 27 27 protected $isRecurring = 0; 28 - protected $recurrenceFrequency = array(); 29 28 30 29 private $isGhostEvent = false; 31 30 protected $instanceOfEventPHID; ··· 52 51 protected $dateFrom; 53 52 protected $dateTo; 54 53 protected $recurrenceEndDate; 55 - 56 - // Frequency Constants 57 - const FREQUENCY_DAILY = 'daily'; 58 - const FREQUENCY_WEEKLY = 'weekly'; 59 - const FREQUENCY_MONTHLY = 'monthly'; 60 - const FREQUENCY_YEARLY = 'yearly'; 54 + protected $recurrenceFrequency = array(); 61 55 62 56 public static function initializeNewCalendarEvent(PhabricatorUser $actor) { 63 57 $app = id(new PhabricatorApplicationQuery()) ··· 85 79 ->setIsAllDay(0) 86 80 ->setIsStub(0) 87 81 ->setIsRecurring(0) 88 - ->setRecurrenceFrequency( 89 - array( 90 - 'rule' => self::FREQUENCY_WEEKLY, 91 - )) 92 82 ->setIcon($default_icon) 93 83 ->setViewPolicy($view_policy) 94 84 ->setEditPolicy($edit_policy) ··· 120 110 ->setInstanceOfEventPHID($this->getPHID()) 121 111 ->setSequenceIndex($sequence) 122 112 ->setIsRecurring(true) 123 - ->setRecurrenceFrequency($this->getRecurrenceFrequency()) 124 113 ->attachParentEvent($this) 125 114 ->setAllDayDateFrom(0) 126 115 ->setAllDayDateTo(0) ··· 456 445 return $this; 457 446 } 458 447 459 - public function getFrequencyRule() { 460 - return idx($this->recurrenceFrequency, 'rule'); 461 - } 462 - 463 - public function getFrequencyUnit() { 464 - $frequency = $this->getFrequencyRule(); 465 - 466 - switch ($frequency) { 467 - case 'daily': 468 - return 'day'; 469 - case 'weekly': 470 - return 'week'; 471 - case 'monthly': 472 - return 'month'; 473 - case 'yearly': 474 - return 'year'; 475 - default: 476 - return 'day'; 477 - } 478 - } 479 - 480 448 public function getURI() { 481 449 if ($this->getIsGhostEvent()) { 482 450 $base = $this->getParentEvent()->getURI(); ··· 840 808 $datetime->newAbsoluteDateTime()->toDictionary()); 841 809 } 842 810 811 + public function setRecurrenceRule(PhutilCalendarRecurrenceRule $rrule) { 812 + return $this->setParameter( 813 + 'recurrenceRule', 814 + $rrule->toDictionary()); 815 + } 843 816 844 817 public function newRecurrenceRule() { 845 818 if ($this->isChildEvent()) { 846 819 return $this->getParentEvent()->newRecurrenceRule(); 847 820 } 848 821 849 - // TODO: This is a little fragile since it relies on the convenient 850 - // definition of FREQUENCY constants here and in RecurrenceRule, but 851 - // should be gone soon. 852 - $map = array( 853 - 'FREQ' => phutil_utf8_strtoupper($this->getFrequencyRule()), 854 - ); 822 + if (!$this->getIsRecurring()) { 823 + return null; 824 + } 825 + 826 + $dict = $this->getParameter('recurrenceRule'); 827 + if (!$dict) { 828 + return null; 829 + } 855 830 856 - $rrule = PhutilCalendarRecurrenceRule::newFromDictionary($map); 831 + $rrule = PhutilCalendarRecurrenceRule::newFromDictionary($dict); 857 832 858 833 $start = $this->newStartDateTime(); 859 834 $rrule->setStartDateTime($start); ··· 869 844 $set = new PhutilCalendarRecurrenceSet(); 870 845 871 846 $rrule = $this->newRecurrenceRule(); 847 + if (!$rrule) { 848 + return null; 849 + } 850 + 872 851 $set->addSource($rrule); 873 852 874 853 return $set;
+57 -17
src/applications/calendar/xaction/PhabricatorCalendarEventFrequencyTransaction.php
··· 6 6 const TRANSACTIONTYPE = 'calendar.frequency'; 7 7 8 8 public function generateOldValue($object) { 9 - return $object->getFrequencyRule(); 9 + $rrule = $object->newRecurrenceRule(); 10 + 11 + if (!$rrule) { 12 + return null; 13 + } 14 + 15 + return $rrule->getFrequency(); 10 16 } 11 17 12 18 public function applyInternalEffects($object, $value) { 13 - $object->setRecurrenceFrequency( 14 - array( 15 - 'rule' => $value, 16 - )); 19 + $rrule = id(new PhutilCalendarRecurrenceRule()) 20 + ->setFrequency($value); 21 + 22 + $dict = $rrule->toDictionary(); 23 + $object->setRecurrenceRule($dict); 24 + } 25 + 26 + public function validateTransactions($object, array $xactions) { 27 + $errors = array(); 28 + 29 + $valid = array( 30 + PhutilCalendarRecurrenceRule::FREQUENCY_DAILY, 31 + PhutilCalendarRecurrenceRule::FREQUENCY_WEEKLY, 32 + PhutilCalendarRecurrenceRule::FREQUENCY_MONTHLY, 33 + PhutilCalendarRecurrenceRule::FREQUENCY_YEARLY, 34 + ); 35 + $valid = array_fuse($valid); 36 + 37 + foreach ($xactions as $xaction) { 38 + $value = $xaction->getNewValue(); 39 + 40 + if (!isset($valid[$value])) { 41 + $errors[] = $this->newInvalidError( 42 + pht( 43 + 'Event frequency "%s" is not valid. Valid frequences are: %s.', 44 + $value, 45 + implode(', ', $valid)), 46 + $xaction); 47 + } 48 + } 49 + 50 + return $errors; 17 51 } 18 52 19 53 public function getTitle() { 20 - $frequency = $this->getFrequencyRule($this->getNewValue()); 54 + $frequency = $this->getFrequency($this->getNewValue()); 21 55 switch ($frequency) { 22 - case PhabricatorCalendarEvent::FREQUENCY_DAILY: 56 + case PhutilCalendarRecurrenceRule::FREQUENCY_DAILY: 23 57 return pht( 24 58 '%s set this event to repeat daily.', 25 59 $this->renderAuthor()); 26 - case PhabricatorCalendarEvent::FREQUENCY_WEEKLY: 60 + case PhutilCalendarRecurrenceRule::FREQUENCY_WEEKLY: 27 61 return pht( 28 62 '%s set this event to repeat weekly.', 29 63 $this->renderAuthor()); 30 - case PhabricatorCalendarEvent::FREQUENCY_MONTHLY: 64 + case PhutilCalendarRecurrenceRule::FREQUENCY_MONTHLY: 31 65 return pht( 32 66 '%s set this event to repeat monthly.', 33 67 $this->renderAuthor()); 34 - case PhabricatorCalendarEvent::FREQUENCY_YEARLY: 68 + case PhutilCalendarRecurrenceRule::FREQUENCY_YEARLY: 35 69 return pht( 36 70 '%s set this event to repeat yearly.', 37 71 $this->renderAuthor()); ··· 39 73 } 40 74 41 75 public function getTitleForFeed() { 42 - $frequency = $this->getFrequencyRule($this->getNewValue()); 76 + $frequency = $this->getFrequency($this->getNewValue()); 43 77 switch ($frequency) { 44 - case PhabricatorCalendarEvent::FREQUENCY_DAILY: 78 + case PhutilCalendarRecurrenceRule::FREQUENCY_DAILY: 45 79 return pht( 46 80 '%s set %s to repeat daily.', 47 81 $this->renderAuthor(), 48 82 $this->renderObject()); 49 - case PhabricatorCalendarEvent::FREQUENCY_WEEKLY: 83 + case PhutilCalendarRecurrenceRule::FREQUENCY_WEEKLY: 50 84 return pht( 51 85 '%s set %s to repeat weekly.', 52 86 $this->renderAuthor(), 53 87 $this->renderObject()); 54 - case PhabricatorCalendarEvent::FREQUENCY_MONTHLY: 88 + case PhutilCalendarRecurrenceRule::FREQUENCY_MONTHLY: 55 89 return pht( 56 90 '%s set %s to repeat monthly.', 57 91 $this->renderAuthor(), 58 92 $this->renderObject()); 59 - case PhabricatorCalendarEvent::FREQUENCY_YEARLY: 93 + case PhutilCalendarRecurrenceRule::FREQUENCY_YEARLY: 60 94 return pht( 61 95 '%s set %s to repeat yearly.', 62 96 $this->renderAuthor(), ··· 64 98 } 65 99 } 66 100 67 - private function getFrequencyRule($value) { 101 + private function getFrequency($value) { 102 + // NOTE: This is normalizing three generations of these transactions 103 + // to use RRULE constants. It would be vaguely nice to migrate them 104 + // for consistency. 105 + 68 106 if (is_array($value)) { 69 107 $value = idx($value, 'rule'); 70 108 } else { 71 - return $value; 109 + $value = $value; 72 110 } 111 + 112 + return phutil_utf8_strtoupper($value); 73 113 } 74 114 75 115 }