diff --git a/ding_reservation.info b/ding_reservation.info index bc113f9..9bfd698 100644 --- a/ding_reservation.info +++ b/ding_reservation.info @@ -3,7 +3,7 @@ description = Allows users to reserve materials. package = Ding! version = "7.x-0.18" core = 7.x -files[] = ding_reservation.module +files[] = includes/ding_reservation_entity.inc files[] = ding_reservation.test dependencies[] = ding_base dependencies[] = ding_provider diff --git a/ding_reservation.make b/ding_reservation.make index d6c6336..6269553 100644 --- a/ding_reservation.make +++ b/ding_reservation.make @@ -3,27 +3,27 @@ core = 7.x ; Contrib -projects[date][subdir] = contrib -projects[date][version] = "2.6" +projects[date][subdir] = "contrib" +projects[date][version] = "2.8" ; Ding 2 modules projects[ding_base][type] = "module" projects[ding_base][download][type] = "git" projects[ding_base][download][url] = "git@github.com:ding2/ding_base.git" -projects[ding_base][download][tag] = "7.x-0.4" +projects[ding_base][download][branch] = "master" projects[ding_popup][type] = "module" projects[ding_popup][download][type] = "git" projects[ding_popup][download][url] = "git@github.com:ding2/ding_popup.git" -projects[ding_popup][download][tag] = "7.x-0.4" +projects[ding_popup][download][branch] = "master" projects[ding_user][type] = "module" projects[ding_user][download][type] = "git" projects[ding_user][download][url] = "git@github.com:ding2/ding_user.git" -projects[ding_user][download][tag] = "7.x-0.19" +projects[ding_user][download][branch] = "master" projects[ding_provider][type] = "module" projects[ding_provider][download][type] = "git" projects[ding_provider][download][url] = "git@github.com:ding2/ding_provider.git" -projects[ding_provider][download][tag] = "7.x-0.13" +projects[ding_provider][download][branch] = "master" diff --git a/ding_reservation.module b/ding_reservation.module index 93a6939..142920c 100644 --- a/ding_reservation.module +++ b/ding_reservation.module @@ -1,12 +1,15 @@ 'Update reservations', 'page callback' => 'drupal_get_form', - 'page arguments' => array('ding_reservation_update_reservations_form', 1, 5), + 'page arguments' => array('ding_reservation_update_reservations_form', 1, 5, 6), 'access callback' => 'ding_reservation_access', 'access arguments' => array(1), ); + $items['user/%user/status/reservations/delete/%'] = array( + 'title' => 'Delete reservations', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('ding_reservation_delete_reservations_form', 1, 5), + 'access callback' => 'ding_reservation_access', + 'access arguments' => array(1), + ); + + $items['ting/object/%ting_object/reserve'] = array( + 'page callback' => 'ding_reservation_reserve_ajax', + 'page arguments' => array(2), + 'delivery callback' => 'ajax_deliver', + 'access arguments' => array('perform reservation'), + ); + return $items; } @@ -46,6 +70,18 @@ function ding_reservation_access($account) { return $user->uid == $account->uid; } +/** + * Implements hook_permission(). + */ +function ding_reservation_permission() { + return array( + 'perform reservation' => array( + 'title' => t('Perform reservation'), + 'description' => t('Perform reservation in the library system.'), + ), + ); +} + /** * Implements hook_ding_entity_menu(). */ @@ -63,10 +99,121 @@ function ding_reservation_ding_entity_menu(&$items, $type, $path, $index) { /** * Implements hook_ding_entity_buttons(). */ -function ding_reservation_ding_entity_buttons($type, $entity) { +function ding_reservation_ding_entity_buttons($type, $entity, $widget = 'default') { + $button = ''; + if ($type == 'ding_entity' && $entity->is('reservable')) { - return array(ding_provider_get_form('ding_reservation_reserve_form', new DingReservationReservableEntity($entity), TRUE)); + switch ($widget) { + case 'ajax': + drupal_add_library('system', 'drupal.ajax'); + + $button = array( + array( + '#theme' => 'link', + '#text' => t('Reserve'), + '#path' => 'ting/object/' . $entity->id . '/reserve', + '#options' => array( + 'attributes' => array( + 'class' => array( + 'action-button', + 'reserve-button', + 'use-ajax', + ), + 'id' => 'reservation-' . $entity->id, + ), + 'html' => FALSE, + ), + ), + ); + break; + + default: + // The last parameter to the form below (TRUE) hides the provider + // options in the form (interest period and branch). + $button = array(ding_provider_get_form('ding_reservation_reserve_form', new DingReservationReservableEntity($entity), TRUE)); + break; + } + } + + return $button; +} + +/** + * Ajax entry callback. + * + * Try to reserve the material, if the user is not logged in trigger a ajax + * login. + * + * @param TingEntity $entity + * Ting entity object. + * @param DingReservationReservable $reservable + * Object with information about the entity to reserve. Used to make + * reservation of periodical, where volume and issue is part of the + * reservation. + * + * @return array + * Render array with Ajax commands. + */ +function ding_reservation_reserve_ajax($entity, $reservable = NULL) { + $commands = array(); + + // Check if the logged in user is a library user. + global $user; + if (!user_is_logged_in()) { + // Trigger log-in (the reservation link will be triggered on success). + $commands[] = ajax_command_ding_user_authenticate(''); } + elseif (!ding_user_is_provider_user($user)) { + // Error not library user. + $commands[] = ajax_command_ding_popup('ding_reservation', t('Error'), '

' . t('Only library user can make reservations.') . '

'); + } + elseif (!(is_object($entity) && $entity instanceof TingEntity)) { + // Error not ting entity. + $commands[] = ajax_command_ding_popup('ding_reservation', t('Error'), '

' . t('Unable to load information about the material.') . '

'); + } + else { + // Check if reservable object was paste. + if (is_null($reservable)) { + // If no object passed assume "normal" reservation (not periodical). + $reservable = new DingReservationReservableEntity($entity); + } + + // Try to make reservation. + try { + // Check if user have preferred branch and interest period, if so + // submit the reservation form. If not display another form for with + // the options to select branch and period. + $defaults = ding_provider_invoke('reservation', 'default_options', $user); + $matches = preg_grep("/preferred_branch$/", array_keys($defaults)); + if (empty($defaults[array_shift($matches)])) { + $form = ding_provider_get_form('ding_reservation_reserve_form', $reservable, FALSE); + $commands[] = ajax_command_ding_popup('ding_reservation', t('Reservation'), render($form)); + } + else { + $form_state = array('values' => array()); + drupal_form_submit('ding_reservation_reserve_form', $form_state, $reservable); + + // Return any status messages set by the form. + $commands[] = ajax_command_ding_popup('ding_reservation', t('Reservation'), theme('status_messages')); + } + } + catch (DingProviderAuthException $exception) { + // The form may have thrown an Auth exception, so display login. (the + // reservation link will be triggered on success). + $commands[] = ajax_command_ding_user_authenticate(''); + } + catch (Exception $exception) { + // The form may have thrown an auth exception as the login may have + // timed-out (the reservation link will be triggered on success). + $commands[] = ajax_command_ding_popup('ding_reservation', t('Error'), '

' . t('Unknown error in reservation, please contact the library.') . '

'); + + // Log exception. + watchdog_exception('ding_reservation', $exception); + } + } + + // Return the ajax commands as an render array. + return array('#type' => 'ajax', '#commands' => $commands); } /** @@ -90,13 +237,17 @@ function ding_reservation_ding_provider_user() { * function, use this to let have their own form id. */ function ding_reservation_forms($form_id, $args) { - $forms['ding_reservation_reservations_ready_form'] = array( - 'callback' => 'ding_reservation_reservations_form', - ); - $forms['ding_reservation_reservations_notready_form'] = array( - 'callback' => 'ding_reservation_reservations_form', + return array( + 'ding_reservation_reservations_ready_form' => array( + 'callback' => 'ding_reservation_reservations_form', + ), + 'ding_reservation_reservations_notready_form' => array( + 'callback' => 'ding_reservation_reservations_form', + ), + 'ding_reservation_reservations_ill' => array( + 'callback' => 'ding_reservation_reservations_form', + ), ); - return $forms; } /** @@ -130,20 +281,37 @@ function ding_reservation_reserve_form($form, &$form_state, $reservable, $hide_o '#value' => array(), ); + // Helps decide if the provider options should be displayed in the reserve + // form. If the user have default value these are used to make a quicker + // reservation process. $hide_options = !isset($form_state['options_hidden']) ? $hide_options : FALSE; $form_state['options_hidden'] = $hide_options; if (!$hide_options) { - if (ding_provider_implements('reservation', 'options') && $provider_form = ding_provider_invoke('reservation', 'options', 'create', $user, $reservable)) { + if (ding_provider_implements('reservation', 'options') && $provider_form = ding_provider_invoke('reservation', 'options', $user)) { $form['provider_options'] = $provider_form + array( '#tree' => TRUE, ); + + // The normal reserve button and the reserve for with provider options are + // the same form. But DDBasic hides the reserve buttons until availability + // have been confirmed. So we need to add a class to the form to make it + // visible. + $form['#attributes'] = array( + 'class' => array('reservable'), + ); } } $form['submit'] = array( '#type' => 'submit', '#value' => t('Reserve'), + '#attributes' => array( + 'class' => array( + 'action-button', + 'reserve-button', + ), + ), '#ajax' => array( 'callback' => 'ding_reservation_reserve_form_callback', 'wrapper' => 'ding-reservation-reserve-form', @@ -154,37 +322,45 @@ function ding_reservation_reserve_form($form, &$form_state, $reservable, $hide_o } /** - * Form validation. + * Reserve form validation. */ function ding_reservation_reserve_form_validate($form, &$form_state) { global $user; - if (ding_provider_implements('reservation', 'options_validate')) { - $res = ding_provider_invoke('reservation', 'options_validate', 'create', $user, $form_state['values']['reservable'], $form_state['values']['provider_options']); - /** - * We cannot set the value of the individual provider form elements, as - * they might not have been show, and thus not exist. However, setting the - * value of the parent element to an associative array gives the same end - * result. - */ - $provider_options = array(); - foreach ($res as $key => $value) { - if (is_array($value) && !empty($value['#error'])) { - if (!$form_state['options_hidden']) { - // Only show an error if the user had a choice. - form_error($form['provider_options'], $res['#error']); + if (user_is_logged_in() && ding_user_is_provider_user($user)) { + if (ding_provider_implements('reservation', 'default_options')) { + $defaults = ding_provider_invoke('reservation', 'default_options', $user); + + $provider_options = array(); + foreach ($defaults as $key => $default) { + // Check if the current provider options has a default value. + if (empty($default) && empty($form_state['values']['provider_options'][$key])) { + // Set form error to trigger display of the form in ding pop-up. The + // message will not be shown and the user will have to select values. + form_error($key, t('Please select a valid value.')); + $form_state['rebuild'] = TRUE; } else { - // Else simply rebuild the form. - $form_state['rebuild'] = TRUE; + if (empty($default) || !empty($form_state['values']['provider_options'][$key])) { + // If default value id not defined, try using the forms value. + $provider_options[$key] = $form_state['values']['provider_options'][$key]; + } + else { + // Default value was set, so we use it (as the forms selection + // options should not have been shown to the user). + $provider_options[$key] = $default; + } } } - else { - $provider_options[$key] = $value; + + // Do not set provider options, if the form was marked for rebuild. The + // user should have a change to select the values first. + if (!$form_state['rebuild']) { + form_set_value($form['provider_options'], $provider_options, $form_state); } } - if (!empty($provider_options)) { - form_set_value($form['provider_options'], $provider_options, $form_state); - } + } + else { + throw new DingProviderAuthException(); } } @@ -194,7 +370,7 @@ function ding_reservation_reserve_form_validate($form, &$form_state) { function ding_reservation_reserve_form_submit($form, &$form_state) { global $user; if (ding_provider_implements('reservation', 'options_submit')) { - ding_provider_invoke('reservation', 'options_submit', 'create', $user, $form_state['values']['reservable'], $form_state['values']['provider_options']); + ding_provider_invoke('reservation', 'options_submit', $user, $form_state['values']['provider_options']); } if ($form_state['values']['reservable']) { $reservable = $form_state['values']['reservable']; @@ -207,12 +383,15 @@ function ding_reservation_reserve_form_submit($form, &$form_state) { drupal_set_message(t('"@title" reserved and will be available for pickup at @branch.', array('@title' => $reservable->getTitle(), '@branch' => $branch_name))); } else { - drupal_set_message(t('"@title" reserved.', array('@title' => $reservable->getProviderId()))); + drupal_set_message(t('"@title" reserved.', array('@title' => $reservable->getTitle()))); } if (is_array($reservation_result) and !empty($reservation_result['queue_number'])) { drupal_set_message(t('You are number @number in queue.', array('@number' => $reservation_result['queue_number']))); } + + // Clear reservation session cache. + ding_reservation_cache_clear(); } catch (DingProviderUserException $e) { drupal_set_message($e->getMessageT(array('@title' => $reservable->getTitle())), 'error'); @@ -239,6 +418,22 @@ function ding_reservation_reserve_form_callback($form, &$form_state) { $html = theme('status_messages'); if ($form_state['rebuild'] || form_get_errors()) { + // Get the default interest period for the current user. + $default_interest_period = ding_provider_invoke('reservation', 'default_interest_period'); + + // Use #value instead of #default_value when rendering forms + // using drupal_render(). + $form['provider_options']['interest_period']['#value'] = $default_interest_period; + + // Hide certain fields, if any. + if (is_array($form_state['removable'])) { + $removal = $form_state['removable']; + foreach ($removal as $v) { + unset($form['provider_options'][$v]); + unset($form['provider_options'][$v . 'description']); + } + } + // Redisplay form. $html .= drupal_render($form); } @@ -251,173 +446,272 @@ function ding_reservation_reserve_form_callback($form, &$form_state) { /** * Show a reservation list form. */ -function ding_reservation_reservations_form($form, &$form_state, $items = array(), $type = 'not_ready_for_pickup') { - $options = array(); - $destination = drupal_get_destination(); - switch ($type) { - case 'ready_for_pickup': - $header = array( - 'title' => t('Title'), - 'created' => t('Created date'), - 'pickup_date' => t('Pickup date'), - 'pickup_branch' => t('Pickup branch'), - 'operations' => '', - ); - uasort($items, 'ding_reservation_sort_queue_by_pickup_date'); - break; - case 'not_ready_for_pickup': - $header = array( - 'title' => t('Title'), - 'created' => t('Created date'), - 'expiry' => t('Expiry date'), - 'pickup_branch' => t('Pickup branch'), - 'queue_number' => t('Queue number'), - 'operations' => '', - ); - uasort($items, 'ding_reservation_sort_queue_by_queue_number'); - break; - } - foreach ($items as $id => $item) { - $entity = $item->entity; - $pickup_branch = ding_provider_invoke('reservation', 'branch_name', $item->pickup_branch_id); - switch ($type) { - case 'ready_for_pickup': - $options[$item->id] = array( - 'title' => array( - 'data' => array($entity ? ting_object_view($entity, 'user_list') : array('#markup' => $item->display_name)), - 'class' => 'title', - ), - 'created' => array( - 'data' => $item->created ? format_date(strtotime(check_plain($item->created)), 'date_only') : '', - 'class' => 'created-date', - ), - 'pickup_date' => array( - 'data' => $item->pickup_date ? format_date(strtotime(check_plain($item->pickup_date)), 'date_only' ) : '', - 'class' => 'pickup-date', - ), - 'pickup_branch' => array( - 'data' => $pickup_branch ? check_plain($pickup_branch) : '', - 'class' => 'pickup-branch', - ), - 'operations' => array( - 'data' => array( - '#prefix' => '
', - '#suffix' => '
', - '#type' => 'submit', - '#submit' => array('ding_reservation_reservations_delete_submit'), - '#reservation_id' => $item->id, - '#name' => 'delete-' . preg_replace('/\W/', '-', $item->id), // Need this for formAPI can tell buttons apart - '#value' => t('Delete'), - '#options' => array('query' => $destination), +function ding_reservation_reservations_form($form, &$form_state, $reservations, $type = DING_RESERVATION_NOT_READY, $conf = array()) { + $form = array( + '#tree' => TRUE, + ); + + // Add title item. + $form['title'] = array( + '#type' => 'checkbox', + '#title' => check_plain(t($conf['reservation_title'])), + '#prefix' => '
', + '#suffix' => '
', + '#attached' => array( + 'js' => array( + drupal_get_path('module', 'ding_reservation') . '/js/ding_reservation.js', + ), + ), + '#weight' => -10, + ); + + // Check if reservations should be able to be deleted by the user. + $able_to_delete = FALSE; + if (ding_provider_implements('reservation', 'reservation_deletion_enabled')) { + $able_to_delete = ding_provider_invoke('reservation', 'reservation_deletion_enabled'); + } + + foreach ($reservations as $reservation) { + $entity = $reservation->entity; + if (!is_object($entity) || $entity->reply == FALSE) { + $entity = ding_provider_get_pseudo_entity($reservation->ding_entity_id); + // Don't link the title as the data well do not known this title. It may + // be a inter library loan. + $title = $entity->getTitle() ? $entity->getTitle() : $reservation->display_name; + } + else { + // Create title that links to the object. + $uri = entity_uri('ting_object', $entity); + $title = l($entity->getTitle(), $uri['path']); + } + + $pickup_branch = ding_provider_invoke('reservation', 'branch_name', $reservation->pickup_branch_id); + switch ($reservation->reservation_type) { + case DING_RESERVATION_READY: + $item = array( + '#type' => 'material_item', + '#id' => $reservation->id, + '#title' => $title, + '#cover' => field_view_field('ting_object', $entity, 'ting_cover', 'user_list'), + '#information' => array( + 'pickup_id' => array( + 'label' => t('Pickup id'), + 'data' => ding_reservation_get_pickup_id($reservation), + 'class' => 'pickup-id', + '#weight' => 0, + ), + 'pickup_date' => array( + 'label' => t('Pickup date'), + 'data' => $reservation->pickup_date ? format_date(strtotime(check_plain($reservation->pickup_date)), 'ding_material_lists_date') : '', + 'class' => 'pickup-date', + '#weight' => 4, + ), + 'pickup_branch' => array( + 'label' => t('Pickup branch'), + 'data' => $pickup_branch ? check_plain($pickup_branch) : t('Unknown branch'), + 'class' => 'pickup-branch', + '#weight' => 8, + ), + 'created' => array( + 'label' => t('Created date'), + 'data' => $reservation->created ? format_date(strtotime(check_plain($reservation->created)), 'ding_material_lists_date') : '', + 'class' => 'created-date', + '#weight' => 16, + ), ), - 'class' => 'operations', - ), - ); - if (isset($item->pickup_order_id)) { - $options[$item->id]['title']['data'][] = array( - '#type' => 'markup', - '#prefix' => '

', - '#markup' => t('(Pickup id: @pickup_id)', array('@pickup_id' => check_plain($item->pickup_order_id))), - '#suffix' => '

', ); - } - if (!isset($item->pickup_order_id) && isset($item->order_id)) { - $options[$item->id]['title']['data'][] = array( - '#type' => 'markup', - '#prefix' => '

', - '#markup' => t('(Order no. @order_id)', array('@order_id' => check_plain($item->order_id))), - '#suffix' => '

', + break; + + case DING_RESERVATION_NOT_READY: + $item = array( + '#type' => 'material_item', + '#id' => $reservation->id, + '#title' => $title, + '#cover' => field_view_field('ting_object', $entity, 'ting_cover', 'user_list'), + '#information' => array( + 'queue_number' => array( + 'label' => t('Queue number'), + 'data' => $reservation->queue_number ? check_plain($reservation->queue_number) : '', + 'class' => 'queue-number', + '#weight' => 0, + ), + 'expiry' => array( + 'label' => t('Expiry date'), + 'data' => $reservation->created ? format_date(strtotime(check_plain($reservation->expiry)), 'ding_material_lists_date') : '', + 'class' => 'expire-date', + '#weight' => 4, + ), + 'pickup_branch' => array( + 'label' => t('Pickup branch'), + 'data' => $pickup_branch ? check_plain($pickup_branch) : '', + 'class' => 'pickup-branch', + '#weight' => 8, + ), + 'created' => array( + 'label' => t('Created date'), + 'data' => $reservation->created ? format_date(strtotime(check_plain($reservation->created)), 'ding_material_lists_date') : '', + 'class' => 'created-date', + '#weight' => 16, + ), + 'order_nr' => array( + 'label' => t('Order nr.'), + 'data' => ding_reservation_get_order_nr($reservation), + 'class' => 'pickup-id', + '#weight' => 32, + ), + ), ); - } - $form['reservations'] = array( - '#type' => 'tableselect_form', - '#header' => $header, - '#options' => $options, - '#empty' => t('No reservations ready for pickup'), - ); - break; - case 'not_ready_for_pickup': - $options[$item->id] = array( - 'title' => array( - 'data' => array($entity ? ting_object_view($entity, 'user_list') : array('#markup' => $item->display_name)), - 'class' => 'title', - ), - 'created' => array( - 'data' => $item->created ? format_date(strtotime(check_plain($item->created)), 'date_only') : '', - 'class' => 'created-date', - ), - 'expiry' => array( - 'data' => $item->created ? format_date(strtotime(check_plain($item->expiry)), 'date_only') : '', - 'class' => 'expire-date', - ), - 'pickup_branch' => array( - 'data' => $pickup_branch ? check_plain($pickup_branch) : '', - 'class' => 'pickup-branch', - ), - 'queue_number' => array( - 'data' => $item->queue_number ? check_plain($item->queue_number) : '', - 'class' => 'queue-number', - ), - 'operations' => array( - 'data' => array( - '#prefix' => '
', - '#suffix' => '
', - '#type' => 'submit', - '#submit' => array('ding_reservation_reservations_delete_submit'), - '#reservation_id' => $item->id, - '#name' => 'delete-' . preg_replace('/\W/', '-', $item->id), // Need this for formAPI can tell buttons apart - '#value' => t('Delete'), - '#options' => array('query' => $destination), + break; + + case DING_RESERVATION_INTERLIBRARY_LOANS: + $item = array( + '#type' => 'material_item', + '#id' => $reservation->id, + '#title' => $title, + '#cover' => field_view_field('ting_object', $entity, 'ting_cover', 'user_list'), + '#information' => array( + 'ill_status' => array( + 'label' => t('Status'), + 'data' => $reservation->ill_status ? t(check_plain($reservation->ill_status)) : t('Unknown status'), + 'class' => 'ill-status', + '#weight' => 0, + ), + 'expiry' => array( + 'label' => t('Expiry date'), + 'data' => $reservation->created ? format_date(strtotime(check_plain($reservation->expiry)), 'ding_material_lists_date') : '', + 'class' => 'expire-date', + '#weight' => 4, + ), + 'pickup_branch' => array( + 'label' => t('Pickup branch'), + 'data' => $pickup_branch ? check_plain($pickup_branch) : '', + 'class' => 'pickup-branch', + '#weight' => 8, + ), + 'created' => array( + 'label' => t('Created date'), + 'data' => $reservation->created ? format_date(strtotime(check_plain($reservation->created)), 'ding_material_lists_date') : '', + 'class' => 'created-date', + '#weight' => 16, + ), + 'order_nr' => array( + 'label' => t('Order nr.'), + 'data' => ding_reservation_get_order_nr($reservation), + 'class' => 'pickup-id', + '#weight' => 32, + ), ), - 'class' => 'operations', - ), - ); + ); + break; + } - if (isset($item->order_id)) { - $options[$item->id]['title']['data'][] = array( - '#type' => 'markup', - '#prefix' => '

', - '#markup' => t('(Order no. @order_id)', array('@order_id' => check_plain($item->order_id))), - '#suffix' => '

', + if (in_array($type, array(DING_RESERVATION_NOT_READY, DING_RESERVATION_INTERLIBRARY_LOANS))) { + // Set reservation expire message. + $expire = strtotime(check_plain($reservation->expiry)); + if ($expire - variable_get('reservation_expire', 604800) <= time()) { + $item['#material_message'] = array( + 'message' => t('This reservation is about to expire.'), + 'class' => 'messages warning', ); + $item['#weight'] = -30; } + } - $form['reservations'] = array( - '#type' => 'tableselect_form', - '#header' => $header, - '#options' => $options, - '#empty' => t('No Reservations'), + // Add extra information if it's a periodical. There is an exception because + // the notes field is also used to set library information for ill's but + // only when they are ready for pick-up. + if (!empty($reservation->notes) && !($reservation->ill_status && $type == DING_RESERVATION_READY)) { + $item['#information']['periodical_number'] = array( + 'label' => t('Periodical no.'), + 'data' => check_plain($reservation->notes), + 'class' => 'periodical-number', + '#weight' => -4, ); - break; } + + // Add the reservation to the form. + $form['reservations'][$reservation->id] = $item; } - $form['submit'] = array( - '#prefix' => '
', + // Add action buttons to the top of the form. + $form['actions_top'] = array( + '#prefix' => '
', '#suffix' => '
', - '#submit' => array('ding_reservation_reservations_delete_submit'), - '#type' => 'submit', - '#value' => t('Delete reservations'), + '#weight' => -20, ); - if ($type == 'not_ready_for_pickup') { - $form['update'] = array( - '#prefix' => '
', + if ($able_to_delete) { + $form['actions_top']['delete'] = array( + '#prefix' => '
', '#suffix' => '
', + '#submit' => array('ding_reservation_deletes_form_submit'), '#type' => 'submit', - /* '#submit' => array('ding_reservation_update_reservation_form'), */ - '#submit' => array('ding_reservation_reservations_form_submit'), - '#value' => t('Update reservations'), + '#value' => t('Delete reservations (@count)', array('@count' => 0)), '#ajax' => array( - 'callback' => 'ding_reservation_reservations_form_callback', + 'callback' => 'ding_reservation_deletes_form_callback', 'wrapper' => 'ding-reservation-reservations-form', ), ); } + if ($type == DING_RESERVATION_NOT_READY) { + $form['actions_top']['update'] = array( + '#prefix' => '
', + '#suffix' => '
', + '#type' => 'submit', + '#submit' => array('ding_reservation_updates_form_submit'), + '#value' => t('Update reservations (@count)', array('@count' => 0)), + '#ajax' => array( + 'callback' => 'ding_reservation_updates_form_callback', + 'wrapper' => 'ding-reservation-updates-form', + ), + ); + } + return $form; } +/** + * Get pickup id number. + * + * @param object $item + * Reserved item object. + * + * @return int + * Pickup id, if any. + */ +function ding_reservation_get_pickup_id($item) { + if (isset($item->order_arrived) && !$item->order_arrived) { + return t('The material is in transit and is still not available for loan on the library'); + } + elseif (isset($item->pickup_order_id)) { + return $item->pickup_order_id; + } + + return ''; +} + +/** + * Get order id number. + * + * @param object $item + * Reserved item object. + * + * @return int + * Order number, if any. + */ +function ding_reservation_get_order_nr($item) { + if (!isset($item->pickup_order_id) && isset($item->order_id)) { + return $item->order_id; + } + else { + return t('On route') . ' ' . l(t('(?)'), current_path(), array('attributes' => array('title' => t('The material is on route to the library. You will be notified when it is ready for pickup.')))); + } +} + +/** + * Submit handler for the reservations form. + */ function ding_reservation_reservations_delete_submit($form, &$form_state) { global $user; if (!empty($form_state['triggering_element']['#reservation_id'])) { @@ -428,31 +722,108 @@ function ding_reservation_reservations_delete_submit($form, &$form_state) { } foreach ($reservations as $entity_id) { ding_provider_invoke('reservation', 'delete', $user, $entity_id); + + // Clear reservation session cache. + ding_reservation_cache_clear(); } } /** - * Submit handler for the form. + * Submit handler for the delete form. + * + * @see ding_reservation_reservations_form() */ -function ding_reservation_reservations_form_submit($form, &$form_state) { +function ding_reservation_deletes_form_submit($form, &$form_state) { global $user; - $ids = join(',', array_map('rawurlencode', array_filter($form_state['values']['reservations'],'is_string'))); + + // Extra checkbox values form the form. + $ids = array(); + foreach ($form_state['values']['reservations'] as $id => $reservations) { + $ids[] = $reservations[$id]; + } + $ids = implode(',', array_map('rawurlencode', array_filter($ids, 'is_string'))); + + // Save the encoded id's and redirect the form. $form_state['encoded_reservations'] = $ids; - $form_state['redirect'] = array('user/' . $user->uid . '/status/reservations/update/' . $ids, array('query' => drupal_get_destination())); + $form_state['redirect'] = array('user/' . $user->uid . '/status/reservations/delete/' . $ids, array('query' => drupal_get_destination())); } /** - * Ajax callback. + * Submit handler for the update form. + * + * @see ding_reservation_reservations_form() */ -function ding_reservation_reservations_form_callback($form, &$form_state) { +function ding_reservation_updates_form_submit($form, &$form_state) { + global $user; + + // Extra checkbox values form the form. + $ids = array(); + foreach ($form_state['values']['reservations'] as $id => $reservations) { + if ($reservations[$id]) { + $ids[] = $reservations[$id]; + } + } + + // Try to make a bette default branch selection based on the selected + // reservations. + $current_branch = $form['reservations']['#value'][$ids[0]]->pickup_branch_id; + foreach ($ids as $id) { + if ($form['reservations']['#value'][$id]->pickup_branch_id != $current_branch) { + $current_branch = FALSE; + break; + } + } + + // Encode the ids. + $ids = implode(',', array_map('rawurlencode', array_filter($ids, 'is_string'))); + + // Save the encoded id's and redirect the form. + $form_state['current_branch'] = $current_branch; + $form_state['encoded_reservations'] = $ids; + $form_state['redirect'] = array('user/' . $user->uid . '/status/reservations/update/' . $ids . '/' . $current_branch, array('query' => drupal_get_destination())); +} + +/** + * Ajax callback for the delete form. + * + * @see ding_reservation_reservations_form() + */ +function ding_reservation_deletes_form_callback($form, &$form_state) { global $user; $response = array( '#type' => 'ajax', '#commands' => array(), ); + // Get delete form. + $form_raw = drupal_get_form('ding_reservation_delete_reservations_form', $user, $form_state['encoded_reservations']); + $html = theme('status_messages'); - $html .= drupal_render(drupal_get_form('ding_reservation_update_reservations_form', $user, $form_state['encoded_reservations'])); + $html .= drupal_render($form_raw); + + if ($html) { + $response['#commands'][] = ajax_command_ding_popup('ding_reservation', t('Delete reservations'), $html, array('refresh' => TRUE)); + } + return $response; +} + +/** + * Ajax callback for the update form. + * + * @see ding_reservation_reservations_form() + */ +function ding_reservation_updates_form_callback($form, &$form_state) { + global $user; + $response = array( + '#type' => 'ajax', + '#commands' => array(), + ); + + // Get reservation form. + $form_raw = drupal_get_form('ding_reservation_update_reservations_form', $user, $form_state['encoded_reservations'], $form_state['current_branch']); + + $html = theme('status_messages'); + $html .= drupal_render($form_raw); if ($html) { $response['#commands'][] = ajax_command_ding_popup('ding_reservation', t('Update reservations'), $html, array('refresh' => TRUE)); @@ -461,9 +832,11 @@ function ding_reservation_reservations_form_callback($form, &$form_state) { } /** - * Update reservations form. + * Delete reservations form. + * + * @see ding_reservation_reservations_form() */ -function ding_reservation_update_reservations_form($form, $form_state, $account, $reservation_ids) { +function ding_reservation_delete_reservations_form($form, $form_state, $account, $reservation_ids) { global $user; $ids = array_map('rawurldecode', explode(',', $reservation_ids)); @@ -474,7 +847,81 @@ function ding_reservation_update_reservations_form($form, $form_state, $account, '#value' => $ids, ); - if (ding_provider_implements('reservation', 'options') && $provider_form = ding_provider_invoke('reservation', 'options', 'update', $user, NULL)) { + $form['confirm_text'] = array( + '#markup' => '
' . t('Are you sure you want to delete these reservations?') . '
', + ); + + $form['submit'] = array( + '#type' => 'submit', + '#submit' => array('ding_reservation_delete_reservations_form_submit'), + '#ajax' => array( + 'callback' => 'ding_reservation_delete_reservations_form_callback', + 'wrapper' => 'ding-reservation-delete-reservation-form', + ), + '#value' => t('Yes'), + '#name' => 'delete_reservations', + ); + + $form['cancel'] = array( + '#type' => 'link', + '#title' => t('Cancel'), + '#href' => 'user/' . $user->uid . '/status/reservations', + '#value' => t('Cancel'), + ); + + return $form; +} + +/** + * Delete reservation submit normal handler. + * + * @see ding_reservation_delete_reservations_form_callback() + */ +function ding_reservation_delete_reservations_form_submit($form, &$form_state) { + global $user; + if (!empty($form_state['triggering_element']['#reservation_id'])) { + $reservations = array($form_state['triggering_element']['#reservation_id']); + } + else { + $reservations = array_filter($form_state['values']['reservations']); + } + foreach ($reservations as $reservation_id) { + ding_provider_invoke('reservation', 'delete', $user, $reservation_id); + + // Clear reservation session cache. + ding_reservation_cache_clear(); + } +} + +/** + * Delete reservation ajax callback. + */ +function ding_reservation_delete_reservations_form_callback($form, &$form_state) { + $response = array( + '#type' => 'ajax', + '#commands' => array(), + ); + + $html = theme('status_messages') . t('Your reservations have been deleted.'); + $response['#commands'][] = ajax_command_ding_popup('ding_reservation', t('Delete reservations'), $html, array('refresh' => TRUE)); + + return $response; +} + +/** + * Update reservations form. + */ +function ding_reservation_update_reservations_form($form, $form_state, $account, $reservation_ids, $current_branch) { + // Decode the reservation ids. + $ids = array_map('rawurldecode', explode(',', $reservation_ids)); + + $form_state['cache'] = TRUE; + $form['reservations'] = array( + '#type' => 'value', + '#value' => $ids, + ); + + if (ding_provider_implements('reservation', 'options') && $provider_form = ding_provider_invoke('reservation', 'options', $account, $current_branch)) { $form['provider_options'] = $provider_form + array( '#tree' => TRUE, ); @@ -489,56 +936,25 @@ function ding_reservation_update_reservations_form($form, $form_state, $account, ), '#value' => t('Update reservations'), ); - return $form; -} -/** - * Validation handler. - */ -function ding_reservation_update_reservations_form_validate($form, &$form_state) { - global $user; - if (ding_provider_implements('reservation', 'options_validate')) { - $res = ding_provider_invoke('reservation', 'options_validate', 'create', $user, $form_state['values']['reservations'], $form_state['values']['provider_options']); - /** - * We cannot set the value of the individual provider form elements, as - * they might not have been show, and thus not exist. However, setting the - * value of the parent element to an associative array gives the same end - * result. - */ - $provider_options = array(); - foreach ($res as $key => $value) { - if (is_array($value) && !empty($value['#error'])) { - if (!$form_state['options_hidden']) { - // Only show an error if the user had a choice. - form_error($form['provider_options'], $res['#error']); - } - else { - // Else simply rebuild the form. - $form_state['rebuild'] = TRUE; - } - } - else { - $provider_options[$key] = $value; - } - } - if (!empty($provider_options)) { - form_set_value($form['provider_options'], $provider_options, $form_state); - } - } + return $form; } /** - * Submit handler. + * Update reservation normal submit handler. * - * Updates selected reservations. + * @see ding_reservation_update_reservations_form_callback() */ function ding_reservation_update_reservations_form_submit($form, &$form_state) { global $user; ding_provider_invoke('reservation', 'update', $user, $form_state['values']['reservations'], $form_state['values']['provider_options']); + + // Clear reservation session cache. + ding_reservation_cache_clear(); } /** - * Ajax callback function. + * Update reservations ajax callback. */ function ding_reservation_update_reservations_form_callback($form, &$form_state) { $response = array( @@ -552,143 +968,100 @@ function ding_reservation_update_reservations_form_callback($form, &$form_state) return $response; } -/** - * Default options handling. - * - * Reservation providers may use these to use standard implementations of - * widgets like pickup branch and get much logic for free. - */ - /** * Create a pickup branch selector. * * Returns form element(s) for selecting a pickup branch. + * + * @param string $name + * The name of the form element to build. + * @param string $default + * The default branch option name eg. hb => Hovedbiblioteket. + * @param array $options + * The branches that should be available for selection in the form element. + * + * @return array + * Form element with a selection input type to select pickup branch. */ -function ding_reservation_default_options_branch($type, $name, $default, $options) { - $create = ($type == 'create'); - $allowed_branches = ($create ? array() : array('' => t('No change'))) + $options; - $default_value = $create ? $default : ''; +function ding_reservation_default_options_branch($name, $default, $options) { + $form = array(); + $form[$name] = array( '#type' => 'select', '#title' => t('Select branch'), - '#options' => $allowed_branches, - '#default_value' => $default_value, + '#options' => $options, + '#required' => TRUE, + '#default_value' => !empty($default) ? $default : '', ); - if ($create) { + + if (empty($default)) { $form[$name . 'description'] = array( '#markup' => '

' . t('In order to make quick reservations, you must select a default pickup branch.') . '

', ); } + return $form; } - /** - * Create an interest period selector. + * Create an interest period form select element. * - * Returns form element(s) for selecting an interest period. + * @param string $name + * The name of the form element to build. + * @param int $default + * The pre-selected value. + * @param array $options + * The periods that should be available for selection in the form element. + * + * @return array + * Form element with a selection input type to select interest period. */ -function ding_reservation_interest_period_selector($type, $name, $default, $options) { - $create = ($type == 'create'); - $allowed_periods = ($create ? array() : array('' => t('No change'))) + $options; - // $default_value = $create ? $default : ''; +function ding_reservation_interest_period_selector($name, $default, $options) { + $form = array(); + + // Used to enable translation of options. + if (is_array($options)) { + foreach ($options as $k => $v) { + $options[$k] = t(check_plain($v)); + } + } + $form[$name] = array( '#type' => 'select', '#title' => t('Select interest period'), - '#options' => $allowed_periods, - '#default_value' => $default, + '#options' => $options, + '#required' => TRUE, + '#default_value' => !empty($default) ? $default : '', ); - if ($create) { + + if (empty($default)) { $form[$name . 'description'] = array( '#markup' => '

' . t('Select an interest period.') . '

', ); } + return $form; } - /** - * Validate pickup branch selector. - * - * Returns what ding_reservation expects. + * Clears the reservations cache if ding_session_cache is active. */ -function ding_reservation_default_options_branch_validate($type, $name, $default, $values) { - if (empty($values[$name])) { - if ($type == 'create' && empty($default)) { - $result['openruth_preferred_branch'] = array( - '#error' => 'You must select a branch', - ); - } - else { - $result[$name] = $default; - } - return $result; - } else { - return $values; +function ding_reservation_cache_clear() { + if (module_exists('ding_session_cache')) { + ding_session_cache_clear('ding_reservation', 'reservations'); } } /** - * Submit pickup branch selector. + * Implements hook_ding_session_cache_defaults(). * - * Returns new properties to save, if any. - */ -function ding_reservation_default_options_branch_submit($type, $name, $default, $values) { - $result = array(); - if ($type == 'create' && !empty($values[$name]) && $values[$name] != $default) { - $result[$name] = $values['name']; - } - return $result; -} - - -/** - * Callback function to sort array by pickup date - */ -function ding_reservation_sort_queue_by_pickup_date($a, $b) { - if ($a->pickup_date == $b->pickup_date) { - return 0; - } - return ($a->pickup_date < $b->pickup_date) ? -1 : 1; -} - -/** - * Callback function for sorting loans by queue_number + * Set default ding_session_cache settings and tell ding_session_cache that this + * module supports it. */ -function ding_reservation_sort_queue_by_queue_number($a, $b) { - if ($a->queue_number == $b->queue_number) { - return 0; - } - return ($a->queue_number < $b->queue_number) ? -1 : 1; -} - -/** - * Interface for reservable items. - */ -interface DingReservationReservable { - public function getProviderId(); - // @todo, this should be optional. - public function getEntity(); - public function getTitle(); -} - -/** - * A reservable entity. - */ -class DingReservationReservableEntity implements DingReservationReservable { - public function __construct($entity) { - $this->entity = $entity; - } - - public function getProviderId() { - return $this->entity->provider_id; - } - - public function getEntity() { - return $this->entity; - } - public function getTitle() { - return $this->entity->title; - } +function ding_reservation_ding_session_cache_defaults() { + return array( + 'titel' => 'Ding reservation', + 'enabled' => TRUE, + 'expire' => 3600, + ); } - diff --git a/ding_reservation.test b/ding_reservation.test index d17df23..a5787cb 100644 --- a/ding_reservation.test +++ b/ding_reservation.test @@ -1,4 +1,8 @@ entity = $entity; + } + + public function getProviderId() { + return isset($this->entity->provider_id) ? $this->entity->provider_id : NULL; + } + + public function getEntity() { + return $this->entity; + } + + public function getTitle() { + return isset($this->entity->record['dc:title'][''][0]) ? $this->entity->record['dc:title'][''][0] : ''; + } +} \ No newline at end of file diff --git a/js/ding_reservation.js b/js/ding_reservation.js new file mode 100644 index 0000000..52ffa53 --- /dev/null +++ b/js/ding_reservation.js @@ -0,0 +1,89 @@ +/** + * Handle reservation checkboxes select all and select count on buttons. + */ +(function ($) { + "use strict"; + $(document).ready(function($) { + // Variables used to make the buttons follow scroll. + var actions = $(".action-buttons"); + var actions_offset = 0; + var win = $(window); + + // Ensure that all checkboxes are not checked (if user reloads the page + // etc.). + $('form input[type=checkbox]').prop('checked', false); + $('.action-buttons input[type=submit]').prop('disabled', 'disabled'); + + // Handle select all checkboxes. + $('.select-all input[type=checkbox]').click(function() { + var checkboxes = $('input[type=checkbox]', $(this).closest('form')); + if ($(this).prop('checked')) { + checkboxes.prop('checked', true); + } + else { + checkboxes.prop('checked', false); + } + checkboxes.change(); + }); + + // Handle checkbox button count. + $('.material-item input[type=checkbox]').change(function() { + var form = $(this).closest('form'); + var buttons = $('input[type=submit]', form); + var count = $('.material-item input[type=checkbox]:checked', form).length; + update_buttons(buttons, count); + + // Handle all checkbox checked state, so if not are all selected the + // checkbox is not checked. + if ($('.material-item input[type=checkbox]', form).length != count) { + $('.select-all input[type=checkbox]:not(:disabled)', form).prop('checked', false); + } + else { + $('.select-all input[type=checkbox]:not(:disabled)', form).prop('checked', true); + } + }); + + /** + * Update count string on the buttons. + */ + function update_buttons(buttons, count) { + buttons.each(function(index) { + var btn = $(buttons[index]); + btn.val(btn.val().replace(/\(\d+\)/, '(' + count + ')')); + + // Toggle buttons based on count. + if (count > 0) { + if (!actions_offset) { + // First time buttons are shown, get their offset value. + actions_offset = actions.offset().top; + } + btn.removeAttr("disabled"); + } + else { + btn.prop('disabled', 'disabled'); + } + }); + + toggle_scroll_buttons(); + } + + // Enable scroll and toggle of buttons. It uses class to this effect can be + // cancelled by removing classes from the theme. + $(window).scroll(function(){ + toggle_scroll_buttons(); + }); + + /** + * Helper function to toggle the "action-buttons-is-scrolling" class, which + * moves the out of flow to follow the top of the screen on scroll. + */ + function toggle_scroll_buttons() { + if (actions_offset < win.scrollTop()) { + actions.addClass('action-buttons-is-scrolling'); + } + else { + actions.removeClass('action-buttons-is-scrolling'); + } + } + }); +})(jQuery); diff --git a/plugins/content_types/reservations/reservations.inc b/plugins/content_types/reservations/reservations.inc index 34eecdf..f8f67ae 100644 --- a/plugins/content_types/reservations/reservations.inc +++ b/plugins/content_types/reservations/reservations.inc @@ -1,9 +1,10 @@ t('User reservation list'), 'description' => t('The current reservations for a user'), @@ -14,73 +15,145 @@ $plugin = array( ); /** - * Render the block + * Render the block. */ function ding_reservation_reservations_content_type_render($subtype, $conf, $panel_args, $context) { - static $preload_reservations = TRUE; - $account = isset($context->data) ? $context->data : NULL; - $preload_ids = array(); - $items = array(); - $reservation_list = array(); + $account = isset($context->data) ? $context->data : NULL; - $block = new stdClass(); + // Defined the return block. + $block = new stdClass(); + $block->title = t('My reservations'); $block->module = 'ding_reservation'; $block->delta = 'reservations'; - switch ($conf['reservation_type_list']) { - case 'ready_for_pickup': - $block->title = t('Reservations ready for pickup'); - $no_reservation_text = t('There are no reservations ready for pickup.'); - $form_id = 'ding_reservation_reservations_ready_form'; - break; - default: - case 'not_ready_for_pickup': - $block->title = t('Reservations'); - $no_reservation_text = t('There are no reservations.'); - $form_id = 'ding_reservation_reservations_notready_form'; - break; + // Get configuration for this pane and set empty text. + $types = array_filter($conf['reservation_type_list']); + $block->content = t($conf['reservation_empty_text']); + $form_id = $conf['reservation_form_id']; + + $content = array(); + if (module_exists('ding_session_cache')) { + $content = ding_session_cache_get('ding_reservation', 'reservations'); } - $list = ding_provider_invoke_page('reservation', 'list', $account); + if (empty($content) || !isset($content[$form_id])) { + // Get the reservations lists. + $reservations = ding_provider_invoke_page('reservation', 'list', $account); - foreach ($list as $item) { - if (isset($item->ding_entity_id)) { - $preload_ids[] = $item->ding_entity_id; - } - if (isset($conf['reservation_type_list'])) { - if ($conf['reservation_type_list'] == 'ready_for_pickup' && $item->ready_for_pickup == TRUE || $conf['reservation_type_list'] == 'not_ready_for_pickup' && $item->ready_for_pickup == FALSE) { - $reservation_list[] = $item; + // Filter items base on configuration and pre-load ting entities. + $preloaded_reservations = &drupal_static(__FUNCTION__, array()); + $items = array(); + foreach ($types as $type) { + // Try to pre-load ting entities, if not already loaded. + if (!isset($preloaded_reservations[$type])) { + $preload_ids = array(); + foreach ($reservations[$type] as $item) { + if (isset($item->ding_entity_id)) { + $preload_ids[] = $item->ding_entity_id; + } + } + + // Pre-load all ting entities. + if (!empty($preload_ids)) { + ding_entity_load_multiple($preload_ids); + } + + // Set this list type as preloaded. + $preloaded_reservations[$type] = TRUE; } + + // Sort the reservations based on type. + $sorted = array(); + switch ($type) { + case DING_RESERVATION_READY: + $sorted = $reservations[$type]; + uasort($sorted, 'ding_reservation_sort_queue_by_pickup_date'); + break; + + case DING_RESERVATION_NOT_READY: + $sorted = $reservations[$type]; + uasort($sorted, 'ding_reservation_sort_queue_by_queue_number'); + break; + + case DING_RESERVATION_INTERLIBRARY_LOANS: + $sorted = $reservations[$type]; + break; + } + + // Add the current type to the items we want. + $items += $sorted; + } + + // Add the pre-load entity to each loan (side effect of getting the entity). + foreach ($items as $id => &$item) { + $item->entity; + } + + $content[$form_id] = $items; + + if (module_exists('ding_session_cache')) { + ding_session_cache_set('ding_reservation', 'reservations', $content); } } - // Preload all ting objects. - if ($preload_reservations) { - ding_entity_load_multiple($preload_ids); - $preload_reservations = FALSE; + + // Build block content. + if (!empty($content[$form_id])) { + if (count($types) > 1) { + // If more than one type is selected we will fallback to the most generic + // form type. + $block->content = ding_provider_get_form($form_id, $content[$form_id], DING_RESERVATION_NOT_READY, $conf); + } + else { + $block->content = ding_provider_get_form($form_id, $content[$form_id], array_shift($types), $conf); + } } - $block->content = count($reservation_list) == TRUE ? ding_provider_get_form($form_id, $reservation_list, $conf['reservation_type_list']) : $no_reservation_text; + return $block; } /** - * Adding the admin form, to be able to control the required context + * Adding the admin form, to be able to control the required context. */ function ding_reservation_reservations_content_type_edit_form($form, &$form_state) { $conf = $form_state['conf']; $form['reservation_type_list'] = array( - '#type' => 'radios', - '#title' => t('Reservation list type'), + '#type' => 'checkboxes', + '#title' => t('Reservation list type(s)'), '#options' => array( - 'not_ready_for_pickup' => t('Not ready for pickup'), - 'ready_for_pickup' => t('Ready for pickup'), + DING_RESERVATION_NOT_READY => t('Not ready for pickup'), + DING_RESERVATION_READY => t('Ready for pickup'), + DING_RESERVATION_INTERLIBRARY_LOANS => t('Interlibrary loans'), ), '#default_value' => !empty($conf['reservation_type_list']) ? $conf['reservation_type_list'] : NULL, ); + + $form['reservation_title'] = array( + '#type' => 'textfield', + '#title' => t('List title'), + '#default_value' => !empty($conf['reservation_title']) ? $conf['reservation_title'] : 'Reservations', + ); + + $form['reservation_empty_text'] = array( + '#type' => 'textfield', + '#title' => t('Empty text'), + '#default_value' => !empty($conf['reservation_empty_text']) ? $conf['reservation_empty_text'] : 'There are no reservations.', + ); + + $form['reservation_form_id'] = array( + '#type' => 'select', + '#title' => t('Form to user for reservations'), + '#options' => array( + 'ding_reservation_reservations_notready_form' => t('Reservation form'), + 'ding_reservation_reservations_ready_form' => t('Reservations ready form'), + 'ding_reservation_reservations_ill' => t('Interlibrary reservation form'), + ), + '#default_value' => !empty($conf['reservation_form_id']) ? $conf['reservation_form_id'] : 'ding_reservation_reservations_notready_form', + ); return $form; } /** - * Submit handler for the admin form + * Submit handler for the admin form. */ function ding_reservation_reservations_content_type_edit_form_submit(&$form, &$form_state) { foreach (element_children($form) as $key) { @@ -89,3 +162,31 @@ function ding_reservation_reservations_content_type_edit_form_submit(&$form, &$f } } } + +/** + * Returns the administrative title. + */ +function ding_reservation_reservations_content_type_admin_title($subtype, $conf) { + $types = implode(', ', array_filter($conf['reservation_type_list'])); + return 'Reservations (' . $types . ')'; +} + +/** + * Callback function to sort array by pickup date. + */ +function ding_reservation_sort_queue_by_pickup_date($a, $b) { + if ($a->pickup_date == $b->pickup_date) { + return 0; + } + return ($a->pickup_date < $b->pickup_date) ? -1 : 1; +} + +/** + * Callback function for sorting loans by queue_number. + */ +function ding_reservation_sort_queue_by_queue_number($a, $b) { + if ($a->queue_number == $b->queue_number) { + return 0; + } + return ($a->queue_number < $b->queue_number) ? -1 : 1; +}