commit 84eda95c2deced1d9b29dac21c152e6c30e92a98 Author: ppound Date: Wed Oct 14 10:55:28 2020 -0300 code as it existed on rooms.library.upei.ca diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9f11b75 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/ diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..d159169 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..dbee10e --- /dev/null +++ b/README.txt @@ -0,0 +1,45 @@ + +-- SUMMARY -- + +Room Reservations is an application for making and managing room reservations. +It enables users to view a calendar of reserved and open rooms by day, and to +make, modify, and cancel room reservations. Confirmation of reservations and +reminders can be sent to multiple parties by email and SMS text message. + +-- REQUIREMENTS -- + +Token module. + +-- INSTALLATION -- + +Install as usual, see http://drupal.org/node/70151 for further information. + +-- CONFIGURATION -- + +Administrators can configure the module to define the following items: + +* application name +* room categories +* rooms +* hours by day when reservations can be made +* reservation policies +* reservation instructions +* SMS wireless network information +* reservation confirmation messages +* reservation reminder messages +* time of day reminders are sent + +Detailed instructions for configuring the module can be found at +http://drupal.org/node/1853318. + +-- CONTACT -- + +Current maintainer: +* Bob Humphrey - http://drupal.org/user/560600 + +-- SPONSOR -- + +This project has been sponsored by: +William Madison Randall Library +University of North Carolina Wilmington +http://library.uncw.edu/ diff --git a/controller/room_reservations.controller.inc b/controller/room_reservations.controller.inc new file mode 100644 index 0000000..0f9555c --- /dev/null +++ b/controller/room_reservations.controller.inc @@ -0,0 +1,43 @@ +uid) { + // User is logged in. + $user_reservations = _room_reservations_user_reservations(); + $count = count($user_reservations); + } + $output .= theme('room_reservations_list', array('user' => $user, 'base_url' => $base_url, 'user_reservations' => $user_reservations, 'count' => $count)); + return $output; +} + +/** + * Display a page showing the reservation system policies. + * + * @return string + * The policies for the reservation system as entered by the administrator + * using the Display Text configuration page. + */ +function room_reservations_policies() { + $output = check_markup(_room_reservations_get_variable('policies')); + return $output; +} + diff --git a/controller/room_reservations_calendar.controller.inc b/controller/room_reservations_calendar.controller.inc new file mode 100644 index 0000000..fe8602e --- /dev/null +++ b/controller/room_reservations_calendar.controller.inc @@ -0,0 +1,345 @@ += $int_first_shift_open) && ($int_time_slot_time < $int_first_shift_close)) || + (($int_time_slot_time >= $int_second_shift_open) && ($int_time_slot_time < $int_second_shift_close))) { + $time_slot['open'] = TRUE; + } + else { + $time_slot['open'] = FALSE; + } + + // if not open ALL day let's limit display to start just before first open shift (or 2nd if only one used) + // this assume you must have 1st shift defined and possible 2nd (i.e. can't define only 2nd shift) + if (!$open_24 && $buffer != 300) { + if ($int_first_shift_open < 9999 && ($hours[$x]['time'] < $int_first_shift_open - $buffer)) { + unset($hours[$x]); + } + else { + $hours[$x] = $time_slot; + } + // and do the same for closing + if (isset($hours[$x])) { + if ($int_second_shift_close < 9999) { + if ($hours[$x]['time'] >= $int_second_shift_close + $buffer) { + unset($hours[$x]); + } + } + elseif ($int_first_shift_close < 9999) { + if ($hours[$x]['time'] >= $int_first_shift_close + $buffer) { + unset($hours[$x]); + } + } + } + } + else { + $hours[$x] = $time_slot; + } + $x++; + } + $all_hours = _room_reservations_hours(); + $times = _room_reservations_times(); + $rooms = _room_reservations_rooms(); + + // Initialize the $reservations array. + $reservations = array(); + foreach ($rooms as $room) { + $room_name = $room['name']; + foreach ($hours as $time_slot) { + $time = $time_slot['time']; + $reservations[$room['nid']][$time] = array( + 'time' => $time, + 'display' => $time_slot['display'], + 'class' => $time_slot['class'], + 'id' => '', + 'booked' => FALSE, + 'start' => FALSE, + 'end' => FALSE, + 'user' => '', + 'name' => '', + ); + } + } + + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_reservation') + ->fieldCondition('reservation_date', 'value', $yyyymmdd . '%', 'like') + ->fieldOrderBy('reservation_time', 'value', 'DESC') + // EFQ respects access so if user does not have access to view a reservation (most likely due to some access module then we will not return a + // reservation for this slot; this means it will not block this slot from being bookable - which is likely worse than being able to + // see the booking title (especially since we have a method to block the title if user really doesn't want anyone to see it) + // so, let's load the reservations as user 1 to make sure we get them all + ->addMetaData('account', user_load(1)); + $result = $query->execute(); + if (isset($result['node'])) { + $result_nids = array_keys($result['node']); + $results = entity_load('node', $result_nids); + foreach ($results as $data) { + $id = $data->nid; + $time = $data->reservation_time[LANGUAGE_NONE][0]['value'];; + $rid = $data->reservation_room[LANGUAGE_NONE][0]['target_id']; + $name = $data->title; + $user_name = $data->name; + $reservations[$rid][$time]['booked'] = TRUE; + $reservations[$rid][$time]['class'] .= ' booked'; + $reservations[$rid][$time]['name'] = $name; + $reservations[$rid][$time]['user_name'] = $user_name; + $reservations[$rid][$time]['id'] = $id; + $reservations[$rid][$time]['series_id'] = isset($data->reservation_series_id[LANGUAGE_NONE][0]['value']) ? $data->reservation_series_id[LANGUAGE_NONE][0]['value'] : ''; + $reservations[$rid][$time]['user'] = $data->uid; + $reservations[$rid][$time]['blocked'] = isset($data->reservation_block_title[LANGUAGE_NONE][0]['value']) ? $data->reservation_block_title[LANGUAGE_NONE][0]['value'] : 0; + + // add rest of slots as booked for the length of this reservation + $length = $data->reservation_length[LANGUAGE_NONE][0]['value'];; + $time_slots = $length / 30; + $reservations[$rid][$time]['slots'] = $time_slots; + $remainder = $length % 30; + if ($remainder > 1) { + $time_slots++; + } + $key = array_search($time, $times); + $x = 2; + while ($x <= $time_slots) { + $key++; + $next_time = $times[$key]; + $reservations[$rid][$next_time]['booked'] = TRUE; + if(isset($reservations[$rid][$next_time]['class'])) { + $reservations[$rid][$next_time]['class'] = $reservations[$rid][$next_time]['class'] . ' booked'; + } + //$reservations[$rid][$next_time]['class'] .= ' booked'; + //$reservations[$rid][$next_time]['class'] = $reservations[$rid][$next_time]['class'] . ' booked'; + $x++; + // unclear how we handle wrapping a reservation to the next day; but reservation time slots can't go passed midnight; i.e. slot 47 + if ($key == 47) { + break; + } + } + + // add in pre/post buffer for setup/takedown (rev 7.x-1.3+) + // - if the slot is part of buffer we add "setup" to class + // - if we don't have admin rights; we also mark it as booked so no one can book in these slots + $category = $categories[$rooms[$rid]['reservations_room_category'][LANGUAGE_NONE][0]['target_id']]; + $preslots = $category['prebuffer'] / 30; + $postslots = $category['postbuffer'] / 30; + $startkey = array_search($reservations[$rid][$time]['time'], $times); + $endkey = $startkey + $time_slots; + $k = $startkey - $preslots; + while ($k < $startkey) { + if (!$reservations[$rid][$times[$k]]['booked']) { + $reservations[$rid][$times[$k]]['class'] .= ' setup'; + } + $reservations[$rid][$times[$k]]['booked'] = user_access('book over buffer') ? $reservations[$rid][$times[$k]]['booked'] : true; + $k++; + } + $k = $endkey; + while ($k < $endkey + $postslots) { + if (!$reservations[$rid][$times[$k]]['booked']) { + $reservations[$rid][$times[$k]]['class'] .= ' setup'; + } + $reservations[$rid][$times[$k]]['booked'] = user_access('book over buffer') ? $reservations[$rid][$times[$k]]['booked'] : true; + $k++; + } + } + } + + // Get any room_reservation records for the previous day that might carry over into the selected day. + // Continue to update the $reservation array with that information. + $previous_day = date('Y-m-d', strtotime("$yyyymmdd -1 days")); + $max_length = user_access('create room reservations extended length') ? variable_get('room_reservations_max_length_extended', 120) : variable_get('room_reservations_max_length_standard', 120); + $lhours = _room_reservations_hours(); + $rhours = array_reverse($lhours); + $slots = ($max_length / 30) - 1; + $late_times = array(); + $search_times = array( + '9999', + '9999', + '9999', + '9999', + '9999', + '9999', + '9999', + ); + for ($j = 0; $j < $slots; $j++) { + $hour = $rhours[$j]; + $late_times[] = $hour['time']; + $search_times[$j] = $hour['time']; + } + $late_times = array_reverse($late_times); + + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_reservation') + ->fieldCondition('reservation_date', 'value', $previous_day . '%', 'like') + ->fieldCondition('reservation_time', 'value', $search_times, 'in') + ->fieldOrderBy('reservation_time', 'value', 'DESC') + // EFQ respects access so let's load the reservations as user 1 to make sure we get them all + ->addMetaData('account', user_load(1)); + $result = $query->execute(); + if (isset($result['node'])) { + $result_nids = array_keys($result['node']); + $results = entity_load('node', $result_nids); + foreach ($results as $data) { + $id = $data->nid; + $time = $data->reservation_time[LANGUAGE_NONE][0]['value']; + $rid = $data->reservation_room[LANGUAGE_NONE][0]['target_id']; + $length = $data->reservation_length[LANGUAGE_NONE][0]['value']; + //$room_name = $data->room; + $name = $data->title; + $user_name = $data->name; + $time_slots = $length / 30; + $remainder = $length % 30; + if ($remainder > 1) { + $time_slots++; + } + for ($j = 0; $j < (($max_length / 30) - 1); $j++) { + if ($late_times[$j] == $time) { + $carry_over_time_slots = ($time_slots + $j - (($max_length / 30) - 1)); + break; + } + } + if ($carry_over_time_slots > 0) { + $reservations[$rid]['0000']['booked'] = TRUE; + $reservations[$rid]['0000']['name'] = $name; + $reservations[$rid]['0000']['user_name'] = $user_name; + $reservations[$rid]['0000']['id'] = $id; + $carry_over_time_slots--; + } + while ($carry_over_time_slots > 0) { + $next_time = $times[$carry_over_time_slots]; + $reservations[$rid][$next_time]['booked'] = TRUE; + $carry_over_time_slots--; + } + } + } + $user_reservations = _room_reservations_user_reservations(); + $output = ''; + $output .= theme('room_reservations', array( + 'dates' => $dates, + 'categories' => $categories, + 'reservations' => $reservations, + 'hours' => $hours, + 'building_hours' => $_room_reservations_building_hours, + 'building_hours_display' => $building_hours_display, + 'rooms' => $rooms, + 'user_reservations' => $user_reservations)); + return $output; +} + +function _room_reservations_earliest_category($categories) { + $earliest = 1000; + foreach ($categories as $cat) { + if ($cat['advmin'] < $earliest) { + $earliest = $cat['advmin']; + $result = $cat['nid']; + } + } + return $result; +} + +function _room_reservations_get_dates() { + // dates are now keyed by Cat ID; but we only need anyone of these to build calendar but make sure its the one which starts earliest + $datesbycat = _room_reservations_dates($selected_month, $selected_day); + $categories = _room_reservations_categories(); + $earliest = _room_reservations_earlist_category($categories); + $dates = $datesbycat[$earliest]; + + // Determine which day has been selected by the user. If the user has entered a url that specifies a day outside of the + // allowable reservation window, then set the current day as the selected day. + $yyyymmdd = $dates[0]['yyyymmdd']; + foreach ($dates as $day) { + if ($day['selected']) { + $yyyymmdd = $day['yyyymmdd']; + } + } + if ($yyyymmdd == $dates[0]['yyyymmdd']) { + $dates[0]['selected'] = TRUE; + $dates[0]['today'] = TRUE; + } + return $dates; +} \ No newline at end of file diff --git a/controller/room_reservations_reservation.controller.inc b/controller/room_reservations_reservation.controller.inc new file mode 100644 index 0000000..e8e1461 --- /dev/null +++ b/controller/room_reservations_reservation.controller.inc @@ -0,0 +1,405 @@ +monthNumber = $month_number; + $res->day = $xday; + $res->time = $time; + $res->room = $xroom; + $parameter_errors = FALSE; + $output = ''; + global $user; + $logged_in = ($user->uid); + $res->userName = ($logged_in) ? $user->name : ""; + $res->emailAddresses = _room_reservations_default_email(); + if (!$logged_in) { + drupal_set_message(t("You must be logged in to make a reservation."), 'error'); + $parameter_errors = TRUE; + } + // Validate date and get day of week, month name, and year. + $dates = _room_reservations_dates(); + $reservation_is_for_today = FALSE; + $day_found = FALSE; + foreach ($dates as $day) { + if (($res->monthNumber == $day['month-number']) && ($res->day == $day['day'])) { + $day_found = TRUE; + $month = $day['month']; + $res->year = $day['year']; + $day_of_week = $day['day-of-week']; + $res->displayDate = $day['display']; + $res->date = $day['yyyymmdd']; + $unix_timestamp = strtotime($res->date); + $res->displayDate = date("l, F j, Y", $unix_timestamp); + if ($day['today']) { + $reservation_is_for_today = TRUE; + } + break; + } + } + if (!$day_found) { + drupal_set_message(t("An invalid date has been entered."), 'error'); + $parameter_errors = TRUE; + } + else { + // Determine if the building is open. + $_room_reservations_building_hours = _room_reservations_facility_hours(); + $building_hours_day = $_room_reservations_building_hours[$res->date]; + if (!$building_hours_day['open']) { + drupal_set_message(t("An invalid date has been entered."), 'error'); + $parameter_errors = TRUE; + } + } + // Validate time. + $hours = _room_reservations_hours(); + $hour_found = FALSE; + foreach ($hours as $time_slot) { + if ($res->time == $time_slot['time']) { + $res->displayTime = $time_slot['display']; + $hour_found = TRUE; + break; + } + } + if (!$hour_found) { + drupal_set_message(t("An invalid time has been entered."), 'error'); + $parameter_errors = TRUE; + } + // Determine if the building is open for the time selected. + if ($day_found) { + $building_is_open = FALSE; + $int_time_slot_time = intval($res->time); + $int_first_shift_open = intval($building_hours_day['first_shift_open']); + $int_first_shift_close = intval($building_hours_day['first_shift_close']); + $int_second_shift_open = intval($building_hours_day['second_shift_open']); + $int_second_shift_close = intval($building_hours_day['second_shift_close']); + $open_24 = $building_hours_day['open_24_hours']; + if ($open_24) { + $building_is_open = TRUE; + } + elseif ((($int_time_slot_time >= $int_first_shift_open) && + ($int_time_slot_time < $int_first_shift_close)) || + (($int_time_slot_time >= $int_second_shift_open) && + ($int_time_slot_time < $int_second_shift_close))) { + $building_is_open = TRUE; + } + else { + $building_is_open = FALSE; + } + if (!$building_is_open) { + drupal_set_message(t("An invalid time has been entered."), 'error'); + $parameter_errors = TRUE; + } + } + // If the reservation is being made for the current date, it must be for a + // time that is later than the current time. + if (!$parameter_errors) { + $reservation_date_time = strtotime($res->date . ' ' . + drupal_substr($res->time, 0, 2) . ':' . + drupal_substr($res->time, 2) . ':00'); + $now = strtotime(date('Y-m-d H:i:s')); + if ($reservation_date_time < $now) { + drupal_set_message(t("A reservation cannot be made for a time that has already passed."), 'error'); + $parameter_errors = TRUE; + } + } + // Validate room. + $rooms = _room_reservations_rooms(); + $room_found = FALSE; + foreach ($rooms as $room) { + if ($res->room == $room['name']) { + $res->roomCapacity = $room['capacity']; + $res->roomCategory = $room['category']; + $room_found = TRUE; + break; + } + } + if (!$room_found) { + drupal_set_message(t("An invalid room has been entered."), 'error'); + $parameter_errors = TRUE; + } + + // Determine if the room and time conflicts with another reservation. + if (!$parameter_errors) { + $conflicts_found = _room_reservations_start_conflicts($res->room, $res->date, $res->time); + if ($conflicts_found) { + $hours = _room_reservations_hours(); + foreach ($hours as $individual_hour) { + if ($individual_hour['time'] == $time) { + $display_time = $individual_hour['display']; + break; + } + } + $unix_timestamp = strtotime($res->date); + $display_date = date("F j, Y", $unix_timestamp); + drupal_set_message(t('Room @room has already been reserved for !date at + !time.', array( + '@room' => $res->room, + '!date' => $display_date, + '!time' => $display_time, + )), 'error'); + $parameter_errors = TRUE; + } + } + // Determine the valid lengths of time for this reservation. + if (!$parameter_errors) { + $res->validLengths = _room_reservations_valid_lengths($res->room, $res->date, $res->time); + } + // Limit the total number of active user reservations. + $user_reservations = _room_reservations_user_reservations(); + $count = count($user_reservations); + $max = variable_get('room_reservations_reservations_per_user', 4); + if ($max) { + if ($count >= $max) { + drupal_set_message(t("You have already made the allowable number of reservations."), 'error'); + $parameter_errors = TRUE; + } + } + // Limit the daily number of user reservations. + if ($res->date) { + if (_room_reservations_daily_max_exceeded($res->date)) { + drupal_set_message(t('You have already made the allowable number of reservations for !date.', array('!date' => $res->displayDate)), 'error'); + $parameter_errors = TRUE; + } + } + // Show either errors or form. + if ($parameter_errors) { + drupal_goto('room_reservations'); + } + else { + $res->length = 0; + $res->name = ''; + $res->groupSize = 0; + $res->id = 0; + $res->textmsg = 0; + $res->carrier = 0; + $res->phone = ''; + $output .= drupal_get_form('room_reservations_res_form', 'add', $res); + } + return $output; +} + +/** + * View an existing reservation. + * + * Determine if the current user has the right to view the requested + * reservation. + * + * @global object $user + * Drupal user object. + * + * @param int $id + * The record key of the requested reservation. + * + * @return string + * A form used for displaying the reservation. + */ +function room_reservations_res_view($id) { + $parameter_errors = FALSE; + $output = ''; + // Current user. + global $user; + $logged_in = ($user->uid); + $user_login_name = ($logged_in) ? $user->name : ""; + if (!$logged_in) { + drupal_set_message(t("You must be logged in to view reservation details.", 'error')); + $parameter_errors = TRUE; + } + // Get the reservation record. + $res = new Reservation($id); + if (!$res->id) { + drupal_set_message(t("A reservation with this record ID could not be found."), 'error'); + $parameter_errors = TRUE; + } + // Format the display date and time; get room capacity. + if ($res->id) { + $res->displayTime = ''; + $res->roomCapacity = 0; + $unix_timestamp = strtotime($res->date); + $res->displayDate = date("l, F j, Y", $unix_timestamp); + $hours = _room_reservations_hours(); + foreach ($hours as $time_slot) { + if ($res->time == $time_slot['time']) { + $res->displayTime = $time_slot['display']; + break; + } + } + $rooms = _room_reservations_rooms(); + foreach ($rooms as $room) { + if ($res->room == $room['name']) { + $res->roomCapacity = $room['capacity']; + $res->roomCategory = $room['category']; + break; + } + } + } + // Determine if the person requesting the record is either the one who made + // the reservation Or an employee that has access to the record. + $current_user_is_owner = FALSE; + if ($res->id) { + if ($res->userName == $user_login_name) { + $current_user_is_owner = TRUE; + } + elseif (_room_reservations_full_access()) { + // Valid user. + } + else { + drupal_set_message(t("You are not allowed to view this reservation record."), 'error'); + $parameter_errors = TRUE; + } + } + // If the reservation is for a future date and/or time, it can be updated by + // the person who made the reservation. + $reservation_can_still_be_updated = FALSE; + if ($res->id) { + $reservation_date_time = strtotime($res->date . ' ' . drupal_substr($res->time, 0, 2) . ':' . drupal_substr($res->time, 2) . ':00'); + $now = strtotime(date('Y-m-d H:i:s')); + if ($reservation_date_time > $now) { + $reservation_can_still_be_updated = TRUE; + } + } + // Set the operation for the form to either 'view' or 'update'. + $operation = 'view'; + if ($res->id) { + if (($current_user_is_owner) && ($reservation_can_still_be_updated)) { + $operation = 'update'; + } + } + $res->validLengths = NULL; + if ((!$parameter_errors) && ($operation == 'update')) { + $res->validLengths = _room_reservations_valid_lengths($res->room, $res->date, $res->time, $res->id); + } + // Show either errors or form. + if ($parameter_errors) { + drupal_goto('room_reservations'); + } + else { + $res->monthNumber = drupal_substr($res->date, 5, 2); + $res->day = drupal_substr($res->date, 8); + $res->year = drupal_substr($res->date, 0, 4); + $res->textmsg = ($res->textmsg == 'Y') ? 1 : 0; + $output .= drupal_get_form('room_reservations_res_form', $operation, $res); + } + return $output; +} + +/** + * Delete a reservation. + * + * Determine if the current user has the right to delete the requested + * reservation. + * + * @global object $user + * Drupal user object. + * + * @param int $id + * The record key of the requested reservation. + * + * @return string + * A form used for deleting the reservation. + */ +function room_reservations_res_delete($id) { + $parameter_errors = FALSE; + $output = ''; + // Current user. + global $user; + $logged_in = ($user->uid); + $user_login_name = ($logged_in) ? $user->name : ""; + if (!$logged_in) { + drupal_set_message(t("You must be logged in to cancel a reservation."), 'error'); + $parameter_errors = TRUE; + } + // Get the reservation record. + $res = new Reservation($id); + if (!$res->id) { + drupal_set_message(t("A reservation with this record ID could not be found."), 'error'); + $parameter_errors = TRUE; + } + // Format the display date and time; get room capacity. + if ($res->id) { + $res->displayTime = ''; + $res->roomCapacity = 0; + $unix_timestamp = strtotime($res->date); + $res->displayDate = date("l, F j, Y", $unix_timestamp); + $hours = _room_reservations_hours(); + foreach ($hours as $time_slot) { + if ($res->time == $time_slot['time']) { + $res->displayTime = $time_slot['display']; + break; + } + } + $rooms = _room_reservations_rooms(); + foreach ($rooms as $room) { + if ($res->room == $room['name']) { + $res->roomCapacity = $room['capacity']; + $res->roomCategory = $room['category']; + break; + } + } + } + // Determine if the person requesting the record is either the one who made + // the reservation or an employee that has access to the record. + $current_user_is_owner = FALSE; + if ($res->id) { + if ($res->userName == $user_login_name) { + $current_user_is_owner = TRUE; + } + else { + drupal_set_message(t("This reservation can only be cancelled by the person who made it."), 'error'); + $parameter_errors = TRUE; + } + } + // If the reservation is for a future date and/or time, it can be deleted by + // the person who made the reservation. + $reservation_can_still_be_updated = FALSE; + if ($res->id) { + $reservation_date_time = strtotime($res->date . ' ' . + drupal_substr($res->time, 0, 2) . ':' . + drupal_substr($res->time, 2) . ':00'); + $now = strtotime(date('Y-m-d H:i:s')); + if ($reservation_date_time > $now) { + $reservation_can_still_be_updated = TRUE; + } + else { + drupal_set_message(t("This reservation can no longer be cancelled."), 'error'); + $parameter_errors = TRUE; + } + } + + // Show either the validation errors or the form. + if ($parameter_errors) { + drupal_goto('room_reservations'); + } + else { + $res->monthNumber = drupal_substr($res->date, 5, 2); + $res->day = drupal_substr($res->date, 8); + $res->year = drupal_substr($res->date, 0, 4); + $output .= drupal_get_form('room_reservations_cancel_form', $res); + } + return $output; +} diff --git a/images/007354-blue-jelly-icon-arrows-arrow-sparkle.png b/images/007354-blue-jelly-icon-arrows-arrow-sparkle.png new file mode 100644 index 0000000..18d4455 Binary files /dev/null and b/images/007354-blue-jelly-icon-arrows-arrow-sparkle.png differ diff --git a/images/Click_here_to_book.png b/images/Click_here_to_book.png new file mode 100644 index 0000000..ac9ed6a Binary files /dev/null and b/images/Click_here_to_book.png differ diff --git a/images/clear.png b/images/clear.png new file mode 100644 index 0000000..8070099 Binary files /dev/null and b/images/clear.png differ diff --git a/room_reservations.admin.inc b/room_reservations.admin.inc new file mode 100644 index 0000000..64c1d1b --- /dev/null +++ b/room_reservations.admin.inc @@ -0,0 +1,1736 @@ + '30', + '60' => '60', + '90' => '90', + '120' => '120', + '150' => '150', + '180' => '180', + '240' => '240', + '360' => '360', + '480' => '480', + '600' => '600', + '720' => '720', + ); + $form['room_reservations_title'] = array( + '#title' => t('Application name'), + '#type' => 'textfield', + '#maxlength' => 50, + '#size' => 50, + '#description' => t('The name of the room reservation application displayed on the menu, breadcrumbs, and heading of the reservation + calendar page.'), + '#default_value' => variable_get('room_reservations_title', 'Room Reservations'), + ); + $form['room_reservations_advance_standard'] = array( + '#title' => t('Days in advance (standard)'), + '#type' => 'select', + '#options' => array(7 => 7, 14 => 14, 30 => 30, 60 => 60, 90 => 90), + '#description' => t('The number of days in advance that a reservation can be made by a standard user. Default is 14. Maximum is 90.'), + '#default_value' => variable_get('room_reservations_advance_standard', 14), + ); + $form['room_reservations_advance_extended'] = array( + '#title' => t('Days in advance (extended)'), + '#type' => 'select', + '#options' => array(30 => 30, 60 => 60, 90 => 90, 180 => 180, 360 => 360), + '#description' => t('The number of days in advance that a reservation can be made by an admin. Default is 180. Maximum is 360.'), + '#default_value' => variable_get('room_reservations_advance_extended', 180), + ); + $form['room_reservations_reservations_per_user'] = array( + '#title' => t('Open reservations per user'), + '#type' => 'textfield', + '#maxlength' => 2, + '#size' => 2, + '#description' => t('The maximum number of reservations that one particular user can have open at any time. Default is 4. Enter 0 to indicate + that there is no maximum limit.'), + '#default_value' => variable_get('room_reservations_reservations_per_user', 4), + ); + $form['room_reservations_reservations_per_day'] = array( + '#title' => t('Reservations per day'), + '#type' => 'textfield', + '#maxlength' => 2, + '#size' => 2, + '#description' => t('The maximum number of reservations that one particular user can make for a single day. Default is 1. Enter 0 to indicate + that there is no maximum limit.'), + '#default_value' => variable_get('room_reservations_reservations_per_day', 1), + ); + $form['room_reservations_max_length_standard'] = array( + '#title' => t('Maximum reservation length (standard)'), + '#type' => 'select', + '#options' => $options, + '#default_value' => variable_get('room_reservations_max_length_standard', 120), + '#description' => t('The maximum amount of time, in minutes, for which a room can be reserved by a standard user. Default is 120.'), + ); + $form['room_reservations_max_length_extended'] = array( + '#title' => t('Maximum reservation length (extended)'), + '#type' => 'select', + '#options' => $options, + '#default_value' => variable_get('room_reservations_max_length_extended', 120), + '#description' => t('The maximum amount of time, in minutes, for which a room can be reserved by an admin. Default is 120.'), + ); + $form['room_reservations_end_early'] = array( + '#title' => t('End reservations 15 minutes before closing time.'), + '#type' => 'checkbox', + '#return_value' => 1, + '#default_value' => variable_get('room_reservations_end_early', 0), + '#description' => t('All reservations end 15 minutes before closing time.'), + ); + $form['room_reservations_before_after_hours'] = array( + '#title' => t('Time to show on calendar before/after open slots'), + '#type' => 'select', + '#options' => array(0 => '0 hours', 1 => t('1 hour'), 2 => t('2 hours'), '3' => t('All day')), + '#default_value' => variable_get('room_reservations_before_after_hours', 3), + '#description' => t('The number of hours before the first and last open slots for the day that are shown on the calendar. The default is to display the entire day.'), + ); + $form['room_reservations_calendar_flipped'] = array( + '#title' => t('Flip orientation of calendar display.'), + '#type' => 'checkbox', + '#return_value' => 1, + '#default_value' => variable_get('room_reservations_calendar_flipped', 0), + '#description' => t('The default orientation of the calendar display has rooms listed along the top and hours along the left side. Selecting this checkbox will + flip this to display hours along the top and the rooms listed along the left side. This can be useful for categories with a very large number of rooms. NOTE: + custom CSS will most likely be required to allow all the hour slots to fit the width of your theme.'), + ); + $form['room_reservations_compressed_labels'] = array( + '#title' => t('Use smaller labels on calendar display.'), + '#type' => 'checkbox', + '#return_value' => 1, + '#default_value' => variable_get('room_reservations_compressed_labels', 0), + '#description' => t('The default display shows the room title and room capacity on both sides of the calendar display. Selecting this option shows only the room title on one side.'), + ); + + $form['room_reservations_date_displays'] = array( + '#type' => 'fieldset', + '#title' => t('Date and time display formats'), + '#collapsible' => true, + '#collapsed' => true, + ); + $form['room_reservations_date_displays']['room_reservations_picker_format'] = array( + '#type' => 'textfield', + '#title' => t('Date format for the popup date picker to select calendar date.'), + '#description' => t('Enter format in the form "y/m/d", "m/d/y", etc.'), + '#default_value' => variable_get('room_reservations_picker_format', 'y/m/d'), + ); + $options = array(0 => '1:00 PM', 1 => '13:00'); + $form['room_reservations_date_displays']['room_reservations_hour_format'] = array( + '#type' => 'radios', + '#title' => t('Hour format for the calendar display.'), + '#description' => t('Select either 2:00 PM or 14:00 display format.'), + '#default_value' => variable_get('room_reservations_hour_format', 0), + '#options' => $options, + ); + + + /* + $form['room_reservations_group_size'] = array( + '#title' => t('Include group size.'), + '#type' => 'checkbox', + '#return_value' => 1, + '#default_value' => variable_get('room_reservations_group_size', 0), + '#description' => t('Show the group size field on the reservation form + and require users to enter this information.'), + '#weight' => -40, + ); + */ + + return system_settings_form($form); +} + +/** + * Form validation for the Settings / General page. + */ +function room_reservations_admin_settings_validate($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + // Open reservations per user. + $data = $form_state['values']['room_reservations_reservations_per_user']; + if (!ctype_digit($data)) { + $field = 'room_reservations_reservations_per_user'; + $message = t('Open reservations per user must be numeric.'); + form_set_error($field, $message); + } + // Reservations per day. + $data = $form_state['values']['room_reservations_reservations_per_day']; + if (!ctype_digit($data)) { + $field = 'room_reservations_reservations_per_day'; + $message = t('Reservations per day must be numeric.'); + form_set_error($field, $message); + } + } +} + +/** + * Form constructor for the Settings / Default Email Address config page. + */ +function room_reservations_admin_settings_default_email($form, &$form_state) { + $options = array( + '0' => t('Do not set a default email address.'), + '1' => t('Set the default email address equal to the user ID.'), + '2' => t('Set the default email address equal to the user ID plus the domain name entered below.'), + ); + $default_option = _room_reservations_get_variable('default_email_address'); + $default_domain = _room_reservations_get_variable('default_email_domain'); + $form['options'] = array( + '#title' => t('Options'), + '#type' => 'radios', + '#options' => $options, + '#description' => t('Options for including a default email address + in the Reminders - Email Addresses field on the reservation form.'), + '#default_value' => $default_option, + '#weight' => -100, + ); + $form['domain'] = array( + '#title' => t('Domain Name'), + '#type' => 'textfield', + '#description' => t('The domain name that is added to the user + name to create the default email address on the reservation form. + Required if the third option above is selected.'), + '#default_value' => $default_domain, + '#maxlength' => 80, + '#size' => 80, + '#weight' => -90, + ); + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + '#weight' => 100, + ); + $form['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset to defaults'), + '#weight' => 101, + ); + + return $form; +} + +/** + * Form validation for the Settings / Default Email Address config page. + */ +function room_reservations_admin_settings_default_email_validate( + $form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $option = $form_state['values']['options']; + $domain = trim($form_state['values']['domain']); + if (($option == 2) && (!$domain)) { + $field = 'domain'; + $message = t('A domain name must be entered when this option is selected.'); + form_set_error($field, $message); + } + } +} + +/** + * Form submission for the Settings / Default Email Address config page. + */ +function room_reservations_admin_settings_default_email_submit($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $option = $form_state['values']['options']; + $domain = trim($form_state['values']['domain']); + $confirmation = ROOM_RESERVATIONS_SAVE_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_SAVE_ERROR_MSG; + } + elseif ($form_state['clicked_button']['#value'] == t('Reset to defaults')) { + $option = 0; + $domain = ''; + $confirmation = ROOM_RESERVATIONS_RESET_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_RESET_ERROR_MSG; + } + $errors = FALSE; + $result = _room_reservations_set_variable('default_email_address', $option); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('default_email_domain', $domain); + if (!$result) { + $errors = TRUE; + } + if ($errors) { + drupal_set_message($error, 'error'); + } + else { + drupal_set_message($confirmation); + } +} + +/** + * Form constructor for the Settings / Reminders configuration page. + */ +function room_reservations_admin_settings_reminders($form, &$form_state) { + $default_send_reminders = _room_reservations_get_variable('send_reminders'); + $default_reminder_time = _room_reservations_get_variable('reminder_time'); + $default_reminder_cutoff = _room_reservations_get_variable('reminder_cutoff'); + $options = array(); + $options['1300'] = t('1:00 PM'); + $options['1400'] = t('2:00 PM'); + $options['1500'] = t('3:00 PM'); + $options['1600'] = t('4:00 PM'); + $options['1700'] = t('5:00 PM'); + $options['1800'] = t('6:00 PM'); + $options['1900'] = t('7:00 PM'); + $options['2000'] = t('8:00 PM'); + $options['2100'] = t('9:00 PM'); + $options['2200'] = t('10:00 PM'); + $options['2300'] = t('11:00 PM'); + $form['send_reminders'] = array( + '#title' => t('Send reminders.'), + '#type' => 'checkbox', + '#return_value' => 1, + '#default_value' => $default_send_reminders, + '#description' => t('Send reservation reminders.'), + '#weight' => -100, + ); + $form['reminder_time'] = array( + '#title' => t('Send reminders daily at'), + '#type' => 'select', + '#description' => t('The time each day when reminders will be sent + for the next day\'s reservations. Note that a cron job must be + set up to run at this time.'), + '#options' => $options, + '#default_value' => $default_reminder_time, + '#multiple' => FALSE, + '#weight' => -90, + ); + $form['reminder_cutoff'] = array( + '#title' => t('Send reminders for reservations created before'), + '#type' => 'select', + '#description' => t('For reservations created on the same day + that the reminder is sent, only send a reminder if the + reservation was created before this time. This is set so that + patrons are not sent a reminder immediately after creating + a reservation.'), + '#options' => $options, + '#default_value' => $default_reminder_cutoff, + '#multiple' => FALSE, + '#weight' => -80, + ); + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + '#weight' => 100, + ); + $form['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset to defaults'), + '#weight' => 101, + ); + return $form; +} + +/** + * Form submission for the Settings / Reminders configuration page. + */ +function room_reservations_admin_settings_reminders_submit($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $send_reminders = $form_state['values']['send_reminders']; + $reminder_time = $form_state['values']['reminder_time']; + $reminder_cutoff = $form_state['values']['reminder_cutoff']; + $confirmation = ROOM_RESERVATIONS_SAVE_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_SAVE_ERROR_MSG; + } + elseif ($form_state['clicked_button']['#value'] == t('Reset to defaults')) { + $send_reminders = 0; + $reminder_time = 1300; + $reminder_cutoff = 1300; + $confirmation = ROOM_RESERVATIONS_RESET_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_RESET_ERROR_MSG; + } + $errors = FALSE; + $result = _room_reservations_set_variable('send_reminders', $send_reminders); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('reminder_time', $reminder_time); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('reminder_cutoff', + $reminder_cutoff); + if (!$result) { + $errors = TRUE; + } + if ($errors) { + drupal_set_message($error, 'error'); + } + else { + drupal_set_message($confirmation); + } +} + +/** + * Form validation for the Settings / Reminders configuration page. + */ +function room_reservations_admin_settings_reminders_validate($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $reminder_time = $form_state['values']['reminder_time']; + $reminder_cutoff = $form_state['values']['reminder_cutoff']; + if ($reminder_time < $reminder_cutoff) { + $field = 'reminder_time'; + $message = t("'Send reminder daily at' time cannot be earlier than 'Send reminders for reservations created before' time."); + form_set_error($field, $message); + } + } +} + +/** + * Form constructor for the Hours / Default Hours configuration page. + */ +function room_reservations_admin_settings_default_hours($form, &$form_state) { + $hours = _room_reservations_hours(); + // Select box options. + $options = array(); + $options['9999'] = t('Select the time'); + $options['0000'] = t('Midnight - start of day'); + foreach ($hours as $hour) { + $time = $hour['time']; + $display = t($hour['display']); + if ($time != '0000') { + $options[$time] = $display; + } + } + $options['2400'] = t('Midnight - end of day'); + // Get saved default hours. + $default_hours + = unserialize(_room_reservations_get_variable('default_hours')); + if (!$default_hours) { + for ($x = 0; $x < 28; $x++) { + $default_hours[$x] = '9999'; + } + } + $days = array( + t('Sunday'), + t('Monday'), + t('Tuesday'), + t('Wednesday'), + t('Thursday'), + t('Friday'), + t('Saturday'), + ); + $form['#tree'] = TRUE; + for ($day = 0; $day < 7; $day++) { + $day_hours = array(); + $day_hours[] = $default_hours[($day * 4)]; + $day_hours[] = $default_hours[($day * 4) + 1]; + $day_hours[] = $default_hours[($day * 4) + 2]; + $day_hours[] = $default_hours[($day * 4) + 3]; + $display_hours = _room_reservations_hours_display($day_hours); + $form['day_' . $day] = array( + '#type' => 'fieldset', + '#title' => $days[$day] . ' (' . $display_hours . ')', + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => -90 + ($day * 10), + ); + $form['day_' . $day]['first_shift_open_' . $day] = array( + '#title' => t('First shift open'), + '#type' => 'select', + '#options' => $options, + '#default_value' => $default_hours[($day * 4)], + '#weight' => -90 + ($day * 10) + 1, + ); + $form['day_' . $day]['first_shift_close_' . $day] = array( + '#title' => t('First shift close'), + '#type' => 'select', + '#options' => $options, + '#default_value' => $default_hours[($day * 4) + 1], + '#weight' => -90 + ($day * 10) + 2, + ); + $form['day_' . $day]['second_shift_open_' . $day] = array( + '#title' => t('Second shift open'), + '#type' => 'select', + '#options' => $options, + '#default_value' => $default_hours[($day * 4) + 2], + '#weight' => -90 + ($day * 10) + 3, + ); + $form['day_' . $day]['second_shift_close_' . $day] = array( + '#title' => t('Second shift close'), + '#type' => 'select', + '#options' => $options, + '#default_value' => $default_hours[($day * 4) + 3], + '#weight' => -90 + ($day * 10) + 4, + ); + } + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + '#weight' => 100, + ); + $form['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset to defaults'), + '#weight' => 101, + ); + return $form; +} + +/** + * Form validation for the Hours / Default Hours configuration page. + */ +function room_reservations_admin_settings_default_hours_validate($form_id, &$form_state) { + $days = array( + t('Sunday'), + t('Monday'), + t('Tuesday'), + t('Wednesday'), + t('Thursday'), + t('Friday'), + t('Saturday'), + ); + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + for ($day = 0; $day < 7; $day++) { + $open = TRUE; + $second_shift = FALSE; + $first_shift_open = $form_state['values']['day_' . $day]['first_shift_open_' . $day]; + $first_shift_close = $form_state['values']['day_' . $day]['first_shift_close_' . $day]; + $second_shift_open = $form_state['values']['day_' . $day]['second_shift_open_' . $day]; + $second_shift_close = $form_state['values']['day_' . $day]['second_shift_close_' . $day]; + $int_first_shift_open = intval($form_state['values']['day_' . $day]['first_shift_open_' . $day]); + $int_first_shift_close = intval($form_state['values']['day_' . $day]['first_shift_close_' . $day]); + $int_second_shift_open = intval($form_state['values']['day_' . $day]['second_shift_open_' . $day]); + $int_second_shift_close = intval($form_state['values']['day_' . $day]['second_shift_close_' . $day]); + // Closed. + if (($int_first_shift_open == 9999) && ($int_first_shift_close == 9999) && ($int_second_shift_open == 9999) && ($int_second_shift_close == 9999)) { + $open = FALSE; + } + // First shift. + if ($open) { + if ($int_first_shift_open == 9999) { + $field = 'day_' . $day . '][first_shift_open_' . $day; + $message = t('!day - First shift open is required.', array('!day' => $days[$day])); + form_set_error($field, $message); + } + elseif ($int_first_shift_close == 9999) { + $field = 'day_' . $day . '][first_shift_close_' . $day; + $message = t('!day - First shift close is required.', array('!day' => $days[$day])); + form_set_error($field, $message); + } + elseif ($int_first_shift_open >= $int_first_shift_close) { + $field = 'day_' . $day . '][first_shift_close_' . $day; + $message = t('!day - First shift close must be later than first shift open.', array('!day' => $days[$day])); + form_set_error($field, $message); + } + } + // Second shift. + if ($open) { + if (($int_second_shift_open != 9999) || ($int_second_shift_close != 9999)) { + $second_shift = TRUE; + } + } + if ($second_shift) { + if (($int_first_shift_open == 9999) && ($int_first_shift_close == 9999)) { + $field = 'day_' . $day . '][second_shift_open_' . $day; + $message = t('!day - Cannot have a second shift without a first shift.', array('!day' => $days[$day])); + form_set_error($field, $message); + } + elseif ($int_second_shift_open == 9999) { + $field = 'day_' . $day . '][second_shift_open_' . $day; + $message = t('!day - Second shift open is missing.', array('!day' => $days[$day])); + form_set_error($field, $message); + } + elseif ($int_second_shift_close == 9999) { + $field = 'day_' . $day . '][second_shift_close_' . $day; + $message = t('!day - Second shift close is missing.', array('!day' => $days[$day])); + form_set_error($field, $message); + } + elseif ($int_second_shift_open <= $int_first_shift_close) { + $field = 'day_' . $day . '][second_shift_open_' . $day; + $message = t('!day - Second shift open must be later than first shift close.', array('!day' => $days[$day])); + form_set_error($field, $message); + } + elseif ($int_second_shift_open >= $int_second_shift_close) { + $field = 'day_' . $day . '][second_shift_close_' . $day; + $message = t('!day - Second shift close must be later than second shift opten.', array('!day' => $days[$day])); + form_set_error($field, $message); + } + } + } + } +} + +/** + * Form submission for the Hours / Default Hours configuration page. + */ +function room_reservations_admin_settings_default_hours_submit($form_id, &$form_state) { + $default_hours = array(); + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + for ($day = 0; $day < 7; $day++) { + $default_hours[] = $form_state['values']['day_' . $day]['first_shift_open_' . $day]; + $default_hours[] = $form_state['values']['day_' . $day]['first_shift_close_' . $day]; + $default_hours[] = $form_state['values']['day_' . $day]['second_shift_open_' . $day]; + $default_hours[] = $form_state['values']['day_' . $day]['second_shift_close_' . $day]; + } + $confirmation = ROOM_RESERVATIONS_SAVE_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_SAVE_ERROR_MSG; + } + if ($form_state['clicked_button']['#value'] == t('Reset to defaults')) { + for ($day = 0; $day < 7; $day++) { + $default_hours[] = '9999'; + $default_hours[] = '9999'; + $default_hours[] = '9999'; + $default_hours[] = '9999'; + } + $confirmation = ROOM_RESERVATIONS_RESET_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_RESET_ERROR_MSG; + } + $errors = FALSE; + $result = _room_reservations_set_variable('default_hours', serialize($default_hours)); + if (!$result) { + $errors = TRUE; + } + else { + + } + // Update monthly hours records. + $sql = "SELECT * FROM {room_reservations_variables} WHERE name LIKE 'monthly_hours_%'"; + $results = db_query($sql); + foreach ($results as $data){ + $name = $data->name; + $month = intval(drupal_substr($name, 19)); + $year = drupal_substr($name, 14, 4); + $mo_hours = unserialize($data->value); + // Days in the month. + $days = date('t', mktime(0, 0, 0, $month, 1, (int) $year)); + $upd_mo_hours = array(); + for ($day = 0; $day < $days; $day++) { + $default_ind = $mo_hours[($day * 5)]; + if ($default_ind == 'D') { + // Update monthly hours with default day of week hours. + // Day of the week. + $dow = date('w', mktime(0, 0, 0, $month, $day + 1, (int) $year)); + $upd_mo_hours[($day * 5)] = 'D'; + $upd_mo_hours[($day * 5) + 1] = $default_hours[($dow * 4)]; + $upd_mo_hours[($day * 5) + 2] = $default_hours[($dow * 4) + 1]; + $upd_mo_hours[($day * 5) + 3] = $default_hours[($dow * 4) + 2]; + $upd_mo_hours[($day * 5) + 4] = $default_hours[($dow * 4) + 3]; + } + elseif ($default_ind == 'O') { + // Leave monthly hours unchanged. + $upd_mo_hours[($day * 5)] = 'O'; + $upd_mo_hours[($day * 5) + 1] = $mo_hours[($day * 5) + 1]; + $upd_mo_hours[($day * 5) + 2] = $mo_hours[($day * 5) + 2]; + $upd_mo_hours[($day * 5) + 3] = $mo_hours[($day * 5) + 3]; + $upd_mo_hours[($day * 5) + 4] = $mo_hours[($day * 5) + 4]; + } + } + $result2 = _room_reservations_set_variable($name, + serialize($upd_mo_hours)); + if (!$result2) { + $errors = TRUE; + } + } + if ($errors) { + drupal_set_message($error, 'error'); + } + else { + drupal_set_message($confirmation); + } +} + +/** + * Form constructor for the Hours / Daily Hours configuration page. + */ +function room_reservations_admin_settings_daily_hours($form, &$form_state, $selected_month = NULL) { + $cur_months = _room_reservations_current_months(); + + if (!$selected_month) { + $selected_month = date('Y_m'); + } + $selected_month = isset($form_state['input']['month']) ? $form_state['input']['month'] : $selected_month; + + // create a form to pick a month. + $month_options = array(); + $first = current($cur_months); + foreach ($cur_months as $cur_month) { + $yyyy_mm = $cur_month['YYYY_MM']; + $display = t($cur_month['display']); + $month_options[$yyyy_mm] = t($cur_month['display']); + } + + // Form. + $form['select_month'] = array( + '#type' => 'container', + '#weight' => -2000, + ); + $form['select_month']['month'] = array( + '#title' => t('Month'), + '#type' => 'select', + '#options' => $month_options, + '#default_value' => $selected_month ? $selected_month : $first['YYYY_MM'], + ); + $form['select_month']['save'] = array( + '#type' => 'submit', + '#value' => t('Select a month'), + ); + + // and if no month has been selected; just return this form + if (!$selected_month) { + return $form; + } + + // If the month has been selected, return a form to update the hours for each day of that month. + $hours = _room_reservations_hours(); + // Select box options. + $options = array(); + $options['9999'] = t('Select the time'); + $options['0000'] = t('Midnight - start of day'); + foreach ($hours as $hour) { + $time = $hour['time']; + $display = t($hour['display']); + if ($time != '0000') { + $options[$time] = $display; + } + } + $options['2400'] = t('Midnight - end of day'); + + $yyyy_mm = $selected_month; + $month = intval(drupal_substr($yyyy_mm, 5)); + $year = drupal_substr($yyyy_mm, 0, 4); + $month_display = date('F Y', mktime(0, 0, 0, $month, 1, $year)); + // Days in the month. + $days = date('t', mktime(0, 0, 0, $month, 1, $year)); + if (!$mo_hours = unserialize(_room_reservations_get_variable('monthly_hours_' . $yyyy_mm))) { + $mo_hours = _room_reservations_create_mo_hours($year, $month, $yyyy_mm, true); + } + // Form. + $form['#tree'] = TRUE; + $form['month_display'] = array( + '#prefix' => '', + '#suffix' => '
', + '#markup' => $month_display, + '#weight' => -100, + ); + $form['note'] = array( + '#markup' => t('Asterisk (*) indicates that the hours have been changed from the default'), + '#weight' => -99, + ); + for ($day = 0; $day < $days; $day++) { + // Day of week. + $dow = date('l', mktime(0, 0, 0, $month, $day + 1, $year)); + $changed_hours = ($mo_hours[($day * 5)] == 'O') ? '*' : ''; + $day_hours = array(); + $day_hours[] = $mo_hours[($day * 5) + 1]; + $day_hours[] = $mo_hours[($day * 5) + 2]; + $day_hours[] = $mo_hours[($day * 5) + 3]; + $day_hours[] = $mo_hours[($day * 5) + 4]; + $display_hours = _room_reservations_hours_display($day_hours); + $title = ($day + 1) . ' ' . $dow . ' (' . $display_hours . ') ' . $changed_hours; + $form['day_' . $day] = array( + '#type' => 'fieldset', + '#title' => $title, + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => -9 + ($day * 2), + ); + $form['day_' . $day]['first_shift_open_' . $day] = array( + '#title' => t('First shift open'), + '#type' => 'select', + '#options' => $options, + '#default_value' => $mo_hours[($day * 5) + 1], + //'#weight' => -9 + ($day * 2) + 1, + ); + $form['day_' . $day]['first_shift_close_' . $day] = array( + '#title' => t('First shift close'), + '#type' => 'select', + '#options' => $options, + '#default_value' => $mo_hours[($day * 5) + 2], + //'#weight' => -9 + ($day * 10) + 2, + ); + $form['day_' . $day]['second_shift_open_' . $day] = array( + '#title' => t('Second shift open'), + '#type' => 'select', + '#options' => $options, + '#default_value' => $mo_hours[($day * 5) + 3], + //'#weight' => -90 + ($day * 10) + 3, + ); + $form['day_' . $day]['second_shift_close_' . $day] = array( + '#title' => t('Second shift close'), + '#type' => 'select', + '#options' => $options, + '#default_value' => $mo_hours[($day * 5) + 4], + //'#weight' => -90 + ($day * 10) + 4, + ); + } + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + '#weight' => 400, + ); + $form['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset to defaults'), + '#weight' => 401, + ); + $form['month'] = array( + '#type' => 'value', + '#value' => $selected_month, + ); + + return $form; +} + +/** + * Form validation for the Hours / Daily Hours configuration page. + */ +function room_reservations_admin_settings_daily_hours_validate($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $month_value = $form_state['values']['month']; + $month = intval(drupal_substr($month_value, 5)); + $year = drupal_substr($month_value, 0, 4); + // Days in the month. + $days = date('t', mktime(0, 0, 0, $month, 1, $year)); + for ($day = 0; $day < $days; $day++) { + // Day of the week. + $dow = date('l', mktime(0, 0, 0, $month, $day + 1, $year)); + // Day of month. + $dom = $day + 1; + $open = TRUE; + $second_shift = FALSE; + $first_shift_open = $form_state['values']['day_' . $day]['first_shift_open_' . $day]; + $first_shift_close = $form_state['values']['day_' . $day]['first_shift_close_' . $day]; + $second_shift_open = $form_state['values']['day_' . $day]['second_shift_open_' . $day]; + $second_shift_close = $form_state['values']['day_' . $day]['second_shift_close_' . $day]; + $int_first_shift_open = intval($form_state['values']['day_' . $day]['first_shift_open_' . $day]); + $int_first_shift_close = intval($form_state['values']['day_' . $day]['first_shift_close_' . $day]); + $int_second_shift_open = intval($form_state['values']['day_' . $day]['second_shift_open_' . $day]); + $int_second_shift_close = intval($form_state['values']['day_' . $day]['second_shift_close_' . $day]); + // Closed. + if (($int_first_shift_open == 9999) && ($int_first_shift_close == 9999) && ($int_second_shift_open == 9999) && ($int_second_shift_close == 9999)) { + $open = FALSE; + } + // First shift. + if ($open) { + if ($int_first_shift_open == 9999) { + $field = 'day_' . $day . '][first_shift_open_' . $day; + $message = t('!day - First shift open is required.', array('!day' => $dom . ' ' . $dow)); + form_set_error($field, $message); + } + elseif ($int_first_shift_close == 9999) { + $field = 'day_' . $day . '][first_shift_close_' . $day; + $message = t('!day - First shift close is required.', array('!day' => $dom . ' ' . $dow)); + form_set_error($field, $message); + } + elseif ($int_first_shift_open >= $int_first_shift_close) { + $field = 'day_' . $day . '][first_shift_close_' . $day; + $message = t('!day - First shift close must be later than first shift open.', array('!day' => $dom . ' ' . $dow)); + form_set_error($field, $message); + } + } + // Second shift. + if ($open) { + if (($int_second_shift_open != 9999) || ($int_second_shift_close != 9999)) { + $second_shift = TRUE; + } + } + if ($second_shift) { + if (($int_first_shift_open == 9999) && ($int_first_shift_close == 9999)) { + $field = 'day_' . $day . '][second_shift_open_' . $day; + $message = t('!day - Cannot have a second shift without a first shift.', array('!day' => $dom . ' ' . $dow)); + form_set_error($field, $message); + } + elseif ($int_second_shift_open == 9999) { + $field = 'day_' . $day . '][second_shift_open_' . $day; + $message = t('!day - Second shift open is missing.', array('!day' => $dom . ' ' . $dow)); + form_set_error($field, $message); + } + elseif ($int_second_shift_close == 9999) { + $field = 'day_' . $day . '][second_shift_close_' . $day; + $message = t('!day - Second shift close is missing.', array('!day' => $dom . ' ' . $dow)); + form_set_error($field, $message); + } + elseif ($int_second_shift_open <= $int_first_shift_close) { + $field = 'day_' . $day . '][second_shift_open_' . $day; + $message = t('!day - Second shift open must be later than first shift close.', array('!day' => $dom . ' ' . $dow)); + form_set_error($field, $message); + } + elseif ($int_second_shift_open >= $int_second_shift_close) { + $field = 'day_' . $day . '][second_shift_close_' . $day; + $message = t('!day - Second shift close must be later than second shift opten.', array('!day' => $dom . ' ' . $dow)); + form_set_error($field, $message); + } + } + } + } +} + +/** + * Form submission for the Hours / Daily Hours configuration page. + */ +function room_reservations_admin_settings_daily_hours_submit($form_id, &$form_state) { + // Select month. + if ($form_state['clicked_button']['#value'] == t('Select a month')) { + $month = $form_state['input']['select_month']['month'] ? $form_state['input']['select_month']['month'] : $form_state['values']['month']; + $form_state['redirect'] = 'admin/config/system/room_reservations/hours/daily_hours/' . $month; + } + // Process the daily hours for the selected month. + elseif ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $confirmation = ROOM_RESERVATIONS_SAVE_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_SAVE_ERROR_MSG; + $month_value = $form_state['values']['month']; + $month = intval(drupal_substr($month_value, 5)); + $year = drupal_substr($month_value, 0, 4); + // Days in the month. + $days = date('t', mktime(0, 0, 0, $month, 1, $year)); + $mo_hours = unserialize(_room_reservations_get_variable('monthly_hours_' . $month_value)); + $updated_mo_hours = array(); + $default_hours = unserialize(_room_reservations_get_variable('default_hours')); + for ($day = 0; $day < $days; $day++) { + // User entered hours for a single day. + $day_first_open = $form_state['values']['day_' . $day]['first_shift_open_' . $day]; + $day_first_close = $form_state['values']['day_' . $day]['first_shift_close_' . $day]; + $day_second_open = $form_state['values']['day_' . $day]['second_shift_open_' . $day]; + $day_second_close = $form_state['values']['day_' . $day]['second_shift_close_' . $day]; + // Default hours for the day of the week. + // Day of week. + $dow = date('w', mktime(0, 0, 0, $month, $day + 1, $year)); + $default_first_open = $default_hours[($dow * 4) + 0]; + $default_first_close = $default_hours[($dow * 4) + 1]; + $default_second_open = $default_hours[($dow * 4) + 2]; + $default_second_close = $default_hours[($dow * 4) + 3]; + // Compare user entered hours to default for the day of the week. + if (($day_first_open == $default_first_open) && + ($day_first_close == $default_first_close) && + ($day_second_open == $default_second_open) && + ($day_second_close == $default_second_close)) { + $day_is_default = TRUE; + } + else { + $day_is_default = FALSE; + } + // Update the monthly hours record. + $updated_mo_hours[($day * 5)] = ($day_is_default) ? 'D' : 'O'; + $updated_mo_hours[($day * 5) + 1] = $day_first_open; + $updated_mo_hours[($day * 5) + 2] = $day_first_close; + $updated_mo_hours[($day * 5) + 3] = $day_second_open; + $updated_mo_hours[($day * 5) + 4] = $day_second_close; + } + $result = _room_reservations_set_variable('monthly_hours_' . $month_value, serialize($updated_mo_hours)); + if ($result) { + drupal_set_message($confirmation); + } + else { + drupal_set_message($error, 'error'); + } + } + // Reset the month to the default hours. + elseif ($form_state['clicked_button']['#value'] == t('Reset to defaults')) { + $confirmation = ROOM_RESERVATIONS_RESET_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_RESET_ERROR_MSG; + $month_value = $form_state['values']['month']; + $month = intval(drupal_substr($month_value, 5)); + $year = drupal_substr($month_value, 0, 4); + $result = _room_reservations_create_mo_hours($year, $month, $month_value); + if ($result) { + drupal_set_message($confirmation); + } + else { + drupal_set_message($error, 'error'); + } + } +} + +/** + * Form constructor for the Display Text configuration page. + */ +function room_reservations_admin_settings_page($form, &$form_state) { + $default_calendar_text = _room_reservations_get_variable('calendar_text'); + $default_reserve_room_instructions_text + = _room_reservations_get_variable('reserve_instructions'); + $default_reserve_form_instructions_text + = _room_reservations_get_variable('reserve_form_instructions'); + $default_policies = _room_reservations_get_variable('policies'); + $form['calendar_text'] = array( + '#title' => t('Calendar page text'), + '#type' => 'textarea', + '#rows' => 5, + '#description' => t('Text displayed at the top of the reservation + calendar page.'), + '#default_value' => $default_calendar_text, + '#weight' => -90, + ); + $form['reserve_instructions_text'] = array( + '#title' => t('Reserve a room instructions text'), + '#type' => 'textarea', + '#rows' => 2, + '#description' => t('Text displayed just above the words Reservation + Calendar on the reservation calendar page.'), + '#default_value' => $default_reserve_room_instructions_text, + '#weight' => -80, + ); + $form['form_instructions_text'] = array( + '#title' => t('Reservation form instructions text'), + '#type' => 'textarea', + '#rows' => 2, + '#description' => t('Text displayed at the top of the reservation form.'), + '#default_value' => $default_reserve_form_instructions_text, + '#weight' => -70, + ); + $form['policies'] = array( + '#title' => t('Policies'), + '#type' => 'textarea', + '#rows' => 10, + '#description' => t('Policies displayed on the reservation policies page.'), + '#default_value' => $default_policies, + '#weight' => -60, + ); + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + '#weight' => 100, + ); + $form['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset to defaults'), + '#weight' => 101, + ); + return $form; +} + +/** + * Form submission for the Display Text configuration page. + */ +function room_reservations_admin_settings_page_submit($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $calendar_text = $form_state['values']['calendar_text']; + $reserve_room_instructions_text + = $form_state['values']['reserve_instructions_text']; + $reserve_form_instructions_text + = $form_state['values']['form_instructions_text']; + $policies = $form_state['values']['policies']; + $confirmation = ROOM_RESERVATIONS_SAVE_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_SAVE_ERROR_MSG; + } + elseif ($form_state['clicked_button']['#value'] == t('Reset to defaults')) { + $calendar_text = ''; + $reserve_room_instructions_text = ''; + $reserve_form_instructions_text = ''; + $policies = ''; + $confirmation = ROOM_RESERVATIONS_RESET_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_RESET_ERROR_MSG; + } + $errors = FALSE; + $result = _room_reservations_set_variable('calendar_text', $calendar_text); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('reserve_instructions', + $reserve_room_instructions_text); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('reserve_form_instructions', + $reserve_form_instructions_text); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('policies', $policies); + if (!$result) { + $errors = TRUE; + } + if ($errors) { + drupal_set_message($error, 'error'); + } + else { + drupal_set_message($confirmation); + } +} + +/** + * Form constructor for the SMS / Wireless Carriers configuration page. + */ +function room_reservations_admin_settings_sms($form, &$form_state) { + $default_sms_option = _room_reservations_get_variable('sms_option'); + $form['#tree'] = TRUE; + $form['sms_option'] = array( + '#title' => t('SMS option'), + '#type' => 'checkbox', + '#return_value' => 1, + '#default_value' => $default_sms_option, + '#description' => t('Give users the option of receiving confirmation + messages and reminders as SMS text messages.'), + '#weight' => -110, + ); + $form['carriers'] = array( + '#type' => 'fieldset', + '#title' => t('Wireless carriers'), + '#collapsible' => TRUE, + '#collapsed' => FALSE, + '#weight' => -100, + ); + $sql = " + SELECT id, value + FROM {room_reservations_variables} + WHERE name = '%s' + ORDER BY value + "; + $result = db_query($sql, 'carrier'); + if ($result) { + $options = array(); + $form['carriers']['list_0'] = array( + '#value' => '', + '#weight' => -99, + ); + $x = 0; + while ($data = db_fetch_object($result)) { + $x++; + $id = $data->id; + $values = explode('~', $data->value); + $options[strval($id)] = $values[0]; + $form['carriers']['list_' . $x] = array( + '#value' => '', + '#weight' => -99 + $x, + ); + $x++; + } + $form['carriers']['list_' . $x] = array( + '#value' => '
' . t('Wireless carrier') . '' . + t('Domain name') . '
' . check_plain($values[0]) . '' . + check_plain($values[1]) . '
', + '#weight' => -99 + $x, + ); + } + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + '#weight' => 100, + ); + $form['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset to defaults'), + '#weight' => 101, + ); + return $form; +} + +/** + * Form submission for the SMS / Wireless Carriers configuration page. + */ +function room_reservations_admin_settings_sms_submit($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $sms_option = $form_state['values']['sms_option']; + $confirmation = ROOM_RESERVATIONS_SAVE_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_SAVE_ERROR_MSG; + } + if ($form_state['clicked_button']['#value'] == t('Reset to defaults')) { + $sms_option = 0; + $confirmation = ROOM_RESERVATIONS_RESET_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_RESET_ERROR_MSG; + } + $result = _room_reservations_set_variable('sms_option', $sms_option); + if (!$result) { + $errors = TRUE; + } + if ($errors) { + drupal_set_message($error, 'error'); + } + else { + drupal_set_message($confirmation); + } +} + +/** + * Form constructor for the SMS / Add Carrier configuration page. + */ +function room_reservations_admin_settings_sms_add($form, &$form_state) { + $form['carrier'] = array( + '#title' => t('Wireless carrier'), + '#type' => 'textfield', + '#maxlength' => 50, + '#size' => 50, + '#description' => t('The name of the wireless carrier, such as !att or + !verizon.', array( + '!att' => 'AT&T', + '!verizon' => 'Verizon', + )), + '#weight' => -20, + ); + $form['domain'] = array( + '#title' => t('Domain name'), + '#type' => 'textfield', + '#maxlength' => 50, + '#size' => 50, + '#description' => t('The domain name of the wireless carrier, such as + !att or !verizon.', array( + '!att' => '@txt.att.net', + '!verizon' => '@vtext.com', + )), + '#weight' => -10, + ); + $form['add_carrier']['add'] = array( + '#type' => 'submit', + '#value' => t('Add'), + '#weight' => 100, + ); + return $form; +} + +/** + * Form validation for the SMS / Add Carrier configuration page. + */ +function room_reservations_admin_settings_sms_add_validate($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Add')) { + $carrier = $form_state['values']['carrier']; + $domain = $form_state['values']['domain']; + if (!$carrier) { + $field = 'add_carrier][carrier'; + $message = t('Wireless carrier is required.'); + form_set_error($field, $message); + } + if (!$domain) { + $field = 'add_carrier][domain'; + $message = t('Domain name is required.'); + form_set_error($field, $message); + } + else { + if (drupal_substr($domain, 0, 1) != '@') { + $field = 'add_carrier][domain'; + $message = t('Domain name must begin with @.'); + form_set_error($field, $message); + } + } + if (($carrier) && ($domain)) { + $sql = " + SELECT value + FROM {room_reservations_variables} + WHERE name = '%s' + "; + $result = db_query($sql, 'carrier'); + if ($result) { + while ($data = db_fetch_object($result)) { + $values = explode('~', $data->value); + if ($values[0] == $carrier) { + $field = 'add_carrier][carrier'; + $message = t('There is already a record for this wireless + carrier.'); + form_set_error($field, $message); + break; + } + } + } + } + } +} + +/** + * Form submission for the SMS / Add Carrier configuration page. + */ +function room_reservations_admin_settings_sms_add_submit($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Add')) { + $carrier = $form_state['values']['carrier']; + $domain = $form_state['values']['domain']; + $sql = " + INSERT INTO {room_reservations_variables} + (name, value) + VALUES ('%s', '%s') + "; + $result = db_query($sql, 'carrier', $carrier . '~' . $domain); + if (!$result) { + drupal_set_message(t('The wireless carrier could not be added.'), + 'error'); + } + else { + drupal_set_message(t('The wireless carrier has been added.')); + } + } +} + +/** + * Form constructor for the SMS / Delete Network configuration page. + */ +function room_reservations_admin_settings_sms_delete($form, &$form_state) { + $sql = " + SELECT id, value + FROM {room_reservations_variables} + WHERE name = '%s' + ORDER BY value + "; + $result = db_query($sql, 'carrier'); + if ($result) { + $options = array(); + $options[strval(0)] = t('Select a wireless carrier'); + $x = 0; + while ($data = db_fetch_object($result)) { + $x++; + $id = $data->id; + $values = explode('~', $data->value); + $options[strval($id)] = $values[0]; + $x++; + } + } + if ($options) { + $form['carrier'] = array( + '#type' => 'select', + '#title' => t('Wireless carrier'), + '#options' => $options, + '#weight' => 25, + ); + $form['delete'] = array( + '#type' => 'submit', + '#value' => t('Delete'), + '#weight' => 30, + ); + } + return $form; +} + +/** + * Form validation for the SMS / Delete Network configuration page. + */ +function room_reservations_admin_settings_sms_delete_validate($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Delete')) { + $carrier = $form_state['values']['carrier']; + if (!$carrier) { + $field = 'delete_carrier][carrier'; + $message = t('Wireless carrier is required.'); + form_set_error($field, $message); + } + } +} + +/** + * Form submission for the SMS / Delete Network configuration page. + */ +function room_reservations_admin_settings_sms_delete_submit($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Delete')) { + $carrier = $form_state['values']['carrier']; + $sql = " + DELETE FROM {room_reservations_variables} + WHERE ID = %d + "; + $result = db_query($sql, $carrier); + if (!$result) { + drupal_set_message(t('The wireless carrier could not be deleted.'), + 'error'); + } + else { + drupal_set_message(t('The wireless carrier has been deleted.')); + } + } +} + +/** + * Form constructor for the Messages / Email Messages configuration page. + */ +function room_reservations_admin_settings_email($form, &$form_state) { + $default_from_address + = _room_reservations_get_variable('from_address'); + $default_confirmation_header_text + = _room_reservations_get_variable('confirmation_header_text'); + $default_confirmation_owner_text + = _room_reservations_get_variable('confirmation_owner_text'); + $default_confirmation_group_text + = _room_reservations_get_variable('confirmation_group_text'); + $default_reminder_header_text + = _room_reservations_get_variable('reminder_header_text'); + $default_reminder_owner_text + = _room_reservations_get_variable('reminder_owner_text'); + $default_reminder_group_text + = _room_reservations_get_variable('reminder_group_text'); + $tokens = array( + '%reservation_name' => t('The name given to the reservation to identify it + on the reservation calendar.'), + '%room' => t('The room that has been reserved.'), + '%month' => t('The full name of the month of the reservation date.'), + '%month_number' => t('The month number of the reservation date.'), + '%day' => t('The numeric day of the month of the reservation date.'), + '%day_of_the_week' => t('The full name of the day of the week of the + reservation date.'), + '%time' => t('The time of the reservation.'), + '%minutes' => t('The length of the reservation in minutes.'), + ); + $token_display = '

' . + t('The following tokens will be replaced with dynamic values in any of the + fields below.') . '

    '; + foreach ($tokens as $key => $value) { + $token_display .= '
  • ' . $key . ' - ' . $value . '
  • '; + } + $token_display .= '
'; + $form['from_address'] = array( + '#title' => t('From address'), + '#type' => 'textfield', + '#maxlength' => 100, + '#size' => 100, + '#description' => t('The from address on all email messages.'), + '#default_value' => $default_from_address, + '#weight' => -110, + ); + $form['tokens'] = array( + '#type' => 'fieldset', + '#title' => t('Token values'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => -100, + ); + $form['tokens']['values'] = array( + '#value' => $token_display, + ); + $form['confirmation_header'] = array( + '#title' => t('Confirmation heading'), + '#type' => 'textfield', + '#maxlength' => 100, + '#size' => 100, + '#description' => t('The heading for all confirmation messages.'), + '#default_value' => $default_confirmation_header_text, + '#weight' => -97, + ); + $form['confirmation_owner'] = array( + '#title' => t('Confirmation body'), + '#type' => 'textarea', + '#rows' => 8, + '#description' => t('Confirmation message sent to the person who has made a + reservation.'), + '#default_value' => $default_confirmation_owner_text, + '#weight' => -95, + ); + $form['confirmation_group'] = array( + '#title' => t('Confirmation to group members body'), + '#type' => 'textarea', + '#rows' => 8, + '#description' => t('Confirmation message sent to all members of the group + except for the person who made the reservation.'), + '#default_value' => $default_confirmation_group_text, + '#weight' => -90, + ); + $form['reminder_header'] = array( + '#title' => t('Reminder heading'), + '#type' => 'textfield', + '#maxlength' => 100, + '#size' => 100, + '#description' => t('The heading for all reminder messages.'), + '#default_value' => $default_reminder_header_text, + '#weight' => -85, + ); + $form['reminder_owner'] = array( + '#title' => t('Reminder body'), + '#type' => 'textarea', + '#rows' => 8, + '#description' => t('Reminder message sent to the person who has made a + reservation.'), + '#default_value' => $default_reminder_owner_text, + '#weight' => -80, + ); + $form['reminder_group'] = array( + '#title' => t('Reminder to group members body'), + '#type' => 'textarea', + '#rows' => 8, + '#description' => t('Reminder message sent to all members of the group + except for the person who made the reservation.'), + '#default_value' => $default_reminder_group_text, + '#weight' => -75, + ); + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + '#weight' => 100, + ); + $form['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset to defaults'), + '#weight' => 101, + ); + return $form; +} + +/** + * Form validation for the Messages / Email Messages configuration page. + */ +function room_reservations_admin_settings_email_validate($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $from_address = $form_state['values']['from_address']; + if (valid_email_address($from_address)) { + // Valid. + } + else { + $field = 'from_address'; + $message = t('Invalid email address.'); + form_set_error($field, $message); + } + } +} + +/** + * Form submission for the Messages / Email Messages configuration page. + */ +function room_reservations_admin_settings_email_submit($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $from_address = $form_state['values']['from_address']; + $confirmation_header = $form_state['values']['confirmation_header']; + $confirmation_owner = $form_state['values']['confirmation_owner']; + $confirmation_group = $form_state['values']['confirmation_group']; + $reminder_header = $form_state['values']['reminder_header']; + $reminder_owner = $form_state['values']['reminder_owner']; + $reminder_group = $form_state['values']['reminder_group']; + $confirmation = ROOM_RESERVATIONS_SAVE_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_SAVE_ERROR_MSG; + } + elseif ($form_state['clicked_button']['#value'] == t('Reset to defaults')) { + $confirmation_header = ''; + $confirmation_owner = ''; + $confirmation_group = ''; + $reminder_header = ''; + $reminder_owner = ''; + $reminder_group = ''; + $confirmation = ROOM_RESERVATIONS_RESET_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_RESET_ERROR_MSG; + } + $errors = FALSE; + $result = _room_reservations_set_variable('from_address', $from_address); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('confirmation_header_text', + $confirmation_header); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('confirmation_owner_text', + $confirmation_owner); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('confirmation_group_text', + $confirmation_group); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('reminder_header_text', + $reminder_header); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('reminder_owner_text', + $reminder_owner); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('reminder_group_text', + $reminder_group); + if (!$result) { + $errors = TRUE; + } + if ($errors) { + drupal_set_message($error, 'error'); + } + else { + drupal_set_message($confirmation); + } +} + +/** + * Form constructor for the Messages / SMS Messages configuration page. + */ +function room_reservations_admin_settings_text($form, &$form_state) { + $default_enable_text + = _room_reservations_get_variable('enable_text'); + $default_from_address + = _room_reservations_get_variable('from_address_sms'); + $default_confirmation_header_text + = _room_reservations_get_variable('confirmation_header_text_sms'); + $default_confirmation_owner_text + = _room_reservations_get_variable('confirmation_owner_text_sms'); + $default_reminder_header_text + = _room_reservations_get_variable('reminder_header_text_sms'); + $default_reminder_owner_text + = _room_reservations_get_variable('reminder_owner_text_sms'); + $tokens = array( + '%reservation_name' => t('The name given to the reservation to identify it + on the reservation calendar.'), + '%room' => t('The room that has been reserved.'), + '%month' => t('The full name of the month of the reservation date.'), + '%month_number' => t('The month number of the reservation date.'), + '%day' => t('The numeric day of the month of the reservation date.'), + '%day_of_the_week' => t('The full name of the day of the week of the + reservation date.'), + '%time' => t('The time of the reservation.'), + '%minutes' => t('The length of the reservation in minutes.'), + ); + $token_display = '

' . + t('The following tokens will be replaced with dynamic values in any of the + fields below.') . '

    '; + foreach ($tokens as $key => $value) { + $token_display .= '
  • ' . $key . ' - ' . $value . '
  • '; + } + $token_display .= '
'; + $form['enable_text'] = array( + '#title' => t('Enable SMS text messages.'), + '#type' => 'checkbox', + '#return_value' => 1, + '#default_value' => $default_enable_text, + '#description' => t('Enable the ability of users to receive reservation + confirmations and reminders by text message.'), + '#weight' => -120, + ); + $form['from_address'] = array( + '#title' => t('From address'), + '#type' => 'textfield', + '#maxlength' => 100, + '#size' => 100, + '#description' => t('The from address on all text messages.'), + '#default_value' => $default_from_address, + '#weight' => -110, + ); + $form['tokens'] = array( + '#type' => 'fieldset', + '#title' => t('Token values'), + '#collapsible' => TRUE, + '#collapsed' => TRUE, + '#weight' => -100, + ); + $form['tokens']['values'] = array( + '#value' => $token_display, + ); + $form['confirmation_header'] = array( + '#title' => t('Confirmation heading'), + '#type' => 'textfield', + '#maxlength' => 100, + '#size' => 100, + '#description' => t('The heading for confirmation text messages.'), + '#default_value' => $default_confirmation_header_text, + '#weight' => -97, + ); + $form['confirmation_owner'] = array( + '#title' => t('Confirmation body'), + '#type' => 'textarea', + '#rows' => 8, + '#description' => t('Confirmation text message body.'), + '#default_value' => $default_confirmation_owner_text, + '#weight' => -95, + ); + $form['reminder_header'] = array( + '#title' => t('Reminder heading'), + '#type' => 'textfield', + '#maxlength' => 100, + '#size' => 100, + '#description' => t('The heading for reminder text messages.'), + '#default_value' => $default_reminder_header_text, + '#weight' => -85, + ); + $form['reminder_owner'] = array( + '#title' => t('Reminder body'), + '#type' => 'textarea', + '#rows' => 8, + '#description' => t('Reminder text message body.'), + '#default_value' => $default_reminder_owner_text, + '#weight' => -80, + ); + $form['save'] = array( + '#type' => 'submit', + '#value' => t('Save configuration'), + '#weight' => 100, + ); + $form['reset'] = array( + '#type' => 'submit', + '#value' => t('Reset to defaults'), + '#weight' => 101, + ); + return $form; +} + +/** + * Form validatiion for the Messages / SMS Messages configuration page. + */ +function room_reservations_admin_settings_text_validate($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $from_address = $form_state['values']['from_address']; + if (valid_email_address($from_address)) { + // Valid. + } + else { + $field = 'from_address'; + $message = t('Invalid email address.'); + form_set_error($field, $message); + } + } +} + +/** + * Form submission for the Messages / SMS Messages configuration page. + */ +function room_reservations_admin_settings_text_submit($form_id, &$form_state) { + if ($form_state['clicked_button']['#value'] == t('Save configuration')) { + $enable_text = $form_state['values']['enable_text']; + $from_address = $form_state['values']['from_address']; + $confirmation_header = $form_state['values']['confirmation_header']; + $confirmation_owner = $form_state['values']['confirmation_owner']; + $reminder_header = $form_state['values']['reminder_header']; + $reminder_owner = $form_state['values']['reminder_owner']; + $confirmation = ROOM_RESERVATIONS_SAVE_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_SAVE_ERROR_MSG; + } + elseif ($form_state['clicked_button']['#value'] == t('Reset to defaults')) { + $enable_text = 0; + $confirmation_header = ''; + $confirmation_owner = ''; + $reminder_header = ''; + $reminder_owner = ''; + $confirmation = ROOM_RESERVATIONS_RESET_CONFIRMATION_MSG; + $error = ROOM_RESERVATIONS_RESET_ERROR_MSG; + } + $errors = FALSE; + $result = _room_reservations_set_variable('enable_text', $enable_text); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('from_address_sms', $from_address); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('confirmation_header_text_sms', + $confirmation_header); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('confirmation_owner_text_sms', + $confirmation_owner); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('reminder_header_text_sms', + $reminder_header); + if (!$result) { + $errors = TRUE; + } + $result = _room_reservations_set_variable('reminder_owner_text_sms', + $reminder_owner); + if (!$result) { + $errors = TRUE; + } + if ($errors) { + drupal_set_message($error, 'error'); + } + else { + drupal_set_message($confirmation); + } +} diff --git a/room_reservations.css b/room_reservations.css new file mode 100644 index 0000000..de5ce25 --- /dev/null +++ b/room_reservations.css @@ -0,0 +1,231 @@ +/* ----------------------------- ROOM RESERVATIONS ---------------------------- */ + +#rooms-calendar .slink { + margin-right: 40px; +} + +.reservations-block ul { + list-style-image: url(images/bullet.gif); +} + +.reservations-block #reservations { + margin-bottom: 5px; +} + +#rooms-calendar .room-tabs { + margin: 0 0 0 7px; + padding: 0; +} + +#rooms-calendar ul li, +#rooms-calendar ul.menu li, +#rooms-calendar .item-list ul li, +#rooms-calendar li.leaf { + background: none; + padding: 0 0.75em 0.2em 0.75em; +} + +#rooms-calendar .room-tabs li { + float: left; + list-style: none; + margin: 0 0 -3px 0; + padding: 2px 3px; +} + +#rooms-calendar .room-tabs li a { + background: #dddddd; + border: 1px solid #999999; + border-top-left-radius: 7px; + border-top-right-radius: 7px; + display: block; + -moz-border-radius-topleft: 7px; + -moz-border-radius-topright: 7px; + padding: 3px 5px; + text-decoration: none; + -webkit-border-top-left-radius: 7px; + -webkit-border-top-right-radius: 7px; +} + +#rooms-calendar .room-tabs li a.active { + background: #ffffff; + border-bottom: 1px solid #ffffff; +} + +#rooms-calendar .panelContainer { + border: 1px solid #999; + clear: left; +} + +#rooms-calendar .panelContainer:after { + content: " "; + display: table; + clear: both; + min-height: 20px; +} + +#rooms-calendar .panel { + padding: 0 10px; +} + +#rooms-calendar #tabbedPanels .date { + clear: both; + margin: 0; + text-align: center; +} + +#rooms-calendar #tabbedPanels .hours { + color: #666666; + font-weight: bold; + margin-bottom: 5px; + text-align: center; +} + +#rooms-calendar #date-change, +#tabbedPanels #info { + text-align: center; +} + +#rooms-calendar .panel .gcolumns .grid-column { + float: left; +} + +#rooms-calendar div.grid-column { + max-width: 115px; +} + +#rooms-calendar .panel .gcolumns ul { + padding: 0 1px; +} + +#rooms-calendar .panel .gcolumns li { + border: 1px solid #dddddd; + font-size: 85%; + height: 1.33em; + line-height: 15px; + list-style-type: none; + margin: -1px auto 0; + padding: 2px 0; + text-align: center; +} + +/*#rooms-calendar .panel .gcolumns li.odd { + border-top: 1px solid #999999; +} +*/ +#rooms-calendar .panel .gcolumns li.room-info { + border: none; +} + +#rooms-calendar .panel .gcolumns li.room-info-heading { + padding-bottom: 5px; +} + +#rooms-calendar .panel .gcolumns li.room-info-footer { + padding-top: 5px; +} + +#rooms-calendar .panel .gcolumns .hours-column li { + width: 120px; +} + +#rooms-calendar .panel .gcolumns .room-column li { + width: 115px; +} + +#rooms-calendar .panel .gcolumns li.timeslot { + background-color: #fff; + *height: 1.33em; + *margin-bottom: 2px; +} + +#rooms-calendar .panel .gcolumns li.reservable { + background-color: #fff; +} + +#rooms-calendar .panel .gcolumns li.open { + background-color: #fff; +} + +#rooms-calendar .panel .gcolumns li.booked { + background-color: #ddd; +} + +#rooms-calendar .gcolumns li.setup { + background-color: #eee; +} + +#rooms-calendar .panel .gcolumns li.closed { + background-color: #d1dfdf; + border: 1px solid #d1dfdf; +} + +#rooms-calendar .hide { + display: none; +} + +.page-room-reservations .form-item label { + display: inline; +} + +#txtmsg-fields { + display: none; +} + +#date-change { + margin-bottom:20px; +} +.clear {clear:both;} + +/* QTIP Overrides */ + +.qtip { + box-shadow: -2px 2px 8px rgba(0,0,0,0.3); +} + +.qtip canvas { + display: none; + width: 400px; +} +.qtip-link, .qtip-additional-element { + border-bottom: 1px dotted #999; +} + +.qtip h4 { + margin: 0px; + margin-top: 0.5em; + font-size: 12px; + color: #999; + font-family: 'AvenirNextLTW01-Regular', Arial; +} + +.qtip-tooltip { + font-family: 'AvenirNextLTW01-Regular', Arial; + font-size: 12px; + margin-bottom: 0.5em; + width: 400px; +} + +/*! Light tooltip style */ +.ui-tooltip-light .ui-tooltip-titlebar, +.ui-tooltip-light .ui-tooltip-content{ + border-color: transparent; + color: #333; +} + +.ui-tooltip-light .ui-tooltip-content { + background-color: white; + width: 270px !important; +} + +.ui-tooltip-light .ui-tooltip-titlebar { + width: 244px !important; +} + +#ui-datepicker-div { + z-index:2 !important; +} + +#rooms-calendar ul li.reservable:hover, #rooms-calendar ul li.open:hover { + background-color:#FFF; +} + diff --git a/room_reservations.inc b/room_reservations.inc new file mode 100644 index 0000000..5b04768 --- /dev/null +++ b/room_reservations.inc @@ -0,0 +1,1597 @@ + $id)); + foreach ($results as $data) { + $this->id = $id; + $this->date = $data->date; + $this->time = $data->time; + $this->length = $data->length; + $this->room = $data->room; + $this->name = $data->name; + $this->groupSize = $data->group_size; + $this->userName = $data->user_name; + $this->emailAddresses = $data->email_addresses; + $this->textmsg = $data->textmsg; + $this->carrier = $data->carrier; + $this->phone = $data->phone; + $record_count++; + } + } + } +} + +/** + * Create an array of all the room categories. + * + * @return array + * An array representing all the room categories. + */ +function _room_reservations_categories() { + $categories = array(); + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_category') + // this partially makes sense - do not show Categories which are unpublished + // issue will be that Rooms maybe be published with categories that aren't.. which will be a mess + //->propertyCondition('status', NODE_PUBLISHED) + + // dangerous as this does more than Order By as it does not include records which do not have this field set + // so let's make sure the Order field is REQUIRED + ->fieldOrderBy('reservations_display_order', 'value', 'ASC'); + $result = $query->execute(); + if (isset($result['node'])) { + $cids = array_keys($result['node']); + $cat_objs = node_load_multiple($cids); + foreach ($cat_objs as $cat) { + $categories[$cat->nid] = array( + 'nid' => $cat->nid, + 'title' => check_plain($cat->title), + 'advmin' => isset($cat->reservations_minadvbooking[LANGUAGE_NONE][0]['value']) ? $cat->reservations_minadvbooking[LANGUAGE_NONE][0]['value'] : 0, + 'prebuffer' => $cat->reservations_prebuffer[LANGUAGE_NONE][0]['value'], + 'postbuffer' => $cat->reservations_postbuffer[LANGUAGE_NONE][0]['value'], + ); + } + } + return $categories; +} + +/** + * Retrieve all the rooms from the database. + * + * @return array + * An array with each element representing a room that can be reserved. + */ +function _room_reservations_rooms() { + $rooms = array(); + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_room') + ->propertyCondition('status', NODE_PUBLISHED) + // dangerous as this does more than Order By as it does not include records which do not have this field set + ->fieldOrderBy('reservations_display_order', 'value', 'ASC'); + $result = $query->execute(); + if (isset($result['node'])) { + $rids = array_keys($result['node']); + $room_objs = node_load_multiple($rids); + foreach ($room_objs as $room) { + $rooms[$room->nid] = (array) $room; + } + } + return $rooms; +} + +/** + * Determine the list ofmonths as far out as we have set Extended advanced booking. + * + * @return array + * Each element represents a month. + */ +function _room_reservations_current_months() { + // How far out do we want to set allowed hours for? + + // build a list of the months we need to look at + $advance_days = variable_get('room_reservations_advance_extended', 180); + $found = false; + for ($x = 0; $x < $advance_days; $x++) { + if (date('m', mktime(0, 0, 0, date("m"), date("d") + $x, date("Y"))) != $found) { + $months[date('Y-m', mktime(0, 0, 0, date("m"), date("d") + $x, date("Y")))]['m'] = date('n', mktime(0, 0, 0, date("m"), date("d") + $x, date("Y"))); + $months[date('Y-m', mktime(0, 0, 0, date("m"), date("d") + $x, date("Y")))]['mm'] = date('m', mktime(0, 0, 0, date("m"), date("d") + $x, date("Y"))); + $months[date('Y-m', mktime(0, 0, 0, date("m"), date("d") + $x, date("Y")))]['y'] = date('Y', mktime(0, 0, 0, date("m"), date("d") + $x, date("Y"))); + $found = date('m', mktime(0, 0, 0, date("m"), date("d") + $x, date("Y"))); + } + } + + // Note: Month names are translated when they are displayed. + $names = array( + 'Unused', + 'January', + 'February', + 'March', + 'April', + 'May', + 'June', + 'July', + 'August', + 'September', + 'October', + 'November', + 'December', + ); + + foreach ($months as $month) { + $item['display'] = $names[$month['m']] . ' ' . $month['y']; + $item['year'] = $month['y']; + $item['month'] = $month['m']; + $item['MM'] = $month['mm']; + $item['YYYY_MM'] = $month['y'] . '_' . $month['mm']; + $results[] = $item; + } + return $results; +} + +/** + * Store daily open hours in the database. + * + * Initialize a record to store all of the daily open hours for a + * single month, and save the record in the room_reservations_variables + * table. + * + * @param int $year + * The year of the month being represented. + * @param int $month + * The month number of the month being represented. + * @param string $yyyy_mm + * The year and month of the month being represented in format YYYY_MM. + * @param int $array + * if TRUE return mo_hours array rather than creating db record + * + * @return object + * A database query result resource, or FALSE if the query was not executed + * correctly. + */ +function _room_reservations_create_mo_hours($year, $month, $yyyy_mm, $array = false) { + $mo_hours = array(); + // Days in the month. + $days = date('t', mktime(0, 0, 0, $month, 1, $year)); + $default_hours = unserialize(_room_reservations_get_variable('default_hours')); + if (!$default_hours) { + for ($x = 0; $x < $days; $x++) { + $mo_hours[] = 'D'; + $mo_hours[] = '9999'; + $mo_hours[] = '9999'; + $mo_hours[] = '9999'; + $mo_hours[] = '9999'; + } + } + else { + for ($x = 0; $x < $days; $x++) { + // Day of week. + $dow = date('w', mktime(0, 0, 0, $month, $x + 1, $year)); + $mo_hours[] = 'D'; + $mo_hours[] = $default_hours[$dow * 4]; + $mo_hours[] = $default_hours[($dow * 4) + 1]; + $mo_hours[] = $default_hours[($dow * 4) + 2]; + $mo_hours[] = $default_hours[($dow * 4) + 3]; + } + } + $name = 'monthly_hours_' . $yyyy_mm; + if ($array) { + return $mo_hours; + } + else { + $result = _room_reservations_set_variable($name, serialize($mo_hours)); + return $result; + } +} + +/** + * Create reservations dates array. + * + * Create an array containing pertinent information about all the + * possible days for which a reservation can be made. + * + * 7.x-1.3 REV: + * - start of adding features on a per Category basis + * - for now let's just key this date's array by Cat ID + * + * @param int $selected_month + * The month of the day currently selected by the user + * @param int $selected_day + * The day of the month of the day currently selected by the user. + * + * @return array + * Information about each day for which a reservation can be made, including + * display name, day of the week, month name and number, day of the month, + * date in the format YYYY-MM-DD, whether the day is currently selected by + * the user, and whether the day is today. + */ +function _room_reservations_dates($selected_month = NULL, $selected_day = NULL, $keyed = false) { + // Determine date information (month, day, year, etc.) for each of these days. + $categories = _room_reservations_categories(); + $dates = array(); + foreach ($categories as $cat) { + $advancedaysmax = user_access('create room reservations extended') + ? variable_get('room_reservations_advance_extended', 180) + : variable_get('room_reservations_advance_standard', 14); + $advancedaysmin = user_access('bypass minimum advance booking') ? 0 : $cat['advmin']; + for ($j = $advancedaysmin; $j < $advancedaysmax; $j++) { + $day = array(); + $day['display'] = date("l, n/j", strtotime("now + " . $j . " days")); + $day['day-of-week'] = date("l", strtotime("now + " . $j . " days")); + $day['month'] = date("F", strtotime("now + " . $j . " days")); + $month_number = date("n", strtotime("now + " . $j . " days")); + $day['month-number'] = $month_number; + $sday = date("j", strtotime("now + " . $j . " days")); + $day['day'] = $sday; + $year = date("Y", strtotime("now + " . $j . " days")); + $day['year'] = $year; + // Determine the date selected by the user. If none selected, default to the first day. + if (($j == 0) && (!$selected_month) && (!$selected_day)) { + $day['selected'] = TRUE; + } + elseif (($selected_month == $month_number) && ($selected_day == $sday)) { + $day['selected'] = TRUE; + } + else { + $day['selected'] = FALSE; + } + // The date in YYYY-MM-DD format. + if ($month_number < 10) { + $month_number = str_pad($month_number, 2, '0', STR_PAD_LEFT); + } + if ($sday < 10) { + $sday = str_pad($sday, 2, '0', STR_PAD_LEFT); + } + $day['yyyymmdd'] = $year . "-" . $month_number . "-" . $sday; + $day['today'] = (($day['month-number'] == date('m')) && + ($day['day'] == date('j'))) ? TRUE : FALSE; + if ($keyed) { + $dates[$cat['nid']][$day['yyyymmdd']] = $day; + } + else { + $dates[$cat['nid']][] = $day; + } + } + } + return $dates; +} + +/** + * Create an array representing every half hour time slot in a single day. + * + * @param string $option + * If set to 'limited', only include time slots in the array that are + * later in the day than the current time minus the longest possible + * reservation length. + * + * @return array + * An array representing reservable time slots in a single day. + */ +function _room_reservations_hours($option = NULL) { + //watchdog('rr', 'START'); + $hours = array(); + $x = 0; + $y = 0; + while ($x <= 23) { + $hours_entry = array(); + $hour = ($x < 10) ? '0' . $x : $x; + if ($x == 0) { + $display_hour = 12; + } + elseif ($x <= 12) { + $display_hour = $x; + } + else { + $display_hour = $x - 12; + } + $minutes = ($y % 2) ? '30' : '00'; + $time = $hour . $minutes; + $ampm = ($y < 24) ? t('AM') : t('PM'); + if ($y == 0) { + // these shouldn't be wrapped in t() since we are just about to do a date() with them anyway + $display = 'Midnight'; + } + elseif ($y == 24) { + $display = 'Noon'; + } + else { + $display = $display_hour . ':' . $minutes . ' ' . $ampm; + } + + // convert display to 24:00 format if required + if (variable_get('room_reservations_hour_format', 0)) { + $display = date('H:i', strtotime($display)); + } + + $class = ($y % 2) ? 'even' : 'odd'; + $hours_node_time = $display_hour . ':' . $minutes . $ampm; + $hours_entry['time'] = $time; + $hours_entry['display'] = $display; + $hours_entry['hours node time'] = $hours_node_time; + $hours_entry['class'] = $class; + $hours_entry['open'] = TRUE; + $hours[] = $hours_entry; + if ($y % 2) { + $x++; + } + $y++; + } + + // Only return time slots that are greater than the current time minus + // the maximum reservation length. + if ($option == 'limited') { + $max_length = user_access('create room reservations extended length') + ? variable_get('room_reservations_max_length_extended', 120) + : variable_get('room_reservations_max_length_standard', 120); + $margin_time = ($max_length / 60) * 100; + if ($max_length % 60) { + $margin_time += 30; + } + $str_current_time = date('H') . date('i'); + $int_current_time = intval($str_current_time); + $cutoff_time = $int_current_time - $margin_time; + $cutoff_time = ($cutoff_time < 0) ? 0 : $cutoff_time; + $limited_hours = array(); + foreach ($hours as $time_slot) { + $time_slot_time = intval($time_slot['time']); + if ($time_slot_time > $cutoff_time) { + $limited_hours[] = $time_slot; + } + } + return $limited_hours; + } + else { + return $hours; + } +} + +/** + * Create open hours array. + * + * Create an array of open hours information for each day for which a + * reservation can be made. + * + * NOTE - D6 version included only current month and next month since we could only book 15 days in advance + * since we can now book up to almost a year in advance; we need to redo how this is done + * + * @return array + * A two dimensional array, with the first dimension representing a single + * day for which a reservation can be made, and the second dimension + * representing information about the facility's open hours for that day, such + * as whether the facility is open that day, the number of open shifts, + * open and close hours, and a string that can be used to display the hours + * in a user friendly way. + */ +function _room_reservations_facility_hours($reset = FALSE) { + static $building_hours; + if (!isset($building_hours) || $reset) { + $building_hours = array(); + if (user_access('create room reservations extended')) { + $advancedays = variable_get('room_reservations_advance_extended', 180); + } + else { + $advancedays = variable_get('room_reservations_advance_standard', 14); + } + + // build a list of the months we need to look at + for ($x = 0; $x < $advancedays; $x++) { + $months[] = date('Y_m', mktime(0, 0, 0, date("m"), date("d") + $x, date("Y"))); + } + $months = array_unique($months); + + //$today = date('Y-m-d'); + + $mo_hours = array(); + foreach ($months as $month) { + $mo_hours_this = unserialize(_room_reservations_get_variable('monthly_hours_' . $month)); + if ($mo_hours_this) { + $mo_hours = array_merge($mo_hours, $mo_hours_this); + } + else { + $m = intval(drupal_substr($month, 5)); + $year = drupal_substr($month, 0, 4); + $mo_hours_this = _room_reservations_create_mo_hours($year, $m, $month, true); + $mo_hours = array_merge($mo_hours, $mo_hours_this); + } + } + $start = (date('j') - 1) * 5; + for ($x = 0; $x < $advancedays; $x++) { + $yyyymmdd = date('Y-m-d', mktime(0, 0, 0, date("m"), date("d") + $x, date("Y"))); + $first_shift_open = $mo_hours[($start + ($x * 5) + 1)]; + $first_shift_close = $mo_hours[($start + ($x * 5) + 2)]; + $second_shift_open = $mo_hours[($start + ($x * 5) + 3)]; + $second_shift_close = $mo_hours[($start + ($x * 5) + 4)]; + if (($first_shift_open == '9999') && ($first_shift_close == '9999') && ($second_shift_open == '9999') && ($second_shift_close == '9999')) { + $open = FALSE; + } + else { + $open = TRUE; + } + if (($open) && ($first_shift_open == '0000') && ($first_shift_close == '2400')) { + $open_24_hours = TRUE; + } + else { + $open_24_hours = FALSE; + } + if (!$open) { + $shifts = 0; + } + elseif ($open_24_hours) { + $shifts = 1; + } + elseif (($open) && ($second_shift_open == '9999') && ($second_shift_close == '9999')) { + $shifts = 1; + } + else { + $shifts = 2; + } + $day_hours = array( + $first_shift_open, + $first_shift_close, + $second_shift_open, + $second_shift_close, + ); + $display = _room_reservations_hours_display($day_hours); + $hours_data = array( + 'open' => $open, + 'open_24_hours' => $open_24_hours, + 'shifts' => $shifts, + 'first_shift_open' => $first_shift_open, + 'first_shift_close' => $first_shift_close, + 'second_shift_open' => $second_shift_open, + 'second_shift_close' => $second_shift_close, + 'display' => $display, + ); + $building_hours[($yyyymmdd)] = $hours_data; + } + } + return $building_hours; +} + +/* + if ($this_day <= $dom) { + // This is a day in the current month. + $yyyymmdd_yyyy = $year; + $yyyymmdd_mm = $month; + if ($this_day < 10) { + $yyyymmdd_dd = '0' . $this_day; + } + else { + $yyyymmdd_dd = $this_day; + } + } + else { + // This is a day in the next month. + $yyyymmdd_yyyy = $next_month_year; + $yyyymmdd_mm = $next_month; + $next_month_day = $this_day - $dom; + if ($next_month_day < 10) { + $yyyymmdd_dd = '0' . $next_month_day; + } + else { + $yyyymmdd_dd = $next_month_day; + } + }*/ + +/** + * Creates a string for displaying the open hours for a single day. + * + * @param array $day_hours + * An array that represents the openning and closing hours for two + * separate shifts in a single day + * + * @return string + * A string that can be used to display the hours for a single day, such as + * 'Open 24 Hours' or 'Noon - 6:00 PM'. + */ +function _room_reservations_hours_display($day_hours) { + $hours = _room_reservations_hours(); + $first_shift_open = $day_hours[0]; + $first_shift_close = $day_hours[1]; + $second_shift_open = $day_hours[2]; + $second_shift_close = $day_hours[3]; + // Closed. + if (($first_shift_open == '9999') && + ($first_shift_close == '9999') && + ($second_shift_open == '9999') && + ($second_shift_close == '9999')) { + return 'Closed'; + } + // Open 24 hours. + if (($first_shift_open == '0000') && + ($first_shift_close == '2400')) { + return 'Open 24 Hours'; + } + // One shift. + if (($second_shift_open == '9999') && + ($second_shift_close == '9999')) { + $first_shift_open_display + = _room_reservations_display_time($first_shift_open); + $first_shift_close_display + = _room_reservations_display_time($first_shift_close); + return $first_shift_open_display . ' - ' . + $first_shift_close_display; + } + // Two shifts. + $first_shift_open_display + = _room_reservations_display_time($first_shift_open); + $first_shift_close_display + = _room_reservations_display_time($first_shift_close); + $second_shift_open_display + = _room_reservations_display_time($second_shift_open); + $second_shift_close_display + = _room_reservations_display_time($second_shift_close); + return $first_shift_open_display . ' - ' . + $first_shift_close_display . ' and ' . + $second_shift_open_display . ' - ' . + $second_shift_close_display; +} + +/** + * Create time slot array. + * + * Create an array with each element representing one of the 48 half hour time + * slots that make up a day. + * + * @return array + * An array with each element representing a half hour time slot. + */ +function _room_reservations_times() { + $times = array(); + $hours = _room_reservations_hours(); + foreach ($hours as $hour) { + $times[] = $hour['time']; + } + return $times; +} + +/** + * Return time in display format. + * + * This function returns the time in display format (Midnight, 12:30 AM, + * 1:00 AM, etc.) for any time slot of the day given in military time + * format (0000, 0030, 0100, etc.). + * + * @param string $military_time + * Time of day represented in four digit military time. + * + * @return string + * Time of day represented as HH:MM AM. + */ +function _room_reservations_display_time($military_time) { + $hours = _room_reservations_hours(); + $hours[] = array( + 'time' => '2400', + 'display' => 'Midnight', + ); + foreach ($hours as $hour) { + $time = $hour['time']; + if ($time == $military_time) { + return $hour['display']; + } + } + return ''; +} + +/** + * Determine closing times. + * + * Determines which half hour time slots represent the last ones before the + * building closes. + * + * @param string $yyyymmdd + * The date for which close times are being determine, in the format + * YYYY-MM-DD. + * + * @return array + * An array representing the time slots just before closing. The array can + * contain 0, 1, or 2 items. + */ +function _room_reservations_close_times($yyyymmdd) { + $closing_times = array(); + $_room_reservations_building_hours = _room_reservations_facility_hours(); + $building_hours_day = $_room_reservations_building_hours[$yyyymmdd]; + // 24 hours. + if ($building_hours_day['open_24_hours']) { + $next_day = date('Y-m-d', strtotime("$yyyymmdd +1 days")); + $next_day_first_time_slot_open + = _room_reservations_first_slot_open($next_day); + if (!$next_day_first_time_slot_open) { + $closing_times[] = '2330'; + } + return $closing_times; + } + // First shift ends at midnight. + if ($building_hours_day['first_shift_close'] === '2400') { + $next_day = date('Y-m-d', strtotime("$yyyymmdd +1 days")); + $next_day_first_time_slot_open + = _room_reservations_first_slot_open($next_day); + if (!$next_day_first_time_slot_open) { + $closing_times[] = '2330'; + } + return $closing_times; + } + // First shift does not end at midnight. + $time = $building_hours_day['first_shift_close']; + $hours = _room_reservations_hours(); + $time_found = FALSE; + while (!$time_found) { + $time_slot = array_pop($hours); + if ($time_slot['time'] == $time) { + $time_found = TRUE; + } + } + $time_slot = array_pop($hours); + $int_second_shift_close = intval($building_hours_day['second_shift_close']); + $closing_times[] = $time_slot['time']; + // Second shift ends at midnight. + if (($building_hours_day['shifts'] == 2) && + ($int_second_shift_close == 2400)) { + $next_day = date('Y-m-d', strtotime("$yyyymmdd +1 days")); + $next_day_first_time_slot_open + = _room_reservations_first_slot_open($next_day); + if (!$next_day_first_time_slot_open) { + $closing_times[] = '2330'; + } + } + // Second shift does not end at midnight. + if (($building_hours_day['shifts'] == 2) && + ($int_second_shift_close < 2400)) { + $time = $building_hours_day['second_shift_close']; + $hours = _room_reservations_hours(); + $time_found = FALSE; + while (!$time_found) { + $time_slot = array_pop($hours); + if ($time_slot['time'] == $time) { + $time_found = TRUE; + } + } + $time_slot = array_pop($hours); + $closing_times[] = $time_slot['time']; + } + return $closing_times; +} + +/** + * Determine if facility is open from midnight to 12:30 AM. + * + * Determines if the facility is open during the first half hour of the day, + * from midnight to 12:30 AM. This information is needed when determining if + * any particular half hour time slot is the last one before the building + * closes. + * + * @param string $yyyymmdd + * The date being examined, in the format YYYY-MM-DD. + * + * @return bool + * TRUE - The facility is open during the first half hour of the day. + * FALSE - The facility is not open during the first half hour of the day. + */ +function _room_reservations_first_slot_open($yyyymmdd) { + $_room_reservations_building_hours = _room_reservations_facility_hours(); + $building_hours_day = $_room_reservations_building_hours[$yyyymmdd]; + if (!$building_hours_day['open']) { + return FALSE; + } + if ($building_hours_day['open_24_hours']) { + return TRUE; + } + if ($building_hours_day['first_shift_open'] == '0000') { + return TRUE; + } + return FALSE; +} + +/** + * Determine reservation start conflicts. + * + * Determines if a new reservation room, date and start time conflicts with a + * previously existing reservation. + * + * @param string $room + * The room that is being reserved. + * @param string $yyyymmdd + * The date of the start time for the reservation, in the format 'yyyy-mm-dd'. + * @param string $time + * The start time for the reservation, in military time. 9:00 AM is + * represented as '0900', and 9:00 PM is represented as '2100'. + * + * @return bool + * TRUE - A scheduling conflict was found. + * FALSE - A scheduling conflict was not found. + */ +function _room_reservations_start_conflicts($room, $yyyymmdd, $time) { + // Previous and next days. + $previous_day = date('Y-m-d', strtotime("$yyyymmdd -1 days")); + // Start times of other reservations that could conflict with this one. + // Since reservations are limited to 2 hours, we are interested in times that + // are less than or equal to the reservation time and greater than or equal + // to 90 minutes before the reservation time. + $max_length = user_access('create room reservations extended length') ? variable_get('room_reservations_max_length_extended', 120) : variable_get('room_reservations_max_length_standard', 120); + $max_slots = $max_length / 30; + $search_items = array(); + for ($x = 0; $x < 8; $x++) { + $search_items = array( + 'date' => '1999-01-01', + 'start_time' => '9999', + 'length' => 999, + ); + } + $day = $yyyymmdd; + $hours = _room_reservations_hours(); + $time_found = FALSE; + $time_slot = NULL; + while (!$time_found) { + $time_slot = array_pop($hours); + if ($time_slot['time'] == $time) { + $time_found = TRUE; + } + } + for ($x = 0; $x < $max_slots; $x++) { + if ($time_slot == NULL) { + $day = $previous_day; + $hours = _room_reservations_hours(); + $time_slot = array_pop($hours); + } + $search_item['date'] = $day; + $search_item['start_time'] = $time_slot['time']; + $search_item['length'] = 30 * $x; + $search_items[] = $search_item; + $time_slot = array_pop($hours); + } + $sql = " + SELECT id FROM {room_reservations} + WHERE ( + (deleted = 'N' AND room = :room AND date = :date0 AND time = :time0 AND length > :length0) + OR + (deleted = 'N' AND room = :room AND date = :date1 AND time = :time1 AND length > :length1) + OR + (deleted = 'N' AND room = :room AND date = :date2 AND time = :time2 AND length > :length2) + OR + (deleted = 'N' AND room = :room AND date = :date3 AND time = :time3 AND length > :length3) + OR + (deleted = 'N' AND room = :room AND date = :date4 AND time = :time4 AND length > :length4) + OR + (deleted = 'N' AND room = :room AND date = :date5 AND time = :time5 AND length > :length5) + OR + (deleted = 'N' AND room = :room AND date = :date6 AND time = :time6 AND length > :length6) + OR + (deleted = 'N' AND room = :room AND date = :date7 AND time = :time7 AND length > :length7) + ) + "; + $conflicts_found = FALSE; + $conflicts_found = db_query($sql, array( + ':room' => $room, + ':date0' => $search_items[0]['date'], ':time0' => $search_items[0]['start_time'], ':length0' => $search_items[0]['length'], + ':date1' => $search_items[1]['date'], ':time1' => $search_items[1]['start_time'], ':length1' => $search_items[1]['length'], + ':date2' => $search_items[2]['date'], ':time2' => $search_items[2]['start_time'], ':length2' => $search_items[2]['length'], + ':date3' => $search_items[3]['date'], ':time3' => $search_items[3]['start_time'], ':length3' => $search_items[3]['length'], + ':date4' => $search_items[4]['date'], ':time4' => $search_items[4]['start_time'], ':length4' => $search_items[4]['length'], + ':date5' => $search_items[5]['date'], ':time5' => $search_items[5]['start_time'], ':length5' => $search_items[5]['length'], + ':date6' => $search_items[6]['date'], ':time6' => $search_items[6]['start_time'], ':length6' => $search_items[6]['length'], + ':date7' => $search_items[7]['date'], ':time7' => $search_items[7]['start_time'], ':length7' => $search_items[7]['length']) + )->rowCount(); + return $conflicts_found; +} + +/** + * Determine valid reservation lengths of time. + * + * Determines which lengths of time are valid for a reservation for a particular + * room starting at particular time. Valid lengths are limited by the + * following: + * (1) Previously scheduled reservations. + * (2) Building close times. + * (3) Last time slot of the day. Reservations possibly end 15 minutes before + * the building closes. + * + * @param string $room + * The room that is being reserved. + * @param string $yyyymmdd + * The date of the start time for the reservation, in the format 'yyyy-mm-dd'. + * @param string $time + * The start time for the reservation, in military time. + * 9:00 AM is represented as '0900', and 9:00 PM is represented as '2100'. + * @param int $id + * The id of the reservation being made. + * @param int $all + * By default we do not check the first time slot since it should be true or we wouldn't have been able to pick it on calendar + * but, for repeating reserverations we need to check all slots. + * + * @return array + * An array with an element for each possible reservation length of time, + * and an indicator showing whether that particular length is valid for the + * reservation being made. + */ +function _room_reservations_valid_lengths($rid, $yyyymmdd, $time, $id = NULL, $all = FALSE) { + // let's first ensure this is a valid RID + $rooms = _room_reservations_rooms(); + if (!isset($rooms[$rid])) { + return null; + } + + $max_length = user_access('create room reservations extended length') + ? variable_get('room_reservations_max_length_extended', 120) + : variable_get('room_reservations_max_length_standard', 120); + $max_slots = $max_length / 30; + $valid_lengths = array(); + for ($x = 30; $x <= $max_length; $x += 30) { + $valid_lengths[] = array( + 'length' => $x, + 'is_valid' => TRUE, + ); + } + // Divide the maximum reservation length into 30 minute time slots and determine the start date and time of each of these slots. + $search_items = array(); + // Time slots for the first day. + $day = $yyyymmdd; + $hours = _room_reservations_hours(); + $x = 0; + $include = FALSE; + foreach ($hours as $time_slot) { + if ($time_slot['time'] == $time) { + $include = TRUE; + } + if ($include) { + $search_item['date'] = $day; + $search_item['start_time'] = $time_slot['time']; + $search_items[] = $search_item; + $x++; + } + if ($x == $max_slots) { + $include = FALSE; + break; + } + } + // If it's possible for the reservation to span 2 days, get the time slots + // for the subsequent day. + if ($x < $max_slots) { + $include = TRUE; + $day = date('Y-m-d', strtotime("$yyyymmdd +1 days")); + $hours = _room_reservations_hours(); + foreach ($hours as $time_slot) { + if ($include) { + $search_item['date'] = $day; + $search_item['start_time'] = $time_slot['time']; + $search_items[] = $search_item; + $x++; + } + if ($x == $max_slots) { + $include = FALSE; + break; + } + } + } + + // Determine if the reservation at each possible length would conflict with another reservation. If so, set is_valid for that length to FALSE. + // The first time slot has already been validated and does not need to be checked again. + if ($all) { + $start = 0; + } + else { + $start = 1; + } + for ($x = $start; $x < $max_slots; $x++) { + $valid_length = $valid_lengths[$x]; + if ($valid_length['is_valid']) { + $search_item = $search_items[$x]; + $date = $search_item['date']; + $start_time = $search_item['start_time']; + + $conflicts_found = false; + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_reservation') + ->fieldCondition('reservation_date', 'value', $date, '=') + ->fieldCondition('reservation_time', 'value', $start_time, '=') + ->fieldCondition('reservation_room', 'target_id', $rid, '=') + // EFQ respects access so let's load the reservations as user 1 to make sure we get them all + ->addMetaData('account', user_load(1)); + // if editing a reservation (series) we need to not include any of the reservations in current series + if (arg(2) == 'edit') { + $nid = arg(1); + $res = node_load($nid); + if (isset($res->reservation_series_id[LANGUAGE_NONE][0]['value']) && $sid = $res->reservation_series_id[LANGUAGE_NONE][0]['value']) { + $query->fieldCondition('reservation_series_id', 'value', $sid, '!='); + } + } + $result = $query->execute(); + if (isset($result['node'])) { + $conflicts_found = true; + for ($y = $x; $y < $max_slots; $y++) { + $valid_lengths[$y]['is_valid'] = FALSE; + } + } + } + } + // need to limit these valid lengths by a prebuffer if it exists for the category this room is in + // but only if not permissions to do this + if (!user_access('book over buffer')) { + $categories = _room_reservations_categories(); + $rooms = _room_reservations_rooms(); + $category = $categories[$rooms[$rid]['reservations_room_category'][LANGUAGE_NONE][0]['target_id']]; + $preslots = $category['prebuffer'] / 30; + if ($preslots) { + foreach ($valid_lengths as $index => $length) { + if ($length['is_valid'] == false) { + $last = $index; + break; + } + } + if (isset($last)) { + for ($x = $last - $preslots; $x <= $last; $x++) { + $valid_lengths[$x]['is_valid'] = false; + } + } + } + } + + // Determine if the reservation at each possible length would conflict with + // the hours that the building is closed. If so, set is_valid for that + // length to FALSE. The first time slot has already been validated and + // does not need to be checked again. + $_room_reservations_building_hours = _room_reservations_facility_hours(); + for ($x = 0; $x < $max_slots; $x++) { + $valid_length = $valid_lengths[$x]; + if ($valid_length['is_valid']) { + $conflicts_found = FALSE; + $search_item = $search_items[$x]; + $date = $search_item['date']; + $start_time = $search_item['start_time']; + $building_hours_day = $_room_reservations_building_hours[$date]; + // If building is closed, set conflict flag. + if (!$building_hours_day['open']) { + $conflicts_found = TRUE; + } + $int_start_time = intval($start_time); + $int_first_shift_open = intval($building_hours_day['first_shift_open']); + $int_first_shift_close = intval($building_hours_day['first_shift_close']); + // One shift. + if ((!$building_hours_day['open_24_hours']) && ($building_hours_day['shifts'] == 1) && + (($int_start_time < $int_first_shift_open) || ($int_start_time >= $int_first_shift_close))) { + $conflicts_found = TRUE; + } + // Two shifts. + $int_second_shift_open = intval($building_hours_day['second_shift_open']); + $int_second_shift_close = intval($building_hours_day['second_shift_close']); + if ((!$building_hours_day['open_24_hours']) && + ($building_hours_day['shifts'] == 2) && + (($int_start_time < $int_first_shift_open) || + (($int_start_time >= $int_first_shift_close) && + ($int_start_time < $int_second_shift_open)) || + ($int_start_time >= $int_second_shift_close)) + ) { + $conflicts_found = TRUE; + } + if ($conflicts_found) { + for ($y = $x; $y < $max_slots; $y++) { + $valid_lengths[$y]['is_valid'] = FALSE; + } + } + } + } + + + /* + // Determine if the reservation at each possible length would be the last time + // slot before the building closes. If so, reduce the length of time for that + // time slot by 15 minutes. All reservations must end 15 minutes before the + // building closes. + $end_early = variable_get('room_reservations_end_early', 0); + if ($end_early) { + for ($x = 0; $x < $max_slots; $x++) { + $valid_length = $valid_lengths[$x]; + if ($valid_length['is_valid']) { + $last_time_slot = FALSE; + $search_item = $search_items[$x]; + $date = $search_item['date']; + $start_time = $search_item['start_time']; + $closing_times = _room_reservations_close_times($yyyymmdd); + if (in_array($start_time, $closing_times)) { + $normal_length = $valid_length['length']; + $adjusted_length = $normal_length - 15; + $valid_length['length'] = $adjusted_length; + $valid_lengths[$x] = $valid_length; + } + } + } + } + */ + + return $valid_lengths; +} + +/** + * Determine if the user has exceeded the maximum reservations per day. + * + * @global object $user + * Drupal user object. + * + * @param string $yyyymmdd + * The date for which maximum allowable reservations are being checked, in + * the format YYYY-MM-DD. + * + * @return bool + * TRUE - the maximum has been exceeded. + * FALSE - the maximum has not been exceeded. + */ +function _room_reservations_daily_max_exceeded($yyyymmdd) { + $max = variable_get('room_reservations_reservations_per_day', 1); + if (!$max) { + return FALSE; + } + global $user; + + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_reservation') + ->propertyCondition('uid', $user->uid) + ->fieldCondition('reservation_date', 'value', $yyyymmdd, '='); + $result = $query->execute(); + if (isset($result['node'])) { + $rids = array_keys($result['node']); + } + + $record_count = isset($rids) ? count($rids) : 0; + + if ($record_count < $max) { + return FALSE; + } + else { + return TRUE; + } +} + +/** + * Determine if the user has exceeded the total maximum number of reservations. + * + * @global object $user + * Drupal user object. + * + * @return bool + * TRUE - the maximum has been exceeded. + * FALSE - the maximum has not been exceeded. + */ +function _room_reservations_user_max_exceeded() { + $max = variable_get('room_reservations_reservations_per_user', 4); + if (!$max) { + return FALSE; + } + global $user; + $yyyymmdd = date('Y-m-d'); + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_reservation') + ->propertyCondition('uid', $user->uid) + ->fieldCondition('reservation_date', 'value', $yyyymmdd, '>='); + $result = $query->execute(); + if (isset($result['node'])) { + $rids = array_keys($result['node']); + } + + $record_count = isset($rids) ? count($rids) : 0; + + if ($record_count < $max) { + return FALSE; + } + else { + return TRUE; + } +} + +/** + * Create friendlier error message. + * + * This function works with the room_reservations_res_form_validate() + * function. When a user is filling out the form to reserve a room, it is + * possible that before she can submit the form, someone else will complete a + * reservation for that same room, same date, and some of the same time. + * If that should occur, and the user has requested a length of time that + * is no longer available, Drupal automatically creates the following error: + * 'An illegal choice has been detected. Please contact the site + * administrator.' Since this message is somewhat alarming to the user + * and not at all informative, it is replaced in this function with a more + * descriptive message. + * + * @param string $message + * A Drupal error message. + * + * @return object + * A string with a more user friendly message, or FALSE to indicate that + * no change needs to be made to the error message. + */ +function room_reservations_rewrite_error($message) { + if (strpos($message, 'An illegal choice has been detected. Please contact + the site administrator.') !== FALSE) { + return t('This room is no longer available for the length of time that + you selected. Please choose one of the lengths listed below.'); + } + return FALSE; +} + +/** + * Find the current user's reservations for the next 14 days. + * + * @global object $user + * A Drupal user object. + * + * @return array + * An array with each element representing one reservation that the user has + * made. + */ +function _room_reservations_user_reservations() { + $user_reservations = array(); + $all_hours = _room_reservations_hours(); + global $user; + if ($user->uid) { + $earliest_date = date('Y-m-d', strtotime(date('Y-m-d'))); + $latest_date = date('Y-m-d', strtotime("now +13 days")); + + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_reservation') + ->propertyCondition('uid', $user->uid) + ->fieldCondition('reservation_date', 'value', $earliest_date, '>=') + ->fieldCondition('reservation_date', 'value', $latest_date, '<=') + ->fieldOrderBy('reservation_date', 'value', 'DESC') + ->fieldOrderBy('reservation_time', 'value', 'DESC'); + $result = $query->execute(); + if (isset($result['node'])) { + $result_nids = array_keys($result['node']); + $results = entity_load('node', $result_nids); + foreach ($results as $data) { + $reservation = array(); + $reservation['id'] = $data->nid; + $unix_timestamp = strtotime($data->reservation_date[LANGUAGE_NONE][0]['value']); + $reservation['date'] = date("l, n/j", $unix_timestamp); + $reservation['time'] = ''; + foreach ($all_hours as $time_slot) { + if ($time_slot['time'] == $data->reservation_time[LANGUAGE_NONE][0]['value']) { + $reservation['time'] = $time_slot['display']; + break; + } + } + $user_reservations[] = $reservation; + } + } + } + return $user_reservations; +} + +/** + * Get list of wireless carriers. + * + * Retrieve from the database all of the wireless carriers that have been + * added to the application by the administrator. + * + * @return array + * An array representing the carriers. + */ +function _room_reservations_carriers() { + $carriers = array(); + $sql = "SELECT * FROM {room_reservations_variables} WHERE name = 'carrier' ORDER BY value"; + $results = db_query($sql); + foreach ($results as $data) { + $carrier = explode('~', $data->value); + $id = $data->id; + $carriers[$id] = check_plain($carrier[0]); + } + return $carriers; +} + +/** + * Get a carrier's sms address. + * + * When sending an sms text message, obtain the sms email address of the user's + * wireless carrier. + * + * @param int $carrier + * The user's wireless carrier, as selected on the reservation form. + * + * @return string + * The email address of the user's wireless carrier. + */ +function _room_reservations_carrier_address($carrier) { + $carrier_address = ''; + $sql = "SELECT * FROM {room_reservations_variables} WHERE name = 'carrier' ORDER BY id"; + $results = db_query($sql); + foreach ($results as $data) { + $car = explode('~', $data->value); + $id = $data->id; + if ($id == $carrier) { + $carrier_address = check_plain($car[1]); + break; + } + } + return $carrier_address; +} + +/** + * Send an sms text message. + * + * @param string $message_type + * Indicates whether the message is a confirmation or a reminder. + * @param array $params + * Reservation specific data to be inserted into the message template. + */ +function _room_reservations_send_sms($message_type, $params) { + $carrier_address = _room_reservations_carrier_address($params['carrier']); + $to = $params['phone'] . $carrier_address; + $subject = _room_reservations_replace_tokens(check_plain( + _room_reservations_get_variable('confirmation_header_text_sms')), $params); + $day_of_week = drupal_substr($params['day_of_week'], 0, 3) . '.'; + if ($message_type == 'confirmation') { + $message_body = _room_reservations_replace_tokens(check_plain( + _room_reservations_get_variable('confirmation_text_sms')), $params); + } + elseif ($message_type == 'reminder') { + $message_body = _room_reservations_replace_tokens(check_plain( + _room_reservations_get_variable('reminder_text_sms')), $params); + } + $message = wordwrap($message_body, 70); + $from = _room_reservations_get_variable('from_address_sms'); + $headers = "From: " . $from; + mail($to, $subject, $message, $headers); +} + +/** + * Replace tokens in messages. + * + * Inserts reservation specific information in email and text messages + * sent as confirmation and reminder of reservations. + * + * @param string $text + * The email or text message. + * @param array $parameters + * Reservation specific information to be inserted into the message. + * + * @return string + * The email or text message updated with reservation specific information. + */ +function _room_reservations_replace_tokens($text, $parameters) { + $tokens = array( + '%reservation_name' => $parameters['name'], + '%room' => $parameters['room'], + '%month_number' => $parameters['month_number'], + '%month' => $parameters['month'], + '%day_of_the_week' => $parameters['day_of_week'], + '%day' => $parameters['day'], + '%time' => $parameters['time'], + '%minutes' => $parameters['minutes'], + ); + foreach ($tokens as $key => $value) { + $text = str_replace($key, $value, $text); + } + return $text; +} + +/** + * Determines the default email address added to the reservation form. + * + * @global object $user + * The user object. + * + * @return string + * Email address. + */ +function _room_reservations_default_email() { + $default_option = _room_reservations_get_variable('default_email_address'); + $default_domain = _room_reservations_get_variable('default_email_domain'); + if (!isset($default_option)) { + return ''; + } + elseif ($default_option == 0) { + return ''; + } + elseif ($default_option == 1) { + global $user; + $logged_in = ($user->uid); + $email = ($logged_in) ? $user->name : ''; + return $email; + } + elseif ($default_option == 2) { + global $user; + $logged_in = ($user->uid); + $email = ($logged_in) ? $user->name . $default_domain : ''; + return $email; + } +} + +/** + * Get a variable from the room_reservations_variables table. + * + * @param string $key + * The key used to identify the variable. + * + * @return object + * The value of the variable, or FALSE, if a record could not be found with + * the supplied $key. + */ +function _room_reservations_get_variable($key) { + $sql = "SELECT value FROM {room_reservations_variables} WHERE name = :name"; + $result = db_query($sql, array(':name' => $key))->fetchField(); + if ($result) { + return $result; + } + else { + return FALSE; + } +} + +/** + * Save a module variable. + * + * Set a variable in the room_reservations_variables table. If a record + * with the key exists, update it. If a record with the key does not exist, + * insert a new record. + * + * @param string $key + * The key used to identify the variable. + * @param object $value + * The value of the variable. + * + * @return object + * A database query result resource. + */ +function _room_reservations_set_variable($key, $value) { + $sql = "SELECT id FROM {room_reservations_variables} WHERE name = :name"; + $id = db_query($sql, array(':name' => $key))->fetchField(); + if ($id) { + $sql = "UPDATE {room_reservations_variables} SET value = :value WHERE id = :id"; + $result = db_query($sql, array(':value' => $value, ':id' => $id)); + return $result; + } + else { + $sql = "INSERT INTO {room_reservations_variables} (name, value) VALUES (:name, :value)"; + $result = db_query($sql, array(':name' => $key, ':value' => $value)); + return $result; + } +} + +/** + * Delete a variable in the room_reservations_variables table. + * + * @param string $key + * The key used to identify the variable. + * + * @return object + * A database query result resource, or FALSE if the record to be deleted + * could not be found in the database. + */ +function _room_reservations_delete_variable($key) { + $sql = "SELECT id FROM {room_reservations_variables} WHERE name = :name"; + $id = db_query($sql, array(':name' => $key))->fetchField(); + if ($id) { + $sql = "DELETE FROM {room_reservations_variables} WHERE id = :id"; + $result = db_query($sql, array(':id' =>$id)); + return $result; + } + return FALSE; +} + +function _room_reservations_is_slot_free($rid, $yyyymmdd, $time, $length) { + $slots = _room_reservations_valid_lengths($rid, $yyyymmdd, $time, NULL, true); + foreach ($slots as $slot) { + if ($slot['length'] == $length && $slot['is_valid']) { + return true; + } + } + return false; +} + +function _room_reservations_yyyymmdd($month, $day) { + // determine if this year or next year + $yearnow = date('Y'); + $absdaynow = date('z'); + $absdaydefault = date('z', mktime(0, 0, 0, $month, $day, $yearnow)); + if ($absdaynow > $absdaydefault) { + $year = $yearnow + 1; + } + else { + $year = $yearnow; + } + return date('Y-m-d', strtotime($year . '-' . $month . '-' . $day)); +} diff --git a/room_reservations.info b/room_reservations.info new file mode 100644 index 0000000..df2ac0b --- /dev/null +++ b/room_reservations.info @@ -0,0 +1,27 @@ +name = Room Reservations +description = Room reservation booking system. +core = 7.x +package = Other +dependencies[] = token +dependencies[] = entityreference +dependencies[] = field +dependencies[] = views +;dependencies[] = qtip +dependencies[] = date +dependencies[] = date_popup +files[] = room_reservations.inc + + +; Information added by Drupal.org packaging script on 2015-01-13 +version = "7.x-1.2" +core = "7.x" +project = "room_reservations" +datestamp = "1421188081" + + +; Information added by Drupal.org packaging script on 2016-12-15 +version = "7.x-2.x-dev" +core = "7.x" +project = "room_reservations" +datestamp = "1481826785" + diff --git a/room_reservations.install b/room_reservations.install new file mode 100644 index 0000000..0cc71d4 --- /dev/null +++ b/room_reservations.install @@ -0,0 +1,234 @@ + '', + 'fields' => array( + 'id' => array( + 'description' => '', + 'type' => 'serial', + 'not null' => TRUE, + ), + 'name' => array( + 'description' => '', + 'type' => 'varchar', + 'length' => '128', + 'not null' => TRUE, + ), + 'value' => array( + 'description' => '', + 'type' => 'text', + 'size' => 'big', + 'not null' => TRUE, + ), + ), + 'primary key' => array('id'), + 'indexes' => array( + 'name' => array('name'), + ), + ); + + return $schema; +} + +/** +* The information that the module should remove includes: +* +* variables that the module has set using variable_set() or system_settings_form() +* modifications to existing tables +* +* The module should not remove its entry from the {system} table. Database tables defined by hook_schema() will be removed automatically. +* +* NOTE - but our tables are node tables and not created via hook_schema so must be cleaned out here +* +*/ +function room_reservations_uninstall() { + /* + db_query("DELETE FROM {node_type} WHERE type LIKE 'room_reservations_%'"); + db_query("DELETE FROM {field_config} WHERE field_name LIKE 'rooms_%'"); + + db_query("DELETE FROM {field_config_instance} WHERE bundle LIKE 'room_reservations_category'"); + db_query("DELETE FROM {field_config_instance} WHERE bundle LIKE 'room_reservations_room'"); + + + + db_drop_table('{field_data_rooms_room_category}'); + db_drop_table('{field_data_rooms_room_capacity}'); + db_drop_table('{field_revision_rooms_room_category}'); + db_drop_table('{field_revision_rooms_room_capacity}'); + + db_drop_table('{field_data_reservation_room}'); + db_drop_table('{field_revision_reservation_room}'); */ + + // remove reservation type + db_query("DELETE FROM {node_type} WHERE type LIKE 'room_reservations_%'"); + db_query("DELETE FROM {field_config} WHERE field_name LIKE 'reservation%'"); + db_query("DELETE FROM {field_config} WHERE field_name IN ('room_category', 'room_capacity')"); + db_query("DELETE FROM {field_config_instance} WHERE bundle LIKE 'room_reservations_%'"); + + // remove our custom fields - this means all RR content will be removed including room nodes + $fields = array( + 'reservation_block_title', + 'reservation_date', + 'reservation_length', + 'reservation_repeat_type', + 'reservation_repeat_until', + 'reservation_room', + 'reservation_series_id', + 'reservation_time', + 'reservations_display_order', + 'room_capacity', + 'room_category', + ); + foreach ($fields as $field) { + db_drop_table('field_data_' . $field); + db_drop_table('field_revision_' . $field); + } + +} + +/** +* attempt to clean up the mess of field names +*/ +function room_reservations_update_7000() { + db_change_field('field_data_room_capacity', 'room_capacity_value', 'reservations_room_capacity_value', array( + 'type' => 'int', + 'length' => 11, + 'description' => 'Number of people the room can accommodate.')); + db_change_field('field_revision_room_capacity', 'room_capacity_value', 'reservations_room_capacity_value', array( + 'type' => 'int', + 'length' => 11, + 'description' => 'Number of people the room can accommodate.')); + db_change_field('field_data_room_category', 'room_category_target_id', 'reservations_room_category_target_id', array( + 'type' => 'int', + 'length' => 10, + 'description' => 'Which category the room is in.')); + db_change_field('field_revision_room_category', 'room_category_target_id', 'reservations_room_category_target_id', array( + 'type' => 'int', + 'length' => 10, + 'description' => 'Which category the room is in.')); + db_change_field('field_data_reservation_display_order', 'reservation_display_order_value', 'reservations_display_order_value', array( + 'type' => 'int', + 'length' => 11, + 'description' => 'Order to display rooms and categories.')); + db_change_field('field_revision_reservation_display_order', 'reservation_display_order_value', 'reservations_display_order_value', array( + 'type' => 'int', + 'length' => 11, + 'description' => 'Order to display rooms and categories.')); + db_rename_table('field_data_room_capacity', 'field_data_reservations_room_capacity'); + db_rename_table('field_data_room_category', 'field_data_reservations_room_category'); + db_rename_table('field_revision_room_capacity', 'field_revision_reservations_room_capacity'); + db_rename_table('field_revision_room_category', 'field_revision_reservations_room_category'); + db_rename_table('field_data_reservation_display_order', 'field_data_reservations_display_order'); + db_rename_table('field_revision_reservation_display_order', 'field_revision_reservations_display_order'); + + db_drop_table('field_data_room_reservations_room_capacity'); + db_drop_table('field_data_room_reservations_room_category'); + db_drop_table('field_data_rooms_room_capacity'); + db_drop_table('field_data_rooms_room_category'); + db_drop_table('field_revision_room_reservations_room_capacity'); + db_drop_table('field_revision_room_reservations_room_category'); + db_drop_table('field_revision_rooms_room_capacity'); + db_drop_table('field_revision_rooms_room_category'); + + db_drop_table('field_data_reservation_carrier'); + db_drop_table('field_data_reservation_email_addresses'); + db_drop_table('field_data_reservation_phone'); + db_drop_table('field_data_reservation_txtmsg'); + db_drop_table('field_revision_reservation_carrier'); + db_drop_table('field_revision_reservation_email_addresses'); + db_drop_table('field_revision_reservation_phone'); + db_drop_table('field_revision_reservation_txtmsg'); + + db_update('field_config') + ->fields(array( + 'field_name' => 'reservations_room_capacity', + )) + ->condition('field_name', 'room_capacity', '=') + ->execute(); + db_update('field_config') + ->fields(array( + 'field_name' => 'reservations_room_category', + )) + ->condition('field_name', 'room_category', '=') + ->execute(); + db_update('field_config') + ->fields(array( + 'field_name' => 'reservations_display_order', + )) + ->condition('field_name', 'reservation_display_order', '=') + ->execute(); + db_update('field_config_instance') + ->fields(array( + 'field_name' => 'reservations_room_capacity', + )) + ->condition('field_name', 'room_capacity', '=') + ->execute(); + db_update('field_config_instance') + ->fields(array( + 'field_name' => 'reservations_room_category', + )) + ->condition('field_name', 'room_category', '=') + ->execute(); + db_update('field_config_instance') + ->fields(array( + 'field_name' => 'reservations_display_order', + )) + ->condition('field_name', 'reservation_display_order', '=') + ->execute(); +} + +/** +* fix a couple of the Notices - due to incomplete node defs +*/ +function room_reservations_update_7001() { + foreach (_room_reservations_installed_fields_room() as $field) { + field_update_field($field); + } + foreach (_room_reservations_installed_instances_room() as $instance) { + $instance['entity_type'] = 'node'; + $instance['bundle'] = 'room_reservations_room'; + field_update_instance($instance); + } +} + +/** +* Add Cateogry field Minimum Advanced Booking, +* Add Cateogry field Setup time, +* Add Cateogry field Takedown time +*/ +function room_reservations_update_7002() { + $existing = field_info_field_map(); + $create = array( + 'reservations_minadvbooking', + 'reservations_prebuffer', + 'reservations_postbuffer', + ); + foreach (_room_reservations_installed_fields_category() as $field) { + if (isset($existing[$field['field_name']])) { + continue; + } + if (in_array($field['field_name'], $create)) { + field_create_field($field); + } + } + foreach (_room_reservations_installed_instances_category() as $instance) { + if (isset($existing[$instance['field_name']])) { + continue; + } + $instance['entity_type'] = 'node'; + $instance['bundle'] = 'room_reservations_category'; + if (in_array($instance['field_name'], $create)) { + field_create_instance($instance); + } + } +} diff --git a/room_reservations.js b/room_reservations.js new file mode 100644 index 0000000..bf061c4 --- /dev/null +++ b/room_reservations.js @@ -0,0 +1,64 @@ +(function ($) { + +Drupal.behaviors.room_reservations = {}; + +Drupal.behaviors.room_reservations.attach = function(context) { + // set default Category tab if one is set in URL anchor + var anchor = window.location.hash; + if (anchor) { + $('.room-tabs a.active').removeClass('active'); + $('.room-tabs li a[href=' + anchor + ']').addClass('active'); + $('.panel').hide(); + $(anchor).show(); + } + + // show the selected category panel + $('.room-tabs a').click(function() { + $this = $(this); + $('.panel').hide(); + $('.room-tabs a.active').removeClass('active'); + $this.addClass('active').blur(); + var panel = $this.attr('href'); + $(panel).fadeIn(250); + return false; + }); + + // change calendar date displayed + $('#edit-date-datepicker-popup-0').change(function() { + var datebits = $(this).val().split('/'); + var formatarr = Drupal.settings.room_reservations.dateformat.split('/'); + var dateobj = new Object(); + $.each(formatarr, function(index, value) { + dateobj[value] = datebits[index]; + }); + var val = dateobj.m + '/' + dateobj.d; + var path = window.location.href; + var loc = path.lastIndexOf('room_reservations'); + if (loc != -1) { + var end = loc + 17; + var newpath = path.substring(0, end).concat('/'); + } else { + var newpath = path.concat('room_reservations/'); + } + window.location.href = newpath.concat(val); + }); + + // show form fields for text message confirmation and reminder + $('#edit-textmsg').each(function() { + if ($(this).attr('checked')) { + $('#txtmsg-fields').slideDown('fast'); + } + else { + $('#txtmsg-fields').slideUp('fast'); + } + }); + $('#edit-textmsg').click(function() { + if ($(this).attr('checked')) { + $('#txtmsg-fields').slideDown('fast'); + } + else { + $('#txtmsg-fields').slideUp('fast'); + } + }); +}; +}(jQuery)); diff --git a/room_reservations.module b/room_reservations.module new file mode 100644 index 0000000..023ecae --- /dev/null +++ b/room_reservations.module @@ -0,0 +1,872 @@ + 2); + } +} + +/** + * Implements hook_menu(). + */ +function room_reservations_menu() { + $app_title = variable_get('room_reservations_title', 'Room Reservations'); + $items['admin/config/system/room_reservations'] = array( + 'title' => $app_title, + 'description' => 'Configure room reservation options', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('room_reservations_admin_settings'), + 'access arguments' => array('administer room reservations system'), + 'type' => MENU_NORMAL_ITEM, + 'file' => 'room_reservations.admin.inc', + 'weight' => 0, + ); + $items['admin/config/system/room_reservations/settings'] = array( + 'title' => 'Settings', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'access arguments' => array('administer room reservations system'), + 'weight' => 10, + ); + $items['admin/config/system/room_reservations/settings/general'] = array( + 'title' => 'General', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'access arguments' => array('administer room reservations system'), + 'weight' => 10, + ); + $items['admin/config/system/room_reservations/hours'] = array( + 'title' => 'Hours', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('room_reservations_admin_settings_default_hours'), + 'access arguments' => array('administer room reservations system'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'room_reservations.admin.inc', + 'weight' => 20, + ); + $items['admin/config/system/room_reservations/hours/default_hours'] = array( + 'title' => 'Default Hours', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'access arguments' => array('administer room reservations system'), + 'weight' => 10, + ); + $items['admin/config/system/room_reservations/hours/daily_hours'] = array( + 'title' => 'Daily Hours', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('room_reservations_admin_settings_daily_hours'), + 'access arguments' => array('administer room reservations system'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'room_reservations.admin.inc', + 'weight' => 20, + ); + $items['admin/config/system/room_reservations/page'] = array( + 'title' => 'Display Text', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('room_reservations_admin_settings_page'), + 'access arguments' => array('administer room reservations system'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'room_reservations.admin.inc', + 'weight' => 30, + ); + /*$items['admin/config/system/room_reservations/sms'] = array( + 'title' => 'SMS', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('room_reservations_admin_settings_sms'), + 'access arguments' => array('administer room reservations system'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'room_reservations.admin.inc', + 'weight' => 40, + ); + $items['admin/config/system/room_reservations/sms/networks'] = array( + 'title' => 'Wireless Carriers', + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'access arguments' => array('administer room reservations system'), + 'weight' => 10, + ); + $items['admin/config/system/room_reservations/sms/add'] = array( + 'title' => 'Add Carrier', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('room_reservations_admin_settings_sms_add'), + 'access arguments' => array('administer room reservations system'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'room_reservations.admin.inc', + 'weight' => 20, + ); + $items['admin/config/system/room_reservations/sms/delete'] = array( + 'title' => 'Delete Carrier', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('room_reservations_admin_settings_sms_delete'), + 'access arguments' => array('administer room reservations system'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'room_reservations.admin.inc', + 'weight' => 30, + ); + $items['admin/config/system/room_reservations/messages'] = array( + 'title' => 'Messages', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('room_reservations_admin_settings_email'), + 'access arguments' => array('administer room reservations system'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'room_reservations.admin.inc', + 'weight' => 50, + ); + $items['admin/config/system/room_reservations/messages/email'] = array( + 'title' => 'Email Messages', + 'access arguments' => array('administer room reservations system'), + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => 10, + ); + $items['admin/config/system/room_reservations/messages/text'] = array( + 'title' => 'SMS Messages', + 'page callback' => 'drupal_get_form', + 'page arguments' => array('room_reservations_admin_settings_text'), + 'access arguments' => array('administer room reservations system'), + 'type' => MENU_LOCAL_TASK, + 'file' => 'room_reservations.admin.inc', + 'weight' => 20, + ); + */ + + /* + $items['room_reservations/add'] = array( + 'title' => 'Reservation Details', + 'page callback' => 'room_reservations_res_add', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + 'file' => '/controller/room_reservations_reservation.controller.inc', + ); + $items['room_reservations/view'] = array( + 'title' => 'Reservation Details', + 'page callback' => 'room_reservations_res_view', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + 'file' => '/controller/room_reservations_reservation.controller.inc', + ); + $items['room_reservations/delete'] = array( + 'title' => 'Reservation Details', + 'page callback' => 'room_reservations_res_delete', + 'access callback' => TRUE, + 'type' => MENU_CALLBACK, + 'file' => '/controller/room_reservations_reservation.controller.inc', + ); */ + $items['room_reservations'] = array( + 'title' => variable_get('room_reservations_title', 'Room Reservations'), + 'page callback' => 'room_reservations', + 'access arguments' => array('view room reservations calendar'), + 'type' => MENU_NORMAL_ITEM, + 'file' => '/controller/room_reservations_calendar.controller.inc', + ); + $items['room_reservations/calendar'] = array( + 'title' => 'Calendar', + 'access arguments' => array('view room reservations calendar'), + 'type' => MENU_DEFAULT_LOCAL_TASK, + 'weight' => 10, + ); + $items['room_reservations/policies'] = array( + 'title' => 'Policies', + 'page callback' => 'room_reservations_policies', + 'access arguments' => array('view room reservations calendar'), + 'type' => MENU_LOCAL_TASK, + 'weight' => 30, + 'file' => '/controller/room_reservations.controller.inc', + ); + return $items; +} + +/** + * Implements hook_init(). +(). + */ +function room_reservations_init() { + // Add javascript. + drupal_add_js(drupal_get_path('module', 'room_reservations') . '/room_reservations.js'); + // Add css. + drupal_add_css(drupal_get_path('module', 'room_reservations') . '/room_reservations.css'); + // Prevent this module from caching. + if ((drupal_match_path($_GET['q'], 'room_reservations')) || (drupal_match_path($_GET['q'], 'room_reservations/*'))) { + $GLOBALS['conf']['cache'] = FALSE; + } +} + +/** + * Implements hook_permission(). + */ +function room_reservations_permission() { + return array( + 'administer room reservations system' => array( + 'title' => t('Administer the room reservations system'), + 'description' => t('Allows administration of room categories, rooms and reservations. Allows administering all system configuration settings.'), + ), + 'edit any room reservation' => array( + 'title' => t('Edit any room reservation'), + 'description' => t('Allows editing of all room reservations in the system.'), + ), + 'delete any room reservation' => array( + 'title' => t('Delete any room reservation'), + 'description' => t('Allows deleting of any room reservation in the system.'), + ), + 'create room reservations standard' => array( + 'title' => t('Create room reservations as well as edit or cancel your own reservations. Book in advance standard limit.'), + 'description' => t('Create room reservations as well as edit or cancel your own reservations'), + ), + 'create room reservations extended' => array( + 'title' => t('Create room reservations as well as edit or cancel your own reservations. Book in advance extended limit.'), + 'description' => t('Create room reservations as well as edit or cancel your own reservations'), + ), + 'bypass minimum advance booking' => array( + 'title' => t('Bypass minimum advanced booking limit.'), + 'description' => t('Bypass minimum advanced booking limit.'), + ), + 'bypass reservations limiters' => array( + 'title' => t('Bypass number of reservations limiters.'), + 'description' => t('Bypass open reservations per user and reservations per day limits.'), + ), + 'book over buffer' => array( + 'title' => t('Allow booking over setup/takedown buffers.'), + 'description' => t('Allow user to book over top of the setup/takedown buffer for an existing reservation.'), + ), + 'create room reservations extended length' => array( + 'title' => t('Create extended maximum length room reservations.'), + 'description' => t('Create extended length reservations.'), + ), + 'view room reservations calendar' => array( + 'title' => t('View the room reservations calendar'), + 'description' => t('Allowed to view the reservations calendar but does not allow creating new reservations.'), + ), + ); +} + +/** + * Implements hook_validate(). + */ +function room_reservations_validate($node, $form, &$form_state) { + // If this is not a room reservation or we are not actually creating a new + // node we don't need to check for max reservations so return TRUE. + if ($node->type != 'room_reservations_reservation' || + $form_state['values']['op'] == 'Cancel Reservation' || !empty($node->nid)) { + return TRUE; + } + // TODO pull into a function as this code is duplicated here and in the form function. + if ($nid = $form['nid']['#value']) { + $res = $form['#node']; + $d = $res->reservation_date['und'][0]['value']; + $yyyymmdd = date('Y-m-d', strtotime($d)); + } + + // Group name must be 25 characters or less. + if ($node->type == 'room_reservations_reservation' && strlen($node->title) > 25 + && $form_state['values']['op'] == 'Save') { + form_error($form, t('Group name must be less than 25 characters.')); + } + + // CREATE NEW. + else { + // Determine if this year or next year. + $yearnow = date('Y'); + $absdaynow = date('z'); + $absdaydefault = date('z', mktime(0, 0, 0, arg(3), arg(4), $yearnow)); + if ($absdaynow > $absdaydefault) { + $year = $yearnow + 1; + } + else { + $year = $yearnow; + } + $yyyymmdd = date('Y-m-d', strtotime($year . '-' . arg(3) . '-' . arg(4))); + } + + + if (user_access('bypass reservations limiters')) { + return; + } + else { + if (_room_reservations_daily_max_exceeded($yyyymmdd)) { + form_error($form, t('You have exceeded your daily reservation limit.')); + } + if (_room_reservations_user_max_exceeded()) { + form_error($form, t('You have exceeded your total number of reservations allowed.')); + } + } +} + +/** + * Implements hook_node_access() + * + * rev 1.3 - change all node access deny to node access ignore so that other modules may better integrate + * - i.e. if DENY is set no other module can override that + * + * NOTE - this hook is not called for user 1 + */ +function room_reservations_node_access($nodetype_or_node, $op, $account) { + if (is_object($nodetype_or_node)) { + $nodetype = $nodetype_or_node->type; + } + else { + $nodetype = $nodetype_or_node; + } + switch ($nodetype) { + case 'room_reservations_category': + case 'room_reservations_room': + switch ($op) { + case 'create': + case 'update': + case 'delete': + return user_access('administer room reservations system') ? NODE_ACCESS_ALLOW : NODE_ACCESS_IGNORE; + } + break; + + case 'room_reservations_reservation': + global $user; + // if it is my reservation; do nothing (let Drupal handle as usual) + if ((is_object($nodetype_or_node) && $nodetype_or_node->uid == $user->uid)) { + return NODE_ACCESS_ALLOW; + } + else { + switch ($op) { + case 'create': + // if we are trying to create a reservation let's make sure user hasn't just entered dates via url + // since available lengths takes allowed dates into account; lets just use that + $yyyymmdd = _room_reservations_yyyymmdd(arg(3), arg(4)); + $lengths = _room_reservations_valid_lengths(arg(6), $yyyymmdd, arg(5)); + if (count($lengths)) { + foreach ($lengths as $length) { + if ($length['is_valid']) { + return (user_access('create room reservations standard') || user_access('create room reservations extended') + || user_access('administer room reservations system')) ? NODE_ACCESS_ALLOW : NODE_ACCESS_IGNORE; + } + } + } + return NODE_ACCESS_DENY; + case 'update': + return (user_access('administer room reservations system') || user_access('edit any room reservation')) ? NODE_ACCESS_ALLOW : NODE_ACCESS_IGNORE; + case 'delete': + return (user_access('administer room reservations system') || user_access('delete any room reservation')) ? NODE_ACCESS_ALLOW : NODE_ACCESS_IGNORE; + case 'view': + return user_access('administer room reservations system') ? NODE_ACCESS_ALLOW : NODE_ACCESS_IGNORE; + } + } + break; + } +} + +/** +* remove std Node perms for all types associated with Room Reservations +* +* @param mixed $form +* @param mixed $form_state +* @param mixed $form_id +*/ +function room_reservations_form_user_admin_permissions_alter(&$form, &$form_state, $form_id) { + foreach ($form['permission'] as $perm => $value) { + if (stristr($perm, 'content') && stristr($perm, 'room_reservations_')) { + unset($form['permission'][$perm]); + } + } + + foreach ($form['checkboxes'] as $box => $settings) { + foreach ($settings['#options'] as $index => $option) { + if (stristr($index, 'content') && stristr($index, 'room_reservations_')) { + unset($form['checkboxes'][$box]['#options'][$index]); + } + } + } +} + +/** + * Description. + * + * @param mixed $form + * $form + * @param mixed $form_state + * $form_state + * @param mixed $form_id + * $form_id + */ +function room_reservations_form_room_reservations_reservation_node_form_alter(&$form, &$form_state, $form_id) { + // Params either passed in on url - CREATE or pulled from node - EDIT. + // EDIT. + if (user_access('administer site configuration') && isset($_GET['edit']) && $_GET['edit'] == 'standard') { + return; + } + + // EDIT + if ($nid = $form['nid']['#value']) { + $res = $form['#node']; + $rid = $res->reservation_room[LANGUAGE_NONE][0]['target_id']; + $d = $res->reservation_date[LANGUAGE_NONE][0]['value']; + $yyyymmdd = date('Y-m-d', strtotime($d)); + $t = $res->reservation_time[LANGUAGE_NONE][0]['value']; + } + + // CREATE NEW + else { + // if no parameters passed; lets redirect to calendar page + if (!arg(3) && !arg(4)) { + drupal_goto('room_reservations'); + } + // determine if this year or next year + $yearnow = date('Y'); + $absdaynow = date('z'); + $absdaydefault = date('z', mktime(0, 0, 0, arg(3), arg(4), $yearnow)); + if ($absdaynow > $absdaydefault) { + $year = $yearnow + 1; + } + else { + $year = $yearnow; + } + $rid = arg(6); + $yyyymmdd = date('Y-m-d', strtotime($year . '-' . arg(3) . '-' . arg(4))); + $d = $yyyymmdd . ' 00:00:00'; + $t = arg(5); + } + + $room = node_load($rid); + $date = date('l, M d, Y', strtotime($d)); + $time = _room_reservations_display_time($t); + + $form['reservation_heading']['#type'] = 'markup'; + $form['reservation_heading']['#markup'] = ' +

' . t('Reservation booking for') . ':

' . t('Room') . ': ' . $room->title . '
+
' . t('Date') . ': ' . $date . '
+
' . t('Time') . ': ' . $time . '

'; + + $form['reservation_room']['#access'] = 0; + $form['reservation_date']['#access'] = 0; + $form['reservation_time']['#access'] = 0; + $form['reservation_room'][LANGUAGE_NONE]['#default_value'] = $room->nid; + + // Date fields are a major pita, setting default does nothing here; + // we need to carry this forward to the node_presave hook + $form['#node']->date_default = $d; + $form['reservation_time'][LANGUAGE_NONE][0]['value']['#default_value'] = $t; + + // remove Preview - can likely do this in node def + unset($form['actions']['preview']); + + // hide Series ID field + $form['reservation_series_id']['#access'] = FALSE; + + // if we are editing; let's do some extra things: + // - disable Repeat options + // - (series) add msg that we are editing a series and link to edit just that entry + if ($nid) { + $form['reservation_repeat_type']['#disabled'] = TRUE; + $form['reservation_repeat_until']['#disabled'] = TRUE; + + // not sure why this was here; if req i don't think structure during edit is correct here + /* + if ($form['reservation_repeat_type'][LANGUAGE_NONE][0]['#value'] > 1) { + $form['reservation_repeat_until'][LANGUAGE_NONE][0]['#value'] = $form['reservation_repeat_until'][LANGUAGE_NONE][0]['#default_value']['value']; + } + */ + + $form['reservation_repeat_until']['und'][0]['#value'] = $form['reservation_repeat_until']['und'][0]['#default_value']['value']; + + // Form alter is hit in/out of form so we alter only in so we don't show + // messages after form is submitted - !count($form_state['input']). + if ($form['reservation_series_id'][LANGUAGE_NONE][0]['value']['#default_value'] && !count($form_state['input'])) { + if (isset($_GET['single'])) { + $series_link = l(t('Click here'), "node/$nid/edit"); + drupal_set_message(t('NOTE: you are editing a SINLGE day in a SERIES of reservations. Any changes made here will impact only the reservation + for this day. !link if you want to edit the entire series.', array('!link' => $series_link)), 'warning'); + } + else { + $single_link = l(t('Click here'), "node/$nid/edit", array('query' => array('single' => 1))); + drupal_set_message(t('NOTE: you are editing a SERIES of reservations. Any changes made here will impact all reservations in this + series. !link if you only want to edit this specific day in this series.', array('!link' => $single_link)), 'warning'); + } + } + + // And then parts that have to be here on both passes. + if ($form['reservation_series_id'][LANGUAGE_NONE][0]['value']['#default_value']) { + if (isset($_GET['single'])) { + // Re-label Delete to Cancel Reservation. + $form['actions']['delete']['#value'] = t('Cancel Reservation for This Day'); + } + else { + // Remove single node delete and add Delete Series button. + unset($form['actions']['delete']); + $form['actions']['delete_series'] = array( + '#type' => 'submit', + '#value' => t('Cancel Entire Series Reservation'), + '#weight' => 20, + '#submit' => array('_room_reservations_series_delete'), + ); + } + } + + // And if not part of a series; let's change DELETE button. + if (!$form['reservation_series_id'][LANGUAGE_NONE][0]['value']['#default_value']) { + $form['actions']['delete']['#value'] = t('Cancel Reservation'); + } + } + + // Only show repeat reservation controls to users with 'Bypass number of reservations limiters' permission. + if (!user_access('Bypass number of reservations limiters')) { + $form['reservation_repeat_type']['#disabled'] = TRUE; + $form['reservation_repeat_until']['#disabled'] = TRUE; + $form['reservation_repeat_type']['#type'] = 'hidden'; + $form['reservation_repeat_until']['#type'] = 'hidden'; + } + + // Always redirect back to reservations calendar page. + $form['actions']['submit']['#submit'][] = 'return_to_reservations_page'; + + // Limit valid lengths so we have no overlaps. + $validlengths = _room_reservations_valid_lengths($rid, $yyyymmdd, $t); + foreach ($validlengths as $length) { + if ($length['is_valid']) { + $lengths[] = $length['length']; + } + } + foreach ($form['reservation_length'][LANGUAGE_NONE]['#options'] as $slot => &$option) { + if (!in_array($slot, $lengths)) { + unset($form['reservation_length'][LANGUAGE_NONE]['#options'][$slot]); + } + } + + $form['reservation_repeat_type'][LANGUAGE_NONE]['#default_value'] = $form['reservation_repeat_type'][LANGUAGE_NONE]['#default_value'] ? $form['reservation_repeat_type'][LANGUAGE_NONE]['#default_value'] : 1; + $form['reservation_repeat_until']['#states'] = array( + 'visible' => array( + ':input[name="reservation_repeat_type[und]"]' => array('!value' => '1'), + ), + ); + + // CHANGE - empty return not needed. + // return; +} + +/** + * Description. + * + * @param mixed $form + * $form + * @param mixed $form_state + * $form_state + * @param mixed $form_id + * $form_id + */ +function room_reservations_form_node_delete_confirm_alter(&$form, &$form_state, $form_id) { + if ($form['#node']->type == 'room_reservations_reservation') { + $form['#submit'][] = 'return_to_reservations_page'; + } + + // CHANGE - empty return statement not needed here. + // return; +} + +/** + * Description. + * + * @param mixed $node + * $node + */ +function room_reservations_node_presave($node) { + switch ($node->type) { + case 'room_reservations_reservation': + if (isset($node->date_default)) { + $node->reservation_date[LANGUAGE_NONE][0]['value'] = $node->date_default; + } + break; + } +} + +/** + * Description. + * + * @param mixed &$element + * $element + * @param mixed $form_state + * $form_state + * @param mixed &$context + * $context + */ +function room_reservations_date_popup_process_alter(&$element, &$form_state, &$context) { + if (isset($element['#field']['field_name'])) { + switch ($element['#field']['field_name']) { + case "reservation_repeat_until": { + if (user_access('create room reservations extended')) { + $advancedays = variable_get('room_reservations_advance_extended', 180); + } + else { + $advancedays = variable_get('room_reservations_advance_standard', 14); + } + $element['#datepicker_options'] = array( + 'minDate' => '+0d', + 'maxDate' => $advancedays . 'D', + ); + $element['date'] = date_popup_process_date_part($element); + } + break; + } + } +} + +/** + * Description. + * + * @param mixed $form + * $form + * @param mixed $form_state + * $form_state + */ +function return_to_reservations_page($form, &$form_state) { + // Can't set redirect here for DELETE function; + // see delete confirm form alter above + if ($form_state['clicked_button']['#id'] == 'edit-delete') { + return; + } + + if(isset($form_state['values']['reservation_room'][LANGUAGE_NONE][0]['target_id'])) { + $rid = $form_state['values']['reservation_room'][LANGUAGE_NONE][0]['target_id']; + $rooms = _room_reservations_rooms(); + $categories = _room_reservations_categories(); + $room = $rooms[$rid]; + $catid = $room['reservations_room_category'][LANGUAGE_NONE][0]['target_id']; + $category = $categories[$catid]; + $anchor = strtolower(preg_replace('/[^a-zA-Z0-9-]+/', '-', $category['title'])); + } + + // Need to handle both update and delete case. + $date = (empty($form_state['values']['reservation_date'][LANGUAGE_NONE][0]['value'])) ? NULL : $form['#node']->reservation_date[LANGUAGE_NONE][0]['value']; + if ($date) { + $return = date('n/d', strtotime($date)); + $form_state['redirect'] = array('room_reservations/' . $return, array('fragment' => $anchor)); + } +} + +/** + * Implements hook_theme(). + */ +function room_reservations_theme() { + return array( + 'room_reservations' => array( + 'variables' => array( + 'dates', + 'categories', + 'hours', + 'building_hours', + 'building_hours_display', + 'rooms', + 'selected_category', + 'user_reservations', + ), + 'file' => '/view/room_reservations_calendar.view.inc', + ), + 'room_reservations_descriptions' => array( + 'variables' => array('categories, rooms'), + 'file' => '/view/room_reservations.view.inc', + ), + 'room_reservations_list' => array( + 'variables' => array('user, base_url, user_reservations, count'), + 'file' => '/view/room_reservations.view.inc', + ), + ); +} + +/** + * Implements hook_mail(). + */ +function room_reservations_mail($key, &$message, $params) { + global $base_url; + $modified_base_url = str_replace('https', 'http', $base_url); + $headers = array( + 'MIME-Version' => '1.0', + 'Content-Type' => 'text/html; charset=UTF-8; format=flowed', + 'Content-Transfer-Encoding' => '8Bit', + 'X-Mailer' => 'Drupal', + ); + switch ($key) { + case 'confirmation': + foreach ($headers as $key => $value) { + $message['headers'][$key] = $value; + } + $message['subject'] = _room_reservations_replace_tokens(check_plain( + _room_reservations_get_variable('confirmation_header_text')), $params); + $body = _room_reservations_replace_tokens(check_markup( + _room_reservations_get_variable('confirmation_owner_text')), $params); + $message['body'][] = '' . $body . ''; + break; + + case 'notification': + foreach ($headers as $key => $value) { + $message['headers'][$key] = $value; + } + $message['subject'] = _room_reservations_replace_tokens(check_plain( + _room_reservations_get_variable('confirmation_header_text')), $params); + $body = _room_reservations_replace_tokens(check_markup( + _room_reservations_get_variable('confirmation_group_text')), $params); + $message['body'][] = '' . $body . ''; + break; + + case 'owner reminder': + foreach ($headers as $key => $value) { + $message['headers'][$key] = $value; + } + $message['subject'] = _room_reservations_replace_tokens(check_plain( + _room_reservations_get_variable('reminder_header_text')), $params); + $body = _room_reservations_replace_tokens(check_markup( + _room_reservations_get_variable('reminder_owner_text')), $params); + $message['body'][] = '' . $body . ''; + break; + + case 'group reminder': + foreach ($headers as $key => $value) { + $message['headers'][$key] = $value; + } + $message['subject'] = _room_reservations_replace_tokens(check_plain( + _room_reservations_get_variable('reminder_header_text')), $params); + $body = _room_reservations_replace_tokens(check_markup( + _room_reservations_get_variable('reminder_group_text')), $params); + $message['body'][] = '' . $body . ''; + break; + } +} + +/** + * Implements hook_cron(). + */ +function xxxroom_reservations_cron() { + // Send reservation reminders. + $send_reminders = _room_reservations_get_variable('send_reminders'); + $last_reminders_sent = _room_reservations_get_variable('last_reminders_sent'); + $reminder_time = (_room_reservations_get_variable('reminder_time') / 100); + $reminder_cutoff = (_room_reservations_get_variable('reminder_cutoff') / 100) . ':00:00'; + $reminders_sent_today = FALSE; + // Reminders are sent once a day. Have they been sent today? + if ($last_reminders_sent) { + if ($last_reminders_sent == date("Y-m-d")) { + $reminders_sent_today = TRUE; + } + } + $current_hour = date("G"); + if (($send_reminders) && (!$reminders_sent_today) && ($current_hour >= $reminder_time)) { + $today = date("Y-m-d"); + $cutoff = $today . ' ' . $reminder_cutoff; + $tomorrow = date('Y-m-d', strtotime("now +1 days")); + $no_reminder_sent = '0000-00-00 00:00:00'; + $not_deleted = 'N'; + $sql = " + SELECT * + FROM {room_reservations} + WHERE date = '%s' + AND reminder_date = '%s' + AND create_date < '%s' + AND deleted = '%s' + "; + $record_count = 0; + $hours = _room_reservations_hours(); + $result = db_query($sql, $tomorrow, $no_reminder_sent, $cutoff, $not_deleted); + if ($result) { + while ($data = db_fetch_object($result)) { + $id = $data->id; + $date = $data->date; + $time = $data->time; + $length = $data->length; + $room = $data->room; + $name = check_plain($data->name); + $group_size = $data->group_size; + $user_name = $data->user_name; + $email_addresses = check_plain($data->email_addresses); + $textmsg = $data->textmsg; + $carrier = $data->carrier; + $phone = check_plain($data->phone); + $record_count++; + $month_name = date("F", strtotime($date)); + $month_number = date("n", strtotime($date)); + $day = date("j", strtotime($date)); + $day_of_week = date("l", strtotime($date)); + foreach ($hours as $individual_hour) { + if ($individual_hour['time'] == $time) { + $display_time = $individual_hour['display']; + break; + } + } + // Send email reminders. + $params = array( + 'room' => $room, + 'month' => $month_name, + 'month_number' => $month_number, + 'day' => $day, + 'day_of_week' => $day_of_week, + 'time' => $display_time, + 'minutes' => $length, + 'name' => $name, + 'id' => $id, + 'carrier' => $carrier, + 'phone' => $phone, + ); + $from = check_plain(_room_reservations_get_variable('from_address')); + // Send an email to each person in the group. If the person is the one + // who made the reservation, send the owner reminder message. + // Otherwise, send the group reminder message. + if (drupal_strlen($email_addresses)) { + $to_addresses = explode(',', $email_addresses); + foreach ($to_addresses as $to_address) { + $to_address = trim($to_address); + $pos = strpos($to_address, $user_name); + if ($pos === FALSE) { + $key = 'group reminder'; + } + else { + $key = 'owner reminder'; + } + $response = drupal_mail('room_reservations', $key, $to_address, language_default(), $params, $from, TRUE); + } + } + // Send a text message if requested. + if ($textmsg == 'Y') { + _room_reservations_send_sms('reminder', $params); + } + // Update the reminder_date field in the db record. + $now = date("Y-m-d H:i"); + $sql2 = "UPDATE {room_reservations} SET reminder_date = '%s' WHERE id = %d"; + $result2 = db_query($sql2, $now, $id); + } + } + _room_reservations_set_variable('last_reminders_sent', $today); + } + // End code to send reminders. + // Update the building hours records. + $update_building_hours = FALSE; + $today = date("Y-m-d"); + $building_hours_update_date = _room_reservations_get_variable('building hours update'); + if ($building_hours_update_date) { + if ($building_hours_update_date < $today) { + $update_building_hours = TRUE; + } + } + if (!$building_hours_update_date) { + $update_building_hours = TRUE; + } + if ($update_building_hours) { + _room_reservations_set_variable('building hours update', $today); + $months = _room_reservations_current_months(); + $x = 0; + foreach ($months as $month) { + // Delete first month - previous month. + if (!$x) { + $result = _room_reservations_delete_variable('MONTHLY_HOURS_' . $month['YYYY_MM']); + $x++; + } + else { + // Create current three months if they don't exist. + $month_hours = _room_reservations_get_variable('MONTHLY_HOURS_' . $month['YYYY_MM']); + if (!$month_hours) { + _room_reservations_create_mo_hours($month['year'], $month['month'], $month['YYYY_MM']); + } + } + } + } +} diff --git a/room_reservations.node.inc b/room_reservations.node.inc new file mode 100644 index 0000000..6936862 --- /dev/null +++ b/room_reservations.node.inc @@ -0,0 +1,1156 @@ + array( + 'name' => t('Room Reservations Category'), + 'base' => 'room_reservations', + 'description' => t('Category of reservable rooms.'), + 'has_title' => TRUE, + 'title_label' => t('Name'), + 'has_body' => FALSE, + 'locked' => TRUE, + ), + 'room_reservations_room' => array( + 'name' => t('Room Reservations Room'), + 'base' => 'room_reservations', + 'description' => t('A reservable room.'), + 'has_title' => TRUE, + 'title_label' => t('Name'), + 'has_body' => TRUE, + 'body_label' => t('Description'), + 'locked' => TRUE, + ), + 'room_reservations_reservation' => array( + 'name' => t('Room Reservations Reservation'), + 'base' => 'room_reservations', + 'description' => t('A room reservation.'), + 'has_title' => TRUE, + 'title_label' => t('Group Name'), + 'title_description' => t('Identifies your group on the reservation calendar.'), + 'has_body' => FALSE, + 'locked' => TRUE, + ), + ); +} + +/** + * Implements hook_node_type_insert(). + * + * Much like hook_node_insert() lets us know that a node is being + * inserted into the database, hook_node_type_insert() lets us know + * that a new content type has been inserted. + * + * Since Drupal will at some point insert our new content type, + * this gives us a chance to add the fields we want. + * + * It is called for all inserts to the content type database, so + * we have to make sure we're only modifying the type we're + * concerned with. + */ +function room_reservations_node_type_insert($content_type) { + switch ($content_type->type) { + case 'room_reservations_category': + // Create all the fields we are adding to our content type. + foreach (_room_reservations_installed_fields_category() as $field) { + field_create_field($field); + } + // Create all the instances for our fields. + foreach (_room_reservations_installed_instances_category() as $instance) { + $instance['entity_type'] = 'node'; + $instance['bundle'] = 'room_reservations_category'; + field_create_instance($instance); + } + break; + + case 'room_reservations_room': + $body_instance = node_add_body_field($content_type, t('Description')); + // Save our changes to the body field instance. + field_update_instance($body_instance); + // Create all the fields we are adding to our content type. + foreach (_room_reservations_installed_fields_room() as $field) { + field_create_field($field); + } + // Create all the instances for our fields. + foreach (_room_reservations_installed_instances_room() as $instance) { + $instance['entity_type'] = 'node'; + $instance['bundle'] = 'room_reservations_room'; + field_create_instance($instance); + } + break; + + case 'room_reservations_reservation': + // Create all the fields we are adding to our content type. + foreach (_room_reservations_installed_fields_reservation() as $field) { + field_create_field($field); + } + // Create all the instances for our fields. + foreach (_room_reservations_installed_instances_reservation() as $instance) { + $instance['entity_type'] = 'node'; + $instance['bundle'] = 'room_reservations_reservation'; + field_create_instance($instance); + } + break; + } +} + +/** + * Implements hook_form(). + * + * Drupal needs for us to provide a form that lets the user + * add content. This is the form that the user will see if + * they go to node/add/node-example. + * + * You can get fancy with this form, or you can just punt + * and return the default form that node_content will provide. + */ +function room_reservations_form($node, $form_state) { + return node_content_form($node, $form_state); +} + +/** + * Define the fields for our content type. + * + * This big array is factored into this function for readability. + * + * @return array + * An associative array specifying the fields we wish to add to our + * new node type. + */ +function _room_reservations_installed_fields_room() { + return array( + 'reservations_room_category' => array( + 'field_name' => 'reservations_room_category', + 'cardinality' => 1, + 'type' => 'entityreference', + 'settings' => array( + 'target_type' => 'node', + 'default_widget' => 'options_select', + 'handler' => 'base', + 'handler_settings' => array('target_bundles' => array('room_reservations_category')), + ), + ), + 'reservations_room_capacity' => array( + 'field_name' => 'reservations_room_capacity', + 'cardinality' => 1, + 'type' => 'number_integer', + 'settings' => array( + 'default_widget' => 'text', + 'max_length' => 255, + ), + ), + ); +} + +function _room_reservations_installed_instances_room() { + return array( + 'reservations_room_capacity' => array( + 'field_name' => 'reservations_room_capacity', + 'required' => true, + 'label' => t('Capacity'), + 'widget' => array( + 'type' => 'text_textfield', + ), + 'settings' => array( + 'text_processing' => 0, + ), + ), + 'reservations_room_category' => array( + 'field_name' => 'reservations_room_category', + 'required' => true, + 'label' => t('Category'), + 'widget' => array( + 'type' => 'options_select', + ), + ), + 'reservations_display_order' => array( + 'bundle' => 'room_reservations_category', + 'default_value' => array( + 0 => array( + 'value' => 1, + ), + ), + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'above', + 'module' => 'list', + 'settings' => array(), + 'type' => 'list_default', + 'weight' => 0, + ), + 'teaser' => array( + 'label' => 'above', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 0, + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservations_display_order', + 'label' => 'Display Order', + 'required' => TRUE, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'options', + 'settings' => array(), + 'type' => 'options_select', + 'weight' => 1, + ), + ), + ); +} + +/** + * Define the fields for our content type. + * + * This big array is factored into this function for readability. + * + * @return array + * An associative array specifying the fields we wish to add to our + * new node type. + */ +function _room_reservations_installed_fields_category() { + $field_bases = array(); + + // Exported field_base: 'reservations_display_order' + $field_bases = array( + 'reservations_display_order' => array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservations_display_order', + 'foreign keys' => array(), + 'indexes' => array( + 'value' => array( + 0 => 'value', + ), + ), + 'locked' => 0, + 'module' => 'list', + 'settings' => array( + 'allowed_values' => array( + 1 => 1, + 2 => 2, + 3 => 3, + 4 => 4, + 5 => 5, + 6 => 6, + 7 => 7, + 8 => 8, + 9 => 9, + 10 => 10, + 11 => 11, + 12 => 12, + 13 => 13, + 14 => 14, + 15 => 15, + 16 => 16, + 17 => 17, + 18 => 18, + 19 => 19, + 20 => 20, + 21 => 21, + 22 => 22, + 23 => 23, + 24 => 24, + 25 => 25, + ), + 'allowed_values_function' => '', + 'profile2_private' => FALSE, + ), + 'translatable' => 0, + 'type' => 'list_integer', + ), + + 'reservations_minadvbooking' => array( + 'field_name' => 'reservations_minadvbooking', + 'cardinality' => 1, + 'type' => 'number_integer', + 'settings' => array( + 'default_widget' => 'text', + 'max_length' => 255, + ), + ), + ); + + // Exported field_base: 'field_reservation_length' + $field_bases['reservations_prebuffer'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservations_prebuffer', + 'foreign keys' => array(), + 'indexes' => array( + 'value' => array( + 0 => 'value', + ), + ), + 'locked' => 0, + 'module' => 'list', + 'settings' => array( + 'allowed_values' => array( + 0 => 'none', + 30 => '30 minutes', + 60 => '1 hour', + 90 => '1.5 hours', + 120 => '2 hours', + ), + ), + 'translatable' => 0, + 'type' => 'list_integer', + ); + + // Exported field_base: 'field_reservation_length' + $field_bases['reservations_postbuffer'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservations_postbuffer', + 'foreign keys' => array(), + 'indexes' => array( + 'value' => array( + 0 => 'value', + ), + ), + 'locked' => 0, + 'module' => 'list', + 'settings' => array( + 'allowed_values' => array( + 0 => 'none', + 30 => '30 minutes', + 60 => '1 hour', + 90 => '1.5 hours', + 120 => '2 hours', + ), + ), + 'translatable' => 0, + 'type' => 'list_integer', + ); + + return $field_bases; +} + +function _room_reservations_installed_instances_category() { + $field_instances = array(); + + // Exported field_instance: 'reservations_display_order' + $field_instances['reservations_display_order'] = array( + 'bundle' => 'room_reservations_category', + 'default_value' => array( + 0 => array( + 'value' => 1, + ), + ), + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'above', + 'module' => 'list', + 'settings' => array(), + 'type' => 'list_default', + 'weight' => 0, + ), + 'teaser' => array( + 'label' => 'above', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 0, + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservations_display_order', + 'label' => 'Display Order', + 'required' => TRUE, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'options', + 'settings' => array(), + 'type' => 'options_select', + 'weight' => 1, + ), + ); + + $field_instances['reservations_minadvbooking'] = array( + 'default_value' => array( + 0 => array( + 'value' => 0, + ), + ), + 'field_name' => 'reservations_minadvbooking', + 'required' => true, + 'label' => 'Minimum Advance Booking', + 'description' => 'Sets the minimum days in advance a room may be booked by a standard user.', + 'widget' => array( + 'type' => 'text_textfield', + 'weight' => 1.1, + ), + 'settings' => array( + 'text_processing' => 0, + ), + ); + + $field_instances['reservations_prebuffer'] = array( + 'bundle' => 'room_reservations_category', + 'default_value' => array(0 => array('value' => '0')), + 'deleted' => 0, + 'description' => 'Setup time required for reservations for all room in this category. This time will be added to each reservation to extend the reserved calendar time.', + 'display' => array( + 'default' => array( + 'label' => 'inline', + 'module' => 'list', + 'settings' => array(), + 'type' => 'list_default', + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservations_prebuffer', + 'label' => 'Room setup buffer', + 'required' => true, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'options', + 'settings' => array(), + 'type' => 'options_select', + 'weight' => 1.2, + ), + ); + + $field_instances['reservations_postbuffer'] = array( + 'bundle' => 'room_reservations_category', + 'default_value' => array(0 => array('value' => '0')), + 'deleted' => 0, + 'description' => 'Takedown time required for reservations for all rooms in this category. This time will be added to each reservation to extend the reserved calendar time.', + 'display' => array( + 'default' => array( + 'label' => 'inline', + 'module' => 'list', + 'settings' => array(), + 'type' => 'list_default', + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservations_postbuffer', + 'label' => 'Room takedown buffer', + 'required' => true, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'options', + 'settings' => array(), + 'type' => 'options_select', + 'weight' => 1.3, + ), + ); + + // Translatables + // Included for use with string extractors like potx. + t('Display Order'); + + return $field_instances; +} + +/** + * Define the fields for our content type. + * + * This big array is factored into this function for readability. + * + * @return array + * An associative array specifying the fields we wish to add to our + * new node type. + */ +function _room_reservations_installed_fields_reservation() { + $field_bases = array(); + + // Exported field_base: 'field_block_title' + $field_bases['reservation_block_title'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservation_block_title', + 'foreign keys' => array(), + 'indexes' => array( + 'value' => array( + 0 => 'value', + ), + ), + 'locked' => 0, + 'module' => 'list', + 'settings' => array( + 'allowed_values' => array( + 0 => '', + 1 => '', + ), + 'allowed_values_function' => '', + ), + 'translatable' => 0, + 'type' => 'list_boolean', + ); + + // Exported field_base: 'field_reservation_date' + $field_bases['reservation_date'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservation_date', + 'foreign keys' => array(), + 'indexes' => array(), + 'locked' => 0, + 'module' => 'date', + 'settings' => array( + 'cache_count' => 4, + 'cache_enabled' => 0, + 'granularity' => array( + 'day' => 'day', + 'hour' => 0, + 'minute' => 0, + 'month' => 'month', + 'second' => 0, + 'year' => 'year', + ), + 'timezone_db' => '', + 'todate' => '', + 'tz_handling' => 'none', + ), + 'translatable' => 0, + 'type' => 'datetime', + ); + + // Exported field_base: 'field_reservation_length' + $field_bases['reservation_length'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservation_length', + 'foreign keys' => array(), + 'indexes' => array( + 'value' => array( + 0 => 'value', + ), + ), + 'locked' => 0, + 'module' => 'list', + 'settings' => array( + 'allowed_values' => array( + 30 => '30 minutes', + 60 => '60 minutes', + 90 => '90 minutes', + 120 => '2 hours', + 150 => '2.5 hours', + 180 => '3 hours', + 210 => '3.5 hours', + 240 => '4 hours', + 270 => '4.5 hours', + 300 => '5 hours', + 330 => '5.5 hours', + 360 => '6 hours', + 390 => '6.5 hours', + 420 => '7 hours', + 450 => '7.5 hours', + 480 => '8 hours', + 510 => '8.5 hours', + 540 => '9 hours', + 600 => '10 hours', + 720 => '12 nours', + ), + ), + 'translatable' => 0, + 'type' => 'list_integer', + ); + + // Exported field_base: 'field_reservation_repeat_type' + $field_bases['reservation_repeat_type'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservation_repeat_type', + 'foreign keys' => array(), + 'indexes' => array( + 'value' => array( + 0 => 'value', + ), + ), + 'locked' => 0, + 'module' => 'list', + 'settings' => array( + 'allowed_values' => array( + 1 => 'No repeat', + 2 => 'Repeat all days until', + 3 => 'Repeat this day of the week until', + ), + ), + 'translatable' => 0, + 'type' => 'list_integer', + ); + + // Exported field_base: 'field_reservation_repeat_until' + $field_bases['reservation_repeat_until'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservation_repeat_until', + 'foreign keys' => array(), + 'indexes' => array(), + 'locked' => 0, + 'module' => 'date', + 'settings' => array( + 'cache_count' => 4, + 'cache_enabled' => 0, + 'granularity' => array( + 'day' => 'day', + 'hour' => 0, + 'minute' => 0, + 'month' => 'month', + 'second' => 0, + 'year' => 'year', + ), + 'timezone_db' => '', + 'todate' => '', + 'tz_handling' => 'none', + ), + 'translatable' => 0, + 'type' => 'datetime', + ); + + // Exported field_base: 'field_reservation_room' + $field_bases['reservation_room'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservation_room', + 'foreign keys' => array( + 'node' => array( + 'columns' => array( + 'target_id' => 'nid', + ), + 'table' => 'node', + ), + ), + 'indexes' => array( + 'target_id' => array( + 0 => 'target_id', + ), + ), + 'locked' => 0, + 'module' => 'entityreference', + 'settings' => array( + 'handler' => 'base', + 'handler_settings' => array( + 'behaviors' => array( + 'views-select-list' => array( + 'status' => 0, + ), + ), + 'sort' => array( + 'type' => 'none', + ), + 'target_bundles' => array( + 'room_reservations_room' => 'room_reservations_room', + ), + ), + 'target_type' => 'node', + ), + 'translatable' => 0, + 'type' => 'entityreference', + ); + + // Exported field_base: 'field_reservation_series_id' + $field_bases['reservation_series_id'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservation_series_id', + 'foreign keys' => array(), + 'indexes' => array(), + 'locked' => 0, + 'module' => 'number', + 'translatable' => 0, + 'type' => 'number_integer', + ); + + // Exported field_base: 'field_reservation_time' + $field_bases['reservation_time'] = array( + 'active' => 1, + 'cardinality' => 1, + 'deleted' => 0, + 'entity_types' => array(), + 'field_name' => 'reservation_time', + 'foreign keys' => array( + 'format' => array( + 'columns' => array( + 'format' => 'format', + ), + 'table' => 'filter_format', + ), + ), + 'indexes' => array( + 'format' => array( + 0 => 'format', + ), + ), + 'locked' => 0, + 'module' => 'text', + 'settings' => array( + 'max_length' => 4, + ), + 'translatable' => 0, + 'type' => 'text', + ); + + return $field_bases; +} + +function _room_reservations_installed_instances_reservation() { + $field_instances = array(); + + // Exported field_instance: 'reservation_block_title' + $field_instances['reservation_block_title'] = array( + 'bundle' => 'room_reservations_reservation', + 'default_value' => array( + 0 => array( + 'value' => 0, + ), + ), + 'deleted' => 0, + 'description' => 'Check this to hide the Group Name for this reservation.', + 'display' => array( + 'default' => array( + 'label' => 'hidden', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 1, + ), + 'teaser' => array( + 'label' => 'above', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 0, + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservation_block_title', + 'label' => 'Private', + 'required' => 0, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'options', + 'settings' => array( + 'display_label' => 1, + ), + 'type' => 'options_onoff', + 'weight' => 1, + ), + ); + + // Exported field_instance: 'node-room_reservations_reservation-field_reservation_date' + $field_instances['reservation_date'] = array( + 'bundle' => 'room_reservations_reservation', + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'inline', + 'module' => 'date', + 'settings' => array( + 'format_type' => 'long', + 'fromto' => 'both', + 'multiple_from' => '', + 'multiple_number' => '', + 'multiple_to' => '', + ), + 'type' => 'date_default', + 'weight' => 3, + ), + 'teaser' => array( + 'label' => 'above', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 0, + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservation_date', + 'label' => 'Date', + 'required' => 0, + 'settings' => array( + 'default_value' => 'now', + 'default_value2' => 'same', + 'default_value_code' => '', + 'default_value_code2' => '', + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'date', + 'settings' => array( + 'display_all_day' => 0, + 'increment' => 15, + 'input_format' => 'Y-m-d H:i:s', + 'input_format_custom' => '', + 'label_position' => 'above', + 'text_parts' => array(), + 'year_range' => '-3:+3', + ), + 'type' => 'date_text', + 'weight' => 3, + ), + ); + + // Exported field_instance: 'node-room_reservations_reservation-field_reservation_length' + $field_instances['reservation_length'] = array( + 'bundle' => 'room_reservations_reservation', + 'default_value' => NULL, + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'inline', + 'module' => 'list', + 'settings' => array(), + 'type' => 'list_default', + 'weight' => 5, + ), + 'teaser' => array( + 'label' => 'above', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 0, + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservation_length', + 'label' => 'Length', + 'required' => true, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'options', + 'settings' => array(), + 'type' => 'options_select', + 'weight' => 5, + ), + ); + + // Exported field_instance: 'node-room_reservations_reservation-field_reservation_repeat_type' + $field_instances['reservation_repeat_type'] = array( + 'bundle' => 'room_reservations_reservation', + 'default_value' => NULL, + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'inline', + 'module' => 'list', + 'settings' => array(), + 'type' => 'list_default', + 'weight' => 7, + ), + 'teaser' => array( + 'label' => 'above', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 0, + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservation_repeat_type', + 'label' => 'Repeat Type', + 'required' => 1, + 'settings' => array( + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'options', + 'settings' => array(), + 'type' => 'options_buttons', + 'weight' => 6, + ), + ); + + // Exported field_instance: 'node-room_reservations_reservation-field_reservation_repeat_until' + $field_instances['reservation_repeat_until'] = array( + 'bundle' => 'room_reservations_reservation', + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'inline', + 'module' => 'date', + 'settings' => array( + 'format_type' => 'long', + 'fromto' => 'both', + 'multiple_from' => '', + 'multiple_number' => '', + 'multiple_to' => '', + ), + 'type' => 'date_default', + 'weight' => 8, + ), + 'teaser' => array( + 'label' => 'above', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 0, + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservation_repeat_until', + 'label' => 'Repeat Until', + 'required' => 0, + 'settings' => array( + 'default_value' => 'now', + 'default_value2' => 'same', + 'default_value_code' => '', + 'default_value_code2' => '', + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'date', + 'settings' => array( + 'display_all_day' => 0, + 'increment' => 15, + 'input_format' => 'Y-m-d H:i:s', + 'input_format_custom' => '', + 'label_position' => 'above', + 'text_parts' => array(), + 'year_range' => '-3:+3', + ), + 'type' => 'date_popup', + 'weight' => 7, + ), + ); + + // Exported field_instance: 'node-room_reservations_reservation-field_reservation_room' + $field_instances['reservation-reservation_room'] = array( + 'bundle' => 'room_reservations_reservation', + 'default_value' => NULL, + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'inline', + 'module' => 'entityreference', + 'settings' => array( + 'link' => FALSE, + ), + 'type' => 'entityreference_label', + 'weight' => 2, + ), + 'teaser' => array( + 'label' => 'above', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 0, + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservation_room', + 'label' => 'Room', + 'required' => 0, + 'settings' => array( + 'nodeaccess_nodereference' => array( + 'all' => array( + 'view' => 0, + ), + 'all_published' => 0, + 'author' => array( + 'delete' => 0, + 'update' => 0, + 'view' => 0, + ), + 'author_published' => 0, + 'priority' => 0, + 'referenced' => array( + 'delete' => array( + 'delete' => 0, + 'update' => 0, + 'view' => 0, + ), + 'published' => 0, + 'update' => array( + 'delete' => 0, + 'update' => 0, + 'view' => 0, + ), + 'view' => array( + 'delete' => 0, + 'update' => 0, + 'view' => 0, + ), + ), + 'unused' => 0, + ), + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'options', + 'settings' => array(), + 'type' => 'options_select', + 'weight' => 2, + ), + ); + + // Exported field_instance: 'node-room_reservations_reservation-field_reservation_series_id' + $field_instances['reservation_series_id'] = array( + 'bundle' => 'room_reservations_reservation', + 'default_value' => NULL, + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'hidden', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 6, + ), + 'teaser' => array( + 'label' => 'hidden', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 0, + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservation_series_id', + 'label' => 'Series ID', + 'required' => 0, + 'settings' => array( + 'max' => '', + 'min' => '', + 'prefix' => '', + 'suffix' => '', + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 0, + 'module' => 'number', + 'settings' => array(), + 'type' => 'number', + 'weight' => 8, + ), + ); + + // Exported field_instance: 'reservation_time' + $field_instances['reservation_time'] = array( + 'bundle' => 'room_reservations_reservation', + 'default_value' => NULL, + 'deleted' => 0, + 'description' => '', + 'display' => array( + 'default' => array( + 'label' => 'inline', + 'module' => 'text', + 'settings' => array(), + 'type' => 'text_default', + 'weight' => 4, + ), + 'teaser' => array( + 'label' => 'above', + 'settings' => array(), + 'type' => 'hidden', + 'weight' => 0, + ), + ), + 'entity_type' => 'node', + 'field_name' => 'reservation_time', + 'label' => 'Time', + 'required' => 0, + 'settings' => array( + 'better_formats' => array( + 'allowed_formats' => array( + 'filtered_html' => 'filtered_html', + 'full_html' => 'full_html', + 'php_code' => 'php_code', + 'plain_text' => 'plain_text', + ), + 'allowed_formats_toggle' => 0, + 'default_order_toggle' => 0, + 'default_order_wrapper' => array( + 'formats' => array( + 'filtered_html' => array( + 'weight' => 0, + ), + 'full_html' => array( + 'weight' => 1, + ), + 'php_code' => array( + 'weight' => 11, + ), + 'plain_text' => array( + 'weight' => 10, + ), + ), + ), + ), + 'text_processing' => 0, + 'user_register_form' => FALSE, + ), + 'widget' => array( + 'active' => 1, + 'module' => 'text', + 'settings' => array( + 'size' => 60, + ), + 'type' => 'text_textfield', + 'weight' => 4, + ), + ); + + // Translatables + // Included for use with string extractors like potx. + t('Block title'); + t('Check this to hide the Group Name for this reservation.'); + t('Date'); + t('Length'); + t('Repeat Type'); + t('Repeat Until'); + t('Room'); + t('Series ID'); + t('Time'); + + return $field_instances; +} + diff --git a/room_reservations.series.inc b/room_reservations.series.inc new file mode 100644 index 0000000..fb44d07 --- /dev/null +++ b/room_reservations.series.inc @@ -0,0 +1,191 @@ +type != 'room_reservations_reservation') { + return; + } + + // when we do a node_save below; it will hit this hook; so if this routine just did the save lets bail + if (isset($node->saved)){ + return; + } + + $rtype = $node->reservation_repeat_type[LANGUAGE_NONE][0]['value']; + $start = $node->reservation_date[LANGUAGE_NONE][0]['value']; + $start_yyyy_mm_dd = date('Y-m-d', strtotime($start)); + $end = date('Y-m-d', strtotime($node->reservation_repeat_until[LANGUAGE_NONE][0]['value'])); + $time = $node->reservation_time[LANGUAGE_NONE][0]['value']; + $length = $node->reservation_length[LANGUAGE_NONE][0]['value']; + $rid = $node->reservation_room[LANGUAGE_NONE][0]['target_id']; + $day = date('l', strtotime($start)); + + $msg = ''; + switch ($rtype) { + // no repeats + case 1: + return; + + // every day until.... + case 2: + $skip = '+1 day'; + $back = '-1 day'; + $msg = t('You have booked every day from %start until %end', array('%start' => $start_yyyy_mm_dd, '%end' => $end)); + break; + + // this day of the week until.. + case 3: + $skip = '+7 day'; + $back = '-7 day'; + $msg = t('You have booked every %day from %start until %end', array('%day' => $day, '%start' => $start_yyyy_mm_dd, '%end' => $end)); + break; + } + + // set NID as Series ID for both the primary node and the repeat nodes + $node->reservation_series_id[LANGUAGE_NONE][0]['value'] = $node->nid; + + $tmp = clone($node); + unset($tmp->nid, $tmp->vid); + // and must unset default date that we just used in hook_node_presave for the initial node + unset($tmp->date_default); + $date = $start; + $booked = array(); + while (strtotime($date) <= strtotime($back, strtotime($end))) { + $date = date('Y-m-d', strtotime($skip, strtotime($date))); + + // must check to see if next booking is available + // the first one we don't check as we could not have picked it if it wasn't + if (_room_reservations_is_slot_free($rid, $date, $time, $length)) { + $new = clone($tmp); + $new->reservation_date[LANGUAGE_NONE][0]['value'] = $date . ' 00:00:00'; // added 12:00:00 to fix TZ issues; this is unlikely correct way to do this + $new->saved = true; + node_save($new); + } + else { + $booked[] = $date; + } + } + + // lets spit out some useful msgs + // first clear the msg stating we just created the reservation node + drupal_get_messages('status'); + drupal_set_message(t('Your reservation series has been booked.')); + drupal_set_message($msg); + if (count($booked)) { + $dates = '
' . implode('
', $booked); + drupal_set_message(t('The following dates were not booked due to scheduling conflicts: !dates', array('!dates' => $dates)), 'warning'); + } +} + +// to handle Series edits +function room_reservations_update($node) { + if ($node->type != 'room_reservations_reservation') { + return; + } + + // when we do a node_save below; it will hit this hook; so if this routine just did the save lets bail + if (isset($node->saved)){ + return; + } + + // if not part of a Series or special single only url; do nothing + if (!isset($node->reservation_series_id[LANGUAGE_NONE][0]) || !($sid = $node->reservation_series_id[LANGUAGE_NONE][0]['value']) || isset($_GET['single'])) { + return; + } + + // reservation details + $start = $node->reservation_date[LANGUAGE_NONE][0]['value']; + $end = date('Y-m-d', strtotime($node->reservation_repeat_until[LANGUAGE_NONE][0]['value'])); + $time = $node->reservation_time[LANGUAGE_NONE][0]['value']; + $length = $node->reservation_length[LANGUAGE_NONE][0]['value']; + $rid = $node->reservation_room[LANGUAGE_NONE][0]['target_id']; + $day = date('l', strtotime($start)); + + // grab all reservations in this series + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_reservation') + ->fieldCondition('reservation_series_id', 'value', $sid, '='); + $result = $query->execute(); + $deleted = array(); + if (isset($result['node'])) { + $result_nids = array_keys($result['node']); + $results = entity_load('node', $result_nids); + foreach ($results as $data) { + // for now, only allow changing Title, Blocked title, Length + // first 2 are easy; but to change length we need to check if that period is available and if not delete entry from Series + $data->title = $node->title; + $data->reservation_block_title[LANGUAGE_NONE][0]['value'] = $node->reservation_block_title[LANGUAGE_NONE][0]['value']; + + $date = date('Y-m-d', strtotime($data->reservation_date[LANGUAGE_NONE][0]['value'])); + if (_room_reservations_is_slot_free($rid, $date, $time, $length)) { + $data->reservation_length[LANGUAGE_NONE][0]['value'] = $node->reservation_length[LANGUAGE_NONE][0]['value']; + $data->saved = true; + node_save($data); + } + // if slot is not available; delete that entry + else { + $deleted[] = $date; + node_delete($data->nid); + } + } + } + + // lets spit out some useful msgs + drupal_set_message(t('Your reservation series has been modified.')); + if (count($deleted)) { + $dates = '
' . implode('
', $deleted); + drupal_set_message(t('The following dates were deleted from the series due to scheduling conflicts: !dates', array('!dates' => $dates)), 'warning'); + } +} + +function _room_reservations_series_delete($form) { + $sid = $form['#node']->reservation_series_id[LANGUAGE_NONE][0]['value']; + // grab all reservations in this series + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_reservation') + ->fieldCondition('reservation_series_id', 'value', $sid, '='); + $result = $query->execute(); + if (isset($result['node'])) { + $nids = array_keys($result['node']); + foreach ($nids as $nid) { + node_delete($nid); + } + } + $title = $form['#node']->title; + drupal_set_message(t('The reservation series @title was deleted.', array('@title' => $title))); + drupal_goto('room_reservations'); +} + +/** +* copy above function for API use only +* - pass SID +* +* @param mixed $sid +*/ +function room_reservations_series_delete($sid) { + // grab all reservations in this series + $query = new EntityFieldQuery(); + $query->entityCondition('entity_type', 'node') + ->entityCondition('bundle', 'room_reservations_reservation') + ->fieldCondition('reservation_series_id', 'value', $sid, '='); + $result = $query->execute(); + if (isset($result['node'])) { + $nids = array_keys($result['node']); + foreach ($nids as $nid) { + node_delete($nid); + } + } + $res = node_load($sid); + drupal_set_message(t('The reservation series @title was deleted.', array('@title' => $res->title))); + drupal_goto('room_reservations'); +} \ No newline at end of file diff --git a/room_reservations.views_default.inc b/room_reservations.views_default.inc new file mode 100644 index 0000000..44f6cec --- /dev/null +++ b/room_reservations.views_default.inc @@ -0,0 +1,435 @@ +name = 'room_reservations'; + $view->description = ''; + $view->tag = 'default'; + $view->base_table = 'node'; + $view->human_name = 'Room Reservations (default views)'; + $view->core = 7; + $view->api_version = '3.0'; + $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ + + /* Display: Master */ + $handler = $view->new_display('default', 'Master', 'default'); + $handler->display->display_options['title'] = 'Room Reservations'; + $handler->display->display_options['use_more_always'] = FALSE; + $handler->display->display_options['access']['type'] = 'perm'; + $handler->display->display_options['cache']['type'] = 'none'; + $handler->display->display_options['query']['type'] = 'views_query'; + $handler->display->display_options['exposed_form']['type'] = 'basic'; + $handler->display->display_options['pager']['type'] = 'full'; + $handler->display->display_options['style_plugin'] = 'default'; + $handler->display->display_options['style_options']['grouping'] = array( + 0 => array( + 'field' => 'reservations_room_category', + 'rendered' => 1, + 'rendered_strip' => 0, + ), + ); + $handler->display->display_options['row_plugin'] = 'fields'; + /* Relationship: Entity Reference: Referenced Entity */ + $handler->display->display_options['relationships']['reservations_room_category_target_id']['id'] = 'reservations_room_category_target_id'; + $handler->display->display_options['relationships']['reservations_room_category_target_id']['table'] = 'field_data_reservations_room_category'; + $handler->display->display_options['relationships']['reservations_room_category_target_id']['field'] = 'reservations_room_category_target_id'; + $handler->display->display_options['relationships']['reservations_room_category_target_id']['label'] = 'category'; + /* Field: Content: Title */ + $handler->display->display_options['fields']['title']['id'] = 'title'; + $handler->display->display_options['fields']['title']['table'] = 'node'; + $handler->display->display_options['fields']['title']['field'] = 'title'; + $handler->display->display_options['fields']['title']['label'] = ''; + $handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE; + $handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE; + /* Field: Content: Body */ + $handler->display->display_options['fields']['body']['id'] = 'body'; + $handler->display->display_options['fields']['body']['table'] = 'field_data_body'; + $handler->display->display_options['fields']['body']['field'] = 'body'; + $handler->display->display_options['fields']['body']['label'] = ''; + $handler->display->display_options['fields']['body']['element_label_colon'] = FALSE; + /* Field: Content: Capacity */ + $handler->display->display_options['fields']['reservations_room_capacity']['id'] = 'reservations_room_capacity'; + $handler->display->display_options['fields']['reservations_room_capacity']['table'] = 'field_data_reservations_room_capacity'; + $handler->display->display_options['fields']['reservations_room_capacity']['field'] = 'reservations_room_capacity'; + $handler->display->display_options['fields']['reservations_room_capacity']['element_type'] = 'span'; + /* Field: Content: Category */ + $handler->display->display_options['fields']['reservations_room_category']['id'] = 'reservations_room_category'; + $handler->display->display_options['fields']['reservations_room_category']['table'] = 'field_data_reservations_room_category'; + $handler->display->display_options['fields']['reservations_room_category']['field'] = 'reservations_room_category'; + $handler->display->display_options['fields']['reservations_room_category']['label'] = ''; + $handler->display->display_options['fields']['reservations_room_category']['exclude'] = TRUE; + $handler->display->display_options['fields']['reservations_room_category']['element_label_colon'] = FALSE; + $handler->display->display_options['fields']['reservations_room_category']['settings'] = array( + 'link' => 0, + ); + /* Sort criterion: Content: Title */ + $handler->display->display_options['sorts']['title_1']['id'] = 'title_1'; + $handler->display->display_options['sorts']['title_1']['table'] = 'node'; + $handler->display->display_options['sorts']['title_1']['field'] = 'title'; + $handler->display->display_options['sorts']['title_1']['relationship'] = 'reservations_room_category_target_id'; + /* Sort criterion: Content: Title */ + $handler->display->display_options['sorts']['title']['id'] = 'title'; + $handler->display->display_options['sorts']['title']['table'] = 'node'; + $handler->display->display_options['sorts']['title']['field'] = 'title'; + /* Filter criterion: Content: Published */ + $handler->display->display_options['filters']['status']['id'] = 'status'; + $handler->display->display_options['filters']['status']['table'] = 'node'; + $handler->display->display_options['filters']['status']['field'] = 'status'; + $handler->display->display_options['filters']['status']['value'] = 1; + $handler->display->display_options['filters']['status']['group'] = 1; + $handler->display->display_options['filters']['status']['expose']['operator'] = FALSE; + /* Filter criterion: Content: Type */ + $handler->display->display_options['filters']['type']['id'] = 'type'; + $handler->display->display_options['filters']['type']['table'] = 'node'; + $handler->display->display_options['filters']['type']['field'] = 'type'; + $handler->display->display_options['filters']['type']['value'] = array( + 'room_reservations_room' => 'room_reservations_room', + ); + + /* Display: Room Descriptions */ + $handler = $view->new_display('page', 'Room Descriptions', 'page_1'); + $handler->display->display_options['path'] = 'room_reservations/rooms'; + $handler->display->display_options['menu']['type'] = 'tab'; + $handler->display->display_options['menu']['title'] = 'Room Descriptions'; + $handler->display->display_options['menu']['weight'] = '20'; + $handler->display->display_options['menu']['context'] = 0; + $handler->display->display_options['menu']['context_only_inline'] = 0; + + /* Display: My Reservations */ + $handler = $view->new_display('page', 'My Reservations', 'page_2'); + $handler->display->display_options['defaults']['relationships'] = FALSE; + $handler->display->display_options['defaults']['fields'] = FALSE; + /* Field: Content: Nid */ + $handler->display->display_options['fields']['nid']['id'] = 'nid'; + $handler->display->display_options['fields']['nid']['table'] = 'node'; + $handler->display->display_options['fields']['nid']['field'] = 'nid'; + $handler->display->display_options['fields']['nid']['label'] = ''; + $handler->display->display_options['fields']['nid']['exclude'] = TRUE; + $handler->display->display_options['fields']['nid']['element_label_colon'] = FALSE; + /* Field: Content: Title */ + $handler->display->display_options['fields']['title']['id'] = 'title'; + $handler->display->display_options['fields']['title']['table'] = 'node'; + $handler->display->display_options['fields']['title']['field'] = 'title'; + $handler->display->display_options['fields']['title']['label'] = 'Group'; + $handler->display->display_options['fields']['title']['alter']['make_link'] = TRUE; + $handler->display->display_options['fields']['title']['alter']['path'] = 'node/[nid]/edit'; + $handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE; + $handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE; + $handler->display->display_options['fields']['title']['element_type'] = 'span'; + $handler->display->display_options['fields']['title']['link_to_node'] = FALSE; + /* Field: Content: Room */ + $handler->display->display_options['fields']['reservation_room']['id'] = 'reservation_room'; + $handler->display->display_options['fields']['reservation_room']['table'] = 'field_data_reservation_room'; + $handler->display->display_options['fields']['reservation_room']['field'] = 'reservation_room'; + $handler->display->display_options['fields']['reservation_room']['element_type'] = 'span'; + $handler->display->display_options['fields']['reservation_room']['settings'] = array( + 'link' => 1, + ); + /* Field: Content: Date */ + $handler->display->display_options['fields']['reservation_date']['id'] = 'reservation_date'; + $handler->display->display_options['fields']['reservation_date']['table'] = 'field_data_reservation_date'; + $handler->display->display_options['fields']['reservation_date']['field'] = 'reservation_date'; + $handler->display->display_options['fields']['reservation_date']['element_type'] = 'span'; + /* Field: Content: Time */ + $handler->display->display_options['fields']['reservation_time']['id'] = 'reservation_time'; + $handler->display->display_options['fields']['reservation_time']['table'] = 'field_data_reservation_time'; + $handler->display->display_options['fields']['reservation_time']['field'] = 'reservation_time'; + $handler->display->display_options['fields']['reservation_time']['element_type'] = 'span'; + $handler->display->display_options['fields']['reservation_time']['settings'] = array( + 'thousand_separator' => '', + 'prefix_suffix' => 0, + ); + /* Field: Content: Length */ + $handler->display->display_options['fields']['reservation_length']['id'] = 'reservation_length'; + $handler->display->display_options['fields']['reservation_length']['table'] = 'field_data_reservation_length'; + $handler->display->display_options['fields']['reservation_length']['field'] = 'reservation_length'; + $handler->display->display_options['fields']['reservation_length']['label'] = 'Booked for'; + $handler->display->display_options['fields']['reservation_length']['element_type'] = 'span'; + $handler->display->display_options['defaults']['sorts'] = FALSE; + /* Sort criterion: Content: Date (reservation_date) */ + $handler->display->display_options['sorts']['reservation_date_value']['id'] = 'reservation_date_value'; + $handler->display->display_options['sorts']['reservation_date_value']['table'] = 'field_data_reservation_date'; + $handler->display->display_options['sorts']['reservation_date_value']['field'] = 'reservation_date_value'; + $handler->display->display_options['defaults']['arguments'] = FALSE; + /* Contextual filter: Content: Author uid */ + $handler->display->display_options['arguments']['uid']['id'] = 'uid'; + $handler->display->display_options['arguments']['uid']['table'] = 'node'; + $handler->display->display_options['arguments']['uid']['field'] = 'uid'; + $handler->display->display_options['arguments']['uid']['default_action'] = 'default'; + $handler->display->display_options['arguments']['uid']['default_argument_type'] = 'current_user'; + $handler->display->display_options['arguments']['uid']['summary']['number_of_records'] = '0'; + $handler->display->display_options['arguments']['uid']['summary']['format'] = 'default_summary'; + $handler->display->display_options['arguments']['uid']['summary_options']['items_per_page'] = '25'; + $handler->display->display_options['defaults']['filter_groups'] = FALSE; + $handler->display->display_options['defaults']['filters'] = FALSE; + /* Filter criterion: Content: Published */ + $handler->display->display_options['filters']['status']['id'] = 'status'; + $handler->display->display_options['filters']['status']['table'] = 'node'; + $handler->display->display_options['filters']['status']['field'] = 'status'; + $handler->display->display_options['filters']['status']['value'] = 1; + $handler->display->display_options['filters']['status']['group'] = 1; + $handler->display->display_options['filters']['status']['expose']['operator'] = FALSE; + /* Filter criterion: Content: Type */ + $handler->display->display_options['filters']['type']['id'] = 'type'; + $handler->display->display_options['filters']['type']['table'] = 'node'; + $handler->display->display_options['filters']['type']['field'] = 'type'; + $handler->display->display_options['filters']['type']['value'] = array( + 'room_reservations_reservation' => 'room_reservations_reservation', + ); + /* Filter criterion: Content: Date (reservation_date) */ + $handler->display->display_options['filters']['reservation_date_value']['id'] = 'reservation_date_value'; + $handler->display->display_options['filters']['reservation_date_value']['table'] = 'field_data_reservation_date'; + $handler->display->display_options['filters']['reservation_date_value']['field'] = 'reservation_date_value'; + $handler->display->display_options['filters']['reservation_date_value']['operator'] = '>='; + $handler->display->display_options['filters']['reservation_date_value']['form_type'] = 'date_text'; + $handler->display->display_options['filters']['reservation_date_value']['default_date'] = 'now'; + $handler->display->display_options['path'] = 'room_reservations/list'; + $handler->display->display_options['menu']['type'] = 'tab'; + $handler->display->display_options['menu']['title'] = 'My Reservations'; + $handler->display->display_options['menu']['weight'] = '15'; + $handler->display->display_options['menu']['context'] = 0; + $handler->display->display_options['menu']['context_only_inline'] = 0; + + $views['room_reservations'] = $view; + + ///////////////////////////////////////////// + + $view = new view(); + $view->name = 'manage_reservations'; + $view->description = ''; + $view->tag = 'default'; + $view->base_table = 'node'; + $view->human_name = 'Manage Reservations'; + $view->core = 7; + $view->api_version = '3.0'; + $view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */ + + /* Display: Master */ + $handler = $view->new_display('default', 'Master', 'default'); + $handler->display->display_options['title'] = 'Manage Reservations'; + $handler->display->display_options['use_more_always'] = FALSE; + $handler->display->display_options['access']['type'] = 'perm'; + $handler->display->display_options['access']['perm'] = 'administer room reservations system'; + $handler->display->display_options['cache']['type'] = 'none'; + $handler->display->display_options['query']['type'] = 'views_query'; + $handler->display->display_options['exposed_form']['type'] = 'basic'; + $handler->display->display_options['pager']['type'] = 'full'; + $handler->display->display_options['pager']['options']['items_per_page'] = '40'; + $handler->display->display_options['style_plugin'] = 'table'; + $handler->display->display_options['style_options']['columns'] = array( + 'title' => 'title', + 'title_2' => 'title', + 'title_1' => 'title', + 'reservation_date' => 'reservation_date', + 'reservation_time' => 'reservation_date', + 'reservation_length' => 'reservation_date', + 'name' => 'title', + 'reservation_repeat_type' => 'reservation_repeat_type', + 'reservation_repeat_until' => 'reservation_repeat_type', + ); + $handler->display->display_options['style_options']['default'] = '-1'; + $handler->display->display_options['style_options']['info'] = array( + 'title' => array( + 'sortable' => 0, + 'default_sort_order' => 'asc', + 'align' => '', + 'separator' => '
', + 'empty_column' => 0, + ), + 'title_2' => array( + 'sortable' => 0, + 'default_sort_order' => 'asc', + 'align' => '', + 'separator' => '', + 'empty_column' => 0, + ), + 'title_1' => array( + 'sortable' => 0, + 'default_sort_order' => 'asc', + 'align' => '', + 'separator' => '', + 'empty_column' => 0, + ), + 'reservation_date' => array( + 'sortable' => 1, + 'default_sort_order' => 'asc', + 'align' => '', + 'separator' => '
', + 'empty_column' => 0, + ), + 'reservation_time' => array( + 'sortable' => 0, + 'default_sort_order' => 'asc', + 'align' => '', + 'separator' => '', + 'empty_column' => 0, + ), + 'reservation_length' => array( + 'sortable' => 0, + 'default_sort_order' => 'asc', + 'align' => '', + 'separator' => '', + 'empty_column' => 0, + ), + 'name' => array( + 'sortable' => 0, + 'default_sort_order' => 'asc', + 'align' => '', + 'separator' => '', + 'empty_column' => 0, + ), + 'reservation_repeat_type' => array( + 'sortable' => 0, + 'default_sort_order' => 'asc', + 'align' => '', + 'separator' => '
', + 'empty_column' => 0, + ), + 'reservation_repeat_until' => array( + 'sortable' => 0, + 'default_sort_order' => 'asc', + 'align' => '', + 'separator' => '', + 'empty_column' => 0, + ), + ); + /* Relationship: Content: Author */ + $handler->display->display_options['relationships']['uid']['id'] = 'uid'; + $handler->display->display_options['relationships']['uid']['table'] = 'node'; + $handler->display->display_options['relationships']['uid']['field'] = 'uid'; + /* Relationship: Entity Reference: Referenced Entity */ + $handler->display->display_options['relationships']['reservation_room_target_id']['id'] = 'reservation_room_target_id'; + $handler->display->display_options['relationships']['reservation_room_target_id']['table'] = 'field_data_reservation_room'; + $handler->display->display_options['relationships']['reservation_room_target_id']['field'] = 'reservation_room_target_id'; + $handler->display->display_options['relationships']['reservation_room_target_id']['label'] = 'room'; + /* Relationship: Entity Reference: Referencing entity */ + $handler->display->display_options['relationships']['reverse_reservations_room_category_node']['id'] = 'reverse_reservations_room_category_node'; + $handler->display->display_options['relationships']['reverse_reservations_room_category_node']['table'] = 'node'; + $handler->display->display_options['relationships']['reverse_reservations_room_category_node']['field'] = 'reverse_reservations_room_category_node'; + $handler->display->display_options['relationships']['reverse_reservations_room_category_node']['relationship'] = 'reservation_room_target_id'; + $handler->display->display_options['relationships']['reverse_reservations_room_category_node']['label'] = 'reservations_room_category'; + /* Field: Content: Title */ + $handler->display->display_options['fields']['title']['id'] = 'title'; + $handler->display->display_options['fields']['title']['table'] = 'node'; + $handler->display->display_options['fields']['title']['field'] = 'title'; + $handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE; + $handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE; + /* Field: Content: Title */ + $handler->display->display_options['fields']['title_2']['id'] = 'title_2'; + $handler->display->display_options['fields']['title_2']['table'] = 'node'; + $handler->display->display_options['fields']['title_2']['field'] = 'title'; + $handler->display->display_options['fields']['title_2']['relationship'] = 'reverse_reservations_room_category_node'; + $handler->display->display_options['fields']['title_2']['label'] = 'Room Category'; + /* Field: Content: Title */ + $handler->display->display_options['fields']['title_1']['id'] = 'title_1'; + $handler->display->display_options['fields']['title_1']['table'] = 'node'; + $handler->display->display_options['fields']['title_1']['field'] = 'title'; + $handler->display->display_options['fields']['title_1']['relationship'] = 'reservation_room_target_id'; + $handler->display->display_options['fields']['title_1']['label'] = 'Room'; + /* Field: Content: Date */ + $handler->display->display_options['fields']['reservation_date']['id'] = 'reservation_date'; + $handler->display->display_options['fields']['reservation_date']['table'] = 'field_data_reservation_date'; + $handler->display->display_options['fields']['reservation_date']['field'] = 'reservation_date'; + $handler->display->display_options['fields']['reservation_date']['label'] = 'Reservation Date'; + $handler->display->display_options['fields']['reservation_date']['settings'] = array( + 'format_type' => 'date_year', + 'fromto' => 'both', + 'multiple_number' => '', + 'multiple_from' => '', + 'multiple_to' => '', + ); + /* Field: Content: Time */ + $handler->display->display_options['fields']['reservation_time']['id'] = 'reservation_time'; + $handler->display->display_options['fields']['reservation_time']['table'] = 'field_data_reservation_time'; + $handler->display->display_options['fields']['reservation_time']['field'] = 'reservation_time'; + $handler->display->display_options['fields']['reservation_time']['label'] = 'Booking Time'; + /* Field: Content: Booking Length */ + $handler->display->display_options['fields']['reservation_length']['id'] = 'reservation_length'; + $handler->display->display_options['fields']['reservation_length']['table'] = 'field_data_reservation_length'; + $handler->display->display_options['fields']['reservation_length']['field'] = 'reservation_length'; + /* Field: User: Name */ + $handler->display->display_options['fields']['name']['id'] = 'name'; + $handler->display->display_options['fields']['name']['table'] = 'users'; + $handler->display->display_options['fields']['name']['field'] = 'name'; + $handler->display->display_options['fields']['name']['relationship'] = 'uid'; + $handler->display->display_options['fields']['name']['label'] = 'Booked by'; + /* Field: Content: Repeat Type */ + $handler->display->display_options['fields']['reservation_repeat_type']['id'] = 'reservation_repeat_type'; + $handler->display->display_options['fields']['reservation_repeat_type']['table'] = 'field_data_reservation_repeat_type'; + $handler->display->display_options['fields']['reservation_repeat_type']['field'] = 'reservation_repeat_type'; + /* Field: Content: Repeat reservation until */ + $handler->display->display_options['fields']['reservation_repeat_until']['id'] = 'reservation_repeat_until'; + $handler->display->display_options['fields']['reservation_repeat_until']['table'] = 'field_data_reservation_repeat_until'; + $handler->display->display_options['fields']['reservation_repeat_until']['field'] = 'reservation_repeat_until'; + $handler->display->display_options['fields']['reservation_repeat_until']['settings'] = array( + 'format_type' => 'date_year', + 'fromto' => 'both', + 'multiple_number' => '', + 'multiple_from' => '', + 'multiple_to' => '', + ); + /* Sort criterion: Content: Post date */ + $handler->display->display_options['sorts']['created']['id'] = 'created'; + $handler->display->display_options['sorts']['created']['table'] = 'node'; + $handler->display->display_options['sorts']['created']['field'] = 'created'; + $handler->display->display_options['sorts']['created']['order'] = 'DESC'; + /* Filter criterion: Content: Published */ + $handler->display->display_options['filters']['status']['id'] = 'status'; + $handler->display->display_options['filters']['status']['table'] = 'node'; + $handler->display->display_options['filters']['status']['field'] = 'status'; + $handler->display->display_options['filters']['status']['value'] = 1; + $handler->display->display_options['filters']['status']['group'] = 1; + $handler->display->display_options['filters']['status']['expose']['operator'] = FALSE; + /* Filter criterion: Content: Type */ + $handler->display->display_options['filters']['type']['id'] = 'type'; + $handler->display->display_options['filters']['type']['table'] = 'node'; + $handler->display->display_options['filters']['type']['field'] = 'type'; + $handler->display->display_options['filters']['type']['value'] = array( + 'room_reservations_reservation' => 'room_reservations_reservation', + ); + /* Filter criterion: Content: Date (reservation_date) */ + $handler->display->display_options['filters']['reservation_date_value']['id'] = 'reservation_date_value'; + $handler->display->display_options['filters']['reservation_date_value']['table'] = 'field_data_reservation_date'; + $handler->display->display_options['filters']['reservation_date_value']['field'] = 'reservation_date_value'; + $handler->display->display_options['filters']['reservation_date_value']['exposed'] = TRUE; + $handler->display->display_options['filters']['reservation_date_value']['expose']['operator_id'] = 'reservation_date_value_op'; + $handler->display->display_options['filters']['reservation_date_value']['expose']['label'] = 'Reservation Date'; + $handler->display->display_options['filters']['reservation_date_value']['expose']['operator'] = 'reservation_date_value_op'; + $handler->display->display_options['filters']['reservation_date_value']['expose']['identifier'] = 'reservation_date_value'; + $handler->display->display_options['filters']['reservation_date_value']['expose']['remember_roles'] = array( + 2 => '2', + 1 => 0, + 3 => 0, + 4 => 0, + 5 => 0, + 6 => 0, + 8 => 0, + 9 => 0, + 10 => 0, + 11 => 0, + ); + $handler->display->display_options['filters']['reservation_date_value']['form_type'] = 'date_popup'; + + /* Display: Page */ + $handler = $view->new_display('page', 'Page', 'page'); + $handler->display->display_options['path'] = 'admin/content/manage-reservations'; + $handler->display->display_options['menu']['type'] = 'tab'; + $handler->display->display_options['menu']['title'] = 'Manage Reservations'; + $handler->display->display_options['menu']['weight'] = '0'; + $handler->display->display_options['menu']['context'] = 0; + $handler->display->display_options['menu']['context_only_inline'] = 0; + + $views['manage_reservations'] = $view; + + return $views; +} diff --git a/view/room_reservations.view.inc b/view/room_reservations.view.inc new file mode 100644 index 0000000..42f104f --- /dev/null +++ b/view/room_reservations.view.inc @@ -0,0 +1,97 @@ + $name, '!capacity' => $capacity)); + $room_options[$key] = $value; + if ($first) { + $room_default = $key; + $first = FALSE; + } + } + ksort($room_options); + // Defaults. + if ($selected_category) { + $room_default = 'room ' . $selected_category; + } + foreach ($dates as $day) { + if ($day['selected']) { + $date_default = $day['month-number'] . '/' . $day['day']; + break; + } + } + // Form. + $form['date'] = array( + '#title' => t('View a Different Day'), + '#type' => 'select', + '#options' => $date_options, + '#default_value' => $date_default, + '#weight' => 10, + ); + $form['room'] = array( + '#title' => t('View a Different Room'), + '#type' => 'select', + '#options' => $room_options, + '#default_value' => $room_default, + '#weight' => 20, + ); + $form['submit'] = array( + '#type' => 'submit', + '#value' => t('Change Day or Room'), + '#weight' => 30, + ); + return $form; +} + +/** + * Form submission for the change date form. + * + * @param string $form_id + * Drupal form id. + * @param array $form_state + * Drupal form state object. + */ +function room_reservations_select_room_date_form_submit($form_id, &$form_state) { + $date = $form_state['values']['date']; + $room = drupal_substr($form_state['values']['room'], 5); + $redirect = "room_reservations/" . $date . "/" . $room; + $form_state['redirect'] = $redirect; +} + + diff --git a/view/room_reservations_calendar.view.inc b/view/room_reservations_calendar.view.inc new file mode 100644 index 0000000..58358d2 --- /dev/null +++ b/view/room_reservations_calendar.view.inc @@ -0,0 +1,387 @@ + array('dateformat' => $format)), 'setting'); + + extract($variables); + + // $selected_category inside $variables isn't formed right to be pulled out as is with extract() + $selected_category = NULL; + + // User information. + $full_access = FALSE; + $user_login_name = NULL; + if ($user->uid) { + $user_login_name = $user->name; + $full_access = user_access('administer room reservations system') || user_access('edit any room reservation'); + } + + // Determine which date has been selected by the user. + foreach ($dates as $day) { + if ($day['selected'] == TRUE) { + $day_of_week = $day['day-of-week']; + $month_number = $day['month-number']; + $month = $day['month']; + $xday = $day['day']; + $year = $day['year']; + $yyyymmdd = $day['yyyymmdd']; + } + } + + $calendar_text = check_markup(_room_reservations_get_variable('calendar_text')); + $reserve_room_instructions_text = check_markup(_room_reservations_get_variable('reserve_instructions')); + if (!$reserve_room_instructions_text) { + $reserve_room_instructions_text = t('To reserve a room, click on the desired time/day in the calendar below. You will be asked to login.'); + } + // Room reservations container. + $output = "
"; + $output .= "
"; + + // Info box - user reservations, maps, link to policies. + $output .= "
$calendar_text
+
+ " . $reserve_room_instructions_text . '
'; + + // Calendar date. + $date = format_date(strtotime($month . ' ' . $xday . ', ' . $year), 'custom', 'l, F d, Y'); + $output .= '

' . t('Reservation Calendar') . '

' . + '
' . $date . '  ' . $building_hours_display . '
'; + + // add new Day Selector as popup calendar since we now allow going out up to 6 months rather than 15 days. + $form = drupal_get_form('room_reservations_admin_date_picker', $yyyymmdd); + $output .= '
' . drupal_render($form) . '
'; + + // Reservation calendar grid: + // + // Each block on the grid is assigned one (or more) of the following classes: + // + // closed - the building is closed; + // booked - the building is open and the time slot has been reserved; + // open - the building is open, the time slot has not been reserved, but the user must login to reserve the time slot; + // reservable - the building is open, the time slot has not been reserved and the user is able to reserve the time slot. + // setup - buffer zones added before/after bookings to allow for setup/takedown (per category) + // + + // Tabs. + $output .= "
    "; + $i = 0; + foreach ($categories as $category) { + // Set the first tab as active + // @todo: use JS to set active based on anchor + $active = ($i == 0) ? " class='active'" : ""; + $i++; + $id = strtolower(preg_replace('/[^a-zA-Z0-9-]+/', '-', $category['title'])); + $output .= '
  • " . $category['title'] . '
  • '; + } + + // Panel container. + $output .= "
"; + // If the user is logged in, the class for each unbooked time slot is 'reservable'. If the user is not logged in, the class is 'open'. + // Only logged in users can click a block of time to open the reservation form. + $unbooked_class = ($user->uid) ? 'reservable' : 'open'; + + // Create a tab for each room category. Each tab contains a grid of time slots and rooms. + $i = 0; + foreach ($categories as $category) { + $table = array(); + // Show the first tab and hide all others. + if (!$selected_category) { + $show = ($i == 0) ? 'show' : 'hide'; + $i++; + } + else { + $show = ($category['title'] == $selected_category) ? 'show' : 'hide'; + } + $id = strtolower(preg_replace('/[^a-zA-Z0-9-]+/', '-', $category['title'])); + $r = 0; $c = 0; + + // Date and building hours. + $flipped = variable_get('room_reservations_calendar_flipped', 0); + $orientclass = $flipped ? 'orient-horiz' : 'orient-vert'; + + // this is the main DIV wrapper for the "table" within each Category + $output .= '
'; + + $table[$r][$c] = "
  • " . t('Room') . '
  • '; + $r++; + $table[$r][$c] = "
  • " . t('Capacity') . '
  • '; + $r++; + + // Available hours column. + foreach ($hours as $time_slot) { + $time_display = ($time_slot['class'] == 'odd') ? t($time_slot['display']) : ""; + $table[$r][$c] = "
  • ' . $time_display . '
  • '; + $r++; + } + $table[$r][$c] = "'; + $r++; + $table[$r][$c] = "
  • " . t('Capacity') . '
  • '; + $r++; + // Count the number of rooms in the selected category. + $rooms_per_category = 0; + foreach ($rooms as $room) { + $rid = $room['nid']; + if ($room['reservations_room_category'][LANGUAGE_NONE][0]['target_id'] == $category['nid']) { + $rooms_per_category++; + } + } + + // Column for each room in the category. + foreach ($rooms as $room) { + $rid = $room['nid']; + $room_name = $room['title']; + $room_link = l($room_name, 'node/' . $rid); + $room_desc = $room['body'] ? $room['body'][LANGUAGE_NONE][0]['safe_value'] : ""; + + // use qtip if we have it + if (module_exists('qtip')) { + $room_info = '' . $room_desc . '' . $room_link . ''; + } + else { + $room_info = $room_link; + } + + if ($room['reservations_room_category'][LANGUAGE_NONE][0]['target_id'] == $category['nid']) { + $c++; $r = 0; + // Room name, capacity. + //$output .= '
      '; + //$output .= '
    • ' . $room_info . '
    • '; + $table[$r][$c] = '
    • ' . $room_info . '
    • '; + $r++; + $table[$r][$c] = "
    • " . $room['reservations_room_capacity'][LANGUAGE_NONE][0]['value'] . '
    • '; + // Populate each time slot. + foreach ($hours as $time_slot) { + $r++; + $time = $time_slot['time']; + $open = $time_slot['open']; + + // lets use slot class from reservation if it is set + $slotclass = isset($reservations[$rid][$time_slot['time']]['class']) ? $reservations[$rid][$time_slot['time']]['class'] : $time_slot['class']; + + // to support min adv booking per cat; let's simply mark all slots as closed for dates not availble to this user for this cat + if (!isset($datespercat[$category['nid']][$yyyymmdd])) { + $open = false; + } + + // Determine if the building is open or closed for this time slot. + if ($open) { + //$booked_class = ($reservations[$rid][$time]['booked']) ? 'booked' : $unbooked_class; + $booked_class = ($reservations[$rid][$time]['booked']) ? '' : $unbooked_class; + } + else { + $booked_class = 'closed'; + } + // The time slot can be reserved by the current user. + $viewable_class = ''; + $widthclass = ''; + if ($booked_class == 'reservable' && + (user_access('create room reservations standard') || user_access('create room reservations extended') || user_access('administer room reservations system'))) { + $link = l( + '', + 'node/add/room-reservations-reservation/' . $month_number . '/' . $xday . '/' . $time_slot['time'] . '/' . $rid, + array('html' => true) + ); + $viewable = ''; + } + // The time slot can be reserved by the current user, but the user must login first. + elseif ($booked_class == 'open') { + $link = ""; + $viewable = ''; + } + elseif ($booked_class == 'closed') { + $link = ''; + } + else { + // The time slot has a reservation that can be edited by the current user. + $reservation = node_load($reservations[$rid][$time]['id']); + $viewable_class = node_access('update', $reservation) ? 'viewable' : ''; + //$viewable_class = (($full_access) || ($reservations[$rid][$time]['user'] == $user->uid)) ? 'viewable' : ''; + if ($viewable_class == 'viewable') { + $id = $reservations[$rid][$time]['id']; + $link = $id ? l($reservations[$rid][$time]['name'], 'node/' . $id . '/edit', + array('attributes' => array( + 'title' => $reservations[$rid][$time]['name'], + 'class' => 'booking-span') + ) + ) : ''; + } + // The time slot has a reservation that cannot be viewed by the current user. and we are NOT allowed to see the Title + else if (isset($reservations[$rid][$time]['blocked']) && $reservations[$rid][$time]['blocked']) { + $link = t('Booked'); + } + // The time slot has a reservation that cannot be edited by the current user. but we are allowed to see the Title + else { + $link = $reservations[$rid][$time]['name']; + } + $slots = isset($reservations[$rid][$time]['slots']) ? $reservations[$rid][$time]['slots'] : ''; + $widthclass = $slots ? 'colspan' . $reservations[$rid][$time]['slots'] : ''; + } + + // allow other modules to modify the $link + drupal_alter('room_reservations_link', $link, $reservations[$rid][$time]); + + // allow other modules adding a custom class to slots + $custom_class = ''; + drupal_alter('room_reservations_custom_class', $custom_class, $reservations[$rid][$time]); + + // add div wrapper to display better + $link = $link ? '
      ' . $link . '
      ' : ''; + + // we used book class to determine if linked or not; which we needed for pre/post slots as well as actual reservation slots + // but we don't want to show booked class now for the slots which are just buffer slots + if (stristr($slotclass, 'setup')) { + $booked_class = ''; + } + $table[$r][$c] = "
    • " . $link . "
    • "; + } + // Room name and capacity. + $r++; + $table[$r][$c] = '
    • ' . $room_info . '
    • '; + $r++; + $table[$r][$c] = '
    • ' . $room['reservations_room_capacity'][LANGUAGE_NONE][0]['value'] . '
    • '; + } + } + + // remove extra table labels based on admin setting + $compressed = false; + if (variable_get('room_reservations_compressed_labels', 0)) { + $compressed = true; + } + + // dump our table contents in std or flipped orientation + if ($flipped) { + $table = _room_reservations_transpose($table); + $m = $r; + $n = $c; + } + else { + $m = $c; + $n = $r; + } + for ($x = 0; $x <= $m; $x++){ + if ($flipped && $compressed && ($x == 1 || $x == $m || $x == $m - 1)) continue; + $output .= "
        "; + for ($y = 0; $y <= $n; $y++) { + if (!$flipped && $compressed && ($y == 1 || $y == $n || $y == $n - 1)) continue; + $output .= $table[$y][$x]; + } + $output .= '
      '; + } + + // end of main DIV wrapper for "table" + $output .= '
    '; + } + + // end of panelContainer that holds all tables for all Categories + $output .= '
    '; + + $output .= '
    '; + $output .= '
    '; + + return $output; +} + +function room_reservations_admin_date_picker($form, &$form_state) { + $yyyymmdd = $form_state['build_info']['args'][0]; + $parts = explode('-', $yyyymmdd); + if (user_access('create room reservations extended')) { + $advancedays = variable_get('room_reservations_advance_extended', 180); + } + else { + $advancedays = variable_get('room_reservations_advance_standard', 14); + } + $yearnow = date('Y'); + $absdaynow = date('z'); + $absdaydefault = date('z', mktime(0, 0, 0, $parts[1], $parts[2], $yearnow)); + if ($absdaynow > $absdaydefault) { + $yeardefault = $yearnow + 1; + } + else { + $yeardefault = $yearnow; + } + + $format = str_replace('y', 'Y', strtolower(variable_get('room_reservations_picker_format', 'y/m/d'))); + $form['date'] = array( + '#type' => 'date_popup', + '#default_value' => $yeardefault . '-' . $parts[1] . '-' . $parts[2] . ' 00:00:00', + '#date_type' => DATE_DATETIME, + '#date_timezone' => date_default_timezone(), + '#date_format' => $format, + '#date_increment' => 1, + '#date_year_range' => '-0:+1', + '#datepicker_options' => array( + 'minDate' => '+0d', + 'maxDate' => $advancedays -1 . 'D', + ) + ); + + $form['#after_build'][] = '_room_reservations_admin_date_picker_afterbuild'; + return $form; +} + +function _room_reservations_admin_date_picker_afterbuild($form, &$form_state) { + $form['date']['date']['#title'] = ''; + $form['date']['date']['#description'] = t('click in box to select date'); + return $form; +} + +function _room_reservations_transpose($array) { + array_unshift($array, null); + return call_user_func_array('array_map', $array); +} + diff --git a/view/room_reservations_reservation.view.inc b/view/room_reservations_reservation.view.inc new file mode 100644 index 0000000..1caf1e2 --- /dev/null +++ b/view/room_reservations_reservation.view.inc @@ -0,0 +1,594 @@ +roomCapacity; $x++) { + $number_persons_options[$x] = $x; + } + // Valid lengths of time. + $length_options = array(); + foreach ($res->validLengths as $valid_length) { + if ($valid_length['is_valid']) { + $length_option = $valid_length['length']; + $length_options[$length_option] = $length_option . ' ' . t('minutes'); + } + } + } + if (drupal_strlen($res->phone) == 10) { + $display_phone = drupal_substr($res->phone, 0, 3) . '-' . + drupal_substr($res->phone, 3, 3) . '-' . + drupal_substr($res->phone, 6); + } + else { + $display_phone = ''; + } + // Form definition. + if ($operation === 'add') { + $form['instructions'] = array( + '#title' => t('Patron'), + '#value' => t($reserve_form_instructions_text), + '#weight' => 0, + ); + } + if ($operation === 'update') { + $form['delete_top'] = array( + '#type' => 'submit', + '#value' => t('Cancel My Reservation'), + '#weight' => 4, + ); + } + $form['details'] = array( + '#title' => t('Details'), + '#type' => 'fieldset', + '#weight' => 5, + ); + $form['details']['room'] = array( + '#title' => t('Room'), + '#type' => 'item', + '#value' => t('@room (capacity: !capacity persons)', + array('@room' => $res->room, '!capacity' => $res->roomCapacity)), + '#weight' => 20, + ); + $form['details']['display_date'] = array( + '#title' => t('Date'), + '#type' => 'item', + '#value' => t($res->displayDate), + '#weight' => 25, + ); + $form['details']['time'] = array( + '#title' => t('Time'), + '#type' => 'item', + '#value' => t($res->displayTime), + '#weight' => 30, + ); + if ($operation == 'add') { + $form['details']['length'] = array( + '#title' => t('Length'), + '#type' => 'radios', + '#options' => $length_options, + '#required' => TRUE, + '#weight' => 35, + ); + } + elseif ($operation == 'update') { + $form['details']['length'] = array( + '#title' => t('Length'), + '#type' => 'radios', + '#options' => $length_options, + '#default_value' => $res->length, + '#required' => TRUE, + '#weight' => 35, + ); + } + else { + $form['details']['length'] = array( + '#title' => t('Length'), + '#type' => 'item', + '#value' => $res->length, + '#weight' => 35, + ); + } + if (($operation == 'add') || ($operation == 'update')) { + $form['details']['group_name'] = array( + '#title' => t('Group name'), + '#type' => 'textfield', + '#description' => t('Identifies your group on the reservation calendar. + 14 character limit.'), + '#default_value' => $res->name, + '#maxlength' => 14, + '#size' => 14, + '#required' => TRUE, + '#weight' => 40, + ); + } + else { + $form['details']['group_name'] = array( + '#title' => t('Group name'), + '#type' => 'item', + '#value' => $res->name, + '#weight' => 40, + ); + } + if ($room_reservations_group_size) { + if ($operation == 'add') { + $form['details']['group_size'] = array( + '#title' => t('Size of the group'), + '#type' => 'radios', + '#options' => $number_persons_options, + '#required' => TRUE, + '#weight' => 45, + ); + } + elseif ($operation == 'update') { + $form['details']['group_size'] = array( + '#title' => t('Size of the group'), + '#type' => 'radios', + '#options' => $number_persons_options, + '#default_value' => $res->groupSize, + '#required' => TRUE, + '#weight' => 45, + ); + } + else { + $form['details']['group_size'] = array( + '#title' => t('Size of the group'), + '#type' => 'item', + '#value' => $res->groupSize, + '#weight' => 45, + ); + } + } + if (_room_reservations_full_access()) { + $form['details']['user_name'] = array( + '#title' => t('User name'), + '#type' => 'item', + '#value' => $res->userName, + '#weight' => 55, + ); + } + $form['reminders'] = array( + '#title' => t('Reminders'), + '#type' => 'fieldset', + '#weight' => 60, + ); + if ($sms_option) { + if (($operation == 'add') || ($operation == 'update')) { + $form['reminders']['textmsg'] = array( + '#title' => t('I want to receive my confirmation and reminder as a text + message.'), + '#type' => 'checkbox', + '#return_value' => 1, + '#default_value' => $res->textmsg, + '#weight' => 65, + ); + $form['reminders']['textmsg_fields_start'] = array( + '#value' => '
    ', + '#weight' => 66, + ); + $form['reminders']['phone'] = array( + '#title' => t('Phone number'), + '#type' => 'textfield', + '#maxlength' => 15, + '#size' => 15, + '#default_value' => $display_phone, + '#weight' => 70, + ); + $carriers = _room_reservations_carriers(); + $form['reminders']['carrier'] = array( + '#title' => t('Carrier'), + '#type' => 'select', + '#options' => $carriers, + '#default_value' => $res->carrier, + '#weight' => 75, + ); + $form['reminders']['charges'] = array( + '#value' => '
    ' . + t('NOTE: Carrier charges may apply if your cell phone service + plan does not include free text messaging.') . '
    ', + '#weight' => 80, + ); + $form['reminders']['testmsg_fields_end'] = array( + '#value' => '
    ', + '#weight' => 85, + ); + } + } + $reminder_columns = 60; + if (($operation == 'add') || ($operation == 'update')) { + $form['reminders']['email_addresses'] = array( + '#title' => t('Email Addresses'), + '#type' => 'textarea', + '#default_value' => $res->emailAddresses, + '#description' => t('Reservation confirmation and reminder will be sent + to any email addresses entered here. Separate addresses with a + comma.'), + '#cols' => $reminder_columns, + '#rows' => 1, + '#weight' => 90, + ); + } + else { + $form['reminders']['email_addresses'] = array( + '#title' => t('Email Addresses'), + '#type' => 'item', + '#value' => $res->emailAddresses, + '#weight' => 90, + ); + } + if ($operation == 'add') { + $form['add'] = array( + '#type' => 'submit', + '#value' => t('Save My Reservation'), + '#weight' => 100, + ); + } + elseif ($operation == 'update') { + $form['update'] = array( + '#type' => 'submit', + '#value' => t('Update My Reservation'), + '#weight' => 100, + ); + $form['delete'] = array( + '#type' => 'submit', + '#value' => t('Cancel My Reservation'), + '#weight' => 101, + ); + } + $form['id'] = array( + '#type' => 'value', + '#value' => $res->id, + ); + $form['month_number'] = array( + '#type' => 'value', + '#value' => $res->monthNumber, + ); + $form['day'] = array( + '#type' => 'value', + '#value' => $res->day, + ); + $form['year'] = array( + '#type' => 'value', + '#value' => $res->year, + ); + $form['time_number'] = array( + '#type' => 'value', + '#value' => $res->time, + ); + $form['room_name'] = array( + '#type' => 'value', + '#value' => $res->room, + ); + $form['category'] = array( + '#type' => 'value', + '#value' => $res->category, + ); + $form['yyyymmdd'] = array( + '#type' => 'value', + '#value' => $res->date, + ); + $form['user_name_hold'] = array( + '#type' => 'value', + '#value' => $res->userName, + ); + return $form; +} + +/** + * Form validation for the reservation form. + * + * @param string $form_id + * Drupal form id. + * @param array $form_state + * Drupal form state object. + */ +function room_reservations_res_form_validate($form_id, &$form_state) { + // When a user is filling out the form to reserve a room, it is possible that + // before she can submit the form, someone else will complete a reservation + // for that same room, same date, and at least part of the same time period. + // If that should occur, and the user has requested a length of time that + // is no longer available, Drupal automatically creates the following error: + // 'An illegal choice has been detected. Please contact the site + // administrator.' Since this message is somewhat alarming to the user and + // not very informative, it is replaced in this function with a more + // descriptive message. + $errors = drupal_get_messages(); + foreach ($errors as $type => $id) { + foreach ($id as $message) { + // Loop through individual messages, looking for ones to remove or + // replace. + if (room_reservations_rewrite_error($message) === FALSE) { + drupal_set_message($message, $type); + } + elseif (room_reservations_rewrite_error($message) !== TRUE) { + drupal_set_message(room_reservations_rewrite_error($message), $type); + } + } + } + // Validate email addresses. + $email_addresses = trim($form_state['values']['email_addresses']); + if (drupal_strlen($email_addresses)) { + $valid_addresses = TRUE; + $list_items = explode(',', $email_addresses); + foreach ($list_items as $address) { + if (!valid_email_address(trim($address))) { + $valid_addresses = FALSE; + } + } + if (!$valid_addresses) { + form_set_error('email_addresses', t('Email Addresses contains an invalid + email address.')); + } + } + if ((!drupal_strlen($email_addresses)) && + (!$form_state['values']['textmsg'])) { + $default_sms_option = _room_reservations_get_variable('sms_option'); + if ($default_sms_option) { + form_set_error('email_addresses', t('Your email address or phone number is + required.')); + } + else { + form_set_error('email_addresses', t('Your email address is + required.')); + } + } + + // Text message information. + if ($form_state['values']['textmsg']) { + $phone = $form_state['values']['phone']; + $phone_number = preg_replace('/[^\d]/', '', $phone); + if (drupal_strlen($phone_number) <> 10) { + form_set_error('phone', t('Ten digit phone number is required + when receiving confirmation by text message.')); + } + if (!$form_state['values']['carrier']) { + form_set_error('carrier', t('Carrier is required + when receiving confirmation by text message.')); + } + } + +} + +/** + * Form submission for the reservation form. + * + * @param string $form_id + * Drupal form id. + * @param array $form_state + * Drupal form state object. + */ +function room_reservations_res_form_submit($form_id, &$form_state) { + $id = $form_state['values']['id']; + $month_number = $form_state['values']['month_number']; + $day = $form_state['values']['day']; + $year = $form_state['values']['year']; + $time = $form_state['values']['time_number']; + $length = $form_state['values']['length']; + $room = $form_state['values']['room_name']; + $group_name = $form_state['values']['group_name']; + $group_size = $form_state['values']['group_size']; + $user_name = $form_state['values']['user_name_hold']; + $category = $form_state['values']['category']; + $yyyymmdd = $form_state['values']['yyyymmdd']; + $email_addresses = trim($form_state['values']['email_addresses']); + $textmsg_checkbox = $form_state['values']['textmsg']; + $carrier = $form_state['values']['carrier']; + $phone_full = $form_state['values']['phone']; + $phone = preg_replace('/[^\d]/', '', $phone_full); + if ($textmsg_checkbox) { + $textmsg = 'Y'; + } + else { + $textmsg = 'N'; + $carrier = 0; + $phone = ''; + } + if ($time < 1000) { + $time = str_pad($time, 4, '0', STR_PAD_LEFT); + } + $hours = _room_reservations_hours(); + foreach ($hours as $individual_hour) { + if ($individual_hour['time'] == $time) { + $display_hour = $individual_hour['display']; + break; + } + } + $day_of_week = date("l", strtotime($yyyymmdd)); + $month_name = date("F", strtotime($yyyymmdd)); + // Add. + if ($form_state['clicked_button']['#value'] == t('Save My Reservation')) { + $result = db_query("INSERT INTO {room_reservations} (date, time, length, room, name, group_size, user_name, email_addresses, create_date, textmsg, carrier, phone ) + VALUES ('%s', '%s', %d, '%s', '%s', %d, '%s', '%s', '%s', '%s', %d, '%s')", + $yyyymmdd, $time, $length, $room, $group_name, $group_size, $user_name, $email_addresses, date('Y-m-d H:i:s', REQUEST_TIME), $textmsg, $carrier, $phone); + // Send a confirmation email or text message. + $id = db_last_insert_id('room_reservations', 'id'); + $params = array( + 'room' => $room, + 'month' => $month_name, + 'month_number' => $month_number, + 'day' => $day, + 'day_of_week' => $day_of_week, + 'time' => $display_hour, + 'minutes' => $length, + 'name' => $group_name, + 'id' => $id, + 'carrier' => $carrier, + 'phone' => $phone, + ); + $from = _room_reservations_get_variable('from_address'); + if ($result) { + drupal_set_message(t('Your group study room reservation has been made.')); + // Send an email to each person in the group. If the person is the one + // who made the reservation, send the confirmation message. Otherwise, + // send the notification message. + if (drupal_strlen($email_addresses)) { + $to_addresses = explode(',', $email_addresses); + foreach ($to_addresses as $to_address) { + $to_address = trim($to_address); + $pos = strpos($to_address, $user_name); + if ($pos === FALSE) { + $key = 'notification'; + } + else { + $key = 'confirmation'; + } + $response = drupal_mail( + 'room_reservations', $key, $to_address, language_default(), + $params, $from, TRUE); + } + } + // If requested, send a text message confirmation to the person who made + // the reservation. + if ($textmsg == 'Y') { + _room_reservations_send_sms('confirmation', $params); + } + } + else { + drupal_set_message(t('Your group study room reservation could not be + made.'), 'error'); + } + $redirect = "room_reservations/" . $month_number . "/" . $day . "/" . $category; + $form_state['redirect'] = $redirect; + } + // Update. + elseif ($form_state['clicked_button']['#value'] == + t('Update My Reservation')) { + $result = db_query(" + UPDATE {room_reservations} + SET length = %d, + name = '%s', + group_size = %d, + email_addresses = '%s', + update_date = '%s', + textmsg = '%s', + carrier = %d, + phone = '%s' + WHERE id = %d", $length, $group_name, $group_size, $email_addresses, date('Y-m-d H:i:s', REQUEST_TIME), $textmsg, $carrier, $phone, $id); + if ($result) { + drupal_set_message(t('Your group study room reservation has been + updated.')); + } + else { + drupal_set_message(t('Your group study room reservation could not be + updated.'), 'error'); + } + $redirect = "room_reservations/" . $month_number . "/" . $day . "/" . $category; + $form_state['redirect'] = $redirect; + } + elseif ($form_state['clicked_button']['#value'] == t('Cancel My Reservation')) { + $redirect = "room_reservations/delete/" . $id; + $form_state['redirect'] = $redirect; + } +} + +/** + * Form constructor for the delete reservation form. + * + * @param Reservation $res + * A reservation object that contains all the details related to the + * reservation being deleted. + */ +function room_reservations_cancel_form(&$form_state, $res) { + $room_capacity = $res->roomCapacity; + // Form definition. + $form['room'] = array( + '#title' => t('Room'), + '#type' => 'item', + '#value' => t('@room (capacity: !capacity persons)', array('@room' => $res->room, '!capacity' => $room_capacity)), + '#weight' => 10, + ); + $form['display_date'] = array( + '#title' => t('Date'), + '#type' => 'item', + '#value' => t($res->displayDate), + '#weight' => 20, + ); + $form['time'] = array( + '#title' => t('Time'), + '#type' => 'item', + '#value' => t($res->displayTime), + '#weight' => 30, + ); + $form['length'] = array( + '#title' => t('Length'), + '#type' => 'item', + '#value' => t('!length minutes', array('!length' => $res->length)), + '#weight' => 40, + ); + $form['group_name'] = array( + '#title' => t('Group name'), + '#type' => 'item', + '#value' => $res->name, + '#weight' => 50, + ); + $form['id'] = array( + '#type' => 'value', + '#value' => $res->id, + ); + $form['month_number'] = array( + '#type' => 'value', + '#value' => $res->monthNumber, + ); + $form['day'] = array( + '#type' => 'value', + '#value' => $res->day, + ); + $form['category'] = array( + '#type' => 'value', + '#value' => $res->roomCategory, + ); + $form['user_name_hold'] = array( + '#type' => 'value', + '#value' => $res->userName, + ); + return confirm_form($form, + t('Cancel'), 'room_reservations/' . $res->monthNumber . '/' . $res->day . '/' . $res->roomCategory, + t('Are you sure you want to cancel this reservation? This action cannot be undone.'), + t('Cancel My Reservation'), + t("Don't Cancel My Reservation") + ); +} + +/** + * Form submission for the delete reservation form. + * + * @param string $form_id + * Drupal form id. + * @param array $form_state + * Drupal form state object. + */ +function room_reservations_cancel_form_submit($form_id, &$form_state) { + $id = $form_state['values']['id']; + $month_number = $form_state['values']['month_number']; + $day = $form_state['values']['day']; + $category = $form_state['values']['category']; + // Delete. + if ($form_state['clicked_button']['#value'] == t('Cancel My Reservation')) { + $result = db_query("UPDATE {room_reservations} SET deleted = '%s', update_date = '%s' WHERE id = %d", 'Y', date('Y-m-d H:i:s', REQUEST_TIME), $id); + if ($result) { + drupal_set_message(t('Your group study room reservation has been cancelled.')); + } + else { + drupal_set_message(t('Your group study room reservation could not be cancelled.'), 'error'); + } + } + $redirect = "room_reservations/" . $month_number . "/" . $day . "/" . $category; + $form_state['redirect'] = $redirect; +}