Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14012508
D11427.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
18 KB
Referenced Files
None
Subscribers
None
D11427.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D11427: Add a "daily routine" trigger clock for backups, etc.
Attached
Detach File
Event Timeline
Log In to Comment