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

Contents of /contributions/modules/audit/audit.module

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


Revision 1.3 - (show annotations) (download) (as text)
Tue Feb 27 02:53:41 2007 UTC (2 years, 9 months ago) by njivy
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +139 -233 lines
File MIME type: text/x-php
Simplified by using nodequeue instead of taxonomy for tracking progress
1 <?php
2 // $Id$
3 // TODO: The word 'process' should read 'audit'.
4
5 /**
6 * Implementation of hook_menu()
7 */
8 function audit_menu($may_cache) {
9 $items = array();
10 if ($may_cache) {
11 $items[] = array('path' => 'admin/settings/audit',
12 'title' => t('Audit'),
13 'callback' => 'drupal_get_form',
14 'callback arguments' => array('audit_admin_form'),
15 'access' => user_access('administer audit system'),
16 );
17 $items[] = array('path' => 'audit',
18 'title' => t('Audit'),
19 'callback' => 'audit_process_page',
20 'access' => user_access('access audit system'),
21 'type' => MENU_CALLBACK,
22 );
23 }
24 return $items;
25 }
26
27
28 //------------------------------------------------------------
29 // Menu callbacks
30 //------------------------------------------------------------
31
32 /**
33 * Content review process page
34 *
35 * @param $selector
36 * nid of content to review; if null, page redirects to next nid
37 */
38 function audit_process_page() {
39 // Ensure we have a valid process.
40 $args = func_get_args();
41 $process_id = array_shift($args);
42 if ($process_id) {
43 $process = node_load($process_id);
44 }
45 if (!$process->nid || $process->type != 'audit_process') {
46 drupal_not_found();
47 return;
48 }
49
50 // Okay, we have a valid process. What shall we do with it?
51 $op = array_shift($args);
52
53 // Return the page shell, to be filled by our JavaScript
54 if (!$op) {
55 global $custom_theme, $base_url;
56 $custom_theme = variable_get('audit_theme', 0);
57 drupal_set_title(''); // Hide the page title to reduce distractions
58 drupal_add_css(drupal_get_path('module', 'audit') .'/audit.css');
59
60 // jquery.js is included when drupal_add_js() is called the first time.
61 drupal_add_js(drupal_get_path('module', 'audit') .'/js/jquery.form.js');
62 drupal_add_js(drupal_get_path('module', 'audit') .'/js/interface-scroll.js');
63 drupal_add_js(drupal_get_path('module', 'audit') ."/js/audit.{$GLOBALS['locale']}.js");
64 drupal_add_js("var audit = {};
65 audit.base_url = '{$base_url}';
66 audit.audit_url = '". url("audit/{$process->nid}", NULL, NULL, TRUE) ."';
67 audit.audit_id = {$process->nid};", 'inline');
68 drupal_add_js('$(function() { intercept_links("body"); load_form(); });', 'inline');
69
70 $process = node_build_content($process, TRUE, FALSE);
71 $links = theme('links', module_invoke_all('link', 'node', $process, FALSE, TRUE));
72 $output = '<div id="a-left"> <div id="a-processed" class="a-list a-process-list" type="processed"></div>
73 <div id="a-bookmarked" class="a-list" type="bookmarked"></div>
74 <div id="a-process">'. theme('box', filter_xss_admin($process->title), drupal_render($process->content) . $links) .'</div></div>';
75 $output .= '<div id="a-right"> <div id="a-in-process" class="a-list a-process-list" type="in-process"></div>
76 <div id="a-unprocessed" class="a-list a-process-list" type="unprocessed"></div></div>';
77 $output .= '<div id="a-middle"><div id="a-form"><noscript class="messages error">JavaScript is required.</noscript></div></div>';
78
79 // Disable blocks for simplified interface
80 print theme('page', $output, FALSE);
81 return;
82 }
83
84 switch ($op) {
85 case 'form':
86 $nid = array_shift($args);
87 print _audit_process_form($process, $nid);
88 break;
89 case 'list':
90 $tag = array_shift($args);
91 print _audit_process_list($process, $tag);
92 break;
93 case 'tag':
94 $nid = array_shift($args);
95 $tag = array_shift($args);
96 print _audit_process_tag($process, $nid, $tag);
97 break;
98 default:
99 // We don't know what to do with any other $op.
100 drupal_not_found();
101 return;
102 }
103 }
104
105
106 //------------------------------------------------------------
107 // Other Drupal hooks and form-related functions
108 //------------------------------------------------------------
109
110 /**
111 * Implementation of hook_perm()
112 */
113 function audit_perm() {
114 return array('administer audit system', 'access audit system');
115 }
116
117 /**
118 * Administrative settings
119 */
120 function audit_admin_form() {
121 $result = db_query("SELECT name FROM {system} WHERE type = 'theme' AND status = 1");
122 $options = array(0 => t('<system default>'));
123 while ($row = db_fetch_object($result)) {
124 $options[$row->name] = $row->name;
125 }
126
127 $form['audit_theme'] = array(
128 '#type' => 'select',
129 '#title' => t('Theme'),
130 '#description' => t('Optionally use a different theme while processing content.'),
131 '#default_value' => variable_get('audit_theme', ''),
132 '#options' => $options,
133 );
134
135 $form['audit_list_size'] = array(
136 '#type' => 'select',
137 '#title' => t('Nodes per list'),
138 '#description' => t('Lists appear while auditing.'),
139 '#default_value' => variable_get('audit_list_size', 5),
140 '#options' => array(3 => 3, 5 => 5, 10 => 10, 15 => 15),
141 );
142
143 $form['#submit']['audit_admin_form_submit'] = array();
144 $form['#submit']['system_settings_form_submit'] = array();
145 return system_settings_form($form);
146 }
147
148 /**
149 * Implementation of hook_form_alter()
150 */
151 function audit_form_alter($form_id, &$form) {
152 if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
153 $form['node_id'] = array(
154 '#type' => 'hidden',
155 '#value' => $form['nid']['#value'],
156 );
157
158 if ($GLOBALS['audit_id']) {
159 if (!count($form['#post'])) {
160 $form['log']['#default_value'] = $form['#node']->log;
161 }
162
163 $form['#redirect'] = url("audit/{$GLOBALS['audit_id']}/form/{$form['node_id']['#value']}", NULL, NULL, TRUE);
164 }
165 }
166 }
167
168
169 /**
170 * Implementation of hook_nodeapi()
171 */
172 function audit_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
173 if ($node->type == 'audit_process') {
174 switch ($op) {
175 case 'view':
176 if ($a4) { // $a4 == $page
177 // Reminders to create queues
178 foreach (array('processed', 'in-process', 'bookmarked') as $type) {
179 $name = _audit_process_lookup($node, 'queue name', $type);
180 $result = db_query("SELECT qid FROM {nodequeue_queue} WHERE title = '%s'", $name);
181 if (!db_num_rows($result)) {
182 drupal_set_message(t('Queue missing: %name', array('%name' => $name)), 'error');
183 }
184 }
185
186 // Reminder to create view
187 $name = _audit_process_lookup($node, 'view name', 'unprocessed');
188 if (!views_get_view($name)) {
189 drupal_set_message(t('View missing: %name', array('%name' => $name)), 'error');
190 }
191
192 // Prepend a progress summary
193 foreach (array('in-process', 'processed', 'bookmarked') as $type) {
194 $titles[$type] = _audit_process_lookup($node, 'state title', $type);
195 $qid = _audit_process_lookup($node, 'queue id', $type);
196 $links[$type] = l($titles[$type], "admin/content/nodequeue/view/$qid");
197 }
198 $titles['unprocessed'] = _audit_process_lookup($node, 'state title', 'unprocessed');
199
200 $node->content['progress'] = array(
201 '#weight' => -5,
202 '#value' => theme('audit_progress_summary', $node, _audit_process_count($node), $links, $titles),
203 );
204 }
205 break;
206 }
207 }
208 }
209
210 /**
211 * Implementation of hook_block()
212 */
213 function audit_block($op = 'list', $delta = 0, $edit = array()) {
214 switch ($op) {
215 case 'list':
216 $blocks[0] = array('info' => t('Unprocessed content per process'), 'region' => 'right', 'weight' => -5);
217 $blocks[1] = array('info' => t('In-process content per process'), 'region' => 'right', 'weight' => -6);
218 $blocks[2] = array('info' => t('Processed content per process'), 'region' => 'left', 'weight' => -5);
219 $blocks[3] = array('info' => t('Bookmarked content per process'), 'region' => 'left', 'weight' => -6);
220 return $blocks;
221
222 case 'view':
223 if (!$edit->nid) {
224 return;
225 }
226 switch ($delta) {
227 case 0:
228 return _audit_block('unprocessed', $edit->nid);
229 case 1:
230 return _audit_block('in-process', $edit->nid);
231 case 2:
232 return _audit_block('processed', $edit->nid);
233 case 3:
234 return _audit_block('bookmarked', $edit->nid);
235 }
236 }
237 }
238
239 /**
240 * Implementation of hook_link()
241 */
242 function audit_link($type, $node = NULL, $teaser = FALSE, $in_process = FALSE) {
243 $links = array();
244 if ($type == 'node' && $node->type == 'audit_process') {
245 if ($in_process) {
246 $links['audit_stop'] = array('title' => t('Stop auditing'), 'href' => 'node/'. $node->nid);
247 }
248 else {
249 $links['audit_start'] = array('title' => t('Start auditing'), 'href' => 'audit/'. $node->nid);
250 }
251 }
252 return $links;
253 }
254
255
256 //------------------------------------------------------------
257 // Helper functions
258 //------------------------------------------------------------
259
260 /**
261 * Creates a block that lists content per process
262 *
263 * @param $process_id
264 * a valid nid of an 'audit_process'-type node
265 *
266 * @param $type
267 * unprocessed, in-process, processed, or bookmarked
268 *
269 * @return Array
270 * an array to be returned by audit_block('view', $delta)
271 */
272 function _audit_block($type, $process_id) {
273 $process = node_load($process_id);
274 if ($process->type != 'audit_process') {
275 watchdog('audit', t('Invalid process ID: @id', array('@id' => $process_id)), WATCHDOG_ERROR);
276 return;
277 }
278 // We know we have a valid process.
279
280 $types = array('unprocessed', 'in-process', 'processed', 'bookmarked');
281 if (!in_array($type, $types)) {
282 watchdog('audit', t('Invalid list type: @type', array('@type' => $type)), WATCHDOG_ERROR);
283 return;
284 }
285 // We know we have a valid list type.
286
287 $list_size = variable_get('audit_list_size', 5);
288 $count = _audit_process_count($process);
289 $title = _audit_process_lookup($process, 'state title', $type);
290 if ($count[$type] > 0) {
291 $output = "<div class=\"description\">{$count[$type]} (". round($count[$type]/$count['all']*100, 2) .'%)</div>';
292 }
293
294 // Prepare the list
295 switch ($type) {
296 // Prepare a query using node_queue
297 case 'in-process':
298 case 'processed':
299 case 'bookmarked':
300 $result = db_query('SELECT n.nid AS nid, n.title AS node_title FROM {nodequeue_nodes} nn INNER JOIN {node} n ON nn.nid = n.nid WHERE nn.qid = %s ORDER BY nn.position DESC, n.title DESC', _audit_process_lookup($process, 'queue id', $type));
301 break;
302
303 // Prepare a query using views
304 case 'unprocessed':
305 $view = views_get_view(_audit_process_lookup($process, 'view name', $type));
306 if (!$view->vid) {
307 return;
308 }
309 $view = views_build_view('queries', $view);
310 $result = db_query($view['query']);
311 break;
312 }
313
314 // Construct it
315 $db_count = db_num_rows($result);
316 for ($i = 0; $i < $list_size && $i < $db_count ; $i++) {
317 $row = db_fetch_object($result);
318 if ($log = db_result(db_query('SELECT nr.log AS log FROM {node_revisions} nr LEFT JOIN {node} n ON nr.vid = n.vid WHERE n.nid = %s', $row->nid))) {
319 $log = '<div class="description">'. filter_xss_admin($log) .'</div>';
320 }
321 $items[] = l($row->node_title, "node/{$row->nid}", array('nid' => "{$row->nid}", 'type' => "{$type}")) . $log;
322 }
323
324 // Render it
325 if (count($items)) {
326 if ($db_count > $list_size) {
327 $items[] = '...';
328 }
329 $output .= theme('item_list', $items);
330 }
331
332 return array('subject' => $title, 'content' => $output);
333 }
334
335 /**
336 * Returns a node edit form
337 *
338 * If $nid == NULL, the next available node is selected.
339 */
340 function _audit_process_form($process, $nid = NULL) {
341 if (!is_numeric($process->nid)) {
342 return drupal_not_found();
343 }
344
345 $GLOBALS['audit_id'] = $process->nid;
346
347 if (!is_numeric($nid)) {
348 $view = views_get_view(_audit_process_lookup($process, 'view name', 'unprocessed'));
349 $view = views_build_view('queries', $view);
350 $row = db_fetch_object(db_query($view['query']));
351 $nid = $row->nid;
352 }
353
354 $node = node_load($nid);
355
356 if (node_access('update', $node)) {
357 $output = drupal_get_form($node->type .'_node_form', $node);
358 return theme('status_messages') .$output;
359 }
360 else {
361 return drupal_access_denied();
362 }
363 }
364
365 /**
366 * Returns a list of content
367 */
368 function _audit_process_list($process, $tag) {
369 if (user_access('manipulate queues')) {
370 $box = _audit_block($tag, $process->nid);
371 return theme('box', $box['subject'], $box['content']);
372 }
373 else {
374 return drupal_access_denied();
375 }
376 }
377
378 /**
379 * Tags a piece of content
380 *
381 * @param $process
382 * node object of the audit process
383 *
384 * @param $nid
385 * nid for the content being audited
386 *
387 * @param $tag
388 * one of {unprocessed, in-process, processed} and optionally {bookmarked}
389 */
390 function _audit_process_tag($process, $nid, $tag) {
391 if (!is_numeric($nid)) {
392 watchdog('audit', t('Invalid node %nid for tag %tag', array('%nid' => $nid, '%tag' => $tag)), WATCHDOG_WARNING);
393 return drupal_access_denied();
394 }
395
396 $node = node_load($nid);
397 if (!user_access('manipulate queues') || !nodequeue_node_access($node->type)) {
398 return drupal_access_denied();
399 }
400
401 if ($tag == 'unprocessed' || $tag == 'in-process' || $tag == 'processed') {
402 $qids = array('in-process' => _audit_process_lookup($process, 'queue id', 'in-process'),
403 'processed' => _audit_process_lookup($process, 'queue id', 'processed'));
404
405 foreach ($qids as $type => $qid) {
406 nodequeue_queue_remove_node($qid, $nid);
407 }
408
409 if ($tag != 'unprocessed') {
410 nodequeue_queue_add($qids[$tag], $nid);
411 }
412 }
413 else {
414 $qid = _audit_process_lookup($process, 'queue id', 'bookmarked');
415 nodequeue_queue_remove_node($qid, $nid);
416 if ($tag == 'bookmarked') {
417 nodequeue_queue_add($qid, $nid);
418 }
419 }
420
421 watchdog('audit', t('Audit @audit: Node @nid is %tag.', array('@audit' => $process->nid, '@nid' => $nid, '%tag' => _audit_process_lookup($process, 'state title', $tag))));
422 }
423
424 /**
425 * Returns common values for audit processes
426 *
427 * @param $node
428 * The 'audit_process'-type node
429 *
430 * @param $key
431 * 'view name', 'term name', 'term id', 'state title'
432 *
433 * @param $type
434 * 'view name' - {unprocessed}
435 * 'queue name' - {in-process, processed, bookmarked}
436 * 'queue id' - {in-process, processed, bookmarked}
437 * 'state title' - {unprocessed, in-process, processed, bookmarked}
438 */
439 function _audit_process_lookup(&$node, $key, $type) {
440 switch ($key) {
441 case 'state title':
442 $titles = array('processed' => t('Done'), 'in-process' => t('In-process'), 'unprocessed' => t('Not done'), 'bookmarked' => t('Saved for later'), 'parent' => t('All'));
443 return $titles[$type];
444
445 case 'view name':
446 switch ($type) {
447 case 'unprocessed':
448 case 'in-process':
449 case 'processed':
450 case 'bookmarked':
451 $type = str_replace('-', '_', $type);
452 return "audit_{$node->nid}_{$type}";
453 }
454 break;
455
456 case 'queue name':
457 switch ($type) {
458 case 'in-process':
459 case 'processed':
460 case 'bookmarked':
461 // The nid in the term title helps us find it again later.
462 return t('Audit #@nid: @type', array('@nid' => $node->nid, '@type' => _audit_process_lookup($node, 'state title', $type)));
463 }
464 break;
465
466 case 'queue id':
467 switch ($type) {
468 case 'in-process':
469 case 'processed':
470 case 'bookmarked':
471 return db_result(db_query("SELECT qid FROM {nodequeue_queue} WHERE title = '%s'", _audit_process_lookup($node, 'queue name', $type)));
472 }
473 break;
474 }
475 }
476
477 /**
478 * Counts nodes in process states
479 *
480 * @param $node
481 * node object for the process
482 *
483 * @return Array
484 * $count[{'all', 'unprocessed', 'in-process', 'processed', 'bookmarked'}]
485 */
486 function _audit_process_count(&$node) {
487 static $count;
488
489 if (!isset($count)) {
490 $count = array();
491
492 $view = views_get_view(_audit_process_lookup($node, 'view name', 'unprocessed'));
493 if ($view->vid) {
494 $view = views_build_view('result', $view);
495 $result = db_query($view['query']);
496 $count['unprocessed'] = db_num_rows($result);
497 $sum += $count['unprocessed'];
498 }
499
500 foreach (array('in-process', 'processed', 'bookmarked') as $type) {
501 $result = db_query('SELECT nid FROM {nodequeue_nodes} WHERE qid = %d', _audit_process_lookup($node, 'queue id', $type));
502 $count[$type] = db_num_rows($result);
503 $sum += $count[$type];
504 }
505
506 $count['all'] = $sum;
507 }
508
509 return $count;
510 }
511
512
513 //------------------------------------------------------------
514 // Theme functions
515 //------------------------------------------------------------
516
517 function theme_audit_progress_summary(&$node, $counts, $links, $titles) {
518 $total_count = $counts['all'];
519 if (!$total_count) {
520 return;
521 }
522
523 unset($counts['all']);
524
525 foreach ($counts as $type => $count) {
526 if ($links[$type]) {
527 $titles[$type] = $links[$type];
528 }
529 $output .= "<div class=\"{$type}\">{$count}" .' <span class="percent">('. round($count/($total_count)*100, 2) .'%) '. $titles[$type] .'</span></div>';
530 }
531
532 $output = '<div class="preview content-review-progress">'. $output .'</div>';
533 return $output;
534 }

  ViewVC Help
Powered by ViewVC 1.1.2