Files
booking/booking.rooms_allocate.inc

512 lines
19 KiB
PHP

<?php
/**
* autocomplete helper to look up names for room allocations
* based on https://www.drupal.org/node/854216
* $string = string for search
*/
function _booking_rooms_name_autocomplete($string) {
global $event;
$matches = array();
$query = db_select('booking_person', 'p')
->fields('p', array('nid', 'booking_firstname', 'booking_lastname'));
$db_or = db_or()
->condition('p.booking_lastname', '%' . db_like($string) . '%', 'LIKE')
->condition('p.booking_firstname', '%' . db_like($string) . '%', 'LIKE')
->condition('p.nid', $string, "=");
$db_and = db_and()->condition($db_or)->condition('p.booking_eventid', $event->eid, '=');
$result = $query->condition($db_and)
->execute();
// save the query to matches
foreach ($result as $row) {
$name = $row->booking_lastname . ', ' . $row->booking_firstname . ' [' . $row->nid . ']';
$matches[$name] = $name;
}
// Return the result to the form in json
drupal_json_output($matches);
}
/**
* Test function for allocating people to rooms with an autocomplete field
* Using a regular table of elements as per https://passingcuriosity.com/2011/drupal-7-forms-tables/
*/
function booking_rooms_allocate_form($node, &$form_state, $location_id) {
global $event;
$form = array();
$attendee_select = array();
$options = array();
$counter = 0;
$prefix = "<p>Enter part of a person's name in the text field and wait for the blue spinning circle to autocomplete with the person's details. Make sure you click on the person from the dropdown list that appears.</p>";
$form['first_para'] = array (
'#type' => 'markup',
'#markup' => $prefix,
);
//verify that $location_id is a number
if (! preg_match('/^[0-9]+$/', $location_id)) {
drupal_set_message("Error: Invalid room location ID '" . $location_id . "' supplied. Unable to allocate rooms.", 'error', FALSE);
drupal_goto('admin/booking/rooms');
return "";
}
//query for existing room allocations
$query = db_select('booking_person', 'p');
$query->leftJoin('booking_room_mapping', 'm', 'm.booking_nodeid = p.nid');
$query->condition('p.booking_eventid', $event->eid, '=');
$query->fields('p', array('booking_lastname', 'booking_firstname'))->fields('m');
$room_mapping = $query->execute()->fetchAllAssoc('booking_nodeid');
//query for room definitions
$room_query = db_query("SELECT r.*, l.* FROM {booking_room_definition} r " .
"INNER JOIN {booking_room_locations} l on r.booking_room_location_id = l.lid " .
"WHERE booking_room_location_id = :lid " .
"ORDER BY CAST(booking_room_number as SIGNED INTEGER) ASC",
array(':lid' => $location_id));
//define the table header
$header = array (
'booking_room_location' => array('data' => t('Room Location'), 'field' => 'booking_room_location_id'),
'booking_room_number' => array('data' => t('Room Number')),
'booking_room_description' => array('data' => t('Room Label')),
'booking_room_ensuite' => array('data' => t('Ensuite?')),
'booking_room_singlebed' => array('data' => t('Single Bed')),
'booking_room_doublebed_p1' => array('data' => t('Double Bed Person 1')),
'booking_room_doublebed_p2' => array('data' => t('Double Bed Person 2')),
'booking_room_queenbed_p1' => array('data' => t('Queen Bed Person 1')),
'booking_room_queenbed_p2' => array('data' => t('Queen Bed Person 2')),
);
//attach the custom css
$form['#attached']['css'] = array(
drupal_get_path('module', 'booking') . '/booking.css',
);
//create the container element for the whole table
$form['rooms'] = array(
'#prefix' => '<div id="rooms">',
'#suffix' => '</div>',
'#tree' => TRUE,
'#theme' => 'table',
'#header' => $header,
'#rows' => array(),
);
//define the default fields in a table row
$default_row = array();
$default_row['booking_room_location'] = "";
$default_row['booking_room_number'] = "";
$default_row['booking_room_description'] = "";
$default_row['booking_room_ensuite'] = "";
$default_row['booking_room_singlebed'] = "";
$default_row['booking_room_doublebed_p1'] = "";
$default_row['booking_room_doublebed_p2'] = "";
$default_row['booking_room_queenbed_p1'] = "";
$default_row['booking_room_queenbed_p2'] = "";
foreach ($room_query as $data) {
//create an array representing the existing bed mappings for this room
$existing_beds = array();
for ($i = 1; $i <= 3; $i++) {
foreach ($room_mapping as $mapping) {
//check that the room id in the mapping table matches the room that we're currently adding to the table
//and also the bed type matches the first dimension in the array
if ($mapping->booking_roomid == $data->rid && $mapping->booking_room_bedtype == $i) {
$existing_beds[$i][] = $mapping->booking_lastname . ', ' . $mapping->booking_firstname . ' [' . $mapping->booking_nodeid . ']';
}
}
}
//create a row that contains just the room location and number and the custom css id for a separating line between the rooms
$new_row = _booking_clone_array($default_row);
//$new_row['booking_room_location'] = _booking_room_location_lookup($data->booking_room_location_id);
$new_row['booking_room_location'] = $data->booking_roomlocation_descrip;
$new_row['booking_room_number'] = $data->booking_room_number;
$new_row['booking_room_description'] = $data->booking_room_description;
$new_row['booking_room_ensuite'] = $data->booking_room_ensuite == 'Y' ? "Yes" : "No";
$form['rooms']['#rows'][$counter++] = array(
'data' => $new_row,
'id' => array("new-group-row"),
);
//create an additional row for each single bed
//remove deprecated pass-by reference as per http://stackoverflow.com/questions/8971261/php-5-4-call-time-pass-by-reference-easy-fix-available
_booking_rooms_allocate_generate_singlebeds($data, $existing_beds, $default_row, $counter, $form);
//create an additional row for each double bed
_booking_rooms_allocate_generate_doublebeds($data, $existing_beds, $default_row, $counter, $form);
//create an additional row for each queen bed
_booking_rooms_allocate_generate_queenbeds($data, $existing_beds, $default_row, $counter, $form);
} //end foreach $room_query
$form['submit'] = array (
'#type' => 'submit',
'#value' => t('Submit'),
);
return array (
'form' => $form,
);
} //end booking_rooms_allocate_test_form
/**
* Process the submission for room assignment
*/
function booking_rooms_allocate_form_submit($form, &$form_state) {
global $event;
$values = $form_state['input'];
//watchdog('booking_debug', "<pre>Room assignment test submission form :\n@info</pre>", array('@info' => print_r( $form, true)));
//query for existing room allocations
$query = db_select('booking_person', 'p');
$query->leftJoin('booking_room_mapping', 'm', 'm.booking_nodeid = p.nid');
$query->condition('p.booking_eventid', $event->eid, '=');
$query->fields('p', array('booking_lastname', 'booking_firstname'))->fields('m');
$room_mapping = $query->execute()->fetchAllAssoc('booking_nodeid');
$bed_inputs = array(
'booking_room_singlebed' => 1,
'booking_room_doublebed_p1' => 2,
'booking_room_doublebed_p2' => 2,
'booking_room_queenbed_p1' => 3,
'booking_room_queenbed_p2' => 3,
);
//go through the different bed types
foreach ($bed_inputs as $type => $type_id)
{
//if this bed type wasn't defined in the form, skip it
if (empty($values[$type]))
continue;
//watchdog('booking_debug', "<pre>Room assignment submission for !type:\n@info</pre>", array('!type' => $type, '@info' => print_r( $values[$type], true)));
//go through each room
foreach($values[$type] as $room => $data)
{
//go through each bed
foreach ($data as $bed_index => $person)
{
//check for previous value
$previous_nid = 0;
$previous_nid = _booking_rooms_allocate_get_previous_value($form, $type, $room, $type . '[' . $room . '][' . $bed_index . ']');
if (! empty($person)) {
//extract nid from $person using regex
if (preg_match('/[\s\w,]+\s\[(\d+)\]/i', $person, $matches)) {
//watchdog('booking_debug', "Processing room assignment for ID !id belonging to person !person ", array('!id' => $matches[1], '!person' => $person));
$nid = $matches[1];
//handle the insert/update/deletes required
_booking_rooms_allocate_form_submit_helper($room_mapping, $room, $type_id, $bed_index, $nid, $previous_nid);
} //parsed node id successfully
} //empty bed check
elseif (empty($person) && $previous_nid > 0)
{
$message = t('Removing person !person previously in room id !room with bed index !index and bed type !type.',
array('!room' => $room, '!index' => $bed_index, '!person' => $previous_nid, '!type' => $type_id));
watchdog('booking', $message);
drupal_set_message($message);
db_delete('booking_room_mapping')
->condition('booking_eventid', $event->eid)
->condition('booking_nodeid', $previous_nid)
->condition('booking_room_bedtype', $type_id)
->condition('booking_roomid', $room)
->execute();
}
else
{
//TODO: Check if this bed used to have someone allocated
//and if so, remove their mapping
}
} //next bed
} //next room
} //next bed type
} //end function
/**
* look through the previous form data and return the matching element
*/
function _booking_rooms_allocate_get_previous_value(&$form, $type, $room, $name) {
foreach($form['form']['rooms']['#rows'] as $key => $value)
{
//watchdog('booking_debug', "<pre>Room assignment checker for type !type in room !room:\n@info</pre>", array('!room' => $room, '!type' => $type, '@info' => print_r( $value, true)));
//return;
if ((!empty($value[$type]['data']['#value'])) && ($value[$type]['data']['#name'] == $name)) {
//watchdog('booking_debug', "Found correct room with room number !num and type !type", array('!num' => $room, '!type' => $type));
//found the correct element, extract the node id
$person = $value[$type]['data']['#value'];
if (preg_match('/[\s\w,]+\s\[(\d+)\]/i', $person, $matches)) {
return $matches[1];
}
}
}
//in case there was no matching value, return an empty string
return 0;
}
/**
* function to update person with correct bed allocation and remove any previous allocation if necessary
*/
function _booking_rooms_allocate_form_submit_helper(&$room_mapping, $room, $type_id, $bed_index, $nid, $previous_nid)
{
global $event;
$message = "";
//remove any person previously defined for this bed that doesn't match what is now defined
if ($previous_nid > 0 && $nid != $previous_nid)
{
$message = t('Bed allocation for room !room and bed index !index has changed. Removing previous !person from this location.',
array('!room' => $room, '!index' => $bed_index, '!person' => $previous_nid));
watchdog('booking', $message);
drupal_set_message($message);
//look for an exact match,
//in case this person has moved to a different bed type during this form submission
db_delete('booking_room_mapping')
->condition('booking_eventid', $event->eid)
->condition('booking_nodeid', $previous_nid)
->condition('booking_room_bedtype', $type_id)
->condition('booking_roomid', $room)
->execute();
}
//if this person didn't previously have a room/bed mapping
if (empty($room_mapping[$nid]))
{
//Validate that there is capacity for the person to be allocated to this room
if (_booking_room_capacity_check($room, $type_id))
{
$message = t('Assigning person id !id to a type !type bed in room id !room.',
array('!id' => $nid, '!room' => $room, '!type' => $type_id));
//double check we haven't already allocated a bed during this submission
$check = db_query("SELECT * FROM {booking_room_mapping} " .
" WHERE booking_eventid = :eid AND booking_roomid = :rid " .
" AND booking_room_bedtype = :type AND booking_nodeid = :nid",
array(':eid' => $event->eid, ':rid' => $room, ':type' => $type_id,
':nid' => $nid,
))->fetchObject();
if (! $check)
{
$result = db_insert('booking_room_mapping')
->fields(array(
'booking_roomid' => $room,
'booking_eventid' => $event->eid,
'booking_nodeid' => $nid,
'booking_room_bedtype' => $type_id,
))
->execute();
}
//this person has already been inserted during this form submission
//so don't add them in twice
else
{
$message .= t(' Except this person already exists.');
}
}
//no capacity available in this room
else
{
$message = t('No capacity to assign person id !id to a type !type bed in room id !room.',
array('!id' => $nid, '!room' => $room, '!type' => $type_id)
);
}
}
//this person previously had a room mapping but to a different room
elseif ((!empty($room_mapping[$nid])) && $room_mapping[$nid]->booking_roomid != $room)
{
$message = t('Changing person id !id from old room !oldroom to new room !room with type !type bed.',
array('!id' => $nid, '!room' => $room, '!type' => $type_id,
'!oldroom' => $room_mapping[$nid]->booking_roomid));
db_update('booking_room_mapping')
->fields(array(
'booking_roomid' => $room,
'booking_room_bedtype' => $type_id,
))
->condition('booking_eventid', $event->eid)
->condition('booking_nodeid', $nid)
->execute();
}
//this person previously had a room mapping but to a different bed type in the same room
elseif ((!empty($room_mapping[$nid])) && $room_mapping[$nid]->booking_room_bedtype != $type_id)
{
$message = t('Changing person id !id in room !room to new bed type type !type .',
array('!id' => $nid, '!room' => $room, '!type' => $type_id,
'!oldroom' => $room_mapping[$nid]->booking_roomid));
db_update('booking_room_mapping')
->fields(array(
'booking_roomid' => $room,
'booking_room_bedtype' => $type_id,
))
->condition('booking_eventid', $event->eid)
->condition('booking_nodeid', $nid)
->execute();
}
//log the result if there was one
if ($message !== "")
{
watchdog('booking', $message);
drupal_set_message($message);
}
}
/**
* function to generate table rows for each single bed defined in this room
*/
function _booking_rooms_allocate_generate_singlebeds($data, $existing_beds, $default_row, &$counter, &$form) {
//create an additional row for each single bed
for ($i = 0; $i < $data->booking_room_singlebeds; $i++)
{
$single_bed = array (
'#id' => 'booking-room-singlebed-' . $data->rid . '-' . $i,
'#type' => 'textfield',
'#title' => 'Name',
'#title_display' => 'invisible',
'#name' => 'booking_room_singlebed[' . $data->rid . '][' . $i . ']',
'#size' => 100,
'#autocomplete_path' => 'booking/rooms/autocomplete',
'#value' => (!empty($existing_beds[1][$i])) ? $existing_beds[1][$i] : '',
'#attributes' => array('style' => array('width:200px')),
);
$form['rooms'][$data->rid][$counter] = array(
'booking-room-singlebed' => &$single_bed,
);
$new_row = _booking_clone_array($default_row);
$new_row['booking_room_singlebed'] = array('data' => &$single_bed);
$form['rooms']['#rows'][$counter] = $new_row;
unset($single_bed);
$counter++;
}
}
/**
* function to generate table rows for each double bed defined in this room
*/
function _booking_rooms_allocate_generate_doublebeds($data, $existing_beds, $default_row, &$counter, &$form) {
//create an additional row for each double bed
//$j is our counter that increments twice as fast as $i to cater for both beds
$j = 0;
for ($i = 0; $i < $data->booking_room_doublebeds; $i++)
{
$double_bed_p1 = array (
'#id' => 'booking-room-doublebed-p1-' . $data->rid . '-' . $i,
'#type' => 'textfield',
'#title' => 'Name',
'#title_display' => 'invisible',
'#name' => 'booking_room_doublebed_p1[' . $data->rid . '][' . $i . ']',
'#size' => 100,
'#autocomplete_path' => 'booking/rooms/autocomplete',
'#value' => (!empty($existing_beds[2][$j])) ? $existing_beds[2][$j++] : '',
'#attributes' => array('style' => array('width:200px')),
);
$double_bed_p2 = array (
'#id' => 'booking-room-doublebed-p2-' . $data->rid . '-' . $i,
'#type' => 'textfield',
'#title' => 'Name',
'#title_display' => 'invisible',
'#name' => 'booking_room_doublebed_p2[' . $data->rid . '][' . $i . ']',
'#size' => 100,
'#autocomplete_path' => 'booking/rooms/autocomplete',
'#value' => (!empty($existing_beds[2][$j])) ? $existing_beds[2][$j++] : '',
'#attributes' => array('style' => array('width:200px')),
);
//include fields for rendering
$form['rooms'][$data->rid][$counter] = array(
'booking-room-doublebed-p1' => &$double_bed_p1,
'booking-room-doublebed-p2' => &$double_bed_p2,
);
//add references to the fields for the row so that theme_table can theme appropriately
$new_row = _booking_clone_array($default_row);
$new_row['booking_room_doublebed_p1'] = array('data' => &$double_bed_p1);
$new_row['booking_room_doublebed_p2'] = array('data' => &$double_bed_p2);
$form['rooms']['#rows'][$counter] = $new_row;
unset($double_bed_p1);
unset($double_bed_p2);
$counter++;
}
}
/**
* function to generate table rows for each queen bed defined in this room
*/
function _booking_rooms_allocate_generate_queenbeds($data, $existing_beds, $default_row, &$counter, &$form) {
//create an additional row for each double bed
//$j is our counter that increments twice as fast as $i to cater for both beds
$j = 0;
for ($i = 0; $i < $data->booking_room_queenbeds; $i++)
{
//calculate the two form elements for queen beds
$queen_bed_p1 = array (
'#id' => 'booking-room-queenbed-p1-' . $data->rid . '-' . $i,
'#type' => 'textfield',
'#title' => 'Name',
'#title_display' => 'invisible',
'#name' => 'booking_room_queenbed_p1[' . $data->rid . '][' . $i . ']',
'#size' => 100,
'#autocomplete_path' => 'booking/rooms/autocomplete',
'#value' => (!empty($existing_beds[3][$j])) ? $existing_beds[3][$j++] : '',
'#attributes' => array('style' => array('width:200px')),
);
$queen_bed_p2 = array (
'#id' => 'booking-room-queenbed-p2-' . $data->rid . '-' . $i,
'#type' => 'textfield',
'#title' => 'Name',
'#title_display' => 'invisible',
'#name' => 'booking_room_queenbed_p2[' . $data->rid . '][' . $i . ']',
'#size' => 100,
'#autocomplete_path' => 'booking/rooms/autocomplete',
'#value' => (!empty($existing_beds[3][$j])) ? $existing_beds[3][$j++] : '',
'#attributes' => array('style' => array('width:200px')),
);
// Include the fields so they'll be rendered and named
// correctly, but they'll be ignored here when rendering as
// we're using #theme => table.
$form['rooms'][$data->rid][$counter] = array(
'booking-room-queenbed-p1' => &$queen_bed_p1,
'booking-room-queenbed-p2' => &$queen_bed_p2,
);
// Now add references to the fields to the rows that
// `theme_table()` will use.
$new_row = _booking_clone_array($default_row);
$new_row['booking_room_queenbed_p1'] = array('data' => &$queen_bed_p1);
$new_row['booking_room_queenbed_p2'] = array('data' => &$queen_bed_p2);
$form['rooms']['#rows'][$counter] = $new_row;
unset($queen_bed_p1);
unset($queen_bed_p2);
$counter++;
}
}