/[drupal]/contributions/modules/signup_status/signup_status.admin.inc
ViewVC logotype

Contents of /contributions/modules/signup_status/signup_status.admin.inc

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


Revision 1.7 - (show annotations) (download) (as text)
Mon Sep 21 03:44:30 2009 UTC (2 months, 1 week ago) by dww
Branch: MAIN
CVS Tags: DRUPAL-6--1-0-ALPHA2, HEAD
Changes since 1.6: +154 -6 lines
File MIME type: text/x-php
#581826 by dww: Fixed the status settings UI so that if an admin
changes the "Modify signup count" for any status values, or if the
status they reassign signups to when deleting a status has a different
value, we now use Batch API to update all the signups, recompute the
effective totals and check the signup limits on all affected nodes.
1 <?php
2 // $Id: signup_status.admin.inc,v 1.6 2009/09/20 23:33:02 dww Exp $
3
4 /**
5 * @file
6 * Code related to the administrative (settings) pages for Signup Status.
7 */
8
9 /**
10 * Render the site-wide signup status settings form.
11 */
12 function theme_signup_status_admin_settings_form($form) {
13 drupal_add_tabledrag('signup-status-admin-settings-table', 'order', 'self', 'signup-status-weight');
14 $header = array(
15 array('data' => t('Name !required', array('!required' => '<span class="form-required" title="' . t('This field is required.') . '">*</span>'))),
16 array('data' => t('Description')),
17 array('data' => t('Modify signup count')),
18 array('data' => t('Show on form')),
19 array('data' => t('Weight')),
20 array('data' => t('Operations'))
21 );
22 foreach (element_children($form['status']) as $key) {
23 $row = array();
24 foreach (array('name', 'description', 'mod_signup_count', 'show_on_form', 'weight', 'delete') as $element) {
25 // Since we're rendering these in a table, remove any #title attributes.
26 if (!empty($form['status'][$key][$element]['#title'])) {
27 unset($form['status'][$key][$element]['#title']);
28 }
29 $row[] = drupal_render($form['status'][$key][$element]);
30 }
31 $rows[] = array(
32 'class' => 'draggable',
33 'data' => $row,
34 );
35 }
36 $rows[] = array(
37 'class' => 'draggable',
38 'data' => array(
39 drupal_render($form['status_add']['name']),
40 drupal_render($form['status_add']['description']),
41 drupal_render($form['status_add']['mod_signup_count']),
42 drupal_render($form['status_add']['show_on_form']),
43 drupal_render($form['status_add']['weight']),
44 NULL,
45 ),
46 );
47 $output = theme('table', $header, $rows, array('id' => 'signup-status-admin-settings-table'));
48 $output .= drupal_render($form);
49 return $output;
50 }
51
52 /**
53 * Build the form for the site-wide signup_status settings table.
54 *
55 * @param $form_state
56 * A form state array.
57 *
58 * @return
59 * The form structured array.
60 */
61 function signup_status_admin_settings_form(&$form_state) {
62 $signup_status_codes = signup_status_codes();
63
64 // Form elements for existing status codes.
65 $form['status']['#tree'] = TRUE;
66 if (!empty($signup_status_codes)) {
67 foreach ($signup_status_codes as $cid => $status) {
68 $form['status'][$cid]['name'] = array(
69 '#title' => t('Name'),
70 '#type' => 'textfield',
71 '#default_value' => $status['name'],
72 '#maxlength' => 128,
73 '#size' => 20,
74 '#required' => TRUE,
75 );
76 $form['status'][$cid]['weight'] = array(
77 '#title' => t('Weight'),
78 '#type' => 'weight',
79 '#default_value' => $status['weight'],
80 '#delta' => 15,
81 '#attributes' => array('class' => 'signup-status-weight'),
82 );
83 $form['status'][$cid]['description'] = array(
84 '#title' => t('Description'),
85 '#type' => 'textfield',
86 '#default_value' => $status['description'],
87 '#maxlength' => 128,
88 '#size' => 30,
89 );
90 $form['status'][$cid]['mod_signup_count'] = array(
91 '#title' => t('Modify signup count'),
92 '#type' => 'checkbox',
93 '#default_value' => $status['mod_signup_count'],
94 );
95 $form['status'][$cid]['show_on_form'] = array(
96 '#title' => t('Show on form'),
97 '#type' => 'checkbox',
98 '#default_value' => $status['show_on_form'],
99 );
100 $form['status'][$cid]['delete'] = array(
101 '#type' => 'markup',
102 '#value' => l(t('delete'), 'admin/settings/signup_status/delete/'. $cid),
103 );
104 }
105 }
106
107 // Form elements for adding a new status.
108 $form['status_add']['#tree'] = TRUE;
109 $form['status_add']['name'] = array(
110 '#type' => 'textfield',
111 '#size' => 20,
112 '#maxlength' => 128,
113 );
114 $form['status_add']['weight'] = array(
115 '#type' => 'weight',
116 '#default_value' => 0,
117 '#delta' => 15,
118 '#attributes' => array('class' => 'signup-status-weight'),
119 );
120 $form['status_add']['description'] = array(
121 '#type' => 'textfield',
122 '#maxlength' => 128,
123 '#size' => 30,
124 );
125 $form['status_add']['mod_signup_count'] = array(
126 '#type' => 'checkbox',
127 );
128 $form['status_add']['show_on_form'] = array(
129 '#type' => 'checkbox',
130 );
131
132 $form['help'] = array(
133 '#type' => 'item',
134 '#description' => t("This table defines the signup status options available on this site. If the 'Modify signup count' box is checked, signups in that status will be counted towards the signup limit (if any). <strong>Note that changing this value once signups exist will update those signups and potentially open or close signups on the affected event(s).</strong> If the 'Show on form' box is checked, users will have the option of selecting the status when they signup or edit an existing signup. The order of statusus in this table determines the ordering choices on the signup form (if any are shown). The blank row at the bottom can be used to add a new status."),
135 );
136
137 $form['submit'] = array(
138 '#type' => 'submit',
139 '#value' => t('Save configuration'),
140 );
141
142 return $form;
143 }
144
145 /**
146 * Validation callback for the signup status code settings form.
147 */
148 function signup_status_admin_settings_form_validate($form, &$form_state) {
149 $values = $form_state['values'];
150 $names = array();
151 if (!empty($values['status'])) {
152 foreach ($values['status'] as $cid => $status) {
153 $name = $status['name'];
154 if (!empty($names[$name])) {
155 form_set_error('status][' . $cid . '][name', t('A status code with the name %name is already in use.', array('%name' => $name)));
156 }
157 else {
158 $names[$name] = $cid;
159 }
160 }
161 }
162 if (!empty($values['status_add']['name'])) {
163 $new_name = $values['status_add']['name'];
164 if (!empty($names[$new_name])) {
165 form_set_error('status_add][name', t('A status code with the name %name is already in use.', array('%name' => $new_name)));
166 }
167 }
168 else {
169 // Ensure that the name is defined if the user set anything else for the
170 // new status code row.
171 if (!empty($values['status_add']['description']) ||
172 !empty($values['status_add']['mod_signup_count']) ||
173 !empty($values['status_add']['show_on_form'])) {
174 form_set_error('status_add][name', t('Name is required for a new status.'));
175 }
176 }
177 }
178
179 /**
180 * Submit callback for the signup status code settings form.
181 */
182 function signup_status_admin_settings_form_submit($form, &$form_state) {
183 $values = $form_state['values'];
184
185 if (!empty($values['status_add']['name'])) {
186 // Adding a new status.
187 drupal_write_record('signup_status_codes', $values['status_add']);
188 drupal_set_message(t('Status code %name has been added.', array('%name' => $values['status_add']['name'])));
189 watchdog('signup_status', 'Status code %name has been added.', array('%name' => $values['status_add']['name']));
190 }
191
192 // See if we modified any existing status codes.
193 if (!empty($values['status'])) {
194 $count_changed_status_codes = array();
195 $signup_status_codes = signup_status_codes();
196 foreach ($values['status'] as $cid => $status) {
197 $status['cid'] = $cid;
198 $diff = array_diff_assoc($status, $signup_status_codes[$cid]);
199 if (!empty($diff)) {
200 if (isset($diff['mod_signup_count'])) {
201 $count_changed_status_codes[] = $status;
202 }
203 drupal_write_record('signup_status_codes', $status, 'cid');
204 drupal_set_message(t('Status code %name has been updated.', array('%name' => $status['name'])));
205 watchdog('signup_status', 'Status code %name has been updated.', array('%name' => $status['name']));
206 }
207 }
208 if (!empty($count_changed_status_codes)) {
209 signup_status_admin_signup_count_change($count_changed_status_codes);
210 }
211 }
212 }
213
214 /**
215 * Change the mod_signup_count and update signup totals via Batch API.
216 *
217 * @param $status_codes
218 * Array of signup status definitions (as returned by signup_status_codes())
219 * that have changed. The 'mod_signup_count' element in each subarray should
220 * hold the new value to change into.
221 *
222 * @see signup_status_codes()
223 * @see signup_status_admin_signup_count_change_batch()
224 * @see signup_status_admin_signup_count_change_finished()
225 */
226 function signup_status_admin_signup_count_change($status_codes) {
227 $title = t('Updating signup totals and checking limits for changes to if the signup status modifies the signup count');
228 $batch = array(
229 'operations' => array(
230 array('signup_status_admin_signup_count_change_batch', array($status_codes)),
231 ),
232 'finished' => 'signup_status_admin_signup_count_change_finished',
233 'title' => $title,
234 'init_message' => $title,
235 'progress_message' => t('Updating signup totals and checking limits...'),
236 'error_message' => t('Error updating signup totals and checking limits.'),
237 'file' => drupal_get_path('module', 'signup_status') . '/signup_status.admin.inc',
238 );
239 batch_set($batch);
240 }
241
242 /**
243 * Batch worker to check signup totals based on changes mod_signup_count.
244 *
245 * @param $status_codes
246 * Array of signup status definitions (as returned by signup_status_codes())
247 * that have changed. The 'mod_signup_count' element in each subarray should
248 * hold the new value to change into.
249 * @param $context
250 * Reference to a Batch API context array to track progress of the batch.
251 *
252 * @see signup_status_admin_signup_count_change()
253 * @see signup_status_admin_signup_count_change_finished()
254 */
255 function signup_status_admin_signup_count_change_batch($status_codes, &$context) {
256 if (empty($context['sandbox']['nodes'])) {
257 // Initialize the work to do -- remember all the nodes that have signups
258 // in any effected status, since we need to modify the totals and check
259 // limits.
260 $status_cids = array();
261 foreach ($status_codes as $code) {
262 $status_cids[] = $code['cid'];
263 }
264 $placeholders = db_placeholders($status_cids);
265 $query = db_query("SELECT nid FROM {signup_log} WHERE status IN ($placeholders) GROUP BY nid", $status_cids);
266 while ($nid = db_result($query)) {
267 $context['sandbox']['nodes'][] = $nid;
268 }
269 $context['finished'] = 0;
270 $context['sandbox']['max'] = count($context['sandbox']['nodes']);
271 $context['sandbox']['progress'] = 0;
272 $context['message'] = t('Checking signup count and limits...');
273 $context['results']['updated'] = 0;
274 $context['results']['processed'] = 0;
275 $context['results']['status_codes'] = $status_codes;
276
277 // In case of a race condition, ensure there's something to process.
278 if (empty($context['sandbox']['max'])) {
279 $context['finished'] = 1;
280 return;
281 }
282 }
283
284 // If there's work to do, process a node.
285 if (!empty($context['sandbox']['nodes'])) {
286 $nid = array_pop($context['sandbox']['nodes']);
287 // First update the signups for all affected status codes.
288 foreach ($status_codes as $status) {
289 db_query("UPDATE {signup_log} SET count_towards_limit = %d WHERE nid = %d AND status = %d", $status['mod_signup_count'], $nid, $status['cid']);
290 }
291 // Now, load the node, which will count and sum the effective signups.
292 $node = node_load($nid);
293 // Finally, check the limit now that the total has potentially changed.
294 _signup_check_limit($node, 'total');
295 $context['results']['updated']++;
296 $context['message'] = t('Processed signups for %title', array('%title' => $node->title));
297 }
298 $context['sandbox']['progress']++;
299 $context['results']['processed']++;
300 $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
301 }
302
303 /**
304 * Batch API callback once all signup totals have been modified.
305 *
306 * @see signup_status_admin_signup_count_change()
307 * @see signup_status_admin_signup_count_change_batch()
308 */
309 function signup_status_admin_signup_count_change_finished($success, $results, $operations) {
310 if ($success) {
311 if (!empty($results)) {
312 drupal_set_message(format_plural($results['updated'], 'Updated signup totals on one node based on changes to signup counts.', 'Updated signup totals on @count nodes based on changes to signup counts.'));
313 foreach ($results['status_codes'] as $status) {
314 if ($status['mod_signup_count']) {
315 drupal_set_message(t('Signup status %name now modifies the signup count.', array('%name' => $status['name'])));
316 }
317 else {
318 drupal_set_message(t('Signup status %name now does not modify the signup count.', array('%name' => $status['name'])));
319 }
320 }
321 }
322 }
323 else {
324 // An error occurred.
325 // $operations contains the operations that remained unprocessed.
326 $error_operation = reset($operations);
327 drupal_set_message(t('An error occurred while processing with arguments: @error', array('@error' => var_export($error_operation[0], TRUE))), 'error');
328 }
329 }
330
331 /**
332 * Implements the signup status code delete page.
333 *
334 * @param $form_state
335 * A form state array.
336 * @param $cid
337 * A signup status ID.
338 *
339 * @return
340 * The form structure.
341 */
342 function signup_status_admin_delete(&$form_state, $cid) {
343 $codes = signup_status_codes();
344 if (empty($codes[$cid])) {
345 drupal_set_message(t('The status you are trying to delete does not exist.'), 'error');
346 return drupal_goto('admin/settings/signup_status');
347 }
348
349 $name = $codes[$cid]['name'];
350 $form['cid'] = array(
351 '#type' => 'value',
352 '#value' => $cid,
353 );
354 $form['name'] = array(
355 '#type' => 'value',
356 '#value' => $name,
357 );
358
359 $total = db_result(db_query('SELECT COUNT(sid) AS total FROM {signup_log} WHERE status = %d', $cid));
360 if ($total > 0) {
361 foreach ($codes as $id => $status) {
362 $options[$id] = $status['name'];
363 }
364 $form['new_status'] = array(
365 '#type' => 'select',
366 '#title' => t('Reassign status'),
367 '#default_value' => $cid,
368 '#options' => $options,
369 '#description' => t('There are !total existing signups with the status of @name. Please select a new status for these signups.', array('!total' => $total, '@name' => $name)),
370 );
371 }
372
373 return confirm_form(
374 $form,
375 t('Are you sure you want to delete the status code %name?', array('%name' => $name)),
376 'admin/settings/signup_status',
377 t('This action cannot be undone.'),
378 t('Delete status'),
379 t('Cancel')
380 );
381 }
382
383 /**
384 * Validation callback for the signup status code delete page.
385 */
386 function signup_status_admin_delete_validate($form, &$form_state) {
387 if ($form_state['values']['new_status'] == $form_state['values']['cid']) {
388 form_set_error('new_status', t('Choose a new signup status for existing signups of status %name.', array('%name' => $form_state['values']['name'])));
389 }
390 }
391
392 /**
393 * Submit callback for the signup status code delete page.
394 */
395 function signup_status_admin_delete_submit($form, &$form_state) {
396 $values = $form_state['values'];
397 $codes = signup_status_codes();
398
399 db_query("DELETE FROM {signup_status_codes} WHERE cid = %d", $values['cid']);
400 drupal_set_message(t('Status code %name has been deleted.', array('%name' => $values['name'])));
401 watchdog('signup_status', 'Status code %name has been deleted.', array('%name' => $values['name']));
402
403 if (isset($values['new_status'])) {
404 // We could update all of the signups to the new status in a single query,
405 // but we need to invoke the hook that the status was changed, and that's
406 // potentially expensive, so we do it via the Batch API.
407 $old_status = $codes[$values['cid']];
408 $new_status = $codes[$values['new_status']];
409 signup_status_admin_reassign_status($old_status, $new_status);
410 }
411 $form_state['redirect'] ='admin/settings/signup_status';
412 }
413
414 /**
415 * Reassign all signups from one status to another using the Batch API.
416 *
417 * @param $old_status
418 * Array defining the old status to assign from. This array must contain at
419 * least the 'cid' and 'name' keys, as returned by signup_status_codes().
420 * @param $new_status
421 * Array defining the new status to assign to. This array must contain at
422 * least the 'cid' and 'name' keys, as returned by signup_status_codes().
423 *
424 * @see signup_status_codes()
425 * @see signup_status_admin_reassign_status_batch()
426 * @see signup_status_admin_reassign_status_finished()
427 */
428 function signup_status_admin_reassign_status($old_status, $new_status) {
429 $batch = array(
430 'operations' => array(
431 array('signup_status_admin_reassign_status_batch', array($old_status, $new_status)),
432 ),
433 'finished' => 'signup_status_admin_reassign_status_finished',
434 'redirect' => 'admin/settings/signup_status',
435 'title' => $title,
436 'init_message' => $title,
437 'progress_message' => t('Reassigning signups from %old_status status to %new_status status...', array('%old_status' => $old_status['name'], '%new_status' => $new_status['name'])),
438 'error_message' => t('Error reassigning signups from %old_status status to %new_status status...', array('%old_status' => $old_status['name'], '%new_status' => $new_status['name'])),
439 'file' => drupal_get_path('module', 'signup_status') . '/signup_status.admin.inc',
440 );
441 batch_set($batch);
442 }
443
444 /**
445 * Batch worker to reassign signups from one status to another.
446 *
447 * @param $old_status
448 * Array defining the old status to assign from. This array must contain at
449 * least the 'cid' and 'name' keys, as returned by signup_status_codes().
450 * @param $new_status
451 * Array defining the new status to assign to. This array must contain at
452 * least the 'cid' and 'name' keys, as returned by signup_status_codes().
453 * @param $context
454 * Reference to a Batch API context array to track progress of the batch.
455 *
456 * @see signup_status_admin_reassign_status()
457 * @see signup_status_admin_reassign_status_finished()
458 */
459 function signup_status_admin_reassign_status_batch($old_status, $new_status, &$context) {
460 if (empty($context['sandbox']['signups'])) {
461 // Initialize the work to do -- remember all the signups we need to change
462 // the status on. Also, if the new status and the old status have a
463 // different value for 'mod_signup_count', we're going to need to process
464 // all the unique nodes that have signups in the old status so that we can
465 // properly update the effective total and check signup limits.
466 $mod_signup_count_change = $old_status['mod_signup_count'] != $new_status['mod_signup_count'];
467 $query = db_query('SELECT sid, nid FROM {signup_log} WHERE status = %d', $old_status['cid']);
468 while ($signup = db_fetch_object($query)) {
469 $context['sandbox']['signups'][] = $signup->sid;
470 if ($mod_signup_count_change) {
471 // Remember all the unique nodes to update totals and check limits on.
472 $context['sandbox']['nodes'][$signup->nid] = $signup->nid;
473 }
474 }
475 $context['finished'] = 0;
476 $context['sandbox']['max'] = count($context['sandbox']['signups']);
477 if ($mod_signup_count_change) {
478 // If mod_signup_count changed, we also need to process each node.
479 $context['sandbox']['max'] += count($context['sandbox']['nodes']);
480 }
481 $context['sandbox']['progress'] = 0;
482 $context['message'] = t('Reassigning signups from %old_status status to %new_status status...', array('%old_status' => $old_status['name'], '%new_status' => $new_status['name']));
483 $context['results']['updated'] = 0;
484 $context['results']['processed'] = 0;
485 $context['results']['old_status'] = $old_status;
486 $context['results']['new_status'] = $new_status;
487
488 // In case of a race condition, ensure there's something to process.
489 if (empty($context['sandbox']['max'])) {
490 $context['finished'] = 1;
491 return;
492 }
493 }
494
495 // If there's work to do, process a signup.
496 if (!empty($context['sandbox']['signups'])) {
497 $sid = array_pop($context['sandbox']['signups']);
498 $signup = signup_load_signup($sid);
499 $signup->old_status = $signup->status;
500 $signup->status = $new_status['cid'];
501 db_query("UPDATE {signup_log} SET status = %d WHERE sid = %d", $signup->status, $signup->sid);
502 _signup_status_change('update', $signup);
503 $context['results']['updated']++;
504 }
505 // If we're done processing signups, see if we need to process any nodes.
506 elseif (!empty($context['sandbox']['nodes'])) {
507 $nid = array_pop($context['sandbox']['nodes']);
508 // Update the count_towards_limit column for all affected signups.
509 db_query("UPDATE {signup_log} SET count_towards_limit = %d WHERE nid = %d AND status = %d AND count_towards_limit = %d", $new_status['mod_signup_count'], $nid, $new_status['cid'], $old_status['mod_signup_count']);
510 // Now, load the node, which will count and sum the effective signups.
511 $node = node_load($nid);
512 // Finally, check the limit now that the total has potentially changed.
513 _signup_check_limit($node, 'total');
514 $context['results']['updated']++;
515 $context['message'] = t('Processed signups for %title', array('%title' => $node->title));
516 }
517 $context['sandbox']['progress']++;
518 $context['results']['processed']++;
519 $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
520 }
521
522 /**
523 * Batch API callback once all signups have been reassigned to a new status.
524 *
525 * @see signup_status_admin_reassign_status()
526 * @see signup_status_admin_reassign_status_batch()
527 */
528 function signup_status_admin_reassign_status_finished($success, $results, $operations) {
529 if ($success) {
530 if (!empty($results)) {
531 drupal_set_message(format_plural($results['updated'], 'Reassigned one signup from the %old_status status to the %new_status status.', 'Reassigned @count signups from the %old_status status to the %new_status status.', array('%old_status' => $results['old_status']['name'], '%new_status' => $results['new_status']['name'])));
532 }
533 }
534 else {
535 // An error occurred.
536 // $operations contains the operations that remained unprocessed.
537 $error_operation = reset($operations);
538 drupal_set_message(t('An error occurred while processing with arguments: @error', array('@error' => var_export($error_operation[0], TRUE))), 'error');
539 }
540 }
541

  ViewVC Help
Powered by ViewVC 1.1.2