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

Contents of /contributions/modules/library/library.module

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


Revision 1.9 - (show annotations) (download) (as text)
Tue Sep 30 17:37:46 2008 UTC (13 months, 3 weeks ago) by jastraat
Branch: MAIN
Changes since 1.8: +17 -0 lines
File MIME type: text/x-php
#314859 - moved unrelated function out of actions file so that triggers is not required.
1 <?php
2 // $Id$
3
4 if (module_exists('trigger')) {
5 include_once(drupal_get_path('module', 'library') .'/library.actions.inc');
6 }
7
8 /**
9 * Item type is not in the library.
10 */
11 define('LIBRARY_ITEM_NOT_IN_LIBRARY', 0);
12
13 /**
14 * Item type is part of the library collection.
15 */
16 define('LIBRARY_ITEM_IN_LIBRARY', 1);
17
18 /**
19 * Library Item is in circulation.
20 */
21 define('LIBRARY_CIRCULATION', 0);
22
23 /**
24 * Library Item is for reference only.
25 */
26 define('LIBRARY_REFERENCE_ONLY', 1);
27
28 /**
29 * Library Item is available.
30 */
31 define('LIBRARY_ITEM_AVAILABLE', 0);
32
33 /**
34 * Library Item is not available.
35 */
36 define('LIBRARY_ITEM_UNAVAILABLE', 1);
37
38 /**
39 * Library Items do not have barcodes.
40 */
41 define('LIBRARY_NO_BARCODES', 0);
42
43 /**
44 * Library Items have barcodes.
45 */
46 define('LIBRARY_BARCODES', 1);
47
48 define('LIBRARY_UNIQUE_TITLES', 1);
49
50 /**
51 * Library action that does not change item status.
52 */
53 define('LIBRARY_ACTION_NO_CHANGE', 0);
54
55 /**
56 * Library action that makes an item unavailable.
57 */
58 define('LIBRARY_ACTION_TYPE_UNAVAILABLE', 1);
59
60 /**
61 * Library action that makes an item available.
62 */
63 define('LIBRARY_ACTION_TYPE_AVAILABLE', 2);
64
65 /**
66 * Number of results to display per page
67 */
68 define('LIBRARY_RESULTS_PER_PAGE', 50);
69
70 /**
71 * Valid permissions for this module
72 * @return array An array of valid permissions for this module
73 */
74 function library_perm() {
75 $permissions = array(
76 'administer library',
77 'administer transactions',
78 'view library history',
79 'view own library history',
80 );
81 foreach (library_actions() as $aid => $action) {
82 $permissions[] = 'submit library '. $action['name'];
83 }
84 return $permissions;
85 }
86
87 /*
88 * Determine whether a user may perform any library action
89 */
90 function library_action_access($aid) {
91 $may_view_patron = FALSE;
92 if (user_access('view patron content')) {
93 $may_view_patron = TRUE;
94 }
95 else {
96 global $user;
97 $user_patron = patron_load_by_uid($user->uid);
98 if (is_object($user_patron)) {
99 $may_view_patron = TRUE;
100 }
101 }
102 if (user_access('administer transactions') && $may_view_patron) {
103 return TRUE;
104 }
105 elseif ($aid && $may_view_patron) {
106 $action = library_get_action($aid);
107 if ($action->name) {
108 return user_access('submit library '. $action->name);
109 }
110 }
111 return FALSE;
112 }
113
114 function library_history_access($node) {
115 if (user_access('view library history')) {
116 return TRUE;
117 }
118 else if (user_access('view own library history')) {
119 global $user;
120 $user_patron = patron_load_by_uid($user->uid);
121 if (is_object($user_patron) && $user_patron->nid == $node->nid) {
122 return TRUE;
123 }
124 }
125 return FALSE;
126 }
127
128 /**
129 * Implementation of hook_help()
130 */
131 function library_help($path, $arg) {
132 global $user;
133
134 switch ($path) {
135 case 'admin/settings/library':
136 return t('<p>Below are display options for library items and lists. </p>');
137 case 'admin/settings/library/duedates':
138 return t('<p>Due date and overdue item functionality are disabled by default. Below are options for handling how long items may be unavailable. To enable due date functionality, set a number of days greater than zero for the period an item may be made unavailable for one of the actions below. You may add further actions on the <a href="@libraryactions">Library Actions</a> page.</p>', array('@libraryactions' => url('admin/settings/library/actions')));
139 case 'admin/settings/library/actions':
140 return t('<p>Two actions are included by default: Check In and Check Out. You may rename these by clicking "edit action" or add more actions below. You must always have at least one action that makes items available and one that makes items unavailable. Each library action generates a custom trigger to which additional Drupal actions may be assigned. See <a href="@link">Tiggers</a> and <a href="@link2">Actions</a></p>', array('@link' => url('admin/build/trigger/library'), '@link2' => url('admin/settings/actions')));
141 case 'library-items/overdue':
142 return t('<p>Below is a list of all overdue library items. If you are a library administrator, you may <a href="@sendemail">send an email notifying all patrons with overdue items</a>.</p>', array('@sendemail' => url('library-items/overdue/email')));
143 }
144 }
145
146 function library_content_extra_fields($type_name) {
147 $extra = array();
148 if (module_exists('content') && variable_get('library_'. $type_name, LIBRARY_ITEM_NOT_IN_LIBRARY) == LIBRARY_ITEM_IN_LIBRARY) {
149 $extra['item_wrapper'] = array('label' => 'Library', 'weight' => -4);
150 }
151 return $extra;
152 }
153
154 /**
155 * Implementation of hook_form_alter()
156 */
157 function library_form_alter(&$form, $form_state, $form_id) {
158 if ($form_id == 'node_type_form' && isset($form['identity']['type']) && $form['#node_type']->type <> 'patron') {
159 $form['workflow']['library'] = array(
160 '#type' => 'radios',
161 '#title' => t('Library Item'),
162 '#default_value' => variable_get('library_'. $form['#node_type']->type, LIBRARY_ITEM_NOT_IN_LIBRARY),
163 '#options' => array(LIBRARY_ITEM_IN_LIBRARY => t('Yes'), LIBRARY_ITEM_NOT_IN_LIBRARY => t('No')),
164 '#description' => t('Library items will appear in library views.')
165 );
166 }
167 else if (isset($form['type']) && isset($form['#node']) && $form['type']['#value'] .'_node_form' == $form_id) {
168 $node = $form['#node'];
169 $node_type = $form['type']['#value'];
170 if (variable_get('library_'. $node_type, LIBRARY_ITEM_NOT_IN_LIBRARY) == LIBRARY_ITEM_IN_LIBRARY) {
171 $form['#cache'] = TRUE;
172
173 if (isset($form_state['item_count'])) {
174 $item_count = $form_state['item_count'];
175 }
176 else {
177 $item_count = max(1, empty($node->items) ? 1 : count($node->items));
178 }
179
180 if (variable_get('library_unique_titles', 0) == LIBRARY_UNIQUE_TITLES) {
181 $type = node_get_types('type', $node_type);
182 if ($type->has_title) {
183 unset($form['title']);
184 }
185 $form['title_wrapper'] = array(
186 '#tree' => FALSE,
187 '#prefix' => '<div class="clear-block" id="title-wrapper">',
188 '#suffix' => '</div>',
189 '#weight' => -5,
190 );
191 $form['title_wrapper']['title'] = array(
192 '#type' => 'textfield',
193 '#title' => check_plain($type->title_label),
194 '#required' => TRUE,
195 '#default_value' => $node->title,
196 '#maxlength' => 255,
197 '#ahah' => array(
198 'path' => 'library/title_js',
199 'wrapper' => 'title-wrapper',
200 ),
201 '#weight' => -5,
202 );
203 }
204
205 // Add a wrapper for the items and more button.
206 $form['item_wrapper'] = array(
207 '#tree' => FALSE,
208 '#weight' => -4,
209 '#prefix' => '<div class="clear-block" id="library-item-wrapper">',
210 '#suffix' => '</div>',
211 );
212 // Container for just the library items.
213 $form['item_wrapper']['items'] = array(
214 '#prefix' => '<div id="library-items">',
215 '#suffix' => '</div>',
216 '#theme' => 'library_items_field',
217 );
218 // Add the current choices to the form.
219 for ($delta = 0; $delta < $item_count; $delta++) {
220 $form['item_wrapper']['items'][$delta] = _library_item_form($delta, $node->items[$delta]);
221 }
222
223 // We name our button 'library_more' to avoid conflicts with other modules using
224 // AHAH-enabled buttons with the id 'more'.
225 $form['item_wrapper']['library_more'] = array(
226 '#type' => 'submit',
227 '#value' => t('Add an Item'),
228 '#weight' => 1,
229 '#submit' => array('library_more_items_submit'), // If no javascript action.
230 '#ahah' => array(
231 'path' => 'library/js',
232 'wrapper' => 'library-items',
233 'method' => 'replace',
234 'effect' => 'fade',
235 ),
236 );
237 $form['#submit'][] = 'library_node_form_submit';
238 }
239 }
240 else if ($form_id == 'search_form' && $form['module']['#value'] == 'library' && user_access('use advanced search')) {
241 // Keyword boxes:
242 $form['advanced'] = array(
243 '#type' => 'fieldset',
244 '#title' => t('More Search Options'),
245 '#collapsible' => TRUE,
246 '#collapsed' => TRUE,
247 '#attributes' => array('class' => 'search-advanced'),
248 );
249 $form['advanced']['keywords'] = array(
250 '#prefix' => '<div class="criterion">',
251 '#suffix' => '</div>',
252 );
253 $form['advanced']['keywords']['or'] = array(
254 '#type' => 'textfield',
255 '#title' => t('Containing any of the words'),
256 '#size' => 30,
257 '#maxlength' => 255,
258 );
259 $form['advanced']['keywords']['phrase'] = array(
260 '#type' => 'textfield',
261 '#title' => t('Containing the phrase'),
262 '#size' => 30,
263 '#maxlength' => 255,
264 );
265 $form['advanced']['keywords']['negative'] = array(
266 '#type' => 'textfield',
267 '#title' => t('Containing none of the words'),
268 '#size' => 30,
269 '#maxlength' => 255,
270 );
271
272 // Taxonomy box:
273 if ($taxonomy = module_invoke('taxonomy', 'form_all', 1)) {
274 $form['advanced']['category'] = array(
275 '#type' => 'select',
276 '#title' => t('Only in the category(s)'),
277 '#prefix' => '<div class="criterion">',
278 '#size' => 10,
279 '#suffix' => '</div>',
280 '#options' => $taxonomy,
281 '#multiple' => TRUE,
282 );
283 }
284
285 // Node types:
286 $types = array_map('check_plain', library_get_item_types('names'));
287
288 $form['advanced']['type'] = array(
289 '#type' => 'checkboxes',
290 '#title' => t('Only of the type(s)'),
291 '#prefix' => '<div class="criterion">',
292 '#suffix' => '</div>',
293 '#options' => $types,
294 );
295
296 $form['advanced']['submit'] = array(
297 '#type' => 'submit',
298 '#value' => t('Advanced search'),
299 '#prefix' => '<div class="action">',
300 '#suffix' => '</div>',
301 );
302
303 $form['#validate'][] = 'node_search_validate';
304 }
305 else if (module_exists('content') && $form_id == 'content_field_edit_form') {
306 $node_type = $form['type_name']['#value'];
307 if (variable_get('library_'. $node_type, LIBRARY_ITEM_NOT_IN_LIBRARY) == LIBRARY_ITEM_IN_LIBRARY) {
308 $newform = array();
309 $newform['library_field_settings'] = array(
310 '#type' => 'fieldset',
311 '#title' => t('Library Settings'),
312 );
313 $newform['library_field_settings']['library_display_field_'. $form['field_name']['#value']] = array(
314 '#type' => 'checkbox',
315 '#title' => t('Display this field in the library'),
316 '#default_value' => variable_get('library_display_field_'. $form['field_name']['#value'], 0),
317 '#return_value' => 1,
318 );
319 $pos = array_search('widget', array_keys($form));
320
321 $form = array_merge(array_slice($form, 0, $pos), $newform, array_slice($form, $pos));
322 $form['#submit'][] = 'library_field_submit';
323 }
324 }
325 else if ($form_id == '') {
326 }
327 else if ($form_id == 'search_block_form' && variable_get('library_search_block', 0) == 1) {
328 $form['#submit'][] = 'library_search_box_form_submit';
329 }
330 }
331
332 function library_field_submit($form_id, &$form_values) {
333 $values = $form_values['values'];
334 variable_set('library_display_field_'. $values['field_name'], $values['library_display_field_'. $values['field_name']]);
335 }
336
337 function library_search_box_form_submit($form, &$form_state) {
338 $form_id = $form['form_id']['#value'];
339 $form_state['redirect'] = 'search/library/'. trim($form_state['values'][$form_id]);
340 }
341
342 /*
343 * Gets the node object, modifies the title, and updates the node in the form_state
344 */
345 function library_node_form_submit($form, &$form_state) {
346 if (variable_get('library_item_barcodes', LIBRARY_NO_BARCODES) == LIBRARY_BARCODES) {
347 $node = node_submit($form_state['values']);
348 $items = $node->items;
349 $row = 0;
350 $last_key = count($items) - 1;
351 foreach ($items as $key => $item) {
352 //Remove empty item instance if it's not being used
353 if ($key > 0 && $key == $last_key && empty($item['id']) && empty($item['barcode'])) {
354 unset($node->items[$key]);
355 }
356 $row++;
357 }
358 $form_state['values'] = (array)$node;
359 }
360 }
361
362 /**
363 * Submit handler to add another instance of a library item to a node form. This handler is used when
364 * javascript is not available. It makes changes to the form state and the
365 * entire form is rebuilt during the page reload.
366 */
367 function library_more_items_submit($form, &$form_state) {
368 // Set the form to rebuild and run submit handlers.
369 node_form_submit_build_node($form, $form_state);
370
371 // Make the changes we want to the form state.
372 if ($form_state['values']['library_more']) {
373 $form_state['item_count'] = count($form_state['values']['items']) + 1;
374 }
375 }
376
377
378 /*
379 * Subform for an instance of a library item on node create/edit
380 */
381 function _library_item_form($delta, $item = array()) {
382
383 $form = array(
384 '#tree' => TRUE,
385 );
386
387 $form['id'] = array(
388 '#type' => 'hidden',
389 '#value' => isset($item['id']) ? $item['id'] : '',
390 '#parents' => array('items', $delta, 'id'),
391 );
392 if (variable_get('library_item_barcodes', LIBRARY_NO_BARCODES) == LIBRARY_BARCODES) {
393 $form['barcode'] = array(
394 '#type' => 'textfield',
395 '#title' => t('Barcode'),
396 '#default_value' => isset($item['barcode']) ? $item['barcode'] : '',
397 '#required' => TRUE,
398 '#parents' => array('items', $delta, 'barcode'),
399 );
400 }
401 $form['in_circulation'] = array(
402 '#type' => 'checkbox',
403 '#title' => t('Reference Only'),
404 '#default_value' => isset($item['in_circulation']) ? $item['in_circulation'] : LIBRARY_CIRCULATION,
405 '#return_value' => LIBRARY_REFERENCE_ONLY,
406 '#parents' => array('items', $delta, 'in_circulation'),
407 );
408 $form['notes'] = array(
409 '#type' => 'textfield',
410 '#title' => t('Notes'),
411 '#size' => 20,
412 '#maxlength' => 128,
413 '#default_value' => isset($item['notes']) ? $item['notes'] : '',
414 '#parents' => array('items', $delta, 'notes'),
415 );
416 if ($delta > 0) {
417 $form['delete'] = array(
418 '#type' => 'checkbox',
419 '#title' => t('Delete'),
420 '#default_value' => 0,
421 '#return_value' => 1,
422 '#parents' => array('items', $delta, 'delete'),
423 );
424 }
425 if ($item['library_status'] == LIBRARY_ITEM_UNAVAILABLE) {
426 $form['in_circulation']['#disabled'] = TRUE;
427 }
428
429 return $form;
430 }
431
432
433 /**
434 * Implementation of hook_init().
435 */
436 function library_init() {
437 drupal_add_css(drupal_get_path('module', 'library') .'/library.css');
438 }
439
440 /**
441 * Menu callback; loads a library object
442 */
443 function library_load($item_id) {
444 if (!is_numeric($item_id)) {
445 return FALSE;
446 }
447 else {
448 $item = db_fetch_object(db_query_range("SELECT * FROM {library} l, {node} n WHERE n.nid = l.nid AND id = %d", $item_id, 0, 1));
449
450 if ($item->in_circulation == LIBRARY_CIRCULATION && $item->library_status == LIBRARY_ITEM_UNAVAILABLE) {
451 $last = library_get_last_transaction_by_item($item, LIBRARY_ACTION_TYPE_UNAVAILABLE);
452 if ($last) {
453 $item->last_patron_id = $last->patron_id;
454 $item->last_transaction_id = $last->tid;
455 $item->last_transaction_name = $last->action_name;
456 if (!empty($last->duedate)) {
457 $item->last_due_date = $last->duedate;
458 }
459 }
460 }
461 }
462 return $item;
463 }
464
465 /**
466 * Implementation of hook_menu().
467 */
468 function library_menu() {
469 $items['library-items'] = array(
470 'title' => 'Library',
471 'page callback' => 'library_display_items',
472 'access arguments' => array('access content'),
473 'type' => MENU_SUGGESTED_ITEM,
474 'file' => 'library.pages.inc',
475 );
476 $items['node/%node/library/history'] = array(
477 'title' => 'History',
478 'page callback' => 'library_history',
479 'page arguments' => array(1),
480 'access callback' => 'library_history_access',
481 'access arguments' => array(1),
482 'type' => MENU_LOCAL_TASK,
483 'weight' => 2,
484 'file' => 'library.pages.inc',
485 );
486 $items['library-items/transaction/view/%'] = array(
487 'title' => 'View a Transaction',
488 'page callback' => 'library_transaction_view',
489 'page arguments' => array(3),
490 'access arguments' => array('administer transactions'),
491 'type' => MENU_CALLBACK,
492 'file' => 'library.pages.inc',
493 );
494 $items['library-items/transaction/%/%library'] = array(
495 'title' => 'Perform a Library Transaction',
496 'page callback' => 'drupal_get_form',
497 'page arguments' => array('library_transaction_form', 2, 3),
498 'access callback' => 'library_action_access',
499 'access arguments' => array(2),
500 'type' => MENU_CALLBACK,
501 'file' => 'library.pages.inc',
502 );
503 $items['library-items/overdue'] = array(
504 'title' => 'Overdue Items',
505 'page callback' => 'library_overdue_items',
506 'access arguments' => array('administer transactions'),
507 'type' => MENU_SUGGESTED_ITEM,
508 'file' => 'library.pages.inc',
509 );
510 $items['library-items/overdue/email'] = array(
511 'title' => 'Email patrons with overdue items',
512 'page callback' => 'library_notify_overdue',
513 'access arguments' => array('administer transactions'),
514 'type' => MENU_CALLBACK,
515 'file' => 'library.admin.inc',
516 );
517
518 $items['admin/settings/library'] = array(
519 'title' => 'Library Settings',
520 'description' => 'Edit Library Settings.',
521 'page callback' => 'drupal_get_form',
522 'page arguments' => array('library_admin_settings'),
523 'access arguments' => array('administer library'),
524 'file' => 'library.admin.inc',
525 );
526 $items['admin/settings/library/display'] = array(
527 'title' => 'Display',
528 'type' => MENU_DEFAULT_LOCAL_TASK,
529 'weight' => -10,
530 );
531 $items['admin/settings/library/duedates'] = array(
532 'title' => 'Loan Periods',
533 'description' => 'Enable loan periods for checkouts and configure overdue emails.',
534 'page callback' => 'drupal_get_form',
535 'page arguments' => array('library_admin_settings_overdue'),
536 'access arguments' => array('administer library'),
537 'file' => 'library.admin.inc',
538 'type' => MENU_LOCAL_TASK,
539 );
540 $items['admin/settings/library/actions'] = array(
541 'title' => 'Library Actions',
542 'description' => 'View, edit, or add library actions.',
543 'page callback' => 'drupal_get_form',
544 'page arguments' => array('library_admin_new_action'),
545 'access arguments' => array('administer library'),
546 'type' => MENU_LOCAL_TASK,
547 'file' => 'library.admin.inc',
548 );
549 $items['admin/settings/library/actions/edit'] = array(
550 'title' => 'Edit Library Action',
551 'page arguments' => array('library_admin_action'),
552 'access arguments' => array('administer library'),
553 'type' => MENU_CALLBACK,
554 'file' => 'library.admin.inc',
555 );
556 $items['library-items/autocomplete'] = array(
557 'title' => t('Library Autocomplete'),
558 'page callback' => 'library_autocomplete',
559 'access arguments' => array('access content'),
560 'type' => MENU_CALLBACK,
561 'file' => 'library.admin.inc',
562 );
563 $items['library/js'] = array(
564 'title' => 'Javascript Add More Form',
565 'page callback' => 'library_item_js',
566 'access arguments' => array('access content'),
567 'type' => MENU_CALLBACK,
568 );
569 $items['library/title_js'] = array(
570 'title' => 'Javascript Title Validation',
571 'page callback' => 'library_title_js',
572 'access arguments' => array('access content'),
573 'type' => MENU_CALLBACK,
574 );
575 return $items;
576 }
577
578 /**
579 * Implementation of hook_forms().
580 */
581 function library_forms() {
582 $forms['library_admin_new_action']['callback'] = 'library_admin_action';
583 return $forms;
584 }
585
586 /**
587 * Menu callback for AHAH additions.
588 */
589 function library_item_js() {
590 $delta = count($_POST['items']);
591
592 $items = $_POST['items'];
593 // Build the new form.
594 $form_state = array('submitted' => FALSE);
595 $form_build_id = $_POST['form_build_id'];
596 // Add the new element to the stored form. Without adding the element to the
597 // form, Drupal is not aware of this new elements existence and will not
598 // process it. We retreive the cached form, add the element, and resave.
599 $form = form_get_cache($form_build_id, $form_state);
600 unset($form['item_wrapper']['items'], $form['item_wrapper']['library_more']);
601 foreach ($items as $key => $item) {
602 $form['item_wrapper']['items'][$key] = _library_item_form($key, $item);
603 }
604 $form['item_wrapper']['items'][$delta] = _library_item_form($delta);
605 form_set_cache($form_build_id, $form, $form_state);
606
607 $form += array(
608 '#post' => $_POST,
609 '#programmed' => FALSE,
610 );
611
612 // Rebuild the form.
613 $form = form_builder($form['type']['#value'] .'_node_form', $form, $form_state);
614
615 // Render the new output.
616 $item_form = $form['item_wrapper']['items'];
617 unset($item_form['#prefix'], $item_form['#suffix']); // Prevent duplicate wrappers.
618 $item_form['#theme'] = 'library_items_field';
619 $item_form[$delta]['#attributes']['class'] = empty($item_form[$delta]['#attributes']['class']) ? 'ahah-new-content' : $item_form[$delta]['#attributes']['class'] .' ahah-new-content';
620 $output = theme('status_messages') . drupal_render($item_form);
621
622 drupal_json(array('status' => TRUE, 'data' => $output));
623 }
624
625 /**
626 * Menu callback for AHAH additions.
627 * Checks whether the title is unique
628 */
629 function library_title_js() {
630 $title = $_POST['title'];
631
632 $form_state = array('submitted' => FALSE);
633 $form_build_id = $_POST['form_build_id'];
634 $form = form_get_cache($form_build_id, $form_state);
635
636 $nid = $form['nid']['#value'];
637 $type = $form['type']['#value'];
638 $title_label = $form['title_wrapper']['title']['#title'];
639
640 unset($form['title_wrapper']['title']);
641 $form['title_wrapper']['title'] = array(
642 '#type' => 'textfield',
643 '#title' => check_plain($title_label),
644 '#required' => TRUE,
645 '#default_value' => $title,
646 '#maxlength' => 255,
647 '#ahah' => array(
648 'path' => 'library/title_js',
649 'wrapper' => 'title-wrapper',
650 ),
651 '#weight' => -5,
652 );
653
654 form_set_cache($form_build_id, $form, $form_state);
655
656 $form += array(
657 '#post' => $_POST,
658 '#programmed' => FALSE,
659 );
660
661 // Rebuild the form.
662 $form = form_builder($form['type']['#value'] .'_node_form', $form, $form_state);
663 $title_field = $form['title_wrapper']['title'];
664
665 if (is_numeric($nid)) {
666 $repeats = db_fetch_object(db_query_range("SELECT nid FROM {node} WHERE title = '%s' AND type = '%s' and nid <> %d", check_plain($title), $type, $nid, 0, 1));
667 }
668 else {
669 $repeats = db_fetch_object(db_query_range("SELECT nid FROM {node} WHERE title = '%s' AND type = '%s'", $title, $type, 0, 1));
670 }
671 if ($repeats) {
672 drupal_json(array('status' => TRUE, 'data' => drupal_render($title_field) .'<p class="ahah-new-content warning">WARNING: Title is not unique. You may want to add this as a copy to the existing node. '. l('See Duplicate', 'node/'. $repeats->nid) .'</p>'));
673 }
674 else {
675 drupal_json(array('status' => TRUE, 'data' => drupal_render($title_field) .'<p class="ahah-new-content ok">Title is unique.</p>'));
676 }
677 }
678
679 /**
680 * Implementation of hook_search()
681 */
682 function library_search($op = 'search', $keys = NULL, $skip_access_check = FALSE) {
683 switch ($op) {
684 case 'name':
685 return t('Library');
686 case 'search':
687 // Build matching conditions
688 list($join1, $where1) = _db_rewrite_sql();
689 $arguments1 = array();
690 $conditions1 = 'n.status = 1';
691
692 if ($type = search_query_extract($keys, 'type')) {
693 $types = array();
694 foreach (explode(',', $type) as $t) {
695 $types[] = "n.type = '%s'";
696 $arguments1[] = $t;
697 }
698 $conditions1 .= ' AND ('. implode(' OR ', $types) .')';
699 $keys = search_query_insert($keys, 'type');
700 }
701 else {
702 $types = array();
703
704 foreach (library_get_item_types() as $t) {
705 $types[] = "n.type = '%s'";
706 $arguments1[] = $t;
707 }
708 $conditions1 .= ' AND ('. implode(' OR ', $types) .')';
709 $keys = search_query_insert($keys, 'type');
710 }
711
712 if ($category = search_query_extract($keys, 'category')) {
713 $categories = array();
714 foreach (explode(',', $category) as $c) {
715 $categories[] = "tn.tid = %d";
716 $arguments1[] = $c;
717 }
718 $conditions1 .= ' AND ('. implode(' OR ', $categories) .')';
719 $join1 .= ' INNER JOIN {term_node} tn ON n.vid = tn.vid';
720 $keys = search_query_insert($keys, 'category');
721 }
722 // Build ranking expression (we try to map each parameter to a
723 // uniform distribution in the range 0..1).
724 $ranking = array();
725 $arguments2 = array();
726 $join2 = '';
727 // Used to avoid joining on node_comment_statistics twice
728 $stats_join = FALSE;
729 $total = 0;
730 if ($weight = (int)variable_get('node_rank_relevance', 5)) {
731 // Average relevance values hover around 0.15
732 $ranking[] = '%d * i.relevance';
733 $arguments2[] = $weight;
734 $total += $weight;
735 }
736 if (module_exists('statistics') && variable_get('statistics_count_content_views', 0) &&
737 $weight = (int)variable_get('node_rank_views', 5)) {
738 // Inverse law that maps the highest view count on the site to 1 and 0 to 0.
739 $scale = variable_get('node_cron_views_scale', 0.0);
740 $ranking[] = '%d * (2.0 - 2.0 / (1.0 + MAX(nc.totalcount) * %f))';
741 $arguments2[] = $weight;
742 $arguments2[] = $scale;
743 $join2 .= ' LEFT JOIN {node_counter} nc ON nc.nid = i.sid';
744 $total += $weight;
745 }
746
747 // When all search factors are disabled (ie they have a weight of zero),
748 // the default score is based only on keyword relevance and there is no need to
749 // adjust the score of each item.
750 if ($total == 0) {
751 $select2 = 'i.relevance AS score';
752 $total = 1;
753 }
754 else {
755 $select2 = implode(' + ', $ranking) .' AS score';
756 }
757
758 // Do search.
759 $find = do_search($keys, 'node', 'INNER JOIN {node} n ON n.nid = i.sid '. $join1, $conditions1 . (empty($where1) ? '' : ' AND '. $where1), $arguments1, $select2, $join2, $arguments2, 'ORDER BY n.title, score DESC');
760
761 // Load results.
762 $results = array();
763 $var = array();
764 $var = library_get_table_header();
765 foreach ($find as $item) {
766 // Build the node body.
767 $node = node_load($item->sid);
768 $node_rows = array();
769
770 $node_rows = library_get_table_row($node, $var);
771 foreach ($node_rows as $row) {
772 $results[] = $row;
773 }
774
775 }
776 return $results;
777 }
778 }
779
780 /**
781 * Implementation of hook_search_page()
782 */
783 function library_search_page($results) {
784 $var = library_get_table_header();
785
786 $header = $var['header'];
787 $rows = $results;
788
789 $output = theme('table', $header, $rows, array('class' => 'library-list'));
790 $output .= theme('pager', NULL, 10);
791 return $output;
792 }
793
794 /**
795 * Implementation of hook_nodeapi()
796 */
797 function library_nodeapi(&$node, $op, $teaser = NULL, $page = NULL) {
798 if (library_item_in_library($node)) {
799 switch ($op) {
800 case 'load':
801 $result = array();
802 $result = library_load_items($node);
803 return $result;
804 break;
805
806 case 'validate':
807 case 'submit':
808 $node = library_validate_items($node);
809 break;
810
811 case 'insert':
812 case 'update':
813 library_update_items($node);
814 break;
815
816 case 'delete':
817 db_query('DELETE FROM {library} WHERE nid = %d', $node->nid);
818 break;
819
820 case 'view':
821 $item_weight = -4;
822 if (module_exists('content')) {
823 foreach (variable_get('content_extra_weights_'. $node->type, array()) as $key => $value) {
824 if ($key == 'item_wrapper') {
825 $item_weight = $value;
826 }
827 }
828 }
829 $node->content['items'] = array(
830 '#value' => theme('library_items', $node),
831 '#weight' => $item_weight,
832 );
833 break;
834 }
835 }
836 }
837
838 /**
839 * Load Library Item Data for a given node
840 */
841 function library_load_items($node) {
842 $result = db_query('SELECT id FROM {library} WHERE nid = %d ORDER BY id', $node->nid);
843 $items = array();
844 $row = 0;
845 if ($result) {
846 while ($item = db_fetch_object($result)) {
847 $library_item = library_load($item->id);
848 $items[$row]['library_status'] = ($library_item->library_status ? $library_item->library_status : LIBRARY_ITEM_AVAILABLE);
849 $items[$row]['last_patron_id'] = ($library_item->last_patron_id ? $library_item->last_patron_id : '');
850 $items[$row]['last_due_date'] = ($library_item->last_due_date ? $library_item->last_due_date : '');
851 $items[$row]['last_transaction_id'] = ($library_item->last_transaction_id ? $library_item->last_transaction_id : '');
852 $items[$row]['last_transaction_name'] = ($library_item->last_transaction_name ? $library_item->last_transaction_name : '');
853 $items[$row]['id'] = $item->id;
854 $items[$row]['in_circulation'] = $library_item->in_circulation;
855 $items[$row]['barcode'] = check_plain($library_item->barcode);
856 $items[$row]['notes'] = check_plain($library_item->notes);
857 $row++;
858 }
859 }
860 else {
861 db_query('INSERT INTO {library} (nid, in_circulation, library_status, created) VALUES (%d, %d, %d, %d)', $node->nid, LIBRARY_CIRCULATION, LIBRARY_ITEM_AVAILABLE, time());
862 $items[$row]['id'] = db_last_insert_id('library', 'id');
863 $items[$row]['in_circulation'] = LIBRARY_CIRCULATION;
864 $items[$row]['library_status'] = LIBRARY_ITEM_AVAILABLE;
865 }
866 return array('items' => $items);
867 }
868
869 /**
870 * Update Library Item Data for a given node
871 */
872 function library_update_items($node) {
873 $items = $node->items;
874 $row = 0;
875 if ($items) {
876 foreach ($items as $key => $item) {
877 if ($item['delete'] == 1) {
878 db_query('DELETE FROM {library} WHERE id = %d', $item['id']);
879 unset($node->items[$key]);
880 }
881 else if (empty($item['id'])) {
882 db_query("INSERT INTO {library} (nid, barcode, in_circulation, library_status, notes, created) VALUES (%d, '%s', %d, %d, '%s', %d)", $node->nid, check_plain($item['barcode'] .''), $item['in_circulation'], LIBRARY_ITEM_AVAILABLE, $item['notes'], time());
883 $node->items[$row]['id'] = db_last_insert_id('library', 'id');
884 }
885 else {
886 db_query("UPDATE {library} SET barcode = '%s', in_circulation = %d, notes = '%s' WHERE id = %d", check_plain($item['barcode'] .''), $item['in_circulation'], check_plain($item['notes']), $item['id']);
887 }
888
889 $row++;
890 }
891 }
892 }
893
894 /**
895 * Validate Library Item Data for a given node
896 */
897 function library_validate_items($node) {
898 if (variable_get('library_item_barcodes', LIBRARY_NO_BARCODES) == LIBRARY_BARCODES) {
899 $items = $node->items;
900 if ($items) {
901 global $base_path;
902 //Check that each barcode is unique
903 foreach ($items as $key => $item) {
904 $result = db_result(db_query("SELECT COUNT(*) FROM {library} WHERE barcode = '%s' AND barcode <> ''", $item['barcode']));
905 if ((empty($item['id']) && $result) || (!empty($item['id']) && $result > 1)) {
906 form_set_error('items]['. $key .'][barcode', t('The barcode %barcode already exists. Please enter a different barcode.', array('%barcode' => $item['barcode'])));
907 }
908 }
909 }
910 else {
911 form_set_error('items', t('You must enter information for at least one item.'));
912 }
913 }
914 }
915
916 /**
917 * Check whether a given item (based on its item type) is part of the library
918 * @returns Boolean
919 */
920 function library_item_in_library($node = NULL, $item_id = NULL) {
921 if ($node && variable_get('library_'. $node->type, LIBRARY_ITEM_NOT_IN_LIBRARY) == LIBRARY_ITEM_IN_LIBRARY) {
922 return TRUE;
923 }
924 else if ($item_id && db_result(db_query("SELECT COUNT(*) FROM {library} WHERE id = %d", $item_id))) {
925 return TRUE;
926 }
927 else {
928 return FALSE;
929 }
930 }
931
932 /**
933 * Returns a list of overdue library items
934 */
935 function library_get_overdue_items() {
936 //Select all the nodes that have ever had an item made unavailable
937 $items = db_query("SELECT id FROM {library} WHERE library_status = %d", LIBRARY_ITEM_UNAVAILABLE);
938 $overdueitems = array();
939 while ($result = db_fetch_object($items)) {
940 $item = library_load($result->id);
941 if (!empty($item->last_patron_id) && !empty($item->last_due_date) && (time() > $item->last_due_date)) {
942 $patron = node_load($item->last_patron_id);
943 if (!isset($overdueitems[$item->last_patron_id]['patron']['patron_email'])) {
944 $overdueitems[$item->last_patron_id]['patron']['patron_email'] = $patron->email;
945 $overdueitems[$item->last_patron_id]['patron']['patron_name'] = $patron->name_first .' '. $patron->name_last;
946 }
947 $overdueitems[$item->last_patron_id]['items'][$item->id] = array('item_name' => $item->title, 'nid' => $item->nid, 'due_date' => $item->last_due_date, 'in_circulation' => $item->in_circulation);
948 }
949 }
950 return $overdueitems;
951 }
952
953 /**
954 * Get the due date given a check out date
955 *
956 * @param $checkout_date
957 * The date an item was made unavailable
958 * @param $action
959 * The clean name of the action performed
960 * @param $type
961 * The node type
962 * @returns a UNIX TIMESTAMP if due dates are enabled else it returns NULL
963 */
964 function library_get_due_date($checkout_date = NULL, $action, $type) {
965 if (empty($checkout_date)) {
966 $checkout_date = time();
967 }
968 $allowed = variable_get('library_period_for_'. $type .'_'. $action, 0);
969 if ($allowed > 0) {
970 return ($checkout_date + $allowed);
971 }
972 else {
973 return NULL;
974 }
975 }
976
977 /**
978 * Check to see if due dates are enabled for any actions
979 *
980 * @returns BOOLEAN
981 */
982 function library_duedates_enabled($type = NULL) {
983 if (is_null($type)) {
984 $duedates = 0;
985 foreach (library_get_item_types() as $type) {
986 if ($duedates == 0) {
987 $duedates = variable_get('library_'. $type .'_due_dates', 0);
988 if ($duedates > 0) {
989 return TRUE;
990 }
991 }
992 else {
993 return TRUE;
994 }
995 }
996 return FALSE;
997 }
998 else {
999 $duedates = variable_get('library_'. $type .'_due_dates', 0);
1000 return ($duedates > 0);
1001 }
1002 }
1003
1004 function library_get_transactions_by_item($item) {
1005 $result = db_query('SELECT lt.created, lt.tid, la.aid, la.name as "action_name",
1006 la.status_change, lt.patron_id, lt.item_id, lt.duedate, lt.notes, l.in_circulation, l.barcode, n.title as "item_name", n.nid
1007 FROM {library_transactions} lt, {library_actions} la, {library} l, {node} n
1008 WHERE la.aid = lt.action_aid AND n.nid = l.nid AND lt.item_id = l.id AND l.id = %d
1009 ORDER BY lt.created DESC', $item->id);
1010 while ($transaction = db_fetch_object($result)) {
1011 $transaction->library_status = isset($item->library_status) ? $item->library_status : LIBRARY_ITEM_AVAILABLE;
1012 $transactions[] = $transaction;
1013 }
1014 if (!empty($transactions)) {
1015 return $transactions;
1016 }
1017 else {
1018 return NULL;
1019 }
1020 }
1021
1022 function library_get_transactions_by_node($node) {
1023 if ($node->type == 'patron') {
1024 $temp_transactions = library_get_transactions_by_patron($node);
1025 if (!empty($temp_transactions)) {
1026 $transactions[] = $temp_transactions;
1027 }
1028 }
1029 else {
1030 foreach ($node->items as $instance) {
1031 $item = (object) $instance;
1032 $temp_transactions = library_get_transactions_by_item($item);
1033 if (!empty($temp_transactions)) {
1034 $transactions[] = library_get_transactions_by_item($item);
1035 }
1036 }
1037 }
1038 return $transactions;
1039 }
1040
1041 function library_get_transactions_by_patron($node) {
1042 $result = db_query('SELECT lt.created, lt.tid, la.aid, la.name as "action_name", la.status_change, lt.patron_id, lt.item_id, lt.duedate, lt.notes, n.title as "item_name", n.nid FROM {library_transactions} lt, {library_actions} la, {node} n WHERE la.aid = lt.action_aid AND n.nid = lt.nid AND lt.patron_id = %d ORDER BY lt.created DESC', $node->nid);
1043
1044 $items_result = db_query('SELECT DISTINCT item_id FROM {library_transactions} WHERE patron_id = %d', $node->nid);
1045 $statuses = array();
1046 $barcodes = array();
1047 $circulation = array();
1048 while ($instance = db_fetch_object($items_result)) {
1049 $item = library_load($instance->item_id);
1050 $barcodes[$item->id] = $item->barcode;
1051 $statuses[$item->id] = $item->library_status;
1052 $circulation[$item->id] = $item->in_circulation;
1053 }
1054 $transactions = array();
1055 while ($transaction = db_fetch_object($result)) {
1056 $transaction->library_status = $statuses[$transaction->item_id];
1057 $transaction->barcode = $barcodes[$transaction->item_id];
1058 $transaction->in_circulation = $circulation[$transaction->item_id];
1059 $transactions[] = $transaction;
1060 }
1061 if (!empty($transactions)) {
1062 return $transactions;
1063 }
1064 else {
1065 return NULL;
1066 }
1067 }
1068
1069 function library_get_transaction_by_tid($tid) {
1070 if (isset($tid) && is_numeric($tid)) {
1071 $result = db_fetch_object(db_query('SELECT lt.created, la.name as "action_name", la.status_change, lp.nid as "patron_id", lp.name_last, lp.name_first, n.title as "item_name", n.type as "item_type", lt.item_id as "item_id", lt.nid as "nid", lt.duedate, lt.notes FROM {library_transactions} lt, {library_actions} la, {library_patrons} lp, {node} n WHERE la.aid = lt.action_aid AND lp.nid = lt.patron_id AND n.nid = lt.nid AND lt.tid = %d', $tid));
1072 return $result;
1073 }
1074 else {
1075 return NULL;
1076 }
1077 }
1078
1079 /**
1080 * @param $node
1081 * Node object or array
1082 * @param $transaction_type
1083 * (optional) Type of transaction to return
1084 * @return
1085 * Transaction array or NULL if none found
1086 */
1087 function library_get_last_transaction_by_item($item, $type = NULL) {
1088 $transactions = library_get_transactions_by_item($item);
1089 if (isset($transactions)) {
1090 foreach ($transactions as $transaction) {
1091 if ($type && $type == $transaction->status_change) {
1092 return $transaction;