Page MenuHomePhabricator

D11427.diff
No OneTemporary

D11427.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -8,10 +8,10 @@
return array(
'names' => array(
'core.pkg.css' => '8d1c0f87',
- 'core.pkg.js' => 'd60876a7',
+ 'core.pkg.js' => '7923c2e6',
'darkconsole.pkg.js' => '8ab24e01',
'differential.pkg.css' => '8af45893',
- 'differential.pkg.js' => 'f437e70e',
+ 'differential.pkg.js' => 'dad3622f',
'diffusion.pkg.css' => '591664fa',
'diffusion.pkg.js' => 'bfc0737b',
'maniphest.pkg.css' => 'e34dfbec',
@@ -166,10 +166,10 @@
'rsrc/externals/javelin/core/__tests__/install.js' => 'c432ee85',
'rsrc/externals/javelin/core/__tests__/stratcom.js' => '88bf7313',
'rsrc/externals/javelin/core/__tests__/util.js' => 'e251703d',
- 'rsrc/externals/javelin/core/init.js' => '8c4e8f8b',
- 'rsrc/externals/javelin/core/init_node.js' => 'c234aded',
+ 'rsrc/externals/javelin/core/init.js' => '76e1fd61',
+ 'rsrc/externals/javelin/core/init_node.js' => '77350e4d',
'rsrc/externals/javelin/core/install.js' => '05270951',
- 'rsrc/externals/javelin/core/util.js' => '93cc50d6',
+ 'rsrc/externals/javelin/core/util.js' => 'bdcfee9e',
'rsrc/externals/javelin/docs/Base.js' => '74676256',
'rsrc/externals/javelin/docs/onload.js' => 'e819c479',
'rsrc/externals/javelin/ext/fx/Color.js' => '7e41274a',
@@ -186,11 +186,11 @@
'rsrc/externals/javelin/ext/view/ViewRenderer.js' => '6c2b09a2',
'rsrc/externals/javelin/ext/view/ViewVisitor.js' => 'efe49472',
'rsrc/externals/javelin/ext/view/__tests__/HTMLView.js' => 'f92d7bcb',
- 'rsrc/externals/javelin/ext/view/__tests__/View.js' => '6450b38b',
+ 'rsrc/externals/javelin/ext/view/__tests__/View.js' => 'bda69c40',
'rsrc/externals/javelin/ext/view/__tests__/ViewInterpreter.js' => '7a94d6a5',
'rsrc/externals/javelin/ext/view/__tests__/ViewRenderer.js' => '6ea96ac9',
- 'rsrc/externals/javelin/lib/Cookie.js' => '62dfea03',
- 'rsrc/externals/javelin/lib/DOM.js' => 'c8fd8db2',
+ 'rsrc/externals/javelin/lib/Cookie.js' => '6b3dcf44',
+ 'rsrc/externals/javelin/lib/DOM.js' => 'c4569c05',
'rsrc/externals/javelin/lib/History.js' => 'c60f4327',
'rsrc/externals/javelin/lib/JSON.js' => '69adf288',
'rsrc/externals/javelin/lib/Leader.js' => '9330f91b',
@@ -215,7 +215,7 @@
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadCompositeSource.js' => '503e17fd',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadOnDemandSource.js' => '8b3fd187',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadPreloadedSource.js' => '54f314a0',
- 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => '2818f5ce',
+ 'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadSource.js' => 'e3b841c8',
'rsrc/externals/javelin/lib/control/typeahead/source/TypeaheadStaticSource.js' => '316b8fa1',
'rsrc/externals/raphael/g.raphael.js' => '40dde778',
'rsrc/externals/raphael/g.raphael.line.js' => '40da039e',
@@ -362,7 +362,7 @@
'rsrc/js/application/differential/behavior-comment-jump.js' => '4fdb476d',
'rsrc/js/application/differential/behavior-comment-preview.js' => '6932def3',
'rsrc/js/application/differential/behavior-diff-radios.js' => 'e1ff79b1',
- 'rsrc/js/application/differential/behavior-dropdown-menus.js' => 'e33d4bc5',
+ 'rsrc/js/application/differential/behavior-dropdown-menus.js' => '3bc14668',
'rsrc/js/application/differential/behavior-edit-inline-comments.js' => '00861799',
'rsrc/js/application/differential/behavior-keyboard-nav.js' => '2c426492',
'rsrc/js/application/differential/behavior-populate.js' => 'bdb3e4d0',
@@ -410,7 +410,7 @@
'rsrc/js/application/projects/behavior-project-create.js' => '065227cc',
'rsrc/js/application/projects/behavior-reorder-columns.js' => 'e1d25dfb',
'rsrc/js/application/releeph/releeph-preview-branch.js' => 'b2b4fbaf',
- 'rsrc/js/application/releeph/releeph-request-state-change.js' => '3a1a4060',
+ 'rsrc/js/application/releeph/releeph-request-state-change.js' => 'ab836011',
'rsrc/js/application/releeph/releeph-request-typeahead.js' => 'de2e896f',
'rsrc/js/application/repository/repository-crossreference.js' => 'f9539603',
'rsrc/js/application/search/behavior-reorder-queries.js' => 'e9581f08',
@@ -444,7 +444,7 @@
'rsrc/js/core/ShapedRequest.js' => '7cbe244b',
'rsrc/js/core/TextAreaUtils.js' => '5c93c52c',
'rsrc/js/core/Title.js' => '5c1c758c',
- 'rsrc/js/core/ToolTip.js' => '1d298e3a',
+ 'rsrc/js/core/ToolTip.js' => '031d4411',
'rsrc/js/core/behavior-active-nav.js' => 'e379b58e',
'rsrc/js/core/behavior-audio-source.js' => '59b251eb',
'rsrc/js/core/behavior-autofocus.js' => '7319e029',
@@ -564,7 +564,7 @@
'javelin-behavior-differential-add-reviewers-and-ccs' => 'e10f8e18',
'javelin-behavior-differential-comment-jump' => '4fdb476d',
'javelin-behavior-differential-diff-radios' => 'e1ff79b1',
- 'javelin-behavior-differential-dropdown-menus' => 'e33d4bc5',
+ 'javelin-behavior-differential-dropdown-menus' => '3bc14668',
'javelin-behavior-differential-edit-inline-comments' => '00861799',
'javelin-behavior-differential-feedback-preview' => '6932def3',
'javelin-behavior-differential-keyboard-navigation' => '2c426492',
@@ -635,7 +635,7 @@
'javelin-behavior-project-create' => '065227cc',
'javelin-behavior-refresh-csrf' => '7814b593',
'javelin-behavior-releeph-preview-branch' => 'b2b4fbaf',
- 'javelin-behavior-releeph-request-state-change' => '3a1a4060',
+ 'javelin-behavior-releeph-request-state-change' => 'ab836011',
'javelin-behavior-releeph-request-typeahead' => 'de2e896f',
'javelin-behavior-remarkup-preview' => 'f7379f45',
'javelin-behavior-reorder-applications' => '76b9fc3e',
@@ -650,9 +650,9 @@
'javelin-behavior-view-placeholder' => '47830651',
'javelin-behavior-workflow' => '0a3f3021',
'javelin-color' => '7e41274a',
- 'javelin-cookie' => '62dfea03',
+ 'javelin-cookie' => '6b3dcf44',
'javelin-diffusion-locate-file-source' => 'b42eddc7',
- 'javelin-dom' => 'c8fd8db2',
+ 'javelin-dom' => 'c4569c05',
'javelin-dynval' => 'f6555212',
'javelin-event' => '85ea0626',
'javelin-fx' => '54b612ba',
@@ -660,7 +660,7 @@
'javelin-install' => '05270951',
'javelin-json' => '69adf288',
'javelin-leader' => '9330f91b',
- 'javelin-magical-init' => '8c4e8f8b',
+ 'javelin-magical-init' => '76e1fd61',
'javelin-mask' => '8a41885b',
'javelin-reactor' => '2b8de964',
'javelin-reactor-dom' => 'c90a04fc',
@@ -677,10 +677,10 @@
'javelin-typeahead-normalizer' => '6f7a9da8',
'javelin-typeahead-ondemand-source' => '8b3fd187',
'javelin-typeahead-preloaded-source' => '54f314a0',
- 'javelin-typeahead-source' => '2818f5ce',
+ 'javelin-typeahead-source' => 'e3b841c8',
'javelin-typeahead-static-source' => '316b8fa1',
'javelin-uri' => '6eff08aa',
- 'javelin-util' => '93cc50d6',
+ 'javelin-util' => 'bdcfee9e',
'javelin-vector' => 'cc1bd0b0',
'javelin-view' => '0f764c35',
'javelin-view-html' => 'fe287620',
@@ -738,7 +738,7 @@
'phabricator-standard-page-view' => '2c96cfb5',
'phabricator-textareautils' => '5c93c52c',
'phabricator-title' => '5c1c758c',
- 'phabricator-tooltip' => '1d298e3a',
+ 'phabricator-tooltip' => '031d4411',
'phabricator-transaction-view-css' => '5d0cae25',
'phabricator-ui-example-css' => '528b19de',
'phabricator-uiexample-javelin-view' => 'd4a14807',
@@ -834,6 +834,12 @@
'029a133d' => array(
'aphront-dialog-view-css',
),
+ '031d4411' => array(
+ 'javelin-install',
+ 'javelin-util',
+ 'javelin-dom',
+ 'javelin-vector',
+ ),
'03d6ed07' => array(
'javelin-behavior',
'javelin-stratcom',
@@ -938,12 +944,6 @@
'javelin-util',
'phabricator-keyboard-shortcut-manager',
),
- '1d298e3a' => array(
- 'javelin-install',
- 'javelin-util',
- 'javelin-dom',
- 'javelin-vector',
- ),
'1def2711' => array(
'javelin-install',
'javelin-dom',
@@ -956,12 +956,6 @@
'javelin-workflow',
'javelin-util',
),
- '2818f5ce' => array(
- 'javelin-install',
- 'javelin-util',
- 'javelin-dom',
- 'javelin-typeahead-normalizer',
- ),
'2926fff2' => array(
'javelin-behavior',
'javelin-dom',
@@ -1029,14 +1023,6 @@
'javelin-json',
'phabricator-prefab',
),
- '3a1a4060' => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'javelin-util',
- 'phabricator-keyboard-shortcut',
- ),
'3ab51e2c' => array(
'javelin-behavior',
'javelin-behavior-device',
@@ -1045,6 +1031,18 @@
'javelin-dom',
'javelin-magical-init',
),
+ '3bc14668' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ 'javelin-util',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'phuix-dropdown-menu',
+ 'phuix-action-list-view',
+ 'phuix-action-view',
+ 'phabricator-phtize',
+ 'changeset-view-manager',
+ ),
'3d51a746' => array(
'javelin-behavior',
'javelin-dom',
@@ -1228,10 +1226,6 @@
'javelin-magical-init',
'javelin-util',
),
- '62dfea03' => array(
- 'javelin-install',
- 'javelin-util',
- ),
'6453c869' => array(
'javelin-install',
'javelin-dom',
@@ -1251,6 +1245,10 @@
'69adf288' => array(
'javelin-install',
),
+ '6b3dcf44' => array(
+ 'javelin-install',
+ 'javelin-util',
+ ),
'6c2b09a2' => array(
'javelin-install',
'javelin-util',
@@ -1562,6 +1560,14 @@
'javelin-util',
'phabricator-prefab',
),
+ 'ab836011' => array(
+ 'javelin-behavior',
+ 'javelin-dom',
+ 'javelin-stratcom',
+ 'javelin-workflow',
+ 'javelin-util',
+ 'phabricator-keyboard-shortcut',
+ ),
'ad7a69ca' => array(
'javelin-install',
'javelin-util',
@@ -1650,6 +1656,13 @@
'javelin-util',
'phabricator-shaped-request',
),
+ 'c4569c05' => array(
+ 'javelin-magical-init',
+ 'javelin-install',
+ 'javelin-util',
+ 'javelin-vector',
+ 'javelin-stratcom',
+ ),
'c51ae228' => array(
'javelin-behavior',
'javelin-util',
@@ -1663,13 +1676,6 @@
'javelin-uri',
'javelin-util',
),
- 'c8fd8db2' => array(
- 'javelin-magical-init',
- 'javelin-install',
- 'javelin-util',
- 'javelin-vector',
- 'javelin-stratcom',
- ),
'c90a04fc' => array(
'javelin-dom',
'javelin-dynval',
@@ -1788,18 +1794,6 @@
'javelin-workflow',
'javelin-vector',
),
- 'e33d4bc5' => array(
- 'javelin-behavior',
- 'javelin-dom',
- 'javelin-util',
- 'javelin-stratcom',
- 'javelin-workflow',
- 'phuix-dropdown-menu',
- 'phuix-action-list-view',
- 'phuix-action-view',
- 'phabricator-phtize',
- 'changeset-view-manager',
- ),
'e379b58e' => array(
'javelin-behavior',
'javelin-stratcom',
@@ -1807,6 +1801,12 @@
'javelin-dom',
'javelin-uri',
),
+ 'e3b841c8' => array(
+ 'javelin-install',
+ 'javelin-util',
+ 'javelin-dom',
+ 'javelin-typeahead-normalizer',
+ ),
'e4cc26b3' => array(
'javelin-behavior',
'javelin-dom',
diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1596,6 +1596,7 @@
'PhabricatorDaemonTasksTableView' => 'applications/daemon/view/PhabricatorDaemonTasksTableView.php',
'PhabricatorDaemonsApplication' => 'applications/daemon/application/PhabricatorDaemonsApplication.php',
'PhabricatorDaemonsSetupCheck' => 'applications/config/check/PhabricatorDaemonsSetupCheck.php',
+ 'PhabricatorDailyRoutineTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorDailyRoutineTriggerClock.php',
'PhabricatorDashboard' => 'applications/dashboard/storage/PhabricatorDashboard.php',
'PhabricatorDashboardAddPanelController' => 'applications/dashboard/controller/PhabricatorDashboardAddPanelController.php',
'PhabricatorDashboardApplication' => 'applications/dashboard/application/PhabricatorDashboardApplication.php',
@@ -4801,6 +4802,7 @@
'PhabricatorDaemonTasksTableView' => 'AphrontView',
'PhabricatorDaemonsApplication' => 'PhabricatorApplication',
'PhabricatorDaemonsSetupCheck' => 'PhabricatorSetupCheck',
+ 'PhabricatorDailyRoutineTriggerClock' => 'PhabricatorTriggerClock',
'PhabricatorDashboard' => array(
'PhabricatorDashboardDAO',
'PhabricatorApplicationTransactionInterface',
diff --git a/src/infrastructure/daemon/workers/clock/PhabricatorDailyRoutineTriggerClock.php b/src/infrastructure/daemon/workers/clock/PhabricatorDailyRoutineTriggerClock.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/daemon/workers/clock/PhabricatorDailyRoutineTriggerClock.php
@@ -0,0 +1,57 @@
+<?php
+
+/**
+ * Triggers a daily routine, like server backups.
+ *
+ * This clock triggers events every 24 hours, using UTC. It does not use a
+ * locale, and is intended for technical processes like backing up a server
+ * every night.
+ *
+ * Because UTC does not have daylight savings, the local hour when this event
+ * occurs will change over the course of the year. For example, from the
+ * perspective of a user in California, it might run backups at 3AM in the
+ * winter and 2AM in the summer. This is desirable for maintenance processes,
+ * but problematic for some human processes. Use a different clock if you're
+ * triggering a human-oriented event.
+ *
+ * The clock uses the time of day of the `start` epoch to calculate the time
+ * of day of the next event, so you can change the time of day when the event
+ * occurs by adjusting the `start` time of day.
+ */
+final class PhabricatorDailyRoutineTriggerClock
+ extends PhabricatorTriggerClock {
+
+ public function validateProperties(array $properties) {
+ PhutilTypeSpec::checkMap(
+ $properties,
+ array(
+ 'start' => 'int',
+ ));
+ }
+
+ public function getNextEventEpoch($last_epoch, $is_reschedule) {
+ $start_epoch = $this->getProperty('start');
+ if (!$last_epoch) {
+ $last_epoch = $start_epoch;
+ }
+
+ $start = new DateTime('@'.$start_epoch);
+ $last = new DateTime('@'.$last_epoch);
+
+ // NOTE: We're choosing the date from the last event, but the time of day
+ // from the start event. This allows callers to change when the event
+ // occurs by updating the trigger's start parameter.
+ $ymd = $last->format('Y-m-d');
+ $hms = $start->format('G:i:s');
+
+ $next = new DateTime("{$ymd} {$hms} UTC");
+
+ // Add a day.
+ // NOTE: DateInterval doesn't exist until PHP 5.3.0, and we currently
+ // target PHP 5.2.3.
+ $next->modify('+1 day');
+
+ return (int)$next->format('U');
+ }
+
+}
diff --git a/src/infrastructure/daemon/workers/clock/__tests__/PhabricatorTriggerClockTestCase.php b/src/infrastructure/daemon/workers/clock/__tests__/PhabricatorTriggerClockTestCase.php
--- a/src/infrastructure/daemon/workers/clock/__tests__/PhabricatorTriggerClockTestCase.php
+++ b/src/infrastructure/daemon/workers/clock/__tests__/PhabricatorTriggerClockTestCase.php
@@ -30,6 +30,90 @@
pht('Should never trigger.'));
}
+ public function testDailyRoutineTriggerClockDaylightSavings() {
+ // These dates are selected to cross daylight savings in PST; they should
+ // be unaffected.
+ $start = strtotime('2015-03-05 16:17:18 UTC');
+
+ $clock = new PhabricatorDailyRoutineTriggerClock(
+ array(
+ 'start' => $start,
+ ));
+
+ $expect_list = array(
+ '2015-03-06 16:17:18',
+ '2015-03-07 16:17:18',
+ '2015-03-08 16:17:18',
+ '2015-03-09 16:17:18',
+ '2015-03-10 16:17:18',
+ );
+
+ $this->expectClock($clock, $expect_list, pht('Daily Routine (PST)'));
+ }
+
+ public function testDailyRoutineTriggerClockLeapSecond() {
+ // These dates cross the leap second on June 30, 2012. There has never
+ // been a negative leap second, so we can't test that yet.
+ $start = strtotime('2012-06-28 23:59:59 UTC');
+
+ $clock = new PhabricatorDailyRoutineTriggerClock(
+ array(
+ 'start' => $start,
+ ));
+
+ $expect_list = array(
+ '2012-06-29 23:59:59',
+ '2012-06-30 23:59:59',
+ '2012-07-01 23:59:59',
+ '2012-07-02 23:59:59',
+ );
+
+ $this->expectClock($clock, $expect_list, pht('Daily Routine (Leap)'));
+ }
+
+
+ public function testCDailyRoutineTriggerClockAdjustTimeOfDay() {
+ // In this case, we're going to update the time of day on the clock and
+ // make sure it keeps track of the date but adjusts the time.
+ $start = strtotime('2015-01-15 6:07:08 UTC');
+
+ $clock = new PhabricatorDailyRoutineTriggerClock(
+ array(
+ 'start' => $start,
+ ));
+
+ $expect_list = array(
+ '2015-01-16 6:07:08',
+ '2015-01-17 6:07:08',
+ '2015-01-18 6:07:08',
+ );
+
+ $last_epoch = $this->expectClock(
+ $clock,
+ $expect_list,
+ pht('Daily Routine (Pre-Adjust)'));
+
+ // Now, change the time of day.
+ $new_start = strtotime('2015-01-08 1:23:45 UTC');
+
+ $clock = new PhabricatorDailyRoutineTriggerClock(
+ array(
+ 'start' => $new_start,
+ ));
+
+ $expect_list = array(
+ '2015-01-19 1:23:45',
+ '2015-01-20 1:23:45',
+ '2015-01-21 1:23:45',
+ );
+
+ $this->expectClock(
+ $clock,
+ $expect_list,
+ pht('Daily Routine (Post-Adjust)'),
+ $last_epoch);
+ }
+
public function testSubscriptionTriggerClock() {
$start = strtotime('2014-01-31 2:34:56 UTC');
@@ -76,7 +160,15 @@
'2016-03-31 2:34:56',
);
- $last_epoch = null;
+ $this->expectClock($clock, $expect_list, pht('Billing Cycle'));
+ }
+
+ private function expectClock(
+ PhabricatorTriggerClock $clock,
+ array $expect_list,
+ $clock_name,
+ $last_epoch = null) {
+
foreach ($expect_list as $cycle => $expect) {
$next_epoch = $clock->getNextEventEpoch(
$last_epoch,
@@ -84,11 +176,13 @@
$this->assertEqual(
$expect,
- id(new DateTime('@'.$next_epoch))->format('Y-m-d g:i:s'),
- pht('Billing cycle %s.', $cycle));
+ id(new DateTime('@'.$next_epoch))->format('Y-m-d G:i:s'),
+ pht('%s (%s)', $clock_name, $cycle));
$last_epoch = $next_epoch;
}
+
+ return $last_epoch;
}
}

File Metadata

Mime Type
text/plain
Expires
Sat, Nov 2, 1:51 PM (1 w, 3 d ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6721993
Default Alt Text
D11427.diff (18 KB)

Event Timeline