· 7 years ago · Feb 08, 2019, 06:44 AM
1<?php
2namespace MRBS;
3
4use MRBS\Form\Form;
5use MRBS\Form\ElementDiv;
6use MRBS\Form\ElementFieldset;
7use MRBS\Form\ElementInputCheckbox;
8use MRBS\Form\ElementInputDate;
9use MRBS\Form\ElementInputHidden;
10use MRBS\Form\ElementInputRadio;
11use MRBS\Form\ElementInputSubmit;
12use MRBS\Form\ElementLabel;
13use MRBS\Form\ElementSelect;
14use MRBS\Form\ElementSpan;
15use MRBS\Form\FieldDiv;
16use MRBS\Form\FieldInputCheckbox;
17use MRBS\Form\FieldInputCheckboxGroup;
18use MRBS\Form\FieldInputDatalist;
19use MRBS\Form\FieldInputDate;
20use MRBS\Form\FieldInputNumber;
21use MRBS\Form\FieldInputRadioGroup;
22use MRBS\Form\FieldInputSubmit;
23use MRBS\Form\FieldInputText;
24use MRBS\Form\FieldInputFile;
25use MRBS\Form\FieldSelect;
26
27// If you want to add some extra columns to the entry and repeat tables to
28// record extra details about bookings then you can do so and this page should
29// automatically recognise them and handle them. NOTE: if you add a column to
30// the entry table you must add an identical column to the repeat table.
31//
32// At the moment support is limited to the following column types:
33//
34// MySQL PostgreSQL Form input type
35// ----- ---------- ---------------
36// bigint bigint text
37// int integer text
38// mediumint text
39// smallint smallint checkbox
40// tinyint checkbox
41// text text textarea
42// tinytext textarea
43// character varying textarea
44// varchar(n) character varying(n) text/textarea, depending on the value of n
45// character text
46// char(n) character(n) text/textarea, depending on the value of n
47//
48// NOTE 1: For char(n) and varchar(n) fields, a text input will be presented if
49// n is less than or equal to $text_input_max, otherwise a textarea box will be
50// presented.
51//
52// NOTE 2: PostgreSQL booleans are not supported, due to difficulties in
53// handling the fields in a database independent way (a PostgreSQL boolean
54// will return a PHP boolean type when read by a PHP query, whereas a MySQL
55// tinyint returns an int). In order to have a boolean field in the room
56// table you should use a smallint in PostgreSQL or a smallint or a tinyint
57// in MySQL.
58//
59// You can put a description of the column that will be used as the label in
60// the form in the $vocab_override variable in the config file using the tag
61// 'entry.[columnname]'. (Note that it is not necessary to add a
62// 'repeat.[columnname]' tag. The entry tag is sufficient.)
63//
64// For example if you want to add a column recording the number of participants
65// you could add a column to the entry and repeat tables called 'participants'
66// of type int. Then in the appropriate lang file(s) you would add the line
67//
68// $vocab_override['en']['entry.participants'] = "Participants"; // or appropriate translation
69//
70// If MRBS can't find an entry for the field in the lang file or $vocab_override,
71// then it will use the fieldname, eg 'coffee_machine'.
72
73
74require "defaultincludes.inc";
75require_once "mrbs_sql.inc";
76
77$fields = db()->field_info($tbl_entry);
78$custom_fields = array();
79
80// Fill $edit_entry_field_order with not yet specified entries.
81$entry_fields = array('name', 'description', 'start_time', 'end_time', 'room_id',
82 'type', 'confirmation_status', 'privacy_status', 'surat_permohonan',);
83
84foreach ($entry_fields as $field)
85{
86 if (!in_array($field, $edit_entry_field_order))
87 {
88 $edit_entry_field_order[] = $field;
89 }
90}
91
92$custom_fields_map = array();
93foreach ($fields as $field)
94{
95 $key = $field['name'];
96 if (!in_array($key, $standard_fields['entry']))
97 {
98 $custom_fields_map[$key] = $field;
99 if (!in_array($key, $edit_entry_field_order))
100 {
101 $edit_entry_field_order[] = $key;
102 }
103 }
104}
105
106
107function get_field_entry_input($params)
108{
109 global $select_options, $datalist_options;
110 global $maxlength;
111
112 if (isset($params['field']))
113 {
114 if (!empty($select_options[$params['field']]))
115 {
116 $class = 'FieldSelect';
117 }
118 elseif (!empty($datalist_options[$params['field']]))
119 {
120 $class = 'FieldInputDatalist';
121 }
122 elseif ($params['field'] == 'entry.description')
123 {
124 $class = 'FieldTextarea';
125 }
126 else
127 {
128 $class = 'FieldInputText';
129 }
130 }
131
132 $full_class = __NAMESPACE__ . "\\Form\\$class";
133 $field = new $full_class();
134 $field->setLabel($params['label'])
135 ->setControlAttribute('name', $params['name']);
136
137 if (!empty($params['required']))
138 {
139 $field->setControlAttribute('required', true);
140 }
141 if (!empty($params['disabled']))
142 {
143 $field->setControlAttribute('disabled', true);
144 }
145
146 switch ($class)
147 {
148 case 'FieldSelect':
149 $options = $select_options[$params['field']];
150 $field->addSelectOptions($options, $params['value']);
151 break;
152
153 case 'FieldInputDatalist':
154 $options = $datalist_options[$params['field']];
155 $field->addDatalistOptions($options);
156 // Drop through
157
158 case 'FieldInputText':
159 if (!empty($params['required']))
160 {
161 // Set a pattern as well as required to prevent a string of whitespace
162 $field->setControlAttribute('pattern', REGEX_TEXT_POS);
163 }
164 // Drop through
165
166 case 'FieldTextarea':
167 if ($class == 'FieldTextarea')
168 {
169 $field->setControlText($params['value']);
170 }
171 else
172 {
173 $field->setControlAttribute('value', $params['value']);
174 }
175 if (isset($maxlength[$params['field']]))
176 {
177 $field->setControlAttribute('maxlength', $maxlength[$params['field']]);
178 }
179 break;
180
181 default:
182 throw new \Exception("Unknown class '$class'");
183 break;
184 }
185
186 return $field;
187}
188
189
190function get_field_name($value, $disabled=false)
191{
192 $params = array('label' => get_vocab('namebooker'),
193 'name' => 'name',
194 'field' => 'entry.name',
195 'value' => $value,
196 'required' => true,
197 'disabled' => $disabled);
198
199 return get_field_entry_input($params);
200}
201
202
203function get_field_description($value, $disabled=false)
204{
205 global $is_mandatory_field;
206
207 $params = array('label' => get_vocab('fulldescription'),
208 'name' => 'description',
209 'field' => 'entry.description',
210 'value' => $value,
211 'required' => !empty($is_mandatory_field['entry.description']),
212 'disabled' => $disabled);
213
214 return get_field_entry_input($params);
215}
216
217function get_field_surat_permohonan($value, $disabled=false)
218{
219 global $is_mandatory_field;
220
221 $params = array('label' => get_vocab('suratpermohonan'),
222 'name' => 'surat_permohonan',
223 'field' => 'entry.surat_permohonan',
224 'value' => $value,
225 'required' => !empty($is_mandatory_field['entry.surat_permohonan']),
226 'disabled' => $disabled);
227
228 return get_field_entry_input($params);
229}
230
231// Generate a time or period selector starting with $first and ending with $last.
232// $time is a full Unix timestamp and is the current value. The selector returns
233// the start time in seconds since the beginning of the day for the start of that slot.
234// Note that these are nominal seconds and do not take account of any DST changes that
235// may have happened earlier in the day. (It's this way because we don't know what day
236// it is as that's controlled by the date selector - and we can't assume that we have
237// JavaScript enabled to go and read it)
238//
239// $display_none parameter sets the display style of the <select> to "none"
240// $disabled parameter disables the input and also generate a hidden input, provided
241// that $display_none is FALSE. (This prevents multiple inputs
242// of the same name)
243// $is_start Boolean. Whether this is the start selector. Default FALSE
244function get_slot_selector($area, $id, $name, $current_s, $display_none=false, $disabled=false, $is_start=false)
245{
246 // Check that $resolution is positive to avoid an infinite loop below.
247 // (Shouldn't be possible, but just in case ...)
248 if (empty($area['resolution']) || ($area['resolution'] < 0))
249 {
250 throw new \Exception("Internal error - resolution is NULL or <= 0");
251 }
252
253 if ($area['enable_periods'])
254 {
255 $base = 12*SECONDS_PER_HOUR; // The start of the first period of the day
256 }
257 else
258 {
259 $format = hour_min_format();
260 }
261
262 // Build the options
263 $options = array();
264 // If we're using periods then the last slot is actually the start of the last period,
265 // or if we're using times and this is the start selector, then we don't show the last
266 // time
267 if ($area['enable_periods'] || $is_start)
268 {
269 $last = $area['last'] - $area['resolution'];
270 }
271 else
272 {
273 $last = $area['last'];
274 }
275 for ($s = $area['first']; $s <= $last; $s += $area['resolution'])
276 {
277 if ($area['enable_periods'])
278 {
279 $options[$s] = $area['periods'][intval(($s-$base)/60)];
280 }
281 else
282 {
283 $options[$s] = hour_min($s);
284 }
285 }
286
287 $field = new ElementSelect();
288 $field->setAttributes(array('id' => $id,
289 'name' => $name,
290 'disabled' => $disabled || $display_none))
291 ->addSelectOptions($options, $current_s, true);
292
293 if ($disabled)
294 {
295 // If $disabled is set, give the element a class so that the JavaScript
296 // knows to keep it disabled
297 $field->setAttribute('class', 'keep_disabled');
298 }
299 if ($display_none)
300 {
301 $field->setAttribute('style', 'display: none');
302 }
303
304 if ($disabled && !$display_none)
305 {
306 $hidden = new ElementInputHidden();
307 $hidden->setAttributes(array('name' => $name,
308 'value' => $current_s));
309 $field->next($hidden);
310 }
311
312 return $field;
313}
314
315
316// Generate the All Day checkbox for an area
317function get_all_day($area, $input_id, $input_name, $display_none=false, $disabled=false)
318{
319 global $drag, $id;
320
321 $element = new ElementDiv();
322
323 if ($display_none || !$area['show_all_day'])
324 {
325 $element->setAttribute('style', 'display: none');
326 }
327
328 // (1) If $display_none or $disabled are set then we'll also disable the select so
329 // that there is only one select passing through the variable to the handler.
330 // (2) If this is an existing booking that we are editing or copying, then we do
331 // not want the default duration applied
332 $disable_field = $disabled || $display_none || !$area['show_all_day'];
333
334 $checkbox = new ElementInputCheckbox();
335 $checkbox->setAttributes(array('name' => $input_name,
336 'id' => $input_id,
337 'data-show' => ($area['show_all_day']) ? '1' : '0',
338 'disabled' => $disable_field))
339 ->setChecked($area['default_duration_all_day'] && !isset($id) && !$drag);
340
341 if ($disable_field)
342 {
343 // and if $disabled is set, give the element a class so that the JavaScript
344 // knows to keep it disabled
345 $checkbox->addClass('keep_disabled');
346 }
347
348 $label = new ElementLabel();
349 $label->setText(get_vocab('all_day'))
350 ->setAttribute('class', 'no_suffix');
351
352 $label->addElement($checkbox);
353 $element->addElement($label);
354
355 return $element;
356}
357
358
359function get_field_start_time($value, $disabled=false)
360{
361 global $areas, $area_id;
362
363 $date = getbookingdate($value);
364 $start_date = format_iso_date($date['year'], $date['mon'], $date['mday']);
365 $current_s = (($date['hours'] * 60) + $date['minutes']) * 60;
366
367 $field = new FieldDiv();
368
369 // Generate the live slot selector and all day checkbox
370 $element_date = new ElementInputDate();
371 $element_date->setAttributes(array('id' => 'start_date',
372 'name' => 'start_date',
373 'value' => $start_date,
374 'disabled' => $disabled,
375 'required' => true));
376
377 $field->setLabel(get_vocab('start'))
378 ->addControlElement($element_date)
379 ->addControlElement(get_slot_selector($areas[$area_id],
380 'start_seconds',
381 'start_seconds',
382 $current_s,
383 false,
384 $disabled,
385 true))
386 ->addControlElement(get_all_day($areas[$area_id],
387 'all_day',
388 'all_day',
389 false,
390 $disabled));
391
392 // Generate the templates for each area
393 foreach ($areas as $a)
394 {
395 $field->addControlElement(get_slot_selector($a,
396 'start_seconds' . $a['id'],
397 'start_seconds',
398 $current_s,
399 true,
400 true,
401 true))
402 ->addControlElement(get_all_day($a,
403 'all_day' . $a['id'],
404 'all_day',
405 true,
406 true));
407 }
408
409 return $field;
410}
411
412
413function get_field_end_time($value, $disabled=false)
414{
415 global $areas, $area_id;
416 global $multiday_allowed;
417
418 $date = getbookingdate($value);
419 $end_date = format_iso_date($date['year'], $date['mon'], $date['mday']);
420 $current_s = (($date['hours'] * 60) + $date['minutes']) * 60;
421
422 $field = new FieldDiv();
423
424 // Generate the live slot selector
425 // If we're using periods the booking model is slightly different,
426 // so subtract one period because the "end" period is actually the beginning
427 // of the last period booked
428 $element_date = new ElementInputDate();
429 $element_date->setAttributes(array('id' => 'end_date',
430 'name' => 'end_date',
431 'value' => $end_date,
432 'disabled' => $disabled));
433
434 // Don't show the end date if multiday bookings are not allowed
435 if (!$multiday_allowed)
436 {
437 $element_date->setAttributes(array('style' => 'visibility: hidden',
438 'disabled' => true));
439 }
440
441 $a = $areas[$area_id];
442 $this_current_s = ($a['enable_periods']) ? $current_s - $a['resolution'] : $current_s;
443
444 $field->setLabel(get_vocab('end'))
445 ->addControlElement($element_date)
446 ->addControlElement(get_slot_selector($areas[$area_id],
447 'end_seconds',
448 'end_seconds',
449 $this_current_s,
450 false,
451 $disabled,
452 false));
453
454 // Generate the templates
455 foreach ($areas as $a)
456 {
457 $this_current_s = ($a['enable_periods']) ? $current_s - $a['resolution'] : $current_s;
458 $field->addControlElement(get_slot_selector($a,
459 'end_seconds' . $a['id'],
460 'end_seconds',
461 $this_current_s,
462 true,
463 true,
464 false));
465 }
466
467 // An empty <span> to hold JavaScript messages
468 $span = new ElementSpan();
469 $span->setAttributes(array('id' => 'end_time_error',
470 'class' => 'error'));
471 $field->addControlElement($span);
472
473 return $field;
474}
475
476
477function get_field_areas($value, $disabled=false)
478{
479 global $areas;
480
481 // No point in being able to choose an area if there aren't more
482 // than one of them.
483 if (count($areas) < 2)
484 {
485 return null;
486 }
487
488 $field = new FieldSelect();
489
490 $options = array();
491 // go through the areas and create the options
492 foreach ($areas as $a)
493 {
494 $options[$a['id']] = $a['area_name'];
495 }
496
497 // We will set the display to none and then turn it on in the JavaScript. That's
498 // because if there's no JavaScript we don't want to display it because we won't
499 // have any means of changing the rooms if the area is changed.
500 $field->setAttributes(array('id' => 'div_areas',
501 'style' => 'display: none'))
502 ->setLabel(get_vocab('area'))
503 ->setControlAttributes(array('name' => 'area',
504 'disabled' => $disabled))
505 ->addSelectOptions($options, $value, true);
506
507 return $field;
508}
509
510
511function get_field_rooms($value, $disabled=false)
512{
513 global $tbl_room, $tbl_area;
514 global $multiroom_allowed, $area_id, $areas, $rooms;
515
516 // First of all generate the rooms for this area
517 $field = new FieldSelect();
518
519 $field->setLabel(get_vocab('rooms'));
520
521 // No point telling them how to select multiple rooms if the input
522 // is disabled or they aren't allowed to
523 if ($multiroom_allowed && !$disabled)
524 {
525 $field->setLabelAttribute('title', get_vocab('ctrl_click'));
526 }
527
528 $field->setAttributes(array('class' => 'multiline',
529 'id' => 'div_rooms'))
530 ->setControlAttributes(array('id' => 'rooms',
531 'name' => 'rooms[]',
532 'multiple' => $multiroom_allowed, // If multiple is not set then required is unnecessary
533 'required' => $multiroom_allowed, // and also causes an HTML5 validation error
534 'disabled' => $disabled,
535 'size' => '5'))
536 ->addSelectOptions($rooms[$area_id], $value, true);
537
538 // Then generate templates for all the rooms
539 foreach ($rooms as $a => $area_rooms)
540 {
541 $room_ids = array_keys($area_rooms);
542
543 $select = new ElementSelect();
544 $select->setAttributes(array('id' => 'rooms' . $a,
545 'style' => 'display: none',
546 'name' => 'rooms[]',
547 'multiple' => $multiroom_allowed, // If multiple is not set then required is unnecessary
548 'required' => $multiroom_allowed, // and also causes an HTML5 validation error
549 'disabled' => true,
550 'size' => '5'))
551 ->addSelectOptions($area_rooms, $room_ids[0], true);
552 // Put in some data about the area for use by the JavaScript
553 $select->setAttributes(array(
554 'data-enable_periods' => ($areas[$a]['enable_periods']) ? 1 : 0,
555 'data-n_periods' => count($areas[$a]['periods']),
556 'data-default_duration' => (isset($areas[$a]['default_duration']) && ($areas[$a]['default_duration'] != 0)) ? $areas[$a]['default_duration'] : SECONDS_PER_HOUR,
557 'data-default_duration_all_day' => ($areas[$a]['default_duration_all_day']) ? 1 : 0,
558 'data-max_duration_enabled' => ($areas[$a]['max_duration_enabled']) ? 1 : 0,
559 'data-max_duration_secs' => $areas[$a]['max_duration_secs'],
560 'data-max_duration_periods' => $areas[$a]['max_duration_periods'],
561 'data-max_duration_qty' => $areas[$a]['max_duration_qty'],
562 'data-max_duration_units' => $areas[$a]['max_duration_units'],
563 'data-timezone' => $areas[$a]['timezone']
564 ));
565 $field->addElement($select);
566
567 } // foreach
568
569 return $field;
570}
571
572
573function get_field_type($value, $disabled=false)
574{
575 global $booking_types, $is_mandatory_field;
576
577 // Don't bother with types if there's only one of them (or even none)
578 if (count($booking_types) < 2)
579 {
580 return null;
581 }
582
583 // Get the options
584 if (!empty($is_mandatory_field['entry.type']))
585 {
586 // Add a blank option to force a selection
587 $options[''] = get_type_vocab('');
588 }
589
590 foreach ($booking_types as $key)
591 {
592 $options[$key] = get_type_vocab($key);
593 }
594
595 $field = new FieldSelect();
596
597 $field->setLabel(get_vocab('type'))
598 ->setControlAttributes(array('name' => 'type',
599 'disabled' => $disabled,
600 'required' => !empty($is_mandatory_field['entry.type'])))
601 ->addSelectOptions($options, $value, true);
602
603 return $field;
604}
605
606
607function get_field_confirmation_status($value, $disabled=false)
608{
609 global $confirmation_enabled;
610
611 if (!$confirmation_enabled)
612 {
613 return null;
614 }
615
616 $options = array(0 => get_vocab('tentative'),
617 1 => get_vocab('confirmed'));
618
619 $value = ($value) ? 0 : 1;
620
621 $field = new FieldInputRadioGroup();
622
623 $field->setLabel(get_vocab('confirmation_status'))
624 ->addRadioOptions($options, 'confirmed', $value, true, $disabled);
625
626 return $field;
627}
628
629
630function get_field_privacy_status($value, $disabled=false)
631{
632 global $private_enabled, $private_mandatory;
633
634 if (!$private_enabled)
635 {
636 return null;
637 }
638
639 $options = array(0 => get_vocab('public'),
640 1 => get_vocab('private'));
641
642 $value = ($value) ? 1 : 0;
643
644 $field = new FieldInputRadioGroup();
645
646 $field->setLabel(get_vocab('privacy_status'))
647 ->addRadioOptions($options, 'private', $value, true, $private_mandatory || $disabled);
648
649 return $field;
650}
651
652
653function get_field_custom($key, $disabled=false)
654{
655 global $custom_fields, $custom_fields_map, $tbl_entry;
656 global $is_mandatory_field, $text_input_max, $maxlength;
657
658 // First check that the custom field exists. It normally will, but won't if
659 // $edit_entry_field_order contains a value for which a field doesn't exist.
660 if (!isset($custom_fields_map[$key]))
661 {
662 return;
663 }
664
665 $custom_field = $custom_fields_map[$key];
666
667 // Output a checkbox if it's a boolean or integer <= 2 bytes (which we will
668 // assume are intended to be booleans)
669 if (($custom_field['nature'] == 'boolean') ||
670 (($custom_field['nature'] == 'integer') && isset($custom_field['length']) && ($custom_field['length'] <= 2)) )
671 {
672 $class = 'FieldInputCheckbox';
673 }
674 // Output a textarea if it's a character string longer than the limit for a
675 // text input
676 elseif (($custom_field['nature'] == 'character') &&
677 isset($custom_field['length']) &&
678 ($custom_field['length'] > $text_input_max))
679 {
680 // HTML5 does not allow a pattern attribute for the textarea element
681 $class = 'FieldTextarea';
682 }
683 // Otherwise check if it's an integer field
684 elseif (($custom_field['nature'] == 'integer') && ($custom_field['length'] > 2))
685 {
686 $class = 'FieldInputNumber';
687 }
688 // Otherwise it's a text input of some kind (which includes <select>s and
689 // <datalist>s)
690 else
691 {
692 $params = array('label' => get_loc_field_name($tbl_entry, $key),
693 'name' => VAR_PREFIX . $key,
694 'field' => "entry.$key",
695 'value' => (isset($custom_fields[$key])) ? $custom_fields[$key] : NULL,
696 'required' => !empty($is_mandatory_field["entry.$key"]),
697 'disabled' => $disabled);
698 return get_field_entry_input($params);
699 }
700
701 $full_class = __NAMESPACE__ . "\\Form\\$class";
702 $field = new $full_class();
703
704 $field->setLabel(get_loc_field_name($tbl_entry, $key))
705 ->setControlAttributes(array('name' => VAR_PREFIX . $key,
706 'value' => (isset($custom_fields[$key])) ? $custom_fields[$key] : null,
707 'disabled' => $disabled,
708 'required' => !empty($is_mandatory_field["entry.$key"])));
709
710 return $field;
711}
712
713
714// Repeat type
715function get_field_rep_type($value, $disabled=false)
716{
717 $field = new FieldDiv();
718
719 $field->setAttributes(array('id' => 'rep_type',
720 'class' => 'multiline'))
721 ->setLabel(get_vocab('rep_type'));
722
723 foreach (array(REP_NONE, REP_DAILY, REP_WEEKLY, REP_MONTHLY, REP_YEARLY) as $i)
724 {
725 $options[$i] = get_vocab("rep_type_$i");
726 }
727 $radio_group = new ElementDiv();
728 $radio_group->setAttribute('class', 'group long')
729 ->addRadioOptions($options, 'rep_type', $value, true);
730
731 $field->addControlElement($radio_group);
732
733 // No point in showing anything more if the repeat fields are disabled
734 // and the repeat type is None
735 if (!$disabled || ($value != REP_NONE))
736 {
737 // And no point in showing the weekly repeat details if the repeat
738 // fields are disabled and the repeat type is not a weekly repeat
739 if (!$disabled || ($value == REP_WEEKLY))
740 {
741 $field->addControlElement(get_fieldset_rep_weekly_details($disabled));
742 }
743
744 // And no point in showing the monthly repeat details if the repeat
745 // fields are disabled and the repeat type is not a monthly repeat
746 if (!$disabled || ($value == REP_MONTHLY))
747 {
748 $field->addControlElement(get_fieldset_rep_monthly_details($disabled));
749 }
750 }
751
752 return $field;
753}
754
755
756// Repeat day
757function get_field_rep_day($disabled=false)
758{
759 global $weekstarts, $strftime_format;
760 global $rep_day;
761
762 for ($i = 0; $i < 7; $i++)
763 {
764 // Display day name checkboxes according to language and preferred weekday start.
765 $wday = ($i + $weekstarts) % 7;
766 // We need to ensure the index is a string to force the array to be associative
767 $options[$wday] = day_name($wday, $strftime_format['dayname_edit']);
768 }
769
770 $field = new FieldInputCheckboxGroup();
771
772 $field->setAttribute('id', 'rep_day')
773 ->setLabel(get_vocab('rep_rep_day'))
774 ->addCheckboxOptions($options, 'rep_day[]', $rep_day, true, $disabled);
775
776 return $field;
777}
778
779
780// Repeat frequency
781function get_field_rep_num_weeks($disabled=false)
782{
783 global $rep_num_weeks;
784
785 $field = new FieldInputNumber();
786
787 $span = new ElementSpan();
788 $span->setAttribute('id', 'num_weeks')
789 ->setText(get_vocab('weeks'));
790
791 $field->setLabel(get_vocab('rep_num_weeks'))
792 ->setControlAttributes(array('name' => 'rep_num_weeks',
793 'min' => REP_NUM_WEEKS_MIN,
794 'value' => $rep_num_weeks,
795 'disabled' => $disabled))
796 ->addElement($span);
797
798 return $field;
799}
800
801
802function get_fieldset_rep_weekly_details($disabled=false)
803{
804 $fieldset = new ElementFieldset();
805
806 $fieldset->setAttributes(array('class' => 'rep_type_details js_none',
807 'id' => 'rep_weekly'));
808 $fieldset->addElement(get_field_rep_day($disabled))
809 ->addElement(get_field_rep_num_weeks($disabled));
810
811 return $fieldset;
812}
813
814
815// MONTH ABSOLUTE (eg Day 15 of every month)
816function get_fieldset_month_absolute($disabled=false)
817{
818 global $month_type, $month_absolute;
819
820 $fieldset = new ElementFieldset();
821
822 $label = new ElementLabel();
823 $label->setAttribute('class', 'no_suffix')
824 ->setText(get_vocab('month_absolute'));
825
826 $radio = new ElementInputRadio();
827 $radio->setAttributes(array('name' => 'month_type',
828 'value' => REP_MONTH_ABSOLUTE,
829 'checked' => ($month_type == REP_MONTH_ABSOLUTE),
830 'disabled' => $disabled));
831
832 $label->addElement($radio);
833
834 $fieldset->addElement($label);
835
836 // We could in the future allow -1 to -31, meaning "the nth last day of
837 // the month", but for the moment we'll keep it simple
838 $options = array();
839 for ($i=1; $i<=31; $i++)
840 {
841 $options[] = $i;
842 }
843 $select = new ElementSelect();
844 $select->setAttributes(array('name' => 'month_absolute',
845 'disabled' => $disabled))
846 ->addSelectOptions($options, $month_absolute, false);
847
848 $fieldset->addElement($select);
849
850 return $fieldset;
851}
852
853
854// MONTH RELATIVE (eg the second Thursday of every month)
855function get_fieldset_month_relative($disabled=false)
856{
857 global $month_type, $month_relative_ord, $month_relative_day;
858 global $weekstarts, $RFC_5545_days;
859
860 $fieldset = new ElementFieldset();
861
862 $label = new ElementLabel();
863 $label->setAttribute('class', 'no_suffix')
864 ->setText(get_vocab('month_relative'));
865
866 $radio = new ElementInputRadio();
867 $radio->setAttributes(array('name' => 'month_type',
868 'value' => REP_MONTH_RELATIVE,
869 'checked' => ($month_type == REP_MONTH_RELATIVE),
870 'disabled' => $disabled));
871
872 $label->addElement($radio);
873
874 $fieldset->addElement($label);
875
876 // Note: the select box order does not internationalise very well and could
877 // do with revisiting. It assumes all languages have the same order as English
878 // eg "the second Wednesday" which is probably not true.
879 $options = array();
880 foreach (array('1', '2', '3', '4', '5', '-1', '-2', '-3', '-4', '-5') as $i)
881 {
882 $options[$i] = get_vocab("ord_" . $i);
883 }
884 $select = new ElementSelect();
885 $select->setAttributes(array('name' => 'month_relative_ord',
886 'disabled' => $disabled))
887 ->addSelectOptions($options, $month_relative_ord, true);
888
889 $fieldset->addElement($select);
890
891 $options = array();
892 for ($i=0; $i<7; $i++)
893 {
894 $i_offset = ($i + $weekstarts)%7;
895 $options[$RFC_5545_days[$i_offset]] = day_name($i_offset);
896 }
897 $select = new ElementSelect();
898 $select->setAttributes(array('name' => 'month_relative_day',
899 'disabled' => $disabled))
900 ->addSelectOptions($options, $month_relative_day, true);
901
902 $fieldset->addElement($select);
903
904 return $fieldset;
905}
906
907
908function get_fieldset_rep_monthly_details($disabled=false)
909{
910 global $month_type;
911
912 $fieldset = new ElementFieldset();
913
914 // If the existing repeat type is other than a monthly repeat, we'll
915 // need to define a default month type in case the user decides to change
916 // to a monthly repeat
917 if (!isset($month_type))
918 {
919 $month_type = REP_MONTH_ABSOLUTE;
920 }
921
922 $fieldset->setAttributes(array('class' => 'rep_type_details js_none',
923 'id' => 'rep_monthly'));
924 $fieldset->addElement(get_fieldset_month_absolute($disabled))
925 ->addElement(get_fieldset_month_relative($disabled));
926
927 return $fieldset;
928}
929
930
931function get_field_rep_end_date($disabled=false)
932{
933 global $rep_end_year, $rep_end_month, $rep_end_day;
934
935 $field = new FieldInputDate();
936
937 $field->setLabel(get_vocab('rep_end_date'))
938 ->setControlAttributes(array('name' => 'rep_end_date',
939 'value' => format_iso_date($rep_end_year, $rep_end_month, $rep_end_day),
940 'disabled' => $disabled));
941
942 return $field;
943}
944
945
946function get_field_skip_conflicts($disabled=false)
947{
948 if ($disabled)
949 {
950 return null;
951 }
952
953 $field = new FieldInputCheckbox();
954
955 $field->setLabel(get_vocab('skip_conflicts'))
956 ->setControlAttribute('name', 'skip')
957 ->setChecked(!empty($skip_default));
958
959 return $field;
960}
961
962
963function get_fieldset_repeat()
964{
965 global $edit_type, $repeats_allowed;
966 global $rep_type;
967
968 // If repeats aren't allowed or this is not a series then disable
969 // the repeat fields - they're for information only
970 // (NOTE: when repeat bookings are restricted to admins, an ordinary user
971 // would not normally be able to get to the stage of trying to edit a series.
972 // But we have to cater for the possibility because it could happen if (a) the
973 // series was created before the policy was introduced or (b) the user has
974 // been demoted since the series was created).
975 $disabled = ($edit_type != "series") || !$repeats_allowed;
976
977 $fieldset = new ElementFieldset();
978 $fieldset->setAttribute('id', 'rep_info');
979
980 $fieldset->addElement(get_field_rep_type($rep_type, $disabled))
981 ->addElement(get_field_rep_end_date($disabled))
982 ->addElement(get_field_skip_conflicts($disabled));
983
984 return $fieldset;
985}
986
987
988function get_fieldset_booking_controls()
989{
990 global $mail_settings;
991
992 $fieldset = new ElementFieldset();
993
994 $fieldset->setAttribute('id', 'booking_controls');
995
996 $field = new FieldInputCheckbox();
997 $field->setLabel(get_vocab('no_mail'))
998 ->setControlAttribute('name', 'no_mail')
999 ->setChecked($mail_settings['no_mail_default']);
1000
1001 $fieldset->addElement($field);
1002
1003 return $fieldset;
1004}
1005
1006
1007function get_fieldset_submit_buttons()
1008{
1009 $fieldset = new ElementFieldset();
1010
1011 // The back and submit buttons
1012 $field = new FieldDiv();
1013
1014 $back = new ElementInputSubmit();
1015 $back->setAttributes(array('name' => 'back_button',
1016 'value' => get_vocab('back'),
1017 'formnovalidate' => true));
1018
1019 $submit = new ElementInputSubmit();
1020 $submit->setAttributes(array('class' => 'default_action',
1021 'name' => 'save_button',
1022 'value' => get_vocab('save')));
1023
1024 // div to hold the results of the Ajax checking of the booking
1025 $div = new ElementDiv();
1026 $span_conflict = new ElementSpan();
1027 $span_conflict->setAttribute('id', 'conflict_check');
1028 $span_policy = new ElementSpan();
1029 $span_policy->setAttribute('id', 'policy_check');
1030 $div->setAttribute('id', 'checks')
1031 ->addElement($span_conflict)
1032 ->addElement($span_policy);
1033
1034 $field->setAttribute('class', 'submit_buttons')
1035 ->addLabelClass('no_suffix')
1036 ->addLabelElement($back)
1037 ->addControlElement($submit)
1038 ->addControlElement($div);
1039
1040 $fieldset->addElement($field);
1041
1042
1043
1044 return $fieldset;
1045}
1046
1047
1048// Returns the booking date for a given time. If the booking day spans midnight and
1049// $t is in the interval between midnight and the end of the day then the booking date
1050// is really the day before.
1051//
1052// If $is_end is set then this is the end time and so if the booking day happens to
1053// last exactly 24 hours, when there will be two possible answers, we want the later
1054// one.
1055function getbookingdate($t, $is_end=false)
1056{
1057 global $eveningends, $eveningends_minutes, $resolution;
1058
1059 $date = getdate($t);
1060
1061 $t_secs = (($date['hours'] * 60) + $date['minutes']) * 60;
1062 $e_secs = (((($eveningends * 60) + $eveningends_minutes) * 60) + $resolution) % SECONDS_PER_DAY;
1063
1064 if (day_past_midnight())
1065 {
1066 if (($t_secs < $e_secs) ||
1067 (($t_secs == $e_secs) && $is_end))
1068 {
1069 $date = getdate(mktime($date['hours'], $date['minutes'], $date['seconds'],
1070 $date['mon'], $date['mday'] -1, $date['year']));
1071 $date['hours'] += 24;
1072 }
1073 }
1074
1075 return $date;
1076}
1077
1078
1079// Get non-standard form variables
1080$hour = get_form_var('hour', 'int');
1081$minute = get_form_var('minute', 'int');
1082$period = get_form_var('period', 'int');
1083$id = get_form_var('id', 'int');
1084$copy = get_form_var('copy', 'int');
1085$edit_type = get_form_var('edit_type', 'string', '');
1086$returl = get_form_var('returl', 'string');
1087// The following variables are used when coming via a JavaScript drag select
1088$drag = get_form_var('drag', 'int');
1089$start_seconds = get_form_var('start_seconds', 'int');
1090$end_seconds = get_form_var('end_seconds', 'int');
1091$selected_rooms = get_form_var('rooms', 'array');
1092$start_date = get_form_var('start_date', 'string');
1093$end_date = get_form_var('end_date', 'string');
1094
1095
1096// Check the CSRF token.
1097// Only check the token if the page is accessed via a POST request. Therefore
1098// this page should not take any action, but only display data.
1099Form::checkToken($post_only=true);
1100
1101// Get the return URL. Need to do this before checkAuthorised().
1102// We might be going through edit_entry more than once, for example if we have to log on on the way. We
1103// still need to preserve the original calling page so that once we've completed edit_entry_handler we can
1104// go back to the page we started at (rather than going to the default view). If this is the first time
1105// through, then $HTTP_REFERER holds the original caller. If this is the second time through we will have
1106// stored it in $returl.
1107if (!isset($returl))
1108{
1109 $returl = isset($HTTP_REFERER) ? $HTTP_REFERER : "";
1110}
1111
1112// Check the user is authorised for this page
1113checkAuthorised();
1114// Also need to know whether they have admin rights
1115$user = getUserName();
1116$is_admin = (authGetUserLevel($user) >= 2);
1117
1118// You're only allowed to make repeat bookings if you're an admin
1119// or else if $auth['only_admin_can_book_repeat'] is not set
1120$repeats_allowed = $is_admin || empty($auth['only_admin_can_book_repeat']);
1121// Similarly for multi-day
1122$multiday_allowed = $is_admin || empty($auth['only_admin_can_book_multiday']);
1123// Similarly for multiple room selection
1124$multiroom_allowed = $is_admin || empty($auth['only_admin_can_select_multiroom']);
1125
1126
1127
1128if (isset($start_seconds))
1129{
1130 $minutes = intval($start_seconds/60);
1131 if ($enable_periods)
1132 {
1133 $period = $minutes - (12*60);
1134 }
1135 else
1136 {
1137 $hour = intval($minutes/60);
1138 $minute = $minutes%60;
1139 }
1140}
1141
1142if (isset($start_date))
1143{
1144 list($year, $month, $day) = explode('-', $start_date);
1145 if (isset($end_date) && ($start_date != $end_date) && $repeats_allowed)
1146 {
1147 $rep_type = REP_DAILY;
1148 list($rep_end_year, $rep_end_month, $rep_end_day) = explode('-', $end_date);
1149 }
1150}
1151
1152
1153// This page will either add or modify a booking
1154
1155// We need to know:
1156// Name of booker
1157// Description of meeting
1158// Date (option select box for day, month, year)
1159// Time
1160// Duration
1161// Internal/External
1162
1163// Firstly we need to know if this is a new booking or modifying an old one
1164// and if it's a modification we need to get all the old data from the db.
1165// If we had $id passed in then it's a modification.
1166
1167if (isset($id))
1168{
1169 $entry = get_entry_by_id($id);
1170
1171 if (is_null($entry))
1172 {
1173 fatal_error(get_vocab("entryid") . $id . get_vocab("not_found"));
1174 }
1175
1176 // We've possibly got a new room and area, so we need to update the settings
1177 // for this area.
1178 $area = get_area($entry['room_id']);
1179 get_area_settings($area);
1180
1181 $private = $entry['private'];
1182 if ($private_mandatory)
1183 {
1184 $private = $private_default;
1185 }
1186 // Need to clear some data if entry is private and user
1187 // does not have permission to edit/view details
1188 if (isset($copy) && ($user != $entry['create_by']))
1189 {
1190 // Entry being copied by different user
1191 // If they don't have rights to view details, clear them
1192 $privatewriteable = getWritable($entry['create_by'], $user, $entry['room_id']);
1193 $keep_private = (is_private_event($private) && !$privatewriteable);
1194 }
1195 else
1196 {
1197 $keep_private = FALSE;
1198 }
1199
1200 // default settings
1201 $rep_day = array();
1202 $rep_type = REP_NONE;
1203 $rep_num_weeks = 1;
1204
1205 foreach ($entry as $column => $value)
1206 {
1207 switch ($column)
1208 {
1209 // Don't bother with these columns
1210 case 'id':
1211 case 'timestamp':
1212 case 'reminded':
1213 case 'info_time':
1214 case 'info_user':
1215 case 'info_text':
1216 case 'private': // We have already done private above
1217 break;
1218
1219 // These columns cannot be made private
1220 case 'room_id':
1221 // We need to preserve the original room_id for existing bookings and pass
1222 // it through to edit_entry_handler. We need this because we need to know
1223 // in edit_entry_handler which room contains the original booking. It's
1224 // possible in this form to select multiple rooms, or even change the room.
1225 // We will need to know which booking is the "original booking" because the
1226 // original booking will keep the same ical_uid and have the ical_sequence
1227 // incremented, whereas new bookings will have a new ical_uid and start with
1228 // an ical_sequence of 0. (If there is more than one room when we get to
1229 // edit_entry_handler and the original room isn't among them, then we will
1230 // just have to make an arbitrary choice as to which is the room containing
1231 // the original booking.)
1232 // NOTE: We do not set the original_room_id if we are copying an entry,
1233 // because when we are copying we are effectively making a new entry and
1234 // so we want edit_entry_handler to assign a new UID, etc.
1235 if (!$copy)
1236 {
1237 $original_room_id = $entry['room_id'];
1238 }
1239 case 'ical_uid':
1240 case 'ical_sequence':
1241 case 'ical_recur_id':
1242 case 'entry_type':
1243 case 'tentative':
1244 $$column = $entry[$column];
1245 break;
1246
1247 // These columns can be made private [not sure about 'type' though - haven't
1248 // checked whether it makes sense/works to make the 'type' column private]
1249 case 'name':
1250 case 'description':
1251 case 'surat_permohonan':
1252 case 'type':
1253 $$column = ($keep_private && isset($is_private_field["entry.$column"]) && $is_private_field["entry.$column"]) ? '' : $entry[$column];
1254 break;
1255
1256 case 'repeat_id':
1257 $rep_id = $entry['repeat_id'];
1258 break;
1259
1260 case 'create_by':
1261 // If we're copying an existing entry then we need to change the create_by (they could be
1262 // different if it's an admin doing the copying)
1263 $create_by = (isset($copy)) ? $user : $entry['create_by'];
1264 break;
1265
1266 case 'start_time':
1267 $start_time = $entry['start_time'];
1268 break;
1269
1270 case 'end_time':
1271 $end_time = $entry['end_time'];
1272 $duration = $entry['end_time'] - $entry['start_time'] - cross_dst($entry['start_time'], $entry['end_time']);
1273 break;
1274
1275 default:
1276 $custom_fields[$column] = ($keep_private && isset($is_private_field["entry.$column"]) && $is_private_field["entry.$column"]) ? '' : $entry[$column];
1277 break;
1278 }
1279 }
1280
1281
1282 if(($entry_type == ENTRY_RPT_ORIGINAL) || ($entry_type == ENTRY_RPT_CHANGED))
1283 {
1284 $sql = "SELECT rep_type, start_time, end_time, end_date, rep_opt, rep_num_weeks,
1285 month_absolute, month_relative
1286 FROM $tbl_repeat
1287 WHERE id=?
1288 LIMIT 1";
1289
1290 $res = db()->query($sql, array($rep_id));
1291
1292 if ($res->count() != 1)
1293 {
1294 fatal_error(get_vocab("repeat_id") . $rep_id . get_vocab("not_found"));
1295 }
1296
1297 $row = $res->row_keyed(0);
1298 unset($res);
1299
1300 $rep_type = $row['rep_type'];
1301
1302 if (!isset($rep_type))
1303 {
1304 $rep_type == REP_NONE;
1305 }
1306
1307 // If it's a repeating entry get the repeat details
1308 if ($rep_type != REP_NONE)
1309 {
1310 // If we're editing the series we want the start_time and end_time to be the
1311 // start and of the first entry of the series, not the start of this entry
1312 if ($edit_type == "series")
1313 {
1314 $start_time = $row['start_time'];
1315 $end_time = $row['end_time'];
1316 }
1317
1318 $rep_end_day = (int)strftime('%d', $row['end_date']);
1319 $rep_end_month = (int)strftime('%m', $row['end_date']);
1320 $rep_end_year = (int)strftime('%Y', $row['end_date']);
1321 // Get the end date in string format as well, for use when
1322 // the input is disabled
1323 $rep_end_date = utf8_strftime('%A %d %B %Y',$row['end_date']);
1324
1325 switch ($rep_type)
1326 {
1327 case REP_WEEKLY:
1328 for ($i=0; $i<7; $i++)
1329 {
1330 if ($row['rep_opt'][$i])
1331 {
1332 $rep_day[] = $i;
1333 }
1334 }
1335 $rep_num_weeks = $row['rep_num_weeks'];
1336 break;
1337 case REP_MONTHLY:
1338 if (isset($row['month_absolute']))
1339 {
1340 $month_type = REP_MONTH_ABSOLUTE;
1341 $month_absolute = $row['month_absolute'];
1342 }
1343 elseif (isset($row['month_relative']))
1344 {
1345 $month_type = REP_MONTH_RELATIVE;
1346 $month_relative = $row['month_relative'];
1347 }
1348 else
1349 {
1350 trigger_error("Invalid monthly repeat", E_USER_WARNING);
1351 }
1352 break;
1353 default:
1354 break;
1355 }
1356 }
1357 }
1358}
1359else
1360{
1361 // It is a new booking. The data comes from whichever button the user clicked
1362 $edit_type = "series";
1363 $name = "";
1364 $create_by = $user;
1365 $description = $default_description;
1366 $surat_permohonan = $default_description;
1367 $type = (empty($is_mandatory_field['entry.type'])) ? $default_type : '';
1368 $room_id = $room;
1369 $private = $private_default;
1370 $tentative = !$confirmed_default;
1371
1372 // Get the hour and minute, converting a period to its MRBS time
1373 // Set some sensible defaults
1374 if ($enable_periods)
1375 {
1376 if (isset($period))
1377 {
1378 $hour = 12 + intval($period/60);
1379 $minute = $period % 60;
1380 }
1381 else
1382 {
1383 $hour = 0;
1384 $minute = 0;
1385 }
1386 }
1387 else
1388 {
1389 if (!isset($hour) || !isset($minute))
1390 {
1391 $hour = $morningstarts;
1392 $minute = $morningstarts_minutes;
1393 }
1394 }
1395
1396 $start_time = mktime($hour, $minute, 0, $month, $day, $year);
1397
1398 // If the start time is not on a slot boundary, then make it so. (It's just possible that it won't be
1399 // if (a) somebody messes with the query string or (b) somebody changes morningstarts or the
1400 // resolution in another browser window and then this page is refreshed with the same query string).
1401 $start_first_slot = get_start_first_slot($month, $day, $year);
1402 $start_time = max($start_first_slot, $start_time);
1403 if (($start_time - $start_first_slot)%$resolution != 0)
1404 {
1405 $start_time = $start_first_slot + intval(($start_time - $start_first_slot)/$resolution); // rounds down
1406 }
1407
1408 if (isset($end_seconds))
1409 {
1410 $end_minutes = intval($end_seconds/60);
1411 $end_hour = intval($end_minutes/60);
1412 $end_minute = $end_minutes%60;
1413 $end_time = mktime($end_hour, $end_minute, 0, $month, $day, $year);
1414 $duration = $end_time - $start_time - cross_dst($start_time, $end_time);
1415 }
1416 else
1417 {
1418 // Set the duration
1419 if ($enable_periods)
1420 {
1421 $duration = 60; // one period
1422 }
1423 else
1424 {
1425 $duration = (isset($default_duration)) ? $default_duration : SECONDS_PER_HOUR;
1426 }
1427
1428 // Make sure the duration doesn't exceed the maximum
1429 if (!$is_admin && $max_duration_enabled)
1430 {
1431 $duration = min($duration, (($enable_periods) ? $max_duration_periods : $max_duration_secs));
1432 }
1433
1434 // If the duration is not an integral number of slots, then make
1435 // it so. And make the duration at least one slot long.
1436 if ($duration%$resolution != 0)
1437 {
1438 $duration = intval(round($duration/$resolution));
1439 $duration = max(1, $duration);
1440 $duration = $duration * $resolution;
1441 }
1442
1443 $end_time = $start_time + $duration;
1444
1445 // Make sure the end_time falls within a booking day. So if there are no
1446 // restrictions, bring it back to the nearest booking day. If the user is not
1447 // allowed multi-day bookings then make sure it is on the first booking day.
1448 if ($is_admin || !$auth['only_admin_can_book_multiday'])
1449 {
1450 $end_time = fit_to_booking_day($end_time, $back=true);
1451 }
1452 else
1453 {
1454 $end_time = min($end_time, get_end_last_slot($month, $day, $year));
1455 }
1456 }
1457
1458 $rep_id = 0;
1459 if (!isset($rep_type)) // We might have set it through a drag selection
1460 {
1461 $rep_type = REP_NONE;
1462 $rep_end_day = $day;
1463 $rep_end_month = $month;
1464 $rep_end_year = $year;
1465 }
1466 $rep_day = array(date('w', $start_time));
1467 $rep_num_weeks = 1;
1468 $month_type = REP_MONTH_ABSOLUTE;
1469}
1470
1471if (!isset($month_relative))
1472{
1473 $month_relative = date_byday($start_time);
1474}
1475if (!isset($month_absolute))
1476{
1477 $month_absolute = date('j', $start_time);
1478}
1479list($month_relative_ord, $month_relative_day) = byday_split($month_relative);
1480
1481$start_hour = strftime('%H', $start_time);
1482$start_min = strftime('%M', $start_time);
1483
1484// These next 4 if statements handle the situation where
1485// this page has been accessed directly and no arguments have
1486// been passed to it.
1487// If we have not been provided with a room_id
1488if (empty( $room_id ) )
1489{
1490 $sql = "SELECT id FROM $tbl_room WHERE disabled=0 LIMIT 1";
1491 $res = db()->query($sql);
1492 $row = $res->row_keyed(0);
1493 $room_id = $row['id'];
1494}
1495
1496// Determine the area id of the room in question first
1497$area_id = mrbsGetRoomArea($room_id);
1498
1499
1500// Remove "Undefined variable" notice
1501if (!isset($rep_num_weeks))
1502{
1503 $rep_num_weeks = "";
1504}
1505
1506$enable_periods ? toPeriodString($start_min, $duration, $dur_units) : toTimeString($duration, $dur_units);
1507
1508//now that we know all the data to fill the form with we start drawing it
1509
1510if (!getWritable($create_by, $user, $room_id))
1511{
1512 showAccessDenied($day, $month, $year, $area, isset($room) ? $room : null);
1513 exit;
1514}
1515
1516print_header($day, $month, $year, $area, isset($room) ? $room : null);
1517
1518// Get the details of all the enabled rooms
1519$rooms = array();
1520$sql = "SELECT R.id, R.room_name, R.area_id
1521 FROM $tbl_room R, $tbl_area A
1522 WHERE R.area_id = A.id
1523 AND R.disabled=0
1524 AND A.disabled=0
1525 ORDER BY R.area_id, R.sort_key";
1526$res = db()->query($sql);
1527
1528for ($i = 0; ($row = $res->row_keyed($i)); $i++)
1529{
1530 $rooms[$row['area_id']][$row['id']] = $row['room_name'];
1531}
1532
1533// Get the details of all the enabled areas
1534$areas = array();
1535$sql = "SELECT id, area_name, resolution, default_duration, default_duration_all_day,
1536 enable_periods, periods, timezone,
1537 morningstarts, morningstarts_minutes, eveningends , eveningends_minutes,
1538 max_duration_enabled, max_duration_secs, max_duration_periods
1539 FROM $tbl_area
1540 WHERE disabled=0
1541 ORDER BY sort_key";
1542$res = db()->query($sql);
1543
1544for ($i = 0; ($row = $res->row_keyed($i)); $i++)
1545{
1546 // We don't want areas that have no enabled rooms because it doesn't make sense
1547 // to try and select them for a booking.
1548 if (empty($rooms[$row['id']]))
1549 {
1550 continue;
1551 }
1552
1553 // Periods are JSON encoded in the database
1554 $row['periods'] = json_decode($row['periods']);
1555
1556 // Make sure we've got the correct resolution when using periods (it's
1557 // probably OK anyway, but just in case)
1558 if ($row['enable_periods'])
1559 {
1560 $row['resolution'] = 60;
1561 }
1562 // Generate some derived settings
1563 $row['max_duration_qty'] = $row['max_duration_secs'];
1564 toTimeString($row['max_duration_qty'], $row['max_duration_units']);
1565 // Get the start and end of the booking day
1566 if ($row['enable_periods'])
1567 {
1568 $first = 12*SECONDS_PER_HOUR;
1569 // If we're using periods we just go to the end of the last slot
1570 $last = $first + (count($row['periods']) * $row['resolution']);
1571 }
1572 else
1573 {
1574 $first = (($row['morningstarts'] * 60) + $row['morningstarts_minutes']) * 60;
1575 $last = ((($row['eveningends'] * 60) + $row['eveningends_minutes']) * 60) + $row['resolution'];
1576 // If the end of the day is the same as or before the start time, then it's really on the next day
1577 if ($first >= $last)
1578 {
1579 $last += SECONDS_PER_DAY;
1580 }
1581 }
1582 $row['first'] = $first;
1583 $row['last'] = $last;
1584 // We don't show the all day checkbox if it's going to result in bookings that
1585 // contravene the policy - ie if max_duration is enabled and an all day booking
1586 // would be longer than the maximum duration allowed.
1587 $row['show_all_day'] = $is_admin ||
1588 !$row['max_duration_enabled'] ||
1589 ( ($row['enable_periods'] && ($row['max_duration_periods'] >= count($row['periods']))) ||
1590 (!$row['enable_periods'] && ($row['max_duration_secs'] >= ($last - $first))) );
1591
1592 // Clean up the settings, getting rid of any nulls and casting boolean fields into bools
1593 $row = clean_area_row($row);
1594
1595 // Now assign the row to the area
1596 $areas[$row['id']] = $row;
1597}
1598
1599
1600if (isset($id) && !isset($copy))
1601{
1602 if ($edit_type == "series")
1603 {
1604 $token = "editseries";
1605 }
1606 else
1607 {
1608 $token = "editentry";
1609 }
1610}
1611else
1612{
1613 if (isset($copy))
1614 {
1615 if ($edit_type == "series")
1616 {
1617 $token = "copyseries";
1618 }
1619 else
1620 {
1621 $token = "copyentry";
1622 }
1623 }
1624 else
1625 {
1626 $token = "addentry";
1627 }
1628}
1629
1630
1631$form = new Form();
1632
1633$form->setAttributes(array('class' => 'standard',
1634 'id' => 'main',
1635 'action' => 'edit_entry_handler.php',
1636 'method' => 'post'));
1637
1638$form->addHiddenInputs(array('returl' => $returl,
1639 'create_by' => $create_by,
1640 'rep_id' => $rep_id,
1641 'edit_type' => $edit_type));
1642
1643// The original_room_id will only be set if this was an existing booking.
1644// If it is an existing booking then edit_entry_handler needs to know the
1645// original room id and the ical_uid and the ical_sequence, because it will
1646// have to keep the ical_uid and increment the ical_sequence for the room that
1647// contained the original booking. If it's a new booking it will generate a new
1648// ical_uid and start the ical_sequence at 0.
1649if (isset($original_room_id))
1650{
1651 $form->addHiddenInputs(array('original_room_id' => $original_room_id,
1652 'ical_uid' => $ical_uid,
1653 'ical_sequence' => $ical_sequence,
1654 'ical_recur_id' => $ical_recur_id));
1655}
1656
1657if(isset($id) && !isset($copy))
1658{
1659 $form->addHiddenInput('id', $id);
1660}
1661
1662$fieldset = new ElementFieldset();
1663$fieldset->addLegend(get_vocab($token));
1664
1665foreach ($edit_entry_field_order as $key)
1666{
1667 switch ($key)
1668 {
1669 case 'name':
1670 $fieldset->addElement(get_field_name($name));
1671 break;
1672
1673 case 'description':
1674 $fieldset->addElement(get_field_description($description));
1675 break;
1676
1677 case 'surat_permohonan':
1678 $fieldset->addElement(get_field_surat_permohonan($surat_permohonan));
1679 break;
1680
1681 case 'start_time':
1682 $fieldset->addElement(get_field_start_time($start_time));
1683 break;
1684
1685 case 'end_time':
1686 $fieldset->addElement(get_field_end_time($end_time));
1687 break;
1688
1689 case 'room_id':
1690 $fieldset->addElement(get_field_areas($area_id));
1691 // $selected_rooms will be populated if we've come from a drag selection
1692 if (empty($selected_rooms))
1693 {
1694 $selected_rooms = array($room_id);
1695 }
1696 $fieldset->addElement(get_field_rooms($selected_rooms));
1697 break;
1698
1699 case 'type':
1700 $fieldset->addElement(get_field_type($type));
1701 break;
1702
1703 case 'confirmation_status':
1704 $fieldset->addElement(get_field_confirmation_status($tentative));
1705 break;
1706
1707 case 'privacy_status':
1708 $fieldset->addElement(get_field_privacy_status($private));
1709 break;
1710
1711 default:
1712 $fieldset->addElement(get_field_custom($key));
1713 break;
1714
1715 } // switch
1716} // foreach
1717
1718$form->addElement($fieldset);
1719
1720// Show the repeat fields if (a) it's a new booking and repeats are allowed,
1721// or else if it's an existing booking and it's a series. (It's not particularly obvious but
1722// if edit_type is "series" then it means that either you're editing an existing
1723// series or else you're making a new booking. This should be tidied up sometime!)
1724if (($edit_type == "series") && $repeats_allowed)
1725{
1726 $form->addElement(get_fieldset_repeat());
1727}
1728
1729// Checkbox for no email
1730if ($need_to_send_mail &&
1731 ($mail_settings['allow_no_mail'] || ($is_admin && $mail_settings['allow_admins_no_mail'])))
1732{
1733 $form->addElement(get_fieldset_booking_controls());
1734}
1735
1736$form->addElement(get_fieldset_submit_buttons());
1737
1738$form->render();
1739
1740
1741output_trailer();