Zero1_Crondoctor - Version 1.0.10

Version Notes

ACL issue fix

Download this release

Release Info

Developer Arron Moss
Extension Zero1_Crondoctor
Version 1.0.10
Comparing to
See all releases


Code changes from version 1.0.6 to 1.0.10

Files changed (26) hide show
  1. app/code/community/Zero1/Crondoctor/Block/Adminhtml/Visual.php +11 -0
  2. app/code/community/Zero1/Crondoctor/Block/Adminhtml/Visual/Form.php +125 -0
  3. app/code/community/Zero1/Crondoctor/Block/Adminhtml/Visual/Lag.php +54 -0
  4. app/code/community/Zero1/Crondoctor/Block/Adminhtml/Visual/Visual.php +179 -0
  5. app/code/community/Zero1/Crondoctor/Model/Observer.php +220 -47
  6. app/code/community/Zero1/Crondoctor/Model/Observer/Zombie.php +66 -0
  7. app/code/community/Zero1/Crondoctor/Model/Schedule.php +9 -0
  8. app/code/community/Zero1/Crondoctor/Model/Schedule/Job.php +182 -0
  9. app/code/community/Zero1/Crondoctor/Model/Schedule/Job/Process.php +52 -0
  10. app/code/community/Zero1/Crondoctor/Model/Visual/Schedule.php +102 -0
  11. app/code/community/Zero1/Crondoctor/Model/Visual/Schedule/Job.php +182 -0
  12. app/code/community/Zero1/Crondoctor/Model/Visual/Schedule/Job/Process.php +54 -0
  13. app/code/community/Zero1/Crondoctor/README.md +59 -0
  14. app/code/community/Zero1/Crondoctor/controllers/Adminhtml/Crondoctor/VisualController.php +65 -0
  15. app/code/community/Zero1/Crondoctor/controllers/Adminhtml/CrondoctorController.php +5 -0
  16. app/code/community/Zero1/Crondoctor/etc/adminhtml.xml +12 -0
  17. app/code/community/Zero1/Crondoctor/etc/config.xml +34 -7
  18. app/code/community/Zero1/Crondoctor/etc/system.xml +19 -0
  19. app/code/community/Zero1/Crondoctor/sql/zero1_crondoctor_setup/upgrade-1.0.7-1.0.8.php +19 -0
  20. app/code/community/Zero1/Crondoctor/sql/zero1_crondoctor_setup/upgrade-1.0.8-1.0.9.php +16 -0
  21. app/design/adminhtml/default/default/layout/crondoctor/crondoctor.xml +6 -0
  22. app/design/adminhtml/default/default/template/crondoctor/visual/container.phtml +14 -0
  23. app/design/adminhtml/default/default/template/crondoctor/visual/visual.phtml +81 -0
  24. package.xml +5 -5
  25. zero1/cron.php +95 -0
  26. zero1/cron.sh +28 -0
app/code/community/Zero1/Crondoctor/Block/Adminhtml/Visual.php ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Zero1_Crondoctor_Block_Adminhtml_Visual extends Mage_Adminhtml_Block_Widget_Container
3
+ {
4
+ protected $_template = 'crondoctor/visual/container.phtml';
5
+ protected $_headerText = 'Cron Doctor Jobs Visual';
6
+
7
+ public function getFormHtml(){
8
+ return $this->getLayout()->createBlock('zero1_crondoctor/adminhtml_visual_form')->toHtml();
9
+ }
10
+
11
+ }
app/code/community/Zero1/Crondoctor/Block/Adminhtml/Visual/Form.php ADDED
@@ -0,0 +1,125 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Open Software License (OSL 3.0)
8
+ * that is bundled with this package in the file LICENSE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://opensource.org/licenses/osl-3.0.php
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage_Adminhtml
23
+ * @copyright Copyright (c) 2013 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
25
+ */
26
+
27
+
28
+ /**
29
+ * Sitemap edit form
30
+ *
31
+ * @category Mage
32
+ * @package Mage_Adminhtml
33
+ * @author Magento Core Team <core@magentocommerce.com>
34
+ */
35
+ class Zero1_Crondoctor_Block_Adminhtml_Visual_Form extends Mage_Adminhtml_Block_Widget_Form
36
+ {
37
+ /**
38
+ * Init form
39
+ */
40
+ public function __construct()
41
+ {
42
+ parent::__construct();
43
+ $this->setId('redirection_form');
44
+ $this->setTitle(Mage::helper('adminhtml')->__('Redirection Information'));
45
+ }
46
+
47
+
48
+ protected function _prepareForm()
49
+ {
50
+ $model = new Varien_Object();
51
+
52
+ $form = new Varien_Data_Form(array(
53
+ 'id' => 'visual_form',
54
+ 'action' => $this->getData('action'),
55
+ 'method' => 'post'
56
+ ));
57
+
58
+ $fieldset = $form->addFieldset(
59
+ 'crondoctor_visual_form',
60
+ array('legend' => Mage::helper('adminhtml')->__('Options')));
61
+
62
+
63
+ $fieldset->addField('from', 'text', array(
64
+ 'label' => Mage::helper('adminhtml')->__('From'),
65
+ 'name' => 'from',
66
+ 'required' => true,
67
+ 'note' => Mage::helper('adminhtml')->__('number of hours from now (accepts -ve and +ve'),
68
+ ));
69
+
70
+ $fieldset->addField('to', 'text', array(
71
+ 'label' => Mage::helper('adminhtml')->__('To'),
72
+ 'name' => 'to',
73
+ 'required' => true,
74
+ 'note' => Mage::helper('adminhtml')->__('number of hours from now (accepts -ve and +ve'),
75
+ ));
76
+
77
+ $fieldset->addField('visual_type', 'select', array(
78
+ 'label' => Mage::helper('adminhtml')->__('Visual Type'),
79
+ 'name' => 'visual_type',
80
+ 'required' => true,
81
+ 'values' => array(
82
+ 'stats' => Mage::helper('adminhtml')->__('Stats'),
83
+ 'lag' => Mage::helper('adminhtml')->__('Lag'),
84
+ ),
85
+ ));
86
+
87
+ $fieldset->addField('order_by', 'select', array(
88
+ 'label' => Mage::helper('adminhtml')->__('Order By'),
89
+ 'name' => 'order_by',
90
+ 'required' => true,
91
+ 'values' => array(
92
+ 'expected_runs' => Mage::helper('adminhtml')->__('Expected Runs'),
93
+ ),
94
+ ));
95
+
96
+ $fieldset->addField('submit', 'button', array(
97
+ 'label' => Mage::helper('adminhtml')->__(''),
98
+ 'name' => Mage::helper('adminhtml')->__('aaa'),
99
+ 'class' => 'save',
100
+ 'onclick' => '
101
+ new Ajax.Request(\''.Mage::helper('adminhtml')->getUrl('adminhtml/crondoctor_visual/ajax').'\',
102
+ {
103
+ method: \'get\',
104
+ parameters: Form.serialize(\'visual_form\'),
105
+ onSuccess: function(transport){
106
+ $(\'visual-container\').update(transport.responseText);
107
+ }
108
+ });'
109
+ ));
110
+
111
+ $form->setValues(array('submit' => 'Submit'));
112
+ $form->setUseContainer(true);
113
+ $this->setForm($form);
114
+
115
+ $this->setChild('form_after', $this->getLayout()->createBlock('adminhtml/widget_form_element_dependence')
116
+ ->addFieldMap('visual_type', 'visual_type')
117
+ ->addFieldMap('order_by', 'order_by')
118
+ ->addFieldDependence('order_by', 'visual_type', 'stats')
119
+ );
120
+
121
+
122
+ return parent::_prepareForm();
123
+ }
124
+
125
+ }
app/code/community/Zero1/Crondoctor/Block/Adminhtml/Visual/Lag.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Zero1_Crondoctor_Block_Adminhtml_Visual_Lag extends Mage_Core_Block_Template{
3
+
4
+ protected $_template = 'crondoctor/visual/lag.phtml';
5
+
6
+ public function getLagData(){
7
+ $cronCollection = Mage::getModel('cron/schedule')->getCollection();
8
+ $cronCollection->addFieldToFilter('executed_at', array('gteq' => strftime('%Y-%m-%d %H:%M:%S', $this->getFrom())));
9
+ $cronCollection->addFieldToFilter('executed_at', array('lteq' => strftime('%Y-%m-%d %H:%M:%S', $this->getTo())));
10
+ $cronCollection->addFieldToFilter('job_code', array('nin' => array('enterprise_refresh_index')));
11
+
12
+
13
+ // return $cronCollection->getSelectSql(true);
14
+ if(!$cronCollection->count()){
15
+ return array();
16
+ }
17
+
18
+ $data = array();
19
+ $values = array(); //used to calc rolling average
20
+
21
+ /* @var $cronJob Mage_Cron_Model_Schedule */
22
+ foreach($cronCollection as $cronJob){
23
+
24
+ $hour = date('H', strtotime($cronJob->getExecutedAt()));
25
+ $dayOfYear = date('z', strtotime($cronJob->getExecutedAt()));
26
+ $lag = (strtotime($cronJob->getExecutedAt()) - strtotime($cronJob->getScheduledAt()));
27
+
28
+ if(!isset($data[$dayOfYear.'-'.$hour])){
29
+ $data[$dayOfYear.'-'.$hour] = array();
30
+ }
31
+ $values[] = $lag;
32
+ if(count($values) > 10){
33
+ array_shift($values);
34
+ }
35
+
36
+ $avg = array_sum($values) / count($values);
37
+ $executionTime = strtotime($cronJob->getFinishedAt()) - strtotime($cronJob->getExecutedAt());
38
+
39
+ $data[$dayOfYear.'-'.$hour][] =
40
+ array(
41
+ 'code' => $cronJob->getJobCode(),
42
+ 'lag' => $lag,
43
+ 'avg' => $avg,
44
+ 'details' => array(
45
+ 'job code' => $cronJob->getJobCode(),
46
+ 'lag' => floor($lag/ 3600).':'. floor($lag%3600 / 60),
47
+ 'execution time' => floor($executionTime/ 3600).':'. floor($executionTime%3600 / 60),
48
+ ),
49
+ );
50
+ }
51
+
52
+ return $data;
53
+ }
54
+ }
app/code/community/Zero1/Crondoctor/Block/Adminhtml/Visual/Visual.php ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Zero1_Crondoctor_Block_Adminhtml_Visual_Visual extends Mage_Core_Block_Template{
3
+
4
+
5
+ protected $_template = 'crondoctor/visual/visual.phtml';
6
+ private $schedule = null;
7
+
8
+ public function _construct(){
9
+ $this->setMinumMinDisplayWidth(12);
10
+ }
11
+
12
+ /* @return Zero1_Crondoctor_Model_Schedule */
13
+ public function getSchedule(){
14
+ if($this->schedule === null){
15
+ $schedule = Mage::getModel('zero1_crondoctor/visual_schedule', array(
16
+ 'until' => $this->getTo(),
17
+ 'from' => $this->getFrom(),
18
+ ));
19
+
20
+ $schedule->orderBy($this->getOrderBy());
21
+ $this->schedule = $schedule;
22
+ }
23
+ return $this->schedule;
24
+ }
25
+
26
+ public function getJobHtml(Zero1_Crondoctor_Model_Schedule_Job $job, $from, $width){
27
+
28
+ $jobDepthInfo = array();
29
+ $jobHeight = 10;
30
+ $jobProccessHtml = '';
31
+ $html = '';
32
+
33
+ foreach($job->getProcesses() as $timestamp => $process){
34
+
35
+ if(!empty($jobDepthInfo)){
36
+ $depthFound = false;
37
+
38
+ $minValue = $this->getLowestSetValue($process);
39
+
40
+ foreach($jobDepthInfo as $depth => $maxValue){
41
+ if($minValue > $maxValue){
42
+ $depthFound = true;
43
+ break;
44
+ }
45
+ }
46
+
47
+ if(!$depthFound){
48
+ $jobDepth = count($jobDepthInfo);
49
+ }else{
50
+ $jobDepth = $depth;
51
+ }
52
+ }else{
53
+ $jobDepth = 0;
54
+ }
55
+
56
+ $jobProccessHtml .= $this->getJobProcessHtml($process, $jobDepth, $jobHeight, $from);
57
+
58
+ $jobDepthInfo[$jobDepth] = max(
59
+ $process->getExecutedAt(),
60
+ $process->getScheduledAt(),
61
+ $process->getFinishedAt(),
62
+ $process->getCreatedAt()
63
+ );
64
+
65
+ }
66
+
67
+ $html .= '<div style="width: '.$width.'px; height: '.(max(count($jobDepthInfo), 4) * $jobHeight).'px; ';
68
+ $html .= ' border-bottom: solid #000000 2px; background-color: #cccccc; position: relative;">';
69
+ $html .= $jobProccessHtml;
70
+ $html .= '<div style="clear: both;"></div></div>';
71
+
72
+ return array($html, max(count($jobDepthInfo), 4));
73
+ }
74
+
75
+ public function getLowestSetValue(Zero1_Crondoctor_Model_Schedule_Job_Process $process){
76
+ $values = array();
77
+ if($process->getCreatedAt()){
78
+ $values[] = $process->getCreatedAt();
79
+ }
80
+ if($process->getUpdatedAt()){
81
+ $values[] = $process->getUpdatedAt();
82
+ }
83
+ if($process->getScheduledAt()){
84
+ $values[] = $process->getScheduledAt();
85
+ }
86
+ if($process->getExecutedAt()){
87
+ $values[] = $process->getExecutedAt();
88
+ }
89
+ if($process->getFinishedAt()){
90
+ $values[] = $process->getFinishedAt();
91
+ }
92
+ return min($values);
93
+ }
94
+
95
+ public function getJobProcessHtml(Zero1_Crondoctor_Model_Visual_Schedule_Job_Process $process, $jobDepth, $jobHeight, $from){
96
+
97
+ $html = '<div id="schedule-id-'.$process->getScheduleId().'">';
98
+
99
+ if($process->getCreatedAt()){
100
+ //time from when it was created, to the time it is scheduled to start
101
+ $html .= $this->createBlock($jobDepth, $jobHeight, $from, $process->getCreatedAt(), $process->getScheduledAt(), 'yellow');
102
+ if($process->getExecutedAt()){
103
+ //time from when it should have started to the time it actually did start
104
+ $html .= $this->createBlock($jobDepth, $jobHeight, $from, $process->getScheduledAt(), $process->getExecutedAt(), 'orange');
105
+ if($process->getFinishedAt()){
106
+ //job ran good, show execution time
107
+ $html .= $this->createBlock($jobDepth, $jobHeight, $from, $process->getExecutedAt(), $process->getFinishedAt(), 'green');
108
+ }else{
109
+ //job error'd
110
+ $html .= $this->createBlock($jobDepth, $jobHeight, $from, $process->getExecutedAt(), $process->getFinishedAt(), 'pink');
111
+ }
112
+ }else{
113
+ //job missed
114
+ $html .= $this->createBlock($jobDepth, $jobHeight, $from, $process->getScheduledAt(), $process->getUpdatedAt(), 'orange');
115
+
116
+ if($process->getStatus() == Mage_Cron_Model_Schedule::STATUS_MISSED){
117
+ $html .= $this->createBlock($jobDepth, $jobHeight, $from, $process->getUpdatedAt(), $process->getUpdatedAt(), 'red');
118
+ }
119
+ }
120
+ }else{
121
+ //job never got scheduled
122
+ $html .= $this->createBlock($jobDepth, $jobHeight, $from, $process->getScheduledAt(), $process->getExecutedAt(), 'black');
123
+ }
124
+
125
+ //add in data for later
126
+ $html .= '<div style="display: none;">';
127
+ $html .= 'status:'.$process->getStatus().'<br />';
128
+ $html .= 'schedule id: '.$process->getScheduledId().'<br />';
129
+ $html .= 'created at: '.strftime('%Y-%m-%d %H:%M', $process->getCreatedAt()).'<br />';
130
+ $html .= 'scheduled at: '.strftime('%Y-%m-%d %H:%M', $process->getScheduledAt()).'<br />';
131
+ $html .= 'executed at: '.strftime('%Y-%m-%d %H:%M:%S', $process->getExecutedAt()).'<br />';
132
+ $html .= 'finished at: '.strftime('%Y-%m-%d %H:%M:%S', $process->getFinishedAt()).'<br />';
133
+ $html .= 'updated at: '.strftime('%Y-%m-%d %H:%M:%S', $process->getUpdatedAt()).'<br />';
134
+ $html .= '</div>';
135
+
136
+ $html .= '</div>';
137
+
138
+ return $html;
139
+ }
140
+
141
+ public function createBlock($jobDepth, $jobHeight, $from, $startTime, $endTime, $colour){
142
+
143
+ $minMinuteDisplayWith = $this->getMinumMinDisplayWidth();
144
+ if(!$startTime){
145
+ return '';
146
+ }
147
+
148
+ $top = ($jobDepth * $jobHeight);
149
+
150
+ if($from > $startTime){
151
+ $left = 0;
152
+ }else{
153
+ $left =(((($startTime - $from)/60)*$minMinuteDisplayWith) + 20);
154
+ }
155
+
156
+ if(!$endTime){
157
+ $width = 2;
158
+ }else{
159
+ if($from > $startTime){
160
+ $width = max((((($endTime - $from)/60)*$minMinuteDisplayWith)+20), 2);
161
+ }else{
162
+ $width = max(((($endTime - $startTime)/60)*$minMinuteDisplayWith), 2);
163
+ }
164
+ }
165
+
166
+ $html = '<div ';
167
+ $html .= 'style="position: absolute; ';
168
+ $html .= 'height: '.$jobHeight.'px; ';
169
+ $html .= 'top: '.$top.'px; ';
170
+ $html .= 'left: '.$left.'px; ';
171
+ $html .= 'background-color: '.$colour.'; ';
172
+ $html .= 'width: '.$width.'px; ';
173
+ $html .= '"></div>';
174
+
175
+ return $html;
176
+ }
177
+
178
+
179
+ }
app/code/community/Zero1/Crondoctor/Model/Observer.php CHANGED
@@ -1,72 +1,151 @@
1
  <?php
2
  class Zero1_Crondoctor_Model_Observer extends Mage_Cron_Model_Observer
3
  {
4
- const XML_PATH_ZOMBIE_EMAIL_TEMPLATE = 'zero1_crondoctor/settings/zombie_email_template';
5
- const XML_PATH_ZOMBIE_EMAIL_TO = 'zero1_crondoctor/settings/zombie_email';
6
- const XML_PATH_ZOMBIE_TIME = 'zero1_crondoctor/settings/zombie_time';
7
-
8
  const XML_PATH_DEVELOPER_MODE = 'zero1_crondoctor/settings/developer_mode';
9
  const XML_PATH_DEVELOPER_MODE_JOBS = 'zero1_crondoctor/settings/developer_mode_jobs';
 
10
 
11
- protected $_zombieEmailSubject = 'Magento Cron Doctor Zombie Report';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
- public function checkForZombieJobs($observer)
 
 
 
 
 
 
 
14
  {
15
- $storeId = Mage::app()->getStore()->getId();
16
- $to = Mage::getStoreConfig(self::XML_PATH_ZOMBIE_EMAIL_TO, $storeId);
 
17
 
18
- if(!$to) {
19
- return; // No destination address.
20
- }
21
 
22
- $cronjob_collection = Mage::getModel('cron/schedule')->getCollection();
23
- $cronjob_collection->addFieldToFilter('status', array(
24
- 'eq' => Mage_Cron_Model_Schedule::STATUS_RUNNING)
25
- );
 
26
 
27
- $job_list_content = '';
28
- foreach($cronjob_collection as $cronjob) {
29
- if($cronjob->getReportedAt()) {
30
- continue; // No need to report more then once.
 
 
31
  }
 
 
 
 
 
 
32
 
33
- $running_time = ceil((time() - strtotime($cronjob->getExecutedAt())) / 60);
 
 
 
 
 
 
 
 
 
 
34
 
35
- if($running_time >= Mage::getStoreConfig(self::XML_PATH_ZOMBIE_TIME, $storeId)) {
36
- $job_list_content .= '"'.ucwords(str_replace('_', ' ', $cronjob->getJobCode()))."'";
37
- $job_list_content .= ' has been running for '.$running_time.' minutes.<br/>';
 
 
 
38
 
39
- $cronjob->setReportedAt(strftime('%Y-%m-%d %H:%M:%S', time()));
40
- $cronjob->save();
41
- }
42
  }
 
43
 
44
- if($job_list_content != '') {
45
- $translate = Mage::getSingleton('core/translate');
46
- $translate->setTranslateInline(false);
47
-
48
- Mage::getModel('core/email_template')
49
- ->setDesignConfig(array('area' => 'frontend', 'store' => $storeId))
50
- ->sendTransactional(
51
- Mage::getStoreConfig(self::XML_PATH_ZOMBIE_EMAIL_TEMPLATE, $storeId),
52
- Mage::getStoreConfig(Mage_Sales_Model_Order::XML_PATH_EMAIL_IDENTITY, $storeId),
53
- $to,
54
- null,
55
- array(
56
- 'subject' => $this->_zombieEmailSubject,
57
- 'job_list_content' => $job_list_content,
58
- )
59
- );
60
-
61
- $translate->setTranslateInline(true);
62
  }
63
  }
64
 
 
 
 
 
 
65
  protected function _generateJobs($jobs, $exists)
66
  {
67
  $devMode = Mage::getStoreConfig(self::XML_PATH_DEVELOPER_MODE);
68
  $devModeJobs = Mage::getStoreConfig(self::XML_PATH_DEVELOPER_MODE_JOBS);
69
- $devModeJobs = preg_replace('/[^a-z0-9_,]*/', '', $devModeJobs);
70
  $devModeJobs = explode(',', $devModeJobs);
71
 
72
  if ($devMode) {
@@ -79,10 +158,104 @@ class Zero1_Crondoctor_Model_Observer extends Mage_Cron_Model_Observer
79
  }
80
  }
81
  }
82
-
83
  return parent::_generateJobs($devJobs, $exists);
84
  }
85
-
86
  return parent::_generateJobs($jobs, $exists);
87
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
88
  }
1
  <?php
2
  class Zero1_Crondoctor_Model_Observer extends Mage_Cron_Model_Observer
3
  {
 
 
 
 
4
  const XML_PATH_DEVELOPER_MODE = 'zero1_crondoctor/settings/developer_mode';
5
  const XML_PATH_DEVELOPER_MODE_JOBS = 'zero1_crondoctor/settings/developer_mode_jobs';
6
+ const STOP_FLAG_NAME = 'cron_stop.flag';
7
 
8
+ private $emulateRunTimes = array(
9
+ 'core/observer::cleanCache' => 20,
10
+ 'directory/observer::scheduledUpdateCurrencyRates' => 20,
11
+ 'catalog/observer::reindexProductPrices' => 20,
12
+ 'catalogrule/observer::dailyCatalogUpdate' => 20,
13
+ 'sales/observer::cleanExpiredQuotes' => 20,
14
+ 'sales/observer::aggregateSalesReportOrderData' => 20,
15
+ 'sales/observer::aggregateSalesReportShipmentData' => 20,
16
+ 'sales/observer::aggregateSalesReportInvoicedData' => 20,
17
+ 'sales/observer::aggregateSalesReportRefundedData' => 20,
18
+ 'sales/observer::aggregateSalesReportBestsellersData' => 20,
19
+ 'salesrule/observer::aggregateSalesReportCouponsData' => 20,
20
+ 'backup/observer::scheduledBackup' => 20,
21
+ 'paypal/observer::fetchReports' => 20,
22
+ 'log/cron::logClean' => 20,
23
+ 'tax/observer::aggregateSalesReportTaxData' => 20,
24
+ 'productalert/observer::process' => 20,
25
+ 'captcha/observer::deleteOldAttempts' => 20,
26
+ 'captcha/observer::deleteExpiredImages' => 20,
27
+ 'newsletter/observer::scheduledSend' => 20,
28
+ 'persistent/observer::clearExpiredCronJob' => 20,
29
+ 'xmlconnect/observer::scheduledSend' => 20,
30
+ 'awcore/logger::exorcise' => 20,
31
+ 'followupemail/cron::cronJobs' => 20,
32
+ 'followupemail/crondaily::cronJobs' => 20,
33
+ 'amazonpayments/observer::rotateLogfiles' => 20,
34
+ 'amazonpayments/observer::pollObjectsData' => 20,
35
+ 'enterprise_index/observer::refreshIndex' => 20,
36
+ 'enterprise_index/cron::scheduledCleanup' => 20,
37
+ 'enterprise_giftcardaccount/cron::updateStates' => 20,
38
+ 'enterprise_giftcardaccount/pool::applyCodesGeneration' => 20,
39
+ 'enterprise_logging/observer::rotateLogs' => 20,
40
+ 'seoredirects/observer::updateRedirectionsCronJob' => 20,
41
+ 'enterprise_reminder/observer::scheduledNotification' => 20,
42
+ 'enterprise_reward/observer::scheduledBalanceExpireNotification' => 20,
43
+ 'enterprise_reward/observer::scheduledPointsExpiration' => 20,
44
+ 'enterprise_salesarchive/observer::archiveOrdersByCron' => 20,
45
+ 'enterprise_search/indexer_indexer::reindexAll' => 20,
46
+ 'enterprise_targetrule/index::cron' => 20,
47
+ 'enterprise_catalog/index_observer_price::refreshSpecialPrices' => 20,
48
+ 'enterprise_staging/observer::automates' => 20,
49
+ 'M2ePro/Cron_Type_Magento::process' => 1200,
50
+ 'xsitemap/observer::scheduledGenerateSitemaps' => 20,
51
+ 'ops/observer::cleanUpOldPaymentData' => 20,
52
+ 'TrueShopping_UltraNotificationImport/observer::import' => 20,
53
+ 'wsalogger/log::truncate' => 20,
54
+ 'bibit/observer::checkNotify' => 180,
55
+ 'channeladvisor/observer::pullOrders' => 20,
56
+ 'channeladvisor/observer::syncStock' => 120,
57
+ 'channeladvisor/observer::syncProducts' => 20,
58
+ 'channeladvisorcse/observer::scheduledGenerateChanneladvisorCSE' => 20,
59
+ 'zero1_crondoctor/observer::checkForZombieJobs' => 1,
60
+ 'zero1_feeds/observer_temp::mail' => 20,
61
+ 'solvitt/observer::receive' => 90,
62
+ 'rmareport/observer::aggregateRmareportReportRmasData' => 20,
63
+ 'enterprise_pagecache/crawler::crawl' => 20,
64
+ 'enterprise_importexport/observer::scheduledLogClean' => 20,
65
+ 'sitemap/observer::scheduledGenerateSitemaps' => 20,
66
+ 'orderspro/observer::scheduledArchiveOrders' => 20,
67
+ 'enterprise_importexport/observer::processScheduledOperation' => 20,
68
+ );
69
 
70
+ /**
71
+ * Process cron queue
72
+ * Generate tasks schedule
73
+ * Cleanup tasks schedule
74
+ *
75
+ * @param Varien_Event_Observer $observer
76
+ */
77
+ public function dispatch($observer)
78
  {
79
+ /* @var $schedules Mage_Cron_Model_Resource_Schedule_Collection */
80
+ $jobsRoot = Mage::getConfig()->getNode('crontab/jobs');
81
+ $defaultJobsRoot = Mage::getConfig()->getNode('default/crontab/jobs');
82
 
83
+ $nextJob = $this->getNextJob();
 
 
84
 
85
+ $maxIterationCounter = 0;
86
+ while($nextJob->count() == 1 && !$this->shouldStop() && $maxIterationCounter < 1000){
87
+ $maxIterationCounter++;
88
+ /** @var $schedule Mage_Cron_Model_Schedule */
89
+ $schedule = $nextJob->getFirstItem();
90
 
91
+ $jobConfig = $jobsRoot->{$schedule->getJobCode()};
92
+ if (!$jobConfig || !$jobConfig->run) {
93
+ $jobConfig = $defaultJobsRoot->{$schedule->getJobCode()};
94
+ if (!$jobConfig || !$jobConfig->run) {
95
+ continue;
96
+ }
97
  }
98
+ $this->_processJob($schedule, $jobConfig);
99
+
100
+ $this->cleanUpJob($schedule);
101
+ $nextJob = $this->getNextJob();
102
+ }
103
+ }
104
 
105
+ /* @return Mage_Cron_Model_Resource_Schedule_Collection */
106
+ private function getNextJob(){
107
+ /* @var $collection Mage_Cron_Model_Resource_Schedule_Collection */
108
+ $collection = Mage::getModel('cron/schedule')->getCollection()
109
+ ->addFieldToFilter('status', Mage_Cron_Model_Schedule::STATUS_PENDING)
110
+ ->addFieldToFilter('scheduled_at', array('lteq' => strftime('%Y-%m-%d %H:%M:00', time())))
111
+ ->addOrder('scheduled_at', 'ASC');
112
+ $collection->getSelect()->limit(1);
113
+ $collection->load();
114
+ return $collection;
115
+ }
116
 
117
+ private function cleanUpJob(Mage_Cron_Model_Schedule $schedule){
118
+ /* @var $collection Mage_Cron_Model_Resource_Schedule_Collection */
119
+ $collection = Mage::getModel('cron/schedule')->getCollection()
120
+ ->addFieldToFilter('status', Mage_Cron_Model_Schedule::STATUS_PENDING)
121
+ ->addFieldToFilter('job_code', $schedule->getJobCode())
122
+ ->addFieldToFilter('scheduled_at', array('lt' => strftime('%Y-%m-%d %H:%M:00', time())));
123
 
124
+ /* @var $oldSchedule Mage_Cron_Model_Schedule */
125
+ foreach($collection as $oldSchedule){
126
+ $oldSchedule->setStatus(Mage_Cron_Model_Schedule::STATUS_MISSED)->save();
127
  }
128
+ }
129
 
130
+ public static function shouldStop(){
131
+ if(file_exists(self::STOP_FLAG_NAME)){
132
+ //Mage::log('stop flag found', Zend_Log::DEBUG, 'cron.log', true);
133
+ return true;
134
+ }else{
135
+ return false;
 
 
 
 
 
 
 
 
 
 
 
 
136
  }
137
  }
138
 
139
+ public function schedule(Varien_Event_Observer $observer){
140
+ $this->generate();
141
+ $this->cleanup();
142
+ }
143
+
144
  protected function _generateJobs($jobs, $exists)
145
  {
146
  $devMode = Mage::getStoreConfig(self::XML_PATH_DEVELOPER_MODE);
147
  $devModeJobs = Mage::getStoreConfig(self::XML_PATH_DEVELOPER_MODE_JOBS);
148
+ $devModeJobs = preg_replace('/[^A-Za-z0-9_,]*/', '', $devModeJobs);
149
  $devModeJobs = explode(',', $devModeJobs);
150
 
151
  if ($devMode) {
158
  }
159
  }
160
  }
 
161
  return parent::_generateJobs($devJobs, $exists);
162
  }
 
163
  return parent::_generateJobs($jobs, $exists);
164
  }
165
+
166
+ /**
167
+ * Process cron task
168
+ *
169
+ * @param Mage_Cron_Model_Schedule $schedule
170
+ * @param $jobConfig
171
+ * @param bool $isAlways
172
+ * @return Mage_Cron_Model_Observer
173
+ */
174
+ protected function _processJob($schedule, $jobConfig, $isAlways = false)
175
+ {
176
+ $runConfig = $jobConfig->run;
177
+ if (!$isAlways) {
178
+ $scheduleLifetime = Mage::getStoreConfig(self::XML_PATH_SCHEDULE_LIFETIME) * 60;
179
+ $now = time();
180
+ $time = strtotime($schedule->getScheduledAt());
181
+ if ($time > $now) {
182
+ return;
183
+ }
184
+ }
185
+
186
+ $errorStatus = Mage_Cron_Model_Schedule::STATUS_ERROR;
187
+ try {
188
+ if (!$isAlways) {
189
+ if (($scheduleLifetime != 0) && ($time < $now - $scheduleLifetime)) {
190
+ $errorStatus = Mage_Cron_Model_Schedule::STATUS_MISSED;
191
+ Mage::throwException(Mage::helper('cron')->__('Too late for the schedule.'));
192
+ }
193
+ }
194
+ if ($runConfig->model) {
195
+ if (!preg_match(self::REGEX_RUN_MODEL, (string)$runConfig->model, $run)) {
196
+ Mage::throwException(Mage::helper('cron')->__('Invalid model/method definition, expecting "model/class::method".'));
197
+ }
198
+ if (!($model = Mage::getModel($run[1])) || !method_exists($model, $run[2])) {
199
+ Mage::throwException(Mage::helper('cron')->__('Invalid callback: %s::%s does not exist', $run[1], $run[2]));
200
+ }
201
+ $callback = array($model, $run[2]);
202
+ $arguments = array($schedule);
203
+ }
204
+ if (empty($callback)) {
205
+ Mage::throwException(Mage::helper('cron')->__('No callbacks found'));
206
+ }
207
+
208
+ if (!$isAlways) {
209
+ if (!$schedule->tryLockJob()) {
210
+ // another cron started this job intermittently, so skip it
211
+ return;
212
+ }
213
+ /**
214
+ though running status is set in tryLockJob we must set it here because the object
215
+ was loaded with a pending status and will set it back to pending if we don't set it here
216
+ */
217
+ }
218
+
219
+ $schedule
220
+ ->setExecutedAt(strftime('%Y-%m-%d %H:%M:%S', time()))
221
+ ->setStatus(Mage_Cron_Model_Schedule::STATUS_RUNNING)
222
+ ->save();
223
+
224
+ call_user_func_array($callback, $arguments);
225
+
226
+ /* Emulation Code
227
+ *
228
+ $runModel = (string)$jobConfig->run->model;
229
+ if(isset($this->emulateRunTimes[$runModel])){
230
+ Mage::log($schedule->getJobCode().' '.$runModel.'::'.$this->emulateRunTimes[$runModel], Zend_Log::DEBUG, 'cron.log', true);
231
+
232
+ $lateness = time() - strtotime($schedule->getScheduledAt());
233
+ $latenessFactor = 0.01; //for every 100 seconds late it increase the run time by 1
234
+ $runtime = ($lateness * $latenessFactor) + $this->emulateRunTimes[$runModel];
235
+
236
+ if($schedule->getJobCode() == 'enterprise_refresh_index'){
237
+ $runtime = 45;
238
+ }
239
+
240
+ Mage::log('runtime: '.$runtime, Zend_Log::DEBUG, 'cron.log', true);
241
+ sleep($runtime);
242
+ }else{
243
+ Mage::log($runModel.'::no sleep time', Zend_Log::DEBUG, 'cron.log', true);
244
+ }
245
+ */
246
+
247
+ //need to reload so we don't overwrite anything that has changed since running
248
+ $schedule->load($schedule->getId());
249
+ $schedule
250
+ ->setStatus(Mage_Cron_Model_Schedule::STATUS_SUCCESS)
251
+ ->setFinishedAt(strftime('%Y-%m-%d %H:%M:%S', time()));
252
+
253
+ } catch (Exception $e) {
254
+ $schedule->setStatus($errorStatus)
255
+ ->setMessages($e->__toString());
256
+ }
257
+ $schedule->save();
258
+
259
+ return $this;
260
+ }
261
  }
app/code/community/Zero1/Crondoctor/Model/Observer/Zombie.php ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Zero1_Crondoctor_Model_Observer_Zombie{
3
+
4
+ const XML_PATH_ZOMBIE_EMAIL_TEMPLATE = 'zero1_crondoctor/settings/zombie_email_template';
5
+ const XML_PATH_ZOMBIE_EMAIL_TO = 'zero1_crondoctor/settings/zombie_email';
6
+ const XML_PATH_ZOMBIE_TIME = 'zero1_crondoctor/settings/zombie_time';
7
+
8
+ const XML_PATH_DEVELOPER_MODE = 'zero1_crondoctor/settings/developer_mode';
9
+ const XML_PATH_DEVELOPER_MODE_JOBS = 'zero1_crondoctor/settings/developer_mode_jobs';
10
+
11
+ protected $_zombieEmailSubject = 'Magento Cron Doctor Zombie Report';
12
+
13
+ public function checkForZombieJobs(Varien_Event_Observer $event){
14
+ $storeId = Mage::app()->getStore()->getId();
15
+ $to = Mage::getStoreConfig(self::XML_PATH_ZOMBIE_EMAIL_TO, $storeId);
16
+
17
+ if(!$to) {
18
+ return; // No destination address.
19
+ }
20
+
21
+ $cronjob_collection = Mage::getModel('cron/schedule')->getCollection();
22
+ $cronjob_collection->addFieldToFilter('job_code', array('nin' => array(
23
+ 'enterprise_refresh_index', //Ignore always jobs, TODO <<properly
24
+ )));
25
+ $cronjob_collection->addFieldToFilter('status', array(
26
+ 'eq' => Mage_Cron_Model_Schedule::STATUS_RUNNING)
27
+ );
28
+
29
+ $job_list_content = '';
30
+ foreach($cronjob_collection as $cronjob) {
31
+ if($cronjob->getReportedAt()) {
32
+ continue; // No need to report more then once.
33
+ }
34
+
35
+ $running_time = ceil((time() - strtotime($cronjob->getExecutedAt())) / 60);
36
+
37
+ if($running_time >= Mage::getStoreConfig(self::XML_PATH_ZOMBIE_TIME, $storeId)) {
38
+ $job_list_content .= '"'.ucwords(str_replace('_', ' ', $cronjob->getJobCode()))."'";
39
+ $job_list_content .= ' has been running for '.$running_time.' minutes.<br/>';
40
+
41
+ $cronjob->setReportedAt(strftime('%Y-%m-%d %H:%M:%S', time()));
42
+ $cronjob->save();
43
+ }
44
+ }
45
+
46
+ if($job_list_content != '') {
47
+ $translate = Mage::getSingleton('core/translate');
48
+ $translate->setTranslateInline(false);
49
+
50
+ Mage::getModel('core/email_template')
51
+ ->setDesignConfig(array('area' => 'frontend', 'store' => $storeId))
52
+ ->sendTransactional(
53
+ Mage::getStoreConfig(self::XML_PATH_ZOMBIE_EMAIL_TEMPLATE, $storeId),
54
+ Mage::getStoreConfig(Mage_Sales_Model_Order::XML_PATH_EMAIL_IDENTITY, $storeId),
55
+ $to,
56
+ null,
57
+ array(
58
+ 'subject' => $this->_zombieEmailSubject,
59
+ 'job_list_content' => $job_list_content,
60
+ )
61
+ );
62
+
63
+ $translate->setTranslateInline(true);
64
+ }
65
+ }
66
+ }
app/code/community/Zero1/Crondoctor/Model/Schedule.php ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Zero1_Crondoctor_Model_Schedule extends Mage_Cron_Model_Schedule{
3
+
4
+ protected function _beforeSave(){
5
+ parent::_beforeSave();
6
+ $this->setUpdatedAt(strftime('%Y-%m-%d %H:%M:%S', time()));
7
+ }
8
+
9
+ }
app/code/community/Zero1/Crondoctor/Model/Schedule/Job.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Zero1_Crondoctor_Model_Schedule_Job
5
+ *
6
+ * @method string getJobCode()
7
+ * @method Zero1_CronDebug_Model_Schedule_Job setJobConfig(Mage_Core_Model_Config_Element $config)
8
+ * @method Mage_Core_Model_Config_Element getJobConfig()
9
+ * @method Zero1_CronDebug_Model_Schedule_Job setScheduleFrom(int $timestamp)
10
+ * @method int getScheduleFrom()
11
+ * @method Zero1_CronDebug_Model_Schedule_Job setScheduleUntil(int $timestamp)
12
+ * @method int getScheduleUntil()
13
+ * @method Zero1_CronDebug_Model_Schedule_Job setJobType(string $type)
14
+ * @method string getJobType()
15
+ */
16
+ class Zero1_Crondoctor_Model_Schedule_Job extends Mage_Cron_Model_Schedule{
17
+
18
+ private $messages = array();
19
+ private $processes = array();
20
+ private $stats = null;
21
+
22
+ const TYPE_DEFAULT = 'default';
23
+ const TYPE_ALWAYS = 'always';
24
+
25
+ public function __construct(){
26
+
27
+ $args = func_get_args();
28
+ if (empty($args[0])) {
29
+ $args[0] = array();
30
+ }
31
+
32
+ if(!isset($args[0]['job_code'], $args[0]['job_config'], $args[0]['from'], $args[0]['until'])){
33
+ throw new InvalidArgumentException(
34
+ 'Missing an argument, expecting job_code, job_config, from, until. Got: '.json_encode(array_keys($args[0]))
35
+ );
36
+ }
37
+
38
+ $this->setJobCode($args[0]['job_code']);
39
+ $this->setJobConfig($args[0]['job_config']);
40
+ $this->setScheduleFrom($args[0]['from']);
41
+ $this->setScheduleUntil($args[0]['until']);
42
+ $this->setJobType(($this->getCronExpr() == 'always')? self::TYPE_ALWAYS : self::TYPE_DEFAULT);
43
+
44
+ $this->generateSchedule($this->getScheduleFrom(), $this->getScheduleUntil());
45
+ }
46
+
47
+ /** @return String */
48
+ public function getCronExpr(){
49
+
50
+ $cronExpr = $this->getData('cron_expr');
51
+ if($cronExpr === null){
52
+
53
+ $cronExpr = '';
54
+
55
+ if($this->getJobConfig()->schedule->config_path){
56
+ $cronExpr = Mage::getStoreConfig((string)$this->getJobConfig()->schedule->config_path);
57
+ }
58
+
59
+ if(empty($cronExpr) && $this->getJobConfig()->schedule->cron_expr){
60
+ $cronExpr = (string)$this->getJobConfig()->schedule->cron_expr;
61
+ }
62
+
63
+ $this->setData('cron_expr', $cronExpr);
64
+ }
65
+
66
+ return $this->getData('cron_expr');
67
+ }
68
+
69
+ public function getCronExprArr(){
70
+
71
+ if($this->getData('cron_expr_arr') === null){
72
+ $cronExpr = $this->getCronExpr();
73
+ if($cronExpr == '' || $this->getJobType() == self::TYPE_ALWAYS){
74
+ $this->setData('cron_expr_arr', array());
75
+ }else{
76
+ $e = preg_split('#\s+#', $cronExpr, null, PREG_SPLIT_NO_EMPTY);
77
+ if (sizeof($e)<5 || sizeof($e)>6) {
78
+ $this->addScheduleMessage('Cron Expr is invalid: '.$cronExpr);
79
+ $this->setData('cron_expr_arr', array());
80
+ }else{
81
+ $this->setData('cron_expr_arr', $e);
82
+ }
83
+ }
84
+ }
85
+ return $this->getData('cron_expr_arr');
86
+ }
87
+
88
+ private function generateSchedule($from, $until){
89
+
90
+ $cronExprArr = $this->getCronExprArr();
91
+
92
+ if(empty($cronExprArr)){
93
+ return;
94
+ }
95
+
96
+ $from -= ($from % 60); //make sure its on an exact min
97
+
98
+
99
+ while($from < $until){
100
+
101
+ $d = getdate(Mage::getSingleton('core/date')->timestamp($from));
102
+
103
+ $match = $this->matchCronExpression($cronExprArr[0], $d['minutes'])
104
+ && $this->matchCronExpression($cronExprArr[1], $d['hours'])
105
+ && $this->matchCronExpression($cronExprArr[2], $d['mday'])
106
+ && $this->matchCronExpression($cronExprArr[3], $d['mon'])
107
+ && $this->matchCronExpression($cronExprArr[4], $d['wday']);
108
+
109
+ if($match){
110
+
111
+ $this->addProcess(
112
+ Mage::getModel('zero1_crondoctor/schedule_job_process', array(
113
+ 'scheduled_at' => $from,
114
+ 'job' => $this,
115
+ ))
116
+ );
117
+ }
118
+
119
+ $from += 60;
120
+ }
121
+ }
122
+
123
+ private function addProcess(Zero1_Crondoctor_Model_Schedule_Job_Process $process){
124
+ $this->processes[$process->getScheduledAt()] = $process;
125
+ }
126
+ public function getProcesses(){
127
+ return $this->processes;
128
+ }
129
+
130
+
131
+ private function addScheduleMessage($msg){
132
+ $this->messages[] = $msg;
133
+ }
134
+ private function getScheduleMessages($lineBreak = '<br />'){
135
+ return implode($lineBreak, $this->messages);
136
+ }
137
+
138
+ public function getStatValue($key){
139
+ $stats = $this->getStats();
140
+ if(isset($stats[$key], $stats[$key]['value'])){
141
+ return $stats[$key]['value'];
142
+ }else{
143
+ return 0;
144
+ }
145
+ }
146
+
147
+ public function getStats(){
148
+ if(!$this->stats){
149
+ $this->stats = array(
150
+ 'scheduled_misses' => $this->getScheduledMisses(),
151
+ 'expected_runs' => $this->calculateExpectedRuns(),
152
+ );
153
+
154
+ }
155
+ return $this->stats;
156
+ }
157
+
158
+ private function getScheduledMisses(){
159
+ $misses = 0;
160
+ /* @var $process Zero1_Crondoctor_Model_Schedule_Job_Process */
161
+ foreach($this->processes as $timestamp => $process){
162
+ if($process->getStatus() == Mage_Cron_Model_Schedule::STATUS_MISSED){
163
+ $misses++;
164
+ }
165
+ }
166
+ return array(
167
+ 'name' => 'Scheduled Misses',
168
+ 'value' => $misses,
169
+ 'description' => 'Number of times the job has been schedule, but then missed (due to being run too late)',
170
+ );
171
+ }
172
+
173
+ private function calculateExpectedRuns(){
174
+ return array(
175
+ 'name' => 'Expected Runs',
176
+ 'value' => count($this->getProcesses()),
177
+ 'description' => 'Number of times the job should run in a perfect world',
178
+ );
179
+ }
180
+
181
+
182
+ }
app/code/community/Zero1/Crondoctor/Model/Schedule/Job/Process.php ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Zero1_Crondoctor_Model_Schedule_Job_Process
5
+ *
6
+ * @method int getScheduledAt()
7
+ * @method Zero1_CronDebug_Model_Schedule_Job getJob()
8
+ * @method Zero1_CronDebug_Model_Schedule_Job setScheduleId(int $scheduleId)
9
+ * @method int getScheduledId()
10
+ * @method Zero1_CronDebug_Model_Schedule_Job setStatus(string $status)
11
+ * @method string getStatus()
12
+ * @method Zero1_CronDebug_Model_Schedule_Job setMessage(string $msg)
13
+ * @method string getMessage()
14
+ * @method Zero1_CronDebug_Model_Schedule_Job setCreatedAt(int $timestamp)
15
+ * @method int|null getCreatedAt()
16
+ * @method Zero1_CronDebug_Model_Schedule_Job setExecutedAt(int $timestamp)
17
+ * @method int|null getExecutedAt()
18
+ * @method Zero1_CronDebug_Model_Schedule_Job setFinishedAt(int $timestamp)
19
+ * @method int|null getFinishedAt()
20
+ *
21
+ */
22
+ class Zero1_Crondoctor_Model_Schedule_Job_Process extends Varien_Object{
23
+
24
+ public function _construct(){
25
+ parent::_construct();
26
+
27
+ $this->importCronSchedule();
28
+ }
29
+
30
+ private function importCronSchedule(){
31
+ /* @var $cronScheduleCollection Mage_Cron_Model_Resource_Schedule_Collection */
32
+ $cronScheduleCollection = Mage::getModel('cron/schedule')->getCollection();
33
+
34
+ $cronScheduleCollection->addFieldToFilter('job_code', $this->getJob()->getJobCode());
35
+ $cronScheduleCollection->addFieldToFilter('scheduled_at', strftime('%Y-%m-%d %H:%M', $this->getScheduledAt()));
36
+
37
+ if($cronScheduleCollection->count()){
38
+ $this->import($cronScheduleCollection->getFirstItem());
39
+ }
40
+ }
41
+
42
+ private function import(Mage_Cron_Model_Schedule $cron){
43
+
44
+ $this->setScheduleId($cron->getScheduleId());
45
+ $this->setScheduledAt(strtotime($cron->getScheduledAt()));
46
+ $this->setStatus($cron->getStatus());
47
+ $this->setMessage((($cron->getMessages() != null)? $cron->getMessages() : ''));
48
+ $this->setCreatedAt(strtotime($cron->getCreatedAt()));
49
+ $this->setExecutedAt((($cron->getExecutedAt() == null)? null : strtotime($cron->getExecutedAt())));
50
+ $this->setFinishedAt((($cron->getFinishedAt() == null)? null : strtotime($cron->getFinishedAt())));
51
+ }
52
+ }
app/code/community/Zero1/Crondoctor/Model/Visual/Schedule.php ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Zero1_Crondoctor_Model_Visual_Schedule{
3
+
4
+ private $schedule = array();
5
+ private $jobNodes = array(
6
+ 'crontab/jobs',
7
+ 'default/crontab/jobs',
8
+ );
9
+ private $orderingBy = null;
10
+
11
+ public function __construct($args){
12
+ if(isset($args['until'])){
13
+ $this->generateScheduleForDateRange(
14
+ $args['until'],
15
+ (isset($args['from'])? $args['from'] : null)
16
+ );
17
+ }
18
+ }
19
+
20
+ public function generateScheduleForDateRange($until, $from = null){
21
+ $this->cleanUpOldScheduling();
22
+ $from = $this->getFromTime($from);
23
+
24
+ $c = 0;
25
+
26
+ foreach($this->jobNodes as $jobNode){
27
+ $jobNodeConfig = Mage::getConfig()->getNode($jobNode);
28
+ if($jobNodeConfig instanceof Mage_Core_Model_Config_Element){
29
+ foreach($jobNodeConfig->children() as $jobCode => $jobConfig){
30
+
31
+ $validCodes = array(
32
+ //'zero1_cron_debug_5min',
33
+ //'zero1_cron_debug_2min',
34
+ //'enterprise_staging_automates',
35
+ );
36
+ if(array_search($jobCode, $validCodes) === false){
37
+ //continue;
38
+ }
39
+ $this->addToSchedule($this->getScheduleJob(
40
+ $jobCode, $jobConfig, $from, $until
41
+ ));
42
+ if($c++ == 5){
43
+ //break;
44
+ }
45
+ }
46
+ }
47
+ }
48
+ }
49
+
50
+ private function getFromTime($from){
51
+ if($from === null){
52
+ return time();
53
+ }else{
54
+ return $from;
55
+ }
56
+ }
57
+
58
+ private function cleanUpOldScheduling(){
59
+ $this->schedule = array();
60
+ }
61
+
62
+ private function addToSchedule(Zero1_Crondoctor_Model_Visual_Schedule_Job $job){
63
+ $this->schedule[$job->getJobCode()] = $job;
64
+ }
65
+
66
+ /**
67
+ * @param $jobCode
68
+ * @param $jobConfig
69
+ * @param $from
70
+ * @param $until
71
+ * @return Zero1_Crondoctor_Model_Visual_Schedule_Job
72
+ */
73
+ public function getScheduleJob($jobCode, $jobConfig, $from, $until){
74
+
75
+ $scheduleJob = Mage::getModel('zero1_crondoctor/visual_schedule_job', array(
76
+ 'job_code' => $jobCode,
77
+ 'job_config' => $jobConfig,
78
+ 'from' => $from,
79
+ 'until' => $until,
80
+ ));
81
+
82
+ return $scheduleJob;
83
+ }
84
+
85
+ public function getSchedule(){
86
+ return $this->schedule;
87
+ }
88
+
89
+ public function orderBy($orderBy){
90
+ $this->orderingBy = $orderBy;
91
+ uasort($this->schedule, array($this, 'uasort'));
92
+ }
93
+
94
+ private function uasort($a, $b){
95
+ /* @var $a Zero1_Crondoctor_Model_Visual_Schedule_Job */
96
+ /* @var $b Zero1_Crondoctor_Model_Visual_Schedule_Job */
97
+ if ($a->getStatValue($this->orderingBy) == $b->getStatValue($this->orderingBy)) {
98
+ return 0;
99
+ }
100
+ return ($a->getStatValue($this->orderingBy) < $b->getStatValue($this->orderingBy)) ? 1 : -1;
101
+ }
102
+ }
app/code/community/Zero1/Crondoctor/Model/Visual/Schedule/Job.php ADDED
@@ -0,0 +1,182 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Zero1_Crondoctor_Model_Visual_Schedule_Job
5
+ *
6
+ * @method string getJobCode()
7
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setJobConfig(Mage_Core_Model_Config_Element $config)
8
+ * @method Mage_Core_Model_Config_Element getJobConfig()
9
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setScheduleFrom(int $timestamp)
10
+ * @method int getScheduleFrom()
11
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setScheduleUntil(int $timestamp)
12
+ * @method int getScheduleUntil()
13
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setJobType(string $type)
14
+ * @method string getJobType()
15
+ */
16
+ class Zero1_Crondoctor_Model_Visual_Schedule_Job extends Mage_Cron_Model_Schedule{
17
+
18
+ private $messages = array();
19
+ private $processes = array();
20
+ private $stats = null;
21
+
22
+ const TYPE_DEFAULT = 'default';
23
+ const TYPE_ALWAYS = 'always';
24
+
25
+ public function __construct(){
26
+
27
+ $args = func_get_args();
28
+ if (empty($args[0])) {
29
+ $args[0] = array();
30
+ }
31
+
32
+ if(!isset($args[0]['job_code'], $args[0]['job_config'], $args[0]['from'], $args[0]['until'])){
33
+ throw new InvalidArgumentException(
34
+ 'Missing an argument, expecting job_code, job_config, from, until. Got: '.json_encode(array_keys($args[0]))
35
+ );
36
+ }
37
+
38
+ $this->setJobCode($args[0]['job_code']);
39
+ $this->setJobConfig($args[0]['job_config']);
40
+ $this->setScheduleFrom($args[0]['from']);
41
+ $this->setScheduleUntil($args[0]['until']);
42
+ $this->setJobType(($this->getCronExpr() == 'always')? self::TYPE_ALWAYS : self::TYPE_DEFAULT);
43
+
44
+ $this->generateSchedule($this->getScheduleFrom(), $this->getScheduleUntil());
45
+ }
46
+
47
+ /** @return String */
48
+ public function getCronExpr(){
49
+
50
+ $cronExpr = $this->getData('cron_expr');
51
+ if($cronExpr === null){
52
+
53
+ $cronExpr = '';
54
+
55
+ if($this->getJobConfig()->schedule->config_path){
56
+ $cronExpr = Mage::getStoreConfig((string)$this->getJobConfig()->schedule->config_path);
57
+ }
58
+
59
+ if(empty($cronExpr) && $this->getJobConfig()->schedule->cron_expr){
60
+ $cronExpr = (string)$this->getJobConfig()->schedule->cron_expr;
61
+ }
62
+
63
+ $this->setData('cron_expr', $cronExpr);
64
+ }
65
+
66
+ return $this->getData('cron_expr');
67
+ }
68
+
69
+ public function getCronExprArr(){
70
+
71
+ if($this->getData('cron_expr_arr') === null){
72
+ $cronExpr = $this->getCronExpr();
73
+ if($cronExpr == '' || $this->getJobType() == self::TYPE_ALWAYS){
74
+ $this->setData('cron_expr_arr', array());
75
+ }else{
76
+ $e = preg_split('#\s+#', $cronExpr, null, PREG_SPLIT_NO_EMPTY);
77
+ if (sizeof($e)<5 || sizeof($e)>6) {
78
+ $this->addScheduleMessage('Cron Expr is invalid: '.$cronExpr);
79
+ $this->setData('cron_expr_arr', array());
80
+ }else{
81
+ $this->setData('cron_expr_arr', $e);
82
+ }
83
+ }
84
+ }
85
+ return $this->getData('cron_expr_arr');
86
+ }
87
+
88
+ private function generateSchedule($from, $until){
89
+
90
+ $cronExprArr = $this->getCronExprArr();
91
+
92
+ if(empty($cronExprArr)){
93
+ return;
94
+ }
95
+
96
+ $from -= ($from % 60); //make sure its on an exact min
97
+
98
+
99
+ while($from < $until){
100
+
101
+ $d = getdate(Mage::getSingleton('core/date')->timestamp($from));
102
+
103
+ $match = $this->matchCronExpression($cronExprArr[0], $d['minutes'])
104
+ && $this->matchCronExpression($cronExprArr[1], $d['hours'])
105
+ && $this->matchCronExpression($cronExprArr[2], $d['mday'])
106
+ && $this->matchCronExpression($cronExprArr[3], $d['mon'])
107
+ && $this->matchCronExpression($cronExprArr[4], $d['wday']);
108
+
109
+ if($match){
110
+
111
+ $this->addProcess(
112
+ Mage::getModel('zero1_crondoctor/visual_schedule_job_process', array(
113
+ 'scheduled_at' => $from,
114
+ 'job' => $this,
115
+ ))
116
+ );
117
+ }
118
+
119
+ $from += 60;
120
+ }
121
+ }
122
+
123
+ private function addProcess(Zero1_Crondoctor_Model_Visual_Schedule_Job_Process $process){
124
+ $this->processes[$process->getScheduledAt()] = $process;
125
+ }
126
+ public function getProcesses(){
127
+ return $this->processes;
128
+ }
129
+
130
+
131
+ private function addScheduleMessage($msg){
132
+ $this->messages[] = $msg;
133
+ }
134
+ private function getScheduleMessages($lineBreak = '<br />'){
135
+ return implode($lineBreak, $this->messages);
136
+ }
137
+
138
+ public function getStatValue($key){
139
+ $stats = $this->getStats();
140
+ if(isset($stats[$key], $stats[$key]['value'])){
141
+ return $stats[$key]['value'];
142
+ }else{
143
+ return 0;
144
+ }
145
+ }
146
+
147
+ public function getStats(){
148
+ if(!$this->stats){
149
+ $this->stats = array(
150
+ 'scheduled_misses' => $this->getScheduledMisses(),
151
+ 'expected_runs' => $this->calculateExpectedRuns(),
152
+ );
153
+
154
+ }
155
+ return $this->stats;
156
+ }
157
+
158
+ private function getScheduledMisses(){
159
+ $misses = 0;
160
+ /* @var $process Zero1_Crondoctor_Model_Visual_Schedule_Job_Process */
161
+ foreach($this->processes as $timestamp => $process){
162
+ if($process->getStatus() == Mage_Cron_Model_Schedule::STATUS_MISSED){
163
+ $misses++;
164
+ }
165
+ }
166
+ return array(
167
+ 'name' => 'Scheduled Misses',
168
+ 'value' => $misses,
169
+ 'description' => 'Number of times the job has been schedule, but then missed (due to being run too late)',
170
+ );
171
+ }
172
+
173
+ private function calculateExpectedRuns(){
174
+ return array(
175
+ 'name' => 'Expected Runs',
176
+ 'value' => count($this->getProcesses()),
177
+ 'description' => 'Number of times the job should run in a perfect world',
178
+ );
179
+ }
180
+
181
+
182
+ }
app/code/community/Zero1/Crondoctor/Model/Visual/Schedule/Job/Process.php ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+
3
+ /**
4
+ * Class Zero1_Crondoctor_Model_Visual_Schedule_Job_Process
5
+ *
6
+ * @method int getScheduledAt()
7
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job getJob()
8
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setScheduleId(int $scheduleId)
9
+ * @method int getScheduledId()
10
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setStatus(string $status)
11
+ * @method string getStatus()
12
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setMessage(string $msg)
13
+ * @method string getMessage()
14
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setCreatedAt(int $timestamp)
15
+ * @method int|null getCreatedAt()
16
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setExecutedAt(int $timestamp)
17
+ * @method int|null getExecutedAt()
18
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setFinishedAt(int $timestamp)
19
+ * @method int|null getFinishedAt()
20
+ * @method Zero1_Crondoctor_Model_Visual_Schedule_Job setUpdatedAt(int $timestamp)
21
+ * @method int|null getUpdatedAt()
22
+ */
23
+ class Zero1_Crondoctor_Model_Visual_Schedule_Job_Process extends Varien_Object{
24
+
25
+ public function _construct(){
26
+ parent::_construct();
27
+
28
+ $this->importCronSchedule();
29
+ }
30
+
31
+ private function importCronSchedule(){
32
+ /* @var $cronScheduleCollection Mage_Cron_Model_Resource_Schedule_Collection */
33
+ $cronScheduleCollection = Mage::getModel('cron/schedule')->getCollection();
34
+
35
+ $cronScheduleCollection->addFieldToFilter('job_code', $this->getJob()->getJobCode());
36
+ $cronScheduleCollection->addFieldToFilter('scheduled_at', strftime('%Y-%m-%d %H:%M', $this->getScheduledAt()));
37
+
38
+ if($cronScheduleCollection->count()){
39
+ $this->import($cronScheduleCollection->getFirstItem());
40
+ }
41
+ }
42
+
43
+ private function import(Mage_Cron_Model_Schedule $cron){
44
+
45
+ $this->setScheduleId($cron->getScheduleId());
46
+ $this->setScheduledAt(strtotime($cron->getScheduledAt()));
47
+ $this->setStatus($cron->getStatus());
48
+ $this->setMessage((($cron->getMessages() != null)? $cron->getMessages() : ''));
49
+ $this->setCreatedAt(strtotime($cron->getCreatedAt()));
50
+ $this->setExecutedAt((($cron->getExecutedAt() == null)? null : strtotime($cron->getExecutedAt())));
51
+ $this->setFinishedAt((($cron->getFinishedAt() == null)? null : strtotime($cron->getFinishedAt())));
52
+ $this->setUpdatedAt((($cron->getUpdatedAt() == null)? null : strtotime($cron->getUpdatedAt())));
53
+ }
54
+ }
app/code/community/Zero1/Crondoctor/README.md ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h1>Zero1 CronDoctor<h1><hr />
2
+
3
+ <h2>Install Instructions</h2>
4
+ <ol>
5
+ <li>Stop magento core cron, you can do this via either stop the crond service or simply commenting out the relative line in your cron tab</li>
6
+ <li>
7
+ wait for all cron processes to finish<br />
8
+ <pre>watch -n1 "ps aux | grep cron.php | grep -v grep"</pre><br />
9
+ </li>
10
+ <li>Install the module as normal, via FTP/Git/MCM</li>
11
+ <li>Flush both caches</li>
12
+ <li>
13
+ Alter the following configuration options:<br />
14
+ <ul>
15
+ <li>
16
+ System > Configuration > Advanced | System > Cron<br />
17
+ <ul>
18
+ <li>"Generate Schedules Every" = 15</li>
19
+ <li>"Schedule Ahead for" = 20</li>
20
+ <li>"Missed if Not Run Within" = 0</li>
21
+ <li>"History Cleanup Every" = [AS DESIRED]</li>
22
+ <li>"Success History Lifetime" = [AS DESIRED]</li>
23
+ <li>"Failure History Lifetime" = [AS DESIRED]</li>
24
+ </ul>
25
+ </li>
26
+ </ul>
27
+ </li>
28
+ <li>Alter your cron job to point to "zero1/cron.php", and restart crond if needed</li>
29
+ <li>
30
+ confirm jobs are running<br />
31
+ <pre>watch -n1 "ps aux | grep cron.php | grep -v grep"</pre><br />
32
+ on the minute you should see jobs ending with the following:<br />
33
+ <ul>
34
+ <li>-mdefault</li>
35
+ <li>-malways</li>
36
+ <li>-schedule</li>
37
+ <li>-zombie</li>
38
+ </ul>
39
+ </li>
40
+ </ol>
41
+ <hr />
42
+ <h2>Misc</h2>
43
+ <h3>Stopping/Starting Cron after installation</h3>
44
+ If you need to stop cron, from the root of your magento installation:<br />
45
+ <pre>touch zero1/cron_stop.flag</pre><br />
46
+ <pre>watch -n1 "ps aux | grep cron.php | grep -v grep"</pre><br />
47
+ wait until all jobs are finished.<br />
48
+ To restart cron:<br />
49
+ <pre>rm zero1/cron_stop.flag</pre><br />
50
+ you should then be able to see all jobs restarting
51
+ <hr />
52
+ <h3>Disaster Recover</h3>
53
+ If cron is stopped with the cron_stop.flag being in place, then you could end up with jobs the are permanently running.
54
+ This can be observed by going to System > Cron Doctor and checking filtering by status = 'running' if there are more than 2 jobs running there is an issue.<br />
55
+ <b>enterprise_refresh_index</b>: is an 'always' job, if this job hasn't ran since cron has been started again i.e status == running and executed_at is before cron was stopped, the status of this job will need changing (to anything but 'running')<br />
56
+ Every other job is the same principle as enterprise_refresh_index.
57
+
58
+ N.B you should expect 0 - 2 jobs running at anyone time if there are 2 jobs running 1 of them should be enterprise_refresh_index, if this is not the case there is an issue.
59
+ <hr />
app/code/community/Zero1/Crondoctor/controllers/Adminhtml/Crondoctor/VisualController.php ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ class Zero1_Crondoctor_Adminhtml_CronDoctor_VisualController extends Mage_Adminhtml_Controller_Action
3
+ {
4
+ protected function _initAction()
5
+ {
6
+ $this->loadLayout()->_setActiveMenu('system/zero1_crondoctor');
7
+ $this->_title(Mage::helper('zero1_crondoctor')->__('Cron Doctor'));
8
+
9
+ return $this;
10
+ }
11
+
12
+ public function indexAction()
13
+ {
14
+ $this->_initAction()->renderLayout();
15
+ }
16
+
17
+ public function ajaxAction(){
18
+
19
+ $inputFrom = $this->getRequest()->getParam('from', time());
20
+ $inputTo = $this->getRequest()->getParam('to', time());
21
+ $visualType = $this->getRequest()->getParam('visual_type', 'stats');
22
+
23
+ $from = min((int)$inputFrom, (int)$inputTo);
24
+ $to = max((int)$inputTo, (int)$inputFrom);
25
+
26
+ $from = time() + (60 * 60 * $from);
27
+ $to = time() + (60 * 60 * $to);
28
+
29
+ echo 'From: '.strftime('%Y-%m-%d %H:%M:%S', $from).'<br />';
30
+ echo 'To: '.strftime('%Y-%m-%d %H:%M:%S', $to).'<br />';
31
+
32
+
33
+ switch($visualType){
34
+ case 'stats':
35
+ $orderBy = $this->getOrderBy($this->getRequest()->getParam('order_by', null));
36
+ $block = $this->getLayout()->createBlock('zero1_crondoctor/adminhtml_visual_visual');
37
+ $block->setFrom($from);
38
+ $block->setTo($to);
39
+ $block->setOrderBy($orderBy);
40
+ echo $block->toHtml();
41
+ break;
42
+ case 'lag':
43
+ $block = $this->getLayout()->createBlock('zero1_crondoctor/adminhtml_visual_lag');
44
+ $block->setFrom($from);
45
+ $block->setTo($to);
46
+ echo $block->toHtml();
47
+ break;
48
+ }
49
+
50
+ die;
51
+ }
52
+
53
+ private function getOrderBy($orderBy){
54
+ switch($orderBy){
55
+ case 'expected_runs':
56
+ default:
57
+ return 'expected_runs';
58
+ }
59
+ }
60
+
61
+ protected function _isAllowed()
62
+ {
63
+ return Mage::getSingleton('admin/session')->isAllowed('admin/system/zero1_crondoctor/zero1_crondoctor_visual');
64
+ }
65
+ }
app/code/community/Zero1/Crondoctor/controllers/Adminhtml/CrondoctorController.php CHANGED
@@ -70,4 +70,9 @@ class Zero1_Crondoctor_Adminhtml_CrondoctorController extends Mage_Adminhtml_Con
70
 
71
  $this->_redirect('*/*/index');
72
  }
 
 
 
 
 
73
  }
70
 
71
  $this->_redirect('*/*/index');
72
  }
73
+
74
+ protected function _isAllowed()
75
+ {
76
+ return Mage::getSingleton('admin/session')->isAllowed('admin/system/zero1_crondoctor');
77
+ }
78
  }
app/code/community/Zero1/Crondoctor/etc/adminhtml.xml CHANGED
@@ -6,6 +6,12 @@
6
  <title>Cron Doctor</title>
7
  <action>adminhtml/crondoctor</action>
8
  <sort_order>99</sort_order>
 
 
 
 
 
 
9
  </zero1_crondoctor>
10
  </children>
11
  </system>
@@ -19,6 +25,12 @@
19
  <zero1_crondoctor translate="title" module="zero1_crondoctor">
20
  <title>Cron Doctor</title>
21
  <sort_order>99</sort_order>
 
 
 
 
 
 
22
  </zero1_crondoctor>
23
 
24
  <config>
6
  <title>Cron Doctor</title>
7
  <action>adminhtml/crondoctor</action>
8
  <sort_order>99</sort_order>
9
+ <children>
10
+ <zero1_crondoctor_visual translate="title" module="zero1_crondoctor">
11
+ <title>Visual</title>
12
+ <action>adminhtml/crondoctor_visual</action>
13
+ </zero1_crondoctor_visual>
14
+ </children>
15
  </zero1_crondoctor>
16
  </children>
17
  </system>
25
  <zero1_crondoctor translate="title" module="zero1_crondoctor">
26
  <title>Cron Doctor</title>
27
  <sort_order>99</sort_order>
28
+ <children>
29
+ <zero1_crondoctor_visual translate="title" module="zero1_crondoctor">
30
+ <title>Visual</title>
31
+ <action>adminhtml/crondoctor_visual</action>
32
+ </zero1_crondoctor_visual>
33
+ </children>
34
  </zero1_crondoctor>
35
 
36
  <config>
app/code/community/Zero1/Crondoctor/etc/config.xml CHANGED
@@ -2,7 +2,7 @@
2
  <config>
3
  <modules>
4
  <Zero1_Crondoctor>
5
- <version>1.0.6</version>
6
  </Zero1_Crondoctor>
7
  </modules>
8
 
@@ -27,6 +27,7 @@
27
  <cron>
28
  <rewrite>
29
  <observer>Zero1_Crondoctor_Model_Observer</observer>
 
30
  </rewrite>
31
  </cron>
32
  </models>
@@ -74,15 +75,41 @@
74
 
75
  <crontab>
76
  <jobs>
77
- <zero1_crondoctor_zombie_check>
 
 
 
 
 
 
 
 
 
78
  <schedule>
79
- <cron_expr>* * * * *</cron_expr>
80
  </schedule>
81
- <run>
82
- <model>zero1_crondoctor/observer::checkForZombieJobs</model>
83
- </run>
84
- </zero1_crondoctor_zombie_check>
85
  </jobs>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
86
  </crontab>
87
 
88
  <default>
2
  <config>
3
  <modules>
4
  <Zero1_Crondoctor>
5
+ <version>1.0.10</version>
6
  </Zero1_Crondoctor>
7
  </modules>
8
 
27
  <cron>
28
  <rewrite>
29
  <observer>Zero1_Crondoctor_Model_Observer</observer>
30
+ <schedule>Zero1_Crondoctor_Model_Schedule</schedule>
31
  </rewrite>
32
  </cron>
33
  </models>
75
 
76
  <crontab>
77
  <jobs>
78
+ <!--<zero1_crondoctor_zombie_check>-->
79
+ <!--<schedule>-->
80
+ <!--<cron_expr>* * * * *</cron_expr>-->
81
+ <!--</schedule>-->
82
+ <!--<run>-->
83
+ <!--<model>zero1_crondoctor/observer::checkForZombieJobs</model>-->
84
+ <!--</run>-->
85
+ <!--</zero1_crondoctor_zombie_check>-->
86
+ <!-- Job Expr Overrides -->
87
+ <M2ePro_cron>
88
  <schedule>
89
+ <config_path>zero1_crondoctor/job_expr_overrides/m2epro_cron</config_path>
90
  </schedule>
91
+ </M2ePro_cron>
 
 
 
92
  </jobs>
93
+ <events>
94
+ <!-- Schedule Addition -->
95
+ <schedule>
96
+ <observers>
97
+ <cron_observer>
98
+ <class>cron/observer</class>
99
+ <method>schedule</method>
100
+ </cron_observer>
101
+ </observers>
102
+ </schedule>
103
+ <!-- Zombie Checker -->
104
+ <zombie>
105
+ <observers>
106
+ <cron_observer>
107
+ <class>zero1_crondoctor/observer_zombie</class>
108
+ <method>checkForZombieJobs</method>
109
+ </cron_observer>
110
+ </observers>
111
+ </zombie>
112
+ </events>
113
  </crontab>
114
 
115
  <default>
app/code/community/Zero1/Crondoctor/etc/system.xml CHANGED
@@ -70,6 +70,25 @@
70
  </developer_mode_jobs>
71
  </fields>
72
  </settings>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
73
  </groups>
74
  </zero1_crondoctor>
75
  </sections>
70
  </developer_mode_jobs>
71
  </fields>
72
  </settings>
73
+ <job_expr_overrides translate="label">
74
+ <label>Job Overrides</label>
75
+ <frontend_type>text</frontend_type>
76
+ <sort_order>2</sort_order>
77
+ <show_in_default>1</show_in_default>
78
+ <show_in_website>0</show_in_website>
79
+ <show_in_store>0</show_in_store>
80
+ <fields>
81
+ <m2epro_cron translate="label comment">
82
+ <label>M2ePro_cron</label>
83
+ <comment><![CDATA[If left blank, config xml value will be used.]]></comment>
84
+ <frontend_type>text</frontend_type>
85
+ <sort_order>1</sort_order>
86
+ <show_in_default>1</show_in_default>
87
+ <show_in_website>0</show_in_website>
88
+ <show_in_store>0</show_in_store>
89
+ </m2epro_cron>
90
+ </fields>
91
+ </job_expr_overrides>
92
  </groups>
93
  </zero1_crondoctor>
94
  </sections>
app/code/community/Zero1/Crondoctor/sql/zero1_crondoctor_setup/upgrade-1.0.7-1.0.8.php ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var $installer Mage_Core_Model_Resource_Setup */
3
+ $installer = $this;
4
+ $installer->startSetup();
5
+
6
+ /**
7
+ * Change columns
8
+ */
9
+ $installer->getConnection()->addColumn(
10
+ $installer->getTable('cron/schedule'),
11
+ 'updated_at',
12
+ array(
13
+ 'type' => Varien_Db_Ddl_Table::TYPE_TIMESTAMP,
14
+ 'nullable' => true,
15
+ 'comment' => 'Updated At'
16
+ )
17
+ );
18
+
19
+ $installer->endSetup();
app/code/community/Zero1/Crondoctor/sql/zero1_crondoctor_setup/upgrade-1.0.8-1.0.9.php ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var $installer Mage_Core_Model_Resource_Setup */
3
+ $installer = $this;
4
+ $installer->startSetup();
5
+
6
+ $installer->getConnection()->addColumn(
7
+ $installer->getTable('cron/schedule'),
8
+ 'reported_at',
9
+ array(
10
+ 'type' => Varien_Db_Ddl_Table::TYPE_TIMESTAMP,
11
+ 'nullable' => true,
12
+ 'comment' => 'Reported At'
13
+ )
14
+ );
15
+
16
+ $installer->endSetup();
app/design/adminhtml/default/default/layout/crondoctor/crondoctor.xml CHANGED
@@ -11,4 +11,10 @@
11
  <block type="zero1_crondoctor/adminhtml_crondoctor_notice" name="zero1_crondoctor_notice" template="crondoctor/notice.phtml" />
12
  </reference>
13
  </default>
 
 
 
 
 
 
14
  </layout>
11
  <block type="zero1_crondoctor/adminhtml_crondoctor_notice" name="zero1_crondoctor_notice" template="crondoctor/notice.phtml" />
12
  </reference>
13
  </default>
14
+
15
+ <adminhtml_crondoctor_visual_index>
16
+ <reference name="content">
17
+ <block type="zero1_crondoctor/adminhtml_visual" name="zero1_crondoctor_visual_container" />
18
+ </reference>
19
+ </adminhtml_crondoctor_visual_index>
20
  </layout>
app/design/adminhtml/default/default/template/crondoctor/visual/container.phtml ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php /* @var $this Zero1_Crondoctor_Block_Adminhtml_Visual */
2
+ ?>
3
+ <div class="content-header">
4
+ <table cellspacing="0">
5
+ <tr>
6
+ <td><h3><?php echo $this->getHeaderText() ?></h3></td>
7
+ <td class="form-buttons">
8
+ <?php echo $this->getButtonsHtml() ?>
9
+ </td>
10
+ </tr>
11
+ </table>
12
+ </div>
13
+ <?php echo $this->getFormHtml() ?>
14
+ <div id="visual-container"></div>
app/design/adminhtml/default/default/template/crondoctor/visual/visual.phtml ADDED
@@ -0,0 +1,81 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /* @var $this Zero1_Crondoctor_Block_Adminhtml_Visual_Visual */
3
+ /* @var $schedule Zero1_Crondoctor_Model_Schedule */
4
+ $schedule = $this->getSchedule();
5
+ $to = $this->getTo();
6
+ $from = $this->getFrom();
7
+
8
+ $defaultWidth = 1200;
9
+ $minMinuteDisplayWith = 12;
10
+
11
+ $diference = ($to - $from) / 60;
12
+ $diference -= ($diference % 60);
13
+
14
+ $naturalWidth = ($diference * $minMinuteDisplayWith);
15
+ $naturalWidth += 40; //padding for time display
16
+ /* @var $job Zero1_Crondoctor_Model_Schedule_Job */
17
+ /* @var $process Zero1_Crondoctor_Model_Schedule_Job_Process */
18
+
19
+ $jobHtml = array();
20
+ foreach($schedule->getSchedule() as $jobCode => $job){
21
+ list($html, $maxDepth) = $this->getJobHtml($job, $from, $naturalWidth);
22
+ $jobHtml[$jobCode] = array(
23
+ 'html' => $html,
24
+ 'max_depth' => $maxDepth,
25
+ );
26
+ }
27
+
28
+
29
+ ?>
30
+ <div style="margin: 20px;">
31
+ <div style="float: left; padding: 40px 0 0 0; border: solid 1px #000000; border-right-width: 0; overflow: visible;">
32
+ <?php foreach($schedule->getSchedule() as $jobCode => $job): ?>
33
+ <div style="height: <?php echo $jobHtml[$jobCode]['max_depth'] * 10; ?>px; border-bottom: solid #000000 2px; background-color: orange; overflow: visible; padding: 0 10px;">
34
+ <?php echo $jobCode; ?><br />
35
+ (<?php echo $job->getCronExpr(); ?>)
36
+ </div>
37
+ <?php endforeach; ?>
38
+ </div>
39
+ <div style="float: left; border: solid 1px #000000;
40
+ width: <?php echo $defaultWidth; ?>px;
41
+ overflow-x: scroll;"
42
+ >
43
+ <div class="timebar" style="width: <?php echo $naturalWidth; ?>px;">
44
+ <div style="float: left; width: 40px; height: 40px; overflow: hidden;">
45
+
46
+ <div style="width: 40px; height: 20px; text-align: center;"><?php echo strftime('%H:%M', $from); ?></div>
47
+ <div style="float: left; width: 18px; height: 20px; border-right: solid; 2px #000000;"></div>
48
+ <div style="float: left; width: 18px; height: 20px; border-left: solid; 2px; #000000;"></div>
49
+
50
+ </div>
51
+ <?php
52
+ $oneHour = (60 * 60);
53
+ for($timeCount = ($from + $oneHour); $timeCount <= $to; $timeCount += $oneHour): ?>
54
+ <div style="float: left; width: <?php echo (($minMinuteDisplayWith * 60) - 40); ?>px; height: 40px;"></div>
55
+
56
+ <div style="float: left; width: 40px; height: 40px; overflow: hidden;">
57
+
58
+ <div style="width: 40px; height: 20px; text-align: center;"><?php echo strftime('%H:%M', $timeCount); ?></div>
59
+ <div style="float: left; width: 18px; height: 20px; border-right: solid #000000 2px;"></div>
60
+ <div style="float: left; width: 18px; height: 20px; border-left: solid #000000 2px;"></div>
61
+
62
+ </div>
63
+ <?php endfor; ?>
64
+ <div style="clear: both;"></div>
65
+ </div>
66
+
67
+ <?php foreach($schedule->getSchedule() as $jobCode => $job): ?>
68
+ <?php echo $jobHtml[$jobCode]['html']; ?>
69
+ <?php endforeach; ?>
70
+ </div>
71
+ <div style="float: left; padding: 40px 0 0 0; border: solid 1px #000000; border-left-width: 0; overflow: visible; width: 200px;">
72
+ <?php foreach($schedule->getSchedule() as $jobCode => $job): ?>
73
+ <div style="height: <?php echo $jobHtml[$jobCode]['max_depth'] * 10; ?>px; border-bottom: solid #000000 2px; overflow-y: scroll; padding: 0 10px;">
74
+ <?php foreach($job->getStats() as $key => $data): ?>
75
+ <?php echo $data['name'].': '.$data['value'].'<br />'; ?>
76
+ <?php //echo $data['description']; ?>
77
+ <?php endforeach; ?>
78
+ </div>
79
+ <?php endforeach; ?>
80
+ </div>
81
+ </div>
package.xml CHANGED
@@ -1,7 +1,7 @@
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Zero1_Crondoctor</name>
4
- <version>1.0.6</version>
5
  <stability>stable</stability>
6
  <license uri="http://shop.zero1.co.uk/LICENSE.txt">Commercial</license>
7
  <channel>community</channel>
@@ -9,11 +9,11 @@
9
  <summary>Crondoctor</summary>
10
  <description>Crondoctor&#xD;
11
  </description>
12
- <notes>Fixed an issue causing zombie cron job observer reporting an error.</notes>
13
  <authors><author><name>Arron Moss</name><user>zero1limited</user><email>arron.moss@zero1.co.uk</email></author></authors>
14
- <date>2013-10-14</date>
15
- <time>15:10:56</time>
16
- <contents><target name="magecommunity"><dir name="Zero1"><dir name="Crondoctor"><dir name="Block"><dir name="Adminhtml"><dir name="Crondoctor"><file name="Grid.php" hash="ff758796ef8d66b388e0b1d60d73f92e"/><file name="Notice.php" hash="6b4c20f69f875fa3410a34acc8d2209a"/></dir><file name="Crondoctor.php" hash="6fa8fd6eee1dd35d9b82f861238738c1"/></dir></dir><dir name="Helper"><file name="Data.php" hash="4b85072f6909ba3f083c99814103a92a"/></dir><dir name="Model"><file name="Observer.php" hash="0ff5bde873ad40ade469820909d1a723"/></dir><dir name="controllers"><dir name="Adminhtml"><file name="CrondoctorController.php" hash="209983731264467ce1c2d3d7989039ac"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="56bcc9e463c033655658d711e3549636"/><file name="config.xml" hash="cf5de2ed2d1e978776171b09b63b8f2b"/><file name="system.xml" hash="0ab03980d1259c4d0a497020ca707baf"/></dir><dir name="sql"><dir name="zero1_crondoctor_setup"><file name="install-1.0.0.php" hash="af6b55e68b5c327efcf58452719d359e"/><file name="mysql4-install-1.0.0.php" hash="af6b55e68b5c327efcf58452719d359e"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Zero1_Crondoctor.xml" hash="3ccefc4ae6efeecc2bfd7df6a59264f1"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="crondoctor"><file name="crondoctor.xml" hash="61e541ccd7156e8e7dad6197792b4315"/></dir></dir><dir name="template"><dir name="crondoctor"><file name="notice.phtml" hash="95575d8f09e623c05c0de950677346ce"/></dir></dir></dir></dir></dir></target><target name="magelocale"><dir name="en_US"><dir name="template"><dir name="email"><file name="crondoctor_zombieemail.html" hash="ad7b0695ba42ddaea95e50b8d9859370"/></dir></dir></dir></target></contents>
17
  <compatible/>
18
  <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
19
  </package>
1
  <?xml version="1.0"?>
2
  <package>
3
  <name>Zero1_Crondoctor</name>
4
+ <version>1.0.10</version>
5
  <stability>stable</stability>
6
  <license uri="http://shop.zero1.co.uk/LICENSE.txt">Commercial</license>
7
  <channel>community</channel>
9
  <summary>Crondoctor</summary>
10
  <description>Crondoctor&#xD;
11
  </description>
12
+ <notes>ACL issue fix</notes>
13
  <authors><author><name>Arron Moss</name><user>zero1limited</user><email>arron.moss@zero1.co.uk</email></author></authors>
14
+ <date>2016-03-08</date>
15
+ <time>09:28:41</time>
16
+ <contents><target name="magecommunity"><dir name="Zero1"><dir name="Crondoctor"><dir name="Block"><dir name="Adminhtml"><dir name="Crondoctor"><file name="Grid.php" hash="ff758796ef8d66b388e0b1d60d73f92e"/><file name="Notice.php" hash="6b4c20f69f875fa3410a34acc8d2209a"/></dir><file name="Crondoctor.php" hash="6fa8fd6eee1dd35d9b82f861238738c1"/><dir name="Visual"><file name="Form.php" hash="2df5703fc827781478fd07d1e28296a5"/><file name="Lag.php" hash="533573979e5c75c7f99b9a0a81cafbf1"/><file name="Visual.php" hash="48d7f42c7469621307134e864033ef1b"/></dir><file name="Visual.php" hash="2b2f08b18ca4c97716bfb9a3cb16c92c"/></dir></dir><dir name="Helper"><file name="Data.php" hash="4b85072f6909ba3f083c99814103a92a"/></dir><dir name="Model"><dir name="Observer"><file name="Zombie.php" hash="325e2b06c599fba12377f5be17738299"/></dir><file name="Observer.php" hash="be0e3653a5187ac5723386f6033f88a1"/><dir name="Schedule"><dir name="Job"><file name="Process.php" hash="57cf2b34b80024af1090e6f5e46c170f"/></dir><file name="Job.php" hash="2f2df91e0aa5f628aa196586d3d4c568"/></dir><file name="Schedule.php" hash="a3f86374eab1771d844a84c9184f3bbe"/><dir name="Visual"><dir name="Schedule"><dir name="Job"><file name="Process.php" hash="4d0b95ec31c3c7b7fcba86ff89159dc6"/></dir><file name="Job.php" hash="c2ea122f797ca735de59cfd1ff9bfc96"/></dir><file name="Schedule.php" hash="f5e4e64b1fe0ccb6bdd3ef380496e2b3"/></dir></dir><file name="README.md" hash="c1514c3ca5f67fefee310915c99e7772"/><dir name="controllers"><dir name="Adminhtml"><dir name="Crondoctor"><file name="VisualController.php" hash="935cb373b65cc193cb6740ab4a140495"/></dir><file name="CrondoctorController.php" hash="fef96b78d8443ed6494a3b19affed8ec"/></dir></dir><dir name="etc"><file name="adminhtml.xml" hash="b32ed969146550cc2a98d4b133eb4543"/><file name="config.xml" hash="30009266047b8057de21eba9e7de1ab1"/><file name="system.xml" hash="b6251a8f7b3ff0a63241d6d421e59eaf"/></dir><dir name="sql"><dir name="zero1_crondoctor_setup"><file name="install-1.0.0.php" hash="af6b55e68b5c327efcf58452719d359e"/><file name="mysql4-install-1.0.0.php" hash="af6b55e68b5c327efcf58452719d359e"/><file name="upgrade-1.0.7-1.0.8.php" hash="4431ab57cc541f82a75734eefa8e494a"/><file name="upgrade-1.0.8-1.0.9.php" hash="a72fda900430ce3cde8cc6e636138ecb"/></dir></dir></dir></dir></target><target name="mageetc"><dir name="modules"><file name="Zero1_Crondoctor.xml" hash="3ccefc4ae6efeecc2bfd7df6a59264f1"/></dir></target><target name="magedesign"><dir name="adminhtml"><dir name="default"><dir name="default"><dir name="layout"><dir name="crondoctor"><file name="crondoctor.xml" hash="3ec02e0cf305a7bd347c78a8420e799f"/></dir></dir><dir name="template"><dir name="crondoctor"><file name="notice.phtml" hash="95575d8f09e623c05c0de950677346ce"/><dir name="visual"><file name="container.phtml" hash="5242e2d186173d5b3bf779b1f14024c8"/><file name="visual.phtml" hash="d9f16c4ac6d847846335b494b39c0169"/></dir></dir></dir></dir></dir></dir></target><target name="magelocale"><dir name="en_US"><dir name="template"><dir name="email"><file name="crondoctor_zombieemail.html" hash="ad7b0695ba42ddaea95e50b8d9859370"/></dir></dir></dir></target><target name="mage"><dir name="zero1"><file name="cron.php" hash="dc8160c75340b38bc5b1554d7a0bf0e2"/><file name="cron.sh" hash="c752a6d282579c3f2ed2e7a2e5864f28"/></dir></target></contents>
17
  <compatible/>
18
  <dependencies><required><php><min>5.2.0</min><max>6.0.0</max></php></required></dependencies>
19
  </package>
zero1/cron.php ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?php
2
+ /**
3
+ * Magento Enterprise Edition
4
+ *
5
+ * NOTICE OF LICENSE
6
+ *
7
+ * This source file is subject to the Magento Enterprise Edition License
8
+ * that is bundled with this package in the file LICENSE_EE.txt.
9
+ * It is also available through the world-wide-web at this URL:
10
+ * http://www.magentocommerce.com/license/enterprise-edition
11
+ * If you did not receive a copy of the license and are unable to
12
+ * obtain it through the world-wide-web, please send an email
13
+ * to license@magentocommerce.com so we can send you a copy immediately.
14
+ *
15
+ * DISCLAIMER
16
+ *
17
+ * Do not edit or add to this file if you wish to upgrade Magento to newer
18
+ * versions in the future. If you wish to customize Magento for your
19
+ * needs please refer to http://www.magentocommerce.com for more information.
20
+ *
21
+ * @category Mage
22
+ * @package Mage
23
+ * @copyright Copyright (c) 2013 Magento Inc. (http://www.magentocommerce.com)
24
+ * @license http://www.magentocommerce.com/license/enterprise-edition
25
+ */
26
+
27
+ // Change current directory to the directory of current script
28
+ chdir(dirname(__FILE__));
29
+
30
+ require '../app/Mage.php';
31
+
32
+ if (!Mage::isInstalled()) {
33
+ echo "Application is not installed yet, please complete install wizard first.";
34
+ exit;
35
+ }
36
+
37
+ // Only for urls
38
+ // Don't remove this
39
+ $_SERVER['SCRIPT_NAME'] = str_replace(basename(__FILE__), 'index.php', $_SERVER['SCRIPT_NAME']);
40
+ $_SERVER['SCRIPT_FILENAME'] = str_replace(basename(__FILE__), 'index.php', $_SERVER['SCRIPT_FILENAME']);
41
+
42
+ Mage::app('admin')->setUseSessionInUrl(false);
43
+
44
+ if(Zero1_Crondoctor_Model_Observer::shouldStop()){
45
+ echo 'Stop Flag Found'.PHP_EOL;
46
+ exit;
47
+ }
48
+
49
+ umask(0);
50
+
51
+ $disabledFuncs = explode(',', ini_get('disable_functions'));
52
+ $isShellDisabled = is_array($disabledFuncs) ? in_array('shell_exec', $disabledFuncs) : true;
53
+ $isShellDisabled = (stripos(PHP_OS, 'win') === false) ? $isShellDisabled : true;
54
+
55
+ $cronModes = array(
56
+ 'always' => 'always',
57
+ 'default' => 'default',
58
+ 'schedule' => 'schedule',
59
+ 'zombie' => 'zombie',
60
+ );
61
+
62
+ try {
63
+ if (stripos(PHP_OS, 'win') === false) {
64
+ $options = getopt('m::');
65
+ if (isset($options['m'])) {
66
+
67
+ if(isset($cronModes[$options['m']])){
68
+ $cronMode = $cronModes[$options['m']];
69
+ }else{
70
+ Mage::throwException('Unrecognized cron mode was defined');
71
+ }
72
+
73
+ } else if (!$isShellDisabled) {
74
+ $fileName = basename(__FILE__);
75
+ $baseDir = dirname(__FILE__);
76
+
77
+ foreach($cronModes as $key => $mode){
78
+ shell_exec("/bin/sh $baseDir/cron.sh $fileName -m".$key." 1 > /dev/null 2>&1 &");
79
+ }
80
+ exit;
81
+ }
82
+ }
83
+
84
+ Mage::getConfig()->init()->loadEventObservers('crontab');
85
+ Mage::app()->addEventArea('crontab');
86
+ if ($isShellDisabled) {
87
+ Mage::dispatchEvent('always');
88
+ Mage::dispatchEvent('default');
89
+ } else {
90
+ Mage::dispatchEvent($cronMode);
91
+ }
92
+ } catch (Exception $e) {
93
+ Mage::printException($e);
94
+ exit(1);
95
+ }
zero1/cron.sh ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/sh
2
+ # location of the php binary
3
+ if [ ! "$1" = "" ] ; then
4
+ CRONSCRIPT=$1
5
+ else
6
+ CRONSCRIPT=cron.php
7
+ fi
8
+
9
+ MODE=""
10
+ if [ ! "$2" = "" ] ; then
11
+ MODE=" $2"
12
+ fi
13
+
14
+ PHP_BIN=`which php`
15
+
16
+ # absolute path to magento installation
17
+ INSTALLDIR=`echo $0 | sed 's/cron\.sh//g'`
18
+
19
+ # prepend the intallation path if not given an absolute path
20
+ if [ "$INSTALLDIR" != "" -a "`expr index $CRONSCRIPT /`" != "1" ];then
21
+ if ! ps auxwww | grep "$INSTALLDIR$CRONSCRIPT$MODE" | grep -v grep 1>/dev/null 2>/dev/null ; then
22
+ $PHP_BIN $INSTALLDIR$CRONSCRIPT$MODE &
23
+ fi
24
+ else
25
+ if ! ps auxwww | grep "$CRONSCRIPT$MODE" | grep -v grep | grep -v cron.sh 1>/dev/null 2>/dev/null ; then
26
+ $PHP_BIN $CRONSCRIPT$MODE &
27
+ fi
28
+ fi