'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' => 'merci_current_inventory',
'access arguments' => array('manage reservations'),
'file' => 'includes/menu.inc',
'type' => MENU_NORMAL_ITEM,
);
// 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',
);
//TODO move to merci_debug module.
$items['admin/merci/unlock'] = array(
'title' => 'Unlock Core MERCI Fields',
'page callback' => 'merci_unlock',
'access arguments' => array('access administration pages'),
'file' => 'includes/merci.admin.inc',
'type' => MENU_CALLBACK,
);
$items['admin/merci/lock'] = array(
'title' => 'Lock Core MERCI Fields',
'page callback' => 'merci_lock',
'access arguments' => array('access administration pages'),
'file' => 'includes/merci.admin.inc',
'type' => MENU_CALLBACK,
);
$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->type);
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 == 'edit' && 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_load().
*/
function merci_load($node) {
if ($node->type == 'merci_reservation') {
$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;
}
}
/**
* 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));
}
}
$merci_debug_setting = variable_get('merci_debug', 0);
function merci_debug($string) {
//set debug state
global $merci_debug_setting;
if ($merci_debug_setting)
watchdog($string);
}
function merci_validate_merci_reservation_date($form, &$form_state) {
$node = (object) $form_state['values'];
// ****
// Build date objects we'll need for our different validations.
// ****
$start = $node->field_merci_date[0]['value'];
$end = $node->field_merci_date[0]['value2'];
$start_object = merci_create_local_date_object($start);
$end_object = merci_create_local_date_object($end);
$hours_of_operation = merci_load_hours_of_operation();
$start_day_of_week = (int) date_format($start_object, 'w');
$end_day_of_week = (int) date_format($end_object, 'w');
$start_month_day = date_format($start_object, 'm-d');
$end_month_day = date_format($end_object, 'm-d');
$start_hours = $hours_of_operation[$start_day_of_week];
$end_hours = $hours_of_operation[$end_day_of_week];
$start_date = date_format($start_object, 'm-d-Y');
$max_days = variable_get("merci_max_days_advance_reservation", '0');
// Hours of operation restrictions, max days, and closed dates checks
// Users in role with Administer MERCI permssion or outside hours of operation skip these checks
if (user_access('create reservations outside hours of operation')) {
merci_debug('merci', 'SKIP Hours of Operation Check');
merci_debug('merci', 'SKIP Max Days Check');
merci_debug('merci', 'SKIP Closed Dates Check');
//check to see if warning should be displayed
if (strtotime(date('G:i', strtotime($start . ' UTC'))) < strtotime($start_hours['open']) || strtotime($start_hours['close']) < strtotime(date('G:i', strtotime($end . ' UTC')))) {
drupal_set_message('' . t('You are making a Reservation outside the normal hours of operation. This may impact access to the items you are reserving.') . '');
}
}
else {
// Reservation start date cannot exceed the max advance
merci_debug('merci', 'CHECKING Max Days');
if ($max_days) {
$max_date = new DateTime("+$max_days day");
//$max_date = date('m-d-Y', mktime(0, 0, 0, date("m"), date("d")+$max_days, date("Y")));
if ($start_object > $max_date) {
form_set_error('field_merci_date][0][value][date', t('You cannot make a Reservation more than %days days in advance. Start the Reservation before %date.', array('%days' => $max_days, '%date' => date_format($max_date, 'm-d-Y'))));
}
}
// Can't start or end a reservation on days that are
// closed dates.
merci_debug('merci', 'CHECKING Closed Dates');
if (in_array($start_month_day, $hours_of_operation['closed_days'])) {
$name = date_format($start_object, 'F jS');
form_set_error('field_merci_date][0][value][date', t('Sorry, but we are closed on %day for a holiday or special event.', array('%day' => $name)));
}
if (in_array($end_month_day, $hours_of_operation['closed_days'])) {
$name = date_format($end_object, 'F jS');
form_set_error('field_merci_date][0][value2][date', t('Sorry, but we are closed on %day for a holiday or special event.', array('%day' => $name)));
}
// Can't start or end a reservation on a day the facility
// has no hours of operation, or outside hours of operation.
merci_debug('merci', 'CHECKING Hours of Operation');
$start_name = date_format($start_object, 'l');
if (!$hours_of_operation[$start_day_of_week]) {
form_set_error('field_merci_date][0][value][date', t('Reservations cannot start on a %day.', array('%day' => $start_name)));
}
else {
$start_time = date_format($start_object, 'H:i');
if ($start_time < $start_hours['open']) {
form_set_error('field_merci_date][0][value][time', t('Reservations cannot start at a time before %start.',
array('%start' => merci_format_time($start_hours['open']))));
}
elseif ($start_time > $start_hours['close']) {
form_set_error('field_merci_date][0][value][time', t('Reservations cannot start at a time after %end.',
array('%end' => merci_format_time($start_hours['close']))));
}
}
$end_name = date_format($end_object, 'l');
if (!$hours_of_operation[$end_day_of_week]) {
form_set_error('field_merci_date][0][value2][date', t('Reservations cannot end on a %day.', array('%day' => $end_name)));
}
else {
$end_time = date_format($end_object, 'H:i');
if ($end_time < $end_hours['open']) {
form_set_error('field_merci_date][0][value2][time', t('Reservations cannot end at a time before %start.',
array('%start' => merci_format_time($end_hours['open']))));
}
elseif ($end_time > $end_hours['close']) {
form_set_error('field_merci_date][0][value2][time', t('Reservations cannot end at a time after %end.',
array('%end' => merci_format_time($end_hours['close']))));
}
}
} // Hours of operation restrictions, max days, and closed dates checks
}
function merci_validate_status($form, &$form_state) {
$node = (object) $form_state['values'];
// Reservations with a checked out status.
if ($node->merci_reservation_status == MERCI_STATUS_CHECKED_OUT) {
// Make sure all existing bucket reservations have an item assigned.
if (empty($node->merci_reservation_items)) {
form_set_error('merci_reservation_status', t('You can not finalize a reservation that has no reserved items.'));
}
else {
foreach ($node->merci_reservation_items as $did => $item) {
if ($item['merci_item_nid'] == "0") {
form_set_error("merci_reservation_items][$did][merci_item_nid", t("The reservation for %title must have an item associated with it for finalized reservations.", array( '%title' => $item['name'])));
}
// Can't add a bucket item and finalize at the same time.
if (!is_numeric($item['merci_item_nid']) and strlen($item['merci_item_nid'])) {
form_set_error("merci_reservation_items][$did][merci_item_nid", t("You cannot finalize a reservation while adding a bucket item."));
}
}
}
}
// Prevent status changes on reservations that have past.
$current_status = $node->merci_original_reservation_status;
if ($current_status && $current_status != $node->merci_reservation_status && time() > strtotime($node->field_merci_date[0]['value2']) && !in_array((int) $node->merci_reservation_status, array(MERCI_STATUS_CANCELLED, MERCI_STATUS_CHECKED_IN, MERCI_STATUS_DENIED))) {
$statuses = merci_record_status();
form_set_error('merci_reservation_status', t('You cannot change the status to %status for a reservation that has past.', array('%status' => $statuses[$node->merci_reservation_status])));
}
}
function merci_validate_merci_selected_items($form, &$form_state) {
$node = (object) $form_state['values'];
$choices = $node->merci_reservation_items;
$reservation_nid = $node->nid;
// ****
// Build date objects we'll need for our different validations.
// ****
$start = $node->field_merci_date[0]['value'];
$end = $node->field_merci_date[0]['value2'];
// Do this even for reservations with merci status of canceled and checked in.
$choice_counts = array();
$total_counts = array();
$messages = array();
foreach ($choices as $did => $item) {
if (is_array($item)) {
$value = $item['merci_item_nid'];
} else {
$value = $item;
}
if (is_numeric($did) and !$value) {
$value = $item['type'];
}
if ($value) {
// Only include active buckets, and content types the user
// can reserve.
$messages[$did] = '';
if (is_numeric($value)) {
$new_item = node_load($value);
$title = $new_item->title;
$type = $new_item->type;
} else {
$title = node_get_types('name',$value);
$type = $value;
}
$content_settings = merci_load_item_settings($type);
// Is this content type active?
if ($content_settings->merci_active_status != MERCI_STATUS_ACTIVE) {
$messages[$did] = '
' . t("%name is not active.", array('%name' => $title)) . '
';
continue;
}
// Does the user have access to manage reservations or this content type?
if(!user_access('manage reservations') && !merci_check_content_type_user_permissions($type)) {
$messages[$did] = '
' . t("You do not have permission to reserve %name.", array('%name' => $title)) . '
';
continue;
}
// Do we have the item available at this time?
if(!isset($total_counts[$type])) {
if (is_numeric($value)) {
$item_options = merci_get_reservable_items($type, $start, $end, $reservation_nid);
if (merci_type_setting($type) == 'bucket') {
$total_counts[$type][$type] = $choice_counts[$type][$type] = merci_get_available_bucket_count($type, $start, $end, $node->vid) - $content_settings->merci_spare_items;
}
foreach($item_options as $nid => $item_nid){
$total_counts[$type][$nid] = $choice_counts[$type][$nid] = 1;
}
} else {
$total_counts[$type][$value] = $choice_counts[$type][$value] = merci_get_available_bucket_count($type, $start, $end, $node->vid) - $content_settings->merci_spare_items;
}
}
if (!$choice_counts[$type][$value] and $total_counts[$type][$value]) {
$messages[$did] = '
' . t("You've selected too many %name's. We only have %amount available at the times you've selected.", array('%name' => $title, '%amount' => $total_counts[$type][$value])) . '
';
}
elseif (!$choice_counts[$type][$value]) {
$messages[$did] .= theme('merci_conflict_grid', $type, $title, $start, $end, $value,$node->vid);
}
else {
//drupal_set_message(t('There are no time conflicts with this Reservation.'));
}
$choice_counts[$type][$value] --;
if (is_numeric($value) ) {
$choice_counts[$type][$type] --;
}
// Check item restrictions. max hours, etc.
$restrictions = merci_check_content_type_restrictions($type, $start, $end);
if (!empty($restrictions)) {
foreach ($restrictions as $restriction) {
$messages[$did] .= '
';
}
}
if(!empty($messages[$did])) {
form_set_error("merci_reservation_items][$did][merci_item_nid", $messages[$did]);
}
}
}
return $messages;
}
/**
* 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_existing_items($form, $form_state);
merci_validate_merci_selected_items($form, &$form_state);
//merci_validate_merci_choices($form, $form_state);
}
/**
* Implementation of hook_insert().
*/
function merci_insert($node) {
if ($node->type == 'merci_reservation') {
drupal_write_record("merci_reservation",$node);
merci_add_reservation_items($node);
}
}
/**
* Implementation of hook_update().
*/
function merci_update($node) {
if ($node->type == 'merci_reservation') {
if ($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) {
if ($node->type == 'merci_reservation') {
if ($node->merci_reservation_status == MERCI_STATUS_CHECKED_OUT) {
foreach ($node->merci_reservation_items as $item) {
$update = array(
'nid' => $item['merci_item_nid'],
'merci_item_status' => MERCI_ITEM_STATUS_AVAILABLE,
);
drupal_write_record('merci_reservation_item_node', $update, 'nid');
}
}
// Delete all reservation placeholder nodes for the reservation.
$placeholders = db_query("SELECT DISTINCT(merci_placeholder_nid) AS nid FROM {merci_reservation_detail} WHERE nid = %d", $node->nid);
while ($placeholder = db_result($placeholders)) {
node_delete($placeholder);
}
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 ($node->type == 'merci_reservation' && !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) {
merci_check_default_timezone();
$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' => '
',
'#suffix' => '
',
);
// 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 ($form_state['clicked_button']['#value'] == "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' => $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['#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 = $form['type']['#value'];
switch ($form_id) {
// Node settings form.
case $type . '_node_form':
if (merci_is_merci_type($type)) {
$node = (object) $form['#node'];
$sub_type = $node->merci_sub_type ? $node->merci_sub_type : MERCI_SUB_TYPE_ITEM;
$default_availability = $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,
);
}
$form['merci_sub_type'] = array(
'#type' => 'value',
'#value' => $sub_type,
);
merci_add_settings_form(&$form, $form_state);
} else if ($type == 'merci_reservation') {
$form['#after_build'][] = '_merci_after_build';
}
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'];
// If any nodes have already been created, lock the type setting.
$nodes = db_result(db_query("SELECT COUNT(nid) FROM {node} WHERE type = '%s'",$type));
if ($type) {
$settings = merci_load_item_settings($type);
}
if(empty($settings) and $nodes) {
return;
}
$warning = '
' . t(' WARNING: changing this setting has no effect on existing reserved items.') . '
';
$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 ? $settings->merci_type_setting : 'disabled',
'#description' => filter_xss(theme('item_list', $description_items)),
);
}
$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' => isset($settings->merci_active_status) ? intval($settings->merci_active_status) : MERCI_STATUS_ACTIVE,
'#description' => t('Set to active to allow this type to be reserved.'),
);
// This setting is only valid for buckets.
if (!isset($settings->merci_type_setting) || $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;
}
}
// Loads the current settings for reservable item nodes.
/* If you just want the content type settings just pass only node->type.
*/
function merci_load_item_settings($object) {
if (is_string($object)) {
$type = $object;
} else {
$node = (array) $object;
$type = $node['type'];
}
$item_settings = array();
// Settings from the content type edit page.
$content_settings = merci_content_types($type);
if(empty($content_settings)) {
$content_settings = array();
}
if ($node['nid']) {
// Settings common to all merci item nodes.
// resource or bucket.
$merci_type = $content_settings['merci_type_setting'];
$item_settings = db_fetch_array(db_query("SELECT merci_default_availability, merci_sub_type, merci_item_status FROM {merci_reservation_item_node} WHERE vid = %d", $node['vid']));
switch ($merci_type) {
case 'bucket':
// TODO: move to seperate module.
if($item_settings['merci_sub_type'] == MERCI_SUB_TYPE_RESERVATION) {
unset($item_settings['merci_default_availability']);
unset($item_settings['merci_item_status']);
$item_settings += db_fetch_array(db_query("SELECT merci_late_fee_per_hour, merci_rate_per_hour, merci_fee_free_hours FROM {merci_bucket_node} WHERE vid = %d", $node['vid']));
}
break;
case 'resource':
// TODO: move to seperate module.
$item_settings += db_fetch_array(db_query("SELECT merci_late_fee_per_hour, merci_rate_per_hour, merci_fee_free_hours, merci_min_cancel_hours, merci_autocheckout, merci_autocheckin, merci_selfcheckout FROM {merci_resource_node} WHERE vid = %d", $node['vid']));
break;
}
}
if ($item_settings) {
return (object)($item_settings + $content_settings);
} else {
return (object)($content_settings);
}
}
/**
* Implementation of hook_nodeapi().
*/
function merci_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
// Process active MERCI node types and reservation nodes.
if (merci_is_merci_type($node->type) || $node->type == 'merci_reservation') {
// reservation, bucket or resource.
$type = $node->type == 'merci_reservation' ? 'reservation' : merci_type_setting($node->type);
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 ($type != 'reservation') {
return (array)(merci_load_item_settings($node));
}
break;
case 'validate':
if ($type != 'reservation') {
merci_validate_default_availability($node);
}
break;
case 'insert':
case 'update':
if ($type != 'reservation') {
if ($op == 'insert' || $node->revision) {
drupal_write_record('merci_'.$type.'_node',$node);
drupal_write_record('merci_reservation_item_node',$node);
}
else {
drupal_write_record('merci_'.$type.'_node',$node,'vid');
drupal_write_record('merci_reservation_item_node',$node,'vid');
}
}
break;
case 'delete':
// In the case were a reservation placeholder node is being
// deleted, remove it from the detail table here.
if ($type != 'reservation') {
$node->merci_placeholder_nid = $node->nid;
merci_delete_record('merci_reservation_detail', $node, 'merci_placeholder_nid');
merci_delete_record('merci_' . $type . '_node', $node, 'nid');
merci_delete_record('merci_reservation_item_node',$node,'nid');
}
break;
case 'delete revision':
if ($type != 'reservation') {
merci_delete_record('merci_' . $type . '_node', $node, 'vid');
merci_delete_record('merci_reservation_item_node',$node,'vid');
} else {
merci_delete_record('merci_reservation',$node,'vid');
merci_delete_record('merci_reservation_detail',$node,'vid');
}
break;
}
}
}
/**
* Implementation of hook_theme().
*/
function merci_theme() {
return array(
'merci_choices' => array(
'arguments' => array('form' => NULL),
),
'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_vid' => 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),
),
);
}
/**
* 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_cron().
*/
function merci_cron() {
// 2009-05-22 20:45:00
$time = gmdate('Y-m-j H:i:s');
// 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_start_date = $db_info['columns']['value']['column'];
$column_end_date = $db_info['columns']['value2']['column'];
$args = array($time, $time, MERCI_STATUS_UNCONFIRMED, MERCI_STATUS_PENDING);
// Select reservation nodes where all reserved items and resources are autocheckout.
$reservations = db_query("SELECT n.nid FROM {node} AS n
INNER JOIN {" . $table . "} ct ON ct.vid = n.vid
INNER JOIN {merci_reservation} AS mr ON n.vid = mr.vid
WHERE $column_start_date <= '%s' AND $column_end_date > '%s'
AND mr.merci_reservation_status IN (%d, %d)", $args);
while ($reservation_nid = db_result($reservations)) {
$reservation = node_load($reservation_nid);
//check child items of that reservations for autocheckout
foreach ($reservaton->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");
$node->merci_reservation_status = MERCI_STATUS_CHECKED_OUT;
node_save($node);
}
$args = array($time, MERCI_STATUS_CHECKED_OUT);
// Select reservation nodes where all reserved items and resources are autocheckin.
$reservations = db_query("SELECT n.nid FROM {node} AS n
INNER JOIN {" . $table . "} ct ON ct.vid = n.vid
INNER JOIN {merci_reservation} AS mr ON n.vid = mr.vid
WHERE $column_end_date <= '%s'
AND mr.merci_reservation_status IN (%d)", $args);
while ($reservation_nid = db_result($reservations)) {
//check child items of that reservations for autocheckin
$reservation = node_load($reservation_nid);
foreach ($reservaton->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");
$node->merci_reservation_status = MERCI_STATUS_CHECKED_IN;
node_save($node);
}
// 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
$args = array($time, MERCI_STATUS_PENDING);
// Select reservation nodes where all reserved items and resources are autocheckin.
$reservations = db_query("SELECT n.nid FROM {node} AS n
INNER JOIN {" . $table . "} ct ON ct.vid = n.vid
INNER JOIN { merci_reservation } AS mr ON n.vid = mr.vid
WHERE $column_start_date <= '%s'
AND mr.merci_reservation_status IN (%d)
", $args);
while ($nid = db_result($reservations)) {
watchdog('merci', "Setting node " . $nid . " to no show");
$node = node_load($nid);
$node->merci_reservation_status = MERCI_STATUS_NO_SHOW;
node_save($node);
}
}
/**
* 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_simpletest().
*/
function merci_simpletest() {
$dir = drupal_get_path('module', 'merci') . '/tests';
$tests = file_scan_directory($dir, '\.test$');
return array_keys($tests);
}
/**
* 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_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',
),
'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',
),
),
);
}
/**
* Submit handler to add more choices to a reservation form. This handler is used when
* javascript is not available. It makes changes to the form state and the
* entire form is rebuilt during the page reload.
*/
function merci_more_choices_submit($form, &$form_state) {
// Set the form to rebuild and run submit handlers.
node_form_submit_build_node($form, $form_state);
}
/**
* Submit handler to date filter items on a reservation form.
* It makes changes to the form state and the entire form is
* rebuilt during the page reload.
*/
function merci_date_filter($form, &$form_state) {
// Set the form to rebuild and run submit handlers.
node_form_submit_build_node($form, $form_state);
}
/**
* 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);
// Group the buckets.
$vid = variable_get('merci_equipment_grouping_vid', 0);
$sorted = array();
foreach ($options['options'] as $grouping => $items) {
if(!is_array($items)) {
continue;
}
foreach($items as $id => $name) {
if(is_numeric($id)) {
// Resource
$type = db_result(db_query('SELECT type FROM {node} WHERE nid = %d', $id));
} else {
$type = $id;
}
$tid = variable_get('merci_grouping_'.$type,0);
if ($tid) {
$term = taxonomy_get_term($tid);
if ($term) {
$sorted[$term->name][$id] = $name;
unset ($options['options'][$grouping][$id]);
}
}
}
if(empty($options['options'][$grouping])) {
unset($options['options'][$grouping]);
}
}
// Order them.
$terms = taxonomy_get_tree($vid);
foreach ($terms as $term) {
if(isset($sorted[$term->name])) {
$options['options'][$term->name] = $sorted[$term->name];
}
}
}
$form = array(
'#type' => 'select',
'#options' => $options['options'],
'#default_value' => $default,
//'#parents' => array('choice', $delta, 'item'),
);
return $form;
}
/**
* Builds the list of all currently reservable items, filtered by date.
*
* @param $node
* The reservation node object.
* @param $form_state
* Current form state array.
* @param $reservation_nid
* (Optional) The nid of a reservation to ignore in the options exclusions.
*
* @return
* An associative array with the following key/value pairs:
* 'options' => An array of available items, in the format used
* for the item selector.
*/
function merci_build_reservable_items($node, $form_state, $reservation_nid = NULL) {
// Newly set dates take precedence.
if (isset($form_state['values']['field_merci_date'])) {
$start = $form_state['values']['field_merci_date'][0]['value'];
$end = $form_state['values']['field_merci_date'][0]['value2'];
}
// Dates loaded from the reservation are next.
elseif (isset($node->nid)) {
$date_info = $node->field_merci_date[0];
$start = $date_info['value'];
$end = $date_info['value2'];
}
// New reservation, so no date filtering.
else {
$is_new = TRUE;
}
$options = array();
$options['options'] = array('' => t('