/[drupal]/contributions/modules/gradebook/gradebookapi.module
ViewVC logotype

Contents of /contributions/modules/gradebook/gradebookapi.module

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


Revision 1.11 - (show annotations) (download) (as text)
Sat Apr 7 05:25:53 2007 UTC (2 years, 7 months ago) by rwohleb
Branch: MAIN
CVS Tags: HEAD
Branch point for: DRUPAL-5, DRUPAL-6--1
Changes since 1.10: +72 -137 lines
File MIME type: text/x-php
* Initial support for Drupal 5
* Fixed 'default' SQL bug in text/blob field for MySQL
1 <?php
2 // $Id: gradebookapi.module,v 1.10 2006/11/15 02:24:11 rwohleb Exp $
3
4 /**
5 * hook_perm
6 *
7 * @return array
8 */
9 function gradebookapi_perm() {
10 return array('admin gradebookapi');
11 }
12
13 /**
14 * hook_menu
15 *
16 * @return array
17 */
18 function gradebookapi_menu($may_cache) {
19 $items = array();
20
21 // The $may_cache parameter is used to divide menu items into two parts. Those
22 // returned when $may_cache is true must be consistently applicable for the
23 // current user at all times; the others may change or be defined at only
24 // certain paths. Most modules will have excusively cacheable menu items.
25 if ($may_cache) {
26 $items[] = array(
27 'path' => 'admin/gradebook',
28 'title' => t('Gradebook'),
29 'description' => t('Administer the suite of Gradebook modules.'),
30 'position' => 'right',
31 'weight' => -5,
32 'callback' => 'system_admin_menu_block_page',
33 'access' => user_access('administer site configuration')
34 );
35 $items[] = array(
36 'path' => 'admin/gradebook/gradebookapi',
37 'title' => t('Gradebook API'),
38 'description' => t('Provides a gradebook API.'),
39 'callback' => 'drupal_get_form',
40 'callback arguments' => array('gradebookapi_admin_settings'),
41 'access' => user_access('administer site configuration'),
42 'type' => MENU_NORMAL_ITEM, // optional
43 );
44 }
45
46 return $items;
47 }
48
49 function gradebookapi_admin_settings() {
50 $form = array();
51
52 $form['#submit']['gradebookapi_admin_settings_submit'] = array(); // custom submit handler
53 $form['#submit']['system_settings_form_submit'] = array(); // form.inc never calls the $callback if a submit handler is defined
54 drupal_set_title(t('Gradebook API configuration'));
55
56 $vid = gradebookapi_get_vid();
57 $vocabulary = taxonomy_get_vocabulary($vid);
58
59 $options = array();
60 $node_types = node_get_types();
61 foreach ($node_types as $id => $type)
62 $options[$id] = $type->name;
63
64 $form['nodes'] = array(
65 '#type' => 'checkboxes',
66 '#title' => t('Assignment Types'),
67 '#default_value' => array_filter($vocabulary->nodes),
68 '#options' => $options,
69 '#description' => t('A list of node types you want to treat as assignments.'),
70 );
71
72 return system_settings_form($form);
73 }
74
75 function gradebookapi_admin_settings_submit($form_id, $form_values) {
76 $vid = gradebookapi_get_vid();
77 $vocabulary = (array)taxonomy_get_vocabulary($vid);
78
79 $vocabulary['nodes'] = array_filter($form_values['nodes']);
80 taxonomy_save_vocabulary($vocabulary);
81
82 // TODO: look over this code
83 // rebuild grades in each gradebook
84 //$terms = taxonomy_get_tree($vid, 0, -1, 1);
85 //foreach ($terms as $term) {
86 // gradebookapi_rebuild_grades($term->tid);
87 //}
88
89 //drupal_set_message(t('Updated gradebookapi settings.'));
90 }
91
92 function gradebookapi_get_assignment_types() {
93 $vid = gradebookapi_get_vid();
94 $vocabulary = (array)taxonomy_get_vocabulary($vid);
95 return $vocabulary['nodes'];
96 }
97
98 function gradebookapi_get_vid() {
99 $vid = variable_get('gradebookapi_nav_vocabulary', '');
100 if (empty($vid)) {
101 // Check to see if a forum vocabulary exists
102 $vid = db_result(db_query("SELECT vid FROM {vocabulary} WHERE module = '%s'", 'gradebook'));
103 if (!$vid) {
104 $edit = array(
105 'name' => 'Gradebook',
106 'multiple' => 0,
107 'required' => 1,
108 'hierarchy' => 1,
109 'relations' => 0,
110 'module' => 'gradebook',
111 'nodes' => array('assignment' => 1)
112 );
113 taxonomy_save_vocabulary($edit);
114 $vid = $edit['vid'];
115 }
116 variable_set('gradebookapi_nav_vocabulary', $vid);
117 }
118
119 return $vid;
120 }
121
122 function gradebookapi_gradebook_load($tid) {
123 if (_gradebookapi_is_root_term($tid)) {
124 return taxonomy_get_term($tid);
125 }
126 return FALSE;
127 }
128
129 function _gradebookapi_is_root_term($tid) {
130 $parents = taxonomy_get_parents($tid);
131 foreach ($parents as $parent) {
132 return FALSE;
133 }
134 return TRUE;
135 }
136
137 // returns the gradebook for a child tid
138 function gradebookapi_get_tid_gradebook($tid) {
139 $parents = taxonomy_get_parents_all($tid);
140 return array_pop($parents);
141 }
142
143 function gradebookapi_select_nodes($gradebook, $tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC') {
144 $vid = gradebookapi_get_vid();
145 $tree = taxonomy_get_tree($vid, $gradebook->tid);
146 $allowed_tids = array_map('_taxonomy_get_tid_from_term', $tree);
147 $allowed_tids[] = $gradebook->tid;
148 $use_tids = array();
149
150 if (!count($tids)) {
151 // use all tids in gradebook
152 $use_tids = $allowed_tids;
153 }
154 else {
155 // make sure tids are only in gradebook
156 foreach ($tids as $tid) {
157 if (in_array($tid, $allowed_tids)) {
158 $use_tids[] = $tid;
159 }
160 }
161 }
162
163 //$tids[] = $gradebook->tid; // add in gradebook tid
164
165 // taxonomy_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC')
166 return _gradebookapi_select_nodes($use_tids, $operator, $depth, $pager, $order);
167 }
168
169 function _gradebookapi_select_nodes($tids = array(), $operator = 'or', $depth = 0, $pager = TRUE, $order = 'n.sticky DESC, n.created DESC') {
170 if (count($tids) > 0) {
171 // For each term ID, generate an array of descendant term IDs to the right depth.
172 $descendant_tids = array();
173 if ($depth === 'all') {
174 $depth = NULL;
175 }
176 foreach ($tids as $index => $tid) {
177 $term = taxonomy_get_term($tid);
178 $tree = taxonomy_get_tree($term->vid, $tid, -1, $depth);
179 $descendant_tids[] = array_merge(array($tid), array_map('_taxonomy_get_tid_from_term', $tree));
180 }
181
182 if ($operator == 'or') {
183 $str_tids = implode(',', call_user_func_array('array_merge', $descendant_tids));
184 $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid INNER JOIN {term_data} td ON tn.tid = td.tid INNER JOIN {gradebookapi_assignment} a ON n.nid = a.nid WHERE tn.tid IN ('. $str_tids .') AND n.status = 1 ORDER BY '. $order;
185 $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n INNER JOIN {term_node} tn ON n.nid = tn.nid INNER JOIN {term_data} td ON tn.tid = td.tid INNER JOIN {gradebookapi_assignment} a ON n.nid = a.nid WHERE tn.tid IN ('. $str_tids .') AND n.status = 1';
186 }
187 else {
188 $joins = 'INNER JOIN {gradebookapi_assignment} a ON n.nid = a.nid';
189 $wheres = '';
190 foreach ($descendant_tids as $index => $tids) {
191 $joins .= ' INNER JOIN {term_node} tn'. $index .' ON n.nid = tn'. $index .'.nid INNER JOIN {term_data} td ON tn'. $index .'.tid = td.tid';
192 $wheres .= ' AND tn'. $index .'.tid IN ('. implode(',', $tids) .')';
193 }
194 $sql = 'SELECT DISTINCT(n.nid), n.sticky, n.title, n.created FROM {node} n '. $joins .' WHERE n.status = 1 '. $wheres .' ORDER BY '. $order;
195 $sql_count = 'SELECT COUNT(DISTINCT(n.nid)) FROM {node} n ' . $joins . ' WHERE n.status = 1 ' . $wheres;
196 }
197 $sql = db_rewrite_sql($sql);
198 $sql_count = db_rewrite_sql($sql_count);
199 if ($pager) {
200 $result = pager_query($sql, variable_get('default_nodes_main', 10), 0, $sql_count);
201 }
202 else {
203 $result = db_query_range($sql, 0, variable_get('feed_default_items', 10));
204 }
205 }
206
207 return $result;
208 }
209
210 function gradebookapi_get_grade($uid, $nid) {
211 $grades = gradebookapi_get_grades(array('uid' => $uid, 'nid' => $nid));
212 if (count($grades)) {
213 return array_pop($grades);
214 }
215 // if no grade exists, return grade with possible value
216 else {
217 $result = db_query(
218 'SELECT a.possible '.
219 'FROM {gradebookapi_assignment} a '.
220 'WHERE a.nid=%d', $nid);
221 $grade = db_fetch_object($result);
222 $grade->uid = $uid;
223 $grade->nid = $nid;
224
225 //gradebookapi_invoke_gradebookapi('load', 'grade', $grade);
226 return $grade;
227 }
228 }
229
230 function gradebookapi_get_grades($param = array()) {
231 $grades = array();
232
233 foreach ($param as $key => $value) {
234 $cond[] = 'g.'. db_escape_string($key) ." = '%s'";
235 $arguments[] = $value;
236 }
237 $cond = implode(' AND ', $cond);
238
239 $result = db_query('SELECT g.uid, g.nid, g.earned, g.exempt, a.possible, g.timestamp, g.note FROM {gradebookapi_grade} g INNER JOIN {gradebookapi_assignment} a ON g.nid = a.nid WHERE '. $cond, $arguments);
240 while ($grade = db_fetch_object($result)) {
241 //gradebookapi_invoke_gradebookapi('load', 'grade', $grade);
242 $grades[] = $grade;
243 }
244
245 return $grades;
246 }
247
248 function gradebookapi_get_term_grade($uid, $tid) {
249 $result = db_query("SELECT uid, tid, earned, possible FROM {gradebookapi_cache} WHERE tid=%d AND uid=%d", $tid, $uid);
250 $grade = db_fetch_object($result);
251
252 //gradebookapi_invoke_gradebookapi('load', 'grade', $grade);
253
254 return $grade;
255 }
256
257 function gradebookapi_set_grade($grade) {
258 $old = gradebookapi_get_grade($grade->uid, $grade->nid);
259 $grade->timestamp = time();
260 if ($old->timestamp) {
261 db_query("UPDATE {gradebookapi_grade} SET earned=%d, exempt=%d, timestamp=%d, note='%s' WHERE uid=%d AND nid=%d", $grade->earned, $grade->exempt, $grade->timestamp, $grade->note, $grade->uid, $grade->nid);
262 //gradebookapi_invoke_gradebookapi('update', 'grade', $grade);
263 }
264 else {
265 db_query("INSERT INTO {gradebookapi_grade} (uid, nid, earned, exempt, timestamp, note) VALUES (%d, %d, %d, %d, %d, '%s')", $grade->uid, $grade->nid, $grade->earned, $grade->exempt, $grade->timestamp, $grade->note);
266 //gradebookapi_invoke_gradebookapi('insert', 'grade', $grade);
267 }
268
269 $terms = taxonomy_node_get_terms_by_vocabulary($grade->nid, gradebookapi_get_vid());
270 foreach ($terms as $term) {
271 gradebookapi_calc_grades($grade->uid, $term->tid);
272 }
273 }
274
275 function gradebookapi_rebuild_grades($tid) {
276 $children = taxonomy_get_children($tid, gradebookapi_get_vid());
277 if ($children) {
278 foreach ($children as $child) {
279 gradebookapi_rebuild_grades($child->tid);
280 }
281 }
282 else {
283 gradebookapi_calc_term_grades($tid);
284 }
285 }
286
287 function gradebookapi_clear_term_grades($tid, $recursive=TRUE, $start=TRUE) {
288 db_query("DELETE FROM {gradebookapi_cache} WHERE tid=%d", $tid);
289 // do a recursive delete?
290 if ($recursive) {
291 $children = taxonomy_get_children($tid, gradebookapi_get_vid());
292 foreach ($children as $child) {
293 gradebookapi_clear_term_grades($child->tid, $recursive, FALSE);
294 }
295 }
296 // tell parents to recalc cache
297 if ($start) {
298 $parents = taxonomy_get_parents($tid);
299 foreach ($parents as $parent) {
300 gradebookapi_calc_term_grades($parent->tid);
301 }
302 }
303 }
304
305 function gradebookapi_calc_grades($uid, $tid, $recursive=TRUE) {
306 $earned = 0;
307 $possible = 0;
308
309 $gradebook = gradebookapi_get_tid_gradebook($tid);
310 //watchdog('gradebook', "_gradebook_calc_grades($uid, $tid, $recursive)");
311 //print_r(debug_backtrace());
312
313 // if UID is < 1, calc grades for ALL users
314 if ($uid<=0) {
315 return gradebookapi_calc_term_grades(taxonomy_get_term($tid));
316 }
317
318 // clear cache
319 db_query("DELETE FROM {gradebookapi_cache} WHERE tid=%d AND uid=%d", $tid, $uid);
320
321 // get grades from child terms
322 $children = taxonomy_get_children($tid, gradebookapi_get_vid());
323 foreach ($children as $child) {
324 $grade = gradebookapi_get_term_grade($uid, $child->tid);
325 if ($grade) {
326 $earned += $grade->earned;
327 $possible += $grade->possible;
328 }
329 }
330
331 // get grades from nodes at this term
332 $result = gradebookapi_select_nodes($gradebook, array($tid), 'or', 0, FALSE);
333 while ($assignment = db_fetch_object($result)) {
334 $grade = gradebookapi_get_grade($uid, $assignment->nid);
335 if ($grade && !$grade->exempt) {
336 $earned += $grade->earned;
337 $possible += $grade->possible;
338 }
339 }
340
341 // cache grade
342 db_query("INSERT INTO {gradebookapi_cache} (uid, tid, earned, possible) VALUES (%d, %d, %d, %d)", $uid, $tid, $earned, $possible);
343
344 if ($recursive) {
345 // recalc parents
346 $parents = taxonomy_get_parents($tid);
347 foreach ($parents as $parent) {
348 gradebookapi_calc_grades($uid, $parent->tid, $recursive);
349 }
350 }
351 }
352
353 function gradebookapi_calc_term_grades($term, $recursive=TRUE) {
354 $gradebook = gradebookapi_get_tid_gradebook($term->tid);
355 $students = gradebookapi_get_students($gradebook);
356 if ( $str_uids = implode(',', $students) )
357 $result = db_query("SELECT u.uid FROM {users} u WHERE u.status != 0 AND u.uid IN (". $str_uids .") ORDER BY u.name ASC");
358 else {
359 $result = FALSE;
360 }
361 if ($result) {
362 while ($account = db_fetch_object($result)) {
363 gradebookapi_calc_grades($account->uid, $term->tid, $recursive);
364 }
365 }
366 }
367
368 function gradebookapi_calc_assignment_grades($node) {
369 $terms = taxonomy_node_get_terms_by_vocabulary($node->nid, gradebookapi_get_vid());
370 foreach ($terms as $term) {
371 gradebookapi_calc_term_grades($term);
372 }
373 }
374
375 /**
376 * hook_taxonomy
377 *
378 * @return NONE
379 */
380 function gradebookapi_taxonomy($op, $type, $obj = NULL) {
381 if ($obj['vid'] == gradebookapi_get_vid()) {
382 switch ($op) {
383 case 'insert':
384 case 'update':
385 switch ($type) {
386 case 'term':
387 // TODO: can this code be removed?
388 // update grade cache
389 //if ($obj['parent_old'] && ($obj['parent_old'] != $obj['parent'])) {
390 // update old parent, but exclude 'tid' as hierarchy is not updated yet
391 //gradebookapi_calc_term_grades($obj['parent_old'], array('tid'=>array($obj['tid'])));
392 //}
393 //gradebookapi_calc_term_grades($obj['tid']);
394
395 // at this point, the term has been saved, but the hierarchy table has not been updated
396 // delete the term grades from cache and force cleaning up OLD tree branch
397 //gradebookapi_clear_term_grades($obj['tid']);
398 // update new parent, forcing inclusion of 'tid'
399 //gradebookapi_calc_term_grades($obj['tid'], array('parents'=>arra($obj['parent'])), array('parents'=>array($obj['parent_old'])));
400
401 // recalc old parent, excluding current 'tid'
402 //if ($obj['parent_old']) {
403 // gradebookapi_calc_term_grades($obj['parent_old'], array('children'=>array($obj['tid'])));
404 //}
405 // recalc new parent, including current 'tid' and excluding old parent
406 //gradebookapi_calc_term_grades($obj['parent'][0], array('parents'=>array($obj['parent_old'])), array('children'=>array($obj['tid'])));
407 break;
408 }
409 break;
410 case 'delete':
411 switch ($type) {
412 case 'term':
413 // TODO: by the time we get here, the hierarchy has already been cleaned.
414 // how do we tell the parent to recalc?
415 gradebookapi_clear_term_grades($obj['tid']);
416 // TODO: delete nodes associated with this term and it's children
417 break;
418 case 'vocabulary':
419 variable_del('gradebookapi_nav_vocabulary');
420 break;
421 }
422 break;
423 }
424 }
425 }
426
427 function gradebookapi_assignment_terms($node) {
428 $terms = array();
429 $vid = gradebookapi_get_vid();
430 $allterms = taxonomy_node_get_terms($node->nid);
431
432 foreach ($allterms as $term) {
433 // make sure we only look in the gradebook vocabulary
434 if ($term->vid == $vid) {
435 $terms[] = $term;
436 }
437 }
438
439 return $terms;
440 }
441
442 function gradebookapi_assignment_load($node) {
443 $additions = db_fetch_array(db_query('SELECT possible FROM {gradebookapi_assignment} WHERE nid = %d', $node->nid));
444 return $additions;
445 }
446
447 function gradebookapi_assignment_insert($node) {
448 db_query("INSERT INTO {gradebookapi_assignment} (nid, possible) VALUES (%d, %d)", $node->nid, $node->possible);
449
450 // we have to manually update terms as we can't control module order
451 taxonomy_node_save($node->nid, $node->taxonomy);
452
453 gradebookapi_calc_assignment_grades($node);
454 }
455
456 function gradebookapi_assignment_update($node) {
457 db_query("UPDATE {gradebookapi_assignment} SET possible = '%s' WHERE nid = %d", $node->possible, $node->nid);
458
459 // we have to manually update terms as we can't control module order
460 taxonomy_node_save($node->nid, $node->taxonomy);
461
462 gradebookapi_calc_assignment_grades($node);
463 }
464
465 function gradebookapi_assignment_delete($node) {
466 db_query('DELETE FROM {gradebookapi_assignment} WHERE nid = %d', $node->nid);
467 db_query('DELETE FROM {gradebookapi_grade} WHERE nid = %d', $node->nid);
468
469 gradebookapi_calc_assignment_grades($node);
470 }
471
472 function gradebookapi_assignment_view(&$node) {
473 // TODO: why is this not working?
474 $node->body = 'Possible: ' . $node->possible . '<br />' . $node->body;
475 $node->teaser = 'Possible: ' . $node->possible . '<br />' . $node->teaser;
476 }
477
478 function gradebookapi_assignment_form_elements($node) {
479 $form = array();
480
481 $form['gradebookapi'] = array(
482 '#type' => 'fieldset',
483 '#title' => t('Assignment Settings'),
484 '#tree' => FALSE,
485 //'#validate' => array('gradebookapi_assignment_validate' => array()),
486 );
487 $form['gradebookapi']['possible'] = array(
488 '#type' => 'textfield',
489 '#title' => t('Possible'),
490 '#default_value' => ($node->possible?$node->possible:'0'),
491 '#size' => 10,
492 '#maxlength' => 10,
493 '#required' => TRUE,
494 );
495
496 return $form;
497 }
498
499 function gradebookapi_assignment_validate($form) {
500 // safe int check
501 if (is_numeric($form->possible) ? intval($form->possible) != $form->possible : TRUE) {
502 form_set_error('possible', t('The possible value for the assignment must be an integer.'));
503 }
504 }
505
506 function gradebookapi_form_alter($form_id, &$form) {
507 if (isset($form['type']) && $form['type']['#value'] .'_node_form' == $form_id) {
508 $node = $form['#node'];
509 $types = gradebookapi_get_assignment_types();
510
511 // are we an assignment type?
512 if (in_array($node->type, $types)) {
513 $form = array_merge($form, gradebookapi_assignment_form_elements($node));
514 }
515 }
516 }
517
518 function gradebookapi_nodeapi($node, $op, $arg = 0) {
519 if (in_array($node->type, gradebookapi_get_assignment_types())) {
520 switch ($op) {
521 case 'load':
522 return gradebookapi_assignment_load($node);
523 case 'insert':
524 gradebookapi_assignment_insert($node);
525 break;
526 case 'update':
527 gradebookapi_assignment_update($node);
528 break;
529 case 'delete':
530 gradebookapi_assignment_delete($node);
531 break;
532 case 'view':
533 gradebookapi_assignment_view($node);
534 break;
535 case 'validate':
536 gradebookapi_assignment_validate($node);
537 break;
538 }
539 }
540 }
541
542 /*
543 *
544 * module_invoke_all('gradebookapi', $op, $type, &$obj);
545 * hook_gradebookapi($op, $type, &$obj);
546 */
547 // custom module_invoke_all due to obj reference
548 // TODO: This needs to be reviewed and reimplemented if needed
549 /*
550 function gradebookapi_invoke_gradebookapi($op, $type, &$obj) {
551 $return = array();
552 foreach (module_implements('gradebookapi') as $name) {
553 $function = $name .'_gradebookapi';
554 $result = $function($op, $type, $obj);
555 if (isset($result) && is_array($result)) {
556 $return = array_merge($return, $result);
557 }
558 else if (isset($result)) {
559 $return[] = $result;
560 }
561 }
562 return $return;
563 }
564 */
565
566 function gradebookapi_is_student($gradebook, $account = NULL) {
567 global $user;
568
569 if (is_null($account)) {
570 $account = $user;
571 }
572
573 // User #1 has all privileges:
574 if ($account->uid == 1) {
575 return TRUE;
576 }
577
578 $students = gradebookapi_get_students($gradebook);
579 if (in_array($account->uid, $students)) {
580 return TRUE;
581 }
582
583 return FALSE;
584 }
585
586 function gradebookapi_is_teacher($gradebook, $account = NULL) {
587 global $user;
588
589 if (is_null($account)) {
590 $account = $user;
591 }
592
593 // User #1 has all privileges:
594 if ($account->uid == 1) {
595 return TRUE;
596 }
597
598 $teachers = gradebookapi_get_teachers($gradebook);
599 if (in_array($account->uid, $teachers)) {
600 return TRUE;
601 }
602
603 return FALSE;
604 }
605
606 function gradebookapi_get_students($gradebook) {
607 return module_invoke_all('gradebookapi_students', $gradebook);
608 }
609
610 function gradebookapi_get_teachers($gradebook) {
611 return module_invoke_all('gradebookapi_teachers', $gradebook);
612 }

  ViewVC Help
Powered by ViewVC 1.1.2