Version Notes
GIT Revision: 4bb4776a589c7cf707be13bb4f67f4d7e5067146, Build Date: 2015-11-09 21:32:36
Download this release
Release Info
Developer | Fabrizio Branca |
Extension | Aoe_Scheduler |
Version | 1.3.0 |
Comparing to | |
See all releases |
Code changes from version 0.3.2 to 1.3.0
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Cron.php +0 -57
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Cron/Grid.php +0 -138
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Instructions.php +20 -0
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Job.php +68 -0
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Job/Edit.php +68 -0
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Job/Edit/Form.php +26 -0
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Job/Edit/Tab/Form.php +286 -0
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Job/Edit/Tabs.php +21 -0
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Job/Grid.php +248 -0
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Scheduler.php +53 -49
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Scheduler/Grid.php +234 -163
- app/code/community/Aoe/Scheduler/Block/Adminhtml/Timeline.php +240 -203
- app/code/community/Aoe/Scheduler/Block/Adminhtml/TimelineDetail.php +36 -36
- app/code/community/Aoe/Scheduler/Controller/AbstractController.php +107 -0
- app/code/community/Aoe/Scheduler/Helper/Compatibility.php +20 -0
- app/code/community/Aoe/Scheduler/Helper/Data.php +316 -153
- app/code/community/Aoe/Scheduler/Helper/GracefulDead.php +70 -0
- app/code/community/Aoe/Scheduler/Model/Adminhtml/System/Config/Source/List/Code/Filtertype.php +24 -0
- app/code/community/Aoe/Scheduler/Model/Api.php +49 -39
- app/code/community/Aoe/Scheduler/Model/Collection/Crons.php +0 -62
- app/code/community/Aoe/Scheduler/Model/Configuration.php +0 -129
- app/code/community/Aoe/Scheduler/Model/HeartbeatTask.php +0 -9
- app/code/community/Aoe/Scheduler/Model/Job.php +194 -0
- app/code/community/Aoe/Scheduler/Model/Observer.php +77 -228
- app/code/community/Aoe/Scheduler/Model/ProcessManager.php +147 -0
- app/code/community/Aoe/Scheduler/Model/Resource/Job.php +358 -0
- app/code/community/Aoe/Scheduler/Model/Resource/Job/Collection.php +368 -0
- app/code/community/Aoe/Scheduler/Model/Resource/Schedule/Collection.php +20 -0
- app/code/community/Aoe/Scheduler/Model/Schedule.php +832 -148
- app/code/community/Aoe/Scheduler/Model/ScheduleManager.php +370 -0
- app/code/community/Aoe/Scheduler/Model/Task/Heartbeat.php +15 -0
- app/code/community/Aoe/Scheduler/Model/Task/Test.php +73 -0
- app/code/community/Aoe/Scheduler/Model/TestTask.php +0 -12
- app/code/community/Aoe/Scheduler/Test/Helper/Data.php +18 -0
- app/code/community/Aoe/Scheduler/Test/Model/Schedule/Runnow.php +177 -0
- app/code/community/Aoe/Scheduler/Test/Model/Schedule/Scheduling.php +102 -0
- app/code/community/Aoe/Scheduler/controllers/Adminhtml/AbstractController.php +0 -66
- app/code/community/Aoe/Scheduler/controllers/Adminhtml/CronController.php +0 -112
- app/code/community/Aoe/Scheduler/controllers/Adminhtml/InstructionsController.php +19 -0
- app/code/community/Aoe/Scheduler/controllers/Adminhtml/JobController.php +267 -0
- app/code/community/Aoe/Scheduler/controllers/Adminhtml/SchedulerController.php +50 -19
- app/code/community/Aoe/Scheduler/controllers/Adminhtml/TimelineController.php +12 -5
- app/code/community/Aoe/Scheduler/data/aoescheduler_setup/data-upgrade-0.5.4-0.5.5.php +30 -0
- app/code/community/Aoe/Scheduler/etc/adminhtml.xml +66 -58
- app/code/community/Aoe/Scheduler/etc/config.xml +175 -101
- app/code/community/Aoe/Scheduler/etc/system.xml +149 -62
- app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/install-1.0.0.php +78 -0
- app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.4.0-0.4.1.php +46 -0
- app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.4.1-0.4.2.php +15 -0
- app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.4.2-0.4.3.php +15 -0
- app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.5.0-0.5.1.php +23 -0
- app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.5.3-0.5.4.php +7 -0
- app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.5.4-1.1.0.php +17 -0
- app/design/adminhtml/default/default/layout/aoe_scheduler/aoe_scheduler.xml +64 -40
- app/design/adminhtml/default/default/template/aoe_scheduler/instructions.phtml +48 -0
- app/design/adminhtml/default/default/template/aoe_scheduler/timeline.phtml +53 -50
- app/design/adminhtml/default/default/template/aoe_scheduler/timeline_detail.phtml +56 -34
- app/etc/modules/Aoe_Scheduler.xml +6 -6
- app/locale/sv_SE/Aoe_Scheduler.csv +10 -0
- package.xml +8 -8
- scheduler_cron.sh +179 -0
- shell/scheduler.php +398 -172
- skin/adminhtml/default/default/aoe_scheduler/Images/animation.gif +0 -0
- skin/adminhtml/default/default/aoe_scheduler/Images/animation2.gif +0 -0
- skin/adminhtml/default/default/aoe_scheduler/Images/red.png +0 -0
- skin/adminhtml/default/default/aoe_scheduler/JavaScript/common.js +58 -55
- skin/adminhtml/default/default/aoe_scheduler/JavaScript/instructions.js +24 -0
- skin/adminhtml/default/default/aoe_scheduler/JavaScript/tooltip.js +161 -161
- skin/adminhtml/default/default/aoe_scheduler/StyleSheet/bars.css +13 -1
- skin/adminhtml/default/default/aoe_scheduler/StyleSheet/instructions.css +17 -0
- skin/adminhtml/default/default/aoe_scheduler/StyleSheet/timeline.css +84 -60
app/code/community/Aoe/Scheduler/Block/Adminhtml/Cron.php
DELETED
@@ -1,57 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Cron block
|
5 |
-
*
|
6 |
-
* @author Fabrizio Branca
|
7 |
-
*/
|
8 |
-
class Aoe_Scheduler_Block_Adminhtml_Cron extends Mage_Adminhtml_Block_Widget_Grid_Container {
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Constructor for Cron Adminhtml Block
|
14 |
-
*/
|
15 |
-
public function __construct() {
|
16 |
-
$this->_blockGroup = 'aoe_scheduler';
|
17 |
-
$this->_controller = 'adminhtml_cron';
|
18 |
-
$this->_headerText = Mage::helper('aoe_scheduler')->__('Available tasks');
|
19 |
-
parent::__construct();
|
20 |
-
}
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Prepare layout
|
26 |
-
*
|
27 |
-
* @return Aoe_Scheduler_Block_Adminhtml_Cron
|
28 |
-
*/
|
29 |
-
protected function _prepareLayout() {
|
30 |
-
$this->removeButton('add');
|
31 |
-
$this->_addButton('add_new', array(
|
32 |
-
'label' => Mage::helper('aoe_scheduler')->__('Generate Schedule'),
|
33 |
-
'onclick' => "setLocation('{$this->getUrl('*/*/generateSchedule')}')",
|
34 |
-
));
|
35 |
-
$this->_addButton('configure', array(
|
36 |
-
'label' => Mage::helper('aoe_scheduler')->__('Cron Configuration'),
|
37 |
-
'onclick' => "setLocation('{$this->getUrl('adminhtml/system_config/edit', array('section' => 'system'))}#system_cron')",
|
38 |
-
));
|
39 |
-
return parent::_prepareLayout();
|
40 |
-
}
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
/**
|
45 |
-
* Returns the CSS class for the header
|
46 |
-
*
|
47 |
-
* Usually 'icon-head' and a more precise class is returned. We return
|
48 |
-
* only an empty string to avoid spacing on the left of the header as we
|
49 |
-
* don't have an icon.
|
50 |
-
*
|
51 |
-
* @return string
|
52 |
-
*/
|
53 |
-
public function getHeaderCssClass() {
|
54 |
-
return '';
|
55 |
-
}
|
56 |
-
|
57 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Cron/Grid.php
DELETED
@@ -1,138 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Block: Cron grid
|
5 |
-
*
|
6 |
-
* @author Fabrizio Branca <fabrizio.branca@aoemedia.de>
|
7 |
-
*/
|
8 |
-
class Aoe_Scheduler_Block_Adminhtml_Cron_Grid extends Mage_Adminhtml_Block_Widget_Grid {
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Constructor
|
14 |
-
*/
|
15 |
-
public function __construct() {
|
16 |
-
parent::__construct();
|
17 |
-
$this->setId('cron_grid');
|
18 |
-
$this->_filterVisibility = false;
|
19 |
-
$this->_pagerVisibility = false;
|
20 |
-
}
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
/**
|
25 |
-
* Preparation of the data that is displayed by the grid.
|
26 |
-
*
|
27 |
-
* @return Aoe_Scheduler_Block_Adminhtml_Cron_Grid Self
|
28 |
-
*/
|
29 |
-
protected function _prepareCollection() {
|
30 |
-
$collection = Mage::getModel('aoe_scheduler/collection_crons');
|
31 |
-
$this->setCollection($collection);
|
32 |
-
return parent::_prepareCollection();
|
33 |
-
}
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
/**
|
38 |
-
* Add mass-actions to grid
|
39 |
-
*
|
40 |
-
* @return Aoe_Scheduler_Block_Adminhtml_Cron_Grid
|
41 |
-
*/
|
42 |
-
protected function _prepareMassaction() {
|
43 |
-
$this->setMassactionIdField('id');
|
44 |
-
$this->getMassactionBlock()->setFormFieldName('codes');
|
45 |
-
$this->getMassactionBlock()->addItem('schedule', array(
|
46 |
-
'label' => Mage::helper('aoe_scheduler')->__('Schedule now'),
|
47 |
-
'url' => $this->getUrl('*/*/scheduleNow'),
|
48 |
-
));
|
49 |
-
$this->getMassactionBlock()->addItem('run', array(
|
50 |
-
'label' => Mage::helper('aoe_scheduler')->__('Run now'),
|
51 |
-
'url' => $this->getUrl('*/*/runNow'),
|
52 |
-
));
|
53 |
-
$this->getMassactionBlock()->addItem('disable', array(
|
54 |
-
'label' => Mage::helper('aoe_scheduler')->__('Disable'),
|
55 |
-
'url' => $this->getUrl('*/*/disable'),
|
56 |
-
));
|
57 |
-
$this->getMassactionBlock()->addItem('enable', array(
|
58 |
-
'label' => Mage::helper('aoe_scheduler')->__('Enable'),
|
59 |
-
'url' => $this->getUrl('*/*/enable'),
|
60 |
-
));
|
61 |
-
return $this;
|
62 |
-
}
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
/**
|
67 |
-
* Preparation of the requested columns of the grid
|
68 |
-
*
|
69 |
-
* @return Aoe_Scheduler_Block_Adminhtml_Cron_Grid Self
|
70 |
-
*/
|
71 |
-
protected function _prepareColumns() {
|
72 |
-
$this->addColumn('id', array (
|
73 |
-
'header' => Mage::helper('aoe_scheduler')->__('Code'),
|
74 |
-
'index' => 'id',
|
75 |
-
'sortable' => false,
|
76 |
-
));
|
77 |
-
$this->addColumn('cron_expr', array (
|
78 |
-
'header' => Mage::helper('aoe_scheduler')->__('Cron Expression'),
|
79 |
-
'index' => 'cron_expr',
|
80 |
-
'sortable' => false,
|
81 |
-
));
|
82 |
-
$this->addColumn('model', array (
|
83 |
-
'header' => Mage::helper('aoe_scheduler')->__('Model'),
|
84 |
-
'index' => 'model',
|
85 |
-
'sortable' => false,
|
86 |
-
));
|
87 |
-
$this->addColumn('status', array (
|
88 |
-
'header' => Mage::helper('aoe_scheduler')->__('Status'),
|
89 |
-
'index' => 'status',
|
90 |
-
'sortable' => false,
|
91 |
-
'frame_callback' => array($this, 'decorateStatus'),
|
92 |
-
));
|
93 |
-
return parent::_prepareColumns();
|
94 |
-
}
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
/**
|
99 |
-
* Decorate status column values
|
100 |
-
*
|
101 |
-
* @return string
|
102 |
-
*/
|
103 |
-
public function decorateStatus($value) {
|
104 |
-
$cell = sprintf('<span class="grid-severity-%s"><span>%s</span></span>',
|
105 |
-
($value == Aoe_Scheduler_Model_Configuration::STATUS_DISABLED) ? 'critical' : 'notice',
|
106 |
-
Mage::helper('aoe_scheduler')->__($value)
|
107 |
-
);
|
108 |
-
return $cell;
|
109 |
-
}
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
/**
|
114 |
-
* Helper function to add store filter condition
|
115 |
-
*
|
116 |
-
* @param Mage_Core_Model_Mysql4_Collection_Abstract $collection Data collection
|
117 |
-
* @param Mage_Adminhtml_Block_Widget_Grid_Column $column Column information to be filtered
|
118 |
-
* @return void
|
119 |
-
*/
|
120 |
-
protected function _filterStoreCondition($collection, $column) {
|
121 |
-
if (!$value = $column->getFilter()->getValue()) {
|
122 |
-
return;
|
123 |
-
}
|
124 |
-
$this->getCollection()->addStoreFilter($value);
|
125 |
-
}
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
/**
|
130 |
-
* Helper function to receive grid functionality urls for current grid
|
131 |
-
*
|
132 |
-
* @return string Requested URL
|
133 |
-
*/
|
134 |
-
public function getGridUrl() {
|
135 |
-
return $this->getUrl('adminhtml/scheduler/cron', array('_current' => true));
|
136 |
-
}
|
137 |
-
|
138 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Instructions.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Job block
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
*/
|
8 |
+
class Aoe_Scheduler_Block_Adminhtml_Instructions extends Mage_Adminhtml_Block_Template
|
9 |
+
{
|
10 |
+
|
11 |
+
public function getCurrentUser()
|
12 |
+
{
|
13 |
+
return trim(shell_exec('whoami'));
|
14 |
+
}
|
15 |
+
|
16 |
+
public function getMagentoRootpath()
|
17 |
+
{
|
18 |
+
return Mage::getBaseDir();
|
19 |
+
}
|
20 |
+
}
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Job.php
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Job block
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
*/
|
8 |
+
class Aoe_Scheduler_Block_Adminhtml_Job extends Mage_Adminhtml_Block_Widget_Grid_Container
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Constructor for Job Adminhtml Block
|
12 |
+
*/
|
13 |
+
public function __construct()
|
14 |
+
{
|
15 |
+
$this->_blockGroup = 'aoe_scheduler';
|
16 |
+
$this->_controller = 'adminhtml_job';
|
17 |
+
$this->_headerText = $this->__('Available Jobs');
|
18 |
+
parent::__construct();
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Prepare layout
|
23 |
+
*
|
24 |
+
* @return $this
|
25 |
+
*/
|
26 |
+
protected function _prepareLayout()
|
27 |
+
{
|
28 |
+
$this->removeButton('add');
|
29 |
+
$this->_addButton(
|
30 |
+
'add_new_job',
|
31 |
+
array(
|
32 |
+
'label' => $this->__('Create new job'),
|
33 |
+
'onclick' => "setLocation('{$this->getUrl('*/*/new')}')",
|
34 |
+
'class' => 'add'
|
35 |
+
)
|
36 |
+
);
|
37 |
+
$this->_addButton(
|
38 |
+
'add_new',
|
39 |
+
array(
|
40 |
+
'label' => $this->__('Generate Schedule'),
|
41 |
+
'onclick' => "setLocation('{$this->getUrl('*/*/generateSchedule')}')",
|
42 |
+
)
|
43 |
+
);
|
44 |
+
$this->_addButton(
|
45 |
+
'configure',
|
46 |
+
array(
|
47 |
+
'label' => $this->__('Cron Configuration'),
|
48 |
+
'onclick' => "setLocation('{$this->getUrl('adminhtml/system_config/edit', array('section' => 'system'))}#system_cron')",
|
49 |
+
)
|
50 |
+
);
|
51 |
+
return parent::_prepareLayout();
|
52 |
+
}
|
53 |
+
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Returns the CSS class for the header
|
57 |
+
*
|
58 |
+
* Usually 'icon-head' and a more precise class is returned. We return
|
59 |
+
* only an empty string to avoid spacing on the left of the header as we
|
60 |
+
* don't have an icon.
|
61 |
+
*
|
62 |
+
* @return string
|
63 |
+
*/
|
64 |
+
public function getHeaderCssClass()
|
65 |
+
{
|
66 |
+
return '';
|
67 |
+
}
|
68 |
+
}
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Job/Edit.php
ADDED
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Job edit container
|
4 |
+
*
|
5 |
+
* @author Fabrizio Branca
|
6 |
+
* @since 2014-08-09
|
7 |
+
*/
|
8 |
+
class Aoe_Scheduler_Block_Adminhtml_Job_Edit extends Mage_Adminhtml_Block_Widget_Form_Container
|
9 |
+
{
|
10 |
+
|
11 |
+
public function __construct()
|
12 |
+
{
|
13 |
+
parent::__construct();
|
14 |
+
if ($this->getJob()->isOverlay()) {
|
15 |
+
$this->updateButton('delete', 'label', $this->__('Reset overlay'));
|
16 |
+
} elseif ($this->getJob()->isXmlOnly()) {
|
17 |
+
$this->removeButton('delete');
|
18 |
+
}
|
19 |
+
$this->removeButton('reset');
|
20 |
+
}
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Internal constructor
|
24 |
+
*
|
25 |
+
*/
|
26 |
+
protected function _construct()
|
27 |
+
{
|
28 |
+
parent::_construct();
|
29 |
+
$this->_objectId = 'job_code';
|
30 |
+
$this->_blockGroup = 'aoe_scheduler';
|
31 |
+
$this->_controller = 'adminhtml_job';
|
32 |
+
}
|
33 |
+
|
34 |
+
/**
|
35 |
+
* Get job
|
36 |
+
*
|
37 |
+
* @return Aoe_Scheduler_Model_Job
|
38 |
+
*/
|
39 |
+
public function getJob()
|
40 |
+
{
|
41 |
+
return Mage::registry('current_job_instance');
|
42 |
+
}
|
43 |
+
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Return translated header text depending on creating/editing action
|
47 |
+
*
|
48 |
+
* @return string
|
49 |
+
*/
|
50 |
+
public function getHeaderText()
|
51 |
+
{
|
52 |
+
if ($this->getJob()->getId()) {
|
53 |
+
return $this->__('Job "%s"', $this->escapeHtml($this->getJob()->getJobCode()));
|
54 |
+
} else {
|
55 |
+
return $this->__('New Job');
|
56 |
+
}
|
57 |
+
}
|
58 |
+
|
59 |
+
/**
|
60 |
+
* Return save url for edit form
|
61 |
+
*
|
62 |
+
* @return string
|
63 |
+
*/
|
64 |
+
public function getSaveUrl()
|
65 |
+
{
|
66 |
+
return $this->getUrl('*/*/save', array('_current'=>true, 'back'=>null));
|
67 |
+
}
|
68 |
+
}
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Job/Edit/Form.php
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Job edit form
|
4 |
+
*
|
5 |
+
* @author Fabrizio Branca
|
6 |
+
* @since 2014-08-09
|
7 |
+
*/
|
8 |
+
class Aoe_Scheduler_Block_Adminhtml_Job_Edit_Form extends Mage_Adminhtml_Block_Widget_Form
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Prepare form before rendering HTML
|
12 |
+
*
|
13 |
+
* @return $this
|
14 |
+
*/
|
15 |
+
protected function _prepareForm()
|
16 |
+
{
|
17 |
+
$form = new Varien_Data_Form(array(
|
18 |
+
'id' => 'edit_form',
|
19 |
+
'action' => $this->getData('action'),
|
20 |
+
'method' => 'post'
|
21 |
+
));
|
22 |
+
$form->setUseContainer(true);
|
23 |
+
$this->setForm($form);
|
24 |
+
return parent::_prepareForm();
|
25 |
+
}
|
26 |
+
}
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Job/Edit/Tab/Form.php
ADDED
@@ -0,0 +1,286 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Job form tab block
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
* @since 2014-08-09
|
8 |
+
*/
|
9 |
+
class Aoe_Scheduler_Block_Adminhtml_Job_Edit_Tab_Form extends Mage_Adminhtml_Block_Widget_Form implements Mage_Adminhtml_Block_Widget_Tab_Interface
|
10 |
+
{
|
11 |
+
/**
|
12 |
+
* Internal constructor
|
13 |
+
*
|
14 |
+
*/
|
15 |
+
protected function _construct()
|
16 |
+
{
|
17 |
+
parent::_construct();
|
18 |
+
$this->setActive(true);
|
19 |
+
}
|
20 |
+
|
21 |
+
/**
|
22 |
+
* Prepare label for tab
|
23 |
+
*
|
24 |
+
* @return string
|
25 |
+
*/
|
26 |
+
public function getTabLabel()
|
27 |
+
{
|
28 |
+
return $this->__('General');
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Prepare title for tab
|
33 |
+
*
|
34 |
+
* @return string
|
35 |
+
*/
|
36 |
+
public function getTabTitle()
|
37 |
+
{
|
38 |
+
return $this->__('General');
|
39 |
+
}
|
40 |
+
|
41 |
+
/**
|
42 |
+
* Returns status flag about this tab can be shown or not
|
43 |
+
*
|
44 |
+
* @return true
|
45 |
+
*/
|
46 |
+
public function canShowTab()
|
47 |
+
{
|
48 |
+
return true;
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Returns status flag about this tab hidden or not
|
53 |
+
*
|
54 |
+
* @return true
|
55 |
+
*/
|
56 |
+
public function isHidden()
|
57 |
+
{
|
58 |
+
return false;
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Get job
|
63 |
+
*
|
64 |
+
* @return Aoe_Scheduler_Model_Job
|
65 |
+
*/
|
66 |
+
public function getJob()
|
67 |
+
{
|
68 |
+
return Mage::registry('current_job_instance');
|
69 |
+
}
|
70 |
+
|
71 |
+
|
72 |
+
/**
|
73 |
+
* Prepare form before rendering HTML
|
74 |
+
*
|
75 |
+
* @return Mage_Widget_Block_Adminhtml_Widget_Instance_Edit_Tab_Main
|
76 |
+
*/
|
77 |
+
protected function _prepareForm()
|
78 |
+
{
|
79 |
+
$job = $this->getJob();
|
80 |
+
$form = new Varien_Data_Form(
|
81 |
+
array(
|
82 |
+
'id' => 'edit_form',
|
83 |
+
'action' => $this->getData('action'),
|
84 |
+
'method' => 'post'
|
85 |
+
)
|
86 |
+
);
|
87 |
+
|
88 |
+
$fieldset = $form->addFieldset('base_fieldset', array('legend' => $this->__('General')));
|
89 |
+
$this->_addElementTypes($fieldset);
|
90 |
+
|
91 |
+
$fieldset->addField(
|
92 |
+
'job_code',
|
93 |
+
'text',
|
94 |
+
array(
|
95 |
+
'name' => 'job_code',
|
96 |
+
'label' => $this->__('Job code'),
|
97 |
+
'title' => $this->__('Job code'),
|
98 |
+
'class' => '',
|
99 |
+
'required' => true,
|
100 |
+
'disabled' => $job->getJobCode() ? true : false,
|
101 |
+
)
|
102 |
+
);
|
103 |
+
|
104 |
+
$fieldset->addField(
|
105 |
+
'name',
|
106 |
+
'text',
|
107 |
+
array(
|
108 |
+
'name' => 'name',
|
109 |
+
'label' => $this->__('Name'),
|
110 |
+
'title' => $this->__('Name'),
|
111 |
+
'class' => '',
|
112 |
+
'required' => false,
|
113 |
+
'after_element_html' => $this->getOriginalValueSnippet($job, 'name'),
|
114 |
+
)
|
115 |
+
);
|
116 |
+
|
117 |
+
$fieldset->addField(
|
118 |
+
'short_description',
|
119 |
+
'textarea',
|
120 |
+
array(
|
121 |
+
'name' => 'short_description',
|
122 |
+
'label' => $this->__('Short description'),
|
123 |
+
'title' => $this->__('Short description'),
|
124 |
+
'class' => '',
|
125 |
+
'required' => false,
|
126 |
+
'after_element_html' => $this->getOriginalValueSnippet($job, 'short_description'),
|
127 |
+
)
|
128 |
+
);
|
129 |
+
|
130 |
+
$fieldset->addField(
|
131 |
+
'description',
|
132 |
+
'textarea',
|
133 |
+
array(
|
134 |
+
'name' => 'description',
|
135 |
+
'label' => $this->__('Description'),
|
136 |
+
'title' => $this->__('Description'),
|
137 |
+
'class' => '',
|
138 |
+
'required' => false,
|
139 |
+
'after_element_html' => $this->getOriginalValueSnippet($job, 'description'),
|
140 |
+
)
|
141 |
+
);
|
142 |
+
|
143 |
+
$fieldset->addField(
|
144 |
+
'run_model',
|
145 |
+
'text',
|
146 |
+
array(
|
147 |
+
'name' => 'run_model',
|
148 |
+
'label' => $this->__('Run model'),
|
149 |
+
'title' => $this->__('Run model'),
|
150 |
+
'class' => '',
|
151 |
+
'required' => true,
|
152 |
+
'note' => $this->__('e.g. "aoe_scheduler/task_heartbeat::run"'),
|
153 |
+
'after_element_html' => $this->getOriginalValueSnippet($job, 'run/model'),
|
154 |
+
)
|
155 |
+
);
|
156 |
+
|
157 |
+
$fieldset->addField(
|
158 |
+
'is_active',
|
159 |
+
'select',
|
160 |
+
array(
|
161 |
+
'name' => 'is_active',
|
162 |
+
'label' => $this->__('Status'),
|
163 |
+
'title' => $this->__('Status'),
|
164 |
+
'required' => true,
|
165 |
+
'options' => array(
|
166 |
+
0 => $this->__('Disabled'),
|
167 |
+
1 => $this->__('Enabled')
|
168 |
+
),
|
169 |
+
'after_element_html' => $this->getOriginalValueSnippetFlag($job, 'is_active', 'Enabled', 'Disabled'),
|
170 |
+
)
|
171 |
+
);
|
172 |
+
|
173 |
+
$fieldset = $form->addFieldset('cron_fieldset', array('legend' => $this->__('Scheduling')));
|
174 |
+
$this->_addElementTypes($fieldset);
|
175 |
+
|
176 |
+
$fieldset->addField(
|
177 |
+
'schedule_config_path',
|
178 |
+
'text',
|
179 |
+
array(
|
180 |
+
'name' => 'schedule_config_path',
|
181 |
+
'label' => $this->__('Cron configuration path'),
|
182 |
+
'title' => $this->__('Cron configuration path'),
|
183 |
+
'class' => '',
|
184 |
+
'required' => false,
|
185 |
+
'note' => $this->__(
|
186 |
+
'Path to system configuration containing the cron configuration for this job. (e.g. system/cron/scheduler_cron_expr_heartbeat) This configuration - if set - has a higher priority over the cron expression configured with the job directly.'
|
187 |
+
),
|
188 |
+
'after_element_html' => $this->getOriginalValueSnippet($job, 'schedule/config_path'),
|
189 |
+
)
|
190 |
+
);
|
191 |
+
|
192 |
+
$fieldset->addField(
|
193 |
+
'schedule_cron_expr',
|
194 |
+
'text',
|
195 |
+
array(
|
196 |
+
'name' => 'schedule_cron_expr',
|
197 |
+
'label' => $this->__('Cron expression'),
|
198 |
+
'title' => $this->__('Cron expression'),
|
199 |
+
'required' => false,
|
200 |
+
'note' => $this->__('e.g "*/5 * * * *" or "always"'),
|
201 |
+
'after_element_html' => $this->getOriginalValueSnippet($job, 'schedule/cron_expr'),
|
202 |
+
)
|
203 |
+
);
|
204 |
+
|
205 |
+
$fieldset = $form->addFieldset('parameter_fieldset', array('legend' => $this->__('Extras')));
|
206 |
+
$this->_addElementTypes($fieldset);
|
207 |
+
|
208 |
+
$fieldset->addField(
|
209 |
+
'parameters',
|
210 |
+
'textarea',
|
211 |
+
array(
|
212 |
+
'name' => 'parameters',
|
213 |
+
'label' => $this->__('Parameters'),
|
214 |
+
'title' => $this->__('Parameters'),
|
215 |
+
'class' => 'textarea',
|
216 |
+
'required' => false,
|
217 |
+
'note' => $this->__('These parameters will be passed to the model. It is up to the model to specify the format of these parameters (e.g. json/xml/...'),
|
218 |
+
'after_element_html' => $this->getOriginalValueSnippet($job, 'parameters'),
|
219 |
+
)
|
220 |
+
);
|
221 |
+
|
222 |
+
$fieldset->addField(
|
223 |
+
'groups',
|
224 |
+
'textarea',
|
225 |
+
array(
|
226 |
+
'name' => 'groups',
|
227 |
+
'label' => $this->__('Groups'),
|
228 |
+
'title' => $this->__('Groups'),
|
229 |
+
'class' => 'textarea',
|
230 |
+
'required' => false,
|
231 |
+
'note' => $this->__('Comma-separated list of groups (tags) that can be used with the include/exclude command line options of scheduler.php'),
|
232 |
+
'after_element_html' => $this->getOriginalValueSnippet($job, 'groups'),
|
233 |
+
)
|
234 |
+
);
|
235 |
+
|
236 |
+
$this->setForm($form);
|
237 |
+
|
238 |
+
return parent::_prepareForm();
|
239 |
+
}
|
240 |
+
|
241 |
+
protected function getOriginalValueSnippet(Aoe_Scheduler_Model_Job $job, $key)
|
242 |
+
{
|
243 |
+
if ($job->isDbOnly()) {
|
244 |
+
return '';
|
245 |
+
}
|
246 |
+
|
247 |
+
$xmlJobData = $job->getXmlJobData();
|
248 |
+
if (!array_key_exists($key, $xmlJobData)) {
|
249 |
+
return '';
|
250 |
+
}
|
251 |
+
|
252 |
+
$value = $xmlJobData[$key];
|
253 |
+
if ($value === null || $value === '') {
|
254 |
+
$value = '<em>empty</em>';
|
255 |
+
}
|
256 |
+
|
257 |
+
return '<p class="original" style="background-color: white"><strong>Original:</strong> ' . $value . '</p>';
|
258 |
+
}
|
259 |
+
|
260 |
+
protected function getOriginalValueSnippetFlag(Aoe_Scheduler_Model_Job $job, $key, $trueLabel, $falseLabel)
|
261 |
+
{
|
262 |
+
if ($job->isDbOnly()) {
|
263 |
+
return '';
|
264 |
+
}
|
265 |
+
|
266 |
+
$xmlJobData = $job->getXmlJobData();
|
267 |
+
if (!array_key_exists($key, $xmlJobData)) {
|
268 |
+
return '';
|
269 |
+
}
|
270 |
+
|
271 |
+
$value = $this->__(!in_array($xmlJobData[$key], array(false, 'false', 0, '0'), true) ? $trueLabel : $falseLabel);
|
272 |
+
|
273 |
+
return '<p class="original" style="background-color: white"><strong>Original:</strong> ' . $value . '</p>';
|
274 |
+
}
|
275 |
+
|
276 |
+
/**
|
277 |
+
* Initialize form fields values
|
278 |
+
*
|
279 |
+
* @return $this
|
280 |
+
*/
|
281 |
+
protected function _initFormValues()
|
282 |
+
{
|
283 |
+
$this->getForm()->addValues($this->getJob()->getData());
|
284 |
+
return parent::_initFormValues();
|
285 |
+
}
|
286 |
+
}
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Job/Edit/Tabs.php
ADDED
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Job edit tabs container
|
4 |
+
*
|
5 |
+
* @author Fabrizio Branca
|
6 |
+
* @since 2014-08-09
|
7 |
+
*/
|
8 |
+
class Aoe_Scheduler_Block_Adminhtml_Job_Edit_Tabs extends Mage_Adminhtml_Block_Widget_Tabs
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Internal constructor
|
12 |
+
*
|
13 |
+
*/
|
14 |
+
protected function _construct()
|
15 |
+
{
|
16 |
+
parent::_construct();
|
17 |
+
$this->setId('job_tabs');
|
18 |
+
$this->setDestElementId('edit_form');
|
19 |
+
$this->setTitle($this->__('Job'));
|
20 |
+
}
|
21 |
+
}
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Job/Grid.php
ADDED
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Block: Job grid
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
*/
|
8 |
+
class Aoe_Scheduler_Block_Adminhtml_Job_Grid extends Mage_Adminhtml_Block_Widget_Grid
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Constructor
|
12 |
+
*/
|
13 |
+
public function __construct()
|
14 |
+
{
|
15 |
+
parent::__construct();
|
16 |
+
$this->setId('job_grid');
|
17 |
+
$this->_filterVisibility = false;
|
18 |
+
$this->_pagerVisibility = false;
|
19 |
+
}
|
20 |
+
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Preparation of the data that is displayed by the grid.
|
24 |
+
*
|
25 |
+
* @return $this
|
26 |
+
*/
|
27 |
+
protected function _prepareCollection()
|
28 |
+
{
|
29 |
+
/** @var Aoe_Scheduler_Model_Resource_Job_Collection $collection */
|
30 |
+
$collection = Mage::getSingleton('aoe_scheduler/job')->getCollection();
|
31 |
+
$this->setCollection($collection);
|
32 |
+
return parent::_prepareCollection();
|
33 |
+
}
|
34 |
+
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Add mass-actions to grid
|
38 |
+
*
|
39 |
+
* @return $this
|
40 |
+
*/
|
41 |
+
protected function _prepareMassaction()
|
42 |
+
{
|
43 |
+
$this->setMassactionIdField('id');
|
44 |
+
$this->getMassactionBlock()->setFormFieldName('codes');
|
45 |
+
$this->getMassactionBlock()->addItem(
|
46 |
+
'schedule',
|
47 |
+
array(
|
48 |
+
'label' => $this->__('Schedule now'),
|
49 |
+
'url' => $this->getUrl('*/*/scheduleNow'),
|
50 |
+
)
|
51 |
+
);
|
52 |
+
if (Mage::getStoreConfig('system/cron/enableRunNow')) {
|
53 |
+
$this->getMassactionBlock()->addItem(
|
54 |
+
'run',
|
55 |
+
array(
|
56 |
+
'label' => $this->__('Run now'),
|
57 |
+
'url' => $this->getUrl('*/*/runNow'),
|
58 |
+
)
|
59 |
+
);
|
60 |
+
}
|
61 |
+
$this->getMassactionBlock()->addItem(
|
62 |
+
'disable',
|
63 |
+
array(
|
64 |
+
'label' => $this->__('Disable'),
|
65 |
+
'url' => $this->getUrl('*/*/disable'),
|
66 |
+
)
|
67 |
+
);
|
68 |
+
$this->getMassactionBlock()->addItem(
|
69 |
+
'enable',
|
70 |
+
array(
|
71 |
+
'label' => $this->__('Enable'),
|
72 |
+
'url' => $this->getUrl('*/*/enable'),
|
73 |
+
)
|
74 |
+
);
|
75 |
+
return $this;
|
76 |
+
}
|
77 |
+
|
78 |
+
|
79 |
+
/**
|
80 |
+
* Preparation of the requested columns of the grid
|
81 |
+
*
|
82 |
+
* @return $this
|
83 |
+
*/
|
84 |
+
protected function _prepareColumns()
|
85 |
+
{
|
86 |
+
$this->addColumn(
|
87 |
+
'job_code',
|
88 |
+
array(
|
89 |
+
'header' => $this->__('Job code'),
|
90 |
+
'index' => 'job_code',
|
91 |
+
'sortable' => false,
|
92 |
+
)
|
93 |
+
);
|
94 |
+
|
95 |
+
$this->addColumn(
|
96 |
+
'name',
|
97 |
+
array(
|
98 |
+
'header' => $this->__('Name'),
|
99 |
+
'index' => 'name',
|
100 |
+
'sortable' => false,
|
101 |
+
)
|
102 |
+
);
|
103 |
+
|
104 |
+
$this->addColumn(
|
105 |
+
'short_description',
|
106 |
+
array(
|
107 |
+
'header' => $this->__('Short Description'),
|
108 |
+
'index' => 'short_description',
|
109 |
+
'sortable' => false,
|
110 |
+
)
|
111 |
+
);
|
112 |
+
|
113 |
+
$this->addColumn(
|
114 |
+
'schedule_cron_expr',
|
115 |
+
array(
|
116 |
+
'header' => $this->__('Cron expression'),
|
117 |
+
'index' => 'schedule_cron_expr',
|
118 |
+
'sortable' => false,
|
119 |
+
'frame_callback' => array($this, 'decorateCronExpression'),
|
120 |
+
)
|
121 |
+
);
|
122 |
+
$this->addColumn(
|
123 |
+
'run_model',
|
124 |
+
array(
|
125 |
+
'header' => $this->__('Run model'),
|
126 |
+
'index' => 'run_model',
|
127 |
+
'sortable' => false,
|
128 |
+
)
|
129 |
+
);
|
130 |
+
$this->addColumn(
|
131 |
+
'parameters',
|
132 |
+
array(
|
133 |
+
'header' => $this->__('Parameters'),
|
134 |
+
'index' => 'parameters',
|
135 |
+
'sortable' => false,
|
136 |
+
'frame_callback' => array($this, 'decorateTrim'),
|
137 |
+
)
|
138 |
+
);
|
139 |
+
$this->addColumn(
|
140 |
+
'groups',
|
141 |
+
array(
|
142 |
+
'header' => $this->__('Groups'),
|
143 |
+
'index' => 'groups',
|
144 |
+
'sortable' => false,
|
145 |
+
'frame_callback' => array($this, 'decorateTrim'),
|
146 |
+
)
|
147 |
+
);
|
148 |
+
$this->addColumn(
|
149 |
+
'type',
|
150 |
+
array(
|
151 |
+
'header' => $this->__('Type'),
|
152 |
+
'sortable' => false,
|
153 |
+
'frame_callback' => array($this, 'decorateType'),
|
154 |
+
)
|
155 |
+
);
|
156 |
+
$this->addColumn(
|
157 |
+
'is_active',
|
158 |
+
array(
|
159 |
+
'header' => $this->__('Status'),
|
160 |
+
'index' => 'is_active',
|
161 |
+
'sortable' => false,
|
162 |
+
'frame_callback' => array($this, 'decorateStatus'),
|
163 |
+
)
|
164 |
+
);
|
165 |
+
return parent::_prepareColumns();
|
166 |
+
}
|
167 |
+
|
168 |
+
|
169 |
+
/**
|
170 |
+
* Decorate status column values
|
171 |
+
*
|
172 |
+
* @param $value
|
173 |
+
*
|
174 |
+
* @return string
|
175 |
+
*/
|
176 |
+
public function decorateStatus($value)
|
177 |
+
{
|
178 |
+
$cell = sprintf(
|
179 |
+
'<span class="grid-severity-%s"><span>%s</span></span>',
|
180 |
+
$value ? 'notice' : 'critical',
|
181 |
+
$this->__($value ? 'Enabled' : 'Disabled')
|
182 |
+
);
|
183 |
+
return $cell;
|
184 |
+
}
|
185 |
+
|
186 |
+
|
187 |
+
/**
|
188 |
+
* Decorate cron expression
|
189 |
+
*
|
190 |
+
* @param $value
|
191 |
+
* @param Aoe_Scheduler_Model_Job $job
|
192 |
+
*
|
193 |
+
* @return string
|
194 |
+
*/
|
195 |
+
public function decorateCronExpression($value, Aoe_Scheduler_Model_Job $job)
|
196 |
+
{
|
197 |
+
return $job->getCronExpression();
|
198 |
+
}
|
199 |
+
|
200 |
+
|
201 |
+
/**
|
202 |
+
* Decorate cron expression
|
203 |
+
*
|
204 |
+
* @param $value
|
205 |
+
*
|
206 |
+
* @return string
|
207 |
+
*/
|
208 |
+
public function decorateTrim($value)
|
209 |
+
{
|
210 |
+
return sprintf('<span title="%s">%s</span>', $value, mb_strimwidth($value, 0, 40, "..."));
|
211 |
+
}
|
212 |
+
|
213 |
+
|
214 |
+
/**
|
215 |
+
* Decorate cron expression
|
216 |
+
*
|
217 |
+
* @param $value
|
218 |
+
* @param Aoe_Scheduler_Model_Job $job
|
219 |
+
*
|
220 |
+
* @return string
|
221 |
+
*/
|
222 |
+
public function decorateType($value, Aoe_Scheduler_Model_Job $job)
|
223 |
+
{
|
224 |
+
return $job->getType();
|
225 |
+
}
|
226 |
+
|
227 |
+
/**
|
228 |
+
* Row click url
|
229 |
+
*
|
230 |
+
* @param object $row
|
231 |
+
*
|
232 |
+
* @return string
|
233 |
+
*/
|
234 |
+
public function getRowUrl($row)
|
235 |
+
{
|
236 |
+
return $this->getUrl('*/*/edit', array('job_code' => $row->getJobCode()));
|
237 |
+
}
|
238 |
+
|
239 |
+
/**
|
240 |
+
* Helper function to receive grid functionality urls for current grid
|
241 |
+
*
|
242 |
+
* @return string Requested URL
|
243 |
+
*/
|
244 |
+
public function getGridUrl()
|
245 |
+
{
|
246 |
+
return $this->getUrl('adminhtml/job/index', array('_current' => true));
|
247 |
+
}
|
248 |
+
}
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Scheduler.php
CHANGED
@@ -5,53 +5,57 @@
|
|
5 |
*
|
6 |
* @author Fabrizio Branca
|
7 |
*/
|
8 |
-
class Aoe_Scheduler_Block_Adminhtml_Scheduler extends Mage_Adminhtml_Block_Widget_Grid_Container
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
|
|
|
|
|
|
|
|
57 |
}
|
5 |
*
|
6 |
* @author Fabrizio Branca
|
7 |
*/
|
8 |
+
class Aoe_Scheduler_Block_Adminhtml_Scheduler extends Mage_Adminhtml_Block_Widget_Grid_Container
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Constructor for Scheduler Adminhtml Block
|
12 |
+
*/
|
13 |
+
public function __construct()
|
14 |
+
{
|
15 |
+
$this->_blockGroup = 'aoe_scheduler';
|
16 |
+
$this->_controller = 'adminhtml_scheduler';
|
17 |
+
$this->_headerText = $this->__('Scheduled tasks');
|
18 |
+
parent::__construct();
|
19 |
+
}
|
20 |
+
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Prepare layout
|
24 |
+
*
|
25 |
+
* @return $this
|
26 |
+
*/
|
27 |
+
protected function _prepareLayout()
|
28 |
+
{
|
29 |
+
$this->removeButton('add');
|
30 |
+
$this->_addButton(
|
31 |
+
'add_new',
|
32 |
+
array(
|
33 |
+
'label' => $this->__('Generate Schedule'),
|
34 |
+
'onclick' => "setLocation('{$this->getUrl('*/*/generateSchedule')}')",
|
35 |
+
)
|
36 |
+
);
|
37 |
+
$this->_addButton(
|
38 |
+
'configure',
|
39 |
+
array(
|
40 |
+
'label' => $this->__('Cron Configuration'),
|
41 |
+
'onclick' => "setLocation('{$this->getUrl('adminhtml/system_config/edit', array('section' => 'system'))}#system_cron')",
|
42 |
+
)
|
43 |
+
);
|
44 |
+
return parent::_prepareLayout();
|
45 |
+
}
|
46 |
+
|
47 |
+
|
48 |
+
/**
|
49 |
+
* Returns the CSS class for the header
|
50 |
+
*
|
51 |
+
* Usually 'icon-head' and a more precise class is returned. We return
|
52 |
+
* only an empty string to avoid spacing on the left of the header as we
|
53 |
+
* don't have an icon.
|
54 |
+
*
|
55 |
+
* @return string
|
56 |
+
*/
|
57 |
+
public function getHeaderCssClass()
|
58 |
+
{
|
59 |
+
return '';
|
60 |
+
}
|
61 |
}
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Scheduler/Grid.php
CHANGED
@@ -3,168 +3,239 @@
|
|
3 |
/**
|
4 |
* Block: Scheduler Grid
|
5 |
*
|
6 |
-
* @author Fabrizio Branca
|
7 |
*/
|
8 |
-
class Aoe_Scheduler_Block_Adminhtml_Scheduler_Grid extends Mage_Adminhtml_Block_Widget_Grid
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
170 |
}
|
3 |
/**
|
4 |
* Block: Scheduler Grid
|
5 |
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
*/
|
8 |
+
class Aoe_Scheduler_Block_Adminhtml_Scheduler_Grid extends Mage_Adminhtml_Block_Widget_Grid
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Constructor. Set basic parameters
|
12 |
+
*/
|
13 |
+
public function __construct()
|
14 |
+
{
|
15 |
+
parent::__construct();
|
16 |
+
$this->setId('scheduler_grid');
|
17 |
+
$this->setUseAjax(false);
|
18 |
+
$this->setDefaultSort('scheduled_at');
|
19 |
+
$this->setDefaultDir('DESC');
|
20 |
+
$this->setSaveParametersInSession(true);
|
21 |
+
}
|
22 |
+
|
23 |
+
|
24 |
+
/**
|
25 |
+
* Preparation of the data that is displayed by the grid.
|
26 |
+
*
|
27 |
+
* @return $this
|
28 |
+
*/
|
29 |
+
protected function _prepareCollection()
|
30 |
+
{
|
31 |
+
/** @var Mage_Cron_Model_Resource_Schedule_Collection $collection */
|
32 |
+
$collection = Mage::getModel('cron/schedule')->getCollection();
|
33 |
+
$this->setCollection($collection);
|
34 |
+
return parent::_prepareCollection();
|
35 |
+
}
|
36 |
+
|
37 |
+
|
38 |
+
/**
|
39 |
+
* Add mass-actions to grid
|
40 |
+
*
|
41 |
+
* @return $this
|
42 |
+
*/
|
43 |
+
protected function _prepareMassaction()
|
44 |
+
{
|
45 |
+
$this->setMassactionIdField('schedule_id');
|
46 |
+
$this->getMassactionBlock()->setFormFieldName('schedule_ids');
|
47 |
+
$this->getMassactionBlock()->addItem(
|
48 |
+
'delete',
|
49 |
+
array(
|
50 |
+
'label' => $this->__('Delete'),
|
51 |
+
'url' => $this->getUrl('*/*/delete'),
|
52 |
+
)
|
53 |
+
);
|
54 |
+
$this->getMassactionBlock()->addItem(
|
55 |
+
'kill',
|
56 |
+
array(
|
57 |
+
'label' => $this->__('Kill'),
|
58 |
+
'url' => $this->getUrl('*/*/kill'),
|
59 |
+
)
|
60 |
+
);
|
61 |
+
return $this;
|
62 |
+
}
|
63 |
+
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Preparation of the requested columns of the grid
|
67 |
+
*
|
68 |
+
* @return $this
|
69 |
+
*/
|
70 |
+
protected function _prepareColumns()
|
71 |
+
{
|
72 |
+
$viewHelper = $this->helper('aoe_scheduler/data');
|
73 |
+
|
74 |
+
$this->addColumn(
|
75 |
+
'schedule_id',
|
76 |
+
array(
|
77 |
+
'header' => $this->__('Id'),
|
78 |
+
'index' => 'schedule_id',
|
79 |
+
)
|
80 |
+
);
|
81 |
+
$config = array(
|
82 |
+
'header' => $this->__('Job'),
|
83 |
+
'index' => 'job_code',
|
84 |
+
);
|
85 |
+
switch (Mage::getStoreConfig('system/cron/listCodeFilterType')) {
|
86 |
+
case Aoe_Scheduler_Model_Adminhtml_System_Config_Source_List_Code_Filtertype::SELECT:
|
87 |
+
$config['type'] = 'options';
|
88 |
+
$config['options'] = Mage::getSingleton('aoe_scheduler/job')->getCollection()->toOptionHash('job_code', 'name');
|
89 |
+
break;
|
90 |
+
case Aoe_Scheduler_Model_Adminhtml_System_Config_Source_List_Code_Filtertype::TEXT:
|
91 |
+
default:
|
92 |
+
$config['type'] = 'text';
|
93 |
+
}
|
94 |
+
$this->addColumn(
|
95 |
+
'job_code',
|
96 |
+
$config
|
97 |
+
);
|
98 |
+
$this->addColumn(
|
99 |
+
'created_at',
|
100 |
+
array(
|
101 |
+
'header' => $this->__('Created'),
|
102 |
+
'index' => 'created_at',
|
103 |
+
'type' => 'datetime'
|
104 |
+
)
|
105 |
+
);
|
106 |
+
$this->addColumn(
|
107 |
+
'scheduled_at',
|
108 |
+
array(
|
109 |
+
'header' => $this->__('Scheduled'),
|
110 |
+
'index' => 'scheduled_at',
|
111 |
+
'type' => 'datetime'
|
112 |
+
)
|
113 |
+
);
|
114 |
+
$this->addColumn(
|
115 |
+
'executed_at',
|
116 |
+
array(
|
117 |
+
'header' => $this->__('Executed'),
|
118 |
+
'index' => 'executed_at',
|
119 |
+
'type' => 'datetime'
|
120 |
+
)
|
121 |
+
);
|
122 |
+
$this->addColumn(
|
123 |
+
'last_seen',
|
124 |
+
array(
|
125 |
+
'header' => $this->__('Last seen'),
|
126 |
+
'index' => 'last_seen',
|
127 |
+
'type' => 'datetime'
|
128 |
+
)
|
129 |
+
);
|
130 |
+
$this->addColumn(
|
131 |
+
'eta',
|
132 |
+
array(
|
133 |
+
'header' => $this->__('ETA'),
|
134 |
+
'index' => 'eta',
|
135 |
+
'type' => 'datetime'
|
136 |
+
)
|
137 |
+
);
|
138 |
+
$this->addColumn(
|
139 |
+
'finished_at',
|
140 |
+
array(
|
141 |
+
'header' => $this->__('Finished'),
|
142 |
+
'index' => 'finished_at',
|
143 |
+
'type' => 'datetime'
|
144 |
+
)
|
145 |
+
);
|
146 |
+
$this->addColumn(
|
147 |
+
'messages',
|
148 |
+
array(
|
149 |
+
'header' => $this->__('Messages'),
|
150 |
+
'index' => 'messages',
|
151 |
+
'frame_callback' => array($this, 'decorateMessages')
|
152 |
+
)
|
153 |
+
);
|
154 |
+
$this->addColumn(
|
155 |
+
'host',
|
156 |
+
array(
|
157 |
+
'header' => $this->__('Host'),
|
158 |
+
'index' => 'host',
|
159 |
+
)
|
160 |
+
);
|
161 |
+
$this->addColumn(
|
162 |
+
'pid',
|
163 |
+
array(
|
164 |
+
'header' => $this->__('Pid'),
|
165 |
+
'index' => 'pid',
|
166 |
+
'width' => '50',
|
167 |
+
)
|
168 |
+
);
|
169 |
+
$this->addColumn(
|
170 |
+
'status',
|
171 |
+
array(
|
172 |
+
'header' => $this->__('Status'),
|
173 |
+
'index' => 'status',
|
174 |
+
'frame_callback' => array($viewHelper, 'decorateStatus'),
|
175 |
+
'type' => 'options',
|
176 |
+
'options' => Mage::getSingleton('cron/schedule')->getStatuses()
|
177 |
+
)
|
178 |
+
);
|
179 |
+
|
180 |
+
return parent::_prepareColumns();
|
181 |
+
}
|
182 |
+
|
183 |
+
|
184 |
+
/**
|
185 |
+
* Decorate message
|
186 |
+
*
|
187 |
+
* @param string $value
|
188 |
+
* @param Aoe_Scheduler_Model_Schedule $row
|
189 |
+
*
|
190 |
+
* @return string
|
191 |
+
*/
|
192 |
+
public function decorateMessages($value, Aoe_Scheduler_Model_Schedule $row)
|
193 |
+
{
|
194 |
+
$return = '';
|
195 |
+
if (!empty($value)) {
|
196 |
+
$return .= '<a href="#" onclick="$(\'messages_' . $row->getScheduleId() . '\').toggle(); return false;">' . $this->__('Message') . '</a>';
|
197 |
+
$return .= '<div class="schedule-message" id="messages_' . $row->getScheduleId() . '" style="display: none; width: 300px; overflow: auto; font-size: small;"><pre>' . $value . '</pre></div>';
|
198 |
+
}
|
199 |
+
return $return;
|
200 |
+
}
|
201 |
+
|
202 |
+
|
203 |
+
/**
|
204 |
+
* Helper function to do after load modifications
|
205 |
+
*
|
206 |
+
* @return void
|
207 |
+
*/
|
208 |
+
protected function _afterLoadCollection()
|
209 |
+
{
|
210 |
+
$this->getCollection()->walk('afterLoad');
|
211 |
+
parent::_afterLoadCollection();
|
212 |
+
}
|
213 |
+
|
214 |
+
|
215 |
+
/**
|
216 |
+
* Helper function to add store filter condition
|
217 |
+
*
|
218 |
+
* @param Mage_Core_Model_Mysql4_Collection_Abstract $collection Data collection
|
219 |
+
* @param Mage_Adminhtml_Block_Widget_Grid_Column $column Column information to be filtered
|
220 |
+
*
|
221 |
+
* @return void
|
222 |
+
*/
|
223 |
+
protected function _filterStoreCondition($collection, $column)
|
224 |
+
{
|
225 |
+
if (!$value = $column->getFilter()->getValue()) {
|
226 |
+
return;
|
227 |
+
}
|
228 |
+
$this->getCollection()->addStoreFilter($value);
|
229 |
+
}
|
230 |
+
|
231 |
+
|
232 |
+
/**
|
233 |
+
* Helper function to receive grid functionality urls for current grid
|
234 |
+
*
|
235 |
+
* @return string Requested URL
|
236 |
+
*/
|
237 |
+
public function getGridUrl()
|
238 |
+
{
|
239 |
+
return $this->getUrl('adminhtml/scheduler/index', array('_current' => true));
|
240 |
+
}
|
241 |
}
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/Timeline.php
CHANGED
@@ -5,207 +5,244 @@
|
|
5 |
*
|
6 |
* @author Fabrizio Branca
|
7 |
*/
|
8 |
-
class Aoe_Scheduler_Block_Adminhtml_Timeline extends Mage_Adminhtml_Block_Widget_Container
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
174 |
-
|
175 |
-
|
176 |
-
|
177 |
-
|
178 |
-
|
179 |
-
|
180 |
-
|
181 |
-
|
182 |
-
|
183 |
-
|
184 |
-
|
185 |
-
|
186 |
-
|
187 |
-
|
188 |
-
|
189 |
-
|
190 |
-
|
191 |
-
|
192 |
-
|
193 |
-
|
194 |
-
|
195 |
-
|
196 |
-
|
197 |
-
|
198 |
-
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
|
210 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
211 |
}
|
5 |
*
|
6 |
* @author Fabrizio Branca
|
7 |
*/
|
8 |
+
class Aoe_Scheduler_Block_Adminhtml_Timeline extends Mage_Adminhtml_Block_Widget_Container
|
9 |
+
{
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var int amount of seconds per pixel
|
13 |
+
*/
|
14 |
+
protected $zoom = 15;
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var int starttime
|
18 |
+
*/
|
19 |
+
protected $starttime;
|
20 |
+
|
21 |
+
/**
|
22 |
+
* @var int endtime
|
23 |
+
*/
|
24 |
+
protected $endtime;
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @var array schedules
|
28 |
+
*/
|
29 |
+
protected $schedules = array();
|
30 |
+
|
31 |
+
|
32 |
+
/**
|
33 |
+
* Constructor
|
34 |
+
*
|
35 |
+
* @return void
|
36 |
+
*/
|
37 |
+
protected function _construct()
|
38 |
+
{
|
39 |
+
$this->_headerText = $this->__('Scheduler Timeline');
|
40 |
+
$this->loadSchedules();
|
41 |
+
parent::_construct();
|
42 |
+
}
|
43 |
+
|
44 |
+
|
45 |
+
/**
|
46 |
+
* Prepare layout
|
47 |
+
*
|
48 |
+
* @return $this
|
49 |
+
*/
|
50 |
+
protected function _prepareLayout()
|
51 |
+
{
|
52 |
+
$this->removeButton('add');
|
53 |
+
$this->_addButton('add_new', array(
|
54 |
+
'label' => $this->__('Generate Schedule'),
|
55 |
+
'onclick' => "setLocation('{$this->getUrl('*/*/generateSchedule')}')",
|
56 |
+
));
|
57 |
+
$this->_addButton('configure', array(
|
58 |
+
'label' => $this->__('Cron Configuration'),
|
59 |
+
'onclick' => "setLocation('{$this->getUrl('adminhtml/system_config/edit', array('section' => 'system'))}#system_cron')",
|
60 |
+
));
|
61 |
+
return parent::_prepareLayout();
|
62 |
+
}
|
63 |
+
|
64 |
+
|
65 |
+
/**
|
66 |
+
* Return the last full houd
|
67 |
+
*
|
68 |
+
* @param int $timestamp
|
69 |
+
* @return int
|
70 |
+
*/
|
71 |
+
protected function hourFloor($timestamp)
|
72 |
+
{
|
73 |
+
return mktime(date('H', $timestamp), 0, 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp));
|
74 |
+
}
|
75 |
+
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Returns the next full hour
|
79 |
+
*
|
80 |
+
* @param int $timestamp
|
81 |
+
* @return int
|
82 |
+
*/
|
83 |
+
protected function hourCeil($timestamp)
|
84 |
+
{
|
85 |
+
return mktime(date('H', $timestamp) + 1, 0, 0, date('n', $timestamp), date('j', $timestamp), date('Y', $timestamp));
|
86 |
+
}
|
87 |
+
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Load schedules
|
91 |
+
*
|
92 |
+
* @return void
|
93 |
+
*/
|
94 |
+
protected function loadSchedules()
|
95 |
+
{
|
96 |
+
/* @var Mage_Cron_Model_Resource_Schedule_Collection $collection */
|
97 |
+
$collection = Mage::getModel('cron/schedule')->getCollection();
|
98 |
+
|
99 |
+
$minDate = null;
|
100 |
+
$maxDate = null;
|
101 |
+
|
102 |
+
foreach ($collection as $schedule) {
|
103 |
+
/* @var Aoe_Scheduler_Model_Schedule $schedule */
|
104 |
+
$startTime = $schedule->getStarttime();
|
105 |
+
if (empty($startTime)) {
|
106 |
+
continue;
|
107 |
+
}
|
108 |
+
$minDate = is_null($minDate) ? $startTime : min($minDate, $startTime);
|
109 |
+
$maxDate = is_null($maxDate) ? $startTime : max($maxDate, $startTime);
|
110 |
+
$this->schedules[$schedule->getJobCode()][] = $schedule;
|
111 |
+
}
|
112 |
+
|
113 |
+
$this->starttime = $this->hourFloor(strtotime($minDate));
|
114 |
+
$this->endtime = $this->hourCeil(strtotime($maxDate));
|
115 |
+
}
|
116 |
+
|
117 |
+
|
118 |
+
/**
|
119 |
+
* Get timeline panel width
|
120 |
+
*
|
121 |
+
* @return int
|
122 |
+
*/
|
123 |
+
public function getTimelinePanelWidth()
|
124 |
+
{
|
125 |
+
return ($this->endtime - $this->starttime) / $this->zoom;
|
126 |
+
}
|
127 |
+
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Get "now" line
|
131 |
+
*
|
132 |
+
* @return float
|
133 |
+
*/
|
134 |
+
public function getNowline()
|
135 |
+
{
|
136 |
+
return (time() - $this->starttime) / $this->zoom;
|
137 |
+
}
|
138 |
+
|
139 |
+
|
140 |
+
/**
|
141 |
+
* Get all available job codes
|
142 |
+
*
|
143 |
+
* @return array
|
144 |
+
*/
|
145 |
+
public function getAvailableJobCodes()
|
146 |
+
{
|
147 |
+
return array_keys($this->schedules);
|
148 |
+
}
|
149 |
+
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Get schedules for given code
|
153 |
+
*
|
154 |
+
* @param string $code
|
155 |
+
* @return array
|
156 |
+
*/
|
157 |
+
public function getSchedulesForCode($code)
|
158 |
+
{
|
159 |
+
return $this->schedules[$code];
|
160 |
+
}
|
161 |
+
|
162 |
+
|
163 |
+
/**
|
164 |
+
* Get starttime
|
165 |
+
*
|
166 |
+
* @return int
|
167 |
+
*/
|
168 |
+
public function getStarttime()
|
169 |
+
{
|
170 |
+
return $this->starttime;
|
171 |
+
}
|
172 |
+
|
173 |
+
|
174 |
+
/**
|
175 |
+
* Get endtime
|
176 |
+
*
|
177 |
+
* @return int
|
178 |
+
*/
|
179 |
+
public function getEndtime()
|
180 |
+
{
|
181 |
+
return $this->endtime;
|
182 |
+
}
|
183 |
+
|
184 |
+
|
185 |
+
/**
|
186 |
+
* Get attributes for div representing a gantt element
|
187 |
+
*
|
188 |
+
* @param Aoe_Scheduler_Model_Schedule $schedule
|
189 |
+
* @return string
|
190 |
+
*/
|
191 |
+
public function getGanttDivAttributes(Aoe_Scheduler_Model_Schedule $schedule)
|
192 |
+
{
|
193 |
+
|
194 |
+
if ($schedule->getStatus() == Aoe_Scheduler_Model_Schedule::STATUS_RUNNING) {
|
195 |
+
$duration = time() - strtotime($schedule->getStarttime());
|
196 |
+
} else {
|
197 |
+
$duration = $schedule->getDuration() ? $schedule->getDuration() : 0;
|
198 |
+
}
|
199 |
+
$duration = $duration / $this->zoom;
|
200 |
+
$duration = ceil($duration / 4) * 4 - 1; // round to numbers dividable by 4, then remove 1 px border
|
201 |
+
$duration = max($duration, 3);
|
202 |
+
|
203 |
+
$offset = (strtotime($schedule->getStarttime()) - $this->starttime) / $this->zoom;
|
204 |
+
|
205 |
+
if ($offset < 0) { // cut bar
|
206 |
+
$duration += $offset;
|
207 |
+
$offset = 0;
|
208 |
+
}
|
209 |
+
|
210 |
+
$result = sprintf(
|
211 |
+
'<div class="task %s" id="id_%s" style="width: %spx; left: %spx;" ></div>',
|
212 |
+
$schedule->getStatus(),
|
213 |
+
$schedule->getScheduleId(),
|
214 |
+
$duration,
|
215 |
+
$offset
|
216 |
+
);
|
217 |
+
|
218 |
+
if ($schedule->getStatus() == Aoe_Scheduler_Model_Schedule::STATUS_RUNNING) {
|
219 |
+
$offset += $duration;
|
220 |
+
|
221 |
+
$duration = strtotime($schedule->getEta()) - time();
|
222 |
+
$duration = $duration / $this->zoom;
|
223 |
+
|
224 |
+
$result = sprintf(
|
225 |
+
'<div class="estimation" style="width: %spx; left: %spx;" ></div>',
|
226 |
+
$duration,
|
227 |
+
$offset
|
228 |
+
) . $result;
|
229 |
+
}
|
230 |
+
|
231 |
+
return $result;
|
232 |
+
}
|
233 |
+
|
234 |
+
/**
|
235 |
+
* Check if symlinks are allowed
|
236 |
+
*
|
237 |
+
* @return string
|
238 |
+
*/
|
239 |
+
public function _toHtml()
|
240 |
+
{
|
241 |
+
$html = parent::_toHtml();
|
242 |
+
if (!$html && !Mage::getStoreConfigFlag('dev/template/allow_symlink')) {
|
243 |
+
$url = $this->getUrl('adminhtml/system_config/edit', array('section' => 'dev')) . '#dev_template';
|
244 |
+
$html = $this->__('Warning: You installed Aoe_Scheduler using symlinks (e.g. via modman), but forgot to allow symlinks for template files! Please go to <a href="%s">System > Configuration > Advanced > Developer > Template Settings</a> and set "Allow Symlinks" to "yes"', $url);
|
245 |
+
}
|
246 |
+
return $html;
|
247 |
+
}
|
248 |
}
|
app/code/community/Aoe/Scheduler/Block/Adminhtml/TimelineDetail.php
CHANGED
@@ -5,40 +5,40 @@
|
|
5 |
*
|
6 |
* @author Fabrizio Branca
|
7 |
*/
|
8 |
-
class Aoe_Scheduler_Block_Adminhtml_TimelineDetail extends Mage_Adminhtml_Block_Template
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
}
|
5 |
*
|
6 |
* @author Fabrizio Branca
|
7 |
*/
|
8 |
+
class Aoe_Scheduler_Block_Adminhtml_TimelineDetail extends Mage_Adminhtml_Block_Template
|
9 |
+
{
|
10 |
+
|
11 |
+
/**
|
12 |
+
* @var string path to default template
|
13 |
+
*/
|
14 |
+
protected $_template = 'aoe_scheduler/timeline_detail.phtml';
|
15 |
+
|
16 |
+
/**
|
17 |
+
* @var Aoe_Scheduler_Model_Schedule
|
18 |
+
*/
|
19 |
+
protected $schedule;
|
20 |
+
|
21 |
+
|
22 |
+
/**
|
23 |
+
* Set schedule
|
24 |
+
*
|
25 |
+
* @param Aoe_Scheduler_Model_Schedule $schedule
|
26 |
+
* @return Aoe_Scheduler_Block_Adminhtml_TimelineDetail
|
27 |
+
*/
|
28 |
+
public function setSchedule(Aoe_Scheduler_Model_Schedule $schedule)
|
29 |
+
{
|
30 |
+
$this->schedule = $schedule;
|
31 |
+
return $this;
|
32 |
+
}
|
33 |
+
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Get schedule
|
37 |
+
*
|
38 |
+
* @return Aoe_Scheduler_Block_Adminhtml_TimelineDetail
|
39 |
+
*/
|
40 |
+
public function getSchedule()
|
41 |
+
{
|
42 |
+
return $this->schedule;
|
43 |
+
}
|
44 |
}
|
app/code/community/Aoe/Scheduler/Controller/AbstractController.php
ADDED
@@ -0,0 +1,107 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Abstract controller
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
*/
|
8 |
+
abstract class Aoe_Scheduler_Controller_AbstractController extends Mage_Adminhtml_Controller_Action
|
9 |
+
{
|
10 |
+
|
11 |
+
public function preDispatch()
|
12 |
+
{
|
13 |
+
parent::preDispatch();
|
14 |
+
if ($this->getRequest()->getActionName() != 'error' && !$this->checkLocalCodePool()) {
|
15 |
+
$this->_forward('error');
|
16 |
+
}
|
17 |
+
}
|
18 |
+
|
19 |
+
public function errorAction()
|
20 |
+
{
|
21 |
+
$this->loadLayout();
|
22 |
+
$this->_setActiveMenu('system');
|
23 |
+
$this->renderLayout();
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* Index action
|
28 |
+
*
|
29 |
+
* @return void
|
30 |
+
*/
|
31 |
+
public function indexAction()
|
32 |
+
{
|
33 |
+
|
34 |
+
if (!Mage::getStoreConfigFlag('system/cron/enable')) {
|
35 |
+
$this->_getSession()->addNotice($this->__('Scheduler is disabled in configuration (system/cron/enable). No schedules will be executed.'));
|
36 |
+
} else {
|
37 |
+
$this->checkHeartbeat();
|
38 |
+
}
|
39 |
+
|
40 |
+
// check configuration
|
41 |
+
if (Mage::getStoreConfig('system/cron/schedule_generate_every') > Mage::getStoreConfig('system/cron/schedule_ahead_for')) {
|
42 |
+
$this->_getSession()->addError($this->__('Configuration problem. "Generate Schedules Every" is higher than "Schedule Ahead for". Please check your <a href="%s">configuration settings</a>.', $this->getUrl('adminhtml/system_config/edit', array('section' => 'system')) . '#system_cron'));
|
43 |
+
}
|
44 |
+
|
45 |
+
$this->loadLayout();
|
46 |
+
|
47 |
+
$this->_setActiveMenu('system');
|
48 |
+
$this->renderLayout();
|
49 |
+
}
|
50 |
+
|
51 |
+
/**
|
52 |
+
* Aoe_Scheduler used to live in the local code pool.
|
53 |
+
* When newer version are installed without removing the old files Aoe_Scheduler will produce fatal errors.
|
54 |
+
* This is an attempt to handle this a little better.
|
55 |
+
*/
|
56 |
+
protected function checkLocalCodePool()
|
57 |
+
{
|
58 |
+
$helper = Mage::helper('aoe_scheduler/compatibility'); /* @var $helper Aoe_Scheduler_Helper_Compatibility */
|
59 |
+
if ($helper->oldConfigXmlExists()) {
|
60 |
+
$this->_getSession()->addError($this->__('Looks like you have an older version of Aoe_Scheduler installed that lived in the local code pool. Please delete everything under "%s"', $helper->getLocalCodeDir()));
|
61 |
+
return false;
|
62 |
+
}
|
63 |
+
return true;
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* Check heartbeat
|
68 |
+
*/
|
69 |
+
protected function checkHeartbeat()
|
70 |
+
{
|
71 |
+
if (!Mage::helper('aoe_scheduler')->isDisabled('aoescheduler_heartbeat')) {
|
72 |
+
$lastHeartbeat = Mage::helper('aoe_scheduler')->getLastHeartbeat();
|
73 |
+
if ($lastHeartbeat === false) {
|
74 |
+
// no heartbeat task found
|
75 |
+
$this->_getSession()->addError($this->__('No heartbeat task found. Check if cron is configured correctly. (<a href="%s">See Instructions</a>)', $this->getUrl('adminhtml/instructions/index')));
|
76 |
+
} else {
|
77 |
+
$timespan = Mage::helper('aoe_scheduler')->dateDiff($lastHeartbeat);
|
78 |
+
if ($timespan <= 5 * 60) {
|
79 |
+
$this->_getSession()->addSuccess($this->__('Scheduler is working. (Last heart beat: %s minute(s) ago)', round($timespan / 60)));
|
80 |
+
} elseif ($timespan > 5 * 60 && $timespan <= 60 * 60) {
|
81 |
+
// heartbeat wasn't executed in the last 5 minutes. Heartbeat schedule could have been modified to not run every five minutes!
|
82 |
+
$this->_getSession()->addNotice($this->__('Last heartbeat is older than %s minutes.', round($timespan / 60)));
|
83 |
+
} else {
|
84 |
+
// everything ok
|
85 |
+
$this->_getSession()->addError($this->__('Last heartbeat is older than one hour. Please check your settings and your configuration!'));
|
86 |
+
}
|
87 |
+
}
|
88 |
+
}
|
89 |
+
}
|
90 |
+
|
91 |
+
/**
|
92 |
+
* Generate schedule now
|
93 |
+
*
|
94 |
+
* @return void
|
95 |
+
*/
|
96 |
+
public function generateScheduleAction()
|
97 |
+
{
|
98 |
+
Mage::app()->removeCache(Mage_Cron_Model_Observer::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT);
|
99 |
+
|
100 |
+
/* @var Aoe_Scheduler_Model_ScheduleManager $scheduleManager */
|
101 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager');
|
102 |
+
$scheduleManager->generateSchedules();
|
103 |
+
|
104 |
+
$this->_getSession()->addSuccess($this->__('Generated schedule'));
|
105 |
+
$this->_redirect('*/*/index');
|
106 |
+
}
|
107 |
+
}
|
app/code/community/Aoe/Scheduler/Helper/Compatibility.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Helper
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
*/
|
8 |
+
class Aoe_Scheduler_Helper_Compatibility extends Mage_Core_Helper_Abstract
|
9 |
+
{
|
10 |
+
|
11 |
+
public function getLocalCodeDir()
|
12 |
+
{
|
13 |
+
return Mage::getBaseDir('code') . DS . 'local' . DS . 'Aoe' . DS . 'Scheduler';
|
14 |
+
}
|
15 |
+
|
16 |
+
public function oldConfigXmlExists()
|
17 |
+
{
|
18 |
+
return is_file($this->getLocalCodeDir() . DS . 'etc' . DS . 'config.xml');
|
19 |
+
}
|
20 |
+
}
|
app/code/community/Aoe/Scheduler/Helper/Data.php
CHANGED
@@ -3,159 +3,322 @@
|
|
3 |
/**
|
4 |
* Helper
|
5 |
*
|
6 |
-
* @author Fabrizio Branca
|
7 |
*/
|
8 |
-
class Aoe_Scheduler_Helper_Data extends Mage_Core_Helper_Abstract
|
9 |
-
|
10 |
-
|
11 |
-
/**
|
12 |
-
* Explodes a string and trims all values for whitespace in the ends.
|
13 |
-
* If $onlyNonEmptyValues is set, then all blank ('') values are removed.
|
14 |
-
*
|
15 |
-
* @see t3lib_div::trimExplode() in TYPO3
|
16 |
-
* @param $delim
|
17 |
-
* @param string $string
|
18 |
-
* @param bool $removeEmptyValues If set, all empty values will be removed in output
|
19 |
-
* @return array Exploded values
|
20 |
-
*/
|
21 |
-
public function trimExplode($delim, $string, $removeEmptyValues=false) {
|
22 |
-
$explodedValues = explode($delim, $string);
|
23 |
-
|
24 |
-
$result = array_map('trim', $explodedValues);
|
25 |
-
|
26 |
-
if ($removeEmptyValues) {
|
27 |
-
$temp = array();
|
28 |
-
foreach ($result as $value) {
|
29 |
-
if ($value !== '') {
|
30 |
-
$temp[] = $value;
|
31 |
-
}
|
32 |
-
}
|
33 |
-
$result = $temp;
|
34 |
-
}
|
35 |
-
|
36 |
-
return $result;
|
37 |
-
}
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
/**
|
42 |
-
* Decorate status values
|
43 |
-
*
|
44 |
-
* @param $status
|
45 |
-
* @return string
|
46 |
-
*/
|
47 |
-
public function decorateStatus($status) {
|
48 |
-
switch ($status) {
|
49 |
-
case Mage_Cron_Model_Schedule::STATUS_SUCCESS:
|
50 |
-
$result = '<span class="bar-green"><span>'.$status.'</span></span>';
|
51 |
-
break;
|
52 |
-
case Mage_Cron_Model_Schedule::STATUS_PENDING:
|
53 |
-
$result = '<span class="bar-lightgray"><span>'.$status.'</span></span>';
|
54 |
-
break;
|
55 |
-
case Mage_Cron_Model_Schedule::STATUS_RUNNING:
|
56 |
-
$result = '<span class="bar-yellow"><span>'.$status.'</span></span>';
|
57 |
-
break;
|
58 |
-
case Mage_Cron_Model_Schedule::STATUS_MISSED:
|
59 |
-
$result = '<span class="bar-orange"><span>'.$status.'</span></span>';
|
60 |
-
break;
|
61 |
-
case Mage_Cron_Model_Schedule::STATUS_ERROR:
|
62 |
-
$result = '<span class="bar-red"><span>'.$status.'</span></span>';
|
63 |
-
break;
|
64 |
-
default:
|
65 |
-
$result = $status;
|
66 |
-
break;
|
67 |
-
}
|
68 |
-
return $result;
|
69 |
-
}
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
/**
|
74 |
-
* Wrapepr for decorateTime to be used a frame_callback to avoid that additional parameters
|
75 |
-
* conflict with the method's optional ones
|
76 |
-
*
|
77 |
-
* @param string $value
|
78 |
-
* @return string
|
79 |
-
*/
|
80 |
-
public function decorateTimeFrameCallBack($value) {
|
81 |
-
return $this->decorateTime($value, false, NULL);
|
82 |
-
}
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
/**
|
87 |
-
* Decorate time values
|
88 |
-
*
|
89 |
-
* @param string $value
|
90 |
-
* @param bool $echoToday if true "Today" will be added
|
91 |
-
* @param string $dateFormat make sure Y-m-d is in it, if you want to have it replaced
|
92 |
-
* @return string
|
93 |
-
*/
|
94 |
-
public function decorateTime($value, $echoToday=false, $dateFormat=NULL) {
|
95 |
-
if (empty($value) || $value == '0000-00-00 00:00:00') {
|
96 |
-
$value = '';
|
97 |
-
} else {
|
98 |
-
$value = Mage::getModel('core/date')->date($dateFormat, $value);
|
99 |
-
$replace = array(
|
100 |
-
Mage::getModel('core/date')->date('Y-m-d ', time()) => $echoToday ? Mage::helper('aoe_scheduler')->__('Today') . ', ' : '', // today
|
101 |
-
Mage::getModel('core/date')->date('Y-m-d ', strtotime('+1 day')) => Mage::helper('aoe_scheduler')->__('Tomorrow') . ', ',
|
102 |
-
Mage::getModel('core/date')->date('Y-m-d ', strtotime('-1 day')) => Mage::helper('aoe_scheduler')->__('Yesterday') . ', ',
|
103 |
-
);
|
104 |
-
$value = str_replace(array_keys($replace), array_values($replace), $value);
|
105 |
-
}
|
106 |
-
return $value;
|
107 |
-
}
|
108 |
-
|
109 |
-
|
110 |
-
/**
|
111 |
-
* Get last heartbeat
|
112 |
-
*/
|
113 |
-
public function getLastHeartbeat() {
|
114 |
-
if ($this->isDisabled('aoescheduler_heartbeat')) {
|
115 |
-
return false;
|
116 |
-
}
|
117 |
-
$schedules = Mage::getModel('cron/schedule')->getCollection(); /* @var $schedules Mage_Cron_Model_Mysql4_Schedule_Collection */
|
118 |
-
$schedules->getSelect()->limit(1)->order('executed_at DESC');
|
119 |
-
$schedules->addFieldToFilter('status', Mage_Cron_Model_Schedule::STATUS_SUCCESS);
|
120 |
-
$schedules->addFieldToFilter('job_code', 'aoescheduler_heartbeat');
|
121 |
-
$schedules->load();
|
122 |
-
if (count($schedules) == 0) {
|
123 |
-
return false;
|
124 |
-
}
|
125 |
-
$executedAt = $schedules->getFirstItem()->getExecutedAt();
|
126 |
-
$value = Mage::getModel('core/date')->date(NULL, $executedAt);
|
127 |
-
return $value;
|
128 |
-
}
|
129 |
-
|
130 |
-
|
131 |
-
/**
|
132 |
-
* Diff between to times;
|
133 |
-
*
|
134 |
-
* @param $time1
|
135 |
-
* @param $time2
|
136 |
-
* @return int
|
137 |
-
*/
|
138 |
-
public function dateDiff($time1, $time2=NULL) {
|
139 |
-
if (is_null($time2)) {
|
140 |
-
$time2 = Mage::getModel('core/date')->date();
|
141 |
-
}
|
142 |
-
$time1 = strtotime($time1);
|
143 |
-
$time2 = strtotime($time2);
|
144 |
-
return $time2 - $time1;
|
145 |
-
}
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
/**
|
150 |
-
* Check if job code is disabled in configuration
|
151 |
-
*
|
152 |
-
* @param $jobCode
|
153 |
-
* @return bool
|
154 |
-
*/
|
155 |
-
public function isDisabled($jobCode) {
|
156 |
-
$disabledJobs = Mage::getStoreConfig('system/cron/disabled_crons');
|
157 |
-
$disabledJobs = $this->trimExplode(',', $disabledJobs);
|
158 |
-
return in_array($jobCode, $disabledJobs);
|
159 |
-
}
|
160 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
161 |
}
|
3 |
/**
|
4 |
* Helper
|
5 |
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
*/
|
8 |
+
class Aoe_Scheduler_Helper_Data extends Mage_Core_Helper_Abstract
|
9 |
+
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
10 |
|
11 |
+
const XML_PATH_MAX_RUNNING_TIME = 'system/cron/max_running_time';
|
12 |
+
const XML_PATH_EMAIL_TEMPLATE = 'system/cron/error_email_template';
|
13 |
+
const XML_PATH_EMAIL_IDENTITY = 'system/cron/error_email_identity';
|
14 |
+
const XML_PATH_EMAIL_RECIPIENT = 'system/cron/error_email';
|
15 |
+
|
16 |
+
protected $groupsToJobsMap = null;
|
17 |
+
|
18 |
+
/**
|
19 |
+
* Explodes a string and trims all values for whitespace in the ends.
|
20 |
+
* If $onlyNonEmptyValues is set, then all blank ('') values are removed.
|
21 |
+
*
|
22 |
+
* @see t3lib_div::trimExplode() in TYPO3
|
23 |
+
* @param $delim
|
24 |
+
* @param string $string
|
25 |
+
* @param bool $removeEmptyValues If set, all empty values will be removed in output
|
26 |
+
* @return array Exploded values
|
27 |
+
*/
|
28 |
+
public function trimExplode($delim, $string, $removeEmptyValues = false)
|
29 |
+
{
|
30 |
+
$explodedValues = explode($delim, $string);
|
31 |
+
|
32 |
+
$result = array_map('trim', $explodedValues);
|
33 |
+
|
34 |
+
if ($removeEmptyValues) {
|
35 |
+
$temp = array();
|
36 |
+
foreach ($result as $value) {
|
37 |
+
if ($value !== '') {
|
38 |
+
$temp[] = $value;
|
39 |
+
}
|
40 |
+
}
|
41 |
+
$result = $temp;
|
42 |
+
}
|
43 |
+
|
44 |
+
return $result;
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Decorate status values
|
49 |
+
*
|
50 |
+
* @param $status
|
51 |
+
* @return string
|
52 |
+
*/
|
53 |
+
public function decorateStatus($status)
|
54 |
+
{
|
55 |
+
switch ($status) {
|
56 |
+
case Aoe_Scheduler_Model_Schedule::STATUS_SUCCESS:
|
57 |
+
case Aoe_Scheduler_Model_Schedule::STATUS_DIDNTDOANYTHING:
|
58 |
+
$result = '<span class="bar-green"><span>' . $status . '</span></span>';
|
59 |
+
break;
|
60 |
+
case Aoe_Scheduler_Model_Schedule::STATUS_PENDING:
|
61 |
+
$result = '<span class="bar-lightgray"><span>' . $status . '</span></span>';
|
62 |
+
break;
|
63 |
+
case Aoe_Scheduler_Model_Schedule::STATUS_RUNNING:
|
64 |
+
$result = '<span class="bar-yellow"><span>' . $status . '</span></span>';
|
65 |
+
break;
|
66 |
+
case Aoe_Scheduler_Model_Schedule::STATUS_SKIP_OTHERJOBRUNNING:
|
67 |
+
case Aoe_Scheduler_Model_Schedule::STATUS_SKIP_LOCKED:
|
68 |
+
case Aoe_Scheduler_Model_Schedule::STATUS_MISSED:
|
69 |
+
$result = '<span class="bar-orange"><span>' . $status . '</span></span>';
|
70 |
+
break;
|
71 |
+
case Aoe_Scheduler_Model_Schedule::STATUS_ERROR:
|
72 |
+
case Aoe_Scheduler_Model_Schedule::STATUS_DISAPPEARED:
|
73 |
+
case Aoe_Scheduler_Model_Schedule::STATUS_KILLED:
|
74 |
+
$result = '<span class="bar-red"><span>' . $status . '</span></span>';
|
75 |
+
break;
|
76 |
+
default:
|
77 |
+
$result = $status;
|
78 |
+
break;
|
79 |
+
}
|
80 |
+
return $result;
|
81 |
+
}
|
82 |
+
|
83 |
+
/**
|
84 |
+
* Wrapper for decorateTime to be used a frame_callback to avoid that additional parameters
|
85 |
+
* conflict with the method's optional ones
|
86 |
+
*
|
87 |
+
* @param string $value
|
88 |
+
* @return string
|
89 |
+
*/
|
90 |
+
public function decorateTimeFrameCallBack($value)
|
91 |
+
{
|
92 |
+
return $this->decorateTime($value, false, null);
|
93 |
+
}
|
94 |
+
|
95 |
+
/**
|
96 |
+
* Decorate time values
|
97 |
+
*
|
98 |
+
* @param string $value
|
99 |
+
* @param bool $echoToday if true "Today" will be added
|
100 |
+
* @param string $dateFormat make sure Y-m-d is in it, if you want to have it replaced
|
101 |
+
* @return string
|
102 |
+
*/
|
103 |
+
public function decorateTime($value, $echoToday = false, $dateFormat = null)
|
104 |
+
{
|
105 |
+
if (empty($value) || $value == '0000-00-00 00:00:00') {
|
106 |
+
$value = '';
|
107 |
+
} else {
|
108 |
+
$value = Mage::getModel('core/date')->date($dateFormat, $value);
|
109 |
+
$replace = array(
|
110 |
+
Mage::getModel('core/date')->date('Y-m-d ', time()) => $echoToday ? Mage::helper('aoe_scheduler')->__('Today') . ', ' : '', // today
|
111 |
+
Mage::getModel('core/date')->date('Y-m-d ', strtotime('+1 day')) => Mage::helper('aoe_scheduler')->__('Tomorrow') . ', ',
|
112 |
+
Mage::getModel('core/date')->date('Y-m-d ', strtotime('-1 day')) => Mage::helper('aoe_scheduler')->__('Yesterday') . ', ',
|
113 |
+
);
|
114 |
+
$value = str_replace(array_keys($replace), array_values($replace), $value);
|
115 |
+
}
|
116 |
+
return $value;
|
117 |
+
}
|
118 |
+
|
119 |
+
/**
|
120 |
+
* Get last heartbeat
|
121 |
+
*/
|
122 |
+
public function getLastHeartbeat()
|
123 |
+
{
|
124 |
+
return $this->getLastExecutionTime('aoescheduler_heartbeat');
|
125 |
+
}
|
126 |
+
|
127 |
+
/**
|
128 |
+
* Get last execution time
|
129 |
+
*
|
130 |
+
* @param $jobCode
|
131 |
+
* @return bool
|
132 |
+
*/
|
133 |
+
public function getLastExecutionTime($jobCode)
|
134 |
+
{
|
135 |
+
if ($this->isDisabled($jobCode)) {
|
136 |
+
return false;
|
137 |
+
}
|
138 |
+
$schedules = Mage::getModel('cron/schedule')->getCollection(); /* @var $schedules Mage_Cron_Model_Mysql4_Schedule_Collection */
|
139 |
+
$schedules->getSelect()->limit(1)->order('executed_at DESC');
|
140 |
+
$schedules->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_SUCCESS);
|
141 |
+
$schedules->addFieldToFilter('job_code', $jobCode);
|
142 |
+
$schedules->load();
|
143 |
+
if (count($schedules) == 0) {
|
144 |
+
return false;
|
145 |
+
}
|
146 |
+
$executedAt = $schedules->getFirstItem()->getExecutedAt();
|
147 |
+
$value = Mage::getModel('core/date')->date(null, $executedAt);
|
148 |
+
return $value;
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* Diff between to times;
|
153 |
+
*
|
154 |
+
* @param $time1
|
155 |
+
* @param $time2
|
156 |
+
* @return int
|
157 |
+
*/
|
158 |
+
public function dateDiff($time1, $time2 = null)
|
159 |
+
{
|
160 |
+
if (is_null($time2)) {
|
161 |
+
$time2 = Mage::getModel('core/date')->date();
|
162 |
+
}
|
163 |
+
$time1 = strtotime($time1);
|
164 |
+
$time2 = strtotime($time2);
|
165 |
+
return $time2 - $time1;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* Check if job code is disabled in configuration
|
170 |
+
*
|
171 |
+
* @param $jobCode
|
172 |
+
* @return bool
|
173 |
+
*/
|
174 |
+
public function isDisabled($jobCode)
|
175 |
+
{
|
176 |
+
/* @var $job Aoe_Scheduler_Model_Job */
|
177 |
+
$job = Mage::getModel('aoe_scheduler/job')->load($jobCode);
|
178 |
+
return ($job->getJobCode() && !$job->getIsActive());
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* Check if a job matches the group include/exclude lists
|
183 |
+
*
|
184 |
+
* @param $jobCode
|
185 |
+
* @param array $include
|
186 |
+
* @param array $exclude
|
187 |
+
* @return mixed
|
188 |
+
*/
|
189 |
+
public function matchesIncludeExclude($jobCode, array $include, array $exclude)
|
190 |
+
{
|
191 |
+
$include = array_filter(array_map('trim', $include));
|
192 |
+
$exclude = array_filter(array_map('trim', $exclude));
|
193 |
+
|
194 |
+
sort($include);
|
195 |
+
sort($exclude);
|
196 |
+
|
197 |
+
$key = $jobCode . '|' . implode(',', $include) . '|' . implode(',', $exclude);
|
198 |
+
static $cache = array();
|
199 |
+
if (!isset($cache[$key])) {
|
200 |
+
if (count($include) == 0 && count($exclude) == 0) {
|
201 |
+
$cache[$key] = true;
|
202 |
+
} else {
|
203 |
+
$cache[$key] = true;
|
204 |
+
/* @var $job Aoe_Scheduler_Model_Job */
|
205 |
+
$job = Mage::getModel('aoe_scheduler/job')->load($jobCode);
|
206 |
+
$groups = $this->trimExplode(',', $job->getGroups(), true);
|
207 |
+
if (count($include) > 0) {
|
208 |
+
$cache[$key] = (count(array_intersect($groups, $include)) > 0);
|
209 |
+
}
|
210 |
+
if (count($exclude) > 0) {
|
211 |
+
if (count(array_intersect($groups, $exclude)) > 0) {
|
212 |
+
$cache[$key] = false;
|
213 |
+
}
|
214 |
+
}
|
215 |
+
}
|
216 |
+
|
217 |
+
}
|
218 |
+
return $cache[$key];
|
219 |
+
}
|
220 |
+
|
221 |
+
public function getGroupsToJobsMap($forceRebuild = false)
|
222 |
+
{
|
223 |
+
if ($this->groupsToJobsMap === null || $forceRebuild) {
|
224 |
+
$map = array();
|
225 |
+
|
226 |
+
/* @var $jobs Aoe_Scheduler_Model_Resource_Job_Collection */
|
227 |
+
$jobs = Mage::getSingleton('aoe_scheduler/job')->getCollection();
|
228 |
+
foreach ($jobs as $job) {
|
229 |
+
/* @var Aoe_Scheduler_Model_Job $job */
|
230 |
+
$groups = $this->trimExplode(',', $job->getGroups(), true);
|
231 |
+
foreach ($groups as $group) {
|
232 |
+
$map[$group][] = $job->getJobCode();
|
233 |
+
}
|
234 |
+
}
|
235 |
+
|
236 |
+
$this->groupsToJobsMap = $map;
|
237 |
+
}
|
238 |
+
|
239 |
+
return $this->groupsToJobsMap;
|
240 |
+
}
|
241 |
+
|
242 |
+
public function addGroupJobs(array $jobs, array $groups)
|
243 |
+
{
|
244 |
+
$map = $this->getGroupsToJobsMap();
|
245 |
+
|
246 |
+
foreach ($groups as $group) {
|
247 |
+
if (isset($map[$group])) {
|
248 |
+
foreach ($map[$group] as $jobCode) {
|
249 |
+
$jobs[] = $jobCode;
|
250 |
+
}
|
251 |
+
}
|
252 |
+
}
|
253 |
+
|
254 |
+
return $jobs;
|
255 |
+
}
|
256 |
+
|
257 |
+
/**
|
258 |
+
* Send error mail
|
259 |
+
*
|
260 |
+
* @param Aoe_Scheduler_Model_Schedule $schedule
|
261 |
+
* @param $error
|
262 |
+
* @return void
|
263 |
+
*/
|
264 |
+
public function sendErrorMail(Aoe_Scheduler_Model_Schedule $schedule, $error)
|
265 |
+
{
|
266 |
+
if (!Mage::getStoreConfig(self::XML_PATH_EMAIL_RECIPIENT)) {
|
267 |
+
return;
|
268 |
+
}
|
269 |
+
|
270 |
+
$translate = Mage::getSingleton('core/translate'); /* @var $translate Mage_Core_Model_Translate */
|
271 |
+
$translate->setTranslateInline(false);
|
272 |
+
|
273 |
+
$emailTemplate = Mage::getModel('core/email_template'); /* @var $emailTemplate Mage_Core_Model_Email_Template */
|
274 |
+
$emailTemplate->setDesignConfig(array('area' => 'backend'));
|
275 |
+
$emailTemplate->sendTransactional(
|
276 |
+
Mage::getStoreConfig(self::XML_PATH_EMAIL_TEMPLATE),
|
277 |
+
Mage::getStoreConfig(self::XML_PATH_EMAIL_IDENTITY),
|
278 |
+
Mage::getStoreConfig(self::XML_PATH_EMAIL_RECIPIENT),
|
279 |
+
null,
|
280 |
+
array('error' => $error, 'schedule' => $schedule)
|
281 |
+
);
|
282 |
+
|
283 |
+
$translate->setTranslateInline(true);
|
284 |
+
}
|
285 |
+
|
286 |
+
/**
|
287 |
+
* Get callback from runModel
|
288 |
+
*
|
289 |
+
* @param $runModel
|
290 |
+
* @return array
|
291 |
+
*/
|
292 |
+
public function getCallBack($runModel)
|
293 |
+
{
|
294 |
+
if (!preg_match(Mage_Cron_Model_Observer::REGEX_RUN_MODEL, (string) $runModel, $run)) {
|
295 |
+
Mage::throwException(Mage::helper('cron')->__('Invalid model/method definition, expecting "model/class::method", got "' . $runModel . '" instead.'));
|
296 |
+
}
|
297 |
+
if (!($model = Mage::getModel($run[1]))) {
|
298 |
+
Mage::throwException(Mage::helper('cron')->__('Invalid callback: Model for %s::%s does not exist', $run[1], $run[2]));
|
299 |
+
}
|
300 |
+
if (!method_exists($model, $run[2])) {
|
301 |
+
Mage::throwException(Mage::helper('cron')->__('Invalid callback: Method for %s::%s does not exist', $run[1], $run[2]));
|
302 |
+
}
|
303 |
+
$callback = array($model, $run[2]);
|
304 |
+
return $callback;
|
305 |
+
}
|
306 |
+
|
307 |
+
/**
|
308 |
+
* Validate cron expression
|
309 |
+
*
|
310 |
+
* @param $cronExpression
|
311 |
+
* @return bool
|
312 |
+
*/
|
313 |
+
public function validateCronExpression($cronExpression)
|
314 |
+
{
|
315 |
+
try {
|
316 |
+
$schedule = Mage::getModel('cron/schedule');
|
317 |
+
/* @var $schedule Mage_Cron_Model_Schedule */
|
318 |
+
$schedule->setCronExpr($cronExpression);
|
319 |
+
} catch (Exception $e) {
|
320 |
+
return false;
|
321 |
+
}
|
322 |
+
return true;
|
323 |
+
}
|
324 |
}
|
app/code/community/Aoe/Scheduler/Helper/GracefulDead.php
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Graceful Dead Helper
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
* @since 2015-07-02
|
8 |
+
*/
|
9 |
+
class Aoe_Scheduler_Helper_GracefulDead
|
10 |
+
{
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Configure graceful dead
|
14 |
+
*/
|
15 |
+
public static function configure()
|
16 |
+
{
|
17 |
+
static $configured = false;
|
18 |
+
if (!$configured) {
|
19 |
+
register_shutdown_function(array('Aoe_Scheduler_Helper_GracefulDead', 'beforeDyingShutdown'));
|
20 |
+
if (extension_loaded('pcntl')) {
|
21 |
+
declare(ticks = 1);
|
22 |
+
pcntl_signal(SIGINT, array('Aoe_Scheduler_Helper_GracefulDead', 'beforeDyingSigint')); // CTRL + C
|
23 |
+
pcntl_signal(SIGTERM, array('Aoe_Scheduler_Helper_GracefulDead', 'beforeDyingSigterm')); // kill <pid>
|
24 |
+
}
|
25 |
+
$configured = true;
|
26 |
+
}
|
27 |
+
}
|
28 |
+
|
29 |
+
public static function beforeDying($message = null, $exit = false)
|
30 |
+
{
|
31 |
+
$schedule = Mage::registry('currently_running_schedule'); /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
32 |
+
if ($schedule !== null) {
|
33 |
+
if ($message) {
|
34 |
+
$schedule->addMessages($message);
|
35 |
+
}
|
36 |
+
$schedule
|
37 |
+
->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_DIED)
|
38 |
+
->setFinishedAt(strftime('%Y-%m-%d %H:%M:%S', time()))
|
39 |
+
->save();
|
40 |
+
Mage::unregister('currently_running_schedule');
|
41 |
+
}
|
42 |
+
if ($exit) {
|
43 |
+
exit;
|
44 |
+
}
|
45 |
+
}
|
46 |
+
|
47 |
+
/**
|
48 |
+
* Callback
|
49 |
+
*/
|
50 |
+
public static function beforeDyingShutdown()
|
51 |
+
{
|
52 |
+
self::beforeDying('TRIGGER: shutdown function', false);
|
53 |
+
}
|
54 |
+
|
55 |
+
/**
|
56 |
+
* Callback
|
57 |
+
*/
|
58 |
+
public static function beforeDyingSigint()
|
59 |
+
{
|
60 |
+
self::beforeDying('TRIGGER: Signal SIGINT', true);
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Callback
|
65 |
+
*/
|
66 |
+
public static function beforeDyingSigterm()
|
67 |
+
{
|
68 |
+
self::beforeDying('TRIGGER: Signal SIGTERM', true);
|
69 |
+
}
|
70 |
+
}
|
app/code/community/Aoe/Scheduler/Model/Adminhtml/System/Config/Source/List/Code/Filtertype.php
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Used in creating options for list code filter type config value selection
|
5 |
+
*
|
6 |
+
*/
|
7 |
+
class Aoe_Scheduler_Model_Adminhtml_System_Config_Source_List_Code_Filtertype
|
8 |
+
{
|
9 |
+
const SELECT = 'select';
|
10 |
+
const TEXT = 'text';
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Options getter
|
14 |
+
*
|
15 |
+
* @return array
|
16 |
+
*/
|
17 |
+
public function toOptionArray()
|
18 |
+
{
|
19 |
+
return array(
|
20 |
+
array('value' => self::SELECT, 'label' => Mage::helper('adminhtml')->__('Select')),
|
21 |
+
array('value' => self::TEXT, 'label' => Mage::helper('adminhtml')->__('Text')),
|
22 |
+
);
|
23 |
+
}
|
24 |
+
}
|
app/code/community/Aoe/Scheduler/Model/Api.php
CHANGED
@@ -1,49 +1,59 @@
|
|
1 |
<?php
|
|
|
2 |
/**
|
3 |
* Scheduler API
|
4 |
*
|
5 |
-
* @author Fabrizio Branca
|
6 |
*/
|
7 |
-
class Aoe_Scheduler_Model_Api extends Mage_Api_Model_Resource_Abstract
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
*/
|
15 |
-
public function runNow($code) {
|
16 |
-
$schedule = Mage::getModel('cron/schedule') /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
17 |
-
->setJobCode($code)
|
18 |
-
->runNow(false) // without trying to lock the job
|
19 |
-
->save();
|
20 |
-
return $schedule->getData();
|
21 |
-
}
|
22 |
|
23 |
-
|
24 |
-
|
25 |
-
*
|
26 |
-
* @param $code
|
27 |
-
* @param null $time
|
28 |
-
* @return array
|
29 |
-
*/
|
30 |
-
public function schedule($code, $time=NULL) {
|
31 |
-
$schedule = Mage::getModel('cron/schedule') /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
32 |
-
->setJobCode($code)
|
33 |
-
->schedule($time)
|
34 |
-
->save();
|
35 |
-
return $schedule->getData();
|
36 |
-
}
|
37 |
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
48 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
49 |
}
|
1 |
<?php
|
2 |
+
|
3 |
/**
|
4 |
* Scheduler API
|
5 |
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
*/
|
8 |
+
class Aoe_Scheduler_Model_Api extends Mage_Api_Model_Resource_Abstract
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Run task
|
12 |
+
*
|
13 |
+
* @param $code
|
14 |
+
* @return array
|
15 |
+
*/
|
16 |
+
public function runNow($code)
|
17 |
+
{
|
18 |
+
if (!Mage::getStoreConfig('system/cron/enableRunNow')) {
|
19 |
+
Mage::throwException("'Run now' disabled by configuration (system/cron/enableRunNow)");
|
20 |
+
}
|
21 |
|
22 |
+
$schedule = Mage::getModel('cron/schedule')/* @var $schedule Aoe_Scheduler_Model_Schedule */
|
23 |
+
->setJobCode($code)
|
24 |
+
->setScheduledReason(Aoe_Scheduler_Model_Schedule::REASON_RUNNOW_API)
|
25 |
+
->runNow(false) // without trying to lock the job
|
26 |
+
->save();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
28 |
+
return $schedule->getData();
|
29 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
30 |
|
31 |
+
/**
|
32 |
+
* Schedule task
|
33 |
+
*
|
34 |
+
* @param $code
|
35 |
+
* @param null $time
|
36 |
+
* @return array
|
37 |
+
*/
|
38 |
+
public function schedule($code, $time = null)
|
39 |
+
{
|
40 |
+
$schedule = Mage::getModel('cron/schedule')/* @var $schedule Aoe_Scheduler_Model_Schedule */
|
41 |
+
->setJobCode($code)
|
42 |
+
->setScheduledReason(Aoe_Scheduler_Model_Schedule::REASON_SCHEDULENOW_API)
|
43 |
+
->schedule($time)
|
44 |
+
->save();
|
45 |
+
return $schedule->getData();
|
46 |
+
}
|
47 |
|
48 |
+
/**
|
49 |
+
* Get info
|
50 |
+
*
|
51 |
+
* @param $id
|
52 |
+
* @return string
|
53 |
+
*/
|
54 |
+
public function info($id)
|
55 |
+
{
|
56 |
+
$schedule = Mage::getModel('cron/schedule')->load($id); /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
57 |
+
return $schedule->getData();
|
58 |
+
}
|
59 |
}
|
app/code/community/Aoe/Scheduler/Model/Collection/Crons.php
DELETED
@@ -1,62 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Collection of available tasks (crons)
|
5 |
-
*
|
6 |
-
* @author Fabrizio Branca <fabrizio.branca@aoemedia.de>
|
7 |
-
*/
|
8 |
-
class Aoe_Scheduler_Model_Collection_Crons extends Varien_Data_Collection {
|
9 |
-
|
10 |
-
protected $_dataLoaded = false;
|
11 |
-
|
12 |
-
/**
|
13 |
-
* Load data
|
14 |
-
*
|
15 |
-
* @param bool $printQuery
|
16 |
-
* @param bool $logQuery
|
17 |
-
* @return Aoe_Scheduler_Model_Collection_Crons
|
18 |
-
*/
|
19 |
-
public function loadData($printQuery = false, $logQuery = false) {
|
20 |
-
if ($this->_dataLoaded) {
|
21 |
-
return $this;
|
22 |
-
}
|
23 |
-
|
24 |
-
foreach ($this->getAllCodes() as $code) {
|
25 |
-
$configuration = Mage::getModel('aoe_scheduler/configuration')->loadByCode($code);
|
26 |
-
$this->addItem($configuration);
|
27 |
-
}
|
28 |
-
|
29 |
-
$this->_dataLoaded = true;
|
30 |
-
return $this;
|
31 |
-
}
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Get all available codes
|
37 |
-
*
|
38 |
-
* @return array
|
39 |
-
*/
|
40 |
-
protected function getAllCodes() {
|
41 |
-
$codes = array();
|
42 |
-
$config = Mage::getConfig()->getNode('crontab/jobs'); /* @var $config Mage_Core_Model_Config_Element */
|
43 |
-
if ($config instanceof Mage_Core_Model_Config_Element) {
|
44 |
-
foreach ($config->children() as $key => $tmp) {
|
45 |
-
if (!in_array($key, $codes)) {
|
46 |
-
$codes[] = $key;
|
47 |
-
}
|
48 |
-
}
|
49 |
-
}
|
50 |
-
$config = Mage::getConfig()->getNode('default/crontab/jobs'); /* @var $config Mage_Core_Model_Config_Element */
|
51 |
-
if ($config instanceof Mage_Core_Model_Config_Element) {
|
52 |
-
foreach ($config->children() as $key => $tmp) {
|
53 |
-
if (!in_array($key, $codes)) {
|
54 |
-
$codes[] = $key;
|
55 |
-
}
|
56 |
-
}
|
57 |
-
}
|
58 |
-
sort($codes);
|
59 |
-
return $codes;
|
60 |
-
}
|
61 |
-
|
62 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/code/community/Aoe/Scheduler/Model/Configuration.php
DELETED
@@ -1,129 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
*
|
5 |
-
* Enter description here ...
|
6 |
-
*
|
7 |
-
* @author Fabrizio
|
8 |
-
*
|
9 |
-
* @method string getModel()
|
10 |
-
* @method string getStatus()
|
11 |
-
* @method string getCronExpr()
|
12 |
-
*/
|
13 |
-
class Aoe_Scheduler_Model_Configuration extends Mage_Core_Model_Abstract {
|
14 |
-
|
15 |
-
const STATUS_DISABLED = 'disabled';
|
16 |
-
const STATUS_ENABLED = 'enabled';
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
/**
|
21 |
-
* Override method.
|
22 |
-
*
|
23 |
-
* @return false
|
24 |
-
*/
|
25 |
-
protected function _getResource() {
|
26 |
-
return false;
|
27 |
-
}
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
/**
|
32 |
-
* Get id field name
|
33 |
-
*
|
34 |
-
* @return string
|
35 |
-
*/
|
36 |
-
public function getIdFieldName() {
|
37 |
-
return 'id';
|
38 |
-
}
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
/**
|
43 |
-
* Load configuration object by code
|
44 |
-
*
|
45 |
-
* @param string $code
|
46 |
-
*/
|
47 |
-
public function loadByCode($code) {
|
48 |
-
$this->setId($code);
|
49 |
-
$this->setName($code);
|
50 |
-
|
51 |
-
$global = $this->getGlobalCrontabJobXmlConfig();
|
52 |
-
$cronExpr = null;
|
53 |
-
if ($global && $global->schedule && $global->schedule->config_path) {
|
54 |
-
$cronExpr = Mage::getStoreConfig((string)$global->schedule->config_path);
|
55 |
-
}
|
56 |
-
if (empty($cronExpr) && $global && $global->schedule && $global->schedule->cron_expr) {
|
57 |
-
$cronExpr = (string)$global->schedule->cron_expr;
|
58 |
-
}
|
59 |
-
if ($cronExpr) {
|
60 |
-
$this->setCronExpr($cronExpr);
|
61 |
-
}
|
62 |
-
if ($global && $global->run && $global->run->model) {
|
63 |
-
$this->setModel((string)$global->run->model);
|
64 |
-
}
|
65 |
-
|
66 |
-
$configurable = $this->getConfigurableCrontabJobXmlConfig();
|
67 |
-
if ($configurable) {
|
68 |
-
if (is_object($configurable->schedule)) {
|
69 |
-
if ($configurable && $configurable->schedule && $configurable->schedule->cron_expr) {
|
70 |
-
$this->setCronExpr((string)$configurable->schedule->cron_expr);
|
71 |
-
}
|
72 |
-
}
|
73 |
-
if (is_object($configurable->run)) {
|
74 |
-
if ($configurable && $configurable->run && $configurable->run->model) {
|
75 |
-
$this->setModel((string)$configurable->run->model);
|
76 |
-
}
|
77 |
-
}
|
78 |
-
}
|
79 |
-
|
80 |
-
if (!$this->getModel()) {
|
81 |
-
Mage::throwException(sprintf('No configuration found for code "%s"', $code));
|
82 |
-
}
|
83 |
-
|
84 |
-
$disabledCrons = Mage::helper('aoe_scheduler')->trimExplode(',', Mage::getStoreConfig('system/cron/disabled_crons'), true);
|
85 |
-
$this->setStatus(in_array($this->getId(), $disabledCrons) ? self::STATUS_DISABLED : self::STATUS_ENABLED);
|
86 |
-
|
87 |
-
return $this;
|
88 |
-
}
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
/**
|
93 |
-
* Get global crontab job xml configuration
|
94 |
-
*
|
95 |
-
* @return Mage_Core_Model_Config_Element|false
|
96 |
-
*/
|
97 |
-
protected function getGlobalCrontabJobXmlConfig() {
|
98 |
-
return $this->getJobXmlConfig('crontab/jobs');
|
99 |
-
}
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
/**
|
104 |
-
* Get configurable crontab job xml configuration
|
105 |
-
*
|
106 |
-
* @return Mage_Core_Model_Config_Element|false
|
107 |
-
*/
|
108 |
-
protected function getConfigurableCrontabJobXmlConfig() {
|
109 |
-
return $this->getJobXmlConfig('default/crontab/jobs');
|
110 |
-
}
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
/**
|
115 |
-
* Get job xml configuration
|
116 |
-
*
|
117 |
-
* @param string $path path to configuration
|
118 |
-
* @return Mage_Core_Model_Config_Element|false
|
119 |
-
*/
|
120 |
-
protected function getJobXmlConfig($path) {
|
121 |
-
$xmlConfig = false;
|
122 |
-
$config = Mage::getConfig()->getNode($path);
|
123 |
-
if ($config instanceof Mage_Core_Model_Config_Element) {
|
124 |
-
$xmlConfig = $config->{$this->getId()};
|
125 |
-
}
|
126 |
-
return $xmlConfig;
|
127 |
-
}
|
128 |
-
|
129 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/code/community/Aoe/Scheduler/Model/HeartbeatTask.php
DELETED
@@ -1,9 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class Aoe_Scheduler_Model_HeartbeatTask {
|
4 |
-
|
5 |
-
public function run() {
|
6 |
-
return true;
|
7 |
-
}
|
8 |
-
|
9 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/code/community/Aoe/Scheduler/Model/Job.php
ADDED
@@ -0,0 +1,194 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* @method Aoe_Scheduler_Model_Job setJobCode($jobCode)
|
5 |
+
* @method string getJobCode()
|
6 |
+
* @method Aoe_Scheduler_Model_Job setName($name)
|
7 |
+
* @method Aoe_Scheduler_Model_Job setDescription($description)
|
8 |
+
* @method string getDescription()
|
9 |
+
* @method Aoe_Scheduler_Model_Job setShortDescription($shortDescription)
|
10 |
+
* @method string getShortDescription()
|
11 |
+
* @method Aoe_Scheduler_Model_Job setScheduleCronExpr($scheduleCronExpr)
|
12 |
+
* @method string getScheduleCronExpr()
|
13 |
+
* @method Aoe_Scheduler_Model_Job setScheduleConfigPath($scheduleConfigPath)
|
14 |
+
* @method string getScheduleConfigPath()
|
15 |
+
* @method Aoe_Scheduler_Model_Job setRunModel($runModel)
|
16 |
+
* @method string getRunModel()
|
17 |
+
* @method Aoe_Scheduler_Model_Job setParameters($parameters)
|
18 |
+
* @method string getParameters()
|
19 |
+
* @method Aoe_Scheduler_Model_Job setGroups($groups)
|
20 |
+
* @method array getGroups()
|
21 |
+
* @method Aoe_Scheduler_Model_Job load($jobCode)
|
22 |
+
* @method Aoe_Scheduler_Model_Resource_Job getResource()
|
23 |
+
* @method Aoe_Scheduler_Model_Resource_Job_Collection getCollection()
|
24 |
+
* @method Aoe_Scheduler_Model_Resource_Job_Collection getResourceCollection()
|
25 |
+
*/
|
26 |
+
class Aoe_Scheduler_Model_Job extends Mage_Core_Model_Abstract
|
27 |
+
{
|
28 |
+
/**
|
29 |
+
* Prefix of model events names
|
30 |
+
*
|
31 |
+
* @var string
|
32 |
+
*/
|
33 |
+
protected $_eventPrefix = 'aoe_scheduler_job';
|
34 |
+
|
35 |
+
/**
|
36 |
+
* Parameter name in event
|
37 |
+
*
|
38 |
+
* @var string
|
39 |
+
*/
|
40 |
+
protected $_eventObject = 'job';
|
41 |
+
|
42 |
+
protected function _construct()
|
43 |
+
{
|
44 |
+
$this->_setResourceModel('aoe_scheduler/job', 'aoe_scheduler/job_collection');
|
45 |
+
}
|
46 |
+
|
47 |
+
public function getName()
|
48 |
+
{
|
49 |
+
$name = $this->getData('name');
|
50 |
+
if (empty($name)) {
|
51 |
+
$name = $this->getJobCode();
|
52 |
+
}
|
53 |
+
return $name;
|
54 |
+
}
|
55 |
+
|
56 |
+
/**
|
57 |
+
* @param bool $flag
|
58 |
+
*
|
59 |
+
* @return $this
|
60 |
+
*/
|
61 |
+
public function setIsActive($flag)
|
62 |
+
{
|
63 |
+
return $this->setData('is_active', !in_array($flag, array(false, 'false', 0, '0'), true));
|
64 |
+
}
|
65 |
+
|
66 |
+
/**
|
67 |
+
* @return bool
|
68 |
+
*/
|
69 |
+
public function getIsActive()
|
70 |
+
{
|
71 |
+
return !in_array($this->getData('is_active'), array(false, 'false', 0, '0'), true);
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* @param string[] $jobData
|
76 |
+
*
|
77 |
+
* @return $this
|
78 |
+
*/
|
79 |
+
public function setXmlJobData(array $jobData)
|
80 |
+
{
|
81 |
+
return $this->setData('xml_job_data', $jobData);
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @return string[]
|
86 |
+
*/
|
87 |
+
public function getXmlJobData()
|
88 |
+
{
|
89 |
+
$jobData = $this->getData('xml_job_data');
|
90 |
+
return (is_array($jobData) ? $jobData : array());
|
91 |
+
}
|
92 |
+
|
93 |
+
/**
|
94 |
+
* @param string[] $jobData
|
95 |
+
*
|
96 |
+
* @return $this
|
97 |
+
*/
|
98 |
+
public function setDbJobData(array $jobData)
|
99 |
+
{
|
100 |
+
return $this->setData('db_job_data', $jobData);
|
101 |
+
}
|
102 |
+
|
103 |
+
/**
|
104 |
+
* @return string[]
|
105 |
+
*/
|
106 |
+
public function getDbJobData()
|
107 |
+
{
|
108 |
+
$jobData = $this->getData('db_job_data');
|
109 |
+
return (is_array($jobData) ? $jobData : array());
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Returns cron expression (and fetches it from configuration if required)
|
114 |
+
*
|
115 |
+
* @return string
|
116 |
+
*/
|
117 |
+
public function getCronExpression()
|
118 |
+
{
|
119 |
+
$cronExpr = null;
|
120 |
+
if ($this->getScheduleConfigPath()) {
|
121 |
+
$cronExpr = Mage::getStoreConfig($this->getScheduleConfigPath(), Mage_Core_Model_Store::ADMIN_CODE);
|
122 |
+
}
|
123 |
+
if (empty($cronExpr) && $this->getScheduleCronExpr()) {
|
124 |
+
$cronExpr = $this->getScheduleCronExpr();
|
125 |
+
}
|
126 |
+
return trim($cronExpr);
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* Is always task
|
131 |
+
*
|
132 |
+
* @return bool
|
133 |
+
*/
|
134 |
+
public function isAlwaysTask()
|
135 |
+
{
|
136 |
+
return $this->getCronExpression() == 'always';
|
137 |
+
}
|
138 |
+
|
139 |
+
public function getCallback()
|
140 |
+
{
|
141 |
+
$helper = Mage::helper('aoe_scheduler');
|
142 |
+
/* @var $helper Aoe_Scheduler_Helper_Data */
|
143 |
+
return $helper->getCallBack($this->getRunModel());
|
144 |
+
}
|
145 |
+
|
146 |
+
public function canBeScheduled()
|
147 |
+
{
|
148 |
+
return $this->getIsActive() && $this->getCronExpression() && !$this->isAlwaysTask();
|
149 |
+
}
|
150 |
+
|
151 |
+
/**
|
152 |
+
* @return bool
|
153 |
+
*/
|
154 |
+
public function isDbOnly()
|
155 |
+
{
|
156 |
+
$xmlJobData = $this->getXmlJobData();
|
157 |
+
$dbJobData = $this->getDbJobData();
|
158 |
+
return empty($xmlJobData) && !empty($dbJobData);
|
159 |
+
}
|
160 |
+
|
161 |
+
/**
|
162 |
+
* @return bool
|
163 |
+
*/
|
164 |
+
public function isXmlOnly()
|
165 |
+
{
|
166 |
+
$xmlJobData = $this->getXmlJobData();
|
167 |
+
$dbJobData = $this->getDbJobData();
|
168 |
+
return !empty($xmlJobData) && empty($dbJobData);
|
169 |
+
}
|
170 |
+
|
171 |
+
/**
|
172 |
+
* @return bool
|
173 |
+
*/
|
174 |
+
public function isOverlay()
|
175 |
+
{
|
176 |
+
$xmlJobData = $this->getXmlJobData();
|
177 |
+
$dbJobData = $this->getDbJobData();
|
178 |
+
return !empty($xmlJobData) && !empty($dbJobData);
|
179 |
+
}
|
180 |
+
|
181 |
+
/**
|
182 |
+
* @return string
|
183 |
+
*/
|
184 |
+
public function getType()
|
185 |
+
{
|
186 |
+
if ($this->isDbOnly()) {
|
187 |
+
return 'db';
|
188 |
+
} elseif ($this->isXmlOnly()) {
|
189 |
+
return 'xml';
|
190 |
+
} else {
|
191 |
+
return 'db_xml';
|
192 |
+
}
|
193 |
+
}
|
194 |
+
}
|
app/code/community/Aoe/Scheduler/Model/Observer.php
CHANGED
@@ -3,233 +3,82 @@
|
|
3 |
/**
|
4 |
* Crontab observer.
|
5 |
*
|
6 |
-
* @author Fabrizio Branca
|
7 |
*/
|
8 |
-
class Aoe_Scheduler_Model_Observer extends Mage_Cron_Model_Observer
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
$messages = get_class($messages);
|
85 |
-
} elseif (!is_scalar($messages)) {
|
86 |
-
$messages = var_export($messages, 1);
|
87 |
-
}
|
88 |
-
$schedule->setMessages($messages);
|
89 |
-
}
|
90 |
-
|
91 |
-
// schedules can report an error state by returning a string that starts with "ERROR:"
|
92 |
-
if (is_string($messages) && strtoupper(substr($messages, 0, 6)) == 'ERROR:') {
|
93 |
-
$schedule->setStatus(Mage_Cron_Model_Schedule::STATUS_ERROR);
|
94 |
-
$this->sendErrorMail($schedule, $messages);
|
95 |
-
Mage::dispatchEvent('cron_' . $schedule->getJobCode() . '_after_error', array('schedule' => $schedule));
|
96 |
-
} else {
|
97 |
-
$schedule->setStatus(Mage_Cron_Model_Schedule::STATUS_SUCCESS);
|
98 |
-
Mage::dispatchEvent('cron_' . $schedule->getJobCode() . '_after_success', array('schedule' => $schedule));
|
99 |
-
}
|
100 |
-
|
101 |
-
$schedule->setFinishedAt(strftime('%Y-%m-%d %H:%M:%S', time()));
|
102 |
-
Mage::dispatchEvent('cron_' . $schedule->getJobCode() . '_after', array('schedule' => $schedule));
|
103 |
-
|
104 |
-
} catch (Exception $e) {
|
105 |
-
$schedule->setStatus($errorStatus)
|
106 |
-
->setMessages($e->__toString());
|
107 |
-
Mage::dispatchEvent('cron_' . $schedule->getJobCode() . '_exception', array('schedule' => $schedule, 'exception' => $e));
|
108 |
-
|
109 |
-
$this->sendErrorMail($schedule, $e->__toString());
|
110 |
-
|
111 |
-
}
|
112 |
-
$schedule->save();
|
113 |
-
}
|
114 |
-
|
115 |
-
$this->generate();
|
116 |
-
$this->checkRunningJobs();
|
117 |
-
$this->cleanup();
|
118 |
-
}
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
/**
|
123 |
-
* Check running jobs
|
124 |
-
*
|
125 |
-
* @return void
|
126 |
-
*/
|
127 |
-
public function checkRunningJobs() {
|
128 |
-
|
129 |
-
$maxAge = time() - Mage::getStoreConfig(self::XML_PATH_MAX_RUNNING_TIME) * 60;
|
130 |
-
|
131 |
-
$schedules = Mage::getModel('cron/schedule')->getCollection()
|
132 |
-
->addFieldToFilter('status', Mage_Cron_Model_Schedule::STATUS_RUNNING)
|
133 |
-
->addFieldToFilter('executed_at', array('lt' => strftime('%Y-%m-%d %H:%M:00', $maxAge)))
|
134 |
-
->load();
|
135 |
-
|
136 |
-
foreach ($schedules->getIterator() as $schedule) { /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
137 |
-
$schedule
|
138 |
-
->setStatus(Mage_Cron_Model_Schedule::STATUS_ERROR)
|
139 |
-
->setMessages('Job was running longer than the configured max_running_time')
|
140 |
-
->save();
|
141 |
-
}
|
142 |
-
}
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
/**
|
147 |
-
* Generate jobs for config information
|
148 |
-
* Rewrites the original method to filter deactivated jobs
|
149 |
-
*
|
150 |
-
* @param $jobs
|
151 |
-
* @param array $exists
|
152 |
-
* @return Mage_Cron_Model_Observer
|
153 |
-
*/
|
154 |
-
protected function _generateJobs($jobs, $exists) {
|
155 |
-
|
156 |
-
$conf = Mage::getStoreConfig('system/cron/disabled_crons');
|
157 |
-
$conf = explode(',', $conf);
|
158 |
-
foreach ($conf as &$c) { $c = trim($c); }
|
159 |
-
|
160 |
-
$newJobs = array();
|
161 |
-
foreach ($jobs as $code => $config) {
|
162 |
-
if (!in_array($code, $conf)) {
|
163 |
-
$newJobs[$code] = $config;
|
164 |
-
}
|
165 |
-
}
|
166 |
-
|
167 |
-
return parent::_generateJobs($newJobs, $exists);
|
168 |
-
}
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
/**
|
173 |
-
* Generate cron schedule.
|
174 |
-
* Rewrites the original method to remove duplicates afterwards (that exists because of a bug)
|
175 |
-
*
|
176 |
-
* @return Mage_Cron_Model_Observer
|
177 |
-
*/
|
178 |
-
public function generate() {
|
179 |
-
$result = parent::generate();
|
180 |
-
|
181 |
-
$cron_schedule = Mage::getSingleton('core/resource')->getTableName('cron_schedule');
|
182 |
-
$conn = Mage::getSingleton('core/resource')->getConnection('core_read');
|
183 |
-
$results = $conn->fetchAll("
|
184 |
-
SELECT
|
185 |
-
GROUP_CONCAT(schedule_id) AS ids,
|
186 |
-
CONCAT(job_code, scheduled_at) AS jobkey,
|
187 |
-
count(*) AS qty
|
188 |
-
FROM {$cron_schedule}
|
189 |
-
WHERE status = 'pending'
|
190 |
-
GROUP BY jobkey
|
191 |
-
HAVING qty > 1;
|
192 |
-
");
|
193 |
-
foreach($results as $row) {
|
194 |
-
$ids = explode(',', $row['ids']);
|
195 |
-
$removeIds = array_slice($ids, 1);
|
196 |
-
foreach ($removeIds as $id) {
|
197 |
-
Mage::getModel('cron/schedule')->load($id)->delete();
|
198 |
-
}
|
199 |
-
}
|
200 |
-
|
201 |
-
return $result;
|
202 |
-
}
|
203 |
-
|
204 |
-
|
205 |
-
/**
|
206 |
-
* Send error mail
|
207 |
-
*
|
208 |
-
* @param Aoe_Scheduler_Model_Schedule $schedule
|
209 |
-
* @param $error
|
210 |
-
* @return Aoe_Scheduler_Model_Observer
|
211 |
-
*/
|
212 |
-
protected function sendErrorMail(Aoe_Scheduler_Model_Schedule $schedule, $error) {
|
213 |
-
if (!Mage::getStoreConfig(self::XML_PATH_EMAIL_RECIPIENT)) {
|
214 |
-
return $this;
|
215 |
-
}
|
216 |
-
|
217 |
-
$translate = Mage::getSingleton('core/translate'); /* @var $translate Mage_Core_Model_Translate */
|
218 |
-
$translate->setTranslateInline(false);
|
219 |
-
|
220 |
-
$emailTemplate = Mage::getModel('core/email_template'); /* @var $emailTemplate Mage_Core_Model_Email_Template */
|
221 |
-
$emailTemplate->setDesignConfig(array('area' => 'backend'));
|
222 |
-
$emailTemplate->sendTransactional(
|
223 |
-
Mage::getStoreConfig(self::XML_PATH_EMAIL_TEMPLATE),
|
224 |
-
Mage::getStoreConfig(self::XML_PATH_EMAIL_IDENTITY),
|
225 |
-
Mage::getStoreConfig(self::XML_PATH_EMAIL_RECIPIENT),
|
226 |
-
null,
|
227 |
-
array('error' => $error, 'schedule' => $schedule)
|
228 |
-
);
|
229 |
-
|
230 |
-
$translate->setTranslateInline(true);
|
231 |
-
|
232 |
-
return $this;
|
233 |
-
}
|
234 |
-
|
235 |
}
|
3 |
/**
|
4 |
* Crontab observer.
|
5 |
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
*/
|
8 |
+
class Aoe_Scheduler_Model_Observer /* extends Mage_Cron_Model_Observer */
|
9 |
+
{
|
10 |
+
/**
|
11 |
+
* Process cron queue
|
12 |
+
* Generate tasks schedule
|
13 |
+
* Cleanup tasks schedule
|
14 |
+
*
|
15 |
+
* @param Varien_Event_Observer $observer
|
16 |
+
*/
|
17 |
+
public function dispatch(Varien_Event_Observer $observer)
|
18 |
+
{
|
19 |
+
if (!Mage::getStoreConfigFlag('system/cron/enable')) {
|
20 |
+
return;
|
21 |
+
}
|
22 |
+
|
23 |
+
$processManager = Mage::getModel('aoe_scheduler/processManager'); /* @var $processManager Aoe_Scheduler_Model_ProcessManager */
|
24 |
+
$processManager->watchdog();
|
25 |
+
|
26 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager'); /* @var $scheduleManager Aoe_Scheduler_Model_ScheduleManager */
|
27 |
+
$scheduleManager->logRun();
|
28 |
+
|
29 |
+
$helper = Mage::helper('aoe_scheduler'); /* @var Aoe_Scheduler_Helper_Data $helper */
|
30 |
+
$includeJobs = $helper->addGroupJobs((array) $observer->getIncludeJobs(), (array) $observer->getIncludeGroups());
|
31 |
+
$excludeJobs = $helper->addGroupJobs((array) $observer->getExcludeJobs(), (array) $observer->getExcludeGroups());
|
32 |
+
|
33 |
+
// Coalesce all jobs that should have run before now, by job code, by marking the oldest entries as missed.
|
34 |
+
$scheduleManager->cleanMissedSchedules();
|
35 |
+
|
36 |
+
// Iterate over all pending jobs
|
37 |
+
foreach ($scheduleManager->getPendingSchedules($includeJobs, $excludeJobs) as $schedule) {
|
38 |
+
/* @var Aoe_Scheduler_Model_Schedule $schedule */
|
39 |
+
$schedule->process();
|
40 |
+
}
|
41 |
+
|
42 |
+
// Generate new schedules
|
43 |
+
$scheduleManager->generateSchedules();
|
44 |
+
|
45 |
+
// Clean up schedule history
|
46 |
+
$scheduleManager->cleanup();
|
47 |
+
}
|
48 |
+
|
49 |
+
/**
|
50 |
+
* Process cron queue for tasks marked as 'always'
|
51 |
+
*
|
52 |
+
* @param Varien_Event_Observer $observer
|
53 |
+
*/
|
54 |
+
public function dispatchAlways(Varien_Event_Observer $observer)
|
55 |
+
{
|
56 |
+
if (!Mage::getStoreConfigFlag('system/cron/enable')) {
|
57 |
+
return;
|
58 |
+
}
|
59 |
+
|
60 |
+
$processManager = Mage::getModel('aoe_scheduler/processManager'); /* @var $processManager Aoe_Scheduler_Model_ProcessManager */
|
61 |
+
$processManager->watchdog();
|
62 |
+
|
63 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager'); /* @var $scheduleManager Aoe_Scheduler_Model_ScheduleManager */
|
64 |
+
|
65 |
+
$helper = Mage::helper('aoe_scheduler'); /* @var Aoe_Scheduler_Helper_Data $helper */
|
66 |
+
$includeJobs = $helper->addGroupJobs((array) $observer->getIncludeJobs(), (array) $observer->getIncludeGroups());
|
67 |
+
$excludeJobs = $helper->addGroupJobs((array) $observer->getExcludeJobs(), (array) $observer->getExcludeGroups());
|
68 |
+
|
69 |
+
/* @var $jobs Aoe_Scheduler_Model_Resource_Job_Collection */
|
70 |
+
$jobs = Mage::getSingleton('aoe_scheduler/job')->getCollection();
|
71 |
+
$jobs->setWhiteList($includeJobs);
|
72 |
+
$jobs->setBlackList($excludeJobs);
|
73 |
+
$jobs->setActiveOnly(true);
|
74 |
+
foreach ($jobs as $job) {
|
75 |
+
/* @var Aoe_Scheduler_Model_Job $job */
|
76 |
+
if ($job->isAlwaysTask() && $job->getRunModel()) {
|
77 |
+
$schedule = $scheduleManager->getScheduleForAlwaysJob($job->getJobCode());
|
78 |
+
if ($schedule !== false) {
|
79 |
+
$schedule->process();
|
80 |
+
}
|
81 |
+
}
|
82 |
+
}
|
83 |
+
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
84 |
}
|
app/code/community/Aoe/Scheduler/Model/ProcessManager.php
ADDED
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Process Manager
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
* @since 2013-10-11
|
8 |
+
*/
|
9 |
+
class Aoe_Scheduler_Model_ProcessManager
|
10 |
+
{
|
11 |
+
|
12 |
+
const XML_PATH_MARK_AS_ERROR = 'system/cron/mark_as_error_after';
|
13 |
+
const XML_PATH_MAX_JOB_RUNTIME = 'system/cron/max_job_runtime';
|
14 |
+
|
15 |
+
/**
|
16 |
+
* Get all schedules running on this server
|
17 |
+
*
|
18 |
+
* @param string $host
|
19 |
+
* @return object
|
20 |
+
*/
|
21 |
+
public function getAllRunningSchedules($host = null)
|
22 |
+
{
|
23 |
+
$collection = Mage::getModel('cron/schedule')->getCollection();
|
24 |
+
$collection->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_RUNNING);
|
25 |
+
if (!is_null($host)) {
|
26 |
+
$collection->addFieldToFilter('host', $host);
|
27 |
+
}
|
28 |
+
return $collection;
|
29 |
+
}
|
30 |
+
|
31 |
+
/**
|
32 |
+
* Get all schedules marked as to be killed
|
33 |
+
*
|
34 |
+
* @param string|null $host
|
35 |
+
* @return object
|
36 |
+
*/
|
37 |
+
public function getAllKillRequests($host = null)
|
38 |
+
{
|
39 |
+
$collection = $this->getAllRunningSchedules($host);
|
40 |
+
$collection->addFieldToFilter('kill_request', array('lt' => strftime('%Y-%m-%d %H:%M:00', time()+60)));
|
41 |
+
return $collection;
|
42 |
+
}
|
43 |
+
|
44 |
+
/**
|
45 |
+
* Check if there's already a job running with the given code
|
46 |
+
*
|
47 |
+
* @param string $jobCode
|
48 |
+
* @param int $ignoreId
|
49 |
+
* @return bool
|
50 |
+
*/
|
51 |
+
public function isJobCodeRunning($jobCode, $ignoreId = null)
|
52 |
+
{
|
53 |
+
$collection = Mage::getModel('cron/schedule')
|
54 |
+
->getCollection()
|
55 |
+
->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_RUNNING)
|
56 |
+
->addFieldToFilter('job_code', $jobCode);
|
57 |
+
if (!is_null($ignoreId)) {
|
58 |
+
$collection->addFieldToFilter('schedule_id', array('neq' => $ignoreId));
|
59 |
+
}
|
60 |
+
foreach ($collection as $schedule) { /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
61 |
+
$alive = $schedule->isAlive();
|
62 |
+
if ($alive !== false) { // TODO: how do we handle null (= we don't know because might be running on a different server?
|
63 |
+
return true;
|
64 |
+
}
|
65 |
+
}
|
66 |
+
return false;
|
67 |
+
}
|
68 |
+
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Check running jobs
|
72 |
+
*
|
73 |
+
* @return void
|
74 |
+
*/
|
75 |
+
public function checkRunningJobs()
|
76 |
+
{
|
77 |
+
$maxJobRuntime = intval(Mage::getStoreConfig(self::XML_PATH_MAX_JOB_RUNTIME));
|
78 |
+
|
79 |
+
if ($maxJobRuntime) {
|
80 |
+
foreach ($this->getAllRunningSchedules(gethostname()) as $schedule) { /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
81 |
+
// checking if the job isn't running too long
|
82 |
+
if ($schedule->isAlive()) {
|
83 |
+
if ($schedule->getDuration() > $maxJobRuntime * 60 && !$schedule->getKillRequest()) {
|
84 |
+
$schedule->requestKill(null, 'Kill requested because job exceeded the max job runtime of ' . $maxJobRuntime . ' minutes.');
|
85 |
+
}
|
86 |
+
}
|
87 |
+
}
|
88 |
+
}
|
89 |
+
|
90 |
+
// fallback (where process cannot be checked or if one of the servers disappeared)
|
91 |
+
// if a task wasn't seen for some time it will be marked as error
|
92 |
+
$markAsErrorAfter = intval(Mage::getStoreConfig(self::XML_PATH_MARK_AS_ERROR));
|
93 |
+
$maxAge = time() - min($markAsErrorAfter, 5) * 60;
|
94 |
+
if ($markAsErrorAfter) {
|
95 |
+
$schedules = Mage::getModel('cron/schedule')->getCollection()/* @var $schedules Mage_Cron_Model_Resource_Schedule_Collection */
|
96 |
+
->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_RUNNING)
|
97 |
+
->addFieldToFilter('last_seen', array('lt' => strftime('%Y-%m-%d %H:%M:00', $maxAge)))
|
98 |
+
->load();
|
99 |
+
|
100 |
+
foreach ($schedules as $schedule) { /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
101 |
+
// check one more time
|
102 |
+
if ($schedule->isAlive() !== true) {
|
103 |
+
$schedule->markAsDisappeared(sprintf('Host "%s" has not been available for a while now to update the status of this task and the task is not reporting back by itself', $schedule->getHost()));
|
104 |
+
$schedule->save();
|
105 |
+
}
|
106 |
+
}
|
107 |
+
}
|
108 |
+
|
109 |
+
// clean up "running"(!?) tasks that have never been seen (for whatever reason) and have been scheduled before maxAge
|
110 |
+
// by robinfritze. @see https://github.com/AOEpeople/Aoe_Scheduler/issues/40#issuecomment-67749476
|
111 |
+
$schedules = Mage::getModel('cron/schedule')->getCollection() /* @var $schedules Mage_Cron_Model_Resource_Schedule_Collection */
|
112 |
+
->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_RUNNING)
|
113 |
+
->addFieldToFilter('last_seen', array('null' => true))
|
114 |
+
->addFieldToFilter('host', array('null' => true))
|
115 |
+
->addFieldToFilter('pid', array('null' => true))
|
116 |
+
->addFieldToFilter('scheduled_at', array('lt' => strftime('%Y-%m-%d %H:%M:00', $maxAge)))
|
117 |
+
->load();
|
118 |
+
|
119 |
+
foreach ($schedules->getIterator() as $schedule) { /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
120 |
+
$schedule->setLastSeen(strftime('%Y-%m-%d %H:%M:%S', time()));
|
121 |
+
$schedule->markAsDisappeared(sprintf('Process "%s" (id: %s) cannot be found anymore', $schedule->getJobCode(), $schedule->getId()));
|
122 |
+
}
|
123 |
+
|
124 |
+
}
|
125 |
+
|
126 |
+
/**
|
127 |
+
* Process kill requests
|
128 |
+
*
|
129 |
+
* @return void
|
130 |
+
*/
|
131 |
+
public function processKillRequests()
|
132 |
+
{
|
133 |
+
foreach ($this->getAllKillRequests(gethostname()) as $schedule) { /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
134 |
+
$schedule->kill();
|
135 |
+
}
|
136 |
+
}
|
137 |
+
|
138 |
+
|
139 |
+
/**
|
140 |
+
* Run maintenance
|
141 |
+
*/
|
142 |
+
public function watchdog()
|
143 |
+
{
|
144 |
+
$this->checkRunningJobs();
|
145 |
+
$this->processKillRequests();
|
146 |
+
}
|
147 |
+
}
|
app/code/community/Aoe/Scheduler/Model/Resource/Job.php
ADDED
@@ -0,0 +1,358 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Aoe_Scheduler_Model_Resource_Job extends Mage_Core_Model_Resource_Db_Abstract
|
4 |
+
{
|
5 |
+
/** @var bool */
|
6 |
+
protected $loaded = false;
|
7 |
+
|
8 |
+
/** @var Aoe_Scheduler_Model_Job[] */
|
9 |
+
protected $jobs = array();
|
10 |
+
|
11 |
+
/**
|
12 |
+
* Resource initialization
|
13 |
+
*/
|
14 |
+
protected function _construct()
|
15 |
+
{
|
16 |
+
$this->_init('core/config_data', 'job_code');
|
17 |
+
}
|
18 |
+
|
19 |
+
public function getJobCodes()
|
20 |
+
{
|
21 |
+
$codes = array();
|
22 |
+
|
23 |
+
$nodes = array('crontab/jobs', 'default/crontab/jobs');
|
24 |
+
foreach ($nodes as $node) {
|
25 |
+
$jobs = Mage::getConfig()->getNode($node);
|
26 |
+
if ($jobs && $jobs->hasChildren()) {
|
27 |
+
foreach ($jobs->children() as $code => $child) {
|
28 |
+
$codes[] = trim($code);
|
29 |
+
}
|
30 |
+
}
|
31 |
+
}
|
32 |
+
|
33 |
+
// Remove empties and de-dupe
|
34 |
+
$codes = array_unique(array_filter($codes));
|
35 |
+
|
36 |
+
// Sort
|
37 |
+
sort($codes);
|
38 |
+
|
39 |
+
return $codes;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* @param Aoe_Scheduler_Model_Job $object
|
44 |
+
* @param mixed $value
|
45 |
+
* @param null $field
|
46 |
+
*
|
47 |
+
* @return $this
|
48 |
+
*/
|
49 |
+
public function load(Mage_Core_Model_Abstract $object, $value, $field = null)
|
50 |
+
{
|
51 |
+
if (!$object instanceof Aoe_Scheduler_Model_Job) {
|
52 |
+
throw new InvalidArgumentException(sprintf("Expected object of type 'Aoe_Scheduler_Model_Job' got '%s'", get_class($object)));
|
53 |
+
}
|
54 |
+
|
55 |
+
/** @var Aoe_Scheduler_Model_Job $object */
|
56 |
+
|
57 |
+
if (!empty($field)) {
|
58 |
+
throw new InvalidArgumentException('Aoe_Scheduler_Model_Resource_Job cannot load by any field except the job code.');
|
59 |
+
}
|
60 |
+
|
61 |
+
if (empty($value)) {
|
62 |
+
$this->setModelFromJobData($object, array());
|
63 |
+
$object->setJobCode('');
|
64 |
+
$object->setXmlJobData(array());
|
65 |
+
$object->setDbJobData(array());
|
66 |
+
return $this;
|
67 |
+
}
|
68 |
+
|
69 |
+
$xmlJobData = $this->getJobDataFromXml($value);
|
70 |
+
$dbJobData = $this->getJobDataFromDb($value);
|
71 |
+
$jobData = array_merge($xmlJobData, $this->getJobDataFromConfig($value, true), $dbJobData);
|
72 |
+
|
73 |
+
$this->setModelFromJobData($object, $jobData);
|
74 |
+
$object->setJobCode($value);
|
75 |
+
$object->setXmlJobData($xmlJobData);
|
76 |
+
$object->setDbJobData($dbJobData);
|
77 |
+
|
78 |
+
$this->unserializeFields($object);
|
79 |
+
$this->_afterLoad($object);
|
80 |
+
|
81 |
+
return $this;
|
82 |
+
}
|
83 |
+
|
84 |
+
/**
|
85 |
+
* @param Aoe_Scheduler_Model_Job $object
|
86 |
+
*
|
87 |
+
* @return $this
|
88 |
+
*/
|
89 |
+
public function save(Mage_Core_Model_Abstract $object)
|
90 |
+
{
|
91 |
+
if ($object->isDeleted()) {
|
92 |
+
return $this->delete($object);
|
93 |
+
}
|
94 |
+
|
95 |
+
if (!$object instanceof Aoe_Scheduler_Model_Job) {
|
96 |
+
throw new InvalidArgumentException(sprintf("Expected object of type 'Aoe_Scheduler_Model_Job' got '%s'", get_class($object)));
|
97 |
+
}
|
98 |
+
|
99 |
+
if (!$object->getJobCode()) {
|
100 |
+
Mage::throwException('Invalid data. Must have job code.');
|
101 |
+
}
|
102 |
+
|
103 |
+
$this->_serializeFields($object);
|
104 |
+
$this->_beforeSave($object);
|
105 |
+
|
106 |
+
$newValues = $this->getJobDataFromModel($object);
|
107 |
+
$oldValues = $this->getJobDataFromDb($object->getJobCode());
|
108 |
+
$defaultValues = $this->getJobDataFromXml($object->getJobCode());
|
109 |
+
|
110 |
+
// Generate key/value lists for Update and Insert
|
111 |
+
$updateValues = array_intersect_key($newValues, $oldValues);
|
112 |
+
$insertValues = array_diff_key($newValues, $oldValues);
|
113 |
+
|
114 |
+
// Remove Updates and Inserts that match defaults
|
115 |
+
$updateValues = array_diff_assoc($updateValues, $defaultValues);
|
116 |
+
$insertValues = array_diff_assoc($insertValues, $defaultValues);
|
117 |
+
|
118 |
+
// Remove empty value inserts if this is a DB only job
|
119 |
+
if (empty($defaultValues)) {
|
120 |
+
foreach ($insertValues as $k => $v) {
|
121 |
+
if ($v === '' || $v === null) {
|
122 |
+
unset($insertValues[$k]);
|
123 |
+
}
|
124 |
+
}
|
125 |
+
}
|
126 |
+
|
127 |
+
// Generate key/value lists for Delete (Old values, not being updated, that are identical to default values)
|
128 |
+
$deleteValues = array_intersect_assoc(array_diff_key($oldValues, $updateValues), $defaultValues);
|
129 |
+
|
130 |
+
$pathPrefix = $this->getJobPathPrefix($object->getJobCode()) . '/';
|
131 |
+
|
132 |
+
$adapter = $this->_getWriteAdapter();
|
133 |
+
foreach ($updateValues as $k => $v) {
|
134 |
+
$adapter->update(
|
135 |
+
$this->getMainTable(),
|
136 |
+
array('value' => $v),
|
137 |
+
array(
|
138 |
+
'scope = ?' => 'default',
|
139 |
+
'scope_id = ?' => 0,
|
140 |
+
'path = ?' => $pathPrefix . $k
|
141 |
+
)
|
142 |
+
);
|
143 |
+
}
|
144 |
+
foreach ($insertValues as $k => $v) {
|
145 |
+
$adapter->insert(
|
146 |
+
$this->getMainTable(),
|
147 |
+
array(
|
148 |
+
'scope' => 'default',
|
149 |
+
'scope_id' => 0,
|
150 |
+
'path' => $pathPrefix . $k,
|
151 |
+
'value' => $v
|
152 |
+
)
|
153 |
+
);
|
154 |
+
}
|
155 |
+
foreach ($deleteValues as $k => $v) {
|
156 |
+
$adapter->delete(
|
157 |
+
$this->getMainTable(),
|
158 |
+
array(
|
159 |
+
'scope = ?' => 'default',
|
160 |
+
'scope_id = ?' => 0,
|
161 |
+
'path = ?' => $pathPrefix . $k
|
162 |
+
)
|
163 |
+
);
|
164 |
+
}
|
165 |
+
|
166 |
+
if (count($updateValues) || count($insertValues) || count($deleteValues)) {
|
167 |
+
Mage::getConfig()->reinit();
|
168 |
+
}
|
169 |
+
|
170 |
+
$this->unserializeFields($object);
|
171 |
+
$this->_afterSave($object);
|
172 |
+
|
173 |
+
return $this;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* {@inheritdoc}
|
178 |
+
*/
|
179 |
+
public function forsedSave(Mage_Core_Model_Abstract $object)
|
180 |
+
{
|
181 |
+
throw new RuntimeException('Method no longer exists');
|
182 |
+
}
|
183 |
+
|
184 |
+
/**
|
185 |
+
* @param Aoe_Scheduler_Model_Job $object
|
186 |
+
*
|
187 |
+
* @return $this
|
188 |
+
*/
|
189 |
+
public function delete(Mage_Core_Model_Abstract $object)
|
190 |
+
{
|
191 |
+
if (!$object instanceof Aoe_Scheduler_Model_Job) {
|
192 |
+
throw new InvalidArgumentException(sprintf("Expected object of type 'Aoe_Scheduler_Model_Job' got '%s'", get_class($object)));
|
193 |
+
}
|
194 |
+
|
195 |
+
$this->_beforeDelete($object);
|
196 |
+
|
197 |
+
if (!$object->getJobCode()) {
|
198 |
+
Mage::throwException('Invalid data. Must have job code.');
|
199 |
+
}
|
200 |
+
|
201 |
+
$adapter = $this->_getWriteAdapter();
|
202 |
+
$adapter->delete(
|
203 |
+
$this->getMainTable(),
|
204 |
+
array(
|
205 |
+
'path LIKE ?' => $this->getJobSearchPath($object->getJobCode()),
|
206 |
+
'scope = ?' => 'default',
|
207 |
+
'scope_id = ?' => 0
|
208 |
+
)
|
209 |
+
);
|
210 |
+
|
211 |
+
Mage::getConfig()->reinit();
|
212 |
+
|
213 |
+
$this->_afterDelete($object);
|
214 |
+
|
215 |
+
return $this;
|
216 |
+
}
|
217 |
+
|
218 |
+
protected function getJobPathPrefix($jobCode)
|
219 |
+
{
|
220 |
+
return 'crontab/jobs/' . $jobCode;
|
221 |
+
}
|
222 |
+
|
223 |
+
protected function getJobSearchPath($jobCode)
|
224 |
+
{
|
225 |
+
return str_replace(array('\\', '%', '_'), array('\\\\', '\\%', '\\_'), $this->getJobPathPrefix($jobCode)) . '/%';
|
226 |
+
}
|
227 |
+
|
228 |
+
private function getJobDataFromConfig($jobCode, $useDefaultScope = false, $default = null)
|
229 |
+
{
|
230 |
+
$config = Mage::getConfig()->getNode(($useDefaultScope ? 'default/' : '') . $this->getJobPathPrefix($jobCode));
|
231 |
+
if (!$config) {
|
232 |
+
return array();
|
233 |
+
}
|
234 |
+
|
235 |
+
$config = $config->asArray();
|
236 |
+
|
237 |
+
$values = array();
|
238 |
+
|
239 |
+
if (isset($config['name'])) {
|
240 |
+
$values['name'] = $config['name'];
|
241 |
+
} elseif ($default !== null) {
|
242 |
+
$values['name'] = $default;
|
243 |
+
}
|
244 |
+
if (isset($config['description'])) {
|
245 |
+
$values['description'] = $config['description'];
|
246 |
+
} elseif ($default !== null) {
|
247 |
+
$values['description'] = $default;
|
248 |
+
}
|
249 |
+
if (isset($config['short_description'])) {
|
250 |
+
$values['short_description'] = $config['short_description'];
|
251 |
+
} elseif ($default !== null) {
|
252 |
+
$values['short_description'] = $default;
|
253 |
+
}
|
254 |
+
if (isset($config['run']['model'])) {
|
255 |
+
$values['run/model'] = $config['run']['model'];
|
256 |
+
} elseif ($default !== null) {
|
257 |
+
$values['run/model'] = $default;
|
258 |
+
}
|
259 |
+
if (isset($config['schedule']['config_path'])) {
|
260 |
+
$values['schedule/config_path'] = $config['schedule']['config_path'];
|
261 |
+
} elseif ($default !== null) {
|
262 |
+
$values['schedule/config_path'] = $default;
|
263 |
+
}
|
264 |
+
if (isset($config['schedule']['cron_expr'])) {
|
265 |
+
$values['schedule/cron_expr'] = $config['schedule']['cron_expr'];
|
266 |
+
} elseif ($default !== null) {
|
267 |
+
$values['schedule/cron_expr'] = $default;
|
268 |
+
}
|
269 |
+
if (isset($config['parameters'])) {
|
270 |
+
$values['parameters'] = $config['parameters'];
|
271 |
+
} elseif ($default !== null) {
|
272 |
+
$values['parameters'] = $default;
|
273 |
+
}
|
274 |
+
if (isset($config['groups'])) {
|
275 |
+
$values['groups'] = $config['groups'];
|
276 |
+
} elseif ($default !== null) {
|
277 |
+
$values['groups'] = $default;
|
278 |
+
}
|
279 |
+
if (isset($config['is_active'])) {
|
280 |
+
$values['is_active'] = $config['is_active'];
|
281 |
+
} elseif ($default !== null) {
|
282 |
+
$values['is_active'] = $default;
|
283 |
+
}
|
284 |
+
|
285 |
+
// Clean up each entry to being a trimmed string
|
286 |
+
$values = array_map('trim', $values);
|
287 |
+
|
288 |
+
return $values;
|
289 |
+
}
|
290 |
+
|
291 |
+
public function getJobDataFromXml($jobCode)
|
292 |
+
{
|
293 |
+
return $this->getJobDataFromConfig($jobCode, false, null);
|
294 |
+
}
|
295 |
+
|
296 |
+
public function getJobDataFromDb($jobCode)
|
297 |
+
{
|
298 |
+
$adapter = $this->_getWriteAdapter();
|
299 |
+
|
300 |
+
$select = $adapter->select()
|
301 |
+
->from($this->getMainTable(), array('path', 'value'))
|
302 |
+
->where('scope = ?', 'default')
|
303 |
+
->where('scope_id = ?', '0')
|
304 |
+
->where('path LIKE ?', $this->getJobSearchPath($jobCode));
|
305 |
+
|
306 |
+
$pathPrefix = $this->getJobPathPrefix($jobCode) . '/';
|
307 |
+
$values = array();
|
308 |
+
foreach ($adapter->query($select)->fetchAll() as $row) {
|
309 |
+
if (strpos($row['path'], $pathPrefix) === 0) {
|
310 |
+
$values[substr($row['path'], strlen($pathPrefix))] = $row['value'];
|
311 |
+
}
|
312 |
+
}
|
313 |
+
|
314 |
+
// Clean up each entry to being a trimmed string
|
315 |
+
$values = array_map('trim', $values);
|
316 |
+
|
317 |
+
return $values;
|
318 |
+
}
|
319 |
+
|
320 |
+
public function getJobDataFromModel(Aoe_Scheduler_Model_Job $job)
|
321 |
+
{
|
322 |
+
$values = array(
|
323 |
+
'name' => $job->getName(),
|
324 |
+
'description' => $job->getDescription(),
|
325 |
+
'short_description' => $job->getShortDescription(),
|
326 |
+
'run/model' => $job->getRunModel(),
|
327 |
+
'schedule/config_path' => $job->getScheduleConfigPath(),
|
328 |
+
'schedule/cron_expr' => $job->getScheduleCronExpr(),
|
329 |
+
'parameters' => $job->getParameters(),
|
330 |
+
'groups' => $job->getGroups(),
|
331 |
+
'is_active' => ($job->getIsActive() ? '1' : '0'),
|
332 |
+
);
|
333 |
+
|
334 |
+
// Strip out the auto-generated name
|
335 |
+
if ($values['name'] === $job->getJobCode()) {
|
336 |
+
$values['name'] = '';
|
337 |
+
}
|
338 |
+
|
339 |
+
// Clean up each entry to being a trimmed string
|
340 |
+
$values = array_map('trim', $values);
|
341 |
+
|
342 |
+
return $values;
|
343 |
+
}
|
344 |
+
|
345 |
+
public function setModelFromJobData(Aoe_Scheduler_Model_Job $job, array $data)
|
346 |
+
{
|
347 |
+
$job->setName(isset($data['name']) ? $data['name'] : '');
|
348 |
+
$job->setDescription(isset($data['description']) ? $data['description'] : '');
|
349 |
+
$job->setShortDescription(isset($data['short_description']) ? $data['short_description'] : '');
|
350 |
+
$job->setRunModel(isset($data['run/model']) ? $data['run/model'] : '');
|
351 |
+
$job->setScheduleConfigPath(isset($data['schedule/config_path']) ? $data['schedule/config_path'] : '');
|
352 |
+
$job->setScheduleCronExpr(isset($data['schedule/cron_expr']) ? $data['schedule/cron_expr'] : '');
|
353 |
+
$job->setParameters(isset($data['parameters']) ? $data['parameters'] : '');
|
354 |
+
$job->setGroups(isset($data['groups']) ? $data['groups'] : '');
|
355 |
+
$job->setIsActive(isset($data['is_active']) ? $data['is_active'] : '');
|
356 |
+
return $job;
|
357 |
+
}
|
358 |
+
}
|
app/code/community/Aoe/Scheduler/Model/Resource/Job/Collection.php
ADDED
@@ -0,0 +1,368 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Aoe_Scheduler_Model_Resource_Job_Collection extends Varien_Data_Collection
|
4 |
+
{
|
5 |
+
protected $model;
|
6 |
+
protected $resourceModel;
|
7 |
+
protected $whiteList = array();
|
8 |
+
protected $blackList = array();
|
9 |
+
protected $activeOnly = false;
|
10 |
+
protected $dbOnly = false;
|
11 |
+
|
12 |
+
public function __construct()
|
13 |
+
{
|
14 |
+
$this->model = 'aoe_scheduler/job';
|
15 |
+
$this->resourceModel = 'aoe_scheduler/job';
|
16 |
+
}
|
17 |
+
|
18 |
+
/**
|
19 |
+
* @return Aoe_Scheduler_Model_Resource_Job
|
20 |
+
*/
|
21 |
+
public function getResource()
|
22 |
+
{
|
23 |
+
$resource = Mage::getResourceSingleton($this->resourceModel);
|
24 |
+
if (!$resource instanceof Aoe_Scheduler_Model_Resource_Job) {
|
25 |
+
Mage::throwException(
|
26 |
+
sprintf(
|
27 |
+
'Invalid resource class. Expected "%s" and received "%s".',
|
28 |
+
'Aoe_Scheduler_Model_Resource_Job',
|
29 |
+
(is_object($resource) ? get_class($resource) : 'UNKNOWN')
|
30 |
+
)
|
31 |
+
);
|
32 |
+
}
|
33 |
+
return $resource;
|
34 |
+
}
|
35 |
+
|
36 |
+
/**
|
37 |
+
* Retrieve collection empty item
|
38 |
+
*
|
39 |
+
* @return Aoe_Scheduler_Model_Job
|
40 |
+
*/
|
41 |
+
public function getNewEmptyItem()
|
42 |
+
{
|
43 |
+
$model = Mage::getModel($this->model);
|
44 |
+
|
45 |
+
if (!$model instanceof Aoe_Scheduler_Model_Job) {
|
46 |
+
Mage::throwException(
|
47 |
+
sprintf(
|
48 |
+
'Invalid model class. Expected "%s" and received "%s".',
|
49 |
+
'Aoe_Scheduler_Model_Job',
|
50 |
+
(is_object($model) ? get_class($model) : 'UNKNOWN')
|
51 |
+
)
|
52 |
+
);
|
53 |
+
}
|
54 |
+
|
55 |
+
return $model;
|
56 |
+
}
|
57 |
+
|
58 |
+
/**
|
59 |
+
* Load data
|
60 |
+
*
|
61 |
+
* @return $this
|
62 |
+
*/
|
63 |
+
public function loadData($printQuery = false, $logQuery = false)
|
64 |
+
{
|
65 |
+
if ($this->isLoaded()) {
|
66 |
+
return $this;
|
67 |
+
}
|
68 |
+
|
69 |
+
$this->clear();
|
70 |
+
|
71 |
+
foreach ($this->getResource()->getJobCodes() as $jobCode) {
|
72 |
+
if (!empty($this->whiteList) && !in_array($jobCode, $this->whiteList)) {
|
73 |
+
continue;
|
74 |
+
}
|
75 |
+
if (!empty($this->blackList) && in_array($jobCode, $this->blackList)) {
|
76 |
+
continue;
|
77 |
+
}
|
78 |
+
|
79 |
+
$job = $this->getNewEmptyItem()->load($jobCode);
|
80 |
+
if ($this->activeOnly && !$job->getIsActive()) {
|
81 |
+
continue;
|
82 |
+
}
|
83 |
+
if ($this->dbOnly && !$job->isDbOnly()) {
|
84 |
+
continue;
|
85 |
+
}
|
86 |
+
|
87 |
+
$this->addItem($job);
|
88 |
+
}
|
89 |
+
|
90 |
+
ksort($this->_items);
|
91 |
+
|
92 |
+
$this->_setIsLoaded(true);
|
93 |
+
|
94 |
+
return $this;
|
95 |
+
}
|
96 |
+
|
97 |
+
/**
|
98 |
+
* @return string[]
|
99 |
+
*/
|
100 |
+
public function getWhiteList()
|
101 |
+
{
|
102 |
+
return $this->whiteList;
|
103 |
+
}
|
104 |
+
|
105 |
+
/**
|
106 |
+
* @param string[] $list
|
107 |
+
*
|
108 |
+
* @return $this
|
109 |
+
*/
|
110 |
+
public function setWhiteList(array $list)
|
111 |
+
{
|
112 |
+
$list = array_unique(array_filter(array_map('trim', array_values($list))));
|
113 |
+
sort($list);
|
114 |
+
if ($this->whiteList !== $list) {
|
115 |
+
$this->clear();
|
116 |
+
$this->whiteList = $list;
|
117 |
+
}
|
118 |
+
return $this;
|
119 |
+
}
|
120 |
+
|
121 |
+
/**
|
122 |
+
* @return string[]
|
123 |
+
*/
|
124 |
+
public function getBlackList()
|
125 |
+
{
|
126 |
+
return $this->blackList;
|
127 |
+
}
|
128 |
+
|
129 |
+
/**
|
130 |
+
* @param string[] $list
|
131 |
+
*
|
132 |
+
* @return $this
|
133 |
+
*/
|
134 |
+
public function setBlackList(array $list)
|
135 |
+
{
|
136 |
+
$list = array_unique(array_filter(array_map('trim', array_values($list))));
|
137 |
+
sort($list);
|
138 |
+
if ($this->blackList !== $list) {
|
139 |
+
$this->clear();
|
140 |
+
$this->blackList = $list;
|
141 |
+
}
|
142 |
+
return $this;
|
143 |
+
}
|
144 |
+
|
145 |
+
/**
|
146 |
+
* @return bool
|
147 |
+
*/
|
148 |
+
public function getActiveOnly()
|
149 |
+
{
|
150 |
+
return $this->activeOnly;
|
151 |
+
}
|
152 |
+
|
153 |
+
/**
|
154 |
+
* @param bool $flag
|
155 |
+
*
|
156 |
+
* @return $this
|
157 |
+
*/
|
158 |
+
public function setActiveOnly($flag = true)
|
159 |
+
{
|
160 |
+
$flag = (bool)$flag;
|
161 |
+
if ($this->activeOnly !== $flag) {
|
162 |
+
$this->clear();
|
163 |
+
$this->activeOnly = $flag;
|
164 |
+
}
|
165 |
+
return $this;
|
166 |
+
}
|
167 |
+
|
168 |
+
/**
|
169 |
+
* @return bool
|
170 |
+
*/
|
171 |
+
public function getDbOnly()
|
172 |
+
{
|
173 |
+
return $this->dbOnly;
|
174 |
+
}
|
175 |
+
|
176 |
+
/**
|
177 |
+
* @param bool $flag
|
178 |
+
*
|
179 |
+
* @return $this
|
180 |
+
*/
|
181 |
+
public function setDbOnly($flag = true)
|
182 |
+
{
|
183 |
+
$flag = (bool)$flag;
|
184 |
+
if ($this->dbOnly !== $flag) {
|
185 |
+
$this->clear();
|
186 |
+
$this->dbOnly = $flag;
|
187 |
+
}
|
188 |
+
return $this;
|
189 |
+
}
|
190 |
+
|
191 |
+
/**
|
192 |
+
* Adding item to item array
|
193 |
+
*
|
194 |
+
* @param Aoe_Scheduler_Model_Job $item
|
195 |
+
*
|
196 |
+
* @return $this
|
197 |
+
*/
|
198 |
+
public function addItem(Varien_Object $item)
|
199 |
+
{
|
200 |
+
if (!$item instanceof Aoe_Scheduler_Model_Job) {
|
201 |
+
Mage::throwException(
|
202 |
+
sprintf(
|
203 |
+
'Invalid model class. Expected "%s" and received "%s".',
|
204 |
+
'Aoe_Scheduler_Model_Job',
|
205 |
+
get_class($item)
|
206 |
+
)
|
207 |
+
);
|
208 |
+
}
|
209 |
+
|
210 |
+
$jobCode = $item->getJobCode();
|
211 |
+
if (!$jobCode) {
|
212 |
+
Mage::throwException('Jobs must have a job code');
|
213 |
+
}
|
214 |
+
|
215 |
+
if (isset($this->_items[$jobCode])) {
|
216 |
+
Mage::throwException('Job with the same job code "' . $item->getJobCode() . '" already exist');
|
217 |
+
}
|
218 |
+
|
219 |
+
$this->_items[$jobCode] = $item;
|
220 |
+
|
221 |
+
return $this;
|
222 |
+
}
|
223 |
+
|
224 |
+
/**
|
225 |
+
* @return string[]
|
226 |
+
*/
|
227 |
+
public function getAllIds()
|
228 |
+
{
|
229 |
+
return array_keys($this->_items);
|
230 |
+
}
|
231 |
+
|
232 |
+
/**
|
233 |
+
* Retrieve field values from all items
|
234 |
+
*
|
235 |
+
* @param string $column
|
236 |
+
*
|
237 |
+
* @return array
|
238 |
+
*/
|
239 |
+
public function getColumnValues($column)
|
240 |
+
{
|
241 |
+
$values = array();
|
242 |
+
foreach ($this as $item) {
|
243 |
+
/** @var Aoe_Scheduler_Model_Job $item */
|
244 |
+
$values[] = $item->getDataUsingMethod($column);
|
245 |
+
}
|
246 |
+
return $values;
|
247 |
+
}
|
248 |
+
|
249 |
+
/**
|
250 |
+
* Search all items by field value
|
251 |
+
*
|
252 |
+
* @param string $column
|
253 |
+
* @param mixed $value
|
254 |
+
*
|
255 |
+
* @return Aoe_Scheduler_Model_Job[]
|
256 |
+
*/
|
257 |
+
public function getItemsByColumnValue($column, $value)
|
258 |
+
{
|
259 |
+
$items = array();
|
260 |
+
foreach ($this as $item) {
|
261 |
+
/** @var Aoe_Scheduler_Model_Job $item */
|
262 |
+
if ($item->getDataUsingMethod($column) == $value) {
|
263 |
+
$items[] = $item;
|
264 |
+
}
|
265 |
+
}
|
266 |
+
return $items;
|
267 |
+
}
|
268 |
+
|
269 |
+
/**
|
270 |
+
* Search first item by field value
|
271 |
+
*
|
272 |
+
* @param string $column
|
273 |
+
* @param mixed $value
|
274 |
+
*
|
275 |
+
* @return Aoe_Scheduler_Model_Job|null
|
276 |
+
*/
|
277 |
+
public function getItemByColumnValue($column, $value)
|
278 |
+
{
|
279 |
+
foreach ($this as $item) {
|
280 |
+
/** @var Aoe_Scheduler_Model_Job $item */
|
281 |
+
if ($item->getDataUsingMethod($column) == $value) {
|
282 |
+
return $item;
|
283 |
+
}
|
284 |
+
}
|
285 |
+
return null;
|
286 |
+
}
|
287 |
+
|
288 |
+
/**
|
289 |
+
* Setting data for all collection items
|
290 |
+
*
|
291 |
+
* @param mixed $key
|
292 |
+
* @param mixed $value
|
293 |
+
*
|
294 |
+
* @return $this
|
295 |
+
*/
|
296 |
+
public function setDataToAll($key, $value = null)
|
297 |
+
{
|
298 |
+
if (is_array($key)) {
|
299 |
+
foreach ($key as $k => $v) {
|
300 |
+
$this->setDataToAll($k, $v);
|
301 |
+
}
|
302 |
+
return $this;
|
303 |
+
}
|
304 |
+
|
305 |
+
foreach ($this->getItems() as $item) {
|
306 |
+
/** @var Aoe_Scheduler_Model_Job $item */
|
307 |
+
$item->setDataUsingMethod($key, $value);
|
308 |
+
}
|
309 |
+
|
310 |
+
return $this;
|
311 |
+
}
|
312 |
+
|
313 |
+
public function toOptionArray($valueField = 'job_code', $labelField = 'name', array $additional = array())
|
314 |
+
{
|
315 |
+
return $this->_toOptionArray($valueField, $labelField, $additional);
|
316 |
+
}
|
317 |
+
|
318 |
+
public function toOptionHash($valueField = 'job_code', $labelField = 'name')
|
319 |
+
{
|
320 |
+
return $this->_toOptionHash($valueField, $labelField);
|
321 |
+
}
|
322 |
+
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Convert items array to array for select options
|
326 |
+
*
|
327 |
+
* @param string $valueField
|
328 |
+
* @param string $labelField
|
329 |
+
*
|
330 |
+
* @return array
|
331 |
+
*/
|
332 |
+
protected function _toOptionArray($valueField = 'job_code', $labelField = 'name', $additional = array())
|
333 |
+
{
|
334 |
+
$options = array();
|
335 |
+
|
336 |
+
$additional['value'] = $valueField;
|
337 |
+
$additional['label'] = $labelField;
|
338 |
+
|
339 |
+
foreach ($this as $item) {
|
340 |
+
/** @var Aoe_Scheduler_Model_Job $item */
|
341 |
+
$data = array();
|
342 |
+
foreach ($additional as $code => $field) {
|
343 |
+
$data[$code] = $item->getDataUsingMethod($field);
|
344 |
+
}
|
345 |
+
$options[] = $data;
|
346 |
+
}
|
347 |
+
return $options;
|
348 |
+
}
|
349 |
+
|
350 |
+
|
351 |
+
/**
|
352 |
+
* Convert items array to hash for select options
|
353 |
+
*
|
354 |
+
* @param string $valueField
|
355 |
+
* @param string $labelField
|
356 |
+
*
|
357 |
+
* @return array
|
358 |
+
*/
|
359 |
+
protected function _toOptionHash($valueField = 'job_code', $labelField = 'name')
|
360 |
+
{
|
361 |
+
$res = array();
|
362 |
+
foreach ($this as $item) {
|
363 |
+
/** @var Aoe_Scheduler_Model_Job $item */
|
364 |
+
$res[$item->getDataUsingMethod($valueField)] = $item->getDataUsingMethod($labelField);
|
365 |
+
}
|
366 |
+
return $res;
|
367 |
+
}
|
368 |
+
}
|
app/code/community/Aoe/Scheduler/Model/Resource/Schedule/Collection.php
ADDED
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Aoe_Scheduler_Model_Resource_Schedule_Collection extends Mage_Cron_Model_Resource_Schedule_Collection
|
4 |
+
{
|
5 |
+
/**
|
6 |
+
* Event name prefix for events that are dispatched by this class
|
7 |
+
*
|
8 |
+
* @var string
|
9 |
+
*/
|
10 |
+
protected $_eventPrefix = 'aoe_scheduler_schedule_collection';
|
11 |
+
|
12 |
+
/**
|
13 |
+
* Event parameter name that references this object in an event
|
14 |
+
*
|
15 |
+
* In an observer method you can use $observer->getData('collection') or $observer->getData('data_object') to get this object
|
16 |
+
*
|
17 |
+
* @var string
|
18 |
+
*/
|
19 |
+
protected $_eventObject = 'collection';
|
20 |
+
}
|
app/code/community/Aoe/Scheduler/Model/Schedule.php
CHANGED
@@ -9,153 +9,837 @@
|
|
9 |
* @method string getMessages()
|
10 |
* @method string getCreatedAt()
|
11 |
* @method string getScheduledAt()
|
|
|
12 |
* @method string getJobCode()
|
13 |
-
* @method
|
14 |
-
* @method
|
15 |
-
* @method
|
16 |
-
* @method
|
17 |
-
* @method
|
18 |
-
* @method
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
*/
|
20 |
-
class Aoe_Scheduler_Model_Schedule extends Mage_Cron_Model_Schedule
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
9 |
* @method string getMessages()
|
10 |
* @method string getCreatedAt()
|
11 |
* @method string getScheduledAt()
|
12 |
+
* @method string setJobCode($jobCode)
|
13 |
* @method string getJobCode()
|
14 |
+
* @method $this setMessages()
|
15 |
+
* @method $this setExecutedAt()
|
16 |
+
* @method $this setCreatedAt()
|
17 |
+
* @method $this setScheduledAt()
|
18 |
+
* @method $this setStatus()
|
19 |
+
* @method $this setFinishedAt()
|
20 |
+
* @method $this setParameters()
|
21 |
+
* @method $this setEta()
|
22 |
+
* @method string getEta()
|
23 |
+
* @method $this setHost()
|
24 |
+
* @method string getHost()
|
25 |
+
* @method $this setPid()
|
26 |
+
* @method string getPid()
|
27 |
+
* @method $this setProgressMessage()
|
28 |
+
* @method string getProgressMessage()
|
29 |
+
* @method string getLastSeen()
|
30 |
+
* @method $this setLastSeen()
|
31 |
+
* @method string getScheduledBy()
|
32 |
+
* @method $this setScheduledBy($scheduledBy)
|
33 |
+
* @method string getScheduledReason()
|
34 |
+
* @method $this setScheduledReason($scheduledReason)
|
35 |
+
* @method string getKillRequest()
|
36 |
+
* @method $this setKillRequest($killRequest)
|
37 |
*/
|
38 |
+
class Aoe_Scheduler_Model_Schedule extends Mage_Cron_Model_Schedule
|
39 |
+
{
|
40 |
+
|
41 |
+
const STATUS_KILLED = 'killed';
|
42 |
+
const STATUS_DISAPPEARED = 'gone';
|
43 |
+
const STATUS_DIDNTDOANYTHING = 'nothing';
|
44 |
+
|
45 |
+
const STATUS_SKIP_LOCKED = 'locked';
|
46 |
+
const STATUS_SKIP_OTHERJOBRUNNING = 'other_job_running';
|
47 |
+
|
48 |
+
const STATUS_DIED = 'died'; // note that died != killed
|
49 |
+
|
50 |
+
const REASON_RUNNOW_WEB = 'run_now_web';
|
51 |
+
const REASON_SCHEDULENOW_WEB = 'schedule_now_web';
|
52 |
+
const REASON_RUNNOW_CLI = 'run_now_cli';
|
53 |
+
const REASON_SCHEDULENOW_CLI = 'schedule_now_cli';
|
54 |
+
const REASON_RUNNOW_API = 'run_now_api';
|
55 |
+
const REASON_SCHEDULENOW_API = 'schedule_now_api';
|
56 |
+
const REASON_GENERATESCHEDULES = 'generate_schedules';
|
57 |
+
const REASON_DEPENDENCY_ALL = 'dependency_all';
|
58 |
+
const REASON_DEPENDENCY_SUCCESS = 'dependency_success';
|
59 |
+
const REASON_DEPENDENCY_FAILURE = 'dependency_failure';
|
60 |
+
|
61 |
+
/**
|
62 |
+
* Event name prefix for events that are dispatched by this class
|
63 |
+
*
|
64 |
+
* @var string
|
65 |
+
*/
|
66 |
+
protected $_eventPrefix = 'aoe_scheduler_schedule';
|
67 |
+
|
68 |
+
/**
|
69 |
+
* Event parameter name that references this object in an event
|
70 |
+
*
|
71 |
+
* In an observer method you can use $observer->getData('schedule') or $observer->getData('data_object') to get this object
|
72 |
+
*
|
73 |
+
* @var string
|
74 |
+
*/
|
75 |
+
protected $_eventObject = 'schedule';
|
76 |
+
|
77 |
+
/**
|
78 |
+
* @var Aoe_Scheduler_Model_Job
|
79 |
+
*/
|
80 |
+
protected $job;
|
81 |
+
|
82 |
+
/**
|
83 |
+
* @var bool
|
84 |
+
*/
|
85 |
+
protected $jobWasLocked = false;
|
86 |
+
|
87 |
+
/**
|
88 |
+
* Placeholder to keep track of active redirect buffer.
|
89 |
+
*
|
90 |
+
* @var bool
|
91 |
+
*/
|
92 |
+
protected $_redirect = false;
|
93 |
+
|
94 |
+
/**
|
95 |
+
* The buffer will be flushed after any output call which causes
|
96 |
+
* the buffer's length to equal or exceed this value.
|
97 |
+
*
|
98 |
+
* Prior to PHP 5.4.0, the value 1 set the chunk size to 4096 bytes.
|
99 |
+
*/
|
100 |
+
protected $_redirectOutputHandlerChunkSize = 100; // bytes
|
101 |
+
|
102 |
+
/**
|
103 |
+
* Backup of the original error settings
|
104 |
+
*
|
105 |
+
* @var array
|
106 |
+
*/
|
107 |
+
protected $errorSettingsBackup = array();
|
108 |
+
|
109 |
+
|
110 |
+
/**
|
111 |
+
* Initialize from job
|
112 |
+
*
|
113 |
+
* @param Aoe_Scheduler_Model_Job $job
|
114 |
+
* @return $this
|
115 |
+
*/
|
116 |
+
public function initializeFromJob(Aoe_Scheduler_Model_Job $job)
|
117 |
+
{
|
118 |
+
$this->setJobCode($job->getJobCode());
|
119 |
+
$this->setCronExpr($job->getCronExpression());
|
120 |
+
$this->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_PENDING);
|
121 |
+
return $this;
|
122 |
+
}
|
123 |
+
|
124 |
+
|
125 |
+
/**
|
126 |
+
* Run this task now
|
127 |
+
*
|
128 |
+
* @param bool $tryLockJob
|
129 |
+
* @param bool $forceRun
|
130 |
+
* @return Aoe_Scheduler_Model_Schedule
|
131 |
+
*/
|
132 |
+
public function runNow($tryLockJob = true, $forceRun = false)
|
133 |
+
{
|
134 |
+
// if this schedule doesn't exist yet, create it
|
135 |
+
if (!$this->getCreatedAt()) {
|
136 |
+
$this->schedule();
|
137 |
+
}
|
138 |
+
|
139 |
+
if (!$forceRun) {
|
140 |
+
// lock job (see below) prevents the exact same schedule from being executed from more than one process (or server)
|
141 |
+
// the following check will prevent multiple schedules of the same type to be run in parallel
|
142 |
+
$processManager = Mage::getModel('aoe_scheduler/processManager'); /* @var $processManager Aoe_Scheduler_Model_ProcessManager */
|
143 |
+
if ($processManager->isJobCodeRunning($this->getJobCode(), $this->getId())) {
|
144 |
+
$this->setStatus(self::STATUS_SKIP_OTHERJOBRUNNING);
|
145 |
+
$this->log(sprintf('Job "%s" (id: %s) will not be executed because there is already another process with the same job code running. Skipping.', $this->getJobCode(), $this->getId()));
|
146 |
+
return $this;
|
147 |
+
}
|
148 |
+
}
|
149 |
+
|
150 |
+
// lock job requires the record to be saved and having status Aoe_Scheduler_Model_Schedule::STATUS_PENDING
|
151 |
+
// workaround could be to do this: $this->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_PENDING)->save();
|
152 |
+
$this->jobWasLocked = false;
|
153 |
+
if ($tryLockJob && !$this->tryLockJob()) {
|
154 |
+
$this->setStatus(self::STATUS_SKIP_LOCKED);
|
155 |
+
// another cron started this job intermittently, so skip it
|
156 |
+
$this->jobWasLocked = true;
|
157 |
+
$this->log(sprintf('Job "%s" (id: %s) is locked. Skipping.', $this->getJobCode(), $this->getId()));
|
158 |
+
return $this;
|
159 |
+
}
|
160 |
+
|
161 |
+
try {
|
162 |
+
$job = $this->getJob();
|
163 |
+
|
164 |
+
if (!$job) {
|
165 |
+
Mage::throwException(sprintf("Could not create job with jobCode '%s'", $this->getJobCode()));
|
166 |
+
}
|
167 |
+
|
168 |
+
$startTime = time();
|
169 |
+
$this
|
170 |
+
->setExecutedAt(strftime('%Y-%m-%d %H:%M:%S', $startTime))
|
171 |
+
->setLastSeen(strftime('%Y-%m-%d %H:%M:%S', $startTime))
|
172 |
+
->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_RUNNING)
|
173 |
+
->setHost(gethostname())
|
174 |
+
->setPid(getmypid())
|
175 |
+
->save();
|
176 |
+
|
177 |
+
Aoe_Scheduler_Helper_GracefulDead::configure();
|
178 |
+
|
179 |
+
Mage::register('currently_running_schedule', $this);
|
180 |
+
|
181 |
+
Mage::dispatchEvent('cron_' . $this->getJobCode() . '_before', array('schedule' => $this));
|
182 |
+
Mage::dispatchEvent('cron_before', array('schedule' => $this));
|
183 |
+
|
184 |
+
Mage::unregister('current_cron_task');
|
185 |
+
Mage::register('current_cron_task', $this);
|
186 |
+
|
187 |
+
$this->log('Start: ' . $this->getJobCode());
|
188 |
+
|
189 |
+
$this->_startBufferToMessages();
|
190 |
+
$this->jobErrorContext();
|
191 |
+
|
192 |
+
$callback = $job->getCallback();
|
193 |
+
|
194 |
+
try {
|
195 |
+
// this is where the magic happens
|
196 |
+
$messages = call_user_func_array($callback, array($this));
|
197 |
+
|
198 |
+
$this->restoreErrorContext();
|
199 |
+
$this->_stopBufferToMessages();
|
200 |
+
} catch (Exception $e) {
|
201 |
+
$this->restoreErrorContext();
|
202 |
+
$this->_stopBufferToMessages();
|
203 |
+
throw $e;
|
204 |
+
}
|
205 |
+
|
206 |
+
$this->log('Stop: ' . $this->getJobCode());
|
207 |
+
|
208 |
+
if (!empty($messages)) {
|
209 |
+
if (is_object($messages)) {
|
210 |
+
$messages = get_class($messages);
|
211 |
+
} elseif (!is_scalar($messages)) {
|
212 |
+
$messages = var_export($messages, 1);
|
213 |
+
}
|
214 |
+
$this->addMessages(PHP_EOL . '---RETURN_VALUE---' . PHP_EOL . $messages);
|
215 |
+
}
|
216 |
+
|
217 |
+
// schedules can report an error state by returning a string that starts with "ERROR:"
|
218 |
+
if ((is_string($messages) && strtoupper(substr($messages, 0, 6)) == 'ERROR:') || $this->getStatus() === Aoe_Scheduler_Model_Schedule::STATUS_ERROR) {
|
219 |
+
$this->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_ERROR);
|
220 |
+
Mage::helper('aoe_scheduler')->sendErrorMail($this, $messages);
|
221 |
+
Mage::dispatchEvent('cron_' . $this->getJobCode() . '_after_error', array('schedule' => $this));
|
222 |
+
Mage::dispatchEvent('cron_after_error', array('schedule' => $this));
|
223 |
+
} elseif ((is_string($messages) && strtoupper(substr($messages, 0, 7)) == 'NOTHING') || $this->getStatus() === Aoe_Scheduler_Model_Schedule::STATUS_DIDNTDOANYTHING) {
|
224 |
+
$this->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_DIDNTDOANYTHING);
|
225 |
+
Mage::dispatchEvent('cron_' . $this->getJobCode() . '_after_nothing', array('schedule' => $this));
|
226 |
+
Mage::dispatchEvent('cron_after_nothing', array('schedule' => $this));
|
227 |
+
} else {
|
228 |
+
$this->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_SUCCESS);
|
229 |
+
Mage::dispatchEvent('cron_' . $this->getJobCode() . '_after_success', array('schedule' => $this));
|
230 |
+
Mage::dispatchEvent('cron_after_success', array('schedule' => $this));
|
231 |
+
}
|
232 |
+
|
233 |
+
} catch (Exception $e) {
|
234 |
+
$this->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_ERROR);
|
235 |
+
$this->addMessages(PHP_EOL . '---EXCEPTION---' . PHP_EOL . $e->__toString());
|
236 |
+
Mage::dispatchEvent('cron_' . $this->getJobCode() . '_exception', array('schedule' => $this, 'exception' => $e));
|
237 |
+
Mage::dispatchEvent('cron_exception', array('schedule' => $this, 'exception' => $e));
|
238 |
+
Mage::helper('aoe_scheduler')->sendErrorMail($this, $e->__toString());
|
239 |
+
}
|
240 |
+
|
241 |
+
$this->setFinishedAt(strftime('%Y-%m-%d %H:%M:%S', time()));
|
242 |
+
Mage::dispatchEvent('cron_' . $this->getJobCode() . '_after', array('schedule' => $this));
|
243 |
+
Mage::dispatchEvent('cron_after', array('schedule' => $this));
|
244 |
+
|
245 |
+
$this->save();
|
246 |
+
Mage::unregister('currently_running_schedule');
|
247 |
+
|
248 |
+
return $this;
|
249 |
+
}
|
250 |
+
|
251 |
+
|
252 |
+
/**
|
253 |
+
* Flag that shows that a previous execution was prevented because the job was locked
|
254 |
+
*
|
255 |
+
* @return bool
|
256 |
+
*/
|
257 |
+
public function getJobWasLocked()
|
258 |
+
{
|
259 |
+
return $this->jobWasLocked;
|
260 |
+
}
|
261 |
+
|
262 |
+
|
263 |
+
/**
|
264 |
+
* Schedule this task to be executed as soon as possible
|
265 |
+
*
|
266 |
+
* @deprecated use Aoe_Scheduler_Model_Schedule::schedule() instead
|
267 |
+
* @return Aoe_Scheduler_Model_Schedule
|
268 |
+
*/
|
269 |
+
public function scheduleNow()
|
270 |
+
{
|
271 |
+
return $this->schedule();
|
272 |
+
}
|
273 |
+
|
274 |
+
|
275 |
+
/**
|
276 |
+
* Schedule this task to be executed at a given time
|
277 |
+
*
|
278 |
+
* @param int $time
|
279 |
+
* @return Aoe_Scheduler_Model_Schedule
|
280 |
+
*/
|
281 |
+
public function schedule($time = null)
|
282 |
+
{
|
283 |
+
if (is_null($time)) {
|
284 |
+
$time = time();
|
285 |
+
}
|
286 |
+
$this->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_PENDING)
|
287 |
+
->setCreatedAt(strftime('%Y-%m-%d %H:%M:%S', time()))
|
288 |
+
->setScheduledAt(strftime('%Y-%m-%d %H:%M:00', $time))
|
289 |
+
->save();
|
290 |
+
return $this;
|
291 |
+
}
|
292 |
+
|
293 |
+
|
294 |
+
/**
|
295 |
+
* Get job configuration
|
296 |
+
*
|
297 |
+
* @return Aoe_Scheduler_Model_Job
|
298 |
+
*/
|
299 |
+
public function getJob()
|
300 |
+
{
|
301 |
+
if (is_null($this->job)) {
|
302 |
+
$this->job = Mage::getModel('aoe_scheduler/job')->load($this->getJobCode());
|
303 |
+
}
|
304 |
+
return $this->job;
|
305 |
+
}
|
306 |
+
|
307 |
+
|
308 |
+
|
309 |
+
/**
|
310 |
+
* Get start time (planned or actual)
|
311 |
+
*
|
312 |
+
* @return string
|
313 |
+
*/
|
314 |
+
public function getStarttime()
|
315 |
+
{
|
316 |
+
$starttime = $this->getExecutedAt();
|
317 |
+
if (empty($starttime) || $starttime == '0000-00-00 00:00:00') {
|
318 |
+
$starttime = $this->getScheduledAt();
|
319 |
+
}
|
320 |
+
return $starttime;
|
321 |
+
}
|
322 |
+
|
323 |
+
|
324 |
+
/**
|
325 |
+
* Get job duration.
|
326 |
+
*
|
327 |
+
* @return bool|int time in seconds, or false
|
328 |
+
*/
|
329 |
+
public function getDuration()
|
330 |
+
{
|
331 |
+
$duration = false;
|
332 |
+
if ($this->getExecutedAt() && ($this->getExecutedAt() != '0000-00-00 00:00:00')) {
|
333 |
+
if ($this->getFinishedAt() && ($this->getFinishedAt() != '0000-00-00 00:00:00')) {
|
334 |
+
$time = strtotime($this->getFinishedAt());
|
335 |
+
} elseif ($this->getStatus() == Aoe_Scheduler_Model_Schedule::STATUS_RUNNING) {
|
336 |
+
$time = time();
|
337 |
+
} else {
|
338 |
+
// Mage::throwException('No finish time found, but the job is not running');
|
339 |
+
return false;
|
340 |
+
}
|
341 |
+
$duration = $time - strtotime($this->getExecutedAt());
|
342 |
+
}
|
343 |
+
return $duration;
|
344 |
+
}
|
345 |
+
|
346 |
+
/**
|
347 |
+
* Is this process still alive?
|
348 |
+
*
|
349 |
+
* true -> alive
|
350 |
+
* false -> dead
|
351 |
+
* null -> we don't know because the task is running on a different server
|
352 |
+
*
|
353 |
+
* @return bool|null
|
354 |
+
*/
|
355 |
+
public function isAlive()
|
356 |
+
{
|
357 |
+
if ($this->getStatus() == Aoe_Scheduler_Model_Schedule::STATUS_RUNNING) {
|
358 |
+
if (time() - strtotime($this->getLastSeen()) < 2 * 60) { // TODO: make this configurable
|
359 |
+
return true;
|
360 |
+
} elseif ($this->getHost() == gethostname()) {
|
361 |
+
if ($this->checkPid()) {
|
362 |
+
$this
|
363 |
+
->setLastSeen(strftime('%Y-%m-%d %H:%M:%S', time()))
|
364 |
+
->save();
|
365 |
+
return true;
|
366 |
+
} else {
|
367 |
+
$this->markAsDisappeared(sprintf('Process "%s" on host "%s" cannot be found anymore', $this->getPid(), $this->getHost()));
|
368 |
+
return false; // dead
|
369 |
+
}
|
370 |
+
} else {
|
371 |
+
// we don't know because the task is running on a different server
|
372 |
+
return null;
|
373 |
+
}
|
374 |
+
}
|
375 |
+
return false;
|
376 |
+
}
|
377 |
+
|
378 |
+
/**
|
379 |
+
* Mark task as disappeared
|
380 |
+
*
|
381 |
+
* @param string $message
|
382 |
+
* @return void
|
383 |
+
*/
|
384 |
+
public function markAsDisappeared($message = null)
|
385 |
+
{
|
386 |
+
if (!is_null($message)) {
|
387 |
+
$this->setMessages($message);
|
388 |
+
}
|
389 |
+
$this
|
390 |
+
->setStatus(self::STATUS_DISAPPEARED)
|
391 |
+
->setFinishedAt($this->getLastSeen())
|
392 |
+
->save();
|
393 |
+
|
394 |
+
$this->log(sprintf('Job "%s" (id: %s) disappeared. Message: ', $this->getJobCode(), $this->getId(), $message));
|
395 |
+
}
|
396 |
+
|
397 |
+
/**
|
398 |
+
* Check if process is running (linux only)
|
399 |
+
*
|
400 |
+
* @return bool
|
401 |
+
*/
|
402 |
+
public function checkPid()
|
403 |
+
{
|
404 |
+
$pid = intval($this->getPid());
|
405 |
+
return $pid && file_exists('/proc/' . $pid);
|
406 |
+
}
|
407 |
+
|
408 |
+
/**
|
409 |
+
* Request kill
|
410 |
+
*
|
411 |
+
* @param int $time
|
412 |
+
* @param string $message
|
413 |
+
* @return $this
|
414 |
+
*/
|
415 |
+
public function requestKill($time = null, $message = null)
|
416 |
+
{
|
417 |
+
if (is_null($time)) {
|
418 |
+
$time = time();
|
419 |
+
}
|
420 |
+
if (!is_null($message)) {
|
421 |
+
$this->addMessages($message);
|
422 |
+
}
|
423 |
+
$this->setKillRequest(strftime('%Y-%m-%d %H:%M:%S', $time))
|
424 |
+
->save();
|
425 |
+
return $this;
|
426 |
+
}
|
427 |
+
|
428 |
+
/**
|
429 |
+
* Kill this process
|
430 |
+
*
|
431 |
+
* @return void
|
432 |
+
*/
|
433 |
+
public function kill()
|
434 |
+
{
|
435 |
+
|
436 |
+
if (!$this->checkPid()) {
|
437 |
+
// already dead
|
438 |
+
$this->markAsDisappeared(sprintf('Did not kill job "%s" (id: %s), because it was already dead.', $this->getJobCode(), $this->getId()));
|
439 |
+
return;
|
440 |
+
}
|
441 |
+
|
442 |
+
// let's be nice first (a.k.a. "Could you please stop running now?")
|
443 |
+
if (posix_kill($this->getPid(), SIGINT)) {
|
444 |
+
$this->log(sprintf('Sending SIGINT to job "%s" (id: %s)', $this->getJobCode(), $this->getId()));
|
445 |
+
} else {
|
446 |
+
$this->log(sprintf('Error while sending SIGINT to job "%s" (id: %s)', $this->getJobCode(), $this->getId()), Zend_Log::ERR);
|
447 |
+
}
|
448 |
+
|
449 |
+
// check if process terminates within 30 seconds
|
450 |
+
$startTime = time();
|
451 |
+
while (($waitTime = (time() - $startTime) < 30) && $this->checkPid()) {
|
452 |
+
sleep(2);
|
453 |
+
}
|
454 |
+
|
455 |
+
if ($this->checkPid()) {
|
456 |
+
// What, you're still alive? OK, time to say goodbye now. You had your chance...
|
457 |
+
if (posix_kill($this->getPid(), SIGKILL)) {
|
458 |
+
$this->log(sprintf('Sending SIGKILL to job "%s" (id: %s)', $this->getJobCode(), $this->getId()));
|
459 |
+
} else {
|
460 |
+
$this->log(sprintf('Error while sending SIGKILL to job "%s" (id: %s)', $this->getJobCode(), $this->getId()), Zend_Log::ERR);
|
461 |
+
}
|
462 |
+
} else {
|
463 |
+
$this->log(sprintf('Killed job "%s" (id: %s) with SIGINT. Job terminated after %s second(s)', $this->getJobCode(), $this->getId(), $waitTime));
|
464 |
+
}
|
465 |
+
|
466 |
+
if ($this->checkPid()) {
|
467 |
+
sleep(5);
|
468 |
+
if ($this->checkPid()) {
|
469 |
+
$this->log(sprintf('Killed job "%s" (id: %s) is still alive!', $this->getJobCode(), $this->getId()), Zend_Log::ERR);
|
470 |
+
return; // without setting the status to "killed"
|
471 |
+
}
|
472 |
+
}
|
473 |
+
|
474 |
+
$this
|
475 |
+
->setStatus(self::STATUS_KILLED)
|
476 |
+
->setFinishedAt(strftime('%Y-%m-%d %H:%M:%S', time()))
|
477 |
+
->save();
|
478 |
+
}
|
479 |
+
|
480 |
+
/**
|
481 |
+
* Log message to configured log file (or skip)
|
482 |
+
*
|
483 |
+
* @param $message
|
484 |
+
* @param null $level
|
485 |
+
*/
|
486 |
+
protected function log($message, $level = null)
|
487 |
+
{
|
488 |
+
if ($logFile = Mage::getStoreConfig('system/cron/logFile')) {
|
489 |
+
Mage::log($message, $level, $logFile);
|
490 |
+
}
|
491 |
+
}
|
492 |
+
|
493 |
+
/**
|
494 |
+
* Check if this is an "always" task
|
495 |
+
*
|
496 |
+
* @return bool
|
497 |
+
*/
|
498 |
+
public function isAlwaysTask()
|
499 |
+
{
|
500 |
+
$isAlwaysTask = false;
|
501 |
+
try {
|
502 |
+
$job = $this->getJob();
|
503 |
+
$isAlwaysTask = $job && $job->isAlwaysTask();
|
504 |
+
} catch (Exception $e) {
|
505 |
+
Mage::logException($e);
|
506 |
+
}
|
507 |
+
return $isAlwaysTask;
|
508 |
+
}
|
509 |
+
|
510 |
+
/**
|
511 |
+
* Processing object before save data
|
512 |
+
*
|
513 |
+
* Check if there are other schedules for the same job at the same time and skip saving in this case.
|
514 |
+
*
|
515 |
+
* @return Mage_Core_Model_Abstract
|
516 |
+
*/
|
517 |
+
protected function _beforeSave()
|
518 |
+
{
|
519 |
+
if (!$this->getScheduledBy() && php_sapi_name() !== 'cli' && Mage::getSingleton('admin/session')->isLoggedIn()) {
|
520 |
+
$this->setScheduledBy(Mage::getSingleton('admin/session')->getUser()->getId());
|
521 |
+
}
|
522 |
+
|
523 |
+
$collection = Mage::getModel('cron/schedule')/* @var $collection Mage_Cron_Model_Resource_Schedule_Collection */
|
524 |
+
->getCollection()
|
525 |
+
->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_PENDING)
|
526 |
+
->addFieldToFilter('job_code', $this->getJobCode())
|
527 |
+
->addFieldToFilter('scheduled_at', $this->getScheduledAt());
|
528 |
+
if ($this->getId() !== null) {
|
529 |
+
$collection->addFieldToFilter('schedule_id', array('neq' => $this->getId()));
|
530 |
+
}
|
531 |
+
$count = $collection->count();
|
532 |
+
if ($count > 0) {
|
533 |
+
$this->_dataSaveAllowed = false; // prevents this object from being stored to database
|
534 |
+
$this->log(sprintf('Pending schedule for "%s" at "%s" already exists %s times. Skipping.', $this->getJobCode(), $this->getScheduledAt(), $count));
|
535 |
+
} else {
|
536 |
+
$this->_dataSaveAllowed = true; // allow the next object to save (because it's not reset automatically)
|
537 |
+
}
|
538 |
+
return parent::_beforeSave();
|
539 |
+
}
|
540 |
+
|
541 |
+
/**
|
542 |
+
* Check if this schedule can be run
|
543 |
+
*
|
544 |
+
* @param bool $throwException
|
545 |
+
* @return bool
|
546 |
+
* @throws Exception
|
547 |
+
* @throws Mage_Core_Exception
|
548 |
+
*/
|
549 |
+
public function canRun($throwException = false)
|
550 |
+
{
|
551 |
+
if ($this->isAlwaysTask()) {
|
552 |
+
return true;
|
553 |
+
}
|
554 |
+
$now = time();
|
555 |
+
$time = strtotime($this->getScheduledAt());
|
556 |
+
if ($time > $now) {
|
557 |
+
// not scheduled yet
|
558 |
+
return false;
|
559 |
+
}
|
560 |
+
$scheduleLifetime = Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_SCHEDULE_LIFETIME) * 60;
|
561 |
+
if ($time < $now - $scheduleLifetime) {
|
562 |
+
$this->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_MISSED);
|
563 |
+
$this->save();
|
564 |
+
if ($throwException) {
|
565 |
+
Mage::throwException(Mage::helper('cron')->__('Too late for the schedule.'));
|
566 |
+
}
|
567 |
+
return false;
|
568 |
+
}
|
569 |
+
return true;
|
570 |
+
}
|
571 |
+
|
572 |
+
/**
|
573 |
+
* Process schedule
|
574 |
+
*
|
575 |
+
* @return $this
|
576 |
+
*/
|
577 |
+
public function process()
|
578 |
+
{
|
579 |
+
if (!$this->canRun(false)) {
|
580 |
+
return $this;
|
581 |
+
}
|
582 |
+
$this->runNow(!$this->isAlwaysTask());
|
583 |
+
return $this;
|
584 |
+
}
|
585 |
+
|
586 |
+
/**
|
587 |
+
* Get parameters (and fallback to job)
|
588 |
+
*
|
589 |
+
* @return mixed
|
590 |
+
*/
|
591 |
+
public function getParameters()
|
592 |
+
{
|
593 |
+
if ($this->getData('parameters')) {
|
594 |
+
return $this->getData('parameters');
|
595 |
+
}
|
596 |
+
// fallback to job
|
597 |
+
$job = $this->getJob();
|
598 |
+
if ($job) {
|
599 |
+
return $job->getParameters();
|
600 |
+
} else {
|
601 |
+
return false;
|
602 |
+
}
|
603 |
+
}
|
604 |
+
|
605 |
+
/**
|
606 |
+
* Switch the job error context
|
607 |
+
*/
|
608 |
+
protected function jobErrorContext()
|
609 |
+
{
|
610 |
+
if (!Mage::getStoreConfigFlag('system/cron/enableErrorLog')) {
|
611 |
+
return;
|
612 |
+
}
|
613 |
+
|
614 |
+
$settings = array(
|
615 |
+
'error_reporting' => intval(Mage::getStoreConfig('system/cron/errorLevel')),
|
616 |
+
'log_errors' => true,
|
617 |
+
'display_errors' => true,
|
618 |
+
'error_log' => $this->getErrorLogFile()
|
619 |
+
);
|
620 |
+
|
621 |
+
restore_error_handler();
|
622 |
+
// (doesn't work for PHP 5.3) set_error_handler(null); // switch to PHP default error handling
|
623 |
+
|
624 |
+
if (!is_dir(dirname($settings['error_log']))) {
|
625 |
+
mkdir(dirname($settings['error_log']), 0775, true);
|
626 |
+
}
|
627 |
+
|
628 |
+
foreach ($settings as $key => $value) {
|
629 |
+
// backup original settings first
|
630 |
+
$this->errorSettingsBackup[$key] = ini_get($key);
|
631 |
+
// set new value
|
632 |
+
ini_set($key, $value);
|
633 |
+
}
|
634 |
+
}
|
635 |
+
|
636 |
+
/**
|
637 |
+
* Restore the original error context
|
638 |
+
*/
|
639 |
+
protected function restoreErrorContext()
|
640 |
+
{
|
641 |
+
if (!Mage::getStoreConfigFlag('system/cron/enableErrorLog')) {
|
642 |
+
return;
|
643 |
+
}
|
644 |
+
|
645 |
+
restore_error_handler();
|
646 |
+
foreach ($this->errorSettingsBackup as $key => $value) {
|
647 |
+
ini_set($key, $value);
|
648 |
+
}
|
649 |
+
}
|
650 |
+
|
651 |
+
/**
|
652 |
+
* Get error log filename
|
653 |
+
*
|
654 |
+
* @return string
|
655 |
+
*/
|
656 |
+
public function getErrorLogFile()
|
657 |
+
{
|
658 |
+
$replace = array(
|
659 |
+
'###PID###' => $this->getPid(),
|
660 |
+
'###ID###' => $this->getId(),
|
661 |
+
'###JOBCODE###' => $this->getJobCode()
|
662 |
+
);
|
663 |
+
$basedir = Mage::getBaseDir('log') . DS . 'cron' . DS;
|
664 |
+
return $basedir . str_replace(array_keys($replace), array_values($replace), Mage::getStoreConfig('system/cron/errorLogFilename'));
|
665 |
+
}
|
666 |
+
|
667 |
+
/**
|
668 |
+
* Redirect all output to the messages field of this Schedule.
|
669 |
+
*
|
670 |
+
* We use ob_start with `_addBufferToMessages` to redirect the output.
|
671 |
+
*
|
672 |
+
* @return $this
|
673 |
+
*/
|
674 |
+
protected function _startBufferToMessages()
|
675 |
+
{
|
676 |
+
if (!Mage::getStoreConfigFlag('system/cron/enableJobOutputBuffer')) {
|
677 |
+
return $this;
|
678 |
+
}
|
679 |
+
|
680 |
+
if ($this->_redirect) {
|
681 |
+
return $this;
|
682 |
+
}
|
683 |
+
|
684 |
+
$this->addMessages('---START---' . PHP_EOL);
|
685 |
+
|
686 |
+
ob_start(
|
687 |
+
array($this, '_addBufferToMessages'),
|
688 |
+
$this->_redirectOutputHandlerChunkSize
|
689 |
+
);
|
690 |
+
|
691 |
+
$this->_redirect = true;
|
692 |
+
}
|
693 |
+
|
694 |
+
/**
|
695 |
+
* Stop redirecting all output to the messages field of this Schedule.
|
696 |
+
*
|
697 |
+
* We use ob_end_flush to stop redirecting the output.
|
698 |
+
*
|
699 |
+
* @return $this
|
700 |
+
*/
|
701 |
+
protected function _stopBufferToMessages()
|
702 |
+
{
|
703 |
+
if (!Mage::getStoreConfigFlag('system/cron/enableJobOutputBuffer')) {
|
704 |
+
return $this;
|
705 |
+
}
|
706 |
+
|
707 |
+
if (!$this->_redirect) {
|
708 |
+
return $this;
|
709 |
+
}
|
710 |
+
|
711 |
+
ob_end_flush();
|
712 |
+
$this->addMessages('---END---' . PHP_EOL);
|
713 |
+
|
714 |
+
$this->_redirect = false;
|
715 |
+
}
|
716 |
+
|
717 |
+
/**
|
718 |
+
* Used as callback function to redirect the output buffer
|
719 |
+
* directly into the messages field of this schedule.
|
720 |
+
*
|
721 |
+
* @param $buffer
|
722 |
+
*
|
723 |
+
* @return string
|
724 |
+
*/
|
725 |
+
public function _addBufferToMessages($buffer)
|
726 |
+
{
|
727 |
+
$this->addMessages($buffer)
|
728 |
+
->saveMessages(); // Save the directly to the schedule record.
|
729 |
+
|
730 |
+
return $buffer;
|
731 |
+
}
|
732 |
+
|
733 |
+
/**
|
734 |
+
* Append data to the current messages field.
|
735 |
+
*
|
736 |
+
* @param $messages
|
737 |
+
*
|
738 |
+
* @return $this
|
739 |
+
*/
|
740 |
+
public function addMessages($messages)
|
741 |
+
{
|
742 |
+
$this->setMessages($this->getMessages() . $messages);
|
743 |
+
|
744 |
+
return $this;
|
745 |
+
}
|
746 |
+
|
747 |
+
/**
|
748 |
+
* Save the messages directly to the schedule record.
|
749 |
+
*
|
750 |
+
* If the `messages` field was not updated in the database,
|
751 |
+
* check if this is because of `data truncation` and fix the message length.
|
752 |
+
*
|
753 |
+
* @return $this
|
754 |
+
*/
|
755 |
+
public function saveMessages()
|
756 |
+
{
|
757 |
+
if (!$this->getId()) {
|
758 |
+
return $this->save();
|
759 |
+
}
|
760 |
+
|
761 |
+
$connection = Mage::getSingleton('core/resource')
|
762 |
+
->getConnection('core_write');
|
763 |
+
|
764 |
+
$count = $connection
|
765 |
+
->update(
|
766 |
+
$this->getResource()->getMainTable(),
|
767 |
+
array('messages' => $this->getMessages()),
|
768 |
+
array('schedule_id = ?' => $this->getId())
|
769 |
+
);
|
770 |
+
|
771 |
+
if (!$count) {
|
772 |
+
/**
|
773 |
+
* Check if the row was not updated because of data truncation.
|
774 |
+
*/
|
775 |
+
$warning = $this->_getPdoWarning($connection->getConnection());
|
776 |
+
if ($warning && $warning->Code = 1265) {
|
777 |
+
$maxLength = strlen($this->getMessages()) - 5000;
|
778 |
+
$this->setMessages($warning->Level . ': ' .
|
779 |
+
str_replace(' at row 1', '.', $warning->Message) . PHP_EOL . PHP_EOL .
|
780 |
+
'...' . substr($this->getMessages(), -$maxLength));
|
781 |
+
}
|
782 |
+
}
|
783 |
+
|
784 |
+
return $this;
|
785 |
+
}
|
786 |
+
|
787 |
+
/**
|
788 |
+
* Retrieve the last PDO warning.
|
789 |
+
*
|
790 |
+
* @param PDO $pdo
|
791 |
+
* @return mixed
|
792 |
+
*/
|
793 |
+
protected function _getPdoWarning(PDO $pdo)
|
794 |
+
{
|
795 |
+
$originalErrorMode = $pdo->getAttribute(PDO::ATTR_ERRMODE);
|
796 |
+
|
797 |
+
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
|
798 |
+
|
799 |
+
$stm = $pdo->query('SHOW WARNINGS');
|
800 |
+
|
801 |
+
$pdo->setAttribute(PDO::ATTR_ERRMODE, $originalErrorMode);
|
802 |
+
|
803 |
+
return $stm->fetchObject();
|
804 |
+
}
|
805 |
+
|
806 |
+
/**
|
807 |
+
* Bypass parent's setCronExpr is the expression is "always"
|
808 |
+
* This will break trySchedule, but always tasks will never be tried to scheduled anyway
|
809 |
+
*
|
810 |
+
* @param $expr
|
811 |
+
* @return $this
|
812 |
+
* @throws Mage_Core_Exception
|
813 |
+
*/
|
814 |
+
public function setCronExpr($expr)
|
815 |
+
{
|
816 |
+
if ($expr == 'always') {
|
817 |
+
$this->setData('cron_expr', $expr);
|
818 |
+
} else {
|
819 |
+
parent::setCronExpr($expr);
|
820 |
+
}
|
821 |
+
return $this;
|
822 |
+
}
|
823 |
+
|
824 |
+
/**
|
825 |
+
* Gets statuses that are currently in the scheduler table
|
826 |
+
*
|
827 |
+
* @return array
|
828 |
+
*/
|
829 |
+
public function getStatuses()
|
830 |
+
{
|
831 |
+
$schedules = clone $this->getCollection()
|
832 |
+
->setOrder('status', Zend_Db_Select::SQL_ASC);
|
833 |
+
$schedules->getSelect()
|
834 |
+
->group('status')
|
835 |
+
->reset(Zend_Db_Select::COLUMNS)
|
836 |
+
->columns('status');
|
837 |
+
$statuses = $schedules->getConnection()
|
838 |
+
->fetchCol($schedules->getSelect());
|
839 |
+
$statusArray = array();
|
840 |
+
foreach ($statuses as $status) {
|
841 |
+
$statusArray[$status] = $status;
|
842 |
+
}
|
843 |
+
return $statusArray;
|
844 |
+
}
|
845 |
+
}
|
app/code/community/Aoe/Scheduler/Model/ScheduleManager.php
ADDED
@@ -0,0 +1,370 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Schedule Manager
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
* @since 2014-08-14
|
8 |
+
*/
|
9 |
+
class Aoe_Scheduler_Model_ScheduleManager
|
10 |
+
{
|
11 |
+
const XML_PATH_HISTORY_MAXNO = 'system/cron/maxNoOfSuccessfulTasks';
|
12 |
+
const CACHE_KEY_SCHEDULER_LASTRUNS = 'cron_lastruns';
|
13 |
+
|
14 |
+
/**
|
15 |
+
* Mark missed schedule records by changing status
|
16 |
+
*
|
17 |
+
* @return $this
|
18 |
+
*/
|
19 |
+
public function cleanMissedSchedules()
|
20 |
+
{
|
21 |
+
$schedules = Mage::getModel('cron/schedule')->getCollection()
|
22 |
+
->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_PENDING)
|
23 |
+
->addFieldToFilter('scheduled_at', array('lt' => strftime('%Y-%m-%d %H:%M:%S', time())))
|
24 |
+
->addOrder('scheduled_at', 'DESC');
|
25 |
+
|
26 |
+
$seenJobs = array();
|
27 |
+
foreach ($schedules as $key => $schedule) {
|
28 |
+
/* @var Aoe_Scheduler_Model_Schedule $schedule */
|
29 |
+
if (isset($seenJobs[$schedule->getJobCode()])) {
|
30 |
+
$schedule
|
31 |
+
->setMessages('Multiple tasks with the same job code were piling up. Skipping execution of duplicates.')
|
32 |
+
->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_MISSED)
|
33 |
+
->save();
|
34 |
+
} else {
|
35 |
+
$seenJobs[$schedule->getJobCode()] = 1;
|
36 |
+
}
|
37 |
+
}
|
38 |
+
|
39 |
+
return $this;
|
40 |
+
}
|
41 |
+
|
42 |
+
/**
|
43 |
+
* Get pending schedules
|
44 |
+
*
|
45 |
+
* @param array $whitelist
|
46 |
+
* @param array $blacklist
|
47 |
+
*
|
48 |
+
* @return Mage_Cron_Model_Resource_Schedule_Collection
|
49 |
+
*/
|
50 |
+
public function getPendingSchedules(array $whitelist = array(), array $blacklist = array())
|
51 |
+
{
|
52 |
+
$pendingSchedules = Mage::getModel('cron/schedule')->getCollection()
|
53 |
+
->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_PENDING)
|
54 |
+
->addFieldToFilter('scheduled_at', array('lt' => strftime('%Y-%m-%d %H:%M:%S', time())))
|
55 |
+
->addOrder('scheduled_at', 'ASC');
|
56 |
+
|
57 |
+
$whitelist = array_filter(array_map('trim', $whitelist));
|
58 |
+
if (!empty($whitelist)) {
|
59 |
+
$pendingSchedules->addFieldToFilter('job_code', array('in' => $whitelist));
|
60 |
+
}
|
61 |
+
|
62 |
+
$blacklist = array_filter(array_map('trim', $blacklist));
|
63 |
+
if (!empty($blacklist)) {
|
64 |
+
$pendingSchedules->addFieldToFilter('job_code', array('nin' => $blacklist));
|
65 |
+
}
|
66 |
+
|
67 |
+
return $pendingSchedules;
|
68 |
+
}
|
69 |
+
|
70 |
+
/**
|
71 |
+
* Get job for task marked as always
|
72 |
+
*
|
73 |
+
* (Instead of reusing existing one - which results in loosing the history - create a new one every time)
|
74 |
+
*
|
75 |
+
* @param $jobCode
|
76 |
+
* @return Aoe_Scheduler_Model_Schedule|false
|
77 |
+
*/
|
78 |
+
public function getScheduleForAlwaysJob($jobCode)
|
79 |
+
{
|
80 |
+
$processManager = Mage::getModel('aoe_scheduler/processManager'); /* @var $processManager Aoe_Scheduler_Model_ProcessManager */
|
81 |
+
if (!$processManager->isJobCodeRunning($jobCode)) {
|
82 |
+
$ts = strftime('%Y-%m-%d %H:%M:00', time());
|
83 |
+
$schedule = Mage::getModel('cron/schedule') /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
84 |
+
->setJobCode($jobCode)
|
85 |
+
->setStatus(Aoe_Scheduler_Model_Schedule::STATUS_RUNNING)
|
86 |
+
->setCreatedAt($ts)
|
87 |
+
->setScheduledAt($ts)
|
88 |
+
->save();
|
89 |
+
return $schedule;
|
90 |
+
}
|
91 |
+
return false;
|
92 |
+
}
|
93 |
+
|
94 |
+
/**
|
95 |
+
* Delete duplicate crons
|
96 |
+
*
|
97 |
+
* @throws Exception
|
98 |
+
*/
|
99 |
+
public function deleteDuplicates()
|
100 |
+
{
|
101 |
+
$cron_schedule = Mage::getSingleton('core/resource')->getTableName('cron_schedule');
|
102 |
+
$conn = Mage::getSingleton('core/resource')->getConnection('core_read');
|
103 |
+
|
104 |
+
// TODO: Direct sql is not nice. We can do better... :)
|
105 |
+
$results = $conn->fetchAll("
|
106 |
+
SELECT
|
107 |
+
GROUP_CONCAT(schedule_id) AS ids,
|
108 |
+
CONCAT(job_code, scheduled_at) AS jobkey,
|
109 |
+
count(*) AS qty
|
110 |
+
FROM {$cron_schedule}
|
111 |
+
WHERE status = '" . Aoe_Scheduler_Model_Schedule::STATUS_PENDING . "'
|
112 |
+
GROUP BY jobkey
|
113 |
+
HAVING qty > 1;
|
114 |
+
");
|
115 |
+
foreach ($results as $row) {
|
116 |
+
$ids = explode(',', $row['ids']);
|
117 |
+
$removeIds = array_slice($ids, 1);
|
118 |
+
foreach ($removeIds as $id) {
|
119 |
+
Mage::getModel('cron/schedule')->load($id)->delete();
|
120 |
+
}
|
121 |
+
}
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Generate cron schedule.
|
126 |
+
* Rewrites the original method to remove duplicates afterwards (that exists because of a bug)
|
127 |
+
*
|
128 |
+
* @return $this
|
129 |
+
*/
|
130 |
+
public function generateSchedules()
|
131 |
+
{
|
132 |
+
/**
|
133 |
+
* check if schedule generation is needed
|
134 |
+
*/
|
135 |
+
$lastRun = Mage::app()->loadCache(Mage_Cron_Model_Observer::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT);
|
136 |
+
if ($lastRun > time() - Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_SCHEDULE_GENERATE_EVERY) * 60) {
|
137 |
+
return $this;
|
138 |
+
}
|
139 |
+
|
140 |
+
$startTime = microtime(true);
|
141 |
+
|
142 |
+
/* @var $jobs Aoe_Scheduler_Model_Resource_Job_Collection */
|
143 |
+
$jobs = Mage::getSingleton('aoe_scheduler/job')->getCollection();
|
144 |
+
$jobs->setActiveOnly(true);
|
145 |
+
foreach ($jobs as $job) {
|
146 |
+
/* @var Aoe_Scheduler_Model_Job $job */
|
147 |
+
$this->generateSchedulesForJob($job);
|
148 |
+
}
|
149 |
+
|
150 |
+
/**
|
151 |
+
* save time schedules generation was ran with no expiration
|
152 |
+
*/
|
153 |
+
Mage::app()->saveCache(time(), Mage_Cron_Model_Observer::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT, array('crontab'), null);
|
154 |
+
|
155 |
+
$this->deleteDuplicates();
|
156 |
+
|
157 |
+
if ($logFile = Mage::getStoreConfig('system/cron/logFile')) {
|
158 |
+
$history = Mage::getModel('cron/schedule')->getCollection()
|
159 |
+
->setPageSize(1)
|
160 |
+
->setOrder('scheduled_at', 'desc')
|
161 |
+
->load();
|
162 |
+
|
163 |
+
$newestSchedule = $history->getFirstItem(); /* @var $newestSchedule Aoe_Scheduler_Model_Schedule */
|
164 |
+
|
165 |
+
$duration = microtime(true) - $startTime;
|
166 |
+
Mage::log('Generated schedule. Newest task is scheduled at "' . $newestSchedule->getScheduledAt() . '". (Duration: ' . round($duration, 2) . ' sec)', null, $logFile);
|
167 |
+
}
|
168 |
+
|
169 |
+
return $this;
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Flushed all future pending schedules.
|
174 |
+
*
|
175 |
+
* @param string $jobCode
|
176 |
+
* @return $this
|
177 |
+
*/
|
178 |
+
public function flushSchedules($jobCode = null)
|
179 |
+
{
|
180 |
+
/* @var $pendingSchedules Mage_Cron_Model_Resource_Schedule_Collection */
|
181 |
+
$pendingSchedules = Mage::getModel('cron/schedule')->getCollection()
|
182 |
+
->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_PENDING)
|
183 |
+
->addFieldToFilter('scheduled_at', array('gt' => strftime('%Y-%m-%d %H:%M:%S', time())))
|
184 |
+
->addOrder('scheduled_at', 'ASC');
|
185 |
+
if (!empty($jobCode)) {
|
186 |
+
$pendingSchedules->addFieldToFilter('job_code', $jobCode);
|
187 |
+
}
|
188 |
+
foreach ($pendingSchedules as $key => $schedule) {
|
189 |
+
/* @var Aoe_Scheduler_Model_Schedule $schedule */
|
190 |
+
$schedule->delete();
|
191 |
+
}
|
192 |
+
Mage::app()->saveCache(0, Mage_Cron_Model_Observer::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT, array('crontab'), null);
|
193 |
+
return $this;
|
194 |
+
}
|
195 |
+
|
196 |
+
/**
|
197 |
+
* Delete all schedules
|
198 |
+
*
|
199 |
+
* @return $this
|
200 |
+
*/
|
201 |
+
public function deleteAll()
|
202 |
+
{
|
203 |
+
/* @var $schedules Mage_Cron_Model_Resource_Schedule_Collection */
|
204 |
+
$schedules = Mage::getModel('cron/schedule')->getCollection();
|
205 |
+
foreach ($schedules as $key => $schedule) { /* @var Aoe_Scheduler_Model_Schedule $schedule */
|
206 |
+
$schedule->delete();
|
207 |
+
}
|
208 |
+
Mage::app()->saveCache(0, Mage_Cron_Model_Observer::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT, array('crontab'), null);
|
209 |
+
return $this;
|
210 |
+
}
|
211 |
+
|
212 |
+
/**
|
213 |
+
* Generate jobs for config information
|
214 |
+
*
|
215 |
+
* @param Aoe_Scheduler_Model_Job $job
|
216 |
+
*
|
217 |
+
* @return $this
|
218 |
+
*/
|
219 |
+
public function generateSchedulesForJob(Aoe_Scheduler_Model_Job $job)
|
220 |
+
{
|
221 |
+
if (!$job->canBeScheduled()) {
|
222 |
+
return $this;
|
223 |
+
}
|
224 |
+
|
225 |
+
$exists = array();
|
226 |
+
foreach ($this->getPendingSchedules(array($job->getJobCode()), array()) as $schedule) {
|
227 |
+
/* @var Aoe_Scheduler_Model_Schedule $schedule */
|
228 |
+
$exists[$schedule->getJobCode() . '/' . $schedule->getScheduledAt()] = 1;
|
229 |
+
}
|
230 |
+
|
231 |
+
$now = time();
|
232 |
+
$scheduleAheadFor = Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_SCHEDULE_AHEAD_FOR)*60;
|
233 |
+
$timeAhead = $now + $scheduleAheadFor;
|
234 |
+
|
235 |
+
$schedule = Mage::getModel('cron/schedule'); /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
236 |
+
$schedule->initializeFromJob($job);
|
237 |
+
$schedule->setScheduledReason(Aoe_Scheduler_Model_Schedule::REASON_GENERATESCHEDULES);
|
238 |
+
|
239 |
+
for ($time = $now; $time < $timeAhead; $time += 60) {
|
240 |
+
$ts = strftime('%Y-%m-%d %H:%M:00', $time);
|
241 |
+
if (!empty($exists[$job->getJobCode().'/'.$ts])) {
|
242 |
+
// already scheduled
|
243 |
+
continue;
|
244 |
+
}
|
245 |
+
if (!$schedule->trySchedule($time)) {
|
246 |
+
// time does not match cron expression
|
247 |
+
continue;
|
248 |
+
}
|
249 |
+
$schedule->unsScheduleId()->save();
|
250 |
+
}
|
251 |
+
|
252 |
+
return $this;
|
253 |
+
}
|
254 |
+
|
255 |
+
/**
|
256 |
+
* Clean up the history of tasks
|
257 |
+
* This override deals with custom states added in Aoe_Scheduler
|
258 |
+
*
|
259 |
+
* @return Mage_Cron_Model_Observer
|
260 |
+
*/
|
261 |
+
public function cleanup()
|
262 |
+
{
|
263 |
+
// check if history cleanup is needed
|
264 |
+
$lastCleanup = Mage::app()->loadCache(Mage_Cron_Model_Observer::CACHE_KEY_LAST_HISTORY_CLEANUP_AT);
|
265 |
+
if ($lastCleanup > time() - Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_HISTORY_CLEANUP_EVERY) * 60) {
|
266 |
+
return $this;
|
267 |
+
}
|
268 |
+
|
269 |
+
$startTime = microtime(true);
|
270 |
+
|
271 |
+
$history = Mage::getModel('cron/schedule')->getCollection()
|
272 |
+
->addFieldToFilter('status', array('nin' => array(
|
273 |
+
Aoe_Scheduler_Model_Schedule::STATUS_PENDING,
|
274 |
+
Aoe_Scheduler_Model_Schedule::STATUS_RUNNING
|
275 |
+
)))
|
276 |
+
->load();
|
277 |
+
|
278 |
+
$historyLifetimes = array(
|
279 |
+
Aoe_Scheduler_Model_Schedule::STATUS_KILLED => Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_HISTORY_SUCCESS)*60,
|
280 |
+
Aoe_Scheduler_Model_Schedule::STATUS_DISAPPEARED => Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_HISTORY_FAILURE)*60,
|
281 |
+
Aoe_Scheduler_Model_Schedule::STATUS_DIDNTDOANYTHING => Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_HISTORY_SUCCESS)*60,
|
282 |
+
Aoe_Scheduler_Model_Schedule::STATUS_SUCCESS => Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_HISTORY_SUCCESS)*60,
|
283 |
+
Aoe_Scheduler_Model_Schedule::STATUS_MISSED => Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_HISTORY_FAILURE)*60,
|
284 |
+
Aoe_Scheduler_Model_Schedule::STATUS_ERROR => Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_HISTORY_FAILURE)*60,
|
285 |
+
Aoe_Scheduler_Model_Schedule::STATUS_DIED => Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_HISTORY_FAILURE)*60,
|
286 |
+
Aoe_Scheduler_Model_Schedule::STATUS_SKIP_LOCKED => Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_HISTORY_FAILURE)*60,
|
287 |
+
Aoe_Scheduler_Model_Schedule::STATUS_SKIP_OTHERJOBRUNNING => Mage::getStoreConfig(Mage_Cron_Model_Observer::XML_PATH_HISTORY_FAILURE)*60,
|
288 |
+
);
|
289 |
+
|
290 |
+
$now = time();
|
291 |
+
foreach ($history->getIterator() as $record) { /* @var $record Aoe_Scheduler_Model_Schedule */
|
292 |
+
if (isset($historyLifetimes[$record->getStatus()])) {
|
293 |
+
if (strtotime($record->getExecutedAt()) < $now - $historyLifetimes[$record->getStatus()]) {
|
294 |
+
$record->delete();
|
295 |
+
}
|
296 |
+
}
|
297 |
+
}
|
298 |
+
|
299 |
+
// save time history cleanup was ran with no expiration
|
300 |
+
Mage::app()->saveCache(time(), Mage_Cron_Model_Observer::CACHE_KEY_LAST_HISTORY_CLEANUP_AT, array('crontab'), null);
|
301 |
+
|
302 |
+
|
303 |
+
// delete successful tasks (beyond the configured max number of tasks to keep)
|
304 |
+
$maxNo = Mage::getStoreConfig(self::XML_PATH_HISTORY_MAXNO);
|
305 |
+
if ($maxNo) {
|
306 |
+
$history = Mage::getModel('cron/schedule')->getCollection()
|
307 |
+
->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_SUCCESS)
|
308 |
+
->setOrder('finished_at', 'desc')
|
309 |
+
->load();
|
310 |
+
$counter = array();
|
311 |
+
foreach ($history->getIterator() as $record) { /* @var $record Aoe_Scheduler_Model_Schedule */
|
312 |
+
$jobCode = $record->getJobCode();
|
313 |
+
if (!isset($counter[$jobCode])) {
|
314 |
+
$counter[$jobCode] = 0;
|
315 |
+
}
|
316 |
+
$counter[$jobCode]++;
|
317 |
+
if ($counter[$jobCode] > $maxNo) {
|
318 |
+
$record->delete();
|
319 |
+
}
|
320 |
+
}
|
321 |
+
}
|
322 |
+
|
323 |
+
if ($logFile = Mage::getStoreConfig('system/cron/logFile')) {
|
324 |
+
$duration = microtime(true) - $startTime;
|
325 |
+
Mage::log('History cleanup (Duration: ' . round($duration, 2) . ' sec)', null, $logFile);
|
326 |
+
}
|
327 |
+
|
328 |
+
return $this;
|
329 |
+
}
|
330 |
+
|
331 |
+
/**
|
332 |
+
* Log run
|
333 |
+
*/
|
334 |
+
public function logRun()
|
335 |
+
{
|
336 |
+
$lastRuns = Mage::app()->loadCache(self::CACHE_KEY_SCHEDULER_LASTRUNS);
|
337 |
+
$lastRuns = explode(',', $lastRuns);
|
338 |
+
$lastRuns[] = time();
|
339 |
+
$lastRuns = array_slice($lastRuns, -100);
|
340 |
+
Mage::app()->saveCache(implode(',', $lastRuns), self::CACHE_KEY_SCHEDULER_LASTRUNS, array('crontab'), null);
|
341 |
+
}
|
342 |
+
|
343 |
+
/**
|
344 |
+
* Create some statistics based on self::CACHE_KEY_SCHEDULER_LASTRUNS
|
345 |
+
*
|
346 |
+
* @return array|bool
|
347 |
+
*/
|
348 |
+
public function getMeasuredCronInterval()
|
349 |
+
{
|
350 |
+
$lastRuns = Mage::app()->loadCache(self::CACHE_KEY_SCHEDULER_LASTRUNS);
|
351 |
+
$lastRuns = array_values(array_filter(explode(',', $lastRuns)));
|
352 |
+
if (count($lastRuns) < 3) {
|
353 |
+
// not enough data points
|
354 |
+
return false;
|
355 |
+
}
|
356 |
+
$gaps = array();
|
357 |
+
foreach ($lastRuns as $index => $run) {
|
358 |
+
if ($index > 0) {
|
359 |
+
$gaps[$index] = intval($lastRuns[$index]) - intval($lastRuns[$index-1]);
|
360 |
+
}
|
361 |
+
}
|
362 |
+
return array(
|
363 |
+
'average' => round((array_sum($gaps) / count($gaps)) / 60, 2),
|
364 |
+
'max' => round(max($gaps) / 60, 2),
|
365 |
+
'min' => round(min($gaps) / 60, 2),
|
366 |
+
'count' => count($gaps),
|
367 |
+
'last' => end($lastRuns)
|
368 |
+
);
|
369 |
+
}
|
370 |
+
}
|
app/code/community/Aoe/Scheduler/Model/Task/Heartbeat.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Class Aoe_Scheduler_Model_HeartbeatTask
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
*/
|
8 |
+
class Aoe_Scheduler_Model_Task_Heartbeat
|
9 |
+
{
|
10 |
+
|
11 |
+
public function run()
|
12 |
+
{
|
13 |
+
return true;
|
14 |
+
}
|
15 |
+
}
|
app/code/community/Aoe/Scheduler/Model/Task/Test.php
ADDED
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Test Task
|
5 |
+
*
|
6 |
+
* @author Fabrizio Branca
|
7 |
+
* @since 2013-10-10
|
8 |
+
*/
|
9 |
+
class Aoe_Scheduler_Model_Task_Test
|
10 |
+
{
|
11 |
+
|
12 |
+
/**
|
13 |
+
* General purpose test task.
|
14 |
+
* Behavior can be controlled via parameters
|
15 |
+
*
|
16 |
+
* @param Aoe_Scheduler_Model_Schedule $schedule
|
17 |
+
* @return string
|
18 |
+
* @throws Exception
|
19 |
+
*/
|
20 |
+
public function run(Aoe_Scheduler_Model_Schedule $schedule)
|
21 |
+
{
|
22 |
+
|
23 |
+
$parameters = $schedule->getParameters();
|
24 |
+
if ($parameters) {
|
25 |
+
$parameters = unserialize($parameters);
|
26 |
+
}
|
27 |
+
|
28 |
+
// fake duration
|
29 |
+
$duration = 0;
|
30 |
+
if ($parameters && isset($parameters['duration'])) {
|
31 |
+
$duration = $parameters['duration'];
|
32 |
+
}
|
33 |
+
sleep($duration);
|
34 |
+
|
35 |
+
/* // testing the error log feature...
|
36 |
+
array_keys('ssdsd');
|
37 |
+
error_log( "Hello, errors!" );
|
38 |
+
$t = I_AM_NOT_DEFINED;
|
39 |
+
*/
|
40 |
+
|
41 |
+
if ($parameters && $parameters['outcome'] == 'error') {
|
42 |
+
return 'ERROR: This schedule has failed.';
|
43 |
+
}
|
44 |
+
|
45 |
+
if ($parameters && $parameters['outcome'] == 'nothing') {
|
46 |
+
return 'NOTHING: Did not do anything';
|
47 |
+
}
|
48 |
+
|
49 |
+
if ($parameters && $parameters['outcome'] == 'exception') {
|
50 |
+
throw new Exception('This is a dummy exception');
|
51 |
+
}
|
52 |
+
|
53 |
+
|
54 |
+
// Simulating ETA;
|
55 |
+
// $starttime = time();
|
56 |
+
// // $endtime = $starttime + rand(180, 360);
|
57 |
+
// $endtime = $starttime + $duration;
|
58 |
+
// $schedule
|
59 |
+
// ->setEta(strftime('%Y-%m-%d %H:%M:%S', $endtime))
|
60 |
+
// ->save();
|
61 |
+
// while ($endtime > time()) {
|
62 |
+
// sleep(5);
|
63 |
+
// $schedule
|
64 |
+
// ->setProgressMessage('Work in progress. Time spent: ' . (time() - $starttime))
|
65 |
+
// ->setEta(strftime('%Y-%m-%d %H:%M:%S', $endtime))
|
66 |
+
// ->save();
|
67 |
+
// }
|
68 |
+
//
|
69 |
+
// $schedule
|
70 |
+
// ->setProgressMessage('')
|
71 |
+
// ->save();
|
72 |
+
}
|
73 |
+
}
|
app/code/community/Aoe/Scheduler/Model/TestTask.php
DELETED
@@ -1,12 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
class Aoe_Scheduler_Model_TestTask {
|
4 |
-
|
5 |
-
public function run() {
|
6 |
-
sleep(rand(60,180));
|
7 |
-
if (rand(0, 1) == 0) {
|
8 |
-
throw new Exception('This is a dummy exception');
|
9 |
-
}
|
10 |
-
}
|
11 |
-
|
12 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/code/community/Aoe/Scheduler/Test/Helper/Data.php
ADDED
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Aoe_Scheduler_Test_Helper_Data extends EcomDev_PHPUnit_Test_Case
|
4 |
+
{
|
5 |
+
/**
|
6 |
+
* @test
|
7 |
+
* @return Aoe_Scheduler_Helper_Data
|
8 |
+
*/
|
9 |
+
public function checkClass()
|
10 |
+
{
|
11 |
+
/* @var Aoe_Scheduler_Helper_Data $helper */
|
12 |
+
$helper = Mage::helper('aoe_scheduler');
|
13 |
+
|
14 |
+
$this->assertInstanceOf('Aoe_Scheduler_Helper_Data', $helper);
|
15 |
+
|
16 |
+
return $helper;
|
17 |
+
}
|
18 |
+
}
|
app/code/community/Aoe/Scheduler/Test/Model/Schedule/Runnow.php
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Aoe_Scheduler_Test_Model_Schedule_Runnow extends EcomDev_PHPUnit_Test_Case
|
4 |
+
{
|
5 |
+
|
6 |
+
public function setup()
|
7 |
+
{
|
8 |
+
// delete all schedules
|
9 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager'); /* @var Aoe_Scheduler_Model_ScheduleManager $scheduleManager */
|
10 |
+
$scheduleManager->deleteAll();
|
11 |
+
}
|
12 |
+
|
13 |
+
/**
|
14 |
+
* @test
|
15 |
+
* @return Aoe_Scheduler_Model_Schedule
|
16 |
+
*/
|
17 |
+
public function checkClass()
|
18 |
+
{
|
19 |
+
/* @var Aoe_Scheduler_Model_Schedule $schedule */
|
20 |
+
$schedule = Mage::getModel('cron/schedule');
|
21 |
+
$this->assertInstanceOf('Aoe_Scheduler_Model_Schedule', $schedule);
|
22 |
+
return $schedule;
|
23 |
+
}
|
24 |
+
|
25 |
+
/**
|
26 |
+
* @test
|
27 |
+
*/
|
28 |
+
public function runJob()
|
29 |
+
{
|
30 |
+
$schedule = Mage::getModel('cron/schedule');
|
31 |
+
|
32 |
+
$jobCode = 'aoescheduler_testtask';
|
33 |
+
|
34 |
+
$schedule->setJobCode($jobCode);
|
35 |
+
$schedule->runNow(false);
|
36 |
+
|
37 |
+
$scheduleId = $schedule->getId();
|
38 |
+
$this->assertGreaterThan(0, intval($schedule->getId()));
|
39 |
+
|
40 |
+
/* @var Aoe_Scheduler_Model_Schedule $loadedSchedule */
|
41 |
+
$loadedSchedule = Mage::getModel('cron/schedule')->load($scheduleId);
|
42 |
+
$this->assertEquals($scheduleId, $loadedSchedule->getId());
|
43 |
+
|
44 |
+
$this->assertEquals(gethostname(), $loadedSchedule->getHost());
|
45 |
+
$this->assertEquals(getmypid(), $loadedSchedule->getPid());
|
46 |
+
|
47 |
+
$this->assertEquals(Aoe_Scheduler_Model_Schedule::STATUS_SUCCESS, $loadedSchedule->getStatus());
|
48 |
+
|
49 |
+
$this->assertEventDispatched(
|
50 |
+
array(
|
51 |
+
'cron_after',
|
52 |
+
'cron_after_success',
|
53 |
+
'cron_' . $jobCode . '_after',
|
54 |
+
'cron_' . $jobCode . '_after_success',
|
55 |
+
'cron_before',
|
56 |
+
'cron_' . $jobCode . '_before',
|
57 |
+
)
|
58 |
+
);
|
59 |
+
}
|
60 |
+
|
61 |
+
/**
|
62 |
+
* @test
|
63 |
+
*/
|
64 |
+
public function runJobWithError()
|
65 |
+
{
|
66 |
+
$schedule = Mage::getModel('cron/schedule');
|
67 |
+
|
68 |
+
$jobCode = 'aoescheduler_testtask';
|
69 |
+
|
70 |
+
$parameter = array('outcome' => 'error');
|
71 |
+
|
72 |
+
$schedule->setJobCode($jobCode);
|
73 |
+
$schedule->setParameters(serialize($parameter));
|
74 |
+
$schedule->runNow(false);
|
75 |
+
|
76 |
+
$scheduleId = $schedule->getId();
|
77 |
+
$this->assertGreaterThan(0, intval($schedule->getId()));
|
78 |
+
|
79 |
+
/* @var Aoe_Scheduler_Model_Schedule $loadedSchedule */
|
80 |
+
$loadedSchedule = Mage::getModel('cron/schedule')->load($scheduleId);
|
81 |
+
$this->assertEquals($scheduleId, $loadedSchedule->getId());
|
82 |
+
|
83 |
+
$this->assertEquals(gethostname(), $loadedSchedule->getHost());
|
84 |
+
$this->assertEquals(getmypid(), $loadedSchedule->getPid());
|
85 |
+
|
86 |
+
$this->assertEquals(Aoe_Scheduler_Model_Schedule::STATUS_ERROR, $loadedSchedule->getStatus());
|
87 |
+
|
88 |
+
$this->assertEventDispatched(
|
89 |
+
array(
|
90 |
+
'cron_after',
|
91 |
+
'cron_after_error',
|
92 |
+
'cron_' . $jobCode . '_after',
|
93 |
+
'cron_' . $jobCode . '_after_error',
|
94 |
+
'cron_before',
|
95 |
+
'cron_' . $jobCode . '_before',
|
96 |
+
)
|
97 |
+
);
|
98 |
+
}
|
99 |
+
|
100 |
+
/**
|
101 |
+
* @test
|
102 |
+
*/
|
103 |
+
public function runJobWithNothing()
|
104 |
+
{
|
105 |
+
$schedule = Mage::getModel('cron/schedule');
|
106 |
+
|
107 |
+
$jobCode = 'aoescheduler_testtask';
|
108 |
+
|
109 |
+
$parameter = array('outcome' => 'nothing');
|
110 |
+
|
111 |
+
$schedule->setJobCode($jobCode);
|
112 |
+
$schedule->setParameters(serialize($parameter));
|
113 |
+
$schedule->runNow(false);
|
114 |
+
|
115 |
+
$scheduleId = $schedule->getId();
|
116 |
+
$this->assertGreaterThan(0, intval($schedule->getId()));
|
117 |
+
|
118 |
+
/* @var Aoe_Scheduler_Model_Schedule $loadedSchedule */
|
119 |
+
$loadedSchedule = Mage::getModel('cron/schedule')->load($scheduleId);
|
120 |
+
$this->assertEquals($scheduleId, $loadedSchedule->getId());
|
121 |
+
|
122 |
+
$this->assertEquals(gethostname(), $loadedSchedule->getHost());
|
123 |
+
$this->assertEquals(getmypid(), $loadedSchedule->getPid());
|
124 |
+
|
125 |
+
$this->assertEquals(Aoe_Scheduler_Model_Schedule::STATUS_DIDNTDOANYTHING, $loadedSchedule->getStatus());
|
126 |
+
|
127 |
+
$this->assertEventDispatched(
|
128 |
+
array(
|
129 |
+
'cron_after',
|
130 |
+
'cron_after_nothing',
|
131 |
+
'cron_' . $jobCode . '_after',
|
132 |
+
'cron_' . $jobCode . '_after_nothing',
|
133 |
+
'cron_before',
|
134 |
+
'cron_' . $jobCode . '_before',
|
135 |
+
)
|
136 |
+
);
|
137 |
+
}
|
138 |
+
|
139 |
+
/**
|
140 |
+
* @test
|
141 |
+
*/
|
142 |
+
public function runJobWithException()
|
143 |
+
{
|
144 |
+
$schedule = Mage::getModel('cron/schedule');
|
145 |
+
|
146 |
+
$jobCode = 'aoescheduler_testtask';
|
147 |
+
|
148 |
+
$parameter = array('outcome' => 'exception');
|
149 |
+
|
150 |
+
$schedule->setJobCode($jobCode);
|
151 |
+
$schedule->setParameters(serialize($parameter));
|
152 |
+
$schedule->runNow(false);
|
153 |
+
|
154 |
+
$scheduleId = $schedule->getId();
|
155 |
+
$this->assertGreaterThan(0, intval($schedule->getId()));
|
156 |
+
|
157 |
+
/* @var Aoe_Scheduler_Model_Schedule $loadedSchedule */
|
158 |
+
$loadedSchedule = Mage::getModel('cron/schedule')->load($scheduleId);
|
159 |
+
$this->assertEquals($scheduleId, $loadedSchedule->getId());
|
160 |
+
|
161 |
+
$this->assertEquals(gethostname(), $loadedSchedule->getHost());
|
162 |
+
$this->assertEquals(getmypid(), $loadedSchedule->getPid());
|
163 |
+
|
164 |
+
$this->assertEquals(Aoe_Scheduler_Model_Schedule::STATUS_ERROR, $loadedSchedule->getStatus());
|
165 |
+
|
166 |
+
$this->assertEventDispatched(
|
167 |
+
array(
|
168 |
+
'cron_after',
|
169 |
+
'cron_exception',
|
170 |
+
'cron_' . $jobCode . '_after',
|
171 |
+
'cron_' . $jobCode . '_exception',
|
172 |
+
'cron_before',
|
173 |
+
'cron_' . $jobCode . '_before',
|
174 |
+
)
|
175 |
+
);
|
176 |
+
}
|
177 |
+
}
|
app/code/community/Aoe/Scheduler/Test/Model/Schedule/Scheduling.php
ADDED
@@ -0,0 +1,102 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
class Aoe_Scheduler_Test_Model_Schedule_Scheduling extends EcomDev_PHPUnit_Test_Case
|
4 |
+
{
|
5 |
+
|
6 |
+
/**
|
7 |
+
* @test
|
8 |
+
*/
|
9 |
+
public function generateSchedule()
|
10 |
+
{
|
11 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager'); /* @var Aoe_Scheduler_Model_ScheduleManager $scheduleManager */
|
12 |
+
|
13 |
+
$scheduleManager->deleteAll();
|
14 |
+
$collection = Mage::getModel('cron/schedule')->getCollection();
|
15 |
+
$this->assertCount(0, $collection);
|
16 |
+
|
17 |
+
$scheduleManager->generateSchedules();
|
18 |
+
$collection = Mage::getModel('cron/schedule')->getCollection(); /* @var $collection Mage_Cron_Model_Resource_Schedule_Collection */
|
19 |
+
$this->assertGreaterThan(0, $collection->count());
|
20 |
+
|
21 |
+
$scheduleManager->deleteAll();
|
22 |
+
$collection = Mage::getModel('cron/schedule')->getCollection();
|
23 |
+
$this->assertCount(0, $collection);
|
24 |
+
}
|
25 |
+
|
26 |
+
/**
|
27 |
+
* @param $runCronCallBack callable
|
28 |
+
* @dataProvider runCronProvider
|
29 |
+
* @test
|
30 |
+
*/
|
31 |
+
public function scheduleJobAndRunCron($runCronCallBack)
|
32 |
+
{
|
33 |
+
// delete all schedules
|
34 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager'); /* @var Aoe_Scheduler_Model_ScheduleManager $scheduleManager */
|
35 |
+
$scheduleManager->deleteAll();
|
36 |
+
|
37 |
+
// fake schedule generation to avoid it to be generated on the next run:
|
38 |
+
Mage::app()->saveCache(time(), Mage_Cron_Model_Observer::CACHE_KEY_LAST_SCHEDULE_GENERATE_AT, array('crontab'), null);
|
39 |
+
|
40 |
+
$schedule = Mage::getModel('cron/schedule'); /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
41 |
+
$jobCode = 'aoescheduler_testtask';
|
42 |
+
$schedule->setJobCode($jobCode);
|
43 |
+
$schedule->schedule();
|
44 |
+
$schedule->setScheduledReason('unittest');
|
45 |
+
$schedule->save();
|
46 |
+
$scheduleId = $schedule->getId();
|
47 |
+
$this->assertGreaterThan(0, intval($scheduleId));
|
48 |
+
|
49 |
+
// check for pending status
|
50 |
+
$loadedSchedule = Mage::getModel('cron/schedule')->load($scheduleId); /* @var Aoe_Scheduler_Model_Schedule $loadedSchedule */
|
51 |
+
$this->assertEquals($scheduleId, $loadedSchedule->getId());
|
52 |
+
$this->assertEquals(Aoe_Scheduler_Model_Schedule::STATUS_PENDING, $loadedSchedule->getStatus());
|
53 |
+
|
54 |
+
// run cron
|
55 |
+
$runCronCallBack();
|
56 |
+
|
57 |
+
// check for success status
|
58 |
+
$loadedSchedule = Mage::getModel('cron/schedule')->load($scheduleId); /* @var Aoe_Scheduler_Model_Schedule $loadedSchedule */
|
59 |
+
$this->assertEquals($scheduleId, $loadedSchedule->getId());
|
60 |
+
$this->assertEquals(Aoe_Scheduler_Model_Schedule::STATUS_SUCCESS, $loadedSchedule->getStatus());
|
61 |
+
}
|
62 |
+
|
63 |
+
/**
|
64 |
+
* Provider for a callback that executed a cron run
|
65 |
+
*
|
66 |
+
* @return array
|
67 |
+
*/
|
68 |
+
public function runCronProvider()
|
69 |
+
{
|
70 |
+
return array(
|
71 |
+
array(function () {
|
72 |
+
// trigger dispatch
|
73 |
+
$observer = Mage::getModel('aoe_scheduler/observer'); /* @var $observers Aoe_Scheduler_Model_Observer */
|
74 |
+
$observer->dispatch(new Varien_Event_Observer());
|
75 |
+
}),
|
76 |
+
array(function () {
|
77 |
+
shell_exec('/usr/bin/php ' . Mage::getBaseDir() . '/cron.php');
|
78 |
+
shell_exec('cd ' . Mage::getBaseDir() . '/shell && /usr/bin/php scheduler.php --action wait');
|
79 |
+
}),
|
80 |
+
array(function () {
|
81 |
+
shell_exec('/bin/sh ' . Mage::getBaseDir() . '/cron.sh');
|
82 |
+
shell_exec('cd ' . Mage::getBaseDir() . '/shell && /usr/bin/php scheduler.php --action wait');
|
83 |
+
}),
|
84 |
+
array(function () {
|
85 |
+
shell_exec('/bin/sh ' . Mage::getBaseDir() . '/cron.sh cron.php -mdefault 1');
|
86 |
+
shell_exec('cd ' . Mage::getBaseDir() . '/shell && /usr/bin/php scheduler.php --action wait');
|
87 |
+
}),
|
88 |
+
array(function () {
|
89 |
+
shell_exec('cd ' . Mage::getBaseDir() . '/shell && /usr/bin/php scheduler.php --action cron --mode default');
|
90 |
+
shell_exec('cd ' . Mage::getBaseDir() . '/shell && /usr/bin/php scheduler.php --action wait');
|
91 |
+
}),
|
92 |
+
array(function () {
|
93 |
+
shell_exec('/bin/bash ' . Mage::getBaseDir() . '/scheduler_cron.sh');
|
94 |
+
shell_exec('cd ' . Mage::getBaseDir() . '/shell && /usr/bin/php scheduler.php --action wait');
|
95 |
+
}),
|
96 |
+
array(function () {
|
97 |
+
shell_exec('/bin/bash ' . Mage::getBaseDir() . '/scheduler_cron.sh --mode default');
|
98 |
+
shell_exec('cd ' . Mage::getBaseDir() . '/shell && /usr/bin/php scheduler.php --action wait');
|
99 |
+
})
|
100 |
+
);
|
101 |
+
}
|
102 |
+
}
|
app/code/community/Aoe/Scheduler/controllers/Adminhtml/AbstractController.php
DELETED
@@ -1,66 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
/**
|
4 |
-
* Abstract controller
|
5 |
-
*
|
6 |
-
* @author Fabrizio Branca <fabrizio.branca@aoemedia.de>
|
7 |
-
*/
|
8 |
-
abstract class Aoe_Scheduler_Adminhtml_AbstractController extends Mage_Adminhtml_Controller_Action {
|
9 |
-
|
10 |
-
/**
|
11 |
-
* Index action (display grid)
|
12 |
-
*
|
13 |
-
* @return void
|
14 |
-
*/
|
15 |
-
public function indexAction() {
|
16 |
-
$this->checkHeartbeat();
|
17 |
-
|
18 |
-
$this->loadLayout();
|
19 |
-
|
20 |
-
$this->_setActiveMenu('system');
|
21 |
-
$this->renderLayout();
|
22 |
-
}
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
/**
|
27 |
-
* Check heartbeat
|
28 |
-
*/
|
29 |
-
protected function checkHeartbeat() {
|
30 |
-
if (!Mage::helper('aoe_scheduler')->isDisabled('aoescheduler_heartbeat')) {
|
31 |
-
$lastHeartbeat = Mage::helper('aoe_scheduler')->getLastHeartbeat();
|
32 |
-
if ($lastHeartbeat === false) {
|
33 |
-
// no heartbeat task found
|
34 |
-
$this->_getSession()->addError('No heartbeat task found. Check if cron is configured correctly.');
|
35 |
-
} else {
|
36 |
-
$timespan = Mage::helper('aoe_scheduler')->dateDiff($lastHeartbeat);
|
37 |
-
if ($timespan <= 5 * 60) {
|
38 |
-
$this->_getSession()->addSuccess(sprintf('Scheduler is working. (Last execution: %s minute(s) ago)', round($timespan/60)));
|
39 |
-
} elseif ($timespan > 5 * 60 && $timespan <= 60 * 60 ) {
|
40 |
-
// heartbeat wasn't executed in the last 5 minutes. Heartbeat schedule could be modified to not run every five minutes!
|
41 |
-
$this->_getSession()->addNotice(sprintf('Last heartbeat is older than %s minutes.', round($timespan/60)));
|
42 |
-
} else {
|
43 |
-
// everything ok
|
44 |
-
$this->_getSession()->addError('Last heartbeat is older than one hour. Please check your settings and your configuration!');
|
45 |
-
}
|
46 |
-
}
|
47 |
-
|
48 |
-
}
|
49 |
-
}
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
/**
|
54 |
-
* Generate schedule now
|
55 |
-
*
|
56 |
-
* @return void
|
57 |
-
*/
|
58 |
-
public function generateScheduleAction() {
|
59 |
-
$observer = Mage::getModel('cron/observer'); /* @var $observer Mage_Cron_Model_Observer */
|
60 |
-
$observer->generate();
|
61 |
-
Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Generated schedule'));
|
62 |
-
$this->_redirect('*/*/index');
|
63 |
-
}
|
64 |
-
|
65 |
-
}
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/code/community/Aoe/Scheduler/controllers/Adminhtml/CronController.php
DELETED
@@ -1,112 +0,0 @@
|
|
1 |
-
<?php
|
2 |
-
|
3 |
-
require_once Mage::getModuleDir('controllers', 'Aoe_Scheduler').'/Adminhtml/AbstractController.php';
|
4 |
-
|
5 |
-
/**
|
6 |
-
* Cron controller
|
7 |
-
*
|
8 |
-
* @author Fabrizio Branca <fabrizio.branca@aoemedia.de>
|
9 |
-
*/
|
10 |
-
class Aoe_Scheduler_Adminhtml_CronController extends Aoe_Scheduler_Adminhtml_AbstractController {
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
/**
|
15 |
-
* Mass action: disable
|
16 |
-
*
|
17 |
-
* @return void
|
18 |
-
*/
|
19 |
-
public function disableAction() {
|
20 |
-
$codes = $this->getRequest()->getParam('codes');
|
21 |
-
$disabledCrons = Mage::helper('aoe_scheduler')->trimExplode(',', Mage::getStoreConfig('system/cron/disabled_crons'), true);
|
22 |
-
foreach ($codes as $code) {
|
23 |
-
if (!in_array($code, $disabledCrons)) {
|
24 |
-
$disabledCrons[] = $code;
|
25 |
-
Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Disabled "%s"', $code));
|
26 |
-
}
|
27 |
-
}
|
28 |
-
Mage::getModel('core/config')->saveConfig('system/cron/disabled_crons/', implode(',', $disabledCrons));
|
29 |
-
Mage::app()->getCache()->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array(Mage_Core_Model_Config::CACHE_TAG));
|
30 |
-
$this->_redirect('*/*/index');
|
31 |
-
}
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
/**
|
36 |
-
* Mass action: enable
|
37 |
-
*
|
38 |
-
* @return void
|
39 |
-
*/
|
40 |
-
public function enableAction() {
|
41 |
-
$codes = $this->getRequest()->getParam('codes');
|
42 |
-
$disabledCrons = Mage::helper('aoe_scheduler')->trimExplode(',', Mage::getStoreConfig('system/cron/disabled_crons'), true);
|
43 |
-
foreach ($codes as $key => $code) {
|
44 |
-
if (in_array($code, $disabledCrons)) {
|
45 |
-
unset($disabledCrons[array_search($code, $disabledCrons)]);
|
46 |
-
Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Enabled "%s"', $code));
|
47 |
-
}
|
48 |
-
}
|
49 |
-
Mage::getModel('core/config')->saveConfig('system/cron/disabled_crons/', implode(',', $disabledCrons));
|
50 |
-
Mage::app()->getCache()->clean(Zend_Cache::CLEANING_MODE_MATCHING_TAG, array(Mage_Core_Model_Config::CACHE_TAG));
|
51 |
-
$this->_redirect('*/*/index');
|
52 |
-
}
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
/**
|
58 |
-
* Mass action: schedule now
|
59 |
-
*
|
60 |
-
* @return void
|
61 |
-
*/
|
62 |
-
public function scheduleNowAction() {
|
63 |
-
$codes = $this->getRequest()->getParam('codes');
|
64 |
-
if (is_array($codes)) {
|
65 |
-
foreach ($codes as $key) {
|
66 |
-
Mage::getModel('cron/schedule') /* @var Aoe_Scheduler_Model_Schedule */
|
67 |
-
->setJobCode($key)
|
68 |
-
->schedule()
|
69 |
-
->save();
|
70 |
-
Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Scheduled "%s"', $key));
|
71 |
-
}
|
72 |
-
}
|
73 |
-
$this->_redirect('*/*/index');
|
74 |
-
}
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
/**
|
79 |
-
* Mass action: run now
|
80 |
-
*
|
81 |
-
* @return void
|
82 |
-
*/
|
83 |
-
public function runNowAction() {
|
84 |
-
$codes = $this->getRequest()->getParam('codes');
|
85 |
-
if (is_array($codes)) {
|
86 |
-
foreach ($codes as $key) {
|
87 |
-
$schedule = Mage::getModel('cron/schedule') /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
88 |
-
->setJobCode($key)
|
89 |
-
->runNow(false) // without trying to lock the job
|
90 |
-
->save();
|
91 |
-
|
92 |
-
$messages = $schedule->getMessages();
|
93 |
-
|
94 |
-
if ($schedule->getStatus() == Mage_Cron_Model_Schedule::STATUS_SUCCESS) {
|
95 |
-
Mage::getSingleton('adminhtml/session')->addSuccess($this->__('Ran "%s" (Duration: %s sec)', $key, intval($schedule->getDuration())));
|
96 |
-
if ($messages) {
|
97 |
-
Mage::getSingleton('adminhtml/session')->addSuccess($this->__('"%s" messages:<pre>%s</pre>', $key, $messages));
|
98 |
-
}
|
99 |
-
} else {
|
100 |
-
Mage::getSingleton('adminhtml/session')->addError($this->__('Error while running "%s"', $key));
|
101 |
-
if ($messages) {
|
102 |
-
Mage::getSingleton('adminhtml/session')->addError($this->__('"%s" messages:<pre>%s</pre>', $key, $messages));
|
103 |
-
}
|
104 |
-
}
|
105 |
-
|
106 |
-
}
|
107 |
-
}
|
108 |
-
$this->_redirect('*/*/index');
|
109 |
-
}
|
110 |
-
|
111 |
-
}
|
112 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
app/code/community/Aoe/Scheduler/controllers/Adminhtml/InstructionsController.php
ADDED
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Instructions controller
|
4 |
+
*
|
5 |
+
* @author Fabrizio Branca
|
6 |
+
*/
|
7 |
+
class Aoe_Scheduler_Adminhtml_InstructionsController extends Aoe_Scheduler_Controller_AbstractController
|
8 |
+
{
|
9 |
+
|
10 |
+
/**
|
11 |
+
* Acl checking
|
12 |
+
*
|
13 |
+
* @return bool
|
14 |
+
*/
|
15 |
+
protected function _isAllowed()
|
16 |
+
{
|
17 |
+
return Mage::getSingleton('admin/session')->isAllowed('system/aoe_scheduler/aoe_scheduler_instructions');
|
18 |
+
}
|
19 |
+
}
|
app/code/community/Aoe/Scheduler/controllers/Adminhtml/JobController.php
ADDED
@@ -0,0 +1,267 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/**
|
3 |
+
* Cron controller
|
4 |
+
*
|
5 |
+
* @author Fabrizio Branca
|
6 |
+
*/
|
7 |
+
class Aoe_Scheduler_Adminhtml_JobController extends Aoe_Scheduler_Controller_AbstractController
|
8 |
+
{
|
9 |
+
/**
|
10 |
+
* Mass action: disable
|
11 |
+
*
|
12 |
+
* @return void
|
13 |
+
*/
|
14 |
+
public function disableAction()
|
15 |
+
{
|
16 |
+
$codes = $this->getMassActionCodes();
|
17 |
+
foreach ($codes as $code) {
|
18 |
+
/** @var Aoe_Scheduler_Model_Job $job */
|
19 |
+
$job = Mage::getModel('aoe_scheduler/job')->load($code);
|
20 |
+
if ($job->getJobCode() && $job->getIsActive()) {
|
21 |
+
$job->setIsActive(false)->save();
|
22 |
+
$this->_getSession()->addSuccess($this->__('Disabled "%s"', $code));
|
23 |
+
|
24 |
+
/* @var Aoe_Scheduler_Model_ScheduleManager $scheduleManager */
|
25 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager');
|
26 |
+
$scheduleManager->flushSchedules($job->getJobCode());
|
27 |
+
$this->_getSession()->addNotice($this->__('Pending schedules for "%s" have been flushed.', $job->getJobCode()));
|
28 |
+
}
|
29 |
+
}
|
30 |
+
$this->_redirect('*/*/index');
|
31 |
+
}
|
32 |
+
|
33 |
+
/**
|
34 |
+
* Mass action: enable
|
35 |
+
*
|
36 |
+
* @return void
|
37 |
+
*/
|
38 |
+
public function enableAction()
|
39 |
+
{
|
40 |
+
$codes = $this->getMassActionCodes();
|
41 |
+
foreach ($codes as $code) {
|
42 |
+
/** @var Aoe_Scheduler_Model_Job $job */
|
43 |
+
$job = Mage::getModel('aoe_scheduler/job')->load($code);
|
44 |
+
if ($job->getJobCode() && !$job->getIsActive()) {
|
45 |
+
$job->setIsActive(true)->save();
|
46 |
+
$this->_getSession()->addSuccess($this->__('Enabled "%s"', $code));
|
47 |
+
|
48 |
+
/* @var Aoe_Scheduler_Model_ScheduleManager $scheduleManager */
|
49 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager');
|
50 |
+
$scheduleManager->generateSchedulesForJob($job);
|
51 |
+
$this->_getSession()->addNotice($this->__('Job "%s" has been scheduled.', $job->getJobCode()));
|
52 |
+
}
|
53 |
+
}
|
54 |
+
$this->_redirect('*/*/index');
|
55 |
+
}
|
56 |
+
|
57 |
+
/**
|
58 |
+
* Mass action: schedule now
|
59 |
+
*
|
60 |
+
* @return void
|
61 |
+
*/
|
62 |
+
public function scheduleNowAction()
|
63 |
+
{
|
64 |
+
$codes = $this->getMassActionCodes();
|
65 |
+
foreach ($codes as $key) {
|
66 |
+
Mage::getModel('cron/schedule')
|
67 |
+
->setJobCode($key)
|
68 |
+
->setScheduledReason(Aoe_Scheduler_Model_Schedule::REASON_SCHEDULENOW_WEB)
|
69 |
+
->schedule()
|
70 |
+
->save();
|
71 |
+
|
72 |
+
$this->_getSession()->addSuccess($this->__('Job "%s" has been scheduled.', $key));
|
73 |
+
}
|
74 |
+
$this->_redirect('*/*/index');
|
75 |
+
}
|
76 |
+
|
77 |
+
/**
|
78 |
+
* Mass action: run now
|
79 |
+
*
|
80 |
+
* @return void
|
81 |
+
*/
|
82 |
+
public function runNowAction()
|
83 |
+
{
|
84 |
+
if (!Mage::getStoreConfig('system/cron/enableRunNow')) {
|
85 |
+
Mage::throwException("'Run now' disabled by configuration (system/cron/enableRunNow)");
|
86 |
+
}
|
87 |
+
$codes = $this->getMassActionCodes();
|
88 |
+
foreach ($codes as $key) {
|
89 |
+
$schedule = Mage::getModel('cron/schedule')
|
90 |
+
->setJobCode($key)
|
91 |
+
->setScheduledReason(Aoe_Scheduler_Model_Schedule::REASON_RUNNOW_WEB)
|
92 |
+
->runNow(false)// without trying to lock the job
|
93 |
+
->save();
|
94 |
+
|
95 |
+
$messages = $schedule->getMessages();
|
96 |
+
|
97 |
+
if (in_array($schedule->getStatus(), array(Aoe_Scheduler_Model_Schedule::STATUS_SUCCESS, Aoe_Scheduler_Model_Schedule::STATUS_DIDNTDOANYTHING))) {
|
98 |
+
$this->_getSession()->addSuccess($this->__('Ran "%s" (Duration: %s sec)', $key, intval($schedule->getDuration())));
|
99 |
+
if ($messages) {
|
100 |
+
$this->_getSession()->addSuccess($this->__('"%s" messages:<pre>%s</pre>', $key, $messages));
|
101 |
+
}
|
102 |
+
} else {
|
103 |
+
$this->_getSession()->addError($this->__('Error while running "%s"', $key));
|
104 |
+
if ($messages) {
|
105 |
+
$this->_getSession()->addError($this->__('"%s" messages:<pre>%s</pre>', $key, $messages));
|
106 |
+
}
|
107 |
+
}
|
108 |
+
}
|
109 |
+
$this->_redirect('*/*/index');
|
110 |
+
}
|
111 |
+
|
112 |
+
/**
|
113 |
+
* Init job instance and set it to registry
|
114 |
+
*
|
115 |
+
* @return Aoe_Scheduler_Model_Job
|
116 |
+
*/
|
117 |
+
protected function _initJob()
|
118 |
+
{
|
119 |
+
$jobCode = $this->getRequest()->getParam('job_code', null);
|
120 |
+
$job = Mage::getModel('aoe_scheduler/job')->load($jobCode);
|
121 |
+
Mage::register('current_job_instance', $job);
|
122 |
+
return $job;
|
123 |
+
}
|
124 |
+
|
125 |
+
protected function getMassActionCodes($key = 'codes')
|
126 |
+
{
|
127 |
+
$codes = $this->getRequest()->getParam($key);
|
128 |
+
if (!is_array($codes)) {
|
129 |
+
return array();
|
130 |
+
}
|
131 |
+
$allowedCodes = Mage::getSingleton('aoe_scheduler/job')->getResource()->getJobCodes();
|
132 |
+
$codes = array_intersect(array_unique(array_filter(array_map('trim', $codes))), $allowedCodes);
|
133 |
+
return $codes;
|
134 |
+
}
|
135 |
+
|
136 |
+
/**
|
137 |
+
* New cron (forward to edit action)
|
138 |
+
*/
|
139 |
+
public function newAction()
|
140 |
+
{
|
141 |
+
$this->_forward('edit');
|
142 |
+
}
|
143 |
+
|
144 |
+
/**
|
145 |
+
* Edit cron action
|
146 |
+
*/
|
147 |
+
public function editAction()
|
148 |
+
{
|
149 |
+
$this->_initJob();
|
150 |
+
$this->loadLayout();
|
151 |
+
$this->renderLayout();
|
152 |
+
}
|
153 |
+
|
154 |
+
protected function _filterPostData($data)
|
155 |
+
{
|
156 |
+
return $data;
|
157 |
+
}
|
158 |
+
|
159 |
+
protected function _validatePostData($data)
|
160 |
+
{
|
161 |
+
try {
|
162 |
+
/* @var Aoe_Scheduler_Helper_Data $helper */
|
163 |
+
$helper = Mage::helper('aoe_scheduler');
|
164 |
+
$helper->getCallBack($data['run_model']);
|
165 |
+
if (!empty($data['schedule_cron_expr'])) {
|
166 |
+
if (!$helper->validateCronExpression($data['schedule_cron_expr'])) {
|
167 |
+
Mage::throwException("Invalid cron expression");
|
168 |
+
}
|
169 |
+
}
|
170 |
+
} catch (Exception $e) {
|
171 |
+
$this->_getSession()->addError($e->getMessage());
|
172 |
+
return false;
|
173 |
+
}
|
174 |
+
// TODO: implement!
|
175 |
+
return true;
|
176 |
+
}
|
177 |
+
|
178 |
+
/**
|
179 |
+
* Save action
|
180 |
+
*
|
181 |
+
*/
|
182 |
+
public function saveAction()
|
183 |
+
{
|
184 |
+
if ($data = $this->getRequest()->getPost()) {
|
185 |
+
$data = $this->_filterPostData($data);
|
186 |
+
$job = $this->_initJob();
|
187 |
+
$job->addData($data);
|
188 |
+
//validating
|
189 |
+
if (!$this->_validatePostData($data)) {
|
190 |
+
$this->_redirect('*/*/edit', array('job_code' => $job->getJobCode(), '_current' => true));
|
191 |
+
return;
|
192 |
+
}
|
193 |
+
|
194 |
+
try {
|
195 |
+
// save the data
|
196 |
+
$job->save();
|
197 |
+
|
198 |
+
// display success message
|
199 |
+
$this->_getSession()->addSuccess(
|
200 |
+
Mage::helper('aoe_scheduler')->__('The job has been saved.')
|
201 |
+
);
|
202 |
+
// clear previously saved data from session
|
203 |
+
$this->_getSession()->setFormData(false);
|
204 |
+
// check if 'Save and Continue'
|
205 |
+
if ($this->getRequest()->getParam('back', false)) {
|
206 |
+
$this->_redirect('*/*/edit', array('job_code' => $job->getJobCode(), '_current' => true));
|
207 |
+
return;
|
208 |
+
}
|
209 |
+
|
210 |
+
/* @var $scheduleManager Aoe_Scheduler_Model_ScheduleManager */
|
211 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager');
|
212 |
+
$scheduleManager->flushSchedules($job->getJobCode());
|
213 |
+
$scheduleManager->generateSchedulesForJob($job);
|
214 |
+
$this->_getSession()->addNotice($this->__('Pending schedules for "%s" have been flushed.', $job->getJobCode()));
|
215 |
+
$this->_getSession()->addNotice($this->__('Job "%s" has been scheduled.', $job->getJobCode()));
|
216 |
+
|
217 |
+
// go to grid
|
218 |
+
$this->_redirect('*/*');
|
219 |
+
return;
|
220 |
+
} catch (Mage_Core_Exception $e) {
|
221 |
+
Mage::logException($e);
|
222 |
+
$this->_getSession()->addError($e->getMessage());
|
223 |
+
} catch (Exception $e) {
|
224 |
+
Mage::logException($e);
|
225 |
+
$this->_getSession()->addError($this->__('An error occurred during saving a job: %s', $e->getMessage()));
|
226 |
+
}
|
227 |
+
|
228 |
+
$this->_getSession()->setFormData($data);
|
229 |
+
$this->_redirect('*/*/edit', array('job_code' => $this->getRequest()->getParam('job_code')));
|
230 |
+
return;
|
231 |
+
}
|
232 |
+
|
233 |
+
$this->_redirect('*/*/', array('_current' => true));
|
234 |
+
}
|
235 |
+
|
236 |
+
/**
|
237 |
+
* Delete Action
|
238 |
+
*
|
239 |
+
*/
|
240 |
+
public function deleteAction()
|
241 |
+
{
|
242 |
+
$job = $this->_initJob();
|
243 |
+
try {
|
244 |
+
$job->delete();
|
245 |
+
$this->_getSession()->addSuccess($this->__('The job has been deleted.'));
|
246 |
+
|
247 |
+
/* @var Aoe_Scheduler_Model_ScheduleManager $scheduleManager */
|
248 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager');
|
249 |
+
$scheduleManager->flushSchedules($job->getJobCode());
|
250 |
+
$this->_getSession()->addNotice($this->__('Pending schedules for "%s" have been flushed.', $job->getJobCode()));
|
251 |
+
} catch (Exception $e) {
|
252 |
+
$this->_getSession()->addError($e->getMessage());
|
253 |
+
}
|
254 |
+
$this->_redirect('*/*/');
|
255 |
+
return;
|
256 |
+
}
|
257 |
+
|
258 |
+
/**
|
259 |
+
* ACL checking
|
260 |
+
*
|
261 |
+
* @return bool
|
262 |
+
*/
|
263 |
+
protected function _isAllowed()
|
264 |
+
{
|
265 |
+
return Mage::getSingleton('admin/session')->isAllowed('system/aoe_scheduler/aoe_scheduler_cron');
|
266 |
+
}
|
267 |
+
}
|
app/code/community/Aoe/Scheduler/controllers/Adminhtml/SchedulerController.php
CHANGED
@@ -1,28 +1,59 @@
|
|
1 |
<?php
|
2 |
-
|
3 |
-
require_once Mage::getModuleDir('controllers', 'Aoe_Scheduler').'/Adminhtml/AbstractController.php';
|
4 |
-
|
5 |
/**
|
6 |
* Scheduler controller
|
7 |
*
|
8 |
-
* @author Fabrizio Branca
|
9 |
*/
|
10 |
-
class Aoe_Scheduler_Adminhtml_SchedulerController extends
|
11 |
-
|
12 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
13 |
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
28 |
}
|
1 |
<?php
|
|
|
|
|
|
|
2 |
/**
|
3 |
* Scheduler controller
|
4 |
*
|
5 |
+
* @author Fabrizio Branca
|
6 |
*/
|
7 |
+
class Aoe_Scheduler_Adminhtml_SchedulerController extends Aoe_Scheduler_Controller_AbstractController
|
8 |
+
{
|
9 |
|
10 |
+
/**
|
11 |
+
* Mass action: delete
|
12 |
+
*
|
13 |
+
* @return void
|
14 |
+
*/
|
15 |
+
public function deleteAction()
|
16 |
+
{
|
17 |
+
$ids = $this->getRequest()->getParam('schedule_ids');
|
18 |
+
foreach ($ids as $id) {
|
19 |
+
Mage::getModel('cron/schedule')->load($id)
|
20 |
+
->delete();
|
21 |
+
}
|
22 |
+
$message = $this->__('Deleted task(s) "%s"', implode(', ', $ids));
|
23 |
+
$this->_getSession()->addSuccess($message);
|
24 |
+
if ($logFile = Mage::getStoreConfig('system/cron/logFile')) {
|
25 |
+
Mage::log($message, null, $logFile);
|
26 |
+
}
|
27 |
+
$this->_redirect('*/*/index');
|
28 |
+
}
|
29 |
|
30 |
+
/**
|
31 |
+
* Mass action: kill
|
32 |
+
*
|
33 |
+
* @return void
|
34 |
+
*/
|
35 |
+
public function killAction()
|
36 |
+
{
|
37 |
+
$ids = $this->getRequest()->getParam('schedule_ids');
|
38 |
+
foreach ($ids as $id) {
|
39 |
+
$schedule = Mage::getModel('cron/schedule'); /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
40 |
+
$schedule->load($id)->requestKill();
|
41 |
+
}
|
42 |
+
$message = $this->__('Kill requests saved for task(s) "%s" (will be killed via cron)', implode(', ', $ids));
|
43 |
+
$this->_getSession()->addSuccess($message);
|
44 |
+
if ($logFile = Mage::getStoreConfig('system/cron/logFile')) {
|
45 |
+
Mage::log($message, null, $logFile);
|
46 |
+
}
|
47 |
+
$this->_redirect('*/*/index');
|
48 |
+
}
|
49 |
|
50 |
+
/**
|
51 |
+
* Acl checking
|
52 |
+
*
|
53 |
+
* @return bool
|
54 |
+
*/
|
55 |
+
protected function _isAllowed()
|
56 |
+
{
|
57 |
+
return Mage::getSingleton('admin/session')->isAllowed('system/aoe_scheduler/aoe_scheduler_scheduler');
|
58 |
+
}
|
59 |
}
|
app/code/community/Aoe/Scheduler/controllers/Adminhtml/TimelineController.php
CHANGED
@@ -1,12 +1,19 @@
|
|
1 |
<?php
|
2 |
-
|
3 |
-
require_once Mage::getModuleDir('controllers', 'Aoe_Scheduler').'/Adminhtml/AbstractController.php';
|
4 |
-
|
5 |
/**
|
6 |
* Timeline controller
|
7 |
*
|
8 |
-
* @author Fabrizio Branca
|
9 |
*/
|
10 |
-
class Aoe_Scheduler_Adminhtml_TimelineController extends
|
|
|
11 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
12 |
}
|
1 |
<?php
|
|
|
|
|
|
|
2 |
/**
|
3 |
* Timeline controller
|
4 |
*
|
5 |
+
* @author Fabrizio Branca
|
6 |
*/
|
7 |
+
class Aoe_Scheduler_Adminhtml_TimelineController extends Aoe_Scheduler_Controller_AbstractController
|
8 |
+
{
|
9 |
|
10 |
+
/**
|
11 |
+
* Acl checking
|
12 |
+
*
|
13 |
+
* @return bool
|
14 |
+
*/
|
15 |
+
protected function _isAllowed()
|
16 |
+
{
|
17 |
+
return Mage::getSingleton('admin/session')->isAllowed('system/aoe_scheduler/aoe_scheduler_timeline');
|
18 |
+
}
|
19 |
}
|
app/code/community/Aoe/Scheduler/data/aoescheduler_setup/data-upgrade-0.5.4-0.5.5.php
ADDED
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* @var $this Mage_Core_Model_Resource_Setup */
|
3 |
+
|
4 |
+
// Migrate to new settings
|
5 |
+
$node = Mage::getConfig()->getNode('default/system/cron/disabled_crons');
|
6 |
+
if ($node) {
|
7 |
+
$allowedCodes = Mage::getSingleton('aoe_scheduler/job')->getResource()->getJobCodes();
|
8 |
+
$codes = array_intersect(array_unique(array_filter(array_map('trim', explode(',', trim($node))))), $allowedCodes);
|
9 |
+
foreach ($codes as $code) {
|
10 |
+
$this->getConnection()->insertOnDuplicate(
|
11 |
+
$this->getTable('core/config_data'),
|
12 |
+
array(
|
13 |
+
'scope' => 'default',
|
14 |
+
'scope_id' => 0,
|
15 |
+
'path' => 'crontab/jobs/' . $code . '/is_active',
|
16 |
+
'value' => 0,
|
17 |
+
)
|
18 |
+
);
|
19 |
+
}
|
20 |
+
}
|
21 |
+
|
22 |
+
// Remove old config setting
|
23 |
+
$this->getConnection()->delete(
|
24 |
+
$this->getTable('core/config_data'),
|
25 |
+
array(
|
26 |
+
'scope = ?' => 'default',
|
27 |
+
'scope_id = ?' => 0,
|
28 |
+
'path = ?' => 'system/cron/disabled_crons'
|
29 |
+
)
|
30 |
+
);
|
app/code/community/Aoe/Scheduler/etc/adminhtml.xml
CHANGED
@@ -1,61 +1,69 @@
|
|
1 |
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
<config>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
61 |
</config>
|
1 |
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
<config>
|
3 |
+
<menu>
|
4 |
+
<system>
|
5 |
+
<children>
|
6 |
+
<aoe_scheduler translate="title">
|
7 |
+
<title>Scheduler</title>
|
8 |
+
<sort_order>1</sort_order>
|
9 |
+
<children>
|
10 |
+
<aoe_scheduler_cron translate="title">
|
11 |
+
<title>Job Configuration</title>
|
12 |
+
<sort_order>10</sort_order>
|
13 |
+
<action>adminhtml/job/index</action>
|
14 |
+
</aoe_scheduler_cron>
|
15 |
+
<aoe_scheduler_scheduler translate="title">
|
16 |
+
<title>List View</title>
|
17 |
+
<sort_order>20</sort_order>
|
18 |
+
<action>adminhtml/scheduler/index</action>
|
19 |
+
</aoe_scheduler_scheduler>
|
20 |
+
<aoe_scheduler_timeline translate="title">
|
21 |
+
<title>Timeline View</title>
|
22 |
+
<sort_order>30</sort_order>
|
23 |
+
<action>adminhtml/timeline/index</action>
|
24 |
+
</aoe_scheduler_timeline>
|
25 |
+
<aoe_scheduler_instructions translate="title">
|
26 |
+
<title>Instructions</title>
|
27 |
+
<sort_order>40</sort_order>
|
28 |
+
<action>adminhtml/instructions/index</action>
|
29 |
+
</aoe_scheduler_instructions>
|
30 |
+
</children>
|
31 |
+
</aoe_scheduler>
|
32 |
+
</children>
|
33 |
+
</system>
|
34 |
+
</menu>
|
35 |
+
<acl>
|
36 |
+
<resources>
|
37 |
+
<admin>
|
38 |
+
<children>
|
39 |
+
<system>
|
40 |
+
<children>
|
41 |
+
<aoe_scheduler translate="title" module="aoe_scheduler">
|
42 |
+
<title>AOE Scheduler</title>
|
43 |
+
<sort_order>1</sort_order>
|
44 |
+
<children>
|
45 |
+
<aoe_scheduler_cron translate="title">
|
46 |
+
<title>Schedule Configuration</title>
|
47 |
+
<sort_order>10</sort_order>
|
48 |
+
</aoe_scheduler_cron>
|
49 |
+
<aoe_scheduler_scheduler translate="title">
|
50 |
+
<title>List View</title>
|
51 |
+
<sort_order>20</sort_order>
|
52 |
+
</aoe_scheduler_scheduler>
|
53 |
+
<aoe_scheduler_timeline translate="title">
|
54 |
+
<title>Timeline View</title>
|
55 |
+
<sort_order>30</sort_order>
|
56 |
+
</aoe_scheduler_timeline>
|
57 |
+
<aoe_scheduler_instructions translate="title">
|
58 |
+
<title>Instructions</title>
|
59 |
+
<sort_order>40</sort_order>
|
60 |
+
</aoe_scheduler_instructions>
|
61 |
+
</children>
|
62 |
+
</aoe_scheduler>
|
63 |
+
</children>
|
64 |
+
</system>
|
65 |
+
</children>
|
66 |
+
</admin>
|
67 |
+
</resources>
|
68 |
+
</acl>
|
69 |
</config>
|
app/code/community/Aoe/Scheduler/etc/config.xml
CHANGED
@@ -1,105 +1,179 @@
|
|
1 |
<?xml version="1.0" ?>
|
2 |
<config>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
104 |
|
105 |
</config>
|
1 |
<?xml version="1.0" ?>
|
2 |
<config>
|
3 |
+
<modules>
|
4 |
+
<Aoe_Scheduler>
|
5 |
+
<version>1.3.0</version>
|
6 |
+
</Aoe_Scheduler>
|
7 |
+
</modules>
|
8 |
+
|
9 |
+
<global>
|
10 |
+
<blocks>
|
11 |
+
<aoe_scheduler>
|
12 |
+
<class>Aoe_Scheduler_Block</class>
|
13 |
+
</aoe_scheduler>
|
14 |
+
</blocks>
|
15 |
+
|
16 |
+
<helpers>
|
17 |
+
<aoe_scheduler>
|
18 |
+
<class>Aoe_Scheduler_Helper</class>
|
19 |
+
</aoe_scheduler>
|
20 |
+
</helpers>
|
21 |
+
|
22 |
+
<models>
|
23 |
+
<aoe_scheduler>
|
24 |
+
<class>Aoe_Scheduler_Model</class>
|
25 |
+
<resourceModel>aoe_scheduler_resource</resourceModel>
|
26 |
+
</aoe_scheduler>
|
27 |
+
|
28 |
+
<aoe_scheduler_resource>
|
29 |
+
<class>Aoe_Scheduler_Model_Resource</class>
|
30 |
+
</aoe_scheduler_resource>
|
31 |
+
|
32 |
+
<cron>
|
33 |
+
<rewrite>
|
34 |
+
<observer>Aoe_Scheduler_Model_Observer</observer>
|
35 |
+
<schedule>Aoe_Scheduler_Model_Schedule</schedule>
|
36 |
+
</rewrite>
|
37 |
+
</cron>
|
38 |
+
<cron_resource>
|
39 |
+
<rewrite>
|
40 |
+
<schedule_collection>Aoe_Scheduler_Model_Resource_Schedule_Collection</schedule_collection>
|
41 |
+
</rewrite>
|
42 |
+
</cron_resource>
|
43 |
+
</models>
|
44 |
+
|
45 |
+
<template>
|
46 |
+
<email>
|
47 |
+
<system_cron_error_email_template translate="label" module="aoe_scheduler">
|
48 |
+
<label>Cron error</label>
|
49 |
+
<file>aoe_scheduler/cron_error.html</file>
|
50 |
+
<type>text</type>
|
51 |
+
</system_cron_error_email_template>
|
52 |
+
</email>
|
53 |
+
</template>
|
54 |
+
|
55 |
+
<resources>
|
56 |
+
<aoescheduler_setup>
|
57 |
+
<setup>
|
58 |
+
<module>Aoe_Scheduler</module>
|
59 |
+
</setup>
|
60 |
+
<connection>
|
61 |
+
<use>core_setup</use>
|
62 |
+
</connection>
|
63 |
+
</aoescheduler_setup>
|
64 |
+
</resources>
|
65 |
+
|
66 |
+
<aoe_logviewer>
|
67 |
+
<logs>
|
68 |
+
<cron.log>
|
69 |
+
<label>cron.log</label>
|
70 |
+
<file_path>###LOGDIR###/cron.log</file_path>
|
71 |
+
<model>aoe_logviewer/log_file</model>
|
72 |
+
<sort_order>10</sort_order>
|
73 |
+
<!--<disabled>1</disabled>-->
|
74 |
+
<commands>
|
75 |
+
<tail>
|
76 |
+
<label>Last n lines (newest first)</label>
|
77 |
+
<command_string><![CDATA[tail -n %2$s '%1$s' | tac]]></command_string>
|
78 |
+
<model>aoe_logviewer/command_shell</model>
|
79 |
+
<sort_order>10</sort_order>
|
80 |
+
<!--<disabled>1</disabled>-->
|
81 |
+
</tail>
|
82 |
+
<tail_err>
|
83 |
+
<label>Last n error lines (newest first)</label>
|
84 |
+
<command_string><![CDATA[grep 'ERR (3)' '%1$s' | tail -n %2$s | tac]]></command_string>
|
85 |
+
<model>aoe_logviewer/command_shell</model>
|
86 |
+
<sort_order>20</sort_order>
|
87 |
+
</tail_err>
|
88 |
+
<duplicates>
|
89 |
+
<label>Find duplicates</label>
|
90 |
+
<command_string><![CDATA[cat '%1$s' | cut -c27- | sort | uniq -c | sort -rn | head -n %2$s]]></command_string>
|
91 |
+
<model>aoe_logviewer/command_shell</model>
|
92 |
+
<sort_order>30</sort_order>
|
93 |
+
</duplicates>
|
94 |
+
</commands>
|
95 |
+
</cron.log>
|
96 |
+
</logs>
|
97 |
+
</aoe_logviewer>
|
98 |
+
|
99 |
+
</global>
|
100 |
+
|
101 |
+
<admin>
|
102 |
+
<routers>
|
103 |
+
<adminhtml>
|
104 |
+
<args>
|
105 |
+
<modules>
|
106 |
+
<Aoe_Scheduler before="Mage_Adminhtml">Aoe_Scheduler_Adminhtml</Aoe_Scheduler>
|
107 |
+
</modules>
|
108 |
+
</args>
|
109 |
+
</adminhtml>
|
110 |
+
</routers>
|
111 |
+
</admin>
|
112 |
+
|
113 |
+
<adminhtml>
|
114 |
+
<translate>
|
115 |
+
<modules>
|
116 |
+
<aoe_scheduler>
|
117 |
+
<files>
|
118 |
+
<default>Aoe_Scheduler.csv</default>
|
119 |
+
</files>
|
120 |
+
</aoe_scheduler>
|
121 |
+
</modules>
|
122 |
+
</translate>
|
123 |
+
<layout>
|
124 |
+
<updates>
|
125 |
+
<aoe_scheduler>
|
126 |
+
<file>aoe_scheduler/aoe_scheduler.xml</file>
|
127 |
+
</aoe_scheduler>
|
128 |
+
</updates>
|
129 |
+
</layout>
|
130 |
+
</adminhtml>
|
131 |
+
|
132 |
+
<crontab>
|
133 |
+
<jobs>
|
134 |
+
<aoescheduler_testtask>
|
135 |
+
<!--<schedule><cron_expr>*/5 * * * *</cron_expr></schedule>-->
|
136 |
+
<run>
|
137 |
+
<model>aoe_scheduler/task_test::run</model>
|
138 |
+
</run>
|
139 |
+
</aoescheduler_testtask>
|
140 |
+
|
141 |
+
<aoescheduler_heartbeat>
|
142 |
+
<schedule>
|
143 |
+
<config_path>system/cron/scheduler_cron_expr_heartbeat</config_path>
|
144 |
+
</schedule>
|
145 |
+
<run>
|
146 |
+
<model>aoe_scheduler/task_heartbeat::run</model>
|
147 |
+
</run>
|
148 |
+
<groups>all</groups>
|
149 |
+
</aoescheduler_heartbeat>
|
150 |
+
</jobs>
|
151 |
+
</crontab>
|
152 |
+
|
153 |
+
<default>
|
154 |
+
<system>
|
155 |
+
<cron>
|
156 |
+
<enable>1</enable>
|
157 |
+
<mark_as_error_after>15</mark_as_error_after>
|
158 |
+
<scheduler_cron_expr_heartbeat>*/5 * * * *</scheduler_cron_expr_heartbeat>
|
159 |
+
<error_email_identity>general</error_email_identity>
|
160 |
+
<error_email_template>system_cron_error_email_template</error_email_template>
|
161 |
+
<logFile>cron.log</logFile>
|
162 |
+
<enableRunNow>0</enableRunNow>
|
163 |
+
<enableErrorLog>0</enableErrorLog>
|
164 |
+
<errorLevel>-1</errorLevel>
|
165 |
+
<errorLogFilename>###JOBCODE###.log</errorLogFilename>
|
166 |
+
<listCodeFilterType>select</listCodeFilterType>
|
167 |
+
</cron>
|
168 |
+
</system>
|
169 |
+
</default>
|
170 |
+
|
171 |
+
<phpunit>
|
172 |
+
<suite>
|
173 |
+
<modules>
|
174 |
+
<Aoe_Scheduler/>
|
175 |
+
</modules>
|
176 |
+
</suite>
|
177 |
+
</phpunit>
|
178 |
|
179 |
</config>
|
app/code/community/Aoe/Scheduler/etc/system.xml
CHANGED
@@ -1,67 +1,154 @@
|
|
1 |
<?xml version="1.0"?>
|
2 |
<config>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
<disabled_crons translate="label">
|
9 |
-
<label>Disabled cron tasks</label>
|
10 |
-
<frontend_type>text</frontend_type>
|
11 |
-
<comment><![CDATA[Do not edit this field manually unless you know what you do. This field is populated by the "disable mass action" of Aoe_Scheduler.]]></comment>
|
12 |
-
<sort_order>100</sort_order>
|
13 |
-
<show_in_default>1</show_in_default>
|
14 |
-
<show_in_website>0</show_in_website>
|
15 |
-
<show_in_store>0</show_in_store>
|
16 |
-
</disabled_crons>
|
17 |
-
<max_running_time translate="label">
|
18 |
-
<label>Mark jobs as failed after</label>
|
19 |
-
<comment><![CDATA[Maximum time in minutes. The will only marked as failed. It won't be killed (if it should actually be still running).]]></comment>
|
20 |
-
<frontend_type>text</frontend_type>
|
21 |
-
<sort_order>110</sort_order>
|
22 |
-
<show_in_default>1</show_in_default>
|
23 |
-
<show_in_website>0</show_in_website>
|
24 |
-
<show_in_store>0</show_in_store>
|
25 |
-
</max_running_time>
|
26 |
-
<scheduler_cron_expr_heartbeat>
|
27 |
-
<label>Heartbeat task schedule (cron syntax)</label>
|
28 |
-
<frontend_type>text</frontend_type>
|
29 |
-
<sort_order>120</sort_order>
|
30 |
-
<show_in_default>1</show_in_default>
|
31 |
-
<show_in_website>0</show_in_website>
|
32 |
-
<show_in_store>0</show_in_store>
|
33 |
-
</scheduler_cron_expr_heartbeat>
|
34 |
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
<frontend_type>select</frontend_type>
|
46 |
-
<source_model>adminhtml/system_config_source_email_identity</source_model>
|
47 |
-
<sort_order>140</sort_order>
|
48 |
-
<show_in_default>1</show_in_default>
|
49 |
-
<show_in_website>0</show_in_website>
|
50 |
-
<show_in_store>0</show_in_store>
|
51 |
-
</error_email_identity>
|
52 |
-
<error_email_template translate="label">
|
53 |
-
<label>Error Email Template</label>
|
54 |
-
<frontend_type>select</frontend_type>
|
55 |
-
<source_model>adminhtml/system_config_source_email_template</source_model>
|
56 |
-
<sort_order>150</sort_order>
|
57 |
-
<show_in_default>1</show_in_default>
|
58 |
-
<show_in_website>0</show_in_website>
|
59 |
-
<show_in_store>0</show_in_store>
|
60 |
-
</error_email_template>
|
61 |
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
67 |
</config>
|
1 |
<?xml version="1.0"?>
|
2 |
<config>
|
3 |
+
<sections>
|
4 |
+
<system>
|
5 |
+
<groups>
|
6 |
+
<cron translate="label comment" module="cron">
|
7 |
+
<fields>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
|
9 |
+
<enable translate="label">
|
10 |
+
<label>Enable Scheduler</label>
|
11 |
+
<sort_order>1</sort_order>
|
12 |
+
<comment><![CDATA[If this is disabled no scheduled jobs will be executed]]></comment>
|
13 |
+
<show_in_default>1</show_in_default>
|
14 |
+
<frontend_type>select</frontend_type>
|
15 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
16 |
+
<show_in_website>0</show_in_website>
|
17 |
+
<show_in_store>0</show_in_store>
|
18 |
+
</enable>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
+
<mark_as_error_after translate="label">
|
21 |
+
<label>Mark job as failed after</label>
|
22 |
+
<comment><![CDATA[A job will be marked es failed after it hasn't been seen for this many minutes (in cases where the process cannot be checked)]]></comment>
|
23 |
+
<frontend_type>text</frontend_type>
|
24 |
+
<sort_order>110</sort_order>
|
25 |
+
<show_in_default>1</show_in_default>
|
26 |
+
<show_in_website>0</show_in_website>
|
27 |
+
<show_in_store>0</show_in_store>
|
28 |
+
</mark_as_error_after>
|
29 |
+
|
30 |
+
<max_job_runtime translate="label">
|
31 |
+
<label>Maximum job runtime</label>
|
32 |
+
<comment><![CDATA[If set a single job can't run longer than this time (in minutes). If the job is actually still running this process will be actively killed.]]></comment>
|
33 |
+
<frontend_type>text</frontend_type>
|
34 |
+
<sort_order>111</sort_order>
|
35 |
+
<show_in_default>1</show_in_default>
|
36 |
+
<show_in_website>0</show_in_website>
|
37 |
+
<show_in_store>0</show_in_store>
|
38 |
+
</max_job_runtime>
|
39 |
+
|
40 |
+
<scheduler_cron_expr_heartbeat>
|
41 |
+
<label>Heartbeat task schedule (cron syntax)</label>
|
42 |
+
<frontend_type>text</frontend_type>
|
43 |
+
<sort_order>120</sort_order>
|
44 |
+
<show_in_default>1</show_in_default>
|
45 |
+
<show_in_website>0</show_in_website>
|
46 |
+
<show_in_store>0</show_in_store>
|
47 |
+
</scheduler_cron_expr_heartbeat>
|
48 |
+
|
49 |
+
<error_email translate="label">
|
50 |
+
<label>Error Email Recipient</label>
|
51 |
+
<frontend_type>text</frontend_type>
|
52 |
+
<sort_order>130</sort_order>
|
53 |
+
<show_in_default>1</show_in_default>
|
54 |
+
<show_in_website>0</show_in_website>
|
55 |
+
<show_in_store>0</show_in_store>
|
56 |
+
</error_email>
|
57 |
+
<error_email_identity translate="label">
|
58 |
+
<label>Error Email Sender</label>
|
59 |
+
<frontend_type>select</frontend_type>
|
60 |
+
<source_model>adminhtml/system_config_source_email_identity</source_model>
|
61 |
+
<sort_order>140</sort_order>
|
62 |
+
<show_in_default>1</show_in_default>
|
63 |
+
<show_in_website>0</show_in_website>
|
64 |
+
<show_in_store>0</show_in_store>
|
65 |
+
</error_email_identity>
|
66 |
+
<error_email_template translate="label">
|
67 |
+
<label>Error Email Template</label>
|
68 |
+
<frontend_type>select</frontend_type>
|
69 |
+
<source_model>adminhtml/system_config_source_email_template</source_model>
|
70 |
+
<sort_order>150</sort_order>
|
71 |
+
<show_in_default>1</show_in_default>
|
72 |
+
<show_in_website>0</show_in_website>
|
73 |
+
<show_in_store>0</show_in_store>
|
74 |
+
</error_email_template>
|
75 |
+
|
76 |
+
<logFile translate="label">
|
77 |
+
<label>Log file</label>
|
78 |
+
<frontend_type>text</frontend_type>
|
79 |
+
<sort_order>160</sort_order>
|
80 |
+
<comment><![CDATA[Keep empty to disable logging]]></comment>
|
81 |
+
<show_in_default>1</show_in_default>
|
82 |
+
</logFile>
|
83 |
+
|
84 |
+
<maxNoOfSuccessfulTasks translate="label">
|
85 |
+
<label>Maximum number of successful schedules to keep</label>
|
86 |
+
<frontend_type>text</frontend_type>
|
87 |
+
<sort_order>170</sort_order>
|
88 |
+
<comment><![CDATA[Only keeps this number of entries for every schedule even if it is shorter than the configured success lifetime. Set to 0 to keep them all until they hit the lifetime.]]></comment>
|
89 |
+
<show_in_default>1</show_in_default>
|
90 |
+
</maxNoOfSuccessfulTasks>
|
91 |
+
|
92 |
+
<enableRunNow translate="label">
|
93 |
+
<label>Enable "run now"</label>
|
94 |
+
<frontend_type>select</frontend_type>
|
95 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
96 |
+
<sort_order>180</sort_order>
|
97 |
+
<comment><![CDATA[Cron tasks are usually designed to run in CLI context and not in the context of the webserver. Also they could run longer than the configured maximum execution time. This is why you should rather use "scheduleNow" instead of "runNow" and let the scheduler pick up the task on the next run. However, if you know what you're doing and/or if you don't want to wait while testing and developing a cron task you can enable this feature here.]]></comment>
|
98 |
+
<show_in_default>1</show_in_default>
|
99 |
+
</enableRunNow>
|
100 |
+
|
101 |
+
<enableJobOutputBuffer translate="label">
|
102 |
+
<label>Enable Job Output Buffering</label>
|
103 |
+
<comment><![CDATA[If this is enabled jobs will capture normal output for storage in the schedule record messages field]]></comment>
|
104 |
+
<frontend_type>select</frontend_type>
|
105 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
106 |
+
<sort_order>200</sort_order>
|
107 |
+
<show_in_default>1</show_in_default>
|
108 |
+
</enableJobOutputBuffer>
|
109 |
+
|
110 |
+
<enableErrorLog translate="label">
|
111 |
+
<label>Enable Error Log</label>
|
112 |
+
<comment><![CDATA[If enabled AOE_Scheduler will bypass Magento's error handling and will capture the error log in a schedule specific file in var/log/cron/]]></comment>
|
113 |
+
<frontend_type>select</frontend_type>
|
114 |
+
<source_model>adminhtml/system_config_source_yesno</source_model>
|
115 |
+
<sort_order>210</sort_order>
|
116 |
+
<show_in_default>1</show_in_default>
|
117 |
+
</enableErrorLog>
|
118 |
+
|
119 |
+
<errorLevel translate="label">
|
120 |
+
<label>Error Level</label>
|
121 |
+
<comment><![CDATA[Error level accepted by 'error_reporting'. This needs to be the int value. Constants are not supported! Example: -1 or 32767 (E_ALL) to log everything.]]></comment>
|
122 |
+
<frontend_type>text</frontend_type>
|
123 |
+
<sort_order>220</sort_order>
|
124 |
+
<show_in_default>1</show_in_default>
|
125 |
+
<depends>
|
126 |
+
<enableErrorLog>1</enableErrorLog>
|
127 |
+
</depends>
|
128 |
+
</errorLevel>
|
129 |
+
|
130 |
+
<errorLogFilename translate="label">
|
131 |
+
<label>Error log filename</label>
|
132 |
+
<comment><![CDATA[###PID### will be replaced with the process id.<br />###ID### will be replaced with the job id.<br />###JOBCODE### will be replaced with the job code.]]></comment>
|
133 |
+
<frontend_type>text</frontend_type>
|
134 |
+
<sort_order>230</sort_order>
|
135 |
+
<show_in_default>1</show_in_default>
|
136 |
+
<depends>
|
137 |
+
<enableErrorLog>1</enableErrorLog>
|
138 |
+
</depends>
|
139 |
+
</errorLogFilename>
|
140 |
+
|
141 |
+
<listCodeFilterType translate="label">
|
142 |
+
<label>Job code filter type in list view</label>
|
143 |
+
<frontend_type>select</frontend_type>
|
144 |
+
<source_model>aoe_scheduler/adminhtml_system_config_source_list_code_filtertype</source_model>
|
145 |
+
<sort_order>240</sort_order>
|
146 |
+
<show_in_default>1</show_in_default>
|
147 |
+
</listCodeFilterType>
|
148 |
+
|
149 |
+
</fields>
|
150 |
+
</cron>
|
151 |
+
</groups>
|
152 |
+
</system>
|
153 |
+
</sections>
|
154 |
</config>
|
app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/install-1.0.0.php
ADDED
@@ -0,0 +1,78 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* @var $this Mage_Core_Model_Resource_Setup */
|
3 |
+
$this->startSetup();
|
4 |
+
|
5 |
+
$tableName = $this->getTable('cron/schedule');
|
6 |
+
|
7 |
+
$this->getConnection()->dropColumn(
|
8 |
+
$tableName,
|
9 |
+
'parameters'
|
10 |
+
);
|
11 |
+
|
12 |
+
$this->getConnection()->addColumn(
|
13 |
+
$tableName,
|
14 |
+
'parameters',
|
15 |
+
"TEXT NULL COMMENT 'Serialized Parameters'"
|
16 |
+
);
|
17 |
+
|
18 |
+
$this->getConnection()->addColumn(
|
19 |
+
$tableName,
|
20 |
+
'eta',
|
21 |
+
"timestamp NULL DEFAULT NULL COMMENT 'Estimated Time of Arrival'"
|
22 |
+
);
|
23 |
+
|
24 |
+
$this->getConnection()->addColumn(
|
25 |
+
$tableName,
|
26 |
+
'host',
|
27 |
+
"varchar(255) NULL COMMENT 'Host running this job'"
|
28 |
+
);
|
29 |
+
|
30 |
+
$this->getConnection()->addColumn(
|
31 |
+
$tableName,
|
32 |
+
'pid',
|
33 |
+
"varchar(255) NULL COMMENT 'Process id of this job'"
|
34 |
+
);
|
35 |
+
|
36 |
+
$this->getConnection()->addColumn(
|
37 |
+
$tableName,
|
38 |
+
'progress_message',
|
39 |
+
"TEXT NULL COMMENT 'Progress message'"
|
40 |
+
);
|
41 |
+
|
42 |
+
$this->getConnection()->addColumn(
|
43 |
+
$tableName,
|
44 |
+
'last_seen',
|
45 |
+
"timestamp NULL DEFAULT NULL COMMENT 'Last seen'"
|
46 |
+
);
|
47 |
+
|
48 |
+
$this->getConnection()->addColumn(
|
49 |
+
$tableName,
|
50 |
+
'kill_request',
|
51 |
+
"timestamp NULL DEFAULT NULL COMMENT 'Kill Request'"
|
52 |
+
);
|
53 |
+
|
54 |
+
$this->getConnection()->addColumn(
|
55 |
+
$tableName,
|
56 |
+
'scheduled_by',
|
57 |
+
array(
|
58 |
+
'type' => Varien_Db_Ddl_Table::TYPE_INTEGER,
|
59 |
+
'unsigned' => true,
|
60 |
+
'nullable' => true,
|
61 |
+
'default' => null,
|
62 |
+
'comment' => 'Scheduled by'
|
63 |
+
)
|
64 |
+
);
|
65 |
+
|
66 |
+
$this->getConnection()->addColumn(
|
67 |
+
$tableName,
|
68 |
+
'scheduled_reason',
|
69 |
+
array(
|
70 |
+
'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
|
71 |
+
'length' => 256,
|
72 |
+
'nullable' => true,
|
73 |
+
'default' => null,
|
74 |
+
'comment' => 'Scheduled Reason'
|
75 |
+
)
|
76 |
+
);
|
77 |
+
|
78 |
+
$this->endSetup();
|
app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.4.0-0.4.1.php
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$installer = $this;
|
4 |
+
/* @var $installer Mage_Core_Model_Resource_Setup */
|
5 |
+
|
6 |
+
$installer->startSetup();
|
7 |
+
|
8 |
+
$tableName = $installer->getTable('cron_schedule');
|
9 |
+
|
10 |
+
try {
|
11 |
+
$installer->getConnection()->dropColumn($tableName, 'parameters');
|
12 |
+
} catch (Exception $e) {
|
13 |
+
// ignored intentionally
|
14 |
+
}
|
15 |
+
|
16 |
+
$installer->getConnection()->addColumn(
|
17 |
+
$tableName,
|
18 |
+
'parameters',
|
19 |
+
"TEXT NULL COMMENT 'Serialized Parameters'"
|
20 |
+
);
|
21 |
+
|
22 |
+
$installer->getConnection()->addColumn(
|
23 |
+
$tableName,
|
24 |
+
'eta',
|
25 |
+
"timestamp NULL DEFAULT NULL COMMENT 'Estimated Time of Arrival'"
|
26 |
+
);
|
27 |
+
|
28 |
+
$installer->getConnection()->addColumn(
|
29 |
+
$tableName,
|
30 |
+
'host',
|
31 |
+
"varchar(255) NULL COMMENT 'Host running this job'"
|
32 |
+
);
|
33 |
+
|
34 |
+
$installer->getConnection()->addColumn(
|
35 |
+
$tableName,
|
36 |
+
'pid',
|
37 |
+
"varchar(255) NULL COMMENT 'Process id of this job'"
|
38 |
+
);
|
39 |
+
|
40 |
+
$installer->getConnection()->addColumn(
|
41 |
+
$tableName,
|
42 |
+
'progress_message',
|
43 |
+
"TEXT NULL COMMENT 'Progress message'"
|
44 |
+
);
|
45 |
+
|
46 |
+
$installer->endSetup();
|
app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.4.1-0.4.2.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$installer = $this; /* @var $installer Mage_Core_Model_Resource_Setup */
|
4 |
+
|
5 |
+
$installer->startSetup();
|
6 |
+
|
7 |
+
$tableName = $installer->getTable('cron_schedule');
|
8 |
+
|
9 |
+
$installer->getConnection()->addColumn(
|
10 |
+
$tableName,
|
11 |
+
'last_seen',
|
12 |
+
"timestamp NULL DEFAULT NULL COMMENT 'Last seen'"
|
13 |
+
);
|
14 |
+
|
15 |
+
$installer->endSetup();
|
app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.4.2-0.4.3.php
ADDED
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$installer = $this; /* @var $installer Mage_Core_Model_Resource_Setup */
|
4 |
+
|
5 |
+
$installer->startSetup();
|
6 |
+
|
7 |
+
$tableName = $installer->getTable('cron_schedule');
|
8 |
+
|
9 |
+
$installer->getConnection()->addColumn(
|
10 |
+
$tableName,
|
11 |
+
'kill_request',
|
12 |
+
"timestamp NULL DEFAULT NULL COMMENT 'Kill Request'"
|
13 |
+
);
|
14 |
+
|
15 |
+
$installer->endSetup();
|
app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.5.0-0.5.1.php
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
|
3 |
+
$installer = $this; /* @var $installer Mage_Core_Model_Resource_Setup */
|
4 |
+
|
5 |
+
$installer->startSetup();
|
6 |
+
|
7 |
+
$installer->getConnection()->addColumn($installer->getTable('cron/schedule'), 'scheduled_by', array(
|
8 |
+
'type' => Varien_Db_Ddl_Table::TYPE_INTEGER,
|
9 |
+
'unsigned' => true,
|
10 |
+
'nullable' => true,
|
11 |
+
'default' => null,
|
12 |
+
'comment' => 'Scheduled by'
|
13 |
+
));
|
14 |
+
|
15 |
+
$installer->getConnection()->addColumn($installer->getTable('cron/schedule'), 'scheduled_reason', array(
|
16 |
+
'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
|
17 |
+
'length' => 256,
|
18 |
+
'nullable' => true,
|
19 |
+
'default' => null,
|
20 |
+
'comment' => 'Scheduled Reason'
|
21 |
+
));
|
22 |
+
|
23 |
+
$installer->endSetup();
|
app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.5.3-0.5.4.php
ADDED
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* @var $this Mage_Core_Model_Resource_Setup */
|
3 |
+
$this->startSetup();
|
4 |
+
|
5 |
+
$this->getConnection()->dropTable($this->getTable('cron_job'));
|
6 |
+
|
7 |
+
$this->endSetup();
|
app/code/community/Aoe/Scheduler/sql/aoescheduler_setup/mysql4-upgrade-0.5.4-1.1.0.php
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* @var $this Mage_Core_Model_Resource_Setup */
|
3 |
+
$this->startSetup();
|
4 |
+
|
5 |
+
$this->getConnection()->modifyColumn(
|
6 |
+
$this->getTable('cron/schedule'),
|
7 |
+
'status',
|
8 |
+
array(
|
9 |
+
'type' => Varien_Db_Ddl_Table::TYPE_TEXT,
|
10 |
+
'length' => 30,
|
11 |
+
'nullable' => false,
|
12 |
+
'default' => 'pending',
|
13 |
+
'comment' => 'Status'
|
14 |
+
)
|
15 |
+
);
|
16 |
+
|
17 |
+
$this->endSetup();
|
app/design/adminhtml/default/default/layout/aoe_scheduler/aoe_scheduler.xml
CHANGED
@@ -1,48 +1,72 @@
|
|
1 |
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
<layout>
|
3 |
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
|
|
29 |
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
47 |
|
48 |
</layout>
|
1 |
<?xml version="1.0" encoding="UTF-8"?>
|
2 |
<layout>
|
3 |
|
4 |
+
<adminhtml_timeline_index>
|
5 |
+
<reference name="head">
|
6 |
+
<action method="addCss"><stylesheet>aoe_scheduler/StyleSheet/timeline.css</stylesheet></action>
|
7 |
+
<action method="addCss"><stylesheet>aoe_scheduler/StyleSheet/bars.css</stylesheet></action>
|
8 |
|
9 |
+
<action method="addItem"><type>skin_js</type><script>aoe_scheduler/JavaScript/jquery-1.6.2.min.js</script></action>
|
10 |
+
<action method="addItem"><type>skin_js</type><script>aoe_scheduler/JavaScript/tooltip.js</script></action>
|
11 |
+
<action method="addItem"><type>skin_js</type><script>aoe_scheduler/JavaScript/tooltip.dynamic.js</script></action>
|
12 |
+
<action method="addItem"><type>skin_js</type><script>aoe_scheduler/JavaScript/common.js</script></action>
|
13 |
+
</reference>
|
14 |
+
<reference name="content">
|
15 |
+
<block type="aoe_scheduler/adminhtml_timeline" name="aoe_scheduler.timeline" template="aoe_scheduler/timeline.phtml"/>
|
16 |
+
<!--<block type="core/text" name="fix.console" as="fix.console">
|
17 |
+
<action method="setText">
|
18 |
+
<text><![CDATA[<script type="text/javascript">
|
19 |
+
iframe = document.createElement('iframe');
|
20 |
+
iframe.style = 'display:none';
|
21 |
+
document.getElementsByTagName('body')[0].appendChild(iframe);
|
22 |
+
window.console = iframe.contentWindow.console;
|
23 |
+
console.firebug = "faketrue";
|
24 |
+
</script>]]></text>
|
25 |
+
</action>
|
26 |
+
</block>
|
27 |
+
-->
|
28 |
+
</reference>
|
29 |
+
</adminhtml_timeline_index>
|
30 |
|
31 |
+
<adminhtml_scheduler_index>
|
32 |
+
<reference name="head">
|
33 |
+
<action method="addCss"><stylesheet>aoe_scheduler/StyleSheet/bars.css</stylesheet></action>
|
34 |
+
</reference>
|
35 |
+
<reference name="content">
|
36 |
+
<block type="aoe_scheduler/adminhtml_scheduler" name="aoe_scheduler.scheduler"/>
|
37 |
+
</reference>
|
38 |
+
</adminhtml_scheduler_index>
|
39 |
|
40 |
+
<adminhtml_job_index>
|
41 |
+
<reference name="head">
|
42 |
+
<action method="addCss"><stylesheet>aoe_scheduler/StyleSheet/bars.css</stylesheet></action>
|
43 |
+
</reference>
|
44 |
+
<reference name="content">
|
45 |
+
<block type="aoe_scheduler/adminhtml_job" name="aoe_scheduler.job"/>
|
46 |
+
</reference>
|
47 |
+
</adminhtml_job_index>
|
48 |
+
|
49 |
+
<adminhtml_job_edit>
|
50 |
+
<reference name="left">
|
51 |
+
<block type="aoe_scheduler/adminhtml_job_edit_tabs" name="aoe_scheduler_edit_tabs">
|
52 |
+
<block type="aoe_scheduler/adminhtml_job_edit_tab_form" name="aoe_scheduler_edit_tab_form"/>
|
53 |
+
<action method="addTab"><name>information_section</name><block>aoe_scheduler_edit_tab_form</block></action>
|
54 |
+
</block>
|
55 |
+
</reference>
|
56 |
+
<reference name="content">
|
57 |
+
<block type="aoe_scheduler/adminhtml_job_edit" name="aoe_scheduler_edit_information"/>
|
58 |
+
</reference>
|
59 |
+
</adminhtml_job_edit>
|
60 |
+
|
61 |
+
<adminhtml_instructions_index>
|
62 |
+
<reference name="head">
|
63 |
+
<action method="addCss"><stylesheet>aoe_scheduler/StyleSheet/instructions.css</stylesheet></action>
|
64 |
+
<action method="addItem"><type>skin_js</type><script>aoe_scheduler/JavaScript/jquery-1.6.2.min.js</script></action>
|
65 |
+
<action method="addItem"><type>skin_js</type><script>aoe_scheduler/JavaScript/instructions.js</script></action>
|
66 |
+
</reference>
|
67 |
+
<reference name="content">
|
68 |
+
<block type="aoe_scheduler/adminhtml_instructions" name="aoe_scheduler.instructions" template="aoe_scheduler/instructions.phtml"/>
|
69 |
+
</reference>
|
70 |
+
</adminhtml_instructions_index>
|
71 |
|
72 |
</layout>
|
app/design/adminhtml/default/default/template/aoe_scheduler/instructions.phtml
ADDED
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
<?php
|
2 |
+
/* @var $this Aoe_Scheduler_Block_Adminhtml_Instructions */
|
3 |
+
|
4 |
+
$maintenanceModeCheck = '<span class="maintenance-check-command">! test -e '.$this->getMagentoRootpath() .'/maintenance.flag && </span>';
|
5 |
+
$schedule = '*<span class="every-five-minutes">/5</span> * * * *';
|
6 |
+
?>
|
7 |
+
|
8 |
+
<div class="content-header">
|
9 |
+
<table cellspacing="0">
|
10 |
+
<tr>
|
11 |
+
<td style="<?php echo $this->getHeaderWidth() ?>"><h3 class="head-empty"><?php echo $this->__('Setup Instructions') ?></h3></td>
|
12 |
+
<td class="form-buttons"><?php echo $this->getButtonsHtml() ?></td>
|
13 |
+
</tr>
|
14 |
+
</table>
|
15 |
+
</div>
|
16 |
+
|
17 |
+
<div id="maincontainer">
|
18 |
+
|
19 |
+
<div class="configuration">
|
20 |
+
<p><input type="checkbox" name="scheduler-cron" checked="checked"><label for="scheduler-cron"><?php echo $this->__('Run scheduler_cron.sh instead cron.sh (recommended)') ?></label></p>
|
21 |
+
<p><input type="checkbox" name="every-minute" checked="checked"><label for="every-minute"><?php echo $this->__('Run every minute (recommended)') ?></label></p>
|
22 |
+
<p><input type="checkbox" name="maintenance-check" checked="checked"><label for="maintenance-check"><?php echo $this->__('Check maintenance mode (recommended)') ?></label></p>
|
23 |
+
<p><input type="checkbox" name="use-crongroups"><label for="use-crongroups"><?php echo $this->__('Use multiple cron groups (example)') ?></label></p>
|
24 |
+
<p><input type="checkbox" name="use-watchdog"><label for="use-watchdog"><?php echo $this->__('Use watchdog') ?></label></p>
|
25 |
+
<p><input type="checkbox" name="add-mailto"><label for="add-mailto"><?php echo $this->__('Include email address for output messages') ?></label></p>
|
26 |
+
</div>
|
27 |
+
|
28 |
+
<h5><?php echo $this->__('Edit your crontab:') ?></h5>
|
29 |
+
<pre>
|
30 |
+
sudo crontab -u <?php echo $this->getCurrentUser() ?> -e
|
31 |
+
</pre>
|
32 |
+
|
33 |
+
|
34 |
+
<h5><?php echo $this->__('Add following configuration:') ?></h5>
|
35 |
+
<pre class="cron-configuration">
|
36 |
+
<span class="croncommand mailto">MAILTO="<?php echo Mage::getSingleton('admin/session')->getUser()->getEmail(); ?>"
|
37 |
+
</span><span class="croncommand classic"><?php echo $schedule ?> <?php echo $maintenanceModeCheck ?>/bin/sh <?php echo $this->getMagentoRootpath() ?>/cron.sh
|
38 |
+
</span><span class="croncommand scheduler"><?php echo $schedule ?> <?php echo $maintenanceModeCheck ?>/bin/bash <?php echo $this->getMagentoRootpath() ?>/scheduler_cron.sh --mode always
|
39 |
+
<?php echo $schedule ?> <?php echo $maintenanceModeCheck ?>/bin/bash <?php echo $this->getMagentoRootpath() ?>/scheduler_cron.sh --mode default
|
40 |
+
</span><span class="croncommand crongroups"><?php echo $schedule ?> <?php echo $maintenanceModeCheck ?>/bin/bash <?php echo $this->getMagentoRootpath() ?>/scheduler_cron.sh --mode always --includeGroups my_queue_jobs
|
41 |
+
<?php echo $schedule ?> <?php echo $maintenanceModeCheck ?>/bin/bash <?php echo $this->getMagentoRootpath() ?>/scheduler_cron.sh --mode always --excludeGroups my_queue_jobs
|
42 |
+
<?php echo $schedule ?> <?php echo $maintenanceModeCheck ?>/bin/bash <?php echo $this->getMagentoRootpath() ?>/scheduler_cron.sh --mode default --includeGroups groupA,groupB
|
43 |
+
<?php echo $schedule ?> <?php echo $maintenanceModeCheck ?>/bin/bash <?php echo $this->getMagentoRootpath() ?>/scheduler_cron.sh --mode default --includeGroups groupC
|
44 |
+
<?php echo $schedule ?> <?php echo $maintenanceModeCheck ?>/bin/bash <?php echo $this->getMagentoRootpath() ?>/scheduler_cron.sh --mode default --excludeGroups groupA,groupB,groupC
|
45 |
+
</span><span class="watchdog">*/10 * * * * <?php echo $maintenanceModeCheck ?>cd <?php echo $this->getMagentoRootpath() ?>/shell && /usr/bin/php scheduler.php --action watchdog
|
46 |
+
</span></pre>
|
47 |
+
|
48 |
+
</div>
|
app/design/adminhtml/default/default/template/aoe_scheduler/timeline.phtml
CHANGED
@@ -1,72 +1,75 @@
|
|
1 |
<?php
|
2 |
/* @var $this Aoe_Scheduler_Block_Adminhtml_Timeline */
|
3 |
-
$_helper = $this->helper('aoe_scheduler/data');
|
|
|
4 |
$_jobCodes = $this->getAvailableJobCodes(); /* @var $_jobCodes array */
|
5 |
?>
|
6 |
|
7 |
<div class="content-header">
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
</div>
|
15 |
|
16 |
<div id="maincontainer">
|
17 |
|
18 |
-
|
19 |
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
|
25 |
-
|
26 |
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
|
|
51 |
|
52 |
-
|
53 |
|
54 |
-
|
55 |
-
|
56 |
-
|
|
|
57 |
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
|
66 |
-
|
67 |
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
|
72 |
</div><!-- id="maincontainer" -->
|
1 |
<?php
|
2 |
/* @var $this Aoe_Scheduler_Block_Adminhtml_Timeline */
|
3 |
+
$_helper = $this->helper('aoe_scheduler/data');
|
4 |
+
/* @var $_helper Aoe_Scheduler_Helper_Data */
|
5 |
$_jobCodes = $this->getAvailableJobCodes(); /* @var $_jobCodes array */
|
6 |
?>
|
7 |
|
8 |
<div class="content-header">
|
9 |
+
<table cellspacing="0">
|
10 |
+
<tr>
|
11 |
+
<td style="<?php echo $this->getHeaderWidth() ?>"><?php echo $this->getHeaderHtml() ?></td>
|
12 |
+
<td class="form-buttons"><?php echo $this->getButtonsHtml() ?></td>
|
13 |
+
</tr>
|
14 |
+
</table>
|
15 |
</div>
|
16 |
|
17 |
<div id="maincontainer">
|
18 |
|
19 |
+
<?php if (count($_jobCodes) > 0): ?>
|
20 |
|
21 |
+
<div id="contentwrapper">
|
22 |
+
<div id="contentcolumn">
|
23 |
+
<div class="timeline-box">
|
24 |
+
<div class="timeline-panel" style="width: <?php echo $this->getTimelinePanelWidth(); ?>px;">
|
25 |
|
26 |
+
<div id="now" style="left: <?php echo $this->getNowline(); ?>px"><div class="arrow"></div></div>
|
27 |
|
28 |
+
<div class="row hours">
|
29 |
+
<div class="timeline">
|
30 |
+
<?php for ($i = $this->getStarttime(); $i < $this->getEndtime(); $i += 60 * 60): ?>
|
31 |
+
<div class="hour"><span><?php echo $_helper->decorateTime($i, false, 'Y-m-d H:i'); ?></span></div>
|
32 |
+
<?php endfor; ?>
|
33 |
+
</div>
|
34 |
+
</div>
|
35 |
|
36 |
+
<?php foreach ($_jobCodes as $jobCode): /* @var $jobCode string */ ?>
|
37 |
+
<?php $_schedules = $this->getSchedulesForCode($jobCode); ?>
|
38 |
+
<div class="row">
|
39 |
+
<div class="timeline timeline_<?php echo $jobCode; ?>">
|
40 |
+
<?php foreach ($_schedules as $_schedule): /* @var $_schedule Aoe_Scheduler_Model_Schedule */ ?>
|
41 |
+
<?php echo $this->getGanttDivAttributes($_schedule); ?>
|
42 |
+
<?php echo $this->getLayout()->createBlock('aoe_scheduler/adminhtml_timelineDetail')->setSchedule($_schedule)->toHtml(); ?>
|
43 |
+
<?php endforeach; ?>
|
44 |
+
</div>
|
45 |
+
</div>
|
46 |
+
<?php endforeach; ?>
|
47 |
|
48 |
+
</div>
|
49 |
+
</div>
|
50 |
+
</div>
|
51 |
+
<!-- id="contentcolumn" -->
|
52 |
+
</div><!-- id="contentwrapper" -->
|
53 |
|
54 |
+
<div id="leftcolumn">
|
55 |
|
56 |
+
<div class="row hours">
|
57 |
+
<div class="caption"><?php echo $_helper->decorateTime($this->getStarttime(), true, 'Y-m-d H:i'); ?>
|
58 |
+
- <?php echo $_helper->decorateTime($this->getEndtime(), true, 'Y-m-d H:i'); ?></div>
|
59 |
+
</div>
|
60 |
|
61 |
+
<?php foreach ($_jobCodes as $jobCode): /* @var $jobCode string */ ?>
|
62 |
+
<div class="row">
|
63 |
+
<div class="configuration">
|
64 |
+
<?php echo $jobCode; ?>
|
65 |
+
</div>
|
66 |
+
</div>
|
67 |
+
<?php endforeach; ?>
|
68 |
|
69 |
+
</div><!-- id="leftcolumn" -->
|
70 |
|
71 |
+
<?php else: ?>
|
72 |
+
<?php echo $this->__('No tasks found.'); ?>
|
73 |
+
<?php endif; ?>
|
74 |
|
75 |
</div><!-- id="maincontainer" -->
|
app/design/adminhtml/default/default/template/aoe_scheduler/timeline_detail.phtml
CHANGED
@@ -1,41 +1,63 @@
|
|
1 |
<?php
|
2 |
/* @var $this Aoe_Scheduler_Block_Adminhtml_TimelineDetail */
|
3 |
-
$_schedule = $this->getSchedule();
|
4 |
$_helper = $this->helper('aoe_scheduler/data'); /* @var $_helper Aoe_Scheduler_Helper_Data */
|
5 |
?>
|
6 |
|
7 |
<div class="details">
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
41 |
</div>
|
1 |
<?php
|
2 |
/* @var $this Aoe_Scheduler_Block_Adminhtml_TimelineDetail */
|
3 |
+
$_schedule = $this->getSchedule(); /* @var $_schedule Aoe_Scheduler_Model_Schedule */
|
4 |
$_helper = $this->helper('aoe_scheduler/data'); /* @var $_helper Aoe_Scheduler_Helper_Data */
|
5 |
?>
|
6 |
|
7 |
<div class="details">
|
8 |
+
<div class="details-headline <?php echo $_schedule->getStatus(); ?>">
|
9 |
+
<h3><?php echo $_schedule->getJobCode(); ?><?php if ($_schedule->isAlwaysTask()): ?>*<?php endif; ?></h3>
|
10 |
+
</div>
|
11 |
+
<div class="details-content">
|
12 |
+
<table>
|
13 |
+
<tr>
|
14 |
+
<td class="label"><?php echo $this->__('Status') ?>:</td>
|
15 |
+
<td>
|
16 |
+
<div class="status"><?php echo $_helper->decorateStatus($_schedule->getStatus()) ?></div>
|
17 |
+
</td>
|
18 |
+
</tr>
|
19 |
+
<tr>
|
20 |
+
<td class="label"><?php echo $this->__('Created at') ?>:</td>
|
21 |
+
<td><?php echo $_helper->decorateTime($_schedule->getCreatedAt()); ?></td>
|
22 |
+
</tr>
|
23 |
+
<tr>
|
24 |
+
<td class="label"><?php echo $this->__('Scheduled at') ?>:</td>
|
25 |
+
<td><?php echo $_helper->decorateTime($_schedule->getScheduledAt()); ?></td>
|
26 |
+
</tr>
|
27 |
+
<tr>
|
28 |
+
<td class="label"><?php echo $this->__('Executed at') ?>:</td>
|
29 |
+
<td><?php echo $_helper->decorateTime($_schedule->getExecutedAt()); ?></td>
|
30 |
+
</tr>
|
31 |
+
<tr>
|
32 |
+
<td class="label"><?php echo $this->__('Finished at') ?>:</td>
|
33 |
+
<td><?php echo $_helper->decorateTime($_schedule->getFinishedAt()); ?></td>
|
34 |
+
</tr>
|
35 |
+
<tr>
|
36 |
+
<td class="label"><?php echo $this->__('Last seen at') ?>:</td>
|
37 |
+
<td><?php echo $_helper->decorateTime($_schedule->getLastSeen()); ?></td>
|
38 |
+
</tr>
|
39 |
+
<tr>
|
40 |
+
<td class="label"><?php echo $this->__('Id') ?>:</td>
|
41 |
+
<td><?php echo $_schedule->getId(); ?></td>
|
42 |
+
</tr>
|
43 |
+
<tr>
|
44 |
+
<td class="label"><?php echo $this->__('Pid') ?>:</td>
|
45 |
+
<td><?php echo $_schedule->getPid(); ?></td>
|
46 |
+
</tr>
|
47 |
+
<tr>
|
48 |
+
<td class="label"><?php echo $this->__('Host') ?>:</td>
|
49 |
+
<td><?php echo $_schedule->getHost(); ?></td>
|
50 |
+
</tr>
|
51 |
+
<?php if ($_schedule->getMessages()): ?>
|
52 |
+
<tr>
|
53 |
+
<td class="label" colspan="2"><?php echo $this->__('Message') ?>:</td>
|
54 |
+
</tr>
|
55 |
+
<tr>
|
56 |
+
<td colspan="2">
|
57 |
+
<pre><?php echo $_schedule->getMessages(); ?></pre>
|
58 |
+
</td>
|
59 |
+
</tr>
|
60 |
+
<?php endif; ?>
|
61 |
+
</table>
|
62 |
+
</div>
|
63 |
</div>
|
app/etc/modules/Aoe_Scheduler.xml
CHANGED
@@ -1,9 +1,9 @@
|
|
1 |
<?xml version="1.0" ?>
|
2 |
<config>
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
</config>
|
1 |
<?xml version="1.0" ?>
|
2 |
<config>
|
3 |
+
<modules>
|
4 |
+
<Aoe_Scheduler>
|
5 |
+
<active>true</active>
|
6 |
+
<codePool>community</codePool>
|
7 |
+
</Aoe_Scheduler>
|
8 |
+
</modules>
|
9 |
</config>
|
app/locale/sv_SE/Aoe_Scheduler.csv
ADDED
@@ -0,0 +1,10 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"Available tasks","Tillgängliga uppgifter"
|
2 |
+
"Delete","Radera"
|
3 |
+
"Disabled ""%s""","Inaktiverade ""%s"""
|
4 |
+
"Disabled cron tasks","Schemalagda uppgifter som inaktiverats"
|
5 |
+
"Generated schedule","Genererat schema"
|
6 |
+
"Invalid model/method definition, expecting ""model/class::method"".","Ogiltig definition av modell/metod, ""modell/klass::metod"" förväntas."
|
7 |
+
"Schedule now","Schemalägg nu"
|
8 |
+
"Scheduled tasks","Schemalagda uppgifter"
|
9 |
+
"Scheduler","Schemaläggare"
|
10 |
+
"Unknown error.","Okänt fel."
|
package.xml
CHANGED
@@ -1,18 +1,18 @@
|
|
1 |
<?xml version="1.0"?>
|
2 |
<package>
|
3 |
<name>Aoe_Scheduler</name>
|
4 |
-
<version>
|
5 |
<stability>stable</stability>
|
6 |
-
<license uri="http://www.
|
7 |
<channel>community</channel>
|
8 |
<extends/>
|
9 |
<summary>Scheduler / Cron Management</summary>
|
10 |
-
<description>This extension allows you to manage
|
11 |
-
<notes>GIT Revision:
|
12 |
<authors><author><name>Fabrizio Branca</name><user>fbrnc</user><email>magento@fabrizio-branca.de</email></author></authors>
|
13 |
-
<date>
|
14 |
-
<time>
|
15 |
-
<contents><target name="magecommunity"><dir><dir name="Aoe"><dir name="Scheduler"><dir><dir name="Block"><dir name="Adminhtml"><dir name="
|
16 |
<compatible/>
|
17 |
-
<dependencies><required><php><min>5.
|
18 |
</package>
|
1 |
<?xml version="1.0"?>
|
2 |
<package>
|
3 |
<name>Aoe_Scheduler</name>
|
4 |
+
<version>1.3.0</version>
|
5 |
<stability>stable</stability>
|
6 |
+
<license uri="http://www.gnu.org/licenses/gpl-3.0.html">GPL v3.0</license>
|
7 |
<channel>community</channel>
|
8 |
<extends/>
|
9 |
<summary>Scheduler / Cron Management</summary>
|
10 |
+
<description>This extension allows you to manage and visualize cron scheduler tasks.</description>
|
11 |
+
<notes>GIT Revision: 4bb4776a589c7cf707be13bb4f67f4d7e5067146, Build Date: 2015-11-09 21:32:36</notes>
|
12 |
<authors><author><name>Fabrizio Branca</name><user>fbrnc</user><email>magento@fabrizio-branca.de</email></author></authors>
|
13 |
+
<date>2015-11-09</date>
|
14 |
+
<time>21:32:36</time>
|
15 |
+
<contents><target name="magecommunity"><dir><dir name="Aoe"><dir name="Scheduler"><dir><dir name="Block"><dir name="Adminhtml"><file name="Instructions.php" hash="06faaecc45fd29a0b68d19ae20ec5ac9"/><dir name="Job"><dir name="Edit"><file name="Form.php" hash="208a89d9909fae432fdf95a527408813"/><dir name="Tab"><file name="Form.php" hash="a89f73d055d1e0a664fb4bcee7d97c57"/></dir><file name="Tabs.php" hash="3b60dfa6535639dec95d1aaad720b8de"/></dir><file name="Edit.php" hash="9be686807fc622d4aae3b23af1631583"/><file name="Grid.php" hash="420de6bb372b3906f8969da67c0052ea"/></dir><file name="Job.php" hash="77a6d165f734292e34de409a929cdec7"/><dir name="Scheduler"><file name="Grid.php" hash="39eef84aa80a8b11957d3d415abedcac"/></dir><file name="Scheduler.php" hash="86d38975946960b5f4925f48be1af3f9"/><file name="Timeline.php" hash="f55d6784f26acd11c27404e617a84794"/><file name="TimelineDetail.php" hash="8275c4750e000a6db86febdb7d701ca6"/></dir></dir><dir name="Controller"><file name="AbstractController.php" hash="40bf08707c436df442bd85ad240de525"/></dir><dir name="Helper"><file name="Compatibility.php" hash="213d2d6d9fc6909104d6b95fdc9db1cd"/><file name="Data.php" hash="4243982588876f6bf26dcc1ddd8590cb"/><file name="GracefulDead.php" hash="dbc7b814d1b5001600c01a5c8ecbf84f"/></dir><dir name="Model"><dir name="Adminhtml"><dir name="System"><dir name="Config"><dir name="Source"><dir name="List"><dir name="Code"><file name="Filtertype.php" hash="81d4d5c81828add26c16c12f4f2ada55"/></dir></dir></dir></dir></dir></dir><file name="Api.php" hash="7377ee0d852af3b7a5ea858f52bca5b6"/><file name="Job.php" hash="31eab4ff034a864e200bf8928fa4aacc"/><file name="Observer.php" hash="10b9ad529f7b24cffe067dc886910b9f"/><file name="ProcessManager.php" hash="842774abff735162a4585bbd214078a0"/><dir name="Resource"><dir name="Job"><file name="Collection.php" hash="b34c2f6a771ccb4358338a1976edc0e9"/></dir><file name="Job.php" hash="4213cd25da8c0fb99a746b8b494c6335"/><dir name="Schedule"><file name="Collection.php" hash="e7fbddf6a9d77544f4bac8dde268a793"/></dir></dir><file name="Schedule.php" hash="2ec06562ebe1a70cc5f54e33422bc3d8"/><file name="ScheduleManager.php" hash="6e90aba20f0105e1cb87bac32caf01f9"/><dir name="Task"><file name="Heartbeat.php" hash="6ff27ead78e4242a498344837fdde044"/><file name="Test.php" hash="ce5492bd62c4173bc6f0972a4561c6cd"/></dir></dir><dir name="Test"><dir name="Helper"><file name="Data.php" hash="c32182b6f44f642bb956b0e074453677"/></dir><dir name="Model"><dir name="Schedule"><file name="Runnow.php" hash="1e6c1bcfa943c3fa88922a76fceef896"/><file name="Scheduling.php" hash="66f4461968982ef1736d797b6c5c9d4f"/></dir></dir></dir><dir name="controllers"><dir name="Adminhtml"><file name="InstructionsController.php" hash="fa35d0bcebf81670c8c727126fc1c175"/><file name="JobController.php" hash="be1315dc49ed360e1ad0fc9a66dfa694"/><file name="SchedulerController.php" hash="3e9a178be9dd7dd61a15488c9dbb454a"/><file name="TimelineController.php" hash="11f8cd380ea04ed6213dae32f04b4f5b"/></dir></dir><dir name="data"><dir name="aoescheduler_setup"><file name="data-upgrade-0.5.4-0.5.5.php" hash="9a1ff96466dbc2fed5f04496d7212da5"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="7d914fe555e46d66958ceeaa92e383ad"/><file name="api.xml" hash="cc6cc05874a1277ba53e07c3450541c5"/><file name="config.xml" hash="eb943352cdd2f61926a260a92ddff9e5"/><file name="system.xml" hash="0a4ff787cdb6d400edb62d68ab1d75f0"/></dir><dir name="sql"><dir name="aoescheduler_setup"><file name="install-1.0.0.php" hash="e8c7cf7e088e52603b74eb337ed75f82"/><file name="mysql4-upgrade-0.4.0-0.4.1.php" hash="8e43f3a42359b6a2f134dbdb9be346de"/><file name="mysql4-upgrade-0.4.1-0.4.2.php" hash="d0d515397c7229822995e547d43d347f"/><file name="mysql4-upgrade-0.4.2-0.4.3.php" hash="c71d4443d7791728ae98b0c0b0a629a9"/><file name="mysql4-upgrade-0.5.0-0.5.1.php" hash="55ccd4aab57b88e3890d239aabc79389"/><file name="mysql4-upgrade-0.5.3-0.5.4.php" hash="13ac6cf731bf750b4b022874af6e1e2b"/><file name="mysql4-upgrade-0.5.4-1.1.0.php" hash="27c1201a3e8a426df1fb62e2bdc50ead"/></dir></dir></dir></dir></dir></dir></target><target name="mageetc"><dir><dir name="modules"><file name="Aoe_Scheduler.xml" hash="34ec6f37fb42feda3a164dc26bac521e"/></dir></dir></target><target name="mageweb"><dir><dir name="shell"><file name="scheduler.php" hash="4dd04b41cce26823decd95e19de84c39"/></dir></dir></target><target name="magelocale"><dir><dir name="de_DE"><file name="Aoe_Scheduler.csv" hash="f06c379c1170bc67f978b05943197601"/></dir><dir name="en_US"><dir name="template"><dir name="email"><dir name="aoe_scheduler"><file name="cron_error.html" hash="0dbbecfef932b0326f86deedc4012c4e"/></dir></dir></dir></dir><dir name="sv_SE"><file name="Aoe_Scheduler.csv" hash="feb09dbeadfc1853df5dd0486a06b896"/></dir></dir></target><target name="magedesign"><dir><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="template"><dir name="aoe_scheduler"><file name="instructions.phtml" hash="74ad585711a519eb643b0ccd586c0ef3"/><file name="timeline.phtml" hash="b2daab6a137a460a9302bbe8f2d3c643"/><file name="timeline_detail.phtml" hash="5a58dd32b5a56ae9cf96a904250120cb"/></dir></dir><dir name="layout"><dir name="aoe_scheduler"><file name="aoe_scheduler.xml" hash="5d29825ad82e6d023beebcf845a41c38"/></dir></dir></dir></dir></dir></dir></target><target name="mageskin"><dir><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="aoe_scheduler"><dir><dir name="Images"><file name="animation.gif" hash="01ecf91547d85d738cbe64c2ff7a2f6f"/><file name="animation2.gif" hash="520e233263997c981bcb54d72a0b398f"/><file name="bg_notifications.gif" hash="df73b8aa7e48bb56e0a644245aa3683f"/><file name="gradient.png" hash="17f6c6ef64b1682a06060950933561c9"/><file name="hour.gif" hash="91a63b82b2b41a046ca938aea3238a41"/><file name="red.png" hash="7644c4b5b491cf69cef0df76bea06482"/></dir><dir name="JavaScript"><file name="common.js" hash="eaa0b86481d3cc4fb39b54989428f9c5"/><file name="instructions.js" hash="111e27ffd2b8dd364c8b074091b271de"/><file name="jquery-1.6.2.min.js" hash="a1a8cb16a060f6280a767187fd22e037"/><file name="tooltip.dynamic.js" hash="c6737dd54890ca0cac816f3fecaf33d9"/><file name="tooltip.js" hash="b438265d9a3fb7b6ae5713fc098ccab6"/></dir><dir name="StyleSheet"><file name="bars.css" hash="17137a4a35e90fa227d9ac6d905e0c52"/><file name="instructions.css" hash="d1f1edcb2c5a84cec429390ea103a069"/><file name="timeline.css" hash="25150cbe4e082704613a50342b046526"/></dir></dir></dir></dir></dir></dir></dir></target><target name="mage"><dir><dir><file name="scheduler_cron.sh" hash="d06305c65844781bbc57bab60f7b893a"/></dir></dir></target></contents>
|
16 |
<compatible/>
|
17 |
+
<dependencies><required><php><min>5.3.0</min><max>6.0.0</max></php></required></dependencies>
|
18 |
</package>
|
scheduler_cron.sh
ADDED
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/bash
|
2 |
+
|
3 |
+
# Generate an error if any variable doesn't exist
|
4 |
+
set -o nounset
|
5 |
+
|
6 |
+
delete_lock() {
|
7 |
+
LOCKDIR=$1
|
8 |
+
rm -rf "${LOCKDIR}"
|
9 |
+
if [ $? -ne 0 ]; then
|
10 |
+
echo "Could not remove lock dir '${LOCKDIR}'. (Check permissions...)"; >&2
|
11 |
+
exit 1;
|
12 |
+
fi
|
13 |
+
}
|
14 |
+
|
15 |
+
# @see http://wiki.bash-hackers.org/howto/mutex
|
16 |
+
acquire_lock () {
|
17 |
+
LOCKDIR=$1
|
18 |
+
PIDFILE="${LOCKDIR}/PID"
|
19 |
+
|
20 |
+
#echo "Trying to acquire lock '${LOCKDIR}'."
|
21 |
+
if mkdir "${LOCKDIR}" &>/dev/null; then
|
22 |
+
|
23 |
+
#echo "Successfully created '${LOCKDIR}'. Lock acquired"
|
24 |
+
|
25 |
+
# lock succeeded
|
26 |
+
trap 'delete_lock "${LOCKDIR}"; exit $?' INT TERM EXIT
|
27 |
+
echo "$$" >"${PIDFILE}"
|
28 |
+
|
29 |
+
else
|
30 |
+
#echo "Failed creating ${LOCKDIR}."
|
31 |
+
if [ ! -f "${PIDFILE}" ]; then
|
32 |
+
|
33 |
+
# no PID file found. This could be
|
34 |
+
# a) because you just updated from a previous version that didn't write a PID file, but a process is legitimetly running
|
35 |
+
# b) because you just updated from a previous version that didn't write a PID file and there's an abandoned lock
|
36 |
+
# c) because you hit the exact time between another process creating the dir and writing the PID file
|
37 |
+
|
38 |
+
# let's wait for a while to acount for c) and see if the file shows up
|
39 |
+
# and this also solves the problem of any old abandoned lock in b)
|
40 |
+
# only problem here is that there's a minimal chance of having two concurrent processes right after updating in case
|
41 |
+
# a new process expecting a PID file overlaps with an old process that didn't write a PID file
|
42 |
+
sleep 5
|
43 |
+
# if there's still no PID file we grab the process
|
44 |
+
if [ ! -f "${PIDFILE}" ]; then
|
45 |
+
#echo "No PID file found. Claiming lock now"
|
46 |
+
delete_lock "${LOCKDIR}"
|
47 |
+
# now try acquire new lock recursively...
|
48 |
+
#echo "Now acquiring new lock"
|
49 |
+
acquire_lock $LOCKDIR;
|
50 |
+
return
|
51 |
+
fi
|
52 |
+
fi
|
53 |
+
|
54 |
+
# lock failed, check if the other PID is alive
|
55 |
+
OTHERPID="$(cat "${PIDFILE}")"
|
56 |
+
# if cat isn't able to read the file, another instance is probably about to remove the lock -- exit, we're *still* locked
|
57 |
+
if [ $? != 0 ]; then
|
58 |
+
#echo "lock failed, PID ${OTHERPID} is active" >&2
|
59 |
+
exit 1;
|
60 |
+
fi
|
61 |
+
|
62 |
+
# check is the other process is still alive
|
63 |
+
if ! kill -0 $OTHERPID &>/dev/null; then
|
64 |
+
# lock is stale, remove it and restart
|
65 |
+
#echo "removing stale lock of nonexistant PID ${OTHERPID}" >&2
|
66 |
+
delete_lock "${LOCKDIR}"
|
67 |
+
# now try acquire new lock recursively...
|
68 |
+
acquire_lock $LOCKDIR;
|
69 |
+
else
|
70 |
+
# lock is valid and OTHERPID is active - exit, we're locked!
|
71 |
+
#echo "Other process is alive. Still locked"
|
72 |
+
exit 1
|
73 |
+
fi
|
74 |
+
fi
|
75 |
+
}
|
76 |
+
|
77 |
+
|
78 |
+
# Location of the php binary
|
79 |
+
PHP_BIN=$(which php || true)
|
80 |
+
if [ -z "${PHP_BIN}" ]; then
|
81 |
+
echo "Could not find a binary for php" 1>&2
|
82 |
+
exit 1
|
83 |
+
fi
|
84 |
+
|
85 |
+
# Location of the md5sum binary
|
86 |
+
MD5SUM_BIN=$(which md5sum || true)
|
87 |
+
if [ -z "${MD5SUM_BIN}" ]; then
|
88 |
+
echo "Could not find a binary for md5sum" 1>&2
|
89 |
+
exit 1
|
90 |
+
fi
|
91 |
+
|
92 |
+
# Absolute path to Magento installation shell scripts
|
93 |
+
DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")/shell" && pwd)
|
94 |
+
if [[ -z "${DIR}" || ! -d "${DIR}" ]]; then
|
95 |
+
echo "Could not resolve base shell directory" 1>&2
|
96 |
+
exit 1
|
97 |
+
fi
|
98 |
+
|
99 |
+
# The scheduler.php script
|
100 |
+
SCHEDULER="scheduler.php"
|
101 |
+
if [[ ! -e "${DIR}/${SCHEDULER}" || ! -r "${DIR}/${SCHEDULER}" ]]; then
|
102 |
+
echo "Could not find scheduler.php script" 1>&2
|
103 |
+
exit 1
|
104 |
+
fi
|
105 |
+
|
106 |
+
# Defaults
|
107 |
+
MODE="default"
|
108 |
+
INCLUDE_GROUPS=""
|
109 |
+
EXCLUDE_GROUPS=""
|
110 |
+
INCLUDE_JOBS=""
|
111 |
+
EXCLUDE_JOBS=""
|
112 |
+
|
113 |
+
# Parse command line args (very simplistic)
|
114 |
+
while [ $# -gt 0 ]; do
|
115 |
+
case "$1" in
|
116 |
+
--mode)
|
117 |
+
MODE=$2
|
118 |
+
shift 2
|
119 |
+
;;
|
120 |
+
--includeGroups)
|
121 |
+
INCLUDE_GROUPS=$2
|
122 |
+
shift 2
|
123 |
+
;;
|
124 |
+
--excludeGroups)
|
125 |
+
EXCLUDE_GROUPS=$2
|
126 |
+
shift 2
|
127 |
+
;;
|
128 |
+
--includeJobs)
|
129 |
+
INCLUDE_JOBS=$2
|
130 |
+
shift 2
|
131 |
+
;;
|
132 |
+
--excludeJobs)
|
133 |
+
EXCLUDE_JOBS=$2
|
134 |
+
shift 2
|
135 |
+
;;
|
136 |
+
--)
|
137 |
+
shift
|
138 |
+
break
|
139 |
+
;;
|
140 |
+
*)
|
141 |
+
echo "Invalid arguments." >&2
|
142 |
+
exit 1
|
143 |
+
;;
|
144 |
+
esac
|
145 |
+
done
|
146 |
+
|
147 |
+
# Verify we have a MODE parameter
|
148 |
+
if [ -z "${MODE}" ]; then
|
149 |
+
echo "Cron run mode MUST be defined." 1>&2
|
150 |
+
exit 1
|
151 |
+
fi
|
152 |
+
|
153 |
+
# Lock process to one run per set of options
|
154 |
+
# This is to prevent multiple processes for the same cron parameters (And the only reason we don't call PHP directly)
|
155 |
+
|
156 |
+
# Unique identifier for this cron job run
|
157 |
+
IDENTIFIER=$(echo -n "${DIR}|${MODE}|${INCLUDE_GROUPS}|${EXCLUDE_GROUPS}|${INCLUDE_JOBS}|${EXCLUDE_JOBS}" | "${MD5SUM_BIN}" - | cut -f1 -d' ')
|
158 |
+
acquire_lock "/tmp/magento.aoe_scheduler.${IDENTIFIER}.lock";
|
159 |
+
|
160 |
+
# Needed because PHP resolves symlinks before setting __FILE__
|
161 |
+
cd "${DIR}"
|
162 |
+
|
163 |
+
# Build the options
|
164 |
+
OPTIONS=""
|
165 |
+
if [ -n "${INCLUDE_GROUPS}" ]; then
|
166 |
+
OPTIONS="${OPTIONS} --includeGroups ${INCLUDE_GROUPS}"
|
167 |
+
fi
|
168 |
+
if [ -n "${EXCLUDE_GROUPS}" ]; then
|
169 |
+
OPTIONS="${OPTIONS} --excludeGroups ${EXCLUDE_GROUPS}"
|
170 |
+
fi
|
171 |
+
if [ -n "${INCLUDE_JOBS}" ]; then
|
172 |
+
OPTIONS="${OPTIONS} --includeJobs ${INCLUDE_JOBS}"
|
173 |
+
fi
|
174 |
+
if [ -n "${EXCLUDE_JOBS}" ]; then
|
175 |
+
OPTIONS="${OPTIONS} --excludeJobs ${EXCLUDE_JOBS}"
|
176 |
+
fi
|
177 |
+
|
178 |
+
# Run the job in the foreground
|
179 |
+
"${PHP_BIN}" "${SCHEDULER}" --action cron --mode ${MODE} ${OPTIONS}
|
shell/scheduler.php
CHANGED
@@ -1,177 +1,403 @@
|
|
1 |
<?php
|
2 |
|
3 |
-
require_once 'abstract.php';
|
4 |
-
|
5 |
-
class Aoe_Scheduler_Shell_Scheduler extends Mage_Shell_Abstract
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
|
57 |
-
|
58 |
-
|
59 |
-
|
60 |
-
|
61 |
-
|
62 |
-
|
63 |
-
|
64 |
-
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
-
|
76 |
-
|
77 |
-
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
-
|
84 |
-
|
85 |
-
|
86 |
-
|
87 |
-
|
88 |
-
|
89 |
-
|
90 |
-
|
91 |
-
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
-
|
101 |
-
|
102 |
-
|
103 |
-
|
104 |
-
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
126 |
-
|
127 |
-
|
128 |
-
|
129 |
-
|
130 |
-
|
131 |
-
|
132 |
-
|
133 |
-
|
134 |
-
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
|
139 |
-
|
140 |
-
|
141 |
-
|
142 |
-
|
143 |
-
|
144 |
-
|
145 |
-
|
146 |
-
|
147 |
-
|
148 |
-
|
149 |
-
|
150 |
-
|
151 |
-
|
152 |
-
|
153 |
-
|
154 |
-
|
155 |
-
|
156 |
-
|
157 |
-
|
158 |
-
|
159 |
-
|
160 |
-
|
161 |
-
|
162 |
-
|
163 |
-
|
164 |
-
|
165 |
-
|
166 |
-
|
167 |
-
|
168 |
-
|
169 |
-
|
170 |
-
|
171 |
-
|
172 |
-
|
173 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
174 |
}
|
175 |
|
176 |
$shell = new Aoe_Scheduler_Shell_Scheduler();
|
177 |
-
$shell->run();
|
1 |
<?php
|
2 |
|
3 |
+
require_once dirname($_SERVER['SCRIPT_NAME']) . DIRECTORY_SEPARATOR . 'abstract.php';
|
4 |
+
|
5 |
+
class Aoe_Scheduler_Shell_Scheduler extends Mage_Shell_Abstract
|
6 |
+
{
|
7 |
+
/**
|
8 |
+
* Run script
|
9 |
+
*
|
10 |
+
* @return void
|
11 |
+
*/
|
12 |
+
public function run()
|
13 |
+
{
|
14 |
+
try {
|
15 |
+
$helper = Mage::helper('aoe_scheduler/compatibility'); /* @var $helper Aoe_Scheduler_Helper_Compatibility */
|
16 |
+
if ($helper->oldConfigXmlExists()) {
|
17 |
+
echo 'Looks like you have an older version of Aoe_Scheduler installed that lived in the local code pool. Please delete everything under "' .$helper->getLocalCodeDir(). '"';
|
18 |
+
exit(1);
|
19 |
+
}
|
20 |
+
$action = $this->getArg('action');
|
21 |
+
if (empty($action)) {
|
22 |
+
echo $this->usageHelp();
|
23 |
+
} else {
|
24 |
+
$actionMethodName = $action . 'Action';
|
25 |
+
if (method_exists($this, $actionMethodName)) {
|
26 |
+
// emulate index.php entry point for correct URLs generation in scheduled cronjobs
|
27 |
+
Mage::register('custom_entry_point', true);
|
28 |
+
// Disable use of SID in generated URLs - This is standard for cron job bootstrapping
|
29 |
+
Mage::app()->setUseSessionInUrl(false);
|
30 |
+
// Disable permissions masking by default - This is Magento standard, but not recommended for security reasons
|
31 |
+
umask(0);
|
32 |
+
// Load the global event area - This is non-standard be should be standard
|
33 |
+
Mage::app()->addEventArea(Mage_Core_Model_App_Area::AREA_GLOBAL);
|
34 |
+
// Load the crontab event area - This is standard for cron job bootstrapping
|
35 |
+
Mage::app()->addEventArea('crontab');
|
36 |
+
// Run the command
|
37 |
+
$this->$actionMethodName();
|
38 |
+
} else {
|
39 |
+
echo "Action $action not found!\n";
|
40 |
+
echo $this->usageHelp();
|
41 |
+
exit(1);
|
42 |
+
}
|
43 |
+
}
|
44 |
+
} catch (Exception $e) {
|
45 |
+
$fh = fopen('php://stderr', 'w');
|
46 |
+
fputs($fh, $e->__toString());
|
47 |
+
fclose($fh);
|
48 |
+
exit(255);
|
49 |
+
}
|
50 |
+
}
|
51 |
+
|
52 |
+
/**
|
53 |
+
* Retrieve Usage Help Message
|
54 |
+
*
|
55 |
+
* @return string
|
56 |
+
*/
|
57 |
+
public function usageHelp()
|
58 |
+
{
|
59 |
+
$help = 'Available actions: ' . "\n";
|
60 |
+
$methods = get_class_methods($this);
|
61 |
+
foreach ($methods as $method) {
|
62 |
+
if (substr($method, -6) == 'Action') {
|
63 |
+
$help .= ' --action ' . substr($method, 0, -6);
|
64 |
+
$helpMethod = $method . 'Help';
|
65 |
+
if (method_exists($this, $helpMethod)) {
|
66 |
+
$help .= ' ' . $this->$helpMethod();
|
67 |
+
}
|
68 |
+
$help .= "\n";
|
69 |
+
}
|
70 |
+
}
|
71 |
+
return $help;
|
72 |
+
}
|
73 |
+
|
74 |
+
/**
|
75 |
+
* List all availables codes / jobs
|
76 |
+
*
|
77 |
+
* @return void
|
78 |
+
*/
|
79 |
+
public function listAllCodesAction()
|
80 |
+
{
|
81 |
+
/** @var Aoe_Scheduler_Model_Resource_Job_Collection $jobs */
|
82 |
+
$jobs = Mage::getSingleton('aoe_scheduler/job')->getCollection();
|
83 |
+
foreach ($jobs as $job) {
|
84 |
+
/* @var $job Aoe_Scheduler_Model_Job */
|
85 |
+
echo sprintf("%-50s %-20s %s\n", $job->getJobCode(), $job->getCronExpression(), $job->getIsActive() ? 'Enabled' : 'Disabled');
|
86 |
+
}
|
87 |
+
}
|
88 |
+
|
89 |
+
/**
|
90 |
+
* Returns the timestamp of the last run of a given job
|
91 |
+
*
|
92 |
+
* @return void
|
93 |
+
*/
|
94 |
+
public function lastRunAction()
|
95 |
+
{
|
96 |
+
$code = $this->getArg('code');
|
97 |
+
if (empty($code)) {
|
98 |
+
echo "\nNo code found!\n\n";
|
99 |
+
echo $this->usageHelp();
|
100 |
+
exit(1);
|
101 |
+
}
|
102 |
+
|
103 |
+
$collection = Mage::getModel('cron/schedule')->getCollection(); /* @var $collection Mage_Cron_Model_Resource_Schedule_Collection */
|
104 |
+
|
105 |
+
$collection->addFieldToFilter('job_code', $code)
|
106 |
+
->addFieldToFilter('status', Aoe_Scheduler_Model_Schedule::STATUS_SUCCESS)
|
107 |
+
->addOrder('finished_at', Varien_Data_Collection_Db::SORT_ORDER_DESC)
|
108 |
+
->getSelect()->limit(1);
|
109 |
+
$schedule = $collection->getFirstItem(); /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
110 |
+
if (!$schedule || !$schedule->getId()) {
|
111 |
+
echo "\nNo schedule found\n\n";
|
112 |
+
exit(1);
|
113 |
+
}
|
114 |
+
|
115 |
+
$time = strtotime($schedule->getFinishedAt());
|
116 |
+
|
117 |
+
if ($this->getArg('secondsFromNow')) {
|
118 |
+
$time = time() - $time;
|
119 |
+
}
|
120 |
+
|
121 |
+
echo $time . PHP_EOL;
|
122 |
+
}
|
123 |
+
|
124 |
+
/**
|
125 |
+
* Display extra help
|
126 |
+
*
|
127 |
+
* @return string
|
128 |
+
*/
|
129 |
+
public function lastRunActionHelp()
|
130 |
+
{
|
131 |
+
return "--code <code> [--secondsFromNow] Get the timestamp of the last successful run of a job for a given code";
|
132 |
+
}
|
133 |
+
|
134 |
+
/**
|
135 |
+
* Schedule a job now
|
136 |
+
*
|
137 |
+
* @return void
|
138 |
+
*/
|
139 |
+
public function scheduleNowAction()
|
140 |
+
{
|
141 |
+
$code = $this->getArg('code');
|
142 |
+
if (empty($code)) {
|
143 |
+
echo "\nNo code found!\n\n";
|
144 |
+
echo $this->usageHelp();
|
145 |
+
exit(1);
|
146 |
+
}
|
147 |
+
|
148 |
+
$allowedCodes = Mage::getSingleton('aoe_scheduler/job')->getResource()->getJobCodes();
|
149 |
+
if (!in_array($code, $allowedCodes)) {
|
150 |
+
echo "\nNo valid job found!\n\n";
|
151 |
+
echo $this->usageHelp();
|
152 |
+
exit(1);
|
153 |
+
}
|
154 |
+
|
155 |
+
$schedule = Mage::getModel('cron/schedule'); /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
156 |
+
$schedule->setScheduledReason(Aoe_Scheduler_Model_Schedule::REASON_SCHEDULENOW_CLI);
|
157 |
+
$schedule->setJobCode($code);
|
158 |
+
$schedule->schedule();
|
159 |
+
$schedule->save();
|
160 |
+
}
|
161 |
+
|
162 |
+
/**
|
163 |
+
* Display extra help
|
164 |
+
*
|
165 |
+
* @return string
|
166 |
+
*/
|
167 |
+
public function scheduleNowActionHelp()
|
168 |
+
{
|
169 |
+
return "--code <code> Schedule a job to be executed as soon as possible";
|
170 |
+
}
|
171 |
+
|
172 |
+
/**
|
173 |
+
* Run a job now
|
174 |
+
*
|
175 |
+
* @return void
|
176 |
+
*/
|
177 |
+
public function runNowAction()
|
178 |
+
{
|
179 |
+
$code = $this->getArg('code');
|
180 |
+
if (empty($code)) {
|
181 |
+
echo "\nNo code found!\n\n";
|
182 |
+
echo $this->usageHelp();
|
183 |
+
exit(1);
|
184 |
+
}
|
185 |
+
|
186 |
+
$allowedCodes = Mage::getSingleton('aoe_scheduler/job')->getResource()->getJobCodes();
|
187 |
+
if (!in_array($code, $allowedCodes)) {
|
188 |
+
echo "\nNo valid job found!\n\n";
|
189 |
+
echo $this->usageHelp();
|
190 |
+
exit(1);
|
191 |
+
}
|
192 |
+
|
193 |
+
$forceRun = ($this->getArg('force'));
|
194 |
+
$tryLock = ($this->getArg('tryLock'));
|
195 |
+
|
196 |
+
$schedule = Mage::getModel('cron/schedule'); /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
197 |
+
$schedule->setJobCode($code);
|
198 |
+
$schedule->setScheduledReason(Aoe_Scheduler_Model_Schedule::REASON_RUNNOW_CLI);
|
199 |
+
$schedule->runNow($tryLock, $forceRun);
|
200 |
+
if ($schedule->getJobWasLocked()) {
|
201 |
+
echo "\nJob was not executed because it was locked!\n\n";
|
202 |
+
exit(1);
|
203 |
+
}
|
204 |
+
$schedule->save();
|
205 |
+
|
206 |
+
echo "\nStatus: " . $schedule->getStatus() . "\n";
|
207 |
+
echo "Messages:\n" . trim($schedule->getMessages(), "\n") . "\n";
|
208 |
+
}
|
209 |
+
|
210 |
+
/**
|
211 |
+
* Display extra help
|
212 |
+
*
|
213 |
+
* @return string
|
214 |
+
*/
|
215 |
+
public function runNowActionHelp()
|
216 |
+
{
|
217 |
+
return "--code <code> [--tryLock] [--force] Run a job directly";
|
218 |
+
}
|
219 |
+
|
220 |
+
/**
|
221 |
+
* Active wait until no schedules are running
|
222 |
+
*/
|
223 |
+
public function waitAction()
|
224 |
+
{
|
225 |
+
$timeout = $this->getArg('timeout') ? $this->getArg('timeout') : 60;
|
226 |
+
$startTime = time();
|
227 |
+
$sleepBetweenPolls = 2;
|
228 |
+
$processManager = Mage::getModel('aoe_scheduler/processManager'); /* @var $processManager Aoe_Scheduler_Model_ProcessManager */
|
229 |
+
do {
|
230 |
+
sleep($sleepBetweenPolls);
|
231 |
+
$aliveSchedules = 0;
|
232 |
+
echo "Currently running schedules:\n";
|
233 |
+
foreach ($processManager->getAllRunningSchedules() as $schedule) { /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
234 |
+
$status = $schedule->isAlive();
|
235 |
+
if (is_null($status)) {
|
236 |
+
$status = '?';
|
237 |
+
} else {
|
238 |
+
$status = $status ? 'alive' : 'dead (updating status to "disappeared")';
|
239 |
+
}
|
240 |
+
if ($status) {
|
241 |
+
$aliveSchedules++;
|
242 |
+
}
|
243 |
+
echo sprintf(
|
244 |
+
"%-30s %-10s %-10s %-10s %-10s\n",
|
245 |
+
$schedule->getJobCode(),
|
246 |
+
$schedule->getHost() ? $schedule->getHost() : '(no host)',
|
247 |
+
$schedule->getPid() ? $schedule->getPid() : '(no pid)',
|
248 |
+
$schedule->getLastSeen() ? $schedule->getLastSeen() : '(never)',
|
249 |
+
$status
|
250 |
+
);
|
251 |
+
}
|
252 |
+
if ($aliveSchedules == 0) {
|
253 |
+
echo "No schedules found\n";
|
254 |
+
return;
|
255 |
+
}
|
256 |
+
} while (time() - $startTime < $timeout);
|
257 |
+
echo "Timeout reached\n";
|
258 |
+
exit(1);
|
259 |
+
}
|
260 |
+
|
261 |
+
/**
|
262 |
+
* Display extra help
|
263 |
+
*
|
264 |
+
* @return string
|
265 |
+
*/
|
266 |
+
public function waitActionHelp()
|
267 |
+
{
|
268 |
+
return "[--timout <timeout=60>] Active wait until no schedules are running.";
|
269 |
+
}
|
270 |
+
|
271 |
+
/**
|
272 |
+
* Flush schedules
|
273 |
+
*/
|
274 |
+
public function flushSchedulesAction()
|
275 |
+
{
|
276 |
+
$scheduleManager = Mage::getModel('aoe_scheduler/scheduleManager'); /* @var $scheduleManager Aoe_Scheduler_Model_ScheduleManager */
|
277 |
+
switch ($this->getArg('mode')) {
|
278 |
+
case 'future':
|
279 |
+
$scheduleManager->flushSchedules();
|
280 |
+
break;
|
281 |
+
case 'all':
|
282 |
+
$scheduleManager->deleteAll();
|
283 |
+
break;
|
284 |
+
default:
|
285 |
+
echo "\nInvalid mode!\n\n";
|
286 |
+
echo $this->usageHelp();
|
287 |
+
exit(1);
|
288 |
+
}
|
289 |
+
}
|
290 |
+
|
291 |
+
/**
|
292 |
+
* Display extra help
|
293 |
+
*
|
294 |
+
* @return string
|
295 |
+
*/
|
296 |
+
public function flushSchedulesActionHelp()
|
297 |
+
{
|
298 |
+
return "--mode (future|all) Flush schedules.";
|
299 |
+
}
|
300 |
+
|
301 |
+
/**
|
302 |
+
* Print all running schedules
|
303 |
+
*
|
304 |
+
* @return void
|
305 |
+
*/
|
306 |
+
public function listAllRunningSchedulesAction()
|
307 |
+
{
|
308 |
+
$processManager = Mage::getModel('aoe_scheduler/processManager'); /* @var $processManager Aoe_Scheduler_Model_ProcessManager */
|
309 |
+
foreach ($processManager->getAllRunningSchedules() as $schedule) { /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
310 |
+
$status = $schedule->isAlive();
|
311 |
+
if (is_null($status)) {
|
312 |
+
$status = '?';
|
313 |
+
} else {
|
314 |
+
$status = $status ? 'alive' : 'dead (updating status to "disappeared")';
|
315 |
+
}
|
316 |
+
echo sprintf(
|
317 |
+
"%-30s %-10s %-10s %-10s %-10s\n",
|
318 |
+
$schedule->getJobCode(),
|
319 |
+
$schedule->getHost() ? $schedule->getHost() : '(no host)',
|
320 |
+
$schedule->getPid() ? $schedule->getPid() : '(no pid)',
|
321 |
+
$schedule->getLastSeen() ? $schedule->getLastSeen() : '(never)',
|
322 |
+
$status
|
323 |
+
);
|
324 |
+
}
|
325 |
+
}
|
326 |
+
|
327 |
+
/**
|
328 |
+
* Kill all
|
329 |
+
*
|
330 |
+
* @return void
|
331 |
+
*/
|
332 |
+
public function killAllAction()
|
333 |
+
{
|
334 |
+
$processManager = Mage::getModel('aoe_scheduler/processManager'); /* @var $processManager Aoe_Scheduler_Model_ProcessManager */
|
335 |
+
foreach ($processManager->getAllRunningSchedules(gethostname()) as $schedule) { /* @var $schedule Aoe_Scheduler_Model_Schedule */
|
336 |
+
if ($schedule->isAlive() === true) {
|
337 |
+
$schedule->kill();
|
338 |
+
echo sprintf(
|
339 |
+
"%-30s %-10s %-10s: Killed\n",
|
340 |
+
$schedule->getJobCode(),
|
341 |
+
$schedule->getHost(),
|
342 |
+
$schedule->getPid()
|
343 |
+
);
|
344 |
+
}
|
345 |
+
}
|
346 |
+
}
|
347 |
+
|
348 |
+
/**
|
349 |
+
* Runs watchdog
|
350 |
+
*/
|
351 |
+
public function watchdogAction()
|
352 |
+
{
|
353 |
+
$processManager = Mage::getModel('aoe_scheduler/processManager'); /* @var $processManager Aoe_Scheduler_Model_ProcessManager */
|
354 |
+
$processManager->watchdog();
|
355 |
+
}
|
356 |
+
|
357 |
+
/**
|
358 |
+
* Cron action
|
359 |
+
*
|
360 |
+
*
|
361 |
+
*/
|
362 |
+
public function cronAction()
|
363 |
+
{
|
364 |
+
$mode = $this->getArg('mode');
|
365 |
+
switch ($mode) {
|
366 |
+
case 'always':
|
367 |
+
case 'default':
|
368 |
+
$includeGroups = array_filter(array_map('trim', explode(',', $this->getArg('includeGroups'))));
|
369 |
+
$excludeGroups = array_filter(array_map('trim', explode(',', $this->getArg('excludeGroups'))));
|
370 |
+
$includeJobs = array_filter(array_map('trim', explode(',', $this->getArg('includeJobs'))));
|
371 |
+
$excludeJobs = array_filter(array_map('trim', explode(',', $this->getArg('excludeJobs'))));
|
372 |
+
Mage::dispatchEvent($mode, array(
|
373 |
+
'include_groups' => $includeGroups,
|
374 |
+
'exclude_groups' => $excludeGroups,
|
375 |
+
'include_jobs' => $includeJobs,
|
376 |
+
'exclude_jobs' => $excludeJobs,
|
377 |
+
));
|
378 |
+
break;
|
379 |
+
default:
|
380 |
+
echo "\nInvalid mode!\n\n";
|
381 |
+
echo $this->usageHelp();
|
382 |
+
exit(1);
|
383 |
+
}
|
384 |
+
}
|
385 |
+
|
386 |
+
/**
|
387 |
+
* Display extra help
|
388 |
+
*
|
389 |
+
* @return string
|
390 |
+
*/
|
391 |
+
public function cronActionHelp()
|
392 |
+
{
|
393 |
+
return "--mode (always|default) [--includeJobs <comma separated list of jobs>] [--excludeJobs <comma separated list of jobs>] [--includeGroups <comma separated list of groups>] [--excludeGroups <comma separated list of groups>]";
|
394 |
+
}
|
395 |
+
|
396 |
+
protected function _applyPhpVariables()
|
397 |
+
{
|
398 |
+
// Disable this feature as cron jobs should run with CLI settings only
|
399 |
+
}
|
400 |
}
|
401 |
|
402 |
$shell = new Aoe_Scheduler_Shell_Scheduler();
|
403 |
+
$shell->run();
|
skin/adminhtml/default/default/aoe_scheduler/Images/animation.gif
ADDED
Binary file
|
skin/adminhtml/default/default/aoe_scheduler/Images/animation2.gif
ADDED
Binary file
|
skin/adminhtml/default/default/aoe_scheduler/Images/red.png
ADDED
Binary file
|
skin/adminhtml/default/default/aoe_scheduler/JavaScript/common.js
CHANGED
@@ -1,56 +1,59 @@
|
|
1 |
$.noConflict();
|
2 |
-
jQuery(function() {
|
3 |
-
|
4 |
-
|
5 |
-
|
6 |
-
|
7 |
-
|
8 |
-
|
9 |
-
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
|
17 |
-
|
18 |
-
|
19 |
-
|
20 |
-
|
21 |
-
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
37 |
-
|
38 |
-
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
-
|
55 |
-
|
56 |
-
}
|
|
|
|
|
|
1 |
$.noConflict();
|
2 |
+
jQuery(function () {
|
3 |
+
jQuery('.timeline-box').scrollLeft(jQuery('.timeline-panel').width());
|
4 |
+
|
5 |
+
jQuery('.task').tooltip({
|
6 |
+
offsetParent: 'body',
|
7 |
+
predelay: 100,
|
8 |
+
position: 'bottom center',
|
9 |
+
onShow: function () {
|
10 |
+
this.getTrigger().addClass('active');
|
11 |
+
},
|
12 |
+
onHide: function () {
|
13 |
+
this.getTrigger().removeClass('active');
|
14 |
+
}
|
15 |
+
}).dynamic();
|
16 |
+
|
17 |
+
// collision detection
|
18 |
+
function getPositions(box) {
|
19 |
+
var $box = jQuery(box);
|
20 |
+
var pos = $box.position();
|
21 |
+
var width = $box.width();
|
22 |
+
var height = $box.height();
|
23 |
+
return [[pos.left, pos.left + width], [pos.top, pos.top + height]];
|
24 |
+
}
|
25 |
+
|
26 |
+
function comparePositions(p1, p2) {
|
27 |
+
var x1 = p1[0] < p2[0] ? p1 : p2;
|
28 |
+
var x2 = p1[0] < p2[0] ? p2 : p1;
|
29 |
+
return (x1[1] > x2[0] || x1[0] === x2[0]);
|
30 |
+
}
|
31 |
+
|
32 |
+
function collision(a, b) {
|
33 |
+
var posA = getPositions(a);
|
34 |
+
var posB = getPositions(b);
|
35 |
+
|
36 |
+
return (posA[1][0] == posB[1][0]) && comparePositions(posA[0], posB[0]);
|
37 |
+
}
|
38 |
+
|
39 |
+
jQuery('.timeline').each(function () {
|
40 |
+
var $tasks = jQuery('.task', jQuery(this));
|
41 |
+
var numberOfTasks = $tasks.length;
|
42 |
+
for (var i = 0; i < numberOfTasks; i++) {
|
43 |
+
var u = Math.min(i + 10, numberOfTasks);
|
44 |
+
for (var j = i + 1; j < u; j++) {
|
45 |
+
if (collision($tasks[i], $tasks[j])) {
|
46 |
+
var $subject = jQuery($tasks[i]);
|
47 |
+
var $object = jQuery($tasks[j]);
|
48 |
+
|
49 |
+
var objectTop = parseInt($subject.css('top'));
|
50 |
+
|
51 |
+
$object.css('top', (objectTop + 4) + 'px');
|
52 |
+
|
53 |
+
$subject.css('height', 18);
|
54 |
+
$object.css('height', 18);
|
55 |
+
}
|
56 |
+
}
|
57 |
+
}
|
58 |
+
});
|
59 |
+
});
|
skin/adminhtml/default/default/aoe_scheduler/JavaScript/instructions.js
ADDED
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
$.noConflict();
|
2 |
+
jQuery(function() {
|
3 |
+
function updateInstructions() {
|
4 |
+
jQuery('.croncommand').hide();
|
5 |
+
jQuery('.configuration input').removeAttr("disabled");
|
6 |
+
if (!jQuery('.configuration input[name="scheduler-cron"]').is(':checked')) {
|
7 |
+
jQuery('.classic').show();
|
8 |
+
jQuery('.configuration input[name="use-crongroups"]').attr('checked', false).attr("disabled", true);
|
9 |
+
}
|
10 |
+
if (jQuery('.configuration input[name="use-crongroups"]').is(':checked')) {
|
11 |
+
jQuery('.configuration input[name="scheduler-cron"]').attr('checked', true).attr("disabled", true);
|
12 |
+
jQuery('.crongroups').show();
|
13 |
+
} else if (jQuery('.configuration input[name="scheduler-cron"]').is(':checked')) {
|
14 |
+
jQuery('.scheduler').show();
|
15 |
+
}
|
16 |
+
jQuery('.every-five-minutes').toggle(!jQuery('.configuration input[name="every-minute"]').is(':checked'));
|
17 |
+
jQuery('.maintenance-check-command').toggle(jQuery('.configuration input[name="maintenance-check"]').is(':checked'));
|
18 |
+
jQuery('.watchdog').toggle(jQuery('.configuration input[name="use-watchdog"]').is(':checked'));
|
19 |
+
jQuery('.mailto').toggle(jQuery('.configuration input[name="add-mailto"]').is(':checked'));
|
20 |
+
|
21 |
+
}
|
22 |
+
updateInstructions();
|
23 |
+
jQuery('.configuration input').change(updateInstructions);
|
24 |
+
});
|
skin/adminhtml/default/default/aoe_scheduler/JavaScript/tooltip.js
CHANGED
@@ -1,215 +1,215 @@
|
|
1 |
/**
|
2 |
-
* @license
|
3 |
* jQuery Tools @VERSION Tooltip - UI essentials
|
4 |
-
*
|
5 |
* NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
|
6 |
-
*
|
7 |
* http://flowplayer.org/tools/tooltip/
|
8 |
*
|
9 |
* Since: November 2008
|
10 |
-
* Date: @DATE
|
11 |
*/
|
12 |
-
(function($) {
|
13 |
// static constructs
|
14 |
$.tools = $.tools || {version: '@VERSION'};
|
15 |
-
|
16 |
$.tools.tooltip = {
|
17 |
-
|
18 |
-
conf: {
|
19 |
-
|
20 |
// default effect variables
|
21 |
-
effect: 'toggle',
|
22 |
fadeOutSpeed: "fast",
|
23 |
predelay: 0,
|
24 |
delay: 30,
|
25 |
-
opacity: 1,
|
26 |
tip: 0,
|
27 |
-
|
28 |
// 'top', 'bottom', 'right', 'left', 'center'
|
29 |
-
position: ['top', 'center'],
|
30 |
offset: [0, 0],
|
31 |
relative: false,
|
32 |
offsetParent: null,
|
33 |
cancelDefault: true,
|
34 |
-
|
35 |
-
// type to event mapping
|
36 |
events: {
|
37 |
def: "mouseenter,mouseleave",
|
38 |
input: "focus,blur",
|
39 |
widget: "focus mouseenter,blur mouseleave",
|
40 |
tooltip: "mouseenter,mouseleave"
|
41 |
},
|
42 |
-
|
43 |
// 1.2
|
44 |
-
layout: '<div
|
45 |
tipClass: 'tooltip'
|
46 |
},
|
47 |
-
|
48 |
addEffect: function(name, loadFn, hideFn) {
|
49 |
-
effects[name] = [loadFn, hideFn];
|
50 |
-
}
|
51 |
};
|
52 |
-
|
53 |
-
|
54 |
-
var effects = {
|
55 |
-
toggle: [
|
56 |
-
function(done) {
|
57 |
var conf = this.getConf(), tip = this.getTip(), o = conf.opacity;
|
58 |
if (o < 1) { tip.css({opacity: o}); }
|
59 |
tip.show();
|
60 |
done.call();
|
61 |
},
|
62 |
-
|
63 |
-
function(done) {
|
64 |
this.getTip().hide();
|
65 |
done.call();
|
66 |
-
}
|
67 |
],
|
68 |
-
|
69 |
fade: [
|
70 |
-
function(done) {
|
71 |
var conf = this.getConf();
|
72 |
-
this.getTip().fadeTo(conf.fadeInSpeed, conf.opacity, done);
|
73 |
-
},
|
74 |
-
function(done) {
|
75 |
-
this.getTip().fadeOut(this.getConf().fadeOutSpeed, done);
|
76 |
-
}
|
77 |
-
]
|
78 |
-
};
|
79 |
-
|
80 |
-
|
81 |
-
/* calculate tip position relative to the trigger */
|
82 |
-
function getPosition(trigger, tip, conf) {
|
83 |
-
|
84 |
-
|
85 |
-
// get origin top/left position
|
86 |
-
var top = conf.relative ? trigger.position().top : trigger.offset().top,
|
87 |
left = conf.relative ? trigger.position().left : trigger.offset().left,
|
88 |
pos = conf.position[0];
|
89 |
|
90 |
top -= tip.outerHeight() - conf.offset[0];
|
91 |
left += trigger.outerWidth() + conf.offset[1];
|
92 |
-
|
93 |
// iPad position fix
|
94 |
if (/iPad/i.test(navigator.userAgent)) {
|
95 |
top -= $(window).scrollTop();
|
96 |
}
|
97 |
-
|
98 |
-
// adjust Y
|
99 |
var height = tip.outerHeight() + trigger.outerHeight();
|
100 |
if (pos == 'center') { top += height / 2; }
|
101 |
if (pos == 'bottom') { top += height; }
|
102 |
-
|
103 |
-
|
104 |
// adjust X
|
105 |
-
pos = conf.position[1];
|
106 |
var width = tip.outerWidth() + trigger.outerWidth();
|
107 |
if (pos == 'center') { left -= width / 2; }
|
108 |
-
if (pos == 'left') { left -= width; }
|
109 |
-
|
110 |
return {top: top, left: left};
|
111 |
-
}
|
|
|
|
|
112 |
|
113 |
-
|
114 |
-
|
115 |
function Tooltip(trigger, conf) {
|
116 |
|
117 |
-
var self = this,
|
118 |
fire = trigger.add(self),
|
119 |
tip,
|
120 |
timer = 0,
|
121 |
-
pretimer = 0,
|
122 |
title = trigger.attr("title"),
|
123 |
tipAttr = trigger.attr("data-tooltip"),
|
124 |
effect = effects[conf.effect],
|
125 |
shown,
|
126 |
-
|
127 |
// get show/hide configuration
|
128 |
-
isInput = trigger.is(":input"),
|
129 |
-
isWidget = isInput && trigger.is(":checkbox, :radio, select, :button, :submit"),
|
130 |
type = trigger.attr("type"),
|
131 |
-
evt = conf.events[type] || conf.events[isInput ? (isWidget ? 'widget' : 'input') : 'def'];
|
132 |
-
|
133 |
-
|
134 |
// check that configuration is sane
|
135 |
-
if (!effect) { throw "Nonexistent effect \"" + conf.effect + "\""; }
|
136 |
-
|
137 |
-
evt = evt.split(/,\s*/);
|
138 |
-
if (evt.length != 2) { throw "Tooltip: bad events configuration for " + type; }
|
139 |
-
|
140 |
-
|
141 |
-
// trigger --> show
|
142 |
trigger.bind(evt[0], function(e) {
|
143 |
|
144 |
clearTimeout(timer);
|
145 |
if (conf.predelay) {
|
146 |
-
pretimer = setTimeout(function() { self.show(e); }, conf.predelay);
|
147 |
-
|
148 |
} else {
|
149 |
-
self.show(e);
|
150 |
}
|
151 |
-
|
152 |
// trigger --> hide
|
153 |
}).bind(evt[1], function(e) {
|
154 |
clearTimeout(pretimer);
|
155 |
if (conf.delay) {
|
156 |
-
timer = setTimeout(function() { self.hide(e); }, conf.delay);
|
157 |
-
|
158 |
} else {
|
159 |
-
self.hide(e);
|
160 |
}
|
161 |
-
|
162 |
-
});
|
163 |
-
|
164 |
-
|
165 |
// remove default title
|
166 |
-
if (title && conf.cancelDefault) {
|
167 |
trigger.removeAttr("title");
|
168 |
-
trigger.data("title", title);
|
169 |
-
}
|
170 |
-
|
171 |
$.extend(self, {
|
172 |
-
|
173 |
-
show: function(e) {
|
174 |
|
175 |
// tip not initialized yet
|
176 |
if (!tip) {
|
177 |
-
|
178 |
-
// data-tooltip
|
179 |
if (tipAttr) {
|
180 |
tip = $(tipAttr);
|
181 |
|
182 |
// single tip element for all
|
183 |
-
} else if (conf.tip) {
|
184 |
tip = $(conf.tip).eq(0);
|
185 |
-
|
186 |
// autogenerated tooltip
|
187 |
-
} else if (title) {
|
188 |
tip = $(conf.layout).addClass(conf.tipClass).appendTo(document.body)
|
189 |
.hide().append(title);
|
190 |
|
191 |
// manual tooltip
|
192 |
-
} else {
|
193 |
-
tip = trigger.next();
|
194 |
if (!tip.length) { tip = trigger.parent().next(); }
|
195 |
-
|
196 |
}
|
197 |
-
|
198 |
if (!tip.length) { throw "Cannot find tooltip for " + trigger; }
|
199 |
-
|
200 |
if (conf.offsetParent) {
|
201 |
tip.appendTo($(conf.offsetParent));
|
202 |
}
|
203 |
-
}
|
204 |
-
|
205 |
-
if (self.isShown()) { return self; }
|
206 |
-
|
207 |
// stop previous animation
|
208 |
-
tip.stop(true, true);
|
209 |
-
|
210 |
// get position
|
211 |
-
var pos = getPosition(trigger, tip, conf);
|
212 |
-
|
213 |
// restore title for single tooltip element
|
214 |
if (conf.tip) {
|
215 |
tip.html(trigger.data("title"));
|
@@ -218,96 +218,96 @@
|
|
218 |
// onBeforeShow
|
219 |
e = e || $.Event();
|
220 |
e.type = "onBeforeShow";
|
221 |
-
fire.trigger(e, [pos]);
|
222 |
if (e.isDefaultPrevented()) { return self; }
|
223 |
-
|
224 |
-
|
225 |
// onBeforeShow may have altered the configuration
|
226 |
pos = getPosition(trigger, tip, conf);
|
227 |
-
|
228 |
// set position
|
229 |
-
tip.css({position:'absolute', top: pos.top, left: pos.left});
|
230 |
-
|
231 |
shown = true;
|
232 |
-
|
233 |
-
// invoke effect
|
234 |
effect[0].call(self, function() {
|
235 |
e.type = "onShow";
|
236 |
shown = 'full';
|
237 |
-
fire.trigger(e);
|
238 |
-
});
|
|
|
239 |
|
240 |
-
|
241 |
-
// tooltip events
|
242 |
var event = conf.events.tooltip.split(/,\s*/);
|
243 |
|
244 |
if (!tip.data("__set")) {
|
245 |
-
|
246 |
-
tip.bind(event[0], function() {
|
247 |
clearTimeout(timer);
|
248 |
clearTimeout(pretimer);
|
249 |
});
|
250 |
-
|
251 |
-
if (event[1] && !trigger.is("input:not(:checkbox, :radio), textarea")) {
|
252 |
tip.bind(event[1], function(e) {
|
253 |
-
|
254 |
// being moved to the trigger element
|
255 |
if (e.relatedTarget != trigger[0]) {
|
256 |
trigger.trigger(evt[1].split(" ")[0]);
|
257 |
}
|
258 |
-
});
|
259 |
-
}
|
260 |
-
|
261 |
tip.data("__set", true);
|
262 |
}
|
263 |
-
|
264 |
return self;
|
265 |
},
|
266 |
-
|
267 |
hide: function(e) {
|
268 |
|
269 |
if (!tip || !self.isShown()) { return self; }
|
270 |
-
|
271 |
// onBeforeHide
|
272 |
e = e || $.Event();
|
273 |
e.type = "onBeforeHide";
|
274 |
-
fire.trigger(e);
|
275 |
if (e.isDefaultPrevented()) { return; }
|
276 |
-
|
277 |
shown = false;
|
278 |
-
|
279 |
effects[conf.effect][1].call(self, function() {
|
280 |
e.type = "onHide";
|
281 |
-
fire.trigger(e);
|
282 |
});
|
283 |
-
|
284 |
return self;
|
285 |
},
|
286 |
-
|
287 |
isShown: function(fully) {
|
288 |
-
return fully ? shown == 'full' : shown;
|
289 |
},
|
290 |
-
|
291 |
getConf: function() {
|
292 |
-
return conf;
|
293 |
},
|
294 |
-
|
295 |
getTip: function() {
|
296 |
-
return tip;
|
297 |
},
|
298 |
-
|
299 |
getTrigger: function() {
|
300 |
-
return trigger;
|
301 |
-
}
|
302 |
|
303 |
-
});
|
304 |
|
305 |
-
// callbacks
|
306 |
$.each("onHide,onBeforeShow,onShow,onBeforeHide".split(","), function(i, name) {
|
307 |
-
|
308 |
// configuration
|
309 |
-
if ($.isFunction(conf[name])) {
|
310 |
-
$(self).bind(name, conf[name]);
|
311 |
}
|
312 |
|
313 |
// API
|
@@ -316,34 +316,34 @@
|
|
316 |
return self;
|
317 |
};
|
318 |
});
|
319 |
-
|
320 |
}
|
321 |
-
|
322 |
-
|
323 |
// jQuery plugin implementation
|
324 |
$.fn.tooltip = function(conf) {
|
325 |
-
|
326 |
// return existing instance
|
327 |
var api = this.data("tooltip");
|
328 |
if (api) { return api; }
|
329 |
|
330 |
conf = $.extend(true, {}, $.tools.tooltip.conf, conf);
|
331 |
-
|
332 |
// position can also be given as string
|
333 |
if (typeof conf.position == 'string') {
|
334 |
-
conf.position = conf.position.split(/,?\s/);
|
335 |
}
|
336 |
-
|
337 |
// install tooltip for each entry in jQuery object
|
338 |
this.each(function() {
|
339 |
-
api = new Tooltip($(this), conf);
|
340 |
-
$(this).data("tooltip", api);
|
341 |
});
|
342 |
-
|
343 |
-
return conf.api ? api: this;
|
344 |
};
|
345 |
-
|
346 |
}) (jQuery);
|
347 |
|
348 |
-
|
349 |
|
1 |
/**
|
2 |
+
* @license
|
3 |
* jQuery Tools @VERSION Tooltip - UI essentials
|
4 |
+
*
|
5 |
* NO COPYRIGHTS OR LICENSES. DO WHAT YOU LIKE.
|
6 |
+
*
|
7 |
* http://flowplayer.org/tools/tooltip/
|
8 |
*
|
9 |
* Since: November 2008
|
10 |
+
* Date: @DATE
|
11 |
*/
|
12 |
+
(function($) {
|
13 |
// static constructs
|
14 |
$.tools = $.tools || {version: '@VERSION'};
|
15 |
+
|
16 |
$.tools.tooltip = {
|
17 |
+
|
18 |
+
conf: {
|
19 |
+
|
20 |
// default effect variables
|
21 |
+
effect: 'toggle',
|
22 |
fadeOutSpeed: "fast",
|
23 |
predelay: 0,
|
24 |
delay: 30,
|
25 |
+
opacity: 1,
|
26 |
tip: 0,
|
27 |
+
|
28 |
// 'top', 'bottom', 'right', 'left', 'center'
|
29 |
+
position: ['top', 'center'],
|
30 |
offset: [0, 0],
|
31 |
relative: false,
|
32 |
offsetParent: null,
|
33 |
cancelDefault: true,
|
34 |
+
|
35 |
+
// type to event mapping
|
36 |
events: {
|
37 |
def: "mouseenter,mouseleave",
|
38 |
input: "focus,blur",
|
39 |
widget: "focus mouseenter,blur mouseleave",
|
40 |
tooltip: "mouseenter,mouseleave"
|
41 |
},
|
42 |
+
|
43 |
// 1.2
|
44 |
+
layout: '<div></div>',
|
45 |
tipClass: 'tooltip'
|
46 |
},
|
47 |
+
|
48 |
addEffect: function(name, loadFn, hideFn) {
|
49 |
+
effects[name] = [loadFn, hideFn];
|
50 |
+
}
|
51 |
};
|
52 |
+
|
53 |
+
|
54 |
+
var effects = {
|
55 |
+
toggle: [
|
56 |
+
function(done) {
|
57 |
var conf = this.getConf(), tip = this.getTip(), o = conf.opacity;
|
58 |
if (o < 1) { tip.css({opacity: o}); }
|
59 |
tip.show();
|
60 |
done.call();
|
61 |
},
|
62 |
+
|
63 |
+
function(done) {
|
64 |
this.getTip().hide();
|
65 |
done.call();
|
66 |
+
}
|
67 |
],
|
68 |
+
|
69 |
fade: [
|
70 |
+
function(done) {
|
71 |
var conf = this.getConf();
|
72 |
+
this.getTip().fadeTo(conf.fadeInSpeed, conf.opacity, done);
|
73 |
+
},
|
74 |
+
function(done) {
|
75 |
+
this.getTip().fadeOut(this.getConf().fadeOutSpeed, done);
|
76 |
+
}
|
77 |
+
]
|
78 |
+
};
|
79 |
+
|
80 |
+
|
81 |
+
/* calculate tip position relative to the trigger */
|
82 |
+
function getPosition(trigger, tip, conf) {
|
83 |
+
|
84 |
+
|
85 |
+
// get origin top/left position
|
86 |
+
var top = conf.relative ? trigger.position().top : trigger.offset().top,
|
87 |
left = conf.relative ? trigger.position().left : trigger.offset().left,
|
88 |
pos = conf.position[0];
|
89 |
|
90 |
top -= tip.outerHeight() - conf.offset[0];
|
91 |
left += trigger.outerWidth() + conf.offset[1];
|
92 |
+
|
93 |
// iPad position fix
|
94 |
if (/iPad/i.test(navigator.userAgent)) {
|
95 |
top -= $(window).scrollTop();
|
96 |
}
|
97 |
+
|
98 |
+
// adjust Y
|
99 |
var height = tip.outerHeight() + trigger.outerHeight();
|
100 |
if (pos == 'center') { top += height / 2; }
|
101 |
if (pos == 'bottom') { top += height; }
|
102 |
+
|
103 |
+
|
104 |
// adjust X
|
105 |
+
pos = conf.position[1];
|
106 |
var width = tip.outerWidth() + trigger.outerWidth();
|
107 |
if (pos == 'center') { left -= width / 2; }
|
108 |
+
if (pos == 'left') { left -= width; }
|
109 |
+
|
110 |
return {top: top, left: left};
|
111 |
+
}
|
112 |
+
|
113 |
+
|
114 |
|
|
|
|
|
115 |
function Tooltip(trigger, conf) {
|
116 |
|
117 |
+
var self = this,
|
118 |
fire = trigger.add(self),
|
119 |
tip,
|
120 |
timer = 0,
|
121 |
+
pretimer = 0,
|
122 |
title = trigger.attr("title"),
|
123 |
tipAttr = trigger.attr("data-tooltip"),
|
124 |
effect = effects[conf.effect],
|
125 |
shown,
|
126 |
+
|
127 |
// get show/hide configuration
|
128 |
+
isInput = trigger.is(":input"),
|
129 |
+
isWidget = isInput && trigger.is(":checkbox, :radio, select, :button, :submit"),
|
130 |
type = trigger.attr("type"),
|
131 |
+
evt = conf.events[type] || conf.events[isInput ? (isWidget ? 'widget' : 'input') : 'def'];
|
132 |
+
|
133 |
+
|
134 |
// check that configuration is sane
|
135 |
+
if (!effect) { throw "Nonexistent effect \"" + conf.effect + "\""; }
|
136 |
+
|
137 |
+
evt = evt.split(/,\s*/);
|
138 |
+
if (evt.length != 2) { throw "Tooltip: bad events configuration for " + type; }
|
139 |
+
|
140 |
+
|
141 |
+
// trigger --> show
|
142 |
trigger.bind(evt[0], function(e) {
|
143 |
|
144 |
clearTimeout(timer);
|
145 |
if (conf.predelay) {
|
146 |
+
pretimer = setTimeout(function() { self.show(e); }, conf.predelay);
|
147 |
+
|
148 |
} else {
|
149 |
+
self.show(e);
|
150 |
}
|
151 |
+
|
152 |
// trigger --> hide
|
153 |
}).bind(evt[1], function(e) {
|
154 |
clearTimeout(pretimer);
|
155 |
if (conf.delay) {
|
156 |
+
timer = setTimeout(function() { self.hide(e); }, conf.delay);
|
157 |
+
|
158 |
} else {
|
159 |
+
self.hide(e);
|
160 |
}
|
161 |
+
|
162 |
+
});
|
163 |
+
|
164 |
+
|
165 |
// remove default title
|
166 |
+
if (title && conf.cancelDefault) {
|
167 |
trigger.removeAttr("title");
|
168 |
+
trigger.data("title", title);
|
169 |
+
}
|
170 |
+
|
171 |
$.extend(self, {
|
172 |
+
|
173 |
+
show: function(e) {
|
174 |
|
175 |
// tip not initialized yet
|
176 |
if (!tip) {
|
177 |
+
|
178 |
+
// data-tooltip
|
179 |
if (tipAttr) {
|
180 |
tip = $(tipAttr);
|
181 |
|
182 |
// single tip element for all
|
183 |
+
} else if (conf.tip) {
|
184 |
tip = $(conf.tip).eq(0);
|
185 |
+
|
186 |
// autogenerated tooltip
|
187 |
+
} else if (title) {
|
188 |
tip = $(conf.layout).addClass(conf.tipClass).appendTo(document.body)
|
189 |
.hide().append(title);
|
190 |
|
191 |
// manual tooltip
|
192 |
+
} else {
|
193 |
+
tip = trigger.next();
|
194 |
if (!tip.length) { tip = trigger.parent().next(); }
|
195 |
+
|
196 |
}
|
197 |
+
|
198 |
if (!tip.length) { throw "Cannot find tooltip for " + trigger; }
|
199 |
+
|
200 |
if (conf.offsetParent) {
|
201 |
tip.appendTo($(conf.offsetParent));
|
202 |
}
|
203 |
+
}
|
204 |
+
|
205 |
+
if (self.isShown()) { return self; }
|
206 |
+
|
207 |
// stop previous animation
|
208 |
+
tip.stop(true, true);
|
209 |
+
|
210 |
// get position
|
211 |
+
var pos = getPosition(trigger, tip, conf);
|
212 |
+
|
213 |
// restore title for single tooltip element
|
214 |
if (conf.tip) {
|
215 |
tip.html(trigger.data("title"));
|
218 |
// onBeforeShow
|
219 |
e = e || $.Event();
|
220 |
e.type = "onBeforeShow";
|
221 |
+
fire.trigger(e, [pos]);
|
222 |
if (e.isDefaultPrevented()) { return self; }
|
223 |
+
|
224 |
+
|
225 |
// onBeforeShow may have altered the configuration
|
226 |
pos = getPosition(trigger, tip, conf);
|
227 |
+
|
228 |
// set position
|
229 |
+
tip.css({position:'absolute', top: pos.top, left: pos.left});
|
230 |
+
|
231 |
shown = true;
|
232 |
+
|
233 |
+
// invoke effect
|
234 |
effect[0].call(self, function() {
|
235 |
e.type = "onShow";
|
236 |
shown = 'full';
|
237 |
+
fire.trigger(e);
|
238 |
+
});
|
239 |
+
|
240 |
|
241 |
+
// tooltip events
|
|
|
242 |
var event = conf.events.tooltip.split(/,\s*/);
|
243 |
|
244 |
if (!tip.data("__set")) {
|
245 |
+
|
246 |
+
tip.bind(event[0], function() {
|
247 |
clearTimeout(timer);
|
248 |
clearTimeout(pretimer);
|
249 |
});
|
250 |
+
|
251 |
+
if (event[1] && !trigger.is("input:not(:checkbox, :radio), textarea")) {
|
252 |
tip.bind(event[1], function(e) {
|
253 |
+
|
254 |
// being moved to the trigger element
|
255 |
if (e.relatedTarget != trigger[0]) {
|
256 |
trigger.trigger(evt[1].split(" ")[0]);
|
257 |
}
|
258 |
+
});
|
259 |
+
}
|
260 |
+
|
261 |
tip.data("__set", true);
|
262 |
}
|
263 |
+
|
264 |
return self;
|
265 |
},
|
266 |
+
|
267 |
hide: function(e) {
|
268 |
|
269 |
if (!tip || !self.isShown()) { return self; }
|
270 |
+
|
271 |
// onBeforeHide
|
272 |
e = e || $.Event();
|
273 |
e.type = "onBeforeHide";
|
274 |
+
fire.trigger(e);
|
275 |
if (e.isDefaultPrevented()) { return; }
|
276 |
+
|
277 |
shown = false;
|
278 |
+
|
279 |
effects[conf.effect][1].call(self, function() {
|
280 |
e.type = "onHide";
|
281 |
+
fire.trigger(e);
|
282 |
});
|
283 |
+
|
284 |
return self;
|
285 |
},
|
286 |
+
|
287 |
isShown: function(fully) {
|
288 |
+
return fully ? shown == 'full' : shown;
|
289 |
},
|
290 |
+
|
291 |
getConf: function() {
|
292 |
+
return conf;
|
293 |
},
|
294 |
+
|
295 |
getTip: function() {
|
296 |
+
return tip;
|
297 |
},
|
298 |
+
|
299 |
getTrigger: function() {
|
300 |
+
return trigger;
|
301 |
+
}
|
302 |
|
303 |
+
});
|
304 |
|
305 |
+
// callbacks
|
306 |
$.each("onHide,onBeforeShow,onShow,onBeforeHide".split(","), function(i, name) {
|
307 |
+
|
308 |
// configuration
|
309 |
+
if ($.isFunction(conf[name])) {
|
310 |
+
$(self).bind(name, conf[name]);
|
311 |
}
|
312 |
|
313 |
// API
|
316 |
return self;
|
317 |
};
|
318 |
});
|
319 |
+
|
320 |
}
|
321 |
+
|
322 |
+
|
323 |
// jQuery plugin implementation
|
324 |
$.fn.tooltip = function(conf) {
|
325 |
+
|
326 |
// return existing instance
|
327 |
var api = this.data("tooltip");
|
328 |
if (api) { return api; }
|
329 |
|
330 |
conf = $.extend(true, {}, $.tools.tooltip.conf, conf);
|
331 |
+
|
332 |
// position can also be given as string
|
333 |
if (typeof conf.position == 'string') {
|
334 |
+
conf.position = conf.position.split(/,?\s/);
|
335 |
}
|
336 |
+
|
337 |
// install tooltip for each entry in jQuery object
|
338 |
this.each(function() {
|
339 |
+
api = new Tooltip($(this), conf);
|
340 |
+
$(this).data("tooltip", api);
|
341 |
});
|
342 |
+
|
343 |
+
return conf.api ? api: this;
|
344 |
};
|
345 |
+
|
346 |
}) (jQuery);
|
347 |
|
348 |
+
|
349 |
|
skin/adminhtml/default/default/aoe_scheduler/StyleSheet/bars.css
CHANGED
@@ -13,7 +13,19 @@
|
|
13 |
.bar-lightgray,
|
14 |
.bar-lightgray span,
|
15 |
.bar-gray,
|
16 |
-
.bar-gray span {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
17 |
|
18 |
.bar-red { background-position:0 0; }
|
19 |
.bar-red span { background-position:100% 0; padding:0 7px 0 0; }
|
13 |
.bar-lightgray,
|
14 |
.bar-lightgray span,
|
15 |
.bar-gray,
|
16 |
+
.bar-gray span {
|
17 |
+
display:block;
|
18 |
+
height:16px;
|
19 |
+
background-image:url(../Images/bg_notifications.gif);
|
20 |
+
background-repeat:no-repeat;
|
21 |
+
font:bold 10px/16px Arial, Helvetica, sans-serif;
|
22 |
+
text-transform:uppercase;
|
23 |
+
text-align:center;
|
24 |
+
padding:0 0 0 7px;
|
25 |
+
margin:1px 0;
|
26 |
+
white-space:nowrap;
|
27 |
+
color:#fff;
|
28 |
+
}
|
29 |
|
30 |
.bar-red { background-position:0 0; }
|
31 |
.bar-red span { background-position:100% 0; padding:0 7px 0 0; }
|
skin/adminhtml/default/default/aoe_scheduler/StyleSheet/instructions.css
ADDED
@@ -0,0 +1,17 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
.configuration label {
|
2 |
+
padding-left: 5px;
|
3 |
+
}
|
4 |
+
|
5 |
+
h5 {
|
6 |
+
margin-top: 20px;
|
7 |
+
}
|
8 |
+
|
9 |
+
pre {
|
10 |
+
padding: 5px 10px;
|
11 |
+
background-color: black;
|
12 |
+
color: #eee;
|
13 |
+
font-size: 15px;
|
14 |
+
overflow: auto;
|
15 |
+
word-wrap: normal;
|
16 |
+
white-space: pre;
|
17 |
+
}
|
skin/adminhtml/default/default/aoe_scheduler/StyleSheet/timeline.css
CHANGED
@@ -1,134 +1,158 @@
|
|
1 |
#contentwrapper {
|
2 |
-
|
3 |
-
|
4 |
}
|
5 |
|
6 |
#contentcolumn {
|
7 |
-
|
8 |
}
|
9 |
|
10 |
#leftcolumn {
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
}
|
15 |
|
16 |
.configuration {
|
17 |
-
|
18 |
}
|
19 |
|
20 |
#leftcolumn .row {
|
21 |
-
|
22 |
-
|
23 |
}
|
24 |
|
25 |
#leftcolumn .row.hours {
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
}
|
31 |
|
32 |
#now {
|
33 |
-
|
34 |
-
|
35 |
-
|
36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
}
|
38 |
|
39 |
div.row {
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
}
|
45 |
|
46 |
div.timeline-box {
|
47 |
-
|
48 |
}
|
49 |
|
50 |
div.timeline-panel {
|
51 |
-
|
52 |
-
|
53 |
-
|
54 |
}
|
55 |
|
56 |
div.timeline {
|
57 |
-
|
58 |
}
|
59 |
|
60 |
div.hours {
|
61 |
-
|
62 |
}
|
63 |
|
64 |
.detailwrap {
|
65 |
-
|
66 |
-
|
67 |
}
|
68 |
|
69 |
.details {
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
74 |
-
|
75 |
}
|
76 |
|
77 |
.details pre {
|
78 |
-
|
79 |
-
|
80 |
-
|
81 |
-
|
82 |
-
|
83 |
}
|
84 |
|
85 |
.hour {
|
86 |
-
|
87 |
-
|
88 |
-
|
|
|
|
|
|
|
|
|
89 |
}
|
90 |
|
91 |
.details-headline {
|
92 |
-
|
93 |
-
|
|
|
94 |
}
|
95 |
|
96 |
.details-headline h3 {
|
97 |
-
|
98 |
-
|
99 |
-
|
100 |
}
|
101 |
|
102 |
.details-content {
|
103 |
-
|
104 |
}
|
105 |
|
106 |
.details-content td.label {
|
107 |
-
|
108 |
}
|
109 |
|
110 |
.caption-container {
|
111 |
-
|
112 |
-
|
113 |
}
|
114 |
|
115 |
-
.task {
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
}
|
121 |
|
122 |
.status {
|
123 |
-
|
124 |
}
|
125 |
|
126 |
.details-headline, .task { background-image: url(../Images/gradient.png); }
|
127 |
|
|
|
|
|
|
|
|
|
|
|
128 |
.details-headline.success, .task.success { background-color: #36B963; }
|
|
|
129 |
.details-headline.pending, .task.pending { background-color: #A9ABA8; }
|
130 |
-
.details-headline.error, .task.error { background-color: #E41300; }
|
131 |
.details-headline.missed, .task.missed { background-color: #F75300; }
|
132 |
-
.details-headline.running, .task.running { background-color: #FE9D00; }
|
|
|
|
|
|
|
|
|
|
|
133 |
|
134 |
-
div.task.active, div.task:hover { background-color: #00A2FA; }
|
1 |
#contentwrapper {
|
2 |
+
float: left;
|
3 |
+
width: 100%;
|
4 |
}
|
5 |
|
6 |
#contentcolumn {
|
7 |
+
margin-left: 300px; /* Set left margin to LeftColumnWidth */
|
8 |
}
|
9 |
|
10 |
#leftcolumn {
|
11 |
+
float: left;
|
12 |
+
width: 300px; /* Width of left column */
|
13 |
+
margin-left: -100%;
|
14 |
}
|
15 |
|
16 |
.configuration {
|
17 |
+
padding: 5px;
|
18 |
}
|
19 |
|
20 |
#leftcolumn .row {
|
21 |
+
background-color: #F6F6F6;
|
22 |
+
border-right: 1px solid #DADFE0;
|
23 |
}
|
24 |
|
25 |
#leftcolumn .row.hours {
|
26 |
+
background-color: transparent;
|
27 |
+
text-align: right;
|
28 |
+
padding-right: 5px;
|
29 |
+
font-size: 10px;
|
30 |
}
|
31 |
|
32 |
#now {
|
33 |
+
position: absolute;
|
34 |
+
width: 1px;
|
35 |
+
height: 100%;
|
36 |
+
background-color: red;
|
37 |
+
}
|
38 |
+
|
39 |
+
#now .arrow {
|
40 |
+
background-image: url(../Images/red.png);
|
41 |
+
width: 16px;
|
42 |
+
height: 16px;
|
43 |
+
position: absolute;
|
44 |
+
top: -3px;
|
45 |
+
left: -7.5px;
|
46 |
}
|
47 |
|
48 |
div.row {
|
49 |
+
border-bottom: 1px solid #DADFE0;
|
50 |
+
padding: 0;
|
51 |
+
margin: 0;
|
52 |
+
height: 40px;
|
53 |
}
|
54 |
|
55 |
div.timeline-box {
|
56 |
+
overflow: auto;
|
57 |
}
|
58 |
|
59 |
div.timeline-panel {
|
60 |
+
background-image: url(../Images/hour.gif);
|
61 |
+
border-right: 1px solid black;
|
62 |
+
position: relative;
|
63 |
}
|
64 |
|
65 |
div.timeline {
|
66 |
+
position: relative;
|
67 |
}
|
68 |
|
69 |
div.hours {
|
70 |
+
height: 20px;
|
71 |
}
|
72 |
|
73 |
.detailwrap {
|
74 |
+
width: 100%;
|
75 |
+
height: 100%;
|
76 |
}
|
77 |
|
78 |
.details {
|
79 |
+
display: none;
|
80 |
+
background-color: white;
|
81 |
+
border: 1px solid black;
|
82 |
+
z-index: 1000;
|
83 |
+
max-width: 400px;
|
84 |
}
|
85 |
|
86 |
.details pre {
|
87 |
+
background-color: white;
|
88 |
+
overflow: auto;
|
89 |
+
max-width: 385px;
|
90 |
+
font-size: 8pt;
|
91 |
+
line-height: 9pt;
|
92 |
}
|
93 |
|
94 |
.hour {
|
95 |
+
width: 240px;
|
96 |
+
float: left;
|
97 |
+
}
|
98 |
+
|
99 |
+
.hour span {
|
100 |
+
margin-left: 5px;
|
101 |
+
font-weight: bold;
|
102 |
}
|
103 |
|
104 |
.details-headline {
|
105 |
+
width: 100%;
|
106 |
+
height: 24px;
|
107 |
+
background-position: top center;
|
108 |
}
|
109 |
|
110 |
.details-headline h3 {
|
111 |
+
padding: 2px 5px;
|
112 |
+
color: white;
|
113 |
+
margin: 0;
|
114 |
}
|
115 |
|
116 |
.details-content {
|
117 |
+
padding: 5px;
|
118 |
}
|
119 |
|
120 |
.details-content td.label {
|
121 |
+
font-weight: bold;
|
122 |
}
|
123 |
|
124 |
.caption-container {
|
125 |
+
width: 500px;
|
126 |
+
overflow: hidden;
|
127 |
}
|
128 |
|
129 |
+
.task, .estimation {
|
130 |
+
position: absolute;
|
131 |
+
height: 24px;
|
132 |
+
top: 8px;
|
133 |
+
cursor: default;
|
134 |
}
|
135 |
|
136 |
.status {
|
137 |
+
width: 100px;
|
138 |
}
|
139 |
|
140 |
.details-headline, .task { background-image: url(../Images/gradient.png); }
|
141 |
|
142 |
+
.details-headline.error, .task.error,
|
143 |
+
.details-headline.killed, .task.killed,
|
144 |
+
.details-headline.died, .task.died,
|
145 |
+
.details-headline.gone, .task.gone { background-color: #E41300; }
|
146 |
+
|
147 |
.details-headline.success, .task.success { background-color: #36B963; }
|
148 |
+
.details-headline.nothing, .task.nothing { background-color: #92D6A9; }
|
149 |
.details-headline.pending, .task.pending { background-color: #A9ABA8; }
|
|
|
150 |
.details-headline.missed, .task.missed { background-color: #F75300; }
|
151 |
+
.details-headline.running, .task.running { background-color: #FE9D00 !important; }
|
152 |
+
.details-headline.running, .task.running { background-image: url(../Images/animation.gif); background-position: center; background-repeat: repeat-x; }
|
153 |
+
|
154 |
+
.estimation { background-color: #B1C5D1; }
|
155 |
+
.estimation { background-image: url(../Images/animation2.gif); background-position: center; background-repeat: repeat-x; }
|
156 |
+
|
157 |
|
158 |
+
div.task.active, div.task:hover { background-color: #00A2FA; }
|