PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.9
VikBooking Hotel Booking Engine & PMS v1.8.9
1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 1.7.3 1.7.4 All 35 releases
vikbooking / admin / controller.php

controller.php in VikBooking Hotel Booking Engine & PMS 1.8.9, at admin/controller.php

15,854 lines 594.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage com_vikbooking
5 * @author Alessio Gaggii - e4j - Extensionsforjoomla.com
6 * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 * @link https://vikwp.com
9 */
10
11 defined('ABSPATH') or die('No script kiddies please!');
12
13 // import Joomla controller library
14 jimport('joomla.application.component.controller');
15
16 class VikBookingController extends JControllerVikBooking
17 {
18 /**
19 * Default controller's method when no task is defined,
20 * or no method exists for that task. If a View is requested.
21 * attempts to set it, otherwise sets the default View.
22 */
23 public function display($cachable = false, $urlparams = array()) {
24
25 $view = VikRequest::getVar('view', '');
26 $header_val = '';
27
28 if (!empty($view)) {
29 $header_val = $view;
30 VikRequest::setVar('view', $view);
31 } else {
32 $header_val = '18';
33 VikRequest::setVar('view', 'dashboard');
34 }
35
36 $hide_menu = JFactory::getApplication()->input->getBool('hide_menu', false);
37
38 if ($hide_menu === false)
39 {
40 VikBookingHelper::printHeader($header_val);
41 }
42
43 parent::display();
44
45 if (VikBooking::showFooter() && $hide_menu === false) {
46 VikBookingHelper::printFooter();
47 }
48 }
49
50 /**
51 * AJAX request for building dynamic donut charts.
52 *
53 * @return void
54 *
55 * @since 1.12.1
56 */
57 public function donut_charts_data() {
58 $fromdt = VikRequest::getString('fromdt', date('Y-m-d'), 'request');
59 $direction = VikRequest::getString('direction', 'next', 'request');
60 $days = VikRequest::getInt('days', 7, 'request');
61 if (empty($fromdt) || !strtotime($fromdt) || empty($days) || $days < 1) {
62 throw new Exception('Missing required data', 400);
63 }
64
65 $from_info = getdate(strtotime($fromdt));
66 if ($direction != 'next') {
67 // fromdt is always the next day after the end of the loop, so the very first next day from the last displayed
68 $from_info = getdate(mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] - ($days * 2)), $from_info['year']));
69 }
70 // always push the start date to the last second (23:59:59)
71 $from_info = getdate(mktime(23, 59, 59, $from_info['mon'], $from_info['mday'], $from_info['year']));
72
73 // months front-end language map
74 $monthsmap = array(
75 JText::translate('VBSHORTMONTHONE'),
76 JText::translate('VBSHORTMONTHTWO'),
77 JText::translate('VBSHORTMONTHTHREE'),
78 JText::translate('VBSHORTMONTHFOUR'),
79 JText::translate('VBSHORTMONTHFIVE'),
80 JText::translate('VBSHORTMONTHSIX'),
81 JText::translate('VBSHORTMONTHSEVEN'),
82 JText::translate('VBSHORTMONTHEIGHT'),
83 JText::translate('VBSHORTMONTHNINE'),
84 JText::translate('VBSHORTMONTHTEN'),
85 JText::translate('VBSHORTMONTHELEVEN'),
86 JText::translate('VBSHORTMONTHTWELVE'),
87 );
88
89 // weekdays front-end language map
90 $wdaysmap = array(
91 JText::translate('VBSUNDAY'),
92 JText::translate('VBMONDAY'),
93 JText::translate('VBTUESDAY'),
94 JText::translate('VBWEDNESDAY'),
95 JText::translate('VBTHURSDAY'),
96 JText::translate('VBFRIDAY'),
97 JText::translate('VBSATURDAY'),
98 );
99
100 // gather information about the rooms and availability
101 $dbo = JFactory::getDbo();
102 $all_rooms_ids = array();
103 $unpublished_rooms = array();
104 $todayymd = date('Y-m-d');
105 $q = "SELECT `id`,`name`,`units`,`params`,`avail` FROM `#__vikbooking_rooms`;";
106 $dbo->setQuery($q);
107 $dbo->execute();
108 if ($dbo->getNumRows()) {
109 $all_rooms = $dbo->loadAssocList();
110 foreach ($all_rooms as $k => $r) {
111 if ($r['avail'] < 1) {
112 $unpublished_rooms[] = $r['id'];
113 }
114 $all_rooms_ids[$r['id']] = $r['name'];
115 }
116 }
117 $q = "SELECT SUM(`units`) FROM `#__vikbooking_rooms` WHERE `avail`=1;";
118 $dbo->setQuery($q);
119 $dbo->execute();
120 $tot_rooms_units = (int)$dbo->loadResult();
121
122 // load busy records
123 $expected_max_ts = mktime(23, 59, 59, $from_info['mon'], ($from_info['mday'] + $days), $from_info['year']);
124 $busy = VikBooking::loadBusyRecordsUnclosed(array_keys($all_rooms_ids), $from_info[0], $expected_max_ts);
125
126 // response body
127 $response = new stdClass;
128 $response->prevweek = ($todayymd != date('Y-m-d', $from_info[0]));
129 $response->nextweek = true;
130 $response->fromd = date('Y-m-d', $from_info[0]);
131 $response->tot_units = $tot_rooms_units;
132 $response->data = array();
133
134 for ($i = 0; $i < $days; $i++) {
135 $tot_booked_today = 0;
136 $today_ts = $from_info[0];
137 $data_obj = new stdClass;
138 $data_obj->ymd = date('Y-m-d', $from_info[0]);
139 $data_obj->lbl = $wdaysmap[(int)$from_info['wday']] . ', ' . $from_info['mday'];
140 $data_obj->lbl = $data_obj->ymd == $todayymd ? JText::translate('VBTODAY') . ', ' . $data_obj->lbl : $data_obj->lbl . ' ' . $monthsmap[($from_info['mon'] - 1)];
141 foreach ($busy as $idroom => $rbusy) {
142 if (in_array($idroom, $unpublished_rooms)) {
143 continue;
144 }
145 foreach ($rbusy as $b) {
146 $tmpone = getdate($b['checkin']);
147 $ritts = mktime(0, 0, 0, $tmpone['mon'], $tmpone['mday'], $tmpone['year']);
148 $tmptwo = getdate($b['checkout']);
149 $conts = mktime(0, 0, 0, $tmptwo['mon'], $tmptwo['mday'], $tmptwo['year']);
150 if ($today_ts >= $ritts && $today_ts < $conts) {
151 $tot_booked_today++;
152 }
153 }
154 }
155
156 $data_obj->tot_booked = $tot_booked_today;
157 $percentage_booked = round((100 * $tot_booked_today / $tot_rooms_units), 2);
158
159 $data_obj->color = '#ff4d4d'; //red
160 if ($percentage_booked > 33 && $percentage_booked <= 66) {
161 $data_obj->color = '#ffa64d'; //orange
162 } elseif ($percentage_booked > 66 && $percentage_booked < 100) {
163 $data_obj->color = '#2a762c'; //green
164 } elseif ($percentage_booked >= 100) {
165 $data_obj->color = '#2482b4'; //light-blue
166 }
167
168 // push today's data
169 array_push($response->data, $data_obj);
170
171 // next day
172 $from_info = getdate(mktime(23, 59, 59, $from_info['mon'], ($from_info['mday'] + 1), $from_info['year']));
173 }
174
175 // update last date (not displayed/included)
176 $response->tod = date('Y-m-d', $from_info[0]);
177
178 echo json_encode($response);
179 exit;
180 }
181
182 /**
183 * AJAX request for adding a new fest.
184 *
185 * @return void
186 *
187 * @since 1.2.0
188 */
189 public function add_fest()
190 {
191 $dt = VikRequest::getString('dt', '', 'request');
192 $type = VikRequest::getString('type', '', 'request');
193 $type = empty($type) ? 'custom' : $type;
194 $name = VikRequest::getString('name', '', 'request');
195 $descr = VikRequest::getString('descr', '', 'request');
196
197 if (empty($name) || empty($dt) || !strtotime($dt)) {
198 VBOHttpDocument::getInstance()->close(400, 'Missing mandatory festivity details');
199 }
200
201 // build fest array
202 $new_fest = [
203 'trans_name' => $name,
204 ];
205
206 $fests = VikBooking::getFestivitiesInstance();
207 $result = $fests->storeFestivity($dt, $new_fest, $type, $descr);
208 if (!$result) {
209 VBOHttpDocument::getInstance()->close(400, 'Could not store festivity details');
210 }
211
212 // reload all festivities for this day for the AJAX response
213 $all_fests = $fests->loadFestDates($dt, $dt);
214 foreach ($all_fests as $k => $v) {
215 // we expect just one record to be returned due to the from/to date limit passed to loadFestDates()
216 VBOHttpDocument::getInstance()->json($v);
217 }
218
219 // no fests found even after storing it
220 VBOHttpDocument::getInstance()->close(404, 'Festivity record not found after saving');
221 }
222
223 /**
224 * AJAX request for removing a fest.
225 *
226 * @return void
227 *
228 * @since 1.2.0
229 */
230 public function remove_fest()
231 {
232 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
233 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
234 }
235
236 $dt = VikRequest::getString('dt', '', 'request');
237 $ind = VikRequest::getInt('ind', 0, 'request');
238 $type = VikRequest::getString('type', '', 'request');
239 $type = empty($type) ? 'custom' : $type;
240 if (empty($dt) || !strtotime($dt)) {
241 echo 'e4j.error.1';
242 exit;
243 }
244
245 $fests = VikBooking::getFestivitiesInstance();
246 $result = $fests->deleteFestivity($dt, $ind, $type);
247 if (!$result) {
248 echo 'e4j.error.2';
249 exit;
250 }
251
252 echo 'e4j.ok';
253 exit;
254 }
255
256 public function einvoicing() {
257 VikBookingHelper::printHeader("einvoicing");
258
259 VikRequest::setVar('view', VikRequest::getCmd('view', 'einvoicing'));
260
261 parent::display();
262
263 if (VikBooking::showFooter()) {
264 VikBookingHelper::printFooter();
265 }
266 }
267
268 public function pmsreports() {
269 if (!JFactory::getUser()->authorise('core.vbo.pms', 'com_vikbooking') && !JFactory::getUser()->authorise('core.vbo.pmsreports', 'com_vikbooking')) {
270 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
271 }
272
273 VikBookingHelper::printHeader("pmsreports");
274
275 VikRequest::setVar('view', VikRequest::getCmd('view', 'pmsreports'));
276
277 parent::display();
278
279 if (VikBooking::showFooter()) {
280 VikBookingHelper::printFooter();
281 }
282 }
283
284 public function ratesoverv() {
285 VikBookingHelper::printHeader("20");
286
287 VikRequest::setVar('view', VikRequest::getCmd('view', 'ratesoverv'));
288
289 parent::display();
290
291 if (VikBooking::showFooter()) {
292 VikBookingHelper::printFooter();
293 }
294 }
295
296 public function stats() {
297 VikBookingHelper::printHeader("stats");
298
299 VikRequest::setVar('view', VikRequest::getCmd('view', 'stats'));
300
301 parent::display();
302
303 if (VikBooking::showFooter()) {
304 VikBookingHelper::printFooter();
305 }
306 }
307
308 /**
309 * AJAX endpoint to calculate the website rates.
310 *
311 * @return void
312 */
313 public function calc_rates()
314 {
315 $response = 'e4j.error.ErrorCode(1) default error';
316 $response_code = 0;
317
318 // availability helper
319 $av_helper = VikBooking::getAvailabilityInstance();
320
321 $currencysymb = VikBooking::getCurrencySymb();
322 $vbo_df = VikBooking::getDateFormat();
323 $df = $vbo_df == "%d/%m/%Y" ? 'd/m/Y' : ($vbo_df == "%m/%d/%Y" ? 'm/d/Y' : 'Y/m/d');
324 $id_room = VikRequest::getInt('id_room', 0, 'request');
325 $checkin = VikRequest::getString('checkin', '', 'request');
326 $nights = VikRequest::getInt('num_nights', 1, 'request');
327 $adults = VikRequest::getInt('num_adults', 0, 'request');
328 $children = VikRequest::getInt('num_children', 0, 'request');
329 /**
330 * The page Calendar may call this task via AJAX to obtain information
331 * about the various rate plans and final costs associated.
332 *
333 * @since 1.13 (J) - 1.3.0 (WP)
334 */
335 $only_rates = VikRequest::getInt('only_rates', 0, 'request');
336 $units = VikRequest::getInt('units', 1, 'request');
337 $checkinfdate = VikRequest::getString('checkinfdate', '', 'request');
338 $checkoutfdate = VikRequest::getString('checkoutfdate', '', 'request');
339 if (!empty($checkinfdate) && empty($checkin)) {
340 $checkin = date('Y-m-d', VikBooking::getDateTimestamp($checkinfdate, 0, 0, 0));
341 }
342
343 $checkin_ts = strtotime($checkin);
344 if (empty($checkin_ts)) {
345 $checkin = date('Y-m-d');
346 $checkin_ts = strtotime($checkin);
347 }
348
349 if (!empty($checkoutfdate) && !empty($checkinfdate) && $nights < 2) {
350 // checkout date was given rather than number of nights
351 $checkout_ts = VikBooking::getDateTimestamp($checkoutfdate, 0, 0, 0);
352 $checkout = date('Y-m-d', $checkout_ts);
353 $nights = $av_helper->countNightsOfStay($checkin_ts, $checkout_ts);
354 } else {
355 // calculate checkout depending on number of nights of stay
356 $is_dst = date('I', $checkin_ts);
357 $checkout_ts = $checkin_ts;
358 for ($i = 1; $i <= $nights; $i++) {
359 $checkout_ts += 86400;
360 $is_now_dst = date('I', $checkout_ts);
361 if ($is_dst != $is_now_dst) {
362 if ((int)$is_dst == 1) {
363 $checkout_ts += 3600;
364 } else {
365 $checkout_ts -= 3600;
366 }
367 $is_dst = $is_now_dst;
368 }
369 }
370 $checkout = date('Y-m-d', $checkout_ts);
371 }
372
373 /**
374 * We got rid of the CURL request to the front-end task of VBO "tac_av_l"
375 * by replacing the call with the new helper class VikBookingAvailability.
376 *
377 * @since 1.15.0 (J) - 1.5.0 (WP)
378 */
379 $av_helper->setStayDates($checkin, $checkout);
380 $av_helper->setRoomParty($adults, $children);
381 // build extra params to obtain the necessary data
382 $params = [
383 'hash' => md5('vbo.e4j.vbo'),
384 'req_type' => 'hotel_availability',
385 'nights' => $nights,
386 'num_rooms' => 1,
387 'only_rates' => $only_rates,
388 'forced_room_ids' => $id_room ? [$id_room] : null,
389 ];
390 $arr_res = $av_helper->getRates($params);
391
392 // pricing pool
393 $price_details = [];
394
395 if (is_array($arr_res)) {
396 if (!strlen($av_helper->getError())) {
397 if (array_key_exists($id_room, $arr_res)) {
398 $response = '';
399 foreach ($arr_res[$id_room] as $rate) {
400 // build pricing object
401 $rplan_details = new stdClass;
402 $rplan_details->idprice = $rate['idprice'];
403 $rplan_details->name = $rate['pricename'];
404 $rplan_details->net = $rate['cost'];
405 $rplan_details->fnet = VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['cost']), $currencysymb);
406 $rplan_details->tax = $rate['taxes'];
407 $rplan_details->ftax = VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['taxes']), $currencysymb);
408 $rplan_details->tot = $rate['cost'] + $rate['taxes'];
409 $rplan_details->ftot = VikBooking::formatCurrencyNumber(VikBooking::numberFormat(($rate['cost'] + $rate['taxes'])), $currencysymb);
410 array_push($price_details, $rplan_details);
411 //
412 $extra_response = '';
413 $response .= '<div class="vbo-calcrates-rateblock" data-idprice="' . $rate['idprice'] . '" data-idroom="' . $id_room . '" data-checkin="' . $checkin . '" data-checkout="' . $checkout . '" data-adults="' . $adults . '" data-children="' . $children . '">';
414 $response .= '<span class="vbo-calcrates-ratename">'.$rate['pricename'].'</span>';
415 $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratenet"><span>'.JText::translate('VBCALCRATESNET').'</span>'.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['cost']), $currencysymb).'</span>';
416 $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratetax"><span>'.JText::translate('VBCALCRATESTAX').'</span>'.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['taxes']), $currencysymb).'</span>';
417 if (!empty($rate['city_taxes'])) {
418 $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratecitytax"><span>'.JText::translate('VBCALCRATESCITYTAX').'</span>'.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['city_taxes']), $currencysymb).'</span>';
419 }
420 if (!empty($rate['fees'])) {
421 $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratefees"><span>'.JText::translate('VBCALCRATESFEES').'</span>'.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['fees']), $currencysymb).'</span>';
422 }
423 if (array_key_exists('affdays', $rate) && $rate['affdays'] > 0) {
424 $extra_response .= '<span class="vbo-calcrates-extrapricedet vbo-calcrates-ratespaffdays"><span>'.JText::translate('VBCALCRATESSPAFFDAYS').'</span>'.$rate['affdays'].'</span>';
425 }
426 if (array_key_exists('diffusagediscount', $rate) && count($rate['diffusagediscount']) > 0) {
427 foreach ($rate['diffusagediscount'] as $roomnumb => $disc) {
428 $extra_response .= '<span class="vbo-calcrates-extrapricedet vbo-calcrates-rateoccupancydisc"><span>'.JText::sprintf('VBCALCRATESADUOCCUPANCY', $rate['diffusage']).'</span>- '.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($disc), $currencysymb).'</span>';
429 break;
430 }
431 } elseif (array_key_exists('diffusagecost', $rate) && count($rate['diffusagecost']) > 0) {
432 foreach ($rate['diffusagecost'] as $roomnumb => $charge) {
433 $extra_response .= '<span class="vbo-calcrates-extrapricedet vbo-calcrates-rateoccupancycharge"><span>'.JText::sprintf('VBCALCRATESADUOCCUPANCY', $rate['diffusage']).'</span>+ '.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($charge), $currencysymb).'</span>';
434 break;
435 }
436 }
437 $tot = $rate['cost'] + $rate['taxes'] + $rate['city_taxes'] + $rate['fees'];
438 $tot = round($tot, 2);
439 $response .= '<span class="vbo-calcrates-ratetotal"><span>'.JText::translate('VBCALCRATESTOT').'</span>'.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($tot), $currencysymb).'</span>';
440 if (!empty($extra_response)) {
441 $response .= '<div class="vbo-calcrates-info">'.$extra_response.'</div>';
442 }
443 $response .= '</div>';
444 }
445 } else {
446 $response = 'e4j.error.'.JText::sprintf('VBCALCRATESROOMNOTAVAILCOMBO', date($df, $checkin_ts), date($df, $checkout_ts));
447 /**
448 * Set a response code so that the View calendar can understand that the room is not available or has no rates.
449 *
450 * @since 1.14 (J) - 1.4.0 (WP)
451 */
452 if (isset($arr_res['fullybooked']) && in_array($id_room, $arr_res['fullybooked'])) {
453 $response_code = -1;
454 }
455 }
456 } else {
457 $response = 'e4j.error.' . $av_helper->getError();
458 /**
459 * Set a response code so that the View calendar can understand that the room is not available or has no rates.
460 *
461 * @since 1.14 (J) - 1.4.0 (WP)
462 */
463 if (isset($arr_res['fullybooked']) && in_array($id_room, $arr_res['fullybooked'])) {
464 $response_code = -1;
465 }
466 }
467 } else {
468 $response = 'e4j.error.' . $av_helper->getError();
469 }
470
471 if (!$arr_res && $id_room && in_array($id_room, $av_helper->getFullyBooked())) {
472 // set the response code to indicate the room is fully booked
473 $response_code = -1;
474 }
475
476 if ($only_rates && strpos($response, 'e4j.error') === false) {
477 VBOHttpDocument::getInstance()->json($price_details);
478 }
479
480 // do not do only echo trim($response); or the currency symbol will not be encoded on some servers
481 $safe_response = array(trim($response));
482 if ($only_rates && !empty($response_code)) {
483 array_push($safe_response, $response_code);
484 }
485
486 VBOHttpDocument::getInstance()->json($safe_response);
487 }
488
489 /**
490 * This is an AJAX endpoint.
491 */
492 public function cron_exec()
493 {
494 ob_start();
495
496 VikRequest::setVar('view', VikRequest::getCmd('view', 'cronexec'));
497
498 parent::display();
499
500 $content = ob_get_contents();
501 ob_end_clean();
502
503 VBOHttpDocument::getInstance()->json([$content]);
504 }
505
506 public function downloadcron()
507 {
508 /**
509 * @wponly no more executable files need to be downloaded for WordPress.
510 */
511 VBOHttpDocument::getInstance()->close(406, 'Cron Jobs must be executed through WPCron');
512 }
513
514 /**
515 * This is an AJAX endpoint.
516 */
517 public function cronlogs()
518 {
519 $dbo = JFactory::getDBO();
520 $pcron_id = VikRequest::getInt('cron_id', '', 'request');
521
522 ob_start();
523
524 $q = "SELECT * FROM `#__vikbooking_cronjobs` WHERE `id`=".(int)$pcron_id.";";
525 $dbo->setQuery($q);
526 $dbo->execute();
527 if ($dbo->getNumRows() == 1) {
528 $cron_data = $dbo->loadAssoc();
529 $cron_data['logs'] = empty($cron_data['logs']) ? '--------' : $cron_data['logs'];
530 echo '<pre>'.print_r($cron_data['logs'], true).'</pre>';
531 }
532
533 $content = ob_get_contents();
534 ob_end_clean();
535
536 VBOHttpDocument::getInstance()->json([$content]);
537 }
538
539 public function packages() {
540 VikBookingHelper::printHeader("packages");
541
542 VikRequest::setVar('view', VikRequest::getCmd('view', 'packages'));
543
544 parent::display();
545
546 if (VikBooking::showFooter()) {
547 VikBookingHelper::printFooter();
548 }
549 }
550
551 public function newpackage() {
552 VikBookingHelper::printHeader("packages");
553
554 VikRequest::setVar('view', VikRequest::getCmd('view', 'managepackage'));
555
556 parent::display();
557
558 if (VikBooking::showFooter()) {
559 VikBookingHelper::printFooter();
560 }
561 }
562
563 public function editpackage() {
564 VikBookingHelper::printHeader("packages");
565
566 VikRequest::setVar('view', VikRequest::getCmd('view', 'managepackage'));
567
568 parent::display();
569
570 if (VikBooking::showFooter()) {
571 VikBookingHelper::printFooter();
572 }
573 }
574
575 public function createpackage()
576 {
577 if (!JSession::checkToken()) {
578 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
579 }
580
581 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
582 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
583 }
584
585 $this->do_createpackage();
586 }
587
588 public function createpackagestay()
589 {
590 if (!JSession::checkToken()) {
591 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
592 }
593
594 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
595 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
596 }
597
598 $this->do_createpackage(true);
599 }
600
601 private function do_createpackage($stay = false) {
602 $dbo = JFactory::getDBO();
603 $mainframe = JFactory::getApplication();
604 $pname = VikRequest::getString('name', '', 'request');
605 $palias = VikRequest::getString('alias', '', 'request');
606 $palias = empty($palias) ? $pname : $palias;
607 $palias = JFilterOutput::stringURLSafe($palias);
608 $pimg = VikRequest::getVar('img', null, 'files', 'array');
609 $pfrom = VikRequest::getString('from', '', 'request');
610 $pto = VikRequest::getString('to', '', 'request');
611 $pexcludeday = VikRequest::getVar('excludeday', array());
612 $strexcldates = array();
613 foreach ($pexcludeday as $exclday) {
614 if (!empty($exclday)) {
615 $strexcldates[] = $exclday;
616 }
617 }
618 $strexcldates = implode(';', $strexcldates);
619 $prooms = VikRequest::getVar('rooms', array());
620 $pminlos = VikRequest::getInt('minlos', '', 'request');
621 $pminlos = $pminlos < 1 ? 1 : $pminlos;
622 $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
623 $pmaxlos = $pmaxlos < 0 ? 0 : $pmaxlos;
624 $pmaxlos = $pmaxlos < $pminlos ? 0 : $pmaxlos;
625 $pcost = VikRequest::getFloat('cost', '', 'request');
626 $paliq = VikRequest::getInt('aliq', '', 'request');
627 $ppernight_total = VikRequest::getInt('pernight_total', '', 'request');
628 $ppernight_total = $ppernight_total == 1 ? 1 : 2;
629 $pperperson = VikRequest::getInt('perperson', '', 'request');
630 $pperperson = $pperperson > 0 ? 1 : 0;
631 $pshowoptions = VikRequest::getInt('showoptions', '', 'request');
632 $pshowoptions = $pshowoptions >= 1 && $pshowoptions <= 3 ? $pshowoptions : 1;
633 $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWRAW);
634 $pshortdescr = VikRequest::getString('shortdescr', '', 'request', VIKREQUEST_ALLOWHTML);
635 $pconditions = VikRequest::getString('conditions', '', 'request', VIKREQUEST_ALLOWRAW);
636 $pbenefits = VikRequest::getString('benefits', '', 'request', VIKREQUEST_ALLOWHTML);
637 $ptsinit = VikBooking::getDateTimestamp($pfrom, '0', '0');
638 $ptsend = VikBooking::getDateTimestamp($pto, '23', '59');
639 $ptsinit = empty($ptsinit) ? time() : $ptsinit;
640 $ptsend = empty($ptsend) || $ptsend < $ptsinit ? $ptsinit : $ptsend;
641 //file upload
642 jimport('joomla.filesystem.file');
643 $gimg = "";
644 if (isset($pimg) && strlen(trim($pimg['name']))) {
645 $pautoresize = VikRequest::getString('autoresize', '', 'request');
646 $presizeto = VikRequest::getInt('resizeto', '', 'request');
647 $creativik = new vikResizer();
648 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimg['name'])));
649 $src = $pimg['tmp_name'];
650 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
651 $j = "";
652 if (file_exists($dest.$filename)) {
653 $j = rand(171, 1717);
654 while (file_exists($dest.$j.$filename)) {
655 $j++;
656 }
657 }
658 $finaldest = $dest.$j.$filename;
659 $check = getimagesize($pimg['tmp_name']);
660 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
661 if (VikBooking::uploadFile($src, $finaldest)) {
662 $gimg = $j.$filename;
663 //orig img
664 $origmod = true;
665 if ($pautoresize == "1" && !empty($presizeto)) {
666 $origmod = $creativik->proportionalImage($finaldest, $dest.'big_'.$j.$filename, $presizeto, $presizeto);
667 } else {
668 VikBooking::uploadFile($finaldest, $dest.'big_'.$j.$filename, true);
669 }
670 //thumb
671 $thumbsize = VikBooking::getThumbSize();
672 $thumb = $creativik->proportionalImage($finaldest, $dest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
673 if (!$thumb || !$origmod) {
674 if (file_exists($dest.'big_'.$j.$filename)) @unlink($dest.'big_'.$j.$filename);
675 if (file_exists($dest.'thumb_'.$j.$filename)) @unlink($dest.'thumb_'.$j.$filename);
676 VikError::raiseWarning('', 'Error Uploading the File: '.$pimg['name']);
677 }
678 @unlink($finaldest);
679 } else {
680 VikError::raiseWarning('', 'Error while uploading image');
681 }
682 } else {
683 VikError::raiseWarning('', 'Uploaded file is not an Image');
684 }
685 }
686 //
687 $goto = "index.php?option=com_vikbooking&task=packages";
688 $q = "INSERT INTO `#__vikbooking_packages` (`name`,`alias`,`img`,`dfrom`,`dto`,`excldates`,`minlos`,`maxlos`,`cost`,`idiva`,`pernight_total`,`perperson`,`descr`,`shortdescr`,`benefits`,`conditions`,`showoptions`) VALUES (".$dbo->quote($pname).", ".$dbo->quote($palias).", ".$dbo->quote($gimg).", ".(int)$ptsinit.", ".(int)$ptsend.", ".$dbo->quote($strexcldates).", ".(int)$pminlos.", ".(int)$pmaxlos.", ".$dbo->quote($pcost).",'".$paliq."', ".(int)$ppernight_total.", ".(int)$pperperson.", ".$dbo->quote($pdescr).", ".$dbo->quote($pshortdescr).", ".$dbo->quote($pbenefits).", ".$dbo->quote($pconditions).", ".(int)$pshowoptions.");";
689 $dbo->setQuery($q);
690 $dbo->execute();
691 $lid = $dbo->insertid();
692 if (!empty($lid)) {
693 $mainframe->enqueueMessage(JText::translate('VBOPKGSAVED'));
694 if ($stay) {
695 $goto = "index.php?option=com_vikbooking&task=editpackage&cid[]=".$lid;
696 }
697 foreach ($prooms as $roomid) {
698 if (!empty($roomid)) {
699 $q = "INSERT INTO `#__vikbooking_packages_rooms` (`idpackage`,`idroom`) VALUES (".(int)$lid.", ".(int)$roomid.");";
700 $dbo->setQuery($q);
701 $dbo->execute();
702 }
703 }
704 }
705 $mainframe->redirect($goto);
706 }
707
708 public function updatepackage()
709 {
710 if (!JSession::checkToken()) {
711 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
712 }
713
714 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
715 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
716 }
717
718 $this->do_updatepackage();
719 }
720
721 public function updatepackagestay()
722 {
723 if (!JSession::checkToken()) {
724 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
725 }
726
727 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
728 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
729 }
730
731 $this->do_updatepackage(true);
732 }
733
734 private function do_updatepackage($stay = false) {
735 $dbo = JFactory::getDBO();
736 $mainframe = JFactory::getApplication();
737 $pwhereup = VikRequest::getInt('whereup', '', 'request');
738 $q = "SELECT * FROM `#__vikbooking_packages` WHERE `id`=".(int)$pwhereup.";";
739 $dbo->setQuery($q);
740 $dbo->execute();
741 if ($dbo->getNumRows() == 1) {
742 $pkg_data = $dbo->loadAssoc();
743 } else {
744 VikError::raiseWarning('', 'Not Found.');
745 $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
746 exit;
747 }
748 $pname = VikRequest::getString('name', '', 'request');
749 $palias = VikRequest::getString('alias', '', 'request');
750 $palias = empty($palias) ? $pname : $palias;
751 $palias = JFilterOutput::stringURLSafe($palias);
752 $pimg = VikRequest::getVar('img', null, 'files', 'array');
753 $pfrom = VikRequest::getString('from', '', 'request');
754 $pto = VikRequest::getString('to', '', 'request');
755 $pexcludeday = VikRequest::getVar('excludeday', array());
756 $strexcldates = array();
757 foreach ($pexcludeday as $exclday) {
758 if (!empty($exclday)) {
759 $strexcldates[] = $exclday;
760 }
761 }
762 $strexcldates = implode(';', $strexcldates);
763 $prooms = VikRequest::getVar('rooms', array());
764 $pminlos = VikRequest::getInt('minlos', '', 'request');
765 $pminlos = $pminlos < 1 ? 1 : $pminlos;
766 $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
767 $pmaxlos = $pmaxlos < 0 ? 0 : $pmaxlos;
768 $pmaxlos = $pmaxlos < $pminlos ? 0 : $pmaxlos;
769 $pcost = VikRequest::getFloat('cost', '', 'request');
770 $paliq = VikRequest::getInt('aliq', '', 'request');
771 $ppernight_total = VikRequest::getInt('pernight_total', '', 'request');
772 $ppernight_total = $ppernight_total == 1 ? 1 : 2;
773 $pperperson = VikRequest::getInt('perperson', '', 'request');
774 $pperperson = $pperperson > 0 ? 1 : 0;
775 $pshowoptions = VikRequest::getInt('showoptions', '', 'request');
776 $pshowoptions = $pshowoptions >= 1 && $pshowoptions <= 3 ? $pshowoptions : 1;
777 $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWRAW);
778 $pshortdescr = VikRequest::getString('shortdescr', '', 'request', VIKREQUEST_ALLOWHTML);
779 $pconditions = VikRequest::getString('conditions', '', 'request', VIKREQUEST_ALLOWRAW);
780 $pbenefits = VikRequest::getString('benefits', '', 'request', VIKREQUEST_ALLOWHTML);
781 $ptsinit = VikBooking::getDateTimestamp($pfrom, '0', '0');
782 $ptsend = VikBooking::getDateTimestamp($pto, '23', '59');
783 $ptsinit = empty($ptsinit) ? time() : $ptsinit;
784 $ptsend = empty($ptsend) || $ptsend < $ptsinit ? $ptsinit : $ptsend;
785 //file upload
786 jimport('joomla.filesystem.file');
787 $gimg = "";
788 if (isset($pimg) && strlen(trim($pimg['name']))) {
789 $pautoresize = VikRequest::getString('autoresize', '', 'request');
790 $presizeto = VikRequest::getInt('resizeto', '', 'request');
791 $creativik = new vikResizer();
792 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimg['name'])));
793 $src = $pimg['tmp_name'];
794 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
795 $j = "";
796 if (file_exists($dest.$filename)) {
797 $j = rand(171, 1717);
798 while (file_exists($dest.$j.$filename)) {
799 $j++;
800 }
801 }
802 $finaldest = $dest.$j.$filename;
803 $check = getimagesize($pimg['tmp_name']);
804 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
805 if (VikBooking::uploadFile($src, $finaldest)) {
806 $gimg = $j.$filename;
807 //orig img
808 $origmod = true;
809 if ($pautoresize == "1" && !empty($presizeto)) {
810 $origmod = $creativik->proportionalImage($finaldest, $dest.'big_'.$j.$filename, $presizeto, $presizeto);
811 } else {
812 VikBooking::uploadFile($finaldest, $dest.'big_'.$j.$filename, true);
813 }
814 //thumb
815 $thumbsize = VikBooking::getThumbSize();
816 $thumb = $creativik->proportionalImage($finaldest, $dest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
817 if (!$thumb || !$origmod) {
818 if (file_exists($dest.'big_'.$j.$filename)) @unlink($dest.'big_'.$j.$filename);
819 if (file_exists($dest.'thumb_'.$j.$filename)) @unlink($dest.'thumb_'.$j.$filename);
820 VikError::raiseWarning('', 'Error Uploading the File: '.$pimg['name']);
821 }
822 @unlink($finaldest);
823 } else {
824 VikError::raiseWarning('', 'Error while uploading image');
825 }
826 } else {
827 VikError::raiseWarning('', 'Uploaded file is not an Image');
828 }
829 }
830 //
831 $goto = "index.php?option=com_vikbooking&task=packages";
832 $q = "UPDATE `#__vikbooking_packages` SET `name`=".$dbo->quote($pname).",`alias`=".$dbo->quote($palias)."".(!empty($gimg) ? ",`img`=".$dbo->quote($gimg) : "").",`dfrom`=".(int)$ptsinit.",`dto`=".(int)$ptsend.",`excldates`=".$dbo->quote($strexcldates).",`minlos`=".(int)$pminlos.",`maxlos`=".(int)$pmaxlos.",`cost`=".$dbo->quote($pcost).",`idiva`='".$paliq."',`pernight_total`=".(int)$ppernight_total.",`perperson`=".(int)$pperperson.",`descr`=".$dbo->quote($pdescr).",`shortdescr`=".$dbo->quote($pshortdescr).",`benefits`=".$dbo->quote($pbenefits).",`conditions`=".$dbo->quote($pconditions).",`showoptions`=".(int)$pshowoptions." WHERE `id`=".(int)$pwhereup.";";
833 $dbo->setQuery($q);
834 $dbo->execute();
835 $q = "DELETE FROM `#__vikbooking_packages_rooms` WHERE `idpackage`=".(int)$pwhereup.";";
836 $dbo->setQuery($q);
837 $dbo->execute();
838 foreach ($prooms as $roomid) {
839 if (!empty($roomid)) {
840 $q = "INSERT INTO `#__vikbooking_packages_rooms` (`idpackage`,`idroom`) VALUES (".(int)$pwhereup.", ".(int)$roomid.");";
841 $dbo->setQuery($q);
842 $dbo->execute();
843 }
844 }
845 $mainframe->enqueueMessage(JText::translate('VBOPKGUPDATED'));
846 if ($stay) {
847 $goto = "index.php?option=com_vikbooking&task=editpackage&cid[]=".$pwhereup;
848 }
849 $mainframe->redirect($goto);
850 }
851
852 public function removepackages()
853 {
854 if (!JSession::checkToken()) {
855 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
856 }
857
858 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
859 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
860 }
861
862 $ids = VikRequest::getVar('cid', array());
863 $dbo = JFactory::getDbo();
864
865 foreach ($ids as $d) {
866 $q = "DELETE FROM `#__vikbooking_packages` WHERE `id`=".(int)$d.";";
867 $dbo->setQuery($q);
868 $dbo->execute();
869 $q = "DELETE FROM `#__vikbooking_packages_rooms` WHERE `idpackage`=".(int)$d.";";
870 $dbo->setQuery($q);
871 $dbo->execute();
872 }
873
874 $mainframe = JFactory::getApplication();
875 $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
876 }
877
878 public function calendar() {
879 VikBookingHelper::printHeader("19");
880
881 VikRequest::setVar('view', VikRequest::getCmd('view', 'calendar'));
882
883 parent::display();
884
885 if (VikBooking::showFooter()) {
886 VikBookingHelper::printFooter();
887 }
888 }
889
890 public function rooms() {
891 VikBookingHelper::printHeader("7");
892
893 VikRequest::setVar('view', VikRequest::getCmd('view', 'rooms'));
894
895 parent::display();
896
897 if (VikBooking::showFooter()) {
898 VikBookingHelper::printFooter();
899 }
900 }
901
902 public function newroom() {
903 VikBookingHelper::printHeader("7");
904
905 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageroom'));
906
907 parent::display();
908
909 if (VikBooking::showFooter()) {
910 VikBookingHelper::printFooter();
911 }
912 }
913
914 public function editroom() {
915 VikBookingHelper::printHeader("7");
916
917 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageroom'));
918
919 parent::display();
920
921 if (VikBooking::showFooter()) {
922 VikBookingHelper::printFooter();
923 }
924 }
925
926 public function createroom()
927 {
928 if (!JSession::checkToken()) {
929 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
930 }
931
932 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
933 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
934 }
935
936 $this->do_createroom();
937 }
938
939 public function createroomstay()
940 {
941 if (!JSession::checkToken()) {
942 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
943 }
944
945 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
946 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
947 }
948
949 $this->do_createroom(true);
950 }
951
952 private function do_createroom($stay = false) {
953 $app = JFactory::getApplication();
954 $pcname = VikRequest::getString('cname', '', 'request');
955 $pccat = VikRequest::getVar('ccat', array(0));
956 $pcdescr = VikRequest::getString('cdescr', '', 'request', VIKREQUEST_ALLOWRAW);
957 $psmalldesc = VikRequest::getString('smalldesc', '', 'request', VIKREQUEST_ALLOWRAW);
958 $pccarat = VikRequest::getVar('ccarat', array(0));
959 $pcoptional = VikRequest::getVar('coptional', array(0));
960 $pcavail = VikRequest::getString('cavail', '', 'request');
961 $pautoresize = VikRequest::getString('autoresize', '', 'request');
962 $presizeto = VikRequest::getString('resizeto', '', 'request');
963 $pautoresizemore = VikRequest::getString('autoresizemore', '', 'request');
964 $presizetomore = VikRequest::getString('resizetomore', '', 'request');
965 $punits = VikRequest::getInt('units', '', 'request');
966 $pimages = VikRequest::getVar('cimgmore', null, 'files', 'array');
967 $pfromadult = VikRequest::getInt('fromadult', '', 'request');
968 $ptoadult = VikRequest::getInt('toadult', '', 'request');
969 $pfromchild = VikRequest::getInt('fromchild', '', 'request');
970 $ptochild = VikRequest::getInt('tochild', '', 'request');
971 $ptotpeople = VikRequest::getInt('totpeople', '', 'request');
972 $pmintotpeople = VikRequest::getInt('mintotpeople', '', 'request');
973 $pmintotpeople = $pmintotpeople < 1 ? 1 : $pmintotpeople;
974 $plastavail = VikRequest::getString('lastavail', '', 'request');
975 $plastavail = empty($plastavail) ? 0 : intval($plastavail);
976 $psuggocc = VikRequest::getInt('suggocc', 1, 'request');
977 $pcustprice = VikRequest::getString('custprice', '', 'request');
978 $pcustprice = empty($pcustprice) ? '' : floatval($pcustprice);
979 $pcustpricetxt = VikRequest::getString('custpricetxt', '', 'request', VIKREQUEST_ALLOWRAW);
980 $pcustpricesubtxt = VikRequest::getString('custpricesubtxt', '', 'request', VIKREQUEST_ALLOWRAW);
981 $preqinfo = VikRequest::getInt('reqinfo', '', 'request');
982 $ppricecal = VikRequest::getInt('pricecal', '', 'request');
983 $pdefcalcost = VikRequest::getString('defcalcost', '', 'request');
984 $pmaxminpeople = VikRequest::getString('maxminpeople', '', 'request');
985 $pcimgcaption = VikRequest::getVar('cimgcaption', array());
986 $pmaxminpeople = in_array($pmaxminpeople, array('0', '1', '2', '3', '4', '5')) ? $pmaxminpeople : '0';
987 $pseasoncal = VikRequest::getInt('seasoncal', 0, 'request');
988 $pseasoncal = $pseasoncal >= 0 || $pseasoncal <= 3 ? $pseasoncal : 0;
989 $pseasoncal_nights = VikRequest::getString('seasoncal_nights', '', 'request');
990 $pseasoncal_prices = VikRequest::getString('seasoncal_prices', '', 'request');
991 $pseasoncal_restr = VikRequest::getString('seasoncal_restr', '', 'request');
992 $pmulti_units = VikRequest::getInt('multi_units', '', 'request');
993 $pmulti_units = $punits > 1 ? $pmulti_units : 0;
994 $psefalias = VikRequest::getString('sefalias', '', 'request');
995 $psefalias = empty($psefalias) ? JFilterOutput::stringURLSafe($pcname) : JFilterOutput::stringURLSafe($psefalias);
996 $pcustptitle = VikRequest::getString('custptitle', '', 'request');
997 $pcustptitlew = VikRequest::getString('custptitlew', '', 'request');
998 $pcustptitlew = in_array($pcustptitlew, array('before', 'after', 'replace')) ? $pcustptitlew : 'before';
999 $pmetakeywords = VikRequest::getString('metakeywords', '', 'request');
1000 $pmetadescription = VikRequest::getString('metadescription', '', 'request');
1001 $pshare_with = VikRequest::getVar('share_with', array());
1002 $scalnights_arr = array();
1003 if (!empty($pseasoncal_nights)) {
1004 $scalnights = explode(',', $pseasoncal_nights);
1005 foreach ($scalnights as $scalnight) {
1006 if (intval(trim($scalnight)) > 0) {
1007 $scalnights_arr[] = intval(trim($scalnight));
1008 }
1009 }
1010 }
1011 if ($scalnights_arr) {
1012 $pseasoncal_nights = implode(', ', $scalnights_arr);
1013 } else {
1014 $pseasoncal_nights = '';
1015 $pseasoncal = 0;
1016 }
1017 $roomparams = [
1018 'lastavail' => $plastavail,
1019 'suggocc' => $psuggocc,
1020 'custprice' => $pcustprice,
1021 'custpricetxt' => $pcustpricetxt,
1022 'custpricesubtxt' => $pcustpricesubtxt,
1023 'reqinfo' => $preqinfo,
1024 'pricecal' => $ppricecal,
1025 'defcalcost' => floatval($pdefcalcost),
1026 'maxminpeople' => $pmaxminpeople,
1027 'seasoncal' => $pseasoncal,
1028 'seasoncal_nights' => $pseasoncal_nights,
1029 'seasoncal_prices' => $pseasoncal_prices,
1030 'seasoncal_restr' => $pseasoncal_restr,
1031 'multi_units' => $pmulti_units,
1032 'custptitle' => $pcustptitle,
1033 'custptitlew' => $pcustptitlew,
1034 'metakeywords' => $pmetakeywords,
1035 'metadescription' => $pmetadescription,
1036 'layout_style' => VikRequest::getString('layout_style', 'default', 'request'),
1037 'checkin' => VikRequest::getString('listing_checkin', '', 'request'),
1038 'checkout' => VikRequest::getString('listing_checkout', '', 'request'),
1039 ];
1040 //distinctive features
1041 $roomparams['features'] = array();
1042 if ($punits > 0) {
1043 for ($i=1; $i <= $punits; $i++) {
1044 $distf_name = VikRequest::getVar('feature-name'.$i, array());
1045 $distf_lang = VikRequest::getVar('feature-lang'.$i, array());
1046 $distf_value = VikRequest::getVar('feature-value'.$i, array());
1047 foreach ($distf_name as $distf_k => $distf) {
1048 if (strlen($distf) > 0 && strlen($distf_value[$distf_k]) > 0) {
1049 $use_key = strlen($distf_lang[$distf_k]) > 0 ? $distf_lang[$distf_k] : $distf;
1050 $roomparams['features'][$i][$use_key] = $distf_value[$distf_k];
1051 }
1052 }
1053 }
1054 }
1055
1056 /**
1057 * Store room geo params information.
1058 *
1059 * @since 1.14 (J) - 1.4.0 (WP)
1060 */
1061 $geo = VikBooking::getGeocodingInstance();
1062 $geo_params = $geo->getRoomGeoTransient(0);
1063 if ($geo_params !== false) {
1064 // make sure the geocoding service was not turned off
1065 $geo_enabled = VikRequest::getInt('geo_enabled', 0, 'request');
1066 if (!$geo_enabled) {
1067 $geo_params->enabled = 0;
1068 }
1069 //
1070 $roomparams['geo'] = $geo_params;
1071 }
1072 //
1073
1074 $roomparamstr = json_encode($roomparams);
1075
1076 if (empty($pcname)) {
1077 $app->enqueueMessage(JText::translate('VBO_PLEASE_FILL_FIELDS'), 'error');
1078 $app->redirect("index.php?option=com_vikbooking&task=rooms");
1079 $app->close();
1080 }
1081
1082 jimport('joomla.filesystem.file');
1083 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
1084
1085 $picon = "";
1086 if (($_FILES['cimg'] ?? null) && !intval($_FILES['cimg']['error']) && VikBooking::caniWrite($updpath) && strlen(trim($_FILES['cimg']['name'])) && @is_uploaded_file($_FILES['cimg']['tmp_name'])) {
1087 $safename = JFile::makeSafe(str_replace(' ', '_', strtolower($_FILES['cimg']['name'])));
1088 $j = '';
1089 $pwhere = $updpath . $safename;
1090 if (file_exists($updpath . $safename)) {
1091 $j = 1;
1092 while (file_exists($updpath . $j . $safename)) {
1093 $j++;
1094 }
1095 $pwhere = $updpath . $j . $safename;
1096 }
1097 if (!getimagesize($_FILES['cimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
1098 @unlink($pwhere);
1099 } elseif (VikBooking::uploadFile($_FILES['cimg']['tmp_name'], $pwhere)) {
1100 $picon = $j . $safename;
1101 if ((int) $pautoresize && !empty($presizeto)) {
1102 $origmod = (new VikResizer)->proportionalImage($pwhere, $updpath . 'r_' . $j . $safename, $presizeto, $presizeto);
1103 if ($origmod) {
1104 @unlink($pwhere);
1105 $picon = 'r_' . $j . $safename;
1106 }
1107 }
1108 /**
1109 * Create a mini-thumbnail of the room/listing main photo.
1110 *
1111 * @since 1.17.5 (J) - 1.7.5 (WP)
1112 */
1113 try {
1114 // resize the original image
1115 (new VikResizer)->proportionalImage($pwhere, $updpath . 'mini_' . $picon, 96, 96);
1116 } catch (Throwable $e) {
1117 // silently catch any PHP GD error and continue
1118 }
1119 }
1120 }
1121
1122 // more images
1123 $creativik = new VikResizer;
1124 $bigsdest = $updpath;
1125 $thumbsdest = $updpath;
1126 $dest = $updpath;
1127 $moreimagestr = "";
1128 $arrimgs = array();
1129 $captiontexts = array();
1130 $imgcaptions = array();
1131 foreach ($pimages['name'] as $kk=>$ci) {
1132 if (!empty($ci)) {
1133 $arrimgs[] = $kk;
1134 $captiontexts[] = isset($pcimgcaption[$kk]) ? $pcimgcaption[$kk] : '';
1135 }
1136 }
1137 foreach ($arrimgs as $ki => $imgk) {
1138 if (strlen(trim($pimages['name'][$imgk]))) {
1139 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimages['name'][$imgk])));
1140 $src = $pimages['tmp_name'][$imgk];
1141 $j = "";
1142 if (file_exists($dest.$filename)) {
1143 $j = rand(171, 1717);
1144 while (file_exists($dest.$j.$filename)) {
1145 $j++;
1146 }
1147 }
1148 $finaldest = $dest.$j.$filename;
1149 $check = getimagesize($pimages['tmp_name'][$imgk]);
1150 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
1151 if (VikBooking::uploadFile($src, $finaldest)) {
1152 $gimg = $j.$filename;
1153 //orig img
1154 $origmod = true;
1155 if ($pautoresizemore == "1" && !empty($presizetomore)) {
1156 $origmod = $creativik->proportionalImage($finaldest, $bigsdest.'big_'.$j.$filename, $presizetomore, $presizetomore);
1157 } else {
1158 VikBooking::uploadFile($finaldest, $bigsdest.'big_'.$j.$filename, true);
1159 }
1160 //thumb
1161 $thumbsize = VikBooking::getThumbSize();
1162 $thumb = $creativik->proportionalImage($finaldest, $thumbsdest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
1163 if (!$thumb || !$origmod) {
1164 if (file_exists($bigsdest.'big_'.$j.$filename)) @unlink($bigsdest.'big_'.$j.$filename);
1165 if (file_exists($thumbsdest.'thumb_'.$j.$filename)) @unlink($thumbsdest.'thumb_'.$j.$filename);
1166 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1167 } else {
1168 $moreimagestr .= $j.$filename.";;";
1169 $imgcaptions[] = $captiontexts[$ki];
1170 }
1171 @unlink($finaldest);
1172 } else {
1173 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1174 }
1175 } else {
1176 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1177 }
1178 }
1179 }
1180 //end more images
1181 if (is_array($pccat) && count($pccat)) {
1182 $pccatdef="";
1183 foreach ($pccat as $ccat) {
1184 if (!empty($ccat)) {
1185 $pccatdef.=$ccat.";";
1186 }
1187 }
1188 } else {
1189 $pccatdef="";
1190 }
1191 if (is_array($pccarat) && count($pccarat)) {
1192 $pccaratdef="";
1193 foreach ($pccarat as $ccarat) {
1194 $pccaratdef.=$ccarat.";";
1195 }
1196 } else {
1197 $pccaratdef="";
1198 }
1199 if (is_array($pcoptional) && count($pcoptional)) {
1200 $pcoptionaldef="";
1201 foreach ($pcoptional as $coptional) {
1202 $pcoptionaldef.=$coptional.";";
1203 }
1204 } else {
1205 $pcoptionaldef="";
1206 }
1207 $pcavaildef=($pcavail=="yes" ? "1" : "0");
1208 if ($pfromadult > $ptoadult) {
1209 $pfromadult = 1;
1210 $ptoadult = 1;
1211 }
1212 if ($pfromchild > $ptochild) {
1213 $pfromchild = 1;
1214 $ptochild = 1;
1215 }
1216 $dbo = JFactory::getDbo();
1217 $q = "INSERT INTO `#__vikbooking_rooms` (`name`,`img`,`idcat`,`idcarat`,`idopt`,`info`,`avail`,`units`,`moreimgs`,`fromadult`,`toadult`,`fromchild`,`tochild`,`smalldesc`,`totpeople`,`mintotpeople`,`params`,`imgcaptions`,`alias`) VALUES(".$dbo->quote($pcname).",".$dbo->quote($picon).",".$dbo->quote($pccatdef).",".$dbo->quote($pccaratdef).",".$dbo->quote($pcoptionaldef).",".$dbo->quote($pcdescr).",".$dbo->quote($pcavaildef).",".($punits > 0 ? $dbo->quote($punits) : "'1'").", ".$dbo->quote($moreimagestr).", '".$pfromadult."', '".$ptoadult."', '".$pfromchild."', '".$ptochild."', ".$dbo->quote($psmalldesc).", ".$ptotpeople.", ".$pmintotpeople.", ".$dbo->quote($roomparamstr).", ".$dbo->quote(json_encode($imgcaptions)).",".$dbo->quote($psefalias).");";
1218 $dbo->setQuery($q);
1219 $dbo->execute();
1220 $lid = $dbo->insertid();
1221 if (empty($lid)) {
1222 $app->enqueueMessage('Could not store the record on the database', 'error');
1223 $app->redirect("index.php?option=com_vikbooking&task=rooms");
1224 $app->close();
1225 }
1226
1227 /**
1228 * Share availability calendars with other rooms.
1229 *
1230 * @since 1.13
1231 */
1232 // always reset relations for this main room
1233 $q = "DELETE FROM `#__vikbooking_calendars_xref` WHERE `mainroom`={$lid};";
1234 $dbo->setQuery($q);
1235 $dbo->execute();
1236 $newxref = array();
1237 foreach ($pshare_with as $cldroom) {
1238 if (!empty($cldroom)) {
1239 array_push($newxref, (int)$cldroom);
1240 }
1241 }
1242 foreach ($newxref as $cldroom) {
1243 $q = "INSERT INTO `#__vikbooking_calendars_xref` (`mainroom`, `childroom`) VALUES ({$lid}, {$cldroom});";
1244 $dbo->setQuery($q);
1245 $dbo->execute();
1246 }
1247
1248 /**
1249 * Room upgrade options.
1250 *
1251 * @since 1.16.0 (J) - 1.6.0 (WP)
1252 */
1253 $config = VBOFactory::getConfig();
1254 $room_upgrade_options = [];
1255 $room_upgrade = VikRequest::getInt('room_upgrade', 0, 'request');
1256 $upgrade_rooms = VikRequest::getVar('upgrade_rooms', array());
1257 $upgrade_discount = VikRequest::getFloat('upgrade_discount', 0, 'request');
1258 if ($room_upgrade && is_array($upgrade_rooms) && count($upgrade_rooms)) {
1259 $upgrade_rooms = array_map(function($rid) {
1260 return (int)$rid;
1261 }, $upgrade_rooms);
1262
1263 $room_upgrade_options = [
1264 'rooms' => $upgrade_rooms,
1265 'discount' => $upgrade_discount,
1266 ];
1267 }
1268 $config->set('room_upgrade_options_' . $lid, json_encode($room_upgrade_options));
1269
1270 if ($stay === true) {
1271 $app->enqueueMessage(JText::translate('VBOROOMSAVEOK').' - <a href="index.php?option=com_vikbooking&task=tariffs&cid[]='.$lid.'">'.JText::translate('VBOGOTORATES').'</a>');
1272 $app->redirect("index.php?option=com_vikbooking&task=editroom&cid[]=".$lid);
1273 $app->close();
1274 }
1275
1276 $app->redirect("index.php?option=com_vikbooking&task=tariffs&cid[]=".$lid);
1277 $app->close();
1278 }
1279
1280 public function updateroom()
1281 {
1282 if (!JSession::checkToken()) {
1283 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1284 }
1285
1286 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1287 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1288 }
1289
1290 $this->do_updateroom();
1291 }
1292
1293 public function updateroomstay()
1294 {
1295 if (!JSession::checkToken()) {
1296 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1297 }
1298
1299 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1300 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1301 }
1302
1303 $this->do_updateroom(true);
1304 }
1305
1306 private function do_updateroom($stay = false)
1307 {
1308 $app = JFactory::getApplication();
1309 $config = VBOFactory::getConfig();
1310 $dbo = JFactory::getDbo();
1311
1312 $pcname = VikRequest::getString('cname', '', 'request');
1313 $pccat = VikRequest::getVar('ccat', array(0));
1314 $pcdescr = VikRequest::getString('cdescr', '', 'request', VIKREQUEST_ALLOWRAW);
1315 $psmalldesc = VikRequest::getString('smalldesc', '', 'request', VIKREQUEST_ALLOWRAW);
1316 $pccarat = VikRequest::getVar('ccarat', array(0));
1317 $pcoptional = VikRequest::getVar('coptional', array(0));
1318 $pcavail = VikRequest::getString('cavail', '', 'request');
1319 $pwhereup = VikRequest::getInt('whereup', 0, 'request');
1320 $pautoresize = VikRequest::getString('autoresize', '', 'request');
1321 $presizeto = VikRequest::getString('resizeto', '', 'request');
1322 $pautoresizemore = VikRequest::getString('autoresizemore', '', 'request');
1323 $presizetomore = VikRequest::getString('resizetomore', '', 'request');
1324 $punits = VikRequest::getInt('units', '', 'request');
1325 $pimages = VikRequest::getVar('cimgmore', null, 'files', 'array');
1326 $pactmoreimgs = VikRequest::getString('actmoreimgs', '', 'request');
1327 $pfromadult = VikRequest::getInt('fromadult', '', 'request');
1328 $ptoadult = VikRequest::getInt('toadult', '', 'request');
1329 $pfromchild = VikRequest::getInt('fromchild', '', 'request');
1330 $ptochild = VikRequest::getInt('tochild', '', 'request');
1331 $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array(0));
1332 $padultsdiffval = VikRequest::getVar('adultsdiffval', array(0));
1333 $padultsdiffnum = VikRequest::getVar('adultsdiffnum', array(0));
1334 $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array(0));
1335 $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array(0));
1336 $ptotpeople = VikRequest::getInt('totpeople', '', 'request');
1337 $pmintotpeople = VikRequest::getInt('mintotpeople', '', 'request');
1338 $pmintotpeople = $pmintotpeople < 1 ? 1 : $pmintotpeople;
1339 $plastavail = VikRequest::getString('lastavail', '', 'request');
1340 $plastavail = empty($plastavail) ? 0 : intval($plastavail);
1341 $psuggocc = VikRequest::getInt('suggocc', 1, 'request');
1342 $pcustprice = VikRequest::getString('custprice', '', 'request');
1343 $pcustprice = empty($pcustprice) ? '' : floatval($pcustprice);
1344 $pcustpricetxt = VikRequest::getString('custpricetxt', '', 'request', VIKREQUEST_ALLOWRAW);
1345 $pcustpricesubtxt = VikRequest::getString('custpricesubtxt', '', 'request', VIKREQUEST_ALLOWRAW);
1346 $preqinfo = VikRequest::getInt('reqinfo', '', 'request');
1347 $ppricecal = VikRequest::getInt('pricecal', '', 'request');
1348 $pdefcalcost = VikRequest::getString('defcalcost', '', 'request');
1349 $pdefrplan = VikRequest::getInt('defrplan', 0, 'request');
1350 $pmaxminpeople = VikRequest::getString('maxminpeople', '', 'request');
1351 $pcimgcaption = VikRequest::getVar('cimgcaption', array());
1352 $pimgsorting = VikRequest::getVar('imgsorting', array());
1353 $pupdatecaption = VikRequest::getInt('updatecaption', '', 'request');
1354 $pmaxminpeople = in_array($pmaxminpeople, array('0', '1', '2', '3', '4', '5')) ? $pmaxminpeople : '0';
1355 $pseasoncal = VikRequest::getInt('seasoncal', 0, 'request');
1356 $pseasoncal = $pseasoncal >= 0 || $pseasoncal <= 3 ? $pseasoncal : 0;
1357 $pseasoncal_nights = VikRequest::getString('seasoncal_nights', '', 'request');
1358 $pseasoncal_prices = VikRequest::getString('seasoncal_prices', '', 'request');
1359 $pseasoncal_restr = VikRequest::getString('seasoncal_restr', '', 'request');
1360 $pmulti_units = VikRequest::getInt('multi_units', '', 'request');
1361 $pmulti_units = $punits > 1 ? $pmulti_units : 0;
1362 $psefalias = VikRequest::getString('sefalias', '', 'request');
1363 $psefalias = empty($psefalias) ? JFilterOutput::stringURLSafe($pcname) : JFilterOutput::stringURLSafe($psefalias);
1364 $pcustptitle = VikRequest::getString('custptitle', '', 'request');
1365 $pcustptitlew = VikRequest::getString('custptitlew', '', 'request');
1366 $pcustptitlew = in_array($pcustptitlew, array('before', 'after', 'replace')) ? $pcustptitlew : 'before';
1367 $pmetakeywords = VikRequest::getString('metakeywords', '', 'request');
1368 $pmetadescription = VikRequest::getString('metadescription', '', 'request');
1369 $pshare_with = VikRequest::getVar('share_with', array());
1370 $scalnights_arr = array();
1371 if (!empty($pseasoncal_nights)) {
1372 $scalnights = explode(',', $pseasoncal_nights);
1373 foreach ($scalnights as $scalnight) {
1374 if (intval(trim($scalnight)) > 0) {
1375 $scalnights_arr[] = intval(trim($scalnight));
1376 }
1377 }
1378 }
1379 if ($scalnights_arr) {
1380 $pseasoncal_nights = implode(', ', $scalnights_arr);
1381 } else {
1382 $pseasoncal_nights = '';
1383 $pseasoncal = 0;
1384 }
1385 $roomparams = [
1386 'lastavail' => $plastavail,
1387 'suggocc' => $psuggocc,
1388 'custprice' => $pcustprice,
1389 'custpricetxt' => $pcustpricetxt,
1390 'custpricesubtxt' => $pcustpricesubtxt,
1391 'reqinfo' => $preqinfo,
1392 'pricecal' => $ppricecal,
1393 'defcalcost' => floatval($pdefcalcost),
1394 'defrplan' => $pdefrplan,
1395 'maxminpeople' => $pmaxminpeople,
1396 'seasoncal' => $pseasoncal,
1397 'seasoncal_nights' => $pseasoncal_nights,
1398 'seasoncal_prices' => $pseasoncal_prices,
1399 'seasoncal_restr' => $pseasoncal_restr,
1400 'multi_units' => $pmulti_units,
1401 'custptitle' => $pcustptitle,
1402 'custptitlew' => $pcustptitlew,
1403 'metakeywords' => $pmetakeywords,
1404 'metadescription' => $pmetadescription,
1405 'layout_style' => VikRequest::getString('layout_style', 'default', 'request'),
1406 'checkin' => VikRequest::getString('listing_checkin', '', 'request'),
1407 'checkout' => VikRequest::getString('listing_checkout', '', 'request'),
1408 ];
1409 //distinctive features
1410 $roomparams['features'] = array();
1411 $newfeatures = array();
1412 if ($punits > 0) {
1413 for ($i=1; $i <= $punits; $i++) {
1414 $distf_name = VikRequest::getVar('feature-name'.$i, array());
1415 $distf_lang = VikRequest::getVar('feature-lang'.$i, array());
1416 $distf_value = VikRequest::getVar('feature-value'.$i, array());
1417 foreach ($distf_name as $distf_k => $distf) {
1418 if (strlen($distf) > 0 && strlen($distf_value[$distf_k]) > 0) {
1419 $use_key = strlen($distf_lang[$distf_k]) > 0 ? $distf_lang[$distf_k] : $distf;
1420 $roomparams['features'][$i][$use_key] = $distf_value[$distf_k];
1421 if ($distf_k < 1) {
1422 //check only the first feature
1423 $newfeatures[$i][$use_key] = $distf_value[$distf_k];
1424 }
1425 }
1426 }
1427 }
1428 }
1429
1430 // load current room record
1431 $dbo->setQuery(
1432 $dbo->getQuery(true)
1433 ->select('*')
1434 ->from($dbo->qn('#__vikbooking_rooms'))
1435 ->where($dbo->qn('id') . ' = ' . (int) $pwhereup)
1436 );
1437 $prevroom = $dbo->loadAssoc();
1438 if (!$prevroom) {
1439 VBOHttpDocument::getInstance()->close(404, 'Record not found');
1440 }
1441
1442 /**
1443 * Store room geo params information.
1444 *
1445 * @since 1.14 (J) - 1.4.0 (WP)
1446 */
1447 $geo = VikBooking::getGeocodingInstance();
1448 $geo_params = $geo->getRoomGeoTransient($pwhereup);
1449 if ($geo_params !== false) {
1450 // make sure the geocoding service was not turned off
1451 $geo_enabled = VikRequest::getInt('geo_enabled', 0, 'request');
1452 if (!$geo_enabled) {
1453 $geo_params->enabled = 0;
1454 }
1455 //
1456 $roomparams['geo'] = $geo_params;
1457 }
1458 //
1459
1460 $roomparamstr = json_encode($roomparams);
1461
1462 jimport('joomla.filesystem.file');
1463 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
1464
1465 if (!empty($pcname)) {
1466
1467 $picon = "";
1468 if (($_FILES['cimg'] ?? null) && !intval($_FILES['cimg']['error']) && VikBooking::caniWrite($updpath) && strlen(trim($_FILES['cimg']['name'])) && @is_uploaded_file($_FILES['cimg']['tmp_name'])) {
1469 $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['cimg']['name'])));
1470 $j = '';
1471 $pwhere = $updpath . $safename;
1472 if (file_exists($updpath . $safename)) {
1473 $j = 1;
1474 while (file_exists($updpath . $j . $safename)) {
1475 $j++;
1476 }
1477 $pwhere = $updpath . $j . $safename;
1478 }
1479 if (!getimagesize($_FILES['cimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
1480 @unlink($pwhere);
1481 } elseif (VikBooking::uploadFile($_FILES['cimg']['tmp_name'], $pwhere)) {
1482 $picon = $j . $safename;
1483 if ((int) $pautoresize && !empty($presizeto)) {
1484 $origmod = (new VikResizer)->proportionalImage($pwhere, $updpath . 'r_' . $j . $safename, $presizeto, $presizeto);
1485 if ($origmod) {
1486 @unlink($pwhere);
1487 $picon = 'r_' . $j . $safename;
1488 }
1489 }
1490 /**
1491 * Create a mini-thumbnail of the room/listing main photo.
1492 *
1493 * @since 1.17.5 (J) - 1.7.5 (WP)
1494 */
1495 try {
1496 // resize the original image
1497 (new VikResizer)->proportionalImage($pwhere, $updpath . 'mini_' . $picon, 96, 96);
1498 } catch (Throwable $e) {
1499 // silently catch any PHP GD error and continue
1500 }
1501 }
1502 }
1503
1504 /**
1505 * Create a mini-thumbnail of the current room/listing main photo.
1506 *
1507 * @since 1.17.5 (J) - 1.7.5 (WP)
1508 */
1509 if (!$picon && !empty($prevroom['img']) && is_file($updpath . $prevroom['img']) && !is_file($updpath . 'mini_' . $prevroom['img'])) {
1510 try {
1511 // resize the original image
1512 (new VikResizer)->proportionalImage($updpath . $prevroom['img'], $updpath . 'mini_' . $prevroom['img'], 96, 96);
1513 } catch (Throwable $e) {
1514 // silently catch any PHP GD error and continue
1515 }
1516 }
1517
1518 // more images
1519 $creativik = new VikResizer;
1520 $bigsdest = $updpath;
1521 $thumbsdest = $updpath;
1522 $dest = $updpath;
1523 $moreimagestr = $pactmoreimgs;
1524 $arrimgs = array();
1525 $captiontexts = array();
1526 $imgcaptions = array();
1527 //captions of uploaded extra images
1528 if (!empty($pactmoreimgs)) {
1529 $sploimgs = explode(';;', $pactmoreimgs);
1530 foreach ($sploimgs as $ki => $oimg) {
1531 if (!empty($oimg)) {
1532 $oldcaption = VikRequest::getString('caption'.$ki, '', 'request', VIKREQUEST_ALLOWHTML);
1533 $imgcaptions[] = $oldcaption;
1534 }
1535 }
1536 }
1537 //
1538 foreach ($pimages['name'] as $kk=>$ci) {
1539 if (!empty($ci)) {
1540 $arrimgs[] = $kk;
1541 $captiontexts[] = isset($pcimgcaption[$kk]) ? $pcimgcaption[$kk] : '';
1542 }
1543 }
1544 foreach ($arrimgs as $ki => $imgk) {
1545 if (strlen(trim($pimages['name'][$imgk]))) {
1546 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimages['name'][$imgk])));
1547 $src = $pimages['tmp_name'][$imgk];
1548 $j = "";
1549 if (file_exists($dest.$filename)) {
1550 $j = rand(171, 1717);
1551 while (file_exists($dest.$j.$filename)) {
1552 $j++;
1553 }
1554 }
1555 $finaldest = $dest.$j.$filename;
1556 $check = getimagesize($pimages['tmp_name'][$imgk]);
1557 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
1558 if (VikBooking::uploadFile($src, $finaldest)) {
1559 $gimg = $j.$filename;
1560 //orig img
1561 $origmod = true;
1562 if ($pautoresizemore == "1" && !empty($presizetomore)) {
1563 $origmod = $creativik->proportionalImage($finaldest, $bigsdest.'big_'.$j.$filename, $presizetomore, $presizetomore);
1564 } else {
1565 VikBooking::uploadFile($finaldest, $bigsdest.'big_'.$j.$filename, true);
1566 }
1567 //thumb
1568 $thumbsize = VikBooking::getThumbSize();
1569 $thumb = $creativik->proportionalImage($finaldest, $thumbsdest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
1570 if (!$thumb || !$origmod) {
1571 if (file_exists($bigsdest.'big_'.$j.$filename)) @unlink($bigsdest.'big_'.$j.$filename);
1572 if (file_exists($thumbsdest.'thumb_'.$j.$filename)) @unlink($thumbsdest.'thumb_'.$j.$filename);
1573 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1574 } else {
1575 $moreimagestr .= $j.$filename.";;";
1576 $imgcaptions[] = $captiontexts[$ki];
1577 }
1578 @unlink($finaldest);
1579 } else {
1580 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1581 }
1582 } else {
1583 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1584 }
1585 }
1586 }
1587 //sorting of extra images
1588 $sorted_extraim = array();
1589 $sorted_captions = array();
1590 $extraim_parts = explode(';;', $moreimagestr);
1591 foreach ($pimgsorting as $k => $v) {
1592 $capkey = -1;
1593 if (isset($extraim_parts[$k])) {
1594 $sorted_extraim[] = $v;
1595 foreach ($extraim_parts as $oldk => $oldv) {
1596 if ($oldv == $v) {
1597 $capkey = $oldk;
1598 break;
1599 }
1600 }
1601 }
1602 if (isset($imgcaptions[$capkey])) {
1603 $sorted_captions[] = $imgcaptions[$capkey];
1604 }
1605 }
1606 $tot_sorted_im = count($sorted_extraim);
1607 if ($tot_sorted_im != count($extraim_parts)) {
1608 foreach ($extraim_parts as $k => $v) {
1609 if ($k <= ($tot_sorted_im - 1)) {
1610 continue;
1611 }
1612 $sorted_extraim[] = $v;
1613 if (isset($imgcaptions[$k])) {
1614 $sorted_captions[] = $imgcaptions[$k];
1615 }
1616 }
1617 }
1618 $moreimagestr = implode(';;', $sorted_extraim);
1619 $imgcaptions = $sorted_captions;
1620 //end more images
1621 if (is_array($pccat) && count($pccat)) {
1622 $pccatdef = "";
1623 foreach ($pccat as $ccat) {
1624 if (!empty($ccat)) {
1625 $pccatdef .= $ccat.";";
1626 }
1627 }
1628 } else {
1629 $pccatdef = "";
1630 }
1631 if (is_array($pccarat) && count($pccarat)) {
1632 $pccaratdef = "";
1633 foreach ($pccarat as $ccarat) {
1634 $pccaratdef .= $ccarat.";";
1635 }
1636 } else {
1637 $pccaratdef = "";
1638 }
1639 if (is_array($pcoptional) && count($pcoptional)) {
1640 $pcoptionaldef = "";
1641 foreach ($pcoptional as $coptional) {
1642 $pcoptionaldef .= $coptional.";";
1643 }
1644 } else {
1645 $pcoptionaldef = "";
1646 }
1647 $pcavaildef=($pcavail=="yes" ? "1" : "0");
1648 if ($pfromadult > $ptoadult) {
1649 $pfromadult = 1;
1650 $ptoadult = 1;
1651 }
1652 if ($pfromchild > $ptochild) {
1653 $pfromchild = 1;
1654 $ptochild = 1;
1655 }
1656
1657 //adults charges/discounts
1658 $adchdisctouch = false;
1659 $q = "SELECT * FROM `#__vikbooking_rooms` WHERE `id`='".$pwhereup."';";
1660 $dbo->setQuery($q);
1661 $dbo->execute();
1662 $oldroom = $dbo->loadAssocList();
1663 $oldroom = $oldroom[0];
1664 if ($oldroom['fromadult'] == $pfromadult && $oldroom['toadult'] == $ptoadult) {
1665 if ($oldroom['toadult'] > 1 && $oldroom['fromadult'] < $oldroom['toadult'] && @count($padultsdiffnum) > 0) {
1666 $startadind = $oldroom['fromadult'] > 0 ? $oldroom['fromadult'] : 1;
1667 for($adi = $startadind; $adi <= $oldroom['toadult']; $adi++) {
1668 foreach ($padultsdiffnum as $kad=>$vad) {
1669 if (intval($vad) == intval($adi) && strlen($padultsdiffval[$kad]) > 0) {
1670 $adchdisctouch = true;
1671 $inschdisc = intval($padultsdiffchdisc[$kad]) == 1 ? 1 : 2;
1672 $insvalpcent = intval($padultsdiffvalpcent[$kad]) == 1 ? 1 : 2;
1673 $inspernight = intval($padultsdiffpernight[$kad]) == 1 ? 1 : 0;
1674 $insvalue = floatval($padultsdiffval[$kad]);
1675 //check if it exists
1676 $q = "SELECT `id` FROM `#__vikbooking_adultsdiff` WHERE `idroom`='".$oldroom['id']."' AND `adults`='".$adi."';";
1677 $dbo->setQuery($q);
1678 $dbo->execute();
1679 if ($dbo->getNumRows() > 0) {
1680 if ($insvalue > 0) {
1681 //update
1682 $q = "UPDATE `#__vikbooking_adultsdiff` SET `chdisc`='".$inschdisc."', `valpcent`='".$insvalpcent."', `value`='".$insvalue."', `pernight`='".$inspernight."' WHERE `idroom`='".$oldroom['id']."' AND `adults`='".$adi."';";
1683 $dbo->setQuery($q);
1684 $dbo->execute();
1685 } else {
1686 //delete
1687 $q = "DELETE FROM `#__vikbooking_adultsdiff` WHERE `idroom`='".$oldroom['id']."' AND `adults`='".$adi."';";
1688 $dbo->setQuery($q);
1689 $dbo->execute();
1690 }
1691 } else {
1692 //insert
1693 $q = "INSERT INTO `#__vikbooking_adultsdiff` (`idroom`,`chdisc`,`valpcent`,`value`,`adults`,`pernight`) VALUES('".$oldroom['id']."', '".$inschdisc."', '".$insvalpcent."', '".$insvalue."', '".$adi."', '".$inspernight."');";
1694 $dbo->setQuery($q);
1695 $dbo->execute();
1696 }
1697 }
1698 }
1699 }
1700 }
1701 } else {
1702 //min and max adults num have changed, delete
1703 $q = "DELETE FROM `#__vikbooking_adultsdiff` WHERE `idroom`='".$oldroom['id']."';";
1704 $dbo->setQuery($q);
1705 $dbo->execute();
1706 }
1707 if ($adchdisctouch == true) {
1708 $app->enqueueMessage(JText::translate('VBUPDROOMADCHDISCSAVED'));
1709 }
1710 //
1711 //check distinctive features if there were any changes
1712 $old_rparams = json_decode($oldroom['params'], true);
1713 $old_rparams = is_array($old_rparams) ? $old_rparams : array();
1714 if (array_key_exists('features', $old_rparams)) {
1715 $oldfeatures = array();
1716 foreach ($old_rparams['features'] as $rnumunit => $oldfeat) {
1717 foreach ($oldfeat as $featname => $featval) {
1718 $oldfeatures[$rnumunit][$featname] = $featval;
1719 break;
1720 }
1721 }
1722 /**
1723 * We reset the sub-unit information to all bookings only in case the new
1724 * number of units is reduced. When we add new units or we modify the contents,
1725 * we keep everything as is for the past reservations.
1726 *
1727 * @since 1.15.2 (J) - 1.5.5 (WP)
1728 */
1729 if ($oldfeatures != $newfeatures && count($newfeatures) < count($oldfeatures)) {
1730 // changes were made to the first index (Room Number by default) of the distinctive features
1731 // set to NULL all the already set roomindexes in bookings
1732 $q = "UPDATE `#__vikbooking_ordersrooms` SET `roomindex`=NULL WHERE `idroom`=".(int)$oldroom['id'].";";
1733 $dbo->setQuery($q);
1734 $dbo->execute();
1735 }
1736 }
1737 //
1738 $q = "UPDATE `#__vikbooking_rooms` SET `name`=".$dbo->quote($pcname).",".(strlen($picon) > 0 ? "`img`='".$picon."'," : "")."`idcat`=".$dbo->quote($pccatdef).",`idcarat`=".$dbo->quote($pccaratdef).",`idopt`=".$dbo->quote($pcoptionaldef).",`info`=".$dbo->quote($pcdescr).",`avail`=".$dbo->quote($pcavaildef).",`units`=".($punits > 0 ? $dbo->quote($punits) : "'1'").",`moreimgs`=".$dbo->quote($moreimagestr).",`fromadult`='".$pfromadult."',`toadult`='".$ptoadult."',`fromchild`='".$pfromchild."',`tochild`='".$ptochild."',`smalldesc`=".$dbo->quote($psmalldesc).",`totpeople`=".$ptotpeople.",`mintotpeople`=".$pmintotpeople.",`params`=".$dbo->quote($roomparamstr).",`imgcaptions`=".$dbo->quote(json_encode($imgcaptions)).",`alias`=".$dbo->quote($psefalias)." WHERE `id`=".$dbo->quote($pwhereup).";";
1739 $dbo->setQuery($q);
1740 $dbo->execute();
1741
1742 /**
1743 * Share availability calendars with other rooms.
1744 *
1745 * @since 1.13
1746 */
1747 // always reset relations for this main room
1748 $q = "DELETE FROM `#__vikbooking_calendars_xref` WHERE `mainroom`={$pwhereup};";
1749 $dbo->setQuery($q);
1750 $dbo->execute();
1751 $newxref = array();
1752 foreach ($pshare_with as $cldroom) {
1753 if (!empty($cldroom)) {
1754 array_push($newxref, (int)$cldroom);
1755 }
1756 }
1757 foreach ($newxref as $cldroom) {
1758 $q = "INSERT INTO `#__vikbooking_calendars_xref` (`mainroom`, `childroom`) VALUES ({$pwhereup}, {$cldroom});";
1759 $dbo->setQuery($q);
1760 $dbo->execute();
1761 }
1762
1763 /**
1764 * Room upgrade options.
1765 *
1766 * @since 1.16.0 (J) - 1.6.0 (WP)
1767 */
1768 $room_upgrade_options = [];
1769 $room_upgrade = VikRequest::getInt('room_upgrade', 0, 'request');
1770 $upgrade_rooms = VikRequest::getVar('upgrade_rooms', array());
1771 $upgrade_discount = VikRequest::getFloat('upgrade_discount', 0, 'request');
1772 if ($room_upgrade && is_array($upgrade_rooms) && count($upgrade_rooms)) {
1773 $upgrade_rooms = array_map(function($rid) {
1774 return (int)$rid;
1775 }, $upgrade_rooms);
1776
1777 $room_upgrade_options = [
1778 'rooms' => $upgrade_rooms,
1779 'discount' => $upgrade_discount,
1780 ];
1781 }
1782 $config->set('room_upgrade_options_' . $pwhereup, json_encode($room_upgrade_options));
1783
1784 /**
1785 * Minimum advance booking offset can be defined at room-level (always in hours).
1786 *
1787 * @since 1.18.3 (J) - 1.8.3 (WP)
1788 */
1789 $pmin_adv_notice_room = VikRequest::getInt('min_adv_notice_room', 0, 'request');
1790 $pmindate = VikRequest::getInt('mindate', 0, 'request');
1791 if ($pmin_adv_notice_room && $pmindate > 0) {
1792 // set value
1793 $config->set("room_{$pwhereup}_min_adv_notice", $pmindate);
1794 } else {
1795 // unset value
1796 $config->set("room_{$pwhereup}_min_adv_notice", null);
1797 }
1798
1799 /**
1800 * Maximum advance booking offset can be defined at room-level.
1801 *
1802 * @since 1.16.3 (J) - 1.6.3 (WP)
1803 */
1804 $pmax_adv_notice_room = VikRequest::getInt('max_adv_notice_room', 0, 'request');
1805 $pmaxdate = VikRequest::getInt('maxdate', 0, 'request');
1806 $pmaxdateinterval = VikRequest::getString('maxdateinterval', '', 'request');
1807 $maxdate_str = '';
1808 if ($pmax_adv_notice_room && $pmaxdate > 0) {
1809 $pmaxdateinterval = !in_array($pmaxdateinterval, array('d', 'w', 'm', 'y')) ? 'y' : $pmaxdateinterval;
1810 $maxdate_str = '+' . $pmaxdate . $pmaxdateinterval;
1811 }
1812 $config->set("room_{$pwhereup}_max_adv_notice", $maxdate_str);
1813
1814 $app->enqueueMessage(JText::translate('VBUPDROOMOK'));
1815 }
1816
1817 if ($pupdatecaption == 1 || $stay === true) {
1818 $app->redirect("index.php?option=com_vikbooking&task=editroom&cid[]=".$pwhereup);
1819 } else {
1820 $app->redirect("index.php?option=com_vikbooking&task=rooms");
1821 }
1822 }
1823
1824 public function modavail() {
1825 if (!JSession::checkToken() && !JSession::checkToken('get')) {
1826 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1827 }
1828 $cid = VikRequest::getVar('cid', array(0));
1829 $room = $cid[0];
1830 if (!empty($room)) {
1831 $dbo = JFactory::getDBO();
1832 $q = "SELECT `avail` FROM `#__vikbooking_rooms` WHERE `id`=".$dbo->quote($room).";";
1833 $dbo->setQuery($q);
1834 $dbo->execute();
1835 $get = $dbo->loadAssocList();
1836 $q = "UPDATE `#__vikbooking_rooms` SET `avail`='".(intval($get[0]['avail'])==1 ? 0 : 1)."' WHERE `id`=".$dbo->quote($room).";";
1837 $dbo->setQuery($q);
1838 $dbo->execute();
1839 }
1840 $mainframe = JFactory::getApplication();
1841 $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
1842 }
1843
1844 public function removeroom()
1845 {
1846 if (!JSession::checkToken()) {
1847 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1848 }
1849
1850 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
1851 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1852 }
1853
1854 $ids = VikRequest::getVar('cid', array(0));
1855 if (@count($ids)) {
1856 $dbo = JFactory::getDBO();
1857 foreach ($ids as $d) {
1858 $q = "DELETE FROM `#__vikbooking_rooms` WHERE `id`=".$dbo->quote($d).";";
1859 $dbo->setQuery($q);
1860 $dbo->execute();
1861 $q = "DELETE FROM `#__vikbooking_dispcost` WHERE `idroom`=".$dbo->quote($d).";";
1862 $dbo->setQuery($q);
1863 $dbo->execute();
1864 }
1865 }
1866 $mainframe = JFactory::getApplication();
1867 $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
1868 }
1869
1870 public function tariffs() {
1871 VikBookingHelper::printHeader("fares");
1872
1873 VikRequest::setVar('view', VikRequest::getCmd('view', 'tariffs'));
1874
1875 parent::display();
1876
1877 if (VikBooking::showFooter()) {
1878 VikBookingHelper::printFooter();
1879 }
1880 }
1881
1882 public function removetariffs()
1883 {
1884 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
1885 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1886 }
1887
1888 $ids = VikRequest::getVar('cid', array(0));
1889 $proomid = VikRequest::getInt('roomid', '', 'request');
1890 if (@count($ids)) {
1891 $dbo = JFactory::getDBO();
1892 foreach ($ids as $r) {
1893 $x=explode(";", $r);
1894 foreach ($x as $rm) {
1895 if (!empty($rm)) {
1896 $q = "DELETE FROM `#__vikbooking_dispcost` WHERE `id`=".$dbo->quote($rm).";";
1897 $dbo->setQuery($q);
1898 $dbo->execute();
1899 }
1900 }
1901 }
1902 }
1903 $mainframe = JFactory::getApplication();
1904 $mainframe->redirect("index.php?option=com_vikbooking&task=tariffs&cid[]=".$proomid);
1905 }
1906
1907 public function editbusy() {
1908 VikBookingHelper::printHeader("8");
1909
1910 VikRequest::setVar('view', VikRequest::getCmd('view', 'editbusy'));
1911
1912 parent::display();
1913
1914 if (VikBooking::showFooter()) {
1915 VikBookingHelper::printFooter();
1916 }
1917 }
1918
1919 public function updatebusy()
1920 {
1921 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1922 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1923 }
1924
1925 $this->do_updatebusy();
1926 }
1927
1928 public function updatebusydoinv()
1929 {
1930 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1931 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1932 }
1933
1934 $this->do_updatebusy('geninvoices');
1935 }
1936
1937 private function do_updatebusy($callback = '')
1938 {
1939 $pidorder = VikRequest::getInt('idorder', 0, 'request');
1940 $pcheckindate = VikRequest::getString('checkindate', '', 'request');
1941 $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
1942 $pcheckinh = VikRequest::getString('checkinh', '', 'request');
1943 $pcheckinm = VikRequest::getString('checkinm', '', 'request');
1944 $pcheckouth = VikRequest::getString('checkouth', '', 'request');
1945 $pcheckoutm = VikRequest::getString('checkoutm', '', 'request');
1946 $pcustdata = VikRequest::getString('custdata', '', 'request');
1947 $pareprices = VikRequest::getString('areprices', '', 'request');
1948 $ptotpaid = VikRequest::getString('totpaid', '', 'request');
1949 $prefund = VikRequest::getString('refund', '', 'request');
1950 $pfrominv = VikRequest::getInt('frominv', '', 'request');
1951 $pvcm = VikRequest::getInt('vcm', '', 'request');
1952 $pgoto = VikRequest::getString('goto', '', 'request');
1953 $pextracn = VikRequest::getVar('extracn', []);
1954 $pextracc = VikRequest::getVar('extracc', []);
1955 $pextractx = VikRequest::getVar('extractx', []);
1956 /**
1957 * This is a "foreign key" integer value useful for other Vik plugins
1958 * to store custom extra services within a VBO reservation. Another
1959 * custom value "extra foreign data" (extracdata) is added. We also
1960 * support a "type" string useful for VCM to determine the type of service.
1961 *
1962 * @since 1.16.0 (J) - 1.6.0 (WP)
1963 * @since 1.16.1 (J) - 1.6.1 (WP) added the "type" string.
1964 */
1965 $pextractype = VikRequest::getVar('extractype', []);
1966 $pextracfk = VikRequest::getVar('extracfk', []);
1967 $pextracdata = VikRequest::getVar('extracdata', [], 'request', 'array', VIKREQUEST_ALLOWRAW);
1968
1969 $dbo = JFactory::getDbo();
1970 $user = JFactory::getUser();
1971 $app = JFactory::getApplication();
1972
1973 // availability helper
1974 $av_helper = VikBooking::getAvailabilityInstance();
1975
1976 $actnow = time();
1977 $nowdf = VikBooking::getDateFormat(true);
1978 if ($nowdf == "%d/%m/%Y") {
1979 $df = 'd/m/Y';
1980 } elseif ($nowdf == "%m/%d/%Y") {
1981 $df = 'm/d/Y';
1982 } else {
1983 $df = 'Y/m/d';
1984 }
1985
1986 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder;
1987 $dbo->setQuery($q, 0, 1);
1988 $ord = $dbo->loadAssoc();
1989 if (!$ord) {
1990 $app->redirect("index.php?option=com_vikbooking&task=rooms");
1991 exit;
1992 }
1993
1994 $q = "SELECT `or`.*,`r`.`name`,`r`.`idopt`,`r`.`units`,`r`.`fromadult`,`r`.`toadult` FROM `#__vikbooking_ordersrooms` AS `or`,`#__vikbooking_rooms` AS `r` WHERE `or`.`idorder`=".$ord['id']." AND `or`.`idroom`=`r`.`id` ORDER BY `or`.`id` ASC;";
1995 $dbo->setQuery($q);
1996 $ordersrooms = $dbo->loadAssocList();
1997
1998 // do not touch this array property because it's used by VCM
1999 $ord['rooms_info'] = $ordersrooms;
2000
2001 // room stay dates in case of split stay
2002 $room_stay_dates = [];
2003 if ($ord['split_stay']) {
2004 if ($ord['status'] == 'confirmed') {
2005 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
2006 } else {
2007 $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $ord['id'], []);
2008 }
2009 // immediately count the number of nights of stay for each split room
2010 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
2011 if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
2012 // overwrite values for compatibility with non-confirmed bookings
2013 $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
2014 $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
2015 }
2016 $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
2017 // overwrite the whole array
2018 $room_stay_dates[$sps_r_k] = $sps_r_v;
2019 }
2020 }
2021
2022 // package or custom rate
2023 $is_package = !empty($ord['pkg']) ? true : false;
2024 $is_cust_cost = false;
2025 foreach ($ordersrooms as $kor => $or) {
2026 if ($is_package !== true && !empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
2027 $is_cust_cost = true;
2028 break;
2029 }
2030 }
2031
2032 // room switching
2033 $toswitch = array();
2034 $idbooked = array();
2035 $rooms_units = array();
2036
2037 $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms`;";
2038 $dbo->setQuery($q);
2039 $all_rooms = $dbo->loadAssocList();
2040 foreach ($all_rooms as $rr) {
2041 $rooms_units[$rr['id']]['name'] = $rr['name'];
2042 $rooms_units[$rr['id']]['units'] = $rr['units'];
2043 }
2044
2045 foreach ($ordersrooms as $ind => $or) {
2046 $switch_command = VikRequest::getString('switch_'.$or['id'], '', 'request');
2047 if (!empty($switch_command) && intval($switch_command) != $or['idroom'] && array_key_exists(intval($switch_command), $rooms_units)) {
2048 if (!isset($idbooked[$or['idroom']])) {
2049 $idbooked[$or['idroom']] = 0;
2050 }
2051 $idbooked[$or['idroom']]++;
2052 $orkey = count($toswitch);
2053 $toswitch[$orkey]['from'] = $or['idroom'];
2054 $toswitch[$orkey]['to'] = intval($switch_command);
2055 $toswitch[$orkey]['record'] = $or;
2056 $toswitch[$orkey]['record_ind'] = $ind;
2057 }
2058 }
2059
2060 if (count($toswitch) && (!empty($ordersrooms[0]['idtar']) || $is_package || $is_cust_cost)) {
2061 foreach ($toswitch as $ksw => $rsw) {
2062 $plusunit = array_key_exists($rsw['to'], $idbooked) ? $idbooked[$rsw['to']] : 0;
2063 $room_checkin = $ord['checkin'];
2064 $room_checkout = $ord['checkout'];
2065 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
2066 $room_checkin = $room_stay_dates[$rsw['record_ind']]['checkin'];
2067 $room_checkout = $room_stay_dates[$rsw['record_ind']]['checkout'];
2068 }
2069 if (!VikBooking::roomBookable($rsw['to'], ($rooms_units[$rsw['to']]['units'] + $plusunit), $room_checkin, $room_checkout)) {
2070 // the room is not available
2071 unset($toswitch[$ksw]);
2072 VikError::raiseWarning('', JText::sprintf('VBSWITCHRERR', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']));
2073 }
2074 }
2075 if (count($toswitch)) {
2076 // reset first record rate
2077 reset($ordersrooms);
2078 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$ordersrooms[0]['id'].";";
2079 $dbo->setQuery($q);
2080 $dbo->execute();
2081
2082 // flag for invoking VCM at a proper time
2083 $vcm_should_run = false;
2084
2085 foreach ($toswitch as $ksw => $rsw) {
2086 // update room reservation record
2087 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idroom`=".$rsw['to'].",`idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$rsw['record']['id'].";";
2088 $dbo->setQuery($q);
2089 $dbo->execute();
2090 $app->enqueueMessage(JText::sprintf('VBSWITCHROK', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']));
2091
2092 // update Notes field for this booking to keep track of the previous room that was assigned
2093 $prev_room_name = array_key_exists($rsw['from'], $rooms_units) ? $rooms_units[$rsw['from']]['name'] : '';
2094 if (!empty($prev_room_name)) {
2095 $new_notes = JText::sprintf('VBOPREVROOMMOVED', $prev_room_name, date($df.' H:i:s'))."\n".$ord['adminnotes'];
2096 $q = "UPDATE `#__vikbooking_orders` SET `adminnotes`=".$dbo->quote($new_notes)." WHERE `id`=".(int)$ord['id'].";";
2097 $dbo->setQuery($q);
2098 $dbo->execute();
2099 }
2100
2101 if ($ord['status'] == 'confirmed') {
2102 // update room record in _busy
2103 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from'] && !empty($room_stay_dates[$rsw['record_ind']]['id'])) {
2104 // in case of a split stay it is fundamental to update the exact busy record ID
2105 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=" . $rsw['to'] . " WHERE `id`=" . (int)$room_stay_dates[$rsw['record_ind']]['id'];
2106 $dbo->setQuery($q);
2107 $dbo->execute();
2108 } else {
2109 // regular processing of a room ID for a reservation, no matter which one, we switch it
2110 $q = "SELECT `b`.`id`,`b`.`idroom`,`ob`.`idorder` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`idroom`=" . $rsw['from'] . " AND `b`.`id`=`ob`.`idbusy` AND `ob`.`idorder`=" . $ord['id'];
2111 $dbo->setQuery($q, 0, 1);
2112 $cur_busy = $dbo->loadAssoc();
2113 if ($cur_busy) {
2114 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=".$rsw['to']." WHERE `id`=".$cur_busy['id']." AND `idroom`=".$cur_busy['idroom']." LIMIT 1;";
2115 $dbo->setQuery($q);
2116 $dbo->execute();
2117 }
2118 }
2119
2120 /**
2121 * Make sure to take care of the shared calendars before invoking VCM.
2122 * Register the flag to run the Channel Manager and leave the booking
2123 * array unchanged to run just one update request.
2124 *
2125 * @since 1.16.0 (J) - 1.6.0 (WP)
2126 */
2127 $vcm_should_run = true;
2128
2129 } elseif ($ord['status'] == 'standby') {
2130 // remove record in _tmplock
2131 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($ord['id']) . ";";
2132 $dbo->setQuery($q);
2133 $dbo->execute();
2134 // check if it's a split stay
2135 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
2136 // update room ID in split stay data
2137 $room_stay_dates[$rsw['record_ind']]['idroom'] = $rsw['to'];
2138 // update configuration record
2139 VBOFactory::getConfig()->set('split_stay_' . $ord['id'], json_encode($room_stay_dates));
2140 }
2141 }
2142 }
2143
2144 // unset any previously booked room due to calendar sharing
2145 VikBooking::cleanSharedCalendarsBusy($ord['id']);
2146 // check if some of the rooms booked have shared calendars
2147 VikBooking::updateSharedCalendars($ord['id']);
2148
2149 if ($vcm_should_run) {
2150 // we can now run the Channel Manager after having updated the shared calendars
2151 $vcm_autosync = VikBooking::vcmAutoUpdate();
2152 if ($vcm_autosync > 0) {
2153 $vcm_obj = VikBooking::getVcmInvoker();
2154 $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
2155 $sync_result = $vcm_obj->doSync();
2156 if ($sync_result === false) {
2157 $vcm_err = $vcm_obj->getError();
2158 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
2159 }
2160 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
2161 VikError::raiseNotice('', JText::translate('VBCHANNELMANAGERINVOKEASK').' <form action="index.php?option=com_vikbooking" method="post"><input type="hidden" name="option" value="com_vikbooking"/><input type="hidden" name="task" value="invoke_vcm"/><input type="hidden" name="stype" value="modify"/><input type="hidden" name="cid[]" value="'.$ord['id'].'"/><input type="hidden" name="origb" value="'.urlencode(json_encode($ord)).'"/><input type="hidden" name="returl" value="'.urlencode("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '')."&cid[]=".$ord['id']).'"/><button type="submit" class="btn btn-primary">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button></form>');
2162 }
2163 }
2164
2165 //Booking History
2166 VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($ord));
2167 //
2168 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2169 exit;
2170 }
2171 }
2172
2173 // update booking data
2174 $first = VikBooking::getDateTimestamp($pcheckindate, $pcheckinh, $pcheckinm);
2175 $second = VikBooking::getDateTimestamp($pcheckoutdate, $pcheckouth, $pcheckoutm);
2176 if ($second <= $first) {
2177 VikError::raiseWarning('', JText::translate('ERRPREV'));
2178 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2179 exit;
2180 }
2181
2182 $secdiff = $second - $first;
2183 $daysdiff = $secdiff / 86400;
2184 if (is_int($daysdiff)) {
2185 if ($daysdiff < 1) {
2186 $daysdiff = 1;
2187 }
2188 } else {
2189 if ($daysdiff < 1) {
2190 $daysdiff = 1;
2191 } else {
2192 $sum = floor($daysdiff) * 86400;
2193 $newdiff = $secdiff - $sum;
2194 $maxhmore = VikBooking::getHoursMoreRb() * 3600;
2195 if ($maxhmore >= $newdiff) {
2196 $daysdiff = floor($daysdiff);
2197 } else {
2198 $daysdiff = ceil($daysdiff);
2199 }
2200 }
2201 }
2202
2203 $groupdays = VikBooking::getGroupDays($first, $second, $daysdiff);
2204 $opertwounits = true;
2205
2206 $units_counter = array();
2207 $prm_room_oid = VikRequest::getInt('rm_room_oid', 0, 'request');
2208 foreach ($ordersrooms as $ind => $or) {
2209 if (!isset($units_counter[$or['idroom']])) {
2210 $units_counter[$or['idroom']] = -1;
2211 }
2212 if ($prm_room_oid != $or['id']) {
2213 $units_counter[$or['idroom']]++;
2214 }
2215 }
2216
2217 /**
2218 * Split stay data for booking and rooms different stay dates.
2219 *
2220 * @since 1.16.0 (J) - 1.6.0 (WP)
2221 */
2222 $split_stay_data = VikRequest::getVar('split_stay_data', array());
2223 $room_modify_dates = VikRequest::getVar('room_modify_dates', array());
2224 $split_stay_checkins = [];
2225 $split_stay_checkouts = [];
2226
2227 if ($ord['split_stay'] && !empty($split_stay_data)) {
2228 // make sure the min/max split stay dates match the booking global dates
2229 foreach ($split_stay_data as $sps_k => $split_stay) {
2230 if (empty($split_stay['checkin']) || empty($split_stay['checkout'])) {
2231 continue;
2232 }
2233 $new_room_checkin = VikBooking::getDateTimestamp($split_stay['checkin'], $pcheckinh, $pcheckinm);
2234 $new_room_checkout = VikBooking::getDateTimestamp($split_stay['checkout'], $pcheckouth, $pcheckoutm);
2235 $split_stay_checkins[] = $new_room_checkin;
2236 $split_stay_checkouts[] = $new_room_checkout;
2237 if (isset($room_stay_dates[$sps_k])) {
2238 $room_stay_dates[$sps_k]['new_checkin'] = $new_room_checkin;
2239 $room_stay_dates[$sps_k]['new_checkout'] = $new_room_checkout;
2240 $room_stay_dates[$sps_k]['new_nights'] = $av_helper->countNightsOfStay($new_room_checkin, $new_room_checkout);
2241 }
2242 }
2243 if (empty($split_stay_checkins) || empty($split_stay_checkouts)) {
2244 // error
2245 VikError::raiseWarning('', 'Error, split stay rooms must have their own stay dates matching the booking check-in and check-out dates');
2246 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2247 exit;
2248 }
2249 if (min($split_stay_checkins) != $first) {
2250 // error
2251 VikError::raiseWarning('', sprintf('Error, the earliest check-in (%s) for the split stay rooms must match the booking check-in date (%s)', date('Y-m-d H:i:s', min($split_stay_checkins)), date('Y-m-d H:i:s', $first)));
2252 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2253 exit;
2254 }
2255 if (max($split_stay_checkouts) != $second) {
2256 // error
2257 VikError::raiseWarning('', sprintf('Error, the latest check-out (%s) for the split stay rooms must match the booking check-out date (%s)', date('Y-m-d H:i:s', max($split_stay_checkouts)), date('Y-m-d H:i:s', $second)));
2258 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2259 exit;
2260 }
2261 }
2262
2263 /**
2264 * We need to make sure the sub-units of the rooms involved are not being overbooked.
2265 * In this case, we simply raise an error message by not stopping the process.
2266 *
2267 * @since 1.13.0 (J) - 1.3.0 (WP)
2268 */
2269 $subunits_involved_bids = array();
2270 //
2271
2272 foreach ($ordersrooms as $ind => $or) {
2273 $num = $ind + 1;
2274 $check = "SELECT `b`.`id`,`b`.`checkin`,`b`.`realback`,`ob`.`idorder` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`idroom`=" . $or['idroom'] . " AND `b`.`realback`>=" . $first . " AND `b`.`id`=`ob`.`idbusy` AND `ob`.`idorder`!=" . $ord['id'] . ";";
2275 $dbo->setQuery($check);
2276 $busy = $dbo->loadAssocList();
2277 if ($busy) {
2278 // determine the days to consider for the count of the availability
2279 $use_groupdays = $groupdays;
2280 $room_checkin = $first;
2281 $room_checkout = $second;
2282 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$ind]) && $room_stay_dates[$ind]['idroom'] == $or['idroom'] && !empty($room_stay_dates[$ind]['new_nights'])) {
2283 $use_groupdays = VikBooking::getGroupDays($room_stay_dates[$ind]['new_checkin'], $room_stay_dates[$ind]['new_checkout'], $room_stay_dates[$ind]['new_nights']);
2284 $room_checkin = $room_stay_dates[$ind]['new_checkin'];
2285 $room_checkout = $room_stay_dates[$ind]['new_checkout'];
2286 } elseif (!$ord['split_stay'] && !$ord['closure'] && $ord['roomsnum'] > 1 && $ord['days'] > 1 && $ord['status'] == 'confirmed' && VikRequest::getInt('room_modify_dates' . $ind, 0, 'request')) {
2287 // room may have individual stay dates
2288 if (isset($room_modify_dates[$ind]) && !empty($room_modify_dates[$ind]['checkin']) && !empty($room_modify_dates[$ind]['checkout'])) {
2289 // get new stay dates (if changed)
2290 $new_room_checkin = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkin'], $pcheckinh, $pcheckinm);
2291 $new_room_checkout = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkout'], $pcheckouth, $pcheckoutm);
2292 $new_room_staynights = $av_helper->countNightsOfStay($new_room_checkin, $new_room_checkout);
2293 $use_groupdays = VikBooking::getGroupDays($new_room_checkin, $new_room_checkout, $new_room_staynights);
2294 $room_checkin = $new_room_checkin;
2295 $room_checkout = $new_room_checkout;
2296 // inject room stay nights and timestamps
2297 $room_modify_dates[$ind]['stay_nights'] = $new_room_staynights;
2298 $room_modify_dates[$ind]['checkin_ts'] = $new_room_checkin;
2299 $room_modify_dates[$ind]['checkout_ts'] = $new_room_checkout;
2300 }
2301 }
2302
2303 foreach ($use_groupdays as $gday) {
2304 // count units booked for each stay timestamp
2305 $bfound = 0;
2306 foreach ($busy as $bu) {
2307 if ($gday >= $bu['checkin'] && $gday <= $bu['realback']) {
2308 // increase units booked found
2309 $bfound++;
2310 // keep track of the IDs involved to avoid overbooking for the sub-units
2311 if (!empty($or['roomindex'])) {
2312 if (!isset($subunits_involved_bids[$bu['idorder']])) {
2313 $subunits_involved_bids[$bu['idorder']] = array();
2314 }
2315 array_push($subunits_involved_bids[$bu['idorder']], array(
2316 'idroom' => $or['idroom'],
2317 'roomindex' => $or['roomindex'],
2318 ));
2319 }
2320 }
2321 }
2322
2323 // units booked must be greater than zero in case of split stays involving the same room multiple times
2324 $detract_multi_units = $units_counter[$or['idroom']];
2325 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$ind]) && $room_stay_dates[$ind]['idroom'] == $or['idroom']) {
2326 // split stay bookings never occupy the same room on the same dates
2327 $detract_multi_units = 0;
2328 }
2329 if ($bfound > 0 && $bfound >= ($or['units'] - $detract_multi_units)) {
2330 $opertwounits = false;
2331 break 2;
2332 }
2333
2334 // make sure the room is not temporarily locked while waiting to be paid/confirmed
2335 if ($ord['status'] == 'confirmed' && !VikBooking::roomNotLocked($or['idroom'], $or['units'], $room_checkin, $room_checkout)) {
2336 $opertwounits = false;
2337 break 2;
2338 }
2339 }
2340 }
2341 }
2342
2343 /**
2344 * Make sure no sub-units are overbooked even though the main room is available.
2345 *
2346 * @since 1.13.0 (J) - 1.3.0 (WP)
2347 */
2348 if ($opertwounits === true && $subunits_involved_bids) {
2349 $subunits_involved_bids = array_unique($subunits_involved_bids);
2350 // grab all the information about the bids involved and the related rooms/indexes
2351 $q = "SELECT `or`.`idorder`, `or`.`idroom`, `or`.`roomindex`
2352 FROM `#__vikbooking_ordersrooms` AS `or`
2353 WHERE `or`.`idorder` IN (" . implode(', ', array_keys($subunits_involved_bids)) . ");";
2354 $dbo->setQuery($q);
2355 $involved_data = $dbo->loadAssocList();
2356 foreach ($involved_data as $invb) {
2357 if (empty($invb['roomindex'])) {
2358 continue;
2359 }
2360 foreach ($subunits_involved_bids[$invb['idorder']] as $bookedindex) {
2361 if ($bookedindex['idroom'] == $invb['idroom'] && $bookedindex['roomindex'] == $invb['roomindex']) {
2362 // this same sub-unit is occupied by this booking ID: raise an error message to inform the administrator
2363 $involved_booking = VikBooking::getBookingInfoFromID($invb['idorder']);
2364 $involved_room = VikBooking::getRoomInfo($invb['idroom'], ['name', 'params'], $no_cache = true);
2365 $subunit_name = $invb['roomindex'];
2366 $room_params = (array) json_decode($involved_room['params'] ?? '[]', true);
2367 foreach (($room_params['features'] ?? []) as $rind => $rfeatures) {
2368 if ($rind == $invb['roomindex']) {
2369 foreach ($rfeatures as $fname => $fval) {
2370 if (strlen($fval)) {
2371 $subunit_name = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
2372 break;
2373 }
2374 }
2375 }
2376 }
2377 $adjust_link = '<br/><a class="btn btn-danger" target="_blank" href="index.php?option=com_vikbooking&task=editorder&cid[]=' . $invb['idorder'] . '">' . JText::translate('VBOSUBUNITOVERBOOKEDGOTO') . '</a>';
2378 $app->enqueueMessage(
2379 JText::sprintf(
2380 'VBOSUBUNITOVERBOOKEDERR',
2381 $subunit_name,
2382 $involved_room['name'] ?? $invb['idroom'],
2383 date($df, $involved_booking['checkin'] ?? 0),
2384 date($df, $involved_booking['checkout'] ?? 0),
2385 $invb['idorder']
2386 ) . $adjust_link,
2387 'error'
2388 );
2389 }
2390 }
2391 }
2392 }
2393
2394 $forcebooking = VikRequest::getInt('forcebooking', 0, 'request');
2395 if ($opertwounits === true || $forcebooking) {
2396 // update dates, customer information, amount paid and busy records before checking the rates
2397 $turnover_secs = VikBooking::getHoursRoomAvail() * 3600;
2398 $realback = $turnover_secs + $second;
2399
2400 $newtotalpaid = strlen($ptotpaid) > 0 ? floatval($ptotpaid) : "";
2401 $newrefund = strlen($prefund) > 0 ? floatval($prefund) : null;
2402 $roomsnum = $ord['roomsnum'];
2403
2404 // add room to existing booking
2405 $room_added = false;
2406 $padd_room_id = VikRequest::getInt('add_room_id', '', 'request');
2407 $padd_room_adults = VikRequest::getInt('add_room_adults', 2, 'request');
2408 $padd_room_children = VikRequest::getInt('add_room_children', 0, 'request');
2409 $padd_room_fname = VikRequest::getString('add_room_fname', '', 'request');
2410 $padd_room_lname = VikRequest::getString('add_room_lname', '', 'request');
2411 $padd_room_price = VikRequest::getFloat('add_room_price', 0, 'request');
2412 $paliq_add_room = VikRequest::getInt('aliq_add_room', 0, 'request');
2413 if ($padd_room_id > 0 && ($padd_room_adults + $padd_room_children) > 0) {
2414 // no need to re-validate the availability for this new room, as it was made via JS in the View.
2415 // increase the rooms number for later update, and insert the new room record
2416 $roomsnum++;
2417 $q = "INSERT INTO `#__vikbooking_ordersrooms` (`idorder`,`idroom`,`adults`,`children`,`t_first_name`,`t_last_name`,`cust_cost`,`cust_idiva`) VALUES(".$ord['id'].", ".$padd_room_id.", ".$padd_room_adults.", ".$padd_room_children.", ".$dbo->quote($padd_room_fname).", ".$dbo->quote($padd_room_lname).", ".($padd_room_price > 0 ? $dbo->quote($padd_room_price) : 'NULL').", ".($padd_room_price > 0 && !empty($paliq_add_room) ? $dbo->quote($paliq_add_room) : 'NULL').");";
2418 $dbo->setQuery($q);
2419 $dbo->execute();
2420 $room_added = true;
2421 }
2422
2423 // remove room from existing booking
2424 $room_removed = false;
2425 $room_removed_index = null;
2426 if ($prm_room_oid > 0 && $roomsnum > 1) {
2427 // check if the requested room record exists for removal
2428 $q = "SELECT * FROM `#__vikbooking_ordersrooms` WHERE `id`=".$prm_room_oid." AND `idorder`=".$ord['id'].";";
2429 $dbo->setQuery($q);
2430 $room_before_rm = $dbo->loadAssoc();
2431 if ($room_before_rm) {
2432 // decrease the rooms number for later update, and remove the requested room record
2433 $roomsnum--;
2434 // find the index of this room in the current list before removal
2435 foreach ($ordersrooms as $kor => $or) {
2436 if ($or['id'] == $prm_room_oid) {
2437 $room_removed_index = $kor;
2438 break;
2439 }
2440 }
2441 // go ahead with the deletion of the room record
2442 $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `id`=".$prm_room_oid." AND `idorder`=".$ord['id']." LIMIT 1;";
2443 $dbo->setQuery($q);
2444 $dbo->execute();
2445 $room_removed = $room_before_rm['idroom'];
2446 }
2447 }
2448
2449 if ($ord['split_stay'] && !empty($split_stay_data) && count($split_stay_checkins) && ($room_added !== false || $room_removed !== false)) {
2450 // split stay booking (even if only 1 room left) and one room was either added or removed: set new global stay dates
2451 if ($room_removed !== false && isset($room_removed_index) && isset($split_stay_checkins[$room_removed_index])) {
2452 // exclude the split stay dates of this room that was just removed
2453 unset($split_stay_checkins[$room_removed_index], $split_stay_checkouts[$room_removed_index]);
2454 }
2455 if (count($split_stay_checkins) && count($split_stay_checkouts)) {
2456 // if we still have rooms, and we should, update the booking global stay dates
2457 $first = min($split_stay_checkins);
2458 $second = max($split_stay_checkouts);
2459 $daysdiff = $av_helper->countNightsOfStay($first, $second);
2460 }
2461 }
2462
2463 // update booking's basic information (customer data, dates, tot paid, number of rooms, refund)
2464 $basic_booking = new stdClass;
2465 $basic_booking->id = $ord['id'];
2466 $basic_booking->custdata = $pcustdata;
2467 $basic_booking->days = (int)$daysdiff;
2468 $basic_booking->checkin = $first;
2469 $basic_booking->checkout = $second;
2470 if (strlen($newtotalpaid) > 0) {
2471 $basic_booking->totpaid = $newtotalpaid;
2472 }
2473 $basic_booking->roomsnum = (int)$roomsnum;
2474 if ($newrefund !== null) {
2475 $basic_booking->refund = $newrefund;
2476 }
2477 if ($ord['split_stay'] && $roomsnum < 2 && $room_removed !== false) {
2478 // there is no point in keep treating this reservation as a split stay
2479 $basic_booking->split_stay = 0;
2480 }
2481 $dbo->updateObject('#__vikbooking_orders', $basic_booking, 'id');
2482
2483 // Booking History log for new amount paid (payment update)
2484 if ($newtotalpaid > 0 && $newtotalpaid > (float)$ord['totpaid']) {
2485 $extra_data = new stdClass;
2486 $extra_data->amount_paid = ($newtotalpaid - (float)$ord['totpaid']);
2487 VikBooking::getBookingHistoryInstance()->setBid($ord['id'])->setExtraData($extra_data)->store('PU', JText::sprintf('VBOPREVAMOUNTPAID', VikBooking::numberFormat((float)$ord['totpaid'])));
2488 }
2489
2490 // booking history log for new refund amount
2491 if ($newrefund !== null && $newrefund != (float)$ord['refund']) {
2492 // update current refund value
2493 $ord['refund'] = $newrefund;
2494 // store event
2495 VikBooking::getBookingHistoryInstance()->setBid($ord['id'])->setExtraData(null)->store('RU', JText::sprintf('VBO_NEWREFUND_AMOUNT', VikBooking::numberFormat($ord['refund']), VikBooking::numberFormat($newrefund)));
2496 }
2497
2498 // update busy records
2499 if ($ord['status'] == 'confirmed') {
2500 $allbusy = [];
2501 if ($ord['split_stay'] && !empty($split_stay_data)) {
2502 // in case of split stay we need to update the busy records according to the nights selected
2503 foreach ($split_stay_data as $sps_k => $split_stay) {
2504 if (empty($split_stay['idbusy']) || empty($split_stay['checkin']) || empty($split_stay['checkout'])) {
2505 // missing data
2506 continue;
2507 }
2508 // get selected dates
2509 $room_checkin = VikBooking::getDateTimestamp($split_stay['checkin'], $pcheckinh, $pcheckinm);
2510 $room_checkout = VikBooking::getDateTimestamp($split_stay['checkout'], $pcheckouth, $pcheckoutm);
2511 $room_realback = $turnover_secs + $room_checkout;
2512 // update the exact record
2513 $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $room_checkin . ", `checkout`=" . $room_checkout . ", `realback`=" . $room_realback . " WHERE `id`=" . (int)$split_stay['idbusy'] . ";";
2514 $dbo->setQuery($q);
2515 $dbo->execute();
2516 }
2517 } else {
2518 // regularly update busy records for all rooms involved
2519 $q = "SELECT `b`.`id`,`b`.`idroom` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`id`=`ob`.`idbusy` AND `ob`.`idorder`=" . $ord['id'] . ";";
2520 $dbo->setQuery($q);
2521 $allbusy = $dbo->loadAssocList();
2522
2523 foreach ($allbusy as $bb) {
2524 $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $first . ", `checkout`=" . $second . ", `realback`=" . $realback . " WHERE `id`=" . $bb['id'] . ";";
2525 $dbo->setQuery($q);
2526 $dbo->execute();
2527 }
2528
2529 if (!$allbusy) {
2530 /**
2531 * If no existing busy records were fetched, it means we have some missing or broken
2532 * records in the database. Proceed with restoring them to occupy the room(s).
2533 *
2534 * @since 1.8.19 (J) - 1.8.9 (WP)
2535 */
2536 $dbo->setQuery(
2537 $dbo->getQuery(true)
2538 ->delete($dbo->qn('#__vikbooking_ordersbusy'))
2539 ->where($dbo->qn('idorder') . ' = ' . (int) $ord['id'])
2540 );
2541 $dbo->execute();
2542 foreach ($ordersrooms as $or) {
2543 if ($room_removed !== false && $or['id'] == $prm_room_oid) {
2544 continue;
2545 }
2546 $restoreBusyRecord = (object) [
2547 'idroom' => $or['idroom'],
2548 'checkin' => $first,
2549 'checkout' => $second,
2550 'realback' => $realback,
2551 ];
2552 $dbo->insertObject('#__vikbooking_busy', $restoreBusyRecord, 'id');
2553 if (!empty($restoreBusyRecord->id)) {
2554 $restoreBusyRelation = (object) [
2555 'idorder' => $ord['id'],
2556 'idbusy' => $restoreBusyRecord->id,
2557 ];
2558 $dbo->insertObject('#__vikbooking_ordersbusy', $restoreBusyRelation, 'id');
2559 }
2560 }
2561 }
2562 }
2563
2564 /**
2565 * Check if some rooms have modified stay dates different than the booking stay dates.
2566 *
2567 * @since 1.16.0 (J) - 1.6.0 (WP)
2568 */
2569 if (!$ord['split_stay'] && !$ord['closure'] && $ord['roomsnum'] > 1 && $ord['days'] > 1) {
2570 // load the occupied stay dates for each room in case they were modified
2571 $room_stay_records = $av_helper->loadSplitStayBusyRecords($ord['id']);
2572 // loop over all rooms to check the requested operations
2573 foreach ($ordersrooms as $ind => $or) {
2574 if (!VikRequest::getInt('room_modify_dates' . $ind, 0, 'request') || !isset($room_stay_records[$ind]) || empty($room_stay_records[$ind]['id'])) {
2575 // toggle is disabled or data is missing
2576 continue;
2577 }
2578 if (isset($room_modify_dates[$ind]) && !empty($room_modify_dates[$ind]['checkin']) && !empty($room_modify_dates[$ind]['checkout'])) {
2579 // calculate the check-in and check-out timestamps, we expect them to be different from the global booking dates
2580 $room_checkin = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkin'], $pcheckinh, $pcheckinm);
2581 $room_checkout = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkout'], $pcheckouth, $pcheckoutm);
2582 $room_realback = $turnover_secs + $room_checkout;
2583 // we don't need to check if the dates are different, we just update the record
2584 $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $room_checkin . ", `checkout`=" . $room_checkout . ", `realback`=" . $room_realback . " WHERE `id`=" . (int)$room_stay_records[$ind]['id'] . ";";
2585 $dbo->setQuery($q);
2586 $dbo->execute();
2587 // inject new room stay timestamps
2588 $ordersrooms[$ind]['modified_checkin'] = $room_checkin;
2589 $ordersrooms[$ind]['modified_checkout'] = $room_checkout;
2590 }
2591 }
2592 }
2593
2594 // add room to existing (confirmed) booking
2595 if ($room_added === true) {
2596 // add busy record for the new room unit
2597 $q = "INSERT INTO `#__vikbooking_busy` (`idroom`,`checkin`,`checkout`,`realback`) VALUES(".$padd_room_id.", ".$dbo->quote($first).", ".$dbo->quote($second).", ".$dbo->quote($realback).");";
2598 $dbo->setQuery($q);
2599 $dbo->execute();
2600 $newbusyid = $dbo->insertid();
2601 $q = "INSERT INTO `#__vikbooking_ordersbusy` (`idorder`,`idbusy`) VALUES(".$ord['id'].", ".(int)$newbusyid.");";
2602 $dbo->setQuery($q);
2603 $dbo->execute();
2604 }
2605
2606 // remove room from existing (confirmed) booking
2607 if ($room_removed !== false) {
2608 // remove busy record for the removed room
2609 if ($ord['split_stay'] && !empty($split_stay_data) && !empty($room_removed_index)) {
2610 // in case of split stay we want to remove the exact dates of the previously booked room
2611 if (count($room_stay_dates) && isset($room_stay_dates[$room_removed_index]) && $room_stay_dates[$room_removed_index]['idroom'] == $room_removed && !empty($room_stay_dates[$room_removed_index]['id'])) {
2612 // remove the exact records
2613 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`=" . $room_stay_dates[$room_removed_index]['id'] . " AND `idroom`=" . $room_removed . ";";
2614 $dbo->setQuery($q);
2615 $dbo->execute();
2616 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=" . $ord['id'] . " AND `idbusy`=" . $room_stay_dates[$room_removed_index]['id'] . ";";
2617 $dbo->setQuery($q);
2618 $dbo->execute();
2619 }
2620 } else {
2621 // regularly remove the first matching room
2622 foreach ($allbusy as $bb) {
2623 if ($bb['idroom'] == $room_removed) {
2624 // remove the first room with this ID that was booked
2625 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`=".$bb['id']." AND `idroom`=".$room_removed.";";
2626 $dbo->setQuery($q);
2627 $dbo->execute();
2628 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".$ord['id']." AND `idbusy`=".$bb['id'].";";
2629 $dbo->setQuery($q);
2630 $dbo->execute();
2631 break;
2632 }
2633 }
2634 }
2635 }
2636
2637 if ($ord['checkin'] != $first || $ord['checkout'] != $second || $room_added === true || $room_removed !== false) {
2638 // unset any previously booked room due to calendar sharing
2639 VikBooking::cleanSharedCalendarsBusy($ord['id']);
2640 // check if some of the rooms booked have shared calendars
2641 VikBooking::updateSharedCalendars($ord['id'], array(), $first, $second);
2642
2643 // invoke Channel Manager
2644 $vcm_autosync = VikBooking::vcmAutoUpdate();
2645 if ($vcm_autosync > 0) {
2646 $vcm_obj = VikBooking::getVcmInvoker();
2647 $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
2648 $sync_result = $vcm_obj->doSync();
2649 if ($sync_result === false) {
2650 $vcm_err = $vcm_obj->getError();
2651 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
2652 }
2653 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
2654 VikError::raiseNotice('', JText::translate('VBCHANNELMANAGERINVOKEASK').' <form action="index.php?option=com_vikbooking" method="post"><input type="hidden" name="option" value="com_vikbooking"/><input type="hidden" name="task" value="invoke_vcm"/><input type="hidden" name="stype" value="modify"/><input type="hidden" name="cid[]" value="'.$ord['id'].'"/><input type="hidden" name="origb" value="'.urlencode(json_encode($ord)).'"/><input type="hidden" name="returl" value="'.urlencode("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '')."&cid[]=".$ord['id']).'"/><button type="submit" class="btn btn-primary">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button></form>');
2655 }
2656 //
2657 }
2658 }
2659
2660 $upd_esit = JText::translate('RESUPDATED');
2661
2662 // update the room rates
2663 $isdue = 0;
2664 $tot_taxes = 0;
2665 $tot_city_taxes = 0;
2666 $tot_fees = 0;
2667 $tot_damage_dep = 0;
2668 $doup = true;
2669 $tars = array();
2670 $cust_costs = array();
2671 $rooms_costs_map = array();
2672 $arrpeople = array();
2673 foreach ($ordersrooms as $kor => $or) {
2674 // remove from existing booking
2675 if ($room_removed !== false) {
2676 if ($or['id'] == $prm_room_oid) {
2677 // do not consider this room for the calculation of the new total amount
2678 // we can unset this array for later use, because the channel manager has already been invoked.
2679 unset($ordersrooms[$kor]);
2680 continue;
2681 }
2682 }
2683
2684 // room index starting from 1
2685 $num = $kor + 1;
2686
2687 // default values to be considered
2688 $room_nights = $daysdiff;
2689 $room_checkin = $ord['checkin'];
2690 $room_checkout = $ord['checkout'];
2691 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom'] && !empty($room_stay_dates[$kor]['new_nights'])) {
2692 // overwrite default values in case of split-stay booking
2693 $room_nights = $room_stay_dates[$kor]['new_nights'];
2694 $room_checkin = $room_stay_dates[$kor]['new_checkin'];
2695 $room_checkout = $room_stay_dates[$kor]['new_checkout'];
2696 } elseif (!$ord['split_stay'] && ($room_modify_dates[$kor]['checkin_ts'] ?? null)) {
2697 // overwrite default values in case of multi-room reservation with different stay dates
2698 $room_nights = $room_modify_dates[$kor]['stay_nights'] ?? $room_nights;
2699 $room_checkin = $room_modify_dates[$kor]['checkin_ts'];
2700 $room_checkout = $room_modify_dates[$kor]['checkout_ts'];
2701 }
2702
2703 $padults = VikRequest::getString('adults' . $num, '', 'request');
2704 $pchildren = VikRequest::getString('children' . $num, '', 'request');
2705 $ppets = VikRequest::getInt('pets' . $num, 0, 'request');
2706 if (strlen($padults) || strlen($pchildren)) {
2707 $arrpeople[$num]['adults'] = (int)$padults;
2708 $arrpeople[$num]['children'] = (int)$pchildren;
2709 $arrpeople[$num]['pets'] = $ppets;
2710 }
2711 $ppriceid = VikRequest::getString('priceid'.$num, '', 'request');
2712 $polderpriceid = VikRequest::getString('olderpriceid'.$num, '', 'request');
2713 $ppkgid = VikRequest::getString('pkgid'.$num, '', 'request');
2714 $pcust_cost = VikRequest::getString('cust_cost'.$num, '', 'request');
2715 $paliq = VikRequest::getString('aliq'.$num, '', 'request');
2716 $pcust_cpolicy_id = VikRequest::getInt('cust_cpolicy_id'.$num, 0, 'request');
2717 if ($is_package === true && !empty($ppkgid)) {
2718 $pkg_cost = $or['cust_cost'];
2719 $pkg_idiva = $or['cust_idiva'];
2720 $pkg_info = VikBooking::getPackage($ppkgid);
2721 if (is_array($pkg_info) && count($pkg_info) > 0) {
2722 $use_adults = array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num]) ? $arrpeople[$num]['adults'] : $or['adults'];
2723 $pkg_cost = $pkg_info['pernight_total'] == 1 ? ($pkg_info['cost'] * $room_nights) : $pkg_info['cost'];
2724 $pkg_cost = $pkg_info['perperson'] == 1 ? ($pkg_cost * ($use_adults > 0 ? $use_adults : 1)) : $pkg_cost;
2725 $pkg_cost = VikBooking::sayPackagePlusIva($pkg_cost, $pkg_info['idiva']);
2726 }
2727 $cust_costs[$num] = array('pkgid' => $ppkgid, 'cust_cost' => $pkg_cost, 'aliq' => $pkg_idiva);
2728 $isdue += $pkg_cost;
2729 $cost_minus_tax = VikBooking::sayPackageMinusIva($pkg_cost, $pkg_idiva);
2730 $tot_taxes += ($pkg_cost - $cost_minus_tax);
2731 continue;
2732 }
2733 if (empty($ppriceid) && !empty($pcust_cost) && floatval($pcust_cost) > 0) {
2734 $cust_costs[$num] = [
2735 'cust_cost' => $pcust_cost,
2736 'aliq' => $paliq,
2737 'cust_cpolicy_id' => $pcust_cpolicy_id,
2738 ];
2739 $cost_after_tax = VikBooking::sayPackagePlusIva((float)$pcust_cost, (int)$paliq);
2740 $isdue += $cost_after_tax;
2741 $cost_minus_tax = VikBooking::sayPackageMinusIva((float)$pcust_cost, (int)$paliq);
2742 $tot_taxes += ($cost_after_tax - $cost_minus_tax);
2743 continue;
2744 }
2745
2746 // load room rates for the requested rate plan and nights
2747 $q = "SELECT * FROM `#__vikbooking_dispcost` WHERE `idroom`=" . (int)$or['idroom'] . " AND `days`=" . $room_nights . " AND `idprice`=" . (int)$ppriceid . ";";
2748 $dbo->setQuery($q);
2749 $tar = $dbo->loadAssocList();
2750 if (!$tar) {
2751 $doup = false;
2752 break;
2753 }
2754
2755 /**
2756 * The current price may be different from the price paid at the time of booking.
2757 * Check whether it has been asked to keep the old price of the time of booking.
2758 *
2759 * @since 1.13.0 (J) - 1.3.0 (WP)
2760 */
2761 $old_price_used = false;
2762 if (!empty($polderpriceid)) {
2763 $older_info = explode(':', $polderpriceid);
2764 if ((int)$older_info[0] == (int)$ppriceid) {
2765 $old_price = isset($older_info[1]) ? (float)$older_info[1] : 0;
2766 if ($old_price > 0) {
2767 // we override the 'cost' property of the tar array by taking the previous cost
2768 $old_price_used = true;
2769 $tar[0]['cost'] = $old_price;
2770 }
2771 }
2772 }
2773
2774 if (!$old_price_used) {
2775 // apply seasonal rates
2776 $tar = VikBooking::applySeasonsRoom($tar, $room_checkin, $room_checkout);
2777 }
2778
2779 // different usage
2780 if (!$old_price_used && $or['fromadult'] <= $or['adults'] && $or['toadult'] >= $or['adults']) {
2781 // apply OBP rules
2782 $tar = VBORoomHelper::getInstance()->applyOBPRules($tar, $or, $or['adults']);
2783 }
2784
2785 $cost_plus_tax = VikBooking::sayCostPlusIva($tar[0]['cost'], $tar[0]['idprice']);
2786 $isdue += $cost_plus_tax;
2787 if ($cost_plus_tax == $tar[0]['cost']) {
2788 $cost_minus_tax = VikBooking::sayCostMinusIva($tar[0]['cost'], $tar[0]['idprice']);
2789 $tot_taxes += ($tar[0]['cost'] - $cost_minus_tax);
2790 } else {
2791 $tot_taxes += ($cost_plus_tax - $tar[0]['cost']);
2792 }
2793 $tars[$num] = $tar;
2794 $rooms_costs_map[$num] = $tar[0]['cost'];
2795 }
2796
2797 if ($doup === true) {
2798 if ($room_added === true) {
2799 // add room to existing booking may require to increase the total amount, and taxes
2800 $padd_room_price = VikRequest::getFloat('add_room_price', 0, 'request');
2801 $paliq_add_room = VikRequest::getInt('aliq_add_room', 0, 'request');
2802 if (!empty($padd_room_price) && floatval($padd_room_price) > 0) {
2803 $isdue += (float)$padd_room_price;
2804 $cost_minus_tax = VikBooking::sayPackageMinusIva((float)$padd_room_price, (int)$paliq_add_room);
2805 $tot_taxes += ((float)$padd_room_price - $cost_minus_tax);
2806 }
2807 }
2808
2809 // load options
2810 $q = "SELECT * FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
2811 $dbo->setQuery($q);
2812 $toptionals = $dbo->loadAssocList();
2813
2814 foreach ($ordersrooms as $kor => $or) {
2815 $num = $kor + 1;
2816
2817 // default values to be considered
2818 $room_nights = $daysdiff;
2819 $room_checkin = $ord['checkin'];
2820 $room_checkout = $ord['checkout'];
2821 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom'] && !empty($room_stay_dates[$kor]['new_nights'])) {
2822 // overwrite default values in case of split-stay booking
2823 $room_nights = $room_stay_dates[$kor]['new_nights'];
2824 $room_checkin = $room_stay_dates[$kor]['new_checkin'];
2825 $room_checkout = $room_stay_dates[$kor]['new_checkout'];
2826 } elseif (!$ord['split_stay'] && ($room_modify_dates[$kor]['checkin_ts'] ?? null)) {
2827 // overwrite default values in case of multi-room reservation with different stay dates
2828 $room_nights = $room_modify_dates[$kor]['stay_nights'] ?? $room_nights;
2829 $room_checkin = $room_modify_dates[$kor]['checkin_ts'];
2830 $room_checkout = $room_modify_dates[$kor]['checkout_ts'];
2831 }
2832
2833 $pt_first_name = VikRequest::getString('t_first_name'.$num, '', 'request');
2834 $pt_last_name = VikRequest::getString('t_last_name'.$num, '', 'request');
2835 $wop = "";
2836
2837 foreach ($toptionals as $opt) {
2838 // option params
2839 $opt_params = !empty($opt['oparams']) ? json_decode($opt['oparams'], true) : [];
2840 $opt_params = is_array($opt_params) ? $opt_params : [];
2841 if (!empty($opt['ageintervals']) && ($or['children'] > 0 || isset($arrpeople[$num]['children']))) {
2842 $tmpvar = VikRequest::getInt('optid'.$num.$opt['id'], []);
2843 if (is_array($tmpvar) && $tmpvar && ($arrpeople[$num]['children'] ?? 0)) {
2844 $opt['quan'] = 1;
2845 $optagenames = VikBooking::getOptionIntervalsAges($opt['ageintervals']);
2846 $optagepcent = VikBooking::getOptionIntervalsPercentage($opt['ageintervals']);
2847 $optageovrct = VikBooking::getOptionIntervalChildOverrides($opt, (isset($arrpeople[$num]) ? $arrpeople[$num]['adults'] : 0), (isset($arrpeople[$num]) ? $arrpeople[$num]['children'] : 0));
2848 $optorigname = $opt['name'];
2849 foreach ($tmpvar as $child_num => $chvar) {
2850 $ageintervals_child_string = isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $opt['ageintervals'];
2851 $optagecosts = VikBooking::getOptionIntervalsCosts($ageintervals_child_string);
2852 $optorigcost = $optagecosts[($chvar - 1)];
2853 $tmp_room_cost = 0;
2854 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 1) {
2855 // percentage value of the adults tariff
2856 if ($is_package !== true && array_key_exists($num, $tars)) {
2857 // type of price
2858 $tmp_room_cost = $tars[$num][0]['cost'];
2859 $optorigcost = $tars[$num][0]['cost'] * $optagecosts[($chvar - 1)] / 100;
2860 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2861 // package
2862 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2863 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2864 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2865 // custom rate + custom tax rate
2866 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2867 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2868 }
2869 } elseif (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 2) {
2870 // percentage value of room base cost
2871 if ($is_package !== true && array_key_exists($num, $tars)) {
2872 // type of price
2873 $usecost = isset($tars[$num][0]['room_base_cost']) ? $tars[$num][0]['room_base_cost'] : $tars[$num][0]['cost'];
2874 $tmp_room_cost = $usecost;
2875 $optorigcost = $usecost * $optagecosts[($chvar - 1)] / 100;
2876 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2877 // package
2878 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2879 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2880 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2881 // custom rate + custom tax rate
2882 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2883 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2884 }
2885 }
2886 $opt['cost'] = $optorigcost;
2887 $opt['name'] = $optorigname.' ('.$optagenames[($chvar - 1)].')';
2888 $opt['chageintv'] = $chvar;
2889 $wop.=$opt['id'].":".$opt['quan']."-".$chvar.";";
2890 $realcost = (intval($opt['perday']) == 1 ? ($opt['cost'] * $room_nights * $opt['quan']) : ($opt['cost'] * $opt['quan']));
2891 if (!empty($opt['maxprice']) && $opt['maxprice'] > 0 && $realcost > $opt['maxprice']) {
2892 $realcost = $opt['maxprice'];
2893 }
2894
2895 /**
2896 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
2897 *
2898 * @since 1.17.7 (J) - 1.7.7 (WP)
2899 */
2900 $custom_calc_booking = array_merge($ord, ['days' => $room_nights]);
2901 $custom_calc_booking_room = array_merge($or, ($arrpeople[$num] ?? []), ($tmp_room_cost ? ['room_cost' => $tmp_room_cost] : []));
2902 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$opt, $custom_calc_booking, $custom_calc_booking_room]);
2903 if ($custom_calculation) {
2904 $realcost = (float) $custom_calculation[0];
2905 }
2906
2907 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $opt['idiva']);
2908 if ($opt['is_citytax'] == 1) {
2909 $tot_city_taxes += $tmpopr;
2910 } elseif ($opt['is_fee'] == 1) {
2911 $tot_fees += $tmpopr;
2912 } elseif ($opt_params['damagedep'] ?? 0) {
2913 $tot_damage_dep += $tmpopr;
2914 }
2915 // VBO 1.11 - always calculate the amount of tax no matter if this is already a tax or a fee
2916 if ($tmpopr == $realcost) {
2917 $opt_minus_iva = VikBooking::sayOptionalsMinusIva($realcost, $opt['idiva']);
2918 $tot_taxes += ($realcost - $opt_minus_iva);
2919 } else {
2920 $tot_taxes += ($tmpopr - $realcost);
2921 }
2922 //
2923 $isdue += $tmpopr;
2924 }
2925 }
2926 } else {
2927 $tmpvar = VikRequest::getString('optid'.$num.$opt['id'], '', 'request');
2928 if (is_array($tmpvar)) {
2929 // prevent errors for unexpected option configuration, probably missing age intervals
2930 continue;
2931 }
2932 $tmp_room_cost = 0;
2933 // options forced per child fix, no age intervals, like children tourist taxes
2934 $forcedquan = 1;
2935 $forceperday = false;
2936 $forceperchild = false;
2937 if (intval($opt['forcesel']) == 1 && strlen($opt['forceval']) > 0 && strlen($tmpvar) > 0) {
2938 $forceparts = explode("-", $opt['forceval']);
2939 $forcedquan = intval($forceparts[0]);
2940 $forceperday = intval($forceparts[1]) == 1 ? true : false;
2941 $forceperchild = intval($forceparts[2]) == 1 ? true : false;
2942 $tmpvar = $forcedquan;
2943 $tmpvar = $forceperchild === true && array_key_exists($num, $arrpeople) && array_key_exists('children', $arrpeople[$num]) ? ($tmpvar * $arrpeople[$num]['children']) : $tmpvar;
2944 }
2945 //
2946 if (!empty($tmpvar)) {
2947 $wop .= $opt['id'].":".$tmpvar.";";
2948 // options percentage cost of the room total fee
2949 if ($is_package !== true && array_key_exists($num, $tars)) {
2950 // type of price
2951 $tmp_room_cost = $tars[$num][0]['cost'];
2952 $deftar_basecosts = $tars[$num][0]['cost'];
2953 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2954 // package
2955 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2956 $deftar_basecosts = $cust_costs[$num]['cust_cost'];
2957 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2958 // custom rate + custom tax rate
2959 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2960 $deftar_basecosts = $cust_costs[$num]['cust_cost'];
2961 }
2962 $opt['cost'] = (int)$opt['pcentroom'] ? ($deftar_basecosts * $opt['cost'] / 100) : $opt['cost'];
2963 //
2964 $realcost = (intval($opt['perday']) == 1 ? ($opt['cost'] * $room_nights * $tmpvar) : ($opt['cost'] * $tmpvar));
2965 if (!empty($opt['maxprice']) && $opt['maxprice'] > 0 && $realcost > $opt['maxprice']) {
2966 $realcost = $opt['maxprice'];
2967 if (intval($opt['hmany']) == 1 && intval($tmpvar) > 1) {
2968 $realcost = $opt['maxprice'] * $tmpvar;
2969 }
2970 }
2971 if ($opt['perperson'] == 1) {
2972 $num_adults = array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num]) ? $arrpeople[$num]['adults'] : 1;
2973 $realcost = $realcost * $num_adults;
2974 }
2975
2976 /**
2977 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
2978 *
2979 * @since 1.17.7 (J) - 1.7.7 (WP)
2980 */
2981 $custom_calc_booking = array_merge($ord, ['days' => $room_nights]);
2982 $custom_calc_booking_room = array_merge($or, ($arrpeople[$num] ?? []), ($tmp_room_cost ? ['room_cost' => $tmp_room_cost] : []));
2983 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$opt, $custom_calc_booking, $custom_calc_booking_room]);
2984 if ($custom_calculation) {
2985 $realcost = (float) $custom_calculation[0];
2986 }
2987
2988 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $opt['idiva']);
2989 if ($opt['is_citytax'] == 1) {
2990 $tot_city_taxes += $tmpopr;
2991 } elseif ($opt['is_fee'] == 1) {
2992 $tot_fees += $tmpopr;
2993 } elseif ($opt_params['damagedep'] ?? 0) {
2994 $tot_damage_dep += $tmpopr;
2995 }
2996 // VBO 1.11 - always calculate the amount of tax no matter if this is already a tax or a fee
2997 if ($tmpopr == $realcost) {
2998 $opt_minus_iva = VikBooking::sayOptionalsMinusIva($realcost, $opt['idiva']);
2999 $tot_taxes += ($realcost - $opt_minus_iva);
3000 } else {
3001 $tot_taxes += ($tmpopr - $realcost);
3002 }
3003 //
3004 $isdue += $tmpopr;
3005 }
3006 }
3007 }
3008
3009 $upd_fields = array();
3010 if ($is_package !== true && array_key_exists($num, $tars)) {
3011 // type of price
3012 $upd_fields[] = "`idtar`='".$tars[$num][0]['id']."'";
3013 $upd_fields[] = "`cust_cost`=NULL";
3014 $upd_fields[] = "`cust_idiva`=NULL";
3015 $upd_fields[] = "`cust_cpolicy_id`=NULL";
3016 $upd_fields[] = "`room_cost`=".(array_key_exists($num, $rooms_costs_map) ? $dbo->quote($rooms_costs_map[$num]) : "NULL");
3017 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
3018 // packages do not update name or cost, just set again the same package ID to avoid risks of empty upd_fields to update
3019 $upd_fields[] = "`idtar`=NULL";
3020 $upd_fields[] = "`pkg_id`='".$cust_costs[$num]['pkgid']."'";
3021 $upd_fields[] = "`cust_cost`='".$cust_costs[$num]['cust_cost']."'";
3022 $upd_fields[] = "`cust_idiva`='".$cust_costs[$num]['aliq']."'";
3023 $upd_fields[] = "`cust_cpolicy_id`='" . (int) ($cust_costs[$num]['cust_cpolicy_id'] ?? 0) . "'";
3024 $upd_fields[] = "`room_cost`=NULL";
3025 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
3026 // custom rate + custom tax rate
3027 $upd_fields[] = "`idtar`=NULL";
3028 $upd_fields[] = "`cust_cost`='".$cust_costs[$num]['cust_cost']."'";
3029 $upd_fields[] = "`cust_idiva`='".$cust_costs[$num]['aliq']."'";
3030 $upd_fields[] = "`cust_cpolicy_id`='" . (int) ($cust_costs[$num]['cust_cpolicy_id'] ?? 0) . "'";
3031 $upd_fields[] = "`room_cost`=NULL";
3032 // inject new room price
3033 $ordersrooms[$kor]['modified_price'] = $cust_costs[$num]['cust_cost'];
3034 }
3035 if ($toptionals) {
3036 $upd_fields[] = "`optionals`='".$wop."'";
3037 }
3038 if (!empty($pt_first_name) || !empty($pt_last_name)) {
3039 $upd_fields[] = "`t_first_name`=".$dbo->quote($pt_first_name);
3040 $upd_fields[] = "`t_last_name`=".$dbo->quote($pt_last_name);
3041 }
3042 if (array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num])) {
3043 $upd_fields[] = "`adults`=".intval($arrpeople[$num]['adults']);
3044 $upd_fields[] = "`children`=".intval($arrpeople[$num]['children']);
3045 if (isset($arrpeople[$num]['pets'])) {
3046 $upd_fields[] = "`pets`=" . $arrpeople[$num]['pets'];
3047 }
3048 }
3049
3050 /**
3051 * Meal plans at room-reservation level.
3052 *
3053 * @since 1.16.1 (J) - 1.6.1 (WP)
3054 */
3055 $pmealplans = VikRequest::getVar('mealplan' . $num, []);
3056 $upd_fields[] = "`meals`=" . ($pmealplans ? $dbo->q(json_encode($pmealplans)) : 'NULL');
3057
3058 // calculate the extra costs and increase taxes + isdue
3059 $extracosts_arr = array();
3060 if (count($pextracn) && isset($pextracn[$num]) && count($pextracn[$num])) {
3061 foreach ($pextracn[$num] as $eck => $ecn) {
3062 if ($ecn && array_key_exists($eck, $pextracc[$num]) && is_numeric($pextracc[$num][$eck])) {
3063 $ecidtax = array_key_exists($eck, $pextractx[$num]) && intval($pextractx[$num][$eck]) > 0 ? (int)$pextractx[$num][$eck] : '';
3064 $extracosts_arr[] = array(
3065 'name' => $ecn,
3066 'cost' => (float)$pextracc[$num][$eck],
3067 'idtax' => $ecidtax,
3068 'type' => isset($pextractype[$num][$eck]) ? $pextractype[$num][$eck] : '',
3069 'fk' => isset($pextracfk[$num][$eck]) ? (string)$pextracfk[$num][$eck] : '',
3070 'data' => isset($pextracdata[$num][$eck]) ? json_decode($pextracdata[$num][$eck]) : null,
3071 );
3072 $ecplustax = !empty($ecidtax) ? VikBooking::sayOptionalsPlusIva((float)$pextracc[$num][$eck], $ecidtax) : (float)$pextracc[$num][$eck];
3073 $ecminustax = !empty($ecidtax) ? VikBooking::sayOptionalsMinusIva((float)$pextracc[$num][$eck], $ecidtax) : (float)$pextracc[$num][$eck];
3074 $ectottax = (float)$pextracc[$num][$eck] - $ecminustax;
3075 $isdue += $ecplustax;
3076 $tot_taxes += $ectottax;
3077 }
3078 }
3079 }
3080
3081 if ($extracosts_arr) {
3082 $upd_fields[] = "`extracosts`=".$dbo->quote(json_encode($extracosts_arr));
3083 } else {
3084 $upd_fields[] = "`extracosts`=NULL";
3085 }
3086
3087 if ($upd_fields) {
3088 $q = "UPDATE `#__vikbooking_ordersrooms` SET ".implode(', ', $upd_fields)." WHERE `idorder`=".$ord['id']." AND `idroom`='".$or['idroom']."' AND `id`='".$or['id']."';";
3089 $dbo->setQuery($q);
3090 $dbo->execute();
3091 }
3092 }
3093
3094 // update split stay transient record if not confirmed booking
3095 if ($ord['split_stay'] && $ord['status'] != 'confirmed' && !empty($room_stay_dates) && !empty($split_stay_data)) {
3096 /**
3097 * Important: if no rates have been selected for all rooms, we won't enter this inner statement.
3098 * It is necessary to select a rate plan for each room in order to update the split stay data.
3099 */
3100 $new_room_stay_dates = [];
3101 foreach ($room_stay_dates as $kor => $room_stay_info) {
3102 // clone the current information
3103 $clean_room_stay_info = $room_stay_info;
3104 // set new stay values
3105 if (!empty($clean_room_stay_info['checkin_ts'])) {
3106 $clean_room_stay_info['checkin_ts'] = $clean_room_stay_info['new_checkin'];
3107 $clean_room_stay_info['checkout_ts'] = $clean_room_stay_info['new_checkout'];
3108 } else {
3109 $clean_room_stay_info['checkin'] = $clean_room_stay_info['new_checkin'];
3110 $clean_room_stay_info['checkout'] = $clean_room_stay_info['new_checkout'];
3111 }
3112 $clean_room_stay_info['nights'] = $clean_room_stay_info['new_nights'];
3113 // clean up unnecessary keys
3114 unset($clean_room_stay_info['new_checkin'], $clean_room_stay_info['new_checkout'], $clean_room_stay_info['new_nights']);
3115 // push new array info
3116 $new_room_stay_dates[$kor] = $clean_room_stay_info;
3117 }
3118 // update configuration record
3119 VBOFactory::getConfig()->set('split_stay_' . $ord['id'], json_encode($new_room_stay_dates));
3120 }
3121
3122 // make sure to re-apply the discount with the coupon code
3123 if ($ord['coupon']) {
3124 $expcoupon = explode(";", $ord['coupon']);
3125 $isdue -= $expcoupon[1];
3126 }
3127
3128 // make sure to apply any previously refunded amount
3129 if ($ord['refund'] > 0) {
3130 $isdue -= $ord['refund'];
3131 }
3132
3133 // update totals
3134 $q = "UPDATE `#__vikbooking_orders` SET `total`='".$isdue."', `tot_taxes`='".$tot_taxes."', `tot_city_taxes`='".$tot_city_taxes."', `tot_fees`='".$tot_fees."', `tot_damage_dep`='".$tot_damage_dep."' WHERE `id`=".$ord['id'].";";
3135 $dbo->setQuery($q);
3136 $dbo->execute();
3137 $upd_esit = JText::translate('VBORESRATESUPDATED');
3138
3139 // Customer Booking
3140 if ($ord['status'] == 'confirmed') {
3141 $q = "SELECT `idcustomer` FROM `#__vikbooking_customers_orders` WHERE `idorder`=".$ord['id'].";";
3142 $dbo->setQuery($q);
3143 $customer_id = $dbo->loadResult();
3144 if ($customer_id) {
3145 $cpin = VikBooking::getCPinIstance();
3146 $cpin->is_admin = true;
3147 $cpin->updateBookingCommissions($ord['id'], $customer_id);
3148 }
3149 }
3150
3151 /**
3152 * Check for any OTA reporting action.
3153 *
3154 * @since 1.16.8 (J) - 1.6.8 (WP)
3155 */
3156 if (class_exists('VCMOtaReporting') && VCMOtaReporting::getInstance($ord)->stayChangeAllowed()) {
3157 // check if an OTA reporting action was selected
3158 $ota_stay_change_data = [];
3159 $ota_stay_change_all = $app->input->getInt('ota_stay_change_all', 0);
3160 foreach ($ordersrooms as $kor => $or) {
3161 $ota_stay_change_room = [];
3162 if ($ota_stay_change_all) {
3163 // set room data for stay change
3164 $ota_stay_change_room = [
3165 'idroom' => $or['idroom'],
3166 'checkin' => date('Y-m-d', $first),
3167 'checkout' => date('Y-m-d', $second),
3168 ];
3169 if (isset($or['modified_price'])) {
3170 $ota_stay_change_room['price'] = $or['modified_price'];
3171 }
3172 } elseif ($app->input->getInt('ota_stay_change_room_' . $kor, 0) && !empty($or['modified_checkin']) && !empty($or['modified_checkout'])) {
3173 // set room index data for stay change
3174 $ota_stay_change_room = [
3175 'idroom' => $or['idroom'],
3176 'index' => $kor,
3177 'checkin' => date('Y-m-d', $or['modified_checkin']),
3178 'checkout' => date('Y-m-d', $or['modified_checkout']),
3179 ];
3180 if (isset($or['modified_price'])) {
3181 $ota_stay_change_room['price'] = $or['modified_price'];
3182 }
3183 }
3184 if ($ota_stay_change_room) {
3185 // push room data for stay change
3186 $ota_stay_change_data[] = $ota_stay_change_room;
3187 }
3188 }
3189
3190 if ($ota_stay_change_data) {
3191 // notify the OTA through Vik Channel Manager
3192 $ota_reporting = VCMOtaReporting::getInstance();
3193 $ota_result = $ota_reporting->notifyStayChange($ota_stay_change_data);
3194 if (!$ota_result) {
3195 // enqueue error message
3196 $app->enqueueMessage($ota_reporting->getError(), 'error');
3197 }
3198 }
3199 }
3200 }
3201
3202 // Booking History
3203 $history_descr = "({$user->name}) " . VikBooking::getLogBookingModification($ord, $room_stay_dates);
3204 if (!$opertwounits && $forcebooking) {
3205 $history_descr .= "\n" . JText::translate('VBO_FORCED_BOOKDATES');
3206 }
3207 VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', $history_descr);
3208
3209 // enqueue result message
3210 $app->enqueueMessage($upd_esit);
3211 } else {
3212 VikError::raiseWarning('', JText::translate('VBROOMNOTRIT')." ".date($df.' H:i', $first)." ".JText::translate('VBROOMNOTCONSTO')." ".date($df.' H:i', $second));
3213 $allow_force = 1;
3214 $app->enqueueMessage(JText::translate('VBO_BOOKING_SHOULDFORCE'), 'notice');
3215 }
3216
3217 if ($callback == 'geninvoices') {
3218 $app->redirect("index.php?option=com_vikbooking&task=orders&cid[]=".$ord['id']."&confirmgen=1");
3219 } else {
3220 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').(isset($allow_force) ? '&canforce=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
3221 }
3222 }
3223
3224 public function removebusy()
3225 {
3226 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
3227 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
3228 }
3229
3230 $dbo = JFactory::getDbo();
3231 $app = JFactory::getApplication();
3232
3233 $user = JFactory::getUser();
3234 $config = VBOFactory::getConfig();
3235
3236 $prev_conf_ids = [];
3237 $pidorder = VikRequest::getInt('idorder', 0, 'request');
3238 $pgoto = VikRequest::getString('goto', '', 'request');
3239
3240 $purged = false;
3241
3242 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder;
3243 $dbo->setQuery($q, 0, 1);
3244 $row = $dbo->loadAssoc();
3245
3246 // check for any cancellation constraints
3247 $canc_denied = false;
3248 if ($row && class_exists('VCMFeesCancellation')) {
3249 // let VCM detect if there are any constraints for the cancellation
3250 $canc_denied = VCMFeesCancellation::getInstance($row, $anew = true)->isBookingConstrained();
3251 if ($canc_denied) {
3252 // set error message
3253 $canc_deny_error = VCMFeesCancellation::getInstance()->getError();
3254 if ($canc_deny_error) {
3255 $app->enqueueMessage($canc_deny_error, 'error');
3256 }
3257 }
3258 }
3259
3260 if ($row && !$canc_denied) {
3261 // set status to cancelled
3262 if ($row['status'] != 'cancelled') {
3263 $q = "UPDATE `#__vikbooking_orders` SET `status`='cancelled' WHERE `id`=".(int)$row['id'].";";
3264 $dbo->setQuery($q);
3265 $dbo->execute();
3266 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($row['id']) . ";";
3267 $dbo->setQuery($q);
3268 $dbo->execute();
3269 if ($row['status'] == 'confirmed') {
3270 $prev_conf_ids[] = $row['id'];
3271 }
3272 // Booking History
3273 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('CB', "({$user->name})");
3274 }
3275
3276 /**
3277 * In case of pending bookings being cancelled, schedule the release through VCM.
3278 *
3279 * @since 1.18.8 (J) - 1.8.8 (WP)
3280 */
3281 if ($row['status'] == 'standby' && method_exists('VCMRequestAvailability', 'setForRelease')) {
3282 // let the CM schedule the release of the involved and unconfirmed booking IDs, if needed
3283 VCMRequestAvailability::getInstance()->setForRelease([$row['id']]);
3284 }
3285
3286 // free records up
3287 $q = "SELECT * FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
3288 $dbo->setQuery($q);
3289 $ordbusy = $dbo->loadAssocList();
3290 if ($ordbusy) {
3291 foreach ($ordbusy as $ob) {
3292 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`='".$ob['idbusy']."';";
3293 $dbo->setQuery($q);
3294 $dbo->execute();
3295 }
3296 }
3297
3298 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
3299 $dbo->setQuery($q);
3300 $dbo->execute();
3301
3302 // check for purge removal
3303 if ($row['status'] == 'cancelled') {
3304 $q = "DELETE FROM `#__vikbooking_customers_orders` WHERE `idorder`=" . intval($row['id']) . ";";
3305 $dbo->setQuery($q);
3306 $dbo->execute();
3307 $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `idorder`=".(int)$row['id'].";";
3308 $dbo->setQuery($q);
3309 $dbo->execute();
3310 $q = "DELETE FROM `#__vikbooking_orderhistory` WHERE `idorder`=".(int)$row['id'].";";
3311 $dbo->setQuery($q);
3312 $dbo->execute();
3313 $q = "DELETE FROM `#__vikbooking_orders` WHERE `id`=".(int)$row['id'].";";
3314 $dbo->setQuery($q);
3315 $dbo->execute();
3316 // in case of split stay booking, remove the transient
3317 if ($row['split_stay']) {
3318 $config->remove('split_stay_' . $row['id']);
3319 }
3320 // turn flag on
3321 $purged = true;
3322 }
3323
3324 // enqueue message
3325 $app->enqueueMessage(JText::translate('VBMESSDELBUSY'));
3326 }
3327
3328 if ($prev_conf_ids) {
3329 $prev_conf_ids_str = '';
3330 foreach ($prev_conf_ids as $prev_id) {
3331 $prev_conf_ids_str .= '&cid[]='.$prev_id;
3332 }
3333 //Invoke Channel Manager
3334 $vcm_autosync = VikBooking::vcmAutoUpdate();
3335 if ($vcm_autosync > 0) {
3336 $vcm_obj = VikBooking::getVcmInvoker();
3337 $vcm_obj->setOids($prev_conf_ids)->setSyncType('cancel');
3338 $sync_result = $vcm_obj->doSync();
3339 if ($sync_result === false) {
3340 $vcm_err = $vcm_obj->getError();
3341 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
3342 }
3343 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
3344 $vcm_sync_url = 'index.php?option=com_vikbooking&task=invoke_vcm&stype=cancel'.$prev_conf_ids_str.'&returl='.urlencode('index.php?option=com_vikbooking&task=orders');
3345 VikError::raiseNotice('', JText::translate('VBCHANNELMANAGERINVOKEASK').' <button type="button" class="btn btn-primary" onclick="document.location.href=\''.$vcm_sync_url.'\';">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button>');
3346 }
3347 //
3348 }
3349
3350 if ($pgoto == 'overv') {
3351 $app->redirect("index.php?option=com_vikbooking&task=overv");
3352 } elseif (!$purged) {
3353 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $pidorder);
3354 } else {
3355 $app->redirect("index.php?option=com_vikbooking&task=orders");
3356 }
3357
3358 $app->close();
3359 }
3360
3361 public function unlockrecords()
3362 {
3363 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
3364 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
3365 }
3366
3367 $ids = VikRequest::getVar('cid', array(0));
3368 if (@count($ids)) {
3369 $dbo = JFactory::getDBO();
3370 foreach ($ids as $d) {
3371 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `id`=".$dbo->quote($d).";";
3372 $dbo->setQuery($q);
3373 $dbo->execute();
3374 }
3375 }
3376 $mainframe = JFactory::getApplication();
3377 $mainframe->redirect("index.php?option=com_vikbooking");
3378 }
3379
3380 public function sortoption() {
3381 if (!JSession::checkToken('get')) {
3382 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3383 }
3384 $sortid = VikRequest::getVar('cid', array(0));
3385 $pmode = VikRequest::getString('mode', '', 'request');
3386 $dbo = JFactory::getDBO();
3387 $mainframe = JFactory::getApplication();
3388 if (!empty($pmode)) {
3389 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
3390 $dbo->setQuery($q);
3391 $dbo->execute();
3392 $totr = $dbo->getNumRows();
3393 if ($totr > 1) {
3394 $data = $dbo->loadAssocList();
3395 if ($pmode == "up") {
3396 foreach ($data as $v) {
3397 if ($v['id'] == $sortid[0]) {
3398 $y = $v['ordering'];
3399 }
3400 }
3401 if ($y && $y > 1) {
3402 $vik = $y - 1;
3403 $found = false;
3404 foreach ($data as $v) {
3405 if (intval($v['ordering']) == intval($vik)) {
3406 $found = true;
3407 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3408 $dbo->setQuery($q);
3409 $dbo->execute();
3410 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3411 $dbo->setQuery($q);
3412 $dbo->execute();
3413 break;
3414 }
3415 }
3416 if (!$found) {
3417 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3418 $dbo->setQuery($q);
3419 $dbo->execute();
3420 }
3421 }
3422 } elseif ($pmode == "down") {
3423 foreach ($data as $v) {
3424 if ($v['id'] == $sortid[0]) {
3425 $y = $v['ordering'];
3426 }
3427 }
3428 if ($y) {
3429 $vik = $y + 1;
3430 $found = false;
3431 foreach ($data as $v) {
3432 if (intval($v['ordering']) == intval($vik)) {
3433 $found = true;
3434 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3435 $dbo->setQuery($q);
3436 $dbo->execute();
3437 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3438 $dbo->setQuery($q);
3439 $dbo->execute();
3440 break;
3441 }
3442 }
3443 if (!$found) {
3444 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3445 $dbo->setQuery($q);
3446 $dbo->execute();
3447 }
3448 }
3449 }
3450 }
3451 $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
3452 } else {
3453 $mainframe->redirect("index.php?option=com_vikbooking");
3454 }
3455 }
3456
3457 public function sortpayment() {
3458 if (!JSession::checkToken('get')) {
3459 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3460 }
3461 $cid = VikRequest::getVar('cid', array(0));
3462 $sortid = $cid[0];
3463 $dbo = JFactory::getDBO();
3464 $mainframe = JFactory::getApplication();
3465 $pmode = VikRequest::getString('mode', '', 'request');
3466 if (!empty($pmode) && !empty($sortid)) {
3467 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_gpayments` ORDER BY `#__vikbooking_gpayments`.`ordering` ASC;";
3468 $dbo->setQuery($q);
3469 $dbo->execute();
3470 $totr=$dbo->getNumRows();
3471 if ($totr > 1) {
3472 $data = $dbo->loadAssocList();
3473 if ($pmode == "up") {
3474 foreach ($data as $v) {
3475 if ($v['id'] == $sortid) {
3476 $y = $v['ordering'];
3477 }
3478 }
3479 if ($y && $y > 1) {
3480 $vik = $y - 1;
3481 $found = false;
3482 foreach ($data as $v) {
3483 if (intval($v['ordering']) == intval($vik)) {
3484 $found = true;
3485 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3486 $dbo->setQuery($q);
3487 $dbo->execute();
3488 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3489 $dbo->setQuery($q);
3490 $dbo->execute();
3491 break;
3492 }
3493 }
3494 if (!$found) {
3495 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3496 $dbo->setQuery($q);
3497 $dbo->execute();
3498 }
3499 }
3500 } elseif ($pmode == "down") {
3501 foreach ($data as $v) {
3502 if ($v['id'] == $sortid) {
3503 $y = $v['ordering'];
3504 }
3505 }
3506 if ($y) {
3507 $vik = $y + 1;
3508 $found = false;
3509 foreach ($data as $v) {
3510 if (intval($v['ordering']) == intval($vik)) {
3511 $found=true;
3512 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3513 $dbo->setQuery($q);
3514 $dbo->execute();
3515 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3516 $dbo->setQuery($q);
3517 $dbo->execute();
3518 break;
3519 }
3520 }
3521 if (!$found) {
3522 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3523 $dbo->setQuery($q);
3524 $dbo->execute();
3525 }
3526 }
3527 }
3528 }
3529 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
3530 } else {
3531 $mainframe->redirect("index.php?option=com_vikbooking");
3532 }
3533 }
3534
3535 public function sortcarat() {
3536 if (!JSession::checkToken('get')) {
3537 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3538 }
3539 $sortid = VikRequest::getVar('cid', array(0));
3540 $pmode = VikRequest::getString('mode', '', 'request');
3541 $dbo = JFactory::getDBO();
3542 $mainframe = JFactory::getApplication();
3543 if (!empty($pmode)) {
3544 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_characteristics` ORDER BY `#__vikbooking_characteristics`.`ordering` ASC;";
3545 $dbo->setQuery($q);
3546 $dbo->execute();
3547 $totr = $dbo->getNumRows();
3548 if ($totr > 1) {
3549 $data = $dbo->loadAssocList();
3550 if ($pmode == "up") {
3551 foreach ($data as $v) {
3552 if ($v['id'] == $sortid[0]) {
3553 $y = $v['ordering'];
3554 }
3555 }
3556 if ($y && $y > 1) {
3557 $vik = $y - 1;
3558 $found = false;
3559 foreach ($data as $v) {
3560 if (intval($v['ordering']) == intval($vik)) {
3561 $found = true;
3562 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3563 $dbo->setQuery($q);
3564 $dbo->execute();
3565 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3566 $dbo->setQuery($q);
3567 $dbo->execute();
3568 break;
3569 }
3570 }
3571 if (!$found) {
3572 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3573 $dbo->setQuery($q);
3574 $dbo->execute();
3575 }
3576 }
3577 } elseif ($pmode == "down") {
3578 foreach ($data as $v) {
3579 if ($v['id'] == $sortid[0]) {
3580 $y = $v['ordering'];
3581 }
3582 }
3583 if ($y) {
3584 $vik = $y + 1;
3585 $found = false;
3586 foreach ($data as $v) {
3587 if (intval($v['ordering']) == intval($vik)) {
3588 $found = true;
3589 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3590 $dbo->setQuery($q);
3591 $dbo->execute();
3592 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3593 $dbo->setQuery($q);
3594 $dbo->execute();
3595 break;
3596 }
3597 }
3598 if (!$found) {
3599 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3600 $dbo->setQuery($q);
3601 $dbo->execute();
3602 }
3603 }
3604 }
3605 }
3606 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
3607 } else {
3608 $mainframe->redirect("index.php?option=com_vikbooking");
3609 }
3610 }
3611
3612 public function resendordemail() {
3613 $this->do_resendorderemail();
3614 }
3615
3616 public function sendcancordemail() {
3617 $this->do_resendorderemail(true);
3618 }
3619
3620 private function do_resendorderemail($cancellation = false)
3621 {
3622 $dbo = JFactory::getDbo();
3623 $app = JFactory::getApplication();
3624 $vbo_tn = VikBooking::getTranslator();
3625
3626 $cid = VikRequest::getVar('cid', array(0));
3627 $oid = (int)$cid[0];
3628
3629 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $oid . ";";
3630 $dbo->setQuery($q);
3631 $dbo->execute();
3632 if (!$dbo->getNumRows()) {
3633 $app->redirect("index.php?option=com_vikbooking&task=orders");
3634 $app->close();
3635 }
3636 $order = $dbo->loadAssoc();
3637
3638 // check if the language in use is the same as the one used during the checkout
3639 if (!empty($order['lang'])) {
3640 $lang = JFactory::getLanguage();
3641 if ($lang->getTag() != $order['lang']) {
3642 $lang->load('com_vikbooking', (VBOPlatformDetection::isWordPress() ? VIKBOOKING_LANG : JPATH_ADMINISTRATOR), $order['lang'], true);
3643 if (defined('_JEXEC') && !defined('ABSPATH')) {
3644 $lang->load('joomla', JPATH_ADMINISTRATOR, $order['lang'], true);
3645 }
3646 }
3647 if ($vbo_tn->getDefaultLang() != $order['lang']) {
3648 // force the translation to start because contents should be translated
3649 $vbo_tn::$force_tolang = $order['lang'];
3650 }
3651 }
3652
3653 // availability helper
3654 $av_helper = VikBooking::getAvailabilityInstance();
3655
3656 /**
3657 * Split stay reservation.
3658 *
3659 * @since 1.16.0 (J) - 1.6.0 (WP)
3660 */
3661 $room_stay_dates = [];
3662 if ($order['split_stay']) {
3663 if ($order['status'] == 'confirmed') {
3664 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($order['id']);
3665 } else {
3666 $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $order['id'], []);
3667 }
3668 // immediately count the number of nights of stay for each split room
3669 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
3670 if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
3671 // overwrite values for compatibility with non-confirmed bookings
3672 $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
3673 $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
3674 }
3675 $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
3676 // overwrite the whole array
3677 $room_stay_dates[$sps_r_k] = $sps_r_v;
3678 }
3679 }
3680
3681 // load rooms booked
3682 $q = "SELECT `or`.*,`r`.`id` AS `r_reference_id`,`r`.`name`,`r`.`units`,`r`.`fromadult`,`r`.`toadult`,`r`.`params` FROM `#__vikbooking_ordersrooms` AS `or`,`#__vikbooking_rooms` AS `r` WHERE `or`.`idorder`=" . (int)$order['id'] . " AND `or`.`idroom`=`r`.`id` ORDER BY `or`.`id` ASC;";
3683 $dbo->setQuery($q);
3684 $dbo->execute();
3685 $ordersrooms = $dbo->loadAssocList();
3686 $vbo_tn->translateContents($ordersrooms, '#__vikbooking_rooms', array('id' => 'r_reference_id'));
3687
3688 $ftitle = VikBooking::getFrontTitle();
3689 $currencyname = VikBooking::getCurrencyName();
3690
3691 $turnover_secs = VikBooking::getHoursRoomAvail() * 3600;
3692 $realback = $turnover_secs + $order['checkout'];
3693
3694 $rooms = array();
3695 $tars = array();
3696 $arrpeople = array();
3697 $is_package = !empty($order['pkg']) ? true : false;
3698 $nowts = time();
3699 foreach ($ordersrooms as $kor => $or) {
3700 $num = $kor + 1;
3701 $rooms[$num] = $or;
3702 $arrpeople[$num]['adults'] = $or['adults'];
3703 $arrpeople[$num]['children'] = $or['children'];
3704 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3705 // package or custom cost set from the back-end
3706 continue;
3707 }
3708
3709 // determine the proper values for this room
3710 $room_nights = $order['days'];
3711 $room_checkin = $order['checkin'];
3712 $room_checkout = $order['checkout'];
3713 if ($order['split_stay'] && !empty($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) {
3714 $room_nights = $room_stay_dates[$kor]['nights'];
3715 $room_checkin = $room_stay_dates[$kor]['checkin'];
3716 $room_checkout = $room_stay_dates[$kor]['checkout'];
3717 }
3718
3719 // load tariff
3720 $q = "SELECT * FROM `#__vikbooking_dispcost` WHERE `id`=" . (int)$or['idtar'] . ";";
3721 $dbo->setQuery($q);
3722 $dbo->execute();
3723 if ($dbo->getNumRows() > 0) {
3724 $tar = $dbo->loadAssocList();
3725 $tar = VikBooking::applySeasonsRoom($tar, $room_checkin, $room_checkout);
3726
3727 // different usage
3728 $tar = VBORoomHelper::getInstance()->applyOBPRules($tar, $or, $or['adults']);
3729
3730 $tars[$num] = $tar[0];
3731 } else {
3732 VikError::raiseWarning('', JText::translate('VBERRNOFAREFOUND'));
3733 }
3734 }
3735
3736 $secdiff = $order['checkout'] - $order['checkin'];
3737 $daysdiff = $secdiff / 86400;
3738 if (is_int($daysdiff)) {
3739 if ($daysdiff < 1) {
3740 $daysdiff = 1;
3741 }
3742 } else {
3743 if ($daysdiff < 1) {
3744 $daysdiff = 1;
3745 } else {
3746 $sum = floor($daysdiff) * 86400;
3747 $newdiff = $secdiff - $sum;
3748 $maxhmore = VikBooking::getHoursMoreRb() * 3600;
3749 if ($maxhmore >= $newdiff) {
3750 $daysdiff = floor($daysdiff);
3751 } else {
3752 $daysdiff = ceil($daysdiff);
3753 }
3754 }
3755 }
3756
3757 $isdue = 0;
3758 $pricestr = array();
3759 $optstr = array();
3760 foreach ($ordersrooms as $kor => $or) {
3761 $num = $kor + 1;
3762
3763 // determine the proper values for this room
3764 $room_nights = $order['days'];
3765 $room_checkin = $order['checkin'];
3766 $room_checkout = $order['checkout'];
3767 if ($order['split_stay'] && !empty($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) {
3768 $room_nights = $room_stay_dates[$kor]['nights'];
3769 $room_checkin = $room_stay_dates[$kor]['checkin'];
3770 $room_checkout = $room_stay_dates[$kor]['checkout'];
3771 }
3772
3773 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3774 // package cost or cust_cost may not be inclusive of taxes if prices tax included is off
3775 $calctar = VikBooking::sayPackagePlusIva($or['cust_cost'], $or['cust_idiva']);
3776 $isdue += $calctar;
3777 $pricestr[$num] = (!empty($or['pkg_name']) ? $or['pkg_name'] : (!empty($or['otarplan']) ? ucwords($or['otarplan']) : JText::translate('VBOROOMCUSTRATEPLAN'))).": ".$calctar." ".$currencyname;
3778 } elseif (array_key_exists($num, $tars) && is_array($tars[$num])) {
3779 $display_rate = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3780 $calctar = VikBooking::sayCostPlusIva($display_rate, $tars[$num]['idprice']);
3781 $tars[$num]['calctar'] = $calctar;
3782 $isdue += $calctar;
3783 $pricestr[$num] = VikBooking::getPriceName($tars[$num]['idprice'], $vbo_tn) . ": " . $calctar . " " . $currencyname . (!empty($tars[$num]['attrdata']) ? "\n" . VikBooking::getPriceAttr($tars[$num]['idprice'], $vbo_tn) . ": " . $tars[$num]['attrdata'] : "");
3784 }
3785 if (!empty($or['optionals'])) {
3786 $stepo = explode(";", $or['optionals']);
3787 foreach ($stepo as $roptkey => $oo) {
3788 if (empty($oo)) {
3789 continue;
3790 }
3791 $stept = explode(":", $oo);
3792 $q = "SELECT * FROM `#__vikbooking_optionals` WHERE `id`=" . $dbo->quote($stept[0]) . ";";
3793 $dbo->setQuery($q);
3794 $dbo->execute();
3795 if (!$dbo->getNumRows()) {
3796 continue;
3797 }
3798 $actopt = $dbo->loadAssocList();
3799 $vbo_tn->translateContents($actopt, '#__vikbooking_optionals', array(), array(), (!empty($order['lang']) ? $order['lang'] : null));
3800 $chvar = '';
3801 if (!empty($actopt[0]['ageintervals']) && $or['children'] > 0 && strstr($stept[1], '-') != false) {
3802 $optagenames = VikBooking::getOptionIntervalsAges($actopt[0]['ageintervals']);
3803 $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt[0]['ageintervals']);
3804 $optageovrct = VikBooking::getOptionIntervalChildOverrides($actopt[0], $or['adults'], $or['children']);
3805 $child_num = VikBooking::getRoomOptionChildNumber($or['optionals'], $actopt[0]['id'], $roptkey, $or['children']);
3806 $optagecosts = VikBooking::getOptionIntervalsCosts(isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $actopt[0]['ageintervals']);
3807 $agestept = explode('-', $stept[1]);
3808 $stept[1] = $agestept[0];
3809 $chvar = $agestept[1];
3810 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 1) {
3811 //percentage value of the adults tariff
3812 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3813 $optagecosts[($chvar - 1)] = $or['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
3814 } else {
3815 $display_rate = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3816 $optagecosts[($chvar - 1)] = $display_rate * $optagecosts[($chvar - 1)] / 100;
3817 }
3818 } elseif (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 2) {
3819 //VBO 1.10 - percentage value of room base cost
3820 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3821 $optagecosts[($chvar - 1)] = $or['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
3822 } else {
3823 $display_rate = isset($tars[$num]['room_base_cost']) ? $tars[$num]['room_base_cost'] : (!empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost']);
3824 $optagecosts[($chvar - 1)] = $display_rate * $optagecosts[($chvar - 1)] / 100;
3825 }
3826 }
3827 $actopt[0]['chageintv'] = $chvar;
3828 $actopt[0]['name'] .= ' ('.$optagenames[($chvar - 1)].')';
3829 $actopt[0]['quan'] = $stept[1];
3830 $realcost = (intval($actopt[0]['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $room_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
3831 } else {
3832 $actopt[0]['quan'] = $stept[1];
3833 // VBO 1.11 - options percentage cost of the room total fee
3834 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3835 $deftar_basecosts = $or['cust_cost'];
3836 } else {
3837 $deftar_basecosts = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3838 }
3839 $actopt[0]['cost'] = (int)$actopt[0]['pcentroom'] ? ($deftar_basecosts * $actopt[0]['cost'] / 100) : $actopt[0]['cost'];
3840 //
3841 $realcost = (intval($actopt[0]['perday']) == 1 ? ($actopt[0]['cost'] * $room_nights * $stept[1]) : ($actopt[0]['cost'] * $stept[1]));
3842 }
3843 if (!empty($actopt[0]['maxprice']) && $actopt[0]['maxprice'] > 0 && $realcost > $actopt[0]['maxprice']) {
3844 $realcost = $actopt[0]['maxprice'];
3845 if (intval($actopt[0]['hmany']) == 1 && intval($stept[1]) > 1) {
3846 $realcost = $actopt[0]['maxprice'] * $stept[1];
3847 }
3848 }
3849 if ($actopt[0]['perperson'] == 1) {
3850 $realcost = $realcost * $or['adults'];
3851 }
3852
3853 /**
3854 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
3855 *
3856 * @since 1.17.7 (J) - 1.7.7 (WP)
3857 */
3858 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt[0], $order, $or]);
3859 if ($custom_calculation) {
3860 $realcost = (float) $custom_calculation[0];
3861 }
3862
3863 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt[0]['idiva']);
3864 $isdue += $tmpopr;
3865 $optstr[$num][] = ($stept[1] > 1 ? $stept[1] . " " : "") . $actopt[0]['name'] . ": " . $tmpopr . " " . $currencyname . "\n";
3866 }
3867 }
3868
3869 // custom extra costs
3870 if (!empty($or['extracosts'])) {
3871 $cur_extra_costs = json_decode($or['extracosts'], true);
3872 foreach ($cur_extra_costs as $eck => $ecv) {
3873 $ecplustax = !empty($ecv['idtax']) ? VikBooking::sayOptionalsPlusIva($ecv['cost'], $ecv['idtax']) : $ecv['cost'];
3874 $isdue += $ecplustax;
3875 $optstr[$num][] = $ecv['name'] . ": " . $ecplustax . " " . $currencyname."\n";
3876 }
3877 }
3878 }
3879
3880 // coupon
3881 $usedcoupon = false;
3882 $origisdue = $isdue;
3883 if (strlen($order['coupon']) > 0) {
3884 $usedcoupon = true;
3885 $expcoupon = explode(";", $order['coupon']);
3886 $isdue = $isdue - $expcoupon[1];
3887 }
3888
3889 // make sure to apply any previously refunded amount
3890 if ($order['refund'] > 0) {
3891 $isdue -= $order['refund'];
3892 }
3893
3894 // ConfirmationNumber
3895 $confirmnumber = $order['confirmnumber'];
3896
3897 $esit_mess = JText::sprintf('VBORDEREMAILRESENT', $order['custmail']);
3898 $status_str = JText::translate('VBCOMPLETED');
3899 if ($cancellation) {
3900 $confirmnumber = '';
3901 $esit_mess = JText::sprintf('VBCANCORDEREMAILSENT', $order['custmail']);
3902 $status_str = JText::translate('VBCANCELLED');
3903 } elseif ($order['status'] == 'standby') {
3904 $confirmnumber = '';
3905 $status_str = JText::translate('VBWAITINGFORPAYMENT');
3906 }
3907 $app->enqueueMessage($esit_mess);
3908
3909 // force the original total amount if rates have changed
3910 if (number_format($isdue, 2) != number_format($order['total'], 2)) {
3911 $isdue = $order['total'];
3912 }
3913
3914 // send email notification to guest (by ignoring the configuration settings)
3915 VikBooking::sendBookingEmail($order['id'], ['guest'], $send = true, $no_config = true);
3916
3917 if ($cancellation) {
3918 /**
3919 * If "send cancellation email", we log the event in the history.
3920 *
3921 * @since 1.14 (J) - 1.4.0 (WP)
3922 */
3923 VikBooking::getBookingHistoryInstance()->setBid($order['id'])->store('EC');
3924 } else {
3925 /**
3926 * Instead, we store an event log to remind that the email was re-sent to the guest
3927 *
3928 * @since 1.16.3 (J) - 1.6.3 (WP)
3929 */
3930 VikBooking::getBookingHistoryInstance()->setBid($order['id'])->store('ER', $esit_mess);
3931 }
3932
3933 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$oid);
3934 $app->close();
3935 }
3936
3937 public function setordconfirmed()
3938 {
3939 $app = JFactory::getApplication();
3940
3941 // the booking ID to confirm
3942 $cid = VikRequest::getVar('cid', array(0));
3943 $oid = (int) $cid[0];
3944
3945 // notify the customer unless it was a re-confirmation
3946 $pskip = $app->input->getInt('skip_notification', 0);
3947
3948 // access the reservation model
3949 $model = VBOModelReservation::getInstance();
3950
3951 // set the booking to confirmed
3952 $confirmed = $model->setConfirmed([
3953 'booking_id' => $oid,
3954 'notify' => (bool) (!$pskip),
3955 ]);
3956
3957 if (!$confirmed) {
3958 $error = $model->getError();
3959 if (!is_string($error) || !$error) {
3960 $error = 'Could not confirm the reservation';
3961 }
3962
3963 // enqueue error message
3964 $app->enqueueMessage($error, 'error');
3965 } else {
3966 // enqueue success message
3967 $app->enqueueMessage(JText::translate('VBORDERSETASCONF'));
3968 }
3969
3970 // redirect
3971 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $oid);
3972 $app->close();
3973 }
3974
3975 public function payments() {
3976 VikBookingHelper::printHeader("14");
3977
3978 VikRequest::setVar('view', VikRequest::getCmd('view', 'payments'));
3979
3980 parent::display();
3981
3982 if (VikBooking::showFooter()) {
3983 VikBookingHelper::printFooter();
3984 }
3985 }
3986
3987 public function newpayment() {
3988 VikBookingHelper::printHeader("14");
3989
3990 VikRequest::setVar('view', VikRequest::getCmd('view', 'managepayment'));
3991
3992 parent::display();
3993
3994 if (VikBooking::showFooter()) {
3995 VikBookingHelper::printFooter();
3996 }
3997 }
3998
3999 public function editpayment() {
4000 VikBookingHelper::printHeader("14");
4001
4002 VikRequest::setVar('view', VikRequest::getCmd('view', 'managepayment'));
4003
4004 parent::display();
4005
4006 if (VikBooking::showFooter()) {
4007 VikBookingHelper::printFooter();
4008 }
4009 }
4010
4011 public function createpayment()
4012 {
4013 if (!JSession::checkToken()) {
4014 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4015 }
4016
4017 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
4018 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4019 }
4020
4021 $mainframe = JFactory::getApplication();
4022 $pname = VikRequest::getString('name', '', 'request');
4023 $ppayment = VikRequest::getString('payment', '', 'request');
4024 $ppublished = VikRequest::getString('published', '', 'request');
4025 $pcharge = VikRequest::getFloat('charge', '', 'request');
4026 $psetconfirmed = VikRequest::getString('setconfirmed', '', 'request');
4027 $phidenonrefund = VikRequest::getInt('hidenonrefund', '', 'request');
4028 $ponlynonrefund = VikRequest::getInt('onlynonrefund', '', 'request');
4029 $pshownotealw = VikRequest::getString('shownotealw', '', 'request');
4030 $pnote = VikRequest::getString('note', '', 'request', VIKREQUEST_ALLOWHTML);
4031 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4032 $pval_pcent = !in_array($pval_pcent, array('1', '2')) ? 1 : $pval_pcent;
4033 $pch_disc = VikRequest::getString('ch_disc', '', 'request');
4034 $pch_disc = !in_array($pch_disc, array('1', '2')) ? 1 : $pch_disc;
4035 $poutposition = VikRequest::getString('outposition', 'top', 'request');
4036 $plogo = VikRequest::getString('logo', '', 'request');
4037 $pall_rooms = VikRequest::getInt('all_rooms', 0, 'request');
4038 $pidrooms = VikRequest::getVar('idrooms', array());
4039 $vikpaymentparams = VikRequest::getVar('vikpaymentparams', array(0));
4040 $payparamarr = array();
4041 $payparamstr = '';
4042 if (count($vikpaymentparams) > 0) {
4043 foreach ($vikpaymentparams as $setting => $cont) {
4044 if (strlen($setting) > 0) {
4045 $payparamarr[$setting] = $cont;
4046 }
4047 }
4048 if (count($payparamarr) > 0) {
4049 $payparamstr = json_encode($payparamarr);
4050 }
4051 }
4052
4053 $dbo = JFactory::getDbo();
4054
4055 $set_idrooms = [];
4056 if (empty($pall_rooms) && !empty($pidrooms)) {
4057 $pidrooms = array_map(function($idroom) {
4058 return (int)$idroom;
4059 }, $pidrooms);
4060 foreach ($pidrooms as $idroom) {
4061 if (empty($idroom) || in_array($idroom, $set_idrooms)) {
4062 continue;
4063 }
4064 $set_idrooms[] = $idroom;
4065 }
4066 }
4067
4068 if (!empty($pname) && !empty($ppayment)) {
4069 $setpub = $ppublished == "1" ? 1 : 0;
4070 $psetconfirmed = $psetconfirmed == "1" ? 1 : 0;
4071 $pshownotealw = $pshownotealw == "1" ? 1 : 0;
4072 $q = "SELECT `id` FROM `#__vikbooking_gpayments` WHERE `file`=".$dbo->quote($ppayment).";";
4073 $dbo->setQuery($q);
4074 $dbo->execute();
4075 if ($dbo->getNumRows() >= 0) {
4076 $q = "INSERT INTO `#__vikbooking_gpayments` (`name`,`file`,`published`,`note`,`charge`,`setconfirmed`,`shownotealw`,`val_pcent`,`ch_disc`,`params`,`hidenonrefund`,`onlynonrefund`,`outposition`,`logo`,`idrooms`) VALUES(".$dbo->quote($pname).",".$dbo->quote($ppayment).",'".$setpub."',".$dbo->quote($pnote).",".$dbo->quote($pcharge).",'".$psetconfirmed."','".$pshownotealw."','".$pval_pcent."','".$pch_disc."',".$dbo->quote($payparamstr).",".($phidenonrefund > 0 ? '1' : '0').",".($ponlynonrefund > 0 ? '1' : '0').", " . $dbo->quote($poutposition) . ", " . $dbo->quote($plogo) . ", " . (count($set_idrooms) ? $dbo->quote(json_encode($set_idrooms)) : 'NULL') . ");";
4077 $dbo->setQuery($q);
4078 $dbo->execute();
4079 $mainframe->enqueueMessage(JText::translate('VBPAYMENTSAVED'));
4080 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4081 } else {
4082 VikError::raiseWarning('', JText::translate('ERRINVFILEPAYMENT'));
4083 $mainframe->redirect("index.php?option=com_vikbooking&task=newpayment");
4084 }
4085 } else {
4086 $mainframe->redirect("index.php?option=com_vikbooking&task=newpayment");
4087 }
4088 }
4089
4090 public function updatepayment()
4091 {
4092 if (!JSession::checkToken()) {
4093 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4094 }
4095
4096 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4097 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4098 }
4099
4100 $this->do_updatepayment($stay = false);
4101 }
4102
4103 public function updatepaymentstay()
4104 {
4105 if (!JSession::checkToken()) {
4106 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4107 }
4108
4109 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4110 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4111 }
4112
4113 $this->do_updatepayment($stay = true);
4114 }
4115
4116 protected function do_updatepayment($stay = false)
4117 {
4118 $mainframe = JFactory::getApplication();
4119
4120 $pwhere = VikRequest::getString('where', '', 'request');
4121 $pname = VikRequest::getString('name', '', 'request');
4122 $ppayment = VikRequest::getString('payment', '', 'request');
4123 $ppublished = VikRequest::getString('published', '', 'request');
4124 $pcharge = VikRequest::getFloat('charge', '', 'request');
4125 $psetconfirmed = VikRequest::getString('setconfirmed', '', 'request');
4126 $phidenonrefund = VikRequest::getInt('hidenonrefund', '', 'request');
4127 $ponlynonrefund = VikRequest::getInt('onlynonrefund', '', 'request');
4128 $pshownotealw = VikRequest::getString('shownotealw', '', 'request');
4129 $pnote = VikRequest::getString('note', '', 'request', VIKREQUEST_ALLOWRAW);
4130 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4131 $pval_pcent = !in_array($pval_pcent, array('1', '2')) ? 1 : $pval_pcent;
4132 $pch_disc = VikRequest::getString('ch_disc', '', 'request');
4133 $pch_disc = !in_array($pch_disc, array('1', '2')) ? 1 : $pch_disc;
4134 $poutposition = VikRequest::getString('outposition', 'top', 'request');
4135 $plogo = VikRequest::getString('logo', '', 'request');
4136 $pall_rooms = VikRequest::getInt('all_rooms', 0, 'request');
4137 $pidrooms = VikRequest::getVar('idrooms', array());
4138 $vikpaymentparams = VikRequest::getVar('vikpaymentparams', array(0));
4139 $payparamarr = array();
4140 $payparamstr = '';
4141 if (count($vikpaymentparams) > 0) {
4142 foreach ($vikpaymentparams as $setting => $cont) {
4143 if (strlen($setting) > 0) {
4144 $payparamarr[$setting] = $cont;
4145 }
4146 }
4147 if (count($payparamarr) > 0) {
4148 $payparamstr = json_encode($payparamarr);
4149 }
4150 }
4151
4152 $dbo = JFactory::getDbo();
4153
4154 $set_idrooms = [];
4155 if (empty($pall_rooms) && !empty($pidrooms)) {
4156 $pidrooms = array_map(function($idroom) {
4157 return (int)$idroom;
4158 }, $pidrooms);
4159 foreach ($pidrooms as $idroom) {
4160 if (empty($idroom) || in_array($idroom, $set_idrooms)) {
4161 continue;
4162 }
4163 $set_idrooms[] = $idroom;
4164 }
4165 }
4166
4167 if (!empty($pname) && !empty($ppayment) && !empty($pwhere)) {
4168 $setpub = $ppublished == "1" ? 1 : 0;
4169 $psetconfirmed = $psetconfirmed == "1" ? 1 : 0;
4170 $pshownotealw = $pshownotealw == "1" ? 1 : 0;
4171 $q = "SELECT `id` FROM `#__vikbooking_gpayments` WHERE `file`=".$dbo->quote($ppayment)." AND `id`!='".$pwhere."';";
4172 $dbo->setQuery($q);
4173 $dbo->execute();
4174 if ($dbo->getNumRows() >= 0) {
4175 $q = "UPDATE `#__vikbooking_gpayments` SET `name`=".$dbo->quote($pname).",`file`=".$dbo->quote($ppayment).",`published`='".$setpub."',`note`=".$dbo->quote($pnote).",`charge`=".$dbo->quote($pcharge).",`setconfirmed`='".$psetconfirmed."',`shownotealw`='".$pshownotealw."',`val_pcent`='".$pval_pcent."',`ch_disc`='".$pch_disc."',`params`=".$dbo->quote($payparamstr).",`hidenonrefund`=".($phidenonrefund > 0 ? '1' : '0').",`onlynonrefund`=".($ponlynonrefund > 0 ? '1' : '0').",`outposition`=" . $dbo->quote($poutposition) . ",`logo`=" . $dbo->quote($plogo) . ",`idrooms`=" . (count($set_idrooms) ? $dbo->quote(json_encode($set_idrooms)) : 'NULL') . " WHERE `id`=".$dbo->quote($pwhere).";";
4176 $dbo->setQuery($q);
4177 $dbo->execute();
4178
4179 $mainframe->enqueueMessage(JText::translate('VBPAYMENTUPDATED'));
4180 if ($stay) {
4181 $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4182 } else {
4183 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4184 }
4185 } else {
4186 VikError::raiseWarning('', JText::translate('ERRINVFILEPAYMENT'));
4187 $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4188 }
4189 } else {
4190 $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4191 }
4192 }
4193
4194 public function removepayments()
4195 {
4196 if (!JSession::checkToken()) {
4197 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4198 }
4199
4200 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
4201 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4202 }
4203
4204 $ids = VikRequest::getVar('cid', array(0));
4205 if ($ids) {
4206 $dbo = JFactory::getDBO();
4207 foreach ($ids as $d) {
4208 $q = "DELETE FROM `#__vikbooking_gpayments` WHERE `id`=".$dbo->quote($d).";";
4209 $dbo->setQuery($q);
4210 $dbo->execute();
4211 }
4212 }
4213 $mainframe = JFactory::getApplication();
4214 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4215 }
4216
4217 public function modavailpayment() {
4218 if (!JSession::checkToken('get')) {
4219 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4220 }
4221 $cid = VikRequest::getVar('cid', array(0));
4222 $idp = $cid[0];
4223 if (!empty($idp)) {
4224 $dbo = JFactory::getDBO();
4225 $q = "SELECT `published` FROM `#__vikbooking_gpayments` WHERE `id`=".intval($idp).";";
4226 $dbo->setQuery($q);
4227 $dbo->execute();
4228 $get = $dbo->loadAssocList();
4229 $q = "UPDATE `#__vikbooking_gpayments` SET `published`=".(intval($get[0]['published']) == 1 ? '0' : '1')." WHERE `id`=".intval($idp).";";
4230 $dbo->setQuery($q);
4231 $dbo->execute();
4232 }
4233 $mainframe = JFactory::getApplication();
4234 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4235 }
4236
4237 public function seasons() {
4238 VikBookingHelper::printHeader("13");
4239
4240 VikRequest::setVar('view', VikRequest::getCmd('view', 'seasons'));
4241
4242 parent::display();
4243
4244 if (VikBooking::showFooter()) {
4245 VikBookingHelper::printFooter();
4246 }
4247 }
4248
4249 public function newseason() {
4250 VikBookingHelper::printHeader("13");
4251
4252 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageseason'));
4253
4254 parent::display();
4255
4256 if (VikBooking::showFooter()) {
4257 VikBookingHelper::printFooter();
4258 }
4259 }
4260
4261 public function editseason() {
4262 VikBookingHelper::printHeader("13");
4263
4264 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageseason'));
4265
4266 parent::display();
4267
4268 if (VikBooking::showFooter()) {
4269 VikBookingHelper::printFooter();
4270 }
4271 }
4272
4273 public function updateseason()
4274 {
4275 if (!JSession::checkToken()) {
4276 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4277 }
4278
4279 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4280 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4281 }
4282
4283 $this->do_updateseason();
4284 }
4285
4286 public function updateseasonstay()
4287 {
4288 if (!JSession::checkToken()) {
4289 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4290 }
4291
4292 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4293 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4294 }
4295
4296 $this->do_updateseason(true);
4297 }
4298
4299 private function do_updateseason($stay = false)
4300 {
4301 $app = JFactory::getApplication();
4302 $dbo = JFactory::getDbo();
4303 $session = JFactory::getSession();
4304
4305 $pwhere = VikRequest::getInt('where', 0, 'request');
4306
4307 $pfrom = VikRequest::getString('from', '', 'request');
4308 $pto = VikRequest::getString('to', '', 'request');
4309 $ptype = VikRequest::getString('type', '', 'request');
4310 $pdiffcost = VikRequest::getFloat('diffcost', '', 'request');
4311 $pidrooms = VikRequest::getVar('idrooms', array());
4312 $pidprices = VikRequest::getVar('idprices', array());
4313 $pwdays = VikRequest::getVar('wdays', array());
4314 $pspname = VikRequest::getString('spname', '', 'request');
4315 $pcheckinincl = VikRequest::getString('checkinincl', '', 'request');
4316 $pcheckinincl = $pcheckinincl == 1 ? 1 : 0;
4317 $pyeartied = VikRequest::getInt('yeartied', 0, 'request');
4318 $pyeartied = $pyeartied == 1 ? 1 : 0;
4319 $tieyear = 0;
4320 $ppromo = VikRequest::getInt('promo', 0, 'request');
4321 $ppromo = $ppromo == 1 ? 1 : 0;
4322 $ppromodaysadv = VikRequest::getInt('promodaysadv', '', 'request');
4323 $ppromominlos = VikRequest::getInt('promominlos', '', 'request');
4324 $ppromotxt = VikRequest::getString('promotxt', '', 'request', VIKREQUEST_ALLOWHTML);
4325 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4326 $pval_pcent = $pval_pcent == "1" ? 1 : 2;
4327 $proundmode = VikRequest::getString('roundmode', '', 'request');
4328 $proundmode = (!empty($proundmode) && in_array($proundmode, array('PHP_ROUND_HALF_UP', 'PHP_ROUND_HALF_DOWN')) ? $proundmode : '');
4329 $pnightsoverrides = VikRequest::getVar('nightsoverrides', array());
4330 $pvaluesoverrides = VikRequest::getVar('valuesoverrides', array());
4331 $pandmoreoverride = VikRequest::getVar('andmoreoverride', array());
4332 $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array());
4333 $padultsdiffval = VikRequest::getVar('adultsdiffval', array());
4334 $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array());
4335 $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array());
4336 $ppromolastmind = VikRequest::getInt('promolastmind', 0, 'request');
4337 $ppromolastminh = VikRequest::getInt('promolastminh', 0, 'request');
4338 $promolastmin = ($ppromolastmind * 86400) + ($ppromolastminh * 3600);
4339 $ppromofinalprice = VikRequest::getInt('promofinalprice', 0, 'request');
4340 $ppromofinalprice = $ppromo ? $ppromofinalprice : 0;
4341 $occupancy_ovr = array();
4342 $losverridestr = "";
4343
4344 $updforvcm = $session->get('vbVcmRatesUpd', '');
4345 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
4346
4347 // check null dates
4348 if ($dbo->getNullDate() == $pfrom) {
4349 $pfrom = '';
4350 }
4351 if ($dbo->getNullDate() == $pto) {
4352 $pto = '';
4353 }
4354
4355 if ((empty($pfrom) || empty($pto)) && !$pwdays) {
4356 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4357 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4358 exit;
4359 }
4360
4361 $skipseason = false;
4362 if (empty($pfrom) || empty($pto)) {
4363 $skipseason = true;
4364 }
4365 $skipdays = false;
4366 $wdaystr = null;
4367 if (count($pwdays) == 0) {
4368 $skipdays = true;
4369 } else {
4370 $wdaystr = "";
4371 foreach ($pwdays as $wd) {
4372 $wdaystr .= $wd.';';
4373 }
4374 }
4375 $roomstr = "";
4376 $roomids = array();
4377 foreach ($pidrooms as $room) {
4378 if (empty($room)) {
4379 continue;
4380 }
4381 $roomstr .= "-".$room."-,";
4382 $roomids[] = (int)$room;
4383 }
4384 $pricestr = "";
4385 $priceids = array();
4386 foreach ($pidprices as $price) {
4387 if (empty($price)) {
4388 continue;
4389 }
4390 $pricestr .= "-".$price."-,";
4391 $priceids[] = (int)$price;
4392 }
4393 $valid = true;
4394 $double_records = array();
4395 $sfrom = null;
4396 $sto = null;
4397
4398 // value overrides
4399 if ($pnightsoverrides && $pvaluesoverrides) {
4400 foreach ($pnightsoverrides as $ko => $no) {
4401 if (!empty($no) && strlen(trim($pvaluesoverrides[$ko])) > 0) {
4402 $infiniteclause = intval($pandmoreoverride[$ko]) == 1 ? '-i' : '';
4403 $losverridestr .= intval($no).$infiniteclause.':'.trim($pvaluesoverrides[$ko]).'_';
4404 }
4405 }
4406 }
4407
4408 if (!$skipseason) {
4409 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
4410 $second = VikBooking::getDateTimestamp($pto, 0, 0);
4411
4412 if ($second > 0 && $second == $first) {
4413 $second += 86399;
4414 }
4415
4416 if (!($second > $first)) {
4417 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4418 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4419 exit;
4420 }
4421
4422 $baseone = getdate($first);
4423 $basets = mktime(0, 0, 0, 1, 1, $baseone['year']);
4424 $sfrom = $baseone[0] - $basets;
4425 $basetwo = getdate($second);
4426 $basets = mktime(0, 0, 0, 1, 1, $basetwo['year']);
4427 $sto = $basetwo[0] - $basets;
4428
4429 // check leap year
4430 if ($baseone['year'] % 4 == 0 && ($baseone['year'] % 100 != 0 || $baseone['year'] % 400 == 0)) {
4431 $leapts = mktime(0, 0, 0, 2, 29, $baseone['year']);
4432 if ($baseone[0] > $leapts) {
4433 $sfrom -= 86400;
4434 /**
4435 * To avoid issue with leap years and dates near Feb 29th, we only reduce the seconds if these were reduced
4436 * for the from-date of the seasons. Doing it just for the to-date in 2019 for 2020 (leap) produced invalid results.
4437 *
4438 * @since July 2nd 2019
4439 */
4440 if ($basetwo['year'] % 4 == 0 && ($basetwo['year'] % 100 != 0 || $basetwo['year'] % 400 == 0)) {
4441 $leapts = mktime(0, 0, 0, 2, 29, $basetwo['year']);
4442 if ($basetwo[0] > $leapts) {
4443 $sto -= date('d-m', $baseone[0]) != '31-12' && date('d-m', $basetwo[0]) == '31-12' ? 1 : 86400;
4444 }
4445 }
4446 }
4447 }
4448
4449 // tied to the year
4450 if ($pyeartied == 1) {
4451 $tieyear = $baseone['year'];
4452 }
4453
4454 // Occupancy Override
4455 if (count($padultsdiffval) > 0) {
4456 foreach ($padultsdiffval as $rid => $valovr_arr) {
4457 if (!is_array($valovr_arr) || !is_array($padultsdiffchdisc[$rid]) || !is_array($padultsdiffvalpcent[$rid]) || !is_array($padultsdiffpernight[$rid])) {
4458 continue;
4459 }
4460 foreach ($valovr_arr as $occ => $valovr) {
4461 if (!(strlen($valovr) > 0) || !(strlen($padultsdiffchdisc[$rid][$occ]) > 0) || !(strlen($padultsdiffvalpcent[$rid][$occ]) > 0) || !(strlen($padultsdiffpernight[$rid][$occ]) > 0)) {
4462 continue;
4463 }
4464 if (!array_key_exists($rid, $occupancy_ovr)) {
4465 $occupancy_ovr[$rid] = array();
4466 }
4467 $occupancy_ovr[$rid][$occ] = array('chdisc' => (int)$padultsdiffchdisc[$rid][$occ], 'valpcent' => (int)$padultsdiffvalpcent[$rid][$occ], 'pernight' => (int)$padultsdiffpernight[$rid][$occ], 'value' => (float)$valovr);
4468 }
4469 }
4470 }
4471
4472 // check if seasons dates are valid
4473 $q = "SELECT `id`,`spname` FROM `#__vikbooking_seasons` WHERE `from`<=".$dbo->quote($sfrom)." AND `to`>=".$dbo->quote($sfrom)." AND `id`!=".$dbo->quote($pwhere)." AND `idrooms`=".$dbo->quote($roomstr)."".(!$skipdays ? " AND `wdays`='".$wdaystr."'" : "").($skipdays ? " AND (`from` > 0 OR `to` > 0) AND `wdays`=''" : "").($pyeartied == 1 ? " AND `year`=".$tieyear : " AND `year` IS NULL")." AND `idprices`=".$dbo->quote($pricestr)." AND `promo`=".$ppromo." AND `promodaysadv`=" . $dbo->quote($ppromodaysadv) . " AND `promominlos`=" . $dbo->quote($ppromominlos) . " AND `promolastmin`=" . $dbo->quote($promolastmin) . " AND `losoverride`=".$dbo->quote($losverridestr)." AND `occupancy_ovr`".(count($occupancy_ovr) > 0 ? "=".$dbo->quote(json_encode($occupancy_ovr)) : " IS NULL").";";
4474 $dbo->setQuery($q);
4475 $similar = $dbo->loadAssocList();
4476 if ($similar) {
4477 $valid = false;
4478 foreach ($similar as $sim) {
4479 $double_records[] = $sim['spname'];
4480 }
4481 }
4482
4483 $q = "SELECT `id`,`spname` FROM `#__vikbooking_seasons` WHERE `from`<=".$dbo->quote($sto)." AND `to`>=".$dbo->quote($sto)." AND `id`!=".$dbo->quote($pwhere)." AND `idrooms`=".$dbo->quote($roomstr)."".(!$skipdays ? " AND `wdays`='".$wdaystr."'" : "").($skipdays ? " AND (`from` > 0 OR `to` > 0) AND `wdays`=''" : "").($pyeartied == 1 ? " AND `year`=".$tieyear : " AND `year` IS NULL")." AND `idprices`=".$dbo->quote($pricestr)." AND `promo`=".$ppromo." AND `promodaysadv`=" . $dbo->quote($ppromodaysadv) . " AND `promominlos`=" . $dbo->quote($ppromominlos) . " AND `promolastmin`=" . $dbo->quote($promolastmin) . " AND `losoverride`=".$dbo->quote($losverridestr)." AND `occupancy_ovr`".(count($occupancy_ovr) > 0 ? "=".$dbo->quote(json_encode($occupancy_ovr)) : " IS NULL").";";
4484 $dbo->setQuery($q);
4485 $similar = $dbo->loadAssocList();
4486 if ($similar) {
4487 $valid = false;
4488 foreach ($similar as $sim) {
4489 $double_records[] = $sim['spname'];
4490 }
4491 }
4492
4493 $q = "SELECT `id`,`spname` FROM `#__vikbooking_seasons` WHERE `from`>=".$dbo->quote($sfrom)." AND `from`<=".$dbo->quote($sto)." AND `to`>=".$dbo->quote($sfrom)." AND `to`<=".$dbo->quote($sto)." AND `id`!=".$dbo->quote($pwhere)." AND `idrooms`=".$dbo->quote($roomstr)."".(!$skipdays ? " AND `wdays`='".$wdaystr."'" : "").($skipdays ? " AND (`from` > 0 OR `to` > 0) AND `wdays`=''" : "").($pyeartied == 1 ? " AND `year`=".$tieyear : " AND `year` IS NULL")." AND `idprices`=".$dbo->quote($pricestr)." AND `promo`=".$ppromo." AND `promodaysadv`=" . $dbo->quote($ppromodaysadv) . " AND `promominlos`=" . $dbo->quote($ppromominlos) . " AND `promolastmin`=" . $dbo->quote($promolastmin) . " AND `losoverride`=".$dbo->quote($losverridestr)." AND `occupancy_ovr`".(count($occupancy_ovr) > 0 ? "=".$dbo->quote(json_encode($occupancy_ovr)) : " IS NULL").";";
4494 $dbo->setQuery($q);
4495 $dbo->execute();
4496 $similar = $dbo->loadAssocList();
4497 if ($similar) {
4498 $valid = false;
4499 foreach ($similar as $sim) {
4500 $double_records[] = $sim['spname'];
4501 }
4502 }
4503 }
4504
4505 // fetch previous record before the update
4506 $q = $dbo->getQuery(true)
4507 ->select('*')
4508 ->from($dbo->qn('#__vikbooking_seasons'))
4509 ->where($dbo->qn('id') . ' = ' . $pwhere);
4510 $dbo->setQuery($q, 0, 1);
4511 $prev_record = $dbo->loadAssoc();
4512
4513 if (!$valid || !$prev_record) {
4514 VikError::raiseWarning('', JText::translate('ERRINVDATEROOMSLOCSEASON').($double_records ? ' ('.implode(', ', array_unique($double_records)).')' : ''));
4515 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4516 exit;
4517 }
4518
4519 /**
4520 * Attempt to access the promotion handlers in advance to perform additional validations.
4521 *
4522 * @since 1.16.4 (J) - 1.6.4 (WP)
4523 */
4524 try {
4525 $promo_handlers = VikBooking::getPromotionHandlers();
4526 } catch (Exception $e) {
4527 // reset the value
4528 $promo_handlers = [];
4529 }
4530
4531 if (!$prev_record['promo'] && $ppromo && $promo_handlers) {
4532 // channels supporting promotions are available, and a regular special price is
4533 // being converted into a promotion - this is not allowed so we make it a non-promotion.
4534 $ppromo = 0;
4535 $app->enqueueMessage(JText::translate('VBO_NOPROMO_UPD_CHANNELS'), 'warning');
4536 }
4537
4538 if ($promo_handlers && $proundmode) {
4539 /**
4540 * Always disallow rounding when channels supporting promotions are available.
4541 *
4542 * @since 1.18.3 (J) - 1.8.3 (WP)
4543 */
4544 $proundmode = '';
4545 $app->enqueueMessage(sprintf('%s: %s.', JText::translate('VBNEWSEASONROUNDCOST'), JText::translate('VBPARAMPRICECALENDARDISABLED')), 'warning');
4546 }
4547
4548 // update record
4549 $upd_record = new stdClass;
4550 $upd_record->id = $prev_record['id'];
4551 $upd_record->type = $ptype == "1" ? 1 : 2;
4552 $upd_record->from = $sfrom;
4553 $upd_record->to = $sto;
4554 $upd_record->diffcost = $pdiffcost;
4555 $upd_record->idrooms = $roomstr;
4556 $upd_record->spname = $pspname;
4557 $upd_record->wdays = $wdaystr;
4558 $upd_record->checkinincl = $pcheckinincl;
4559 $upd_record->val_pcent = $pval_pcent;
4560 $upd_record->losoverride = $losverridestr;
4561 $upd_record->roundmode = !empty($proundmode) ? $proundmode : null;
4562 $upd_record->year = $pyeartied == 1 ? $tieyear : null;
4563 $upd_record->idprices = $pricestr;
4564 $upd_record->promo = $ppromo;
4565 $upd_record->promodaysadv = !empty($ppromodaysadv) ? $ppromodaysadv : null;
4566 $upd_record->promotxt = $ppromotxt;
4567 $upd_record->promominlos = !empty($ppromominlos) ? $ppromominlos : 0;
4568 $upd_record->occupancy_ovr = $occupancy_ovr ? json_encode($occupancy_ovr) : null;
4569 $upd_record->promolastmin = (int)$promolastmin;
4570 $upd_record->promofinalprice = $ppromofinalprice;
4571
4572 $dbo->updateObject('#__vikbooking_seasons', $upd_record, 'id', $nulls = true);
4573
4574 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
4575
4576 // update session values
4577 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
4578 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
4579 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $first ? $first : $updforvcm['dfrom'];
4580 } else {
4581 $updforvcm['dfrom'] = $first;
4582 }
4583 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
4584 $updforvcm['dto'] = $updforvcm['dto'] < $second ? $second : $updforvcm['dto'];
4585 } else {
4586 $updforvcm['dto'] = $second;
4587 }
4588 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
4589 foreach ($roomids as $rid) {
4590 if (!in_array($rid, $updforvcm['rooms'])) {
4591 $updforvcm['rooms'][] = $rid;
4592 }
4593 }
4594 } else {
4595 $updforvcm['rooms'] = $roomids;
4596 }
4597 if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
4598 foreach ($roomids as $rid) {
4599 if (array_key_exists($rid, $updforvcm['rplans'])) {
4600 $updforvcm['rplans'][$rid] = $updforvcm['rplans'][$rid] + $priceids;
4601 } else {
4602 $updforvcm['rplans'][$rid] = $priceids;
4603 }
4604 }
4605 } else {
4606 $updforvcm['rplans'] = array();
4607 foreach ($roomids as $rid) {
4608 $updforvcm['rplans'][$rid] = $priceids;
4609 }
4610 }
4611 $session->set('vbVcmRatesUpd', $updforvcm);
4612
4613 /**
4614 * Query promotion handlers, if any, to trigger the update/delete promotion event.
4615 *
4616 * @since 1.15.0 (J) - 1.5.0 (WP)
4617 * @since 1.16.4 (J) - 1.6.4 (WP) added control to perform a delete operation.
4618 */
4619 $promo_update_type = $prev_record['promo'] && !$ppromo ? 'triggerDelete' : 'triggerUpdate';
4620 $promo_method_type = $prev_record['promo'] && !$ppromo ? 'delete' : 'update';
4621 try {
4622 if ($ppromo && is_array($promo_handlers) && $promo_handlers) {
4623 foreach ($promo_handlers as $promo_handler) {
4624 if (!isset($promo_handler->instance) || !is_object($promo_handler->instance) || !method_exists($promo_handler->instance, $promo_update_type)) {
4625 // outdated handler object
4626 continue;
4627 }
4628 if (!is_callable(array($promo_handler->instance, $promo_update_type)) || !$promo_handler->instance->{$promo_update_type}()) {
4629 // promotion handler does not support update/delete promotion event
4630 continue;
4631 }
4632 // invoke the update/delete promotion event for this handler
4633 $ch_result = $promo_handler->instance->createPromotion(['vbo_promo_id' => $pwhere], $promo_method_type);
4634 if (!$ch_result) {
4635 VikError::raiseWarning('', $promo_handler->instance->getName() . ': ' . $promo_handler->instance->getError());
4636 }
4637 }
4638 }
4639 } catch (Exception $e) {
4640 // do nothing
4641 }
4642
4643 if ($stay) {
4644 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4645 } else {
4646 $app->redirect("index.php?option=com_vikbooking&task=seasons");
4647 }
4648 $app->close();
4649 }
4650
4651 public function createseason()
4652 {
4653 if (!JSession::checkToken()) {
4654 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4655 }
4656
4657 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
4658 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4659 }
4660
4661 $this->do_createseason();
4662 }
4663
4664 public function createseason_new()
4665 {
4666 if (!JSession::checkToken()) {
4667 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4668 }
4669
4670 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
4671 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4672 }
4673
4674 $this->do_createseason(true);
4675 }
4676
4677 private function do_createseason($andnew = false)
4678 {
4679 $app = JFactory::getApplication();
4680 $dbo = JFactory::getDbo();
4681 $session = JFactory::getSession();
4682
4683 $pfrom = VikRequest::getString('from', '', 'request');
4684 $pto = VikRequest::getString('to', '', 'request');
4685 $ptype = VikRequest::getString('type', '', 'request');
4686 $pdiffcost = VikRequest::getFloat('diffcost', '', 'request');
4687 $pidrooms = VikRequest::getVar('idrooms', array());
4688 $pidprices = VikRequest::getVar('idprices', array());
4689 $pwdays = VikRequest::getVar('wdays', array());
4690 $pspname = VikRequest::getString('spname', '', 'request');
4691 $pcheckinincl = VikRequest::getString('checkinincl', '', 'request');
4692 $pcheckinincl = $pcheckinincl == 1 ? 1 : 0;
4693 $pyeartied = VikRequest::getInt('yeartied', 0, 'request');
4694 $pyeartied = $pyeartied == 1 ? 1 : 0;
4695 $tieyear = 0;
4696 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4697 $pval_pcent = $pval_pcent == "1" ? 1 : 2;
4698 $proundmode = VikRequest::getString('roundmode', '', 'request');
4699 $proundmode = (!empty($proundmode) && in_array($proundmode, array('PHP_ROUND_HALF_UP', 'PHP_ROUND_HALF_DOWN')) ? $proundmode : '');
4700 $ppromo = VikRequest::getInt('promo', 0, 'request');
4701 $ppromodaysadv = VikRequest::getInt('promodaysadv', '', 'request');
4702 $ppromominlos = VikRequest::getInt('promominlos', '', 'request');
4703 $ppromotxt = VikRequest::getString('promotxt', '', 'request', VIKREQUEST_ALLOWHTML);
4704 $pnightsoverrides = VikRequest::getVar('nightsoverrides', array());
4705 $pvaluesoverrides = VikRequest::getVar('valuesoverrides', array());
4706 $pandmoreoverride = VikRequest::getVar('andmoreoverride', array());
4707 $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array());
4708 $padultsdiffval = VikRequest::getVar('adultsdiffval', array());
4709 $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array());
4710 $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array());
4711 $ppromolastmind = VikRequest::getInt('promolastmind', 0, 'request');
4712 $ppromolastminh = VikRequest::getInt('promolastminh', 0, 'request');
4713 $promolastmin = ($ppromolastmind * 86400) + ($ppromolastminh * 3600);
4714 $ppromofinalprice = VikRequest::getInt('promofinalprice', 0, 'request');
4715 $ppromofinalprice = $ppromo ? $ppromofinalprice : 0;
4716 $pchannels = VikRequest::getVar('channels', array());
4717 $occupancy_ovr = array();
4718 $losverridestr = "";
4719
4720 $updforvcm = $session->get('vbVcmRatesUpd', '');
4721 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
4722
4723 // check null dates
4724 if ($dbo->getNullDate() == $pfrom) {
4725 $pfrom = '';
4726 }
4727 if ($dbo->getNullDate() == $pto) {
4728 $pto = '';
4729 }
4730
4731 if ((empty($pfrom) || empty($pto)) && !$pwdays) {
4732 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4733 $app->redirect("index.php?option=com_vikbooking&task=newseason");
4734 exit;
4735 }
4736
4737 $skipseason = false;
4738 if (empty($pfrom) || empty($pto)) {
4739 $skipseason = true;
4740 }
4741 $skipdays = false;
4742 $wdaystr = null;
4743 if (!$pwdays) {
4744 $skipdays = true;
4745 } else {
4746 $wdaystr = "";
4747 foreach ($pwdays as $wd) {
4748 $wdaystr .= $wd.';';
4749 }
4750 }
4751 $roomstr = "";
4752 $roomids = array();
4753 foreach ($pidrooms as $room) {
4754 if (empty($room)) {
4755 continue;
4756 }
4757 $roomstr .= "-".$room."-,";
4758 $roomids[] = (int)$room;
4759 }
4760 $pricestr = "";
4761 $priceids = array();
4762 foreach ($pidprices as $price) {
4763 if (empty($price)) {
4764 continue;
4765 }
4766 $pricestr .= "-".$price."-,";
4767 $priceids[] = (int)$price;
4768 }
4769 $valid = true;
4770 $double_records = array();
4771 $sfrom = null;
4772 $sto = null;
4773
4774 // value overrides
4775 if ($pnightsoverrides && $pvaluesoverrides) {
4776 foreach ($pnightsoverrides as $ko => $no) {
4777 if (!empty($no) && strlen(trim($pvaluesoverrides[$ko])) > 0) {
4778 $infiniteclause = intval($pandmoreoverride[$ko]) == 1 ? '-i' : '';
4779 $losverridestr .= intval($no).$infiniteclause.':'.trim($pvaluesoverrides[$ko]).'_';
4780 }
4781 }
4782 }
4783
4784 if (!$skipseason) {
4785 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
4786 $second = VikBooking::getDateTimestamp($pto, 0, 0);
4787
4788 if ($second > 0 && $second == $first) {
4789 $second += 86399;
4790 }
4791
4792 if (!($second > $first)) {
4793 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4794 $app->redirect("index.php?option=com_vikbooking&task=newseason");
4795 exit;
4796 }
4797
4798 $baseone = getdate($first);
4799 $basets = mktime(0, 0, 0, 1, 1, $baseone['year']);
4800 $sfrom = $baseone[0] - $basets;
4801 $basetwo = getdate($second);
4802 $basets = mktime(0, 0, 0, 1, 1, $basetwo['year']);
4803 $sto = $basetwo[0] - $basets;
4804
4805 // check leap year
4806 if ($baseone['year'] % 4 == 0 && ($baseone['year'] % 100 != 0 || $baseone['year'] % 400 == 0)) {
4807 $leapts = mktime(0, 0, 0, 2, 29, $baseone['year']);
4808 if ($baseone[0] > $leapts) {
4809 $sfrom -= 86400;
4810 /**
4811 * To avoid issue with leap years and dates near Feb 29th, we only reduce the seconds if these were reduced
4812 * for the from-date of the seasons. Doing it just for the to-date in 2019 for 2020 (leap) produced invalid results.
4813 *
4814 * @since July 2nd 2019
4815 */
4816 if ($basetwo['year'] % 4 == 0 && ($basetwo['year'] % 100 != 0 || $basetwo['year'] % 400 == 0)) {
4817 $leapts = mktime(0, 0, 0, 2, 29, $basetwo['year']);
4818 if ($basetwo[0] > $leapts) {
4819 $sto -= date('d-m', $baseone[0]) != '31-12' && date('d-m', $basetwo[0]) == '31-12' ? 1 : 86400;
4820 }
4821 }
4822 }
4823 }
4824
4825 // tied to the year
4826 if ($pyeartied == 1) {
4827 $tieyear = $baseone['year'];
4828 }
4829
4830 // Occupancy Override
4831 if ($padultsdiffval) {
4832 foreach ($padultsdiffval as $rid => $valovr_arr) {
4833 if (!is_array($valovr_arr) || !is_array($padultsdiffchdisc[$rid]) || !is_array($padultsdiffvalpcent[$rid]) || !is_array($padultsdiffpernight[$rid])) {
4834 continue;
4835 }
4836 foreach ($valovr_arr as $occ => $valovr) {
4837 if (!(strlen($valovr) > 0) || !(strlen($padultsdiffchdisc[$rid][$occ]) > 0) || !(strlen($padultsdiffvalpcent[$rid][$occ]) > 0) || !(strlen($padultsdiffpernight[$rid][$occ]) > 0)) {
4838 continue;
4839 }
4840 if (!array_key_exists($rid, $occupancy_ovr)) {
4841 $occupancy_ovr[$rid] = array();
4842 }
4843 $occupancy_ovr[$rid][$occ] = array('chdisc' => (int)$padultsdiffchdisc[$rid][$occ], 'valpcent' => (int)$padultsdiffvalpcent[$rid][$occ], 'pernight' => (int)$padultsdiffpernight[$rid][$occ], 'value' => (float)$valovr);
4844 }
4845 }
4846 }
4847
4848 // check if seasons dates are valid
4849 $q = "SELECT `id`,`spname` FROM `#__vikbooking_seasons` WHERE `from`<=".$dbo->quote($sfrom)." AND `to`>".$dbo->quote($sfrom)." AND `idrooms`=".$dbo->quote($roomstr)."".(!$skipdays ? " AND `wdays`='".$wdaystr."'" : "").($skipdays ? " AND (`from` > 0 OR `to` > 0) AND `wdays`=''" : "").($pyeartied == 1 ? " AND `year`=".$tieyear : " AND `year` IS NULL")." AND `idprices`=".$dbo->quote($pricestr)." AND `promo`=".$ppromo." AND `promodaysadv`=" . $dbo->quote($ppromodaysadv) . " AND `promominlos`=" . $dbo->quote($ppromominlos) . " AND `promolastmin`=" . $dbo->quote($promolastmin) . " AND `losoverride`=".$dbo->quote($losverridestr)." AND `occupancy_ovr`".(count($occupancy_ovr) > 0 ? "=".$dbo->quote(json_encode($occupancy_ovr)) : " IS NULL").";";
4850 $dbo->setQuery($q);
4851 $similar = $dbo->loadAssocList();
4852 if ($similar) {
4853 $valid = false;
4854 foreach ($similar as $sim) {
4855 $double_records[] = $sim['spname'];
4856 }
4857 }
4858
4859 $q = "SELECT `id`,`spname` FROM `#__vikbooking_seasons` WHERE `from`<=".$dbo->quote($sto)." AND `to`>=".$dbo->quote($sto)." AND `idrooms`=".$dbo->quote($roomstr)."".(!$skipdays ? " AND `wdays`='".$wdaystr."'" : "").($skipdays ? " AND (`from` > 0 OR `to` > 0) AND `wdays`=''" : "").($pyeartied == 1 ? " AND `year`=".$tieyear : " AND `year` IS NULL")." AND `idprices`=".$dbo->quote($pricestr)." AND `promo`=".$ppromo." AND `promodaysadv`=" . $dbo->quote($ppromodaysadv) . " AND `promominlos`=" . $dbo->quote($ppromominlos) . " AND `promolastmin`=" . $dbo->quote($promolastmin) . " AND `losoverride`=".$dbo->quote($losverridestr)." AND `occupancy_ovr`".(count($occupancy_ovr) > 0 ? "=".$dbo->quote(json_encode($occupancy_ovr)) : " IS NULL").";";
4860 $dbo->setQuery($q);
4861 $similar = $dbo->loadAssocList();
4862 if ($similar) {
4863 $valid = false;
4864 foreach ($similar as $sim) {
4865 $double_records[] = $sim['spname'];
4866 }
4867 }
4868
4869 $q = "SELECT `id`,`spname` FROM `#__vikbooking_seasons` WHERE `from`>=".$dbo->quote($sfrom)." AND `from`<=".$dbo->quote($sto)." AND `to`>=".$dbo->quote($sfrom)." AND `to`<=".$dbo->quote($sto)." AND `idrooms`=".$dbo->quote($roomstr)."".(!$skipdays ? " AND `wdays`='".$wdaystr."'" : "").($skipdays ? " AND (`from` > 0 OR `to` > 0) AND `wdays`=''" : "").($pyeartied == 1 ? " AND `year`=".$tieyear : " AND `year` IS NULL")." AND `idprices`=".$dbo->quote($pricestr)." AND `promo`=".$ppromo." AND `promodaysadv`=" . $dbo->quote($ppromodaysadv) . " AND `promominlos`=" . $dbo->quote($ppromominlos) . " AND `promolastmin`=" . $dbo->quote($promolastmin) . " AND `losoverride`=".$dbo->quote($losverridestr)." AND `occupancy_ovr`".(count($occupancy_ovr) > 0 ? "=".$dbo->quote(json_encode($occupancy_ovr)) : " IS NULL").";";
4870 $dbo->setQuery($q);
4871 $similar = $dbo->loadAssocList();
4872 if ($similar) {
4873 $valid = false;
4874 foreach ($similar as $sim) {
4875 $double_records[] = $sim['spname'];
4876 }
4877 }
4878 }
4879
4880 if (!$valid && !$ppromo) {
4881 VikError::raiseWarning('', JText::translate('ERRINVDATEROOMSLOCSEASON').(count($double_records) ? ' ('.implode(', ', array_unique($double_records)).')' : ''));
4882 $app->redirect("index.php?option=com_vikbooking&task=newseason");
4883 exit;
4884 }
4885
4886 if ($pchannels && $proundmode) {
4887 /**
4888 * Always disallow rounding when channels supporting promotions are available.
4889 *
4890 * @since 1.18.3 (J) - 1.8.3 (WP)
4891 */
4892 $proundmode = '';
4893 $app->enqueueMessage(sprintf('%s: %s.', JText::translate('VBNEWSEASONROUNDCOST'), JText::translate('VBPARAMPRICECALENDARDISABLED')), 'warning');
4894 }
4895
4896 // insert new record
4897 $sea_record = new stdClass;
4898 $sea_record->type = $ptype == "1" ? 1 : 2;
4899 $sea_record->from = $sfrom;
4900 $sea_record->to = $sto;
4901 $sea_record->diffcost = $pdiffcost;
4902 $sea_record->idrooms = $roomstr;
4903 $sea_record->spname = $pspname;
4904 $sea_record->wdays = $wdaystr;
4905 $sea_record->checkinincl = $pcheckinincl;
4906 $sea_record->val_pcent = $pval_pcent;
4907 $sea_record->losoverride = $losverridestr;
4908 $sea_record->roundmode = !empty($proundmode) ? $proundmode : null;
4909 $sea_record->year = $pyeartied == 1 ? $tieyear : null;
4910 $sea_record->idprices = $pricestr;
4911 $sea_record->promo = $ppromo == 1 ? 1 : 0;
4912 $sea_record->promodaysadv = !empty($ppromodaysadv) ? $ppromodaysadv : null;
4913 $sea_record->promotxt = $ppromotxt;
4914 $sea_record->promominlos = !empty($ppromominlos) ? $ppromominlos : 0;
4915 $sea_record->occupancy_ovr = $occupancy_ovr ? json_encode($occupancy_ovr) : null;
4916 $sea_record->promolastmin = (int)$promolastmin;
4917 $sea_record->promofinalprice = $ppromofinalprice;
4918
4919 $dbo->insertObject('#__vikbooking_seasons', $sea_record, 'id');
4920
4921 $vbo_promo_id = $sea_record->id;
4922
4923 $app->enqueueMessage(JText::translate('VBSEASONSAVED'));
4924
4925 // update session values
4926 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
4927 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
4928 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $first ? $first : $updforvcm['dfrom'];
4929 } else {
4930 $updforvcm['dfrom'] = $first;
4931 }
4932 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
4933 $updforvcm['dto'] = $updforvcm['dto'] < $second ? $second : $updforvcm['dto'];
4934 } else {
4935 $updforvcm['dto'] = $second;
4936 }
4937 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
4938 foreach ($roomids as $rid) {
4939 if (!in_array($rid, $updforvcm['rooms'])) {
4940 $updforvcm['rooms'][] = $rid;
4941 }
4942 }
4943 } else {
4944 $updforvcm['rooms'] = $roomids;
4945 }
4946 if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
4947 foreach ($roomids as $rid) {
4948 if (array_key_exists($rid, $updforvcm['rplans'])) {
4949 $updforvcm['rplans'][$rid] = $updforvcm['rplans'][$rid] + $priceids;
4950 } else {
4951 $updforvcm['rplans'][$rid] = $priceids;
4952 }
4953 }
4954 } else {
4955 $updforvcm['rplans'] = array();
4956 foreach ($roomids as $rid) {
4957 $updforvcm['rplans'][$rid] = $priceids;
4958 }
4959 }
4960 if (!$ppromo) {
4961 $session->set('vbVcmRatesUpd', $updforvcm);
4962 }
4963
4964 /**
4965 * Create the promotion also on the selected channels
4966 *
4967 * @since 1.13.0 (J) - 1.3.0 (WP)
4968 */
4969 if ($ppromo && $pchannels) {
4970 foreach ($pchannels as $channel_key) {
4971 $promo_obj = VikBooking::getPromotionHandlers($channel_key);
4972 if (!is_object($promo_obj)) {
4973 continue;
4974 }
4975 /**
4976 * We inject for VCM the ID of the newly created promotion in VBO.
4977 *
4978 * @since 1.15.0 (J) - 1.5.0 (WP)
4979 */
4980 $ch_result = $promo_obj->createPromotion(array('vbo_promo_id' => $vbo_promo_id), 'new');
4981 if (!$ch_result) {
4982 VikError::raiseWarning('', $promo_obj->getName() . ': ' . $promo_obj->getError());
4983 } else {
4984 $resp = $promo_obj->getResponse();
4985 $app->enqueueMessage($promo_obj->getName() . ': ' . JText::translate('VBOCHPROMOSUCCESS') . (!empty($resp) ? ' (' . str_replace('e4j.ok.', '', $resp) . ')' : ''));
4986 // in case of success, unset the current session values in VCM
4987 $session->set('vcmBPromo', '');
4988 }
4989 }
4990 }
4991
4992 $app->redirect("index.php?option=com_vikbooking&task=".($andnew ? 'newseason' : 'seasons'));
4993 $app->close();
4994 }
4995
4996 public function removeseasons()
4997 {
4998 if (!JSession::checkToken()) {
4999 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5000 }
5001
5002 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
5003 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5004 }
5005
5006 $app = JFactory::getApplication();
5007 $dbo = JFactory::getDbo();
5008
5009 $ids = VikRequest::getVar('cid', array(0));
5010 $pidroom = VikRequest::getInt('idroom', '', 'request');
5011 $pwhere = VikRequest::getInt('where', '', 'request');
5012 if (!empty($pwhere)) {
5013 $ids[] = $pwhere;
5014 }
5015 $tot_removed = array();
5016 $prev_promos = array();
5017 foreach ($ids as $d) {
5018 if (empty($d)) {
5019 continue;
5020 }
5021 // check if it was a promotion
5022 $q = "SELECT `id` FROM `#__vikbooking_seasons` WHERE `id`=" . (int)$d . " AND `promo`=1;";
5023 $dbo->setQuery($q);
5024 $dbo->execute();
5025 if ($dbo->getNumRows()) {
5026 // push it as a previous promo
5027 array_push($prev_promos, $d);
5028 }
5029
5030 // delete the record
5031 $q = "DELETE FROM `#__vikbooking_seasons` WHERE `id`=".$dbo->quote($d).";";
5032 $dbo->setQuery($q);
5033 $dbo->execute();
5034 $tot_removed[] = $d;
5035 }
5036
5037 /**
5038 * Query promotion handlers, if any, to trigger the delete promotion event.
5039 *
5040 * @since 1.15.0 (J) - 1.5.0 (WP)
5041 */
5042 $promo_handlers = VikBooking::getPromotionHandlers();
5043 foreach ($prev_promos as $vbo_promo_id) {
5044 try {
5045 if (is_array($promo_handlers)) {
5046 foreach ($promo_handlers as $promo_handler) {
5047 if (!isset($promo_handler->instance) || !is_object($promo_handler->instance) || !method_exists($promo_handler->instance, 'triggerDelete')) {
5048 // outdated handler object
5049 continue;
5050 }
5051 if (!is_callable(array($promo_handler->instance, 'triggerDelete')) || !$promo_handler->instance->triggerDelete()) {
5052 // promotion handler does not support delete promotion event
5053 continue;
5054 }
5055 // invoke the delete promotion event for this handler
5056 $ch_result = $promo_handler->instance->createPromotion(array('vbo_promo_id' => $vbo_promo_id), 'delete');
5057 if (!$ch_result) {
5058 VikError::raiseWarning('', $promo_handler->instance->getName() . ': ' . $promo_handler->instance->getError());
5059 }
5060 }
5061 }
5062 } catch (Exception $e) {
5063 // do nothing
5064 }
5065 }
5066
5067 $app->enqueueMessage(JText::sprintf('VBRECORDSREMOVED', count($tot_removed)));
5068 $app->redirect("index.php?option=com_vikbooking&task=seasons".(!empty($pidroom) ? '&idroom='.$pidroom : ''));
5069 $app->close();
5070 }
5071
5072 public function updatecustomer()
5073 {
5074 if (!JSession::checkToken()) {
5075 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5076 }
5077
5078 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5079 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5080 }
5081
5082 $this->do_updatecustomer();
5083 }
5084
5085 public function updatecustomerstay()
5086 {
5087 if (!JSession::checkToken()) {
5088 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5089 }
5090
5091 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5092 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5093 }
5094
5095 $this->do_updatecustomer(true);
5096 }
5097
5098 private function do_updatecustomer($stay = false) {
5099 $dbo = JFactory::getDbo();
5100 $mainframe = JFactory::getApplication();
5101 $pfirst_name = VikRequest::getString('first_name', '', 'request');
5102 $plast_name = VikRequest::getString('last_name', '', 'request');
5103 $pcompany = VikRequest::getString('company', '', 'request');
5104 $pvat = VikRequest::getString('vat', '', 'request');
5105 $pemail = VikRequest::getString('email', '', 'request');
5106 $pphone = VikRequest::getString('phone', '', 'request');
5107 $pcountry = VikRequest::getString('country', '', 'request');
5108 $pstate = VikRequest::getString('state', '', 'request');
5109 $ppin = VikRequest::getString('pin', '', 'request');
5110 $pujid = VikRequest::getInt('ujid', '', 'request');
5111 $paddress = VikRequest::getString('address', '', 'request');
5112 $pcity = VikRequest::getString('city', '', 'request');
5113 $pzip = VikRequest::getString('zip', '', 'request');
5114 $pfisccode = VikRequest::getString('fisccode', '', 'request');
5115 $ppec = VikRequest::getString('pec', '', 'request');
5116 $precipcode = VikRequest::getString('recipcode', '', 'request');
5117 $pgender = VikRequest::getString('gender', '', 'request');
5118 $pgender = in_array($pgender, array('F', 'M')) ? $pgender : '';
5119 $pbdate = VikRequest::getString('bdate', '', 'request');
5120 $ppbirth = VikRequest::getString('pbirth', '', 'request');
5121 $pdoctype = VikRequest::getString('doctype', '', 'request');
5122 $pdocnum = VikRequest::getString('docnum', '', 'request');
5123 $pnotes = VikRequest::getString('notes', '', 'request');
5124 $pscandocimg = VikRequest::getString('scandocimg', '', 'request');
5125 $pischannel = VikRequest::getInt('ischannel', '', 'request');
5126 $pcommission = VikRequest::getFloat('commission', '', 'request');
5127 $pcalccmmon = VikRequest::getInt('calccmmon', '', 'request');
5128 $papplycmmon = VikRequest::getInt('applycmmon', '', 'request');
5129 $pchname = VikRequest::getString('chname', '', 'request');
5130 $pchcolor = VikRequest::getString('chcolor', '', 'request');
5131 $pwhere = VikRequest::getInt('where', '', 'request');
5132 $ptmpl = VikRequest::getString('tmpl', '', 'request');
5133 $pcheckin = VikRequest::getInt('checkin', '', 'request');
5134 $pbid = VikRequest::getInt('bid', '', 'request');
5135 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
5136 if (!empty($pwhere) && !empty($pfirst_name) && !empty($plast_name) && !empty($pemail)) {
5137 $q = "SELECT * FROM `#__vikbooking_customers` WHERE `id`=".(int)$pwhere." LIMIT 1;";
5138 $dbo->setQuery($q);
5139 $dbo->execute();
5140 if ($dbo->getNumRows() == 1) {
5141 $customer = $dbo->loadAssoc();
5142 } else {
5143 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5144 exit;
5145 }
5146 /**
5147 * Existing customers are recognized by equal first name, last name and email address.
5148 *
5149 * @since 1.3.0
5150 */
5151 $q = "SELECT * FROM `#__vikbooking_customers` WHERE `first_name`=".$dbo->quote($pfirst_name)." AND `last_name`=".$dbo->quote($plast_name)." AND `email`=".$dbo->quote($pemail)." AND `id`!=".(int)$pwhere." LIMIT 1;";
5152 $dbo->setQuery($q);
5153 $dbo->execute();
5154 if ($dbo->getNumRows() == 0) {
5155 $cpin = VikBooking::getCPinIstance();
5156 if (empty($ppin)) {
5157 $ppin = $customer['pin'];
5158 } elseif ($cpin->pinExists($ppin, $customer['pin'])) {
5159 $ppin = $cpin->generateUniquePin();
5160 }
5161 //file upload
5162 jimport('joomla.filesystem.file');
5163 $pimg = VikRequest::getVar('docimg', null, 'files', 'array');
5164 $gimg = "";
5165 if (isset($pimg) && strlen(trim($pimg['name']))) {
5166 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($pimg['name'])));
5167 $src = $pimg['tmp_name'];
5168 $dest = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans'.DIRECTORY_SEPARATOR;
5169 $j = "";
5170 if (file_exists($dest.$filename)) {
5171 $j = rand(171, 1717);
5172 while (file_exists($dest.$j.$filename)) {
5173 $j++;
5174 }
5175 }
5176 $finaldest = $dest.$j.$filename;
5177 $check = getimagesize($pimg['tmp_name']);
5178 if (($check[2] & imagetypes()) || preg_match("/application\/(zip|pdf)$/", $pimg['type'])) {
5179 if (VikBooking::uploadFile($src, $finaldest)) {
5180 $gimg = $j.$filename;
5181 } else {
5182 VikError::raiseWarning('', 'Error while uploading image');
5183 }
5184 } else {
5185 VikError::raiseWarning('', 'Uploaded file is not an Image');
5186 }
5187 } elseif (!empty($pscandocimg)) {
5188 $gimg = $pscandocimg;
5189 }
5190 //
5191 $pischannel = $pischannel > 0 ? 1 : 0;
5192 $pcalccmmon = $pcalccmmon > 0 ? 1 : 0;
5193 $papplycmmon = $papplycmmon > 0 ? 1 : 0;
5194 $pchname = str_replace(' ', '', trim($pchname));
5195 $pchname = strlen($pchname) <= 0 && $pischannel > 0 ? str_replace(' ', '', trim($pfirst_name.' '.$plast_name)) : $pchname;
5196 $chparams = array(
5197 'commission' => ($pcommission > 0.00 ? $pcommission : 0),
5198 'calccmmon' => $pcalccmmon,
5199 'applycmmon' => $papplycmmon,
5200 'chcolor' => $pchcolor,
5201 'chname' => $pchname
5202 );
5203
5204 /**
5205 * Customer profile picture (URL or uploaded file).
5206 *
5207 * @since 1.15.3 (J) - 1.5.5 (WP)
5208 */
5209 $customer_pic = VikRequest::getString('pic', '', 'request');
5210 $customer_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
5211 if (is_array($customer_pic_img) && !empty($customer_pic_img['name'])) {
5212 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($customer_pic_img['name'])));
5213 $src = $customer_pic_img['tmp_name'];
5214 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
5215 $j = "";
5216 if (is_file($dest.$filename)) {
5217 $j = rand(1, 99999);
5218 while (is_file($dest . $j .$filename)) {
5219 $j++;
5220 }
5221 }
5222 $finaldest = $dest . $j . $filename;
5223 $check = getimagesize($customer_pic_img['tmp_name']);
5224 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
5225 if (VikBooking::uploadFile($src, $finaldest)) {
5226 $customer_pic = $j . $filename;
5227 } else {
5228 VikError::raiseWarning('', 'Error while uploading image');
5229 }
5230 } else {
5231 VikError::raiseWarning('', 'Uploaded file is not an Image');
5232 }
5233 }
5234
5235 // update customer object
5236 $new_customer = new stdClass;
5237 $new_customer->id = (int)$pwhere;
5238 $new_customer->first_name = $pfirst_name;
5239 $new_customer->last_name = $plast_name;
5240 $new_customer->email = $pemail;
5241 $new_customer->phone = $pphone;
5242 $new_customer->country = $pcountry;
5243 $new_customer->pin = $ppin;
5244 $new_customer->ujid = $pujid;
5245 $new_customer->address = $paddress;
5246 $new_customer->city = $pcity;
5247 $new_customer->zip = $pzip;
5248 $new_customer->state = $pstate;
5249 $new_customer->doctype = $pdoctype;
5250 $new_customer->docnum = $pdocnum;
5251 if (!empty($gimg)) {
5252 $new_customer->docimg = $gimg;
5253 }
5254 $new_customer->notes = $pnotes;
5255 $new_customer->ischannel = $pischannel;
5256 $new_customer->chdata = json_encode($chparams);
5257 $new_customer->company = $pcompany;
5258 $new_customer->vat = $pvat;
5259 $new_customer->gender = $pgender;
5260 $new_customer->bdate = $pbdate;
5261 $new_customer->pbirth = $ppbirth;
5262 $new_customer->fisccode = $pfisccode;
5263 $new_customer->pec = $ppec;
5264 $new_customer->recipcode = $precipcode;
5265 $new_customer->pic = $customer_pic;
5266 /**
5267 * We need to update the previous information stored through
5268 * the custom fields when making a reservation for/by this client.
5269 *
5270 * @since 1.13
5271 */
5272 $skip_prev_fields = array(
5273 'id',
5274 'ujid',
5275 'docimg',
5276 'ischannel',
5277 'chdata',
5278 'notes',
5279 );
5280 if (!empty($customer['cfields'])) {
5281 $custf_info = json_decode($customer['cfields'], true);
5282 foreach ($new_customer as $fname => $fnewval) {
5283 if (!isset($customer[$fname]) || in_array($fname, $skip_prev_fields)) {
5284 continue;
5285 }
5286 // seek for old value in custom fields submitted
5287 foreach ($custf_info as $k => $v) {
5288 if (!empty($customer[$fname]) && $v == $customer[$fname]) {
5289 // field found, replace it with the new value
5290 $custf_info[$k] = $fnewval;
5291 }
5292 }
5293 }
5294 // update value on db
5295 $new_customer->cfields = json_encode($custf_info);
5296 }
5297
5298 // trigger the customer before-update event
5299 $cpin->pluginCustomerSync($new_customer->id, 'update', (array)$new_customer, $before = true);
5300
5301 // update customer record
5302 $dbo->updateObject('#__vikbooking_customers', $new_customer, 'id');
5303
5304 // trigger the customer after-save event
5305 $cpin->pluginCustomerSync($new_customer->id, 'update', (array)$new_customer, $before = false);
5306
5307 // update all the bookings affected by this Customer ID as a sales channel
5308 $source_name = 'customer'.$pwhere.'_'.$pchname;
5309 if ($pischannel > 0) {
5310 $oid_clause = '';
5311 if ($customer['ischannel'] < 1) {
5312 //Was not a sales channel but now it is, so update all his bookings
5313 $q = "SELECT `o`.`idorderota`, `co`.`idorder`
5314 FROM `#__vikbooking_customers_orders` AS `co`
5315 LEFT JOIN `#__vikbooking_orders` AS `o` ON `co`.`idorder`=`o`.`id`
5316 WHERE `co`.`idcustomer`=".$customer['id'].";";
5317 $dbo->setQuery($q);
5318 $all_bids = $dbo->loadAssocList();
5319 if ($all_bids) {
5320 $bids = array();
5321 foreach ($all_bids as $bid) {
5322 if (empty($idorderota) && !in_array($bid['idorder'], $bids)) {
5323 $bids[] = $bid['idorder'];
5324 }
5325 }
5326 if ($bids) {
5327 $oid_clause = " OR `id` IN (".implode(',', $bids).")";
5328 }
5329 }
5330 }
5331 $q = "UPDATE `#__vikbooking_orders` SET `channel`=".$dbo->quote($source_name)." WHERE `channel` LIKE 'customer".$pwhere."%'".$oid_clause.";";
5332 } else {
5333 $q = "UPDATE `#__vikbooking_orders` SET `channel`=NULL,`cmms`=NULL WHERE `channel` LIKE 'customer".$pwhere."%';";
5334 }
5335 $dbo->setQuery($q);
5336 $dbo->execute();
5337 //
5338 $mainframe->enqueueMessage(JText::translate('VBCUSTOMERSAVED'));
5339 } else {
5340 //email already exists
5341 $ex_customer = $dbo->loadAssoc();
5342 //check if coming from the Check-in view or not
5343 if (!empty($pcheckin) && !empty($pbid)) {
5344 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5345 /**
5346 * @wponly - this task is executed via Ajax for the Modal forms listener. We must redirect to the booking details page and let the user restart the procedure
5347 */
5348 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5349 //
5350 exit;
5351 } elseif (!empty($pgoto)) {
5352 // check if coming from a specific task
5353 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5354 $mainframe->redirect(base64_decode($pgoto));
5355 exit;
5356 } else {
5357 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').'<br/><a href="index.php?option=com_vikbooking&task=editcustomer&cid[]='.$ex_customer['id'].'" target="_blank">'.$ex_customer['first_name'].' '.$ex_customer['last_name'].'</a>');
5358 $mainframe->redirect("index.php?option=com_vikbooking&task=editcustomer&cid[]=".$pwhere);
5359 exit;
5360 }
5361 }
5362 }
5363
5364 //check if coming from the Check-in view
5365 if (!empty($pcheckin) && !empty($pbid)) {
5366 /**
5367 * @wponly - this task is executed via Ajax for the Modal forms listener. We must redirect to the booking details page and let the user restart the procedure
5368 */
5369 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $pbid);
5370 exit;
5371 }
5372
5373 if ($stay) {
5374 $mainframe->redirect("index.php?option=com_vikbooking&task=editcustomer&cid[]=" . $pwhere . (!empty($pgoto) ? '&goto=' . $pgoto : ''));
5375 exit;
5376 }
5377
5378 // check if coming from a specific task
5379 if (!empty($pgoto)) {
5380 $mainframe->redirect(base64_decode($pgoto));
5381 exit;
5382 }
5383
5384 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5385 }
5386
5387 public function savecustomer() {
5388 if (!JSession::checkToken()) {
5389 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5390 }
5391 $dbo = JFactory::getDbo();
5392 $mainframe = JFactory::getApplication();
5393 $pfirst_name = VikRequest::getString('first_name', '', 'request');
5394 $plast_name = VikRequest::getString('last_name', '', 'request');
5395 $pcompany = VikRequest::getString('company', '', 'request');
5396 $pvat = VikRequest::getString('vat', '', 'request');
5397 $pemail = VikRequest::getString('email', '', 'request');
5398 $pphone = VikRequest::getString('phone', '', 'request');
5399 $pcountry = VikRequest::getString('country', '', 'request');
5400 $pstate = VikRequest::getString('state', '', 'request');
5401 $ppin = VikRequest::getString('pin', '', 'request');
5402 $pujid = VikRequest::getInt('ujid', '', 'request');
5403 $paddress = VikRequest::getString('address', '', 'request');
5404 $pcity = VikRequest::getString('city', '', 'request');
5405 $pzip = VikRequest::getString('zip', '', 'request');
5406 $pfisccode = VikRequest::getString('fisccode', '', 'request');
5407 $ppec = VikRequest::getString('pec', '', 'request');
5408 $precipcode = VikRequest::getString('recipcode', '', 'request');
5409 $pgender = VikRequest::getString('gender', '', 'request');
5410 $pgender = in_array($pgender, array('F', 'M')) ? $pgender : '';
5411 $pbdate = VikRequest::getString('bdate', '', 'request');
5412 $ppbirth = VikRequest::getString('pbirth', '', 'request');
5413 $pdoctype = VikRequest::getString('doctype', '', 'request');
5414 $pdocnum = VikRequest::getString('docnum', '', 'request');
5415 $pnotes = VikRequest::getString('notes', '', 'request');
5416 $pscandocimg = VikRequest::getString('scandocimg', '', 'request');
5417 $pischannel = VikRequest::getInt('ischannel', '', 'request');
5418 $pcommission = VikRequest::getFloat('commission', '', 'request');
5419 $pcalccmmon = VikRequest::getInt('calccmmon', '', 'request');
5420 $papplycmmon = VikRequest::getInt('applycmmon', '', 'request');
5421 $pchname = VikRequest::getString('chname', '', 'request');
5422 $pchcolor = VikRequest::getString('chcolor', '', 'request');
5423 $ptmpl = VikRequest::getString('tmpl', '', 'request');
5424 $pcheckin = VikRequest::getInt('checkin', '', 'request');
5425 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
5426 $pbid = VikRequest::getInt('bid', '', 'request');
5427 if (!empty($pfirst_name) && !empty($plast_name) && !empty($pemail)) {
5428 $cpin = VikBooking::getCPinIstance();
5429 /**
5430 * Existing customers are recognized by equal first name, last name and email address.
5431 *
5432 * @since 1.3.0
5433 */
5434 $q = "SELECT * FROM `#__vikbooking_customers` WHERE `first_name`=".$dbo->quote($pfirst_name)." AND `last_name`=".$dbo->quote($plast_name)." AND `email`=".$dbo->quote($pemail)." LIMIT 1;";
5435 $dbo->setQuery($q);
5436 $dbo->execute();
5437 if ($dbo->getNumRows() == 0) {
5438 if (empty($ppin)) {
5439 $ppin = $cpin->generateUniquePin();
5440 } elseif ($cpin->pinExists($ppin)) {
5441 $ppin = $cpin->generateUniquePin();
5442 }
5443 //file upload
5444 jimport('joomla.filesystem.file');
5445 $pimg = VikRequest::getVar('docimg', null, 'files', 'array');
5446 $gimg = "";
5447 if (isset($pimg) && strlen(trim($pimg['name']))) {
5448 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($pimg['name'])));
5449 $src = $pimg['tmp_name'];
5450 $dest = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans'.DIRECTORY_SEPARATOR;
5451 $j = "";
5452 if (file_exists($dest.$filename)) {
5453 $j = rand(171, 1717);
5454 while (file_exists($dest.$j.$filename)) {
5455 $j++;
5456 }
5457 }
5458 $finaldest = $dest.$j.$filename;
5459 $check = getimagesize($pimg['tmp_name']);
5460 if (($check[2] & imagetypes()) || preg_match("/application\/(zip|pdf)$/", $pimg['type'])) {
5461 if (VikBooking::uploadFile($src, $finaldest)) {
5462 $gimg = $j.$filename;
5463 } else {
5464 VikError::raiseWarning('', 'Error while uploading image');
5465 }
5466 } else {
5467 VikError::raiseWarning('', 'Uploaded file is not an Image');
5468 }
5469 } elseif (!empty($pscandocimg)) {
5470 $gimg = $pscandocimg;
5471 }
5472 //
5473 $pischannel = $pischannel > 0 ? 1 : 0;
5474 $pcalccmmon = $pcalccmmon > 0 ? 1 : 0;
5475 $papplycmmon = $papplycmmon > 0 ? 1 : 0;
5476 $pchname = str_replace(' ', '', trim($pchname));
5477 $pchname = strlen($pchname) <= 0 && $pischannel > 0 ? str_replace(' ', '', trim($pfirst_name.' '.$plast_name)) : $pchname;
5478 $chparams = array(
5479 'commission' => ($pcommission > 0.00 ? $pcommission : 0),
5480 'calccmmon' => $pcalccmmon,
5481 'applycmmon' => $papplycmmon,
5482 'chcolor' => $pchcolor,
5483 'chname' => $pchname
5484 );
5485
5486 /**
5487 * Customer profile picture (URL or uploaded file).
5488 *
5489 * @since 1.15.3 (J) - 1.5.5 (WP)
5490 */
5491 $customer_pic = VikRequest::getString('pic', '', 'request');
5492 $customer_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
5493 if (is_array($customer_pic_img) && !empty($customer_pic_img['name'])) {
5494 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($customer_pic_img['name'])));
5495 $src = $customer_pic_img['tmp_name'];
5496 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
5497 $j = "";
5498 if (is_file($dest.$filename)) {
5499 $j = rand(1, 99999);
5500 while (is_file($dest . $j .$filename)) {
5501 $j++;
5502 }
5503 }
5504 $finaldest = $dest . $j . $filename;
5505 $check = getimagesize($customer_pic_img['tmp_name']);
5506 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
5507 if (VikBooking::uploadFile($src, $finaldest)) {
5508 $customer_pic = $j . $filename;
5509 } else {
5510 VikError::raiseWarning('', 'Error while uploading image');
5511 }
5512 } else {
5513 VikError::raiseWarning('', 'Uploaded file is not an Image');
5514 }
5515 }
5516
5517 // build customer record
5518 $customer_obj = new stdClass;
5519 $customer_obj->first_name = $pfirst_name;
5520 $customer_obj->last_name = $plast_name;
5521 $customer_obj->email = $pemail;
5522 $customer_obj->phone = $pphone;
5523 $customer_obj->country = $pcountry;
5524 $customer_obj->pin = $ppin;
5525 $customer_obj->ujid = $pujid;
5526 $customer_obj->address = $paddress;
5527 $customer_obj->city = $pcity;
5528 $customer_obj->zip = $pzip;
5529 $customer_obj->state = $pstate;
5530 $customer_obj->doctype = $pdoctype;
5531 $customer_obj->docnum = $pdocnum;
5532 $customer_obj->docimg = $gimg;
5533 $customer_obj->notes = $pnotes;
5534 $customer_obj->ischannel = $pischannel;
5535 $customer_obj->chdata = json_encode($chparams);
5536 $customer_obj->company = $pcompany;
5537 $customer_obj->vat = $pvat;
5538 $customer_obj->gender = $pgender;
5539 $customer_obj->bdate = $pbdate;
5540 $customer_obj->pbirth = $ppbirth;
5541 $customer_obj->fisccode = $pfisccode;
5542 $customer_obj->pec = $ppec;
5543 $customer_obj->recipcode = $precipcode;
5544 $customer_obj->pic = !empty($customer_pic) ? $customer_pic : null;
5545
5546 // trigger the customer before-insert event
5547 $cpin->pluginCustomerSync(0, 'insert', (array)$customer_obj, $before = true);
5548
5549 // insert the new customer record
5550 $dbo->insertObject('#__vikbooking_customers', $customer_obj, 'id');
5551 $lid = isset($customer_obj->id) ? $customer_obj->id : null;
5552
5553 // trigger the customer after-save event
5554 $cpin->pluginCustomerSync($lid, 'insert', (array)$customer_obj, $before = false);
5555
5556 if (!empty($lid)) {
5557 $mainframe->enqueueMessage(JText::translate('VBCUSTOMERSAVED'));
5558 //check if coming from the Check-in view
5559 if (!empty($pcheckin) && !empty($pbid)) {
5560 $cpin->setNewPin($ppin);
5561 $cpin->setNewCustomerId($lid);
5562 $cpin->saveCustomerBooking($pbid);
5563 /**
5564 * @wponly - this task is executed via Ajax for the Modal forms listener. We must redirect to the booking details page and let the user restart the procedure
5565 */
5566 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5567 //
5568 exit;
5569 }
5570 // check if coming from a specific task
5571 if (!empty($pgoto) && !empty($pbid)) {
5572 $cpin->setNewPin($ppin);
5573 $cpin->setNewCustomerId($lid);
5574 $cpin->saveCustomerBooking($pbid);
5575 $mainframe->redirect(base64_decode($pgoto));
5576 exit;
5577 }
5578 }
5579 } else {
5580 //email already exists
5581 $ex_customer = $dbo->loadAssoc();
5582 //check if coming from the Check-in view or not
5583 if (!empty($pcheckin) && !empty($pbid)) {
5584 $cpin->setNewPin($ex_customer['pin']);
5585 $cpin->setNewCustomerId($ex_customer['id']);
5586 $cpin->saveCustomerBooking($pbid);
5587 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5588 /**
5589 * @wponly - this task is executed via Ajax for the Modal forms listener. We must redirect to the booking details page and let the user restart the procedure
5590 */
5591 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5592 //
5593 exit;
5594 } elseif (!empty($pgoto) && !empty($pbid)) {
5595 // check if coming from a specific task
5596 $cpin->setNewPin($ex_customer['pin']);
5597 $cpin->setNewCustomerId($ex_customer['id']);
5598 $cpin->saveCustomerBooking($pbid);
5599 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5600 $mainframe->redirect(base64_decode($pgoto));
5601 exit;
5602 } else {
5603 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').'<br/><a href="index.php?option=com_vikbooking&task=editcustomer&cid[]='.$ex_customer['id'].'" target="_blank">'.$ex_customer['first_name'].' '.$ex_customer['last_name'].'</a>');
5604 }
5605 }
5606 }
5607 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5608 }
5609
5610 public function customers() {
5611 VikBookingHelper::printHeader("22");
5612
5613 VikRequest::setVar('view', VikRequest::getCmd('view', 'customers'));
5614
5615 parent::display();
5616
5617 if (VikBooking::showFooter()) {
5618 VikBookingHelper::printFooter();
5619 }
5620 }
5621
5622 public function newcustomer() {
5623 VikBookingHelper::printHeader("22");
5624
5625 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomer'));
5626
5627 parent::display();
5628
5629 if (VikBooking::showFooter()) {
5630 VikBookingHelper::printFooter();
5631 }
5632 }
5633
5634 public function editcustomer() {
5635 VikBookingHelper::printHeader("22");
5636
5637 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomer'));
5638
5639 parent::display();
5640
5641 if (VikBooking::showFooter()) {
5642 VikBookingHelper::printFooter();
5643 }
5644 }
5645
5646 public function removecustomers()
5647 {
5648 if (!JSession::checkToken()) {
5649 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5650 }
5651
5652 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
5653 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5654 }
5655
5656 $ids = VikRequest::getVar('cid', array(0));
5657 if ($ids) {
5658 $dbo = JFactory::getDBO();
5659 $cpin = VikBooking::getCPinIstance();
5660 foreach ($ids as $d) {
5661 $cpin->pluginCustomerSync($d, 'delete');
5662 $q = "DELETE FROM `#__vikbooking_customers` WHERE `id`=".(int)$d.";";
5663 $dbo->setQuery($q);
5664 $dbo->execute();
5665 }
5666 }
5667 $mainframe = JFactory::getApplication();
5668 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5669 }
5670
5671 public function restrictions() {
5672 VikBookingHelper::printHeader("restrictions");
5673
5674 VikRequest::setVar('view', VikRequest::getCmd('view', 'restrictions'));
5675
5676 parent::display();
5677
5678 if (VikBooking::showFooter()) {
5679 VikBookingHelper::printFooter();
5680 }
5681 }
5682
5683 public function newrestriction() {
5684 VikBookingHelper::printHeader("restrictions");
5685
5686 VikRequest::setVar('view', VikRequest::getCmd('view', 'managerestriction'));
5687
5688 parent::display();
5689
5690 if (VikBooking::showFooter()) {
5691 VikBookingHelper::printFooter();
5692 }
5693 }
5694
5695 public function editrestriction() {
5696 VikBookingHelper::printHeader("restrictions");
5697
5698 VikRequest::setVar('view', VikRequest::getCmd('view', 'managerestriction'));
5699
5700 parent::display();
5701
5702 if (VikBooking::showFooter()) {
5703 VikBookingHelper::printFooter();
5704 }
5705 }
5706
5707 public function createrestriction()
5708 {
5709 if (!JSession::checkToken()) {
5710 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5711 }
5712
5713 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
5714 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5715 }
5716
5717 $dbo = JFactory::getDBO();
5718 $session = JFactory::getSession();
5719 $mainframe = JFactory::getApplication();
5720 $updforvcm = $session->get('vbVcmRatesUpd', '');
5721 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
5722 $pname = VikRequest::getString('name', '', 'request');
5723 $pmonth = VikRequest::getInt('month', '', 'request');
5724 $pmonth = empty($pmonth) ? 0 : $pmonth;
5725 $pname = empty($pname) ? 'Restriction '.$pmonth : $pname;
5726 $pdfrom = VikRequest::getString('dfrom', '', 'request');
5727 $pdto = VikRequest::getString('dto', '', 'request');
5728 $pwday = VikRequest::getString('wday', '', 'request');
5729 $pwdaytwo = VikRequest::getString('wdaytwo', '', 'request');
5730 $pwdaytwo = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday == $pwdaytwo ? '' : $pwdaytwo;
5731 $pcomboa = VikRequest::getString('comboa', '', 'request');
5732 $pcomboa = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboa : '';
5733 $pcombob = VikRequest::getString('combob', '', 'request');
5734 $pcombob = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombob : '';
5735 $pcomboc = VikRequest::getString('comboc', '', 'request');
5736 $pcomboc = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboc : '';
5737 $pcombod = VikRequest::getString('combod', '', 'request');
5738 $pcombod = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombod : '';
5739 $combostr = '';
5740 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboa) ? $pcomboa.':' : ':';
5741 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombob) ? $pcombob.':' : ':';
5742 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboc) ? $pcomboc.':' : ':';
5743 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombod) ? $pcombod : '';
5744 $pminlos = VikRequest::getInt('minlos', '', 'request');
5745 $pminlos = $pminlos < 1 ? 1 : $pminlos;
5746 $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
5747 $pmaxlos = empty($pmaxlos) ? 0 : $pmaxlos;
5748 $pmultiplyminlos = VikRequest::getString('multiplyminlos', '', 'request');
5749 $pmultiplyminlos = empty($pmultiplyminlos) ? 0 : 1;
5750 $pallrooms = VikRequest::getString('allrooms', '', 'request');
5751 $pallrooms = $pallrooms == "1" ? 1 : 0;
5752 $pidrooms = VikRequest::getVar('idrooms', array(0));
5753 $ridr = '';
5754 $roomidsforsess = array();
5755 if (!empty($pidrooms) && @count($pidrooms) && $pallrooms == 0) {
5756 foreach ($pidrooms as $idr) {
5757 if (empty($idr)) {
5758 continue;
5759 }
5760 $ridr .= '-'.$idr.'-;';
5761 $roomidsforsess[] = (int)$idr;
5762 }
5763 } elseif ($pallrooms > 0) {
5764 $q = "SELECT `id` FROM `#__vikbooking_rooms`;";
5765 $dbo->setQuery($q);
5766 $dbo->execute();
5767 if ($dbo->getNumRows() > 0) {
5768 $fetchids = $dbo->loadAssocList();
5769 foreach ($fetchids as $fetchid) {
5770 $roomidsforsess[] = (int)$fetchid['id'];
5771 }
5772 }
5773 }
5774 $pcta = VikRequest::getInt('cta', '', 'request');
5775 $pctd = VikRequest::getInt('ctd', '', 'request');
5776 $pctad = VikRequest::getVar('ctad', array());
5777 $pctdd = VikRequest::getVar('ctdd', array());
5778 if ($pminlos == 1 && strlen($pwday) == 0 && empty($pctad) && empty($pctdd) && $pmaxlos < 1) {
5779 // VBO 1.11 - we now allow restrictions with just 1 night of stay
5780 // VikError::raiseWarning('', JText::translate('VBUSELESSRESTRICTION'));
5781 // $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5782 // exit;
5783 }
5784
5785 //check if there are restrictions for this month
5786 if ($pmonth > 0) {
5787 $q = "SELECT `id` FROM `#__vikbooking_restrictions` WHERE `month`='".$pmonth."';";
5788 $dbo->setQuery($q);
5789 $dbo->execute();
5790 if ($dbo->getNumRows() > 0) {
5791 VikError::raiseWarning('', JText::translate('VBRESTRICTIONMONTHEXISTS'));
5792 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5793 exit;
5794 }
5795 $pdfrom = 0;
5796 $pdto = 0;
5797 } else {
5798 //dates range
5799 if (empty($pdfrom) || empty($pdto)) {
5800 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
5801 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5802 exit;
5803 } else {
5804 $housto = $pdfrom == $pdto ? 23 : 0;
5805 $minsto = $pdfrom == $pdto ? 59 : 0;
5806 $secsto = $pdfrom == $pdto ? 59 : 0;
5807 $pdfrom = VikBooking::getDateTimestamp($pdfrom, 0, 0);
5808 $pdto = VikBooking::getDateTimestamp($pdto, $housto, $minsto, $secsto);
5809 }
5810 if ($pdfrom > $pdto) {
5811 // invalid dates in the past
5812 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
5813 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5814 exit;
5815 }
5816 }
5817 //CTA and CTD
5818 $setcta = array();
5819 $setctd = array();
5820 if ($pcta > 0 && count($pctad) > 0) {
5821 foreach ($pctad as $ctwd) {
5822 if (strlen($ctwd)) {
5823 $setcta[] = '-'.(int)$ctwd.'-';
5824 }
5825 }
5826 }
5827 if ($pctd > 0 && count($pctdd) > 0) {
5828 foreach ($pctdd as $ctwd) {
5829 if (strlen($ctwd)) {
5830 $setctd[] = '-'.(int)$ctwd.'-';
5831 }
5832 }
5833 }
5834 //
5835 //update session values
5836 if (!($pdfrom > 0)) {
5837 $attemptyear = (int)date('Y');
5838 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
5839 if ($attemptfrom < time()) {
5840 $attemptyear++;
5841 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
5842 }
5843 $attemptto = mktime(0, 0, 0, $pmonth, date('t', $attemptfrom), $attemptyear);
5844 } else {
5845 $attemptfrom = $pdfrom;
5846 $attemptto = $pdto;
5847 }
5848 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
5849 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
5850 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $attemptfrom ? $attemptfrom : $updforvcm['dfrom'];
5851 } else {
5852 $updforvcm['dfrom'] = $attemptfrom;
5853 }
5854 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
5855 $updforvcm['dto'] = $updforvcm['dto'] < $attemptto ? $attemptto : $updforvcm['dto'];
5856 } else {
5857 $updforvcm['dto'] = $attemptto;
5858 }
5859 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
5860 foreach ($roomidsforsess as $rid) {
5861 if (!in_array($rid, $updforvcm['rooms'])) {
5862 $updforvcm['rooms'][] = $rid;
5863 }
5864 }
5865 } else {
5866 $updforvcm['rooms'] = $roomidsforsess;
5867 }
5868 if (!array_key_exists('rplans', $updforvcm) || !is_array($updforvcm['rplans'])) {
5869 $updforvcm['rplans'] = array();
5870 }
5871 $session->set('vbVcmRatesUpd', $updforvcm);
5872 //
5873 $q = "INSERT INTO `#__vikbooking_restrictions` (`name`,`month`,`wday`,`minlos`,`multiplyminlos`,`maxlos`,`dfrom`,`dto`,`wdaytwo`,`wdaycombo`,`allrooms`,`idrooms`,`ctad`,`ctdd`) VALUES(".$dbo->quote($pname).", '".$pmonth."', ".(strlen($pwday) > 0 ? "'".$pwday."'" : "NULL").", '".$pminlos."', '".$pmultiplyminlos."', '".$pmaxlos."', ".$pdfrom.", ".$pdto.", ".(strlen($pwday) > 0 && strlen($pwdaytwo) > 0 ? intval($pwdaytwo) : "NULL").", ".(strlen($combostr) > 0 ? $dbo->quote($combostr) : "NULL").", ".$pallrooms.", ".(strlen($ridr) > 0 ? $dbo->quote($ridr) : "NULL").", ".(count($setcta) > 0 ? $dbo->quote(implode(',', $setcta)) : "NULL").", ".(count($setctd) > 0 ? $dbo->quote(implode(',', $setctd)) : "NULL").");";
5874 $dbo->setQuery($q);
5875 $dbo->execute();
5876 $lid = $dbo->insertid();
5877 if (!empty($lid)) {
5878 /**
5879 * Repeat restriction on the selected week days until the limit
5880 *
5881 * @since 1.13
5882 */
5883 $prepeat = VikRequest::getInt('repeat', 0, 'request');
5884 $prepeatuntil = VikRequest::getString('repeatuntil', '', 'request');
5885 if ($prepeat > 0 && !empty($prepeatuntil) && $pdfrom > 0 && $pdto > 0) {
5886 $repeat_intervals = array();
5887 $start = getdate($pdfrom);
5888 $end = getdate($pdto);
5889 $wdays = array();
5890 while ($start[0] <= $end[0]) {
5891 // push requested week day
5892 array_push($wdays, $start['wday']);
5893 // next day
5894 $start = getdate(mktime($start['hours'], $start['minutes'], $start['seconds'], $start['mon'], ($start['mday'] + 1), $start['year']));
5895 }
5896 $dtuntil = VikBooking::getDateTimestamp($prepeatuntil, 23, 59, 59);
5897 if (count($wdays) < 7 && $dtuntil > $pdto) {
5898 // increment end date for the repeat
5899 $end = getdate(mktime($end['hours'], $end['minutes'], $end['seconds'], $end['mon'], ($end['mday'] + 1), $end['year']));
5900 //
5901 $until_info = getdate($dtuntil);
5902 $interval = array();
5903 while ($end[0] <= $until_info[0]) {
5904 if (in_array($end['wday'], $wdays)) {
5905 if (!isset($interval['from'])) {
5906 $interval['from'] = $end[0];
5907 }
5908 $interval['to'] = $end[0];
5909 } else {
5910 if (isset($interval['from'])) {
5911 // append interval
5912 array_push($repeat_intervals, $interval);
5913 // reset interval
5914 $interval = array();
5915 }
5916 }
5917 // next day
5918 $end = getdate(mktime($end['hours'], $end['minutes'], $end['seconds'], $end['mon'], ($end['mday'] + 1), $end['year']));
5919 }
5920 if (isset($interval['from'])) {
5921 // append last hanging interval
5922 array_push($repeat_intervals, $interval);
5923 }
5924 if (count($repeat_intervals)) {
5925 // create the repeated records for the calculated intervals
5926 $repeat_count = 2;
5927 foreach ($repeat_intervals as $rp) {
5928 if (date('Y-m-d', $rp['from']) == date('Y-m-d', $rp['to'])) {
5929 // adjust time in case of equal dates (1 single day restriction)
5930 $rpfrom = getdate($rp['from']);
5931 $rpto = getdate($rp['to']);
5932 $rp['from'] = mktime(0, 0, 0, $rpfrom['mon'], $rpfrom['mday'], $rpfrom['year']);
5933 /**
5934 * The end date of the restriction must cover the whole day until 23:59:59.
5935 *
5936 * @since 1.15.4 (J) - 1.5.4 (WP)
5937 */
5938 $rp['to'] = mktime(23, 59, 59, $rpto['mon'], $rpto['mday'], $rpto['year']);
5939 }
5940 // adjust name
5941 $restr_rp_name = $pname . " #{$repeat_count}";
5942 //
5943 $q = "INSERT INTO `#__vikbooking_restrictions` (`name`,`month`,`wday`,`minlos`,`multiplyminlos`,`maxlos`,`dfrom`,`dto`,`wdaytwo`,`wdaycombo`,`allrooms`,`idrooms`,`ctad`,`ctdd`) VALUES(".$dbo->quote($restr_rp_name).", '".$pmonth."', ".(strlen($pwday) > 0 ? "'".$pwday."'" : "NULL").", '".$pminlos."', '".$pmultiplyminlos."', '".$pmaxlos."', ".$rp['from'].", ".$rp['to'].", ".(strlen($pwday) > 0 && strlen($pwdaytwo) > 0 ? intval($pwdaytwo) : "NULL").", ".(strlen($combostr) > 0 ? $dbo->quote($combostr) : "NULL").", ".$pallrooms.", ".(strlen($ridr) > 0 ? $dbo->quote($ridr) : "NULL").", ".(count($setcta) > 0 ? $dbo->quote(implode(',', $setcta)) : "NULL").", ".(count($setctd) > 0 ? $dbo->quote(implode(',', $setctd)) : "NULL").");";
5944 $dbo->setQuery($q);
5945 $dbo->execute();
5946 $lid = $dbo->insertid();
5947 if (!empty($lid)) {
5948 $repeat_count++;
5949 }
5950 }
5951 }
5952 }
5953 }
5954 //
5955 $mainframe->enqueueMessage(JText::translate('VBRESTRICTIONSAVED'));
5956 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
5957 } else {
5958 VikError::raiseWarning('', 'Error while saving');
5959 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5960 }
5961 }
5962
5963 public function updaterestriction()
5964 {
5965 if (!JSession::checkToken()) {
5966 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5967 }
5968
5969 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5970 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5971 }
5972
5973 $dbo = JFactory::getDBO();
5974 $session = JFactory::getSession();
5975 $mainframe = JFactory::getApplication();
5976 $updforvcm = $session->get('vbVcmRatesUpd', '');
5977 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
5978 $pwhere = VikRequest::getInt('where', '', 'request');
5979 $pname = VikRequest::getString('name', '', 'request');
5980 $pmonth = VikRequest::getInt('month', '', 'request');
5981 $pmonth = empty($pmonth) ? 0 : $pmonth;
5982 $pname = empty($pname) ? 'Restriction '.$pmonth : $pname;
5983 $pdfrom = VikRequest::getString('dfrom', '', 'request');
5984 $pdto = VikRequest::getString('dto', '', 'request');
5985 $pwday = VikRequest::getString('wday', '', 'request');
5986 $pwdaytwo = VikRequest::getString('wdaytwo', '', 'request');
5987 $pwdaytwo = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday == $pwdaytwo ? '' : $pwdaytwo;
5988 $pcomboa = VikRequest::getString('comboa', '', 'request');
5989 $pcomboa = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboa : '';
5990 $pcombob = VikRequest::getString('combob', '', 'request');
5991 $pcombob = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombob : '';
5992 $pcomboc = VikRequest::getString('comboc', '', 'request');
5993 $pcomboc = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboc : '';
5994 $pcombod = VikRequest::getString('combod', '', 'request');
5995 $pcombod = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombod : '';
5996 $combostr = '';
5997 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboa) ? $pcomboa.':' : ':';
5998 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombob) ? $pcombob.':' : ':';
5999 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboc) ? $pcomboc.':' : ':';
6000 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombod) ? $pcombod : '';
6001 $pminlos = VikRequest::getInt('minlos', '', 'request');
6002 $pminlos = $pminlos < 1 ? 1 : $pminlos;
6003 $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
6004 $pmaxlos = empty($pmaxlos) ? 0 : $pmaxlos;
6005 $pmultiplyminlos = VikRequest::getString('multiplyminlos', '', 'request');
6006 $pmultiplyminlos = empty($pmultiplyminlos) ? 0 : 1;
6007 $pallrooms = VikRequest::getString('allrooms', '', 'request');
6008 $pallrooms = $pallrooms == "1" ? 1 : 0;
6009 $pidrooms = VikRequest::getVar('idrooms', array(0));
6010 $ridr = '';
6011 $roomidsforsess = array();
6012 if (!empty($pidrooms) && @count($pidrooms) && $pallrooms == 0) {
6013 foreach ($pidrooms as $idr) {
6014 if (empty($idr)) {
6015 continue;
6016 }
6017 $ridr .= '-'.$idr.'-;';
6018 $roomidsforsess[] = (int)$idr;
6019 }
6020 } elseif ($pallrooms > 0) {
6021 $q = "SELECT `id` FROM `#__vikbooking_rooms`;";
6022 $dbo->setQuery($q);
6023 $dbo->execute();
6024 if ($dbo->getNumRows() > 0) {
6025 $fetchids = $dbo->loadAssocList();
6026 foreach ($fetchids as $fetchid) {
6027 $roomidsforsess[] = (int)$fetchid['id'];
6028 }
6029 }
6030 }
6031 $pcta = VikRequest::getInt('cta', '', 'request');
6032 $pctd = VikRequest::getInt('ctd', '', 'request');
6033 $pctad = VikRequest::getVar('ctad', array());
6034 $pctdd = VikRequest::getVar('ctdd', array());
6035 if ($pminlos == 1 && strlen($pwday) == 0 && empty($pctad) && empty($pctdd) && $pmaxlos < 1) {
6036 // VBO 1.11 - we now allow restrictions with just 1 night of stay
6037 // VikError::raiseWarning('', JText::translate('VBUSELESSRESTRICTION'));
6038 // $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6039 // exit;
6040 }
6041 //check if there are restrictions for this month
6042 if ($pmonth > 0) {
6043 $q = "SELECT `id` FROM `#__vikbooking_restrictions` WHERE `month`='".$pmonth."' AND `id`!='".$pwhere."';";
6044 $dbo->setQuery($q);
6045 $dbo->execute();
6046 if ($dbo->getNumRows() > 0) {
6047 VikError::raiseWarning('', JText::translate('VBRESTRICTIONMONTHEXISTS'));
6048 $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6049 exit;
6050 }
6051 $pdfrom = 0;
6052 $pdto = 0;
6053 } else {
6054 //dates range
6055 if (empty($pdfrom) || empty($pdto)) {
6056 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
6057 $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6058 exit;
6059 } else {
6060 $housto = $pdfrom == $pdto ? 23 : 0;
6061 $minsto = $pdfrom == $pdto ? 59 : 0;
6062 $secsto = $pdfrom == $pdto ? 59 : 0;
6063 $pdfrom = VikBooking::getDateTimestamp($pdfrom, 0, 0);
6064 $pdto = VikBooking::getDateTimestamp($pdto, $housto, $minsto, $secsto);
6065 }
6066 if ($pdfrom > $pdto) {
6067 // invalid dates in the past
6068 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
6069 $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6070 exit;
6071 }
6072 }
6073 //CTA and CTD
6074 $setcta = array();
6075 $setctd = array();
6076 if ($pcta > 0 && count($pctad) > 0) {
6077 foreach ($pctad as $ctwd) {
6078 if (strlen($ctwd)) {
6079 $setcta[] = '-'.(int)$ctwd.'-';
6080 }
6081 }
6082 }
6083 if ($pctd > 0 && count($pctdd) > 0) {
6084 foreach ($pctdd as $ctwd) {
6085 if (strlen($ctwd)) {
6086 $setctd[] = '-'.(int)$ctwd.'-';
6087 }
6088 }
6089 }
6090 //
6091 //update session values
6092 if (!($pdfrom > 0)) {
6093 $attemptyear = (int)date('Y');
6094 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
6095 if ($attemptfrom < time()) {
6096 $attemptyear++;
6097 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
6098 }
6099 $attemptto = mktime(0, 0, 0, $pmonth, date('t', $attemptfrom), $attemptyear);
6100 } else {
6101 $attemptfrom = $pdfrom;
6102 $attemptto = $pdto;
6103 }
6104 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
6105 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
6106 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $attemptfrom ? $attemptfrom : $updforvcm['dfrom'];
6107 } else {
6108 $updforvcm['dfrom'] = $attemptfrom;
6109 }
6110 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
6111 $updforvcm['dto'] = $updforvcm['dto'] < $attemptto ? $attemptto : $updforvcm['dto'];
6112 } else {
6113 $updforvcm['dto'] = $attemptto;
6114 }
6115 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
6116 foreach ($roomidsforsess as $rid) {
6117 if (!in_array($rid, $updforvcm['rooms'])) {
6118 $updforvcm['rooms'][] = $rid;
6119 }
6120 }
6121 } else {
6122 $updforvcm['rooms'] = $roomidsforsess;
6123 }
6124 if (!array_key_exists('rplans', $updforvcm) || !is_array($updforvcm['rplans'])) {
6125 $updforvcm['rplans'] = array();
6126 }
6127 $session->set('vbVcmRatesUpd', $updforvcm);
6128 //
6129 $q = "UPDATE `#__vikbooking_restrictions` SET `name`=".$dbo->quote($pname).",`month`='".$pmonth."',`wday`=".(strlen($pwday) > 0 ? "'".$pwday."'" : "NULL").",`minlos`='".$pminlos."',`multiplyminlos`='".$pmultiplyminlos."',`maxlos`='".$pmaxlos."',`dfrom`=".$pdfrom.",`dto`=".$pdto.",`wdaytwo`=".(strlen($pwday) > 0 && strlen($pwdaytwo) > 0 ? intval($pwdaytwo) : "NULL").",`wdaycombo`=".(strlen($combostr) > 0 ? $dbo->quote($combostr) : "NULL").",`allrooms`=".$pallrooms.",`idrooms`=".(strlen($ridr) > 0 ? $dbo->quote($ridr) : "NULL").", `ctad`=".(count($setcta) > 0 ? $dbo->quote(implode(',', $setcta)) : "NULL").", `ctdd`=".(count($setctd) > 0 ? $dbo->quote(implode(',', $setctd)) : "NULL")." WHERE `id`='".$pwhere."';";
6130 $dbo->setQuery($q);
6131 $dbo->execute();
6132 $mainframe->enqueueMessage(JText::translate('VBRESTRICTIONSAVED'));
6133 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
6134 }
6135
6136 public function removerestrictions()
6137 {
6138 if (!JSession::checkToken()) {
6139 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6140 }
6141
6142 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6143 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6144 }
6145
6146 $ids = VikRequest::getVar('cid', array(0));
6147 if ($ids) {
6148 $dbo = JFactory::getDBO();
6149 foreach ($ids as $d) {
6150 $q = "DELETE FROM `#__vikbooking_restrictions` WHERE `id`=".(int)$d.";";
6151 $dbo->setQuery($q);
6152 $dbo->execute();
6153 }
6154 }
6155 $mainframe = JFactory::getApplication();
6156 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
6157 }
6158
6159 public function prices() {
6160 VikBookingHelper::printHeader("1");
6161
6162 VikRequest::setVar('view', VikRequest::getCmd('view', 'prices'));
6163
6164 parent::display();
6165
6166 if (VikBooking::showFooter()) {
6167 VikBookingHelper::printFooter();
6168 }
6169 }
6170
6171 public function newprice() {
6172 VikBookingHelper::printHeader("1");
6173
6174 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageprice'));
6175
6176 parent::display();
6177
6178 if (VikBooking::showFooter()) {
6179 VikBookingHelper::printFooter();
6180 }
6181 }
6182
6183 public function editprice() {
6184 VikBookingHelper::printHeader("1");
6185
6186 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageprice'));
6187
6188 parent::display();
6189
6190 if (VikBooking::showFooter()) {
6191 VikBookingHelper::printFooter();
6192 }
6193 }
6194
6195 public function createprice()
6196 {
6197 if (!JSession::checkToken()) {
6198 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6199 }
6200
6201 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6202 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6203 }
6204
6205 $this->do_createprice();
6206 }
6207
6208 public function createprice_new()
6209 {
6210 if (!JSession::checkToken()) {
6211 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6212 }
6213
6214 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6215 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6216 }
6217
6218 $this->do_createprice(true);
6219 }
6220
6221 private function do_createprice($new = false)
6222 {
6223 $app = JFactory::getApplication();
6224 $dbo = JFactory::getDbo();
6225
6226 $pprice = VikRequest::getString('price', '', 'request');
6227 $pattr = VikRequest::getString('attr', '', 'request');
6228 $ppraliq = VikRequest::getInt('praliq', '', 'request');
6229 $pmeal_plans = (array)VikRequest::getVar('meal_plans', []);
6230 $pbreakfast_included = in_array('breakfast', $pmeal_plans) ? 1 : 0;
6231 $pfree_cancellation = VikRequest::getInt('free_cancellation', 0, 'request');
6232 $pfree_cancellation = $pfree_cancellation == 1 ? 1 : 0;
6233 $pcanc_deadline = VikRequest::getInt('canc_deadline', '', 'request');
6234 $pminlos = VikRequest::getInt('minlos', 0, 'request');
6235 $pminlos = $pminlos < 0 ? 0 : $pminlos;
6236 $pminhadv = VikRequest::getInt('minhadv', '', 'request');
6237 $pminhadv = $pminhadv < 0 ? 0 : $pminhadv;
6238 $pcanc_policy = VikRequest::getString('canc_policy', '', 'request', VIKREQUEST_ALLOWHTML);
6239
6240 $is_derived = $app->input->getInt('is_derived', 0);
6241 $derived_id = $app->input->getUInt('derived_id', 0);
6242 $derived_data = $app->input->get('derived_data', [], 'array');
6243
6244 $parent_id = 0;
6245 $derived_info = null;
6246
6247 if ($is_derived && $derived_id && $derived_data) {
6248 $parent_id = $derived_id;
6249 $derived_info = $derived_data;
6250 $derived_info['mode'] = ($derived_info['mode'] ?? '') == 'charge' ? 'charge' : 'discount';
6251 $derived_info['type'] = ($derived_info['type'] ?? '') == 'absolute' ? 'absolute' : 'percent';
6252 $derived_info['value'] = (float) ($derived_info['value'] ?? 0);
6253 $derived_info['follow_restr'] = isset($derived_info['follow_restr']) ? 1 : 0;
6254 if (!$derived_info['value']) {
6255 $parent_id = 0;
6256 $derived_info = null;
6257 }
6258 }
6259
6260 if (!empty($pprice)) {
6261 $q = "INSERT INTO `#__vikbooking_prices` (`name`,`attr`,`idiva`,`breakfast_included`,`free_cancellation`,`canc_deadline`,`canc_policy`,`minlos`,`minhadv`,`meal_plans`,`derived_id`,`derived_data`) VALUES(" . $dbo->q($pprice) . ", " . $dbo->q($pattr) . ", " . $dbo->q($ppraliq) . ", " . $pbreakfast_included . ", " . $pfree_cancellation . ", " . $pcanc_deadline . ", " . $dbo->q($pcanc_policy) . ", " . $pminlos . ", " . $pminhadv . ", " . $dbo->q(json_encode($pmeal_plans)) . ", {$parent_id}, " . ($derived_info ? $dbo->q(json_encode($derived_info)) : 'NULL') . ");";
6262 $dbo->setQuery($q);
6263 $dbo->execute();
6264
6265 $new_rplan_id = $dbo->insertid();
6266
6267 /**
6268 * Allow to populate base rates for newly created rate plan for all room types using the parent rate.
6269 *
6270 * @since 1.18.6 (J) - 1.8.6 (WP)
6271 */
6272 if ($app->input->getBool('set_derived_rates', false) && $is_derived && $derived_id && $derived_info) {
6273 // find all rooms with base rates defined for the parent rate plan
6274 $dbo->setQuery(
6275 $dbo->getQuery(true)
6276 ->select($dbo->qn('idroom'))
6277 ->from($dbo->qn('#__vikbooking_dispcost'))
6278 ->where($dbo->qn('idprice') . ' = ' . $derived_id)
6279 ->group($dbo->qn('idroom'))
6280 ->order($dbo->qn('idroom') . ' ASC')
6281 );
6282 $populateRoomIds = array_map('intval', $dbo->loadColumn());
6283
6284 // determine rates table range of nights of stays
6285 $fromNights = $pminlos ?: 1;
6286 $maxNights = $app->input->getUInt('set_max_nights') ?: $pminlos ?: 1;
6287 $maxNights = $maxNights < $fromNights ? $fromNights : $maxNights;
6288
6289 // fetch base rates for all the involved room types
6290 $dbo->setQuery(
6291 $dbo->getQuery(true)
6292 ->select([
6293 $dbo->qn('idroom'),
6294 $dbo->qn('days'),
6295 $dbo->qn('cost'),
6296 ])
6297 ->from($dbo->qn('#__vikbooking_dispcost'))
6298 ->where($dbo->qn('idroom') . ' IN (' . implode(', ', $populateRoomIds) . ')')
6299 ->where($dbo->qn('idprice') . ' = ' . $derived_id)
6300 ->order($dbo->qn('idroom') . ' ASC')
6301 ->order($dbo->qn('days') . ' ASC')
6302 );
6303 $roomBaseRates = $dbo->loadAssocList();
6304
6305 // iterate all rooms involved
6306 foreach ($populateRoomIds as $roomId) {
6307 // loop through the interval of nights of stay
6308 for ($n = $fromNights; $n <= $maxNights; $n++) {
6309 // fetch current room rate in parent rate plan
6310 $roomParentNightlyRate = 0;
6311 $roomParentExactRate = 0;
6312 foreach ($roomBaseRates as $roomBaseRate) {
6313 if ($roomBaseRate['idroom'] != $roomId) {
6314 // ignore room
6315 continue;
6316 }
6317 if (!$roomParentNightlyRate) {
6318 // set rate for the lowest number of nights of stay
6319 $roomParentNightlyRate = $roomBaseRate['cost'] / ($roomBaseRate['days'] ?: 1);
6320 }
6321 if ($roomBaseRate['days'] == $n) {
6322 // set room exact rate for this number of nights of stay
6323 $roomParentExactRate = $roomBaseRate['cost'];
6324 // do not proceed
6325 break;
6326 }
6327 }
6328
6329 if (!$roomParentNightlyRate) {
6330 // missing pricing information from parent rate plan
6331 continue;
6332 }
6333
6334 // determine the cost to apply for the newly created derived rate plan
6335 $nightlyDerivedRate = $roomParentExactRate ?: $roomParentNightlyRate;
6336
6337 // check how the new rate was derived
6338 if ($derived_info['mode'] == 'charge') {
6339 // increase rate
6340 if ($derived_info['type'] == 'absolute') {
6341 // fixed increase
6342 $nightlyDerivedRate += $derived_info['value'];
6343 } else {
6344 // percent increase
6345 $nightlyDerivedRate *= (100 + $derived_info['value']) / 100;
6346 }
6347 } else {
6348 // discount rate
6349 if ($derived_info['type'] == 'absolute') {
6350 // fixed discount
6351 $nightlyDerivedRate -= $derived_info['value'];
6352 } else {
6353 // percent discount
6354 $nightlyDerivedRate *= (100 - $derived_info['value']) / 100;
6355 }
6356 }
6357
6358 if (!$roomParentExactRate) {
6359 // multiply rate by number of nights of stay if started from the parent lowest number of nights
6360 $nightlyDerivedRate *= $n;
6361 }
6362
6363 // build new room base rate record
6364 $rateRecord = [
6365 'idroom' => $roomId,
6366 'days' => $n,
6367 'idprice' => $new_rplan_id,
6368 'cost' => round($nightlyDerivedRate, 2),
6369 ];
6370
6371 // cast to object
6372 $rateRecord = (object) $rateRecord;
6373
6374 // insert record
6375 $dbo->insertObject('#__vikbooking_dispcost', $rateRecord, 'id');
6376 }
6377 }
6378 }
6379 }
6380
6381 $app->redirect("index.php?option=com_vikbooking&task=" . ($new ? 'newprice' : 'prices'));
6382 $app->close();
6383 }
6384
6385 public function updateprice()
6386 {
6387 if (!JSession::checkToken()) {
6388 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6389 }
6390
6391 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6392 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6393 }
6394
6395 $this->do_updateprice();
6396 }
6397
6398 public function updatepricestay()
6399 {
6400 if (!JSession::checkToken()) {
6401 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6402 }
6403
6404 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6405 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6406 }
6407
6408 $this->do_updateprice(true);
6409 }
6410
6411 private function do_updateprice($stay = false)
6412 {
6413 $app = JFactory::getApplication();
6414 $dbo = JFactory::getDbo();
6415
6416 $pprice = VikRequest::getString('price', '', 'request');
6417 $pattr = VikRequest::getString('attr', '', 'request');
6418 $ppraliq = VikRequest::getInt('praliq', '', 'request');
6419 $pmeal_plans = (array)VikRequest::getVar('meal_plans', []);
6420 $pbreakfast_included = in_array('breakfast', $pmeal_plans) ? 1 : 0;
6421 $pfree_cancellation = VikRequest::getInt('free_cancellation', '', 'request');
6422 $pfree_cancellation = $pfree_cancellation == 1 ? 1 : 0;
6423 $pcanc_deadline = VikRequest::getInt('canc_deadline', '', 'request');
6424 $pminlos = VikRequest::getInt('minlos', 0, 'request');
6425 $pminlos = $pminlos < 0 ? 0 : $pminlos;
6426 $pminhadv = VikRequest::getInt('minhadv', '', 'request');
6427 $pminhadv = $pminhadv < 0 ? 0 : $pminhadv;
6428 $pcanc_policy = VikRequest::getString('canc_policy', '', 'request', VIKREQUEST_ALLOWHTML);
6429 $pwhereup = VikRequest::getInt('whereup', 0, 'request');
6430
6431 $is_derived = $app->input->getInt('is_derived', 0);
6432 $derived_id = $app->input->getUInt('derived_id', 0);
6433 $derived_data = $app->input->get('derived_data', [], 'array');
6434
6435 $parent_id = 0;
6436 $derived_info = null;
6437
6438 if ($is_derived && $derived_id && $derived_data) {
6439 $parent_id = $derived_id;
6440 $derived_info = $derived_data;
6441 $derived_info['mode'] = ($derived_info['mode'] ?? '') == 'charge' ? 'charge' : 'discount';
6442 $derived_info['type'] = ($derived_info['type'] ?? '') == 'absolute' ? 'absolute' : 'percent';
6443 $derived_info['value'] = (float) ($derived_info['value'] ?? 0);
6444 $derived_info['follow_restr'] = isset($derived_info['follow_restr']) ? 1 : 0;
6445 if (!$derived_info['value']) {
6446 $parent_id = 0;
6447 $derived_info = null;
6448 }
6449 }
6450
6451 if (!empty($pprice) && $pwhereup) {
6452 $q = "UPDATE `#__vikbooking_prices` SET `name`=" . $dbo->q($pprice) . ",`attr`=" . $dbo->q($pattr) . ",`idiva`=" . $dbo->q($ppraliq) . ",`breakfast_included`=" . $pbreakfast_included . ",`free_cancellation`=" . $pfree_cancellation . ",`canc_deadline`=" . $pcanc_deadline . ",`canc_policy`=" . $dbo->q($pcanc_policy) . ",`minlos`=" . $pminlos . ",`minhadv`=" . $pminhadv . ",`meal_plans`=" . $dbo->q(json_encode($pmeal_plans)) . ",`derived_id`={$parent_id},`derived_data`=" . ($derived_info ? $dbo->q(json_encode($derived_info)) : 'NULL') . " WHERE `id`=" . $dbo->q($pwhereup) . ";";
6453 $dbo->setQuery($q);
6454 $dbo->execute();
6455 }
6456
6457 $app->redirect("index.php?option=com_vikbooking&task=" . ($stay ? 'editprice&cid[]=' . $pwhereup : 'prices'));
6458 $app->close();
6459 }
6460
6461 public function removeprice()
6462 {
6463 if (!JSession::checkToken()) {
6464 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6465 }
6466
6467 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6468 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6469 }
6470
6471 $ids = VikRequest::getVar('cid', array(0));
6472 if ($ids) {
6473 $dbo = JFactory::getDBO();
6474 foreach ($ids as $d) {
6475 $q = "DELETE FROM `#__vikbooking_prices` WHERE `id`=".$dbo->quote($d).";";
6476 $dbo->setQuery($q);
6477 $dbo->execute();
6478 $q = "DELETE FROM `#__vikbooking_dispcost` WHERE `idprice`=".intval($d).";";
6479 $dbo->setQuery($q);
6480 $dbo->execute();
6481 }
6482 }
6483 $mainframe = JFactory::getApplication();
6484 $mainframe->redirect("index.php?option=com_vikbooking&task=prices");
6485 }
6486
6487 public function iva() {
6488 VikBookingHelper::printHeader("2");
6489
6490 VikRequest::setVar('view', VikRequest::getCmd('view', 'iva'));
6491
6492 parent::display();
6493
6494 if (VikBooking::showFooter()) {
6495 VikBookingHelper::printFooter();
6496 }
6497 }
6498
6499 public function newiva() {
6500 VikBookingHelper::printHeader("2");
6501
6502 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageiva'));
6503
6504 parent::display();
6505
6506 if (VikBooking::showFooter()) {
6507 VikBookingHelper::printFooter();
6508 }
6509 }
6510
6511 public function editiva() {
6512 VikBookingHelper::printHeader("2");
6513
6514 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageiva'));
6515
6516 parent::display();
6517
6518 if (VikBooking::showFooter()) {
6519 VikBookingHelper::printFooter();
6520 }
6521 }
6522
6523 public function createiva()
6524 {
6525 if (!JSession::checkToken()) {
6526 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6527 }
6528
6529 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6530 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6531 }
6532
6533 $paliqname = VikRequest::getString('aliqname', '', 'request');
6534 $paliqperc = VikRequest::getFloat('aliqperc', '', 'request');
6535 $pbreakdown_name = VikRequest::getVar('breakdown_name', array());
6536 $pbreakdown_rate = VikRequest::getVar('breakdown_rate', array());
6537 $ptaxcap = VikRequest::getFloat('taxcap', 0, 'request');
6538 if (!empty($paliqperc)) {
6539 $dbo = JFactory::getDBO();
6540 $breakdown_str = '';
6541 if (count($pbreakdown_name) > 0) {
6542 $breakdown_values = array();
6543 $bkcount = 0;
6544 $tot_sub_aliq = 0;
6545 foreach ($pbreakdown_name as $key => $subtax) {
6546 if (!empty($subtax) && floatval($pbreakdown_rate[$key]) > 0) {
6547 $breakdown_values[$bkcount]['name'] = $subtax;
6548 $breakdown_values[$bkcount]['aliq'] = (float)$pbreakdown_rate[$key];
6549 $tot_sub_aliq += (float)$pbreakdown_rate[$key];
6550 $bkcount++;
6551 }
6552 }
6553 if (count($breakdown_values) > 0) {
6554 $breakdown_str = json_encode($breakdown_values);
6555 if ($tot_sub_aliq < (float)$paliqperc || $tot_sub_aliq > (float)$paliqperc) {
6556 VikError::raiseWarning('', JText::translate('VBOTAXBKDWNERRNOMATCH'));
6557 }
6558 }
6559 }
6560 $q = "INSERT INTO `#__vikbooking_iva` (`name`,`aliq`,`breakdown`,`taxcap`) VALUES(".$dbo->quote($paliqname).", ".$dbo->quote($paliqperc).", ".(empty($breakdown_str) ? 'NULL' : $dbo->quote($breakdown_str)).", ".($ptaxcap > 0 ? $dbo->quote($ptaxcap) : 'NULL').");";
6561 $dbo->setQuery($q);
6562 $dbo->execute();
6563 }
6564 $mainframe = JFactory::getApplication();
6565 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6566 }
6567
6568 public function updateiva()
6569 {
6570 if (!JSession::checkToken()) {
6571 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6572 }
6573
6574 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6575 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6576 }
6577
6578 $paliqname = VikRequest::getString('aliqname', '', 'request');
6579 $paliqperc = VikRequest::getFloat('aliqperc', '', 'request');
6580 $pbreakdown_name = VikRequest::getVar('breakdown_name', array());
6581 $pbreakdown_rate = VikRequest::getVar('breakdown_rate', array());
6582 $ptaxcap = VikRequest::getFloat('taxcap', 0, 'request');
6583 $pwhereup = VikRequest::getInt('whereup', 0, 'request');
6584 if (!empty($paliqperc)) {
6585 $dbo = JFactory::getDBO();
6586 $breakdown_str = '';
6587 if (count($pbreakdown_name) > 0) {
6588 $breakdown_values = array();
6589 $bkcount = 0;
6590 $tot_sub_aliq = 0;
6591 foreach ($pbreakdown_name as $key => $subtax) {
6592 if (!empty($subtax) && floatval($pbreakdown_rate[$key]) > 0) {
6593 $breakdown_values[$bkcount]['name'] = $subtax;
6594 $breakdown_values[$bkcount]['aliq'] = (float)$pbreakdown_rate[$key];
6595 $tot_sub_aliq += (float)$pbreakdown_rate[$key];
6596 $bkcount++;
6597 }
6598 }
6599 if (count($breakdown_values) > 0) {
6600 $breakdown_str = json_encode($breakdown_values);
6601 if ($tot_sub_aliq < (float)$paliqperc || $tot_sub_aliq > (float)$paliqperc) {
6602 VikError::raiseWarning('', JText::translate('VBOTAXBKDWNERRNOMATCH'));
6603 }
6604 }
6605 }
6606 $q = "UPDATE `#__vikbooking_iva` SET `name`=".$dbo->quote($paliqname).",`aliq`=".$dbo->quote($paliqperc).",`breakdown`=".(empty($breakdown_str) ? 'NULL' : $dbo->quote($breakdown_str)).",`taxcap`=".($ptaxcap > 0 ? $dbo->quote($ptaxcap) : 'NULL')." WHERE `id`=".$dbo->quote($pwhereup).";";
6607 $dbo->setQuery($q);
6608 $dbo->execute();
6609 }
6610 $mainframe = JFactory::getApplication();
6611 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6612 }
6613
6614 public function removeiva()
6615 {
6616 if (!JSession::checkToken()) {
6617 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6618 }
6619
6620 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6621 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6622 }
6623
6624 $ids = VikRequest::getVar('cid', array(0));
6625 if ($ids) {
6626 $dbo = JFactory::getDBO();
6627 foreach ($ids as $d) {
6628 $q = "DELETE FROM `#__vikbooking_iva` WHERE `id`=".$dbo->quote($d).";";
6629 $dbo->setQuery($q);
6630 $dbo->execute();
6631 }
6632 }
6633 $mainframe = JFactory::getApplication();
6634 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6635 }
6636
6637 public function categories() {
6638 VikBookingHelper::printHeader("4");
6639
6640 VikRequest::setVar('view', VikRequest::getCmd('view', 'categories'));
6641
6642 parent::display();
6643
6644 if (VikBooking::showFooter()) {
6645 VikBookingHelper::printFooter();
6646 }
6647 }
6648
6649 public function newcat() {
6650 VikBookingHelper::printHeader("4");
6651
6652 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecategory'));
6653
6654 parent::display();
6655
6656 if (VikBooking::showFooter()) {
6657 VikBookingHelper::printFooter();
6658 }
6659 }
6660
6661 public function editcat() {
6662 VikBookingHelper::printHeader("4");
6663
6664 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecategory'));
6665
6666 parent::display();
6667
6668 if (VikBooking::showFooter()) {
6669 VikBookingHelper::printFooter();
6670 }
6671 }
6672
6673 public function createcat()
6674 {
6675 if (!JSession::checkToken()) {
6676 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6677 }
6678
6679 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6680 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6681 }
6682
6683 $pcatname = VikRequest::getString('catname', '', 'request');
6684 $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWHTML);
6685 if (!empty($pcatname)) {
6686 $dbo = JFactory::getDBO();
6687 $q = "INSERT INTO `#__vikbooking_categories` (`name`,`descr`) VALUES(".$dbo->quote($pcatname).", ".$dbo->quote($pdescr).");";
6688 $dbo->setQuery($q);
6689 $dbo->execute();
6690 }
6691 $mainframe = JFactory::getApplication();
6692 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6693 }
6694
6695 public function updatecat()
6696 {
6697 if (!JSession::checkToken()) {
6698 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6699 }
6700
6701 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6702 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6703 }
6704
6705 $pcatname = VikRequest::getString('catname', '', 'request');
6706 $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWHTML);
6707 $pwhereup = VikRequest::getString('whereup', '', 'request');
6708 if (!empty($pcatname)) {
6709 $dbo = JFactory::getDBO();
6710 $q = "UPDATE `#__vikbooking_categories` SET `name`=".$dbo->quote($pcatname).", `descr`=".$dbo->quote($pdescr)." WHERE `id`=".$dbo->quote($pwhereup).";";
6711 $dbo->setQuery($q);
6712 $dbo->execute();
6713 }
6714 $mainframe = JFactory::getApplication();
6715 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6716 }
6717
6718 public function removecat()
6719 {
6720 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6721 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6722 }
6723
6724 $ids = VikRequest::getVar('cid', array(0));
6725 if ($ids) {
6726 $dbo = JFactory::getDBO();
6727 foreach ($ids as $d) {
6728 $q = "DELETE FROM `#__vikbooking_categories` WHERE `id`=".$dbo->quote($d).";";
6729 $dbo->setQuery($q);
6730 $dbo->execute();
6731 }
6732 }
6733 $mainframe = JFactory::getApplication();
6734 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6735 }
6736
6737 public function carat() {
6738 VikBookingHelper::printHeader("5");
6739
6740 VikRequest::setVar('view', VikRequest::getCmd('view', 'carat'));
6741
6742 parent::display();
6743
6744 if (VikBooking::showFooter()) {
6745 VikBookingHelper::printFooter();
6746 }
6747 }
6748
6749 public function newcarat() {
6750 VikBookingHelper::printHeader("5");
6751
6752 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecarat'));
6753
6754 parent::display();
6755
6756 if (VikBooking::showFooter()) {
6757 VikBookingHelper::printFooter();
6758 }
6759 }
6760
6761 public function editcarat() {
6762 VikBookingHelper::printHeader("5");
6763
6764 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecarat'));
6765
6766 parent::display();
6767
6768 if (VikBooking::showFooter()) {
6769 VikBookingHelper::printFooter();
6770 }
6771 }
6772
6773 public function createcarat()
6774 {
6775 if (!JSession::checkToken()) {
6776 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6777 }
6778
6779 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6780 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6781 }
6782
6783 $pcaratname = VikRequest::getString('caratname', '', 'request');
6784 $pcarattextimg = VikRequest::getString('carattextimg', '', 'request', VIKREQUEST_ALLOWRAW);
6785 $pautoresize = VikRequest::getString('autoresize', '', 'request');
6786 $presizeto = VikRequest::getString('resizeto', '', 'request');
6787 $pidrooms = VikRequest::getVar('idrooms', array());
6788 if (!empty($pcaratname)) {
6789 if (intval($_FILES['caraticon']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['caraticon']['name'])!="") {
6790 jimport('joomla.filesystem.file');
6791 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
6792 if (@is_uploaded_file($_FILES['caraticon']['tmp_name'])) {
6793 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['caraticon']['name'])));
6794 if (file_exists($updpath.$safename)) {
6795 $j=1;
6796 while (file_exists($updpath.$j.$safename)) {
6797 $j++;
6798 }
6799 $pwhere=$updpath.$j.$safename;
6800 } else {
6801 $j="";
6802 $pwhere=$updpath.$safename;
6803 }
6804 if (!getimagesize($_FILES['caraticon']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
6805 @unlink($pwhere);
6806 $picon="";
6807 } else {
6808 VikBooking::uploadFile($_FILES['caraticon']['tmp_name'], $pwhere);
6809 @chmod($pwhere, 0644);
6810 $picon=$j.$safename;
6811 if ($pautoresize=="1" && !empty($presizeto)) {
6812 $eforj = new vikResizer();
6813 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
6814 if ($origmod) {
6815 @unlink($pwhere);
6816 $picon='r_'.$j.$safename;
6817 }
6818 }
6819 }
6820 } else {
6821 $picon="";
6822 }
6823 } else {
6824 $picon="";
6825 }
6826 $dbo = JFactory::getDbo();
6827 // get new ordering
6828 $q = "SELECT `ordering` FROM `#__vikbooking_characteristics` ORDER BY `#__vikbooking_characteristics`.`ordering` DESC LIMIT 1;";
6829 $dbo->setQuery($q);
6830 $dbo->execute();
6831 if ($dbo->getNumRows()) {
6832 $newsortnum = $dbo->loadResult() + 1;
6833 } else {
6834 $newsortnum = 1;
6835 }
6836 $pordering = VikRequest::getInt('ordering', 0, 'request');
6837 $newsortnum = !empty($pordering) ? $pordering : $newsortnum;
6838 //
6839 $q = "INSERT INTO `#__vikbooking_characteristics` (`name`,`icon`,`textimg`,`ordering`) VALUES(".$dbo->quote($pcaratname).", ".$dbo->quote($picon).", ".$dbo->quote($pcarattextimg).", {$newsortnum});";
6840 $dbo->setQuery($q);
6841 $dbo->execute();
6842
6843 $new_carat_id = $dbo->insertid();
6844 if (!empty($new_carat_id)) {
6845 // assign/unset carat-rooms relations
6846 $rooms_with_carat = array();
6847 if (count($pidrooms)) {
6848 // assign this new carat to the requested rooms
6849 foreach ($pidrooms as $idroom) {
6850 if (empty($idroom)) {
6851 continue;
6852 }
6853 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
6854 $dbo->setQuery($q);
6855 $dbo->execute();
6856 if (!$dbo->getNumRows()) {
6857 continue;
6858 }
6859 $room_data = $dbo->loadAssoc();
6860 array_push($rooms_with_carat, $room_data['id']);
6861 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6862 if (in_array((string)$new_carat_id, $current_carats)) {
6863 continue;
6864 }
6865 if (count($current_carats) === 1 && (string)$current_carats[0] == '0') {
6866 // make sure we do not concatenate a real ID to 0
6867 $current_carats = array();
6868 }
6869 array_push($current_carats, $new_carat_id);
6870 $new_opts = implode(';', $current_carats) . ';';
6871 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
6872 $dbo->setQuery($q);
6873 $dbo->execute();
6874 }
6875 }
6876 if (!count($rooms_with_carat)) {
6877 // get all rooms to unset this carat (if previously set)
6878 array_push($rooms_with_carat, '0');
6879 }
6880 // unset the carat from the other rooms that may have it
6881 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_carat) . ");";
6882 $dbo->setQuery($q);
6883 $dbo->execute();
6884 if ($dbo->getNumRows()) {
6885 $unset_rooms_carat = $dbo->loadAssocList();
6886 foreach ($unset_rooms_carat as $room_data) {
6887 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6888 if (!in_array((string)$new_carat_id, $current_carats)) {
6889 // this room is not using this carat
6890 continue;
6891 }
6892 $caratkey = array_search((string)$new_carat_id, $current_carats);
6893 if ($caratkey === false) {
6894 // key not found
6895 continue;
6896 }
6897 // unset this carat ID from the string
6898 unset($current_carats[$caratkey]);
6899 if (!count($current_carats)) {
6900 // a room with no carats assigned will be listed as "0;"
6901 $current_carats = array(0);
6902 }
6903 $new_opts = implode(';', $current_carats) . ';';
6904 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
6905 $dbo->setQuery($q);
6906 $dbo->execute();
6907 }
6908 }
6909 //
6910 }
6911 }
6912 $mainframe = JFactory::getApplication();
6913 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
6914 }
6915
6916 public function updatecarat()
6917 {
6918 if (!JSession::checkToken()) {
6919 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6920 }
6921
6922 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6923 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6924 }
6925
6926 $pcaratname = VikRequest::getString('caratname', '', 'request');
6927 $pcarattextimg = VikRequest::getString('carattextimg', '', 'request', VIKREQUEST_ALLOWRAW);
6928 $pwhereup = VikRequest::getString('whereup', '', 'request');
6929 $pautoresize = VikRequest::getString('autoresize', '', 'request');
6930 $presizeto = VikRequest::getString('resizeto', '', 'request');
6931 $pidrooms = VikRequest::getVar('idrooms', array());
6932 $pordering = VikRequest::getInt('ordering', 1, 'request');
6933 if (!empty($pcaratname)) {
6934 if (intval($_FILES['caraticon']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['caraticon']['name'])!="") {
6935 jimport('joomla.filesystem.file');
6936 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
6937 if (@is_uploaded_file($_FILES['caraticon']['tmp_name'])) {
6938 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['caraticon']['name'])));
6939 if (file_exists($updpath.$safename)) {
6940 $j=1;
6941 while (file_exists($updpath.$j.$safename)) {
6942 $j++;
6943 }
6944 $pwhere=$updpath.$j.$safename;
6945 } else {
6946 $j="";
6947 $pwhere=$updpath.$safename;
6948 }
6949 if (!getimagesize($_FILES['caraticon']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
6950 @unlink($pwhere);
6951 $picon="";
6952 } else {
6953 VikBooking::uploadFile($_FILES['caraticon']['tmp_name'], $pwhere);
6954 @chmod($pwhere, 0644);
6955 $picon=$j.$safename;
6956 if ($pautoresize=="1" && !empty($presizeto)) {
6957 $eforj = new vikResizer();
6958 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
6959 if ($origmod) {
6960 @unlink($pwhere);
6961 $picon='r_'.$j.$safename;
6962 }
6963 }
6964 }
6965 } else {
6966 $picon="";
6967 }
6968 } else {
6969 $picon="";
6970 }
6971 $dbo = JFactory::getDbo();
6972 $q = "UPDATE `#__vikbooking_characteristics` SET `name`=".$dbo->quote($pcaratname).",".(strlen($picon) > 0 ? "`icon`='".$picon."'," : "")."`textimg`=".$dbo->quote($pcarattextimg).",`ordering`={$pordering} WHERE `id`=".$dbo->quote($pwhereup).";";
6973 $dbo->setQuery($q);
6974 $dbo->execute();
6975
6976 // assign/unset carat-rooms relations
6977 $rooms_with_carat = array();
6978 if (count($pidrooms)) {
6979 // assign this new carat to the requested rooms
6980 foreach ($pidrooms as $idroom) {
6981 if (empty($idroom)) {
6982 continue;
6983 }
6984 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
6985 $dbo->setQuery($q);
6986 $dbo->execute();
6987 if (!$dbo->getNumRows()) {
6988 continue;
6989 }
6990 $room_data = $dbo->loadAssoc();
6991 array_push($rooms_with_carat, $room_data['id']);
6992 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6993 if (in_array((string)$pwhereup, $current_carats)) {
6994 continue;
6995 }
6996 if (count($current_carats) === 1 && (string)$current_carats[0] == '0') {
6997 // make sure we do not concatenate a real ID to 0
6998 $current_carats = array();
6999 }
7000 array_push($current_carats, $pwhereup);
7001 $new_carats = implode(';', $current_carats) . ';';
7002 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_carats) . " WHERE `id`={$room_data['id']};";
7003 $dbo->setQuery($q);
7004 $dbo->execute();
7005 }
7006 }
7007 if (!count($rooms_with_carat)) {
7008 // get all rooms to unset this carat (if previously set)
7009 array_push($rooms_with_carat, '0');
7010 }
7011 // unset the carat from the other rooms that may have it
7012 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_carat) . ");";
7013 $dbo->setQuery($q);
7014 $dbo->execute();
7015 if ($dbo->getNumRows()) {
7016 $unset_rooms_carat = $dbo->loadAssocList();
7017 foreach ($unset_rooms_carat as $room_data) {
7018 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
7019 if (!in_array((string)$pwhereup, $current_carats)) {
7020 // this room is not using this carat
7021 continue;
7022 }
7023 $caratkey = array_search((string)$pwhereup, $current_carats);
7024 if ($caratkey === false) {
7025 // key not found
7026 continue;
7027 }
7028 // unset this carat ID from the string
7029 unset($current_carats[$caratkey]);
7030 if (!count($current_carats)) {
7031 // a room with no carats assigned will be listed as "0;"
7032 $current_carats = array(0);
7033 }
7034 $new_carats = implode(';', $current_carats) . ';';
7035 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_carats) . " WHERE `id`={$room_data['id']};";
7036 $dbo->setQuery($q);
7037 $dbo->execute();
7038 }
7039 }
7040 //
7041 }
7042 $mainframe = JFactory::getApplication();
7043 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
7044 }
7045
7046 public function removecarat()
7047 {
7048 if (!JSession::checkToken()) {
7049 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7050 }
7051
7052 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7053 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7054 }
7055
7056 $ids = VikRequest::getVar('cid', array(0));
7057 if ($ids) {
7058 $dbo = JFactory::getDBO();
7059 foreach ($ids as $d) {
7060 $q = "SELECT `icon` FROM `#__vikbooking_characteristics` WHERE `id`=".$dbo->quote($d).";";
7061 $dbo->setQuery($q);
7062 $dbo->execute();
7063 if ($dbo->getNumRows() == 1) {
7064 $rows = $dbo->loadAssocList();
7065 if (!empty($rows[0]['icon']) && file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['icon'])) {
7066 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['icon']);
7067 }
7068 }
7069 $q = "DELETE FROM `#__vikbooking_characteristics` WHERE `id`=".$dbo->quote($d).";";
7070 $dbo->setQuery($q);
7071 $dbo->execute();
7072 }
7073 }
7074 $mainframe = JFactory::getApplication();
7075 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
7076 }
7077
7078 public function coupons() {
7079 VikBookingHelper::printHeader("17");
7080
7081 VikRequest::setVar('view', VikRequest::getCmd('view', 'coupons'));
7082
7083 parent::display();
7084
7085 if (VikBooking::showFooter()) {
7086 VikBookingHelper::printFooter();
7087 }
7088 }
7089
7090 public function newcoupon() {
7091 VikBookingHelper::printHeader("17");
7092
7093 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecoupon'));
7094
7095 parent::display();
7096
7097 if (VikBooking::showFooter()) {
7098 VikBookingHelper::printFooter();
7099 }
7100 }
7101
7102 public function editcoupon() {
7103 VikBookingHelper::printHeader("17");
7104
7105 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecoupon'));
7106
7107 parent::display();
7108
7109 if (VikBooking::showFooter()) {
7110 VikBookingHelper::printFooter();
7111 }
7112 }
7113
7114 public function createcoupon()
7115 {
7116 if (!JSession::checkToken()) {
7117 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7118 }
7119
7120 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
7121 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7122 }
7123
7124 $pcode = VikRequest::getString('code', '', 'request');
7125 $pvalue = VikRequest::getString('value', '', 'request');
7126 $pfrom = VikRequest::getString('from', '', 'request');
7127 $pto = VikRequest::getString('to', '', 'request');
7128 $pidrooms = VikRequest::getVar('idrooms', array(0));
7129 $ptype = VikRequest::getString('type', '', 'request');
7130 $ptype = $ptype == "1" ? 1 : 2;
7131 $ppercentot = VikRequest::getString('percentot', '', 'request');
7132 $ppercentot = $ppercentot == "1" ? 1 : 2;
7133 $pallvehicles = VikRequest::getString('allvehicles', '', 'request');
7134 $pallvehicles = $pallvehicles == "1" ? 1 : 0;
7135 $pmintotord = VikRequest::getFloat('mintotord', 0, 'request');
7136 $pmaxtotord = VikRequest::getFloat('maxtotord', 0, 'request');
7137 $pexcludetaxes = VikRequest::getInt('excludetaxes', 0, 'request');
7138 $pminlos = VikRequest::getInt('minlos', 0, 'request');
7139 $pcustomers = VikRequest::getVar('customers', array());
7140 $pautomatic = VikRequest::getInt('automatic', 0, 'request');
7141 $stridrooms = "";
7142 if (count($pidrooms) > 0 && $pallvehicles != 1) {
7143 foreach ($pidrooms as $ch) {
7144 if (!empty($ch)) {
7145 $stridrooms .= ";".$ch.";";
7146 }
7147 }
7148 }
7149 $strdatevalid = "";
7150 if (strlen($pfrom) > 0 && strlen($pto) > 0) {
7151 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
7152 $second = VikBooking::getDateTimestamp($pto, 0, 0);
7153 if ($first < $second) {
7154 $strdatevalid .= $first."-".$second;
7155 }
7156 }
7157
7158 $dbo = JFactory::getDbo();
7159 $app = JFactory::getApplication();
7160
7161 $q = "SELECT * FROM `#__vikbooking_coupons` WHERE `code`=".$dbo->quote($pcode).";";
7162 $dbo->setQuery($q);
7163 $dbo->execute();
7164 if ($dbo->getNumRows() > 0) {
7165 VikError::raiseWarning('', JText::translate('VBCOUPONEXISTS'));
7166 } else {
7167 $q = "INSERT INTO `#__vikbooking_coupons` (`code`,`type`,`percentot`,`value`,`datevalid`,`allvehicles`,`idrooms`,`mintotord`,`excludetaxes`,`minlos`,`maxtotord`) VALUES(".$dbo->quote($pcode).",'".$ptype."','".$ppercentot."',".$dbo->quote($pvalue).",'".$strdatevalid."','".$pallvehicles."','".$stridrooms."', ".$dbo->quote($pmintotord).", {$pexcludetaxes}, {$pminlos}, " . $dbo->quote($pmaxtotord) . ");";
7168 $dbo->setQuery($q);
7169 $dbo->execute();
7170
7171 $id_coupon = $dbo->insertid();
7172
7173 $app->enqueueMessage(JText::translate('VBCOUPONSAVEOK'));
7174
7175 // check if this coupon should be assigned to specific customers
7176 foreach ($pcustomers as $id_customer) {
7177 $customer_coupon = new stdClass;
7178 $customer_coupon->idcustomer = (int)$id_customer;
7179 $customer_coupon->idcoupon = (int)$id_coupon;
7180 $customer_coupon->automatic = $pautomatic ? 1 : 0;
7181
7182 $dbo->insertObject('#__vikbooking_customers_coupons', $customer_coupon, 'id');
7183 }
7184 }
7185 $app->redirect("index.php?option=com_vikbooking&task=coupons");
7186 }
7187
7188 public function updatecoupon()
7189 {
7190 if (!JSession::checkToken()) {
7191 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7192 }
7193
7194 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7195 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7196 }
7197
7198 $this->do_updatecoupon($stay = false);
7199 }
7200
7201 public function updatecoupon_stay()
7202 {
7203 if (!JSession::checkToken()) {
7204 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7205 }
7206
7207 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7208 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7209 }
7210
7211 $this->do_updatecoupon($stay = true);
7212 }
7213
7214 protected function do_updatecoupon($stay = false)
7215 {
7216 if (!JSession::checkToken()) {
7217 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7218 }
7219 $pcode = VikRequest::getString('code', '', 'request');
7220 $pvalue = VikRequest::getString('value', '', 'request');
7221 $pfrom = VikRequest::getString('from', '', 'request');
7222 $pto = VikRequest::getString('to', '', 'request');
7223 $pidrooms = VikRequest::getVar('idrooms', array(0));
7224 $pwhere = VikRequest::getInt('where', 0, 'request');
7225 $ptype = VikRequest::getString('type', '', 'request');
7226 $ptype = $ptype == "1" ? 1 : 2;
7227 $ppercentot = VikRequest::getString('percentot', '', 'request');
7228 $ppercentot = $ppercentot == "1" ? 1 : 2;
7229 $pallvehicles = VikRequest::getString('allvehicles', '', 'request');
7230 $pallvehicles = $pallvehicles == "1" ? 1 : 0;
7231 $pmintotord = VikRequest::getFloat('mintotord', 0, 'request');
7232 $pmaxtotord = VikRequest::getFloat('maxtotord', 0, 'request');
7233 $pexcludetaxes = VikRequest::getInt('excludetaxes', 0, 'request');
7234 $pminlos = VikRequest::getInt('minlos', 0, 'request');
7235 $pcustomers = VikRequest::getVar('customers', array());
7236 $pautomatic = VikRequest::getInt('automatic', 0, 'request');
7237 $stridrooms = "";
7238 if (count($pidrooms) > 0 && $pallvehicles != 1) {
7239 foreach ($pidrooms as $ch) {
7240 if (!empty($ch)) {
7241 $stridrooms .= ";".$ch.";";
7242 }
7243 }
7244 }
7245 $strdatevalid = "";
7246 if (strlen($pfrom) > 0 && strlen($pto) > 0) {
7247 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
7248 $second = VikBooking::getDateTimestamp($pto, 0, 0);
7249 if ($first < $second) {
7250 $strdatevalid .= $first."-".$second;
7251 }
7252 }
7253
7254 $dbo = JFactory::getDbo();
7255 $app = JFactory::getApplication();
7256
7257 $q = "SELECT * FROM `#__vikbooking_coupons` WHERE `code`=".$dbo->quote($pcode)." AND `id`!='".$pwhere."';";
7258 $dbo->setQuery($q);
7259 $dbo->execute();
7260 if ($dbo->getNumRows() > 0) {
7261 VikError::raiseWarning('', JText::translate('VBCOUPONEXISTS'));
7262 } else {
7263 $q = "UPDATE `#__vikbooking_coupons` SET `code`=".$dbo->quote($pcode).",`type`='".$ptype."',`percentot`='".$ppercentot."',`value`=".$dbo->quote($pvalue).",`datevalid`='".$strdatevalid."',`allvehicles`='".$pallvehicles."',`idrooms`='".$stridrooms."',`mintotord`=".$dbo->quote($pmintotord).",`excludetaxes`={$pexcludetaxes},`minlos`={$pminlos},`maxtotord`= " . $dbo->quote($pmaxtotord) . " WHERE `id`=" . $pwhere . ";";
7264 $dbo->setQuery($q);
7265 $dbo->execute();
7266
7267 $app->enqueueMessage(JText::translate('VBCOUPONSAVEOK'));
7268
7269 // clean up any previously created record with customers
7270 $q = "DELETE FROM `#__vikbooking_customers_coupons` WHERE `idcoupon`=" . $pwhere;
7271 $dbo->setQuery($q);
7272 $dbo->execute();
7273
7274 // check if this coupon should be assigned to specific customers
7275 foreach ($pcustomers as $id_customer) {
7276 $customer_coupon = new stdClass;
7277 $customer_coupon->idcustomer = (int)$id_customer;
7278 $customer_coupon->idcoupon = (int)$pwhere;
7279 $customer_coupon->automatic = $pautomatic ? 1 : 0;
7280
7281 $dbo->insertObject('#__vikbooking_customers_coupons', $customer_coupon, 'id');
7282 }
7283 }
7284
7285 if ($stay) {
7286 $app->redirect("index.php?option=com_vikbooking&task=editcoupon&cid[]=$pwhere");
7287 } else {
7288 $app->redirect("index.php?option=com_vikbooking&task=coupons");
7289 }
7290 }
7291
7292 public function removecoupons()
7293 {
7294 if (!JSession::checkToken()) {
7295 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7296 }
7297
7298 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7299 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7300 }
7301
7302 $dbo = JFactory::getDbo();
7303
7304 $ids = VikRequest::getVar('cid', array(0));
7305
7306 if ($ids) {
7307 foreach ($ids as $d) {
7308 // delete coupon record
7309 $q = "DELETE FROM `#__vikbooking_coupons` WHERE `id`=".$dbo->quote($d).";";
7310 $dbo->setQuery($q);
7311 $dbo->execute();
7312
7313 // clean up any previously created record with customers
7314 $q = "DELETE FROM `#__vikbooking_customers_coupons` WHERE `idcoupon`=" . (int)$d;
7315 $dbo->setQuery($q);
7316 $dbo->execute();
7317 }
7318 }
7319
7320 JFactory::getApplication()->redirect("index.php?option=com_vikbooking&task=coupons");
7321 }
7322
7323 public function removemoreimgs()
7324 {
7325 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7326 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7327 }
7328
7329 $mainframe = JFactory::getApplication();
7330 $proomid = VikRequest::getInt('roomid', '', 'request');
7331 $pimgind = VikRequest::getInt('imgind', '', 'request');
7332 if (!strlen($pimgind)) {
7333 $mainframe->redirect("index.php?option=com_vikbooking");
7334 exit;
7335 }
7336 $dbo = JFactory::getDBO();
7337 $q = "SELECT `moreimgs`,`imgcaptions` FROM `#__vikbooking_rooms` WHERE `id`='".$proomid."';";
7338 $dbo->setQuery($q);
7339 $dbo->execute();
7340 $row = $dbo->loadAssoc();
7341 $actmore = $row['moreimgs'];
7342 if (!empty($actmore)) {
7343 $actsplit = explode(';;', $actmore);
7344 $captions = json_decode($row['imgcaptions'], true);
7345 $captions = !is_array($captions) ? array() : $captions;
7346 if ($pimgind < 0) {
7347 foreach ($actsplit as $img) {
7348 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'big_'.$img);
7349 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'thumb_'.$img);
7350 }
7351 // reset images and captions
7352 $actsplit = array();
7353 $captions = array();
7354 } else {
7355 if (array_key_exists($pimgind, $actsplit)) {
7356 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'big_'.$actsplit[$pimgind]);
7357 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'thumb_'.$actsplit[$pimgind]);
7358 // unset current image
7359 unset($actsplit[$pimgind]);
7360 // unset caption if exists
7361 if (isset($captions[$pimgind])) {
7362 unset($captions[$pimgind]);
7363 $captions = array_values($captions);
7364 }
7365 }
7366 }
7367 $newstr = "";
7368 foreach ($actsplit as $oi) {
7369 if (!empty($oi)) {
7370 $newstr .= $oi.';;';
7371 }
7372 }
7373 $q = "UPDATE `#__vikbooking_rooms` SET `moreimgs`=".$dbo->quote($newstr).", `imgcaptions`=".$dbo->quote(json_encode($captions))." WHERE `id`='".$proomid."';";
7374 $dbo->setQuery($q);
7375 $dbo->execute();
7376 }
7377 $mainframe->redirect("index.php?option=com_vikbooking&task=editroom&cid[]=".$proomid);
7378 }
7379
7380 public function sortfield() {
7381 if (!JSession::checkToken('get')) {
7382 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7383 }
7384 $mainframe = JFactory::getApplication();
7385 $sortid = VikRequest::getVar('cid', array(0));
7386 $pmode = VikRequest::getString('mode', '', 'request');
7387 $dbo = JFactory::getDBO();
7388 if (!empty($pmode)) {
7389 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_custfields` ORDER BY `#__vikbooking_custfields`.`ordering` ASC;";
7390 $dbo->setQuery($q);
7391 $dbo->execute();
7392 $totr=$dbo->getNumRows();
7393 if ($totr > 1) {
7394 $data = $dbo->loadAssocList();
7395 if ($pmode == "up") {
7396 foreach ($data as $v) {
7397 if ($v['id'] == $sortid[0]) {
7398 $y = $v['ordering'];
7399 }
7400 }
7401 if ($y && $y > 1) {
7402 $vik = $y - 1;
7403 $found = false;
7404 foreach ($data as $v) {
7405 if (intval($v['ordering']) == intval($vik)) {
7406 $found=true;
7407 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
7408 $dbo->setQuery($q);
7409 $dbo->execute();
7410 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7411 $dbo->setQuery($q);
7412 $dbo->execute();
7413 break;
7414 }
7415 }
7416 if (!$found) {
7417 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7418 $dbo->setQuery($q);
7419 $dbo->execute();
7420 }
7421 }
7422 } elseif ($pmode == "down") {
7423 foreach ($data as $v) {
7424 if ($v['id'] == $sortid[0]) {
7425 $y = $v['ordering'];
7426 }
7427 }
7428 if ($y) {
7429 $vik = $y + 1;
7430 $found = false;
7431 foreach ($data as $v) {
7432 if (intval($v['ordering']) == intval($vik)) {
7433 $found=true;
7434 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
7435 $dbo->setQuery($q);
7436 $dbo->execute();
7437 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7438 $dbo->setQuery($q);
7439 $dbo->execute();
7440 break;
7441 }
7442 }
7443 if (!$found) {
7444 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7445 $dbo->setQuery($q);
7446 $dbo->execute();
7447 }
7448 }
7449 }
7450 }
7451 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7452 } else {
7453 $mainframe->redirect("index.php?option=com_vikbooking");
7454 }
7455 }
7456
7457 public function customf() {
7458 VikBookingHelper::printHeader("16");
7459
7460 VikRequest::setVar('view', VikRequest::getCmd('view', 'customf'));
7461
7462 parent::display();
7463
7464 if (VikBooking::showFooter()) {
7465 VikBookingHelper::printFooter();
7466 }
7467 }
7468
7469 public function newcustomf() {
7470 VikBookingHelper::printHeader("16");
7471
7472 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomf'));
7473
7474 parent::display();
7475
7476 if (VikBooking::showFooter()) {
7477 VikBookingHelper::printFooter();
7478 }
7479 }
7480
7481 public function editcustomf() {
7482 VikBookingHelper::printHeader("16");
7483
7484 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomf'));
7485
7486 parent::display();
7487
7488 if (VikBooking::showFooter()) {
7489 VikBookingHelper::printFooter();
7490 }
7491 }
7492
7493 public function createcustomf()
7494 {
7495 if (!JSession::checkToken()) {
7496 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7497 }
7498
7499 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
7500 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7501 }
7502
7503 $pname = VikRequest::getString('name', '', 'request', VIKREQUEST_ALLOWHTML);
7504 $ptype = VikRequest::getString('type', '', 'request');
7505 $pchoose = VikRequest::getVar('choose', array(0));
7506 $prequired = VikRequest::getString('required', '', 'request');
7507 $prequired = $prequired == "1" ? 1 : 0;
7508 $pflag = VikRequest::getString('flag', '', 'request');
7509 $pisemail = $pflag == 'isemail' ? 1 : 0;
7510 $pisnominative = $pflag == 'isnominative' && $ptype == 'text' ? 1 : 0;
7511 $pisphone = $pflag == 'isphone' && $ptype == 'text' ? 1 : 0;
7512 $pisaddress = $pflag == 'isaddress' && $ptype == 'text' ? 1 : 0;
7513 $piscity = $pflag == 'iscity' && $ptype == 'text' ? 1 : 0;
7514 $piszip = $pflag == 'iszip' && $ptype == 'text' ? 1 : 0;
7515 $piscompany = $pflag == 'iscompany' && $ptype == 'text' ? 1 : 0;
7516 $pisvat = $pflag == 'isvat' && $ptype == 'text' ? 1 : 0;
7517 $pisfisccode = $pflag == 'isfisccode' && $ptype == 'text' ? 1 : 0;
7518 $pispec = $pflag == 'ispec' && $ptype == 'text' ? 1 : 0;
7519 $pisrecipcode = $pflag == 'isrecipcode' && $ptype == 'text' ? 1 : 0;
7520 $fieldflag = '';
7521 if ($pisaddress == 1) {
7522 $fieldflag = 'address';
7523 } elseif ($piscity == 1) {
7524 $fieldflag = 'city';
7525 } elseif ($piszip == 1) {
7526 $fieldflag = 'zip';
7527 } elseif ($piscompany == 1) {
7528 $fieldflag = 'company';
7529 } elseif ($pisvat == 1) {
7530 $fieldflag = 'vat';
7531 } elseif ($pisfisccode == 1) {
7532 $fieldflag = 'fisccode';
7533 } elseif ($pispec == 1) {
7534 $fieldflag = 'pec';
7535 } elseif ($pisrecipcode == 1) {
7536 $fieldflag = 'recipcode';
7537 }
7538 $ppoplink = VikRequest::getString('poplink', '', 'request');
7539 $choosestr = "";
7540 if (is_array($pchoose)) {
7541 foreach ($pchoose as $ch) {
7542 if (!empty($ch)) {
7543 $choosestr .= $ch.";;__;;";
7544 }
7545 }
7546 }
7547 $defvalue = VikRequest::getString('defvalue', '', 'request');
7548
7549 $dbo = JFactory::getDbo();
7550
7551 $q = "SELECT `ordering` FROM `#__vikbooking_custfields` ORDER BY `#__vikbooking_custfields`.`ordering` DESC LIMIT 1;";
7552 $dbo->setQuery($q);
7553 $dbo->execute();
7554 if ($dbo->getNumRows() == 1) {
7555 $getlast = $dbo->loadResult();
7556 $newsortnum = $getlast + 1;
7557 } else {
7558 $newsortnum = 1;
7559 }
7560 $q = "INSERT INTO `#__vikbooking_custfields` (`name`,`type`,`choose`,`required`,`ordering`,`isemail`,`poplink`,`isnominative`,`isphone`,`flag`,`defvalue`) VALUES(".$dbo->quote($pname).", ".$dbo->quote($ptype).", ".$dbo->quote($choosestr).", ".$dbo->quote($prequired).", ".$dbo->quote($newsortnum).", ".$dbo->quote($pisemail).", ".$dbo->quote($ppoplink).", ".$pisnominative.", ".$pisphone.", ".$dbo->quote($fieldflag).", ".$dbo->quote($defvalue).");";
7561 $dbo->setQuery($q);
7562 $dbo->execute();
7563 $mainframe = JFactory::getApplication();
7564 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7565 }
7566
7567 public function updatecustomf()
7568 {
7569 if (!JSession::checkToken()) {
7570 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7571 }
7572
7573 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7574 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7575 }
7576
7577 $pname = VikRequest::getString('name', '', 'request', VIKREQUEST_ALLOWHTML);
7578 $ptype = VikRequest::getString('type', '', 'request');
7579 $pchoose = VikRequest::getVar('choose', array(0));
7580 $prequired = VikRequest::getString('required', '', 'request');
7581 $prequired = $prequired == "1" ? 1 : 0;
7582 $pflag = VikRequest::getString('flag', '', 'request');
7583 $pisemail = $pflag == 'isemail' ? 1 : 0;
7584 $pisnominative = $pflag == 'isnominative' && $ptype == 'text' ? 1 : 0;
7585 $pisphone = $pflag == 'isphone' && $ptype == 'text' ? 1 : 0;
7586 $pisaddress = $pflag == 'isaddress' && $ptype == 'text' ? 1 : 0;
7587 $piscity = $pflag == 'iscity' && $ptype == 'text' ? 1 : 0;
7588 $piszip = $pflag == 'iszip' && $ptype == 'text' ? 1 : 0;
7589 $piscompany = $pflag == 'iscompany' && $ptype == 'text' ? 1 : 0;
7590 $pisvat = $pflag == 'isvat' && $ptype == 'text' ? 1 : 0;
7591 $pisfisccode = $pflag == 'isfisccode' && $ptype == 'text' ? 1 : 0;
7592 $pispec = $pflag == 'ispec' && $ptype == 'text' ? 1 : 0;
7593 $pisrecipcode = $pflag == 'isrecipcode' && $ptype == 'text' ? 1 : 0;
7594 $fieldflag = '';
7595 if ($pisaddress == 1) {
7596 $fieldflag = 'address';
7597 } elseif ($piscity == 1) {
7598 $fieldflag = 'city';
7599 } elseif ($piszip == 1) {
7600 $fieldflag = 'zip';
7601 } elseif ($piscompany == 1) {
7602 $fieldflag = 'company';
7603 } elseif ($pisvat == 1) {
7604 $fieldflag = 'vat';
7605 } elseif ($pisfisccode == 1) {
7606 $fieldflag = 'fisccode';
7607 } elseif ($pispec == 1) {
7608 $fieldflag = 'pec';
7609 } elseif ($pisrecipcode == 1) {
7610 $fieldflag = 'recipcode';
7611 }
7612 $ppoplink = VikRequest::getString('poplink', '', 'request');
7613 $pwhere = VikRequest::getInt('where', '', 'request');
7614 $choosestr = "";
7615 if (is_array($pchoose)) {
7616 foreach ($pchoose as $ch) {
7617 if (!empty($ch)) {
7618 $choosestr .= $ch.";;__;;";
7619 }
7620 }
7621 }
7622 $defvalue = VikRequest::getString('defvalue', '', 'request');
7623
7624 $dbo = JFactory::getDbo();
7625
7626 $q = "UPDATE `#__vikbooking_custfields` SET `name`=".$dbo->quote($pname).",`type`=".$dbo->quote($ptype).",`choose`=".$dbo->quote($choosestr).",`required`=".$dbo->quote($prequired).",`isemail`=".$dbo->quote($pisemail).",`poplink`=".$dbo->quote($ppoplink).",`isnominative`=".$pisnominative.",`isphone`=".$pisphone.",`flag`=".$dbo->quote($fieldflag).",`defvalue`=".$dbo->quote($defvalue)." WHERE `id`=".$dbo->quote($pwhere).";";
7627 $dbo->setQuery($q);
7628 $dbo->execute();
7629 $mainframe = JFactory::getApplication();
7630 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7631 }
7632
7633 public function removecustomf()
7634 {
7635 if (!JSession::checkToken()) {
7636 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7637 }
7638
7639 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7640 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7641 }
7642
7643 $ids = VikRequest::getVar('cid', array(0));
7644 if ($ids) {
7645 $dbo = JFactory::getDBO();
7646 foreach ($ids as $d) {
7647 $q = "DELETE FROM `#__vikbooking_custfields` WHERE `id`=".$dbo->quote($d).";";
7648 $dbo->setQuery($q);
7649 $dbo->execute();
7650 }
7651 }
7652 $mainframe = JFactory::getApplication();
7653 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7654 }
7655
7656 public function overv() {
7657 VikBookingHelper::printHeader("15");
7658
7659 VikRequest::setVar('view', VikRequest::getCmd('view', 'overv'));
7660
7661 parent::display();
7662
7663 if (VikBooking::showFooter()) {
7664 VikBookingHelper::printFooter();
7665 }
7666 }
7667
7668 public function translations() {
7669 VikBookingHelper::printHeader("21");
7670
7671 VikRequest::setVar('view', VikRequest::getCmd('view', 'translations'));
7672
7673 parent::display();
7674
7675 if (VikBooking::showFooter()) {
7676 VikBookingHelper::printFooter();
7677 }
7678 }
7679
7680 public function savetranslation() {
7681 if (!JSession::checkToken()) {
7682 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7683 }
7684 $this->do_savetranslation();
7685 }
7686
7687 public function savetranslationstay() {
7688 if (!JSession::checkToken()) {
7689 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7690 }
7691 $this->do_savetranslation(true);
7692 }
7693
7694 private function do_savetranslation($stay = false) {
7695 $dbo = JFactory::getDBO();
7696 $mainframe = JFactory::getApplication();
7697 $vbo_tn = VikBooking::getTranslator();
7698 $table = VikRequest::getString('vbo_table', '', 'request');
7699 $cur_langtab = VikRequest::getString('vbo_lang', '', 'request');
7700 $langs = $vbo_tn->getLanguagesList();
7701 $xml_tables = $vbo_tn->getTranslationTables();
7702 if (!empty($table) && array_key_exists($table, $xml_tables)) {
7703 $tn = VikRequest::getVar('tn', array(), 'request', 'array', VIKREQUEST_ALLOWRAW);
7704 $tn_saved = 0;
7705 $table_cols = $vbo_tn->getTableColumns($table);
7706 foreach ($langs as $ltag => $lang) {
7707 if ($ltag == $vbo_tn->default_lang) {
7708 continue;
7709 }
7710 if (array_key_exists($ltag, $tn) && count($tn[$ltag]) > 0) {
7711 foreach ($tn[$ltag] as $reference_id => $translation) {
7712 $lang_translation = array();
7713 foreach ($table_cols as $field => $fdetails) {
7714 if (!array_key_exists($field, $translation)) {
7715 continue;
7716 }
7717 $ftype = $fdetails['type'];
7718 if ($ftype == 'skip') {
7719 continue;
7720 }
7721
7722 if (is_array($translation[$field])) {
7723 foreach ($translation[$field] as $tn_field_k => $tn_field_v) {
7724 if (!is_string($tn_field_v)) {
7725 continue;
7726 }
7727 // replace any possible placeholder for special tags
7728 $translation[$field][$tn_field_k] = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
7729 return $match[2];
7730 }, $translation[$field][$tn_field_k]);
7731 }
7732 } elseif (!empty($translation[$field])) {
7733 // replace any possible placeholder for special tags
7734 $translation[$field] = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
7735 return $match[2];
7736 }, $translation[$field]);
7737 }
7738
7739 if ($ftype == 'json' && !is_scalar($translation[$field])) {
7740 $translation[$field] = json_encode($translation[$field]);
7741 }
7742 $lang_translation[$field] = $translation[$field];
7743 }
7744 if (count($lang_translation) > 0) {
7745 $q = "SELECT `id` FROM `#__vikbooking_translations` WHERE `table`=".$dbo->quote($table)." AND `lang`=".$dbo->quote($ltag)." AND `reference_id`=".$dbo->quote((int)$reference_id).";";
7746 $dbo->setQuery($q);
7747 $dbo->execute();
7748 if ($dbo->getNumRows() > 0) {
7749 $last_id = $dbo->loadResult();
7750 $q = "UPDATE `#__vikbooking_translations` SET `content`=".$dbo->quote(json_encode($lang_translation))." WHERE `id`=".(int)$last_id.";";
7751 } else {
7752 $q = "INSERT INTO `#__vikbooking_translations` (`table`,`lang`,`reference_id`,`content`) VALUES (".$dbo->quote($table).", ".$dbo->quote($ltag).", ".$dbo->quote((int)$reference_id).", ".$dbo->quote(json_encode($lang_translation)).");";
7753 }
7754 $dbo->setQuery($q);
7755 $dbo->execute();
7756 $tn_saved++;
7757 }
7758 }
7759 }
7760 }
7761 if ($tn_saved > 0) {
7762 $mainframe->enqueueMessage(JText::translate('VBOTRANSLSAVEDOK'));
7763 }
7764 } else {
7765 VikError::raiseWarning('', JText::translate('VBTRANSLATIONERRINVTABLE'));
7766 }
7767 $mainframe->redirect("index.php?option=com_vikbooking".($stay ? '&task=translations&vbo_table='.$vbo_tn->replacePrefix($table).'&vbo_lang='.$cur_langtab : '').'&limitstart='.$vbo_tn->lim0.'&limit='.$vbo_tn->lim);
7768 }
7769
7770 public function choosebusy() {
7771 VikBookingHelper::printHeader("8");
7772
7773 VikRequest::setVar('view', VikRequest::getCmd('view', 'choosebusy'));
7774
7775 parent::display();
7776
7777 if (VikBooking::showFooter()) {
7778 VikBookingHelper::printFooter();
7779 }
7780 }
7781
7782 public function orders() {
7783 VikBookingHelper::printHeader("8");
7784
7785 VikRequest::setVar('view', VikRequest::getCmd('view', 'orders'));
7786
7787 parent::display();
7788
7789 if (VikBooking::showFooter()) {
7790 VikBookingHelper::printFooter();
7791 }
7792 }
7793
7794 public function vieworders() {
7795 //alias method of orders() for backward compatibility with VCM
7796 $this->orders();
7797 }
7798
7799 public function editorder() {
7800 VikBookingHelper::printHeader("8");
7801
7802 VikRequest::setVar('view', VikRequest::getCmd('view', 'editorder'));
7803
7804 parent::display();
7805
7806 if (VikBooking::showFooter()) {
7807 VikBookingHelper::printFooter();
7808 }
7809 }
7810
7811 public function removeorders()
7812 {
7813 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7814 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7815 }
7816
7817 $dbo = JFactory::getDbo();
7818 $app = JFactory::getApplication();
7819
7820 $ids = VikRequest::getVar('cid', array(0));
7821 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
7822
7823 $user = JFactory::getUser();
7824 $config = VBOFactory::getConfig();
7825
7826 $prev_conf_ids = [];
7827 $purged = false;
7828
7829 $tot_cancs = 0;
7830
7831 if (is_array($ids) && count($ids)) {
7832 foreach ($ids as $d) {
7833 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $dbo->quote($d);
7834 $dbo->setQuery($q, 0, 1);
7835 $row = $dbo->loadAssoc();
7836
7837 // check for any cancellation constraints
7838 $canc_denied = false;
7839 if ($row && class_exists('VCMFeesCancellation')) {
7840 // let VCM detect if there are any constraints for the cancellation
7841 $canc_denied = VCMFeesCancellation::getInstance($row, $anew = true)->isBookingConstrained();
7842 if ($canc_denied) {
7843 // set error message
7844 $canc_deny_error = VCMFeesCancellation::getInstance()->getError();
7845 if ($canc_deny_error) {
7846 $app->enqueueMessage($canc_deny_error, 'error');
7847 }
7848 }
7849 }
7850
7851 if ($row && !$canc_denied) {
7852 // increase counter
7853 $tot_cancs++;
7854
7855 // set status to cancelled
7856 if ($row['status'] != 'cancelled') {
7857 $q = "UPDATE `#__vikbooking_orders` SET `status`='cancelled' WHERE `id`=".(int)$row['id'].";";
7858 $dbo->setQuery($q);
7859 $dbo->execute();
7860 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($row['id']) . ";";
7861 $dbo->setQuery($q);
7862 $dbo->execute();
7863 if ($row['status'] == 'confirmed') {
7864 $prev_conf_ids[] = $row['id'];
7865 }
7866 // Booking History
7867 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('CB', "({$user->name})");
7868 }
7869
7870 /**
7871 * In case of pending bookings being cancelled, schedule the release through VCM.
7872 *
7873 * @since 1.18.8 (J) - 1.8.8 (WP)
7874 */
7875 if ($row['status'] == 'standby' && method_exists('VCMRequestAvailability', 'setForRelease')) {
7876 // let the CM schedule the release of the involved and unconfirmed booking IDs, if needed
7877 VCMRequestAvailability::getInstance()->setForRelease([$row['id']]);
7878 }
7879
7880 // free records up
7881 $q = "SELECT * FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
7882 $dbo->setQuery($q);
7883 $ordbusy = $dbo->loadAssocList();
7884 if ($ordbusy) {
7885 foreach ($ordbusy as $ob) {
7886 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`='".$ob['idbusy']."';";
7887 $dbo->setQuery($q);
7888 $dbo->execute();
7889 }
7890 }
7891
7892 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
7893 $dbo->setQuery($q);
7894 $dbo->execute();
7895
7896 // check for purge removal
7897 if ($row['status'] == 'cancelled') {
7898 $q = "DELETE FROM `#__vikbooking_customers_orders` WHERE `idorder`=" . intval($row['id']) . ";";
7899 $dbo->setQuery($q);
7900 $dbo->execute();
7901 $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `idorder`=".(int)$row['id'].";";
7902 $dbo->setQuery($q);
7903 $dbo->execute();
7904 $q = "DELETE FROM `#__vikbooking_orderhistory` WHERE `idorder`=".(int)$row['id'].";";
7905 $dbo->setQuery($q);
7906 $dbo->execute();
7907 $q = "DELETE FROM `#__vikbooking_orders` WHERE `id`=".(int)$row['id'].";";
7908 $dbo->setQuery($q);
7909 $dbo->execute();
7910 // in case of split stay booking, remove the transient
7911 if ($row['split_stay']) {
7912 $config->remove('split_stay_' . $row['id']);
7913 }
7914 // turn flag on
7915 $purged = true;
7916 }
7917 }
7918 }
7919
7920 if ($tot_cancs) {
7921 // enqueue system message
7922 $app->enqueueMessage(JText::translate('VBMESSDELBUSY'));
7923 }
7924 }
7925
7926 if ($prev_conf_ids) {
7927 $prev_conf_ids_str = '';
7928 foreach ($prev_conf_ids as $prev_id) {
7929 $prev_conf_ids_str .= '&cid[]='.$prev_id;
7930 }
7931 //Invoke Channel Manager
7932 $vcm_autosync = VikBooking::vcmAutoUpdate();
7933 if ($vcm_autosync > 0) {
7934 $vcm_obj = VikBooking::getVcmInvoker();
7935 $vcm_obj->setOids($prev_conf_ids)->setSyncType('cancel');
7936 $sync_result = $vcm_obj->doSync();
7937 if ($sync_result === false) {
7938 $vcm_err = $vcm_obj->getError();
7939 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
7940 }
7941 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
7942 $vcm_sync_url = 'index.php?option=com_vikbooking&task=invoke_vcm&stype=cancel'.$prev_conf_ids_str.'&returl='.urlencode('index.php?option=com_vikbooking&task=orders');
7943 VikError::raiseNotice('', JText::translate('VBCHANNELMANAGERINVOKEASK').' <button type="button" class="btn btn-primary" onclick="document.location.href=\''.$vcm_sync_url.'\';">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button>');
7944 }
7945 //
7946 }
7947
7948 if (!empty($pgoto)) {
7949 if (is_numeric($pgoto) && is_array($ids) && count($ids) === 1) {
7950 if ($purged) {
7951 // go back to the bookings list page
7952 $app->redirect("index.php?option=com_vikbooking&task=orders");
7953 } else {
7954 // go back to the booking details page
7955 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . (int)$ids[0]);
7956 }
7957 exit;
7958 }
7959 // we expect the goto URL to be base64 encoded
7960 $app->redirect(base64_decode($pgoto));
7961 exit;
7962 }
7963
7964 // go back to the bookings list page
7965 $app->redirect("index.php?option=com_vikbooking&task=orders");
7966 }
7967
7968 public function config() {
7969 VikBookingHelper::printHeader("11");
7970
7971 VikRequest::setVar('view', VikRequest::getCmd('view', 'config'));
7972
7973 parent::display();
7974
7975 if (VikBooking::showFooter()) {
7976 VikBookingHelper::printFooter();
7977 }
7978 }
7979
7980 public function saveconfig()
7981 {
7982 if (!JSession::checkToken()) {
7983 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7984 }
7985
7986 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking') || !JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
7987 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7988 }
7989
7990 $dbo = JFactory::getDbo();
7991 $app = JFactory::getApplication();
7992
7993 $config = VBOFactory::getConfig();
7994
7995 $pallowbooking = VikRequest::getString('allowbooking', '', 'request');
7996 $pdisabledbookingmsg = VikRequest::getString('disabledbookingmsg', '', 'request', VIKREQUEST_ALLOWHTML);
7997 $ptimeopenstorefh = VikRequest::getString('timeopenstorefh', '', 'request');
7998 $ptimeopenstorefm = VikRequest::getString('timeopenstorefm', '', 'request');
7999 $ptimeopenstoreth = VikRequest::getString('timeopenstoreth', '', 'request');
8000 $ptimeopenstoretm = VikRequest::getString('timeopenstoretm', '', 'request');
8001 $phoursmorebookingback = VikRequest::getString('hoursmorebookingback', '', 'request');
8002 $pdateformat = VikRequest::getString('dateformat', '', 'request');
8003 $pdatesep = VikRequest::getString('datesep', '', 'request');
8004 $pdatesep = empty($pdatesep) ? "/" : $pdatesep;
8005 $presmodcanc = VikRequest::getInt('resmodcanc', 1, 'request');
8006 $presmodcancmin = VikRequest::getInt('resmodcancmin', 1, 'request');
8007 $pshowcategories = VikRequest::getString('showcategories', '', 'request');
8008 $pshowchildren = VikRequest::getString('showchildren', '', 'request');
8009 $psearchsuggestions = VikRequest::getInt('searchsuggestions', '', 'request');
8010 $ptokenform = VikRequest::getString('tokenform', '', 'request');
8011 $padminemail = VikRequest::getString('adminemail', '', 'request');
8012 $psenderemail = VikRequest::getString('senderemail', '', 'request');
8013 $pminuteslock = VikRequest::getString('minuteslock', '', 'request');
8014 $pminautoremove = VikRequest::getInt('minautoremove', '', 'request');
8015 $pfooterordmail = VikRequest::getString('footerordmail', '', 'request', VIKREQUEST_ALLOWHTML);
8016 $ptermsconds = VikRequest::getString('termsconds', '', 'request', VIKREQUEST_ALLOWHTML);
8017 $prequirelogin = VikRequest::getString('requirelogin', '', 'request');
8018 $pautoroomunit = VikRequest::getInt('autoroomunit', '', 'request');
8019 $ptodaybookings = VikRequest::getInt('todaybookings', '', 'request');
8020 $ptodaybookings = $ptodaybookings === 1 ? 1 : 0;
8021 $ploadbootstrap = VikRequest::getInt('loadbootstrap', '', 'request');
8022 $ploadbootstrap = $ploadbootstrap === 1 ? 1 : 0;
8023 $pusefa = VikRequest::getInt('usefa', '', 'request');
8024 $pusefa = $pusefa > 0 ? 1 : 0;
8025 $ploadjquery = VikRequest::getString('loadjquery', '', 'request');
8026 $ploadjquery = $ploadjquery == "yes" ? "1" : "0";
8027 $pcalendar = VikRequest::getString('calendar', '', 'request');
8028 $pcalendar = $pcalendar == "joomla" ? "joomla" : "jqueryui";
8029 $penablecoupons = VikRequest::getString('enablecoupons', '', 'request');
8030 $penablecoupons = $penablecoupons == "1" ? 1 : 0;
8031 $penablepin = VikRequest::getString('enablepin', '', 'request');
8032 $penablepin = $penablepin == "1" ? 1 : 0;
8033 $pmindaysadvance = VikRequest::getInt('mindaysadvance', '', 'request');
8034 $pmindaysadvance = $pmindaysadvance < 0 ? 0 : $pmindaysadvance;
8035 $pautodefcalnights = VikRequest::getInt('autodefcalnights', '', 'request');
8036 $pautodefcalnights = $pautodefcalnights >= 1 ? $pautodefcalnights : '1';
8037 $pnumrooms = VikRequest::getInt('numrooms', '', 'request');
8038 $pnumrooms = $pnumrooms > 0 ? $pnumrooms : '5';
8039 $pnumadultsfrom = VikRequest::getString('numadultsfrom', '', 'request');
8040 $pnumadultsfrom = intval($pnumadultsfrom) >= 0 ? $pnumadultsfrom : '1';
8041 $pnumadultsto = VikRequest::getString('numadultsto', '', 'request');
8042 $pnumadultsto = intval($pnumadultsto) > 0 ? $pnumadultsto : '10';
8043 if (intval($pnumadultsfrom) > intval($pnumadultsto)) {
8044 $pnumadultsfrom = '1';
8045 $pnumadultsto = '10';
8046 }
8047 $pnumchildrenfrom = VikRequest::getString('numchildrenfrom', '', 'request');
8048 $pnumchildrenfrom = intval($pnumchildrenfrom) >= 0 ? $pnumchildrenfrom : '1';
8049 $pnumchildrento = VikRequest::getString('numchildrento', '', 'request');
8050 $pnumchildrento = intval($pnumchildrento) > 0 ? $pnumchildrento : '4';
8051 if (intval($pnumchildrenfrom) > intval($pnumchildrento)) {
8052 $pnumadultsfrom = '1';
8053 $pnumadultsto = '4';
8054 }
8055 $confnumadults = $pnumadultsfrom.'-'.$pnumadultsto;
8056 $confnumchildren = $pnumchildrenfrom.'-'.$pnumchildrento;
8057 $pmaxdate = VikRequest::getString('maxdate', '', 'request');
8058 $pmaxdate = intval($pmaxdate) < 1 ? 2 : $pmaxdate;
8059 $pmaxdateinterval = VikRequest::getString('maxdateinterval', '', 'request');
8060 $pmaxdateinterval = !in_array($pmaxdateinterval, array('d', 'w', 'm', 'y')) ? 'y' : $pmaxdateinterval;
8061 $maxdate_str = '+'.$pmaxdate.$pmaxdateinterval;
8062 $pcronkey = VikRequest::getString('cronkey', '', 'request');
8063 $pcdsfrom = VikRequest::getVar('cdsfrom', array());
8064 $pcdsto = VikRequest::getVar('cdsto', array());
8065 $closing_dates = array();
8066 if (count($pcdsfrom)) {
8067 foreach ($pcdsfrom as $kcd => $vcdfrom) {
8068 if (!empty($vcdfrom) && array_key_exists($kcd, $pcdsto) && !empty($pcdsto[$kcd])) {
8069 $tscdfrom = VikBooking::getDateTimestamp($vcdfrom, '0', '0');
8070 $tscdto = VikBooking::getDateTimestamp($pcdsto[$kcd], '0', '0');
8071 if (!empty($tscdfrom) && !empty($tscdto) && $tscdto >= $tscdfrom) {
8072 $cdval = array('from' => $tscdfrom, 'to' => $tscdto);
8073 if (!in_array($cdval, $closing_dates)) {
8074 $closing_dates[] = $cdval;
8075 }
8076 }
8077 }
8078 }
8079 }
8080 $psmartsearch = VikRequest::getString('smartsearch', '', 'request');
8081 $psmartsearch = $psmartsearch == "dynamic" ? "dynamic" : "automatic";
8082 $pvbosef = VikRequest::getInt('vbosef', '', 'request');
8083 $vbosef = file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php');
8084 if ($pvbosef === 1) {
8085 if (!$vbosef) {
8086 rename(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'_router.php', VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php');
8087 }
8088 } else {
8089 if ($vbosef) {
8090 rename(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php', VBO_SITE_PATH.DIRECTORY_SEPARATOR.'_router.php');
8091 }
8092 }
8093 $pmultilang = VikRequest::getString('multilang', '', 'request');
8094 $pmultilang = $pmultilang == "1" ? 1 : 0;
8095 $pvcmautoupd = VikRequest::getInt('vcmautoupd', '', 'request');
8096 $pvcmautoupd = $pvcmautoupd > 0 ? 1 : 0;
8097 /**
8098 * Chat params and configuration settings
8099 *
8100 * @since 1.12
8101 */
8102 $pchatenabled = VikRequest::getInt('chatenabled', 0, 'request');
8103 if (is_file(VCM_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'lib.vikchannelmanager.php')) {
8104 $config->set('chatenabled', $pchatenabled);
8105
8106 // chat params
8107 $pchat_res_status = explode(';', VikRequest::getString('chat_res_status', '', 'request'));
8108 $chat_res_status = array();
8109 foreach ($pchat_res_status as $chatrs) {
8110 if (!empty($chatrs)) {
8111 array_push($chat_res_status, $chatrs);
8112 }
8113 }
8114 $chatparams = new stdClass;
8115 $chatparams->res_status = $chat_res_status;
8116 $chatparams->av_type = VikRequest::getString('chat_av_type', '', 'request');
8117 $chatparams->av_days = VikRequest::getInt('chat_av_days', 0, 'request');
8118
8119 $config->set('chatparams', $chatparams);
8120 }
8121
8122 /**
8123 * Pre check-in configuration settings
8124 *
8125 * @since 1.12
8126 */
8127 $pprecheckinenabled = VikRequest::getInt('precheckinenabled', 0, 'request');
8128 $pprecheckinenabled = $pprecheckinenabled > 0 ? 1 : 0;
8129
8130 $config->set('precheckinenabled', $pprecheckinenabled);
8131 // this may be a negative integer, it should not be unsigned
8132 $config->set('precheckinminoffset', VikRequest::getInt('precheckinminoffset', 0, 'request'));
8133
8134 $pupsellingenabled = VikRequest::getInt('upsellingenabled', 0, 'request');
8135 $pupsellingenabled = $pupsellingenabled > 0 ? 1 : 0;
8136 $config->set('upselling', $pupsellingenabled);
8137
8138 $porphanscal = VikRequest::getString('orphanscal', 'next', 'request');
8139 $porphanscal = $porphanscal == 'prevnext' ? 'prevnext' : 'next';
8140 $config->set('orphanscalculation', $porphanscal);
8141
8142 $psrcrtpl = VikRequest::getString('srcrtpl', 'compact', 'request');
8143 $config->set('searchrestmpl', $psrcrtpl);
8144
8145 /**
8146 * Guest Reviews settings
8147 *
8148 * @since 1.13
8149 */
8150 $pgrenabled = VikRequest::getInt('grenabled', 0, 'request');
8151 $pgrminchars = VikRequest::getInt('grminchars', 0, 'request');
8152 $pgrappr = VikRequest::getString('grappr', 'auto', 'request');
8153 $pgrappr = $pgrappr == 'auto' ? 'auto' : 'manual';
8154 $pgrtype = VikRequest::getString('grtype', 'service', 'request');
8155 $pgrtype = $pgrtype == 'service' ? 'service' : 'global';
8156 $pgrsrv = VikRequest::getVar('grsrv', array(), 'request', 'array');
8157 $config->set('grenabled', $pgrenabled);
8158 $config->set('grminchars', $pgrminchars);
8159 $config->set('grappr', $pgrappr);
8160 $config->set('grtype', $pgrtype);
8161 try {
8162 // always truncate service names (this query may require special permissions)
8163 $q = "TRUNCATE TABLE `#__vikbooking_greview_service`;";
8164 $dbo->setQuery($q);
8165 $dbo->execute();
8166 } catch (Exception $e) {
8167 // do nothing
8168 }
8169 foreach ($pgrsrv as $srvname) {
8170 $q = "INSERT INTO `#__vikbooking_greview_service` (`service_name`) VALUES (" . $dbo->quote($srvname) . ");";
8171 $dbo->setQuery($q);
8172 $dbo->execute();
8173 }
8174
8175 /**
8176 * Preferred countries ordering, or custom countries.
8177 *
8178 * @since 1.14 (J) - 1.3.11 (WP)
8179 * @since 1.14.1 (J) - 1.4.1 (WP) we also support "cust_pref_countries"
8180 */
8181 $pref_countries = VikRequest::getVar('pref_countries', array());
8182 $cust_pref_countries = VikRequest::getString('cust_pref_countries', '', 'request');
8183 $pref_countries = !is_array($pref_countries) || empty($pref_countries[0]) ? VikBooking::preferredCountriesOrdering() : $pref_countries;
8184 if (!empty($cust_pref_countries)) {
8185 $all_custom_prefcountries = array();
8186 $cust_pref_countries = explode(',', $cust_pref_countries);
8187 foreach ($cust_pref_countries as $cust_pref_country) {
8188 $cust_pref_country = trim(strtolower($cust_pref_country));
8189 if (empty($cust_pref_country) || strlen($cust_pref_country) != 2) {
8190 continue;
8191 }
8192 array_push($all_custom_prefcountries, $cust_pref_country);
8193 }
8194 if (count($all_custom_prefcountries)) {
8195 $pref_countries = $all_custom_prefcountries;
8196 }
8197 }
8198 $config->set('preferred_countries', $pref_countries);
8199 //
8200
8201 $gmapskey = VikRequest::getString('gmapskey', '', 'request');
8202 $config->set('gmapskey', $gmapskey);
8203
8204 $pref_textcolor = VikRequest::getString('pref_textcolor', '', 'request');
8205 $pref_bgcolor = VikRequest::getString('pref_bgcolor', '', 'request');
8206 $pref_fontcolor = VikRequest::getString('pref_fontcolor', '', 'request');
8207 $pref_bgcolorhov = VikRequest::getString('pref_bgcolorhov', '', 'request');
8208 $pref_fontcolorhov = VikRequest::getString('pref_fontcolorhov', '', 'request');
8209 $pref_colors = array(
8210 'textcolor' => $pref_textcolor,
8211 'bgcolor' => $pref_bgcolor,
8212 'fontcolor' => $pref_fontcolor,
8213 'bgcolorhov' => $pref_bgcolorhov,
8214 'fontcolorhov' => $pref_fontcolorhov,
8215 );
8216 $config->set('pref_colors', $pref_colors);
8217
8218 $interactive_map = VikRequest::getInt('interactive_map', 0, 'request');
8219 $config->set('interactive_map', $interactive_map);
8220 $config->set('search_filters', VikRequest::getInt('search_filters', 0, 'request'));
8221
8222 $noemptydecimals = VikRequest::getInt('noemptydecimals', 0, 'request');
8223 $config->set('noemptydecimals', $noemptydecimals);
8224
8225 /**
8226 * Appearance preferences (light, auto, dark mode).
8227 *
8228 * @since 1.15.0 (J) - 1.5.0 (WP)
8229 * @since 1.16.10 (J) - 1.6.10 (WP) mirrored on VCM.
8230 */
8231 $appearance_pref = VikRequest::getString('appearance_pref', '');
8232 $config->set('appearance_pref', $appearance_pref);
8233 if (class_exists('VCMFactory')) {
8234 VCMFactory::getConfig()->set('appearance_pref', $appearance_pref);
8235 }
8236
8237 $res_backend_path = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR;
8238 $picon = "";
8239 if (intval($_FILES['sitelogo']['error']) == 0 && trim($_FILES['sitelogo']['name'])!="") {
8240 jimport('joomla.filesystem.file');
8241 if (@is_uploaded_file($_FILES['sitelogo']['tmp_name'])) {
8242 $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['sitelogo']['name'])));
8243 if (file_exists($res_backend_path.$safename)) {
8244 $j = 1;
8245 while (file_exists($res_backend_path.$j.$safename)) {
8246 $j++;
8247 }
8248 $pwhere = $res_backend_path.$j.$safename;
8249 } else {
8250 $j = "";
8251 $pwhere = $res_backend_path.$safename;
8252 }
8253 if (!getimagesize($_FILES['sitelogo']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
8254 @unlink($pwhere);
8255 $picon = "";
8256 } else {
8257 VikBooking::uploadFile($_FILES['sitelogo']['tmp_name'], $pwhere);
8258 @chmod($pwhere, 0644);
8259 $picon = $j.$safename;
8260 }
8261 }
8262 if (!empty($picon)) {
8263 $config->set('sitelogo', $picon);
8264 }
8265 }
8266 $pbackicon = "";
8267 if (intval($_FILES['backlogo']['error']) == 0 && trim($_FILES['backlogo']['name'])!="") {
8268 jimport('joomla.filesystem.file');
8269 if (@is_uploaded_file($_FILES['backlogo']['tmp_name'])) {
8270 $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['backlogo']['name'])));
8271 if (file_exists($res_backend_path.$safename)) {
8272 $j = 1;
8273 while (file_exists($res_backend_path.$j.$safename)) {
8274 $j++;
8275 }
8276 $pwhere = $res_backend_path.$j.$safename;
8277 } else {
8278 $j = "";
8279 $pwhere = $res_backend_path.$safename;
8280 }
8281 if (!getimagesize($_FILES['backlogo']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
8282 @unlink($pwhere);
8283 $pbackicon = "";
8284 } else {
8285 VikBooking::uploadFile($_FILES['backlogo']['tmp_name'], $pwhere);
8286 @chmod($pwhere, 0644);
8287 $pbackicon = $j.$safename;
8288 }
8289 }
8290 if (!empty($pbackicon)) {
8291 $config->set('backlogo', $pbackicon);
8292 }
8293 }
8294 $config->set('vcmautoupd', $pvcmautoupd);
8295 $config->set('allowbooking', empty($pallowbooking) || $pallowbooking != "1" ? 0 : 1);
8296 $config->set('showcategories', empty($pshowcategories) || $pshowcategories != "yes" ? 0 : 1);
8297 $config->set('showchildren', empty($pshowchildren) || $pshowchildren != "yes" ? 0 : 1);
8298 $config->set('searchsuggestions', $psearchsuggestions);
8299 $config->set('tokenform', empty($ptokenform) || $ptokenform != "yes" ? 0 : 1);
8300 $config->set('guests_label', $app->input->getString('guests_label', 'adults'));
8301 $config->set('search_show_busy_listings', $app->input->getInt('search_show_busy_listings', 0));
8302 $config->set('search_link_roomdetails', $app->input->getInt('search_link_roomdetails', 0));
8303
8304 // translatable text
8305 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pfooterordmail)." WHERE `param`='footerordmail';";
8306 $dbo->setQuery($q);
8307 $dbo->execute();
8308
8309 // translatable text
8310 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pdisabledbookingmsg)." WHERE `param`='disabledbookingmsg';";
8311 $dbo->setQuery($q);
8312 $dbo->execute();
8313
8314 // translatable text
8315 $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($app->input->getString('guests_allowed_policy', '', 'raw')) . " WHERE `param`='guests_allowed_policy';";
8316 $dbo->setQuery($q);
8317 $dbo->execute();
8318
8319 // terms and conditions
8320 $q = "SELECT `id`,`setting` FROM `#__vikbooking_texts` WHERE `param`='termsconds';";
8321 $dbo->setQuery($q);
8322 $dbo->execute();
8323 if ($dbo->getNumRows() > 0) {
8324 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($ptermsconds)." WHERE `param`='termsconds';";
8325 $dbo->setQuery($q);
8326 $dbo->execute();
8327 } else {
8328 $q = "INSERT INTO `#__vikbooking_texts` (`param`,`exp`,`setting`) VALUES ('termsconds','Terms and Conditions',".$dbo->quote($ptermsconds).");";
8329 $dbo->setQuery($q);
8330 $dbo->execute();
8331 }
8332
8333 $config->set('adminemail', $padminemail);
8334 $config->set('senderemail', $psenderemail);
8335 $config->set('dateformat', empty($pdateformat) ? "%d/%m/%Y" : $pdateformat);
8336 $config->set('datesep', $pdatesep);
8337 $config->set('resmodcanc', $presmodcanc);
8338 $config->set('resmodcancmin', $presmodcancmin);
8339 $config->set('minuteslock', $pminuteslock);
8340 $config->set('minautoremove', $pminautoremove);
8341
8342 $openingh = $ptimeopenstorefh * 3600;
8343 $openingm = $ptimeopenstorefm * 60;
8344 $openingts = $openingh + $openingm;
8345 $closingh = $ptimeopenstoreth * 3600;
8346 $closingm = $ptimeopenstoretm * 60;
8347 $closingts = $closingh + $closingm;
8348 // check if the check-in/out times have changed and if there are future bookings with the old time to prevent availability errors
8349 $prevtimes = $config->get('timeopenstore', '');
8350 if ($prevtimes != $openingts . "-" . $closingts) {
8351 $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `checkout`>".time().";";
8352 $dbo->setQuery($q);
8353 $dbo->execute();
8354 if ($dbo->getNumRows() > 0) {
8355 VikError::raiseWarning('', JText::translate('VBOCONFIGWARNDIFFCHECKINOUT'));
8356 /**
8357 * VBO 1.10 Patch - we concatenate a button to unify the check-in/out times
8358 * for all reservations to avoid issues with the availability.
8359 *
8360 * @since August 29th 2018
8361 */
8362 VikError::raiseWarning('', '<br/><a href="index.php?option=com_vikbooking&task=unifycheckinout&fh='.$ptimeopenstorefh.'&fm='.$ptimeopenstorefm.'&th='.$ptimeopenstoreth.'&tm='.$ptimeopenstoretm.'" class="btn btn-large btn-warning">'.JText::translate('VBAPPLY').'</a>');
8363 //
8364 }
8365 }
8366 $config->set('timeopenstore', $openingts . "-" . $closingts);
8367
8368 // set the hours of extended gratuity period to the difference between checkin and checkout if checkout is later
8369 $phoursmorebookingback = "0";
8370 if ($closingts > $openingts) {
8371 $diffcheck = ($closingts - $openingts) / 3600;
8372 $phoursmorebookingback = ceil($diffcheck);
8373 }
8374 $config->set('hoursmorebookingback', $phoursmorebookingback);
8375 $config->set('hoursmoreroomavail', '0');
8376 $config->set('multilang', $pmultilang);
8377 $config->set('requirelogin', $prequirelogin == "1" ? 1 : 0);
8378 $config->set('autoroomunit', $pautoroomunit ? 1 : 0);
8379 $config->set('todaybookings', $ptodaybookings);
8380 $config->set('bootstrap', $ploadbootstrap);
8381 $config->set('usefa', $pusefa);
8382 $config->set('loadjquery', $ploadjquery);
8383 $config->set('calendar', $pcalendar ?: 'jqueryui');
8384 $config->set('dboptimizetime', $app->input->getString('dboptimizetime', ''));
8385 $config->set('enablecoupons', $penablecoupons);
8386 $config->set('enablepin', $penablepin);
8387 $config->set('mindaysadvance', $pmindaysadvance);
8388 $config->set('autodefcalnights', $pautodefcalnights);
8389 $config->set('numrooms', $pnumrooms);
8390 $config->set('numadults', $confnumadults);
8391 $config->set('numchildren', $confnumchildren);
8392 $config->set('closingdates', $closing_dates);
8393 $config->set('smartsearch', $psmartsearch);
8394 $config->set('maxdate', $maxdate_str);
8395 $config->set('cronkey', $pcronkey);
8396
8397 $pfronttitle = VikRequest::getString('fronttitle', '', 'request');
8398 $pfronttitletag = VikRequest::getString('fronttitletag', '', 'request');
8399 $pfronttitletagclass = VikRequest::getString('fronttitletagclass', '', 'request');
8400 $pshowfooter = VikRequest::getString('showfooter', '', 'request');
8401 $pintromain = VikRequest::getString('intromain', '', 'request', VIKREQUEST_ALLOWHTML);
8402 $pclosingmain = VikRequest::getString('closingmain', '', 'request', VIKREQUEST_ALLOWHTML);
8403 $pcurrencyname = VikRequest::getString('currencyname', '', 'request', VIKREQUEST_ALLOWHTML);
8404 $pcurrencysymb = VikRequest::getString('currencysymb', '', 'request', VIKREQUEST_ALLOWHTML);
8405 $pcurrencycodepp = VikRequest::getString('currencycodepp', '', 'request');
8406 $pnumdecimals = VikRequest::getString('numdecimals', '', 'request');
8407 $pnumdecimals = intval($pnumdecimals);
8408 $pdecseparator = VikRequest::getString('decseparator', '', 'request');
8409 $pdecseparator = empty($pdecseparator) ? '.' : $pdecseparator;
8410 $pthoseparator = VikRequest::getString('thoseparator', '', 'request');
8411 $numberformatstr = $pnumdecimals.':'.$pdecseparator.':'.$pthoseparator;
8412 $pshowpartlyreserved = VikRequest::getString('showpartlyreserved', '', 'request');
8413 $pshowpartlyreserved = $pshowpartlyreserved == "yes" ? 1 : 0;
8414 $pshowcheckinoutonly = VikRequest::getInt('showcheckinoutonly', '', 'request');
8415 $pshowcheckinoutonly = $pshowcheckinoutonly > 0 ? 1 : 0;
8416 $pnumcalendars = VikRequest::getInt('numcalendars', '', 'request');
8417 $pnumcalendars = $pnumcalendars > -1 ? $pnumcalendars : 3;
8418 $pthumbsize = VikRequest::getInt('thumbsize', 0, 'request');
8419 $pfirstwday = VikRequest::getString('firstwday', '', 'request');
8420 $pfirstwday = intval($pfirstwday) >= 0 && intval($pfirstwday) <= 6 ? $pfirstwday : '0';
8421 $pbctagname = VikRequest::getVar('bctagname', array());
8422 $pbctagcolor = VikRequest::getVar('bctagcolor', array());
8423 $pbctagrule = VikRequest::getVar('bctagrule', array());
8424 $bctags_arr = array();
8425 $bctags_rules = array();
8426 if (count($pbctagname) > 0) {
8427 foreach ($pbctagname as $bctk => $bctv) {
8428 if (!empty($bctv) && !empty($pbctagcolor[$bctk]) && strlen($pbctagrule[$bctk]) > 0) {
8429 if (intval($pbctagrule[$bctk]) == 0 || !in_array($pbctagrule[$bctk], $bctags_rules)) {
8430 $bctags_rules[] = $pbctagrule[$bctk];
8431 $bctags_arr[] = array('color' => $pbctagcolor[$bctk], 'name' => $bctv, 'rule' => $pbctagrule[$bctk]);
8432 }
8433 }
8434 }
8435 }
8436 //theme
8437 $ptheme = VikRequest::getString('theme', '', 'request');
8438 if (empty($ptheme) || $ptheme == 'default') {
8439 $ptheme = 'default';
8440 } else {
8441 $validtheme = false;
8442 $themes = glob(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'themes'.DIRECTORY_SEPARATOR.'*');
8443 if (count($themes) > 0) {
8444 $strip = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'themes'.DIRECTORY_SEPARATOR;
8445 foreach ($themes as $th) {
8446 if (is_dir($th)) {
8447 $tname = str_replace($strip, '', $th);
8448 if ($tname == $ptheme) {
8449 $validtheme = true;
8450 break;
8451 }
8452 }
8453 }
8454 }
8455 if ($validtheme == false) {
8456 $ptheme = 'default';
8457 }
8458 }
8459 $config->set('theme', $ptheme);
8460 //
8461 $config->set('showpartlyreserved', $pshowpartlyreserved);
8462 $config->set('showcheckinoutonly', $pshowcheckinoutonly);
8463 $config->set('numcalendars', $pnumcalendars);
8464
8465 // record may not be set
8466 $config->set('thumbsize', $pthumbsize);
8467
8468 $config->set('firstwday', $pfirstwday);
8469
8470 // translatable text
8471 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pfronttitle)." WHERE `param`='fronttitle';";
8472 $dbo->setQuery($q);
8473 $dbo->execute();
8474
8475 $config->set('fronttitletag', $pfronttitletag);
8476 $config->set('fronttitletagclass', $pfronttitletagclass);
8477 $config->set('showfooter', empty($pshowfooter) || $pshowfooter != "yes" ? 0 : 1);
8478
8479 // translatable texts
8480 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pintromain)." WHERE `param`='intromain';";
8481 $dbo->setQuery($q);
8482 $dbo->execute();
8483 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pclosingmain)." WHERE `param`='closingmain';";
8484 $dbo->setQuery($q);
8485 $dbo->execute();
8486
8487 $config->set('currencyname', $pcurrencyname);
8488 $config->set('currencysymb', $pcurrencysymb);
8489 $config->set('currencypos', $app->input->getAlnum('currencypos', 'before'));
8490 $config->set('currencycodepp', $pcurrencycodepp);
8491 $config->set('numberformat', $numberformatstr);
8492 // bookings color tags
8493 $config->set('bookingsctags', $bctags_arr);
8494
8495 $pivainclusa = VikRequest::getString('ivainclusa', '', 'request');
8496 $ptaxsummary = VikRequest::getString('taxsummary', '', 'request');
8497 $ptaxsummary = empty($ptaxsummary) || $ptaxsummary != "yes" ? "0" : "1";
8498 $pccpaypal = VikRequest::getString('ccpaypal', '', 'request');
8499 $ppaytotal = VikRequest::getString('paytotal', '', 'request');
8500 $ppayaccpercent = VikRequest::getString('payaccpercent', '', 'request');
8501 $ptypedeposit = VikRequest::getString('typedeposit', '', 'request');
8502 $ptypedeposit = $ptypedeposit == 'fixed' ? 'fixed' : 'pcent';
8503 $pdepoverrides = VikRequest::getString('depoverrides', '', 'request');
8504 $ppaymentname = VikRequest::getString('paymentname', '', 'request');
8505 $pdisclaimer = VikRequest::getString('disclaimer', '', 'request', VIKREQUEST_ALLOWHTML);
8506 $pmultipay = VikRequest::getString('multipay', '', 'request');
8507 $pmultipay = $pmultipay == "yes" ? 1 : 0;
8508 $pdepifdaysadv = VikRequest::getInt('depifdaysadv', '', 'request');
8509 $pnodepnonrefund = VikRequest::getInt('nodepnonrefund', '', 'request');
8510 $pdepcustchoice = VikRequest::getString('depcustchoice', '', 'request');
8511 $pdepcustchoice = $pdepcustchoice == "yes" ? 1 : 0;
8512
8513 // translatable text
8514 $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($ppaymentname) . " WHERE `param`='paymentname';";
8515 $dbo->setQuery($q);
8516 $dbo->execute();
8517 $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($pdisclaimer) . " WHERE `param`='disclaimer';";
8518 $dbo->setQuery($q);
8519 $dbo->execute();
8520
8521 $config->set('ivainclusa', empty($pivainclusa) || $pivainclusa != "yes" ? 0 : 1);
8522 $config->set('taxsummary', $ptaxsummary);
8523 $config->set('paytotal', empty($ppaytotal) || $ppaytotal != "yes" ? 0 : 1);
8524
8525 $config->set('ccpaypal', $pccpaypal);
8526 $config->set('payaccpercent', $ppayaccpercent);
8527 $config->set('typedeposit', $ptypedeposit);
8528 $config->set('depoverrides', $pdepoverrides);
8529 $config->set('multipay', $pmultipay);
8530 $config->set('depifdaysadv', $pdepifdaysadv);
8531 $config->set('nodepnonrefund', $pnodepnonrefund);
8532 $config->set('depcustchoice', $pdepcustchoice);
8533 $config->set('depbalancedays', $app->input->getInt('depbalancedays', null));
8534
8535 $psendemailwhen = VikRequest::getInt('sendemailwhen', '', 'request');
8536 $psendemailwhen = $psendemailwhen > 1 ? 2 : 1;
8537 $pattachical = VikRequest::getInt('attachical', 0, 'request');
8538 $pattachical = $pattachical >= 0 && $pattachical <= 3 ? $pattachical : 1;
8539 $config->set('emailsendwhen', $psendemailwhen);
8540 $config->set('attachical', $pattachical);
8541
8542 // SMS APIs
8543 $psmsapi = VikRequest::getString('smsapi', '', 'request');
8544 $psmsautosend = VikRequest::getString('smsautosend', '', 'request');
8545 $psmsautosend = intval($psmsautosend) > 0 ? 1 : 0;
8546 $psmssendto = VikRequest::getVar('smssendto', array());
8547 $sms_sendto = array();
8548 foreach ($psmssendto as $sto) {
8549 if (in_array($sto, array('admin', 'customer'))) {
8550 $sms_sendto[] = $sto;
8551 }
8552 }
8553 $psmssendwhen = VikRequest::getInt('smssendwhen', '', 'request');
8554 $psmssendwhen = $psmssendwhen > 1 ? 2 : 1;
8555 $psmsadminphone = VikRequest::getString('smsadminphone', '', 'request');
8556 $psmsadmintpl = VikRequest::getString('smsadmintpl', '', 'request', VIKREQUEST_ALLOWRAW);
8557 $psmscustomertpl = VikRequest::getString('smscustomertpl', '', 'request', VIKREQUEST_ALLOWRAW);
8558 $psmsadmintplpend = VikRequest::getString('smsadmintplpend', '', 'request', VIKREQUEST_ALLOWRAW);
8559 $psmscustomertplpend = VikRequest::getString('smscustomertplpend', '', 'request', VIKREQUEST_ALLOWRAW);
8560 $psmsadmintplcanc = VikRequest::getString('smsadmintplcanc', '', 'request', VIKREQUEST_ALLOWRAW);
8561 $psmscustomertplcanc = VikRequest::getString('smscustomertplcanc', '', 'request', VIKREQUEST_ALLOWRAW);
8562 $viksmsparams = VikRequest::getVar('viksmsparams', array());
8563 $smsparamarr = array();
8564 if (count($viksmsparams) > 0) {
8565 foreach ($viksmsparams as $setting => $cont) {
8566 if (strlen($setting) > 0) {
8567 $smsparamarr[$setting] = $cont;
8568 }
8569 }
8570 }
8571 $config->set('smsapi', $psmsapi);
8572 $config->set('smsautosend', $psmsautosend);
8573 $config->set('smssendto', $sms_sendto);
8574 $config->set('smssendwhen', $psmssendwhen);
8575 $config->set('smsadminphone', $psmsadminphone);
8576 $config->set('smsparams', $smsparamarr);
8577
8578 // translatable texts
8579 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintpl)." WHERE `param`='smsadmintpl';";
8580 $dbo->setQuery($q);
8581 $dbo->execute();
8582 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertpl)." WHERE `param`='smscustomertpl';";
8583 $dbo->setQuery($q);
8584 $dbo->execute();
8585 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintplpend)." WHERE `param`='smsadmintplpend';";
8586 $dbo->setQuery($q);
8587 $dbo->execute();
8588 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertplpend)." WHERE `param`='smscustomertplpend';";
8589 $dbo->setQuery($q);
8590 $dbo->execute();
8591 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintplcanc)." WHERE `param`='smsadmintplcanc';";
8592 $dbo->setQuery($q);
8593 $dbo->execute();
8594 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertplcanc)." WHERE `param`='smscustomertplcanc';";
8595 $dbo->setQuery($q);
8596 $dbo->execute();
8597
8598 /**
8599 * Backup settings
8600 *
8601 * @since 1.15.0 (J) - 1.5.0 (WP)
8602 */
8603 $backup_type = $app->input->getString('backuptype', 'full');
8604 $backup_folder = $app->input->getString('backupfolder', '');
8605
8606 $tmp = $app->get('tmp_path');
8607
8608 if (!$backup_folder)
8609 {
8610 // path not specified, use temporary folder
8611 $backup_folder = $tmp;
8612 }
8613
8614 $current = $config->get('backupfolder');
8615
8616 if (!$current)
8617 {
8618 // path was missing, use temporary folder
8619 $current = $tmp;
8620 }
8621
8622 // check whether the backup folder has been moved
8623 if ($current && $backup_folder && rtrim($current, DIRECTORY_SEPARATOR) !== rtrim($backup_folder, DIRECTORY_SEPARATOR))
8624 {
8625 $backupModel = new VBOModelBackup();
8626
8627 // backup folder moved, try to copy all the existing overrides
8628 if (!$backupModel->moveArchives($backup_folder))
8629 {
8630 // iterate all errors and display them
8631 foreach ($backupModel->getErrors() as $error)
8632 {
8633 $app->enqueueMessage($error, 'warning');
8634 }
8635 }
8636 }
8637
8638 // save configuration
8639 $config->set('backuptype', $backup_type);
8640 $config->set('backupfolder', $backup_folder);
8641
8642 /**
8643 * Check-in data collection type.
8644 *
8645 * @since 1.15.0 (J) - 1.5.0 (WP)
8646 */
8647 $config->set('checkindata', VikRequest::getString('checkindata', 'basic', 'request'));
8648
8649 /**
8650 * Front-end appearance.
8651 *
8652 * @since 1.15.0 (J) - 1.5.0 (WP) (patch)
8653 */
8654 $config->set('appearance_front', VikRequest::getInt('appearance_front', 0, 'request'));
8655
8656 /**
8657 * Split stays.
8658 *
8659 * @since 1.16.0 (J) - 1.6.0 (WP)
8660 */
8661 $glob_split_stay = VikRequest::getInt('split_stay', 0, 'request');
8662 $split_stay_ratio = VikRequest::getFloat('split_stay_ratio', 0, 'request');
8663 $split_stay_ratio = $split_stay_ratio > 100 ? 100 : $split_stay_ratio;
8664 $config->set('split_stay_ratio', ($glob_split_stay && $split_stay_ratio > 0 ? $split_stay_ratio : 0));
8665
8666 /**
8667 * Re-build Web App manifest file to let the event trigger.
8668 *
8669 * @since 1.16.5 (J) - 1.6.5 (WP)
8670 */
8671 try {
8672 VBOWebappManifest::build();
8673 } catch (Exception $e) {
8674 // do nothing
8675 }
8676
8677 // redirect
8678 $app->enqueueMessage(JText::translate('VBSETTINGSAVED'));
8679 $app->redirect('index.php?option=com_vikbooking&task=config');
8680 $app->close();
8681 }
8682
8683 /**
8684 * Task to unify the check-in and check-out times for all reservations.
8685 */
8686 public function unifycheckinout()
8687 {
8688 $dbo = JFactory::getDbo();
8689 $app = JFactory::getApplication();
8690 $user = JFactory::getUser();
8691
8692 $fh = VikRequest::getInt('fh', 12, 'request');
8693 $fm = VikRequest::getInt('fm', 0, 'request');
8694 $th = VikRequest::getInt('th', 10, 'request');
8695 $tm = VikRequest::getInt('tm', 0, 'request');
8696
8697 $now = time();
8698 $totmod = 0;
8699 $totbookmod = 0;
8700
8701 // query all busy records
8702 $q = $dbo->getQuery(true)
8703 ->select('*')
8704 ->from($dbo->qn('#__vikbooking_busy'));
8705
8706 $dbo->setQuery($q);
8707 $records = $dbo->loadAssocList();
8708
8709 foreach ($records as $v) {
8710 $info_start = getdate($v['checkin']);
8711 $info_end = getdate($v['checkout']);
8712 $new_start = mktime($fh, $fm, 0, $info_start['mon'], $info_start['mday'], $info_start['year']);
8713 $new_end = mktime($th, $tm, 0, $info_end['mon'], $info_end['mday'], $info_end['year']);
8714
8715 $q = $dbo->getQuery(true)
8716 ->update($dbo->qn('#__vikbooking_busy'))
8717 ->set($dbo->qn('checkin') . ' = ' . $new_start)
8718 ->set($dbo->qn('checkout') . ' = ' . $new_end)
8719 ->set($dbo->qn('realback') . ' = ' . $new_end)
8720 ->where($dbo->qn('id') . ' = ' . (int)$v['id']);
8721
8722 $dbo->setQuery($q, 0, 1);
8723 $dbo->execute();
8724
8725 $totmod++;
8726 }
8727
8728 // query all bookings
8729 $q = $dbo->getQuery(true)
8730 ->select($dbo->qn([
8731 'id',
8732 'days',
8733 'checkin',
8734 'checkout',
8735 'total',
8736 ]))
8737 ->from($dbo->qn('#__vikbooking_orders'))
8738 ->order($dbo->qn('checkin') . ' DESC');
8739
8740 $dbo->setQuery($q);
8741 $records = $dbo->loadAssocList();
8742
8743 foreach ($records as $v) {
8744 $info_start = getdate($v['checkin']);
8745 $info_end = getdate($v['checkout']);
8746 $new_start = mktime($fh, $fm, 0, $info_start['mon'], $info_start['mday'], $info_start['year']);
8747 $new_end = mktime($th, $tm, 0, $info_end['mon'], $info_end['mday'], $info_end['year']);
8748
8749 $q = $dbo->getQuery(true)
8750 ->update($dbo->qn('#__vikbooking_orders'))
8751 ->set($dbo->qn('checkin') . ' = ' . $new_start)
8752 ->set($dbo->qn('checkout') . ' = ' . $new_end)
8753 ->where($dbo->qn('id') . ' = ' . (int)$v['id']);
8754
8755 $dbo->setQuery($q, 0, 1);
8756 $dbo->execute();
8757
8758 /**
8759 * In case the operation changed the check-in/check-out time for this booking,
8760 * store a new history record for a booking modification.
8761 *
8762 * @since 1.16.6 (J) - 1.6.6 (WP)
8763 */
8764 if ($v['checkout'] > $now && ($info_start['hours'] != $fh || $info_end['hours'] != $th)) {
8765 // Booking History
8766 VikBooking::getBookingHistoryInstance($v['id'])->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($v));
8767 }
8768
8769 $totbookmod++;
8770 }
8771
8772 $app->enqueueMessage('OK: ' . $totbookmod);
8773 $app->redirect("index.php?option=com_vikbooking&task=config");
8774 $app->close();
8775 }
8776
8777 public function savetmplfile()
8778 {
8779 $app = JFactory::getApplication();
8780
8781 if (!JSession::checkToken()) {
8782 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
8783 }
8784
8785 if (!JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
8786 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8787 }
8788
8789 $fpath = VikRequest::getString('path', '', 'request', VIKREQUEST_ALLOWRAW);
8790 $pcont = VikRequest::getString('cont', '', 'request', VIKREQUEST_ALLOWRAW);
8791 $pajax = VikRequest::getInt('ajax', 0, 'request');
8792
8793 // default status
8794 $result = [
8795 'status' => 0,
8796 'message' => 'Generic error',
8797 ];
8798
8799 $exists = file_exists($fpath) ? true : false;
8800 if (!$exists) {
8801 $fpath = urldecode($fpath);
8802 }
8803 $fpath = file_exists($fpath) ? $fpath : '';
8804 if (!empty($fpath)) {
8805 $fp = fopen($fpath, 'wb');
8806 $byt = (int) fwrite($fp, $pcont);
8807 fclose($fp);
8808 if ($byt > 0) {
8809 // success
8810 $result = [
8811 'status' => 1,
8812 'message' => JText::translate('VBOUPDTMPLFILEOK'),
8813 ];
8814
8815 if (VBOPlatformDetection::isWordPress()) {
8816 /**
8817 * @wponly call the UpdateManager Class to temporarily store modifications made to template files
8818 */
8819 VikBookingUpdateManager::storeTemplateContent($fpath, $pcont);
8820 }
8821 } else {
8822 // error
8823 $result = [
8824 'status' => 0,
8825 'message' => JText::translate('VBOUPDTMPLFILENOBYTES'),
8826 ];
8827 }
8828 } else {
8829 // error
8830 $result = [
8831 'status' => 0,
8832 'message' => JText::translate('VBOUPDTMPLFILEERR'),
8833 ];
8834 }
8835
8836 if ($pajax) {
8837 if ($result['status']) {
8838 VBOHttpDocument::getInstance($app)->json($result);
8839 } else {
8840 VBOHttpDocument::getInstance($app)->close(500, $result['message']);
8841 }
8842 } else {
8843 if ($result['status']) {
8844 $app->enqueueMessage($result['message']);
8845 } else {
8846 VikError::raiseWarning('', $result['message']);
8847 }
8848 }
8849
8850 $app->redirect("index.php?option=com_vikbooking&task=edittmplfile&path=".$fpath."&tmpl=component");
8851 $app->close();
8852 }
8853
8854 public function edittmplfile()
8855 {
8856 // this view should be rendered through AJAX
8857 VikRequest::setVar('view', VikRequest::getCmd('view', 'edittmplfile'));
8858
8859 if (JFactory::getApplication()->input->getBool('ajax') && VBOPlatformDetection::isJoomla()) {
8860 /**
8861 * @todo This needs to be changed for Joomla in the future versions.
8862 * Right now no HTML document tree is being added as an AJAX response, because
8863 * the View output is captured within a buffer, but the CodeMirror will not work.
8864 * In case the View was rendered normally and sent to output, the CodeMirror would
8865 * work fine, but the response appended to the modal body would contain HTML head tags
8866 * and so accessing the language definitions through JS would fail after the first response.
8867 * The solution for both Joomla and WordPress is probably to use a completely different endpoint
8868 * that returns just the file buffer/content, and maybe the file type, so that who makes the requests
8869 * can set the content and render the proper CodeMirror editor manually at runtime.
8870 */
8871
8872 // start output buffer
8873 ob_start();
8874
8875 try {
8876 // display view
8877 parent::display();
8878 } catch (Exception $e) {
8879 // clear output buffer
8880 ob_end_clean();
8881
8882 // raise error
8883 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
8884 }
8885
8886 // obtain view HTML from buffer
8887 $html = ob_get_contents();
8888
8889 // clear output buffer
8890 ob_end_clean();
8891
8892 // encode HTML in JSON to avoid encoding issues
8893 VBOHttpDocument::getInstance()->json(json_encode($html));
8894
8895 } else {
8896 // regular view display
8897 parent::display();
8898 }
8899 }
8900
8901 public function tmplfileprew() {
8902 //modal box, so we do not set menu or footer
8903
8904 VikRequest::setVar('view', VikRequest::getCmd('view', 'tmplfileprew'));
8905
8906 parent::display();
8907 }
8908
8909 public function invoices() {
8910 VikBookingHelper::printHeader("invoices");
8911
8912 VikRequest::setVar('view', VikRequest::getCmd('view', 'invoices'));
8913
8914 parent::display();
8915
8916 if (VikBooking::showFooter()) {
8917 VikBookingHelper::printFooter();
8918 }
8919 }
8920
8921 public function newmaninvoice() {
8922 VikBookingHelper::printHeader("invoices");
8923
8924 VikRequest::setVar('view', VikRequest::getCmd('view', 'managemaninvoice'));
8925
8926 parent::display();
8927
8928 if (VikBooking::showFooter()) {
8929 VikBookingHelper::printFooter();
8930 }
8931 }
8932
8933 public function editmaninvoice() {
8934 VikBookingHelper::printHeader("invoices");
8935
8936 VikRequest::setVar('view', VikRequest::getCmd('view', 'managemaninvoice'));
8937
8938 parent::display();
8939
8940 if (VikBooking::showFooter()) {
8941 VikBookingHelper::printFooter();
8942 }
8943 }
8944
8945 public function savemaninvoice() {
8946 if (!JSession::checkToken()) {
8947 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
8948 }
8949 $this->do_storemaninvoice('save');
8950 $mainframe = JFactory::getApplication();
8951 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8952 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8953 if (!empty($pgoto)) {
8954 $mainframe->redirect(base64_decode($pgoto));
8955 exit;
8956 }
8957 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
8958 }
8959
8960 public function updatemaninvoice()
8961 {
8962 if (!JSession::checkToken()) {
8963 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
8964 }
8965
8966 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
8967 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8968 }
8969
8970 $invid = VikRequest::getInt('whereup', 0, 'request');
8971 $this->do_storemaninvoice('update', $invid);
8972 $mainframe = JFactory::getApplication();
8973 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8974 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8975 if (!empty($pgoto)) {
8976 $mainframe->redirect(base64_decode($pgoto));
8977 exit;
8978 }
8979 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
8980 }
8981
8982 public function updatemaninvoicestay()
8983 {
8984 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
8985 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8986 }
8987
8988 $invid = VikRequest::getInt('whereup', 0, 'request');
8989 $this->do_storemaninvoice('updatestay', $invid);
8990 $mainframe = JFactory::getApplication();
8991 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8992 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8993 if (!empty($pgoto)) {
8994 $mainframe->redirect(base64_decode($pgoto));
8995 exit;
8996 }
8997 $mainframe->redirect("index.php?option=com_vikbooking&task=editmaninvoice&cid[]=".$invid);
8998 }
8999
9000 private function do_storemaninvoice($action, $invid = 0) {
9001 $dbo = JFactory::getDBO();
9002 $mainframe = JFactory::getApplication();
9003 $pinvoice_num = VikRequest::getInt('invoice_num', '', 'request');
9004 $pinvoice_num = $pinvoice_num <= 0 ? 1 : $pinvoice_num;
9005 $pinvoice_suff = VikRequest::getString('invoice_suff', '', 'request');
9006 $pcompany_info = VikRequest::getString('company_info', '', 'request', VIKREQUEST_ALLOWHTML);
9007 $pcompany_info = strpos($pcompany_info, '<') !== false ? $pcompany_info : nl2br($pcompany_info);
9008 $pinvoice_notes = VikRequest::getString('invoice_notes', '', 'request', VIKREQUEST_ALLOWHTML);
9009 $pinvoice_notes = strpos($pinvoice_notes, '<') !== false ? $pinvoice_notes : nl2br($pinvoice_notes);
9010 $pidcustomer = VikRequest::getInt('idcustomer', '', 'request');
9011 $error_uri = strpos($action, 'update') !== false && !empty($invid) ? 'index.php?option=com_vikbooking&task=editmaninvoice&cid[]='.$invid : 'index.php?option=com_vikbooking&task=newmaninvoice';
9012 if (empty($pidcustomer)) {
9013 VikError::raiseWarning('', JText::translate('VBNOCUSTOMERS'));
9014 $mainframe->redirect($error_uri);
9015 exit;
9016 }
9017 $services = VikRequest::getVar('service', array());
9018 $nets = VikRequest::getVar('net', array());
9019 $aliqs = VikRequest::getVar('aliq', array());
9020 $taxs = VikRequest::getVar('tax', array());
9021 $tots = VikRequest::getVar('tot', array());
9022 $ptotalnet = VikRequest::getFloat('totalnet', 0, 'request');
9023 $ptotaltax = VikRequest::getFloat('totaltax', 0, 'request');
9024 $ptotaltot = VikRequest::getFloat('totaltot', 0, 'request');
9025 if (!count($services) || count($services) != count($nets) || count($services) != count($taxs) || count($services) != count($tots)) {
9026 VikError::raiseWarning('', 'Missing data.');
9027 $mainframe->redirect($error_uri);
9028 exit;
9029 }
9030 $rawcont = array(
9031 'rows' => array(),
9032 'totalnet' => $ptotalnet,
9033 'totaltax' => $ptotaltax,
9034 'totaltot' => $ptotaltot,
9035 'notes' => $pinvoice_notes,
9036 );
9037 foreach ($services as $k => $service) {
9038 if (empty($service)) {
9039 continue;
9040 }
9041 array_push($rawcont['rows'], array(
9042 'service' => $service,
9043 'net' => (float)$nets[$k],
9044 'aliq' => (isset($aliqs[$k]) ? (float)$aliqs[$k] : 0),
9045 'tax' => (float)$taxs[$k],
9046 'tot' => (float)$tots[$k],
9047 ));
9048 }
9049 // store/update manual invoice
9050 $nowts = time();
9051 $retval = 0;
9052 if (strpos($action, 'save') !== false) {
9053 $pdffname = $nowts . '_' . rand() . '.pdf';
9054 $q = "INSERT INTO `#__vikbooking_invoices` (`number`,`file_name`,`idorder`,`idcustomer`,`created_on`,`for_date`,`rawcont`) VALUES (".$dbo->quote($pinvoice_num.$pinvoice_suff).", ".$dbo->quote($pdffname).", ".($pinvoice_num - ($pinvoice_num * 2)).", ".$dbo->quote($pidcustomer).", ".$nowts.", ".$nowts.", ".$dbo->quote(json_encode($rawcont)).");";
9055 $dbo->setQuery($q);
9056 $dbo->execute();
9057 $retval = $dbo->insertid();
9058 } else {
9059 // fetch old record
9060 $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id`=".(int)$invid.";";
9061 $dbo->setQuery($q);
9062 $dbo->execute();
9063 if (!$dbo->getNumRows()) {
9064 VikError::raiseWarning('', JText::translate('VBNOINVOICESFOUND'));
9065 $mainframe->redirect($error_uri);
9066 exit;
9067 }
9068 $previnvoice = $dbo->loadAssoc();
9069 //
9070 $q = "UPDATE `#__vikbooking_invoices` SET `number`=".$dbo->quote($pinvoice_num.$pinvoice_suff).",`file_name`=".$dbo->quote($previnvoice['file_name']).",`idorder`=".($pinvoice_num - ($pinvoice_num * 2)).",`idcustomer`=".$dbo->quote($pidcustomer).",`created_on`=".$nowts.",`rawcont`=".$dbo->quote(json_encode($rawcont))." WHERE `id`=".(int)$previnvoice['id'].";";
9071 $dbo->setQuery($q);
9072 $dbo->execute();
9073 $retval = $previnvoice['id'];
9074 }
9075 // update config values for the invoice
9076 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pcompany_info)." WHERE `param`='invcompanyinfo';";
9077 $dbo->setQuery($q);
9078 $dbo->execute();
9079 // generate the custom invoice
9080 $result = VikBooking::generateCustomInvoice($retval);
9081 //
9082 $nextinv = VikBooking::getNextInvoiceNumber();
9083 $updatenum = ($pinvoice_num >= $nextinv);
9084 if ($updatenum) {
9085 /**
9086 * IMPORTANT: update the next invoice number after calling the e-Invocing drivers
9087 * to avoid conflicts with the drivers for the e-invoices generation.
9088 */
9089 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(($pinvoice_num - 1))." WHERE `param`='invoiceinum';";
9090 $dbo->setQuery($q);
9091 $dbo->execute();
9092 }
9093
9094 return $retval;
9095 }
9096
9097 public function downloadinvoices() {
9098 $ids = VikRequest::getVar('cid', array(0));
9099 if (@count($ids) > 0) {
9100 $dbo = JFactory::getDBO();
9101 $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id` IN (".implode(', ', $ids).");";
9102 $dbo->setQuery($q);
9103 $dbo->execute();
9104 if ($dbo->getNumRows() > 0) {
9105 $invoices = $dbo->loadAssocList();
9106 if (!(count($invoices) > 1)) {
9107 //Single Invoice Download
9108 if (file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoices[0]['file_name'])) {
9109 header("Content-type:application/pdf");
9110 header("Content-Disposition:attachment;filename=".$invoices[0]['file_name']);
9111 readfile(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoices[0]['file_name']);
9112 exit;
9113 }
9114 } else {
9115 //Multiple Invoices Download
9116 $to_zip = array();
9117 foreach ($invoices as $k => $invoice) {
9118 $to_zip[$k]['name'] = $invoice['file_name'];
9119 $to_zip[$k]['path'] = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoice['file_name'];
9120 }
9121 if (class_exists('ZipArchive')) {
9122 $zip_path = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.date('Y-m-d').'-invoices.zip';
9123 $zip = new ZipArchive;
9124 $zip->open($zip_path, ZipArchive::CREATE);
9125 foreach ($to_zip as $k => $zipv) {
9126 $zip->addFile($zipv['path'], $zipv['name']);
9127 }
9128 $zip->close();
9129 header("Content-type:application/zip");
9130 header("Content-Disposition:attachment;filename=".date('Y-m-d').'-invoices.zip');
9131 header("Content-Length:".filesize($zip_path));
9132 readfile($zip_path);
9133 unlink($zip_path);
9134 exit;
9135 } else {
9136 //Class ZipArchive does not exist
9137 VikError::raiseWarning('', 'Class ZipArchive does not exist on your server. Download the files one by one.');
9138 }
9139 }
9140 }
9141 }
9142 $mainframe = JFactory::getApplication();
9143 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9144 }
9145
9146 public function resendinvoices() {
9147 $ids = VikRequest::getVar('cid', array(0));
9148 $mainframe = JFactory::getApplication();
9149 if (!(count($ids) > 0)) {
9150 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9151 exit;
9152 }
9153 $dbo = JFactory::getDBO();
9154 $invoices = array();
9155 $q = "SELECT `i`.*,`o`.`custmail`,`c`.`email` AS `customer_email`, CONCAT_WS(' ',`c`.`first_name`,`c`.`last_name`) AS `customer_name`,`c`.`country` AS `customer_country`,`nat`.`country_name` ".
9156 "FROM `#__vikbooking_invoices` AS `i` " .
9157 "LEFT JOIN `#__vikbooking_orders` `o` ON `o`.`id`=`i`.`idorder` " .
9158 "LEFT JOIN `#__vikbooking_customers` `c` ON `c`.`id`=`i`.`idcustomer` " .
9159 "LEFT JOIN `#__vikbooking_countries` `nat` ON `nat`.`country_3_code`=`c`.`country` " .
9160 "WHERE `i`.`id` IN (".implode(', ', $ids).") AND (`i`.`idorder` < 0 OR (`o`.`status`='confirmed' AND `o`.`total` > 0)) ORDER BY `o`.`id` ASC;";
9161 $dbo->setQuery($q);
9162 $dbo->execute();
9163 if ($dbo->getNumRows() > 0) {
9164 $invoices = $dbo->loadAssocList();
9165 }
9166 if (!(count($invoices) > 0)) {
9167 VikError::raiseWarning('', JText::translate('VBOGENINVERRNOBOOKINGS'));
9168 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9169 exit;
9170 }
9171 $tot_generated = 0;
9172 $tot_sent = 0;
9173 foreach ($invoices as $bkey => $invoice) {
9174 $invoice['custmail'] = empty($invoice['custmail']) && !empty($invoice['customer_email']) ? $invoice['customer_email'] : $invoice['custmail'];
9175 $invoices[$bkey] = $invoice;
9176 $send_res = VikBooking::sendBookingInvoice($invoice['id'], $invoice);
9177 if ($send_res !== false) {
9178 $tot_sent++;
9179 }
9180 }
9181 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', $tot_generated, $tot_sent));
9182 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9183 }
9184
9185 public function removeinvoices()
9186 {
9187 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
9188 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9189 }
9190
9191 $ids = VikRequest::getVar('cid', array());
9192 $tot_removed = 0;
9193 $dbo = JFactory::getDbo();
9194
9195 foreach ($ids as $d) {
9196 $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id`=".(int)$d.";";
9197 $dbo->setQuery($q);
9198 $dbo->execute();
9199 if ($dbo->getNumRows() == 1) {
9200 $cur_invoice = $dbo->loadAssoc();
9201 if (file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$cur_invoice['file_name'])) {
9202 unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$cur_invoice['file_name']);
9203 }
9204 $q = "DELETE FROM `#__vikbooking_invoices` WHERE `id`=".(int)$d.";";
9205 $dbo->setQuery($q);
9206 $dbo->execute();
9207 $tot_removed++;
9208 }
9209 }
9210
9211 $mainframe = JFactory::getApplication();
9212 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESRMVD', $tot_removed));
9213 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9214 }
9215
9216 public function geninvoices()
9217 {
9218 $dbo = JFactory::getDbo();
9219 $app = JFactory::getApplication();
9220
9221 $ids = VikRequest::getVar('cid', array());
9222
9223 if (!$ids) {
9224 $app->redirect("index.php?option=com_vikbooking&task=orders");
9225 exit;
9226 }
9227
9228 $pinvoice_num = VikRequest::getInt('invoice_num', '', 'request');
9229 $pinvoice_num = $pinvoice_num <= 0 ? 1 : $pinvoice_num;
9230 $pinvoice_suff = VikRequest::getString('invoice_suff', '', 'request');
9231 $pinvoice_date = VikRequest::getString('invoice_date', '', 'request');
9232 $pcompany_info = VikRequest::getString('company_info', '', 'request', VIKREQUEST_ALLOWHTML);
9233 $pcompany_info = strpos($pcompany_info, '<') !== false ? $pcompany_info : nl2br($pcompany_info);
9234 $pinvoice_send = VikRequest::getInt('invoice_send', '', 'request');
9235 $pinvoice_send = $pinvoice_send > 0 ? true : false;
9236 $increment_inv = true;
9237 $pconfirmgen = VikRequest::getInt('confirmgen', '', 'request');
9238
9239 // if editing an invoice (re-creating an existing invoice for a booking), do not increment the invoice number
9240 if (count($ids) === 1) {
9241 $q = "SELECT `number` FROM `#__vikbooking_invoices` WHERE `idorder`=".(int)$ids[0].";";
9242 $dbo->setQuery($q);
9243 $dbo->execute();
9244 if ($dbo->getNumRows() == 1) {
9245 $increment_inv = false;
9246 }
9247 }
9248
9249 // get bookings
9250 $dbo->setQuery(
9251 $dbo->getQuery(true)
9252 ->select($dbo->qn('o') . '.*')
9253 ->select($dbo->qn('co.idcustomer'))
9254 ->select('CONCAT_WS(\' \', ' . $dbo->qn('c.first_name') . ', ' . $dbo->qn('c.last_name') . ') AS ' . $dbo->qn('customer_name'))
9255 ->select([
9256 $dbo->qn('c.pin', 'customer_pin'),
9257 $dbo->qn('nat.country_name'),
9258 ])
9259 ->from($dbo->qn('#__vikbooking_orders', 'o'))
9260 ->leftJoin($dbo->qn('#__vikbooking_customers_orders', 'co') . ' ON ' . $dbo->qn('co.idorder') . ' = ' . $dbo->qn('o.id'))
9261 ->leftJoin($dbo->qn('#__vikbooking_customers', 'c') . ' ON ' . $dbo->qn('c.id') . ' = ' . $dbo->qn('co.idcustomer'))
9262 ->leftJoin($dbo->qn('#__vikbooking_countries', 'nat') . ' ON ' . $dbo->qn('nat.country_3_code') . ' = ' . $dbo->qn('o.country'))
9263 ->where($dbo->qn('o.id') . ' IN (' . implode(', ', array_map('intval', $ids)) . ')')
9264 ->where($dbo->qn('o.status') . ' = ' . $dbo->q('confirmed'))
9265 ->where($dbo->qn('o.total') . ' > 0')
9266 ->order($dbo->qn('o.id') . ' ASC')
9267 );
9268
9269 $bookings = $dbo->loadAssocList();
9270
9271 if (!$bookings) {
9272 VikError::raiseWarning('', JText::translate('VBOGENINVERRNOBOOKINGS'));
9273 $app->redirect("index.php?option=com_vikbooking&task=orders");
9274 exit;
9275 }
9276
9277 $tot_generated = 0;
9278 $tot_sent = 0;
9279 foreach ($bookings as $bkey => $booking) {
9280 $gen_res = VikBooking::generateBookingInvoice($booking, $pinvoice_num, $pinvoice_suff, $pinvoice_date, $pcompany_info);
9281 if ($gen_res !== false && $gen_res > 0) {
9282 $tot_generated++;
9283 $pinvoice_num++;
9284 if ($pinvoice_send) {
9285 $send_res = VikBooking::sendBookingInvoice($gen_res, $booking);
9286 if ($send_res !== false) {
9287 $tot_sent++;
9288 }
9289 }
9290 } else {
9291 VikError::raiseWarning('', JText::sprintf('VBOGENINVERRBOOKING', $booking['id']));
9292 }
9293 }
9294
9295 if ($tot_generated > 0 && $increment_inv === true) {
9296 /**
9297 * IMPORTANT: update the next invoice number after calling generateBookingInvoice()
9298 * to avoid conflicts with the drivers for the e-invoices generation.
9299 */
9300 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(($pinvoice_num - 1))." WHERE `param`='invoiceinum';";
9301 $dbo->setQuery($q);
9302 $dbo->execute();
9303 }
9304
9305 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pinvoice_suff)." WHERE `param`='invoicesuffix';";
9306 $dbo->setQuery($q);
9307 $dbo->execute();
9308
9309 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pcompany_info)." WHERE `param`='invcompanyinfo';";
9310 $dbo->setQuery($q);
9311 $dbo->execute();
9312
9313 $app->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', $tot_generated, $tot_sent));
9314
9315 if ($pconfirmgen > 0) {
9316 $app->redirect("index.php?option=com_vikbooking&task=invoices&show=".$pconfirmgen);
9317 } elseif (count($bookings) === 1) {
9318 // go to the back-end booking details page
9319 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $bookings[0]['id']);
9320 } else {
9321 $app->redirect("index.php?option=com_vikbooking&task=orders");
9322 }
9323 }
9324
9325 public function optionals() {
9326 VikBookingHelper::printHeader("6");
9327
9328 VikRequest::setVar('view', VikRequest::getCmd('view', 'optionals'));
9329
9330 parent::display();
9331
9332 if (VikBooking::showFooter()) {
9333 VikBookingHelper::printFooter();
9334 }
9335 }
9336
9337 public function newoptionals() {
9338 VikBookingHelper::printHeader("6");
9339
9340 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoptional'));
9341
9342 parent::display();
9343
9344 if (VikBooking::showFooter()) {
9345 VikBookingHelper::printFooter();
9346 }
9347 }
9348
9349 public function editoptional() {
9350 VikBookingHelper::printHeader("6");
9351
9352 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoptional'));
9353
9354 parent::display();
9355
9356 if (VikBooking::showFooter()) {
9357 VikBookingHelper::printFooter();
9358 }
9359 }
9360
9361 public function updateoptional()
9362 {
9363 if (!JSession::checkToken()) {
9364 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9365 }
9366
9367 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
9368 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9369 }
9370
9371 $this->do_updateoptional();
9372 }
9373
9374 public function updateoptionalstay()
9375 {
9376 if (!JSession::checkToken()) {
9377 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9378 }
9379
9380 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
9381 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9382 }
9383
9384 $this->do_updateoptional(true);
9385 }
9386
9387 private function do_updateoptional($stay = false) {
9388 $dbo = JFactory::getDbo();
9389 $app = JFactory::getApplication();
9390 $poptname = VikRequest::getString('optname', '', 'request');
9391 $poptdescr = VikRequest::getString('optdescr', '', 'request', VIKREQUEST_ALLOWHTML);
9392 $poptcost = VikRequest::getFloat('optcost', '', 'request');
9393 $poptperday = VikRequest::getString('optperday', '', 'request');
9394 $poptperperson = VikRequest::getString('optperperson', '', 'request');
9395 $pmaxprice = VikRequest::getFloat('maxprice', '', 'request');
9396 $popthmany = VikRequest::getString('opthmany', '', 'request');
9397 $poptaliq = VikRequest::getInt('optaliq', '', 'request');
9398 $pwhereup = VikRequest::getString('whereup', '', 'request');
9399 $pautoresize = VikRequest::getString('autoresize', '', 'request');
9400 $presizeto = VikRequest::getString('resizeto', '', 'request');
9401 $pifchildren = VikRequest::getString('ifchildren', '', 'request');
9402 $pifchildren = $pifchildren == "1" ? 1 : 0;
9403 $pmaxquant = VikRequest::getString('maxquant', '', 'request');
9404 $pmaxquant = empty($pmaxquant) ? 0 : intval($pmaxquant);
9405 $pforcesel = VikRequest::getString('forcesel', '', 'request');
9406 $pforceval = VikRequest::getString('forceval', '', 'request');
9407 $pforcevalperday = VikRequest::getString('forcevalperday', '', 'request');
9408 $pforcevalperchild = VikRequest::getString('forcevalperchild', '', 'request');
9409 $pforcesummary = VikRequest::getString('forcesummary', '', 'request');
9410 $pforcesel = $pforcesel == "1" ? 1 : 0;
9411 $pis_citytax = VikRequest::getString('is_citytax', '', 'request');
9412 $pis_fee = VikRequest::getString('is_fee', '', 'request');
9413 $pis_citytax = $pis_citytax == "1" && $pis_fee != "1" ? 1 : 0;
9414 $pis_fee = $pis_fee == "1" && $pis_citytax == 0 ? 1 : 0;
9415 $pagefrom = VikRequest::getVar('agefrom', array());
9416 $pageto = VikRequest::getVar('ageto', array());
9417 $pagecost = VikRequest::getVar('agecost', array());
9418 $pagectype = VikRequest::getVar('agectype', array());
9419 $palwaysav = VikRequest::getInt('alwaysav', 0, 'request');
9420 $pavfrom = VikRequest::getString('avfrom', '', 'request');
9421 $pavto = VikRequest::getString('avto', '', 'request');
9422 $ppcentroom = VikRequest::getInt('pcentroom', 0, 'request');
9423 $pidrooms = VikRequest::getVar('idrooms', array());
9424 $optavstr = empty($palwaysav) && !empty($pavfrom) && !empty($pavto) ? VikBooking::getDateTimestamp($pavfrom, 0, 0, 0).';'.VikBooking::getDateTimestamp($pavto, 23, 59, 59) : '';
9425 if ($pforcesel == 1) {
9426 $strforceval = intval($pforceval)."-".($pforcevalperday == "1" ? "1" : "0")."-".($pforcevalperchild == "1" ? "1" : "0")."-".($pforcesummary == "1" ? "1" : "0");
9427 } else {
9428 $strforceval = "";
9429 }
9430 $minguestsnum = VikRequest::getInt('minguestsnum', 0, 'request');
9431 $mingueststype = VikRequest::getString('mingueststype', 'guests', 'request');
9432 $minguestsnum = $minguestsnum < 0 ? 0 : $minguestsnum;
9433 $mingueststype = !empty($mingueststype) && !in_array($mingueststype, array('adults', 'guests')) ? 'guests' : $mingueststype;
9434 $maxguestsnum = VikRequest::getInt('maxguestsnum', 0, 'request');
9435 $maxgueststype = VikRequest::getString('maxgueststype', 'guests', 'request');
9436 $maxguestsnum = $maxguestsnum < 0 ? 0 : $maxguestsnum;
9437 $maxgueststype = !empty($maxgueststype) && !in_array($maxgueststype, array('adults', 'guests')) ? 'guests' : $maxgueststype;
9438 $minguests = VikRequest::getInt('minguests', 0, 'request');
9439 $minguests_conflict = false;
9440 if ($minguests > 0 && $minguestsnum > 0 && $maxguestsnum > 0) {
9441 if ($minguestsnum >= $maxguestsnum) {
9442 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL1');
9443 } elseif (($maxguestsnum - $minguestsnum) < 2) {
9444 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL2');
9445 }
9446 }
9447 if (!$minguests || $minguests_conflict !== false) {
9448 $minguestsnum = 0;
9449 $maxguestsnum = 0;
9450 if ($minguests_conflict !== false) {
9451 // raise warning, but do not stop the process
9452 VikError::raiseWarning('', $minguests_conflict);
9453 }
9454 }
9455 $damagedep = VikRequest::getInt('damagedep', 0, 'request');
9456 $pet_fee = VikRequest::getInt('pet_fee', 0, 'request');
9457 $custom_checkinout = VikRequest::getInt('custom_checkinout', 0, 'request');
9458 $set_checkin = VikRequest::getInt('set_checkin', 0, 'request');
9459 $set_checkout = VikRequest::getInt('set_checkout', 0, 'request');
9460 if (!$custom_checkinout) {
9461 $set_checkin = 0;
9462 $set_checkout = 0;
9463 }
9464 if ((!$set_checkin && !$set_checkout) || $set_checkin == $set_checkout) {
9465 // check-in and check-out times should not be equal or both empty
9466 $custom_checkinout = 0;
9467 }
9468 $damagedep_settings = $damagedep ? ((array) $app->input->get('damagedep_settings', [], 'array')) : [];
9469 $oparams = [
9470 'minguestsnum' => $minguestsnum,
9471 'mingueststype' => $mingueststype,
9472 'maxguestsnum' => $maxguestsnum,
9473 'maxgueststype' => $maxgueststype,
9474 'damagedep' => $damagedep,
9475 'damagedep_settings' => $damagedep_settings,
9476 'pet_fee' => $pet_fee,
9477 'custom_checkinout' => $custom_checkinout,
9478 'set_checkin' => $set_checkin,
9479 'set_checkout' => $set_checkout,
9480 ];
9481 /**
9482 * We fetch the previous params to merge them with the new ones
9483 * in case some properties have been set somewhere else.
9484 * For example, the damage deposit transmission to Booking.com.
9485 */
9486 $cur_oparams = array();
9487 $q = "SELECT `oparams` FROM `#__vikbooking_optionals` WHERE `id`=" . (int)$pwhereup . ";";
9488 $dbo->setQuery($q);
9489 $dbo->execute();
9490 if ($dbo->getNumRows()) {
9491 $cur_oparams = $dbo->loadResult();
9492 $cur_oparams = !empty($cur_oparams) ? json_decode($cur_oparams, true) : array();
9493 $cur_oparams = !is_array($cur_oparams) ? array() : $cur_oparams;
9494 // merge previous params with the new ones to get the new values
9495 $oparams = array_merge($cur_oparams, $oparams);
9496 }
9497
9498 /**
9499 * Ensure options of type city tax never get a tax rate.
9500 *
9501 * @since 1.18.3 (J) - 1.8.3 (WP)
9502 */
9503 if ($pis_citytax) {
9504 $poptaliq = 0;
9505 }
9506
9507 if (!empty($poptname)) {
9508 if (intval($_FILES['optimg']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['optimg']['name'])!="") {
9509 jimport('joomla.filesystem.file');
9510 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9511 if (@is_uploaded_file($_FILES['optimg']['tmp_name'])) {
9512 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['optimg']['name'])));
9513 if (file_exists($updpath.$safename)) {
9514 $j=1;
9515 while (file_exists($updpath.$j.$safename)) {
9516 $j++;
9517 }
9518 $pwhere=$updpath.$j.$safename;
9519 } else {
9520 $j="";
9521 $pwhere=$updpath.$safename;
9522 }
9523 if (!getimagesize($_FILES['optimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
9524 @unlink($pwhere);
9525 $picon="";
9526 } else {
9527 VikBooking::uploadFile($_FILES['optimg']['tmp_name'], $pwhere);
9528 @chmod($pwhere, 0644);
9529 $picon=$j.$safename;
9530 if ($pautoresize=="1" && !empty($presizeto)) {
9531 $eforj = new vikResizer();
9532 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
9533 if ($origmod) {
9534 @unlink($pwhere);
9535 $picon='r_'.$j.$safename;
9536 }
9537 }
9538 }
9539 } else {
9540 $picon="";
9541 }
9542 } else {
9543 $picon="";
9544 }
9545 ($poptperday=="each" ? $poptperday="1" : $poptperday="0");
9546 $poptperperson=($poptperperson=="each" ? "1" : "0");
9547 ($popthmany=="yes" ? $popthmany="1" : $popthmany="0");
9548 $ageintervalstr = '';
9549 if ($pifchildren == 1 && count($pagefrom) > 0 && count($pagecost) > 0 && count($pagefrom) == count($pagecost)) {
9550 foreach ($pagefrom as $kage => $vage) {
9551 $afrom = intval($vage);
9552 $ato = intval($pageto[$kage]);
9553 $acost = floatval($pagecost[$kage]);
9554 if (strlen($vage) > 0 && strlen($pagecost[$kage]) > 0) {
9555 if ($ato < $afrom) $ato = $afrom;
9556 $ageintervalstr .= $afrom.'_'.$ato.'_'.$acost.(array_key_exists($kage, $pagectype) && strpos($pagectype[$kage], '%') !== false ? '_%'.(strpos($pagectype[$kage], '%b') !== false ? 'b' : '') : '').';;';
9557 }
9558 }
9559 $ageintervalstr = rtrim($ageintervalstr, ';;');
9560 if (!empty($ageintervalstr)) {
9561 $pforcesel = 1;
9562 }
9563 }
9564 $q = "UPDATE `#__vikbooking_optionals` SET `name`=".$dbo->quote($poptname).",`descr`=".$dbo->quote($poptdescr).",`cost`=".$dbo->quote($poptcost).",`perday`=".$dbo->quote($poptperday).",`hmany`=".$dbo->quote($popthmany).",".(strlen($picon)>0 ? "`img`='".$picon."'," : "")."`idiva`=".$dbo->quote($poptaliq).", `maxprice`=".$dbo->quote($pmaxprice).", `forcesel`='".$pforcesel."', `forceval`='".$strforceval."', `perperson`='".$poptperperson."', `ifchildren`='".$pifchildren."', `maxquant`='".$pmaxquant."', `ageintervals`='".$ageintervalstr."',`is_citytax`=".$pis_citytax.",`is_fee`=".$pis_fee.",`alwaysav`=".$dbo->quote($optavstr).",`pcentroom`=".$dbo->quote($ppcentroom).",`oparams`=" . $dbo->quote(json_encode($oparams)) . " WHERE `id`=".$dbo->quote($pwhereup).";";
9565 $dbo->setQuery($q);
9566 $dbo->execute();
9567 $app->enqueueMessage(JText::translate('VBOSUCCUPDOPTION'));
9568
9569 // assign/unset option-rooms relations
9570 $rooms_with_opt = array();
9571 if (count($pidrooms)) {
9572 // assign this new option to the requested rooms
9573 foreach ($pidrooms as $idroom) {
9574 if (empty($idroom)) {
9575 continue;
9576 }
9577 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
9578 $dbo->setQuery($q);
9579 $dbo->execute();
9580 if (!$dbo->getNumRows()) {
9581 continue;
9582 }
9583 $room_data = $dbo->loadAssoc();
9584 array_push($rooms_with_opt, $room_data['id']);
9585 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9586 if (in_array((string)$pwhereup, $current_opts)) {
9587 continue;
9588 }
9589 if (count($current_opts) === 1 && (string)$current_opts[0] == '0') {
9590 // make sure we do not concatenate a real ID to 0
9591 $current_opts = array();
9592 }
9593 array_push($current_opts, $pwhereup);
9594 $new_opts = implode(';', $current_opts) . ';';
9595 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9596 $dbo->setQuery($q);
9597 $dbo->execute();
9598 }
9599 }
9600 if (!count($rooms_with_opt)) {
9601 // get all rooms to unset this option (if previously set)
9602 array_push($rooms_with_opt, '0');
9603 }
9604 // unset the option from the other rooms that may have it
9605 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_opt) . ");";
9606 $dbo->setQuery($q);
9607 $dbo->execute();
9608 if ($dbo->getNumRows()) {
9609 $unset_rooms_opt = $dbo->loadAssocList();
9610 foreach ($unset_rooms_opt as $room_data) {
9611 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9612 if (!in_array((string)$pwhereup, $current_opts)) {
9613 // this room is not using this option
9614 continue;
9615 }
9616 $optkey = array_search((string)$pwhereup, $current_opts);
9617 if ($optkey === false) {
9618 // key not found
9619 continue;
9620 }
9621 // unset this option ID from the string
9622 unset($current_opts[$optkey]);
9623 if (!count($current_opts)) {
9624 // a room with no options assigned will be listed as "0;"
9625 $current_opts = array(0);
9626 }
9627 $new_opts = implode(';', $current_opts) . ';';
9628 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9629 $dbo->setQuery($q);
9630 $dbo->execute();
9631 }
9632 }
9633 //
9634
9635 }
9636 $app->redirect("index.php?option=com_vikbooking&task=" . ($stay ? 'editoptional&cid[]=' . $pwhereup : 'optionals'));
9637 }
9638
9639 public function createoptionals()
9640 {
9641 if (!JSession::checkToken()) {
9642 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9643 }
9644
9645 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
9646 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9647 }
9648
9649 $this->do_createoptionals();
9650 }
9651
9652 public function createoptionalsstay()
9653 {
9654 if (!JSession::checkToken()) {
9655 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9656 }
9657
9658 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
9659 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9660 }
9661
9662 $this->do_createoptionals(true);
9663 }
9664
9665 private function do_createoptionals($stay = false)
9666 {
9667 $app = JFactory::getApplication();
9668 $dbo = JFactory::getDbo();
9669
9670 $poptname = VikRequest::getString('optname', '', 'request');
9671 $poptdescr = VikRequest::getString('optdescr', '', 'request', VIKREQUEST_ALLOWHTML);
9672 $poptcost = VikRequest::getFloat('optcost', '', 'request');
9673 $poptperday = VikRequest::getString('optperday', '', 'request');
9674 $poptperperson = VikRequest::getString('optperperson', '', 'request');
9675 $pmaxprice = VikRequest::getFloat('maxprice', '', 'request');
9676 $popthmany = VikRequest::getString('opthmany', '', 'request');
9677 $poptaliq = VikRequest::getInt('optaliq', '', 'request');
9678 $pautoresize = VikRequest::getString('autoresize', '', 'request');
9679 $presizeto = VikRequest::getString('resizeto', '', 'request');
9680 $pifchildren = VikRequest::getString('ifchildren', '', 'request');
9681 $pifchildren = $pifchildren == "1" ? 1 : 0;
9682 $pmaxquant = VikRequest::getString('maxquant', '', 'request');
9683 $pmaxquant = empty($pmaxquant) ? 0 : intval($pmaxquant);
9684 $pforcesel = VikRequest::getString('forcesel', '', 'request');
9685 $pforceval = VikRequest::getString('forceval', '', 'request');
9686 $pforcevalperday = VikRequest::getString('forcevalperday', '', 'request');
9687 $pforcevalperchild = VikRequest::getString('forcevalperchild', '', 'request');
9688 $pforcesummary = VikRequest::getString('forcesummary', '', 'request');
9689 $pforcesel = $pforcesel == "1" ? 1 : 0;
9690 $pis_citytax = VikRequest::getString('is_citytax', '', 'request');
9691 $pis_fee = VikRequest::getString('is_fee', '', 'request');
9692 $pis_citytax = $pis_citytax == "1" && $pis_fee != "1" ? 1 : 0;
9693 $pis_fee = $pis_fee == "1" && $pis_citytax == 0 ? 1 : 0;
9694 $pagefrom = VikRequest::getVar('agefrom', array());
9695 $pageto = VikRequest::getVar('ageto', array());
9696 $pagecost = VikRequest::getVar('agecost', array());
9697 $pagectype = VikRequest::getVar('agectype', array());
9698 $palwaysav = VikRequest::getInt('alwaysav', 0, 'request');
9699 $pavfrom = VikRequest::getString('avfrom', '', 'request');
9700 $pavto = VikRequest::getString('avto', '', 'request');
9701 $ppcentroom = VikRequest::getInt('pcentroom', 0, 'request');
9702 $pidrooms = VikRequest::getVar('idrooms', array());
9703 $optavstr = empty($palwaysav) && !empty($pavfrom) && !empty($pavto) ? VikBooking::getDateTimestamp($pavfrom, 0, 0, 0).';'.VikBooking::getDateTimestamp($pavto, 23, 59, 59) : '';
9704 if ($pforcesel == 1) {
9705 $strforceval = intval($pforceval)."-".($pforcevalperday == "1" ? "1" : "0")."-".($pforcevalperchild == "1" ? "1" : "0")."-".($pforcesummary == "1" ? "1" : "0");
9706 } else {
9707 $strforceval = "";
9708 }
9709 $minguestsnum = VikRequest::getInt('minguestsnum', 0, 'request');
9710 $mingueststype = VikRequest::getString('mingueststype', 'guests', 'request');
9711 $minguestsnum = $minguestsnum < 0 ? 0 : $minguestsnum;
9712 $mingueststype = !empty($mingueststype) && !in_array($mingueststype, array('adults', 'guests')) ? 'guests' : $mingueststype;
9713 $maxguestsnum = VikRequest::getInt('maxguestsnum', 0, 'request');
9714 $maxgueststype = VikRequest::getString('maxgueststype', 'guests', 'request');
9715 $maxguestsnum = $maxguestsnum < 0 ? 0 : $maxguestsnum;
9716 $maxgueststype = !empty($maxgueststype) && !in_array($maxgueststype, array('adults', 'guests')) ? 'guests' : $maxgueststype;
9717 $minguests = VikRequest::getInt('minguests', 0, 'request');
9718 $minguests_conflict = false;
9719 if ($minguests > 0 && $minguestsnum > 0 && $maxguestsnum > 0) {
9720 if ($minguestsnum >= $maxguestsnum) {
9721 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL1');
9722 } elseif (($maxguestsnum - $minguestsnum) < 2) {
9723 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL2');
9724 }
9725 }
9726 if (!$minguests || $minguests_conflict !== false) {
9727 $minguestsnum = 0;
9728 $maxguestsnum = 0;
9729 if ($minguests_conflict !== false) {
9730 // raise warning, but do not stop the process
9731 VikError::raiseWarning('', $minguests_conflict);
9732 }
9733 }
9734 $damagedep = VikRequest::getInt('damagedep', 0, 'request');
9735 $pet_fee = VikRequest::getInt('pet_fee', 0, 'request');
9736 $custom_checkinout = VikRequest::getInt('custom_checkinout', 0, 'request');
9737 $set_checkin = VikRequest::getInt('set_checkin', 0, 'request');
9738 $set_checkout = VikRequest::getInt('set_checkout', 0, 'request');
9739 if (!$custom_checkinout) {
9740 $set_checkin = 0;
9741 $set_checkout = 0;
9742 }
9743 if ((!$set_checkin && !$set_checkout) || $set_checkin == $set_checkout) {
9744 // check-in and check-out times should not be equal or both empty
9745 $custom_checkinout = 0;
9746 }
9747 $damagedep_settings = $damagedep ? ((array) $app->input->get('damagedep_settings', [], 'array')) : [];
9748 $oparams = [
9749 'minguestsnum' => $minguestsnum,
9750 'mingueststype' => $mingueststype,
9751 'maxguestsnum' => $maxguestsnum,
9752 'maxgueststype' => $maxgueststype,
9753 'damagedep' => $damagedep,
9754 'damagedep_settings' => $damagedep_settings,
9755 'pet_fee' => $pet_fee,
9756 'custom_checkinout' => $custom_checkinout,
9757 'set_checkin' => $set_checkin,
9758 'set_checkout' => $set_checkout,
9759 ];
9760
9761 /**
9762 * Ensure options of type city tax never get a tax rate.
9763 *
9764 * @since 1.18.3 (J) - 1.8.3 (WP)
9765 */
9766 if ($pis_citytax) {
9767 $poptaliq = 0;
9768 }
9769
9770 if (!empty($poptname)) {
9771 if (intval($_FILES['optimg']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['optimg']['name'])!="") {
9772 jimport('joomla.filesystem.file');
9773 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9774 if (@is_uploaded_file($_FILES['optimg']['tmp_name'])) {
9775 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['optimg']['name'])));
9776 if (file_exists($updpath.$safename)) {
9777 $j = 1;
9778 while (file_exists($updpath.$j.$safename)) {
9779 $j++;
9780 }
9781 $pwhere = $updpath.$j.$safename;
9782 } else {
9783 $j = "";
9784 $pwhere = $updpath.$safename;
9785 }
9786 if (!getimagesize($_FILES['optimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
9787 @unlink($pwhere);
9788 $picon = "";
9789 } else {
9790 VikBooking::uploadFile($_FILES['optimg']['tmp_name'], $pwhere);
9791 @chmod($pwhere, 0644);
9792 $picon = $j.$safename;
9793 if ($pautoresize == "1" && !empty($presizeto)) {
9794 $eforj = new vikResizer();
9795 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
9796 if ($origmod) {
9797 @unlink($pwhere);
9798 $picon = 'r_'.$j.$safename;
9799 }
9800 }
9801 }
9802 } else {
9803 $picon = "";
9804 }
9805 } else {
9806 $picon = "";
9807 }
9808 $poptperday = ($poptperday == "each" ? "1" : "0");
9809 $poptperperson = ($poptperperson == "each" ? "1" : "0");
9810 ($popthmany == "yes" ? $popthmany = "1" : $popthmany = "0");
9811 $ageintervalstr = '';
9812 if ($pifchildren == 1 && count($pagefrom) > 0 && count($pagecost) > 0 && count($pagefrom) == count($pagecost)) {
9813 foreach ($pagefrom as $kage => $vage) {
9814 $afrom = intval($vage);
9815 $ato = intval($pageto[$kage]);
9816 $acost = floatval($pagecost[$kage]);
9817 if (strlen($vage) > 0 && strlen($pagecost[$kage]) > 0) {
9818 if ($ato < $afrom) $ato = $afrom;
9819 $ageintervalstr .= $afrom.'_'.$ato.'_'.$acost.(array_key_exists($kage, $pagectype) && strpos($pagectype[$kage], '%') !== false ? '_%'.(strpos($pagectype[$kage], '%b') !== false ? 'b' : '') : '').';;';
9820 }
9821 }
9822 $ageintervalstr = rtrim($ageintervalstr, ';;');
9823 if (!empty($ageintervalstr)) {
9824 $pforcesel = 1;
9825 }
9826 }
9827 $q = "SELECT `ordering` FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` DESC LIMIT 1;";
9828 $dbo->setQuery($q);
9829 $dbo->execute();
9830 if ($dbo->getNumRows() == 1) {
9831 $getlast = $dbo->loadResult();
9832 $newsortnum = $getlast + 1;
9833 } else {
9834 $newsortnum = 1;
9835 }
9836 $q = "INSERT INTO `#__vikbooking_optionals` (`name`,`descr`,`cost`,`perday`,`hmany`,`img`,`idiva`,`maxprice`,`forcesel`,`forceval`,`perperson`,`ifchildren`,`maxquant`,`ordering`,`ageintervals`,`is_citytax`,`is_fee`,`alwaysav`,`pcentroom`,`oparams`) VALUES(".$dbo->quote($poptname).", ".$dbo->quote($poptdescr).", ".$dbo->quote($poptcost).", ".$dbo->quote($poptperday).", ".$dbo->quote($popthmany).", '".$picon."', ".$dbo->quote($poptaliq).", ".$dbo->quote($pmaxprice).", '".$pforcesel."', '".$strforceval."', '".$poptperperson."', '".$pifchildren."', '".$pmaxquant."', '".$newsortnum."', '".$ageintervalstr."', '".$pis_citytax."', '".$pis_fee."', ".$dbo->quote($optavstr).", ".$dbo->quote($ppcentroom).", " . $dbo->quote(json_encode($oparams)) . ");";
9837 $dbo->setQuery($q);
9838 $dbo->execute();
9839 $newoptid = $dbo->insertid();
9840
9841 if (!empty($newoptid)) {
9842 // assign/unset option-rooms relations
9843 $rooms_with_opt = array();
9844 if (count($pidrooms)) {
9845 // assign this new option to the requested rooms
9846 foreach ($pidrooms as $idroom) {
9847 if (empty($idroom)) {
9848 continue;
9849 }
9850 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
9851 $dbo->setQuery($q);
9852 $dbo->execute();
9853 if (!$dbo->getNumRows()) {
9854 continue;
9855 }
9856 $room_data = $dbo->loadAssoc();
9857 array_push($rooms_with_opt, $room_data['id']);
9858 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9859 if (in_array((string)$newoptid, $current_opts)) {
9860 continue;
9861 }
9862 if (count($current_opts) === 1 && (string)$current_opts[0] == '0') {
9863 // make sure we do not concatenate a real ID to 0
9864 $current_opts = array();
9865 }
9866 array_push($current_opts, $newoptid);
9867 $new_opts = implode(';', $current_opts) . ';';
9868 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9869 $dbo->setQuery($q);
9870 $dbo->execute();
9871 }
9872 }
9873 if (!count($rooms_with_opt)) {
9874 // get all rooms to unset this option (if previously set)
9875 array_push($rooms_with_opt, '0');
9876 }
9877 // unset the option from the other rooms that may have it
9878 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_opt) . ");";
9879 $dbo->setQuery($q);
9880 $dbo->execute();
9881 if ($dbo->getNumRows()) {
9882 $unset_rooms_opt = $dbo->loadAssocList();
9883 foreach ($unset_rooms_opt as $room_data) {
9884 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9885 if (!in_array((string)$newoptid, $current_opts)) {
9886 // this room is not using this option
9887 continue;
9888 }
9889 $optkey = array_search((string)$newoptid, $current_opts);
9890 if ($optkey === false) {
9891 // key not found
9892 continue;
9893 }
9894 // unset this option ID from the string
9895 unset($current_opts[$optkey]);
9896 if (!count($current_opts)) {
9897 // a room with no options assigned will be listed as "0;"
9898 $current_opts = array(0);
9899 }
9900 $new_opts = implode(';', $current_opts) . ';';
9901 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9902 $dbo->setQuery($q);
9903 $dbo->execute();
9904 }
9905 }
9906 //
9907 }
9908
9909 }
9910 $mainframe = JFactory::getApplication();
9911 $mainframe->redirect("index.php?option=com_vikbooking&task=" . ($stay && isset($newoptid) && !empty($newoptid) ? 'editoptional&cid[]=' . $newoptid : 'optionals'));
9912 }
9913
9914 public function removeoptionals()
9915 {
9916 if (!JSession::checkToken()) {
9917 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9918 }
9919
9920 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
9921 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9922 }
9923
9924 $ids = VikRequest::getVar('cid', array(0));
9925 if ($ids) {
9926 $dbo = JFactory::getDbo();
9927 foreach ($ids as $d) {
9928 $q = "SELECT `img` FROM `#__vikbooking_optionals` WHERE `id`=".$dbo->quote($d).";";
9929 $dbo->setQuery($q);
9930 $dbo->execute();
9931 if ($dbo->getNumRows() == 1) {
9932 $rows = $dbo->loadAssocList();
9933 if (!empty($rows[0]['img']) && file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['img'])) {
9934 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['img']);
9935 }
9936 }
9937 $q = "DELETE FROM `#__vikbooking_optionals` WHERE `id`=".$dbo->quote($d).";";
9938 $dbo->setQuery($q);
9939 $dbo->execute();
9940 }
9941 }
9942 $mainframe = JFactory::getApplication();
9943 $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
9944 }
9945
9946 public function sendcustomsms() {
9947 $mainframe = JFactory::getApplication();
9948 $pphone = VikRequest::getString('phone', '', 'request');
9949 $psmscont = VikRequest::getString('smscont', '', 'request');
9950 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
9951 $pgoto = !empty($pgoto) ? urldecode($pgoto) : 'index.php?option=com_vikbooking';
9952 if (!empty($pphone) && !empty($psmscont)) {
9953 $sms_api = VikBooking::getSMSAPIClass();
9954 $sms_api_params = VikBooking::getSMSParams();
9955 if (!empty($sms_api) && file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api) && !empty($sms_api_params)) {
9956 require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
9957 $sms_obj = new VikSmsApi(array(), $sms_api_params);
9958 $response_obj = $sms_obj->sendMessage($pphone, $psmscont);
9959 if ( !$sms_obj->validateResponse($response_obj) ) {
9960 VikError::raiseWarning('', $sms_obj->getLog());
9961 } else {
9962 $mainframe->enqueueMessage(JText::translate('VBSENDSMSOK'));
9963 }
9964 } else {
9965 VikError::raiseWarning('', JText::translate('VBSENDSMSERRMISSAPI'));
9966 }
9967 } else {
9968 VikError::raiseWarning('', JText::translate('VBSENDSMSERRMISSDATA'));
9969 }
9970 $mainframe->redirect($pgoto);
9971 }
9972
9973 public function sendcustomemail() {
9974 $dbo = JFactory::getDbo();
9975 $mainframe = JFactory::getApplication();
9976 $vbo_tn = VikBooking::getTranslator();
9977 $pbid = VikRequest::getInt('bid', '', 'request');
9978 $pemailsubj = VikRequest::getString('emailsubj', '', 'request');
9979 $pemail = VikRequest::getString('email', '', 'request');
9980 $pemailcont = VikRequest::getString('emailcont', '', 'request', VIKREQUEST_ALLOWRAW);
9981 $pemailfrom = VikRequest::getString('emailfrom', '', 'request');
9982 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
9983 $pgoto = !empty($pgoto) ? urldecode($pgoto) : 'index.php?option=com_vikbooking';
9984 if (!empty($pemail) && !empty($pemailcont)) {
9985 $email_attach = null;
9986 jimport('joomla.filesystem.file');
9987 $pemailattch = VikRequest::getVar('emailattch', null, 'files', 'array');
9988 if (isset($pemailattch) && strlen(trim($pemailattch['name']))) {
9989 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pemailattch['name'])));
9990 $src = $pemailattch['tmp_name'];
9991 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9992 $j = "";
9993 if (file_exists($dest.$filename)) {
9994 $j = rand(171, 1717);
9995 while (file_exists($dest.$j.$filename)) {
9996 $j++;
9997 }
9998 }
9999 $finaldest = $dest.$j.$filename;
10000 if (VikBooking::uploadFile($src, $finaldest)) {
10001 $email_attach = $finaldest;
10002 } else {
10003 VikError::raiseWarning('', 'Error uploading the attachment. Email not sent.');
10004 $mainframe->redirect($pgoto);
10005 exit;
10006 }
10007 }
10008 //VBO 1.10 - special tags for the custom email template files and messages
10009 $orig_mail_cont = $pemailcont;
10010 if (strpos($pemailcont, '{') !== false && strpos($pemailcont, '}') !== false) {
10011 // replace any possible placeholder for special tags
10012 $pemailcont = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
10013 return $match[2];
10014 }, $pemailcont);
10015
10016 $booking = array();
10017 $q = "SELECT `o`.*,`co`.`idcustomer`,CONCAT_WS(' ',`c`.`first_name`,`c`.`last_name`) AS `customer_name`,`c`.`pin` AS `customer_pin`,`nat`.`country_name` FROM `#__vikbooking_orders` AS `o` LEFT JOIN `#__vikbooking_customers_orders` `co` ON `co`.`idorder`=`o`.`id` AND `co`.`idorder`=".(int)$pbid." LEFT JOIN `#__vikbooking_customers` `c` ON `c`.`id`=`co`.`idcustomer` LEFT JOIN `#__vikbooking_countries` `nat` ON `nat`.`country_3_code`=`o`.`country` WHERE `o`.`id`=".(int)$pbid.";";
10018 $dbo->setQuery($q);
10019 $dbo->execute();
10020 if ($dbo->getNumRows() > 0) {
10021 $booking = $dbo->loadAssoc();
10022 }
10023 $booking_rooms = array();
10024 $q = "SELECT `or`.*,`r`.`name` AS `room_name` FROM `#__vikbooking_ordersrooms` AS `or` LEFT JOIN `#__vikbooking_rooms` `r` ON `r`.`id`=`or`.`idroom` WHERE `or`.`idorder`=".(int)$pbid.";";
10025 $dbo->setQuery($q);
10026 $dbo->execute();
10027 if ($dbo->getNumRows() > 0) {
10028 $booking_rooms = $dbo->loadAssocList();
10029 if (!empty($booking['lang'])) {
10030 $vbo_tn->translateContents($booking_rooms, '#__vikbooking_rooms', array('id' => 'idroom', 'room_name' => 'name'), array(), $booking['lang']);
10031 }
10032 }
10033 //we use the same parsing function as the one for the Customer SMS Template
10034 $pemailcont = VikBooking::parseCustomerSMSTemplate($booking, $booking_rooms, null, $pemailcont);
10035 }
10036 //
10037 // allow the use of token {booking_id} in subject
10038 $pemailsubj = str_replace('{booking_id}', $pbid, $pemailsubj);
10039 //
10040 $is_html = (strpos($pemailcont, '<') !== false && strpos($pemailcont, '>') !== false);
10041 $pemailcont = !$is_html ? nl2br($pemailcont) : $pemailcont;
10042 $vbo_app = VikBooking::getVboApplication();
10043 $vbo_app->sendMail($pemailfrom, $pemailfrom, $pemail, $pemailfrom, $pemailsubj, $pemailcont, $is_html, 'base64', $email_attach);
10044 $mainframe->enqueueMessage(JText::translate('VBSENDEMAILOK'));
10045 if ($email_attach !== null) {
10046 @unlink($email_attach);
10047 }
10048 //Booking History
10049 VikBooking::getBookingHistoryInstance()->setBid($pbid)->store('CE', nl2br($pemailsubj . "\n\n" . $pemailcont));
10050 //
10051 //Save email template for future sending
10052 $config_rec_exists = false;
10053 $emtpl = array(
10054 'emailsubj' => $pemailsubj,
10055 'emailcont' => $orig_mail_cont,
10056 'emailfrom' => $pemailfrom
10057 );
10058 $cur_emtpl = array();
10059 $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='customemailtpls';";
10060 $dbo->setQuery($q);
10061 $dbo->execute();
10062 if ($dbo->getNumRows() > 0) {
10063 $config_rec_exists = true;
10064 $cur_emtpl = $dbo->loadResult();
10065 $cur_emtpl = empty($cur_emtpl) ? array() : json_decode($cur_emtpl, true);
10066 $cur_emtpl = is_array($cur_emtpl) ? $cur_emtpl : array();
10067 }
10068 if (count($cur_emtpl) > 0) {
10069 $existing_subj = false;
10070 foreach ($cur_emtpl as $emk => $emv) {
10071 if (array_key_exists('emailsubj', $emv) && $emv['emailsubj'] == $emtpl['emailsubj']) {
10072 $cur_emtpl[$emk] = $emtpl;
10073 $existing_subj = true;
10074 break;
10075 }
10076 }
10077 if ($existing_subj === false) {
10078 $cur_emtpl[] = $emtpl;
10079 }
10080 } else {
10081 $cur_emtpl[] = $emtpl;
10082 }
10083 if (count($cur_emtpl) > 10) {
10084 //Max 10 templates to avoid problems with the size of the field and truncated json strings
10085 $exceed = count($cur_emtpl) - 10;
10086 for ($tl=0; $tl < $exceed; $tl++) {
10087 unset($cur_emtpl[$tl]);
10088 }
10089 $cur_emtpl = array_values($cur_emtpl);
10090 }
10091 if ($config_rec_exists === true) {
10092 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($cur_emtpl))." WHERE `param`='customemailtpls';";
10093 $dbo->setQuery($q);
10094 $dbo->execute();
10095 } else {
10096 $q = "INSERT INTO `#__vikbooking_config` (`param`,`setting`) VALUES ('customemailtpls', ".$dbo->quote(json_encode($cur_emtpl)).");";
10097 $dbo->setQuery($q);
10098 $dbo->execute();
10099 }
10100 //
10101 } else {
10102 VikError::raiseWarning('', JText::translate('VBSENDEMAILERRMISSDATA'));
10103 }
10104 $mainframe->redirect($pgoto);
10105 }
10106
10107 public function rmcustomemailtpl() {
10108 $cid = VikRequest::getVar('cid', array(0));
10109 $oid = $cid[0];
10110 $dbo = JFactory::getDBO();
10111 $mainframe = JFactory::getApplication();
10112 $tplind = VikRequest::getInt('tplind', '', 'request');
10113 if (empty($oid) || !(strlen($tplind) > 0)) {
10114 VikError::raiseWarning('', 'Missing Data.');
10115 $mainframe->redirect('index.php?option=com_vikbooking');
10116 exit;
10117 }
10118 $cur_emtpl = array();
10119 $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='customemailtpls';";
10120 $dbo->setQuery($q);
10121 $dbo->execute();
10122 if ($dbo->getNumRows() > 0) {
10123 $cur_emtpl = $dbo->loadResult();
10124 $cur_emtpl = empty($cur_emtpl) ? array() : json_decode($cur_emtpl, true);
10125 $cur_emtpl = is_array($cur_emtpl) ? $cur_emtpl : array();
10126 } else {
10127 VikError::raiseWarning('', 'Missing Templates Record.');
10128 $mainframe->redirect('index.php?option=com_vikbooking');
10129 exit;
10130 }
10131 if (array_key_exists($tplind, $cur_emtpl)) {
10132 unset($cur_emtpl[$tplind]);
10133 $cur_emtpl = count($cur_emtpl) > 0 ? array_values($cur_emtpl) : array();
10134 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($cur_emtpl))." WHERE `param`='customemailtpls';";
10135 $dbo->setQuery($q);
10136 $dbo->execute();
10137 }
10138 $mainframe->redirect('index.php?option=com_vikbooking&task=editorder&cid[]='.$oid.'&customemail=1');
10139 exit;
10140 }
10141
10142 public function exportcustomers() {
10143 //we do not set the menu for this view
10144
10145 VikRequest::setVar('view', VikRequest::getCmd('view', 'exportcustomers'));
10146
10147 parent::display();
10148
10149 if (VikBooking::showFooter()) {
10150 VikBookingHelper::printFooter();
10151 }
10152 }
10153
10154 public function csvexportprepare() {
10155 //modal box, so we do not set menu or footer
10156
10157 VikRequest::setVar('view', VikRequest::getCmd('view', 'csvexportprepare'));
10158
10159 parent::display();
10160 }
10161
10162 public function icsexportprepare() {
10163 //modal box, so we do not set menu or footer
10164
10165 VikRequest::setVar('view', VikRequest::getCmd('view', 'icsexportprepare'));
10166
10167 parent::display();
10168 }
10169
10170 public function bookingcheckin() {
10171 //modal box, so we do not set menu or footer
10172
10173 VikRequest::setVar('view', VikRequest::getCmd('view', 'bookingcheckin'));
10174
10175 parent::display();
10176 }
10177
10178 public function gencheckindoc() {
10179 //modal box, so we do not set menu or footer
10180
10181 VikRequest::setVar('view', VikRequest::getCmd('view', 'gencheckindoc'));
10182
10183 parent::display();
10184 }
10185
10186 public function checkversion() {
10187 //to be called via ajax
10188 $params = new stdClass;
10189 $params->version = VIKBOOKING_SOFTWARE_VERSION;
10190 $params->alias = 'com_vikbooking';
10191
10192 $result = array();
10193
10194 if (!count($result)) {
10195 $result = new stdClass;
10196 $result->status = 0;
10197 } else {
10198 $result = $result[0];
10199 }
10200
10201 echo json_encode($result);
10202 exit;
10203 }
10204
10205 public function updateprogram() {
10206 $params = new stdClass;
10207 $params->version = VIKBOOKING_SOFTWARE_VERSION;
10208 $params->alias = 'com_vikbooking';
10209
10210 $result = array();
10211
10212 if (!count($result) || !$result[0]) {
10213 if (class_exists('JEventDispatcher')) {
10214 $dispatcher = JEventDispatcher::getInstance();
10215 $result = $dispatcher->trigger('checkVersion', array(&$params));
10216 } else {
10217 $app = JFactory::getApplication();
10218 if (method_exists($app, 'triggerEvent')) {
10219 $result = $app->triggerEvent('checkVersion', array(&$params));
10220 }
10221 }
10222 }
10223
10224 if (!count($result) || !$result[0]->status || !$result[0]->response->status) {
10225 exit('Error, plugin disabled');
10226 }
10227
10228 JToolbarHelper::title(JText::translate('VBMAINTITLEUPDATEPROGRAM'));
10229
10230 VikBookingHelper::pUpdateProgram($result[0]->response);
10231 }
10232
10233 public function updateprogramlaunch() {
10234 $params = new stdClass;
10235 $params->version = VIKBOOKING_SOFTWARE_VERSION;
10236 $params->alias = 'com_vikbooking';
10237
10238 $json = new stdClass;
10239 $json->status = false;
10240
10241 echo json_encode($json);
10242 exit;
10243 }
10244
10245 public function invoke_vcm()
10246 {
10247 $app = JFactory::getApplication();
10248
10249 $oids = VikRequest::getVar('cid', []);
10250 $sync_type = VikRequest::getString('stype', 'new', 'request');
10251 $sync_type = !in_array($sync_type, ['new', 'modify', 'cancel']) ? 'new' : $sync_type;
10252 $original_booking_js = VikRequest::getString('origb', '', 'request', VIKREQUEST_ALLOWRAW);
10253 $return_url = VikRequest::getString('returl', '', 'request', VIKREQUEST_ALLOWRAW);
10254 $return_url = !empty($return_url) ? urldecode($return_url) : $return_url;
10255
10256 if (!$oids || !is_file(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
10257 $app->redirect("index.php?option=com_vikbooking&task=orders");
10258 $app->close();
10259 }
10260
10261 $result = VikBooking::getVcmInvoker()
10262 ->setOids($oids)
10263 ->setSyncType($sync_type)
10264 ->setOriginalBooking($original_booking_js, true)
10265 ->doSync();
10266
10267 if ($result === true) {
10268 $app->enqueueMessage(JText::translate('VBCHANNELMANAGERRESULTOK'));
10269 } else {
10270 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a>');
10271 }
10272
10273 if (!empty($return_url)) {
10274 $app->redirect($return_url);
10275 } else {
10276 $app->redirect("index.php?option=com_vikbooking&task=orders");
10277 }
10278
10279 $app->close();
10280 }
10281
10282 public function multiphotosupload() {
10283 jimport('joomla.filesystem.file');
10284
10285 $dbo = JFactory::getDBO();
10286 $proomid = VikRequest::getInt('roomid', '', 'request');
10287
10288 $resp = array('files' => array());
10289 $error_messages = array(
10290 1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
10291 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
10292 3 => 'The uploaded file was only partially uploaded',
10293 4 => 'No file was uploaded',
10294 6 => 'Missing a temporary folder',
10295 7 => 'Failed to write file to disk',
10296 8 => 'A PHP extension stopped the file upload',
10297 'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini',
10298 'max_file_size' => 'File is too big',
10299 'min_file_size' => 'File is too small',
10300 'accept_file_types' => 'Filetype not allowed',
10301 'max_number_of_files' => 'Maximum number of files exceeded',
10302 'max_width' => 'Image exceeds maximum width',
10303 'min_width' => 'Image requires a minimum width',
10304 'max_height' => 'Image exceeds maximum height',
10305 'min_height' => 'Image requires a minimum height',
10306 'abort' => 'File upload aborted',
10307 'image_resize' => 'Failed to resize image',
10308 'vbo_type' => 'The file type cannot be accepted',
10309 'vbo_jupload' => 'The upload has failed. Check your CMS settings and permissions',
10310 'vbo_perm' => 'Error moving the uploaded files. Check your permissions'
10311 );
10312
10313 $creativik = new vikResizer();
10314 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
10315 $bigsdest = $updpath;
10316 $thumbsdest = $updpath;
10317 $dest = $updpath;
10318 $moreimagestr = '';
10319 $cur_captions = json_encode(array());
10320
10321 $q = "SELECT `moreimgs`,`imgcaptions` FROM `#__vikbooking_rooms` WHERE `id`=".$proomid.";";
10322 $dbo->setQuery($q);
10323 $dbo->execute();
10324 if ($dbo->getNumRows() == 1) {
10325 $photo_data = $dbo->loadAssocList();
10326 $cur_captions = $photo_data[0]['imgcaptions'];
10327 $cur_photos = $photo_data[0]['moreimgs'];
10328 if (!empty($cur_photos)) {
10329 $moreimagestr .= $cur_photos;
10330 }
10331 }
10332
10333 $bulkphotos = VikRequest::getVar('bulkphotos', null, 'files', 'array');
10334
10335 if (is_array($bulkphotos) && count($bulkphotos) > 0 && array_key_exists('name', $bulkphotos) && count($bulkphotos['name']) > 0) {
10336 foreach ($bulkphotos['name'] as $updk => $photoname) {
10337 $uploaded_image = array();
10338 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($photoname)));
10339 $src = $bulkphotos['tmp_name'][$updk];
10340 $j = "";
10341 if (file_exists($dest.$filename)) {
10342 $j = rand(171, 1717);
10343 while (file_exists($dest.$j.$filename)) {
10344 $j++;
10345 }
10346 }
10347 $finaldest=$dest.$j.$filename;
10348 $is_error = false;
10349 $err_key = '';
10350 if (array_key_exists('error', $bulkphotos) && array_key_exists($updk, $bulkphotos['error']) && !empty($bulkphotos['error'][$updk])) {
10351 if (array_key_exists($bulkphotos['error'][$updk], $error_messages)) {
10352 $is_error = true;
10353 $err_key = $bulkphotos['error'][$updk];
10354 }
10355 }
10356 if (!$is_error) {
10357 $check = getimagesize($bulkphotos['tmp_name'][$updk]);
10358 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
10359 if (VikBooking::uploadFile($src, $finaldest)) {
10360 $gimg = $j.$filename;
10361 //orig img
10362 $origmod = true;
10363 VikBooking::uploadFile($finaldest, $bigsdest.'big_'.$j.$filename, true);
10364 //thumb
10365 $thumbsize = VikBooking::getThumbSize();
10366 $thumb = $creativik->proportionalImage($finaldest, $thumbsdest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
10367 if (!$thumb || !$origmod) {
10368 if (file_exists($bigsdest.'big_'.$j.$filename)) @unlink($bigsdest.'big_'.$j.$filename);
10369 if (file_exists($thumbsdest.'thumb_'.$j.$filename)) @unlink($thumbsdest.'thumb_'.$j.$filename);
10370 $is_error = true;
10371 $err_key = 'vbo_perm';
10372 } else {
10373 $moreimagestr.=$j.$filename.";;";
10374 }
10375 @unlink($finaldest);
10376 } else {
10377 $is_error = true;
10378 $err_key = 'vbo_jupload';
10379 }
10380 } else {
10381 $is_error = true;
10382 $err_key = 'vbo_type';
10383 }
10384 }
10385 $img = new stdClass();
10386 if ($is_error) {
10387 $img->name = '';
10388 $img->size = '';
10389 $img->type = '';
10390 $img->url = '';
10391 $img->error = array_key_exists($err_key, $error_messages) ? $error_messages[$err_key] : 'Generic Error for Upload';
10392 } else {
10393 $img->name = $photoname;
10394 $img->size = $bulkphotos['size'][$updk];
10395 $img->type = $bulkphotos['type'][$updk];
10396 $img->url = VBO_SITE_URI.'resources/uploads/big_'.$j.$filename;
10397 }
10398 $resp['files'][] = $img;
10399 }
10400 } else {
10401 $res = new stdClass();
10402 $res->name = '';
10403 $res->size = '';
10404 $res->type = '';
10405 $res->url = '';
10406 $res->error = 'No images received for upload';
10407 $resp['files'][] = $res;
10408 }
10409 //Update current extra images string
10410 $q = "UPDATE `#__vikbooking_rooms` SET `moreimgs`=".$dbo->quote($moreimagestr)." WHERE `id`=".$proomid.";";
10411 $dbo->setQuery($q);
10412 $dbo->execute();
10413 $resp['actmoreimgs'] = $moreimagestr;
10414 //Update current extra images uploaded
10415 $cur_thumbs = '';
10416 $morei=explode(';;', $moreimagestr);
10417 if (@count($morei) > 0) {
10418 $imgcaptions = json_decode($cur_captions, true);
10419 $usecaptions = empty($imgcaptions) || is_null($imgcaptions) || !is_array($imgcaptions) || !(count($imgcaptions) > 0) ? false : true;
10420 $cur_thumbs .= '<ul class="vbo-sortable">';
10421 foreach ($morei as $ki => $mi) {
10422 if (!empty($mi)) {
10423 $cur_thumbs .= '<li class="vbo-editroom-currentphoto">';
10424 $cur_thumbs .= '<a href="'.VBO_SITE_URI.'resources/uploads/big_'.$mi.'" target="_blank" class="vbomodal"><img src="'.VBO_SITE_URI.'resources/uploads/thumb_'.$mi.'" class="maxfifty"/></a>';
10425 $cur_thumbs .= '<a class="vbo-toggle-imgcaption" href="javascript: void(0);" onclick="vbOpenImgDetails(\''.$ki.'\', this)"><i class="'.VikBookingIcons::i('cog').'"></i></a>';
10426 $cur_thumbs .= '<div id="vbimgdetbox'.$ki.'" class="vbimagedetbox" style="display: none;"><div class="captionlabel"><span>'.JText::translate('VBIMGCAPTION').'</span><input type="text" name="caption'.$ki.'" value="'.($usecaptions === true && isset($imgcaptions[$ki]) ? $imgcaptions[$ki] : "").'" size="40"/></div><input type="hidden" name="imgsorting[]" value="'.$mi.'"/><input class="captionsubmit" type="button" name="updcatpion" value="'.JText::translate('VBIMGUPDATE').'" onclick="javascript: updateCaptions();"/><div class="captionremoveimg"><a class="vbimgrm btn btn-danger" href="index.php?option=com_vikbooking&task=removemoreimgs&roomid='.$proomid.'&imgind='.$ki.'" title="'.JText::translate('VBREMOVEIMG').'"><i class="icon-remove"></i>'.JText::translate('VBREMOVEIMG').'</a></div></div>';
10427 $cur_thumbs .= '</li>';
10428 }
10429 }
10430 $cur_thumbs .= '</ul>';
10431 $cur_thumbs .= '<br clear="all"/>';
10432 }
10433 $resp['currentthumbs'] = $cur_thumbs;
10434
10435 echo json_encode($resp);
10436 exit;
10437 }
10438
10439 public function loadsmsbalance() {
10440 //to be called via ajax
10441 $html = 'Error1 [N/A]';
10442 $sms_api = VikBooking::getSMSAPIClass();
10443 if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api)) {
10444 require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
10445 $sms_obj = new VikSmsApi(array(), VikBooking::getSMSParams());
10446 if (method_exists('VikSmsApi', 'estimate')) {
10447 $array_result = $sms_obj->estimate("+393711271611", "estimate credit");
10448 if ( $array_result->errorCode != 0 ) {
10449 $html = 'Error3 ['.$array_result->errorMsg.']';
10450 } else {
10451 $html = VikBooking::getCurrencySymb().' '.$array_result->userCredit;
10452 }
10453 } else {
10454 $html = 'Error2 [N/A]';
10455 }
10456 }
10457 echo $html;
10458 exit;
10459 }
10460
10461 public function loadsmsparams() {
10462 //to be called via ajax
10463 $html = '---------';
10464 $phpfile = VikRequest::getString('phpfile', '', 'request');
10465 if (!empty($phpfile)) {
10466 $sms_api = VikBooking::getSMSAPIClass();
10467 $sms_params = $sms_api == $phpfile ? VikBooking::getSMSParams(false) : '';
10468 $html = VikBooking::displaySMSParameters($phpfile, $sms_params);
10469 }
10470 echo $html;
10471 exit;
10472 }
10473
10474 public function loadcronparams() {
10475 //to be called via ajax
10476 $html = '---------';
10477 $phpfile = VikRequest::getString('phpfile', '', 'request');
10478 if (!empty($phpfile)) {
10479 $html = VikBooking::displayCronParameters($phpfile);
10480 }
10481 echo $html;
10482 exit;
10483 }
10484
10485 public function loadpaymentparams() {
10486 //to be called via ajax
10487 $html = '<p>---------</p>';
10488 $phpfile = VikRequest::getString('phpfile', '', 'request');
10489 if (!empty($phpfile)) {
10490 $html = VikBooking::displayPaymentParameters($phpfile);
10491 }
10492 echo $html;
10493 exit;
10494 }
10495
10496 public function setbookingtag() {
10497 //to be called via ajax
10498 $dbo = JFactory::getDBO();
10499 $pidorder = VikRequest::getInt('idorder', '', 'request');
10500 $ptagkey = VikRequest::getInt('tagkey', '', 'request');
10501 if (!empty($pidorder) && $ptagkey >= 0) {
10502 $all_tags = VikBooking::loadBookingsColorTags();
10503 if (array_key_exists($ptagkey, $all_tags)) {
10504 $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `id`=".(int)$pidorder.";";
10505 $dbo->setQuery($q);
10506 $dbo->execute();
10507 if ($dbo->getNumRows() > 0) {
10508 $newcolortag = json_encode($all_tags[$ptagkey]);
10509 $q = "UPDATE `#__vikbooking_orders` SET `colortag`=".$dbo->quote($newcolortag)." WHERE `id`=".(int)$pidorder.";";
10510 $dbo->setQuery($q);
10511 $dbo->execute();
10512 $newcolortag = $all_tags[$ptagkey];
10513 $newcolortag['name'] = JText::translate($newcolortag['name']);
10514 $newcolortag['fontcolor'] = VikBooking::getBestColorContrast($newcolortag['color']);
10515 echo json_encode($newcolortag);
10516 } else {
10517 echo 'e4j.error.Booking ('.$pidorder.') not found';
10518 }
10519 } else {
10520 echo 'e4j.error.Color Tag ('.$ptagkey.') not found';
10521 }
10522 } else {
10523 echo 'e4j.error.Missing Data';
10524 }
10525 exit;
10526 }
10527
10528 public function updatereceiptnum() {
10529 //to be called via ajax
10530 $pnewnum = VikRequest::getInt('newnum', '', 'request');
10531 $pnewnotes = VikRequest::getString('newnotes', '', 'request', VIKREQUEST_ALLOWRAW);
10532 $poid = VikRequest::getInt('oid', '', 'request');
10533 if ($pnewnum > 0) {
10534 VikBooking::getNextReceiptNumber($poid, $pnewnum);
10535 VikBooking::getReceiptNotes($pnewnotes);
10536 //Booking History
10537 VikBooking::getBookingHistoryInstance()->setBid($poid)->store('BR', JText::translate('VBOFISCRECEIPTNUM').': '.$pnewnum);
10538 //
10539 echo 'e4j.ok';
10540 exit;
10541 }
10542 echo 'e4j.error';
10543 exit;
10544 }
10545
10546 /**
10547 * AJAX endpoint to check if a room ID is available on specific dates.
10548 */
10549 public function isroombookable()
10550 {
10551 $app = JFactory::getApplication();
10552 $dbo = JFactory::getDbo();
10553
10554 $prid = $app->input->getUInt('rid', 0);
10555 $pfdate = $app->input->getString('fdate', '');
10556 $ptdate = $app->input->getString('tdate', '');
10557
10558 if (empty($prid) || empty($pfdate) || empty($ptdate)) {
10559 VBOHttpDocument::getInstance($app)->close(400, 'Missing request values.');
10560 }
10561
10562 $res = [
10563 'status' => 0,
10564 'err' => '',
10565 ];
10566
10567 $room_info = VikBooking::getRoomInfo($prid);
10568 if (!$room_info) {
10569 VBOHttpDocument::getInstance($app)->close(404, 'Room not found.');
10570 }
10571
10572 $pcheckinh = 0;
10573 $pcheckinm = 0;
10574 $pcheckouth = 0;
10575 $pcheckoutm = 0;
10576 $timeopst = VikBooking::getTimeOpenStore();
10577 if (is_array($timeopst)) {
10578 $opent = VikBooking::getHoursMinutes($timeopst[0]);
10579 $closet = VikBooking::getHoursMinutes($timeopst[1]);
10580 $pcheckinh = $opent[0];
10581 $pcheckinm = $opent[1];
10582 $pcheckouth = $closet[0];
10583 $pcheckoutm = $closet[1];
10584 }
10585
10586 $from_ts = VikBooking::getDateTimestamp($pfdate, $pcheckinh, $pcheckinm);
10587 $to_ts = VikBooking::getDateTimestamp($ptdate, $pcheckouth, $pcheckoutm);
10588
10589 if (!empty($from_ts) && !empty($to_ts) && VikBooking::roomBookable($room_info['id'], $room_info['units'], $from_ts, $to_ts)) {
10590 $res['status'] = 1;
10591 } else {
10592 if (empty($from_ts) || empty($to_ts)) {
10593 $res['err'] = 'Invalid dates';
10594 } else {
10595 // not available
10596 $res['err'] = JText::sprintf('VBOBOOKADDROOMERR', $room_info['name'], $pfdate, $ptdate);
10597 }
10598 }
10599
10600 // send response to output
10601 VBOHttpDocument::getInstance($app)->json($res);
10602 }
10603
10604 public function uploadsnapshot() {
10605 $snap_base_path = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans';
10606 /**
10607 * We no longer access the uploaded file from php://input, we now retrieve it as a regular file upload.
10608 * The old snapshot collection script with Flash no longer works in 2021.
10609 *
10610 * @since 1.14 (J) - 1.4.0 (WP)
10611 */
10612 $result = null;
10613 try {
10614 $result = VikBooking::uploadFileFromRequest(VikRequest::getVar('snapshot', null, 'files', 'array'), $snap_base_path, 'png,jpg,jpeg');
10615 } catch (RuntimeException $e) {
10616 echo "e4j.error.Error " . $e->getMessage();
10617 exit;
10618 }
10619
10620 if (!is_object($result)) {
10621 echo "e4j.error.Invalid upload response";
10622 exit;
10623 }
10624
10625 echo $result->filename;
10626 exit;
10627 }
10628
10629 public function checkvcmrateschanges() {
10630 //to be called via ajax
10631 $session = JFactory::getSession();
10632 $ret = array('changesCount' => 0, 'changesData' => '');
10633 $updforvcm = $session->get('vbVcmRatesUpd', '');
10634 if (!empty($updforvcm) && is_array($updforvcm) && count($updforvcm) > 0) {
10635 $ret['changesCount'] = $updforvcm['count'];
10636 $ret['changesData'] = $updforvcm;
10637 }
10638
10639 echo json_encode($ret);
10640 exit;
10641 }
10642
10643 /**
10644 * AJAX endpoint to load the details of one or more bookings.
10645 *
10646 * @return void
10647 *
10648 * @since 1.16.0 (J) - 1.6.0 (WP) the method was refactored.
10649 */
10650 public function getbookingsinfo()
10651 {
10652 //to be called via ajax
10653 $dbo = JFactory::getDbo();
10654
10655 $booking_infos = [];
10656 $bookings = [];
10657
10658 $pidorders = VikRequest::getString('idorders', '', 'request');
10659 $psubroom = VikRequest::getString('subroom', '', 'request');
10660 $pstatus = VikRequest::getString('status', '', 'request');
10661 $pstay_date = VikRequest::getString('stay_date', '', 'request');
10662 $pidroom = VikRequest::getInt('idroom', 0, 'request');
10663 $psharedcal = VikRequest::getInt('sharedcal', 0, 'request');
10664
10665 if (!empty($pidorders)) {
10666 $bookings = explode(',', $pidorders);
10667 foreach ($bookings as $k => $v) {
10668 $v = intval(str_replace('-', '', $v));
10669 if (empty($v)) {
10670 unset($bookings[$k]);
10671 continue;
10672 }
10673 $bookings[$k] = $v;
10674 }
10675 }
10676 $bookings = array_values($bookings);
10677
10678 if (!$bookings) {
10679 /**
10680 * AJAX requests made by the page availability overview may contain empty booking IDs
10681 * due to SQL errors that only occupied the room, but could not save the booking record.
10682 * Clean up busy records where the busy relations contain empty booking IDs.
10683 *
10684 * @since 1.14 (J) - 1.4.0 (WP)
10685 */
10686 $hanging_busy_ids = [];
10687
10688 $q = "SELECT `idbusy` FROM `#__vikbooking_ordersbusy` WHERE `idorder` = 0 OR `idorder` IS NULL;";
10689 $dbo->setQuery($q);
10690 $removelist = $dbo->loadAssocList();
10691 if ($removelist) {
10692 foreach ($removelist as $hanging_busy) {
10693 $hanging_busy_id = (int)$hanging_busy['idbusy'];
10694 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
10695 array_push($hanging_busy_ids, $hanging_busy_id);
10696 }
10697 }
10698 }
10699
10700 // let's check also for ghost records that only occupy the room
10701 $q = "SELECT `b`.*,`ob`.`idorder` FROM `#__vikbooking_busy` AS `b` LEFT JOIN `#__vikbooking_ordersbusy` AS `ob` ON `b`.`id`=`ob`.`idbusy` WHERE `b`.`checkout` >= " . time() . " AND (`ob`.`idorder` = 0 OR `ob`.`idorder` IS NULL);";
10702 $dbo->setQuery($q);
10703 $removelist = $dbo->loadAssocList();
10704 if ($removelist) {
10705 foreach ($removelist as $hanging_busy) {
10706 $hanging_busy_id = (int)$hanging_busy['id'];
10707 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
10708 array_push($hanging_busy_ids, $hanging_busy_id);
10709 }
10710 }
10711 }
10712
10713 if ($hanging_busy_ids) {
10714 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id` IN (" . implode(', ', $hanging_busy_ids) . ");";
10715 $dbo->setQuery($q);
10716 $dbo->execute();
10717 }
10718 //
10719
10720 // output the error
10721 VBOHttpDocument::getInstance()->close(500, '1 - ' . JText::translate('VBOVWGETBKERRMISSDATA'));
10722 }
10723
10724 $nowdf = VikBooking::getDateFormat(true);
10725 if ($nowdf == "%d/%m/%Y") {
10726 $df = 'd/m/Y';
10727 } elseif ($nowdf == "%m/%d/%Y") {
10728 $df = 'm/d/Y';
10729 } else {
10730 $df = 'Y/m/d';
10731 }
10732 $datesep = VikBooking::getDateSeparator(true);
10733 $currencysymb = VikBooking::getCurrencySymb();
10734 $current_y = date('Y');
10735 $current_ts = time();
10736 $short_meal_enums = VBOMealplanManager::getInstance()->getShortMealPlans();
10737
10738 $query = $dbo->getQuery(true);
10739 $query->select('o.*');
10740 $query->from($dbo->qn('#__vikbooking_orders', 'o'));
10741 if (!empty($pstay_date) && !empty($pidroom) && $pstatus == 'any') {
10742 // include the requested booking IDs and the cancelled reservations for this stay date
10743 $stay_date_info = getdate(strtotime($pstay_date));
10744 $lim_ts_to = mktime(23, 59, 59, $stay_date_info['mon'], $stay_date_info['mday'], $stay_date_info['year']);
10745 $query->where('((' . $dbo->qn('o.checkin') . ' <= ' . $lim_ts_to . ' AND ' . $dbo->qn('o.checkout') . ' > ' . $lim_ts_to . ') OR ' . $dbo->qn('o.id') . ' IN (' . implode(', ', $bookings) . '))');
10746 // exclude the pending reservations
10747 $query->where($dbo->qn('o.status') . ' IN (' . $dbo->q('confirmed') . ', ' . $dbo->q('cancelled') . ')');
10748 } else {
10749 // include only the requested booking IDs
10750 $query->where($dbo->qn('o.id') . ' IN (' . implode(', ', $bookings) . ')');
10751 }
10752 if ($pstatus != 'any') {
10753 $query->where($dbo->qn('o.status') . ' != ' . $dbo->q('cancelled'));
10754 }
10755 if (!empty($pstay_date) && $pstatus == 'any') {
10756 // sort by confirmed status before cancelled status
10757 $query->order('CASE WHEN ' . $dbo->qn('o.status') . ' = ' . $dbo->q('confirmed') . ' THEN 1 ELSE 0 END DESC');
10758 $query->order($dbo->qn('o.id') . ' ASC');
10759 }
10760 $dbo->setQuery($query);
10761 $booking_infos = $dbo->loadAssocList();
10762
10763 foreach ($booking_infos as $k => $row) {
10764 // rooms, amounts and guests information
10765 $rooms = VikBooking::loadOrdersRoomsData($row['id']);
10766 $rids_involved = [];
10767 $room_names = [];
10768 $totadults = 0;
10769 $totchildren = 0;
10770 foreach ($rooms as $rr) {
10771 $rids_involved[] = $rr['idroom'];
10772 $totadults += $rr['adults'];
10773 $totchildren += $rr['children'];
10774 $room_names[] = $rr['room_name'];
10775 if ($row['split_stay']) {
10776 // do not sum guests in case of split stay booking
10777 $totadults = $rr['adults'];
10778 $totchildren = $rr['children'];
10779 }
10780 }
10781
10782 if (!empty($pstay_date) && !empty($pidroom) && $pstatus == 'any') {
10783 // make sure we have fetched a reservation for the correct room (in case of cancellations included)
10784 if (!in_array($pidroom, $rids_involved)) {
10785 $is_out_of_scope = true;
10786 if ($psharedcal && count($bookings) === 1) {
10787 $is_out_of_scope = ($row['id'] != $bookings[0]);
10788 }
10789 if ($is_out_of_scope) {
10790 // out of scope reservation, unset it and go to the next one
10791 unset($booking_infos[$k]);
10792 continue;
10793 }
10794 }
10795 }
10796
10797 // included meal plans to be displayed in case of single-room booking
10798 $included_meals = [];
10799 $rplan_name = '';
10800 if (count($rooms) === 1) {
10801 // rate plan name and ID, if any
10802 $active_rplan_id = 0;
10803 if (!empty($rooms[0]['otarplan'])) {
10804 $rplan_name = $rooms[0]['otarplan'];
10805 } else {
10806 list($rplan_name, $active_rplan_id) = VBOMealplanManager::getInstance()->getPriceData($rooms[0]['idtar']);
10807 }
10808
10809 // find the included meals
10810 if (!empty($rooms[0]['meals'])) {
10811 // display included meals defined at room-reservation record
10812 $included_meals = VBOMealplanManager::getInstance()->roomRateIncludedMeals($rooms[0]);
10813 } else {
10814 // fetch default included meals in the selected rate plan
10815 $included_meals = $active_rplan_id ? VBOMealplanManager::getInstance()->ratePlanIncludedMeals($active_rplan_id) : [];
10816 }
10817 if (!$included_meals && empty($row['meals']) && !empty($row['idorderota']) && !empty($row['channel']) && !empty($row['custdata'])) {
10818 // attempt to fetch the included meal plans from the raw customer data or OTA reservation and room
10819 $included_meals = VBOMealplanManager::getInstance()->otaDataIncludedMeals($row, $rooms[0]);
10820 }
10821 }
10822
10823 if ($included_meals) {
10824 $short_incl_meals = [];
10825 foreach ($included_meals as $meal_enum => $meal_name) {
10826 $short_incl_meals[] = $short_meal_enums[$meal_enum];
10827 }
10828 $booking_infos[$k]['meals_included'] = $short_incl_meals;
10829 }
10830
10831 $booking_infos[$k]['rateplan_name'] = $rplan_name;
10832 $booking_infos[$k]['currency_symb'] = $currencysymb;
10833 if ($row['status'] == 'confirmed') {
10834 $booking_infos[$k]['status_lbl'] = JText::translate('VBCONFIRMED');
10835 if ($row['checkout'] < $current_ts) {
10836 $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSOUT');
10837 } elseif ($row['checkin'] < $current_ts && $row['checkout'] > $current_ts) {
10838 if ($row['checked'] == 1) {
10839 $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSIN');
10840 } elseif ($row['checked'] == -1) {
10841 $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSNOS');
10842 }
10843 }
10844 } elseif ($row['status'] == 'standby') {
10845 $booking_infos[$k]['status_lbl'] = JText::translate('VBSTANDBY');
10846 } elseif ($row['status'] == 'cancelled') {
10847 $booking_infos[$k]['status_lbl'] = JText::translate('VBCANCELLED');
10848 } else {
10849 $booking_infos[$k]['status_lbl'] = $row['status'];
10850 }
10851 $booking_infos[$k]['colortag'] = VikBooking::applyBookingColorTag($row);
10852 if ($booking_infos[$k]['colortag']) {
10853 $booking_infos[$k]['colortag']['name'] = JText::translate($booking_infos[$k]['colortag']['name']);
10854 }
10855 $booking_infos[$k]['room_names'] = implode(', ', $room_names);
10856 $booking_infos[$k]['tot_adults'] = $totadults;
10857 $booking_infos[$k]['tot_children'] = $totchildren;
10858 $booking_infos[$k]['format_tot'] = VikBooking::numberFormat($row['total']);
10859 $booking_infos[$k]['format_totpaid'] = VikBooking::numberFormat($row['totpaid']);
10860
10861 // room indexes
10862 $rindexes = [];
10863 $av_room_indexes = [];
10864 $used_indexes_map = [];
10865 $sub_units_data = [];
10866 $optindexes = [];
10867 $subroomdata = !empty($psubroom) ? explode('-', $psubroom) : array();
10868 $missing_index = false;
10869 foreach ($rooms as $kor => $or) {
10870 if ($row['status'] != "confirmed" || $row['closure'] || empty($or['params'])) {
10871 // cannot build room indexes data
10872 continue;
10873 }
10874
10875 $room_params = json_decode($or['params'], true);
10876 if (!is_array($room_params) || empty($room_params['features']) || !is_array($room_params['features'])) {
10877 // no distinctive features information
10878 continue;
10879 }
10880
10881 if (!strlen($or['roomindex'])) {
10882 // turn flag on for missing index when room does support them
10883 $missing_index = true;
10884 // build array with available room indexes
10885 $av_indexes = [];
10886 $unavailable_indexes = VikBooking::getRoomUnitNumsUnavailable($row, $or['idroom']);
10887 foreach ($room_params['features'] as $rind => $rfeatures) {
10888 if (in_array($rind, $unavailable_indexes) || (isset($used_indexes_map[$or['idroom']]) && in_array($rind, $used_indexes_map[$or['idroom']]))) {
10889 continue;
10890 }
10891 foreach ($rfeatures as $fname => $fval) {
10892 if ($fval) {
10893 $av_indexes[$rind] = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
10894 break;
10895 }
10896 }
10897 }
10898 if ($av_indexes) {
10899 // push available indexes for this room
10900 $av_room_indexes[$kor] = [
10901 'rid' => $or['idroom'],
10902 'name' => $or['room_name'],
10903 'list' => $av_indexes,
10904 ];
10905 }
10906 // do not proceed any further
10907 continue;
10908 }
10909
10910 // parse distinctive features
10911 foreach ($room_params['features'] as $rind => $rfeatures) {
10912 if ($rind != $or['roomindex']) {
10913 continue;
10914 }
10915 $ind_str = '';
10916 $ind_str_short = '';
10917 foreach ($rfeatures as $fname => $fval) {
10918 if (strlen($fval)) {
10919 $ind_str = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
10920 $ind_str_short = $fval;
10921 break;
10922 }
10923 }
10924 if (!isset($rindexes[$or['room_name']])) {
10925 $rindexes[$or['room_name']] = $ind_str;
10926 $sub_units_data[$or['room_name']] = $ind_str_short;
10927 } else {
10928 $rindexes[$or['room_name']] .= ', ' . $ind_str;
10929 $sub_units_data[$or['room_name']] .= ', ' . $ind_str_short;
10930 }
10931 break;
10932 }
10933
10934 // build options to switch sub-unit index
10935 if (count($subroomdata) && !count($optindexes) && $or['idroom'] == (int)$subroomdata[0]) {
10936 // build the options for switching the room index for this room
10937 foreach ($room_params['features'] as $rind => $rfeatures) {
10938 foreach ($rfeatures as $fname => $fval) {
10939 if (strlen((string)$fval)) {
10940 $optindexes[] = '<option value="'.$rind.'"'.($rind == (int)$subroomdata[1] ? ' selected="selected"' : '').'>#'.$rind.' - '.JText::translate($fname).': '.$fval.'</option>';
10941 break;
10942 }
10943 }
10944 }
10945 }
10946 }
10947
10948 if ($rindexes) {
10949 $booking_infos[$k]['rindexes'] = $rindexes;
10950 $booking_infos[$k]['sub_units_data'] = $sub_units_data;
10951 }
10952
10953 if ($optindexes) {
10954 $booking_infos[$k]['optindexes'] = $optindexes;
10955 }
10956
10957 if ($missing_index && $av_room_indexes) {
10958 $booking_infos[$k]['av_room_indexes'] = $av_room_indexes;
10959 }
10960
10961 // include flag for missing room index
10962 $booking_infos[$k]['missing_index'] = $missing_index;
10963
10964 // channel provenience and small logo URL
10965 $ota_logo_img = JText::translate('VBORDFROMSITE');
10966 $booking_avatar_src = null;
10967 $booking_avatar_alt = null;
10968 if (!empty($row['channel'])) {
10969 $channelparts = explode('_', $row['channel']);
10970 $otachannel = array_key_exists(1, $channelparts) && strlen($channelparts[1]) > 0 ? $channelparts[1] : ucwords($channelparts[0]);
10971 $ota_logo_img = VikBooking::getVcmChannelsLogo($row['channel']);
10972 if ($ota_logo_img === false) {
10973 $ota_logo_img = $otachannel;
10974 } else {
10975 $ota_logo_img = '<img src="'.$ota_logo_img.'" class="vbo-channelimg-small"/>';
10976 }
10977 $logo_helper = VikBooking::getVcmChannelsLogo($row['channel'], $get_istance = true);
10978 if ($logo_helper !== false) {
10979 $booking_avatar_src = $logo_helper->getSmallLogoURL();
10980 $booking_avatar_alt = $logo_helper->provenience;
10981 }
10982 }
10983 $booking_infos[$k]['channelimg'] = $ota_logo_img;
10984 $booking_infos[$k]['avatar_src'] = $booking_avatar_src;
10985 $booking_infos[$k]['avatar_alt'] = $booking_avatar_alt;
10986
10987 // Customer Details
10988 $custdata = $row['custdata'];
10989 $custdata_parts = explode("\n", $row['custdata']);
10990 if (count($custdata_parts) > 2 && strpos($custdata_parts[0], ':') !== false && strpos($custdata_parts[1], ':') !== false) {
10991 //get the first two fields
10992 $custvalues = [];
10993 foreach ($custdata_parts as $custdet) {
10994 if (strlen($custdet) < 1) {
10995 continue;
10996 }
10997 $custdet_parts = explode(':', $custdet);
10998 if (count($custdet_parts) >= 2) {
10999 unset($custdet_parts[0]);
11000 array_push($custvalues, trim(implode(':', $custdet_parts)));
11001 }
11002 if (count($custvalues) > 1) {
11003 break;
11004 }
11005 }
11006 if (count($custvalues) > 1) {
11007 $custdata = implode(' ', $custvalues);
11008 }
11009 }
11010 if (strlen($custdata) > 45) {
11011 $custdata = (function_exists('mb_substr') ? mb_substr($custdata, 0, 45, 'UTF-8') : substr($custdata, 0, 45)) . " ...";
11012 }
11013
11014 // customer record details
11015 $customer = [];
11016 $q = "SELECT `c`.*,`co`.`idorder` FROM `#__vikbooking_customers` AS `c` LEFT JOIN `#__vikbooking_customers_orders` `co` ON `c`.`id`=`co`.`idcustomer` WHERE `co`.`idorder`=" . $row['id'];
11017 $dbo->setQuery($q, 0, 1);
11018 $dbo->execute();
11019 if ($dbo->getNumRows()) {
11020 $customer = $dbo->loadAssoc();
11021 if (!empty($customer['first_name'])) {
11022 $custdata = $customer['first_name'].' '.$customer['last_name'];
11023 if (!empty($customer['country'])) {
11024 if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$customer['country'].'.png')) {
11025 $custdata .= '<img src="'.VBO_ADMIN_URI.'resources/countries/'.$customer['country'].'.png'.'" title="'.htmlspecialchars($customer['country']).'" class="vbo-country-flag vbo-country-flag-left"/>';
11026 }
11027 }
11028 }
11029 }
11030 $booking_infos[$k]['customer'] = $customer;
11031
11032 // check if a profile picture is available for the customer
11033 if (!empty($customer['pic'])) {
11034 $booking_avatar_src = strpos($customer['pic'], 'http') === 0 ? $customer['pic'] : VBO_SITE_URI . 'resources/uploads/' . $customer['pic'];
11035 $booking_avatar_alt = basename($booking_avatar_src);
11036 $booking_infos[$k]['avatar_src'] = $booking_avatar_src;
11037 $booking_infos[$k]['avatar_alt'] = $booking_avatar_alt;
11038 }
11039
11040 // whether this is a closure
11041 $booking_infos[$k]['closure'] = (int)$row['closure'];
11042 $booking_infos[$k]['closure_txt'] = $row['closure'] ? JText::translate('VBDBTEXTROOMCLOSED') : null;
11043
11044 // short customer information
11045 $custdata = JText::translate('VBDBTEXTROOMCLOSED') == $row['custdata'] ? '<span class="vbordersroomclosed">'.JText::translate('VBDBTEXTROOMCLOSED').'</span>' : $custdata;
11046 $booking_infos[$k]['cinfo'] = $custdata;
11047
11048 // formatted dates
11049 $booking_infos[$k]['ts'] = date(str_replace("/", $datesep, $df).' H:i', $row['ts']);
11050 $booking_infos[$k]['checkin'] = date(str_replace("/", $datesep, $df).' H:i', $row['checkin']);
11051 $booking_infos[$k]['checkout'] = date(str_replace("/", $datesep, $df).' H:i', $row['checkout']);
11052
11053 // short booking date, check-in, check-out date format
11054 $stay_info_in = getdate($row['checkin']);
11055 $stay_info_out = getdate($row['checkout']);
11056 $str_checkin = date('d', $row['checkin']);
11057 $str_checkin .= $stay_info_in['mon'] != $stay_info_out['mon'] ? ' ' . VikBooking::sayMonth($stay_info_in['mon'], $short = true) : '';
11058 $str_checkout = date('d', $row['checkout']) . ' ' . VikBooking::sayMonth($stay_info_out['mon'], $short = true);
11059 if ($stay_info_in['year'] != $stay_info_out['year'] || $stay_info_in['year'] != $current_y || $stay_info_out['year'] != $current_y) {
11060 $str_checkout .= ' ' . $stay_info_out['year'];
11061 }
11062 $booking_infos[$k]['checkin_short'] = $str_checkin;
11063 $booking_infos[$k]['checkout_short'] = $str_checkout;
11064 $booking_infos[$k]['book_date'] = date(str_replace("/", $datesep, $df), $row['ts']);
11065 $booking_infos[$k]['book_time'] = date('H:i', $row['ts']);
11066 }
11067
11068 if (!$booking_infos) {
11069 if (!empty($pidroom) && $psharedcal) {
11070 // when this flag is enabled, we are excluding bookings not made directly
11071 // for the given room type ID, hence we may get an empty list due to shared calendars
11072 VBOHttpDocument::getInstance()->close(500, 'No bookings made directly for this room-type.');
11073 }
11074 // output the error
11075 VBOHttpDocument::getInstance()->close(500, '2 - ' . JText::translate('VBOVWGETBKERRMISSDATA'));
11076 }
11077
11078 // output the JSON encoded response and exit
11079 VBOHttpDocument::getInstance()->json($booking_infos);
11080 }
11081
11082 /**
11083 * AJAX endpoint to switch a booking room index.
11084 *
11085 * @return void
11086 *
11087 * @since 1.18.2 (J) - 1.8.2 (WP) method refactored.
11088 * @since 1.18.7 (J) - 1.8.7 (WP) introduced history update.
11089 */
11090 public function switchRoomIndex()
11091 {
11092 if (!JSession::checkToken()) {
11093 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
11094 }
11095
11096 $app = JFactory::getApplication();
11097 $dbo = JFactory::getDbo();
11098
11099 $bid = $app->input->getInt('bid', 0);
11100 $rid = $app->input->getInt('rid', 0);
11101 $old_rindex = $app->input->getInt('old_rindex', 0);
11102 $new_rindex = $app->input->getInt('new_rindex', 0);
11103 $is_tmp_row = $app->input->getBool('is_tmp_row', false);
11104 $is_from_tmp_row = $app->input->getBool('is_from_tmp_row', false);
11105
11106 if (empty($bid) || empty($rid) || (empty($old_rindex) && !$is_from_tmp_row) || (empty($new_rindex) && !$is_tmp_row) || $new_rindex < 0) {
11107 // abort for missing or invalid room indexes
11108 VBOHttpDocument::getInstance($app)->close(200, 'e4j.error.#1 Missing Data');
11109 }
11110
11111 // fetch the current booking room record
11112 $dbo->setQuery(
11113 $dbo->getQuery(true)
11114 ->select('*')
11115 ->from($dbo->qn('#__vikbooking_ordersrooms'))
11116 ->where($dbo->qn('idorder') . ' = ' . $bid)
11117 ->where($dbo->qn('idroom') . ' = ' . $rid)
11118 ->where($dbo->qn('roomindex') . ((empty($old_rindex) || $old_rindex == -1) && $is_from_tmp_row ? ' IS NULL' : ' = ' . $old_rindex))
11119 ->order($dbo->qn('id') . ' ASC')
11120 );
11121 $roomRow = $dbo->loadAssoc();
11122
11123 if (!$roomRow) {
11124 // abort for record not found
11125 VBOHttpDocument::getInstance($app)->close(200, 'e4j.error.#2 Record not found');
11126 }
11127
11128 // load booking and booking rooms data for the history before updating
11129 $booking = VikBooking::getBookingInfoFromID($bid);
11130 $prev_booking_rooms = VikBooking::loadOrdersRoomsData($bid);
11131 $current_booking_rooms = $prev_booking_rooms;
11132 // update new room index for current booking rooms
11133 foreach ($current_booking_rooms as $k => $booking_room) {
11134 if ($booking_room['id'] == $roomRow['id']) {
11135 // update new room index
11136 $current_booking_rooms[$k]['roomindex'] = empty($new_rindex) && $is_tmp_row ? null : $new_rindex;
11137 break;
11138 }
11139 }
11140
11141 // update booking room record by switching sub-unit
11142 $dbo->setQuery(
11143 $dbo->getQuery(true)
11144 ->update($dbo->qn('#__vikbooking_ordersrooms'))
11145 ->set($dbo->qn('roomindex') . ' = ' . (empty($new_rindex) && $is_tmp_row ? 'NULL' : $new_rindex))
11146 ->where($dbo->qn('id') . ' = ' . (int) $roomRow['id'])
11147 );
11148 $dbo->execute();
11149
11150 // update history record by setting the proper bookings data
11151 $user = JFactory::getUser();
11152 VikBooking::getBookingHistoryInstance($bid)
11153 ->setPrevBooking(array_merge($booking, ['rooms_info' => $prev_booking_rooms]))
11154 ->setBookingData($booking, $current_booking_rooms)
11155 ->store(
11156 'MB',
11157 sprintf(
11158 '%s [%d → %d]',
11159 JText::translate('VBODEFAULTDISTFEATUREONE'),
11160 (int) $roomRow['roomindex'],
11161 (int) (empty($new_rindex) && $is_tmp_row ? 0 : $new_rindex)
11162 ) . " ({$user->name})"
11163 );
11164
11165 // process completed
11166 VBOHttpDocument::getInstance($app)->close(200, 'e4j.ok');
11167 }
11168
11169 public function searchcustomer()
11170 {
11171 // to be called via ajax
11172 $dbo = JFactory::getDbo();
11173
11174 $kw = VikRequest::getString('kw', '', 'request');
11175 $nopin = VikRequest::getInt('nopin', '', 'request');
11176 $email = VikRequest::getInt('email', 0, 'request');
11177 $selector = VikRequest::getString('selector', 'vbo-custsearchres-entry', 'request');
11178 $no_script = VikRequest::getInt('no_script', 0, 'request');
11179
11180 if (!strlen($kw)) {
11181 VBOHttpDocument::getInstance()->close(200, '');
11182 }
11183
11184 if ($nopin > 0) {
11185 //page all bookings
11186 $q = "SELECT * FROM `#__vikbooking_customers` WHERE CONCAT_WS(' ', `first_name`, `last_name`) LIKE ".$dbo->quote("%".$kw."%")." OR `email` LIKE ".$dbo->quote("%".$kw."%")." ORDER BY `first_name` ASC LIMIT 30;";
11187 } elseif ($email > 0) {
11188 // page calendar for checking if an email exists
11189 $q = "SELECT `first_name`, `last_name`, `email` FROM `#__vikbooking_customers` WHERE `email`=".$dbo->quote($kw).";";
11190 } else {
11191 //page calendar
11192 $q = "SELECT * FROM `#__vikbooking_customers` WHERE CONCAT_WS(' ', `first_name`, `last_name`) LIKE ".$dbo->quote("%".$kw."%")." OR `email` LIKE ".$dbo->quote("%".$kw."%")." OR `pin` LIKE ".$dbo->quote("%".$kw."%")." ORDER BY `first_name` ASC;";
11193 }
11194 $dbo->setQuery($q);
11195 $customers = $dbo->loadAssocList();
11196
11197 if (!$customers) {
11198 VBOHttpDocument::getInstance()->close(200, '');
11199 }
11200
11201 if ($email > 0) {
11202 VBOHttpDocument::getInstance()->json($customers[0]);
11203 }
11204
11205 $cust_old_fields = array();
11206 $cstring_search = '<div class="vbo-custsearchres-inner">' . "\n";
11207 foreach ($customers as $k => $v) {
11208 $cstring_search .= '<div class="' . $selector . '" data-custid="' . (int) $v['id'] . '" data-email="' . htmlspecialchars($v['email']) . '" data-phone="' . htmlspecialchars($v['phone']) . '" data-country="' . htmlspecialchars($v['country']) . '" data-pin="' . htmlspecialchars($v['pin']) . '" data-firstname="' . htmlspecialchars($v['first_name']) . '" data-lastname="' . htmlspecialchars($v['last_name']) . '">'."\n";
11209 $cstring_search .= '<span class="vbo-custsearchres-cflag">';
11210 if (!empty($v['pic'])) {
11211 $cstring_search .= '<img src="' . (strpos($v['pic'], 'http') === 0 ? $v['pic'] : VBO_SITE_URI . 'resources/uploads/' . $v['pic']) . '" class="vbo-country-flag vbo-customer-avatar-flag"/>'."\n";
11212 } elseif (is_file(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$v['country'].'.png')) {
11213 $cstring_search .= '<img src="'.VBO_ADMIN_URI.'resources/countries/'.$v['country'].'.png'.'" title="'.htmlspecialchars($v['country']).'" class="vbo-country-flag"/>'."\n";
11214 } else {
11215 $cstring_search .= '<i class="' . VikBookingIcons::i('globe') . '"></i>';
11216 }
11217 $cstring_search .= '</span>';
11218 $cstring_search .= '<span class="vbo-custsearchres-name" title="'.htmlspecialchars($v['email']).'">'.$v['first_name'].' '.$v['last_name'].'</span>'."\n";
11219 if (!($nopin > 0)) {
11220 $cstring_search .= '<span class="vbo-custsearchres-pin">'.$v['pin'].'</span>'."\n";
11221 }
11222 $cstring_search .= '</div>'."\n";
11223 if (!empty($v['cfields'])) {
11224 $oldfields = json_decode($v['cfields'], true);
11225 if (is_array($oldfields) && count($oldfields)) {
11226 $cust_old_fields[$v['id']] = $oldfields;
11227 }
11228 }
11229 }
11230 $cstring_search .= '</div>'."\n";
11231
11232 /**
11233 * Add the necessary JS code for the arrow navigation.
11234 */
11235 $cstring_search_js = '<script type="text/javascript">';
11236 $cstring_search_js .= '
11237 var vboCust = jQuery(".' . $selector . '");
11238 var vboCustSelected = null;
11239 var vboCustomerNavigationFn = (e) => {
11240 if (e.which === 40) {
11241 if (vboCustSelected) {
11242 vboCustSelected.removeClass("' . $selector . '-highligthed");
11243 next = vboCustSelected.next();
11244 if (next.length > 0) {
11245 vboCustSelected = next.addClass("' . $selector . '-highligthed");
11246 } else {
11247 vboCustSelected = vboCust.eq(0).addClass("' . $selector . '-highligthed");
11248 }
11249 } else {
11250 vboCustSelected = vboCust.eq(0).addClass("' . $selector . '-highligthed");
11251 }
11252 } else if (e.which === 38) {
11253 if (vboCustSelected) {
11254 vboCustSelected.removeClass("' . $selector . '-highligthed");
11255 next = vboCustSelected.prev();
11256 if (next.length > 0) {
11257 vboCustSelected = next.addClass("' . $selector . '-highligthed");
11258 } else {
11259 vboCustSelected = vboCust.last().addClass("' . $selector . '-highligthed");
11260 }
11261 } else {
11262 vboCustSelected = vboCust.last().addClass("' . $selector . '-highligthed");
11263 }
11264 } else if (e.which === 13) {
11265 if (vboCustSelected) {
11266 vboCustSelected.trigger("click");
11267 }
11268 }
11269 };
11270 jQuery(window).off("keydown", vboCustomerNavigationFn);
11271 jQuery(window).keydown(vboCustomerNavigationFn);
11272 document.addEventListener("vbo-search-customers-navigation-dismissed", (e) => {
11273 jQuery(window).off("keydown", vboCustomerNavigationFn);
11274 })
11275 jQuery(".' . $selector . '").off("hover");
11276 jQuery(".' . $selector . '").hover(function() {
11277 if (vboCustSelected) {
11278 vboCustSelected.removeClass("' . $selector . '-highligthed");
11279 vboCustSelected = null;
11280 }
11281 vboCustSelected = jQuery(this).addClass("' . $selector . '-highligthed");
11282 }, function() {
11283 if (vboCustSelected) {
11284 vboCustSelected.removeClass("' . $selector . '-highligthed");
11285 vboCustSelected = null;
11286 }
11287 jQuery(this).removeClass("' . $selector . '-highligthed");
11288 });';
11289 $cstring_search_js .= '</script>';
11290
11291 if (!$no_script) {
11292 // append JS
11293 $cstring_search .= $cstring_search_js;
11294 }
11295
11296 VBOHttpDocument::getInstance()->json([($nopin > 0 ? '' : $cust_old_fields), $cstring_search]);
11297 }
11298
11299 public function sharesignaturelink() {
11300 //to be called via ajax
11301 $dbo = JFactory::getDBO();
11302 $response = array(
11303 'status' => 0,
11304 'error' => 'Generic Error'
11305 );
11306 $pbid = VikRequest::getInt('bid', '', 'request');
11307 $phow = VikRequest::getString('how', '', 'request');
11308 $pto = VikRequest::getString('to', '', 'request');
11309 $pcustomer = VikRequest::getInt('customer', '', 'request');
11310 $cpin = VikBooking::getCPinIstance();
11311 $customer_info = $cpin->getCustomerByID($pcustomer);
11312 if (!empty($pbid) && !empty($phow) && !empty($pto) && count($customer_info) > 0) {
11313 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$pbid." AND `status`='confirmed' AND `checked` > 0;";
11314 $dbo->setQuery($q);
11315 $dbo->execute();
11316 if ($dbo->getNumRows() > 0) {
11317 $row = $dbo->loadAssoc();
11318
11319 $share_link = JUri::root() . 'index.php?option=com_vikbooking&task=signature&sid=' . (!empty($row['idorderota']) && !empty($row['channel']) ? $row['idorderota'] : $row['sid']) . '&ts=' . $row['ts'];
11320 if (VBOPlatformDetection::isWordPress()) {
11321 /**
11322 * @wponly Rewrite URI for front-end signature
11323 */
11324 $share_link = str_replace(JUri::root(), '', $share_link);
11325 $model = JModel::getInstance('vikbooking', 'shortcodes');
11326 $itemid = $model->all('post_id', $full = true);
11327 if (count($itemid)) {
11328 $share_link = JRoute::rewrite($share_link . "&Itemid={$itemid[0]->post_id}", false);
11329 }
11330 } else {
11331 /**
11332 * @joomlaonly
11333 */
11334 $best_menuitem_id = VikBooking::findProperItemIdType(['vikbooking', 'booking'], $row['lang']);
11335 if ($best_menuitem_id) {
11336 $share_base = str_replace(JUri::root(), '', $share_link);
11337 $share_link = VikBooking::externalroute($share_base, $xhtml = false, $best_menuitem_id);
11338 }
11339 }
11340
11341 $share_message = JText::sprintf('VBOSIGNSHAREMESSAGE', ltrim($customer_info['first_name'].' '.$customer_info['last_name']), $share_link, VikBooking::getFrontTitle());
11342 if ($phow == 'email') {
11343 $sender = VikBooking::getSenderMail();
11344 $vbo_app = VikBooking::getVboApplication();
11345 $vbo_app->sendMail($sender, $sender, $pto, $sender, JText::translate('VBOSIGNSHARESUBJECT'), $share_message, false);
11346 $response['status'] = 1;
11347 } elseif ($phow == 'sms') {
11348 $share_message = JText::sprintf('VBOSIGNSHAREMESSAGESMS', ltrim($customer_info['first_name'].' '.$customer_info['last_name']), $share_link, VikBooking::getFrontTitle());
11349 $sms_api = VikBooking::getSMSAPIClass();
11350 $sms_api_params = VikBooking::getSMSParams();
11351 if (!empty($sms_api) && file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api) && !empty($sms_api_params)) {
11352 require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
11353 $sms_obj = new VikSmsApi(array(), $sms_api_params);
11354 $response_obj = $sms_obj->sendMessage($pto, $share_message);
11355 if ($sms_obj->validateResponse($response_obj)) {
11356 $response['status'] = 1;
11357 } else {
11358 $response['error'] = $sms_obj->getLog();
11359 }
11360 } else {
11361 $response['error'] = 'No SMS Provider Configured';
11362 }
11363 } else {
11364 $response['error'] = 'Invalid Sending Method';
11365 }
11366 } else {
11367 $response['error'] = 'Invalid Booking ID';
11368 }
11369 } else {
11370 $response['error'] = 'Empty values';
11371 }
11372
11373 echo json_encode($response);
11374 exit;
11375 }
11376
11377 public function dayselectioncount() {
11378 //to be called via ajax
11379 $tsinit = VikRequest::getString('dinit', '', 'request');
11380 $tsend = VikRequest::getString('dend', '', 'request');
11381 if (strlen($tsinit) > 0 && strlen($tsend) > 0) {
11382 $ptsinit=VikBooking::getDateTimestamp($tsinit, '0', '0');
11383 $ptsend=VikBooking::getDateTimestamp($tsend, '23', '59');
11384 $diff = $ptsend - $ptsinit;
11385 if ($diff >= 172800) {
11386 $datef = VikBooking::getDateFormat(true);
11387 if ($datef=="%d/%m/%Y") {
11388 $df = 'd-m-Y';
11389 } else {
11390 $df = 'Y-m-d';
11391 }
11392 //minimum 2 days for excluding some days
11393 $daysdiff = floor($diff / 86400);
11394 $infoinit = getdate($ptsinit);
11395 $select = '';
11396 $select .= '<div style="display: inline-block;"><select name="excludeday[]" multiple="multiple" size="'.($daysdiff > 8 ? 8 : $daysdiff).'" id="vboexclusion">';
11397 for($i = 0; $i <= $daysdiff; $i++) {
11398 $ts = $i > 0 ? mktime(0, 0, 0, $infoinit['mon'], ((int)$infoinit['mday'] + $i), $infoinit['year']) : $ptsinit;
11399 $infots = getdate($ts);
11400 $optval = $infots['mon'].'-'.$infots['mday'].'-'.$infots['year'];
11401 $select .= '<option value="'.$optval.'">'.date($df, $ts).'</option>';
11402 }
11403 $select .= '</select></div>';
11404 //excluded days of the week
11405 if ($daysdiff >= 14) {
11406 $select .= '<div style="display: inline-block; margin-left: 40px;"><select name="excludewdays[]" multiple="multiple" size="8" id="excludewdays" onchange="vboExcludeWDays();">';
11407 $select .= '<optgroup label="'.JText::translate('VBOEXCLWEEKD').'">';
11408 $select .= '<option value="0">'.JText::translate('VBSUNDAY').'</option><option value="1">'.JText::translate('VBMONDAY').'</option><option value="2">'.JText::translate('VBTUESDAY').'</option><option value="3">'.JText::translate('VBWEDNESDAY').'</option><option value="4">'.JText::translate('VBTHURSDAY').'</option><option value="5">'.JText::translate('VBFRIDAY').'</option><option value="6">'.JText::translate('VBSATURDAY').'</option>';
11409 $select .= '</optgroup>';
11410 $select .= '</select></div>';
11411 }
11412 //
11413 echo $select;
11414 } else {
11415 echo '';
11416 }
11417 } else {
11418 echo '';
11419 }
11420 exit;
11421 }
11422
11423 public function createcheckindoc()
11424 {
11425 if (!JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking')) {
11426 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
11427 }
11428
11429 $cid = VikRequest::getVar('cid', array(0));
11430 $id = $cid[0];
11431
11432 $dbo = JFactory::getDBO();
11433 $mainframe = JFactory::getApplication();
11434 $vbo_tn = VikBooking::getTranslator();
11435 $lang = JFactory::getLanguage();
11436 $ptmpl = VikRequest::getString('tmpl', '', 'request');
11437 $psignature = VikRequest::getString('signature', '', 'request', VIKREQUEST_ALLOWRAW);
11438 $ppad_width = VikRequest::getInt('pad_width', '', 'request');
11439 $ppad_ratio = VikRequest::getInt('pad_ratio', '', 'request');
11440 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$id." AND `status`='confirmed' AND `checked` > 0;";
11441 $dbo->setQuery($q);
11442 $row = $dbo->loadAssoc();
11443 if (!$row) {
11444 $mainframe->redirect('index.php');
11445 exit;
11446 }
11447 if (!empty($row['lang'])) {
11448 if ($lang->getTag() != $row['lang']) {
11449 if (VBOPlatformDetection::isWordPress()) {
11450 $lang->load('com_vikbooking', VIKBOOKING_LANG, $row['lang'], true);
11451 } else {
11452 $lang->load('com_vikbooking', JPATH_SITE, $row['lang'], true);
11453 $lang->load('com_vikbooking', JPATH_ADMINISTRATOR, $row['lang'], true);
11454 $lang->load('joomla', JPATH_SITE, $row['lang'], true);
11455 $lang->load('joomla', JPATH_ADMINISTRATOR, $row['lang'], true);
11456 }
11457 }
11458 if ($vbo_tn->getDefaultLang() != $row['lang']) {
11459 // force the translation to start because contents should be translated
11460 $vbo_tn::$force_tolang = $row['lang'];
11461 }
11462 }
11463 $customer = array();
11464 $q = "SELECT `c`.*,`co`.`idorder`,`co`.`signature`,`co`.`pax_data`,`co`.`comments`,`co`.`checkindoc` FROM `#__vikbooking_customers` AS `c` LEFT JOIN `#__vikbooking_customers_orders` `co` ON `c`.`id`=`co`.`idcustomer` WHERE `co`.`idorder`=".$row['id'].";";
11465 $dbo->setQuery($q);
11466 $dbo->execute();
11467 if ($dbo->getNumRows() > 0) {
11468 $customer = $dbo->loadAssoc();
11469 if (!empty($customer['country'])) {
11470 if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$customer['country'].'.png')) {
11471 $customer['country_img'] = '<img src="'.VBO_ADMIN_URI.'resources/countries/'.$customer['country'].'.png'.'" title="'.htmlspecialchars($customer['country']).'" class="vbo-country-flag vbo-country-flag-left"/>';
11472 }
11473 }
11474 }
11475 if (!(count($customer) > 0)) {
11476 VikError::raiseWarning('', JText::translate('VBOCHECKINERRNOCUSTOMER'));
11477 $mainframe->redirect('index.php?option=com_vikbooking&task=newcustomer&checkin=1&bid='.$row['id'].($ptmpl == 'component' ? '&tmpl=component' : ''));
11478 exit;
11479 }
11480 $customer['pax_data'] = !empty($customer['pax_data']) ? json_decode($customer['pax_data'], true) : array();
11481 //check if the signature has been submitted
11482 $signature_data = '';
11483 $cont_type = '';
11484 if (!empty($psignature)) {
11485 //check whether the format is accepted
11486 if (strpos($psignature, 'image/png') !== false || strpos($psignature, 'image/jpeg') !== false || strpos($psignature, 'image/svg') !== false) {
11487 $parts = explode(';base64,', $psignature);
11488 $cont_type_parts = explode('image/', $parts[0]);
11489 $cont_type = $cont_type_parts[1];
11490 if (!empty($parts[1])) {
11491 $signature_data = base64_decode($parts[1]);
11492 }
11493 }
11494 }
11495 if (!empty($signature_data)) {
11496 //write file
11497 $sign_fname = $row['id'].'_'.$row['sid'].'_'.$customer['id'].'.'.$cont_type;
11498 $filepath = VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'idscans' . DIRECTORY_SEPARATOR . $sign_fname;
11499 $fp = fopen($filepath, 'w+');
11500 $bytes = fwrite($fp, $signature_data);
11501 fclose($fp);
11502 if ($bytes !== false && $bytes > 0) {
11503 //update the signature in the DB
11504 $q = "UPDATE `#__vikbooking_customers_orders` SET `signature`=".$dbo->quote($sign_fname)." WHERE `idorder`=".(int)$row['id'].";";
11505 $dbo->setQuery($q);
11506 $dbo->execute();
11507 $customer['signature'] = $sign_fname;
11508 //resize image for screens with high resolution
11509 if ($ppad_ratio > 1) {
11510 $new_width = floor(($ppad_width / 2));
11511 $creativik = new vikResizer();
11512 $creativik->proportionalImage($filepath, $filepath, $new_width, $new_width);
11513 } else {
11514 /**
11515 * @wponly - trigger files mirroring
11516 */
11517 VikBookingLoader::import('update.manager');
11518 VikBookingUpdateManager::triggerUploadBackup($filepath);
11519 //
11520 }
11521 //
11522 } else {
11523 VikError::raiseWarning('', JText::translate('VBOERRSTORESIGNFILE'));
11524 }
11525 }
11526 //
11527 //generate PDF for check-in document by parsing the apposite template file
11528 $booking_rooms = array();
11529 $q = "SELECT `or`.*,`r`.`name` AS `room_name`,`r`.`fromadult`,`r`.`toadult` FROM `#__vikbooking_ordersrooms` AS `or` LEFT JOIN `#__vikbooking_rooms` `r` ON `r`.`id`=`or`.`idroom` WHERE `or`.`idorder`=".(int)$row['id'].";";
11530 $dbo->setQuery($q);
11531 $dbo->execute();
11532 if ($dbo->getNumRows() > 0) {
11533 $booking_rooms = $dbo->loadAssocList();
11534 if (!empty($row['lang'])) {
11535 $vbo_tn->translateContents($booking_rooms, '#__vikbooking_rooms', array('id' => 'idroom', 'room_name' => 'name'), array(), $row['lang']);
11536 }
11537 }
11538 if (!class_exists('TCPDF')) {
11539 require_once(VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "tcpdf" . DIRECTORY_SEPARATOR . 'tcpdf.php');
11540 }
11541 $usepdffont = is_file(VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "tcpdf" . DIRECTORY_SEPARATOR . "fonts" . DIRECTORY_SEPARATOR . "dejavusans.php") ? 'dejavusans' : 'helvetica';
11542
11543 /**
11544 * Trigger event to allow third party plugins to return a specific font name.
11545 *
11546 * @since 1.16.0 (J) - 1.6.0 (WP)
11547 */
11548 $custom_pdf_font = VBOFactory::getPlatform()->getDispatcher()->filter('onGetPdfFontNameVikBooking', [$usepdffont]);
11549 if (is_array($custom_pdf_font) && !empty($custom_pdf_font[0])) {
11550 $usepdffont = $custom_pdf_font[0];
11551 }
11552
11553 list($checkintpl, $pdfparams) = VikBooking::loadCheckinDocTmpl($row, $booking_rooms, $customer);
11554 $checkin_body = VikBooking::parseCheckinDocTemplate($checkintpl, $row, $booking_rooms, $customer);
11555
11556 // build the proper document SID for bc
11557 $doc_sid = $row['sid'] ?: $row['idorderota'] ?: '';
11558 $pdffname = $row['id'] . '_' . $doc_sid . '.pdf';
11559
11560 $pathpdf = VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "checkins" . DIRECTORY_SEPARATOR . "generated" . DIRECTORY_SEPARATOR . $pdffname;
11561 if (file_exists($pathpdf)) @unlink($pathpdf);
11562 $pdf_page_format = is_array($pdfparams['pdf_page_format']) ? $pdfparams['pdf_page_format'] : constant($pdfparams['pdf_page_format']);
11563 $pdf = new TCPDF(constant($pdfparams['pdf_page_orientation']), constant($pdfparams['pdf_unit']), $pdf_page_format, true, 'UTF-8', false);
11564 $pdf->SetTitle(JText::translate('VBOCHECKINDOCTITLE'));
11565 //Header for each page of the pdf
11566 if ($pdfparams['show_header'] == 1 && count($pdfparams['header_data']) > 0) {
11567 $pdf->SetHeaderData($pdfparams['header_data'][0], $pdfparams['header_data'][1], $pdfparams['header_data'][2], $pdfparams['header_data'][3], $pdfparams['header_data'][4], $pdfparams['header_data'][5]);
11568 }
11569 //header and footer fonts
11570 $pdf->setHeaderFont(array($usepdffont, '', $pdfparams['header_font_size']));
11571 $pdf->setFooterFont(array($usepdffont, '', $pdfparams['footer_font_size']));
11572 //margins
11573 $pdf->SetMargins(constant($pdfparams['pdf_margin_left']), constant($pdfparams['pdf_margin_top']), constant($pdfparams['pdf_margin_right']));
11574 $pdf->SetHeaderMargin(constant($pdfparams['pdf_margin_header']));
11575 $pdf->SetFooterMargin(constant($pdfparams['pdf_margin_footer']));
11576 //
11577 $pdf->SetAutoPageBreak(true, constant($pdfparams['pdf_margin_bottom']));
11578 $pdf->setImageScale(constant($pdfparams['pdf_image_scale_ratio']));
11579 $pdf->SetFont($usepdffont, '', (int)$pdfparams['body_font_size']);
11580 if ($pdfparams['show_header'] == 0 || !(count($pdfparams['header_data']) > 0)) {
11581 $pdf->SetPrintHeader(false);
11582 }
11583 if ($pdfparams['show_footer'] == 0) {
11584 $pdf->SetPrintFooter(false);
11585 }
11586 $pdf->AddPage();
11587 $pdf->writeHTML($checkin_body, true, false, true, false, '');
11588 $pdf->lastPage();
11589 $pdf->Output($pathpdf, 'F');
11590 if (!file_exists($pathpdf)) {
11591 VikError::raiseWarning('', JText::translate('VBOERRGENCHECKINDOC'));
11592 } else {
11593 $q = "UPDATE `#__vikbooking_customers_orders` SET `checkindoc`=".$dbo->quote($pdffname)." WHERE `idorder`=".(int)$row['id'].";";
11594 $dbo->setQuery($q);
11595 $dbo->execute();
11596 $mainframe->enqueueMessage(JText::translate('VBOGENCHECKINDOCSUCCESS'));
11597 /**
11598 * @wponly - trigger files mirroring
11599 */
11600 VikBookingLoader::import('update.manager');
11601 VikBookingUpdateManager::triggerUploadBackup($pathpdf);
11602 //
11603 }
11604 //
11605 /**
11606 * @wponly - this task is executed via Ajax for the Modal forms listener. We cannot redirect to tmpl=component
11607 */
11608 $mainframe->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id']);
11609 exit;
11610 }
11611
11612 public function updatebookingcheckin()
11613 {
11614 if (!JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking')) {
11615 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
11616 }
11617
11618 $cid = VikRequest::getVar('cid', array(0));
11619 $id = $cid[0];
11620
11621 $dbo = JFactory::getDbo();
11622 $app = JFactory::getApplication();
11623
11624 $ptmpl = $app->input->getString('tmpl', '');
11625 $pnewtotpaid = $app->input->getFloat('newtotpaid', 0);
11626 $pguests = $app->input->get('guests', [], 'array');
11627 $pcomments = JComponentHelper::filterText($app->input->get('comments', '', 'raw'));
11628 $pcheckin_action = $app->input->getInt('checkin_action', 0);
11629 $valid_actions = array(-1, 0, 1, 2);
11630 if (!in_array($pcheckin_action, $valid_actions)) {
11631 $app->redirect('index.php');
11632 exit;
11633 }
11634 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$id." AND `status`='confirmed';";
11635 $dbo->setQuery($q);
11636 $dbo->execute();
11637 if ($dbo->getNumRows() < 1) {
11638 $app->redirect('index.php');
11639 exit;
11640 }
11641 $row = $dbo->loadAssoc();
11642 $q = "SELECT * FROM `#__vikbooking_customers_orders` WHERE `idorder`=".$row['id'].";";
11643 $dbo->setQuery($q);
11644 $dbo->execute();
11645 if ($dbo->getNumRows() < 1) {
11646 VikError::raiseWarning('', JText::translate('VBOCHECKINERRNOCUSTOMER'));
11647 $app->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id'].($ptmpl == 'component' ? '&tmpl=component' : ''));
11648 exit;
11649 }
11650 $custorder = $dbo->loadAssoc();
11651 //update checked status and new total paid
11652 $q = "UPDATE `#__vikbooking_orders` SET `checked`=".$pcheckin_action."".($pnewtotpaid > 0 ? ', `totpaid`='.$pnewtotpaid : '')." WHERE `id`=".$row['id'].";";
11653 $dbo->setQuery($q);
11654 $dbo->execute();
11655 // Booking History log for new amount paid (payment update)
11656 if ($pnewtotpaid > 0 && $pnewtotpaid > (float)$row['totpaid']) {
11657 $extra_data = new stdClass;
11658 $extra_data->amount_paid = ($pnewtotpaid - (float)$row['totpaid']);
11659 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->setExtraData($extra_data)->store('PU', JText::sprintf('VBOPREVAMOUNTPAID', VikBooking::numberFormat((float)$row['totpaid'])));
11660 }
11661 //
11662 //Booking History
11663 $hist_type = 'A';
11664 if ($pcheckin_action < 0) {
11665 $hist_type = 'Z';
11666 } elseif ($pcheckin_action == 1) {
11667 $hist_type = 'B';
11668 } elseif ($pcheckin_action == 2) {
11669 $hist_type = 'C';
11670 }
11671 $user = JFactory::getUser();
11672 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('R' . $hist_type, "({$user->name})");
11673 //
11674 //Guests Details
11675 $guests_details = array();
11676 list($pax_fields, $pax_fields_attributes) = VikBooking::getPaxFields();
11677 // grab also the fields for front-end pre check-in
11678 list($pre_pax_fields, $pre_pax_fields_attributes) = VikBooking::getPaxFields(true);
11679 //
11680 foreach ($pguests as $ind => $adults) {
11681 foreach ($adults as $aduind => $details) {
11682 foreach ($pax_fields as $key => $v) {
11683 if (isset($details[$key]) && ((is_scalar($details[$key]) && strlen($details[$key])) || !empty($details[$key]))) {
11684 if (!isset($guests_details[$ind])) {
11685 $guests_details[$ind] = array();
11686 }
11687 if (!isset($guests_details[$ind][$aduind])) {
11688 $guests_details[$ind][$aduind] = array();
11689 }
11690 $guests_details[$ind][$aduind][$key] = $details[$key];
11691 }
11692 }
11693 foreach ($pre_pax_fields as $key => $v) {
11694 if (isset($pax_fields[$key])) {
11695 // we must have parsed this back-end field already
11696 continue;
11697 }
11698 if (isset($details[$key]) && ((is_scalar($details[$key]) && strlen($details[$key])) || !empty($details[$key]))) {
11699 if (!isset($guests_details[$ind])) {
11700 $guests_details[$ind] = array();
11701 }
11702 if (!isset($guests_details[$ind][$aduind])) {
11703 $guests_details[$ind][$aduind] = array();
11704 }
11705 if (!isset($guests_details[$ind][$aduind][$key])) {
11706 $guests_details[$ind][$aduind][$key] = $details[$key];
11707 }
11708 }
11709 }
11710 }
11711 }
11712
11713 if ($guests_details) {
11714 // current pax data may contain some extra information collected via front-end pre-checkin so we need to merge them
11715 $curpaxdata = json_decode($custorder['pax_data'], true);
11716 if (is_array($curpaxdata) && $curpaxdata) {
11717 // scan new guest registration details
11718 foreach ($guests_details as $ind => $groom) {
11719 foreach ($groom as $aduind => $aduinfo) {
11720 if (isset($curpaxdata[$ind][$aduind])) {
11721 $guests_details[$ind][$aduind] = array_merge($curpaxdata[$ind][$aduind], $guests_details[$ind][$aduind]);
11722 // unset some default pax fields that were not specified now, or data cannot be deleted for guests
11723 foreach ($guests_details[$ind][$aduind] as $key => $det) {
11724 if (isset($pguests[$ind][$aduind][$key]) && empty($pguests[$ind][$aduind][$key])) {
11725 // this default pax field was specified as empty now, so we cannot merge it
11726 unset($guests_details[$ind][$aduind][$key]);
11727 }
11728 }
11729 }
11730 }
11731 }
11732
11733 /**
11734 * In order to not lose any custom registration data added through PMS reports,
11735 * we scan the previous registration data to ensure we keep them in the update.
11736 *
11737 * @since 1.16.10 (J) - 1.6.10 (WP)
11738 */
11739 foreach ($curpaxdata as $ind => $groom) {
11740 if (!isset($guests_details[$ind])) {
11741 // ignore deleted room registration
11742 continue;
11743 }
11744 foreach ($groom as $aduind => $aduinfo) {
11745 if (!isset($guests_details[$ind][$aduind]) || !is_array($aduinfo)) {
11746 // ignore deleted room-guest registration
11747 continue;
11748 }
11749 foreach ($aduinfo as $field_key => $field_val) {
11750 if (!isset($guests_details[$ind][$aduind][$field_key]) && !empty($field_val)) {
11751 // merge previous room-guest registration data
11752 $guests_details[$ind][$aduind][$field_key] = $field_val;
11753 }
11754 }
11755 }
11756 }
11757 }
11758
11759 $q = "UPDATE `#__vikbooking_customers_orders` SET `pax_data`=" . $dbo->q(json_encode($guests_details)) . " WHERE `id`=" . (int) $custorder['id'] . ";";
11760 $dbo->setQuery($q);
11761 $dbo->execute();
11762 }
11763
11764 //'checked' status comments
11765 $q = "UPDATE `#__vikbooking_customers_orders` SET `comments`=".$dbo->quote($pcomments)." WHERE `id`=".$custorder['id'].";";
11766 $dbo->setQuery($q);
11767 $dbo->execute();
11768
11769 $app->enqueueMessage(JText::translate('VBOCHECKINSTATUSUPDATED'));
11770 $app->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id'].($pcheckin_action != $row['checked'] ? '&changed=1' : '').($ptmpl == 'component' ? '&tmpl=component' : ''));
11771 exit;
11772 }
11773
11774 public function alterbooking()
11775 {
11776 $dbo = JFactory::getDbo();
11777 $app = JFactory::getApplication();
11778 $user = JFactory::getUser();
11779
11780 $response = array(
11781 'esit' => 1,
11782 'message' => '',
11783 'vcm' => '',
11784 );
11785
11786 // must be a string as it may contain a dash
11787 $pidorder = VikRequest::getString('idorder', '', 'request');
11788 $pidorder = intval(str_replace('-', '', $pidorder));
11789
11790 $poldidroom = VikRequest::getInt('oldidroom', '', 'request');
11791 $pidroom = VikRequest::getInt('idroom', 0, 'request');
11792 $pfromdate = VikRequest::getString('fromdate', '', 'request');
11793 $ptodate = VikRequest::getString('todate', '', 'request');
11794 $pdebug = VikRequest::getInt('e4j_debug', 0, 'request');
11795 if ($pdebug == 1) {
11796 echo 'e4j.error.'.print_r($app->input->post->getArray(), true);
11797 exit;
11798 }
11799
11800 $nowdf = VikBooking::getDateFormat(true);
11801 if ($nowdf == "%d/%m/%Y") {
11802 $df = 'd/m/Y';
11803 } elseif ($nowdf == "%m/%d/%Y") {
11804 $df = 'm/d/Y';
11805 } else {
11806 $df = 'Y/m/d';
11807 }
11808 $pcheckinh = 0;
11809 $pcheckinm = 0;
11810 $pcheckouth = 0;
11811 $pcheckoutm = 0;
11812 $timeopst = VikBooking::getTimeOpenStore();
11813 if (is_array($timeopst)) {
11814 $opent = VikBooking::getHoursMinutes($timeopst[0]);
11815 $closet = VikBooking::getHoursMinutes($timeopst[1]);
11816 $pcheckinh = $opent[0];
11817 $pcheckinm = $opent[1];
11818 $pcheckouth = $closet[0];
11819 $pcheckoutm = $closet[1];
11820 }
11821 $info_tsto = getdate(strtotime($ptodate));
11822 $actualtsto = mktime(0, 0, 0, $info_tsto['mon'], ($info_tsto['mday'] + 1), $info_tsto['year']);
11823 $first = VikBooking::getDateTimestamp(date($df, strtotime($pfromdate)), $pcheckinh, $pcheckinm);
11824 $second = VikBooking::getDateTimestamp(date($df, $actualtsto), $pcheckouth, $pcheckoutm);
11825 $ptodate = date('Y-m-d', $second);
11826 if (!($second > $first)) {
11827 echo 'e4j.error.1 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11828 exit;
11829 }
11830 if (!($pidorder > 0) || !($pidroom > 0) || empty($pfromdate) || empty($ptodate)) {
11831 echo 'e4j.error.2 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11832 exit;
11833 }
11834
11835 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder . " AND `status`='confirmed'";
11836 $dbo->setQuery($q, 0, 1);
11837 $dbo->execute();
11838 if (!$dbo->getNumRows()) {
11839 echo 'e4j.error.3 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11840 exit;
11841 }
11842 $ord = $dbo->loadAssoc();
11843
11844 $q = "SELECT `or`.*,`r`.`name`,`r`.`idopt`,`r`.`units`,`r`.`fromadult`,`r`.`toadult` FROM `#__vikbooking_ordersrooms` AS `or`,`#__vikbooking_rooms` AS `r` WHERE `or`.`idorder`=" . $ord['id'] . " AND `or`.`idroom`=`r`.`id` ORDER BY `or`.`id` ASC;";
11845 $dbo->setQuery($q);
11846 $dbo->execute();
11847 $ordersrooms = $dbo->loadAssocList();
11848
11849 // store for VCM the current rooms before the modification
11850 $ord['rooms_info'] = $ordersrooms;
11851
11852 // package or custom rate
11853 $is_package = !empty($ord['pkg']) ? true : false;
11854 $is_cust_cost = false;
11855 foreach ($ordersrooms as $kor => $or) {
11856 if ($is_package !== true && !empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
11857 $is_cust_cost = true;
11858 break;
11859 }
11860 }
11861
11862 // availability helper
11863 $av_helper = VikBooking::getAvailabilityInstance();
11864
11865 // room stay dates in case of split stay
11866 $room_stay_dates = [];
11867 if ($ord['split_stay']) {
11868 // no need to get the transient based on booking status, as the booking must be confirmed
11869 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
11870 // immediately count the number of nights of stay for each split room
11871 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
11872 $room_stay_dates[$sps_r_k]['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
11873 }
11874 }
11875
11876 // determine if dates have changed
11877 $dates_changed = false;
11878 if (date('Y-m-d', $ord['checkin']) != $pfromdate || date('Y-m-d', $ord['checkout']) != $ptodate) {
11879 $dates_changed = true;
11880 }
11881
11882 $toswitch = array();
11883 $idbooked = array();
11884 $rooms_units = array();
11885 $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms`;";
11886 $dbo->setQuery($q);
11887 $dbo->execute();
11888 $all_rooms = $dbo->loadAssocList();
11889 foreach ($all_rooms as $rr) {
11890 $rooms_units[$rr['id']]['name'] = $rr['name'];
11891 $rooms_units[$rr['id']]['units'] = $rr['units'];
11892 }
11893
11894 // switch room
11895 if ($poldidroom != $pidroom) {
11896 foreach ($ordersrooms as $ind => $or) {
11897 if ($poldidroom == $or['idroom'] && array_key_exists($pidroom, $rooms_units)) {
11898 if (!isset($idbooked[$or['idroom']])) {
11899 $idbooked[$or['idroom']] = 0;
11900 }
11901 // $idbooked is not really needed as switch is never made for the same room id
11902 $idbooked[$or['idroom']]++;
11903 //
11904 $orkey = count($toswitch);
11905 $toswitch[$orkey]['from'] = $or['idroom'];
11906 $toswitch[$orkey]['to'] = $pidroom;
11907 $toswitch[$orkey]['record'] = $or;
11908 $toswitch[$orkey]['record_ind'] = $ind;
11909 break;
11910 }
11911 }
11912 }
11913 if (count($toswitch)) {
11914 foreach ($toswitch as $ksw => $rsw) {
11915 $plusunit = array_key_exists($rsw['to'], $idbooked) ? $idbooked[$rsw['to']] : 0;
11916 $room_checkin = $ord['checkin'];
11917 $room_checkout = $ord['checkout'];
11918 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
11919 $room_checkin = $room_stay_dates[$rsw['record_ind']]['checkin'];
11920 $room_checkout = $room_stay_dates[$rsw['record_ind']]['checkout'];
11921 }
11922 if (!VikBooking::roomBookable($rsw['to'], ($rooms_units[$rsw['to']]['units'] + $plusunit), $room_checkin, $room_checkout)) {
11923 // the room is not available
11924 unset($toswitch[$ksw]);
11925 echo 'e4j.error.'.JText::sprintf('VBSWITCHRERR', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']);
11926 exit;
11927 }
11928 }
11929 if (count($toswitch)) {
11930 //reset first record rate so that rates can be set again (rates are unset only if the room is switched, if just the dates are different the rates are kept equal as the num nights is the same)
11931 reset($ordersrooms);
11932 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$ordersrooms[0]['id'].";";
11933 $dbo->setQuery($q);
11934 $dbo->execute();
11935 //
11936 foreach ($toswitch as $ksw => $rsw) {
11937 // update room reservation record
11938 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idroom`=" . $rsw['to'] . ",`idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=" . $rsw['record']['id'] . ";";
11939 $dbo->setQuery($q);
11940 $dbo->execute();
11941 $response['message'] .= JText::sprintf('VBOVWALTBKSWITCHROK', $rsw['record']['name'], $rooms_units[$rsw['to']]['name'])."\n";
11942
11943 // update Notes field for this booking to keep track of the previous room that was assigned
11944 $prev_room_name = array_key_exists($rsw['from'], $rooms_units) ? $rooms_units[$rsw['from']]['name'] : '';
11945 if (!empty($prev_room_name)) {
11946 $new_notes = JText::sprintf('VBOPREVROOMMOVED', $prev_room_name, date($df.' H:i:s'))."\n".$ord['adminnotes'];
11947 $q = "UPDATE `#__vikbooking_orders` SET `adminnotes`=".$dbo->quote($new_notes)." WHERE `id`=".(int)$ord['id'].";";
11948 $dbo->setQuery($q);
11949 $dbo->execute();
11950 }
11951
11952 if ($ord['status'] == 'confirmed') {
11953 // update room record in _busy
11954 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from'] && !empty($room_stay_dates[$rsw['record_ind']]['id'])) {
11955 // in case of a split stay it is fundamental to update the exact busy record ID
11956 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=" . $rsw['to'] . " WHERE `id`=" . (int)$room_stay_dates[$rsw['record_ind']]['id'];
11957 $dbo->setQuery($q);
11958 $dbo->execute();
11959 } else {
11960 // regular processing of a room ID for a reservation, no matter which one, we switch it
11961 $q = "SELECT `b`.`id`,`b`.`idroom`,`ob`.`idorder` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`idroom`=" . $rsw['from'] . " AND `b`.`id`=`ob`.`idbusy` AND `ob`.`idorder`=" . $ord['id'] . " LIMIT 1;";
11962 $dbo->setQuery($q);
11963 $dbo->execute();
11964 if ($dbo->getNumRows() == 1) {
11965 $cur_busy = $dbo->loadAssocList();
11966 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=".$rsw['to']." WHERE `id`=".$cur_busy[0]['id']." AND `idroom`=".$cur_busy[0]['idroom']." LIMIT 1;";
11967 $dbo->setQuery($q);
11968 $dbo->execute();
11969 }
11970 }
11971
11972 // if automated updates enabled, keep $response['vcm'] empty
11973 // Invoke Channel Manager
11974 if (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
11975 $response['vcm'] = JText::translate('VBCHANNELMANAGERINVOKEASK').' <form action="index.php?option=com_vikbooking" method="post"><input type="hidden" name="option" value="com_vikbooking"/><input type="hidden" name="task" value="invoke_vcm"/><input type="hidden" name="stype" value="modify"/><input type="hidden" name="cid[]" value="'.$ord['id'].'"/><input type="hidden" name="origb" value="'.urlencode(json_encode($ord)).'"/><input type="hidden" name="returl" value="'.urlencode("index.php?option=com_vikbooking&task=overview").'"/><button type="submit" class="btn btn-primary">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button></form>';
11976 }
11977 } elseif ($ord['status'] == 'standby') {
11978 // remove record in _tmplock
11979 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($ord['id']) . ";";
11980 $dbo->setQuery($q);
11981 $dbo->execute();
11982 }
11983 }
11984
11985 // check if sub-units should be assigned again when switching room
11986 if (!$dates_changed && !$ord['split_stay'] && VikBooking::autoRoomUnit()) {
11987 $new_order_rooms = VikBooking::loadOrdersRoomsData($ord['id']);
11988 $room_indexes_usemap = [];
11989 foreach ($new_order_rooms as $kor => $or) {
11990 $num = $kor + 1;
11991 // assign room specific unit
11992 $room_indexes = VikBooking::getRoomUnitNumsAvailable($ord, $or['idroom']);
11993 $use_ind_key = 0;
11994 if ($room_indexes) {
11995 if (!array_key_exists($or['idroom'], $room_indexes_usemap)) {
11996 $room_indexes_usemap[$or['idroom']] = $use_ind_key;
11997 } else {
11998 $use_ind_key = $room_indexes_usemap[$or['idroom']];
11999 }
12000 $q = "UPDATE `#__vikbooking_ordersrooms` SET `roomindex`=".(int)$room_indexes[$use_ind_key]." WHERE `id`=".(int)$or['id'].";";
12001 $dbo->setQuery($q);
12002 $dbo->execute();
12003 $room_indexes_usemap[$or['idroom']]++;
12004 }
12005 }
12006 }
12007
12008 // do not terminate the process when there is a switch, proceed to check the dates.
12009 }
12010 }
12011
12012 // change dates
12013 if ($dates_changed) {
12014 if ($ord['split_stay']) {
12015 // we do not allow to drag and change dates for rooms in a split stay reservation
12016 echo 'e4j.error.' . JText::sprintf('VBO_BOOK_SPLIT_STAY_CANNOTDRAG', $ord['id']);
12017 exit;
12018 }
12019
12020 // total nights of stay
12021 $daysdiff = $ord['days'];
12022
12023 // re-read ordersrooms (as rooms may have been switched)
12024 $q = "SELECT `or`.*,`r`.`name`,`r`.`idopt`,`r`.`units`,`r`.`fromadult`,`r`.`toadult` FROM `#__vikbooking_ordersrooms` AS `or`,`#__vikbooking_rooms` AS `r` WHERE `or`.`idorder`=" . $ord['id'] . " AND `or`.`idroom`=`r`.`id` ORDER BY `or`.`id` ASC;";
12025 $dbo->setQuery($q);
12026 $dbo->execute();
12027 $ordersrooms = $dbo->loadAssocList();
12028
12029 $groupdays = VikBooking::getGroupDays($first, $second, $daysdiff);
12030 $opertwounits = true;
12031 $units_counter = array();
12032 foreach ($ordersrooms as $ind => $or) {
12033 if (!isset($units_counter[$or['idroom']])) {
12034 $units_counter[$or['idroom']] = -1;
12035 }
12036 $units_counter[$or['idroom']]++;
12037 }
12038
12039 foreach ($ordersrooms as $ind => $or) {
12040 $num = $ind + 1;
12041 $check = "SELECT `b`.`id`,`b`.`checkin`,`b`.`realback`,`ob`.`idorder` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`idroom`=" . $or['idroom'] . " AND `b`.`realback`>=" . $first . " AND `b`.`id`=`ob`.`idbusy` AND `ob`.`idorder`!=" . $ord['id'] . ";";
12042 $dbo->setQuery($check);
12043 $dbo->execute();
12044 if ($dbo->getNumRows() > 0) {
12045 $busy = $dbo->loadAssocList();
12046 foreach ($groupdays as $gday) {
12047 $bfound = 0;
12048 foreach ($busy as $bu) {
12049 if ($gday >= $bu['checkin'] && $gday <= $bu['realback']) {
12050 $bfound++;
12051 }
12052 }
12053 if ($bfound >= ($or['units'] - $units_counter[$or['idroom']]) || !VikBooking::roomNotLocked($or['idroom'], $or['units'], $first, $second)) {
12054 $opertwounits = false;
12055 break 2;
12056 }
12057 }
12058 }
12059 }
12060 if ($opertwounits !== true) {
12061 $response['esit'] = 0;
12062 $response['message'] = JText::translate('VBROOMNOTRIT')." ".date($df.' H:i', $first)." ".JText::translate('VBROOMNOTCONSTO')." ".date($df.' H:i', $second);
12063 echo json_encode($response);
12064 exit;
12065 }
12066
12067 // update dates and busy records
12068 $realback = VikBooking::getHoursRoomAvail() * 3600;
12069 $realback += $second;
12070 $q = "UPDATE `#__vikbooking_orders` SET `checkin`='".$first."', `checkout`='".$second."' WHERE `id`=".$ord['id'].";";
12071 $dbo->setQuery($q);
12072 $dbo->execute();
12073 if ($ord['status'] == 'confirmed') {
12074 $q = "SELECT `b`.`id` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`id`=`ob`.`idbusy` AND `ob`.`idorder`=".$ord['id'].";";
12075 $dbo->setQuery($q);
12076 $dbo->execute();
12077 $allbusy = $dbo->loadAssocList();
12078 foreach ($allbusy as $bb) {
12079 $q = "UPDATE `#__vikbooking_busy` SET `checkin`='".$first."', `checkout`='".$second."', `realback`='".$realback."' WHERE `id`='".$bb['id']."';";
12080 $dbo->setQuery($q);
12081 $dbo->execute();
12082 }
12083 // if automated updates enabled, keep $response['vcm'] empty
12084 // Invoke Channel Manager
12085 if (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
12086 $response['vcm'] = JText::translate('VBCHANNELMANAGERINVOKEASK').' <form action="index.php?option=com_vikbooking" method="post"><input type="hidden" name="option" value="com_vikbooking"/><input type="hidden" name="task" value="invoke_vcm"/><input type="hidden" name="stype" value="modify"/><input type="hidden" name="cid[]" value="'.$ord['id'].'"/><input type="hidden" name="origb" value="'.urlencode(json_encode($ord)).'"/><input type="hidden" name="returl" value="'.urlencode("index.php?option=com_vikbooking&task=overview").'"/><button type="submit" class="btn btn-primary">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button></form>';
12087 }
12088 }
12089 $response['message'] .= JText::translate('RESUPDATED')."\n";
12090 }
12091
12092 if (count($toswitch)) {
12093 /**
12094 * Rooms have changed so the new rates must be re-calculated.
12095 * Maybe they should be calculated in any case, even if just
12096 * the dates have changed. For the moment the rates are reset.
12097 */
12098 }
12099
12100 // unset any previously booked room due to calendar sharing
12101 VikBooking::cleanSharedCalendarsBusy($ord['id']);
12102 // check if some of the rooms booked have shared calendars
12103 VikBooking::updateSharedCalendars($ord['id']);
12104 //
12105
12106 //Booking History
12107 VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($ord));
12108 //
12109
12110 $vcm_autosync = VikBooking::vcmAutoUpdate();
12111 if ($vcm_autosync > 0 && !empty($response['vcm'])) {
12112 //unset the vcm property as no buttons should be displayed when in auto-sync
12113 $response['vcm'] = '';
12114 $vcm_obj = VikBooking::getVcmInvoker();
12115 $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
12116 $sync_result = $vcm_obj->doSync();
12117 if ($sync_result === false) {
12118 $response['message'] .= JText::translate('VBCHANNELMANAGERRESULTKO')." (".$vcm_obj->getError().")\n";
12119 }
12120 }
12121
12122 // in case of error but not empty VCM message, set an error that will be displayed after the mustReload
12123 if ($response['esit'] < 1 && !empty($response['vcm'])) {
12124 VikError::raiseNotice('', $response['vcm']);
12125 }
12126
12127 $response['message'] = nl2br($response['message']);
12128 echo json_encode($response);
12129 exit;
12130 }
12131
12132 public function modroomrateplans()
12133 {
12134 $dbo = JFactory::getDbo();
12135 $session = JFactory::getSession();
12136
12137 $updforvcm = $session->get('vbVcmRatesUpd', '');
12138 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
12139
12140 $pid_room = VikRequest::getInt('id_room', '', 'request');
12141 $pid_price = VikRequest::getInt('id_price', '', 'request');
12142 $ptype = VikRequest::getString('type', '', 'request');
12143 $pfromdate = VikRequest::getString('fromdate', '', 'request');
12144 $ptodate = VikRequest::getString('todate', '', 'request');
12145
12146 if (empty($pid_room) || empty($pid_price) || empty($ptype) || empty($pfromdate) || empty($ptodate) || !(strtotime($pfromdate) > 0) || !(strtotime($ptodate) > 0)) {
12147 echo 'e4j.error.'.addslashes(JText::translate('VBRATESOVWERRMODRPLANS'));
12148 exit;
12149 }
12150
12151 $q = "SELECT * FROM `#__vikbooking_prices` WHERE `id`=".$pid_price.";";
12152 $dbo->setQuery($q);
12153 $price_record = $dbo->loadAssoc();
12154
12155 if (!$price_record) {
12156 echo 'e4j.error.'.addslashes(JText::translate('VBRATESOVWERRMODRPLANS')).'.';
12157 exit;
12158 }
12159
12160 $current_closed = array();
12161 if (!empty($price_record['closingd'])) {
12162 $current_closed = json_decode($price_record['closingd'], true);
12163 }
12164 $current_closed = !is_array($current_closed) ? array() : $current_closed;
12165
12166 $start_ts = strtotime($pfromdate);
12167 $end_ts = strtotime($ptodate);
12168 $infostart = getdate($start_ts);
12169 $all_days = array();
12170 $output = array();
12171 while ($infostart[0] > 0 && $infostart[0] <= $end_ts) {
12172 $all_days[] = date('Y-m-d', $infostart[0]);
12173 $indkey = $infostart['mday'].'-'.$infostart['mon'].'-'.$infostart['year'].'-'.$pid_price;
12174 $output[$indkey] = array();
12175 $infostart = getdate(mktime(0, 0, 0, $infostart['mon'], ($infostart['mday'] + 1), $infostart['year']));
12176 }
12177
12178 if ($ptype == 'close') {
12179 // close
12180 if (!array_key_exists($pid_room, $current_closed)) {
12181 $current_closed[$pid_room] = array();
12182 }
12183 foreach ($all_days as $daymod) {
12184 if (!in_array($daymod, $current_closed[$pid_room])) {
12185 $current_closed[$pid_room][] = $daymod;
12186 }
12187 }
12188 } else {
12189 // open
12190 if (array_key_exists($pid_room, $current_closed)) {
12191 foreach ($all_days as $daymod) {
12192 if (in_array($daymod, $current_closed[$pid_room])) {
12193 foreach ($current_closed[$pid_room] as $ck => $cv) {
12194 if ($daymod == $cv) {
12195 unset($current_closed[$pid_room][$ck]);
12196 }
12197 }
12198 }
12199 }
12200 } else {
12201 $current_closed[$pid_room] = array();
12202 }
12203 }
12204
12205 if (!$current_closed[$pid_room]) {
12206 unset($current_closed[$pid_room]);
12207 }
12208
12209 $q = "UPDATE `#__vikbooking_prices` SET `closingd`=".(count($current_closed) > 0 ? $dbo->quote(json_encode($current_closed)) : "NULL")." WHERE `id`=".(int)$pid_price.";";
12210 $dbo->setQuery($q);
12211 $dbo->execute();
12212
12213 $oldcsscls = $ptype == 'close' ? 'vbo-roverw-rplan-on' : 'vbo-roverw-rplan-off';
12214 $newcsscls = $ptype == 'close' ? 'vbo-roverw-rplan-off' : 'vbo-roverw-rplan-on';
12215
12216 foreach ($output as $ok => $ov) {
12217 $output[$ok] = array('oldcls' => $oldcsscls, 'newcls' => $newcsscls);
12218 }
12219
12220 // build new session values
12221 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
12222
12223 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
12224 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $start_ts ? $start_ts : $updforvcm['dfrom'];
12225 } else {
12226 $updforvcm['dfrom'] = $start_ts;
12227 }
12228
12229 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
12230 $updforvcm['dto'] = $updforvcm['dto'] < $end_ts ? $end_ts : $updforvcm['dto'];
12231 } else {
12232 $updforvcm['dto'] = $end_ts;
12233 }
12234
12235 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
12236 if (!in_array($pid_room, $updforvcm['rooms'])) {
12237 $updforvcm['rooms'][] = $pid_room;
12238 }
12239 } else {
12240 $updforvcm['rooms'] = array($pid_room);
12241 }
12242
12243 if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
12244 if (array_key_exists($pid_room, $updforvcm['rplans'])) {
12245 if (!in_array($pid_price, $updforvcm['rplans'][$pid_room])) {
12246 $updforvcm['rplans'][$pid_room][] = $pid_price;
12247 }
12248 } else {
12249 $updforvcm['rplans'][$pid_room] = array($pid_price);
12250 }
12251 } else {
12252 $updforvcm['rplans'] = array($pid_room => array($pid_price));
12253 }
12254
12255 /**
12256 * Rather than suggesting the administrator to manually invoke VCM to launch a Bulk Action,
12257 * we try to silently trigger an automatic bulk action before updating the session values.
12258 *
12259 * @since 1.17.1 (J) - 1.7.1 (WP)
12260 * @since 1.17.5 (J) - 1.7.5 (WP) the "rate_id" property is passed along the auto-bulk data.
12261 */
12262 $rates_aligned = false;
12263 try {
12264 if (class_exists('VikChannelManager')) {
12265 $rates_aligned = VikChannelManager::autoBulkActions([
12266 'from_date' => $pfromdate,
12267 'to_date' => $ptodate,
12268 'forced_rooms' => [$pid_room],
12269 'rate_id' => $pid_price,
12270 'update' => 'rates',
12271 ]);
12272 }
12273 } catch (Throwable $e) {
12274 // do nothing
12275 $rates_aligned = false;
12276 }
12277
12278 if (!$rates_aligned) {
12279 // update session values
12280 $session->set('vbVcmRatesUpd', $updforvcm);
12281 }
12282
12283 echo json_encode($output);
12284 exit;
12285 }
12286
12287 public function icsexportlaunch() {
12288 $dbo = JFactory::getDBO();
12289 $pcheckindate = VikRequest::getString('checkindate', '', 'request');
12290 $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
12291 $pstatus = VikRequest::getString('status', '', 'request');
12292 $validstatus = array('confirmed', 'standby', 'cancelled');
12293 $filterstatus = '';
12294 $filterfirst = 0;
12295 $filtersecond = 0;
12296 $nowdf = VikBooking::getDateFormat(true);
12297 if ($nowdf == "%d/%m/%Y") {
12298 $df = 'd/m/Y';
12299 } elseif ($nowdf == "%m/%d/%Y") {
12300 $df = 'm/d/Y';
12301 } else {
12302 $df = 'Y/m/d';
12303 }
12304 $currencyname = VikBooking::getCurrencyName();
12305 if (!empty($pstatus) && in_array($pstatus, $validstatus)) {
12306 $filterstatus = $pstatus;
12307 }
12308 if (!empty($pcheckindate)) {
12309 if (VikBooking::dateIsValid($pcheckindate)) {
12310 $first=VikBooking::getDateTimestamp($pcheckindate, '0', '0');
12311 $filterfirst = $first;
12312 }
12313 }
12314 if (!empty($pcheckoutdate)) {
12315 if (VikBooking::dateIsValid($pcheckoutdate)) {
12316 $second=VikBooking::getDateTimestamp($pcheckoutdate, '23', '59');
12317 if ($second > $first) {
12318 $filtersecond = $second;
12319 }
12320 }
12321 }
12322 $clause = array();
12323 if ($filterfirst > 0) {
12324 $clause[] = "`o`.`checkin` >= ".$filterfirst;
12325 }
12326 if ($filtersecond > 0) {
12327 $clause[] = "`o`.`checkout` <= ".$filtersecond;
12328 }
12329 if (!empty($filterstatus)) {
12330 $clause[] = "`o`.`status` = '".$filterstatus."'";
12331 }
12332 $q = "SELECT `o`.*,`or`.`idroom`,`or`.`adults`,`or`.`children`,`r`.`name` FROM `#__vikbooking_orders` AS `o` LEFT JOIN `#__vikbooking_ordersrooms` `or` ON `or`.`idorder`=`o`.`id` LEFT JOIN `#__vikbooking_rooms` `r` ON `or`.`idroom`=`r`.`id` ".(count($clause) > 0 ? "WHERE ".implode(" AND ", $clause)." " : "")."ORDER BY `o`.`checkin` ASC;";
12333 $dbo->setQuery($q);
12334 $dbo->execute();
12335 if ($dbo->getNumRows() > 0) {
12336 $orders = $dbo->loadAssocList();
12337 $icscontent = "BEGIN:VCALENDAR\n";
12338 $icscontent .= "VERSION:2.0\n";
12339 $icscontent .= "PRODID:-//e4j//VikBooking//EN\n";
12340 $icscontent .= "CALSCALE:GREGORIAN\n";
12341 $str = "";
12342 foreach ($orders as $kord => $ord) {
12343 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12344 continue;
12345 }
12346 $usecurrencyname = $currencyname;
12347 $usecurrencyname = !empty($ord['idorderota']) && !empty($ord['chcurrency']) ? $ord['chcurrency'] : $usecurrencyname;
12348 $statusstr = '';
12349 if ($ord['status'] == 'confirmed') {
12350 $statusstr = JText::translate('VBCSVSTATUSCONFIRMED');
12351 } elseif ($ord['status'] == 'standby') {
12352 $statusstr = JText::translate('VBCSVSTATUSSTANDBY');
12353 } elseif ($ord['status'] == 'cancelled') {
12354 $statusstr = JText::translate('VBCSVSTATUSCANCELLED');
12355 }
12356 $uri = JURI::root().'index.php?option=com_vikbooking&view=booking&sid='.$ord['sid'].'&ts='.$ord['ts'];
12357 /**
12358 * @wponly Rewrite URI for front-end
12359 */
12360 $uri = str_replace(JUri::root(), '', $uri);
12361 $model = JModel::getInstance('vikbooking', 'shortcodes');
12362 $itemid = $model->best('booking');
12363 if ($itemid) {
12364 $uri = JRoute::rewrite($uri . "&Itemid={$itemid}", false);
12365 }
12366 //
12367 $ordnumbstr = $ord['id'].(!empty($ord['confirmnumber']) ? ' - '.$ord['confirmnumber'] : '').(!empty($ord['idorderota']) ? ' ('.ucwords($ord['channel']).')' : '').' - '.$statusstr;
12368 $peoplestr = ($ord['adults'] + $ord['children']).($ord['children'] > 0 ? ' ('.JText::translate('VBCSVCHILDREN').': '.$ord['children'].')' : '');
12369 $totalstring = ($ord['total'] > 0 ? ($usecurrencyname.' '.VikBooking::numberFormat($ord['total'])) : '');
12370 $totalpaidstring = ($ord['totpaid'] > 0 ? (' ('.VikBooking::numberFormat($ord['totpaid']).')') : '');
12371 $description = JText::sprintf('VBICSEXPDESCRIPTION', $ordnumbstr."\\n", $peoplestr."\\n", $ord['days']."\\n", $totalstring.$totalpaidstring."\\n", "\\n".str_replace("\n", "\\n", trim($ord['custdata'])));
12372 $str .= "BEGIN:VEVENT\n";
12373 $str .= "DTEND:" . JFactory::getDate(date('Y-m-d H:i:s', $ord['checkout']), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12374 $str .= "UID:" . uniqid() . "\n";
12375 $str .= "DTSTAMP:" . JFactory::getDate(date('Y-m-d H:i:s'), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12376 $str .= ((strlen($description) > 0 ) ? "DESCRIPTION:".preg_replace('/([\,;])/','\\\$1', $description)."\n" : "");
12377 $str .= "URL;VALUE=URI:" . preg_replace('/([\,;])/','\\\$1', $uri) . "\n";
12378 $str .= "SUMMARY:" . JText::sprintf('VBICSEXPSUMMARY', date($df, $ord['checkin'])) . "\n";
12379 $str .= "DTSTART:" . JFactory::getDate(date('Y-m-d H:i:s', $ord['checkin']), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12380 $str .= "END:VEVENT\n";
12381 }
12382 $icscontent .= $str;
12383 $icscontent .= "END:VCALENDAR\n";
12384 //download file from buffer
12385 header("Content-Type: application/octet-stream; ");
12386 header("Cache-Control: no-store, no-cache");
12387 header('Content-Disposition: attachment; filename="bookings_export.ics"');
12388 $f = fopen('php://output', "w");
12389 fwrite($f, $icscontent);
12390 fclose($f);
12391 exit;
12392 } else {
12393 VikError::raiseWarning('', JText::translate('VBICSEXPNORECORDS'));
12394 $mainframe = JFactory::getApplication();
12395 $mainframe->redirect("index.php?option=com_vikbooking&task=icsexportprepare&checkindate=".$pcheckindate."&checkoutdate=".$pcheckoutdate."&status=".$pstatus."&tmpl=component");
12396 }
12397 }
12398
12399 public function csvexportlaunch()
12400 {
12401 $dbo = JFactory::getDbo();
12402 $app = JFactory::getApplication();
12403
12404 $pdatefilt = VikRequest::getString('datefilt', '', 'request');
12405 $proomfilt = VikRequest::getString('roomfilt', '', 'request');
12406 $pchfilt = VikRequest::getString('chfilt', '', 'request');
12407 $ppayfilt = VikRequest::getString('payfilt', '', 'request');
12408 $pcheckindate = VikRequest::getString('checkindate', '', 'request');
12409 $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
12410 $pstatus = VikRequest::getString('status', '', 'request');
12411 $pcatfilt = VikRequest::getInt('catfilt', 0, 'request');
12412 $pformat = VikRequest::getString('format', 'csv', 'request');
12413
12414 // let the report class (a generic one) generate the CSV file in the proper format
12415 $report_obj = VikBooking::getReportInstance('revenue')->setExportCSVFormat($pformat);
12416
12417 $validstatus = array('confirmed', 'standby', 'cancelled');
12418 $validdates = array('ts', 'checkin', 'checkout');
12419
12420 $filterdate = '';
12421 $filterstatus = '';
12422 $first = 0;
12423 $filterfirst = 0;
12424 $filtersecond = 0;
12425 $nowdf = VikBooking::getDateFormat(true);
12426 if ($nowdf == "%d/%m/%Y") {
12427 $df = 'd/m/Y';
12428 } elseif ($nowdf == "%m/%d/%Y") {
12429 $df = 'm/d/Y';
12430 } else {
12431 $df = 'Y/m/d';
12432 }
12433 $datesep = VikBooking::getDateSeparator(true);
12434 $currencyname = VikBooking::getCurrencyName();
12435
12436 if (!empty($pstatus) && in_array($pstatus, $validstatus)) {
12437 $filterstatus = $pstatus;
12438 }
12439 if (!empty($pdatefilt) && in_array($pdatefilt, $validdates)) {
12440 $filterdate = $pdatefilt;
12441 }
12442 if (!empty($pcheckindate) && !empty($filterdate)) {
12443 if (VikBooking::dateIsValid($pcheckindate)) {
12444 $first = VikBooking::getDateTimestamp($pcheckindate, '0', '0');
12445 $filterfirst = $first;
12446 }
12447 }
12448 if (!empty($pcheckoutdate) && !empty($filterdate)) {
12449 if (VikBooking::dateIsValid($pcheckoutdate)) {
12450 $second = VikBooking::getDateTimestamp($pcheckoutdate, '23', '59');
12451 if ($second > $first) {
12452 $filtersecond = $second;
12453 }
12454 }
12455 }
12456 $clause = array();
12457 if ($filterfirst > 0) {
12458 $clause[] = "`o`.`".$filterdate."` >= ".$filterfirst;
12459 }
12460 if ($filtersecond > 0) {
12461 $clause[] = "`o`.`".$filterdate."` <= ".$filtersecond;
12462 }
12463 if (!empty($filterstatus)) {
12464 $clause[] = "`o`.`status` = '".$filterstatus."'";
12465 }
12466 if (!empty($pchfilt)) {
12467 $clause[] = "`o`.`channel` LIKE ".$dbo->quote("%".$pchfilt."%");
12468 }
12469 if (!empty($ppayfilt)) {
12470 $clause[] = "`o`.`idpayment` LIKE '".$ppayfilt."=%'";
12471 }
12472 if (!empty($proomfilt)) {
12473 $clause[] = "`or`.`idroom` = '".(int)$proomfilt."'";
12474 }
12475
12476 if (!empty($pcatfilt)) {
12477 $room_cat_ids = array();
12478 $q = "SELECT `id`,`idcat` FROM `#__vikbooking_rooms` WHERE `idcat` LIKE " . $dbo->quote("%$pcatfilt%");
12479 $dbo->setQuery($q);
12480 $dbo->execute();
12481 if ($dbo->getNumRows()) {
12482 $records = $dbo->loadAssocList();
12483 foreach ($records as $rcat) {
12484 $parts = explode(';', $rcat['idcat']);
12485 if (in_array($pcatfilt, $parts)) {
12486 $room_cat_ids[] = $rcat['id'];
12487 }
12488 }
12489 }
12490 if (count($room_cat_ids)) {
12491 $clause[] = "`or`.`idroom` IN (" . implode(', ', $room_cat_ids) . ")";
12492 }
12493 }
12494
12495 $q = "SELECT `o`.*,`or`.`idroom`,`or`.`adults`,`or`.`children`,`or`.`idtar`,`or`.`optionals`,`or`.`t_first_name`,`or`.`t_last_name`,`or`.`extracosts`,`or`.`cust_cost`,`or`.`cust_idiva`,`or`.`room_cost`,`r`.`name`,`d`.`idprice`,`p`.`idiva`,`t`.`aliq`,`t`.`breakdown` FROM `#__vikbooking_orders` AS `o` LEFT JOIN `#__vikbooking_ordersrooms` `or` ON `or`.`idorder`=`o`.`id` LEFT JOIN `#__vikbooking_rooms` `r` ON `or`.`idroom`=`r`.`id` LEFT JOIN `#__vikbooking_dispcost` `d` ON `or`.`idtar`=`d`.`id` LEFT JOIN `#__vikbooking_prices` `p` ON `d`.`idprice`=`p`.`id` LEFT JOIN `#__vikbooking_iva` `t` ON `p`.`idiva`=`t`.`id` ".(count($clause) > 0 ? "WHERE ".implode(" AND ", $clause)." " : "")."ORDER BY `o`.`checkin` ASC;";
12496 $dbo->setQuery($q);
12497 $orders = $dbo->loadAssocList();
12498 if (!$orders) {
12499 $app->enqueueMessage(JText::translate('VBCSVEXPNORECORDS'), 'error');
12500 $app->redirect("index.php?option=com_vikbooking&task=csvexportprepare&checkindate=".$pcheckindate."&checkoutdate=".$pcheckoutdate."&status=".$pstatus."&tmpl=component");
12501 $app->close();
12502 }
12503
12504 // options
12505 $all_options = array();
12506 $q = "SELECT * FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
12507 $dbo->setQuery($q);
12508 $options = $dbo->loadAssocList();
12509 if ($options) {
12510 foreach ($options as $ok => $ov) {
12511 $all_options[$ov['id']] = $ov;
12512 }
12513 }
12514
12515 // build columns
12516 $columns = [
12517 [
12518 'label' => JText::translate('VBDASHBOOKINGID'),
12519 ],
12520 [
12521 'label' => JText::translate('VBPVIEWORDERSONE'),
12522 ],
12523 [
12524 'label' => JText::translate('VBCSVCHECKIN'),
12525 ],
12526 [
12527 'label' => JText::translate('VBCSVCHECKOUT'),
12528 ],
12529 [
12530 'label' => JText::translate('VBCSVNIGHTS'),
12531 ],
12532 [
12533 'label' => JText::translate('VBCSVROOM'),
12534 ],
12535 [
12536 'label' => JText::translate('VBCSVPEOPLE'),
12537 ],
12538 [
12539 'label' => JText::translate('VBCSVCUSTINFO'),
12540 ],
12541 [
12542 'label' => JText::translate('ORDER_SPREQUESTS'),
12543 ],
12544 [
12545 'label' => JText::translate('ORDER_NOTES'),
12546 ],
12547 [
12548 'label' => JText::translate('VBCSVCREATEDBY'),
12549 ],
12550 [
12551 'label' => JText::translate('VBCSVCUSTMAIL'),
12552 ],
12553 [
12554 'label' => JText::translate('ORDER_PHONE'),
12555 ],
12556 [
12557 'label' => JText::translate('VBCSVOPTIONS'),
12558 ],
12559 [
12560 'label' => JText::translate('VBCSVPAYMENTMETHOD'),
12561 ],
12562 [
12563 'label' => JText::translate('VBCSVORDIDCONFNUMB'),
12564 ],
12565 [
12566 'label' => JText::translate('VBOCHANNEL'),
12567 ],
12568 [
12569 'label' => JText::translate('VBCSVEXPFILTBSTATUS'),
12570 ],
12571 [
12572 'label' => JText::translate('VBCSVTOTAL'),
12573 ],
12574 [
12575 'label' => JText::translate('VBCSVTOTPAID'),
12576 ],
12577 [
12578 'label' => JText::translate('VBCSVTOTTAXES'),
12579 ],
12580 ];
12581
12582 // booking cancellation details
12583 $cancellation_timestamps = [];
12584
12585 if (empty($filterstatus) || $filterstatus === 'cancelled') {
12586 // insert column for cancellation date at index 2
12587 array_splice($columns, 2, 0, [['label' => JText::translate('VBO_CANC_DATE')]]);
12588 // gather all cancelled bookings, if any
12589 $cancellation_ids = [];
12590 foreach ($orders as $order) {
12591 if ($order['status'] === 'cancelled' && !in_array($order['id'], $cancellation_ids)) {
12592 $cancellation_ids[] = $order['id'];
12593 }
12594 }
12595 if ($cancellation_ids && $cancHistoryEvents = VikBooking::getBookingHistoryInstance(0)->getBookingEventsType('cancelled')) {
12596 // list of booking IDs with cancellation events processed
12597 $cancBidsProcessed = [];
12598
12599 // query the database to fetch the needed history records
12600 $dbo->setQuery(
12601 $dbo->getQuery(true)
12602 ->select([
12603 $dbo->qn('idorder'),
12604 $dbo->qn('dt'),
12605 ])
12606 ->from($dbo->qn('#__vikbooking_orderhistory'))
12607 ->where($dbo->qn('idorder') . ' IN (' . implode(', ', array_map('intval', $cancellation_ids)) . ')')
12608 ->where($dbo->qn('type') . ' IN (' . implode(', ', array_map([$dbo, 'q'], $cancHistoryEvents)) . ')')
12609 ->order($dbo->qn('idorder') . ' ASC')
12610 ->order($dbo->qn('dt') . ' ASC')
12611 );
12612
12613 // scan all booking cancellation records
12614 foreach ($dbo->loadAssocList() as $cancRecord) {
12615 if (!($cancBidsProcessed[$cancRecord['idorder']] ?? 0)) {
12616 // turn flag on to process this booking only once and get the earliest (first) cancellation
12617 $cancBidsProcessed[$cancRecord['idorder']] = 1;
12618
12619 // convert the cancellation date from UTC to local timezone and set booking cancellation timestamp
12620 $cancellation_timestamps[$cancRecord['idorder']] = JHtml::fetch('date', $cancRecord['dt'], 'U');
12621 }
12622 }
12623 }
12624 }
12625
12626 // set CSV columns
12627 $report_obj->setReportCols($columns);
12628
12629 // prepare the container for the CSV rows
12630 $orderscsv = [];
12631
12632 // availability helper
12633 $av_helper = VikBooking::getAvailabilityInstance();
12634
12635 $room_inds = [];
12636 $room_stay_dates = [];
12637 foreach ($orders as $kord => $ord) {
12638 // room index in this booking
12639 if (!isset($room_inds[$ord['id']])) {
12640 $room_inds[$ord['id']] = -1;
12641 }
12642 $room_inds[$ord['id']]++;
12643
12644 /**
12645 * Split stay reservation.
12646 *
12647 * @since 1.16.0 (J) - 1.6.0 (WP)
12648 */
12649 $room_stay_dates = $room_inds[$ord['id']] > 0 ? $room_stay_dates : [];
12650 if ($ord['split_stay']) {
12651 if ($ord['status'] == 'confirmed') {
12652 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
12653 } else {
12654 $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $ord['id'], []);
12655 }
12656 // immediately count the number of nights of stay for each split room
12657 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
12658 if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
12659 // overwrite values for compatibility with non-confirmed bookings
12660 $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
12661 $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
12662 }
12663 $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
12664 // overwrite the whole array
12665 $room_stay_dates[$sps_r_k] = $sps_r_v;
12666 }
12667 }
12668
12669 // determine nights and dates for this room booking
12670 $booking_nights = $ord['days'];
12671 $booking_checkin = $ord['checkin'];
12672 $booking_checkout = $ord['checkout'];
12673 if ($ord['split_stay'] && !empty($room_stay_dates) && isset($room_stay_dates[$room_inds[$ord['id']]]) && $room_stay_dates[$room_inds[$ord['id']]]['idroom'] == $ord['idroom']) {
12674 $booking_nights = $room_stay_dates[$room_inds[$ord['id']]]['nights'];
12675 $booking_checkin = $room_stay_dates[$room_inds[$ord['id']]]['checkin'];
12676 $booking_checkout = $room_stay_dates[$room_inds[$ord['id']]]['checkout'];
12677 }
12678
12679 $usecurrencyname = $currencyname;
12680 $usecurrencyname = !empty($ord['idorderota']) && !empty($ord['chcurrency']) ? $ord['chcurrency'] : $usecurrencyname;
12681 $peoplestr = ($ord['adults'] + $ord['children']).($ord['children'] > 0 ? ' ('.JText::translate('VBCSVCHILDREN').': '.$ord['children'].')' : '');
12682 $custinfostr = str_replace(",", " ", $ord['custdata']);
12683 $customer = VikBooking::getCPinIstance()->getCustomerFromBooking($ord['id']);
12684 if (count($customer)) {
12685 $custinfostr = $customer['first_name'] . ' ' . $customer['last_name'];
12686 }
12687 $special_requests = '';
12688 if (preg_match("/(?:special requests:\s*)(.*?)$/is", $ord['custdata'], $match)) {
12689 $special_requests = $match[1];
12690 } elseif (preg_match("/(?:special request:\s*)(.*?)$/is", $ord['custdata'], $match)) {
12691 $special_requests = $match[1];
12692 } elseif (preg_match("/(?:special request\s*)(.*?)$/is", $ord['custdata'], $match)) {
12693 $special_requests = $match[1];
12694 } elseif (preg_match("/(?:" . JText::translate('ORDER_SPREQUESTS') . ":\s*)(.*?)$/is", $ord['custdata'], $match)) {
12695 $special_requests = $match[1];
12696 }
12697 $paystr = '';
12698 if (!empty($ord['idpayment'])) {
12699 $payparts = explode('=', $ord['idpayment']);
12700 $paystr = $payparts[1];
12701 }
12702 $ordnumbstr = $ord['id'] . ' - ' . $ord['confirmnumber'] . (!empty($ord['idorderota']) ? ' (' . $ord['idorderota'] . ')' : '');
12703 $bookingSource = JText::translate('VBORDFROMSITE');
12704 if (!empty($ord['channel']) && !empty($ord['idorderota'])) {
12705 $chparts = explode('_', $ord['channel']);
12706 $bookingSource = ($chparts[1] ?? '') ?: $chparts[0];
12707 }
12708 $statusstr = '';
12709 if ($ord['status'] == 'confirmed') {
12710 $statusstr = JText::translate('VBCSVSTATUSCONFIRMED');
12711 } elseif ($ord['status'] == 'standby') {
12712 $statusstr = JText::translate('VBCSVSTATUSSTANDBY');
12713 } elseif ($ord['status'] == 'cancelled') {
12714 $statusstr = JText::translate('VBCSVSTATUSCANCELLED');
12715 }
12716 $totalstring = $usecurrencyname . ' ' . VikBooking::numberFormat($ord['total']);
12717 if ($ord['roomsnum'] > 1) {
12718 // take the cost for the individual room
12719 $totalstring = !empty($ord['cust_cost']) && $ord['cust_cost'] > 0 ? ($usecurrencyname . ' ' . VikBooking::numberFormat($ord['cust_cost'])) : ($usecurrencyname . ' ' . VikBooking::numberFormat($ord['room_cost']));
12720 }
12721 $totalpaidstring = $usecurrencyname . ' ' . VikBooking::numberFormat($ord['totpaid']);
12722 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12723 // total paid will be printed only for the last room booked
12724 $totalpaidstring = '';
12725 }
12726 $options_str = '';
12727 if (!empty($ord['optionals'])) {
12728 $stepo = explode(";", $ord['optionals']);
12729 foreach ($stepo as $roptkey => $oo) {
12730 if (!empty($oo)) {
12731 $stept = explode(":", $oo);
12732 if (array_key_exists($stept[0], $all_options)) {
12733 $actopt = $all_options[$stept[0]];
12734 $optpcent = false;
12735 if (!empty($actopt['ageintervals']) && $ord['children'] > 0 && strstr($stept[1], '-') != false) {
12736 $optagenames = VikBooking::getOptionIntervalsAges($actopt['ageintervals']);
12737 $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt['ageintervals']);
12738 $optageovrct = VikBooking::getOptionIntervalChildOverrides($actopt, $ord['adults'], $ord['children']);
12739 $child_num = VikBooking::getRoomOptionChildNumber($ord['optionals'], $actopt['id'], $roptkey, $ord['children']);
12740 $optagecosts = VikBooking::getOptionIntervalsCosts(isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $actopt['ageintervals']);
12741 $agestept = explode('-', $stept[1]);
12742 $stept[1] = $agestept[0];
12743 $chvar = $agestept[1];
12744 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] > 0) {
12745 $optpcent = true;
12746 }
12747 $actopt['chageintv'] = $chvar;
12748 if (isset($optagenames[($chvar - 1)])) {
12749 $actopt['name'] .= ' ('.$optagenames[($chvar - 1)].')';
12750 }
12751 if (isset($optagecosts[($chvar - 1)])) {
12752 $realcost = (intval($actopt['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $booking_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
12753 } else {
12754 $realcost = 0;
12755 }
12756 } else {
12757 // VBO 1.11 - options percentage cost of the room total fee
12758 $optpcent = (int)$actopt['pcentroom'] ? true : $optpcent;
12759 //
12760 $realcost = (intval($actopt['perday']) == 1 ? ($actopt['cost'] * $booking_nights * $stept[1]) : ($actopt['cost'] * $stept[1]));
12761 }
12762 if ($actopt['maxprice'] > 0 && $realcost > $actopt['maxprice']) {
12763 $realcost=$actopt['maxprice'];
12764 if (intval($actopt['hmany']) == 1 && intval($stept[1]) > 1) {
12765 $realcost = $actopt['maxprice'] * $stept[1];
12766 }
12767 }
12768 $realcost = $actopt['perperson'] == 1 ? ($realcost * $ord['adults']) : $realcost;
12769
12770 /**
12771 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
12772 *
12773 * @since 1.17.7 (J) - 1.7.7 (WP)
12774 */
12775 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt, $ord, $ord]);
12776 if ($custom_calculation) {
12777 $realcost = (float) $custom_calculation[0];
12778 }
12779
12780 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt['idiva']);
12781 $options_str .= ($stept[1] > 1 ? $stept[1]." " : "").$actopt['name'].": ".(!$optpcent ? $currencyname : '')." ".VikBooking::numberFormat($tmpopr).($optpcent ? ' %' : '')." \r\n";
12782 }
12783 }
12784 }
12785 }
12786
12787 // custom extra costs
12788 if (!empty($ord['extracosts'])) {
12789 $cur_extra_costs = json_decode($ord['extracosts'], true);
12790 foreach ($cur_extra_costs as $eck => $ecv) {
12791 $ecplustax = !empty($ecv['idtax']) ? VikBooking::sayOptionalsPlusIva($ecv['cost'], $ecv['idtax']) : $ecv['cost'];
12792 $options_str .= $ecv['name'].": ".$currencyname." ".VikBooking::numberFormat($ecplustax)." \r\n";
12793 }
12794 }
12795
12796 // taxes
12797 $taxes_str = '';
12798 if ($ord['tot_taxes'] > 0.00) {
12799 $taxes_str .= $usecurrencyname.' '.VikBooking::numberFormat($ord['tot_taxes']);
12800 if (!empty($ord['aliq']) && !empty($ord['breakdown'])) {
12801 $tax_breakdown = json_decode($ord['breakdown'], true);
12802 $tax_breakdown = is_array($tax_breakdown) && count($tax_breakdown) > 0 ? $tax_breakdown : array();
12803 if (count($tax_breakdown)) {
12804 foreach ($tax_breakdown as $tbkk => $tbkv) {
12805 $tax_break_cost = $ord['tot_taxes'] * floatval($tbkv['aliq']) / $ord['aliq'];
12806 $taxes_str .= "\r\n".$tbkv['name'].": ".$usecurrencyname.' '.VikBooking::numberFormat($tax_break_cost);
12807 }
12808 }
12809 }
12810 }
12811 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12812 // total taxes will be printed only for the last room booked
12813 $taxes_str = '';
12814 }
12815
12816 // created by
12817 $created_by = '';
12818 if (!empty($ord['ujid'])) {
12819 $creator = new JUser($ord['ujid']);
12820 if (property_exists($creator, 'name')) {
12821 $created_by = $creator->name.' ('.$creator->username.')';
12822 }
12823 }
12824 if (empty($created_by) && !empty($ord['t_first_name'])) {
12825 $created_by = $ord['t_first_name'].' '.$ord['t_last_name'];
12826 }
12827
12828 // build CSV line data
12829 $line_data = [
12830 [
12831 'value' => $ord['id'],
12832 ],
12833 [
12834 'value' => date(str_replace("/", $datesep, $df), $ord['ts']),
12835 ],
12836 [
12837 'value' => date(str_replace("/", $datesep, $df), $booking_checkin),
12838 ],
12839 [
12840 'value' => date(str_replace("/", $datesep, $df), $booking_checkout),
12841 ],
12842 [
12843 'value' => $booking_nights,
12844 ],
12845 [
12846 'value' => $ord['name'],
12847 ],
12848 [
12849 'value' => $peoplestr,
12850 ],
12851 [
12852 'value' => $custinfostr,
12853 ],
12854 [
12855 'value' => $special_requests,
12856 ],
12857 [
12858 'value' => $ord['adminnotes'],
12859 ],
12860 [
12861 'value' => $created_by,
12862 ],
12863 [
12864 'value' => $ord['custmail'],
12865 ],
12866 [
12867 'value' => $ord['phone'],
12868 ],
12869 [
12870 'value' => $options_str,
12871 ],
12872 [
12873 'value' => $paystr,
12874 ],
12875 [
12876 'value' => $ordnumbstr,
12877 ],
12878 [
12879 'value' => $bookingSource,
12880 ],
12881 [
12882 'value' => $statusstr,
12883 ],
12884 [
12885 'value' => $totalstring,
12886 ],
12887 [
12888 'value' => $totalpaidstring,
12889 ],
12890 [
12891 'value' => $taxes_str,
12892 ],
12893 ];
12894
12895 if (empty($filterstatus) || $filterstatus === 'cancelled') {
12896 // obtain cancellation date for this booking
12897 $booking_canc_date = $cancellation_timestamps[$ord['id']] ?? '';
12898 if ($booking_canc_date) {
12899 $booking_canc_date = date(str_replace("/", $datesep, $df), $booking_canc_date);
12900 }
12901 // insert column for cancellation date at index 2
12902 array_splice($line_data, 2, 0, [['value' => $booking_canc_date]]);
12903 }
12904
12905 // push line for export
12906 $orderscsv[] = $line_data;
12907 }
12908
12909 // set CSV rows
12910 $report_obj->setReportRows($orderscsv);
12911
12912 // build lines to export
12913 $csvlines = $report_obj->getExportCSVLines($no_data = true);
12914
12915 // set export file name
12916 $report_obj->setExportCSVFileName('bookings_export_' . date('Y-m-d') . '.csv');
12917
12918 // force the download of the CSV file
12919 $report_obj->outputHeaders();
12920
12921 // send lines to output
12922 $report_obj->outputCSV($csvlines);
12923
12924 exit;
12925 }
12926
12927 public function exportcustomerslaunch() {
12928 $cid = VikRequest::getVar('cid', array(0));
12929 $dbo = JFactory::getDBO();
12930 $pnotes = VikRequest::getInt('notes', '', 'request');
12931 $pscanimg = VikRequest::getInt('scanimg', '', 'request');
12932 $ppin = VikRequest::getInt('pin', '', 'request');
12933 $pcountry = VikRequest::getString('country', '', 'request');
12934 $pfromdate = VikRequest::getString('fromdate', '', 'request');
12935 $ptodate = VikRequest::getString('todate', '', 'request');
12936 $pdatefilt = VikRequest::getInt('datefilt', '', 'request');
12937 $clauses = array();
12938 if (count($cid) > 0 && !empty($cid[0])) {
12939 $clauses[] = "`c`.`id` IN (".implode(', ', $cid).")";
12940 }
12941 if (!empty($pcountry)) {
12942 $clauses[] = "`c`.`country`=".$dbo->quote($pcountry);
12943 }
12944 $datescol = '`bk`.`ts`';
12945 if ($pdatefilt > 0) {
12946 if ($pdatefilt == 1) {
12947 $datescol = '`bk`.`ts`';
12948 } elseif ($pdatefilt == 2) {
12949 $datescol = '`bk`.`checkin`';
12950 } elseif ($pdatefilt == 3) {
12951 $datescol = '`bk`.`checkout`';
12952 }
12953 }
12954 if (!empty($pfromdate)) {
12955 $from_ts = VikBooking::getDateTimestamp($pfromdate, 0, 0);
12956 $clauses[] = $datescol.">=".$from_ts;
12957 }
12958 if (!empty($ptodate)) {
12959 $to_ts = VikBooking::getDateTimestamp($ptodate, 23, 59);
12960 $clauses[] = $datescol."<=".$to_ts;
12961 }
12962 //this query below is safe with the error #1055 when sql_mode=only_full_group_by
12963 $q = "SELECT `c`.`id`,`c`.`first_name`,`c`.`last_name`,`c`.`email`,`c`.`phone`,`c`.`country`,`c`.`cfields`,`c`.`pin`,`c`.`ujid`,`c`.`address`,`c`.`city`,`c`.`zip`,`c`.`doctype`,`c`.`docnum`,`c`.`docimg`,`c`.`notes`,`c`.`ischannel`,`c`.`chdata`,`c`.`company`,`c`.`vat`,`c`.`gender`,`c`.`bdate`,`c`.`pbirth`,".
12964 "(SELECT COUNT(*) FROM `#__vikbooking_customers_orders` AS `co` WHERE `co`.`idcustomer`=`c`.`id`) AS `tot_bookings`,".
12965 "`cy`.`country_3_code`,`cy`.`country_name` ".
12966 "FROM `#__vikbooking_customers` AS `c` LEFT JOIN `#__vikbooking_countries` `cy` ON `cy`.`country_3_code`=`c`.`country` ".
12967 "LEFT JOIN `#__vikbooking_customers_orders` `co` ON `co`.`idcustomer`=`c`.`id` ".
12968 "LEFT JOIN `#__vikbooking_orders` `bk` ON `bk`.`id`=`co`.`idorder`".
12969 (count($clauses) > 0 ? " WHERE ".implode(' AND ', $clauses) : "")."
12970 GROUP BY `c`.`id`,`c`.`first_name`,`c`.`last_name`,`c`.`email`,`c`.`phone`,`c`.`country`,`c`.`cfields`,`c`.`pin`,`c`.`ujid`,`c`.`address`,`c`.`city`,`c`.`zip`,`c`.`doctype`,`c`.`docnum`,`c`.`docimg`,`c`.`notes`,`c`.`ischannel`,`c`.`chdata`,`c`.`company`,`c`.`vat`,`c`.`gender`,`c`.`bdate`,`c`.`pbirth`,`cy`.`country_3_code`,`cy`.`country_name` ".
12971 "ORDER BY `c`.`last_name` ASC;";
12972 $dbo->setQuery($q);
12973 $customers = $dbo->loadAssocList();
12974 if (!$customers) {
12975 VikError::raiseWarning('', JText::translate('VBONORECORDSCSVCUSTOMERS'));
12976 $mainframe = JFactory::getApplication();
12977 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
12978 exit;
12979 }
12980 $csvlines = [];
12981 $csvheadline = [
12982 'ID',
12983 JText::translate('VBCUSTOMERLASTNAME'),
12984 JText::translate('VBCUSTOMERFIRSTNAME'),
12985 JText::translate('VBCUSTOMEREMAIL'),
12986 JText::translate('VBCUSTOMERPHONE'),
12987 JText::translate('VBCUSTOMERADDRESS'),
12988 JText::translate('VBCUSTOMERCITY'),
12989 JText::translate('VBCUSTOMERZIP'),
12990 JText::translate('VBCUSTOMERCOUNTRY'),
12991 JText::translate('VBCUSTOMERGENDER'),
12992 JText::translate('ORDER_DBIRTH'),
12993 JText::translate('VBCUSTOMERTOTBOOKINGS'),
12994 ];
12995 if ($ppin > 0) {
12996 $csvheadline[] = JText::translate('VBCUSTOMERPIN');
12997 }
12998 if ($pscanimg > 0) {
12999 $csvheadline[] = JText::translate('VBCUSTOMERDOCTYPE');
13000 $csvheadline[] = JText::translate('VBCUSTOMERDOCNUM');
13001 $csvheadline[] = JText::translate('VBCUSTOMERDOCIMG');
13002 }
13003 if ($pnotes > 0) {
13004 $csvheadline[] = JText::translate('VBCUSTOMERNOTES');
13005 }
13006 $csvlines[] = $csvheadline;
13007 foreach ($customers as $customer) {
13008 $csvcustomerline = [
13009 $customer['id'],
13010 $customer['last_name'],
13011 $customer['first_name'],
13012 $customer['email'],
13013 $customer['phone'],
13014 $customer['address'],
13015 $customer['city'],
13016 $customer['zip'],
13017 $customer['country_name'],
13018 $customer['gender'],
13019 $customer['bdate'],
13020 $customer['tot_bookings'],
13021 ];
13022 if ($ppin > 0) {
13023 $csvcustomerline[] = $customer['pin'];
13024 }
13025 if ($pscanimg > 0) {
13026 $csvcustomerline[] = $customer['doctype'];
13027 $csvcustomerline[] = $customer['docnum'];
13028 $csvcustomerline[] = (!empty($customer['docimg']) ? VBO_ADMIN_URI.'resources/idscans/'.$customer['docimg'] : '');
13029 }
13030 if ($pnotes > 0) {
13031 $csvcustomerline[] = $customer['notes'];
13032 }
13033 $csvlines[] = $csvcustomerline;
13034 }
13035 header("Content-type: text/csv");
13036 header("Cache-Control: no-store, no-cache");
13037 header('Content-Disposition: attachment; filename="customers_export_'.(!empty($pcountry) ? strtolower($pcountry).'_' : '').date('Y-m-d').'.csv"');
13038 $outstream = fopen("php://output", 'w');
13039 foreach ($csvlines as $csvline) {
13040 fputcsv($outstream, $csvline, $separator = ',', $enclosure = '"', $escape = '');
13041 }
13042 fclose($outstream);
13043 exit;
13044 }
13045
13046 public function renewsession() {
13047 /*
13048 * @wponly
13049 * We just destroy the session
13050 */
13051 JSessionHandler::destroy();
13052 $mainframe = JFactory::getApplication();
13053 $mainframe->redirect("index.php?option=com_vikbooking&task=config");
13054 }
13055
13056 public function trackings() {
13057 VikBookingHelper::printHeader("trackings");
13058
13059 VikRequest::setVar('view', VikRequest::getCmd('view', 'trackings'));
13060
13061 parent::display();
13062
13063 if (VikBooking::showFooter()) {
13064 VikBookingHelper::printFooter();
13065 }
13066 }
13067
13068 public function trkconfig() {
13069 VikBookingHelper::printHeader("trackings");
13070
13071 VikRequest::setVar('view', VikRequest::getCmd('view', 'trkconfig'));
13072
13073 parent::display();
13074
13075 if (VikBooking::showFooter()) {
13076 VikBookingHelper::printFooter();
13077 }
13078 }
13079
13080 public function savetrkconfigstay() {
13081 if (!JSession::checkToken()) {
13082 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13083 }
13084 $this->do_savetrkconfig(true);
13085 }
13086
13087 public function savetrkconfig() {
13088 if (!JSession::checkToken()) {
13089 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13090 }
13091 $this->do_savetrkconfig();
13092 }
13093
13094 private function do_savetrkconfig($stay = false) {
13095 $dbo = JFactory::getDBO();
13096 $trkenabled = VikRequest::getInt('trkenabled', 0, 'request');
13097 $trkenabled = $trkenabled == 1 ? 1 : 0;
13098 $trkcookierfrdur = VikRequest::getFloat('trkcookierfrdur', 1, 'request');
13099 $trkcookierfrdur = $trkcookierfrdur < 0.1 ? 1 : $trkcookierfrdur;
13100 $trkcampname = VikRequest::getVar('trkcampname', array());
13101 $trkcampkey = VikRequest::getVar('trkcampkey', array());
13102 $trkcampval = VikRequest::getVar('trkcampval', array());
13103 $trkcampaigns = array();
13104 foreach ($trkcampname as $k => $v) {
13105 if (empty($trkcampkey[$k])) {
13106 continue;
13107 }
13108 $trkcampkey[$k] = str_replace(' ', '', trim($trkcampkey[$k]));
13109 $name = !empty($v) ? $v : date('Y-m-d').' '.(count($trkcampaigns) + 1);
13110 $trkcampaigns[$trkcampkey[$k]] = array(
13111 'key' => $trkcampkey[$k],
13112 'value' => $trkcampval[$k],
13113 'name' => $name,
13114 );
13115 }
13116
13117 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($trkenabled)." WHERE `param`='trkenabled';";
13118 $dbo->setQuery($q);
13119 $dbo->execute();
13120 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($trkcookierfrdur)." WHERE `param`='trkcookierfrdur';";
13121 $dbo->setQuery($q);
13122 $dbo->execute();
13123 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($trkcampaigns))." WHERE `param`='trkcampaigns';";
13124 $dbo->setQuery($q);
13125 $dbo->execute();
13126
13127 $mainframe = JFactory::getApplication();
13128 $mainframe->redirect("index.php?option=com_vikbooking&task=".($stay ? 'trkconfig' : 'trackings'));
13129 }
13130
13131 public function modtracking() {
13132 $dbo = JFactory::getDbo();
13133 $cid = VikRequest::getVar('cid', array());
13134 foreach ($cid as $id) {
13135 if (!empty($id)) {
13136 $q = "SELECT `id`,`published` FROM `#__vikbooking_trackings` WHERE `id`=".(int)$id.";";
13137 $dbo->setQuery($q);
13138 $dbo->execute();
13139 if ($dbo->getNumRows()) {
13140 $data = $dbo->loadAssoc();
13141 $q = "UPDATE `#__vikbooking_trackings` SET `published`=".($data['published'] ? '0' : '1')." WHERE `id`=".(int)$data['id'].";";
13142 $dbo->setQuery($q);
13143 $dbo->execute();
13144 }
13145 }
13146 }
13147 $mainframe = JFactory::getApplication();
13148 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13149 }
13150
13151 public function removetrackings()
13152 {
13153 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
13154 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13155 }
13156
13157 $ids = VikRequest::getVar('cid', array());
13158 $dbo = JFactory::getDbo();
13159
13160 foreach ($ids as $d) {
13161 $q = "DELETE FROM `#__vikbooking_trackings` WHERE `id`=".(int)$d.";";
13162 $dbo->setQuery($q);
13163 $dbo->execute();
13164 $q = "DELETE FROM `#__vikbooking_tracking_infos` WHERE `idtracking`=".(int)$d.";";
13165 $dbo->setQuery($q);
13166 $dbo->execute();
13167 }
13168
13169 $mainframe = JFactory::getApplication();
13170 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13171 }
13172
13173 /**
13174 * Invokes the Tracker class to obtain
13175 * geo information about the IP addresses.
13176 * This task is called via ajax.
13177 *
13178 * @since 1.11
13179 */
13180 public function getgeoinfo() {
13181 $ips = VikRequest::getVar('ips', array());
13182 if (!count($ips)) {
13183 echo 'e4j.error.empty IPs';
13184 exit;
13185 }
13186
13187 // require the Tracker class without instantiating the object
13188 VikBooking::getTracker(true);
13189 $geo_info = VikBookingTracker::getIpGeoInfo($ips);
13190
13191 if ($geo_info === false) {
13192 echo 'e4j.error.Tracker error, could not get geo info from IPs';
13193 exit;
13194 }
13195
13196 // update db values and compose response
13197 $dbo = JFactory::getDbo();
13198 $resp = array();
13199 foreach ($geo_info as $id => $geo) {
13200 if (is_null($geo) || $geo === false) {
13201 continue;
13202 }
13203 // compose geo info string
13204 $geovals = array();
13205 if (!empty($geo['city'])) {
13206 array_push($geovals, $geo['city']);
13207 }
13208 if (!empty($geo['region'])) {
13209 array_push($geovals, $geo['region']);
13210 }
13211 $threecode = '';
13212 $cname = '';
13213 if (!empty($geo['country'])) {
13214 // returned country is a 2-char code, get the 3-char country code
13215 $q = "SELECT `country_3_code`,`country_name` FROM `#__vikbooking_countries` WHERE `country_2_code`=".$dbo->quote($geo['country']).";";
13216 $dbo->setQuery($q);
13217 $dbo->execute();
13218 if ($dbo->getNumRows()) {
13219 $cinfo = $dbo->loadAssoc();
13220 $threecode = $cinfo['country_3_code'];
13221 $cname = $cinfo['country_name'];
13222 }
13223 array_push($geovals, (empty($cname) ? $geo['country'] : $cname));
13224 }
13225
13226 // full geo information string
13227 $geoinfostr = implode(', ', $geovals);
13228
13229 // push data to the response pool
13230 $resp[$id] = array();
13231 $resp[$id]['geo'] = $geoinfostr;
13232 if (!empty($cname)) {
13233 $resp[$id]['country'] = $cname;
13234 }
13235 if (!empty($threecode)) {
13236 $resp[$id]['country3'] = $threecode;
13237 }
13238
13239 // update main tracking record
13240 $q = "UPDATE `#__vikbooking_trackings` SET `geo`=".$dbo->quote($geoinfostr).(!empty($threecode) ? ', `country`='.$dbo->quote($threecode) : '')." WHERE `id`=".(int)$id.";";
13241 $dbo->setQuery($q);
13242 $dbo->execute();
13243 }
13244
13245 // output the JSON response
13246 echo json_encode($resp);
13247 exit;
13248 }
13249
13250 /**
13251 * Counts the orphan dates for all published rooms
13252 * depending on their restrictions and booked dates.
13253 * By default, the task takes up to 3 months ahead.
13254 * It is possible to filter the request by rooms and months.
13255 * This task should be called via ajax.
13256 *
13257 * @since 1.11
13258 */
13259 public function orphanscount()
13260 {
13261 $dbo = JFactory::getDbo();
13262 $orphans = array();
13263
13264 $nowdf = VikBooking::getDateFormat();
13265 if ($nowdf == "%d/%m/%Y") {
13266 $df = 'd/m/Y';
13267 } elseif ($nowdf == "%m/%d/%Y") {
13268 $df = 'm/d/Y';
13269 } else {
13270 $df = 'Y/m/d';
13271 }
13272
13273 // global min los
13274 $glob_minlos = VikBooking::getDefaultNightsCalendar();
13275 $glob_minlos = $glob_minlos < 1 ? 1 : $glob_minlos;
13276
13277 // rooms and dates
13278 $roomids = VikRequest::getVar('roomids', array(), 'request', 'int');
13279 $months = VikRequest::getInt('months', 3, 'request');
13280 $from = VikRequest::getString('from', '', 'request');
13281 $today = strtotime(date('Y').'-'.date('m').'-'.date('d'));
13282 if (!empty($from)) {
13283 $fromts = VikBooking::getDateTimestamp($from, 0, 0);
13284 if (!empty($fromts)) {
13285 // custom starting date
13286 $today = $fromts;
13287 }
13288 }
13289 $until = strtotime("+{$months} months", $today);
13290
13291 // load all rooms
13292 $rooms = array();
13293 $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms` WHERE `avail`=1".(count($roomids) ? ' AND `id` IN ('.implode(', ', $roomids).')' : '').";";
13294 $dbo->setQuery($q);
13295 $dbo->execute();
13296 if ($dbo->getNumRows()) {
13297 $allrooms = $dbo->loadAssocList();
13298 foreach ($allrooms as $r) {
13299 $rooms[$r['id']] = $r;
13300 }
13301 }
13302 if (!count($rooms)) {
13303 // no rooms found, exit
13304 echo json_encode($orphans);
13305 exit;
13306 }
13307
13308 // load availabilities
13309 $q = "SELECT `b`.*,`ob`.`idorder` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`idroom` IN (".implode(', ', array_keys($rooms)).") AND `b`.`id`=`ob`.`idbusy` AND (`b`.`checkin`>=".$today." OR `b`.`checkout`>=".$today.") AND (`b`.`checkin`<=".$until." OR `b`.`checkout`<=".$today.");";
13310 $dbo->setQuery($q);
13311 $dbo->execute();
13312 if (!$dbo->getNumRows()) {
13313 // no booked dates found, exit
13314 echo json_encode($orphans);
13315 exit;
13316 }
13317 $busy = $dbo->loadAssocList();
13318
13319 // sort booked dates by room id
13320 $rooms_busy = array();
13321 foreach ($busy as $b) {
13322 if (!isset($rooms_busy[$b['idroom']])) {
13323 $rooms_busy[$b['idroom']] = array();
13324 }
13325 array_push($rooms_busy[$b['idroom']], $b);
13326 }
13327
13328 // load restrictions
13329 $rooms_restr = array();
13330 foreach ($rooms as $rid => $r) {
13331 $restrictions = VikBooking::loadRestrictions(true, array($rid));
13332 if (count($restrictions)) {
13333 $rooms_restr[$rid] = $restrictions;
13334 }
13335 }
13336 if (!count($rooms_restr) && $glob_minlos < 2) {
13337 // no restrictions found and minlos=1, exit
13338 echo json_encode($orphans);
13339 exit;
13340 }
13341
13342 // count availability and minlos per day
13343 $rooms_data = array();
13344 foreach ($rooms as $rid => $r) {
13345 $rooms_data[$rid] = array(
13346 'avail' => array(),
13347 'restr' => array()
13348 );
13349 $nowts = getdate($today);
13350 while ($nowts[0] <= $until) {
13351 $dateind = date('Y-m-d', $nowts[0]);
13352
13353 // remaining availability
13354 if (!isset($rooms_busy[$rid])) {
13355 // no bookings for this room, set full availability for this day
13356 $rooms_data[$rid]['avail'][] = array(
13357 'dt' => $dateind,
13358 'units' => $r['units']
13359 );
13360 } else {
13361 // check remaining availability for this day
13362 $totfound = 0;
13363 foreach ($rooms_busy[$rid] as $b) {
13364 $tmpone = getdate($b['checkin']);
13365 $rit = ($tmpone['mon'] < 10 ? "0".$tmpone['mon'] : $tmpone['mon'])."/".($tmpone['mday'] < 10 ? "0".$tmpone['mday'] : $tmpone['mday'])."/".$tmpone['year'];
13366 $ritts = strtotime($rit);
13367 $tmptwo = getdate($b['checkout']);
13368 $con = ($tmptwo['mon'] < 10 ? "0".$tmptwo['mon'] : $tmptwo['mon'])."/".($tmptwo['mday'] < 10 ? "0".$tmptwo['mday'] : $tmptwo['mday'])."/".$tmptwo['year'];
13369 $conts = strtotime($con);
13370 if ($nowts[0] >= $ritts && $nowts[0] < $conts) {
13371 $totfound++;
13372 }
13373 }
13374 $totfound = $totfound > $r['units'] ? $r['units'] : $totfound;
13375 $rooms_data[$rid]['avail'][] = array(
13376 'dt' => $dateind,
13377 'units' => ($r['units'] - $totfound)
13378 );
13379 }
13380
13381 // restrictions
13382 if (!isset($rooms_restr[$rid])) {
13383 // no restrictions for this room, set global minlos for this day
13384 $rooms_data[$rid]['restr'][] = array(
13385 'dt' => $dateind,
13386 'minlos' => $glob_minlos
13387 );
13388 } else {
13389 // get restriction for this day
13390 $today_tsin = mktime(0, 0, 0, $nowts['mon'], $nowts['mday'], $nowts['year']);
13391 $today_tsout = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
13392
13393 $restr = VikBooking::parseSeasonRestrictions($today_tsin, $today_tsout, 1, $rooms_restr[$rid]);
13394 $minlos = count($restr) ? $restr['minlos'] : $glob_minlos;
13395
13396 $rooms_data[$rid]['restr'][] = array(
13397 'dt' => $dateind,
13398 'minlos' => $minlos
13399 );
13400 }
13401
13402 // next loop
13403 $dayts = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
13404 $nowts = getdate($dayts);
13405 }
13406 }
13407
13408 // week days and months labels
13409 $days_labels = array(
13410 JText::translate('VBSUNDAY'),
13411 JText::translate('VBMONDAY'),
13412 JText::translate('VBTUESDAY'),
13413 JText::translate('VBWEDNESDAY'),
13414 JText::translate('VBTHURSDAY'),
13415 JText::translate('VBFRIDAY'),
13416 JText::translate('VBSATURDAY')
13417 );
13418 $months_labels = array(
13419 JText::translate('VBMONTHONE'),
13420 JText::translate('VBMONTHTWO'),
13421 JText::translate('VBMONTHTHREE'),
13422 JText::translate('VBMONTHFOUR'),
13423 JText::translate('VBMONTHFIVE'),
13424 JText::translate('VBMONTHSIX'),
13425 JText::translate('VBMONTHSEVEN'),
13426 JText::translate('VBMONTHEIGHT'),
13427 JText::translate('VBMONTHNINE'),
13428 JText::translate('VBMONTHTEN'),
13429 JText::translate('VBMONTHELEVEN'),
13430 JText::translate('VBMONTHTWELVE')
13431 );
13432
13433 // orphan dates calculation method
13434 $calc_method = VikBooking::orphansCalculation();
13435
13436 // parse data and build orphans if any
13437 foreach ($rooms_data as $rid => $data) {
13438 foreach ($data['avail'] as $ind => $av) {
13439 if (!isset($data['restr'][$ind]) || $av['units'] < 1) {
13440 // continue, no restriction set or no availability for this day
13441 continue;
13442 }
13443 if ($data['restr'][$ind]['minlos'] < 2) {
13444 // continue, no min los > 1 set for this day
13445 continue;
13446 }
13447 // check if any night after today, until min los, is fully booked
13448 $hasorphans = false;
13449 $forward_count = 0;
13450 for ($i = 1; $i < $data['restr'][$ind]['minlos']; $i++) {
13451 if (!isset($data['avail'][($ind + $i)])) {
13452 // break loop, no info for this day after
13453 break;
13454 }
13455 if ($data['avail'][($ind + $i)]['units'] > 0) {
13456 // continue, availability found for tomorrow, we need a non available next-day
13457 continue;
13458 }
13459 // orphan found
13460 $hasorphans = true;
13461 $forward_count = $i;
13462 break;
13463 }
13464
13465 /**
13466 * Backward calculation method only if "prevnext".
13467 *
13468 * @since 1.3.0
13469 */
13470 $backward_count = 0;
13471 for ($i = 1; $i <= $data['restr'][$ind]['minlos']; $i++) {
13472 if (!isset($data['avail'][($ind - $i)])) {
13473 // break loop, no info for this prev day
13474 break;
13475 }
13476 if ($data['avail'][($ind - $i)]['units'] > 0) {
13477 // increase free nights going backward
13478 $backward_count++;
13479 }
13480 }
13481 if ($calc_method == 'prevnext' && $hasorphans && $backward_count > 0 && ($backward_count >= $data['restr'][$ind]['minlos'] || ($backward_count + $forward_count) >= $data['restr'][$ind]['minlos'])) {
13482 // this should not be an orphan date because of enough free days back, or enough free days in between
13483 $hasorphans = false;
13484 }
13485 //
13486
13487 if ($hasorphans) {
13488 // we pass the name of the room, the list of raw dates (Y-m-d), the list of readable dates, and the fist date with the VBO format
13489 if (!isset($orphans[$rid])) {
13490 $orphans[$rid] = array(
13491 'name' => $rooms[$rid]['name'],
13492 'dates' => array(),
13493 'rdates' => array(),
13494 'linkd' => date($df, strtotime($av['dt']))
13495 );
13496 }
13497 array_push($orphans[$rid]['dates'], $av['dt']);
13498 // build the value for the readable date
13499 $dtinfo = getdate(strtotime($av['dt']));
13500 $rdate = $days_labels[$dtinfo['wday']] . ', ' . $months_labels[($dtinfo['mon'] - 1)] . ' ' . $dtinfo['mday'] . ' ' . $dtinfo['year'];
13501 array_push($orphans[$rid]['rdates'], $rdate);
13502 }
13503 }
13504 }
13505
13506 // output response
13507 echo json_encode($orphans);
13508 exit;
13509 }
13510
13511 public function tableaux() {
13512 VikBookingHelper::printHeader("tableaux");
13513
13514 VikRequest::setVar('view', VikRequest::getCmd('view', 'tableaux'));
13515
13516 parent::display();
13517
13518 if (VikBooking::showFooter()) {
13519 VikBookingHelper::printFooter();
13520 }
13521 }
13522
13523 public function operators() {
13524 VikBookingHelper::printHeader("operators");
13525
13526 VikRequest::setVar('view', VikRequest::getCmd('view', 'operators'));
13527
13528 parent::display();
13529
13530 if (VikBooking::showFooter()) {
13531 VikBookingHelper::printFooter();
13532 }
13533 }
13534
13535 public function newoperator() {
13536 VikBookingHelper::printHeader("operators");
13537
13538 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoperator'));
13539
13540 parent::display();
13541
13542 if (VikBooking::showFooter()) {
13543 VikBookingHelper::printFooter();
13544 }
13545 }
13546
13547 public function editoperator() {
13548 VikBookingHelper::printHeader("operators");
13549
13550 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoperator'));
13551
13552 parent::display();
13553
13554 if (VikBooking::showFooter()) {
13555 VikBookingHelper::printFooter();
13556 }
13557 }
13558
13559 public function updateoperator()
13560 {
13561 if (!JSession::checkToken()) {
13562 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13563 }
13564
13565 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
13566 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13567 }
13568
13569 $this->do_updateoperator();
13570 }
13571
13572 public function updateoperatorstay()
13573 {
13574 if (!JSession::checkToken()) {
13575 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13576 }
13577
13578 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
13579 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13580 }
13581
13582 $this->do_updateoperator(true);
13583 }
13584
13585 private function do_updateoperator($stay = false)
13586 {
13587 $dbo = JFactory::getDbo();
13588 $app = JFactory::getApplication();
13589 $pfirst_name = VikRequest::getString('first_name', '', 'request');
13590 $plast_name = VikRequest::getString('last_name', '', 'request');
13591 $pemail = VikRequest::getString('email', '', 'request');
13592 $pphone = VikRequest::getString('phone', '', 'request');
13593 $pcode = VikRequest::getString('code', '', 'request');
13594 $pujid = VikRequest::getInt('ujid', '', 'request');
13595 $pwhere = VikRequest::getInt('where', '', 'request');
13596
13597 $work_days_week = (array) $app->input->get('work_days_week', [], 'array');
13598 $work_days_exceptions = (array) $app->input->get('work_days_exceptions', [], 'array');
13599
13600 // normalize to linear arrays
13601 $work_days_week_schedule = array_combine(array_keys($work_days_week), array_values($work_days_week));
13602 $work_days_week = [];
13603 foreach ($work_days_week_schedule as $wday => $whours) {
13604 $work_days_week[] = [
13605 'wday' => $wday,
13606 'hours' => $whours,
13607 ];
13608 }
13609 foreach ($work_days_exceptions as &$wexceptions) {
13610 if (is_scalar($wexceptions)) {
13611 $wexceptions = json_decode($wexceptions, true);
13612 }
13613 }
13614 unset($wexceptions);
13615
13616 if (!empty($pfirst_name) && !empty($pemail) && !empty($pcode)) {
13617 $q = "SELECT * FROM `#__vikbooking_operators` WHERE `id`=".(int)$pwhere." LIMIT 1;";
13618 $dbo->setQuery($q);
13619 $customer = $dbo->loadAssoc();
13620 if (!$customer) {
13621 $app->redirect("index.php?option=com_vikbooking&task=operators");
13622 exit;
13623 }
13624
13625 $q = "SELECT * FROM `#__vikbooking_operators` WHERE (`email`=".$dbo->quote($pemail)." OR ".(!empty($pcode) ? "`code`=".$dbo->quote($pcode) : "`ujid`=".$dbo->quote($pujid)).") AND `id`!=".(int)$pwhere." LIMIT 1;";
13626 $dbo->setQuery($q);
13627 $ex_operator = $dbo->loadAssoc();
13628 if (!$ex_operator) {
13629 // update fingerprint for the operator
13630 $fingpt = md5($pwhere . $pemail);
13631
13632 /**
13633 * Operator profile picture (URL or uploaded file).
13634 *
13635 * @since 1.16.9 (J) - 1.6.9 (WP)
13636 */
13637 $operator_pic = VikRequest::getString('pic', '', 'request');
13638 $operator_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
13639 if (is_array($operator_pic_img) && !empty($operator_pic_img['name'])) {
13640 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($operator_pic_img['name'])));
13641 $src = $operator_pic_img['tmp_name'];
13642 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
13643 $j = "";
13644 if (is_file($dest.$filename)) {
13645 $j = rand(1, 99999);
13646 while (is_file($dest . $j .$filename)) {
13647 $j++;
13648 }
13649 }
13650 $finaldest = $dest . $j . $filename;
13651 $check = getimagesize($operator_pic_img['tmp_name']);
13652 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
13653 if (VikBooking::uploadFile($src, $finaldest)) {
13654 $operator_pic = $j . $filename;
13655 } else {
13656 VikError::raiseWarning('', 'Error while uploading image');
13657 }
13658 } else {
13659 VikError::raiseWarning('', 'Uploaded file is not an Image');
13660 }
13661 }
13662
13663 // update record
13664 $q = "UPDATE `#__vikbooking_operators` SET `first_name`=" . $dbo->q($pfirst_name) . ",`last_name`=" . $dbo->q($plast_name) . ",`email`=" . $dbo->q($pemail) . ",`phone`=" . $dbo->q($pphone) . ",`code`=" . $dbo->q($pcode) . ",`ujid`=" . $dbo->q($pujid) . ",`fingpt`=" . $dbo->q($fingpt) . ",`pic`=" . $dbo->q($operator_pic) . ",`work_days_week`=" . ($work_days_week ? $dbo->q(json_encode($work_days_week)) : 'NULL') . ",`work_days_exceptions`=" . ($work_days_exceptions ? $dbo->q(json_encode($work_days_exceptions)) : 'NULL') . " WHERE `id`=" . (int)$pwhere;
13665 $dbo->setQuery($q);
13666 $dbo->execute();
13667 $app->enqueueMessage(JText::translate('VBOPERATORSAVED'));
13668 } else {
13669 //email already exists
13670 VikError::raiseWarning('', JText::translate('VBERROPERATOREXISTS').'<br/><a href="index.php?option=com_vikbooking&task=editoperator&cid[]='.$ex_operator['id'].'" target="_blank">'.$ex_operator['first_name'].' '.$ex_operator['last_name'].'</a>');
13671 $app->redirect("index.php?option=com_vikbooking&task=editoperator&cid[]=".$pwhere);
13672 exit;
13673 }
13674 } else {
13675 VikError::raiseWarning('', JText::translate('VBERROPERATORDATA'));
13676 }
13677
13678 if ($stay) {
13679 $app->redirect("index.php?option=com_vikbooking&task=editoperator&cid[]=".$pwhere);
13680 } else {
13681 $app->redirect("index.php?option=com_vikbooking&task=operators");
13682 }
13683
13684 $app->close();
13685 }
13686
13687 public function saveoperator()
13688 {
13689 if (!JSession::checkToken()) {
13690 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13691 }
13692
13693 $dbo = JFactory::getDbo();
13694 $app = JFactory::getApplication();
13695 $pfirst_name = VikRequest::getString('first_name', '', 'request');
13696 $plast_name = VikRequest::getString('last_name', '', 'request');
13697 $pemail = VikRequest::getString('email', '', 'request');
13698 $pphone = VikRequest::getString('phone', '', 'request');
13699 $pcode = VikRequest::getString('code', '', 'request');
13700 $pujid = VikRequest::getInt('ujid', '', 'request');
13701
13702 $work_days_week = (array) $app->input->get('work_days_week', [], 'array');
13703 $work_days_exceptions = (array) $app->input->get('work_days_exceptions', [], 'array');
13704
13705 // normalize to linear arrays
13706 $work_days_week_schedule = array_combine(array_keys($work_days_week), array_values($work_days_week));
13707 $work_days_week = [];
13708 foreach ($work_days_week_schedule as $wday => $whours) {
13709 $work_days_week[] = [
13710 'wday' => $wday,
13711 'hours' => $whours,
13712 ];
13713 }
13714 foreach ($work_days_exceptions as &$wexceptions) {
13715 if (is_scalar($wexceptions)) {
13716 $wexceptions = json_decode($wexceptions, true);
13717 }
13718 }
13719 unset($wexceptions);
13720
13721 if (!empty($pfirst_name) && !empty($pemail) && !empty($pcode)) {
13722 $q = "SELECT * FROM `#__vikbooking_operators` WHERE `email`=".$dbo->quote($pemail)." OR ".(!empty($pcode) ? "`code`=".$dbo->quote($pcode) : "`ujid`=".$dbo->quote($pujid))." LIMIT 1;";
13723 $dbo->setQuery($q);
13724 $ex_operator = $dbo->loadAssoc();
13725 if (!$ex_operator) {
13726 /**
13727 * Operator profile picture (URL or uploaded file).
13728 *
13729 * @since 1.16.9 (J) - 1.6.9 (WP)
13730 */
13731 $operator_pic = VikRequest::getString('pic', '', 'request');
13732 $operator_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
13733 if (is_array($operator_pic_img) && !empty($operator_pic_img['name'])) {
13734 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($operator_pic_img['name'])));
13735 $src = $operator_pic_img['tmp_name'];
13736 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
13737 $j = "";
13738 if (is_file($dest.$filename)) {
13739 $j = rand(1, 99999);
13740 while (is_file($dest . $j .$filename)) {
13741 $j++;
13742 }
13743 }
13744 $finaldest = $dest . $j . $filename;
13745 $check = getimagesize($operator_pic_img['tmp_name']);
13746 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
13747 if (VikBooking::uploadFile($src, $finaldest)) {
13748 $operator_pic = $j . $filename;
13749 } else {
13750 VikError::raiseWarning('', 'Error while uploading image');
13751 }
13752 } else {
13753 VikError::raiseWarning('', 'Uploaded file is not an Image');
13754 }
13755 }
13756
13757 $q = "INSERT INTO `#__vikbooking_operators` (`first_name`,`last_name`,`email`,`phone`,`code`,`ujid`,`pic`,`work_days_week`,`work_days_exceptions`) VALUES(" . $dbo->q($pfirst_name) . ", " . $dbo->q($plast_name) . ", " . $dbo->q($pemail) . ", " . $dbo->q($pphone) . ", " . $dbo->q($pcode) . ", " . $dbo->q($pujid) . ", " . $dbo->q($operator_pic) . ", " . ($work_days_week ? $dbo->q(json_encode($work_days_week)) : 'NULL') . ", " . ($work_days_exceptions ? $dbo->q(json_encode($work_days_exceptions)) : 'NULL') . ");";
13758 $dbo->setQuery($q);
13759 $dbo->execute();
13760 $lid = $dbo->insertid();
13761 if (!empty($lid)) {
13762 $app->enqueueMessage(JText::translate('VBOPERATORSAVED'));
13763 // generate fingerprint for the operator
13764 $q = "UPDATE `#__vikbooking_operators` SET `fingpt`=".$dbo->q(md5($lid.$pemail))." WHERE `id`=".(int)$lid.";";
13765 $dbo->setQuery($q);
13766 $dbo->execute();
13767 }
13768 } else {
13769 // email already exists
13770 VikError::raiseWarning('', JText::translate('VBERROPERATOREXISTS').'<br/><a href="index.php?option=com_vikbooking&task=editoperator&cid[]='.$ex_operator['id'].'" target="_blank">'.$ex_operator['first_name'].' '.$ex_operator['last_name'].'</a>');
13771 }
13772 } else {
13773 VikError::raiseWarning('', JText::translate('VBERROPERATORDATA'));
13774 }
13775
13776 $app->redirect("index.php?option=com_vikbooking&task=operators");
13777 $app->close();
13778 }
13779
13780 public function removeoperators()
13781 {
13782 if (!JSession::checkToken()) {
13783 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13784 }
13785
13786 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
13787 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13788 }
13789
13790 $ids = VikRequest::getVar('cid', array(0));
13791 if ($ids) {
13792 $dbo = JFactory::getDBO();
13793 foreach ($ids as $d) {
13794 $q = "DELETE FROM `#__vikbooking_operators` WHERE `id`=".(int)$d.";";
13795 $dbo->setQuery($q);
13796 $dbo->execute();
13797 }
13798 }
13799 $mainframe = JFactory::getApplication();
13800 $mainframe->redirect("index.php?option=com_vikbooking&task=operators");
13801 }
13802
13803 public function canceloperator() {
13804 $mainframe = JFactory::getApplication();
13805 $mainframe->redirect("index.php?option=com_vikbooking&task=operators");
13806 }
13807
13808 public function cancelcrons() {
13809 $mainframe = JFactory::getApplication();
13810 $mainframe->redirect("index.php?option=com_vikbooking&task=crons");
13811 }
13812
13813 public function cancelpackages() {
13814 $mainframe = JFactory::getApplication();
13815 $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
13816 }
13817
13818 public function cancelcustomer() {
13819 $mainframe = JFactory::getApplication();
13820 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
13821 if (!empty($pgoto)) {
13822 $mainframe->redirect(base64_decode($pgoto));
13823 exit;
13824 }
13825 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
13826 }
13827
13828 public function cancelbusyvcm() {
13829 $mainframe = JFactory::getApplication();
13830 $mainframe->redirect("index.php?option=com_vikchannelmanager&task=oversight");
13831 }
13832
13833 public function cancelrestriction() {
13834 $mainframe = JFactory::getApplication();
13835 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
13836 }
13837
13838 public function cancelcoupon() {
13839 $mainframe = JFactory::getApplication();
13840 $mainframe->redirect("index.php?option=com_vikbooking&task=coupons");
13841 }
13842
13843 public function cancelcustomf() {
13844 $mainframe = JFactory::getApplication();
13845 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
13846 }
13847
13848 public function cancelpayment() {
13849 $mainframe = JFactory::getApplication();
13850 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
13851 }
13852
13853 public function cancelseason() {
13854 $mainframe = JFactory::getApplication();
13855 $mainframe->redirect("index.php?option=com_vikbooking&task=seasons");
13856 }
13857
13858 public function goconfig() {
13859 $mainframe = JFactory::getApplication();
13860 $mainframe->redirect("index.php?option=com_vikbooking&task=config");
13861 }
13862
13863 public function canceledorder() {
13864 $pgoto = VikRequest::getString('goto', 'orders', 'request');
13865 $mainframe = JFactory::getApplication();
13866 $mainframe->redirect("index.php?option=com_vikbooking&task=" . $pgoto);
13867 }
13868
13869 public function cancelbusy() {
13870 $pidorder = VikRequest::getString('idorder', '', 'request');
13871 $pgoto = VikRequest::getString('goto', '', 'request');
13872 $mainframe = JFactory::getApplication();
13873 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pidorder.($pgoto == 'overv' ? '&goto=overv' : ''));
13874 }
13875
13876 public function canceloverv() {
13877 $mainframe = JFactory::getApplication();
13878 $mainframe->redirect("index.php?option=com_vikbooking&task=overv");
13879 }
13880
13881 public function canceltableaux() {
13882 $mainframe = JFactory::getApplication();
13883 $mainframe->redirect("index.php?option=com_vikbooking&task=tableaux");
13884 }
13885
13886 public function cancelcalendar() {
13887 $pidroom = VikRequest::getString('idroom', '', 'request');
13888 $mainframe = JFactory::getApplication();
13889 $mainframe->redirect("index.php?option=com_vikbooking&task=calendar&cid[]=".$pidroom);
13890 }
13891
13892 public function canceloptionals() {
13893 $mainframe = JFactory::getApplication();
13894 $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
13895 }
13896
13897 public function cancel() {
13898 $mainframe = JFactory::getApplication();
13899 $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
13900 }
13901
13902 public function cancelcarat() {
13903 $mainframe = JFactory::getApplication();
13904 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
13905 }
13906
13907 public function cancelcat() {
13908 $mainframe = JFactory::getApplication();
13909 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
13910 }
13911
13912 public function cancelprice() {
13913 $mainframe = JFactory::getApplication();
13914 $mainframe->redirect("index.php?option=com_vikbooking&task=prices");
13915 }
13916
13917 public function canceliva() {
13918 $mainframe = JFactory::getApplication();
13919 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
13920 }
13921
13922 public function canceltrk() {
13923 $mainframe = JFactory::getApplication();
13924 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13925 }
13926
13927 public function canceldash() {
13928 $mainframe = JFactory::getApplication();
13929 $mainframe->redirect("index.php?option=com_vikbooking");
13930 }
13931
13932 public function cancelinvoice() {
13933 $mainframe = JFactory::getApplication();
13934 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
13935 if (!empty($pgoto)) {
13936 $mainframe->redirect(base64_decode($pgoto));
13937 exit;
13938 }
13939 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
13940 }
13941
13942 /**
13943 * AJAX upload the customer documents.
13944 *
13945 * @return void
13946 *
13947 * @throws Exception
13948 */
13949 public function upload_customer_document()
13950 {
13951 $input = JFactory::getApplication()->input;
13952 $dbo = JFactory::getDbo();
13953
13954 $customer_id = $input->getUint('customer', 0);
13955
13956 $result = new stdClass;
13957 $result->status = 0;
13958
13959 try
13960 {
13961 $q = $dbo->getQuery(true)
13962 ->select($dbo->qn(array(
13963 'id',
13964 'first_name',
13965 'last_name',
13966 'email',
13967 'docsfolder',
13968 )))
13969 ->from($dbo->qn('#__vikbooking_customers'))
13970 ->where($dbo->qn('id') . ' = ' . $customer_id);
13971
13972 $dbo->setQuery($q, 0, 1);
13973 $dbo->execute();
13974
13975 if (!$dbo->getNumRows())
13976 {
13977 throw new Exception(sprintf('Customer [%d] not found', $customer_id), 404);
13978 }
13979
13980 $customer = $dbo->loadObject();
13981
13982 // fetch documents folder path
13983 $dirpath = VBO_CUSTOMERS_PATH . DIRECTORY_SEPARATOR;
13984
13985 // check if we have a valid directory
13986 if (empty($customer->docsfolder) || !is_dir($dirpath . $customer->docsfolder))
13987 {
13988 // randomize string
13989 $customer->seed = uniqid();
13990
13991 // create blocks for hashed folder
13992 $parts = [
13993 $customer->first_name,
13994 $customer->last_name,
13995 md5(serialize($customer)),
13996 ];
13997
13998 // join fetched parts
13999 $customer->docsfolder = JFilterOutput::stringURLSafe(implode('-', array_filter($parts)));
14000
14001 if (strlen($customer->docsfolder) < 16)
14002 {
14003 throw new Exception('Possible security breach. Please specify the most details as possible.', 400);
14004 }
14005
14006 jimport('joomla.filesystem.folder');
14007
14008 // create a folder for this customer
14009 $created = JFolder::create($dirpath . $customer->docsfolder);
14010
14011 if (!$created)
14012 {
14013 throw new Exception(sprintf('Unable to create the folder [%s]', $dirpath . $customer->docsfolder), 403);
14014 }
14015
14016 unset($customer->seed);
14017
14018 // update docs folder
14019 $dbo->updateObject('#__vikbooking_customers', $customer, 'id');
14020 }
14021
14022 // get file from request
14023 $file = $input->files->get('file', array(), 'array');
14024
14025 // try to upload the file
14026 $result = VikBooking::uploadFileFromRequest($file, $dirpath . $customer->docsfolder, 'png,jpg,jpeg,bmp,heic,zip,rar,pdf,doc,docx,rtf,odt,pages,xls,xlsx,csv,ods,numbers,txt,md');
14027 $result->status = 1;
14028
14029 $result->size = JHtml::fetch('number.bytes', filesize($result->path), 'auto', 0);
14030 $result->url = str_replace(DIRECTORY_SEPARATOR, '/', str_replace(VBO_CUSTOMERS_PATH . DIRECTORY_SEPARATOR, VBO_CUSTOMERS_URI, $result->path));
14031 }
14032 catch (Exception $e)
14033 {
14034 $result->error = $e->getMessage();
14035 $result->code = $e->getCode();
14036 }
14037
14038 echo json_encode($result);
14039 exit;
14040 }
14041
14042 /**
14043 * AJAX delete the customer documents.
14044 *
14045 * @return void
14046 *
14047 * @throws Exception
14048 */
14049 public function delete_customer_document()
14050 {
14051 $input = JFactory::getApplication()->input;
14052 $dbo = JFactory::getDbo();
14053
14054 $customer_id = $input->getUint('customer', 0);
14055
14056 $result = new stdClass;
14057 $result->status = 0;
14058
14059 $q = $dbo->getQuery(true)
14060 ->select($dbo->qn('docsfolder'))
14061 ->from($dbo->qn('#__vikbooking_customers'))
14062 ->where($dbo->qn('id') . ' = ' . $customer_id);
14063
14064 $dbo->setQuery($q, 0, 1);
14065 $dbo->execute();
14066
14067 if (!$dbo->getNumRows())
14068 {
14069 throw new Exception(sprintf('Customer [%d] not found', $customer_id), 404);
14070 }
14071
14072 $folder = $dbo->loadResult();
14073
14074 if (!$folder)
14075 {
14076 throw new Exception('The customer does not have any documents', 500);
14077 }
14078
14079 $file = $input->getString('file');
14080
14081 if (!$file)
14082 {
14083 throw new Exception('File to remove not specified', 400);
14084 }
14085
14086 $path = implode(DIRECTORY_SEPARATOR, array(VBO_CUSTOMERS_PATH, $folder, $file));
14087
14088 if (!is_file($path))
14089 {
14090 throw new Exception(sprintf('File [%s] not found', $path), 404);
14091 }
14092
14093 jimport('joomla.filesystem.file');
14094
14095 $removed = JFile::delete($path);
14096
14097 echo json_encode(array('status' => (int) $removed));
14098 exit;
14099 }
14100
14101 /**
14102 * AJAX task to invoke a specific report and obtain information.
14103 *
14104 * @since 1.3.0
14105 */
14106 public function get_report_data()
14107 {
14108 $report_name = VikRequest::getString('report_name', '', 'request');
14109 $current_fest = VikRequest::getString('current_fest', '', 'request');
14110 $current_fromdate = VikRequest::getString('current_fromdate', '', 'request');
14111 $current_todate = VikRequest::getString('current_todate', '', 'request');
14112 $step = VikRequest::getString('step', 'weekend', 'request');
14113 $direction = VikRequest::getString('direction', 'load', 'request');
14114 $period = VikRequest::getString('period', 'full', 'request');
14115 $krsort = VikRequest::getString('krsort', 'occupancy', 'request');
14116 $krorder = VikRequest::getString('krorder', 'DESC', 'request');
14117 $chart_datatype = VikRequest::getVar('chart_datatype', array(), 'request');
14118 $chart_meta_data = VikRequest::getString('chart_meta_data', '', 'request', VIKREQUEST_ALLOWRAW);
14119 $chart_meta_data = !empty($chart_meta_data) ? json_decode($chart_meta_data, true) : array();
14120 // idroom can be an array of IDs or just one ID as int/string
14121 $idroom = VikRequest::getVar('idroom', null, 'request');
14122 //
14123
14124 if (empty($report_name) || empty($current_fromdate) || empty($current_todate)) {
14125 throw new Exception("Missing request data", 400);
14126 }
14127
14128 // get requested report instance
14129 $report = VikBooking::getReportInstance($report_name);
14130 if (!$report) {
14131 throw new Exception("Report not found", 404);
14132 }
14133
14134 // chart data
14135 if (empty($chart_datatype)) {
14136 $chart_datatype = array(
14137 'type' => 'doughnut',
14138 'depth' => 1,
14139 'keys' => array($krsort),
14140 );
14141 }
14142
14143 // website date format
14144 $df = $report->getDateFormat();
14145
14146 // prepare request params for the report
14147 $rparams = array(
14148 'fromdate' => $current_fromdate,
14149 'todate' => $current_todate,
14150 'period' => $period,
14151 'krsort' => $krsort,
14152 'krorder' => $krorder,
14153 'idroom' => $idroom,
14154 );
14155
14156 // starting dates info and timestamps
14157 $from_ts = VikBooking::getDateTimestamp($current_fromdate, 0, 0, 0);
14158 $to_ts = VikBooking::getDateTimestamp($current_todate, 23, 59, 59);
14159 $from_info = getdate($from_ts);
14160 $to_info = getdate($to_ts);
14161
14162 // the name of the period requested and whether it's a fest
14163 $period_name = '';
14164 $is_fest = null;
14165
14166 if ($direction == 'prev' || $direction == 'next') {
14167 // calculate prev or next dates
14168 if ($step == 'weekend') {
14169 $period_name = JText::translate('VBOWEEKND');
14170 if ($direction == 'next') {
14171 // next weekend from current end date
14172 $next_ts = strtotime("next friday", $to_ts);
14173 } else {
14174 // prev weekend from current start date
14175 $next_ts = strtotime("previous friday", $from_ts);
14176 }
14177 $next_info = getdate($next_ts);
14178 $new_from_ts = $next_ts;
14179 $new_to_ts = mktime(23, 59, 59, $next_info['mon'], ($next_info['mday'] + 1), $next_info['year']);
14180 $rparams['fromdate'] = date($df, $new_from_ts);
14181 $rparams['todate'] = date($df, $new_to_ts);
14182 } elseif ($step == 'week') {
14183 $period_name = JText::translate('VBOWEEK');
14184 if ($direction == 'next') {
14185 // start next week from the current end date
14186 $new_from_ts = $to_ts;
14187 $new_to_ts = mktime(23, 59, 59, $to_info['mon'], ($to_info['mday'] + 7), $to_info['year']);
14188 $rparams['fromdate'] = $rparams['todate'];
14189 $rparams['todate'] = date($df, $new_to_ts);
14190 } else {
14191 // end prev week from the current from date
14192 $new_from_ts = mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] - 7), $from_info['year']);
14193 $new_to_ts = $from_ts;
14194 $rparams['todate'] = $rparams['fromdate'];
14195 $rparams['fromdate'] = date($df, $new_from_ts);
14196 }
14197 } else {
14198 // month
14199 $period_name = JText::translate('VBPVIEWRESTRICTIONSTWO');
14200 if ($direction == 'next') {
14201 // next month from the current from date
14202 $nextmonts = mktime(0, 0, 0, ($from_info['mon'] + 1), 1, $from_info['year']);
14203 $new_from_ts = $nextmonts;
14204 $new_to_ts = mktime(23, 59, 59, ($from_info['mon'] + 1), date('t', $nextmonts), $from_info['year']);
14205 $rparams['fromdate'] = date($df, $new_from_ts);
14206 $rparams['todate'] = date($df, $new_to_ts);
14207 } else {
14208 // prev month from the current from date
14209 $nextmonts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']);
14210 $new_from_ts = $nextmonts;
14211 $new_to_ts = mktime(23, 59, 59, ($from_info['mon'] - 1), date('t', $nextmonts), $from_info['year']);
14212 $rparams['fromdate'] = date($df, $new_from_ts);
14213 $rparams['todate'] = date($df, $new_to_ts);
14214 }
14215 }
14216
14217 // get the next festivities
14218 $fests = VikBooking::getFestivitiesInstance();
14219 $next_fests = $fests->loadFestDates();
14220 if (count($next_fests)) {
14221 // check whether a festivity should be displayed rather than the calculated period of dates
14222 foreach ($next_fests as $fest) {
14223 $fest_found = false;
14224 if ($direction == 'next' && $fest['festinfo'][0]->from_ts > $from_ts && $fest['festinfo'][0]->from_ts <= $new_to_ts) {
14225 $fest_found = true;
14226 } elseif ($direction == 'prev' && $fest['festinfo'][0]->from_ts < $to_ts && $fest['festinfo'][0]->from_ts >= $new_from_ts) {
14227 $fest_found = true;
14228 }
14229 if ($fest_found && (string)$fest['festinfo'][0]->next_ts != $current_fest) {
14230 // festivity found before next calculated period
14231 $is_fest = $fest['festinfo'][0]->next_ts;
14232 $period_name = $fest['festinfo'][0]->trans_name;
14233 $new_from_ts = $fest['festinfo'][0]->from_ts;
14234 $new_to_ts = $fest['festinfo'][0]->to_ts;
14235 $rparams['fromdate'] = date($df, $new_from_ts);
14236 $rparams['todate'] = date($df, $new_to_ts);
14237 break;
14238 }
14239 }
14240 }
14241 } else {
14242 // load requested dates by skipping the festivities
14243 $new_from_ts = $from_ts;
14244 $new_to_ts = $to_ts;
14245 }
14246
14247 // invoke report
14248 $report->injectParams($rparams);
14249 $report_values = $report->getReportValues(1);
14250 $report_cols = $report->getColumnsValues();
14251 $report_chart = null;
14252 $report_chart_metas = array();
14253 $chart_meta_data = array(
14254 'keys' => array(
14255 'occupancy',
14256 'tot_bookings',
14257 'nights_booked',
14258 ),
14259 );
14260 $error = null;
14261
14262 if (!count($report_values)) {
14263 $error = strlen($report->getError()) ? $report->getError() : JText::translate('VBNOTRACKINGS');
14264 } else {
14265 // get doughnut Chart for the requested key
14266 $report_chart = $report->getChart((array) $chart_datatype);
14267
14268 // get Chart meta data
14269 $all_chart_metas = $report->getChartMetaData(null, $chart_meta_data);
14270 if (count($all_chart_metas)) {
14271 // merge all positions into one array
14272 foreach ($all_chart_metas as $pos_metas) {
14273 $report_chart_metas = array_merge($report_chart_metas, $pos_metas);
14274 }
14275 }
14276
14277 if (empty($period_name)) {
14278 $period_name = $report->getProperty('chartTitle');
14279 }
14280 }
14281
14282 // build response
14283 $response = new stdClass;
14284 $response->error = $error;
14285 $response->fromdate = $rparams['fromdate'];
14286 $response->todate = $rparams['todate'];
14287 $response->in_days = $report->countDaysTo($new_from_ts);
14288 $response->in_days_to = $report->countDaysTo($new_to_ts);
14289 $response->in_days_avg = $report->countAverageDays($response->in_days, $response->in_days_to);
14290 $response->period_name = $period_name;
14291 $response->period_date = count($report_values) && isset($report_values['day']) ? $report_values['day']['display_value'] : '';
14292 $response->is_fest = $is_fest;
14293 $response->report_chart = $report_chart;
14294 $response->report_cols = $report_cols;
14295 $response->report_values = $report_values;
14296 $response->report_script = $report->getScript();
14297 $response->chart_labels = $report->getProperty('chartJsLabels');
14298 $response->dataset_label = $report->getProperty('chartJsDataSetLabel');
14299 $response->chart_colors = $report->getProperty('chartJsColors');
14300 $response->chart_data = $report->getProperty('chartJsData');
14301 $response->report_chart_metas = $report_chart_metas;
14302
14303 echo json_encode($response);
14304 exit;
14305 }
14306
14307 /**
14308 * Go to the previous booking.
14309 *
14310 * @uses navigateToBooking()
14311 *
14312 * @since 1.3.0
14313 */
14314 public function prev_booking()
14315 {
14316 $this->navigateToBooking('prev');
14317 }
14318
14319 /**
14320 * Go to the next booking.
14321 *
14322 * @uses navigateToBooking()
14323 *
14324 * @since 1.3.0
14325 */
14326 public function next_booking()
14327 {
14328 $this->navigateToBooking('next');
14329 }
14330
14331 /**
14332 * Given the current booking ID in the request, we navigate
14333 * either to the next or to the previous reservation (if any).
14334 *
14335 * @param string $direction either next or prev.
14336 *
14337 * @return void
14338 *
14339 * @since 1.3.0
14340 */
14341 private function navigateToBooking($direction = 'next')
14342 {
14343 $bid = VikRequest::getInt('whereup', 0, 'request');
14344 if (empty($bid) || $bid < 1 || !in_array($direction, array('prev', 'next'))) {
14345 throw new Exception("Invalid request", 400);
14346 }
14347
14348 $dbo = JFactory::getDbo();
14349 $app = JFactory::getApplication();
14350
14351 $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `id`" . ($direction == 'next' ? '>' : '<') . "{$bid} ORDER BY `id` " . ($direction == 'next' ? 'ASC' : 'DESC');
14352 $dbo->setQuery($q, 0, 1);
14353 $dbo->execute();
14354 if (!$dbo->getNumRows()) {
14355 VikError::raiseWarning('', JText::translate('VBPEDITBUSYONE'));
14356 $app->redirect("index.php?option=com_vikbooking&task=orders");
14357 exit;
14358 }
14359
14360 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $dbo->loadResult());
14361 exit;
14362 }
14363
14364 /**
14365 * AJAX request: from a list of reservation IDs, we return the ones
14366 * that have a review with the related review ID on VCM.
14367 *
14368 * @since 1.13
14369 */
14370 public function bookings_have_reviews()
14371 {
14372 if (!JSession::checkToken()) {
14373 // missing CSRF-proof token
14374 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14375 }
14376
14377 $dbo = JFactory::getDbo();
14378
14379 $bids = VikRequest::getVar('bids', [], 'request', 'array');
14380 $vcm_installed = class_exists('VikChannelManager');
14381 $withreviews = [];
14382
14383 if ($vcm_installed && $bids) {
14384 $bids = array_filter(array_map('intval', (array) $bids));
14385 $bids = $bids ?: [0];
14386
14387 try {
14388 $q = "SELECT `id`, `idorder` FROM `#__vikchannelmanager_otareviews` WHERE `idorder` IN (" . implode(', ', $bids) . ");";
14389 $dbo->setQuery($q);
14390 $reviews = $dbo->loadAssocList();
14391
14392 foreach ($reviews as $r) {
14393 $withreviews[$r['idorder']] = $r['id'];
14394 }
14395 } catch (Exception $e) {
14396 // do nothing, outdated version
14397 }
14398 }
14399
14400 // output list of booking IDs found, if any
14401 VBOHttpDocument::getInstance()->json($withreviews);
14402 }
14403
14404 /**
14405 * AJAX request for adding a new room-day note.
14406 *
14407 * @return void
14408 *
14409 * @since 1.13.5
14410 */
14411 public function add_roomdaynote()
14412 {
14413 $dt = VikRequest::getString('dt', '', 'request');
14414 $idroom = VikRequest::getInt('idroom', 0, 'request');
14415 $subunit = VikRequest::getInt('subunit', 0, 'request');
14416 $type = VikRequest::getString('type', '', 'request');
14417 $type = empty($type) ? 'custom' : $type;
14418 $name = VikRequest::getString('name', '', 'request');
14419 $descr = VikRequest::getString('descr', '', 'request');
14420 $cdays = VikRequest::getInt('cdays', 0, 'request');
14421 $cdays = $cdays < 0 ? 0 : $cdays;
14422 $cdays = $cdays > 365 ? 365 : $cdays;
14423 if (empty($idroom) || empty($dt) || !strtotime($dt)) {
14424 echo 'e4j.error.1';
14425 exit;
14426 }
14427
14428 // reload end date
14429 $end_date = $dt;
14430
14431 // build critical date object
14432 $new_note = array(
14433 'name' => $name,
14434 'type' => $type,
14435 'descr' => $descr,
14436 );
14437
14438 // get object
14439 $notes = VikBooking::getCriticalDatesInstance();
14440
14441 // store the notes for all consecutive dates
14442 for ($i = 0; $i <= $cdays; $i++) {
14443 $store_dt = $dt;
14444 if ($i > 0) {
14445 $dt_info = getdate(strtotime($store_dt));
14446 $store_dt = date('Y-m-d', mktime(0, 0, 0, $dt_info['mon'], ($dt_info['mday'] + $i), $dt_info['year']));
14447 $end_date = $store_dt;
14448 }
14449 $result = $notes->storeDayNote($new_note, $store_dt, $idroom, $subunit);
14450 if (!$result) {
14451 echo 'e4j.error.2';
14452 exit;
14453 }
14454 }
14455
14456 // reload all room day notes for this day for the AJAX response
14457 $all_notes = $notes->loadRoomDayNotes($dt, $end_date, $idroom, $subunit);
14458
14459 if (!$all_notes || !count($all_notes)) {
14460 // no notes found even after storing it
14461 echo 'e4j.error.3';
14462 exit;
14463 }
14464
14465 echo json_encode($all_notes);
14466 exit;
14467 }
14468
14469 /**
14470 * AJAX request for removing a room day note.
14471 *
14472 * @return void
14473 *
14474 * @since 1.13.5
14475 */
14476 public function remove_roomdaynote()
14477 {
14478 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
14479 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14480 }
14481
14482 $dt = VikRequest::getString('dt', '', 'request');
14483 $idroom = VikRequest::getInt('idroom', 0, 'request');
14484 $subunit = VikRequest::getInt('subunit', 0, 'request');
14485 $type = VikRequest::getString('type', '', 'request');
14486 $type = empty($type) ? 'custom' : $type;
14487 $ind = VikRequest::getInt('ind', 0, 'request');
14488 if (empty($dt) || !strtotime($dt)) {
14489 echo 'e4j.error.1';
14490 exit;
14491 }
14492
14493 $notes = VikBooking::getCriticalDatesInstance();
14494 $result = $notes->deleteDayNote($ind, $dt, $idroom, $subunit, $type);
14495 if (!$result) {
14496 echo 'e4j.error.2';
14497 exit;
14498 }
14499
14500 echo 'e4j.ok';
14501 exit;
14502 }
14503
14504 /**
14505 * AJAX request for storing an event for a booking.
14506 * Firstly developed for the VCM Reporting API - Guest Misconduct,
14507 * but it can be used for any other purpose.
14508 *
14509 * @return void
14510 *
14511 * @since 1.13.5
14512 */
14513 public function store_booking_history_event()
14514 {
14515 $bid = VikRequest::getInt('bid', 0, 'request');
14516 $event = VikRequest::getString('event', '', 'request');
14517 $descr = VikRequest::getString('descr', '', 'request');
14518
14519 if (empty($bid) || empty($event)) {
14520 throw new Exception("Missing required information", 500);
14521 }
14522
14523 // Booking History
14524 VikBooking::getBookingHistoryInstance()->setBid($bid)->store($event, $descr);
14525 //
14526
14527 echo 'e4j.ok';
14528 exit;
14529 }
14530
14531 /**
14532 * AJAX request for updating an option/extra service.
14533 * Firstly developed for the VCM Vacation Rentals Essentials API - Damage Deposit,
14534 * but it can be used for any other purpose.
14535 *
14536 * @return void
14537 *
14538 * @since 1.13.5
14539 */
14540 public function update_option_params()
14541 {
14542 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
14543 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14544 }
14545
14546 $optid = VikRequest::getInt('optid', 0, 'request');
14547 $oparams = VikRequest::getVar('oparams', array(), 'request', 'array');
14548
14549 if (empty($optid) || !is_array($oparams) || empty($oparams)) {
14550 throw new Exception("Missing required information", 500);
14551 }
14552
14553 $dbo = JFactory::getDbo();
14554 $q = "SELECT `oparams` FROM `#__vikbooking_optionals` WHERE `id`=" . (int)$optid . ";";
14555 $dbo->setQuery($q);
14556 $dbo->execute();
14557 if (!$dbo->getNumRows()) {
14558 throw new Exception("Option not found", 404);
14559 }
14560 $cur_params = $dbo->loadResult();
14561 $cur_params = !empty($cur_params) ? json_decode($cur_params, true) : array();
14562 $cur_params = !is_array($cur_params) ? array() : $cur_params;
14563
14564 foreach ($oparams as $k => $v) {
14565 if (empty($k)) {
14566 continue;
14567 }
14568 $cur_params[$k] = $v;
14569 }
14570
14571 $q = "UPDATE `#__vikbooking_optionals` SET `oparams`=" . $dbo->quote(json_encode($cur_params)) ." WHERE `id`=" . (int)$optid . ";";
14572 $dbo->setQuery($q);
14573 $dbo->execute();
14574
14575 echo 'e4j.ok';
14576 exit;
14577 }
14578
14579 /**
14580 * Hidden task to clean up duplicate records in certain database tables
14581 * due to a double execution of the installation queries. Ghost records,
14582 * if any, are also removed to clean up issues with hanging records.
14583 *
14584 * @since November 4th 2020
14585 * @since 1.16.3 (J) - 1.6.3 (WP)
14586 */
14587 public function clean_duplicate_records()
14588 {
14589 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14590 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14591 }
14592
14593 $dbo = JFactory::getDbo();
14594
14595 $tables_with_duplicates = [
14596 '#__vikbooking_config' => [
14597 'id_key' => 'id',
14598 'compare_key' => 'param',
14599 ],
14600 '#__vikbooking_countries' => [
14601 'id_key' => 'id',
14602 'compare_key' => 'country_3_code',
14603 ],
14604 '#__vikbooking_custfields' => [
14605 'id_key' => 'id',
14606 'compare_key' => 'name',
14607 ],
14608 '#__vikbooking_texts' => [
14609 'id_key' => 'id',
14610 'compare_key' => 'param',
14611 ],
14612 ];
14613
14614 foreach ($tables_with_duplicates as $tblname => $data) {
14615 $doubles = [];
14616 $storage = [];
14617 $rmlist = [];
14618
14619 $q = "SELECT * FROM `{$tblname}` ORDER BY `{$data['id_key']}` DESC;";
14620 $dbo->setQuery($q);
14621 $rows = $dbo->loadAssocList();
14622 if (!$rows) {
14623 echo "<p>No records found in table {$tblname}</p>";
14624 continue;
14625 }
14626
14627 foreach ($rows as $row) {
14628 if (!isset($doubles[$row[$data['compare_key']]])) {
14629 $doubles[$row[$data['compare_key']]] = 0;
14630 }
14631 $doubles[$row[$data['compare_key']]]++;
14632 if (!isset($storage[$row[$data['compare_key']]])) {
14633 $storage[$row[$data['compare_key']]] = [];
14634 }
14635 array_push($storage[$row[$data['compare_key']]], $row[$data['id_key']]);
14636 }
14637
14638 foreach ($doubles as $paramkey => $paramcount) {
14639 if ($paramcount < 2 || !isset($storage[$paramkey]) || count($storage[$paramkey]) < 2 || $paramcount != count($storage[$paramkey])) {
14640 continue;
14641 }
14642 $exceeding = $paramcount - 1;
14643 for ($x = 0; $x < $exceeding; $x++) {
14644 array_push($rmlist, $storage[$paramkey][$x]);
14645 }
14646 }
14647
14648 echo "<p>Total records found in table {$tblname}: " . count($rows) . "</p>";
14649 echo '<p>Total records to remove: ' . count($rmlist) . '</p>';
14650 echo '<pre style="display: none;">'.print_r($rmlist, true).'</pre><br/>';
14651
14652 if (count($rmlist)) {
14653 $q = "DELETE FROM `{$tblname}` WHERE `{$data['id_key']}` IN (" . implode(', ', $rmlist) . ");";
14654 $dbo->setQuery($q);
14655 $dbo->execute();
14656 }
14657 }
14658
14659 /**
14660 * Clean up busy records where the busy relations contain empty booking IDs.
14661 */
14662 $hanging_busy_ids = [];
14663
14664 $q = "SELECT `idbusy` FROM `#__vikbooking_ordersbusy` WHERE `idorder` = 0 OR `idorder` IS NULL;";
14665 $dbo->setQuery($q);
14666 $removelist = $dbo->loadAssocList();
14667 if ($removelist) {
14668 foreach ($removelist as $hanging_busy) {
14669 $hanging_busy_id = (int)$hanging_busy['idbusy'];
14670 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
14671 array_push($hanging_busy_ids, $hanging_busy_id);
14672 }
14673 }
14674 }
14675
14676 // let's check also for ghost records that only occupy the room
14677 $q = "SELECT `b`.*,`ob`.`idorder` FROM `#__vikbooking_busy` AS `b` LEFT JOIN `#__vikbooking_ordersbusy` AS `ob` ON `b`.`id`=`ob`.`idbusy` WHERE `b`.`checkout` >= " . time() . " AND (`ob`.`idorder` = 0 OR `ob`.`idorder` IS NULL);";
14678 $dbo->setQuery($q);
14679 $removelist = $dbo->loadAssocList();
14680 if ($removelist) {
14681 foreach ($removelist as $hanging_busy) {
14682 $hanging_busy_id = (int)$hanging_busy['id'];
14683 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
14684 array_push($hanging_busy_ids, $hanging_busy_id);
14685 }
14686 }
14687 }
14688
14689 if ($hanging_busy_ids) {
14690 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id` IN (" . implode(', ', $hanging_busy_ids) . ");";
14691 $dbo->setQuery($q);
14692 $dbo->execute();
14693 }
14694
14695 echo "<p>Total ghost records removed: " . count($hanging_busy_ids) . "</p>";
14696
14697 return;
14698 }
14699
14700 /**
14701 * Hidden task to scan all database tables of VikBooking and Vik Channel Manager
14702 * to ensure the column `id` is defined as a primary key and got an auto-increment
14703 * extra flag properly defined and set. We've noticed that some third-party plugins
14704 * used to migrate WP sites may break the primary keys, and so new records won't get an ID.
14705 *
14706 * @since 1.16.8 (J) - 1.6.8 (WP)
14707 */
14708 public function fix_autoincrement_tables()
14709 {
14710 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14711 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14712 }
14713
14714 $dbo = JFactory::getDbo();
14715
14716 // load all the installed database tables
14717 $tables = $dbo->getTableList();
14718
14719 // get current database prefix
14720 $prefix = $dbo->getPrefix();
14721
14722 // replace prefix with placeholder
14723 $tables = array_map(function($table) use ($prefix)
14724 {
14725 return preg_replace("/^{$prefix}/", '#__', $table);
14726 }, $tables);
14727
14728 // remove all the tables that do not belong to VikBooking/VCM
14729 $tables = array_values(array_filter($tables, function($table)
14730 {
14731 if (preg_match("/^#__vik(?:booking|channelmanager)_config$/", $table))
14732 {
14733 // exclude the configuration table, which will be handled in a different way
14734 return false;
14735 }
14736
14737 return preg_match("/^#__vik(?:booking|channelmanager)_/", $table);
14738 }));
14739
14740 foreach ($tables as $table) {
14741 $columns = $dbo->getTableColumns($table, false);
14742 if (!isset($columns['id']) || empty($columns['id']->Type) || !empty($columns['id']->Extra)) {
14743 continue;
14744 }
14745
14746 echo 'Fixing ' . $table. ' for missing auto-increment<br/><pre>' . print_r($columns['id'], true) . '</pre><br/>';
14747
14748 // set auto-increment and primary key
14749 $dbo->setQuery("ALTER TABLE `{$table}` MODIFY `id` " . $columns['id']->Type . " NOT NULL AUTO_INCREMENT PRIMARY KEY;");
14750 $dbo->execute();
14751
14752 // count next auto-increment
14753 $dbo->setQuery("SELECT MAX(`id`) FROM `{$table}`");
14754 $next_ai = (int) $dbo->loadResult() + 1;
14755
14756 // update next auto-increment value
14757 $dbo->setQuery("ALTER TABLE `{$table}` AUTO_INCREMENT = {$next_ai}");
14758 $dbo->execute();
14759 }
14760 }
14761
14762 /**
14763 * Hidden task to (re-)run the update queries from a given plugin version.
14764 * Useful to ensure the database structure is up-to-date and no update queries went lost.
14765 *
14766 * @since 1.17.6 (J) - 1.7.6 (WP)
14767 */
14768 public function run_update_queries()
14769 {
14770 $app = JFactory::getApplication();
14771 $dbo = JFactory::getDbo();
14772
14773 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14774 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14775 }
14776
14777 $from_version = $app->input->getString('from_version');
14778
14779 if (empty($from_version)) {
14780 VBOHttpDocument::getInstance()->close(400, 'Missing from version value.');
14781 }
14782
14783 // determine the SQL updates directory path
14784 $sql_updates_path = '';
14785 if (VBOPlatformDetection::isWordPress()) {
14786 $sql_updates_path = implode(DIRECTORY_SEPARATOR, [VIKBOOKING_BASE, 'sql', 'update', 'mysql']);
14787 } else {
14788 $sql_updates_path = implode(DIRECTORY_SEPARATOR, [VBO_ADMIN_PATH, 'sql', 'updates', 'mysql']);
14789 }
14790
14791 if (!$sql_updates_path || !is_dir($sql_updates_path)) {
14792 VBOHttpDocument::getInstance()->close(500, 'Could not find SQL updates path.');
14793 }
14794
14795 // read all SQL update files
14796 $sql_update_files = JFolder::files($sql_updates_path, '\.sql', $recurse = false, $full = true);
14797
14798 // filter SQL files with just the valid ones
14799 $sql_update_files = array_filter($sql_update_files, function($sql_update_file) use ($from_version) {
14800 $file_version = basename($sql_update_file, '.sql');
14801 return version_compare($file_version, $from_version, '>=');
14802 });
14803
14804 // sort files by version ascending
14805 usort($sql_update_files, function($a, $b) {
14806 return version_compare(basename($a, '.sql'), basename($b, '.sql'));
14807 });
14808
14809 if (!$sql_update_files) {
14810 VBOHttpDocument::getInstance()->close(500, sprintf('Could not find any suitable SQL update file from version %s.', $from_version));
14811 }
14812
14813 $success_queries = 0;
14814
14815 foreach ($sql_update_files as $file) {
14816 $handle = fopen($file, 'r');
14817
14818 $bytes = '';
14819 while (!feof($handle)) {
14820 $bytes .= fread($handle, 8192);
14821 }
14822
14823 fclose($handle);
14824
14825 if (VBOPlatformDetection::isWordPress()) {
14826 $queries_list = JDatabaseHelper::splitSql($bytes);
14827 } else {
14828 try {
14829 if (class_exists('JDatabaseDriver')) {
14830 $queries_list = JDatabaseDriver::splitSql($bytes);
14831 } else {
14832 $queries_list = Joomla\Database\DatabaseDriver::splitSql($bytes);
14833 }
14834 } catch(Throwable $e) {
14835 $app->enqueueMessage(sprintf('Error splitting queries: %s', $e->getMessage()), 'error');
14836 $queries_list = [];
14837 }
14838 }
14839
14840 foreach ($queries_list as $q) {
14841 try {
14842 $dbo->setQuery($q);
14843 $result = $dbo->execute();
14844 } catch (Exception $e) {
14845 $result = false;
14846 $app->enqueueMessage(sprintf('Error executing query: %s', $e->getMessage()), 'warning');
14847 }
14848
14849 if ($result) {
14850 $success_queries++;
14851 }
14852 }
14853 }
14854
14855 if ($success_queries) {
14856 $app->enqueueMessage(sprintf('Successful queries: %d', $success_queries), 'success');
14857 }
14858
14859 // send response to output
14860 echo '<pre>'.print_r($sql_update_files, true).'</pre><br/>';
14861 }
14862
14863 /**
14864 * Loads a specific admin widget ID and executes the requested method.
14865 * Useful for loading a newly added widget, or to execute custom methods.
14866 *
14867 * @see this is an AJAX endpoint.
14868 *
14869 * @since 1.14 (J) - 1.4.0 (WP)
14870 * @since 1.15 (J) - 1.5.0 (WP) widget callback can return values rather than just echoing.
14871 * @since 1.16.5 (J) - 1.6.5 (WP) widgets are rendered within a try-catch statement.
14872 */
14873 public function exec_admin_widget()
14874 {
14875 if (!JSession::checkToken()) {
14876 // missing CSRF-proof token
14877 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14878 }
14879
14880 $widget_id = VikRequest::getString('widget_id', '', 'request');
14881 $call = VikRequest::getString('call', '', 'request');
14882 $return = VikRequest::getInt('return', 0, 'request');
14883 $vbo_page = VikRequest::getString('vbo_page', '', 'request');
14884 $vbo_uri = VikRequest::getString('vbo_uri', '', 'request');
14885 $multitask = VikRequest::getInt('multitask', 0, 'request');
14886
14887 if (empty($widget_id)) {
14888 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget ID');
14889 }
14890
14891 if (empty($call) || !is_string($call)) {
14892 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget Callback');
14893 }
14894
14895 // invoke admin widgets helper
14896 $widgets_helper = VikBooking::getAdminWidgetsInstance();
14897 $widget = $widgets_helper->getWidget($widget_id);
14898
14899 if ($widget === false) {
14900 VBOHttpDocument::getInstance()->close(404, 'Requested Admin Widget not found');
14901 }
14902
14903 if (!method_exists($widget, $call) || !is_callable(array($widget, $call))) {
14904 VBOHttpDocument::getInstance()->close(403, 'Admin Widget Callback not found or not callable');
14905 }
14906
14907 // get the multitask parser object
14908 $parser = VBOMultitaskParser::getInstance($vbo_page, $vbo_uri);
14909
14910 // check if arguments should be passed
14911 $call_args = [];
14912 if ($multitask && $call === 'render') {
14913 // build the multitask data object and inject it to the args as the first index
14914 $call_args[] = $parser->getData();
14915
14916 // bind options within the widget, if any
14917 $widget->bindOptions($call_args[0]);
14918 } else {
14919 // always bind multitask options, if any
14920 $widget->bindOptions($parser->getOptions());
14921 }
14922
14923 try {
14924 if ($return) {
14925 // invoke the widget's method and get the value returned
14926 $widget_response = $call_args ? call_user_func_array([$widget, $call], $call_args) : $widget->{$call}();
14927 } else {
14928 // invoke the widget's method within a buffer
14929 ob_start();
14930 if ($call_args) {
14931 $res = call_user_func_array([$widget, $call], $call_args);
14932 } else {
14933 $widget->{$call}();
14934 }
14935 $widget_response = ob_get_contents();
14936 ob_end_clean();
14937 }
14938 } catch (Throwable $e) {
14939 VBOHttpDocument::getInstance()->close($e->getCode() ?: 500, sprintf("%s\n%s at line %d", $e->getMessage(), $e->getFile(), $e->getLine()));
14940 } catch (Exception $e) {
14941 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
14942 }
14943
14944 // prepare response object with a property equal to the called method
14945 $response = new stdClass;
14946 $response->{$call} = $widget_response;
14947
14948 // output the JSON encoded response and exit
14949 VBOHttpDocument::getInstance()->json($response);
14950 }
14951
14952 /**
14953 * Updates the map of admin widgets.
14954 *
14955 * @throws Exception this is an AJAX endpoint.
14956 *
14957 * @since 1.4.0
14958 */
14959 public function save_admin_widgets()
14960 {
14961 if (!JSession::checkToken()) {
14962 // missing CSRF-proof token
14963 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14964 }
14965
14966 // make sure permissions are sufficient
14967 if (!JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
14968 VBOHttpDocument::getInstance()->close(403, 'You are not authorized to modify the widgets.');
14969 }
14970
14971 $psections = VikRequest::getVar('sections', array(), 'request', 'array');
14972 if (!is_array($psections) || !count($psections)) {
14973 VBOHttpDocument::getInstance()->close(500, 'No sections found in map');
14974 }
14975
14976 // request values are all converted to arrays, so restore the object styling
14977 $psections = json_decode(json_encode($psections));
14978
14979 // update map
14980 $result = VikBooking::getAdminWidgetsInstance()->updateWidgetsMap($psections);
14981
14982 $response = new stdClass;
14983 $response->status = (int)$result;
14984
14985 // output the JSON encoded response and exit
14986 VBOHttpDocument::getInstance()->json($response);
14987 }
14988
14989 /**
14990 * Restores the default admin widgets map.
14991 *
14992 * @since 1.4.0
14993 */
14994 public function reset_admin_widgets()
14995 {
14996 // reset map and redirect to dashboard
14997 VikBooking::getAdminWidgetsInstance()->restoreDefaultWidgetsMap();
14998
14999 JFactory::getApplication()->redirect('index.php?option=com_vikbooking');
15000 exit;
15001 }
15002
15003 /**
15004 * Updates the welcome message status for the widget's customizer via AJAX.
15005 *
15006 * @since 1.4.0
15007 */
15008 public function admin_widgets_welcome()
15009 {
15010 if (!JSession::checkToken()) {
15011 // missing CSRF-proof token
15012 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15013 }
15014
15015 $hide_welcome = VikRequest::getInt('hide_welcome', 0, 'request');
15016 // update configuration value
15017 VikBooking::getAdminWidgetsInstance()->updateWelcome($hide_welcome);
15018
15019 $response = new stdClass;
15020 $response->status = $hide_welcome;
15021
15022 // output the JSON encoded response and exit
15023 VBOHttpDocument::getInstance()->json($response);
15024 }
15025
15026 public function newcondtext()
15027 {
15028 VikBookingHelper::printHeader("11");
15029
15030 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecondtext'));
15031
15032 parent::display();
15033
15034 if (VikBooking::showFooter()) {
15035 VikBookingHelper::printFooter();
15036 }
15037 }
15038
15039 public function editcondtext()
15040 {
15041 VikBookingHelper::printHeader("11");
15042
15043 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecondtext'));
15044
15045 parent::display();
15046
15047 if (VikBooking::showFooter()) {
15048 VikBookingHelper::printFooter();
15049 }
15050 }
15051
15052 public function cancelcondtext()
15053 {
15054 JFactory::getApplication()->redirect('index.php?option=com_vikbooking&task=config&tab=7');
15055 }
15056
15057 public function createcondtext()
15058 {
15059 if (!JSession::checkToken()) {
15060 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15061 }
15062
15063 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
15064 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15065 }
15066
15067 $this->_doCreateCondText();
15068 }
15069
15070 public function createcondtextstay()
15071 {
15072 if (!JSession::checkToken()) {
15073 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15074 }
15075
15076 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
15077 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15078 }
15079
15080 $this->_doCreateCondText(true);
15081 }
15082
15083 private function _doCreateCondText($stay = false)
15084 {
15085 $dbo = JFactory::getDbo();
15086 $app = JFactory::getApplication();
15087 $rules_helper = VikBooking::getConditionalRulesInstance();
15088 $rules_list = $rules_helper->composeRulesParamsFromRequest();
15089
15090 $condtextname = VikRequest::getString('condtextname', '', 'request');
15091 $condtexttkn = VikRequest::getString('condtexttkn', '', 'request');
15092 $msg = VikRequest::getString('msg', '', 'request', VIKREQUEST_ALLOWRAW);
15093 $debug = VikRequest::getInt('debug', 0, 'request');
15094 if (empty($condtextname)) {
15095 $condtextname = date('Y-m-dHis');
15096 $condtexttkn = '{condition: ' . date('YmdHis') . '}';
15097 }
15098
15099 $existing_tokens = $rules_helper->getSpecialTags();
15100 if (count($existing_tokens) && isset($existing_tokens[$condtexttkn])) {
15101 VikError::raiseWarning('', 'Another conditional text with the same special tag already exists');
15102 $app->redirect('index.php?option=com_vikbooking&task=newcondtext');
15103 exit;
15104 }
15105
15106 $data = new stdClass;
15107 $data->name = $condtextname;
15108 $data->token = $condtexttkn;
15109 $data->rules = json_encode($rules_list);
15110 $data->msg = $msg;
15111 $data->lastupd = JDate::getInstance()->toSql();
15112 $data->debug = $debug;
15113
15114 $dbo->insertObject('#__vikbooking_condtexts', $data, 'id');
15115
15116 if (isset($data->id)) {
15117 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
15118 }
15119
15120 if (!$stay || !isset($data->id)) {
15121 $this->cancelcondtext();
15122 exit;
15123 }
15124
15125 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $data->id);
15126 }
15127
15128 public function updatecondtext()
15129 {
15130 if (!JSession::checkToken()) {
15131 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15132 }
15133
15134 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
15135 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15136 }
15137
15138 $this->_doUpdateCondText();
15139 }
15140
15141 public function updatecondtextstay()
15142 {
15143 if (!JSession::checkToken()) {
15144 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15145 }
15146
15147 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
15148 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15149 }
15150
15151 $this->_doUpdateCondText(true);
15152 }
15153
15154 private function _doUpdateCondText($stay = false)
15155 {
15156 $dbo = JFactory::getDbo();
15157 $app = JFactory::getApplication();
15158 $rules_helper = VikBooking::getConditionalRulesInstance();
15159 $rules_list = $rules_helper->composeRulesParamsFromRequest();
15160
15161 $pwhere = VikRequest::getInt('where', '', 'request');
15162 $condtextname = VikRequest::getString('condtextname', '', 'request');
15163 $condtexttkn = VikRequest::getString('condtexttkn', '', 'request');
15164 $msg = VikRequest::getString('msg', '', 'request', VIKREQUEST_ALLOWRAW);
15165 $debug = VikRequest::getInt('debug', 0, 'request');
15166 if (empty($condtextname)) {
15167 $condtextname = date('Y-m-dHis');
15168 $condtexttkn = '{condition: ' . date('YmdHis') . '}';
15169 }
15170
15171 $existing_tokens = $rules_helper->getSpecialTags();
15172 if (count($existing_tokens) && isset($existing_tokens[$condtexttkn]) && ($existing_tokens[$condtexttkn]['id'] != $pwhere)) {
15173 VikError::raiseWarning('', 'Another conditional text with the same special tag already exists (' . $existing_tokens[$condtexttkn]['name'] . ')');
15174 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $pwhere);
15175 exit;
15176 }
15177
15178 $data = new stdClass;
15179 $data->id = $pwhere;
15180 $data->name = $condtextname;
15181 $data->token = $condtexttkn;
15182 $data->rules = json_encode($rules_list);
15183 $data->msg = $msg;
15184 $data->lastupd = JDate::getInstance()->toSql();
15185 $data->debug = $debug;
15186
15187 $dbo->updateObject('#__vikbooking_condtexts', $data, 'id');
15188
15189 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
15190
15191 if (!$stay) {
15192 $this->cancelcondtext();
15193 exit;
15194 }
15195
15196 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $data->id);
15197 }
15198
15199 public function removecondtext()
15200 {
15201 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
15202 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15203 }
15204
15205 $dbo = JFactory::getDbo();
15206 $ids = VikRequest::getVar('cid', array());
15207
15208 VikBooking::getConditionalRulesInstance(true);
15209 $templates = VikBookingHelperConditionalRules::getTemplateFilesPaths();
15210
15211 foreach ($ids as $d) {
15212 $q = "SELECT `token` FROM `#__vikbooking_condtexts` WHERE `id`=" . (int)$d . ";";
15213 $dbo->setQuery($q);
15214 $dbo->execute();
15215 if (!$dbo->getNumRows()) {
15216 continue;
15217 }
15218 $special_tag = $dbo->loadResult();
15219
15220 // remove the token from each template file if it was used before
15221 if (!empty($special_tag)) {
15222 // remove token from all template files
15223 foreach ($templates as $tkey => $tpath) {
15224 // get requested file content
15225 $fcontent = VikBookingHelperConditionalRules::getTemplateFileCode($tkey);
15226 if (empty($fcontent) || !is_string($fcontent)) {
15227 break;
15228 }
15229 // remove tag from code content
15230 $fcontent = str_replace($special_tag, '', $fcontent);
15231 // update the file code
15232 VikBookingHelperConditionalRules::writeTemplateFileCode($tkey, $fcontent);
15233 }
15234 }
15235
15236 // delete the record
15237 $q = "DELETE FROM `#__vikbooking_condtexts` WHERE `id`=" . (int)$d . ";";
15238 $dbo->setQuery($q);
15239 $dbo->execute();
15240 }
15241
15242 $this->cancelcondtext();
15243 }
15244
15245 /**
15246 * AJAX endpoint to update one template file with the given tag or styles.
15247 * A JSON response will be echoed by exiting the process.
15248 */
15249 public function condtext_update_tmpl()
15250 {
15251 VikBooking::getConditionalRulesInstance(true);
15252
15253 $tagaction = VikRequest::getString('tagaction', '', 'request');
15254 $tag = VikRequest::getString('tag', '', 'request');
15255 $file = VikRequest::getString('file', '', 'request', VIKREQUEST_ALLOWRAW);
15256 $newcontent = VikRequest::getString('newcontent', '', 'request', VIKREQUEST_ALLOWRAW);
15257 $custom_classes = VikRequest::getVar('custom_classes', array(), 'request', 'array');
15258
15259 $allowed_actions = array(
15260 'add',
15261 'remove',
15262 'styles',
15263 'restore',
15264 );
15265
15266 if (empty($tagaction) || empty($file) || !in_array($tagaction, $allowed_actions)) {
15267 throw new Exception("Invalid request submitted", 500);
15268 }
15269
15270 if (in_array($tagaction, array('add', 'remove')) && empty($tag)) {
15271 throw new Exception("Invalid request submitted - missing tag", 500);
15272 }
15273
15274 if (in_array($tagaction, array('add', 'styles')) && empty($newcontent)) {
15275 throw new Exception("Invalid request submitted - missing new HTML content", 500);
15276 }
15277
15278 if ($tagaction == 'styles' && (!is_array($custom_classes) || !count($custom_classes))) {
15279 throw new Exception("No custom CSS classes to parse", 500);
15280 }
15281
15282 if ($tagaction == 'restore') {
15283 // immediately restore the requested file to avoid script interruptions
15284 VikBookingHelperConditionalRules::restoreTemplateFileCode($file);
15285 }
15286
15287 // get requested file content
15288 $fcontent = VikBookingHelperConditionalRules::getTemplateFileCode($file);
15289 if (empty($fcontent) || !is_string($fcontent)) {
15290 throw new Exception("File not found or its code is unreadable", 404);
15291 }
15292
15293 if ($tagaction == 'remove') {
15294 // remove tag from code content
15295 $fcontent = str_replace($tag, '', $fcontent);
15296 } elseif ($tagaction == 'add') {
15297 // add tag to code content in the same exact position
15298 $fcontent = VikBookingHelperConditionalRules::addTagByComparingSources($tag, $file, $newcontent, $fcontent);
15299 } elseif ($tagaction == 'styles') {
15300 // apply the same styling rules
15301 $fcontent = VikBookingHelperConditionalRules::addStylesByComparingSources($custom_classes, $file, $newcontent, $fcontent);
15302 }
15303
15304 // update the file code
15305 $res = VikBookingHelperConditionalRules::writeTemplateFileCode($file, $fcontent);
15306
15307 if (!$res) {
15308 throw new Exception("Could not update the source code of the template file", 500);
15309 }
15310
15311 // parse new HTML content
15312 $newhtmls = VikBookingHelperConditionalRules::getTemplateFilesContents($file);
15313 if (!is_array($newhtmls) || !isset($newhtmls[$file])) {
15314 throw new Exception("Could not parse new template file content", 404);
15315 }
15316
15317 // trigger backup/mirroring, if available
15318 if (VBOPlatformDetection::isWordPress()) {
15319 VikBookingUpdateManager::storeTemplateContent($file, $newhtmls[$file]);
15320 }
15321
15322 // build output
15323 $output = new stdClass;
15324 $output->newhtml = $newhtmls[$file];
15325 $output->log = VikBookingHelperConditionalRules::getEditingLog();
15326
15327 echo json_encode($output);
15328 exit;
15329 }
15330
15331 /**
15332 * AJAX endpoint to invoke methods of the geocoding helper.
15333 */
15334 public function geocoding_endpoint()
15335 {
15336 $geo = VikBooking::getGeocodingInstance();
15337 $callback = VikRequest::getString('callback', '', 'request');
15338
15339 if (empty($callback) || !method_exists($geo, $callback) || !is_callable(array($geo, $callback))) {
15340 throw new Exception("Callback not available", 403);
15341 }
15342
15343 // invoke requested method
15344 $res = $geo->{$callback}();
15345
15346 // prepare response
15347 $response = new stdClass;
15348 $response->{$callback} = $res;
15349
15350 echo json_encode($response);
15351 exit;
15352 }
15353
15354 public function refundtn()
15355 {
15356 //modal box, so we do not set menu or footer
15357
15358 VikRequest::setVar('view', VikRequest::getCmd('view', 'refundtn'));
15359
15360 parent::display();
15361 }
15362
15363 public function do_refundtn()
15364 {
15365 $dbo = JFactory::getDbo();
15366 $app = JFactory::getApplication();
15367
15368 $bid = VikRequest::getInt('bid', 0, 'request');
15369 $amount = VikRequest::getFloat('amount', 0, 'request');
15370 $refund_reason = VikRequest::getString('refund_reason', '', 'request');
15371 $tmpl = VikRequest::getString('tmpl', '', 'request');
15372 $nav_suffix = $tmpl == 'component' ? '&tmpl=component' : '';
15373
15374 $currencysymb = VikBooking::getCurrencySymb();
15375
15376 if (empty($bid) || $amount <= 0) {
15377 VikError::raiseWarning('', JText::translate('VBO_PLEASE_FILL_FIELDS'));
15378 $app->redirect('index.php?option=com_vikbooking');
15379 exit;
15380 }
15381
15382 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $bid . " AND `status`!='standby';";
15383 $dbo->setQuery($q);
15384 $row = $dbo->loadAssoc();
15385 if (!$row) {
15386 VikError::raiseWarning('', 'Booking not found');
15387 $app->redirect('index.php?option=com_vikbooking');
15388 exit;
15389 }
15390
15391 // get booking history instance
15392 $history_obj = VikBooking::getBookingHistoryInstance();
15393 $history_obj->setBid($row['id']);
15394
15395 // get payment information
15396 $payment = VikBooking::getPayment($row['idpayment']);
15397 $tn_driver = is_array($payment) ? $payment['file'] : null;
15398
15399 // transaction data validation callback
15400 $tn_data_callback = function($data) use ($tn_driver) {
15401 return (is_object($data) && isset($data->driver) && basename($data->driver, '.php') == basename($tn_driver, '.php'));
15402 };
15403 // get previous transactions
15404 $prev_tn_data = $history_obj->getEventsWithData(array('P0', 'PN'), $tn_data_callback);
15405
15406 if (!is_array($prev_tn_data) || !count($prev_tn_data)) {
15407 // no previous transactions found
15408 VikError::raiseWarning('', 'No previous transactions found, unable to issue the refund');
15409 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15410 exit;
15411 }
15412
15413 // push refund information for the payment gateway
15414 $row['total_to_refund'] = $amount;
15415 $row['transaction'] = $prev_tn_data;
15416 $row['refund_reason'] = $refund_reason;
15417
15418 // push the transaction currency information
15419 $row['transaction_currency'] = VikBooking::getCurrencyCodePp();
15420
15421 /**
15422 * Trigger event to allow third-party plugins to manipulate the transaction data.
15423 *
15424 * @since 1.18.5 (J) - 1.8.5 (WP)
15425 */
15426 VBOFactory::getPlatform()->getDispatcher()->trigger('onInitRefundTransaction', [&$row, &$payment['params']]);
15427
15428 if (VBOPlatformDetection::isWordPress()) {
15429 /**
15430 * @wponly The payment gateway is loaded
15431 * through the apposite dispatcher.
15432 */
15433 JLoader::import('adapter.payment.dispatcher');
15434 $obj = JPaymentDispatcher::getInstance('vikbooking', $payment['file'], $row, $payment['params']);
15435 } else {
15436 /**
15437 * @joomlaonly The Payment Factory library will invoke the gateway.
15438 *
15439 * @since 1.14.3
15440 */
15441 require_once VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'payments' . DIRECTORY_SEPARATOR . 'libraries' . DIRECTORY_SEPARATOR . 'factory.php';
15442 $obj = VBOPaymentFactory::getPaymentInstance($payment['file'], $row, $payment['params']);
15443 }
15444
15445 if (!method_exists($obj, 'isRefundSupported') || !$obj->isRefundSupported()) {
15446 // refund not supported
15447 VikError::raiseWarning('', 'The selected payment method does not support refunds');
15448 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15449 exit;
15450 }
15451
15452 // perform the refund transaction
15453 $array_result = $obj->refund();
15454
15455 if ($array_result['verified'] != 1) {
15456 // raise warning by getting the message
15457 if (!empty($array_result['log']) && is_string($array_result['log'])) {
15458 VikError::raiseWarning('', $array_result['log']);
15459 } else {
15460 VikError::raiseWarning('', 'Operation failed');
15461 }
15462 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15463 exit;
15464 }
15465
15466 /**
15467 * New payment plugins can return the total amount refunded ('tot_paid').
15468 *
15469 * @since 1.15.4 (J) - 1.5.10 (WP)
15470 */
15471 if (!empty($array_result['tot_paid'])) {
15472 // overwrite the requested amount with the returned one
15473 $amount = (float)$array_result['tot_paid'];
15474 }
15475
15476 /**
15477 * The history event extra data will contain the "amount_paid" (refunded).
15478 *
15479 * @since 1.16.9 (J) - 1.6.9 (WP)
15480 */
15481 $history_obj->setExtraData([
15482 'amount_paid' => $amount,
15483 ]);
15484
15485 // update total paid, total and refund columns for the booking
15486 $booking = new stdClass;
15487 $booking->id = $row['id'];
15488 if ($row['totpaid'] > 0) {
15489 $booking->totpaid = $row['totpaid'] - $amount;
15490 }
15491 if ($row['total'] > 0) {
15492 $booking->total = $row['total'] - $amount;
15493 }
15494 $booking->refund = (float)$row['refund'] + $amount;
15495 // update record in db
15496 $dbo->updateObject('#__vikbooking_orders', $booking, 'id');
15497
15498 // store the refund event
15499 $event_descr = [
15500 '(' . $payment['name'] . ')',
15501 $refund_reason,
15502 $currencysymb . ' ' . VikBooking::numberFormat($amount),
15503 ];
15504 $history_obj->store('RF', implode("\n", $event_descr));
15505
15506 // display success message and redirect
15507 $app->enqueueMessage(JText::translate('VBO_REFUND_SUCCESS'));
15508 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . '&success=1' . $nav_suffix);
15509 exit;
15510 }
15511
15512 /**
15513 * AJAX upload endpoint for media files.
15514 *
15515 * @return void
15516 *
15517 * @throws Exception
15518 *
15519 * @since 1.15.0 (J) - 1.5.0 (WP)
15520 */
15521 public function upload_media_file()
15522 {
15523 $input = JFactory::getApplication()->input;
15524
15525 // allowed types
15526 $type = $input->getString('type', '');
15527 $mask = 'png,apng,jpg,jpeg,bmp,heic,webp,gif,ico,svg';
15528
15529 if ($type != 'image') {
15530 $mask .= ',zip,rar,pdf,doc,docx,rtf,odt,pages,xls,xlsx,csv,ods,numbers,txt,md';
15531 }
15532
15533 // response object
15534 $result = new stdClass;
15535 $result->status = 0;
15536
15537 try
15538 {
15539 // get file from request
15540 $file = $input->files->get('file', array(), 'array');
15541
15542 // try to upload the file
15543 $result = VikBooking::uploadFileFromRequest($file, VBO_MEDIA_PATH, $mask);
15544 $result->status = 1;
15545
15546 $result->size = JHtml::fetch('number.bytes', filesize($result->path), 'auto', 0);
15547 $result->url = str_replace(DIRECTORY_SEPARATOR, '/', str_replace(VBO_MEDIA_PATH . DIRECTORY_SEPARATOR, VBO_MEDIA_URI, $result->path));
15548 }
15549 catch (Exception $e)
15550 {
15551 $result->error = $e->getMessage();
15552 $result->code = $e->getCode();
15553 }
15554
15555 echo json_encode($result);
15556 exit;
15557 }
15558
15559 /**
15560 * AJAX endpoint to invoke a report object's method.
15561 *
15562 * @return void
15563 *
15564 * @since 1.15.0 (J) - 1.5.0 (WP)
15565 * @since 1.18.6 (J) - 1.8.6 (WP) added support for "call_args".
15566 */
15567 public function invoke_report()
15568 {
15569 $app = JFactory::getApplication();
15570
15571 $report_name = $app->input->getString('report', '');
15572 $report_call = $app->input->getString('call', '');
15573 $call_args = $app->input->get('call_args', [], 'array');
15574 $params = $app->input->get('params', [], 'array');
15575
15576 if (empty($report_name)) {
15577 VBOHttpDocument::getInstance($app)->close(400, 'Missing report name');
15578 }
15579
15580 if (empty($report_call)) {
15581 VBOHttpDocument::getInstance($app)->close(400, 'Missing report call');
15582 }
15583
15584 // get requested report instance
15585 $report = VikBooking::getReportInstance($report_name);
15586 if (!$report) {
15587 VBOHttpDocument::getInstance($app)->close(404, 'Report not found');
15588 }
15589
15590 if (!method_exists($report, $report_call) || !is_callable(array($report, $report_call))) {
15591 VBOHttpDocument::getInstance($app)->close(403, sprintf('Cannot call [%s] on report', $report_call));
15592 }
15593
15594 try {
15595 // call on report's method
15596 if ($call_args) {
15597 $result = call_user_func_array([$report, $report_call], $call_args);
15598 } else {
15599 $result = $report->{$report_call}($params);
15600 }
15601 } catch (Exception $e) {
15602 VBOHttpDocument::getInstance($app)->close($e->getCode() ?: 500, $e->getMessage());
15603 }
15604
15605 if (is_null($result)) {
15606 VBOHttpDocument::getInstance($app)->close(400, 'Null response');
15607 }
15608
15609 if (is_scalar($result)) {
15610 // wrap result within an array for a JSON encoded response
15611 VBOHttpDocument::getInstance($app)->json([$result]);
15612 }
15613
15614 // output the JSON encoded array/object returned
15615 VBOHttpDocument::getInstance($app)->json($result);
15616 }
15617
15618 /**
15619 * Handles requests for the multitask widgets panel.
15620 *
15621 * @see this is an AJAX endpoint.
15622 *
15623 * @since 1.15.0 (J) - 1.5.0 (WP)
15624 * @since 1.16.5 (J) - 1.6.5 (WP) widgets are rendered within a try-catch statement.
15625 */
15626 public function exec_multitask_widgets()
15627 {
15628 if (!JSession::checkToken()) {
15629 // missing CSRF-proof token
15630 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15631 }
15632
15633 $call = VikRequest::getString('call', '', 'request');
15634 $call_args = VikRequest::getVar('call_args', array(), 'request', 'array');
15635
15636 if (empty($call)) {
15637 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget Callback');
15638 }
15639
15640 // invoke admin widgets helper
15641 $widgets_helper = VikBooking::getAdminWidgetsInstance();
15642
15643 if (!method_exists($widgets_helper, $call) || !is_callable(array($widgets_helper, $call))) {
15644 VBOHttpDocument::getInstance()->close(403, 'Admin Widgets Callback not found or not callable');
15645 }
15646
15647 try {
15648 // invoke the helper's method and get the value returned
15649 if (is_array($call_args) && count($call_args)) {
15650 $result = call_user_func_array(array($widgets_helper, $call), $call_args);
15651 } else {
15652 $result = $widgets_helper->{$call}();
15653 }
15654 } catch (Throwable $e) {
15655 VBOHttpDocument::getInstance()->close($e->getCode() ?: 500, sprintf("%s\n%s at line %d", $e->getMessage(), $e->getFile(), $e->getLine()));
15656 } catch (Exception $e) {
15657 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
15658 }
15659
15660 // prepare response object with the result property
15661 $response = new stdClass;
15662 $response->result = $result;
15663
15664 // output the JSON response and exit
15665 VBOHttpDocument::getInstance()->json($response);
15666 }
15667
15668 /**
15669 * Handles requests for displaying a browser notification being dispatched.
15670 *
15671 * @see this is an AJAX endpoint.
15672 *
15673 * @since 1.15.0 (J) - 1.5.0 (WP)
15674 */
15675 public function notification_displayer()
15676 {
15677 $payload_str = VikRequest::getString('payload', '', 'request', VIKREQUEST_ALLOWRAW);
15678
15679 if (empty($payload_str)) {
15680 VBOHttpDocument::getInstance()->close(500, 'Empty notification payload');
15681 }
15682
15683 // attempt to decode the notification payload
15684 $payload = json_decode($payload_str);
15685
15686 if (!is_object($payload)) {
15687 VBOHttpDocument::getInstance()->close(500, 'Could not decode notification payload: ' . $payload_str);
15688 }
15689
15690 // get notification displayer for this type of notification
15691 $displayer = VBONotificationBuilder::getInstance($payload)->getDisplayer();
15692 if (!$displayer) {
15693 VBOHttpDocument::getInstance()->close(500, 'Could not build notification display data from payload: ' . $payload_str);
15694 }
15695
15696 // compose the notification display data object
15697 try {
15698 $notif_data = $displayer->getData();
15699 if (!$notif_data) {
15700 throw new Exception('Error building the notification display data', 500);
15701 }
15702 } catch (Exception $e) {
15703 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
15704 }
15705
15706 // output the JSON response and exit
15707 VBOHttpDocument::getInstance()->json($notif_data);
15708 }
15709
15710 /**
15711 * Handles requests for watching widgets data and getting
15712 * new events to trigger browser notifications.
15713 *
15714 * @see this is an AJAX endpoint.
15715 *
15716 * @since 1.15.0 (J) - 1.5.0 (WP)
15717 * @since 1.16.8 (J) - 1.6.8 (WP) introduced notification events.
15718 */
15719 public function widgets_watch_data()
15720 {
15721 if (!JSession::checkToken()) {
15722 // missing CSRF-proof token
15723 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15724 }
15725
15726 $app = JFactory::getApplication();
15727
15728 $watch_data_str = $app->input->get('watch_data', '', 'raw');
15729 $pushed_data_str = $app->input->get('pushed_data', '[]', 'raw');
15730
15731 if (empty($watch_data_str)) {
15732 VBOHttpDocument::getInstance()->close(500, 'Empty watch-data payload');
15733 }
15734
15735 // attempt to decode the watch-data payload
15736 $watch_data = json_decode($watch_data_str, true);
15737
15738 if (!$watch_data) {
15739 VBOHttpDocument::getInstance()->close(500, 'Could not decode watch-data payload: ' . $watch_data_str);
15740 }
15741
15742 // check if any pushed data was set
15743 $pushed_data = (array)json_decode($pushed_data_str, true);
15744
15745 // container for new notifications
15746 $notifs_pool = [];
15747
15748 // container for the events data to dispatch
15749 $events_pool = [];
15750
15751 // get admin widgets helper
15752 $widgets_helper = VikBooking::getAdminWidgetsInstance();
15753
15754 foreach ($watch_data as $widget_id => $data) {
15755 // invoke admin widget (with no pre-loading)
15756 $widget_instance = $widgets_helper->getWidget($widget_id);
15757 if (!$widget_instance) {
15758 continue;
15759 }
15760
15761 // build the widget watch data object
15762 $widget_watch_data = VBONotificationWatchdata::getInstance($data)->setPushedData($pushed_data);
15763
15764 // check if the widget needs to emit browser notifications
15765 list($watch_next, $notifications) = $widget_instance->getNotifications($widget_watch_data);
15766
15767 // check if the widget needs to emit JavaScript events
15768 $events = $widget_instance->getNotificationEvents($widget_watch_data);
15769
15770 if ($watch_next) {
15771 // update next watch-data object for this widget
15772 $watch_data[$widget_id] = $watch_next;
15773 }
15774
15775 if (is_array($notifications) && $notifications) {
15776 // merge notifications
15777 $notifs_pool = array_merge($notifs_pool, $notifications);
15778 }
15779
15780 if (is_array($events) && $events) {
15781 // push notification events for this widget
15782 $events_pool[] = $events;
15783 }
15784 }
15785
15786 // build the response object
15787 $response = new stdClass;
15788 $response->watch_data = $watch_data;
15789 $response->notifications = $notifs_pool;
15790 $response->events = $events_pool;
15791
15792 // output the JSON response and exit
15793 VBOHttpDocument::getInstance()->json($response);
15794 }
15795
15796 /**
15797 * Outputs a list of CSS assets required to render the admin widgets
15798 * externally from Vik Booking. Useful i.e. to Vik Channel Manager.
15799 *
15800 * @see this is an AJAX endpoint.
15801 *
15802 * @since 1.16.0 (J) - 1.6.0 (WP)
15803 */
15804 public function widgets_get_assets()
15805 {
15806 // list of needed CSS asset details
15807 $assets_pool = [];
15808
15809 // appearance preference assets (one or none)
15810 $app_pref_asset = VikBooking::loadAppearancePreferenceAssets($get_info = true);
15811
15812 if (VBOPlatformDetection::isWordPress()) {
15813 // WordPress (main CSS)
15814 $assets_pool[] = [
15815 'rel' => 'stylesheet',
15816 'id' => 'vbo-style-css',
15817 'href' => VIKBOOKING_ADMIN_ASSETS_URI . 'vikbooking.css?ver=' . VIKBOOKING_SOFTWARE_VERSION,
15818 'media' => 'all',
15819 ];
15820
15821 if (is_array($app_pref_asset) && !empty($app_pref_asset['href'])) {
15822 // appearance preference CSS
15823 $assets_pool[] = [
15824 'rel' => 'stylesheet',
15825 'id' => (!empty($app_pref_asset['id']) ? $app_pref_asset['id'] : rand()),
15826 'href' => $app_pref_asset['href'] . '?ver=' . VIKBOOKING_SOFTWARE_VERSION,
15827 'media' => 'all',
15828 ];
15829 }
15830 } else {
15831 // Joomla (main CSS)
15832 $assets_pool[] = [
15833 'rel' => 'stylesheet',
15834 'id' => 'vbo-style-css',
15835 'href' => VBO_ADMIN_URI . 'vikbooking.css?' . VIKBOOKING_SOFTWARE_VERSION,
15836 'media' => 'all',
15837 ];
15838
15839 if (is_array($app_pref_asset) && !empty($app_pref_asset['href'])) {
15840 // appearance preference CSS
15841 $assets_pool[] = [
15842 'rel' => 'stylesheet',
15843 'id' => (!empty($app_pref_asset['id']) ? $app_pref_asset['id'] : rand()),
15844 'href' => $app_pref_asset['href'] . '?' . VIKBOOKING_SOFTWARE_VERSION,
15845 'media' => 'all',
15846 ];
15847 }
15848 }
15849
15850 // output the JSON response and exit
15851 VBOHttpDocument::getInstance()->json($assets_pool);
15852 }
15853 }
15854