You are here

merci.module in MERCI (Manage Equipment Reservations, Checkout and Inventory) 6.2

Same filename and directory in other branches
  1. 8.2 merci.module
  2. 6 merci.module
  3. 7.2 merci.module

MERCI - Managed Equipment Reservation Checkout and Inventory

File

merci.module
View source
<?php

/**
 * @file
 * MERCI - Managed Equipment Reservation Checkout and Inventory
 */

// Item default availability options.
define('MERCI_AVA_F', 1);
define('MERCI_UNA_F', 2);
define('MERCI_AVA_T', 3);
define('MERCI_UNA_S', 4);

// Reservation status options.
define('MERCI_STATUS_UNCONFIRMED', 1);
define('MERCI_STATUS_PENDING', 2);
define('MERCI_STATUS_CHECKED_OUT', 3);
define('MERCI_STATUS_CHECKED_IN', 4);
define('MERCI_STATUS_CANCELLED', 5);
define('MERCI_STATUS_DENIED', 6);
define('MERCI_STATUS_NO_SHOW', 7);

// Bucket/resource status options.
define('MERCI_STATUS_ACTIVE', 1);
define('MERCI_STATUS_INACTIVE', 2);

// Bucket/resource sub-types.
define('MERCI_SUB_TYPE_ITEM', 1);
define('MERCI_SUB_TYPE_RESERVATION', 2);

// Item status for reservations.
define('MERCI_ITEM_STATUS_CANCELED', -2);
define('MERCI_ITEM_STATUS_CHECKED_IN', -1);
define('MERCI_ITEM_STATUS_AVAILABLE', 0);
define('MERCI_ITEM_STATUS_RESERVED', 1);
define('MERCI_ITEM_STATUS_CHECKED_OUT', 2);
module_load_include('inc', 'merci', 'includes/database');
module_load_include('inc', 'merci', 'includes/api');

/**
 * Implementation of hook_perm().
 */
function merci_perm() {
  return array(
    'create reservations',
    'create confirmed reservations',
    'suspend MERCI access',
    'administer MERCI',
    'create reservations outside hours of operation',
    'manage reservations',
    'override max hours over closed days',
    'view all reservations',
  );
}

/**
 * Implementation of hook_menu().
 */
function merci_menu() {

  // Callback for AJAX adding of item selectors.
  $items['merci/js'] = array(
    'title' => 'Javascript Choice Form',
    'page callback' => 'merci_choice_js',
    'access arguments' => array(
      'access content',
    ),
    'file' => 'includes/menu.inc',
    'type' => MENU_CALLBACK,
  );
  $items['merci/taxonomy/%node/%/%'] = array(
    'title' => 'JSON interface for node taxonomy',
    'description' => 'Takes a node ID and returns taxonomy data as JSON',
    'page callback' => 'merci_taxonomy_json',
    'access arguments' => array(
      'manage reservations',
    ),
    'page arguments' => array(
      2,
      3,
      4,
    ),
    'file' => 'includes/menu.inc',
    'type' => MENU_CALLBACK,
  );

  // Adds Manage Equipment to Admin Interfaces
  //
  $items['admin/merci/manage'] = array(
    'title' => 'Manage Equipment',
    'description' => 'Manage Equipment Reservations, Checkout and Inventory (MERCI)',
    'position' => 'right',
    'page callback' => 'system_admin_menu_block_page',
    'access arguments' => array(
      'manage reservations',
    ),
    'file' => 'system.admin.inc',
    'file path' => drupal_get_path('module', 'system'),
    'weight' => -19,
  );
  $items['admin/merci/manage/current_inventory'] = array(
    'title' => 'Current Inventory',
    'description' => 'Displays list',
    'page callback' => 'theme',
    'page arguments' => array(
      'merci_current_inventory',
    ),
    'access arguments' => array(
      'manage reservations',
    ),
    'type' => MENU_NORMAL_ITEM,
  );
  $items['admin/merci/manage/current_inventory/%'] = array(
    'title' => 'Current Inventory',
    'description' => 'Displays list',
    'type' => MENU_CALLBACK,
  );

  // Standard Administration settings.
  $items['admin/settings/merci'] = array(
    'title' => 'MERCI Configuration',
    'page callback' => 'drupal_get_form',
    'page arguments' => array(
      'merci_admin_settings',
    ),
    'access callback' => 'user_access',
    'access arguments' => array(
      'administer MERCI',
    ),
    'description' => 'Configure system settings for MERCI.',
    'file' => 'includes/merci.admin.inc',
  );
  $items['admin/settings/merci/edit'] = array(
    'title' => 'Edit',
    'type' => MENU_DEFAULT_LOCAL_TASK,
    'weight' => -10,
  );
  return $items;
}

/**
 * Implementation of hook_node_info().
 */
function merci_node_info() {
  return array(
    // Reservation nodes.
    'merci_reservation' => array(
      'name' => t('Reservation'),
      'module' => 'merci',
      'has_body' => FALSE,
      'description' => t("A reservation reserves a resource or group of resources for some period of time."),
    ),
  );
}

/**
 * Implementation of hook_node_type().
 */
function merci_node_type($op, $info) {
  switch ($op) {
    case 'update':
      if (isset($info->old_type) && $info->type != $info->old_type) {
        merci_node_type_update($info);
      }
      break;
    case 'delete':
      merci_node_type_delete($info);
      break;
  }
}

/**
 * Implementation of hook_access().
 */
function merci_access($op, $node, $account) {
  global $user;
  $type = isset($node->type) ? $node->type : $node;
  $uid = isset($node->uid) ? $node->uid : FALSE;
  if ($type == 'merci_reservation') {
    if (user_access('manage reservations')) {
      return TRUE;
    }
    elseif (user_access('view all reservations') && $op == 'view') {
      return TRUE;
    }
    elseif (user_access('create reservations') and !user_access('suspend MERCI access')) {

      //users working with their own reservations access reservation

      //additional check in merci_form permission to edit confirmed reservations

      // Users without administer or manage reservations permission can only alter their own Unconfirmed Reservations.
      if (($op == 'delete' or $op == 'update') && isset($node->merci_reservation_status) && $node->merci_reservation_status != MERCI_STATUS_UNCONFIRMED && !user_access('create confirmed reservations')) {
        return FALSE;
      }
      if ($uid === FALSE || $uid == $account->uid) {
        return TRUE;
      }
    }
    return FALSE;
  }
}

/**
 * Implementation of hook_init().
 */
function merci_init() {
  drupal_add_css(drupal_get_path('module', 'merci') . '/merci.css');
}

/**
 * Implementation of hook_prepare().
 */
function merci_prepare(&$node) {
  if (!isset($node->merci_reservation_status)) {
    $node->merci_reservation_status = variable_get('merci_default_reservation_status', strval(MERCI_STATUS_UNCONFIRMED));
  }
}

/**
 * Implementation of hook_validate().
 */
function merci_node_validate($form, &$form_state) {
  if (!empty($form_state['ahah_submission'])) {
    return;
  }

  // No validation necessary on deletion.
  if ($form_state['clicked_button']['#id'] == 'edit-delete') {
    return;
  }

  // Do no validation if their errors from the main validation function.
  if (form_get_errors()) {
    return;
  }
  merci_validate_status($form, $form_state);
  merci_validate_merci_reservation_date($form, $form_state);

  // Tests for existing items.
  merci_validate_empty_reservation_items($form, $form_state);
  merci_validate_merci_selected_items($form, $form_state);
}

/**
 * Implementation of hook_insert().
 */
function merci_insert($node) {
  drupal_write_record("merci_reservation", $node);
  merci_add_reservation_items($node);
}

/**
 * Implementation of hook_update().
 */
function merci_update($node) {
  if (!empty($node->revision)) {
    drupal_write_record("merci_reservation", $node);
  }
  else {
    drupal_write_record("merci_reservation", $node, "vid");
  }
  merci_add_reservation_items($node);
}

/**
 * Implementation of hook_delete().
 */
function merci_delete($node) {
  foreach ($node->merci_reservation_items as $item) {

    // Set the checked out item back to checked in.
    if ($node->merci_reservation_status == MERCI_STATUS_CHECKED_OUT) {
      $update = array(
        'nid' => $item['merci_item_nid'],
        'merci_item_status' => MERCI_ITEM_STATUS_AVAILABLE,
      );
      drupal_write_record('merci_reservation_item_node', $update, 'nid');
    }

    // Remove the placeholder node.
    node_delete($item['merci_placeholder_nid']);
  }
  merci_delete_record('merci_reservation', $node, 'nid');
  merci_delete_record('merci_reservation_detail', $node, 'nid');
}

/**
 * Implementation of hook_view().
 */
function merci_view($node, $teaser = FALSE, $page = FALSE) {

  // TODO: should we fix node previews?
  if (!isset($node->preview)) {
    $node->content['merci_reservation_status'] = array(
      '#value' => drupal_get_form('merci_display_reservation_status', merci_record_status($node->merci_reservation_status)),
      '#weight' => 0,
    );
    if ($page) {
      $reservation_table = drupal_get_form('merci_build_reservation_table_form', $node);
      $node = node_prepare($node, $teaser);
      $node->content['merci_reservation_items'] = array(
        '#value' => $reservation_table,
        '#weight' => 1,
      );
    }
    return $node;
  }
}

/**
 * Implementation of hook_form().
 */
function merci_form(&$node, $form_state) {
  $form = node_content_form($node, $form_state);
  if (isset($form_state['node'])) {
    $node = $form_state['node'] + (array) $node;
  }
  $node = (object) $node;

  // Add a wrapper for the choices and more button.
  $form['choice_wrapper'] = array(
    '#tree' => FALSE,
    '#prefix' => '<div class="clear-block" id="merci-choice-wrapper">',
    '#suffix' => '</div>',
  );

  // Build existing reserved items table on existing reservations.

  //if (isset($node->nid)) {
  $form['choice_wrapper']['merci_reservation_items'] = merci_build_reservation_table_form($form_state, $node, TRUE);

  //}

  // Choice adding code mostly stolen from poll module.
  $choice_count = empty($node->choice_count) ? 3 : $node->choice_count;
  if (array_key_exists('op', $form_state['post']) and $form_state['post']['op'] == "Add more items") {
    $choice_count += 3;
  }
  $form['choice_wrapper']['choice_count'] = array(
    '#type' => 'value',
    '#value' => $choice_count,
  );

  // Add the current choices to the form.
  for ($delta = 1; $delta <= $choice_count; $delta++) {
    $default = isset($node->merci_reservation_items["choice_" . $delta]['merci_item_nid']) ? $node->merci_reservation_items["choice_" . $delta]['merci_item_nid'] : '';
    $form['choice_wrapper']['merci_reservation_items']["choice_" . $delta]['merci_item_nid'] = _merci_choice_form($node, $form_state, $delta, $default);
  }
  $options = array();
  for ($i = 1; $i < 20; $i++) {
    $options[$i] = $i;
  }

  // We name our button 'merci_more' to avoid conflicts with other modules using
  // AHAH-enabled buttons with the id 'more'.
  $form['choice_wrapper']['merci_more'] = array(
    '#type' => 'submit',
    '#value' => t('Add more items'),
    '#description' => t("If the number of items above isn't enough, click here to add more items."),
    '#weight' => 1,
    '#submit' => array(
      'merci_more_choices_submit',
    ),
    // If no javascript action.
    '#ahah' => array(
      'path' => 'merci/js',
      'wrapper' => 'merci-choice-wrapper',
      'method' => 'replace',
      'effect' => 'fade',
    ),
  );
  if (user_access('manage reservations')) {
    $form['merci_reservation_status'] = array(
      '#title' => t('Status'),
      '#type' => 'radios',
      '#options' => merci_record_status(),
      '#default_value' => $node->merci_reservation_status,
      '#description' => t('Finalized bookings can not have time conflicts with each other.'),
    );
  }
  else {
    $form['merci_reservation_status'] = array(
      '#type' => 'value',
      '#value' => $node->merci_reservation_status,
    );
  }
  $form['merci_original_reservation_status'] = array(
    '#type' => 'value',
    '#value' => isset($node->merci_original_reservation_status) ? $node->merci_original_reservation_status : $node->merci_reservation_status,
  );

  // Since hook_validate is broken in 6.x, we add our own
  // custom validation here.
  // TODO check if this fixed.
  $form['#validate'][] = 'merci_node_validate';
  $form['#after_build'][] = '_merci_after_build';
  $form['#cache'] = TRUE;

  // Make sure the form is cached.
  // Pull the correct action out of form_state if it's there to avoid AHAH+Validation action-rewrite.
  if (isset($form_state['action'])) {
    $form['#action'] = $form_state['action'];
  }
  return $form;
}

/**
 * Implementation of hook_form_alter().
 */
function merci_form_alter(&$form, $form_state, $form_id) {

  // Node add/edit forms.
  $type = isset($form['type']['#value']) ? $form['type']['#value'] : NULL;
  switch ($form_id) {

    // Node settings form.
    case $type . '_node_form':
      if (merci_is_merci_type($type)) {
        $node = (object) $form['#node'];
        $sub_type = isset($node->merci_sub_type) ? $node->merci_sub_type : MERCI_SUB_TYPE_ITEM;
        $default_availability = isset($node->merci_default_availability) ? $node->merci_default_availability : MERCI_AVA_F;
        if ($sub_type == MERCI_SUB_TYPE_ITEM) {
          if (empty($form['merci'])) {
            $form['merci'] = array(
              '#type' => 'fieldset',
              '#title' => t('MERCI settings'),
              '#collapsible' => TRUE,
              '#collapsed' => TRUE,
            );
          }
          $form['merci']['merci_default_availability'] = array(
            '#title' => t('Default booking availability'),
            '#type' => 'radios',
            '#options' => merci_item_status(),
            '#description' => t('If no availability information is defined for a given time, the resource falls back onto this setting.'),
            '#default_value' => $default_availability,
            '#element_validate' => array(
              'merci_validate_default_availability',
            ),
          );
        }
        $form['merci_sub_type'] = array(
          '#type' => 'value',
          '#value' => $sub_type,
        );
        merci_add_settings_form($form, $form_state);
      }
      else {
        if ($form_id == 'merci_reservation_node_form') {
          $form['field_merci_date_button'] = array(
            '#type' => 'submit',
            '#value' => t('Limit Lists to Available Items'),
            '#submit' => array(
              'merci_date_filter',
            ),
            '#ahah' => array(
              'path' => 'merci/js',
              'wrapper' => 'merci-choice-wrapper',
              'method' => 'replace',
              'effect' => 'fade',
            ),
          );
        }
      }
      break;
    case 'node_type_form':

      // Reservation content type can't used for other MERCI functionality.
      if (isset($form['#node_type']->type) && $form['#node_type']->type == 'merci_reservation') {
        return;
      }
      $type = $form['old_type']['#value'];
      $settings = new stdClass();
      $settings->merci_type_setting = 'disabled';
      $settings->merci_active_status = MERCI_STATUS_ACTIVE;
      $settings->merci_max_hours_per_reservation = 0;
      $settings->merci_allow_overnight = 0;
      $settings->merci_allow_weekends = 0;
      $settings->merci_rate_per_hour = 0;
      $settings->merci_late_fee_per_hour = 0;
      $settings->merci_fee_free_hours = 0;
      $settings->merci_min_cancel_hours = 0;
      $settings->merci_autocheckout = 0;
      $settings->merci_autocheckin = 0;
      $settings->merci_selfcheckout = 0;
      $settings->merci_spare_items = 0;
      $settings->merci_auto_assign_bucket_item = 0;
      $nodes = 0;

      // If any nodes have already been created, lock the type setting.
      if ($type) {
        $nodes = merci_nodes_for_type_count($type);
        if (merci_is_merci_type($type)) {
          $settings = merci_load_item_settings($type);
        }
      }
      $warning = '<div>' . t('<strong> WARNING:</strong> changing this setting has no effect on existing reserved items.') . '</div>';
      $options = array(
        'disabled' => t('Disabled'),
        'bucket' => t('Bucket'),
        'resource' => t('Resource'),
      );
      $form['merci'] = array(
        '#type' => 'fieldset',
        '#title' => t('MERCI settings'),
        '#collapsible' => TRUE,
        '#collapsed' => TRUE,
      );

      // If any nodes have already been created, lock the type setting.
      if ($nodes) {
        $form['merci']['merci_type_setting'] = array(
          '#type' => 'value',
          '#value' => $settings->merci_type_setting,
        );
        $form['merci']['merci_type_setting_display'] = array(
          '#type' => 'item',
          '#title' => t('Reservable item type'),
          '#value' => $options[$settings->merci_type_setting],
          '#description' => t('The setting can not be changed because content already exists for this type.'),
        );
      }
      else {
        $description_items = array(
          t('Resource:  Use this content type to create unique items that can be reserved.'),
          t('Bucket:  Use this content type to create interchangable items that can be reserved (ex. Camera). Buckets reference interchangable items. The actual item does not have to be chosen until the reservation is checked out.'),
        );
        $form['merci']['merci_type_setting'] = array(
          '#type' => 'radios',
          '#title' => t('Reservable item type'),
          '#options' => $options,
          '#default_value' => $settings->merci_type_setting,
          '#description_items' => $description_items,
          '#theme' => 'merci_type_setting',
        );
      }
      $status = array(
        MERCI_STATUS_ACTIVE => t('Active'),
        MERCI_STATUS_INACTIVE => t('Inactive'),
      );
      $form['merci']['merci_active_status'] = array(
        '#type' => 'radios',
        '#title' => t('Status'),
        '#options' => $status,
        '#default_value' => intval($settings->merci_active_status),
        '#description' => t('Set to active to allow this type to be reserved.'),
      );

      // This setting is only valid for buckets.
      if ($settings->merci_type_setting == 'disabled' || $settings->merci_type_setting == 'bucket') {
        $form['merci']['merci_spare_items'] = array(
          '#type' => 'textfield',
          '#title' => t('Spare items'),
          '#size' => 10,
          '#default_value' => $settings ? $settings->merci_spare_items : 0,
          '#element_validate' => array(
            'merci_is_numeric_validate',
          ),
          '#description' => filter_xss(t("Set this to the number of items of this type that should always be unavailable and thus unreservable.  This way you'll still have enough items for future reservations in case something breaks.") . $warning),
        );
        $form['merci']['merci_auto_assign_bucket_item'] = array(
          '#type' => 'checkbox',
          '#title' => t('Automatically assign a bucket item'),
          '#default_value' => $settings ? $settings->merci_auto_assign_bucket_item : 0,
          '#description' => t('Automatically assign the best fit bucket item when reserving a new bucket item.'),
        );
      }
      $vid = variable_get('merci_equipment_grouping_vid', 0);
      $form['merci']['merci_grouping'] = taxonomy_form($vid, variable_get('merci_grouping_' . $type, 0), t('This will alter order the content types are displayed to users reserving items from buckets.  Terms added to the MERCI Equipment Groupings taxonomy will appear here.'), t('Grouping'));
      $form['merci']['merci_max_hours_per_reservation'] = array(
        '#type' => 'textfield',
        '#title' => t('Maximum hours per reservation'),
        '#size' => 10,
        '#default_value' => $settings ? $settings->merci_max_hours_per_reservation : 0,
        '#element_validate' => array(
          'merci_is_numeric_validate',
        ),
        '#description' => filter_xss(t('The maximum hours the item can be reserved for in one reservation. Set to zero for no limit.') . $warning),
      );
      $form['merci']['merci_allow_overnight'] = array(
        '#type' => 'checkbox',
        '#title' => t('Allow overnight reservation'),
        '#default_value' => $settings ? $settings->merci_allow_overnight : 0,
        '#description' => filter_xss(t('Allow a reservation to continue over multiple days.  If this is not checked, items in this content type must be returned before the checkout closes.') . $warning),
      );
      $form['merci']['merci_allow_weekends'] = array(
        '#type' => 'checkbox',
        '#title' => t('Allow weekend reservation'),
        '#default_value' => $settings ? $settings->merci_allow_weekends : 0,
        '#description' => filter_xss(t('Allow a reservation to be made over days defined as weekend.') . $warning),
      );
      merci_add_settings_form($form, $form_state);
      $form['#validate'][] = 'merci_node_type_save_validate';
      $form['#submit'][] = 'merci_node_type_save_submit';
      break;
    case 'node_delete_confirm':
      $node = node_load((int) arg(1));
      if (!merci_delete_item_validate($node)) {
        unset($form['actions']['submit']);
      }
      break;
    case 'node_type_delete_confirm':
      $type = str_replace('-', '_', arg(3));
      merci_delete_node_type_validate($form);
      break;
    case 'node_admin_content':
      if (!isset($form['#validate'])) {
        $form['#validate'] = array();
      }
      $form['#validate'][] = 'merci_node_admin_delete_validate';
      break;
  }
}

/**
 * Implementation of hook_content_extra_fields.
 */
function merci_content_extra_fields() {
  $extras['merci'] = array(
    'label' => t('MERCI Settings'),
    'description' => t('Allows user to select Reservation status.'),
    'weight' => 100,
  );
  $extras['choice_wrapper'] = array(
    'label' => t('MERCI Choices'),
    'description' => t('Child items included in the Reservation.'),
    'weight' => 80,
  );
  return $extras;
}

/**
 * Implementation of hook_node_operations().
 */
function merci_node_operations($return = NULL) {
  $operations = array(
    'merci_update' => array(
      'label' => t('Confirm Reservation(s)'),
      'callback' => 'merci_operations_update',
    ),
  );
  return $operations;
}

/**
 * Implementation of hook_token_list().
 *
 */
function merci_token_list($type = 'all') {
  if ($type == 'node' || $type == 'all') {

    //@TODO: Fix This Token

    //$tokens['node']['merci_resources'] = t('Reserved resource');
    return $tokens;
  }
}

/**
 * Implementation of hook_token_values().
 * @see {merci_token_list}
 */
function merci_token_values($type, $object = NULL, $options = array()) {
  switch ($type) {
    case 'node':
      $node = merci_load($object);
      if ($node) {
        $values['merci_resources'] = '';

        // We want these timestamps generated in UTC.
        $old_timezone = date_default_timezone_get();
        date_default_timezone_set('UTC');
        $starthour = strtotime($node->field_merci_date[0]['value']);
        $endhour = strtotime($node->field_merci_date[0]['value2']);
        date_default_timezone_set($old_timezone);
        $hours = round(($endhour - $starthour) / 3600, 2);
        $titles = array();
        foreach ($node->merci_reservation_items as $item) {
          $item_node = node_load($item['merci_placeholder_nid']);
          if ($item['item_title'] != '') {
            $titles[] = $item['item_title'];
          }
          else {
            $titles[] = $item['merci_placeholder_title'];
          }
        }
        $values['merci_resources'] = check_plain(implode(", ", $titles));
        return $values;
      }
  }
}

/**
 * Implementation of hook_views_api().
 */
function merci_views_api() {
  return array(
    'api' => 2,
    'path' => drupal_get_path('module', 'merci'),
  );
}

/**
 * Implementation of hook_views_handlers().
 */
function merci_views_handlers() {
  return array(
    'info' => array(
      'path' => drupal_get_path('module', 'merci') . '/handlers',
    ),
    'handlers' => array(
      'views_handler_field_item_node_nid' => array(
        'parent' => 'views_handler_field_prerender_list',
        'file' => 'views_handler_field_item_node_nid.inc',
      ),
      'merci_handler_field_merci_node_type_type_setting' => array(
        'parent' => 'views_handler_field',
        'file' => 'merci_handler_field.inc',
      ),
      'merci_handler_filter_merci_node_type_type_setting' => array(
        'parent' => 'views_handler_filter_in_operator',
        'file' => 'merci_handler_filter_in_operator.inc',
      ),
      'merci_handler_filter_merci_node_type' => array(
        'parent' => 'views_handler_filter_in_operator',
        'file' => 'merci_handler_filter_merci_node_type.inc',
      ),
      'merci_handler_field_merci_node_type_status' => array(
        'parent' => 'views_handler_field',
        'file' => 'merci_handler_field.inc',
      ),
      'merci_handler_filter_merci_node_type_status' => array(
        'parent' => 'views_handler_filter_in_operator',
        'file' => 'merci_handler_filter_in_operator.inc',
      ),
      'merci_handler_field_merci_reservation_status' => array(
        'parent' => 'views_handler_field',
        'file' => 'merci_handler_field.inc',
      ),
      'merci_handler_filter_merci_reservation_status' => array(
        'parent' => 'views_handler_filter_in_operator',
        'file' => 'merci_handler_filter_in_operator.inc',
      ),
      'merci_handler_field_merci_bucket_resource_node_default_availability' => array(
        'parent' => 'views_handler_field',
        'file' => 'merci_handler_field.inc',
      ),
      'merci_handler_filter_merci_bucket_resource_node_default_availability' => array(
        'parent' => 'views_handler_filter_in_operator',
        'file' => 'merci_handler_filter_in_operator.inc',
      ),
      'merci_handler_field_merci_bucket_resource_node_sub_type' => array(
        'parent' => 'views_handler_field',
        'file' => 'merci_handler_field.inc',
      ),
      'merci_handler_filter_merci_bucket_resource_node_sub_type' => array(
        'parent' => 'views_handler_filter_in_operator',
        'file' => 'merci_handler_filter_in_operator.inc',
      ),
      // filter
      'merci_views_handler_filter_reserved_item_nid' => array(
        'parent' => 'views_handler_filter_many_to_one',
      ),
      'merci_handler_field_merci_reservation_item_node_item_status' => array(
        'parent' => 'views_handler_field',
        'file' => 'merci_handler_field.inc',
      ),
      'merci_handler_filter_merci_reservation_item_node_item_status' => array(
        'parent' => 'views_handler_filter_in_operator',
        'file' => 'merci_handler_filter_in_operator.inc',
      ),
    ),
  );
}

/**
 * Implementation of hook_cron().
 */
function merci_cron() {

  // 2009-05-22 20:45:00
  $time = gmdate('Y-m-j H:i:s');
  $nodes = merci_db_reservations_by_status_in_timespan(array(
    MERCI_STATUS_UNCONFIRMED,
    MERCI_STATUS_PENDING,
  ), $time, $time);
  foreach (array_keys($nodes) as $reservation_nid) {
    $reservation = node_load($reservation_nid);

    //check child items of that reservations for autocheckout
    foreach ($reservation->merci_reservation_items as $item) {
      $node = node_load($item['merci_item_nid']);
      if (!$node or !$node->merci_autocheckout) {

        // skip out to the next reservation.
        continue 2;
      }
    }

    //after checking all of the autocheckout settings for all the child items, are they all autocheckout?
    watchdog('merci', "Setting node " . $reservation_nid . " to checked out");
    $reservation->merci_reservation_status = MERCI_STATUS_CHECKED_OUT;
    node_save($reservation);
  }
  $nodes = merci_db_reservations_by_status_in_timespan(array(
    MERCI_STATUS_CHECKED_OUT,
  ), NULL, $time, TRUE);
  foreach (array_keys($nodes) as $reservation_nid) {

    //check child items of that reservations for autocheckin
    $reservation = node_load($reservation_nid);
    foreach ($reservation->merci_reservation_items as $item) {
      $node = node_load($item['merci_item_nid']);
      if (!$node or !$node->merci_autocheckin) {

        // skip out to the next reservation.
        continue 2;
      }
    }

    //after checking all of the autocheckout settings for all the child items, are they all autocheckout?
    watchdog('merci', "Setting node " . $reservation_nid . " to checked in");
    $reservation->merci_reservation_status = MERCI_STATUS_CHECKED_IN;
    node_save($reservation);
  }

  // Give no shows a one hour grace period.
  // TODO: move grace period to admin option.
  // 2009-05-22 20:45:00
  $time = gmdate('Y-m-j H:i:s', time() - 3600);

  //find all pending reservations that have started and set their stauts to no show
  $nodes = merci_db_reservations_by_status_in_timespan(array(
    MERCI_STATUS_PENDING,
  ), $time, NULL);
  foreach (array_keys($nodes) as $reservation_nid) {
    watchdog('merci', "Setting node " . $reservation_nid . " to no show");
    $node = node_load($reservation_nid);
    $node->merci_reservation_status = MERCI_STATUS_NO_SHOW;
    node_save($node);
  }
}

/**
 * Implementation of hook_load().
 */
function merci_load($node) {
  $return = new stdClass();
  $return->merci_reservation_status = merci_reservation_status($node);

  // TODO get rid of merci array.  should match form api post fields.
  $return->merci_reservation_items = merci_reservation_items($node);
  return $return;
}
function merci_node_load(&$nodes, $types) {

  // Process active MERCI node types and reservation nodes.
  foreach ($nodes as $nid => $node) {
    if (merci_is_merci_type($node->type)) {
      $settings = merci_load_item_settings($node);
      foreach ($settings as $key => $value) {
        $nodes[$nid]->{$key} = $value;
      }
    }
  }
}
function merci_node_insert($node) {

  // Process active MERCI node types and reservation nodes.
  if (merci_is_merci_type($node->type)) {
    $merci_type = merci_type_setting($node->type);
    drupal_write_record('merci_' . $merci_type . '_node', $node);
    drupal_write_record('merci_reservation_item_node', $node);
  }
}
function merci_node_update($node) {

  // Process active MERCI node types and reservation nodes.
  if (merci_is_merci_type($node->type)) {
    $merci_type = merci_type_setting($node->type);
    if ($node->revision) {
      drupal_write_record('merci_' . $merci_type . '_node', $node);
      drupal_write_record('merci_reservation_item_node', $node);
    }
    else {
      drupal_write_record('merci_' . $merci_type . '_node', $node, 'vid');
      drupal_write_record('merci_reservation_item_node', $node, 'vid');
    }
  }
}
function merci_node_delete($node) {

  // Process active MERCI node types and reservation nodes.
  if (merci_is_merci_type($node->type)) {
    $merci_type = merci_type_setting($node->type);
    $node->merci_placeholder_nid = $node->nid;
    merci_delete_record('merci_reservation_detail', $node, 'merci_placeholder_nid');
    merci_delete_record('merci_' . $merci_type . '_node', $node, 'nid');
    merci_delete_record('merci_reservation_item_node', $node, 'nid');
  }
}
function merci_node_revision_delete($node) {

  // Process active MERCI node types and reservation nodes.
  if (merci_is_merci_type($node->type)) {
    $merci_type = merci_type_setting($node->type);
    merci_delete_record('merci_' . $merci_type . '_node', $node, 'vid');
    merci_delete_record('merci_reservation_item_node', $node, 'vid');
  }
  else {
    if ($type == 'merci_reservation') {
      merci_delete_record('merci_reservation', $node, 'vid');
      merci_delete_record('merci_reservation_detail', $node, 'vid');
    }
  }
}

/**
 * Implementation of hook_nodeapi().
 */
function merci_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
  switch ($op) {

    //case 'prepare': prepare and load are always called one after the other.  Just need to use one.
    case 'load':

      // Merge in reservable items settings or else just use the default defined in the content type.
      if (merci_is_merci_type($node->type)) {
        return (array) merci_load_item_settings($node);
      }
      break;
    case 'insert':
      merci_node_insert($node);
      break;
    case 'update':
      merci_node_update($node);
      break;
    case 'delete':
      merci_node_delete($node);
      break;
    case 'delete revision':
      merci_node_revision_delete($node);
      break;
  }
}

/**
 * Implementation of hook_theme().
 */
function merci_theme() {
  return array(
    'merci_choices' => array(
      'arguments' => array(
        'form' => NULL,
      ),
      'file' => 'theme.inc',
    ),
    'merci_build_reservation_table_form' => array(
      'arguments' => array(
        'form' => NULL,
      ),
      'file' => 'theme/theme.inc',
    ),
    'merci_conflict_grid' => array(
      'template' => 'merci_conflict_grid',
      'arguments' => array(
        'type' => NULL,
        'title' => NULL,
        'start' => NULL,
        'end' => NULL,
        'nid' => NULL,
        'reservation_nid' => NULL,
      ),
      'path' => drupal_get_path('module', 'merci') . '/theme',
      'file' => 'theme.inc',
    ),
    'merci_reservation_table' => array(
      'template' => 'merci_reservation_table',
      'path' => drupal_get_path('module', 'merci') . '/theme',
      'arguments' => array(
        'reservations' => NULL,
        'count' => NULL,
        'hours' => NULL,
        'title' => NULL,
      ),
    ),
    'merci_current_inventory' => array(
      'path' => drupal_get_path('module', 'merci') . '/theme',
      'file' => 'theme.inc',
    ),
    'merci_type_setting' => array(
      'path' => drupal_get_path('module', 'merci') . '/theme',
      'arguments' => array(
        'form' => NULL,
      ),
      'file' => 'theme.inc',
    ),
  );
}

/**
 * Implementation of hook_simpletest().
 */
function merci_simpletest() {
  $dir = drupal_get_path('module', 'merci') . '/tests';
  $tests = file_scan_directory($dir, '\\.test$');
  return array_keys($tests);
}
function _merci_content_type_info($reset = FALSE) {
  static $info;
  if ($reset || !isset($info)) {
    if (!$reset && ($cached = cache_get('merci_content_type_info'))) {
      $info = $cached->data;
    }
    else {
      $info = array();

      // Load MERCI node type settings.
      $merci_settings = db_query("SELECT * FROM {merci_node_type} mt INNER JOIN {node_type} nt ON mt.type = nt.type WHERE merci_type_setting <> 'disabled'");

      // with the correct stuff..
      while ($merci_setting = db_fetch_array($merci_settings)) {
        $merci_setting['type_name'] = $merci_setting['name'];
        $grouping = $merci_setting['merci_type_setting'] == 'resource' ? $merci_setting['type_name'] : t('Buckets');
        $tid = variable_get('merci_grouping_' . $merci_setting['type'], 0);
        if ($tid) {
          $term = taxonomy_get_term($tid);
          if ($term) {
            $grouping = $term->name;
          }
        }
        $merci_setting['merci_item_grouping'] = $grouping;
        unset($merci_setting['name']);
        $info[$merci_setting['type']] = $merci_setting;
      }
      cache_set('merci_content_type_info', $info);
    }
  }
  return $info;
}

/**
 * Validates if an item node can be deleted.
 *
 * @param $node
 *   The item node.
 * @param $single
 *   TRUE if a single item node deletion is being processed, FALSE otherwise.
 *
 * @return
 *   TRUE if the item can be deleted, FALSE otherwise.
 */
function merci_delete_item_validate($node) {

  // Only validate bucket/resource items.
  if ($node->type != 'merci_reservation' && $node->merci_type_setting != 'disabled' && $node->merci_sub_type == MERCI_SUB_TYPE_ITEM) {

    // Determine CCK table and columns the date data is stored in.
    $field = content_fields('field_merci_date');
    $db_info = content_database_info($field);
    $table = $db_info['table'];

    // Join on nid here so that any version of the reservation that contain
    // the item is caught.
    // Pull any reservations that use the item in question
    $reservations = db_query("SELECT n.nid, n.title FROM {node} n INNER JOIN {" . $table . "} ct ON ct.vid = n.vid INNER JOIN {merci_reservation_detail} md ON ct.vid = md.vid WHERE md.merci_item_nid = %d", $node->nid);
    $bad_reservations = array();
    while ($reservation = db_fetch_object($reservations)) {

      // Key by nid to prevent duplicate revisions from appearing.
      $bad_reservations[$reservation->nid] = l($reservation->title, "node/{$reservation->nid}/edit", array(
        'query' => drupal_get_destination(),
      ));
    }
    if (!empty($bad_reservations)) {
      drupal_set_message(t('%title can not be deleted, because it is associated with the following reservations:', array(
        '%title' => $node->title,
      )) . theme('item_list', $bad_reservations), 'error');

      // Lock out single deletion attempts here.
      return FALSE;
    }
  }
  return TRUE;
}

/**
 * Validates saving of MERCI node types.
 */
function merci_node_type_save_validate($form, &$form_state) {
  $values = $form_state['values'];

  // Only validate node types set to an inactive status.
  if ($values['merci_type_setting'] != 'disabled' && (int) $values['merci_active_status'] == MERCI_STATUS_INACTIVE) {

    // Determine CCK table and columns the date data is stored in.
    $field = content_fields('field_merci_date');
    $db_info = content_database_info($field);
    $table = $db_info['table'];
    $column_end_date = $db_info['columns']['value2']['column'];
    $time = gmdate('Y-m-d H:i:s');
    $type_setting = $values['merci_type_setting'];

    // Pull all active reservations that use the node type.
    $reservations = db_query("SELECT ctn.nid, ctn.title FROM {" . $table . "} ct INNER JOIN {merci_reservation_detail} md ON ct.vid = md.vid INNER JOIN {node} ctn ON ct.vid = ctn.vid INNER JOIN {merci_{$type_setting}_node} m ON md.merci_placeholder_nid = m.nid INNER JOIN {node} mn ON m.vid = mn.vid  WHERE mn.type = '%s' AND m.merci_sub_type = %d AND {$column_end_date} >= '%s' AND NOT (md.merci_item_status <= %d)", $values['old_type'], MERCI_SUB_TYPE_RESERVATION, $time, MERCI_ITEM_STATUS_CHECKED_IN);
    $bad_reservations = array();
    while ($reservation = db_fetch_object($reservations)) {
      $bad_reservations[] = l($reservation->title, "node/{$reservation->nid}/edit", array(
        'query' => drupal_get_destination(),
      ));
    }
    if (!empty($bad_reservations)) {
      form_set_error('merci_active_status', t('@type_setting can not be set to an inactive status until all @type_setting items are removed from the following reservations:', array(
        '@type_setting' => $type_setting,
      )) . theme('item_list', $bad_reservations));
    }
  }
}

/**
 * Validates deletion of node types.
 *
 * @param $type
 *   The type being deleted.
 */
function merci_delete_node_type_validate(&$form) {
  $type = $form['type']['#value'];

  // Only validate active MERCI node types.
  if (merci_is_merci_type($type)) {

    //$settings = merci_load_item_settings($type);
    $merci_type_setting = merci_type_setting($type);

    // Determine CCK table and columns the date data is stored in.
    $field = content_fields('field_merci_date');
    $db_info = content_database_info($field);
    $table = $db_info['table'];

    // Join on nid here so that any version of the reservation that contain
    // the bucket/resource is caught.
    $reservations = db_query("SELECT ctn.nid, ctn.title FROM {" . $table . "} ct INNER JOIN {merci_reservation_detail} md ON ct.vid = md.vid INNER JOIN {node} ctn ON ct.nid = ctn.nid INNER JOIN {merci_{$merci_type_setting}_node} m ON md.merci_placeholder_nid = m.nid INNER JOIN {node} mn ON m.vid = mn.vid  WHERE mn.type = '%s' AND m.merci_sub_type = %d ORDER BY ct.nid, ct.vid", $type, MERCI_SUB_TYPE_RESERVATION);
    $bad_reservations = array();
    while ($reservation = db_fetch_object($reservations)) {
      $bad_reservations[$reservation->nid] = l($reservation->title, "node/{$reservation->nid}/edit", array(
        'query' => drupal_get_destination(),
      ));
    }
    if (!empty($bad_reservations)) {
      $name = node_get_types('name', $type);
      drupal_set_message(t('@type can not be deleted because it is associated with the following reservations:', array(
        '@type' => $name,
      )) . theme('item_list', $bad_reservations), 'error');
      unset($form['actions']['submit']);
    }
  }
}

/**
 * Does the very standard things that must be done in any normal callback.
 * Used by each callback in this example module.
 */
function merci_ahah_get_form() {

  // The form is generated in an include file which we need to include manually.
  include_once 'modules/node/node.pages.inc';
  $form_state = array(
    'storage' => NULL,
    'submitted' => FALSE,
  );

  //$form_state = array('storage' => TRUE, 'submitted' => TRUE);
  $form_build_id = $_POST['form_build_id'];
  $form = form_get_cache($form_build_id, $form_state);
  $args = $form['#parameters'];
  $form_id = array_shift($args);
  $form_state['post'] = $form['#post'] = $_POST;

  // Enable the submit/validate handlers to determine whether AHAH-submittted.
  $form_state['ahah_submission'] = TRUE;
  $form['#programmed'] = $form['#redirect'] = FALSE;

  // Stash original form action to avoid overwriting with drupal_rebuild_form().
  $form_state['action'] = $form['#action'];

  // Ripped off from drupal_process_form.
  // We do the same except call validation.
  $form = form_builder($form_id, $form, $form_state);

  // We have to do this in order for the date_combo to be rebuilt properly.
  $element = $form['field_merci_date'][0];
  date_combo_validate($element, $form_state);

  // Continue ripping off drupal_proecss_form
  form_clean_id(NULL, TRUE);
  $form_state['redirect'] = NULL;
  form_execute_handlers('submit', $form, $form_state);

  // We'll clear out the cached copies of the form and its stored data
  // here, as we've finished with them. The in-memory copies are still
  // here, though.
  if (variable_get('cache', CACHE_DISABLED) == CACHE_DISABLED && !empty($form_state['values']['form_build_id'])) {
    cache_clear_all('form_' . $form_state['values']['form_build_id'], 'cache_form');
    cache_clear_all('storage_' . $form_state['values']['form_build_id'], 'cache_form');
  }

  // END ripping drupal_process_form.
  $form = drupal_rebuild_form($form_id, $form_state, $args, $form_build_id);

  // Clear any validation errors.  Only the date field is validated so this will clear the error so it won't
  // be outlined in red.
  form_set_error(NULL, '', TRUE);
  return array(
    $form,
    $form_state,
  );
}
function merci_ahah_output($selected_portion, $show_status = FALSE) {
  if ($show_status) {
    $output .= theme('status_messages');
  }

  // To avoid doubling-up the wrapper, we have to remove it here.
  if (is_array($selected_portion)) {
    unset($selected_portion['#prefix'], $selected_portion['#suffix']);

    //$output = theme('status_messages') . drupal_render($selected_portion);

    // BAD HACK to not show validation error messages for the date combo.  Not sure how to turn validation off for that.
    $output .= drupal_render($selected_portion);
  }
  else {
    if (is_string($selected_portion)) {
      $output .= $selected_portion;
    }
  }

  // Final rendering callback.
  drupal_json(array(
    'status' => TRUE,
    'data' => $output,
  ));
}

// TODO: should not do theming in validation funcitons.
function merci_theme_conflict_grid($type, $title, $start, $end, $value, $nid) {
  return theme('merci_conflict_grid', $type, $title, $start, $end, $value, $nid);
}

/**
 * Builds the form item for the status display.
 *
 * @param $form_state
 *   Current form state.
 * @param $status
 *   Current status
 *
 * @return
 *   The form array.
 */
function merci_display_reservation_status(&$form_state, $status) {
  $form['merci_reservation_status'] = array(
    '#type' => 'item',
    '#title' => t('Status'),
    '#value' => $status,
  );
  return $form;
}
function merci_build_accessory_form($form_state, $node, $did) {
  $vocabularies = taxonomy_get_vocabularies($node->type);
  $form = array();
  foreach ($node->taxonomy as $tid => $term) {
    $value[] = $tid;
  }
  foreach ($vocabularies as $vocabulary) {
    $taxonomy_form = taxonomy_form($vocabulary->vid, $value);
    $taxonomy_form['#title'] = '';
    unset($taxonomy_form['#theme']);
    $taxonomy_form['#ahah'] = array(
      'path' => 'merci/taxonomy/' . $node->nid . '/' . $vocabulary->vid . '/' . $did,
      'wrapper' => 'merci-accessories-' . $node->nid,
      'method' => 'prepend',
      'effect' => 'fade',
    );
    $form[$vocabulary->vid] = $taxonomy_form;
  }
  return $form;
}

/**
 * Builds the table of existing reserved items.
 *
 * @param $form_state
 *   Current form state.
 * @param $node
 *   The reservation node.
 * @param $edit_page
 *   TRUE if the table is on the edit page for the reservation, FALSE otherwise.
 *
 * @return
 *   The form array.
 */
function merci_build_reservation_table_form(&$form_state, $node, $edit_page = FALSE) {
  global $user;
  $table = array();
  $table['#theme'] = 'merci_build_reservation_table_form';
  $table['#node'] = $node;
  $table['#tree'] = TRUE;
  $table['#table'] = array();
  $table['#header'] = array(
    t('Item'),
  );
  if (!isset($node->merci_reservation_items)) {
    return $table;
  }
  $items = $node->merci_reservation_items;
  $table['#header'][] = t('Type');
  foreach ($items as $did => $item) {
    if (!is_numeric($did)) {
      continue;
    }
    $form = array();
    foreach ($item as $key => $value) {
      $form[$key] = array(
        '#type' => 'value',
        '#value' => $value,
      );
    }
    $form['display_name']['#value'] = $form['name']['#value'];
    $form['display_item_title']['#value'] = $form['item_title']['#value'];
    if (user_access("manage reservations")) {
      if (merci_has_accessories($item['type'])) {
        $placeholder_node = node_load($item['merci_placeholder_nid']);
        if (!in_array(t('Accessories'), $table['#header'])) {
          $table['#header'][] = t('Accessories');
        }
        if ($edit_page) {
          if ($node->merci_reservation_status == MERCI_STATUS_CHECKED_OUT) {
            $collapsed = FALSE;
          }
          else {
            $collapsed = TRUE;
          }

          // Container for just the item selector.
          $form['accessories'] = array(
            '#type' => 'fieldset',
            '#title' => t('Add accessories'),
            '#collapsible' => TRUE,
            '#collapsed' => $collapsed,
            '#prefix' => '<div id="merci-accessories-' . $placeholder_node->nid . '">',
            '#suffix' => '</div>',
            'choices' => merci_build_accessory_form($form_state, $placeholder_node, $did),
          );
        }
        else {
          $accessories = '';
          $terms = taxonomy_node_get_terms($placeholder_node);
          if (!empty($terms)) {
            foreach ($terms as $accessory) {
              $accessories .= $accessory->name . ', ';
            }
          }
          $form['accessories'] = array(
            '#type' => 'markup',
            '#title' => t('Accessories'),
            '#value' => $accessories,
          );
        }

        //$operations .= ' &nbsp;&nbsp;' . l(t('edit'), "node/$item->merci_placeholder_nid/edit", array('query' => drupal_get_destination()));

        // Only allow editing or deletion if unconfirmed or confirmed.
        if ($edit_page && $node->merci_reservation_status >= MERCI_STATUS_CHECKED_OUT) {
          foreach ($form['accessories']['choices'] as $vid => $values) {
            $form['accessories']['choices'][$vid]['#disabled'] = TRUE;
          }
          $form['accessories']['#title'] = t('Accessories');
        }
      }
    }

    // Only allow editing or deletion if unconfirmed or confirmed.
    if ($edit_page && $node->merci_reservation_status < MERCI_STATUS_CHECKED_OUT) {
      if (!in_array(t('Operations'), $table['#header'])) {
        $table['#header'][] = t('Operations');
      }

      //@TODO: Should users be able to remove items from confirmed reservations?
      if (user_access("manage reservations") || $node->uid == $user->uid) {
        if (!in_array(t('Operations'), $table['#header'])) {
          $table['#header'][] = t('Operations');
        }
        $operations = ' &nbsp;&nbsp;' . l(t('delete'), "node/" . $item['merci_placeholder_nid'] . "/delete", array(
          'query' => drupal_get_destination(),
        ));
      }
      $operations .= ' &nbsp;&nbsp;' . l(t('edit'), "node/" . $item['merci_placeholder_nid'] . "/edit", array(
        'query' => drupal_get_destination(),
      ));
      $form['ops']['#value'] = $operations;
    }
    $type_setting = merci_type_setting($item['type']);
    if ($edit_page && $type_setting == 'bucket') {

      // Only users with manage reservations permission can change the bucket item assignment.
      if (user_access('manage reservations')) {
        $options = array(
          0 => t('<Select>'),
        );
        $options += merci_get_available_bucket_items($node, $item['type']);
        $form['merci_item_nid'] = array(
          '#type' => 'select',
          '#options' => $options,
          '#default_value' => $item['merci_item_nid'],
        );
        if ($node->merci_reservation_status >= MERCI_STATUS_CHECKED_OUT) {
          $form['merci_item_nid']['#disabled'] = true;
        }
      }
    }

    //$form['#table'][$did]['accessories'] = $accessories;
    $table[$did] = $form;
  }
  return $table;
}
function merci_add_settings_form(&$form, $form_state) {

  // Only admin can edit these values.
  if (!user_access('administer MERCI')) {
    return;
  }
  $type = array_key_exists('old_type', $form) ? $form['old_type']['#value'] : $form['type']['#value'];

  //$merci_settings = mnerci_content_types($type);

  //if(!$merci_settings) return;
  if (merci_is_merci_type($type)) {
    $settings = merci_load_item_settings($type);
  }
  else {
    $settings = new stdClass();
    $settings->merci_type_setting = 'disabled';
    $settings->merci_active_status = MERCI_STATUS_ACTIVE;
    $settings->merci_max_hours_per_reservation = 0;
    $settings->merci_allow_overnight = 0;
    $settings->merci_allow_weekends = 0;
    $settings->merci_rate_per_hour = 0;
    $settings->merci_late_fee_per_hour = 0;
    $settings->merci_fee_free_hours = 0;
    $settings->merci_min_cancel_hours = 0;
    $settings->merci_autocheckout = 0;
    $settings->merci_autocheckin = 0;
    $settings->merci_selfcheckout = 0;
  }
  if ($form['#id'] == 'node-type-form') {
    $node = $settings;
  }
  else {
    $node = (object) $form['#node'];
    if (empty($node->merci_type_setting)) {
      if (merci_is_merci_type($node->type)) {
        foreach ($settings as $key => $value) {
          if (!isset($node->{$key})) {
            $node->{$key} = $value;
          }
        }
      }
    }
  }

  //merci_load_item_settings($node);

  // New nodes are always sub type item.
  $sub_type = isset($node->merci_sub_type) ? $node->merci_sub_type : MERCI_SUB_TYPE_ITEM;
  if (empty($form['merci'])) {
    $form['merci'] = array(
      '#type' => 'fieldset',
      '#title' => t('MERCI settings'),
      '#collapsible' => TRUE,
      '#collapsed' => TRUE,
    );
  }

  // Bucket item nodes have no individual pricing, so just zero these values out.
  // Althought you can override them on the reserervation nodes.
  if ($form['#id'] == 'node-type-form' or $sub_type == MERCI_SUB_TYPE_RESERVATION or $node->merci_type_setting == 'resource') {
    $form['merci']['merci_rate_per_hour'] = array(
      '#type' => 'textfield',
      '#title' => t('Rate per hour'),
      '#size' => 10,
      '#default_value' => $node->merci_rate_per_hour,
      '#element_validate' => array(
        'merci_is_numeric_validate',
      ),
      '#description' => t('The per hour rental fee for the item.'),
    );
    $form['merci']['merci_late_fee_per_hour'] = array(
      '#type' => 'textfield',
      '#title' => t('Late fee per hour'),
      '#size' => 10,
      '#default_value' => $node->merci_late_fee_per_hour,
      '#element_validate' => array(
        'merci_is_numeric_validate',
      ),
      '#description' => t('The per hour fee for returning the item late.'),
    );
    $form['merci']['merci_fee_free_hours'] = array(
      '#type' => 'textfield',
      '#title' => t('Fee free hours'),
      '#size' => 10,
      '#default_value' => $node->merci_fee_free_hours,
      '#element_validate' => array(
        'merci_is_numeric_validate',
      ),
      '#description' => t('The number of hours the item can be used before fees are charged.'),
    );
  }
  if ($form['#id'] == 'node-type-form' or $node->merci_type_setting == 'resource' and $sub_type == MERCI_SUB_TYPE_ITEM) {
    $form['merci']['merci_min_cancel_hours'] = array(
      '#type' => 'textfield',
      '#title' => t('Minimum hours for cancelation without No Show'),
      '#size' => 10,
      '#default_value' => $node->merci_min_cancel_hours,
      '#element_validate' => array(
        'merci_is_numeric_validate',
      ),
      '#description' => t('Minimum number of hours before the start time a user may cancel a reservation for the item.'),
    );
    $form['merci']['merci_autocheckout'] = array(
      '#type' => 'checkbox',
      '#title' => t('Auto checkout'),
      '#default_value' => $node->merci_autocheckout,
      '#description' => t('Automatically check this item out when the Reservation starts.'),
    );
    $form['merci']['merci_autocheckin'] = array(
      '#type' => 'checkbox',
      '#title' => t('Auto checkin'),
      '#default_value' => $node->merci_autocheckin,
      '#description' => t('Automatically check this item in when the Reservation ends.'),
    );
    $form['merci']['merci_selfcheckout'] = array(
      '#type' => 'checkbox',
      '#title' => t('Self checkout'),
      '#default_value' => $node->merci_selfcheckout,
      '#description' => t('Manage checkout with additional code.'),
    );
  }
}
function _merci_after_build($form, &$form_state) {
  $weight = $form['field_merci_date']['#weight'] + 1;
  $form['field_merci_date_button']['#weight'] = $weight;
  return $form;
}

/**
 * Builds an individual item selector.
 *
 * @param $node
 *   The reservation node object.
 * @param $form_state
 *   Current form state array.
 * @param $delta
 *   Which selector number to build.
 * @param $default
 *   Default value for the select.
 *
 * @return
 *   The form array for the selector.
 */
function _merci_choice_form($node, $form_state, $delta, $default = '', $reset = NULL) {
  static $options = array();

  // We'll manually set the #parents property of these fields so that
  // their values appear in the $form_state['values']['choice'] array.

  //$buckets = t('Buckets');
  if (empty($options) or $reset) {

    // NOTE: we don't filter by node here because we only want items not

    //reserved by any node including the node calling the function.
    $options = merci_build_reservable_items($node, $form_state, NULL);
  }
  $form = array(
    '#type' => 'select',
    '#options' => $options['options'],
    '#default_value' => $default,
  );
  return $form;
}

Functions

Namesort descending Description
merci_access Implementation of hook_access().
merci_add_settings_form
merci_ahah_get_form Does the very standard things that must be done in any normal callback. Used by each callback in this example module.
merci_ahah_output
merci_build_accessory_form
merci_build_reservation_table_form Builds the table of existing reserved items.
merci_content_extra_fields Implementation of hook_content_extra_fields.
merci_cron Implementation of hook_cron().
merci_delete Implementation of hook_delete().
merci_delete_item_validate Validates if an item node can be deleted.
merci_delete_node_type_validate Validates deletion of node types.
merci_display_reservation_status Builds the form item for the status display.
merci_form Implementation of hook_form().
merci_form_alter Implementation of hook_form_alter().
merci_init Implementation of hook_init().
merci_insert Implementation of hook_insert().
merci_load Implementation of hook_load().
merci_menu Implementation of hook_menu().
merci_nodeapi Implementation of hook_nodeapi().
merci_node_delete
merci_node_info Implementation of hook_node_info().
merci_node_insert
merci_node_load
merci_node_operations Implementation of hook_node_operations().
merci_node_revision_delete
merci_node_type Implementation of hook_node_type().
merci_node_type_save_validate Validates saving of MERCI node types.
merci_node_update
merci_node_validate Implementation of hook_validate().
merci_perm Implementation of hook_perm().
merci_prepare Implementation of hook_prepare().
merci_simpletest Implementation of hook_simpletest().
merci_theme Implementation of hook_theme().
merci_theme_conflict_grid
merci_token_list Implementation of hook_token_list().
merci_token_values Implementation of hook_token_values().
merci_update Implementation of hook_update().
merci_view Implementation of hook_view().
merci_views_api Implementation of hook_views_api().
merci_views_handlers Implementation of hook_views_handlers().
_merci_after_build
_merci_choice_form Builds an individual item selector.
_merci_content_type_info

Constants