/[drupal]/contributions/modules/cronplus/cronplus.module
ViewVC logotype

Contents of /contributions/modules/cronplus/cronplus.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.10 - (show annotations) (download) (as text)
Fri Dec 19 10:09:27 2008 UTC (11 months, 1 week ago) by syscrusher
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-BETA4, HEAD
Branch point for: DRUPAL-6--1
Changes since 1.9: +28 -30 lines
File MIME type: text/x-php
Fixed accidental commit of Drupal 5 version to wrong branch.
1 <?php
2 /**
3 * $Id: cronplus.module,v 1.8.2.4 2008/12/19 09:47:18 syscrusher Exp $
4 *
5 * CronPlus enhances the Drupal cron feature to provide specific calls to
6 * other modules at hourly, daily, weekly, monthly, and yearly intervals.
7 *
8 * Author: Syscrusher (Scott Courtney) License: GPL
9 */
10
11 /**
12 * Implements hook_help().
13 */
14 function cronplus_help($section) {
15 switch ($section) {
16 case 'admin/modules#description':
17 return t('Calls module-specific hourly, daily, weekly, monthly, and yearly functions.');
18 break;
19 }
20 }
21
22 /**
23 * Implements hook_menu().
24 */
25 function cronplus_menu() {
26 $items = array();
27 $items['admin/settings/cronplus'] = array(
28 'title' => t('cronplus'),
29 'page callback' => 'cronplus_settings_main',
30 'description' => 'Select which hour and on what day you would like houly and daily crons to run. Also see the status of cronplus',
31 'access callback' => 'user_access',
32 'access arguments' => array('administer site configuration'),
33 // 'type' => MENU_LOCAL_TASK,
34 );
35 $items['admin/settings/cronplus/main'] = array(
36 'title' => t('configuration'),
37 'type' => MENU_DEFAULT_LOCAL_TASK,
38 'access callback' => 'user_access',
39 'access arguments' => array('administer site configuration'),
40 'weight' => -10,
41 );
42 $items['admin/settings/cronplus/status'] = array(
43 'title' => t('status report'),
44 'page callback' => 'cronplus_settings_status', 'access' => user_access('administer site configuration'),
45 'access callback' => 'user_access',
46 'access arguments' => array('administer site configuration'),
47 'type' => MENU_LOCAL_TASK, 'weight' => 10
48 );
49 return $items;
50 }
51
52 /**
53 * Handle validation of the settings form
54 *
55 * Right now, all the fields are dropdown/select, so validation errors are
56 * unlikely.
57 */
58 function cronplus_settings_form_validate($form_id, $form_values) {
59 $cronplus_preferred_hour = intval($form_values['cronplus_preferred_hour']);
60 if ($cronplus_preferred_hour < 0 || $cronplus_preferred_hour > 23) {
61 form_set_error('cronplus_preferred_hour', t('Hour must be 0 to 23 inclusive'));
62 }
63 $cronplus_preferred_wkdy = intval($form_values['cronplus_preferred_wkdy']);
64 if ($cronplus_preferred_wkdy < 0 || $cronplus_preferred_wkdy > 6) {
65 form_set_error('cronplus_preferred_wkdy', t('Weekday must be 0 to 6 (Sunday through Saturday, respectively) inclusive'));
66 }
67 }
68
69 /**
70 * Handle submission of the settings form
71 */
72 function cronplus_settings_form_submit($form_id, $form_values) {
73 $cronplus_preferred_hour = intval($form_values['cronplus_preferred_hour']);
74 $cronplus_preferred_wkdy = intval($form_values['cronplus_preferred_wkdy']);
75 variable_set('cronplus_preferred_hour', $cronplus_preferred_hour);
76 variable_set('cronplus_preferred_wkdy', $cronplus_preferred_wkdy);
77 drupal_set_message(t('Cronplus settings saved'), 'info');
78 }
79
80 /**
81 * Handle the module's settings.
82 */
83 function cronplus_settings_main() {
84 return drupal_get_form('cronplus_settings_form');
85 }
86
87 function cronplus_settings_form() {
88 $form = array();
89 $form['cronplus_preferred_hour'] = array(
90 '#type' => 'select',
91 '#title' => t('Preferred hour'),
92 '#description' => t('Preferred hour of the day at which daily, weekly, monthly, and yearly jobs will run, if possible. Not guaranteed.'),
93 '#default_value' => intval(variable_get('cronplus_preferred_hour', 0)),
94 '#options' => _cronplus_int_options(0, 23),
95 );
96 $form['cronplus_preferred_wkdy'] = array(
97 '#type' => 'select',
98 '#title' => t('Preferred weekday'),
99 '#description' => t('Preferred day of the week on which weekly, monthly, and yearly jobs will run, if possible. Not guaranteed.'),
100 '#default_value' => intval(variable_get('cronplus_preferred_wkdy', 0)),
101 '#options' => _cronplus_int_options(0, 6, array('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'), TRUE),
102 );
103 $form['cronplus_submit'] = array(
104 '#type' => 'submit',
105 '#value' => t('Save settings'),
106 );
107 return system_settings_form($form);
108 }
109
110 /**
111 * Returns an array of the time periods supported by the module. This can
112 * be used by other modules to offer administrators or users options of
113 * which time period should be attached to a particular feature of the
114 * application.
115 *
116 * The index of the array is the raw period-name as applicable to hook
117 * functions. The values are the translated version of that name.
118 */
119 function cronplus_periods() {
120 $periods = array();
121 foreach (array('hourly', 'daily', 'weekly', 'monthly', 'yearly') as $period) {
122 $periods[$period] = t($period);
123 }
124 return $periods;
125 }
126
127 /**
128 * Returns a really simple form for viewing the cronplus log entries.
129 */
130 function cronplus_logview_form() {
131 $form = array(
132 'submit' => array(
133 '#type' => 'submit',
134 '#value' => t('View cronplus log'),
135 ),
136 );
137 return $form;
138 }
139
140 /**
141 * Handles submit of the log view form
142 */
143 function cronplus_logview_form_submit($form_id, $form_values) {
144 $_SESSION['watchdog_overview_filter'] = 'cronplus';
145 drupal_goto('admin/reports/dblog');
146 }
147
148 /**
149 * Shows a page listing all the places that implement each of the cronplus
150 * hooks.
151 */
152 function cronplus_settings_status() {
153 $html = '<p>'. t('This section lists modules implementing the cronplus hook for each available time period. The listing is purely informational; there are no settings to change here.');
154 $html .= '<p>' . drupal_get_form('cronplus_logview_form');
155 $periods =& cronplus_periods();
156 // Add one special case.
157 $periods['cronplus'] = 'cronplus '. t('(multiplex hook)');
158 foreach ($periods as $period => $period_t) {
159 $html .= '<h2>'. $period_t .'</h2>';
160 $html .= '<div class="cronplus_status">';
161 $hook = ($period == 'cronplus') ? 'cronplus' : 'cronplus_'. $period;
162 $group = '<p>'. t('Hook: {module}_%hook()', array('%hook' => $hook)) .'<p>'. t('Implemented by: ');
163 $modules = module_implements($hook);
164 if (count($modules) > 0) {
165 $group .= '<span class="cronplus_modules">'. implode(', ', $modules) .'</span>';
166 }
167 else {
168 $group .= t('None');
169 }
170 $group .= '<p>'. t('Last invoked: ');
171 if ($period == 'cronplus') {
172 $group .= t('Invoked along with each of the above');
173 }
174 else {
175 $last_invoked = intval(variable_get('cronplus_'. $period .'_last', 0));
176 $group .= $last_invoked ? gmdate('Y-m-d H:i:s', $last_invoked) .'&nbsp;UTC' : t('Never');
177 }
178 $html .= $group .'</div>';
179 }
180 $html .= '<p>';
181 print theme("page", $html);
182 }
183
184 /**
185 * Implements hook_cron. Examines the current date and time, and makes the
186 * appropriate calls to other modules based on what parts of the date and
187 * time have changed since the last run.
188 */
189 function cronplus_cron() {
190 // Gather information...
191 $last_cron = intval(variable_get('cronplus_last_cron', 0));
192 $now = time();
193 $now_wkdy = intval(gmdate('w', $now)); // 0=Sunday
194 // For the $now_week, weeks start on Monday, not Sunday. This is a mismatch
195 // against $now_wkdy, but it doesn't matter because all we are looking for
196 // in $now_week is a *change* -- we don't care about the actual value.
197 $now_week = intval(gmdate('W', $now));
198 $now_month = gmdate('Y-m', $now);
199 $now_year = intval(gmdate('Y', $now));
200 $now_date = gmdate('Y-m-d', $now);
201 $now_hour = intval(gmdate('H', $now));
202
203 // Gather the last time each cronplus hook was invoked
204 $cronplus_hourly_last = intval(variable_get('cronplus_hourly_last', 0));
205 $cronplus_daily_last = intval(variable_get('cronplus_daily_last', 0));
206 $cronplus_weekly_last = intval(variable_get('cronplus_weekly_last', 0));
207 $cronplus_monthly_last = intval(variable_get('cronplus_monthly_last', 0));
208 $cronplus_yearly_last = intval(variable_get('cronplus_yearly_last', 0));
209
210 $last_cron_hour = intval(gmdate('H', $cronplus_hourly_last));
211 $last_cron_day = gmdate('Y-m-d', $cronplus_daily_last);
212 $last_cron_week = intval(gmdate('W', $cronplus_weekly_last));
213 $last_cron_month = gmdate('Y-m', $cronplus_monthly_last);
214 $last_cron_year = intval(gmdate('Y', $cronplus_yearly_last));
215
216 // Preferred hour affects daily, weekly, monthly, and yearly runs
217 $prefer_hour = intval(variable_get('cronplus_preferred_hour', 0));
218 // Preferred wkdy affects weekly, monthly, and yearly runs
219 $prefer_wkdy = intval(variable_get('cronplus_preferred_wkdy', 0));
220 $interval = $now - $last_cron; // Seconds since last run
221
222 // Make the appropriate runs. In each case, we try to run in the preferred
223 // window, but if it's been too long since the last run, we toss the
224 // preference and just get the job done. Note that those "too long" values
225 // are just arbitrarily assigned. They need to be a little bigger than the
226 // real length of the corresponding time interval so that the module will
227 // tend to converge onto the preferred timings over the long term.
228
229 // Hourly
230 if ($now_hour != $last_cron_hour || $interval >= 4000) {
231 cronplus_invoke_hook('hourly', $now, $last_cron);
232 }
233 // Daily
234 if (substr($last_cron_day, 0, 10) != $now_date && ($now_hour >= $prefer_hour || $interval > 96000)) {
235 // The sleep() calls in the following logic ensure that calls to cronplus_invoke_hook()
236 // cannot occur multiple times in the same second, no matter how fast the system is.
237 // This is needed because log entries have a precision of only seconds, and we want
238 // to ensure that they are displayed in a sensible order for the administrator.
239 // Note that it's not needed for the hourly call.
240 //
241 // The maximum combined delay from the sleep() calls is 4 seconds, worst case,
242 // and that is only possible one time per year.
243 sleep(1);
244 cronplus_invoke_hook('daily', $now, $last_cron);
245 }
246 // Weekly
247 if ($now_week != $last_cron_week && (($now_wkdy >= $prefer_wkdy && $now_hour >= $prefer_hour) || $interval > (86400*8))) {
248 sleep(1);
249 cronplus_invoke_hook('weekly', $now, $last_cron);
250 }
251 // Monthly
252 if ($now_month != $last_cron_month && (($now_wkdy >= $prefer_wkdy && $now_hour >= $prefer_hour) || $interval > 86400*35)) {
253 sleep(1);
254 cronplus_invoke_hook('monthly', $now, $last_cron);
255 }
256 // Yearly
257 if ($now_year > $last_cron_year && (($now_wkdy >= $prefer_wkdy && $now_hour >= $prefer_hour) || $interval > (86400*380))) {
258 sleep(1);
259 cronplus_invoke_hook('yearly', $now, $last_cron);
260 }
261
262 // Record the current run time
263 variable_set('cronplus_last_cron', $now);
264 }
265
266 /**
267 * Invokes the proper functions in all other modules that define them. $now is passed
268 * down the tree because that is the "official" timestamp for this cron run as a whole,
269 * even though the execution start time of each function call may be a later.
270 */
271 function cronplus_invoke_hook($period, $now, $last_cron) {
272 $now_fmt = gmdate('Y-m-d H:i:s', $now);
273 $function_name = 'cronplus_'. $period;
274 $last_this = variable_get($function_name .'_last', 0);
275 variable_set($function_name .'_last', $now);
276 watchdog('cronplus', 'Processing %period cron for %d UTC, calling {module}_%func() for all modules', array('%d' => $now_fmt, '%period' => t($period), '%func' => $function_name), WATCHDOG_NOTICE);
277 @module_invoke_all($function_name, $now, $last_cron, $last_this);
278 @module_invoke_all('cronplus', $period, $now, $last_cron, $last_this);
279 }
280
281 /**
282 * Returns an associative array filled with integers pointing to themselves, or
283 * to the corresponding values from the $values array, if specified. The values
284 * are translated if $translate is TRUE.
285 */
286 function _cronplus_int_options($min, $max, $values=NULL, $translate=FALSE) {
287 $options = array();
288 for ($i=$min; $i<=$max; $i++) {
289 if (is_array($values) && isset($values[$i])) {
290 $options[$i] = $translate ? t($values[$i]) : $values[$i];
291 }
292 else {
293 $options[$i] = $i;
294 }
295 }
296 return $options;
297 }

  ViewVC Help
Powered by ViewVC 1.1.2