PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.8
VikBooking Hotel Booking Engine & PMS v1.8.8
1.8.16 1.8.15 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 All 37 releases
vikbooking / admin / controller.php

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

15,819 lines 593.6 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 foreach ($allbusy as $bb) {
2523 $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $first . ", `checkout`=" . $second . ", `realback`=" . $realback . " WHERE `id`=" . $bb['id'] . ";";
2524 $dbo->setQuery($q);
2525 $dbo->execute();
2526 }
2527 }
2528
2529 /**
2530 * Check if some rooms have modified stay dates different than the booking stay dates.
2531 *
2532 * @since 1.16.0 (J) - 1.6.0 (WP)
2533 */
2534 if (!$ord['split_stay'] && !$ord['closure'] && $ord['roomsnum'] > 1 && $ord['days'] > 1) {
2535 // load the occupied stay dates for each room in case they were modified
2536 $room_stay_records = $av_helper->loadSplitStayBusyRecords($ord['id']);
2537 // loop over all rooms to check the requested operations
2538 foreach ($ordersrooms as $ind => $or) {
2539 if (!VikRequest::getInt('room_modify_dates' . $ind, 0, 'request') || !isset($room_stay_records[$ind]) || empty($room_stay_records[$ind]['id'])) {
2540 // toggle is disabled or data is missing
2541 continue;
2542 }
2543 if (isset($room_modify_dates[$ind]) && !empty($room_modify_dates[$ind]['checkin']) && !empty($room_modify_dates[$ind]['checkout'])) {
2544 // calculate the check-in and check-out timestamps, we expect them to be different from the global booking dates
2545 $room_checkin = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkin'], $pcheckinh, $pcheckinm);
2546 $room_checkout = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkout'], $pcheckouth, $pcheckoutm);
2547 $room_realback = $turnover_secs + $room_checkout;
2548 // we don't need to check if the dates are different, we just update the record
2549 $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $room_checkin . ", `checkout`=" . $room_checkout . ", `realback`=" . $room_realback . " WHERE `id`=" . (int)$room_stay_records[$ind]['id'] . ";";
2550 $dbo->setQuery($q);
2551 $dbo->execute();
2552 // inject new room stay timestamps
2553 $ordersrooms[$ind]['modified_checkin'] = $room_checkin;
2554 $ordersrooms[$ind]['modified_checkout'] = $room_checkout;
2555 }
2556 }
2557 }
2558
2559 // add room to existing (confirmed) booking
2560 if ($room_added === true) {
2561 // add busy record for the new room unit
2562 $q = "INSERT INTO `#__vikbooking_busy` (`idroom`,`checkin`,`checkout`,`realback`) VALUES(".$padd_room_id.", ".$dbo->quote($first).", ".$dbo->quote($second).", ".$dbo->quote($realback).");";
2563 $dbo->setQuery($q);
2564 $dbo->execute();
2565 $newbusyid = $dbo->insertid();
2566 $q = "INSERT INTO `#__vikbooking_ordersbusy` (`idorder`,`idbusy`) VALUES(".$ord['id'].", ".(int)$newbusyid.");";
2567 $dbo->setQuery($q);
2568 $dbo->execute();
2569 }
2570
2571 // remove room from existing (confirmed) booking
2572 if ($room_removed !== false) {
2573 // remove busy record for the removed room
2574 if ($ord['split_stay'] && !empty($split_stay_data) && !empty($room_removed_index)) {
2575 // in case of split stay we want to remove the exact dates of the previously booked room
2576 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'])) {
2577 // remove the exact records
2578 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`=" . $room_stay_dates[$room_removed_index]['id'] . " AND `idroom`=" . $room_removed . ";";
2579 $dbo->setQuery($q);
2580 $dbo->execute();
2581 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=" . $ord['id'] . " AND `idbusy`=" . $room_stay_dates[$room_removed_index]['id'] . ";";
2582 $dbo->setQuery($q);
2583 $dbo->execute();
2584 }
2585 } else {
2586 // regularly remove the first matching room
2587 foreach ($allbusy as $bb) {
2588 if ($bb['idroom'] == $room_removed) {
2589 // remove the first room with this ID that was booked
2590 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`=".$bb['id']." AND `idroom`=".$room_removed.";";
2591 $dbo->setQuery($q);
2592 $dbo->execute();
2593 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".$ord['id']." AND `idbusy`=".$bb['id'].";";
2594 $dbo->setQuery($q);
2595 $dbo->execute();
2596 break;
2597 }
2598 }
2599 }
2600 }
2601
2602 if ($ord['checkin'] != $first || $ord['checkout'] != $second || $room_added === true || $room_removed !== false) {
2603 // unset any previously booked room due to calendar sharing
2604 VikBooking::cleanSharedCalendarsBusy($ord['id']);
2605 // check if some of the rooms booked have shared calendars
2606 VikBooking::updateSharedCalendars($ord['id'], array(), $first, $second);
2607
2608 // invoke Channel Manager
2609 $vcm_autosync = VikBooking::vcmAutoUpdate();
2610 if ($vcm_autosync > 0) {
2611 $vcm_obj = VikBooking::getVcmInvoker();
2612 $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
2613 $sync_result = $vcm_obj->doSync();
2614 if ($sync_result === false) {
2615 $vcm_err = $vcm_obj->getError();
2616 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
2617 }
2618 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
2619 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>');
2620 }
2621 //
2622 }
2623 }
2624
2625 $upd_esit = JText::translate('RESUPDATED');
2626
2627 // update the room rates
2628 $isdue = 0;
2629 $tot_taxes = 0;
2630 $tot_city_taxes = 0;
2631 $tot_fees = 0;
2632 $tot_damage_dep = 0;
2633 $doup = true;
2634 $tars = array();
2635 $cust_costs = array();
2636 $rooms_costs_map = array();
2637 $arrpeople = array();
2638 foreach ($ordersrooms as $kor => $or) {
2639 // remove from existing booking
2640 if ($room_removed !== false) {
2641 if ($or['id'] == $prm_room_oid) {
2642 // do not consider this room for the calculation of the new total amount
2643 // we can unset this array for later use, because the channel manager has already been invoked.
2644 unset($ordersrooms[$kor]);
2645 continue;
2646 }
2647 }
2648
2649 // room index starting from 1
2650 $num = $kor + 1;
2651
2652 // default values to be considered
2653 $room_nights = $daysdiff;
2654 $room_checkin = $ord['checkin'];
2655 $room_checkout = $ord['checkout'];
2656 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'])) {
2657 // overwrite default values in case of split-stay booking
2658 $room_nights = $room_stay_dates[$kor]['new_nights'];
2659 $room_checkin = $room_stay_dates[$kor]['new_checkin'];
2660 $room_checkout = $room_stay_dates[$kor]['new_checkout'];
2661 } elseif (!$ord['split_stay'] && ($room_modify_dates[$kor]['checkin_ts'] ?? null)) {
2662 // overwrite default values in case of multi-room reservation with different stay dates
2663 $room_nights = $room_modify_dates[$kor]['stay_nights'] ?? $room_nights;
2664 $room_checkin = $room_modify_dates[$kor]['checkin_ts'];
2665 $room_checkout = $room_modify_dates[$kor]['checkout_ts'];
2666 }
2667
2668 $padults = VikRequest::getString('adults' . $num, '', 'request');
2669 $pchildren = VikRequest::getString('children' . $num, '', 'request');
2670 $ppets = VikRequest::getInt('pets' . $num, 0, 'request');
2671 if (strlen($padults) || strlen($pchildren)) {
2672 $arrpeople[$num]['adults'] = (int)$padults;
2673 $arrpeople[$num]['children'] = (int)$pchildren;
2674 $arrpeople[$num]['pets'] = $ppets;
2675 }
2676 $ppriceid = VikRequest::getString('priceid'.$num, '', 'request');
2677 $polderpriceid = VikRequest::getString('olderpriceid'.$num, '', 'request');
2678 $ppkgid = VikRequest::getString('pkgid'.$num, '', 'request');
2679 $pcust_cost = VikRequest::getString('cust_cost'.$num, '', 'request');
2680 $paliq = VikRequest::getString('aliq'.$num, '', 'request');
2681 $pcust_cpolicy_id = VikRequest::getInt('cust_cpolicy_id'.$num, 0, 'request');
2682 if ($is_package === true && !empty($ppkgid)) {
2683 $pkg_cost = $or['cust_cost'];
2684 $pkg_idiva = $or['cust_idiva'];
2685 $pkg_info = VikBooking::getPackage($ppkgid);
2686 if (is_array($pkg_info) && count($pkg_info) > 0) {
2687 $use_adults = array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num]) ? $arrpeople[$num]['adults'] : $or['adults'];
2688 $pkg_cost = $pkg_info['pernight_total'] == 1 ? ($pkg_info['cost'] * $room_nights) : $pkg_info['cost'];
2689 $pkg_cost = $pkg_info['perperson'] == 1 ? ($pkg_cost * ($use_adults > 0 ? $use_adults : 1)) : $pkg_cost;
2690 $pkg_cost = VikBooking::sayPackagePlusIva($pkg_cost, $pkg_info['idiva']);
2691 }
2692 $cust_costs[$num] = array('pkgid' => $ppkgid, 'cust_cost' => $pkg_cost, 'aliq' => $pkg_idiva);
2693 $isdue += $pkg_cost;
2694 $cost_minus_tax = VikBooking::sayPackageMinusIva($pkg_cost, $pkg_idiva);
2695 $tot_taxes += ($pkg_cost - $cost_minus_tax);
2696 continue;
2697 }
2698 if (empty($ppriceid) && !empty($pcust_cost) && floatval($pcust_cost) > 0) {
2699 $cust_costs[$num] = [
2700 'cust_cost' => $pcust_cost,
2701 'aliq' => $paliq,
2702 'cust_cpolicy_id' => $pcust_cpolicy_id,
2703 ];
2704 $cost_after_tax = VikBooking::sayPackagePlusIva((float)$pcust_cost, (int)$paliq);
2705 $isdue += $cost_after_tax;
2706 $cost_minus_tax = VikBooking::sayPackageMinusIva((float)$pcust_cost, (int)$paliq);
2707 $tot_taxes += ($cost_after_tax - $cost_minus_tax);
2708 continue;
2709 }
2710
2711 // load room rates for the requested rate plan and nights
2712 $q = "SELECT * FROM `#__vikbooking_dispcost` WHERE `idroom`=" . (int)$or['idroom'] . " AND `days`=" . $room_nights . " AND `idprice`=" . (int)$ppriceid . ";";
2713 $dbo->setQuery($q);
2714 $tar = $dbo->loadAssocList();
2715 if (!$tar) {
2716 $doup = false;
2717 break;
2718 }
2719
2720 /**
2721 * The current price may be different from the price paid at the time of booking.
2722 * Check whether it has been asked to keep the old price of the time of booking.
2723 *
2724 * @since 1.13.0 (J) - 1.3.0 (WP)
2725 */
2726 $old_price_used = false;
2727 if (!empty($polderpriceid)) {
2728 $older_info = explode(':', $polderpriceid);
2729 if ((int)$older_info[0] == (int)$ppriceid) {
2730 $old_price = isset($older_info[1]) ? (float)$older_info[1] : 0;
2731 if ($old_price > 0) {
2732 // we override the 'cost' property of the tar array by taking the previous cost
2733 $old_price_used = true;
2734 $tar[0]['cost'] = $old_price;
2735 }
2736 }
2737 }
2738
2739 if (!$old_price_used) {
2740 // apply seasonal rates
2741 $tar = VikBooking::applySeasonsRoom($tar, $room_checkin, $room_checkout);
2742 }
2743
2744 // different usage
2745 if (!$old_price_used && $or['fromadult'] <= $or['adults'] && $or['toadult'] >= $or['adults']) {
2746 // apply OBP rules
2747 $tar = VBORoomHelper::getInstance()->applyOBPRules($tar, $or, $or['adults']);
2748 }
2749
2750 $cost_plus_tax = VikBooking::sayCostPlusIva($tar[0]['cost'], $tar[0]['idprice']);
2751 $isdue += $cost_plus_tax;
2752 if ($cost_plus_tax == $tar[0]['cost']) {
2753 $cost_minus_tax = VikBooking::sayCostMinusIva($tar[0]['cost'], $tar[0]['idprice']);
2754 $tot_taxes += ($tar[0]['cost'] - $cost_minus_tax);
2755 } else {
2756 $tot_taxes += ($cost_plus_tax - $tar[0]['cost']);
2757 }
2758 $tars[$num] = $tar;
2759 $rooms_costs_map[$num] = $tar[0]['cost'];
2760 }
2761
2762 if ($doup === true) {
2763 if ($room_added === true) {
2764 // add room to existing booking may require to increase the total amount, and taxes
2765 $padd_room_price = VikRequest::getFloat('add_room_price', 0, 'request');
2766 $paliq_add_room = VikRequest::getInt('aliq_add_room', 0, 'request');
2767 if (!empty($padd_room_price) && floatval($padd_room_price) > 0) {
2768 $isdue += (float)$padd_room_price;
2769 $cost_minus_tax = VikBooking::sayPackageMinusIva((float)$padd_room_price, (int)$paliq_add_room);
2770 $tot_taxes += ((float)$padd_room_price - $cost_minus_tax);
2771 }
2772 }
2773
2774 // load options
2775 $q = "SELECT * FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
2776 $dbo->setQuery($q);
2777 $toptionals = $dbo->loadAssocList();
2778
2779 foreach ($ordersrooms as $kor => $or) {
2780 $num = $kor + 1;
2781
2782 // default values to be considered
2783 $room_nights = $daysdiff;
2784 $room_checkin = $ord['checkin'];
2785 $room_checkout = $ord['checkout'];
2786 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'])) {
2787 // overwrite default values in case of split-stay booking
2788 $room_nights = $room_stay_dates[$kor]['new_nights'];
2789 $room_checkin = $room_stay_dates[$kor]['new_checkin'];
2790 $room_checkout = $room_stay_dates[$kor]['new_checkout'];
2791 } elseif (!$ord['split_stay'] && ($room_modify_dates[$kor]['checkin_ts'] ?? null)) {
2792 // overwrite default values in case of multi-room reservation with different stay dates
2793 $room_nights = $room_modify_dates[$kor]['stay_nights'] ?? $room_nights;
2794 $room_checkin = $room_modify_dates[$kor]['checkin_ts'];
2795 $room_checkout = $room_modify_dates[$kor]['checkout_ts'];
2796 }
2797
2798 $pt_first_name = VikRequest::getString('t_first_name'.$num, '', 'request');
2799 $pt_last_name = VikRequest::getString('t_last_name'.$num, '', 'request');
2800 $wop = "";
2801
2802 foreach ($toptionals as $opt) {
2803 // option params
2804 $opt_params = !empty($opt['oparams']) ? json_decode($opt['oparams'], true) : [];
2805 $opt_params = is_array($opt_params) ? $opt_params : [];
2806 if (!empty($opt['ageintervals']) && ($or['children'] > 0 || isset($arrpeople[$num]['children']))) {
2807 $tmpvar = VikRequest::getInt('optid'.$num.$opt['id'], []);
2808 if (is_array($tmpvar) && $tmpvar && ($arrpeople[$num]['children'] ?? 0)) {
2809 $opt['quan'] = 1;
2810 $optagenames = VikBooking::getOptionIntervalsAges($opt['ageintervals']);
2811 $optagepcent = VikBooking::getOptionIntervalsPercentage($opt['ageintervals']);
2812 $optageovrct = VikBooking::getOptionIntervalChildOverrides($opt, (isset($arrpeople[$num]) ? $arrpeople[$num]['adults'] : 0), (isset($arrpeople[$num]) ? $arrpeople[$num]['children'] : 0));
2813 $optorigname = $opt['name'];
2814 foreach ($tmpvar as $child_num => $chvar) {
2815 $ageintervals_child_string = isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $opt['ageintervals'];
2816 $optagecosts = VikBooking::getOptionIntervalsCosts($ageintervals_child_string);
2817 $optorigcost = $optagecosts[($chvar - 1)];
2818 $tmp_room_cost = 0;
2819 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 1) {
2820 // percentage value of the adults tariff
2821 if ($is_package !== true && array_key_exists($num, $tars)) {
2822 // type of price
2823 $tmp_room_cost = $tars[$num][0]['cost'];
2824 $optorigcost = $tars[$num][0]['cost'] * $optagecosts[($chvar - 1)] / 100;
2825 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2826 // package
2827 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2828 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2829 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2830 // custom rate + custom tax rate
2831 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2832 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2833 }
2834 } elseif (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 2) {
2835 // percentage value of room base cost
2836 if ($is_package !== true && array_key_exists($num, $tars)) {
2837 // type of price
2838 $usecost = isset($tars[$num][0]['room_base_cost']) ? $tars[$num][0]['room_base_cost'] : $tars[$num][0]['cost'];
2839 $tmp_room_cost = $usecost;
2840 $optorigcost = $usecost * $optagecosts[($chvar - 1)] / 100;
2841 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2842 // package
2843 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2844 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2845 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2846 // custom rate + custom tax rate
2847 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2848 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2849 }
2850 }
2851 $opt['cost'] = $optorigcost;
2852 $opt['name'] = $optorigname.' ('.$optagenames[($chvar - 1)].')';
2853 $opt['chageintv'] = $chvar;
2854 $wop.=$opt['id'].":".$opt['quan']."-".$chvar.";";
2855 $realcost = (intval($opt['perday']) == 1 ? ($opt['cost'] * $room_nights * $opt['quan']) : ($opt['cost'] * $opt['quan']));
2856 if (!empty($opt['maxprice']) && $opt['maxprice'] > 0 && $realcost > $opt['maxprice']) {
2857 $realcost = $opt['maxprice'];
2858 }
2859
2860 /**
2861 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
2862 *
2863 * @since 1.17.7 (J) - 1.7.7 (WP)
2864 */
2865 $custom_calc_booking = array_merge($ord, ['days' => $room_nights]);
2866 $custom_calc_booking_room = array_merge($or, ($arrpeople[$num] ?? []), ($tmp_room_cost ? ['room_cost' => $tmp_room_cost] : []));
2867 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$opt, $custom_calc_booking, $custom_calc_booking_room]);
2868 if ($custom_calculation) {
2869 $realcost = (float) $custom_calculation[0];
2870 }
2871
2872 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $opt['idiva']);
2873 if ($opt['is_citytax'] == 1) {
2874 $tot_city_taxes += $tmpopr;
2875 } elseif ($opt['is_fee'] == 1) {
2876 $tot_fees += $tmpopr;
2877 } elseif ($opt_params['damagedep'] ?? 0) {
2878 $tot_damage_dep += $tmpopr;
2879 }
2880 // VBO 1.11 - always calculate the amount of tax no matter if this is already a tax or a fee
2881 if ($tmpopr == $realcost) {
2882 $opt_minus_iva = VikBooking::sayOptionalsMinusIva($realcost, $opt['idiva']);
2883 $tot_taxes += ($realcost - $opt_minus_iva);
2884 } else {
2885 $tot_taxes += ($tmpopr - $realcost);
2886 }
2887 //
2888 $isdue += $tmpopr;
2889 }
2890 }
2891 } else {
2892 $tmpvar = VikRequest::getString('optid'.$num.$opt['id'], '', 'request');
2893 if (is_array($tmpvar)) {
2894 // prevent errors for unexpected option configuration, probably missing age intervals
2895 continue;
2896 }
2897 $tmp_room_cost = 0;
2898 // options forced per child fix, no age intervals, like children tourist taxes
2899 $forcedquan = 1;
2900 $forceperday = false;
2901 $forceperchild = false;
2902 if (intval($opt['forcesel']) == 1 && strlen($opt['forceval']) > 0 && strlen($tmpvar) > 0) {
2903 $forceparts = explode("-", $opt['forceval']);
2904 $forcedquan = intval($forceparts[0]);
2905 $forceperday = intval($forceparts[1]) == 1 ? true : false;
2906 $forceperchild = intval($forceparts[2]) == 1 ? true : false;
2907 $tmpvar = $forcedquan;
2908 $tmpvar = $forceperchild === true && array_key_exists($num, $arrpeople) && array_key_exists('children', $arrpeople[$num]) ? ($tmpvar * $arrpeople[$num]['children']) : $tmpvar;
2909 }
2910 //
2911 if (!empty($tmpvar)) {
2912 $wop .= $opt['id'].":".$tmpvar.";";
2913 // options percentage cost of the room total fee
2914 if ($is_package !== true && array_key_exists($num, $tars)) {
2915 // type of price
2916 $tmp_room_cost = $tars[$num][0]['cost'];
2917 $deftar_basecosts = $tars[$num][0]['cost'];
2918 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2919 // package
2920 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2921 $deftar_basecosts = $cust_costs[$num]['cust_cost'];
2922 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2923 // custom rate + custom tax rate
2924 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2925 $deftar_basecosts = $cust_costs[$num]['cust_cost'];
2926 }
2927 $opt['cost'] = (int)$opt['pcentroom'] ? ($deftar_basecosts * $opt['cost'] / 100) : $opt['cost'];
2928 //
2929 $realcost = (intval($opt['perday']) == 1 ? ($opt['cost'] * $room_nights * $tmpvar) : ($opt['cost'] * $tmpvar));
2930 if (!empty($opt['maxprice']) && $opt['maxprice'] > 0 && $realcost > $opt['maxprice']) {
2931 $realcost = $opt['maxprice'];
2932 if (intval($opt['hmany']) == 1 && intval($tmpvar) > 1) {
2933 $realcost = $opt['maxprice'] * $tmpvar;
2934 }
2935 }
2936 if ($opt['perperson'] == 1) {
2937 $num_adults = array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num]) ? $arrpeople[$num]['adults'] : 1;
2938 $realcost = $realcost * $num_adults;
2939 }
2940
2941 /**
2942 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
2943 *
2944 * @since 1.17.7 (J) - 1.7.7 (WP)
2945 */
2946 $custom_calc_booking = array_merge($ord, ['days' => $room_nights]);
2947 $custom_calc_booking_room = array_merge($or, ($arrpeople[$num] ?? []), ($tmp_room_cost ? ['room_cost' => $tmp_room_cost] : []));
2948 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$opt, $custom_calc_booking, $custom_calc_booking_room]);
2949 if ($custom_calculation) {
2950 $realcost = (float) $custom_calculation[0];
2951 }
2952
2953 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $opt['idiva']);
2954 if ($opt['is_citytax'] == 1) {
2955 $tot_city_taxes += $tmpopr;
2956 } elseif ($opt['is_fee'] == 1) {
2957 $tot_fees += $tmpopr;
2958 } elseif ($opt_params['damagedep'] ?? 0) {
2959 $tot_damage_dep += $tmpopr;
2960 }
2961 // VBO 1.11 - always calculate the amount of tax no matter if this is already a tax or a fee
2962 if ($tmpopr == $realcost) {
2963 $opt_minus_iva = VikBooking::sayOptionalsMinusIva($realcost, $opt['idiva']);
2964 $tot_taxes += ($realcost - $opt_minus_iva);
2965 } else {
2966 $tot_taxes += ($tmpopr - $realcost);
2967 }
2968 //
2969 $isdue += $tmpopr;
2970 }
2971 }
2972 }
2973
2974 $upd_fields = array();
2975 if ($is_package !== true && array_key_exists($num, $tars)) {
2976 // type of price
2977 $upd_fields[] = "`idtar`='".$tars[$num][0]['id']."'";
2978 $upd_fields[] = "`cust_cost`=NULL";
2979 $upd_fields[] = "`cust_idiva`=NULL";
2980 $upd_fields[] = "`cust_cpolicy_id`=NULL";
2981 $upd_fields[] = "`room_cost`=".(array_key_exists($num, $rooms_costs_map) ? $dbo->quote($rooms_costs_map[$num]) : "NULL");
2982 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2983 // packages do not update name or cost, just set again the same package ID to avoid risks of empty upd_fields to update
2984 $upd_fields[] = "`idtar`=NULL";
2985 $upd_fields[] = "`pkg_id`='".$cust_costs[$num]['pkgid']."'";
2986 $upd_fields[] = "`cust_cost`='".$cust_costs[$num]['cust_cost']."'";
2987 $upd_fields[] = "`cust_idiva`='".$cust_costs[$num]['aliq']."'";
2988 $upd_fields[] = "`cust_cpolicy_id`='" . (int) ($cust_costs[$num]['cust_cpolicy_id'] ?? 0) . "'";
2989 $upd_fields[] = "`room_cost`=NULL";
2990 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2991 // custom rate + custom tax rate
2992 $upd_fields[] = "`idtar`=NULL";
2993 $upd_fields[] = "`cust_cost`='".$cust_costs[$num]['cust_cost']."'";
2994 $upd_fields[] = "`cust_idiva`='".$cust_costs[$num]['aliq']."'";
2995 $upd_fields[] = "`cust_cpolicy_id`='" . (int) ($cust_costs[$num]['cust_cpolicy_id'] ?? 0) . "'";
2996 $upd_fields[] = "`room_cost`=NULL";
2997 // inject new room price
2998 $ordersrooms[$kor]['modified_price'] = $cust_costs[$num]['cust_cost'];
2999 }
3000 if ($toptionals) {
3001 $upd_fields[] = "`optionals`='".$wop."'";
3002 }
3003 if (!empty($pt_first_name) || !empty($pt_last_name)) {
3004 $upd_fields[] = "`t_first_name`=".$dbo->quote($pt_first_name);
3005 $upd_fields[] = "`t_last_name`=".$dbo->quote($pt_last_name);
3006 }
3007 if (array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num])) {
3008 $upd_fields[] = "`adults`=".intval($arrpeople[$num]['adults']);
3009 $upd_fields[] = "`children`=".intval($arrpeople[$num]['children']);
3010 if (isset($arrpeople[$num]['pets'])) {
3011 $upd_fields[] = "`pets`=" . $arrpeople[$num]['pets'];
3012 }
3013 }
3014
3015 /**
3016 * Meal plans at room-reservation level.
3017 *
3018 * @since 1.16.1 (J) - 1.6.1 (WP)
3019 */
3020 $pmealplans = VikRequest::getVar('mealplan' . $num, []);
3021 $upd_fields[] = "`meals`=" . ($pmealplans ? $dbo->q(json_encode($pmealplans)) : 'NULL');
3022
3023 // calculate the extra costs and increase taxes + isdue
3024 $extracosts_arr = array();
3025 if (count($pextracn) && isset($pextracn[$num]) && count($pextracn[$num])) {
3026 foreach ($pextracn[$num] as $eck => $ecn) {
3027 if ($ecn && array_key_exists($eck, $pextracc[$num]) && is_numeric($pextracc[$num][$eck])) {
3028 $ecidtax = array_key_exists($eck, $pextractx[$num]) && intval($pextractx[$num][$eck]) > 0 ? (int)$pextractx[$num][$eck] : '';
3029 $extracosts_arr[] = array(
3030 'name' => $ecn,
3031 'cost' => (float)$pextracc[$num][$eck],
3032 'idtax' => $ecidtax,
3033 'type' => isset($pextractype[$num][$eck]) ? $pextractype[$num][$eck] : '',
3034 'fk' => isset($pextracfk[$num][$eck]) ? (string)$pextracfk[$num][$eck] : '',
3035 'data' => isset($pextracdata[$num][$eck]) ? json_decode($pextracdata[$num][$eck]) : null,
3036 );
3037 $ecplustax = !empty($ecidtax) ? VikBooking::sayOptionalsPlusIva((float)$pextracc[$num][$eck], $ecidtax) : (float)$pextracc[$num][$eck];
3038 $ecminustax = !empty($ecidtax) ? VikBooking::sayOptionalsMinusIva((float)$pextracc[$num][$eck], $ecidtax) : (float)$pextracc[$num][$eck];
3039 $ectottax = (float)$pextracc[$num][$eck] - $ecminustax;
3040 $isdue += $ecplustax;
3041 $tot_taxes += $ectottax;
3042 }
3043 }
3044 }
3045
3046 if ($extracosts_arr) {
3047 $upd_fields[] = "`extracosts`=".$dbo->quote(json_encode($extracosts_arr));
3048 } else {
3049 $upd_fields[] = "`extracosts`=NULL";
3050 }
3051
3052 if ($upd_fields) {
3053 $q = "UPDATE `#__vikbooking_ordersrooms` SET ".implode(', ', $upd_fields)." WHERE `idorder`=".$ord['id']." AND `idroom`='".$or['idroom']."' AND `id`='".$or['id']."';";
3054 $dbo->setQuery($q);
3055 $dbo->execute();
3056 }
3057 }
3058
3059 // update split stay transient record if not confirmed booking
3060 if ($ord['split_stay'] && $ord['status'] != 'confirmed' && !empty($room_stay_dates) && !empty($split_stay_data)) {
3061 /**
3062 * Important: if no rates have been selected for all rooms, we won't enter this inner statement.
3063 * It is necessary to select a rate plan for each room in order to update the split stay data.
3064 */
3065 $new_room_stay_dates = [];
3066 foreach ($room_stay_dates as $kor => $room_stay_info) {
3067 // clone the current information
3068 $clean_room_stay_info = $room_stay_info;
3069 // set new stay values
3070 if (!empty($clean_room_stay_info['checkin_ts'])) {
3071 $clean_room_stay_info['checkin_ts'] = $clean_room_stay_info['new_checkin'];
3072 $clean_room_stay_info['checkout_ts'] = $clean_room_stay_info['new_checkout'];
3073 } else {
3074 $clean_room_stay_info['checkin'] = $clean_room_stay_info['new_checkin'];
3075 $clean_room_stay_info['checkout'] = $clean_room_stay_info['new_checkout'];
3076 }
3077 $clean_room_stay_info['nights'] = $clean_room_stay_info['new_nights'];
3078 // clean up unnecessary keys
3079 unset($clean_room_stay_info['new_checkin'], $clean_room_stay_info['new_checkout'], $clean_room_stay_info['new_nights']);
3080 // push new array info
3081 $new_room_stay_dates[$kor] = $clean_room_stay_info;
3082 }
3083 // update configuration record
3084 VBOFactory::getConfig()->set('split_stay_' . $ord['id'], json_encode($new_room_stay_dates));
3085 }
3086
3087 // make sure to re-apply the discount with the coupon code
3088 if ($ord['coupon']) {
3089 $expcoupon = explode(";", $ord['coupon']);
3090 $isdue -= $expcoupon[1];
3091 }
3092
3093 // make sure to apply any previously refunded amount
3094 if ($ord['refund'] > 0) {
3095 $isdue -= $ord['refund'];
3096 }
3097
3098 // update totals
3099 $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'].";";
3100 $dbo->setQuery($q);
3101 $dbo->execute();
3102 $upd_esit = JText::translate('VBORESRATESUPDATED');
3103
3104 // Customer Booking
3105 if ($ord['status'] == 'confirmed') {
3106 $q = "SELECT `idcustomer` FROM `#__vikbooking_customers_orders` WHERE `idorder`=".$ord['id'].";";
3107 $dbo->setQuery($q);
3108 $customer_id = $dbo->loadResult();
3109 if ($customer_id) {
3110 $cpin = VikBooking::getCPinIstance();
3111 $cpin->is_admin = true;
3112 $cpin->updateBookingCommissions($ord['id'], $customer_id);
3113 }
3114 }
3115
3116 /**
3117 * Check for any OTA reporting action.
3118 *
3119 * @since 1.16.8 (J) - 1.6.8 (WP)
3120 */
3121 if (class_exists('VCMOtaReporting') && VCMOtaReporting::getInstance($ord)->stayChangeAllowed()) {
3122 // check if an OTA reporting action was selected
3123 $ota_stay_change_data = [];
3124 $ota_stay_change_all = $app->input->getInt('ota_stay_change_all', 0);
3125 foreach ($ordersrooms as $kor => $or) {
3126 $ota_stay_change_room = [];
3127 if ($ota_stay_change_all) {
3128 // set room data for stay change
3129 $ota_stay_change_room = [
3130 'idroom' => $or['idroom'],
3131 'checkin' => date('Y-m-d', $first),
3132 'checkout' => date('Y-m-d', $second),
3133 ];
3134 if (isset($or['modified_price'])) {
3135 $ota_stay_change_room['price'] = $or['modified_price'];
3136 }
3137 } elseif ($app->input->getInt('ota_stay_change_room_' . $kor, 0) && !empty($or['modified_checkin']) && !empty($or['modified_checkout'])) {
3138 // set room index data for stay change
3139 $ota_stay_change_room = [
3140 'idroom' => $or['idroom'],
3141 'index' => $kor,
3142 'checkin' => date('Y-m-d', $or['modified_checkin']),
3143 'checkout' => date('Y-m-d', $or['modified_checkout']),
3144 ];
3145 if (isset($or['modified_price'])) {
3146 $ota_stay_change_room['price'] = $or['modified_price'];
3147 }
3148 }
3149 if ($ota_stay_change_room) {
3150 // push room data for stay change
3151 $ota_stay_change_data[] = $ota_stay_change_room;
3152 }
3153 }
3154
3155 if ($ota_stay_change_data) {
3156 // notify the OTA through Vik Channel Manager
3157 $ota_reporting = VCMOtaReporting::getInstance();
3158 $ota_result = $ota_reporting->notifyStayChange($ota_stay_change_data);
3159 if (!$ota_result) {
3160 // enqueue error message
3161 $app->enqueueMessage($ota_reporting->getError(), 'error');
3162 }
3163 }
3164 }
3165 }
3166
3167 // Booking History
3168 $history_descr = "({$user->name}) " . VikBooking::getLogBookingModification($ord, $room_stay_dates);
3169 if (!$opertwounits && $forcebooking) {
3170 $history_descr .= "\n" . JText::translate('VBO_FORCED_BOOKDATES');
3171 }
3172 VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', $history_descr);
3173
3174 // enqueue result message
3175 $app->enqueueMessage($upd_esit);
3176 } else {
3177 VikError::raiseWarning('', JText::translate('VBROOMNOTRIT')." ".date($df.' H:i', $first)." ".JText::translate('VBROOMNOTCONSTO')." ".date($df.' H:i', $second));
3178 $allow_force = 1;
3179 $app->enqueueMessage(JText::translate('VBO_BOOKING_SHOULDFORCE'), 'notice');
3180 }
3181
3182 if ($callback == 'geninvoices') {
3183 $app->redirect("index.php?option=com_vikbooking&task=orders&cid[]=".$ord['id']."&confirmgen=1");
3184 } else {
3185 $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" : ""));
3186 }
3187 }
3188
3189 public function removebusy()
3190 {
3191 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
3192 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
3193 }
3194
3195 $dbo = JFactory::getDbo();
3196 $app = JFactory::getApplication();
3197
3198 $user = JFactory::getUser();
3199 $config = VBOFactory::getConfig();
3200
3201 $prev_conf_ids = [];
3202 $pidorder = VikRequest::getInt('idorder', 0, 'request');
3203 $pgoto = VikRequest::getString('goto', '', 'request');
3204
3205 $purged = false;
3206
3207 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder;
3208 $dbo->setQuery($q, 0, 1);
3209 $row = $dbo->loadAssoc();
3210
3211 // check for any cancellation constraints
3212 $canc_denied = false;
3213 if ($row && class_exists('VCMFeesCancellation')) {
3214 // let VCM detect if there are any constraints for the cancellation
3215 $canc_denied = VCMFeesCancellation::getInstance($row, $anew = true)->isBookingConstrained();
3216 if ($canc_denied) {
3217 // set error message
3218 $canc_deny_error = VCMFeesCancellation::getInstance()->getError();
3219 if ($canc_deny_error) {
3220 $app->enqueueMessage($canc_deny_error, 'error');
3221 }
3222 }
3223 }
3224
3225 if ($row && !$canc_denied) {
3226 // set status to cancelled
3227 if ($row['status'] != 'cancelled') {
3228 $q = "UPDATE `#__vikbooking_orders` SET `status`='cancelled' WHERE `id`=".(int)$row['id'].";";
3229 $dbo->setQuery($q);
3230 $dbo->execute();
3231 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($row['id']) . ";";
3232 $dbo->setQuery($q);
3233 $dbo->execute();
3234 if ($row['status'] == 'confirmed') {
3235 $prev_conf_ids[] = $row['id'];
3236 }
3237 // Booking History
3238 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('CB', "({$user->name})");
3239 }
3240
3241 /**
3242 * In case of pending bookings being cancelled, schedule the release through VCM.
3243 *
3244 * @since 1.18.8 (J) - 1.8.8 (WP)
3245 */
3246 if ($row['status'] == 'standby' && method_exists('VCMRequestAvailability', 'setForRelease')) {
3247 // let the CM schedule the release of the involved and unconfirmed booking IDs, if needed
3248 VCMRequestAvailability::getInstance()->setForRelease([$row['id']]);
3249 }
3250
3251 // free records up
3252 $q = "SELECT * FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
3253 $dbo->setQuery($q);
3254 $ordbusy = $dbo->loadAssocList();
3255 if ($ordbusy) {
3256 foreach ($ordbusy as $ob) {
3257 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`='".$ob['idbusy']."';";
3258 $dbo->setQuery($q);
3259 $dbo->execute();
3260 }
3261 }
3262
3263 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
3264 $dbo->setQuery($q);
3265 $dbo->execute();
3266
3267 // check for purge removal
3268 if ($row['status'] == 'cancelled') {
3269 $q = "DELETE FROM `#__vikbooking_customers_orders` WHERE `idorder`=" . intval($row['id']) . ";";
3270 $dbo->setQuery($q);
3271 $dbo->execute();
3272 $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `idorder`=".(int)$row['id'].";";
3273 $dbo->setQuery($q);
3274 $dbo->execute();
3275 $q = "DELETE FROM `#__vikbooking_orderhistory` WHERE `idorder`=".(int)$row['id'].";";
3276 $dbo->setQuery($q);
3277 $dbo->execute();
3278 $q = "DELETE FROM `#__vikbooking_orders` WHERE `id`=".(int)$row['id'].";";
3279 $dbo->setQuery($q);
3280 $dbo->execute();
3281 // in case of split stay booking, remove the transient
3282 if ($row['split_stay']) {
3283 $config->remove('split_stay_' . $row['id']);
3284 }
3285 // turn flag on
3286 $purged = true;
3287 }
3288
3289 // enqueue message
3290 $app->enqueueMessage(JText::translate('VBMESSDELBUSY'));
3291 }
3292
3293 if ($prev_conf_ids) {
3294 $prev_conf_ids_str = '';
3295 foreach ($prev_conf_ids as $prev_id) {
3296 $prev_conf_ids_str .= '&cid[]='.$prev_id;
3297 }
3298 //Invoke Channel Manager
3299 $vcm_autosync = VikBooking::vcmAutoUpdate();
3300 if ($vcm_autosync > 0) {
3301 $vcm_obj = VikBooking::getVcmInvoker();
3302 $vcm_obj->setOids($prev_conf_ids)->setSyncType('cancel');
3303 $sync_result = $vcm_obj->doSync();
3304 if ($sync_result === false) {
3305 $vcm_err = $vcm_obj->getError();
3306 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
3307 }
3308 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
3309 $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');
3310 VikError::raiseNotice('', JText::translate('VBCHANNELMANAGERINVOKEASK').' <button type="button" class="btn btn-primary" onclick="document.location.href=\''.$vcm_sync_url.'\';">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button>');
3311 }
3312 //
3313 }
3314
3315 if ($pgoto == 'overv') {
3316 $app->redirect("index.php?option=com_vikbooking&task=overv");
3317 } elseif (!$purged) {
3318 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $pidorder);
3319 } else {
3320 $app->redirect("index.php?option=com_vikbooking&task=orders");
3321 }
3322
3323 $app->close();
3324 }
3325
3326 public function unlockrecords()
3327 {
3328 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
3329 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
3330 }
3331
3332 $ids = VikRequest::getVar('cid', array(0));
3333 if (@count($ids)) {
3334 $dbo = JFactory::getDBO();
3335 foreach ($ids as $d) {
3336 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `id`=".$dbo->quote($d).";";
3337 $dbo->setQuery($q);
3338 $dbo->execute();
3339 }
3340 }
3341 $mainframe = JFactory::getApplication();
3342 $mainframe->redirect("index.php?option=com_vikbooking");
3343 }
3344
3345 public function sortoption() {
3346 if (!JSession::checkToken('get')) {
3347 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3348 }
3349 $sortid = VikRequest::getVar('cid', array(0));
3350 $pmode = VikRequest::getString('mode', '', 'request');
3351 $dbo = JFactory::getDBO();
3352 $mainframe = JFactory::getApplication();
3353 if (!empty($pmode)) {
3354 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
3355 $dbo->setQuery($q);
3356 $dbo->execute();
3357 $totr = $dbo->getNumRows();
3358 if ($totr > 1) {
3359 $data = $dbo->loadAssocList();
3360 if ($pmode == "up") {
3361 foreach ($data as $v) {
3362 if ($v['id'] == $sortid[0]) {
3363 $y = $v['ordering'];
3364 }
3365 }
3366 if ($y && $y > 1) {
3367 $vik = $y - 1;
3368 $found = false;
3369 foreach ($data as $v) {
3370 if (intval($v['ordering']) == intval($vik)) {
3371 $found = true;
3372 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3373 $dbo->setQuery($q);
3374 $dbo->execute();
3375 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3376 $dbo->setQuery($q);
3377 $dbo->execute();
3378 break;
3379 }
3380 }
3381 if (!$found) {
3382 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3383 $dbo->setQuery($q);
3384 $dbo->execute();
3385 }
3386 }
3387 } elseif ($pmode == "down") {
3388 foreach ($data as $v) {
3389 if ($v['id'] == $sortid[0]) {
3390 $y = $v['ordering'];
3391 }
3392 }
3393 if ($y) {
3394 $vik = $y + 1;
3395 $found = false;
3396 foreach ($data as $v) {
3397 if (intval($v['ordering']) == intval($vik)) {
3398 $found = true;
3399 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3400 $dbo->setQuery($q);
3401 $dbo->execute();
3402 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3403 $dbo->setQuery($q);
3404 $dbo->execute();
3405 break;
3406 }
3407 }
3408 if (!$found) {
3409 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3410 $dbo->setQuery($q);
3411 $dbo->execute();
3412 }
3413 }
3414 }
3415 }
3416 $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
3417 } else {
3418 $mainframe->redirect("index.php?option=com_vikbooking");
3419 }
3420 }
3421
3422 public function sortpayment() {
3423 if (!JSession::checkToken('get')) {
3424 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3425 }
3426 $cid = VikRequest::getVar('cid', array(0));
3427 $sortid = $cid[0];
3428 $dbo = JFactory::getDBO();
3429 $mainframe = JFactory::getApplication();
3430 $pmode = VikRequest::getString('mode', '', 'request');
3431 if (!empty($pmode) && !empty($sortid)) {
3432 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_gpayments` ORDER BY `#__vikbooking_gpayments`.`ordering` ASC;";
3433 $dbo->setQuery($q);
3434 $dbo->execute();
3435 $totr=$dbo->getNumRows();
3436 if ($totr > 1) {
3437 $data = $dbo->loadAssocList();
3438 if ($pmode == "up") {
3439 foreach ($data as $v) {
3440 if ($v['id'] == $sortid) {
3441 $y = $v['ordering'];
3442 }
3443 }
3444 if ($y && $y > 1) {
3445 $vik = $y - 1;
3446 $found = false;
3447 foreach ($data as $v) {
3448 if (intval($v['ordering']) == intval($vik)) {
3449 $found = true;
3450 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3451 $dbo->setQuery($q);
3452 $dbo->execute();
3453 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3454 $dbo->setQuery($q);
3455 $dbo->execute();
3456 break;
3457 }
3458 }
3459 if (!$found) {
3460 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3461 $dbo->setQuery($q);
3462 $dbo->execute();
3463 }
3464 }
3465 } elseif ($pmode == "down") {
3466 foreach ($data as $v) {
3467 if ($v['id'] == $sortid) {
3468 $y = $v['ordering'];
3469 }
3470 }
3471 if ($y) {
3472 $vik = $y + 1;
3473 $found = false;
3474 foreach ($data as $v) {
3475 if (intval($v['ordering']) == intval($vik)) {
3476 $found=true;
3477 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3478 $dbo->setQuery($q);
3479 $dbo->execute();
3480 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3481 $dbo->setQuery($q);
3482 $dbo->execute();
3483 break;
3484 }
3485 }
3486 if (!$found) {
3487 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3488 $dbo->setQuery($q);
3489 $dbo->execute();
3490 }
3491 }
3492 }
3493 }
3494 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
3495 } else {
3496 $mainframe->redirect("index.php?option=com_vikbooking");
3497 }
3498 }
3499
3500 public function sortcarat() {
3501 if (!JSession::checkToken('get')) {
3502 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3503 }
3504 $sortid = VikRequest::getVar('cid', array(0));
3505 $pmode = VikRequest::getString('mode', '', 'request');
3506 $dbo = JFactory::getDBO();
3507 $mainframe = JFactory::getApplication();
3508 if (!empty($pmode)) {
3509 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_characteristics` ORDER BY `#__vikbooking_characteristics`.`ordering` ASC;";
3510 $dbo->setQuery($q);
3511 $dbo->execute();
3512 $totr = $dbo->getNumRows();
3513 if ($totr > 1) {
3514 $data = $dbo->loadAssocList();
3515 if ($pmode == "up") {
3516 foreach ($data as $v) {
3517 if ($v['id'] == $sortid[0]) {
3518 $y = $v['ordering'];
3519 }
3520 }
3521 if ($y && $y > 1) {
3522 $vik = $y - 1;
3523 $found = false;
3524 foreach ($data as $v) {
3525 if (intval($v['ordering']) == intval($vik)) {
3526 $found = true;
3527 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3528 $dbo->setQuery($q);
3529 $dbo->execute();
3530 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3531 $dbo->setQuery($q);
3532 $dbo->execute();
3533 break;
3534 }
3535 }
3536 if (!$found) {
3537 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3538 $dbo->setQuery($q);
3539 $dbo->execute();
3540 }
3541 }
3542 } elseif ($pmode == "down") {
3543 foreach ($data as $v) {
3544 if ($v['id'] == $sortid[0]) {
3545 $y = $v['ordering'];
3546 }
3547 }
3548 if ($y) {
3549 $vik = $y + 1;
3550 $found = false;
3551 foreach ($data as $v) {
3552 if (intval($v['ordering']) == intval($vik)) {
3553 $found = true;
3554 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3555 $dbo->setQuery($q);
3556 $dbo->execute();
3557 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3558 $dbo->setQuery($q);
3559 $dbo->execute();
3560 break;
3561 }
3562 }
3563 if (!$found) {
3564 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3565 $dbo->setQuery($q);
3566 $dbo->execute();
3567 }
3568 }
3569 }
3570 }
3571 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
3572 } else {
3573 $mainframe->redirect("index.php?option=com_vikbooking");
3574 }
3575 }
3576
3577 public function resendordemail() {
3578 $this->do_resendorderemail();
3579 }
3580
3581 public function sendcancordemail() {
3582 $this->do_resendorderemail(true);
3583 }
3584
3585 private function do_resendorderemail($cancellation = false)
3586 {
3587 $dbo = JFactory::getDbo();
3588 $app = JFactory::getApplication();
3589 $vbo_tn = VikBooking::getTranslator();
3590
3591 $cid = VikRequest::getVar('cid', array(0));
3592 $oid = (int)$cid[0];
3593
3594 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $oid . ";";
3595 $dbo->setQuery($q);
3596 $dbo->execute();
3597 if (!$dbo->getNumRows()) {
3598 $app->redirect("index.php?option=com_vikbooking&task=orders");
3599 $app->close();
3600 }
3601 $order = $dbo->loadAssoc();
3602
3603 // check if the language in use is the same as the one used during the checkout
3604 if (!empty($order['lang'])) {
3605 $lang = JFactory::getLanguage();
3606 if ($lang->getTag() != $order['lang']) {
3607 $lang->load('com_vikbooking', (VBOPlatformDetection::isWordPress() ? VIKBOOKING_LANG : JPATH_ADMINISTRATOR), $order['lang'], true);
3608 if (defined('_JEXEC') && !defined('ABSPATH')) {
3609 $lang->load('joomla', JPATH_ADMINISTRATOR, $order['lang'], true);
3610 }
3611 }
3612 if ($vbo_tn->getDefaultLang() != $order['lang']) {
3613 // force the translation to start because contents should be translated
3614 $vbo_tn::$force_tolang = $order['lang'];
3615 }
3616 }
3617
3618 // availability helper
3619 $av_helper = VikBooking::getAvailabilityInstance();
3620
3621 /**
3622 * Split stay reservation.
3623 *
3624 * @since 1.16.0 (J) - 1.6.0 (WP)
3625 */
3626 $room_stay_dates = [];
3627 if ($order['split_stay']) {
3628 if ($order['status'] == 'confirmed') {
3629 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($order['id']);
3630 } else {
3631 $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $order['id'], []);
3632 }
3633 // immediately count the number of nights of stay for each split room
3634 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
3635 if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
3636 // overwrite values for compatibility with non-confirmed bookings
3637 $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
3638 $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
3639 }
3640 $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
3641 // overwrite the whole array
3642 $room_stay_dates[$sps_r_k] = $sps_r_v;
3643 }
3644 }
3645
3646 // load rooms booked
3647 $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;";
3648 $dbo->setQuery($q);
3649 $dbo->execute();
3650 $ordersrooms = $dbo->loadAssocList();
3651 $vbo_tn->translateContents($ordersrooms, '#__vikbooking_rooms', array('id' => 'r_reference_id'));
3652
3653 $ftitle = VikBooking::getFrontTitle();
3654 $currencyname = VikBooking::getCurrencyName();
3655
3656 $turnover_secs = VikBooking::getHoursRoomAvail() * 3600;
3657 $realback = $turnover_secs + $order['checkout'];
3658
3659 $rooms = array();
3660 $tars = array();
3661 $arrpeople = array();
3662 $is_package = !empty($order['pkg']) ? true : false;
3663 $nowts = time();
3664 foreach ($ordersrooms as $kor => $or) {
3665 $num = $kor + 1;
3666 $rooms[$num] = $or;
3667 $arrpeople[$num]['adults'] = $or['adults'];
3668 $arrpeople[$num]['children'] = $or['children'];
3669 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3670 // package or custom cost set from the back-end
3671 continue;
3672 }
3673
3674 // determine the proper values for this room
3675 $room_nights = $order['days'];
3676 $room_checkin = $order['checkin'];
3677 $room_checkout = $order['checkout'];
3678 if ($order['split_stay'] && !empty($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) {
3679 $room_nights = $room_stay_dates[$kor]['nights'];
3680 $room_checkin = $room_stay_dates[$kor]['checkin'];
3681 $room_checkout = $room_stay_dates[$kor]['checkout'];
3682 }
3683
3684 // load tariff
3685 $q = "SELECT * FROM `#__vikbooking_dispcost` WHERE `id`=" . (int)$or['idtar'] . ";";
3686 $dbo->setQuery($q);
3687 $dbo->execute();
3688 if ($dbo->getNumRows() > 0) {
3689 $tar = $dbo->loadAssocList();
3690 $tar = VikBooking::applySeasonsRoom($tar, $room_checkin, $room_checkout);
3691
3692 // different usage
3693 $tar = VBORoomHelper::getInstance()->applyOBPRules($tar, $or, $or['adults']);
3694
3695 $tars[$num] = $tar[0];
3696 } else {
3697 VikError::raiseWarning('', JText::translate('VBERRNOFAREFOUND'));
3698 }
3699 }
3700
3701 $secdiff = $order['checkout'] - $order['checkin'];
3702 $daysdiff = $secdiff / 86400;
3703 if (is_int($daysdiff)) {
3704 if ($daysdiff < 1) {
3705 $daysdiff = 1;
3706 }
3707 } else {
3708 if ($daysdiff < 1) {
3709 $daysdiff = 1;
3710 } else {
3711 $sum = floor($daysdiff) * 86400;
3712 $newdiff = $secdiff - $sum;
3713 $maxhmore = VikBooking::getHoursMoreRb() * 3600;
3714 if ($maxhmore >= $newdiff) {
3715 $daysdiff = floor($daysdiff);
3716 } else {
3717 $daysdiff = ceil($daysdiff);
3718 }
3719 }
3720 }
3721
3722 $isdue = 0;
3723 $pricestr = array();
3724 $optstr = array();
3725 foreach ($ordersrooms as $kor => $or) {
3726 $num = $kor + 1;
3727
3728 // determine the proper values for this room
3729 $room_nights = $order['days'];
3730 $room_checkin = $order['checkin'];
3731 $room_checkout = $order['checkout'];
3732 if ($order['split_stay'] && !empty($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) {
3733 $room_nights = $room_stay_dates[$kor]['nights'];
3734 $room_checkin = $room_stay_dates[$kor]['checkin'];
3735 $room_checkout = $room_stay_dates[$kor]['checkout'];
3736 }
3737
3738 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3739 // package cost or cust_cost may not be inclusive of taxes if prices tax included is off
3740 $calctar = VikBooking::sayPackagePlusIva($or['cust_cost'], $or['cust_idiva']);
3741 $isdue += $calctar;
3742 $pricestr[$num] = (!empty($or['pkg_name']) ? $or['pkg_name'] : (!empty($or['otarplan']) ? ucwords($or['otarplan']) : JText::translate('VBOROOMCUSTRATEPLAN'))).": ".$calctar." ".$currencyname;
3743 } elseif (array_key_exists($num, $tars) && is_array($tars[$num])) {
3744 $display_rate = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3745 $calctar = VikBooking::sayCostPlusIva($display_rate, $tars[$num]['idprice']);
3746 $tars[$num]['calctar'] = $calctar;
3747 $isdue += $calctar;
3748 $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'] : "");
3749 }
3750 if (!empty($or['optionals'])) {
3751 $stepo = explode(";", $or['optionals']);
3752 foreach ($stepo as $roptkey => $oo) {
3753 if (empty($oo)) {
3754 continue;
3755 }
3756 $stept = explode(":", $oo);
3757 $q = "SELECT * FROM `#__vikbooking_optionals` WHERE `id`=" . $dbo->quote($stept[0]) . ";";
3758 $dbo->setQuery($q);
3759 $dbo->execute();
3760 if (!$dbo->getNumRows()) {
3761 continue;
3762 }
3763 $actopt = $dbo->loadAssocList();
3764 $vbo_tn->translateContents($actopt, '#__vikbooking_optionals', array(), array(), (!empty($order['lang']) ? $order['lang'] : null));
3765 $chvar = '';
3766 if (!empty($actopt[0]['ageintervals']) && $or['children'] > 0 && strstr($stept[1], '-') != false) {
3767 $optagenames = VikBooking::getOptionIntervalsAges($actopt[0]['ageintervals']);
3768 $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt[0]['ageintervals']);
3769 $optageovrct = VikBooking::getOptionIntervalChildOverrides($actopt[0], $or['adults'], $or['children']);
3770 $child_num = VikBooking::getRoomOptionChildNumber($or['optionals'], $actopt[0]['id'], $roptkey, $or['children']);
3771 $optagecosts = VikBooking::getOptionIntervalsCosts(isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $actopt[0]['ageintervals']);
3772 $agestept = explode('-', $stept[1]);
3773 $stept[1] = $agestept[0];
3774 $chvar = $agestept[1];
3775 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 1) {
3776 //percentage value of the adults tariff
3777 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3778 $optagecosts[($chvar - 1)] = $or['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
3779 } else {
3780 $display_rate = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3781 $optagecosts[($chvar - 1)] = $display_rate * $optagecosts[($chvar - 1)] / 100;
3782 }
3783 } elseif (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 2) {
3784 //VBO 1.10 - percentage value of room base cost
3785 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3786 $optagecosts[($chvar - 1)] = $or['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
3787 } else {
3788 $display_rate = isset($tars[$num]['room_base_cost']) ? $tars[$num]['room_base_cost'] : (!empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost']);
3789 $optagecosts[($chvar - 1)] = $display_rate * $optagecosts[($chvar - 1)] / 100;
3790 }
3791 }
3792 $actopt[0]['chageintv'] = $chvar;
3793 $actopt[0]['name'] .= ' ('.$optagenames[($chvar - 1)].')';
3794 $actopt[0]['quan'] = $stept[1];
3795 $realcost = (intval($actopt[0]['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $room_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
3796 } else {
3797 $actopt[0]['quan'] = $stept[1];
3798 // VBO 1.11 - options percentage cost of the room total fee
3799 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3800 $deftar_basecosts = $or['cust_cost'];
3801 } else {
3802 $deftar_basecosts = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3803 }
3804 $actopt[0]['cost'] = (int)$actopt[0]['pcentroom'] ? ($deftar_basecosts * $actopt[0]['cost'] / 100) : $actopt[0]['cost'];
3805 //
3806 $realcost = (intval($actopt[0]['perday']) == 1 ? ($actopt[0]['cost'] * $room_nights * $stept[1]) : ($actopt[0]['cost'] * $stept[1]));
3807 }
3808 if (!empty($actopt[0]['maxprice']) && $actopt[0]['maxprice'] > 0 && $realcost > $actopt[0]['maxprice']) {
3809 $realcost = $actopt[0]['maxprice'];
3810 if (intval($actopt[0]['hmany']) == 1 && intval($stept[1]) > 1) {
3811 $realcost = $actopt[0]['maxprice'] * $stept[1];
3812 }
3813 }
3814 if ($actopt[0]['perperson'] == 1) {
3815 $realcost = $realcost * $or['adults'];
3816 }
3817
3818 /**
3819 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
3820 *
3821 * @since 1.17.7 (J) - 1.7.7 (WP)
3822 */
3823 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt[0], $order, $or]);
3824 if ($custom_calculation) {
3825 $realcost = (float) $custom_calculation[0];
3826 }
3827
3828 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt[0]['idiva']);
3829 $isdue += $tmpopr;
3830 $optstr[$num][] = ($stept[1] > 1 ? $stept[1] . " " : "") . $actopt[0]['name'] . ": " . $tmpopr . " " . $currencyname . "\n";
3831 }
3832 }
3833
3834 // custom extra costs
3835 if (!empty($or['extracosts'])) {
3836 $cur_extra_costs = json_decode($or['extracosts'], true);
3837 foreach ($cur_extra_costs as $eck => $ecv) {
3838 $ecplustax = !empty($ecv['idtax']) ? VikBooking::sayOptionalsPlusIva($ecv['cost'], $ecv['idtax']) : $ecv['cost'];
3839 $isdue += $ecplustax;
3840 $optstr[$num][] = $ecv['name'] . ": " . $ecplustax . " " . $currencyname."\n";
3841 }
3842 }
3843 }
3844
3845 // coupon
3846 $usedcoupon = false;
3847 $origisdue = $isdue;
3848 if (strlen($order['coupon']) > 0) {
3849 $usedcoupon = true;
3850 $expcoupon = explode(";", $order['coupon']);
3851 $isdue = $isdue - $expcoupon[1];
3852 }
3853
3854 // make sure to apply any previously refunded amount
3855 if ($order['refund'] > 0) {
3856 $isdue -= $order['refund'];
3857 }
3858
3859 // ConfirmationNumber
3860 $confirmnumber = $order['confirmnumber'];
3861
3862 $esit_mess = JText::sprintf('VBORDEREMAILRESENT', $order['custmail']);
3863 $status_str = JText::translate('VBCOMPLETED');
3864 if ($cancellation) {
3865 $confirmnumber = '';
3866 $esit_mess = JText::sprintf('VBCANCORDEREMAILSENT', $order['custmail']);
3867 $status_str = JText::translate('VBCANCELLED');
3868 } elseif ($order['status'] == 'standby') {
3869 $confirmnumber = '';
3870 $status_str = JText::translate('VBWAITINGFORPAYMENT');
3871 }
3872 $app->enqueueMessage($esit_mess);
3873
3874 // force the original total amount if rates have changed
3875 if (number_format($isdue, 2) != number_format($order['total'], 2)) {
3876 $isdue = $order['total'];
3877 }
3878
3879 // send email notification to guest (by ignoring the configuration settings)
3880 VikBooking::sendBookingEmail($order['id'], ['guest'], $send = true, $no_config = true);
3881
3882 if ($cancellation) {
3883 /**
3884 * If "send cancellation email", we log the event in the history.
3885 *
3886 * @since 1.14 (J) - 1.4.0 (WP)
3887 */
3888 VikBooking::getBookingHistoryInstance()->setBid($order['id'])->store('EC');
3889 } else {
3890 /**
3891 * Instead, we store an event log to remind that the email was re-sent to the guest
3892 *
3893 * @since 1.16.3 (J) - 1.6.3 (WP)
3894 */
3895 VikBooking::getBookingHistoryInstance()->setBid($order['id'])->store('ER', $esit_mess);
3896 }
3897
3898 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$oid);
3899 $app->close();
3900 }
3901
3902 public function setordconfirmed()
3903 {
3904 $app = JFactory::getApplication();
3905
3906 // the booking ID to confirm
3907 $cid = VikRequest::getVar('cid', array(0));
3908 $oid = (int) $cid[0];
3909
3910 // notify the customer unless it was a re-confirmation
3911 $pskip = $app->input->getInt('skip_notification', 0);
3912
3913 // access the reservation model
3914 $model = VBOModelReservation::getInstance();
3915
3916 // set the booking to confirmed
3917 $confirmed = $model->setConfirmed([
3918 'booking_id' => $oid,
3919 'notify' => (bool) (!$pskip),
3920 ]);
3921
3922 if (!$confirmed) {
3923 $error = $model->getError();
3924 if (!is_string($error) || !$error) {
3925 $error = 'Could not confirm the reservation';
3926 }
3927
3928 // enqueue error message
3929 $app->enqueueMessage($error, 'error');
3930 } else {
3931 // enqueue success message
3932 $app->enqueueMessage(JText::translate('VBORDERSETASCONF'));
3933 }
3934
3935 // redirect
3936 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $oid);
3937 $app->close();
3938 }
3939
3940 public function payments() {
3941 VikBookingHelper::printHeader("14");
3942
3943 VikRequest::setVar('view', VikRequest::getCmd('view', 'payments'));
3944
3945 parent::display();
3946
3947 if (VikBooking::showFooter()) {
3948 VikBookingHelper::printFooter();
3949 }
3950 }
3951
3952 public function newpayment() {
3953 VikBookingHelper::printHeader("14");
3954
3955 VikRequest::setVar('view', VikRequest::getCmd('view', 'managepayment'));
3956
3957 parent::display();
3958
3959 if (VikBooking::showFooter()) {
3960 VikBookingHelper::printFooter();
3961 }
3962 }
3963
3964 public function editpayment() {
3965 VikBookingHelper::printHeader("14");
3966
3967 VikRequest::setVar('view', VikRequest::getCmd('view', 'managepayment'));
3968
3969 parent::display();
3970
3971 if (VikBooking::showFooter()) {
3972 VikBookingHelper::printFooter();
3973 }
3974 }
3975
3976 public function createpayment()
3977 {
3978 if (!JSession::checkToken()) {
3979 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3980 }
3981
3982 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
3983 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
3984 }
3985
3986 $mainframe = JFactory::getApplication();
3987 $pname = VikRequest::getString('name', '', 'request');
3988 $ppayment = VikRequest::getString('payment', '', 'request');
3989 $ppublished = VikRequest::getString('published', '', 'request');
3990 $pcharge = VikRequest::getFloat('charge', '', 'request');
3991 $psetconfirmed = VikRequest::getString('setconfirmed', '', 'request');
3992 $phidenonrefund = VikRequest::getInt('hidenonrefund', '', 'request');
3993 $ponlynonrefund = VikRequest::getInt('onlynonrefund', '', 'request');
3994 $pshownotealw = VikRequest::getString('shownotealw', '', 'request');
3995 $pnote = VikRequest::getString('note', '', 'request', VIKREQUEST_ALLOWHTML);
3996 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
3997 $pval_pcent = !in_array($pval_pcent, array('1', '2')) ? 1 : $pval_pcent;
3998 $pch_disc = VikRequest::getString('ch_disc', '', 'request');
3999 $pch_disc = !in_array($pch_disc, array('1', '2')) ? 1 : $pch_disc;
4000 $poutposition = VikRequest::getString('outposition', 'top', 'request');
4001 $plogo = VikRequest::getString('logo', '', 'request');
4002 $pall_rooms = VikRequest::getInt('all_rooms', 0, 'request');
4003 $pidrooms = VikRequest::getVar('idrooms', array());
4004 $vikpaymentparams = VikRequest::getVar('vikpaymentparams', array(0));
4005 $payparamarr = array();
4006 $payparamstr = '';
4007 if (count($vikpaymentparams) > 0) {
4008 foreach ($vikpaymentparams as $setting => $cont) {
4009 if (strlen($setting) > 0) {
4010 $payparamarr[$setting] = $cont;
4011 }
4012 }
4013 if (count($payparamarr) > 0) {
4014 $payparamstr = json_encode($payparamarr);
4015 }
4016 }
4017
4018 $dbo = JFactory::getDbo();
4019
4020 $set_idrooms = [];
4021 if (empty($pall_rooms) && !empty($pidrooms)) {
4022 $pidrooms = array_map(function($idroom) {
4023 return (int)$idroom;
4024 }, $pidrooms);
4025 foreach ($pidrooms as $idroom) {
4026 if (empty($idroom) || in_array($idroom, $set_idrooms)) {
4027 continue;
4028 }
4029 $set_idrooms[] = $idroom;
4030 }
4031 }
4032
4033 if (!empty($pname) && !empty($ppayment)) {
4034 $setpub = $ppublished == "1" ? 1 : 0;
4035 $psetconfirmed = $psetconfirmed == "1" ? 1 : 0;
4036 $pshownotealw = $pshownotealw == "1" ? 1 : 0;
4037 $q = "SELECT `id` FROM `#__vikbooking_gpayments` WHERE `file`=".$dbo->quote($ppayment).";";
4038 $dbo->setQuery($q);
4039 $dbo->execute();
4040 if ($dbo->getNumRows() >= 0) {
4041 $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') . ");";
4042 $dbo->setQuery($q);
4043 $dbo->execute();
4044 $mainframe->enqueueMessage(JText::translate('VBPAYMENTSAVED'));
4045 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4046 } else {
4047 VikError::raiseWarning('', JText::translate('ERRINVFILEPAYMENT'));
4048 $mainframe->redirect("index.php?option=com_vikbooking&task=newpayment");
4049 }
4050 } else {
4051 $mainframe->redirect("index.php?option=com_vikbooking&task=newpayment");
4052 }
4053 }
4054
4055 public function updatepayment()
4056 {
4057 if (!JSession::checkToken()) {
4058 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4059 }
4060
4061 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4062 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4063 }
4064
4065 $this->do_updatepayment($stay = false);
4066 }
4067
4068 public function updatepaymentstay()
4069 {
4070 if (!JSession::checkToken()) {
4071 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4072 }
4073
4074 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4075 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4076 }
4077
4078 $this->do_updatepayment($stay = true);
4079 }
4080
4081 protected function do_updatepayment($stay = false)
4082 {
4083 $mainframe = JFactory::getApplication();
4084
4085 $pwhere = VikRequest::getString('where', '', 'request');
4086 $pname = VikRequest::getString('name', '', 'request');
4087 $ppayment = VikRequest::getString('payment', '', 'request');
4088 $ppublished = VikRequest::getString('published', '', 'request');
4089 $pcharge = VikRequest::getFloat('charge', '', 'request');
4090 $psetconfirmed = VikRequest::getString('setconfirmed', '', 'request');
4091 $phidenonrefund = VikRequest::getInt('hidenonrefund', '', 'request');
4092 $ponlynonrefund = VikRequest::getInt('onlynonrefund', '', 'request');
4093 $pshownotealw = VikRequest::getString('shownotealw', '', 'request');
4094 $pnote = VikRequest::getString('note', '', 'request', VIKREQUEST_ALLOWRAW);
4095 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4096 $pval_pcent = !in_array($pval_pcent, array('1', '2')) ? 1 : $pval_pcent;
4097 $pch_disc = VikRequest::getString('ch_disc', '', 'request');
4098 $pch_disc = !in_array($pch_disc, array('1', '2')) ? 1 : $pch_disc;
4099 $poutposition = VikRequest::getString('outposition', 'top', 'request');
4100 $plogo = VikRequest::getString('logo', '', 'request');
4101 $pall_rooms = VikRequest::getInt('all_rooms', 0, 'request');
4102 $pidrooms = VikRequest::getVar('idrooms', array());
4103 $vikpaymentparams = VikRequest::getVar('vikpaymentparams', array(0));
4104 $payparamarr = array();
4105 $payparamstr = '';
4106 if (count($vikpaymentparams) > 0) {
4107 foreach ($vikpaymentparams as $setting => $cont) {
4108 if (strlen($setting) > 0) {
4109 $payparamarr[$setting] = $cont;
4110 }
4111 }
4112 if (count($payparamarr) > 0) {
4113 $payparamstr = json_encode($payparamarr);
4114 }
4115 }
4116
4117 $dbo = JFactory::getDbo();
4118
4119 $set_idrooms = [];
4120 if (empty($pall_rooms) && !empty($pidrooms)) {
4121 $pidrooms = array_map(function($idroom) {
4122 return (int)$idroom;
4123 }, $pidrooms);
4124 foreach ($pidrooms as $idroom) {
4125 if (empty($idroom) || in_array($idroom, $set_idrooms)) {
4126 continue;
4127 }
4128 $set_idrooms[] = $idroom;
4129 }
4130 }
4131
4132 if (!empty($pname) && !empty($ppayment) && !empty($pwhere)) {
4133 $setpub = $ppublished == "1" ? 1 : 0;
4134 $psetconfirmed = $psetconfirmed == "1" ? 1 : 0;
4135 $pshownotealw = $pshownotealw == "1" ? 1 : 0;
4136 $q = "SELECT `id` FROM `#__vikbooking_gpayments` WHERE `file`=".$dbo->quote($ppayment)." AND `id`!='".$pwhere."';";
4137 $dbo->setQuery($q);
4138 $dbo->execute();
4139 if ($dbo->getNumRows() >= 0) {
4140 $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).";";
4141 $dbo->setQuery($q);
4142 $dbo->execute();
4143
4144 $mainframe->enqueueMessage(JText::translate('VBPAYMENTUPDATED'));
4145 if ($stay) {
4146 $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4147 } else {
4148 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4149 }
4150 } else {
4151 VikError::raiseWarning('', JText::translate('ERRINVFILEPAYMENT'));
4152 $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4153 }
4154 } else {
4155 $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4156 }
4157 }
4158
4159 public function removepayments()
4160 {
4161 if (!JSession::checkToken()) {
4162 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4163 }
4164
4165 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
4166 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4167 }
4168
4169 $ids = VikRequest::getVar('cid', array(0));
4170 if ($ids) {
4171 $dbo = JFactory::getDBO();
4172 foreach ($ids as $d) {
4173 $q = "DELETE FROM `#__vikbooking_gpayments` WHERE `id`=".$dbo->quote($d).";";
4174 $dbo->setQuery($q);
4175 $dbo->execute();
4176 }
4177 }
4178 $mainframe = JFactory::getApplication();
4179 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4180 }
4181
4182 public function modavailpayment() {
4183 if (!JSession::checkToken('get')) {
4184 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4185 }
4186 $cid = VikRequest::getVar('cid', array(0));
4187 $idp = $cid[0];
4188 if (!empty($idp)) {
4189 $dbo = JFactory::getDBO();
4190 $q = "SELECT `published` FROM `#__vikbooking_gpayments` WHERE `id`=".intval($idp).";";
4191 $dbo->setQuery($q);
4192 $dbo->execute();
4193 $get = $dbo->loadAssocList();
4194 $q = "UPDATE `#__vikbooking_gpayments` SET `published`=".(intval($get[0]['published']) == 1 ? '0' : '1')." WHERE `id`=".intval($idp).";";
4195 $dbo->setQuery($q);
4196 $dbo->execute();
4197 }
4198 $mainframe = JFactory::getApplication();
4199 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4200 }
4201
4202 public function seasons() {
4203 VikBookingHelper::printHeader("13");
4204
4205 VikRequest::setVar('view', VikRequest::getCmd('view', 'seasons'));
4206
4207 parent::display();
4208
4209 if (VikBooking::showFooter()) {
4210 VikBookingHelper::printFooter();
4211 }
4212 }
4213
4214 public function newseason() {
4215 VikBookingHelper::printHeader("13");
4216
4217 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageseason'));
4218
4219 parent::display();
4220
4221 if (VikBooking::showFooter()) {
4222 VikBookingHelper::printFooter();
4223 }
4224 }
4225
4226 public function editseason() {
4227 VikBookingHelper::printHeader("13");
4228
4229 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageseason'));
4230
4231 parent::display();
4232
4233 if (VikBooking::showFooter()) {
4234 VikBookingHelper::printFooter();
4235 }
4236 }
4237
4238 public function updateseason()
4239 {
4240 if (!JSession::checkToken()) {
4241 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4242 }
4243
4244 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4245 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4246 }
4247
4248 $this->do_updateseason();
4249 }
4250
4251 public function updateseasonstay()
4252 {
4253 if (!JSession::checkToken()) {
4254 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4255 }
4256
4257 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4258 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4259 }
4260
4261 $this->do_updateseason(true);
4262 }
4263
4264 private function do_updateseason($stay = false)
4265 {
4266 $app = JFactory::getApplication();
4267 $dbo = JFactory::getDbo();
4268 $session = JFactory::getSession();
4269
4270 $pwhere = VikRequest::getInt('where', 0, 'request');
4271
4272 $pfrom = VikRequest::getString('from', '', 'request');
4273 $pto = VikRequest::getString('to', '', 'request');
4274 $ptype = VikRequest::getString('type', '', 'request');
4275 $pdiffcost = VikRequest::getFloat('diffcost', '', 'request');
4276 $pidrooms = VikRequest::getVar('idrooms', array());
4277 $pidprices = VikRequest::getVar('idprices', array());
4278 $pwdays = VikRequest::getVar('wdays', array());
4279 $pspname = VikRequest::getString('spname', '', 'request');
4280 $pcheckinincl = VikRequest::getString('checkinincl', '', 'request');
4281 $pcheckinincl = $pcheckinincl == 1 ? 1 : 0;
4282 $pyeartied = VikRequest::getInt('yeartied', 0, 'request');
4283 $pyeartied = $pyeartied == 1 ? 1 : 0;
4284 $tieyear = 0;
4285 $ppromo = VikRequest::getInt('promo', 0, 'request');
4286 $ppromo = $ppromo == 1 ? 1 : 0;
4287 $ppromodaysadv = VikRequest::getInt('promodaysadv', '', 'request');
4288 $ppromominlos = VikRequest::getInt('promominlos', '', 'request');
4289 $ppromotxt = VikRequest::getString('promotxt', '', 'request', VIKREQUEST_ALLOWHTML);
4290 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4291 $pval_pcent = $pval_pcent == "1" ? 1 : 2;
4292 $proundmode = VikRequest::getString('roundmode', '', 'request');
4293 $proundmode = (!empty($proundmode) && in_array($proundmode, array('PHP_ROUND_HALF_UP', 'PHP_ROUND_HALF_DOWN')) ? $proundmode : '');
4294 $pnightsoverrides = VikRequest::getVar('nightsoverrides', array());
4295 $pvaluesoverrides = VikRequest::getVar('valuesoverrides', array());
4296 $pandmoreoverride = VikRequest::getVar('andmoreoverride', array());
4297 $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array());
4298 $padultsdiffval = VikRequest::getVar('adultsdiffval', array());
4299 $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array());
4300 $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array());
4301 $ppromolastmind = VikRequest::getInt('promolastmind', 0, 'request');
4302 $ppromolastminh = VikRequest::getInt('promolastminh', 0, 'request');
4303 $promolastmin = ($ppromolastmind * 86400) + ($ppromolastminh * 3600);
4304 $ppromofinalprice = VikRequest::getInt('promofinalprice', 0, 'request');
4305 $ppromofinalprice = $ppromo ? $ppromofinalprice : 0;
4306 $occupancy_ovr = array();
4307 $losverridestr = "";
4308
4309 $updforvcm = $session->get('vbVcmRatesUpd', '');
4310 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
4311
4312 // check null dates
4313 if ($dbo->getNullDate() == $pfrom) {
4314 $pfrom = '';
4315 }
4316 if ($dbo->getNullDate() == $pto) {
4317 $pto = '';
4318 }
4319
4320 if ((empty($pfrom) || empty($pto)) && !$pwdays) {
4321 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4322 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4323 exit;
4324 }
4325
4326 $skipseason = false;
4327 if (empty($pfrom) || empty($pto)) {
4328 $skipseason = true;
4329 }
4330 $skipdays = false;
4331 $wdaystr = null;
4332 if (count($pwdays) == 0) {
4333 $skipdays = true;
4334 } else {
4335 $wdaystr = "";
4336 foreach ($pwdays as $wd) {
4337 $wdaystr .= $wd.';';
4338 }
4339 }
4340 $roomstr = "";
4341 $roomids = array();
4342 foreach ($pidrooms as $room) {
4343 if (empty($room)) {
4344 continue;
4345 }
4346 $roomstr .= "-".$room."-,";
4347 $roomids[] = (int)$room;
4348 }
4349 $pricestr = "";
4350 $priceids = array();
4351 foreach ($pidprices as $price) {
4352 if (empty($price)) {
4353 continue;
4354 }
4355 $pricestr .= "-".$price."-,";
4356 $priceids[] = (int)$price;
4357 }
4358 $valid = true;
4359 $double_records = array();
4360 $sfrom = null;
4361 $sto = null;
4362
4363 // value overrides
4364 if ($pnightsoverrides && $pvaluesoverrides) {
4365 foreach ($pnightsoverrides as $ko => $no) {
4366 if (!empty($no) && strlen(trim($pvaluesoverrides[$ko])) > 0) {
4367 $infiniteclause = intval($pandmoreoverride[$ko]) == 1 ? '-i' : '';
4368 $losverridestr .= intval($no).$infiniteclause.':'.trim($pvaluesoverrides[$ko]).'_';
4369 }
4370 }
4371 }
4372
4373 if (!$skipseason) {
4374 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
4375 $second = VikBooking::getDateTimestamp($pto, 0, 0);
4376
4377 if ($second > 0 && $second == $first) {
4378 $second += 86399;
4379 }
4380
4381 if (!($second > $first)) {
4382 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4383 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4384 exit;
4385 }
4386
4387 $baseone = getdate($first);
4388 $basets = mktime(0, 0, 0, 1, 1, $baseone['year']);
4389 $sfrom = $baseone[0] - $basets;
4390 $basetwo = getdate($second);
4391 $basets = mktime(0, 0, 0, 1, 1, $basetwo['year']);
4392 $sto = $basetwo[0] - $basets;
4393
4394 // check leap year
4395 if ($baseone['year'] % 4 == 0 && ($baseone['year'] % 100 != 0 || $baseone['year'] % 400 == 0)) {
4396 $leapts = mktime(0, 0, 0, 2, 29, $baseone['year']);
4397 if ($baseone[0] > $leapts) {
4398 $sfrom -= 86400;
4399 /**
4400 * To avoid issue with leap years and dates near Feb 29th, we only reduce the seconds if these were reduced
4401 * for the from-date of the seasons. Doing it just for the to-date in 2019 for 2020 (leap) produced invalid results.
4402 *
4403 * @since July 2nd 2019
4404 */
4405 if ($basetwo['year'] % 4 == 0 && ($basetwo['year'] % 100 != 0 || $basetwo['year'] % 400 == 0)) {
4406 $leapts = mktime(0, 0, 0, 2, 29, $basetwo['year']);
4407 if ($basetwo[0] > $leapts) {
4408 $sto -= date('d-m', $baseone[0]) != '31-12' && date('d-m', $basetwo[0]) == '31-12' ? 1 : 86400;
4409 }
4410 }
4411 }
4412 }
4413
4414 // tied to the year
4415 if ($pyeartied == 1) {
4416 $tieyear = $baseone['year'];
4417 }
4418
4419 // Occupancy Override
4420 if (count($padultsdiffval) > 0) {
4421 foreach ($padultsdiffval as $rid => $valovr_arr) {
4422 if (!is_array($valovr_arr) || !is_array($padultsdiffchdisc[$rid]) || !is_array($padultsdiffvalpcent[$rid]) || !is_array($padultsdiffpernight[$rid])) {
4423 continue;
4424 }
4425 foreach ($valovr_arr as $occ => $valovr) {
4426 if (!(strlen($valovr) > 0) || !(strlen($padultsdiffchdisc[$rid][$occ]) > 0) || !(strlen($padultsdiffvalpcent[$rid][$occ]) > 0) || !(strlen($padultsdiffpernight[$rid][$occ]) > 0)) {
4427 continue;
4428 }
4429 if (!array_key_exists($rid, $occupancy_ovr)) {
4430 $occupancy_ovr[$rid] = array();
4431 }
4432 $occupancy_ovr[$rid][$occ] = array('chdisc' => (int)$padultsdiffchdisc[$rid][$occ], 'valpcent' => (int)$padultsdiffvalpcent[$rid][$occ], 'pernight' => (int)$padultsdiffpernight[$rid][$occ], 'value' => (float)$valovr);
4433 }
4434 }
4435 }
4436
4437 // check if seasons dates are valid
4438 $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").";";
4439 $dbo->setQuery($q);
4440 $similar = $dbo->loadAssocList();
4441 if ($similar) {
4442 $valid = false;
4443 foreach ($similar as $sim) {
4444 $double_records[] = $sim['spname'];
4445 }
4446 }
4447
4448 $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").";";
4449 $dbo->setQuery($q);
4450 $similar = $dbo->loadAssocList();
4451 if ($similar) {
4452 $valid = false;
4453 foreach ($similar as $sim) {
4454 $double_records[] = $sim['spname'];
4455 }
4456 }
4457
4458 $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").";";
4459 $dbo->setQuery($q);
4460 $dbo->execute();
4461 $similar = $dbo->loadAssocList();
4462 if ($similar) {
4463 $valid = false;
4464 foreach ($similar as $sim) {
4465 $double_records[] = $sim['spname'];
4466 }
4467 }
4468 }
4469
4470 // fetch previous record before the update
4471 $q = $dbo->getQuery(true)
4472 ->select('*')
4473 ->from($dbo->qn('#__vikbooking_seasons'))
4474 ->where($dbo->qn('id') . ' = ' . $pwhere);
4475 $dbo->setQuery($q, 0, 1);
4476 $prev_record = $dbo->loadAssoc();
4477
4478 if (!$valid || !$prev_record) {
4479 VikError::raiseWarning('', JText::translate('ERRINVDATEROOMSLOCSEASON').($double_records ? ' ('.implode(', ', array_unique($double_records)).')' : ''));
4480 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4481 exit;
4482 }
4483
4484 /**
4485 * Attempt to access the promotion handlers in advance to perform additional validations.
4486 *
4487 * @since 1.16.4 (J) - 1.6.4 (WP)
4488 */
4489 try {
4490 $promo_handlers = VikBooking::getPromotionHandlers();
4491 } catch (Exception $e) {
4492 // reset the value
4493 $promo_handlers = [];
4494 }
4495
4496 if (!$prev_record['promo'] && $ppromo && $promo_handlers) {
4497 // channels supporting promotions are available, and a regular special price is
4498 // being converted into a promotion - this is not allowed so we make it a non-promotion.
4499 $ppromo = 0;
4500 $app->enqueueMessage(JText::translate('VBO_NOPROMO_UPD_CHANNELS'), 'warning');
4501 }
4502
4503 if ($promo_handlers && $proundmode) {
4504 /**
4505 * Always disallow rounding when channels supporting promotions are available.
4506 *
4507 * @since 1.18.3 (J) - 1.8.3 (WP)
4508 */
4509 $proundmode = '';
4510 $app->enqueueMessage(sprintf('%s: %s.', JText::translate('VBNEWSEASONROUNDCOST'), JText::translate('VBPARAMPRICECALENDARDISABLED')), 'warning');
4511 }
4512
4513 // update record
4514 $upd_record = new stdClass;
4515 $upd_record->id = $prev_record['id'];
4516 $upd_record->type = $ptype == "1" ? 1 : 2;
4517 $upd_record->from = $sfrom;
4518 $upd_record->to = $sto;
4519 $upd_record->diffcost = $pdiffcost;
4520 $upd_record->idrooms = $roomstr;
4521 $upd_record->spname = $pspname;
4522 $upd_record->wdays = $wdaystr;
4523 $upd_record->checkinincl = $pcheckinincl;
4524 $upd_record->val_pcent = $pval_pcent;
4525 $upd_record->losoverride = $losverridestr;
4526 $upd_record->roundmode = !empty($proundmode) ? $proundmode : null;
4527 $upd_record->year = $pyeartied == 1 ? $tieyear : null;
4528 $upd_record->idprices = $pricestr;
4529 $upd_record->promo = $ppromo;
4530 $upd_record->promodaysadv = !empty($ppromodaysadv) ? $ppromodaysadv : null;
4531 $upd_record->promotxt = $ppromotxt;
4532 $upd_record->promominlos = !empty($ppromominlos) ? $ppromominlos : 0;
4533 $upd_record->occupancy_ovr = $occupancy_ovr ? json_encode($occupancy_ovr) : null;
4534 $upd_record->promolastmin = (int)$promolastmin;
4535 $upd_record->promofinalprice = $ppromofinalprice;
4536
4537 $dbo->updateObject('#__vikbooking_seasons', $upd_record, 'id', $nulls = true);
4538
4539 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
4540
4541 // update session values
4542 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
4543 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
4544 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $first ? $first : $updforvcm['dfrom'];
4545 } else {
4546 $updforvcm['dfrom'] = $first;
4547 }
4548 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
4549 $updforvcm['dto'] = $updforvcm['dto'] < $second ? $second : $updforvcm['dto'];
4550 } else {
4551 $updforvcm['dto'] = $second;
4552 }
4553 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
4554 foreach ($roomids as $rid) {
4555 if (!in_array($rid, $updforvcm['rooms'])) {
4556 $updforvcm['rooms'][] = $rid;
4557 }
4558 }
4559 } else {
4560 $updforvcm['rooms'] = $roomids;
4561 }
4562 if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
4563 foreach ($roomids as $rid) {
4564 if (array_key_exists($rid, $updforvcm['rplans'])) {
4565 $updforvcm['rplans'][$rid] = $updforvcm['rplans'][$rid] + $priceids;
4566 } else {
4567 $updforvcm['rplans'][$rid] = $priceids;
4568 }
4569 }
4570 } else {
4571 $updforvcm['rplans'] = array();
4572 foreach ($roomids as $rid) {
4573 $updforvcm['rplans'][$rid] = $priceids;
4574 }
4575 }
4576 $session->set('vbVcmRatesUpd', $updforvcm);
4577
4578 /**
4579 * Query promotion handlers, if any, to trigger the update/delete promotion event.
4580 *
4581 * @since 1.15.0 (J) - 1.5.0 (WP)
4582 * @since 1.16.4 (J) - 1.6.4 (WP) added control to perform a delete operation.
4583 */
4584 $promo_update_type = $prev_record['promo'] && !$ppromo ? 'triggerDelete' : 'triggerUpdate';
4585 $promo_method_type = $prev_record['promo'] && !$ppromo ? 'delete' : 'update';
4586 try {
4587 if ($ppromo && is_array($promo_handlers) && $promo_handlers) {
4588 foreach ($promo_handlers as $promo_handler) {
4589 if (!isset($promo_handler->instance) || !is_object($promo_handler->instance) || !method_exists($promo_handler->instance, $promo_update_type)) {
4590 // outdated handler object
4591 continue;
4592 }
4593 if (!is_callable(array($promo_handler->instance, $promo_update_type)) || !$promo_handler->instance->{$promo_update_type}()) {
4594 // promotion handler does not support update/delete promotion event
4595 continue;
4596 }
4597 // invoke the update/delete promotion event for this handler
4598 $ch_result = $promo_handler->instance->createPromotion(['vbo_promo_id' => $pwhere], $promo_method_type);
4599 if (!$ch_result) {
4600 VikError::raiseWarning('', $promo_handler->instance->getName() . ': ' . $promo_handler->instance->getError());
4601 }
4602 }
4603 }
4604 } catch (Exception $e) {
4605 // do nothing
4606 }
4607
4608 if ($stay) {
4609 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4610 } else {
4611 $app->redirect("index.php?option=com_vikbooking&task=seasons");
4612 }
4613 $app->close();
4614 }
4615
4616 public function createseason()
4617 {
4618 if (!JSession::checkToken()) {
4619 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4620 }
4621
4622 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
4623 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4624 }
4625
4626 $this->do_createseason();
4627 }
4628
4629 public function createseason_new()
4630 {
4631 if (!JSession::checkToken()) {
4632 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4633 }
4634
4635 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
4636 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4637 }
4638
4639 $this->do_createseason(true);
4640 }
4641
4642 private function do_createseason($andnew = false)
4643 {
4644 $app = JFactory::getApplication();
4645 $dbo = JFactory::getDbo();
4646 $session = JFactory::getSession();
4647
4648 $pfrom = VikRequest::getString('from', '', 'request');
4649 $pto = VikRequest::getString('to', '', 'request');
4650 $ptype = VikRequest::getString('type', '', 'request');
4651 $pdiffcost = VikRequest::getFloat('diffcost', '', 'request');
4652 $pidrooms = VikRequest::getVar('idrooms', array());
4653 $pidprices = VikRequest::getVar('idprices', array());
4654 $pwdays = VikRequest::getVar('wdays', array());
4655 $pspname = VikRequest::getString('spname', '', 'request');
4656 $pcheckinincl = VikRequest::getString('checkinincl', '', 'request');
4657 $pcheckinincl = $pcheckinincl == 1 ? 1 : 0;
4658 $pyeartied = VikRequest::getInt('yeartied', 0, 'request');
4659 $pyeartied = $pyeartied == 1 ? 1 : 0;
4660 $tieyear = 0;
4661 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4662 $pval_pcent = $pval_pcent == "1" ? 1 : 2;
4663 $proundmode = VikRequest::getString('roundmode', '', 'request');
4664 $proundmode = (!empty($proundmode) && in_array($proundmode, array('PHP_ROUND_HALF_UP', 'PHP_ROUND_HALF_DOWN')) ? $proundmode : '');
4665 $ppromo = VikRequest::getInt('promo', 0, 'request');
4666 $ppromodaysadv = VikRequest::getInt('promodaysadv', '', 'request');
4667 $ppromominlos = VikRequest::getInt('promominlos', '', 'request');
4668 $ppromotxt = VikRequest::getString('promotxt', '', 'request', VIKREQUEST_ALLOWHTML);
4669 $pnightsoverrides = VikRequest::getVar('nightsoverrides', array());
4670 $pvaluesoverrides = VikRequest::getVar('valuesoverrides', array());
4671 $pandmoreoverride = VikRequest::getVar('andmoreoverride', array());
4672 $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array());
4673 $padultsdiffval = VikRequest::getVar('adultsdiffval', array());
4674 $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array());
4675 $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array());
4676 $ppromolastmind = VikRequest::getInt('promolastmind', 0, 'request');
4677 $ppromolastminh = VikRequest::getInt('promolastminh', 0, 'request');
4678 $promolastmin = ($ppromolastmind * 86400) + ($ppromolastminh * 3600);
4679 $ppromofinalprice = VikRequest::getInt('promofinalprice', 0, 'request');
4680 $ppromofinalprice = $ppromo ? $ppromofinalprice : 0;
4681 $pchannels = VikRequest::getVar('channels', array());
4682 $occupancy_ovr = array();
4683 $losverridestr = "";
4684
4685 $updforvcm = $session->get('vbVcmRatesUpd', '');
4686 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
4687
4688 // check null dates
4689 if ($dbo->getNullDate() == $pfrom) {
4690 $pfrom = '';
4691 }
4692 if ($dbo->getNullDate() == $pto) {
4693 $pto = '';
4694 }
4695
4696 if ((empty($pfrom) || empty($pto)) && !$pwdays) {
4697 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4698 $app->redirect("index.php?option=com_vikbooking&task=newseason");
4699 exit;
4700 }
4701
4702 $skipseason = false;
4703 if (empty($pfrom) || empty($pto)) {
4704 $skipseason = true;
4705 }
4706 $skipdays = false;
4707 $wdaystr = null;
4708 if (!$pwdays) {
4709 $skipdays = true;
4710 } else {
4711 $wdaystr = "";
4712 foreach ($pwdays as $wd) {
4713 $wdaystr .= $wd.';';
4714 }
4715 }
4716 $roomstr = "";
4717 $roomids = array();
4718 foreach ($pidrooms as $room) {
4719 if (empty($room)) {
4720 continue;
4721 }
4722 $roomstr .= "-".$room."-,";
4723 $roomids[] = (int)$room;
4724 }
4725 $pricestr = "";
4726 $priceids = array();
4727 foreach ($pidprices as $price) {
4728 if (empty($price)) {
4729 continue;
4730 }
4731 $pricestr .= "-".$price."-,";
4732 $priceids[] = (int)$price;
4733 }
4734 $valid = true;
4735 $double_records = array();
4736 $sfrom = null;
4737 $sto = null;
4738
4739 // value overrides
4740 if ($pnightsoverrides && $pvaluesoverrides) {
4741 foreach ($pnightsoverrides as $ko => $no) {
4742 if (!empty($no) && strlen(trim($pvaluesoverrides[$ko])) > 0) {
4743 $infiniteclause = intval($pandmoreoverride[$ko]) == 1 ? '-i' : '';
4744 $losverridestr .= intval($no).$infiniteclause.':'.trim($pvaluesoverrides[$ko]).'_';
4745 }
4746 }
4747 }
4748
4749 if (!$skipseason) {
4750 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
4751 $second = VikBooking::getDateTimestamp($pto, 0, 0);
4752
4753 if ($second > 0 && $second == $first) {
4754 $second += 86399;
4755 }
4756
4757 if (!($second > $first)) {
4758 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4759 $app->redirect("index.php?option=com_vikbooking&task=newseason");
4760 exit;
4761 }
4762
4763 $baseone = getdate($first);
4764 $basets = mktime(0, 0, 0, 1, 1, $baseone['year']);
4765 $sfrom = $baseone[0] - $basets;
4766 $basetwo = getdate($second);
4767 $basets = mktime(0, 0, 0, 1, 1, $basetwo['year']);
4768 $sto = $basetwo[0] - $basets;
4769
4770 // check leap year
4771 if ($baseone['year'] % 4 == 0 && ($baseone['year'] % 100 != 0 || $baseone['year'] % 400 == 0)) {
4772 $leapts = mktime(0, 0, 0, 2, 29, $baseone['year']);
4773 if ($baseone[0] > $leapts) {
4774 $sfrom -= 86400;
4775 /**
4776 * To avoid issue with leap years and dates near Feb 29th, we only reduce the seconds if these were reduced
4777 * for the from-date of the seasons. Doing it just for the to-date in 2019 for 2020 (leap) produced invalid results.
4778 *
4779 * @since July 2nd 2019
4780 */
4781 if ($basetwo['year'] % 4 == 0 && ($basetwo['year'] % 100 != 0 || $basetwo['year'] % 400 == 0)) {
4782 $leapts = mktime(0, 0, 0, 2, 29, $basetwo['year']);
4783 if ($basetwo[0] > $leapts) {
4784 $sto -= date('d-m', $baseone[0]) != '31-12' && date('d-m', $basetwo[0]) == '31-12' ? 1 : 86400;
4785 }
4786 }
4787 }
4788 }
4789
4790 // tied to the year
4791 if ($pyeartied == 1) {
4792 $tieyear = $baseone['year'];
4793 }
4794
4795 // Occupancy Override
4796 if ($padultsdiffval) {
4797 foreach ($padultsdiffval as $rid => $valovr_arr) {
4798 if (!is_array($valovr_arr) || !is_array($padultsdiffchdisc[$rid]) || !is_array($padultsdiffvalpcent[$rid]) || !is_array($padultsdiffpernight[$rid])) {
4799 continue;
4800 }
4801 foreach ($valovr_arr as $occ => $valovr) {
4802 if (!(strlen($valovr) > 0) || !(strlen($padultsdiffchdisc[$rid][$occ]) > 0) || !(strlen($padultsdiffvalpcent[$rid][$occ]) > 0) || !(strlen($padultsdiffpernight[$rid][$occ]) > 0)) {
4803 continue;
4804 }
4805 if (!array_key_exists($rid, $occupancy_ovr)) {
4806 $occupancy_ovr[$rid] = array();
4807 }
4808 $occupancy_ovr[$rid][$occ] = array('chdisc' => (int)$padultsdiffchdisc[$rid][$occ], 'valpcent' => (int)$padultsdiffvalpcent[$rid][$occ], 'pernight' => (int)$padultsdiffpernight[$rid][$occ], 'value' => (float)$valovr);
4809 }
4810 }
4811 }
4812
4813 // check if seasons dates are valid
4814 $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").";";
4815 $dbo->setQuery($q);
4816 $similar = $dbo->loadAssocList();
4817 if ($similar) {
4818 $valid = false;
4819 foreach ($similar as $sim) {
4820 $double_records[] = $sim['spname'];
4821 }
4822 }
4823
4824 $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").";";
4825 $dbo->setQuery($q);
4826 $similar = $dbo->loadAssocList();
4827 if ($similar) {
4828 $valid = false;
4829 foreach ($similar as $sim) {
4830 $double_records[] = $sim['spname'];
4831 }
4832 }
4833
4834 $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").";";
4835 $dbo->setQuery($q);
4836 $similar = $dbo->loadAssocList();
4837 if ($similar) {
4838 $valid = false;
4839 foreach ($similar as $sim) {
4840 $double_records[] = $sim['spname'];
4841 }
4842 }
4843 }
4844
4845 if (!$valid && !$ppromo) {
4846 VikError::raiseWarning('', JText::translate('ERRINVDATEROOMSLOCSEASON').(count($double_records) ? ' ('.implode(', ', array_unique($double_records)).')' : ''));
4847 $app->redirect("index.php?option=com_vikbooking&task=newseason");
4848 exit;
4849 }
4850
4851 if ($pchannels && $proundmode) {
4852 /**
4853 * Always disallow rounding when channels supporting promotions are available.
4854 *
4855 * @since 1.18.3 (J) - 1.8.3 (WP)
4856 */
4857 $proundmode = '';
4858 $app->enqueueMessage(sprintf('%s: %s.', JText::translate('VBNEWSEASONROUNDCOST'), JText::translate('VBPARAMPRICECALENDARDISABLED')), 'warning');
4859 }
4860
4861 // insert new record
4862 $sea_record = new stdClass;
4863 $sea_record->type = $ptype == "1" ? 1 : 2;
4864 $sea_record->from = $sfrom;
4865 $sea_record->to = $sto;
4866 $sea_record->diffcost = $pdiffcost;
4867 $sea_record->idrooms = $roomstr;
4868 $sea_record->spname = $pspname;
4869 $sea_record->wdays = $wdaystr;
4870 $sea_record->checkinincl = $pcheckinincl;
4871 $sea_record->val_pcent = $pval_pcent;
4872 $sea_record->losoverride = $losverridestr;
4873 $sea_record->roundmode = !empty($proundmode) ? $proundmode : null;
4874 $sea_record->year = $pyeartied == 1 ? $tieyear : null;
4875 $sea_record->idprices = $pricestr;
4876 $sea_record->promo = $ppromo == 1 ? 1 : 0;
4877 $sea_record->promodaysadv = !empty($ppromodaysadv) ? $ppromodaysadv : null;
4878 $sea_record->promotxt = $ppromotxt;
4879 $sea_record->promominlos = !empty($ppromominlos) ? $ppromominlos : 0;
4880 $sea_record->occupancy_ovr = $occupancy_ovr ? json_encode($occupancy_ovr) : null;
4881 $sea_record->promolastmin = (int)$promolastmin;
4882 $sea_record->promofinalprice = $ppromofinalprice;
4883
4884 $dbo->insertObject('#__vikbooking_seasons', $sea_record, 'id');
4885
4886 $vbo_promo_id = $sea_record->id;
4887
4888 $app->enqueueMessage(JText::translate('VBSEASONSAVED'));
4889
4890 // update session values
4891 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
4892 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
4893 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $first ? $first : $updforvcm['dfrom'];
4894 } else {
4895 $updforvcm['dfrom'] = $first;
4896 }
4897 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
4898 $updforvcm['dto'] = $updforvcm['dto'] < $second ? $second : $updforvcm['dto'];
4899 } else {
4900 $updforvcm['dto'] = $second;
4901 }
4902 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
4903 foreach ($roomids as $rid) {
4904 if (!in_array($rid, $updforvcm['rooms'])) {
4905 $updforvcm['rooms'][] = $rid;
4906 }
4907 }
4908 } else {
4909 $updforvcm['rooms'] = $roomids;
4910 }
4911 if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
4912 foreach ($roomids as $rid) {
4913 if (array_key_exists($rid, $updforvcm['rplans'])) {
4914 $updforvcm['rplans'][$rid] = $updforvcm['rplans'][$rid] + $priceids;
4915 } else {
4916 $updforvcm['rplans'][$rid] = $priceids;
4917 }
4918 }
4919 } else {
4920 $updforvcm['rplans'] = array();
4921 foreach ($roomids as $rid) {
4922 $updforvcm['rplans'][$rid] = $priceids;
4923 }
4924 }
4925 if (!$ppromo) {
4926 $session->set('vbVcmRatesUpd', $updforvcm);
4927 }
4928
4929 /**
4930 * Create the promotion also on the selected channels
4931 *
4932 * @since 1.13.0 (J) - 1.3.0 (WP)
4933 */
4934 if ($ppromo && $pchannels) {
4935 foreach ($pchannels as $channel_key) {
4936 $promo_obj = VikBooking::getPromotionHandlers($channel_key);
4937 if (!is_object($promo_obj)) {
4938 continue;
4939 }
4940 /**
4941 * We inject for VCM the ID of the newly created promotion in VBO.
4942 *
4943 * @since 1.15.0 (J) - 1.5.0 (WP)
4944 */
4945 $ch_result = $promo_obj->createPromotion(array('vbo_promo_id' => $vbo_promo_id), 'new');
4946 if (!$ch_result) {
4947 VikError::raiseWarning('', $promo_obj->getName() . ': ' . $promo_obj->getError());
4948 } else {
4949 $resp = $promo_obj->getResponse();
4950 $app->enqueueMessage($promo_obj->getName() . ': ' . JText::translate('VBOCHPROMOSUCCESS') . (!empty($resp) ? ' (' . str_replace('e4j.ok.', '', $resp) . ')' : ''));
4951 // in case of success, unset the current session values in VCM
4952 $session->set('vcmBPromo', '');
4953 }
4954 }
4955 }
4956
4957 $app->redirect("index.php?option=com_vikbooking&task=".($andnew ? 'newseason' : 'seasons'));
4958 $app->close();
4959 }
4960
4961 public function removeseasons()
4962 {
4963 if (!JSession::checkToken()) {
4964 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4965 }
4966
4967 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
4968 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4969 }
4970
4971 $app = JFactory::getApplication();
4972 $dbo = JFactory::getDbo();
4973
4974 $ids = VikRequest::getVar('cid', array(0));
4975 $pidroom = VikRequest::getInt('idroom', '', 'request');
4976 $pwhere = VikRequest::getInt('where', '', 'request');
4977 if (!empty($pwhere)) {
4978 $ids[] = $pwhere;
4979 }
4980 $tot_removed = array();
4981 $prev_promos = array();
4982 foreach ($ids as $d) {
4983 if (empty($d)) {
4984 continue;
4985 }
4986 // check if it was a promotion
4987 $q = "SELECT `id` FROM `#__vikbooking_seasons` WHERE `id`=" . (int)$d . " AND `promo`=1;";
4988 $dbo->setQuery($q);
4989 $dbo->execute();
4990 if ($dbo->getNumRows()) {
4991 // push it as a previous promo
4992 array_push($prev_promos, $d);
4993 }
4994
4995 // delete the record
4996 $q = "DELETE FROM `#__vikbooking_seasons` WHERE `id`=".$dbo->quote($d).";";
4997 $dbo->setQuery($q);
4998 $dbo->execute();
4999 $tot_removed[] = $d;
5000 }
5001
5002 /**
5003 * Query promotion handlers, if any, to trigger the delete promotion event.
5004 *
5005 * @since 1.15.0 (J) - 1.5.0 (WP)
5006 */
5007 $promo_handlers = VikBooking::getPromotionHandlers();
5008 foreach ($prev_promos as $vbo_promo_id) {
5009 try {
5010 if (is_array($promo_handlers)) {
5011 foreach ($promo_handlers as $promo_handler) {
5012 if (!isset($promo_handler->instance) || !is_object($promo_handler->instance) || !method_exists($promo_handler->instance, 'triggerDelete')) {
5013 // outdated handler object
5014 continue;
5015 }
5016 if (!is_callable(array($promo_handler->instance, 'triggerDelete')) || !$promo_handler->instance->triggerDelete()) {
5017 // promotion handler does not support delete promotion event
5018 continue;
5019 }
5020 // invoke the delete promotion event for this handler
5021 $ch_result = $promo_handler->instance->createPromotion(array('vbo_promo_id' => $vbo_promo_id), 'delete');
5022 if (!$ch_result) {
5023 VikError::raiseWarning('', $promo_handler->instance->getName() . ': ' . $promo_handler->instance->getError());
5024 }
5025 }
5026 }
5027 } catch (Exception $e) {
5028 // do nothing
5029 }
5030 }
5031
5032 $app->enqueueMessage(JText::sprintf('VBRECORDSREMOVED', count($tot_removed)));
5033 $app->redirect("index.php?option=com_vikbooking&task=seasons".(!empty($pidroom) ? '&idroom='.$pidroom : ''));
5034 $app->close();
5035 }
5036
5037 public function updatecustomer()
5038 {
5039 if (!JSession::checkToken()) {
5040 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5041 }
5042
5043 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5044 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5045 }
5046
5047 $this->do_updatecustomer();
5048 }
5049
5050 public function updatecustomerstay()
5051 {
5052 if (!JSession::checkToken()) {
5053 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5054 }
5055
5056 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5057 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5058 }
5059
5060 $this->do_updatecustomer(true);
5061 }
5062
5063 private function do_updatecustomer($stay = false) {
5064 $dbo = JFactory::getDbo();
5065 $mainframe = JFactory::getApplication();
5066 $pfirst_name = VikRequest::getString('first_name', '', 'request');
5067 $plast_name = VikRequest::getString('last_name', '', 'request');
5068 $pcompany = VikRequest::getString('company', '', 'request');
5069 $pvat = VikRequest::getString('vat', '', 'request');
5070 $pemail = VikRequest::getString('email', '', 'request');
5071 $pphone = VikRequest::getString('phone', '', 'request');
5072 $pcountry = VikRequest::getString('country', '', 'request');
5073 $pstate = VikRequest::getString('state', '', 'request');
5074 $ppin = VikRequest::getString('pin', '', 'request');
5075 $pujid = VikRequest::getInt('ujid', '', 'request');
5076 $paddress = VikRequest::getString('address', '', 'request');
5077 $pcity = VikRequest::getString('city', '', 'request');
5078 $pzip = VikRequest::getString('zip', '', 'request');
5079 $pfisccode = VikRequest::getString('fisccode', '', 'request');
5080 $ppec = VikRequest::getString('pec', '', 'request');
5081 $precipcode = VikRequest::getString('recipcode', '', 'request');
5082 $pgender = VikRequest::getString('gender', '', 'request');
5083 $pgender = in_array($pgender, array('F', 'M')) ? $pgender : '';
5084 $pbdate = VikRequest::getString('bdate', '', 'request');
5085 $ppbirth = VikRequest::getString('pbirth', '', 'request');
5086 $pdoctype = VikRequest::getString('doctype', '', 'request');
5087 $pdocnum = VikRequest::getString('docnum', '', 'request');
5088 $pnotes = VikRequest::getString('notes', '', 'request');
5089 $pscandocimg = VikRequest::getString('scandocimg', '', 'request');
5090 $pischannel = VikRequest::getInt('ischannel', '', 'request');
5091 $pcommission = VikRequest::getFloat('commission', '', 'request');
5092 $pcalccmmon = VikRequest::getInt('calccmmon', '', 'request');
5093 $papplycmmon = VikRequest::getInt('applycmmon', '', 'request');
5094 $pchname = VikRequest::getString('chname', '', 'request');
5095 $pchcolor = VikRequest::getString('chcolor', '', 'request');
5096 $pwhere = VikRequest::getInt('where', '', 'request');
5097 $ptmpl = VikRequest::getString('tmpl', '', 'request');
5098 $pcheckin = VikRequest::getInt('checkin', '', 'request');
5099 $pbid = VikRequest::getInt('bid', '', 'request');
5100 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
5101 if (!empty($pwhere) && !empty($pfirst_name) && !empty($plast_name) && !empty($pemail)) {
5102 $q = "SELECT * FROM `#__vikbooking_customers` WHERE `id`=".(int)$pwhere." LIMIT 1;";
5103 $dbo->setQuery($q);
5104 $dbo->execute();
5105 if ($dbo->getNumRows() == 1) {
5106 $customer = $dbo->loadAssoc();
5107 } else {
5108 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5109 exit;
5110 }
5111 /**
5112 * Existing customers are recognized by equal first name, last name and email address.
5113 *
5114 * @since 1.3.0
5115 */
5116 $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;";
5117 $dbo->setQuery($q);
5118 $dbo->execute();
5119 if ($dbo->getNumRows() == 0) {
5120 $cpin = VikBooking::getCPinIstance();
5121 if (empty($ppin)) {
5122 $ppin = $customer['pin'];
5123 } elseif ($cpin->pinExists($ppin, $customer['pin'])) {
5124 $ppin = $cpin->generateUniquePin();
5125 }
5126 //file upload
5127 jimport('joomla.filesystem.file');
5128 $pimg = VikRequest::getVar('docimg', null, 'files', 'array');
5129 $gimg = "";
5130 if (isset($pimg) && strlen(trim($pimg['name']))) {
5131 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($pimg['name'])));
5132 $src = $pimg['tmp_name'];
5133 $dest = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans'.DIRECTORY_SEPARATOR;
5134 $j = "";
5135 if (file_exists($dest.$filename)) {
5136 $j = rand(171, 1717);
5137 while (file_exists($dest.$j.$filename)) {
5138 $j++;
5139 }
5140 }
5141 $finaldest = $dest.$j.$filename;
5142 $check = getimagesize($pimg['tmp_name']);
5143 if (($check[2] & imagetypes()) || preg_match("/application\/(zip|pdf)$/", $pimg['type'])) {
5144 if (VikBooking::uploadFile($src, $finaldest)) {
5145 $gimg = $j.$filename;
5146 } else {
5147 VikError::raiseWarning('', 'Error while uploading image');
5148 }
5149 } else {
5150 VikError::raiseWarning('', 'Uploaded file is not an Image');
5151 }
5152 } elseif (!empty($pscandocimg)) {
5153 $gimg = $pscandocimg;
5154 }
5155 //
5156 $pischannel = $pischannel > 0 ? 1 : 0;
5157 $pcalccmmon = $pcalccmmon > 0 ? 1 : 0;
5158 $papplycmmon = $papplycmmon > 0 ? 1 : 0;
5159 $pchname = str_replace(' ', '', trim($pchname));
5160 $pchname = strlen($pchname) <= 0 && $pischannel > 0 ? str_replace(' ', '', trim($pfirst_name.' '.$plast_name)) : $pchname;
5161 $chparams = array(
5162 'commission' => ($pcommission > 0.00 ? $pcommission : 0),
5163 'calccmmon' => $pcalccmmon,
5164 'applycmmon' => $papplycmmon,
5165 'chcolor' => $pchcolor,
5166 'chname' => $pchname
5167 );
5168
5169 /**
5170 * Customer profile picture (URL or uploaded file).
5171 *
5172 * @since 1.15.3 (J) - 1.5.5 (WP)
5173 */
5174 $customer_pic = VikRequest::getString('pic', '', 'request');
5175 $customer_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
5176 if (is_array($customer_pic_img) && !empty($customer_pic_img['name'])) {
5177 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($customer_pic_img['name'])));
5178 $src = $customer_pic_img['tmp_name'];
5179 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
5180 $j = "";
5181 if (is_file($dest.$filename)) {
5182 $j = rand(1, 99999);
5183 while (is_file($dest . $j .$filename)) {
5184 $j++;
5185 }
5186 }
5187 $finaldest = $dest . $j . $filename;
5188 $check = getimagesize($customer_pic_img['tmp_name']);
5189 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
5190 if (VikBooking::uploadFile($src, $finaldest)) {
5191 $customer_pic = $j . $filename;
5192 } else {
5193 VikError::raiseWarning('', 'Error while uploading image');
5194 }
5195 } else {
5196 VikError::raiseWarning('', 'Uploaded file is not an Image');
5197 }
5198 }
5199
5200 // update customer object
5201 $new_customer = new stdClass;
5202 $new_customer->id = (int)$pwhere;
5203 $new_customer->first_name = $pfirst_name;
5204 $new_customer->last_name = $plast_name;
5205 $new_customer->email = $pemail;
5206 $new_customer->phone = $pphone;
5207 $new_customer->country = $pcountry;
5208 $new_customer->pin = $ppin;
5209 $new_customer->ujid = $pujid;
5210 $new_customer->address = $paddress;
5211 $new_customer->city = $pcity;
5212 $new_customer->zip = $pzip;
5213 $new_customer->state = $pstate;
5214 $new_customer->doctype = $pdoctype;
5215 $new_customer->docnum = $pdocnum;
5216 if (!empty($gimg)) {
5217 $new_customer->docimg = $gimg;
5218 }
5219 $new_customer->notes = $pnotes;
5220 $new_customer->ischannel = $pischannel;
5221 $new_customer->chdata = json_encode($chparams);
5222 $new_customer->company = $pcompany;
5223 $new_customer->vat = $pvat;
5224 $new_customer->gender = $pgender;
5225 $new_customer->bdate = $pbdate;
5226 $new_customer->pbirth = $ppbirth;
5227 $new_customer->fisccode = $pfisccode;
5228 $new_customer->pec = $ppec;
5229 $new_customer->recipcode = $precipcode;
5230 $new_customer->pic = $customer_pic;
5231 /**
5232 * We need to update the previous information stored through
5233 * the custom fields when making a reservation for/by this client.
5234 *
5235 * @since 1.13
5236 */
5237 $skip_prev_fields = array(
5238 'id',
5239 'ujid',
5240 'docimg',
5241 'ischannel',
5242 'chdata',
5243 'notes',
5244 );
5245 if (!empty($customer['cfields'])) {
5246 $custf_info = json_decode($customer['cfields'], true);
5247 foreach ($new_customer as $fname => $fnewval) {
5248 if (!isset($customer[$fname]) || in_array($fname, $skip_prev_fields)) {
5249 continue;
5250 }
5251 // seek for old value in custom fields submitted
5252 foreach ($custf_info as $k => $v) {
5253 if (!empty($customer[$fname]) && $v == $customer[$fname]) {
5254 // field found, replace it with the new value
5255 $custf_info[$k] = $fnewval;
5256 }
5257 }
5258 }
5259 // update value on db
5260 $new_customer->cfields = json_encode($custf_info);
5261 }
5262
5263 // trigger the customer before-update event
5264 $cpin->pluginCustomerSync($new_customer->id, 'update', (array)$new_customer, $before = true);
5265
5266 // update customer record
5267 $dbo->updateObject('#__vikbooking_customers', $new_customer, 'id');
5268
5269 // trigger the customer after-save event
5270 $cpin->pluginCustomerSync($new_customer->id, 'update', (array)$new_customer, $before = false);
5271
5272 // update all the bookings affected by this Customer ID as a sales channel
5273 $source_name = 'customer'.$pwhere.'_'.$pchname;
5274 if ($pischannel > 0) {
5275 $oid_clause = '';
5276 if ($customer['ischannel'] < 1) {
5277 //Was not a sales channel but now it is, so update all his bookings
5278 $q = "SELECT `o`.`idorderota`, `co`.`idorder`
5279 FROM `#__vikbooking_customers_orders` AS `co`
5280 LEFT JOIN `#__vikbooking_orders` AS `o` ON `co`.`idorder`=`o`.`id`
5281 WHERE `co`.`idcustomer`=".$customer['id'].";";
5282 $dbo->setQuery($q);
5283 $all_bids = $dbo->loadAssocList();
5284 if ($all_bids) {
5285 $bids = array();
5286 foreach ($all_bids as $bid) {
5287 if (empty($idorderota) && !in_array($bid['idorder'], $bids)) {
5288 $bids[] = $bid['idorder'];
5289 }
5290 }
5291 if ($bids) {
5292 $oid_clause = " OR `id` IN (".implode(',', $bids).")";
5293 }
5294 }
5295 }
5296 $q = "UPDATE `#__vikbooking_orders` SET `channel`=".$dbo->quote($source_name)." WHERE `channel` LIKE 'customer".$pwhere."%'".$oid_clause.";";
5297 } else {
5298 $q = "UPDATE `#__vikbooking_orders` SET `channel`=NULL,`cmms`=NULL WHERE `channel` LIKE 'customer".$pwhere."%';";
5299 }
5300 $dbo->setQuery($q);
5301 $dbo->execute();
5302 //
5303 $mainframe->enqueueMessage(JText::translate('VBCUSTOMERSAVED'));
5304 } else {
5305 //email already exists
5306 $ex_customer = $dbo->loadAssoc();
5307 //check if coming from the Check-in view or not
5308 if (!empty($pcheckin) && !empty($pbid)) {
5309 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5310 /**
5311 * @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
5312 */
5313 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5314 //
5315 exit;
5316 } elseif (!empty($pgoto)) {
5317 // check if coming from a specific task
5318 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5319 $mainframe->redirect(base64_decode($pgoto));
5320 exit;
5321 } else {
5322 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>');
5323 $mainframe->redirect("index.php?option=com_vikbooking&task=editcustomer&cid[]=".$pwhere);
5324 exit;
5325 }
5326 }
5327 }
5328
5329 //check if coming from the Check-in view
5330 if (!empty($pcheckin) && !empty($pbid)) {
5331 /**
5332 * @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
5333 */
5334 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $pbid);
5335 exit;
5336 }
5337
5338 if ($stay) {
5339 $mainframe->redirect("index.php?option=com_vikbooking&task=editcustomer&cid[]=" . $pwhere . (!empty($pgoto) ? '&goto=' . $pgoto : ''));
5340 exit;
5341 }
5342
5343 // check if coming from a specific task
5344 if (!empty($pgoto)) {
5345 $mainframe->redirect(base64_decode($pgoto));
5346 exit;
5347 }
5348
5349 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5350 }
5351
5352 public function savecustomer() {
5353 if (!JSession::checkToken()) {
5354 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5355 }
5356 $dbo = JFactory::getDbo();
5357 $mainframe = JFactory::getApplication();
5358 $pfirst_name = VikRequest::getString('first_name', '', 'request');
5359 $plast_name = VikRequest::getString('last_name', '', 'request');
5360 $pcompany = VikRequest::getString('company', '', 'request');
5361 $pvat = VikRequest::getString('vat', '', 'request');
5362 $pemail = VikRequest::getString('email', '', 'request');
5363 $pphone = VikRequest::getString('phone', '', 'request');
5364 $pcountry = VikRequest::getString('country', '', 'request');
5365 $pstate = VikRequest::getString('state', '', 'request');
5366 $ppin = VikRequest::getString('pin', '', 'request');
5367 $pujid = VikRequest::getInt('ujid', '', 'request');
5368 $paddress = VikRequest::getString('address', '', 'request');
5369 $pcity = VikRequest::getString('city', '', 'request');
5370 $pzip = VikRequest::getString('zip', '', 'request');
5371 $pfisccode = VikRequest::getString('fisccode', '', 'request');
5372 $ppec = VikRequest::getString('pec', '', 'request');
5373 $precipcode = VikRequest::getString('recipcode', '', 'request');
5374 $pgender = VikRequest::getString('gender', '', 'request');
5375 $pgender = in_array($pgender, array('F', 'M')) ? $pgender : '';
5376 $pbdate = VikRequest::getString('bdate', '', 'request');
5377 $ppbirth = VikRequest::getString('pbirth', '', 'request');
5378 $pdoctype = VikRequest::getString('doctype', '', 'request');
5379 $pdocnum = VikRequest::getString('docnum', '', 'request');
5380 $pnotes = VikRequest::getString('notes', '', 'request');
5381 $pscandocimg = VikRequest::getString('scandocimg', '', 'request');
5382 $pischannel = VikRequest::getInt('ischannel', '', 'request');
5383 $pcommission = VikRequest::getFloat('commission', '', 'request');
5384 $pcalccmmon = VikRequest::getInt('calccmmon', '', 'request');
5385 $papplycmmon = VikRequest::getInt('applycmmon', '', 'request');
5386 $pchname = VikRequest::getString('chname', '', 'request');
5387 $pchcolor = VikRequest::getString('chcolor', '', 'request');
5388 $ptmpl = VikRequest::getString('tmpl', '', 'request');
5389 $pcheckin = VikRequest::getInt('checkin', '', 'request');
5390 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
5391 $pbid = VikRequest::getInt('bid', '', 'request');
5392 if (!empty($pfirst_name) && !empty($plast_name) && !empty($pemail)) {
5393 $cpin = VikBooking::getCPinIstance();
5394 /**
5395 * Existing customers are recognized by equal first name, last name and email address.
5396 *
5397 * @since 1.3.0
5398 */
5399 $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;";
5400 $dbo->setQuery($q);
5401 $dbo->execute();
5402 if ($dbo->getNumRows() == 0) {
5403 if (empty($ppin)) {
5404 $ppin = $cpin->generateUniquePin();
5405 } elseif ($cpin->pinExists($ppin)) {
5406 $ppin = $cpin->generateUniquePin();
5407 }
5408 //file upload
5409 jimport('joomla.filesystem.file');
5410 $pimg = VikRequest::getVar('docimg', null, 'files', 'array');
5411 $gimg = "";
5412 if (isset($pimg) && strlen(trim($pimg['name']))) {
5413 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($pimg['name'])));
5414 $src = $pimg['tmp_name'];
5415 $dest = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans'.DIRECTORY_SEPARATOR;
5416 $j = "";
5417 if (file_exists($dest.$filename)) {
5418 $j = rand(171, 1717);
5419 while (file_exists($dest.$j.$filename)) {
5420 $j++;
5421 }
5422 }
5423 $finaldest = $dest.$j.$filename;
5424 $check = getimagesize($pimg['tmp_name']);
5425 if (($check[2] & imagetypes()) || preg_match("/application\/(zip|pdf)$/", $pimg['type'])) {
5426 if (VikBooking::uploadFile($src, $finaldest)) {
5427 $gimg = $j.$filename;
5428 } else {
5429 VikError::raiseWarning('', 'Error while uploading image');
5430 }
5431 } else {
5432 VikError::raiseWarning('', 'Uploaded file is not an Image');
5433 }
5434 } elseif (!empty($pscandocimg)) {
5435 $gimg = $pscandocimg;
5436 }
5437 //
5438 $pischannel = $pischannel > 0 ? 1 : 0;
5439 $pcalccmmon = $pcalccmmon > 0 ? 1 : 0;
5440 $papplycmmon = $papplycmmon > 0 ? 1 : 0;
5441 $pchname = str_replace(' ', '', trim($pchname));
5442 $pchname = strlen($pchname) <= 0 && $pischannel > 0 ? str_replace(' ', '', trim($pfirst_name.' '.$plast_name)) : $pchname;
5443 $chparams = array(
5444 'commission' => ($pcommission > 0.00 ? $pcommission : 0),
5445 'calccmmon' => $pcalccmmon,
5446 'applycmmon' => $papplycmmon,
5447 'chcolor' => $pchcolor,
5448 'chname' => $pchname
5449 );
5450
5451 /**
5452 * Customer profile picture (URL or uploaded file).
5453 *
5454 * @since 1.15.3 (J) - 1.5.5 (WP)
5455 */
5456 $customer_pic = VikRequest::getString('pic', '', 'request');
5457 $customer_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
5458 if (is_array($customer_pic_img) && !empty($customer_pic_img['name'])) {
5459 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($customer_pic_img['name'])));
5460 $src = $customer_pic_img['tmp_name'];
5461 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
5462 $j = "";
5463 if (is_file($dest.$filename)) {
5464 $j = rand(1, 99999);
5465 while (is_file($dest . $j .$filename)) {
5466 $j++;
5467 }
5468 }
5469 $finaldest = $dest . $j . $filename;
5470 $check = getimagesize($customer_pic_img['tmp_name']);
5471 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
5472 if (VikBooking::uploadFile($src, $finaldest)) {
5473 $customer_pic = $j . $filename;
5474 } else {
5475 VikError::raiseWarning('', 'Error while uploading image');
5476 }
5477 } else {
5478 VikError::raiseWarning('', 'Uploaded file is not an Image');
5479 }
5480 }
5481
5482 // build customer record
5483 $customer_obj = new stdClass;
5484 $customer_obj->first_name = $pfirst_name;
5485 $customer_obj->last_name = $plast_name;
5486 $customer_obj->email = $pemail;
5487 $customer_obj->phone = $pphone;
5488 $customer_obj->country = $pcountry;
5489 $customer_obj->pin = $ppin;
5490 $customer_obj->ujid = $pujid;
5491 $customer_obj->address = $paddress;
5492 $customer_obj->city = $pcity;
5493 $customer_obj->zip = $pzip;
5494 $customer_obj->state = $pstate;
5495 $customer_obj->doctype = $pdoctype;
5496 $customer_obj->docnum = $pdocnum;
5497 $customer_obj->docimg = $gimg;
5498 $customer_obj->notes = $pnotes;
5499 $customer_obj->ischannel = $pischannel;
5500 $customer_obj->chdata = json_encode($chparams);
5501 $customer_obj->company = $pcompany;
5502 $customer_obj->vat = $pvat;
5503 $customer_obj->gender = $pgender;
5504 $customer_obj->bdate = $pbdate;
5505 $customer_obj->pbirth = $ppbirth;
5506 $customer_obj->fisccode = $pfisccode;
5507 $customer_obj->pec = $ppec;
5508 $customer_obj->recipcode = $precipcode;
5509 $customer_obj->pic = !empty($customer_pic) ? $customer_pic : null;
5510
5511 // trigger the customer before-insert event
5512 $cpin->pluginCustomerSync(0, 'insert', (array)$customer_obj, $before = true);
5513
5514 // insert the new customer record
5515 $dbo->insertObject('#__vikbooking_customers', $customer_obj, 'id');
5516 $lid = isset($customer_obj->id) ? $customer_obj->id : null;
5517
5518 // trigger the customer after-save event
5519 $cpin->pluginCustomerSync($lid, 'insert', (array)$customer_obj, $before = false);
5520
5521 if (!empty($lid)) {
5522 $mainframe->enqueueMessage(JText::translate('VBCUSTOMERSAVED'));
5523 //check if coming from the Check-in view
5524 if (!empty($pcheckin) && !empty($pbid)) {
5525 $cpin->setNewPin($ppin);
5526 $cpin->setNewCustomerId($lid);
5527 $cpin->saveCustomerBooking($pbid);
5528 /**
5529 * @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
5530 */
5531 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5532 //
5533 exit;
5534 }
5535 // check if coming from a specific task
5536 if (!empty($pgoto) && !empty($pbid)) {
5537 $cpin->setNewPin($ppin);
5538 $cpin->setNewCustomerId($lid);
5539 $cpin->saveCustomerBooking($pbid);
5540 $mainframe->redirect(base64_decode($pgoto));
5541 exit;
5542 }
5543 }
5544 } else {
5545 //email already exists
5546 $ex_customer = $dbo->loadAssoc();
5547 //check if coming from the Check-in view or not
5548 if (!empty($pcheckin) && !empty($pbid)) {
5549 $cpin->setNewPin($ex_customer['pin']);
5550 $cpin->setNewCustomerId($ex_customer['id']);
5551 $cpin->saveCustomerBooking($pbid);
5552 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5553 /**
5554 * @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
5555 */
5556 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5557 //
5558 exit;
5559 } elseif (!empty($pgoto) && !empty($pbid)) {
5560 // check if coming from a specific task
5561 $cpin->setNewPin($ex_customer['pin']);
5562 $cpin->setNewCustomerId($ex_customer['id']);
5563 $cpin->saveCustomerBooking($pbid);
5564 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5565 $mainframe->redirect(base64_decode($pgoto));
5566 exit;
5567 } else {
5568 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>');
5569 }
5570 }
5571 }
5572 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5573 }
5574
5575 public function customers() {
5576 VikBookingHelper::printHeader("22");
5577
5578 VikRequest::setVar('view', VikRequest::getCmd('view', 'customers'));
5579
5580 parent::display();
5581
5582 if (VikBooking::showFooter()) {
5583 VikBookingHelper::printFooter();
5584 }
5585 }
5586
5587 public function newcustomer() {
5588 VikBookingHelper::printHeader("22");
5589
5590 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomer'));
5591
5592 parent::display();
5593
5594 if (VikBooking::showFooter()) {
5595 VikBookingHelper::printFooter();
5596 }
5597 }
5598
5599 public function editcustomer() {
5600 VikBookingHelper::printHeader("22");
5601
5602 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomer'));
5603
5604 parent::display();
5605
5606 if (VikBooking::showFooter()) {
5607 VikBookingHelper::printFooter();
5608 }
5609 }
5610
5611 public function removecustomers()
5612 {
5613 if (!JSession::checkToken()) {
5614 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5615 }
5616
5617 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
5618 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5619 }
5620
5621 $ids = VikRequest::getVar('cid', array(0));
5622 if ($ids) {
5623 $dbo = JFactory::getDBO();
5624 $cpin = VikBooking::getCPinIstance();
5625 foreach ($ids as $d) {
5626 $cpin->pluginCustomerSync($d, 'delete');
5627 $q = "DELETE FROM `#__vikbooking_customers` WHERE `id`=".(int)$d.";";
5628 $dbo->setQuery($q);
5629 $dbo->execute();
5630 }
5631 }
5632 $mainframe = JFactory::getApplication();
5633 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5634 }
5635
5636 public function restrictions() {
5637 VikBookingHelper::printHeader("restrictions");
5638
5639 VikRequest::setVar('view', VikRequest::getCmd('view', 'restrictions'));
5640
5641 parent::display();
5642
5643 if (VikBooking::showFooter()) {
5644 VikBookingHelper::printFooter();
5645 }
5646 }
5647
5648 public function newrestriction() {
5649 VikBookingHelper::printHeader("restrictions");
5650
5651 VikRequest::setVar('view', VikRequest::getCmd('view', 'managerestriction'));
5652
5653 parent::display();
5654
5655 if (VikBooking::showFooter()) {
5656 VikBookingHelper::printFooter();
5657 }
5658 }
5659
5660 public function editrestriction() {
5661 VikBookingHelper::printHeader("restrictions");
5662
5663 VikRequest::setVar('view', VikRequest::getCmd('view', 'managerestriction'));
5664
5665 parent::display();
5666
5667 if (VikBooking::showFooter()) {
5668 VikBookingHelper::printFooter();
5669 }
5670 }
5671
5672 public function createrestriction()
5673 {
5674 if (!JSession::checkToken()) {
5675 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5676 }
5677
5678 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
5679 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5680 }
5681
5682 $dbo = JFactory::getDBO();
5683 $session = JFactory::getSession();
5684 $mainframe = JFactory::getApplication();
5685 $updforvcm = $session->get('vbVcmRatesUpd', '');
5686 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
5687 $pname = VikRequest::getString('name', '', 'request');
5688 $pmonth = VikRequest::getInt('month', '', 'request');
5689 $pmonth = empty($pmonth) ? 0 : $pmonth;
5690 $pname = empty($pname) ? 'Restriction '.$pmonth : $pname;
5691 $pdfrom = VikRequest::getString('dfrom', '', 'request');
5692 $pdto = VikRequest::getString('dto', '', 'request');
5693 $pwday = VikRequest::getString('wday', '', 'request');
5694 $pwdaytwo = VikRequest::getString('wdaytwo', '', 'request');
5695 $pwdaytwo = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday == $pwdaytwo ? '' : $pwdaytwo;
5696 $pcomboa = VikRequest::getString('comboa', '', 'request');
5697 $pcomboa = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboa : '';
5698 $pcombob = VikRequest::getString('combob', '', 'request');
5699 $pcombob = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombob : '';
5700 $pcomboc = VikRequest::getString('comboc', '', 'request');
5701 $pcomboc = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboc : '';
5702 $pcombod = VikRequest::getString('combod', '', 'request');
5703 $pcombod = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombod : '';
5704 $combostr = '';
5705 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboa) ? $pcomboa.':' : ':';
5706 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombob) ? $pcombob.':' : ':';
5707 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboc) ? $pcomboc.':' : ':';
5708 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombod) ? $pcombod : '';
5709 $pminlos = VikRequest::getInt('minlos', '', 'request');
5710 $pminlos = $pminlos < 1 ? 1 : $pminlos;
5711 $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
5712 $pmaxlos = empty($pmaxlos) ? 0 : $pmaxlos;
5713 $pmultiplyminlos = VikRequest::getString('multiplyminlos', '', 'request');
5714 $pmultiplyminlos = empty($pmultiplyminlos) ? 0 : 1;
5715 $pallrooms = VikRequest::getString('allrooms', '', 'request');
5716 $pallrooms = $pallrooms == "1" ? 1 : 0;
5717 $pidrooms = VikRequest::getVar('idrooms', array(0));
5718 $ridr = '';
5719 $roomidsforsess = array();
5720 if (!empty($pidrooms) && @count($pidrooms) && $pallrooms == 0) {
5721 foreach ($pidrooms as $idr) {
5722 if (empty($idr)) {
5723 continue;
5724 }
5725 $ridr .= '-'.$idr.'-;';
5726 $roomidsforsess[] = (int)$idr;
5727 }
5728 } elseif ($pallrooms > 0) {
5729 $q = "SELECT `id` FROM `#__vikbooking_rooms`;";
5730 $dbo->setQuery($q);
5731 $dbo->execute();
5732 if ($dbo->getNumRows() > 0) {
5733 $fetchids = $dbo->loadAssocList();
5734 foreach ($fetchids as $fetchid) {
5735 $roomidsforsess[] = (int)$fetchid['id'];
5736 }
5737 }
5738 }
5739 $pcta = VikRequest::getInt('cta', '', 'request');
5740 $pctd = VikRequest::getInt('ctd', '', 'request');
5741 $pctad = VikRequest::getVar('ctad', array());
5742 $pctdd = VikRequest::getVar('ctdd', array());
5743 if ($pminlos == 1 && strlen($pwday) == 0 && empty($pctad) && empty($pctdd) && $pmaxlos < 1) {
5744 // VBO 1.11 - we now allow restrictions with just 1 night of stay
5745 // VikError::raiseWarning('', JText::translate('VBUSELESSRESTRICTION'));
5746 // $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5747 // exit;
5748 }
5749
5750 //check if there are restrictions for this month
5751 if ($pmonth > 0) {
5752 $q = "SELECT `id` FROM `#__vikbooking_restrictions` WHERE `month`='".$pmonth."';";
5753 $dbo->setQuery($q);
5754 $dbo->execute();
5755 if ($dbo->getNumRows() > 0) {
5756 VikError::raiseWarning('', JText::translate('VBRESTRICTIONMONTHEXISTS'));
5757 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5758 exit;
5759 }
5760 $pdfrom = 0;
5761 $pdto = 0;
5762 } else {
5763 //dates range
5764 if (empty($pdfrom) || empty($pdto)) {
5765 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
5766 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5767 exit;
5768 } else {
5769 $housto = $pdfrom == $pdto ? 23 : 0;
5770 $minsto = $pdfrom == $pdto ? 59 : 0;
5771 $secsto = $pdfrom == $pdto ? 59 : 0;
5772 $pdfrom = VikBooking::getDateTimestamp($pdfrom, 0, 0);
5773 $pdto = VikBooking::getDateTimestamp($pdto, $housto, $minsto, $secsto);
5774 }
5775 if ($pdfrom > $pdto) {
5776 // invalid dates in the past
5777 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
5778 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5779 exit;
5780 }
5781 }
5782 //CTA and CTD
5783 $setcta = array();
5784 $setctd = array();
5785 if ($pcta > 0 && count($pctad) > 0) {
5786 foreach ($pctad as $ctwd) {
5787 if (strlen($ctwd)) {
5788 $setcta[] = '-'.(int)$ctwd.'-';
5789 }
5790 }
5791 }
5792 if ($pctd > 0 && count($pctdd) > 0) {
5793 foreach ($pctdd as $ctwd) {
5794 if (strlen($ctwd)) {
5795 $setctd[] = '-'.(int)$ctwd.'-';
5796 }
5797 }
5798 }
5799 //
5800 //update session values
5801 if (!($pdfrom > 0)) {
5802 $attemptyear = (int)date('Y');
5803 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
5804 if ($attemptfrom < time()) {
5805 $attemptyear++;
5806 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
5807 }
5808 $attemptto = mktime(0, 0, 0, $pmonth, date('t', $attemptfrom), $attemptyear);
5809 } else {
5810 $attemptfrom = $pdfrom;
5811 $attemptto = $pdto;
5812 }
5813 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
5814 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
5815 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $attemptfrom ? $attemptfrom : $updforvcm['dfrom'];
5816 } else {
5817 $updforvcm['dfrom'] = $attemptfrom;
5818 }
5819 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
5820 $updforvcm['dto'] = $updforvcm['dto'] < $attemptto ? $attemptto : $updforvcm['dto'];
5821 } else {
5822 $updforvcm['dto'] = $attemptto;
5823 }
5824 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
5825 foreach ($roomidsforsess as $rid) {
5826 if (!in_array($rid, $updforvcm['rooms'])) {
5827 $updforvcm['rooms'][] = $rid;
5828 }
5829 }
5830 } else {
5831 $updforvcm['rooms'] = $roomidsforsess;
5832 }
5833 if (!array_key_exists('rplans', $updforvcm) || !is_array($updforvcm['rplans'])) {
5834 $updforvcm['rplans'] = array();
5835 }
5836 $session->set('vbVcmRatesUpd', $updforvcm);
5837 //
5838 $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").");";
5839 $dbo->setQuery($q);
5840 $dbo->execute();
5841 $lid = $dbo->insertid();
5842 if (!empty($lid)) {
5843 /**
5844 * Repeat restriction on the selected week days until the limit
5845 *
5846 * @since 1.13
5847 */
5848 $prepeat = VikRequest::getInt('repeat', 0, 'request');
5849 $prepeatuntil = VikRequest::getString('repeatuntil', '', 'request');
5850 if ($prepeat > 0 && !empty($prepeatuntil) && $pdfrom > 0 && $pdto > 0) {
5851 $repeat_intervals = array();
5852 $start = getdate($pdfrom);
5853 $end = getdate($pdto);
5854 $wdays = array();
5855 while ($start[0] <= $end[0]) {
5856 // push requested week day
5857 array_push($wdays, $start['wday']);
5858 // next day
5859 $start = getdate(mktime($start['hours'], $start['minutes'], $start['seconds'], $start['mon'], ($start['mday'] + 1), $start['year']));
5860 }
5861 $dtuntil = VikBooking::getDateTimestamp($prepeatuntil, 23, 59, 59);
5862 if (count($wdays) < 7 && $dtuntil > $pdto) {
5863 // increment end date for the repeat
5864 $end = getdate(mktime($end['hours'], $end['minutes'], $end['seconds'], $end['mon'], ($end['mday'] + 1), $end['year']));
5865 //
5866 $until_info = getdate($dtuntil);
5867 $interval = array();
5868 while ($end[0] <= $until_info[0]) {
5869 if (in_array($end['wday'], $wdays)) {
5870 if (!isset($interval['from'])) {
5871 $interval['from'] = $end[0];
5872 }
5873 $interval['to'] = $end[0];
5874 } else {
5875 if (isset($interval['from'])) {
5876 // append interval
5877 array_push($repeat_intervals, $interval);
5878 // reset interval
5879 $interval = array();
5880 }
5881 }
5882 // next day
5883 $end = getdate(mktime($end['hours'], $end['minutes'], $end['seconds'], $end['mon'], ($end['mday'] + 1), $end['year']));
5884 }
5885 if (isset($interval['from'])) {
5886 // append last hanging interval
5887 array_push($repeat_intervals, $interval);
5888 }
5889 if (count($repeat_intervals)) {
5890 // create the repeated records for the calculated intervals
5891 $repeat_count = 2;
5892 foreach ($repeat_intervals as $rp) {
5893 if (date('Y-m-d', $rp['from']) == date('Y-m-d', $rp['to'])) {
5894 // adjust time in case of equal dates (1 single day restriction)
5895 $rpfrom = getdate($rp['from']);
5896 $rpto = getdate($rp['to']);
5897 $rp['from'] = mktime(0, 0, 0, $rpfrom['mon'], $rpfrom['mday'], $rpfrom['year']);
5898 /**
5899 * The end date of the restriction must cover the whole day until 23:59:59.
5900 *
5901 * @since 1.15.4 (J) - 1.5.4 (WP)
5902 */
5903 $rp['to'] = mktime(23, 59, 59, $rpto['mon'], $rpto['mday'], $rpto['year']);
5904 }
5905 // adjust name
5906 $restr_rp_name = $pname . " #{$repeat_count}";
5907 //
5908 $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").");";
5909 $dbo->setQuery($q);
5910 $dbo->execute();
5911 $lid = $dbo->insertid();
5912 if (!empty($lid)) {
5913 $repeat_count++;
5914 }
5915 }
5916 }
5917 }
5918 }
5919 //
5920 $mainframe->enqueueMessage(JText::translate('VBRESTRICTIONSAVED'));
5921 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
5922 } else {
5923 VikError::raiseWarning('', 'Error while saving');
5924 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5925 }
5926 }
5927
5928 public function updaterestriction()
5929 {
5930 if (!JSession::checkToken()) {
5931 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5932 }
5933
5934 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5935 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5936 }
5937
5938 $dbo = JFactory::getDBO();
5939 $session = JFactory::getSession();
5940 $mainframe = JFactory::getApplication();
5941 $updforvcm = $session->get('vbVcmRatesUpd', '');
5942 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
5943 $pwhere = VikRequest::getInt('where', '', 'request');
5944 $pname = VikRequest::getString('name', '', 'request');
5945 $pmonth = VikRequest::getInt('month', '', 'request');
5946 $pmonth = empty($pmonth) ? 0 : $pmonth;
5947 $pname = empty($pname) ? 'Restriction '.$pmonth : $pname;
5948 $pdfrom = VikRequest::getString('dfrom', '', 'request');
5949 $pdto = VikRequest::getString('dto', '', 'request');
5950 $pwday = VikRequest::getString('wday', '', 'request');
5951 $pwdaytwo = VikRequest::getString('wdaytwo', '', 'request');
5952 $pwdaytwo = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday == $pwdaytwo ? '' : $pwdaytwo;
5953 $pcomboa = VikRequest::getString('comboa', '', 'request');
5954 $pcomboa = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboa : '';
5955 $pcombob = VikRequest::getString('combob', '', 'request');
5956 $pcombob = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombob : '';
5957 $pcomboc = VikRequest::getString('comboc', '', 'request');
5958 $pcomboc = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboc : '';
5959 $pcombod = VikRequest::getString('combod', '', 'request');
5960 $pcombod = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombod : '';
5961 $combostr = '';
5962 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboa) ? $pcomboa.':' : ':';
5963 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombob) ? $pcombob.':' : ':';
5964 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboc) ? $pcomboc.':' : ':';
5965 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombod) ? $pcombod : '';
5966 $pminlos = VikRequest::getInt('minlos', '', 'request');
5967 $pminlos = $pminlos < 1 ? 1 : $pminlos;
5968 $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
5969 $pmaxlos = empty($pmaxlos) ? 0 : $pmaxlos;
5970 $pmultiplyminlos = VikRequest::getString('multiplyminlos', '', 'request');
5971 $pmultiplyminlos = empty($pmultiplyminlos) ? 0 : 1;
5972 $pallrooms = VikRequest::getString('allrooms', '', 'request');
5973 $pallrooms = $pallrooms == "1" ? 1 : 0;
5974 $pidrooms = VikRequest::getVar('idrooms', array(0));
5975 $ridr = '';
5976 $roomidsforsess = array();
5977 if (!empty($pidrooms) && @count($pidrooms) && $pallrooms == 0) {
5978 foreach ($pidrooms as $idr) {
5979 if (empty($idr)) {
5980 continue;
5981 }
5982 $ridr .= '-'.$idr.'-;';
5983 $roomidsforsess[] = (int)$idr;
5984 }
5985 } elseif ($pallrooms > 0) {
5986 $q = "SELECT `id` FROM `#__vikbooking_rooms`;";
5987 $dbo->setQuery($q);
5988 $dbo->execute();
5989 if ($dbo->getNumRows() > 0) {
5990 $fetchids = $dbo->loadAssocList();
5991 foreach ($fetchids as $fetchid) {
5992 $roomidsforsess[] = (int)$fetchid['id'];
5993 }
5994 }
5995 }
5996 $pcta = VikRequest::getInt('cta', '', 'request');
5997 $pctd = VikRequest::getInt('ctd', '', 'request');
5998 $pctad = VikRequest::getVar('ctad', array());
5999 $pctdd = VikRequest::getVar('ctdd', array());
6000 if ($pminlos == 1 && strlen($pwday) == 0 && empty($pctad) && empty($pctdd) && $pmaxlos < 1) {
6001 // VBO 1.11 - we now allow restrictions with just 1 night of stay
6002 // VikError::raiseWarning('', JText::translate('VBUSELESSRESTRICTION'));
6003 // $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6004 // exit;
6005 }
6006 //check if there are restrictions for this month
6007 if ($pmonth > 0) {
6008 $q = "SELECT `id` FROM `#__vikbooking_restrictions` WHERE `month`='".$pmonth."' AND `id`!='".$pwhere."';";
6009 $dbo->setQuery($q);
6010 $dbo->execute();
6011 if ($dbo->getNumRows() > 0) {
6012 VikError::raiseWarning('', JText::translate('VBRESTRICTIONMONTHEXISTS'));
6013 $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6014 exit;
6015 }
6016 $pdfrom = 0;
6017 $pdto = 0;
6018 } else {
6019 //dates range
6020 if (empty($pdfrom) || empty($pdto)) {
6021 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
6022 $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6023 exit;
6024 } else {
6025 $housto = $pdfrom == $pdto ? 23 : 0;
6026 $minsto = $pdfrom == $pdto ? 59 : 0;
6027 $secsto = $pdfrom == $pdto ? 59 : 0;
6028 $pdfrom = VikBooking::getDateTimestamp($pdfrom, 0, 0);
6029 $pdto = VikBooking::getDateTimestamp($pdto, $housto, $minsto, $secsto);
6030 }
6031 if ($pdfrom > $pdto) {
6032 // invalid dates in the past
6033 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
6034 $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6035 exit;
6036 }
6037 }
6038 //CTA and CTD
6039 $setcta = array();
6040 $setctd = array();
6041 if ($pcta > 0 && count($pctad) > 0) {
6042 foreach ($pctad as $ctwd) {
6043 if (strlen($ctwd)) {
6044 $setcta[] = '-'.(int)$ctwd.'-';
6045 }
6046 }
6047 }
6048 if ($pctd > 0 && count($pctdd) > 0) {
6049 foreach ($pctdd as $ctwd) {
6050 if (strlen($ctwd)) {
6051 $setctd[] = '-'.(int)$ctwd.'-';
6052 }
6053 }
6054 }
6055 //
6056 //update session values
6057 if (!($pdfrom > 0)) {
6058 $attemptyear = (int)date('Y');
6059 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
6060 if ($attemptfrom < time()) {
6061 $attemptyear++;
6062 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
6063 }
6064 $attemptto = mktime(0, 0, 0, $pmonth, date('t', $attemptfrom), $attemptyear);
6065 } else {
6066 $attemptfrom = $pdfrom;
6067 $attemptto = $pdto;
6068 }
6069 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
6070 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
6071 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $attemptfrom ? $attemptfrom : $updforvcm['dfrom'];
6072 } else {
6073 $updforvcm['dfrom'] = $attemptfrom;
6074 }
6075 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
6076 $updforvcm['dto'] = $updforvcm['dto'] < $attemptto ? $attemptto : $updforvcm['dto'];
6077 } else {
6078 $updforvcm['dto'] = $attemptto;
6079 }
6080 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
6081 foreach ($roomidsforsess as $rid) {
6082 if (!in_array($rid, $updforvcm['rooms'])) {
6083 $updforvcm['rooms'][] = $rid;
6084 }
6085 }
6086 } else {
6087 $updforvcm['rooms'] = $roomidsforsess;
6088 }
6089 if (!array_key_exists('rplans', $updforvcm) || !is_array($updforvcm['rplans'])) {
6090 $updforvcm['rplans'] = array();
6091 }
6092 $session->set('vbVcmRatesUpd', $updforvcm);
6093 //
6094 $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."';";
6095 $dbo->setQuery($q);
6096 $dbo->execute();
6097 $mainframe->enqueueMessage(JText::translate('VBRESTRICTIONSAVED'));
6098 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
6099 }
6100
6101 public function removerestrictions()
6102 {
6103 if (!JSession::checkToken()) {
6104 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6105 }
6106
6107 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6108 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6109 }
6110
6111 $ids = VikRequest::getVar('cid', array(0));
6112 if ($ids) {
6113 $dbo = JFactory::getDBO();
6114 foreach ($ids as $d) {
6115 $q = "DELETE FROM `#__vikbooking_restrictions` WHERE `id`=".(int)$d.";";
6116 $dbo->setQuery($q);
6117 $dbo->execute();
6118 }
6119 }
6120 $mainframe = JFactory::getApplication();
6121 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
6122 }
6123
6124 public function prices() {
6125 VikBookingHelper::printHeader("1");
6126
6127 VikRequest::setVar('view', VikRequest::getCmd('view', 'prices'));
6128
6129 parent::display();
6130
6131 if (VikBooking::showFooter()) {
6132 VikBookingHelper::printFooter();
6133 }
6134 }
6135
6136 public function newprice() {
6137 VikBookingHelper::printHeader("1");
6138
6139 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageprice'));
6140
6141 parent::display();
6142
6143 if (VikBooking::showFooter()) {
6144 VikBookingHelper::printFooter();
6145 }
6146 }
6147
6148 public function editprice() {
6149 VikBookingHelper::printHeader("1");
6150
6151 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageprice'));
6152
6153 parent::display();
6154
6155 if (VikBooking::showFooter()) {
6156 VikBookingHelper::printFooter();
6157 }
6158 }
6159
6160 public function createprice()
6161 {
6162 if (!JSession::checkToken()) {
6163 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6164 }
6165
6166 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6167 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6168 }
6169
6170 $this->do_createprice();
6171 }
6172
6173 public function createprice_new()
6174 {
6175 if (!JSession::checkToken()) {
6176 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6177 }
6178
6179 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6180 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6181 }
6182
6183 $this->do_createprice(true);
6184 }
6185
6186 private function do_createprice($new = false)
6187 {
6188 $app = JFactory::getApplication();
6189 $dbo = JFactory::getDbo();
6190
6191 $pprice = VikRequest::getString('price', '', 'request');
6192 $pattr = VikRequest::getString('attr', '', 'request');
6193 $ppraliq = VikRequest::getInt('praliq', '', 'request');
6194 $pmeal_plans = (array)VikRequest::getVar('meal_plans', []);
6195 $pbreakfast_included = in_array('breakfast', $pmeal_plans) ? 1 : 0;
6196 $pfree_cancellation = VikRequest::getInt('free_cancellation', 0, 'request');
6197 $pfree_cancellation = $pfree_cancellation == 1 ? 1 : 0;
6198 $pcanc_deadline = VikRequest::getInt('canc_deadline', '', 'request');
6199 $pminlos = VikRequest::getInt('minlos', 0, 'request');
6200 $pminlos = $pminlos < 0 ? 0 : $pminlos;
6201 $pminhadv = VikRequest::getInt('minhadv', '', 'request');
6202 $pminhadv = $pminhadv < 0 ? 0 : $pminhadv;
6203 $pcanc_policy = VikRequest::getString('canc_policy', '', 'request', VIKREQUEST_ALLOWHTML);
6204
6205 $is_derived = $app->input->getInt('is_derived', 0);
6206 $derived_id = $app->input->getUInt('derived_id', 0);
6207 $derived_data = $app->input->get('derived_data', [], 'array');
6208
6209 $parent_id = 0;
6210 $derived_info = null;
6211
6212 if ($is_derived && $derived_id && $derived_data) {
6213 $parent_id = $derived_id;
6214 $derived_info = $derived_data;
6215 $derived_info['mode'] = ($derived_info['mode'] ?? '') == 'charge' ? 'charge' : 'discount';
6216 $derived_info['type'] = ($derived_info['type'] ?? '') == 'absolute' ? 'absolute' : 'percent';
6217 $derived_info['value'] = (float) ($derived_info['value'] ?? 0);
6218 $derived_info['follow_restr'] = isset($derived_info['follow_restr']) ? 1 : 0;
6219 if (!$derived_info['value']) {
6220 $parent_id = 0;
6221 $derived_info = null;
6222 }
6223 }
6224
6225 if (!empty($pprice)) {
6226 $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') . ");";
6227 $dbo->setQuery($q);
6228 $dbo->execute();
6229
6230 $new_rplan_id = $dbo->insertid();
6231
6232 /**
6233 * Allow to populate base rates for newly created rate plan for all room types using the parent rate.
6234 *
6235 * @since 1.18.6 (J) - 1.8.6 (WP)
6236 */
6237 if ($app->input->getBool('set_derived_rates', false) && $is_derived && $derived_id && $derived_info) {
6238 // find all rooms with base rates defined for the parent rate plan
6239 $dbo->setQuery(
6240 $dbo->getQuery(true)
6241 ->select($dbo->qn('idroom'))
6242 ->from($dbo->qn('#__vikbooking_dispcost'))
6243 ->where($dbo->qn('idprice') . ' = ' . $derived_id)
6244 ->group($dbo->qn('idroom'))
6245 ->order($dbo->qn('idroom') . ' ASC')
6246 );
6247 $populateRoomIds = array_map('intval', $dbo->loadColumn());
6248
6249 // determine rates table range of nights of stays
6250 $fromNights = $pminlos ?: 1;
6251 $maxNights = $app->input->getUInt('set_max_nights') ?: $pminlos ?: 1;
6252 $maxNights = $maxNights < $fromNights ? $fromNights : $maxNights;
6253
6254 // fetch base rates for all the involved room types
6255 $dbo->setQuery(
6256 $dbo->getQuery(true)
6257 ->select([
6258 $dbo->qn('idroom'),
6259 $dbo->qn('days'),
6260 $dbo->qn('cost'),
6261 ])
6262 ->from($dbo->qn('#__vikbooking_dispcost'))
6263 ->where($dbo->qn('idroom') . ' IN (' . implode(', ', $populateRoomIds) . ')')
6264 ->where($dbo->qn('idprice') . ' = ' . $derived_id)
6265 ->order($dbo->qn('idroom') . ' ASC')
6266 ->order($dbo->qn('days') . ' ASC')
6267 );
6268 $roomBaseRates = $dbo->loadAssocList();
6269
6270 // iterate all rooms involved
6271 foreach ($populateRoomIds as $roomId) {
6272 // loop through the interval of nights of stay
6273 for ($n = $fromNights; $n <= $maxNights; $n++) {
6274 // fetch current room rate in parent rate plan
6275 $roomParentNightlyRate = 0;
6276 $roomParentExactRate = 0;
6277 foreach ($roomBaseRates as $roomBaseRate) {
6278 if ($roomBaseRate['idroom'] != $roomId) {
6279 // ignore room
6280 continue;
6281 }
6282 if (!$roomParentNightlyRate) {
6283 // set rate for the lowest number of nights of stay
6284 $roomParentNightlyRate = $roomBaseRate['cost'] / ($roomBaseRate['days'] ?: 1);
6285 }
6286 if ($roomBaseRate['days'] == $n) {
6287 // set room exact rate for this number of nights of stay
6288 $roomParentExactRate = $roomBaseRate['cost'];
6289 // do not proceed
6290 break;
6291 }
6292 }
6293
6294 if (!$roomParentNightlyRate) {
6295 // missing pricing information from parent rate plan
6296 continue;
6297 }
6298
6299 // determine the cost to apply for the newly created derived rate plan
6300 $nightlyDerivedRate = $roomParentExactRate ?: $roomParentNightlyRate;
6301
6302 // check how the new rate was derived
6303 if ($derived_info['mode'] == 'charge') {
6304 // increase rate
6305 if ($derived_info['type'] == 'absolute') {
6306 // fixed increase
6307 $nightlyDerivedRate += $derived_info['value'];
6308 } else {
6309 // percent increase
6310 $nightlyDerivedRate *= (100 + $derived_info['value']) / 100;
6311 }
6312 } else {
6313 // discount rate
6314 if ($derived_info['type'] == 'absolute') {
6315 // fixed discount
6316 $nightlyDerivedRate -= $derived_info['value'];
6317 } else {
6318 // percent discount
6319 $nightlyDerivedRate *= (100 - $derived_info['value']) / 100;
6320 }
6321 }
6322
6323 if (!$roomParentExactRate) {
6324 // multiply rate by number of nights of stay if started from the parent lowest number of nights
6325 $nightlyDerivedRate *= $n;
6326 }
6327
6328 // build new room base rate record
6329 $rateRecord = [
6330 'idroom' => $roomId,
6331 'days' => $n,
6332 'idprice' => $new_rplan_id,
6333 'cost' => round($nightlyDerivedRate, 2),
6334 ];
6335
6336 // cast to object
6337 $rateRecord = (object) $rateRecord;
6338
6339 // insert record
6340 $dbo->insertObject('#__vikbooking_dispcost', $rateRecord, 'id');
6341 }
6342 }
6343 }
6344 }
6345
6346 $app->redirect("index.php?option=com_vikbooking&task=" . ($new ? 'newprice' : 'prices'));
6347 $app->close();
6348 }
6349
6350 public function updateprice()
6351 {
6352 if (!JSession::checkToken()) {
6353 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6354 }
6355
6356 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6357 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6358 }
6359
6360 $this->do_updateprice();
6361 }
6362
6363 public function updatepricestay()
6364 {
6365 if (!JSession::checkToken()) {
6366 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6367 }
6368
6369 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6370 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6371 }
6372
6373 $this->do_updateprice(true);
6374 }
6375
6376 private function do_updateprice($stay = false)
6377 {
6378 $app = JFactory::getApplication();
6379 $dbo = JFactory::getDbo();
6380
6381 $pprice = VikRequest::getString('price', '', 'request');
6382 $pattr = VikRequest::getString('attr', '', 'request');
6383 $ppraliq = VikRequest::getInt('praliq', '', 'request');
6384 $pmeal_plans = (array)VikRequest::getVar('meal_plans', []);
6385 $pbreakfast_included = in_array('breakfast', $pmeal_plans) ? 1 : 0;
6386 $pfree_cancellation = VikRequest::getInt('free_cancellation', '', 'request');
6387 $pfree_cancellation = $pfree_cancellation == 1 ? 1 : 0;
6388 $pcanc_deadline = VikRequest::getInt('canc_deadline', '', 'request');
6389 $pminlos = VikRequest::getInt('minlos', 0, 'request');
6390 $pminlos = $pminlos < 0 ? 0 : $pminlos;
6391 $pminhadv = VikRequest::getInt('minhadv', '', 'request');
6392 $pminhadv = $pminhadv < 0 ? 0 : $pminhadv;
6393 $pcanc_policy = VikRequest::getString('canc_policy', '', 'request', VIKREQUEST_ALLOWHTML);
6394 $pwhereup = VikRequest::getInt('whereup', 0, 'request');
6395
6396 $is_derived = $app->input->getInt('is_derived', 0);
6397 $derived_id = $app->input->getUInt('derived_id', 0);
6398 $derived_data = $app->input->get('derived_data', [], 'array');
6399
6400 $parent_id = 0;
6401 $derived_info = null;
6402
6403 if ($is_derived && $derived_id && $derived_data) {
6404 $parent_id = $derived_id;
6405 $derived_info = $derived_data;
6406 $derived_info['mode'] = ($derived_info['mode'] ?? '') == 'charge' ? 'charge' : 'discount';
6407 $derived_info['type'] = ($derived_info['type'] ?? '') == 'absolute' ? 'absolute' : 'percent';
6408 $derived_info['value'] = (float) ($derived_info['value'] ?? 0);
6409 $derived_info['follow_restr'] = isset($derived_info['follow_restr']) ? 1 : 0;
6410 if (!$derived_info['value']) {
6411 $parent_id = 0;
6412 $derived_info = null;
6413 }
6414 }
6415
6416 if (!empty($pprice) && $pwhereup) {
6417 $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) . ";";
6418 $dbo->setQuery($q);
6419 $dbo->execute();
6420 }
6421
6422 $app->redirect("index.php?option=com_vikbooking&task=" . ($stay ? 'editprice&cid[]=' . $pwhereup : 'prices'));
6423 $app->close();
6424 }
6425
6426 public function removeprice()
6427 {
6428 if (!JSession::checkToken()) {
6429 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6430 }
6431
6432 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6433 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6434 }
6435
6436 $ids = VikRequest::getVar('cid', array(0));
6437 if ($ids) {
6438 $dbo = JFactory::getDBO();
6439 foreach ($ids as $d) {
6440 $q = "DELETE FROM `#__vikbooking_prices` WHERE `id`=".$dbo->quote($d).";";
6441 $dbo->setQuery($q);
6442 $dbo->execute();
6443 $q = "DELETE FROM `#__vikbooking_dispcost` WHERE `idprice`=".intval($d).";";
6444 $dbo->setQuery($q);
6445 $dbo->execute();
6446 }
6447 }
6448 $mainframe = JFactory::getApplication();
6449 $mainframe->redirect("index.php?option=com_vikbooking&task=prices");
6450 }
6451
6452 public function iva() {
6453 VikBookingHelper::printHeader("2");
6454
6455 VikRequest::setVar('view', VikRequest::getCmd('view', 'iva'));
6456
6457 parent::display();
6458
6459 if (VikBooking::showFooter()) {
6460 VikBookingHelper::printFooter();
6461 }
6462 }
6463
6464 public function newiva() {
6465 VikBookingHelper::printHeader("2");
6466
6467 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageiva'));
6468
6469 parent::display();
6470
6471 if (VikBooking::showFooter()) {
6472 VikBookingHelper::printFooter();
6473 }
6474 }
6475
6476 public function editiva() {
6477 VikBookingHelper::printHeader("2");
6478
6479 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageiva'));
6480
6481 parent::display();
6482
6483 if (VikBooking::showFooter()) {
6484 VikBookingHelper::printFooter();
6485 }
6486 }
6487
6488 public function createiva()
6489 {
6490 if (!JSession::checkToken()) {
6491 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6492 }
6493
6494 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6495 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6496 }
6497
6498 $paliqname = VikRequest::getString('aliqname', '', 'request');
6499 $paliqperc = VikRequest::getFloat('aliqperc', '', 'request');
6500 $pbreakdown_name = VikRequest::getVar('breakdown_name', array());
6501 $pbreakdown_rate = VikRequest::getVar('breakdown_rate', array());
6502 $ptaxcap = VikRequest::getFloat('taxcap', 0, 'request');
6503 if (!empty($paliqperc)) {
6504 $dbo = JFactory::getDBO();
6505 $breakdown_str = '';
6506 if (count($pbreakdown_name) > 0) {
6507 $breakdown_values = array();
6508 $bkcount = 0;
6509 $tot_sub_aliq = 0;
6510 foreach ($pbreakdown_name as $key => $subtax) {
6511 if (!empty($subtax) && floatval($pbreakdown_rate[$key]) > 0) {
6512 $breakdown_values[$bkcount]['name'] = $subtax;
6513 $breakdown_values[$bkcount]['aliq'] = (float)$pbreakdown_rate[$key];
6514 $tot_sub_aliq += (float)$pbreakdown_rate[$key];
6515 $bkcount++;
6516 }
6517 }
6518 if (count($breakdown_values) > 0) {
6519 $breakdown_str = json_encode($breakdown_values);
6520 if ($tot_sub_aliq < (float)$paliqperc || $tot_sub_aliq > (float)$paliqperc) {
6521 VikError::raiseWarning('', JText::translate('VBOTAXBKDWNERRNOMATCH'));
6522 }
6523 }
6524 }
6525 $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').");";
6526 $dbo->setQuery($q);
6527 $dbo->execute();
6528 }
6529 $mainframe = JFactory::getApplication();
6530 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6531 }
6532
6533 public function updateiva()
6534 {
6535 if (!JSession::checkToken()) {
6536 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6537 }
6538
6539 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6540 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6541 }
6542
6543 $paliqname = VikRequest::getString('aliqname', '', 'request');
6544 $paliqperc = VikRequest::getFloat('aliqperc', '', 'request');
6545 $pbreakdown_name = VikRequest::getVar('breakdown_name', array());
6546 $pbreakdown_rate = VikRequest::getVar('breakdown_rate', array());
6547 $ptaxcap = VikRequest::getFloat('taxcap', 0, 'request');
6548 $pwhereup = VikRequest::getInt('whereup', 0, 'request');
6549 if (!empty($paliqperc)) {
6550 $dbo = JFactory::getDBO();
6551 $breakdown_str = '';
6552 if (count($pbreakdown_name) > 0) {
6553 $breakdown_values = array();
6554 $bkcount = 0;
6555 $tot_sub_aliq = 0;
6556 foreach ($pbreakdown_name as $key => $subtax) {
6557 if (!empty($subtax) && floatval($pbreakdown_rate[$key]) > 0) {
6558 $breakdown_values[$bkcount]['name'] = $subtax;
6559 $breakdown_values[$bkcount]['aliq'] = (float)$pbreakdown_rate[$key];
6560 $tot_sub_aliq += (float)$pbreakdown_rate[$key];
6561 $bkcount++;
6562 }
6563 }
6564 if (count($breakdown_values) > 0) {
6565 $breakdown_str = json_encode($breakdown_values);
6566 if ($tot_sub_aliq < (float)$paliqperc || $tot_sub_aliq > (float)$paliqperc) {
6567 VikError::raiseWarning('', JText::translate('VBOTAXBKDWNERRNOMATCH'));
6568 }
6569 }
6570 }
6571 $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).";";
6572 $dbo->setQuery($q);
6573 $dbo->execute();
6574 }
6575 $mainframe = JFactory::getApplication();
6576 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6577 }
6578
6579 public function removeiva()
6580 {
6581 if (!JSession::checkToken()) {
6582 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6583 }
6584
6585 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6586 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6587 }
6588
6589 $ids = VikRequest::getVar('cid', array(0));
6590 if ($ids) {
6591 $dbo = JFactory::getDBO();
6592 foreach ($ids as $d) {
6593 $q = "DELETE FROM `#__vikbooking_iva` WHERE `id`=".$dbo->quote($d).";";
6594 $dbo->setQuery($q);
6595 $dbo->execute();
6596 }
6597 }
6598 $mainframe = JFactory::getApplication();
6599 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6600 }
6601
6602 public function categories() {
6603 VikBookingHelper::printHeader("4");
6604
6605 VikRequest::setVar('view', VikRequest::getCmd('view', 'categories'));
6606
6607 parent::display();
6608
6609 if (VikBooking::showFooter()) {
6610 VikBookingHelper::printFooter();
6611 }
6612 }
6613
6614 public function newcat() {
6615 VikBookingHelper::printHeader("4");
6616
6617 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecategory'));
6618
6619 parent::display();
6620
6621 if (VikBooking::showFooter()) {
6622 VikBookingHelper::printFooter();
6623 }
6624 }
6625
6626 public function editcat() {
6627 VikBookingHelper::printHeader("4");
6628
6629 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecategory'));
6630
6631 parent::display();
6632
6633 if (VikBooking::showFooter()) {
6634 VikBookingHelper::printFooter();
6635 }
6636 }
6637
6638 public function createcat()
6639 {
6640 if (!JSession::checkToken()) {
6641 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6642 }
6643
6644 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6645 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6646 }
6647
6648 $pcatname = VikRequest::getString('catname', '', 'request');
6649 $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWHTML);
6650 if (!empty($pcatname)) {
6651 $dbo = JFactory::getDBO();
6652 $q = "INSERT INTO `#__vikbooking_categories` (`name`,`descr`) VALUES(".$dbo->quote($pcatname).", ".$dbo->quote($pdescr).");";
6653 $dbo->setQuery($q);
6654 $dbo->execute();
6655 }
6656 $mainframe = JFactory::getApplication();
6657 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6658 }
6659
6660 public function updatecat()
6661 {
6662 if (!JSession::checkToken()) {
6663 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6664 }
6665
6666 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6667 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6668 }
6669
6670 $pcatname = VikRequest::getString('catname', '', 'request');
6671 $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWHTML);
6672 $pwhereup = VikRequest::getString('whereup', '', 'request');
6673 if (!empty($pcatname)) {
6674 $dbo = JFactory::getDBO();
6675 $q = "UPDATE `#__vikbooking_categories` SET `name`=".$dbo->quote($pcatname).", `descr`=".$dbo->quote($pdescr)." WHERE `id`=".$dbo->quote($pwhereup).";";
6676 $dbo->setQuery($q);
6677 $dbo->execute();
6678 }
6679 $mainframe = JFactory::getApplication();
6680 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6681 }
6682
6683 public function removecat()
6684 {
6685 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6686 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6687 }
6688
6689 $ids = VikRequest::getVar('cid', array(0));
6690 if ($ids) {
6691 $dbo = JFactory::getDBO();
6692 foreach ($ids as $d) {
6693 $q = "DELETE FROM `#__vikbooking_categories` WHERE `id`=".$dbo->quote($d).";";
6694 $dbo->setQuery($q);
6695 $dbo->execute();
6696 }
6697 }
6698 $mainframe = JFactory::getApplication();
6699 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6700 }
6701
6702 public function carat() {
6703 VikBookingHelper::printHeader("5");
6704
6705 VikRequest::setVar('view', VikRequest::getCmd('view', 'carat'));
6706
6707 parent::display();
6708
6709 if (VikBooking::showFooter()) {
6710 VikBookingHelper::printFooter();
6711 }
6712 }
6713
6714 public function newcarat() {
6715 VikBookingHelper::printHeader("5");
6716
6717 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecarat'));
6718
6719 parent::display();
6720
6721 if (VikBooking::showFooter()) {
6722 VikBookingHelper::printFooter();
6723 }
6724 }
6725
6726 public function editcarat() {
6727 VikBookingHelper::printHeader("5");
6728
6729 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecarat'));
6730
6731 parent::display();
6732
6733 if (VikBooking::showFooter()) {
6734 VikBookingHelper::printFooter();
6735 }
6736 }
6737
6738 public function createcarat()
6739 {
6740 if (!JSession::checkToken()) {
6741 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6742 }
6743
6744 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6745 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6746 }
6747
6748 $pcaratname = VikRequest::getString('caratname', '', 'request');
6749 $pcarattextimg = VikRequest::getString('carattextimg', '', 'request', VIKREQUEST_ALLOWRAW);
6750 $pautoresize = VikRequest::getString('autoresize', '', 'request');
6751 $presizeto = VikRequest::getString('resizeto', '', 'request');
6752 $pidrooms = VikRequest::getVar('idrooms', array());
6753 if (!empty($pcaratname)) {
6754 if (intval($_FILES['caraticon']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['caraticon']['name'])!="") {
6755 jimport('joomla.filesystem.file');
6756 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
6757 if (@is_uploaded_file($_FILES['caraticon']['tmp_name'])) {
6758 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['caraticon']['name'])));
6759 if (file_exists($updpath.$safename)) {
6760 $j=1;
6761 while (file_exists($updpath.$j.$safename)) {
6762 $j++;
6763 }
6764 $pwhere=$updpath.$j.$safename;
6765 } else {
6766 $j="";
6767 $pwhere=$updpath.$safename;
6768 }
6769 if (!getimagesize($_FILES['caraticon']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
6770 @unlink($pwhere);
6771 $picon="";
6772 } else {
6773 VikBooking::uploadFile($_FILES['caraticon']['tmp_name'], $pwhere);
6774 @chmod($pwhere, 0644);
6775 $picon=$j.$safename;
6776 if ($pautoresize=="1" && !empty($presizeto)) {
6777 $eforj = new vikResizer();
6778 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
6779 if ($origmod) {
6780 @unlink($pwhere);
6781 $picon='r_'.$j.$safename;
6782 }
6783 }
6784 }
6785 } else {
6786 $picon="";
6787 }
6788 } else {
6789 $picon="";
6790 }
6791 $dbo = JFactory::getDbo();
6792 // get new ordering
6793 $q = "SELECT `ordering` FROM `#__vikbooking_characteristics` ORDER BY `#__vikbooking_characteristics`.`ordering` DESC LIMIT 1;";
6794 $dbo->setQuery($q);
6795 $dbo->execute();
6796 if ($dbo->getNumRows()) {
6797 $newsortnum = $dbo->loadResult() + 1;
6798 } else {
6799 $newsortnum = 1;
6800 }
6801 $pordering = VikRequest::getInt('ordering', 0, 'request');
6802 $newsortnum = !empty($pordering) ? $pordering : $newsortnum;
6803 //
6804 $q = "INSERT INTO `#__vikbooking_characteristics` (`name`,`icon`,`textimg`,`ordering`) VALUES(".$dbo->quote($pcaratname).", ".$dbo->quote($picon).", ".$dbo->quote($pcarattextimg).", {$newsortnum});";
6805 $dbo->setQuery($q);
6806 $dbo->execute();
6807
6808 $new_carat_id = $dbo->insertid();
6809 if (!empty($new_carat_id)) {
6810 // assign/unset carat-rooms relations
6811 $rooms_with_carat = array();
6812 if (count($pidrooms)) {
6813 // assign this new carat to the requested rooms
6814 foreach ($pidrooms as $idroom) {
6815 if (empty($idroom)) {
6816 continue;
6817 }
6818 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
6819 $dbo->setQuery($q);
6820 $dbo->execute();
6821 if (!$dbo->getNumRows()) {
6822 continue;
6823 }
6824 $room_data = $dbo->loadAssoc();
6825 array_push($rooms_with_carat, $room_data['id']);
6826 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6827 if (in_array((string)$new_carat_id, $current_carats)) {
6828 continue;
6829 }
6830 if (count($current_carats) === 1 && (string)$current_carats[0] == '0') {
6831 // make sure we do not concatenate a real ID to 0
6832 $current_carats = array();
6833 }
6834 array_push($current_carats, $new_carat_id);
6835 $new_opts = implode(';', $current_carats) . ';';
6836 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
6837 $dbo->setQuery($q);
6838 $dbo->execute();
6839 }
6840 }
6841 if (!count($rooms_with_carat)) {
6842 // get all rooms to unset this carat (if previously set)
6843 array_push($rooms_with_carat, '0');
6844 }
6845 // unset the carat from the other rooms that may have it
6846 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_carat) . ");";
6847 $dbo->setQuery($q);
6848 $dbo->execute();
6849 if ($dbo->getNumRows()) {
6850 $unset_rooms_carat = $dbo->loadAssocList();
6851 foreach ($unset_rooms_carat as $room_data) {
6852 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6853 if (!in_array((string)$new_carat_id, $current_carats)) {
6854 // this room is not using this carat
6855 continue;
6856 }
6857 $caratkey = array_search((string)$new_carat_id, $current_carats);
6858 if ($caratkey === false) {
6859 // key not found
6860 continue;
6861 }
6862 // unset this carat ID from the string
6863 unset($current_carats[$caratkey]);
6864 if (!count($current_carats)) {
6865 // a room with no carats assigned will be listed as "0;"
6866 $current_carats = array(0);
6867 }
6868 $new_opts = implode(';', $current_carats) . ';';
6869 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
6870 $dbo->setQuery($q);
6871 $dbo->execute();
6872 }
6873 }
6874 //
6875 }
6876 }
6877 $mainframe = JFactory::getApplication();
6878 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
6879 }
6880
6881 public function updatecarat()
6882 {
6883 if (!JSession::checkToken()) {
6884 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6885 }
6886
6887 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6888 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6889 }
6890
6891 $pcaratname = VikRequest::getString('caratname', '', 'request');
6892 $pcarattextimg = VikRequest::getString('carattextimg', '', 'request', VIKREQUEST_ALLOWRAW);
6893 $pwhereup = VikRequest::getString('whereup', '', 'request');
6894 $pautoresize = VikRequest::getString('autoresize', '', 'request');
6895 $presizeto = VikRequest::getString('resizeto', '', 'request');
6896 $pidrooms = VikRequest::getVar('idrooms', array());
6897 $pordering = VikRequest::getInt('ordering', 1, 'request');
6898 if (!empty($pcaratname)) {
6899 if (intval($_FILES['caraticon']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['caraticon']['name'])!="") {
6900 jimport('joomla.filesystem.file');
6901 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
6902 if (@is_uploaded_file($_FILES['caraticon']['tmp_name'])) {
6903 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['caraticon']['name'])));
6904 if (file_exists($updpath.$safename)) {
6905 $j=1;
6906 while (file_exists($updpath.$j.$safename)) {
6907 $j++;
6908 }
6909 $pwhere=$updpath.$j.$safename;
6910 } else {
6911 $j="";
6912 $pwhere=$updpath.$safename;
6913 }
6914 if (!getimagesize($_FILES['caraticon']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
6915 @unlink($pwhere);
6916 $picon="";
6917 } else {
6918 VikBooking::uploadFile($_FILES['caraticon']['tmp_name'], $pwhere);
6919 @chmod($pwhere, 0644);
6920 $picon=$j.$safename;
6921 if ($pautoresize=="1" && !empty($presizeto)) {
6922 $eforj = new vikResizer();
6923 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
6924 if ($origmod) {
6925 @unlink($pwhere);
6926 $picon='r_'.$j.$safename;
6927 }
6928 }
6929 }
6930 } else {
6931 $picon="";
6932 }
6933 } else {
6934 $picon="";
6935 }
6936 $dbo = JFactory::getDbo();
6937 $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).";";
6938 $dbo->setQuery($q);
6939 $dbo->execute();
6940
6941 // assign/unset carat-rooms relations
6942 $rooms_with_carat = array();
6943 if (count($pidrooms)) {
6944 // assign this new carat to the requested rooms
6945 foreach ($pidrooms as $idroom) {
6946 if (empty($idroom)) {
6947 continue;
6948 }
6949 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
6950 $dbo->setQuery($q);
6951 $dbo->execute();
6952 if (!$dbo->getNumRows()) {
6953 continue;
6954 }
6955 $room_data = $dbo->loadAssoc();
6956 array_push($rooms_with_carat, $room_data['id']);
6957 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6958 if (in_array((string)$pwhereup, $current_carats)) {
6959 continue;
6960 }
6961 if (count($current_carats) === 1 && (string)$current_carats[0] == '0') {
6962 // make sure we do not concatenate a real ID to 0
6963 $current_carats = array();
6964 }
6965 array_push($current_carats, $pwhereup);
6966 $new_carats = implode(';', $current_carats) . ';';
6967 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_carats) . " WHERE `id`={$room_data['id']};";
6968 $dbo->setQuery($q);
6969 $dbo->execute();
6970 }
6971 }
6972 if (!count($rooms_with_carat)) {
6973 // get all rooms to unset this carat (if previously set)
6974 array_push($rooms_with_carat, '0');
6975 }
6976 // unset the carat from the other rooms that may have it
6977 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_carat) . ");";
6978 $dbo->setQuery($q);
6979 $dbo->execute();
6980 if ($dbo->getNumRows()) {
6981 $unset_rooms_carat = $dbo->loadAssocList();
6982 foreach ($unset_rooms_carat as $room_data) {
6983 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6984 if (!in_array((string)$pwhereup, $current_carats)) {
6985 // this room is not using this carat
6986 continue;
6987 }
6988 $caratkey = array_search((string)$pwhereup, $current_carats);
6989 if ($caratkey === false) {
6990 // key not found
6991 continue;
6992 }
6993 // unset this carat ID from the string
6994 unset($current_carats[$caratkey]);
6995 if (!count($current_carats)) {
6996 // a room with no carats assigned will be listed as "0;"
6997 $current_carats = array(0);
6998 }
6999 $new_carats = implode(';', $current_carats) . ';';
7000 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_carats) . " WHERE `id`={$room_data['id']};";
7001 $dbo->setQuery($q);
7002 $dbo->execute();
7003 }
7004 }
7005 //
7006 }
7007 $mainframe = JFactory::getApplication();
7008 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
7009 }
7010
7011 public function removecarat()
7012 {
7013 if (!JSession::checkToken()) {
7014 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7015 }
7016
7017 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7018 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7019 }
7020
7021 $ids = VikRequest::getVar('cid', array(0));
7022 if ($ids) {
7023 $dbo = JFactory::getDBO();
7024 foreach ($ids as $d) {
7025 $q = "SELECT `icon` FROM `#__vikbooking_characteristics` WHERE `id`=".$dbo->quote($d).";";
7026 $dbo->setQuery($q);
7027 $dbo->execute();
7028 if ($dbo->getNumRows() == 1) {
7029 $rows = $dbo->loadAssocList();
7030 if (!empty($rows[0]['icon']) && file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['icon'])) {
7031 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['icon']);
7032 }
7033 }
7034 $q = "DELETE FROM `#__vikbooking_characteristics` WHERE `id`=".$dbo->quote($d).";";
7035 $dbo->setQuery($q);
7036 $dbo->execute();
7037 }
7038 }
7039 $mainframe = JFactory::getApplication();
7040 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
7041 }
7042
7043 public function coupons() {
7044 VikBookingHelper::printHeader("17");
7045
7046 VikRequest::setVar('view', VikRequest::getCmd('view', 'coupons'));
7047
7048 parent::display();
7049
7050 if (VikBooking::showFooter()) {
7051 VikBookingHelper::printFooter();
7052 }
7053 }
7054
7055 public function newcoupon() {
7056 VikBookingHelper::printHeader("17");
7057
7058 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecoupon'));
7059
7060 parent::display();
7061
7062 if (VikBooking::showFooter()) {
7063 VikBookingHelper::printFooter();
7064 }
7065 }
7066
7067 public function editcoupon() {
7068 VikBookingHelper::printHeader("17");
7069
7070 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecoupon'));
7071
7072 parent::display();
7073
7074 if (VikBooking::showFooter()) {
7075 VikBookingHelper::printFooter();
7076 }
7077 }
7078
7079 public function createcoupon()
7080 {
7081 if (!JSession::checkToken()) {
7082 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7083 }
7084
7085 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
7086 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7087 }
7088
7089 $pcode = VikRequest::getString('code', '', 'request');
7090 $pvalue = VikRequest::getString('value', '', 'request');
7091 $pfrom = VikRequest::getString('from', '', 'request');
7092 $pto = VikRequest::getString('to', '', 'request');
7093 $pidrooms = VikRequest::getVar('idrooms', array(0));
7094 $ptype = VikRequest::getString('type', '', 'request');
7095 $ptype = $ptype == "1" ? 1 : 2;
7096 $ppercentot = VikRequest::getString('percentot', '', 'request');
7097 $ppercentot = $ppercentot == "1" ? 1 : 2;
7098 $pallvehicles = VikRequest::getString('allvehicles', '', 'request');
7099 $pallvehicles = $pallvehicles == "1" ? 1 : 0;
7100 $pmintotord = VikRequest::getFloat('mintotord', 0, 'request');
7101 $pmaxtotord = VikRequest::getFloat('maxtotord', 0, 'request');
7102 $pexcludetaxes = VikRequest::getInt('excludetaxes', 0, 'request');
7103 $pminlos = VikRequest::getInt('minlos', 0, 'request');
7104 $pcustomers = VikRequest::getVar('customers', array());
7105 $pautomatic = VikRequest::getInt('automatic', 0, 'request');
7106 $stridrooms = "";
7107 if (count($pidrooms) > 0 && $pallvehicles != 1) {
7108 foreach ($pidrooms as $ch) {
7109 if (!empty($ch)) {
7110 $stridrooms .= ";".$ch.";";
7111 }
7112 }
7113 }
7114 $strdatevalid = "";
7115 if (strlen($pfrom) > 0 && strlen($pto) > 0) {
7116 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
7117 $second = VikBooking::getDateTimestamp($pto, 0, 0);
7118 if ($first < $second) {
7119 $strdatevalid .= $first."-".$second;
7120 }
7121 }
7122
7123 $dbo = JFactory::getDbo();
7124 $app = JFactory::getApplication();
7125
7126 $q = "SELECT * FROM `#__vikbooking_coupons` WHERE `code`=".$dbo->quote($pcode).";";
7127 $dbo->setQuery($q);
7128 $dbo->execute();
7129 if ($dbo->getNumRows() > 0) {
7130 VikError::raiseWarning('', JText::translate('VBCOUPONEXISTS'));
7131 } else {
7132 $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) . ");";
7133 $dbo->setQuery($q);
7134 $dbo->execute();
7135
7136 $id_coupon = $dbo->insertid();
7137
7138 $app->enqueueMessage(JText::translate('VBCOUPONSAVEOK'));
7139
7140 // check if this coupon should be assigned to specific customers
7141 foreach ($pcustomers as $id_customer) {
7142 $customer_coupon = new stdClass;
7143 $customer_coupon->idcustomer = (int)$id_customer;
7144 $customer_coupon->idcoupon = (int)$id_coupon;
7145 $customer_coupon->automatic = $pautomatic ? 1 : 0;
7146
7147 $dbo->insertObject('#__vikbooking_customers_coupons', $customer_coupon, 'id');
7148 }
7149 }
7150 $app->redirect("index.php?option=com_vikbooking&task=coupons");
7151 }
7152
7153 public function updatecoupon()
7154 {
7155 if (!JSession::checkToken()) {
7156 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7157 }
7158
7159 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7160 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7161 }
7162
7163 $this->do_updatecoupon($stay = false);
7164 }
7165
7166 public function updatecoupon_stay()
7167 {
7168 if (!JSession::checkToken()) {
7169 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7170 }
7171
7172 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7173 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7174 }
7175
7176 $this->do_updatecoupon($stay = true);
7177 }
7178
7179 protected function do_updatecoupon($stay = false)
7180 {
7181 if (!JSession::checkToken()) {
7182 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7183 }
7184 $pcode = VikRequest::getString('code', '', 'request');
7185 $pvalue = VikRequest::getString('value', '', 'request');
7186 $pfrom = VikRequest::getString('from', '', 'request');
7187 $pto = VikRequest::getString('to', '', 'request');
7188 $pidrooms = VikRequest::getVar('idrooms', array(0));
7189 $pwhere = VikRequest::getInt('where', 0, 'request');
7190 $ptype = VikRequest::getString('type', '', 'request');
7191 $ptype = $ptype == "1" ? 1 : 2;
7192 $ppercentot = VikRequest::getString('percentot', '', 'request');
7193 $ppercentot = $ppercentot == "1" ? 1 : 2;
7194 $pallvehicles = VikRequest::getString('allvehicles', '', 'request');
7195 $pallvehicles = $pallvehicles == "1" ? 1 : 0;
7196 $pmintotord = VikRequest::getFloat('mintotord', 0, 'request');
7197 $pmaxtotord = VikRequest::getFloat('maxtotord', 0, 'request');
7198 $pexcludetaxes = VikRequest::getInt('excludetaxes', 0, 'request');
7199 $pminlos = VikRequest::getInt('minlos', 0, 'request');
7200 $pcustomers = VikRequest::getVar('customers', array());
7201 $pautomatic = VikRequest::getInt('automatic', 0, 'request');
7202 $stridrooms = "";
7203 if (count($pidrooms) > 0 && $pallvehicles != 1) {
7204 foreach ($pidrooms as $ch) {
7205 if (!empty($ch)) {
7206 $stridrooms .= ";".$ch.";";
7207 }
7208 }
7209 }
7210 $strdatevalid = "";
7211 if (strlen($pfrom) > 0 && strlen($pto) > 0) {
7212 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
7213 $second = VikBooking::getDateTimestamp($pto, 0, 0);
7214 if ($first < $second) {
7215 $strdatevalid .= $first."-".$second;
7216 }
7217 }
7218
7219 $dbo = JFactory::getDbo();
7220 $app = JFactory::getApplication();
7221
7222 $q = "SELECT * FROM `#__vikbooking_coupons` WHERE `code`=".$dbo->quote($pcode)." AND `id`!='".$pwhere."';";
7223 $dbo->setQuery($q);
7224 $dbo->execute();
7225 if ($dbo->getNumRows() > 0) {
7226 VikError::raiseWarning('', JText::translate('VBCOUPONEXISTS'));
7227 } else {
7228 $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 . ";";
7229 $dbo->setQuery($q);
7230 $dbo->execute();
7231
7232 $app->enqueueMessage(JText::translate('VBCOUPONSAVEOK'));
7233
7234 // clean up any previously created record with customers
7235 $q = "DELETE FROM `#__vikbooking_customers_coupons` WHERE `idcoupon`=" . $pwhere;
7236 $dbo->setQuery($q);
7237 $dbo->execute();
7238
7239 // check if this coupon should be assigned to specific customers
7240 foreach ($pcustomers as $id_customer) {
7241 $customer_coupon = new stdClass;
7242 $customer_coupon->idcustomer = (int)$id_customer;
7243 $customer_coupon->idcoupon = (int)$pwhere;
7244 $customer_coupon->automatic = $pautomatic ? 1 : 0;
7245
7246 $dbo->insertObject('#__vikbooking_customers_coupons', $customer_coupon, 'id');
7247 }
7248 }
7249
7250 if ($stay) {
7251 $app->redirect("index.php?option=com_vikbooking&task=editcoupon&cid[]=$pwhere");
7252 } else {
7253 $app->redirect("index.php?option=com_vikbooking&task=coupons");
7254 }
7255 }
7256
7257 public function removecoupons()
7258 {
7259 if (!JSession::checkToken()) {
7260 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7261 }
7262
7263 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7264 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7265 }
7266
7267 $dbo = JFactory::getDbo();
7268
7269 $ids = VikRequest::getVar('cid', array(0));
7270
7271 if ($ids) {
7272 foreach ($ids as $d) {
7273 // delete coupon record
7274 $q = "DELETE FROM `#__vikbooking_coupons` WHERE `id`=".$dbo->quote($d).";";
7275 $dbo->setQuery($q);
7276 $dbo->execute();
7277
7278 // clean up any previously created record with customers
7279 $q = "DELETE FROM `#__vikbooking_customers_coupons` WHERE `idcoupon`=" . (int)$d;
7280 $dbo->setQuery($q);
7281 $dbo->execute();
7282 }
7283 }
7284
7285 JFactory::getApplication()->redirect("index.php?option=com_vikbooking&task=coupons");
7286 }
7287
7288 public function removemoreimgs()
7289 {
7290 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7291 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7292 }
7293
7294 $mainframe = JFactory::getApplication();
7295 $proomid = VikRequest::getInt('roomid', '', 'request');
7296 $pimgind = VikRequest::getInt('imgind', '', 'request');
7297 if (!strlen($pimgind)) {
7298 $mainframe->redirect("index.php?option=com_vikbooking");
7299 exit;
7300 }
7301 $dbo = JFactory::getDBO();
7302 $q = "SELECT `moreimgs`,`imgcaptions` FROM `#__vikbooking_rooms` WHERE `id`='".$proomid."';";
7303 $dbo->setQuery($q);
7304 $dbo->execute();
7305 $row = $dbo->loadAssoc();
7306 $actmore = $row['moreimgs'];
7307 if (!empty($actmore)) {
7308 $actsplit = explode(';;', $actmore);
7309 $captions = json_decode($row['imgcaptions'], true);
7310 $captions = !is_array($captions) ? array() : $captions;
7311 if ($pimgind < 0) {
7312 foreach ($actsplit as $img) {
7313 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'big_'.$img);
7314 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'thumb_'.$img);
7315 }
7316 // reset images and captions
7317 $actsplit = array();
7318 $captions = array();
7319 } else {
7320 if (array_key_exists($pimgind, $actsplit)) {
7321 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'big_'.$actsplit[$pimgind]);
7322 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'thumb_'.$actsplit[$pimgind]);
7323 // unset current image
7324 unset($actsplit[$pimgind]);
7325 // unset caption if exists
7326 if (isset($captions[$pimgind])) {
7327 unset($captions[$pimgind]);
7328 $captions = array_values($captions);
7329 }
7330 }
7331 }
7332 $newstr = "";
7333 foreach ($actsplit as $oi) {
7334 if (!empty($oi)) {
7335 $newstr .= $oi.';;';
7336 }
7337 }
7338 $q = "UPDATE `#__vikbooking_rooms` SET `moreimgs`=".$dbo->quote($newstr).", `imgcaptions`=".$dbo->quote(json_encode($captions))." WHERE `id`='".$proomid."';";
7339 $dbo->setQuery($q);
7340 $dbo->execute();
7341 }
7342 $mainframe->redirect("index.php?option=com_vikbooking&task=editroom&cid[]=".$proomid);
7343 }
7344
7345 public function sortfield() {
7346 if (!JSession::checkToken('get')) {
7347 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7348 }
7349 $mainframe = JFactory::getApplication();
7350 $sortid = VikRequest::getVar('cid', array(0));
7351 $pmode = VikRequest::getString('mode', '', 'request');
7352 $dbo = JFactory::getDBO();
7353 if (!empty($pmode)) {
7354 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_custfields` ORDER BY `#__vikbooking_custfields`.`ordering` ASC;";
7355 $dbo->setQuery($q);
7356 $dbo->execute();
7357 $totr=$dbo->getNumRows();
7358 if ($totr > 1) {
7359 $data = $dbo->loadAssocList();
7360 if ($pmode == "up") {
7361 foreach ($data as $v) {
7362 if ($v['id'] == $sortid[0]) {
7363 $y = $v['ordering'];
7364 }
7365 }
7366 if ($y && $y > 1) {
7367 $vik = $y - 1;
7368 $found = false;
7369 foreach ($data as $v) {
7370 if (intval($v['ordering']) == intval($vik)) {
7371 $found=true;
7372 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
7373 $dbo->setQuery($q);
7374 $dbo->execute();
7375 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7376 $dbo->setQuery($q);
7377 $dbo->execute();
7378 break;
7379 }
7380 }
7381 if (!$found) {
7382 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7383 $dbo->setQuery($q);
7384 $dbo->execute();
7385 }
7386 }
7387 } elseif ($pmode == "down") {
7388 foreach ($data as $v) {
7389 if ($v['id'] == $sortid[0]) {
7390 $y = $v['ordering'];
7391 }
7392 }
7393 if ($y) {
7394 $vik = $y + 1;
7395 $found = false;
7396 foreach ($data as $v) {
7397 if (intval($v['ordering']) == intval($vik)) {
7398 $found=true;
7399 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
7400 $dbo->setQuery($q);
7401 $dbo->execute();
7402 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7403 $dbo->setQuery($q);
7404 $dbo->execute();
7405 break;
7406 }
7407 }
7408 if (!$found) {
7409 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7410 $dbo->setQuery($q);
7411 $dbo->execute();
7412 }
7413 }
7414 }
7415 }
7416 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7417 } else {
7418 $mainframe->redirect("index.php?option=com_vikbooking");
7419 }
7420 }
7421
7422 public function customf() {
7423 VikBookingHelper::printHeader("16");
7424
7425 VikRequest::setVar('view', VikRequest::getCmd('view', 'customf'));
7426
7427 parent::display();
7428
7429 if (VikBooking::showFooter()) {
7430 VikBookingHelper::printFooter();
7431 }
7432 }
7433
7434 public function newcustomf() {
7435 VikBookingHelper::printHeader("16");
7436
7437 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomf'));
7438
7439 parent::display();
7440
7441 if (VikBooking::showFooter()) {
7442 VikBookingHelper::printFooter();
7443 }
7444 }
7445
7446 public function editcustomf() {
7447 VikBookingHelper::printHeader("16");
7448
7449 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomf'));
7450
7451 parent::display();
7452
7453 if (VikBooking::showFooter()) {
7454 VikBookingHelper::printFooter();
7455 }
7456 }
7457
7458 public function createcustomf()
7459 {
7460 if (!JSession::checkToken()) {
7461 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7462 }
7463
7464 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
7465 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7466 }
7467
7468 $pname = VikRequest::getString('name', '', 'request', VIKREQUEST_ALLOWHTML);
7469 $ptype = VikRequest::getString('type', '', 'request');
7470 $pchoose = VikRequest::getVar('choose', array(0));
7471 $prequired = VikRequest::getString('required', '', 'request');
7472 $prequired = $prequired == "1" ? 1 : 0;
7473 $pflag = VikRequest::getString('flag', '', 'request');
7474 $pisemail = $pflag == 'isemail' ? 1 : 0;
7475 $pisnominative = $pflag == 'isnominative' && $ptype == 'text' ? 1 : 0;
7476 $pisphone = $pflag == 'isphone' && $ptype == 'text' ? 1 : 0;
7477 $pisaddress = $pflag == 'isaddress' && $ptype == 'text' ? 1 : 0;
7478 $piscity = $pflag == 'iscity' && $ptype == 'text' ? 1 : 0;
7479 $piszip = $pflag == 'iszip' && $ptype == 'text' ? 1 : 0;
7480 $piscompany = $pflag == 'iscompany' && $ptype == 'text' ? 1 : 0;
7481 $pisvat = $pflag == 'isvat' && $ptype == 'text' ? 1 : 0;
7482 $pisfisccode = $pflag == 'isfisccode' && $ptype == 'text' ? 1 : 0;
7483 $pispec = $pflag == 'ispec' && $ptype == 'text' ? 1 : 0;
7484 $pisrecipcode = $pflag == 'isrecipcode' && $ptype == 'text' ? 1 : 0;
7485 $fieldflag = '';
7486 if ($pisaddress == 1) {
7487 $fieldflag = 'address';
7488 } elseif ($piscity == 1) {
7489 $fieldflag = 'city';
7490 } elseif ($piszip == 1) {
7491 $fieldflag = 'zip';
7492 } elseif ($piscompany == 1) {
7493 $fieldflag = 'company';
7494 } elseif ($pisvat == 1) {
7495 $fieldflag = 'vat';
7496 } elseif ($pisfisccode == 1) {
7497 $fieldflag = 'fisccode';
7498 } elseif ($pispec == 1) {
7499 $fieldflag = 'pec';
7500 } elseif ($pisrecipcode == 1) {
7501 $fieldflag = 'recipcode';
7502 }
7503 $ppoplink = VikRequest::getString('poplink', '', 'request');
7504 $choosestr = "";
7505 if (is_array($pchoose)) {
7506 foreach ($pchoose as $ch) {
7507 if (!empty($ch)) {
7508 $choosestr .= $ch.";;__;;";
7509 }
7510 }
7511 }
7512 $defvalue = VikRequest::getString('defvalue', '', 'request');
7513
7514 $dbo = JFactory::getDbo();
7515
7516 $q = "SELECT `ordering` FROM `#__vikbooking_custfields` ORDER BY `#__vikbooking_custfields`.`ordering` DESC LIMIT 1;";
7517 $dbo->setQuery($q);
7518 $dbo->execute();
7519 if ($dbo->getNumRows() == 1) {
7520 $getlast = $dbo->loadResult();
7521 $newsortnum = $getlast + 1;
7522 } else {
7523 $newsortnum = 1;
7524 }
7525 $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).");";
7526 $dbo->setQuery($q);
7527 $dbo->execute();
7528 $mainframe = JFactory::getApplication();
7529 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7530 }
7531
7532 public function updatecustomf()
7533 {
7534 if (!JSession::checkToken()) {
7535 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7536 }
7537
7538 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7539 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7540 }
7541
7542 $pname = VikRequest::getString('name', '', 'request', VIKREQUEST_ALLOWHTML);
7543 $ptype = VikRequest::getString('type', '', 'request');
7544 $pchoose = VikRequest::getVar('choose', array(0));
7545 $prequired = VikRequest::getString('required', '', 'request');
7546 $prequired = $prequired == "1" ? 1 : 0;
7547 $pflag = VikRequest::getString('flag', '', 'request');
7548 $pisemail = $pflag == 'isemail' ? 1 : 0;
7549 $pisnominative = $pflag == 'isnominative' && $ptype == 'text' ? 1 : 0;
7550 $pisphone = $pflag == 'isphone' && $ptype == 'text' ? 1 : 0;
7551 $pisaddress = $pflag == 'isaddress' && $ptype == 'text' ? 1 : 0;
7552 $piscity = $pflag == 'iscity' && $ptype == 'text' ? 1 : 0;
7553 $piszip = $pflag == 'iszip' && $ptype == 'text' ? 1 : 0;
7554 $piscompany = $pflag == 'iscompany' && $ptype == 'text' ? 1 : 0;
7555 $pisvat = $pflag == 'isvat' && $ptype == 'text' ? 1 : 0;
7556 $pisfisccode = $pflag == 'isfisccode' && $ptype == 'text' ? 1 : 0;
7557 $pispec = $pflag == 'ispec' && $ptype == 'text' ? 1 : 0;
7558 $pisrecipcode = $pflag == 'isrecipcode' && $ptype == 'text' ? 1 : 0;
7559 $fieldflag = '';
7560 if ($pisaddress == 1) {
7561 $fieldflag = 'address';
7562 } elseif ($piscity == 1) {
7563 $fieldflag = 'city';
7564 } elseif ($piszip == 1) {
7565 $fieldflag = 'zip';
7566 } elseif ($piscompany == 1) {
7567 $fieldflag = 'company';
7568 } elseif ($pisvat == 1) {
7569 $fieldflag = 'vat';
7570 } elseif ($pisfisccode == 1) {
7571 $fieldflag = 'fisccode';
7572 } elseif ($pispec == 1) {
7573 $fieldflag = 'pec';
7574 } elseif ($pisrecipcode == 1) {
7575 $fieldflag = 'recipcode';
7576 }
7577 $ppoplink = VikRequest::getString('poplink', '', 'request');
7578 $pwhere = VikRequest::getInt('where', '', 'request');
7579 $choosestr = "";
7580 if (is_array($pchoose)) {
7581 foreach ($pchoose as $ch) {
7582 if (!empty($ch)) {
7583 $choosestr .= $ch.";;__;;";
7584 }
7585 }
7586 }
7587 $defvalue = VikRequest::getString('defvalue', '', 'request');
7588
7589 $dbo = JFactory::getDbo();
7590
7591 $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).";";
7592 $dbo->setQuery($q);
7593 $dbo->execute();
7594 $mainframe = JFactory::getApplication();
7595 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7596 }
7597
7598 public function removecustomf()
7599 {
7600 if (!JSession::checkToken()) {
7601 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7602 }
7603
7604 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7605 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7606 }
7607
7608 $ids = VikRequest::getVar('cid', array(0));
7609 if ($ids) {
7610 $dbo = JFactory::getDBO();
7611 foreach ($ids as $d) {
7612 $q = "DELETE FROM `#__vikbooking_custfields` WHERE `id`=".$dbo->quote($d).";";
7613 $dbo->setQuery($q);
7614 $dbo->execute();
7615 }
7616 }
7617 $mainframe = JFactory::getApplication();
7618 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7619 }
7620
7621 public function overv() {
7622 VikBookingHelper::printHeader("15");
7623
7624 VikRequest::setVar('view', VikRequest::getCmd('view', 'overv'));
7625
7626 parent::display();
7627
7628 if (VikBooking::showFooter()) {
7629 VikBookingHelper::printFooter();
7630 }
7631 }
7632
7633 public function translations() {
7634 VikBookingHelper::printHeader("21");
7635
7636 VikRequest::setVar('view', VikRequest::getCmd('view', 'translations'));
7637
7638 parent::display();
7639
7640 if (VikBooking::showFooter()) {
7641 VikBookingHelper::printFooter();
7642 }
7643 }
7644
7645 public function savetranslation() {
7646 if (!JSession::checkToken()) {
7647 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7648 }
7649 $this->do_savetranslation();
7650 }
7651
7652 public function savetranslationstay() {
7653 if (!JSession::checkToken()) {
7654 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7655 }
7656 $this->do_savetranslation(true);
7657 }
7658
7659 private function do_savetranslation($stay = false) {
7660 $dbo = JFactory::getDBO();
7661 $mainframe = JFactory::getApplication();
7662 $vbo_tn = VikBooking::getTranslator();
7663 $table = VikRequest::getString('vbo_table', '', 'request');
7664 $cur_langtab = VikRequest::getString('vbo_lang', '', 'request');
7665 $langs = $vbo_tn->getLanguagesList();
7666 $xml_tables = $vbo_tn->getTranslationTables();
7667 if (!empty($table) && array_key_exists($table, $xml_tables)) {
7668 $tn = VikRequest::getVar('tn', array(), 'request', 'array', VIKREQUEST_ALLOWRAW);
7669 $tn_saved = 0;
7670 $table_cols = $vbo_tn->getTableColumns($table);
7671 foreach ($langs as $ltag => $lang) {
7672 if ($ltag == $vbo_tn->default_lang) {
7673 continue;
7674 }
7675 if (array_key_exists($ltag, $tn) && count($tn[$ltag]) > 0) {
7676 foreach ($tn[$ltag] as $reference_id => $translation) {
7677 $lang_translation = array();
7678 foreach ($table_cols as $field => $fdetails) {
7679 if (!array_key_exists($field, $translation)) {
7680 continue;
7681 }
7682 $ftype = $fdetails['type'];
7683 if ($ftype == 'skip') {
7684 continue;
7685 }
7686
7687 if (is_array($translation[$field])) {
7688 foreach ($translation[$field] as $tn_field_k => $tn_field_v) {
7689 if (!is_string($tn_field_v)) {
7690 continue;
7691 }
7692 // replace any possible placeholder for special tags
7693 $translation[$field][$tn_field_k] = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
7694 return $match[2];
7695 }, $translation[$field][$tn_field_k]);
7696 }
7697 } elseif (!empty($translation[$field])) {
7698 // replace any possible placeholder for special tags
7699 $translation[$field] = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
7700 return $match[2];
7701 }, $translation[$field]);
7702 }
7703
7704 if ($ftype == 'json' && !is_scalar($translation[$field])) {
7705 $translation[$field] = json_encode($translation[$field]);
7706 }
7707 $lang_translation[$field] = $translation[$field];
7708 }
7709 if (count($lang_translation) > 0) {
7710 $q = "SELECT `id` FROM `#__vikbooking_translations` WHERE `table`=".$dbo->quote($table)." AND `lang`=".$dbo->quote($ltag)." AND `reference_id`=".$dbo->quote((int)$reference_id).";";
7711 $dbo->setQuery($q);
7712 $dbo->execute();
7713 if ($dbo->getNumRows() > 0) {
7714 $last_id = $dbo->loadResult();
7715 $q = "UPDATE `#__vikbooking_translations` SET `content`=".$dbo->quote(json_encode($lang_translation))." WHERE `id`=".(int)$last_id.";";
7716 } else {
7717 $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)).");";
7718 }
7719 $dbo->setQuery($q);
7720 $dbo->execute();
7721 $tn_saved++;
7722 }
7723 }
7724 }
7725 }
7726 if ($tn_saved > 0) {
7727 $mainframe->enqueueMessage(JText::translate('VBOTRANSLSAVEDOK'));
7728 }
7729 } else {
7730 VikError::raiseWarning('', JText::translate('VBTRANSLATIONERRINVTABLE'));
7731 }
7732 $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);
7733 }
7734
7735 public function choosebusy() {
7736 VikBookingHelper::printHeader("8");
7737
7738 VikRequest::setVar('view', VikRequest::getCmd('view', 'choosebusy'));
7739
7740 parent::display();
7741
7742 if (VikBooking::showFooter()) {
7743 VikBookingHelper::printFooter();
7744 }
7745 }
7746
7747 public function orders() {
7748 VikBookingHelper::printHeader("8");
7749
7750 VikRequest::setVar('view', VikRequest::getCmd('view', 'orders'));
7751
7752 parent::display();
7753
7754 if (VikBooking::showFooter()) {
7755 VikBookingHelper::printFooter();
7756 }
7757 }
7758
7759 public function vieworders() {
7760 //alias method of orders() for backward compatibility with VCM
7761 $this->orders();
7762 }
7763
7764 public function editorder() {
7765 VikBookingHelper::printHeader("8");
7766
7767 VikRequest::setVar('view', VikRequest::getCmd('view', 'editorder'));
7768
7769 parent::display();
7770
7771 if (VikBooking::showFooter()) {
7772 VikBookingHelper::printFooter();
7773 }
7774 }
7775
7776 public function removeorders()
7777 {
7778 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7779 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7780 }
7781
7782 $dbo = JFactory::getDbo();
7783 $app = JFactory::getApplication();
7784
7785 $ids = VikRequest::getVar('cid', array(0));
7786 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
7787
7788 $user = JFactory::getUser();
7789 $config = VBOFactory::getConfig();
7790
7791 $prev_conf_ids = [];
7792 $purged = false;
7793
7794 $tot_cancs = 0;
7795
7796 if (is_array($ids) && count($ids)) {
7797 foreach ($ids as $d) {
7798 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $dbo->quote($d);
7799 $dbo->setQuery($q, 0, 1);
7800 $row = $dbo->loadAssoc();
7801
7802 // check for any cancellation constraints
7803 $canc_denied = false;
7804 if ($row && class_exists('VCMFeesCancellation')) {
7805 // let VCM detect if there are any constraints for the cancellation
7806 $canc_denied = VCMFeesCancellation::getInstance($row, $anew = true)->isBookingConstrained();
7807 if ($canc_denied) {
7808 // set error message
7809 $canc_deny_error = VCMFeesCancellation::getInstance()->getError();
7810 if ($canc_deny_error) {
7811 $app->enqueueMessage($canc_deny_error, 'error');
7812 }
7813 }
7814 }
7815
7816 if ($row && !$canc_denied) {
7817 // increase counter
7818 $tot_cancs++;
7819
7820 // set status to cancelled
7821 if ($row['status'] != 'cancelled') {
7822 $q = "UPDATE `#__vikbooking_orders` SET `status`='cancelled' WHERE `id`=".(int)$row['id'].";";
7823 $dbo->setQuery($q);
7824 $dbo->execute();
7825 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($row['id']) . ";";
7826 $dbo->setQuery($q);
7827 $dbo->execute();
7828 if ($row['status'] == 'confirmed') {
7829 $prev_conf_ids[] = $row['id'];
7830 }
7831 // Booking History
7832 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('CB', "({$user->name})");
7833 }
7834
7835 /**
7836 * In case of pending bookings being cancelled, schedule the release through VCM.
7837 *
7838 * @since 1.18.8 (J) - 1.8.8 (WP)
7839 */
7840 if ($row['status'] == 'standby' && method_exists('VCMRequestAvailability', 'setForRelease')) {
7841 // let the CM schedule the release of the involved and unconfirmed booking IDs, if needed
7842 VCMRequestAvailability::getInstance()->setForRelease([$row['id']]);
7843 }
7844
7845 // free records up
7846 $q = "SELECT * FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
7847 $dbo->setQuery($q);
7848 $ordbusy = $dbo->loadAssocList();
7849 if ($ordbusy) {
7850 foreach ($ordbusy as $ob) {
7851 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`='".$ob['idbusy']."';";
7852 $dbo->setQuery($q);
7853 $dbo->execute();
7854 }
7855 }
7856
7857 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
7858 $dbo->setQuery($q);
7859 $dbo->execute();
7860
7861 // check for purge removal
7862 if ($row['status'] == 'cancelled') {
7863 $q = "DELETE FROM `#__vikbooking_customers_orders` WHERE `idorder`=" . intval($row['id']) . ";";
7864 $dbo->setQuery($q);
7865 $dbo->execute();
7866 $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `idorder`=".(int)$row['id'].";";
7867 $dbo->setQuery($q);
7868 $dbo->execute();
7869 $q = "DELETE FROM `#__vikbooking_orderhistory` WHERE `idorder`=".(int)$row['id'].";";
7870 $dbo->setQuery($q);
7871 $dbo->execute();
7872 $q = "DELETE FROM `#__vikbooking_orders` WHERE `id`=".(int)$row['id'].";";
7873 $dbo->setQuery($q);
7874 $dbo->execute();
7875 // in case of split stay booking, remove the transient
7876 if ($row['split_stay']) {
7877 $config->remove('split_stay_' . $row['id']);
7878 }
7879 // turn flag on
7880 $purged = true;
7881 }
7882 }
7883 }
7884
7885 if ($tot_cancs) {
7886 // enqueue system message
7887 $app->enqueueMessage(JText::translate('VBMESSDELBUSY'));
7888 }
7889 }
7890
7891 if ($prev_conf_ids) {
7892 $prev_conf_ids_str = '';
7893 foreach ($prev_conf_ids as $prev_id) {
7894 $prev_conf_ids_str .= '&cid[]='.$prev_id;
7895 }
7896 //Invoke Channel Manager
7897 $vcm_autosync = VikBooking::vcmAutoUpdate();
7898 if ($vcm_autosync > 0) {
7899 $vcm_obj = VikBooking::getVcmInvoker();
7900 $vcm_obj->setOids($prev_conf_ids)->setSyncType('cancel');
7901 $sync_result = $vcm_obj->doSync();
7902 if ($sync_result === false) {
7903 $vcm_err = $vcm_obj->getError();
7904 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
7905 }
7906 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
7907 $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');
7908 VikError::raiseNotice('', JText::translate('VBCHANNELMANAGERINVOKEASK').' <button type="button" class="btn btn-primary" onclick="document.location.href=\''.$vcm_sync_url.'\';">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button>');
7909 }
7910 //
7911 }
7912
7913 if (!empty($pgoto)) {
7914 if (is_numeric($pgoto) && is_array($ids) && count($ids) === 1) {
7915 if ($purged) {
7916 // go back to the bookings list page
7917 $app->redirect("index.php?option=com_vikbooking&task=orders");
7918 } else {
7919 // go back to the booking details page
7920 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . (int)$ids[0]);
7921 }
7922 exit;
7923 }
7924 // we expect the goto URL to be base64 encoded
7925 $app->redirect(base64_decode($pgoto));
7926 exit;
7927 }
7928
7929 // go back to the bookings list page
7930 $app->redirect("index.php?option=com_vikbooking&task=orders");
7931 }
7932
7933 public function config() {
7934 VikBookingHelper::printHeader("11");
7935
7936 VikRequest::setVar('view', VikRequest::getCmd('view', 'config'));
7937
7938 parent::display();
7939
7940 if (VikBooking::showFooter()) {
7941 VikBookingHelper::printFooter();
7942 }
7943 }
7944
7945 public function saveconfig()
7946 {
7947 if (!JSession::checkToken()) {
7948 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7949 }
7950
7951 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking') || !JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
7952 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7953 }
7954
7955 $dbo = JFactory::getDbo();
7956 $app = JFactory::getApplication();
7957
7958 $config = VBOFactory::getConfig();
7959
7960 $pallowbooking = VikRequest::getString('allowbooking', '', 'request');
7961 $pdisabledbookingmsg = VikRequest::getString('disabledbookingmsg', '', 'request', VIKREQUEST_ALLOWHTML);
7962 $ptimeopenstorefh = VikRequest::getString('timeopenstorefh', '', 'request');
7963 $ptimeopenstorefm = VikRequest::getString('timeopenstorefm', '', 'request');
7964 $ptimeopenstoreth = VikRequest::getString('timeopenstoreth', '', 'request');
7965 $ptimeopenstoretm = VikRequest::getString('timeopenstoretm', '', 'request');
7966 $phoursmorebookingback = VikRequest::getString('hoursmorebookingback', '', 'request');
7967 $pdateformat = VikRequest::getString('dateformat', '', 'request');
7968 $pdatesep = VikRequest::getString('datesep', '', 'request');
7969 $pdatesep = empty($pdatesep) ? "/" : $pdatesep;
7970 $presmodcanc = VikRequest::getInt('resmodcanc', 1, 'request');
7971 $presmodcancmin = VikRequest::getInt('resmodcancmin', 1, 'request');
7972 $pshowcategories = VikRequest::getString('showcategories', '', 'request');
7973 $pshowchildren = VikRequest::getString('showchildren', '', 'request');
7974 $psearchsuggestions = VikRequest::getInt('searchsuggestions', '', 'request');
7975 $ptokenform = VikRequest::getString('tokenform', '', 'request');
7976 $padminemail = VikRequest::getString('adminemail', '', 'request');
7977 $psenderemail = VikRequest::getString('senderemail', '', 'request');
7978 $pminuteslock = VikRequest::getString('minuteslock', '', 'request');
7979 $pminautoremove = VikRequest::getInt('minautoremove', '', 'request');
7980 $pfooterordmail = VikRequest::getString('footerordmail', '', 'request', VIKREQUEST_ALLOWHTML);
7981 $ptermsconds = VikRequest::getString('termsconds', '', 'request', VIKREQUEST_ALLOWHTML);
7982 $prequirelogin = VikRequest::getString('requirelogin', '', 'request');
7983 $pautoroomunit = VikRequest::getInt('autoroomunit', '', 'request');
7984 $ptodaybookings = VikRequest::getInt('todaybookings', '', 'request');
7985 $ptodaybookings = $ptodaybookings === 1 ? 1 : 0;
7986 $ploadbootstrap = VikRequest::getInt('loadbootstrap', '', 'request');
7987 $ploadbootstrap = $ploadbootstrap === 1 ? 1 : 0;
7988 $pusefa = VikRequest::getInt('usefa', '', 'request');
7989 $pusefa = $pusefa > 0 ? 1 : 0;
7990 $ploadjquery = VikRequest::getString('loadjquery', '', 'request');
7991 $ploadjquery = $ploadjquery == "yes" ? "1" : "0";
7992 $pcalendar = VikRequest::getString('calendar', '', 'request');
7993 $pcalendar = $pcalendar == "joomla" ? "joomla" : "jqueryui";
7994 $penablecoupons = VikRequest::getString('enablecoupons', '', 'request');
7995 $penablecoupons = $penablecoupons == "1" ? 1 : 0;
7996 $penablepin = VikRequest::getString('enablepin', '', 'request');
7997 $penablepin = $penablepin == "1" ? 1 : 0;
7998 $pmindaysadvance = VikRequest::getInt('mindaysadvance', '', 'request');
7999 $pmindaysadvance = $pmindaysadvance < 0 ? 0 : $pmindaysadvance;
8000 $pautodefcalnights = VikRequest::getInt('autodefcalnights', '', 'request');
8001 $pautodefcalnights = $pautodefcalnights >= 1 ? $pautodefcalnights : '1';
8002 $pnumrooms = VikRequest::getInt('numrooms', '', 'request');
8003 $pnumrooms = $pnumrooms > 0 ? $pnumrooms : '5';
8004 $pnumadultsfrom = VikRequest::getString('numadultsfrom', '', 'request');
8005 $pnumadultsfrom = intval($pnumadultsfrom) >= 0 ? $pnumadultsfrom : '1';
8006 $pnumadultsto = VikRequest::getString('numadultsto', '', 'request');
8007 $pnumadultsto = intval($pnumadultsto) > 0 ? $pnumadultsto : '10';
8008 if (intval($pnumadultsfrom) > intval($pnumadultsto)) {
8009 $pnumadultsfrom = '1';
8010 $pnumadultsto = '10';
8011 }
8012 $pnumchildrenfrom = VikRequest::getString('numchildrenfrom', '', 'request');
8013 $pnumchildrenfrom = intval($pnumchildrenfrom) >= 0 ? $pnumchildrenfrom : '1';
8014 $pnumchildrento = VikRequest::getString('numchildrento', '', 'request');
8015 $pnumchildrento = intval($pnumchildrento) > 0 ? $pnumchildrento : '4';
8016 if (intval($pnumchildrenfrom) > intval($pnumchildrento)) {
8017 $pnumadultsfrom = '1';
8018 $pnumadultsto = '4';
8019 }
8020 $confnumadults = $pnumadultsfrom.'-'.$pnumadultsto;
8021 $confnumchildren = $pnumchildrenfrom.'-'.$pnumchildrento;
8022 $pmaxdate = VikRequest::getString('maxdate', '', 'request');
8023 $pmaxdate = intval($pmaxdate) < 1 ? 2 : $pmaxdate;
8024 $pmaxdateinterval = VikRequest::getString('maxdateinterval', '', 'request');
8025 $pmaxdateinterval = !in_array($pmaxdateinterval, array('d', 'w', 'm', 'y')) ? 'y' : $pmaxdateinterval;
8026 $maxdate_str = '+'.$pmaxdate.$pmaxdateinterval;
8027 $pcronkey = VikRequest::getString('cronkey', '', 'request');
8028 $pcdsfrom = VikRequest::getVar('cdsfrom', array());
8029 $pcdsto = VikRequest::getVar('cdsto', array());
8030 $closing_dates = array();
8031 if (count($pcdsfrom)) {
8032 foreach ($pcdsfrom as $kcd => $vcdfrom) {
8033 if (!empty($vcdfrom) && array_key_exists($kcd, $pcdsto) && !empty($pcdsto[$kcd])) {
8034 $tscdfrom = VikBooking::getDateTimestamp($vcdfrom, '0', '0');
8035 $tscdto = VikBooking::getDateTimestamp($pcdsto[$kcd], '0', '0');
8036 if (!empty($tscdfrom) && !empty($tscdto) && $tscdto >= $tscdfrom) {
8037 $cdval = array('from' => $tscdfrom, 'to' => $tscdto);
8038 if (!in_array($cdval, $closing_dates)) {
8039 $closing_dates[] = $cdval;
8040 }
8041 }
8042 }
8043 }
8044 }
8045 $psmartsearch = VikRequest::getString('smartsearch', '', 'request');
8046 $psmartsearch = $psmartsearch == "dynamic" ? "dynamic" : "automatic";
8047 $pvbosef = VikRequest::getInt('vbosef', '', 'request');
8048 $vbosef = file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php');
8049 if ($pvbosef === 1) {
8050 if (!$vbosef) {
8051 rename(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'_router.php', VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php');
8052 }
8053 } else {
8054 if ($vbosef) {
8055 rename(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php', VBO_SITE_PATH.DIRECTORY_SEPARATOR.'_router.php');
8056 }
8057 }
8058 $pmultilang = VikRequest::getString('multilang', '', 'request');
8059 $pmultilang = $pmultilang == "1" ? 1 : 0;
8060 $pvcmautoupd = VikRequest::getInt('vcmautoupd', '', 'request');
8061 $pvcmautoupd = $pvcmautoupd > 0 ? 1 : 0;
8062 /**
8063 * Chat params and configuration settings
8064 *
8065 * @since 1.12
8066 */
8067 $pchatenabled = VikRequest::getInt('chatenabled', 0, 'request');
8068 if (is_file(VCM_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'lib.vikchannelmanager.php')) {
8069 $config->set('chatenabled', $pchatenabled);
8070
8071 // chat params
8072 $pchat_res_status = explode(';', VikRequest::getString('chat_res_status', '', 'request'));
8073 $chat_res_status = array();
8074 foreach ($pchat_res_status as $chatrs) {
8075 if (!empty($chatrs)) {
8076 array_push($chat_res_status, $chatrs);
8077 }
8078 }
8079 $chatparams = new stdClass;
8080 $chatparams->res_status = $chat_res_status;
8081 $chatparams->av_type = VikRequest::getString('chat_av_type', '', 'request');
8082 $chatparams->av_days = VikRequest::getInt('chat_av_days', 0, 'request');
8083
8084 $config->set('chatparams', $chatparams);
8085 }
8086
8087 /**
8088 * Pre check-in configuration settings
8089 *
8090 * @since 1.12
8091 */
8092 $pprecheckinenabled = VikRequest::getInt('precheckinenabled', 0, 'request');
8093 $pprecheckinenabled = $pprecheckinenabled > 0 ? 1 : 0;
8094
8095 $config->set('precheckinenabled', $pprecheckinenabled);
8096 // this may be a negative integer, it should not be unsigned
8097 $config->set('precheckinminoffset', VikRequest::getInt('precheckinminoffset', 0, 'request'));
8098
8099 $pupsellingenabled = VikRequest::getInt('upsellingenabled', 0, 'request');
8100 $pupsellingenabled = $pupsellingenabled > 0 ? 1 : 0;
8101 $config->set('upselling', $pupsellingenabled);
8102
8103 $porphanscal = VikRequest::getString('orphanscal', 'next', 'request');
8104 $porphanscal = $porphanscal == 'prevnext' ? 'prevnext' : 'next';
8105 $config->set('orphanscalculation', $porphanscal);
8106
8107 $psrcrtpl = VikRequest::getString('srcrtpl', 'compact', 'request');
8108 $config->set('searchrestmpl', $psrcrtpl);
8109
8110 /**
8111 * Guest Reviews settings
8112 *
8113 * @since 1.13
8114 */
8115 $pgrenabled = VikRequest::getInt('grenabled', 0, 'request');
8116 $pgrminchars = VikRequest::getInt('grminchars', 0, 'request');
8117 $pgrappr = VikRequest::getString('grappr', 'auto', 'request');
8118 $pgrappr = $pgrappr == 'auto' ? 'auto' : 'manual';
8119 $pgrtype = VikRequest::getString('grtype', 'service', 'request');
8120 $pgrtype = $pgrtype == 'service' ? 'service' : 'global';
8121 $pgrsrv = VikRequest::getVar('grsrv', array(), 'request', 'array');
8122 $config->set('grenabled', $pgrenabled);
8123 $config->set('grminchars', $pgrminchars);
8124 $config->set('grappr', $pgrappr);
8125 $config->set('grtype', $pgrtype);
8126 try {
8127 // always truncate service names (this query may require special permissions)
8128 $q = "TRUNCATE TABLE `#__vikbooking_greview_service`;";
8129 $dbo->setQuery($q);
8130 $dbo->execute();
8131 } catch (Exception $e) {
8132 // do nothing
8133 }
8134 foreach ($pgrsrv as $srvname) {
8135 $q = "INSERT INTO `#__vikbooking_greview_service` (`service_name`) VALUES (" . $dbo->quote($srvname) . ");";
8136 $dbo->setQuery($q);
8137 $dbo->execute();
8138 }
8139
8140 /**
8141 * Preferred countries ordering, or custom countries.
8142 *
8143 * @since 1.14 (J) - 1.3.11 (WP)
8144 * @since 1.14.1 (J) - 1.4.1 (WP) we also support "cust_pref_countries"
8145 */
8146 $pref_countries = VikRequest::getVar('pref_countries', array());
8147 $cust_pref_countries = VikRequest::getString('cust_pref_countries', '', 'request');
8148 $pref_countries = !is_array($pref_countries) || empty($pref_countries[0]) ? VikBooking::preferredCountriesOrdering() : $pref_countries;
8149 if (!empty($cust_pref_countries)) {
8150 $all_custom_prefcountries = array();
8151 $cust_pref_countries = explode(',', $cust_pref_countries);
8152 foreach ($cust_pref_countries as $cust_pref_country) {
8153 $cust_pref_country = trim(strtolower($cust_pref_country));
8154 if (empty($cust_pref_country) || strlen($cust_pref_country) != 2) {
8155 continue;
8156 }
8157 array_push($all_custom_prefcountries, $cust_pref_country);
8158 }
8159 if (count($all_custom_prefcountries)) {
8160 $pref_countries = $all_custom_prefcountries;
8161 }
8162 }
8163 $config->set('preferred_countries', $pref_countries);
8164 //
8165
8166 $gmapskey = VikRequest::getString('gmapskey', '', 'request');
8167 $config->set('gmapskey', $gmapskey);
8168
8169 $pref_textcolor = VikRequest::getString('pref_textcolor', '', 'request');
8170 $pref_bgcolor = VikRequest::getString('pref_bgcolor', '', 'request');
8171 $pref_fontcolor = VikRequest::getString('pref_fontcolor', '', 'request');
8172 $pref_bgcolorhov = VikRequest::getString('pref_bgcolorhov', '', 'request');
8173 $pref_fontcolorhov = VikRequest::getString('pref_fontcolorhov', '', 'request');
8174 $pref_colors = array(
8175 'textcolor' => $pref_textcolor,
8176 'bgcolor' => $pref_bgcolor,
8177 'fontcolor' => $pref_fontcolor,
8178 'bgcolorhov' => $pref_bgcolorhov,
8179 'fontcolorhov' => $pref_fontcolorhov,
8180 );
8181 $config->set('pref_colors', $pref_colors);
8182
8183 $interactive_map = VikRequest::getInt('interactive_map', 0, 'request');
8184 $config->set('interactive_map', $interactive_map);
8185 $config->set('search_filters', VikRequest::getInt('search_filters', 0, 'request'));
8186
8187 $noemptydecimals = VikRequest::getInt('noemptydecimals', 0, 'request');
8188 $config->set('noemptydecimals', $noemptydecimals);
8189
8190 /**
8191 * Appearance preferences (light, auto, dark mode).
8192 *
8193 * @since 1.15.0 (J) - 1.5.0 (WP)
8194 * @since 1.16.10 (J) - 1.6.10 (WP) mirrored on VCM.
8195 */
8196 $appearance_pref = VikRequest::getString('appearance_pref', '');
8197 $config->set('appearance_pref', $appearance_pref);
8198 if (class_exists('VCMFactory')) {
8199 VCMFactory::getConfig()->set('appearance_pref', $appearance_pref);
8200 }
8201
8202 $res_backend_path = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR;
8203 $picon = "";
8204 if (intval($_FILES['sitelogo']['error']) == 0 && trim($_FILES['sitelogo']['name'])!="") {
8205 jimport('joomla.filesystem.file');
8206 if (@is_uploaded_file($_FILES['sitelogo']['tmp_name'])) {
8207 $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['sitelogo']['name'])));
8208 if (file_exists($res_backend_path.$safename)) {
8209 $j = 1;
8210 while (file_exists($res_backend_path.$j.$safename)) {
8211 $j++;
8212 }
8213 $pwhere = $res_backend_path.$j.$safename;
8214 } else {
8215 $j = "";
8216 $pwhere = $res_backend_path.$safename;
8217 }
8218 if (!getimagesize($_FILES['sitelogo']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
8219 @unlink($pwhere);
8220 $picon = "";
8221 } else {
8222 VikBooking::uploadFile($_FILES['sitelogo']['tmp_name'], $pwhere);
8223 @chmod($pwhere, 0644);
8224 $picon = $j.$safename;
8225 }
8226 }
8227 if (!empty($picon)) {
8228 $config->set('sitelogo', $picon);
8229 }
8230 }
8231 $pbackicon = "";
8232 if (intval($_FILES['backlogo']['error']) == 0 && trim($_FILES['backlogo']['name'])!="") {
8233 jimport('joomla.filesystem.file');
8234 if (@is_uploaded_file($_FILES['backlogo']['tmp_name'])) {
8235 $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['backlogo']['name'])));
8236 if (file_exists($res_backend_path.$safename)) {
8237 $j = 1;
8238 while (file_exists($res_backend_path.$j.$safename)) {
8239 $j++;
8240 }
8241 $pwhere = $res_backend_path.$j.$safename;
8242 } else {
8243 $j = "";
8244 $pwhere = $res_backend_path.$safename;
8245 }
8246 if (!getimagesize($_FILES['backlogo']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
8247 @unlink($pwhere);
8248 $pbackicon = "";
8249 } else {
8250 VikBooking::uploadFile($_FILES['backlogo']['tmp_name'], $pwhere);
8251 @chmod($pwhere, 0644);
8252 $pbackicon = $j.$safename;
8253 }
8254 }
8255 if (!empty($pbackicon)) {
8256 $config->set('backlogo', $pbackicon);
8257 }
8258 }
8259 $config->set('vcmautoupd', $pvcmautoupd);
8260 $config->set('allowbooking', empty($pallowbooking) || $pallowbooking != "1" ? 0 : 1);
8261 $config->set('showcategories', empty($pshowcategories) || $pshowcategories != "yes" ? 0 : 1);
8262 $config->set('showchildren', empty($pshowchildren) || $pshowchildren != "yes" ? 0 : 1);
8263 $config->set('searchsuggestions', $psearchsuggestions);
8264 $config->set('tokenform', empty($ptokenform) || $ptokenform != "yes" ? 0 : 1);
8265 $config->set('guests_label', $app->input->getString('guests_label', 'adults'));
8266 $config->set('search_show_busy_listings', $app->input->getInt('search_show_busy_listings', 0));
8267 $config->set('search_link_roomdetails', $app->input->getInt('search_link_roomdetails', 0));
8268
8269 // translatable text
8270 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pfooterordmail)." WHERE `param`='footerordmail';";
8271 $dbo->setQuery($q);
8272 $dbo->execute();
8273
8274 // translatable text
8275 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pdisabledbookingmsg)." WHERE `param`='disabledbookingmsg';";
8276 $dbo->setQuery($q);
8277 $dbo->execute();
8278
8279 // translatable text
8280 $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($app->input->getString('guests_allowed_policy', '', 'raw')) . " WHERE `param`='guests_allowed_policy';";
8281 $dbo->setQuery($q);
8282 $dbo->execute();
8283
8284 // terms and conditions
8285 $q = "SELECT `id`,`setting` FROM `#__vikbooking_texts` WHERE `param`='termsconds';";
8286 $dbo->setQuery($q);
8287 $dbo->execute();
8288 if ($dbo->getNumRows() > 0) {
8289 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($ptermsconds)." WHERE `param`='termsconds';";
8290 $dbo->setQuery($q);
8291 $dbo->execute();
8292 } else {
8293 $q = "INSERT INTO `#__vikbooking_texts` (`param`,`exp`,`setting`) VALUES ('termsconds','Terms and Conditions',".$dbo->quote($ptermsconds).");";
8294 $dbo->setQuery($q);
8295 $dbo->execute();
8296 }
8297
8298 $config->set('adminemail', $padminemail);
8299 $config->set('senderemail', $psenderemail);
8300 $config->set('dateformat', empty($pdateformat) ? "%d/%m/%Y" : $pdateformat);
8301 $config->set('datesep', $pdatesep);
8302 $config->set('resmodcanc', $presmodcanc);
8303 $config->set('resmodcancmin', $presmodcancmin);
8304 $config->set('minuteslock', $pminuteslock);
8305 $config->set('minautoremove', $pminautoremove);
8306
8307 $openingh = $ptimeopenstorefh * 3600;
8308 $openingm = $ptimeopenstorefm * 60;
8309 $openingts = $openingh + $openingm;
8310 $closingh = $ptimeopenstoreth * 3600;
8311 $closingm = $ptimeopenstoretm * 60;
8312 $closingts = $closingh + $closingm;
8313 // check if the check-in/out times have changed and if there are future bookings with the old time to prevent availability errors
8314 $prevtimes = $config->get('timeopenstore', '');
8315 if ($prevtimes != $openingts . "-" . $closingts) {
8316 $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `checkout`>".time().";";
8317 $dbo->setQuery($q);
8318 $dbo->execute();
8319 if ($dbo->getNumRows() > 0) {
8320 VikError::raiseWarning('', JText::translate('VBOCONFIGWARNDIFFCHECKINOUT'));
8321 /**
8322 * VBO 1.10 Patch - we concatenate a button to unify the check-in/out times
8323 * for all reservations to avoid issues with the availability.
8324 *
8325 * @since August 29th 2018
8326 */
8327 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>');
8328 //
8329 }
8330 }
8331 $config->set('timeopenstore', $openingts . "-" . $closingts);
8332
8333 // set the hours of extended gratuity period to the difference between checkin and checkout if checkout is later
8334 $phoursmorebookingback = "0";
8335 if ($closingts > $openingts) {
8336 $diffcheck = ($closingts - $openingts) / 3600;
8337 $phoursmorebookingback = ceil($diffcheck);
8338 }
8339 $config->set('hoursmorebookingback', $phoursmorebookingback);
8340 $config->set('hoursmoreroomavail', '0');
8341 $config->set('multilang', $pmultilang);
8342 $config->set('requirelogin', $prequirelogin == "1" ? 1 : 0);
8343 $config->set('autoroomunit', $pautoroomunit ? 1 : 0);
8344 $config->set('todaybookings', $ptodaybookings);
8345 $config->set('bootstrap', $ploadbootstrap);
8346 $config->set('usefa', $pusefa);
8347 $config->set('loadjquery', $ploadjquery);
8348 $config->set('calendar', $pcalendar ?: 'jqueryui');
8349 $config->set('dboptimizetime', $app->input->getString('dboptimizetime', ''));
8350 $config->set('enablecoupons', $penablecoupons);
8351 $config->set('enablepin', $penablepin);
8352 $config->set('mindaysadvance', $pmindaysadvance);
8353 $config->set('autodefcalnights', $pautodefcalnights);
8354 $config->set('numrooms', $pnumrooms);
8355 $config->set('numadults', $confnumadults);
8356 $config->set('numchildren', $confnumchildren);
8357 $config->set('closingdates', $closing_dates);
8358 $config->set('smartsearch', $psmartsearch);
8359 $config->set('maxdate', $maxdate_str);
8360 $config->set('cronkey', $pcronkey);
8361
8362 $pfronttitle = VikRequest::getString('fronttitle', '', 'request');
8363 $pfronttitletag = VikRequest::getString('fronttitletag', '', 'request');
8364 $pfronttitletagclass = VikRequest::getString('fronttitletagclass', '', 'request');
8365 $pshowfooter = VikRequest::getString('showfooter', '', 'request');
8366 $pintromain = VikRequest::getString('intromain', '', 'request', VIKREQUEST_ALLOWHTML);
8367 $pclosingmain = VikRequest::getString('closingmain', '', 'request', VIKREQUEST_ALLOWHTML);
8368 $pcurrencyname = VikRequest::getString('currencyname', '', 'request', VIKREQUEST_ALLOWHTML);
8369 $pcurrencysymb = VikRequest::getString('currencysymb', '', 'request', VIKREQUEST_ALLOWHTML);
8370 $pcurrencycodepp = VikRequest::getString('currencycodepp', '', 'request');
8371 $pnumdecimals = VikRequest::getString('numdecimals', '', 'request');
8372 $pnumdecimals = intval($pnumdecimals);
8373 $pdecseparator = VikRequest::getString('decseparator', '', 'request');
8374 $pdecseparator = empty($pdecseparator) ? '.' : $pdecseparator;
8375 $pthoseparator = VikRequest::getString('thoseparator', '', 'request');
8376 $numberformatstr = $pnumdecimals.':'.$pdecseparator.':'.$pthoseparator;
8377 $pshowpartlyreserved = VikRequest::getString('showpartlyreserved', '', 'request');
8378 $pshowpartlyreserved = $pshowpartlyreserved == "yes" ? 1 : 0;
8379 $pshowcheckinoutonly = VikRequest::getInt('showcheckinoutonly', '', 'request');
8380 $pshowcheckinoutonly = $pshowcheckinoutonly > 0 ? 1 : 0;
8381 $pnumcalendars = VikRequest::getInt('numcalendars', '', 'request');
8382 $pnumcalendars = $pnumcalendars > -1 ? $pnumcalendars : 3;
8383 $pthumbsize = VikRequest::getInt('thumbsize', 0, 'request');
8384 $pfirstwday = VikRequest::getString('firstwday', '', 'request');
8385 $pfirstwday = intval($pfirstwday) >= 0 && intval($pfirstwday) <= 6 ? $pfirstwday : '0';
8386 $pbctagname = VikRequest::getVar('bctagname', array());
8387 $pbctagcolor = VikRequest::getVar('bctagcolor', array());
8388 $pbctagrule = VikRequest::getVar('bctagrule', array());
8389 $bctags_arr = array();
8390 $bctags_rules = array();
8391 if (count($pbctagname) > 0) {
8392 foreach ($pbctagname as $bctk => $bctv) {
8393 if (!empty($bctv) && !empty($pbctagcolor[$bctk]) && strlen($pbctagrule[$bctk]) > 0) {
8394 if (intval($pbctagrule[$bctk]) == 0 || !in_array($pbctagrule[$bctk], $bctags_rules)) {
8395 $bctags_rules[] = $pbctagrule[$bctk];
8396 $bctags_arr[] = array('color' => $pbctagcolor[$bctk], 'name' => $bctv, 'rule' => $pbctagrule[$bctk]);
8397 }
8398 }
8399 }
8400 }
8401 //theme
8402 $ptheme = VikRequest::getString('theme', '', 'request');
8403 if (empty($ptheme) || $ptheme == 'default') {
8404 $ptheme = 'default';
8405 } else {
8406 $validtheme = false;
8407 $themes = glob(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'themes'.DIRECTORY_SEPARATOR.'*');
8408 if (count($themes) > 0) {
8409 $strip = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'themes'.DIRECTORY_SEPARATOR;
8410 foreach ($themes as $th) {
8411 if (is_dir($th)) {
8412 $tname = str_replace($strip, '', $th);
8413 if ($tname == $ptheme) {
8414 $validtheme = true;
8415 break;
8416 }
8417 }
8418 }
8419 }
8420 if ($validtheme == false) {
8421 $ptheme = 'default';
8422 }
8423 }
8424 $config->set('theme', $ptheme);
8425 //
8426 $config->set('showpartlyreserved', $pshowpartlyreserved);
8427 $config->set('showcheckinoutonly', $pshowcheckinoutonly);
8428 $config->set('numcalendars', $pnumcalendars);
8429
8430 // record may not be set
8431 $config->set('thumbsize', $pthumbsize);
8432
8433 $config->set('firstwday', $pfirstwday);
8434
8435 // translatable text
8436 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pfronttitle)." WHERE `param`='fronttitle';";
8437 $dbo->setQuery($q);
8438 $dbo->execute();
8439
8440 $config->set('fronttitletag', $pfronttitletag);
8441 $config->set('fronttitletagclass', $pfronttitletagclass);
8442 $config->set('showfooter', empty($pshowfooter) || $pshowfooter != "yes" ? 0 : 1);
8443
8444 // translatable texts
8445 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pintromain)." WHERE `param`='intromain';";
8446 $dbo->setQuery($q);
8447 $dbo->execute();
8448 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pclosingmain)." WHERE `param`='closingmain';";
8449 $dbo->setQuery($q);
8450 $dbo->execute();
8451
8452 $config->set('currencyname', $pcurrencyname);
8453 $config->set('currencysymb', $pcurrencysymb);
8454 $config->set('currencypos', $app->input->getAlnum('currencypos', 'before'));
8455 $config->set('currencycodepp', $pcurrencycodepp);
8456 $config->set('numberformat', $numberformatstr);
8457 // bookings color tags
8458 $config->set('bookingsctags', $bctags_arr);
8459
8460 $pivainclusa = VikRequest::getString('ivainclusa', '', 'request');
8461 $ptaxsummary = VikRequest::getString('taxsummary', '', 'request');
8462 $ptaxsummary = empty($ptaxsummary) || $ptaxsummary != "yes" ? "0" : "1";
8463 $pccpaypal = VikRequest::getString('ccpaypal', '', 'request');
8464 $ppaytotal = VikRequest::getString('paytotal', '', 'request');
8465 $ppayaccpercent = VikRequest::getString('payaccpercent', '', 'request');
8466 $ptypedeposit = VikRequest::getString('typedeposit', '', 'request');
8467 $ptypedeposit = $ptypedeposit == 'fixed' ? 'fixed' : 'pcent';
8468 $pdepoverrides = VikRequest::getString('depoverrides', '', 'request');
8469 $ppaymentname = VikRequest::getString('paymentname', '', 'request');
8470 $pdisclaimer = VikRequest::getString('disclaimer', '', 'request', VIKREQUEST_ALLOWHTML);
8471 $pmultipay = VikRequest::getString('multipay', '', 'request');
8472 $pmultipay = $pmultipay == "yes" ? 1 : 0;
8473 $pdepifdaysadv = VikRequest::getInt('depifdaysadv', '', 'request');
8474 $pnodepnonrefund = VikRequest::getInt('nodepnonrefund', '', 'request');
8475 $pdepcustchoice = VikRequest::getString('depcustchoice', '', 'request');
8476 $pdepcustchoice = $pdepcustchoice == "yes" ? 1 : 0;
8477
8478 // translatable text
8479 $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($ppaymentname) . " WHERE `param`='paymentname';";
8480 $dbo->setQuery($q);
8481 $dbo->execute();
8482 $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($pdisclaimer) . " WHERE `param`='disclaimer';";
8483 $dbo->setQuery($q);
8484 $dbo->execute();
8485
8486 $config->set('ivainclusa', empty($pivainclusa) || $pivainclusa != "yes" ? 0 : 1);
8487 $config->set('taxsummary', $ptaxsummary);
8488 $config->set('paytotal', empty($ppaytotal) || $ppaytotal != "yes" ? 0 : 1);
8489
8490 $config->set('ccpaypal', $pccpaypal);
8491 $config->set('payaccpercent', $ppayaccpercent);
8492 $config->set('typedeposit', $ptypedeposit);
8493 $config->set('depoverrides', $pdepoverrides);
8494 $config->set('multipay', $pmultipay);
8495 $config->set('depifdaysadv', $pdepifdaysadv);
8496 $config->set('nodepnonrefund', $pnodepnonrefund);
8497 $config->set('depcustchoice', $pdepcustchoice);
8498 $config->set('depbalancedays', $app->input->getInt('depbalancedays', null));
8499
8500 $psendemailwhen = VikRequest::getInt('sendemailwhen', '', 'request');
8501 $psendemailwhen = $psendemailwhen > 1 ? 2 : 1;
8502 $pattachical = VikRequest::getInt('attachical', 0, 'request');
8503 $pattachical = $pattachical >= 0 && $pattachical <= 3 ? $pattachical : 1;
8504 $config->set('emailsendwhen', $psendemailwhen);
8505 $config->set('attachical', $pattachical);
8506
8507 // SMS APIs
8508 $psmsapi = VikRequest::getString('smsapi', '', 'request');
8509 $psmsautosend = VikRequest::getString('smsautosend', '', 'request');
8510 $psmsautosend = intval($psmsautosend) > 0 ? 1 : 0;
8511 $psmssendto = VikRequest::getVar('smssendto', array());
8512 $sms_sendto = array();
8513 foreach ($psmssendto as $sto) {
8514 if (in_array($sto, array('admin', 'customer'))) {
8515 $sms_sendto[] = $sto;
8516 }
8517 }
8518 $psmssendwhen = VikRequest::getInt('smssendwhen', '', 'request');
8519 $psmssendwhen = $psmssendwhen > 1 ? 2 : 1;
8520 $psmsadminphone = VikRequest::getString('smsadminphone', '', 'request');
8521 $psmsadmintpl = VikRequest::getString('smsadmintpl', '', 'request', VIKREQUEST_ALLOWRAW);
8522 $psmscustomertpl = VikRequest::getString('smscustomertpl', '', 'request', VIKREQUEST_ALLOWRAW);
8523 $psmsadmintplpend = VikRequest::getString('smsadmintplpend', '', 'request', VIKREQUEST_ALLOWRAW);
8524 $psmscustomertplpend = VikRequest::getString('smscustomertplpend', '', 'request', VIKREQUEST_ALLOWRAW);
8525 $psmsadmintplcanc = VikRequest::getString('smsadmintplcanc', '', 'request', VIKREQUEST_ALLOWRAW);
8526 $psmscustomertplcanc = VikRequest::getString('smscustomertplcanc', '', 'request', VIKREQUEST_ALLOWRAW);
8527 $viksmsparams = VikRequest::getVar('viksmsparams', array());
8528 $smsparamarr = array();
8529 if (count($viksmsparams) > 0) {
8530 foreach ($viksmsparams as $setting => $cont) {
8531 if (strlen($setting) > 0) {
8532 $smsparamarr[$setting] = $cont;
8533 }
8534 }
8535 }
8536 $config->set('smsapi', $psmsapi);
8537 $config->set('smsautosend', $psmsautosend);
8538 $config->set('smssendto', $sms_sendto);
8539 $config->set('smssendwhen', $psmssendwhen);
8540 $config->set('smsadminphone', $psmsadminphone);
8541 $config->set('smsparams', $smsparamarr);
8542
8543 // translatable texts
8544 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintpl)." WHERE `param`='smsadmintpl';";
8545 $dbo->setQuery($q);
8546 $dbo->execute();
8547 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertpl)." WHERE `param`='smscustomertpl';";
8548 $dbo->setQuery($q);
8549 $dbo->execute();
8550 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintplpend)." WHERE `param`='smsadmintplpend';";
8551 $dbo->setQuery($q);
8552 $dbo->execute();
8553 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertplpend)." WHERE `param`='smscustomertplpend';";
8554 $dbo->setQuery($q);
8555 $dbo->execute();
8556 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintplcanc)." WHERE `param`='smsadmintplcanc';";
8557 $dbo->setQuery($q);
8558 $dbo->execute();
8559 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertplcanc)." WHERE `param`='smscustomertplcanc';";
8560 $dbo->setQuery($q);
8561 $dbo->execute();
8562
8563 /**
8564 * Backup settings
8565 *
8566 * @since 1.15.0 (J) - 1.5.0 (WP)
8567 */
8568 $backup_type = $app->input->getString('backuptype', 'full');
8569 $backup_folder = $app->input->getString('backupfolder', '');
8570
8571 $tmp = $app->get('tmp_path');
8572
8573 if (!$backup_folder)
8574 {
8575 // path not specified, use temporary folder
8576 $backup_folder = $tmp;
8577 }
8578
8579 $current = $config->get('backupfolder');
8580
8581 if (!$current)
8582 {
8583 // path was missing, use temporary folder
8584 $current = $tmp;
8585 }
8586
8587 // check whether the backup folder has been moved
8588 if ($current && $backup_folder && rtrim($current, DIRECTORY_SEPARATOR) !== rtrim($backup_folder, DIRECTORY_SEPARATOR))
8589 {
8590 $backupModel = new VBOModelBackup();
8591
8592 // backup folder moved, try to copy all the existing overrides
8593 if (!$backupModel->moveArchives($backup_folder))
8594 {
8595 // iterate all errors and display them
8596 foreach ($backupModel->getErrors() as $error)
8597 {
8598 $app->enqueueMessage($error, 'warning');
8599 }
8600 }
8601 }
8602
8603 // save configuration
8604 $config->set('backuptype', $backup_type);
8605 $config->set('backupfolder', $backup_folder);
8606
8607 /**
8608 * Check-in data collection type.
8609 *
8610 * @since 1.15.0 (J) - 1.5.0 (WP)
8611 */
8612 $config->set('checkindata', VikRequest::getString('checkindata', 'basic', 'request'));
8613
8614 /**
8615 * Front-end appearance.
8616 *
8617 * @since 1.15.0 (J) - 1.5.0 (WP) (patch)
8618 */
8619 $config->set('appearance_front', VikRequest::getInt('appearance_front', 0, 'request'));
8620
8621 /**
8622 * Split stays.
8623 *
8624 * @since 1.16.0 (J) - 1.6.0 (WP)
8625 */
8626 $glob_split_stay = VikRequest::getInt('split_stay', 0, 'request');
8627 $split_stay_ratio = VikRequest::getFloat('split_stay_ratio', 0, 'request');
8628 $split_stay_ratio = $split_stay_ratio > 100 ? 100 : $split_stay_ratio;
8629 $config->set('split_stay_ratio', ($glob_split_stay && $split_stay_ratio > 0 ? $split_stay_ratio : 0));
8630
8631 /**
8632 * Re-build Web App manifest file to let the event trigger.
8633 *
8634 * @since 1.16.5 (J) - 1.6.5 (WP)
8635 */
8636 try {
8637 VBOWebappManifest::build();
8638 } catch (Exception $e) {
8639 // do nothing
8640 }
8641
8642 // redirect
8643 $app->enqueueMessage(JText::translate('VBSETTINGSAVED'));
8644 $app->redirect('index.php?option=com_vikbooking&task=config');
8645 $app->close();
8646 }
8647
8648 /**
8649 * Task to unify the check-in and check-out times for all reservations.
8650 */
8651 public function unifycheckinout()
8652 {
8653 $dbo = JFactory::getDbo();
8654 $app = JFactory::getApplication();
8655 $user = JFactory::getUser();
8656
8657 $fh = VikRequest::getInt('fh', 12, 'request');
8658 $fm = VikRequest::getInt('fm', 0, 'request');
8659 $th = VikRequest::getInt('th', 10, 'request');
8660 $tm = VikRequest::getInt('tm', 0, 'request');
8661
8662 $now = time();
8663 $totmod = 0;
8664 $totbookmod = 0;
8665
8666 // query all busy records
8667 $q = $dbo->getQuery(true)
8668 ->select('*')
8669 ->from($dbo->qn('#__vikbooking_busy'));
8670
8671 $dbo->setQuery($q);
8672 $records = $dbo->loadAssocList();
8673
8674 foreach ($records as $v) {
8675 $info_start = getdate($v['checkin']);
8676 $info_end = getdate($v['checkout']);
8677 $new_start = mktime($fh, $fm, 0, $info_start['mon'], $info_start['mday'], $info_start['year']);
8678 $new_end = mktime($th, $tm, 0, $info_end['mon'], $info_end['mday'], $info_end['year']);
8679
8680 $q = $dbo->getQuery(true)
8681 ->update($dbo->qn('#__vikbooking_busy'))
8682 ->set($dbo->qn('checkin') . ' = ' . $new_start)
8683 ->set($dbo->qn('checkout') . ' = ' . $new_end)
8684 ->set($dbo->qn('realback') . ' = ' . $new_end)
8685 ->where($dbo->qn('id') . ' = ' . (int)$v['id']);
8686
8687 $dbo->setQuery($q, 0, 1);
8688 $dbo->execute();
8689
8690 $totmod++;
8691 }
8692
8693 // query all bookings
8694 $q = $dbo->getQuery(true)
8695 ->select($dbo->qn([
8696 'id',
8697 'days',
8698 'checkin',
8699 'checkout',
8700 'total',
8701 ]))
8702 ->from($dbo->qn('#__vikbooking_orders'))
8703 ->order($dbo->qn('checkin') . ' DESC');
8704
8705 $dbo->setQuery($q);
8706 $records = $dbo->loadAssocList();
8707
8708 foreach ($records as $v) {
8709 $info_start = getdate($v['checkin']);
8710 $info_end = getdate($v['checkout']);
8711 $new_start = mktime($fh, $fm, 0, $info_start['mon'], $info_start['mday'], $info_start['year']);
8712 $new_end = mktime($th, $tm, 0, $info_end['mon'], $info_end['mday'], $info_end['year']);
8713
8714 $q = $dbo->getQuery(true)
8715 ->update($dbo->qn('#__vikbooking_orders'))
8716 ->set($dbo->qn('checkin') . ' = ' . $new_start)
8717 ->set($dbo->qn('checkout') . ' = ' . $new_end)
8718 ->where($dbo->qn('id') . ' = ' . (int)$v['id']);
8719
8720 $dbo->setQuery($q, 0, 1);
8721 $dbo->execute();
8722
8723 /**
8724 * In case the operation changed the check-in/check-out time for this booking,
8725 * store a new history record for a booking modification.
8726 *
8727 * @since 1.16.6 (J) - 1.6.6 (WP)
8728 */
8729 if ($v['checkout'] > $now && ($info_start['hours'] != $fh || $info_end['hours'] != $th)) {
8730 // Booking History
8731 VikBooking::getBookingHistoryInstance($v['id'])->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($v));
8732 }
8733
8734 $totbookmod++;
8735 }
8736
8737 $app->enqueueMessage('OK: ' . $totbookmod);
8738 $app->redirect("index.php?option=com_vikbooking&task=config");
8739 $app->close();
8740 }
8741
8742 public function savetmplfile()
8743 {
8744 $app = JFactory::getApplication();
8745
8746 if (!JSession::checkToken()) {
8747 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
8748 }
8749
8750 if (!JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
8751 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8752 }
8753
8754 $fpath = VikRequest::getString('path', '', 'request', VIKREQUEST_ALLOWRAW);
8755 $pcont = VikRequest::getString('cont', '', 'request', VIKREQUEST_ALLOWRAW);
8756 $pajax = VikRequest::getInt('ajax', 0, 'request');
8757
8758 // default status
8759 $result = [
8760 'status' => 0,
8761 'message' => 'Generic error',
8762 ];
8763
8764 $exists = file_exists($fpath) ? true : false;
8765 if (!$exists) {
8766 $fpath = urldecode($fpath);
8767 }
8768 $fpath = file_exists($fpath) ? $fpath : '';
8769 if (!empty($fpath)) {
8770 $fp = fopen($fpath, 'wb');
8771 $byt = (int) fwrite($fp, $pcont);
8772 fclose($fp);
8773 if ($byt > 0) {
8774 // success
8775 $result = [
8776 'status' => 1,
8777 'message' => JText::translate('VBOUPDTMPLFILEOK'),
8778 ];
8779
8780 if (VBOPlatformDetection::isWordPress()) {
8781 /**
8782 * @wponly call the UpdateManager Class to temporarily store modifications made to template files
8783 */
8784 VikBookingUpdateManager::storeTemplateContent($fpath, $pcont);
8785 }
8786 } else {
8787 // error
8788 $result = [
8789 'status' => 0,
8790 'message' => JText::translate('VBOUPDTMPLFILENOBYTES'),
8791 ];
8792 }
8793 } else {
8794 // error
8795 $result = [
8796 'status' => 0,
8797 'message' => JText::translate('VBOUPDTMPLFILEERR'),
8798 ];
8799 }
8800
8801 if ($pajax) {
8802 if ($result['status']) {
8803 VBOHttpDocument::getInstance($app)->json($result);
8804 } else {
8805 VBOHttpDocument::getInstance($app)->close(500, $result['message']);
8806 }
8807 } else {
8808 if ($result['status']) {
8809 $app->enqueueMessage($result['message']);
8810 } else {
8811 VikError::raiseWarning('', $result['message']);
8812 }
8813 }
8814
8815 $app->redirect("index.php?option=com_vikbooking&task=edittmplfile&path=".$fpath."&tmpl=component");
8816 $app->close();
8817 }
8818
8819 public function edittmplfile()
8820 {
8821 // this view should be rendered through AJAX
8822 VikRequest::setVar('view', VikRequest::getCmd('view', 'edittmplfile'));
8823
8824 if (JFactory::getApplication()->input->getBool('ajax') && VBOPlatformDetection::isJoomla()) {
8825 /**
8826 * @todo This needs to be changed for Joomla in the future versions.
8827 * Right now no HTML document tree is being added as an AJAX response, because
8828 * the View output is captured within a buffer, but the CodeMirror will not work.
8829 * In case the View was rendered normally and sent to output, the CodeMirror would
8830 * work fine, but the response appended to the modal body would contain HTML head tags
8831 * and so accessing the language definitions through JS would fail after the first response.
8832 * The solution for both Joomla and WordPress is probably to use a completely different endpoint
8833 * that returns just the file buffer/content, and maybe the file type, so that who makes the requests
8834 * can set the content and render the proper CodeMirror editor manually at runtime.
8835 */
8836
8837 // start output buffer
8838 ob_start();
8839
8840 try {
8841 // display view
8842 parent::display();
8843 } catch (Exception $e) {
8844 // clear output buffer
8845 ob_end_clean();
8846
8847 // raise error
8848 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
8849 }
8850
8851 // obtain view HTML from buffer
8852 $html = ob_get_contents();
8853
8854 // clear output buffer
8855 ob_end_clean();
8856
8857 // encode HTML in JSON to avoid encoding issues
8858 VBOHttpDocument::getInstance()->json(json_encode($html));
8859
8860 } else {
8861 // regular view display
8862 parent::display();
8863 }
8864 }
8865
8866 public function tmplfileprew() {
8867 //modal box, so we do not set menu or footer
8868
8869 VikRequest::setVar('view', VikRequest::getCmd('view', 'tmplfileprew'));
8870
8871 parent::display();
8872 }
8873
8874 public function invoices() {
8875 VikBookingHelper::printHeader("invoices");
8876
8877 VikRequest::setVar('view', VikRequest::getCmd('view', 'invoices'));
8878
8879 parent::display();
8880
8881 if (VikBooking::showFooter()) {
8882 VikBookingHelper::printFooter();
8883 }
8884 }
8885
8886 public function newmaninvoice() {
8887 VikBookingHelper::printHeader("invoices");
8888
8889 VikRequest::setVar('view', VikRequest::getCmd('view', 'managemaninvoice'));
8890
8891 parent::display();
8892
8893 if (VikBooking::showFooter()) {
8894 VikBookingHelper::printFooter();
8895 }
8896 }
8897
8898 public function editmaninvoice() {
8899 VikBookingHelper::printHeader("invoices");
8900
8901 VikRequest::setVar('view', VikRequest::getCmd('view', 'managemaninvoice'));
8902
8903 parent::display();
8904
8905 if (VikBooking::showFooter()) {
8906 VikBookingHelper::printFooter();
8907 }
8908 }
8909
8910 public function savemaninvoice() {
8911 if (!JSession::checkToken()) {
8912 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
8913 }
8914 $this->do_storemaninvoice('save');
8915 $mainframe = JFactory::getApplication();
8916 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8917 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8918 if (!empty($pgoto)) {
8919 $mainframe->redirect(base64_decode($pgoto));
8920 exit;
8921 }
8922 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
8923 }
8924
8925 public function updatemaninvoice()
8926 {
8927 if (!JSession::checkToken()) {
8928 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
8929 }
8930
8931 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
8932 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8933 }
8934
8935 $invid = VikRequest::getInt('whereup', 0, 'request');
8936 $this->do_storemaninvoice('update', $invid);
8937 $mainframe = JFactory::getApplication();
8938 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8939 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8940 if (!empty($pgoto)) {
8941 $mainframe->redirect(base64_decode($pgoto));
8942 exit;
8943 }
8944 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
8945 }
8946
8947 public function updatemaninvoicestay()
8948 {
8949 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
8950 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8951 }
8952
8953 $invid = VikRequest::getInt('whereup', 0, 'request');
8954 $this->do_storemaninvoice('updatestay', $invid);
8955 $mainframe = JFactory::getApplication();
8956 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8957 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8958 if (!empty($pgoto)) {
8959 $mainframe->redirect(base64_decode($pgoto));
8960 exit;
8961 }
8962 $mainframe->redirect("index.php?option=com_vikbooking&task=editmaninvoice&cid[]=".$invid);
8963 }
8964
8965 private function do_storemaninvoice($action, $invid = 0) {
8966 $dbo = JFactory::getDBO();
8967 $mainframe = JFactory::getApplication();
8968 $pinvoice_num = VikRequest::getInt('invoice_num', '', 'request');
8969 $pinvoice_num = $pinvoice_num <= 0 ? 1 : $pinvoice_num;
8970 $pinvoice_suff = VikRequest::getString('invoice_suff', '', 'request');
8971 $pcompany_info = VikRequest::getString('company_info', '', 'request', VIKREQUEST_ALLOWHTML);
8972 $pcompany_info = strpos($pcompany_info, '<') !== false ? $pcompany_info : nl2br($pcompany_info);
8973 $pinvoice_notes = VikRequest::getString('invoice_notes', '', 'request', VIKREQUEST_ALLOWHTML);
8974 $pinvoice_notes = strpos($pinvoice_notes, '<') !== false ? $pinvoice_notes : nl2br($pinvoice_notes);
8975 $pidcustomer = VikRequest::getInt('idcustomer', '', 'request');
8976 $error_uri = strpos($action, 'update') !== false && !empty($invid) ? 'index.php?option=com_vikbooking&task=editmaninvoice&cid[]='.$invid : 'index.php?option=com_vikbooking&task=newmaninvoice';
8977 if (empty($pidcustomer)) {
8978 VikError::raiseWarning('', JText::translate('VBNOCUSTOMERS'));
8979 $mainframe->redirect($error_uri);
8980 exit;
8981 }
8982 $services = VikRequest::getVar('service', array());
8983 $nets = VikRequest::getVar('net', array());
8984 $aliqs = VikRequest::getVar('aliq', array());
8985 $taxs = VikRequest::getVar('tax', array());
8986 $tots = VikRequest::getVar('tot', array());
8987 $ptotalnet = VikRequest::getFloat('totalnet', 0, 'request');
8988 $ptotaltax = VikRequest::getFloat('totaltax', 0, 'request');
8989 $ptotaltot = VikRequest::getFloat('totaltot', 0, 'request');
8990 if (!count($services) || count($services) != count($nets) || count($services) != count($taxs) || count($services) != count($tots)) {
8991 VikError::raiseWarning('', 'Missing data.');
8992 $mainframe->redirect($error_uri);
8993 exit;
8994 }
8995 $rawcont = array(
8996 'rows' => array(),
8997 'totalnet' => $ptotalnet,
8998 'totaltax' => $ptotaltax,
8999 'totaltot' => $ptotaltot,
9000 'notes' => $pinvoice_notes,
9001 );
9002 foreach ($services as $k => $service) {
9003 if (empty($service)) {
9004 continue;
9005 }
9006 array_push($rawcont['rows'], array(
9007 'service' => $service,
9008 'net' => (float)$nets[$k],
9009 'aliq' => (isset($aliqs[$k]) ? (float)$aliqs[$k] : 0),
9010 'tax' => (float)$taxs[$k],
9011 'tot' => (float)$tots[$k],
9012 ));
9013 }
9014 // store/update manual invoice
9015 $nowts = time();
9016 $retval = 0;
9017 if (strpos($action, 'save') !== false) {
9018 $pdffname = $nowts . '_' . rand() . '.pdf';
9019 $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)).");";
9020 $dbo->setQuery($q);
9021 $dbo->execute();
9022 $retval = $dbo->insertid();
9023 } else {
9024 // fetch old record
9025 $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id`=".(int)$invid.";";
9026 $dbo->setQuery($q);
9027 $dbo->execute();
9028 if (!$dbo->getNumRows()) {
9029 VikError::raiseWarning('', JText::translate('VBNOINVOICESFOUND'));
9030 $mainframe->redirect($error_uri);
9031 exit;
9032 }
9033 $previnvoice = $dbo->loadAssoc();
9034 //
9035 $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'].";";
9036 $dbo->setQuery($q);
9037 $dbo->execute();
9038 $retval = $previnvoice['id'];
9039 }
9040 // update config values for the invoice
9041 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pcompany_info)." WHERE `param`='invcompanyinfo';";
9042 $dbo->setQuery($q);
9043 $dbo->execute();
9044 // generate the custom invoice
9045 $result = VikBooking::generateCustomInvoice($retval);
9046 //
9047 $nextinv = VikBooking::getNextInvoiceNumber();
9048 $updatenum = ($pinvoice_num >= $nextinv);
9049 if ($updatenum) {
9050 /**
9051 * IMPORTANT: update the next invoice number after calling the e-Invocing drivers
9052 * to avoid conflicts with the drivers for the e-invoices generation.
9053 */
9054 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(($pinvoice_num - 1))." WHERE `param`='invoiceinum';";
9055 $dbo->setQuery($q);
9056 $dbo->execute();
9057 }
9058
9059 return $retval;
9060 }
9061
9062 public function downloadinvoices() {
9063 $ids = VikRequest::getVar('cid', array(0));
9064 if (@count($ids) > 0) {
9065 $dbo = JFactory::getDBO();
9066 $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id` IN (".implode(', ', $ids).");";
9067 $dbo->setQuery($q);
9068 $dbo->execute();
9069 if ($dbo->getNumRows() > 0) {
9070 $invoices = $dbo->loadAssocList();
9071 if (!(count($invoices) > 1)) {
9072 //Single Invoice Download
9073 if (file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoices[0]['file_name'])) {
9074 header("Content-type:application/pdf");
9075 header("Content-Disposition:attachment;filename=".$invoices[0]['file_name']);
9076 readfile(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoices[0]['file_name']);
9077 exit;
9078 }
9079 } else {
9080 //Multiple Invoices Download
9081 $to_zip = array();
9082 foreach ($invoices as $k => $invoice) {
9083 $to_zip[$k]['name'] = $invoice['file_name'];
9084 $to_zip[$k]['path'] = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoice['file_name'];
9085 }
9086 if (class_exists('ZipArchive')) {
9087 $zip_path = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.date('Y-m-d').'-invoices.zip';
9088 $zip = new ZipArchive;
9089 $zip->open($zip_path, ZipArchive::CREATE);
9090 foreach ($to_zip as $k => $zipv) {
9091 $zip->addFile($zipv['path'], $zipv['name']);
9092 }
9093 $zip->close();
9094 header("Content-type:application/zip");
9095 header("Content-Disposition:attachment;filename=".date('Y-m-d').'-invoices.zip');
9096 header("Content-Length:".filesize($zip_path));
9097 readfile($zip_path);
9098 unlink($zip_path);
9099 exit;
9100 } else {
9101 //Class ZipArchive does not exist
9102 VikError::raiseWarning('', 'Class ZipArchive does not exist on your server. Download the files one by one.');
9103 }
9104 }
9105 }
9106 }
9107 $mainframe = JFactory::getApplication();
9108 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9109 }
9110
9111 public function resendinvoices() {
9112 $ids = VikRequest::getVar('cid', array(0));
9113 $mainframe = JFactory::getApplication();
9114 if (!(count($ids) > 0)) {
9115 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9116 exit;
9117 }
9118 $dbo = JFactory::getDBO();
9119 $invoices = array();
9120 $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` ".
9121 "FROM `#__vikbooking_invoices` AS `i` " .
9122 "LEFT JOIN `#__vikbooking_orders` `o` ON `o`.`id`=`i`.`idorder` " .
9123 "LEFT JOIN `#__vikbooking_customers` `c` ON `c`.`id`=`i`.`idcustomer` " .
9124 "LEFT JOIN `#__vikbooking_countries` `nat` ON `nat`.`country_3_code`=`c`.`country` " .
9125 "WHERE `i`.`id` IN (".implode(', ', $ids).") AND (`i`.`idorder` < 0 OR (`o`.`status`='confirmed' AND `o`.`total` > 0)) ORDER BY `o`.`id` ASC;";
9126 $dbo->setQuery($q);
9127 $dbo->execute();
9128 if ($dbo->getNumRows() > 0) {
9129 $invoices = $dbo->loadAssocList();
9130 }
9131 if (!(count($invoices) > 0)) {
9132 VikError::raiseWarning('', JText::translate('VBOGENINVERRNOBOOKINGS'));
9133 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9134 exit;
9135 }
9136 $tot_generated = 0;
9137 $tot_sent = 0;
9138 foreach ($invoices as $bkey => $invoice) {
9139 $invoice['custmail'] = empty($invoice['custmail']) && !empty($invoice['customer_email']) ? $invoice['customer_email'] : $invoice['custmail'];
9140 $invoices[$bkey] = $invoice;
9141 $send_res = VikBooking::sendBookingInvoice($invoice['id'], $invoice);
9142 if ($send_res !== false) {
9143 $tot_sent++;
9144 }
9145 }
9146 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', $tot_generated, $tot_sent));
9147 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9148 }
9149
9150 public function removeinvoices()
9151 {
9152 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
9153 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9154 }
9155
9156 $ids = VikRequest::getVar('cid', array());
9157 $tot_removed = 0;
9158 $dbo = JFactory::getDbo();
9159
9160 foreach ($ids as $d) {
9161 $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id`=".(int)$d.";";
9162 $dbo->setQuery($q);
9163 $dbo->execute();
9164 if ($dbo->getNumRows() == 1) {
9165 $cur_invoice = $dbo->loadAssoc();
9166 if (file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$cur_invoice['file_name'])) {
9167 unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$cur_invoice['file_name']);
9168 }
9169 $q = "DELETE FROM `#__vikbooking_invoices` WHERE `id`=".(int)$d.";";
9170 $dbo->setQuery($q);
9171 $dbo->execute();
9172 $tot_removed++;
9173 }
9174 }
9175
9176 $mainframe = JFactory::getApplication();
9177 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESRMVD', $tot_removed));
9178 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9179 }
9180
9181 public function geninvoices()
9182 {
9183 $dbo = JFactory::getDbo();
9184 $app = JFactory::getApplication();
9185
9186 $ids = VikRequest::getVar('cid', array());
9187
9188 if (!$ids) {
9189 $app->redirect("index.php?option=com_vikbooking&task=orders");
9190 exit;
9191 }
9192
9193 $pinvoice_num = VikRequest::getInt('invoice_num', '', 'request');
9194 $pinvoice_num = $pinvoice_num <= 0 ? 1 : $pinvoice_num;
9195 $pinvoice_suff = VikRequest::getString('invoice_suff', '', 'request');
9196 $pinvoice_date = VikRequest::getString('invoice_date', '', 'request');
9197 $pcompany_info = VikRequest::getString('company_info', '', 'request', VIKREQUEST_ALLOWHTML);
9198 $pcompany_info = strpos($pcompany_info, '<') !== false ? $pcompany_info : nl2br($pcompany_info);
9199 $pinvoice_send = VikRequest::getInt('invoice_send', '', 'request');
9200 $pinvoice_send = $pinvoice_send > 0 ? true : false;
9201 $increment_inv = true;
9202 $pconfirmgen = VikRequest::getInt('confirmgen', '', 'request');
9203
9204 // if editing an invoice (re-creating an existing invoice for a booking), do not increment the invoice number
9205 if (count($ids) === 1) {
9206 $q = "SELECT `number` FROM `#__vikbooking_invoices` WHERE `idorder`=".(int)$ids[0].";";
9207 $dbo->setQuery($q);
9208 $dbo->execute();
9209 if ($dbo->getNumRows() == 1) {
9210 $increment_inv = false;
9211 }
9212 }
9213
9214 // get bookings
9215 $dbo->setQuery(
9216 $dbo->getQuery(true)
9217 ->select($dbo->qn('o') . '.*')
9218 ->select($dbo->qn('co.idcustomer'))
9219 ->select('CONCAT_WS(\' \', ' . $dbo->qn('c.first_name') . ', ' . $dbo->qn('c.last_name') . ') AS ' . $dbo->qn('customer_name'))
9220 ->select([
9221 $dbo->qn('c.pin', 'customer_pin'),
9222 $dbo->qn('nat.country_name'),
9223 ])
9224 ->from($dbo->qn('#__vikbooking_orders', 'o'))
9225 ->leftJoin($dbo->qn('#__vikbooking_customers_orders', 'co') . ' ON ' . $dbo->qn('co.idorder') . ' = ' . $dbo->qn('o.id'))
9226 ->leftJoin($dbo->qn('#__vikbooking_customers', 'c') . ' ON ' . $dbo->qn('c.id') . ' = ' . $dbo->qn('co.idcustomer'))
9227 ->leftJoin($dbo->qn('#__vikbooking_countries', 'nat') . ' ON ' . $dbo->qn('nat.country_3_code') . ' = ' . $dbo->qn('o.country'))
9228 ->where($dbo->qn('o.id') . ' IN (' . implode(', ', array_map('intval', $ids)) . ')')
9229 ->where($dbo->qn('o.status') . ' = ' . $dbo->q('confirmed'))
9230 ->where($dbo->qn('o.total') . ' > 0')
9231 ->order($dbo->qn('o.id') . ' ASC')
9232 );
9233
9234 $bookings = $dbo->loadAssocList();
9235
9236 if (!$bookings) {
9237 VikError::raiseWarning('', JText::translate('VBOGENINVERRNOBOOKINGS'));
9238 $app->redirect("index.php?option=com_vikbooking&task=orders");
9239 exit;
9240 }
9241
9242 $tot_generated = 0;
9243 $tot_sent = 0;
9244 foreach ($bookings as $bkey => $booking) {
9245 $gen_res = VikBooking::generateBookingInvoice($booking, $pinvoice_num, $pinvoice_suff, $pinvoice_date, $pcompany_info);
9246 if ($gen_res !== false && $gen_res > 0) {
9247 $tot_generated++;
9248 $pinvoice_num++;
9249 if ($pinvoice_send) {
9250 $send_res = VikBooking::sendBookingInvoice($gen_res, $booking);
9251 if ($send_res !== false) {
9252 $tot_sent++;
9253 }
9254 }
9255 } else {
9256 VikError::raiseWarning('', JText::sprintf('VBOGENINVERRBOOKING', $booking['id']));
9257 }
9258 }
9259
9260 if ($tot_generated > 0 && $increment_inv === true) {
9261 /**
9262 * IMPORTANT: update the next invoice number after calling generateBookingInvoice()
9263 * to avoid conflicts with the drivers for the e-invoices generation.
9264 */
9265 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(($pinvoice_num - 1))." WHERE `param`='invoiceinum';";
9266 $dbo->setQuery($q);
9267 $dbo->execute();
9268 }
9269
9270 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pinvoice_suff)." WHERE `param`='invoicesuffix';";
9271 $dbo->setQuery($q);
9272 $dbo->execute();
9273
9274 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pcompany_info)." WHERE `param`='invcompanyinfo';";
9275 $dbo->setQuery($q);
9276 $dbo->execute();
9277
9278 $app->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', $tot_generated, $tot_sent));
9279
9280 if ($pconfirmgen > 0) {
9281 $app->redirect("index.php?option=com_vikbooking&task=invoices&show=".$pconfirmgen);
9282 } elseif (count($bookings) === 1) {
9283 // go to the back-end booking details page
9284 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $bookings[0]['id']);
9285 } else {
9286 $app->redirect("index.php?option=com_vikbooking&task=orders");
9287 }
9288 }
9289
9290 public function optionals() {
9291 VikBookingHelper::printHeader("6");
9292
9293 VikRequest::setVar('view', VikRequest::getCmd('view', 'optionals'));
9294
9295 parent::display();
9296
9297 if (VikBooking::showFooter()) {
9298 VikBookingHelper::printFooter();
9299 }
9300 }
9301
9302 public function newoptionals() {
9303 VikBookingHelper::printHeader("6");
9304
9305 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoptional'));
9306
9307 parent::display();
9308
9309 if (VikBooking::showFooter()) {
9310 VikBookingHelper::printFooter();
9311 }
9312 }
9313
9314 public function editoptional() {
9315 VikBookingHelper::printHeader("6");
9316
9317 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoptional'));
9318
9319 parent::display();
9320
9321 if (VikBooking::showFooter()) {
9322 VikBookingHelper::printFooter();
9323 }
9324 }
9325
9326 public function updateoptional()
9327 {
9328 if (!JSession::checkToken()) {
9329 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9330 }
9331
9332 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
9333 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9334 }
9335
9336 $this->do_updateoptional();
9337 }
9338
9339 public function updateoptionalstay()
9340 {
9341 if (!JSession::checkToken()) {
9342 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9343 }
9344
9345 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
9346 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9347 }
9348
9349 $this->do_updateoptional(true);
9350 }
9351
9352 private function do_updateoptional($stay = false) {
9353 $dbo = JFactory::getDbo();
9354 $app = JFactory::getApplication();
9355 $poptname = VikRequest::getString('optname', '', 'request');
9356 $poptdescr = VikRequest::getString('optdescr', '', 'request', VIKREQUEST_ALLOWHTML);
9357 $poptcost = VikRequest::getFloat('optcost', '', 'request');
9358 $poptperday = VikRequest::getString('optperday', '', 'request');
9359 $poptperperson = VikRequest::getString('optperperson', '', 'request');
9360 $pmaxprice = VikRequest::getFloat('maxprice', '', 'request');
9361 $popthmany = VikRequest::getString('opthmany', '', 'request');
9362 $poptaliq = VikRequest::getInt('optaliq', '', 'request');
9363 $pwhereup = VikRequest::getString('whereup', '', 'request');
9364 $pautoresize = VikRequest::getString('autoresize', '', 'request');
9365 $presizeto = VikRequest::getString('resizeto', '', 'request');
9366 $pifchildren = VikRequest::getString('ifchildren', '', 'request');
9367 $pifchildren = $pifchildren == "1" ? 1 : 0;
9368 $pmaxquant = VikRequest::getString('maxquant', '', 'request');
9369 $pmaxquant = empty($pmaxquant) ? 0 : intval($pmaxquant);
9370 $pforcesel = VikRequest::getString('forcesel', '', 'request');
9371 $pforceval = VikRequest::getString('forceval', '', 'request');
9372 $pforcevalperday = VikRequest::getString('forcevalperday', '', 'request');
9373 $pforcevalperchild = VikRequest::getString('forcevalperchild', '', 'request');
9374 $pforcesummary = VikRequest::getString('forcesummary', '', 'request');
9375 $pforcesel = $pforcesel == "1" ? 1 : 0;
9376 $pis_citytax = VikRequest::getString('is_citytax', '', 'request');
9377 $pis_fee = VikRequest::getString('is_fee', '', 'request');
9378 $pis_citytax = $pis_citytax == "1" && $pis_fee != "1" ? 1 : 0;
9379 $pis_fee = $pis_fee == "1" && $pis_citytax == 0 ? 1 : 0;
9380 $pagefrom = VikRequest::getVar('agefrom', array());
9381 $pageto = VikRequest::getVar('ageto', array());
9382 $pagecost = VikRequest::getVar('agecost', array());
9383 $pagectype = VikRequest::getVar('agectype', array());
9384 $palwaysav = VikRequest::getInt('alwaysav', 0, 'request');
9385 $pavfrom = VikRequest::getString('avfrom', '', 'request');
9386 $pavto = VikRequest::getString('avto', '', 'request');
9387 $ppcentroom = VikRequest::getInt('pcentroom', 0, 'request');
9388 $pidrooms = VikRequest::getVar('idrooms', array());
9389 $optavstr = empty($palwaysav) && !empty($pavfrom) && !empty($pavto) ? VikBooking::getDateTimestamp($pavfrom, 0, 0, 0).';'.VikBooking::getDateTimestamp($pavto, 23, 59, 59) : '';
9390 if ($pforcesel == 1) {
9391 $strforceval = intval($pforceval)."-".($pforcevalperday == "1" ? "1" : "0")."-".($pforcevalperchild == "1" ? "1" : "0")."-".($pforcesummary == "1" ? "1" : "0");
9392 } else {
9393 $strforceval = "";
9394 }
9395 $minguestsnum = VikRequest::getInt('minguestsnum', 0, 'request');
9396 $mingueststype = VikRequest::getString('mingueststype', 'guests', 'request');
9397 $minguestsnum = $minguestsnum < 0 ? 0 : $minguestsnum;
9398 $mingueststype = !empty($mingueststype) && !in_array($mingueststype, array('adults', 'guests')) ? 'guests' : $mingueststype;
9399 $maxguestsnum = VikRequest::getInt('maxguestsnum', 0, 'request');
9400 $maxgueststype = VikRequest::getString('maxgueststype', 'guests', 'request');
9401 $maxguestsnum = $maxguestsnum < 0 ? 0 : $maxguestsnum;
9402 $maxgueststype = !empty($maxgueststype) && !in_array($maxgueststype, array('adults', 'guests')) ? 'guests' : $maxgueststype;
9403 $minguests = VikRequest::getInt('minguests', 0, 'request');
9404 $minguests_conflict = false;
9405 if ($minguests > 0 && $minguestsnum > 0 && $maxguestsnum > 0) {
9406 if ($minguestsnum >= $maxguestsnum) {
9407 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL1');
9408 } elseif (($maxguestsnum - $minguestsnum) < 2) {
9409 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL2');
9410 }
9411 }
9412 if (!$minguests || $minguests_conflict !== false) {
9413 $minguestsnum = 0;
9414 $maxguestsnum = 0;
9415 if ($minguests_conflict !== false) {
9416 // raise warning, but do not stop the process
9417 VikError::raiseWarning('', $minguests_conflict);
9418 }
9419 }
9420 $damagedep = VikRequest::getInt('damagedep', 0, 'request');
9421 $pet_fee = VikRequest::getInt('pet_fee', 0, 'request');
9422 $custom_checkinout = VikRequest::getInt('custom_checkinout', 0, 'request');
9423 $set_checkin = VikRequest::getInt('set_checkin', 0, 'request');
9424 $set_checkout = VikRequest::getInt('set_checkout', 0, 'request');
9425 if (!$custom_checkinout) {
9426 $set_checkin = 0;
9427 $set_checkout = 0;
9428 }
9429 if ((!$set_checkin && !$set_checkout) || $set_checkin == $set_checkout) {
9430 // check-in and check-out times should not be equal or both empty
9431 $custom_checkinout = 0;
9432 }
9433 $damagedep_settings = $damagedep ? ((array) $app->input->get('damagedep_settings', [], 'array')) : [];
9434 $oparams = [
9435 'minguestsnum' => $minguestsnum,
9436 'mingueststype' => $mingueststype,
9437 'maxguestsnum' => $maxguestsnum,
9438 'maxgueststype' => $maxgueststype,
9439 'damagedep' => $damagedep,
9440 'damagedep_settings' => $damagedep_settings,
9441 'pet_fee' => $pet_fee,
9442 'custom_checkinout' => $custom_checkinout,
9443 'set_checkin' => $set_checkin,
9444 'set_checkout' => $set_checkout,
9445 ];
9446 /**
9447 * We fetch the previous params to merge them with the new ones
9448 * in case some properties have been set somewhere else.
9449 * For example, the damage deposit transmission to Booking.com.
9450 */
9451 $cur_oparams = array();
9452 $q = "SELECT `oparams` FROM `#__vikbooking_optionals` WHERE `id`=" . (int)$pwhereup . ";";
9453 $dbo->setQuery($q);
9454 $dbo->execute();
9455 if ($dbo->getNumRows()) {
9456 $cur_oparams = $dbo->loadResult();
9457 $cur_oparams = !empty($cur_oparams) ? json_decode($cur_oparams, true) : array();
9458 $cur_oparams = !is_array($cur_oparams) ? array() : $cur_oparams;
9459 // merge previous params with the new ones to get the new values
9460 $oparams = array_merge($cur_oparams, $oparams);
9461 }
9462
9463 /**
9464 * Ensure options of type city tax never get a tax rate.
9465 *
9466 * @since 1.18.3 (J) - 1.8.3 (WP)
9467 */
9468 if ($pis_citytax) {
9469 $poptaliq = 0;
9470 }
9471
9472 if (!empty($poptname)) {
9473 if (intval($_FILES['optimg']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['optimg']['name'])!="") {
9474 jimport('joomla.filesystem.file');
9475 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9476 if (@is_uploaded_file($_FILES['optimg']['tmp_name'])) {
9477 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['optimg']['name'])));
9478 if (file_exists($updpath.$safename)) {
9479 $j=1;
9480 while (file_exists($updpath.$j.$safename)) {
9481 $j++;
9482 }
9483 $pwhere=$updpath.$j.$safename;
9484 } else {
9485 $j="";
9486 $pwhere=$updpath.$safename;
9487 }
9488 if (!getimagesize($_FILES['optimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
9489 @unlink($pwhere);
9490 $picon="";
9491 } else {
9492 VikBooking::uploadFile($_FILES['optimg']['tmp_name'], $pwhere);
9493 @chmod($pwhere, 0644);
9494 $picon=$j.$safename;
9495 if ($pautoresize=="1" && !empty($presizeto)) {
9496 $eforj = new vikResizer();
9497 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
9498 if ($origmod) {
9499 @unlink($pwhere);
9500 $picon='r_'.$j.$safename;
9501 }
9502 }
9503 }
9504 } else {
9505 $picon="";
9506 }
9507 } else {
9508 $picon="";
9509 }
9510 ($poptperday=="each" ? $poptperday="1" : $poptperday="0");
9511 $poptperperson=($poptperperson=="each" ? "1" : "0");
9512 ($popthmany=="yes" ? $popthmany="1" : $popthmany="0");
9513 $ageintervalstr = '';
9514 if ($pifchildren == 1 && count($pagefrom) > 0 && count($pagecost) > 0 && count($pagefrom) == count($pagecost)) {
9515 foreach ($pagefrom as $kage => $vage) {
9516 $afrom = intval($vage);
9517 $ato = intval($pageto[$kage]);
9518 $acost = floatval($pagecost[$kage]);
9519 if (strlen($vage) > 0 && strlen($pagecost[$kage]) > 0) {
9520 if ($ato < $afrom) $ato = $afrom;
9521 $ageintervalstr .= $afrom.'_'.$ato.'_'.$acost.(array_key_exists($kage, $pagectype) && strpos($pagectype[$kage], '%') !== false ? '_%'.(strpos($pagectype[$kage], '%b') !== false ? 'b' : '') : '').';;';
9522 }
9523 }
9524 $ageintervalstr = rtrim($ageintervalstr, ';;');
9525 if (!empty($ageintervalstr)) {
9526 $pforcesel = 1;
9527 }
9528 }
9529 $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).";";
9530 $dbo->setQuery($q);
9531 $dbo->execute();
9532 $app->enqueueMessage(JText::translate('VBOSUCCUPDOPTION'));
9533
9534 // assign/unset option-rooms relations
9535 $rooms_with_opt = array();
9536 if (count($pidrooms)) {
9537 // assign this new option to the requested rooms
9538 foreach ($pidrooms as $idroom) {
9539 if (empty($idroom)) {
9540 continue;
9541 }
9542 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
9543 $dbo->setQuery($q);
9544 $dbo->execute();
9545 if (!$dbo->getNumRows()) {
9546 continue;
9547 }
9548 $room_data = $dbo->loadAssoc();
9549 array_push($rooms_with_opt, $room_data['id']);
9550 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9551 if (in_array((string)$pwhereup, $current_opts)) {
9552 continue;
9553 }
9554 if (count($current_opts) === 1 && (string)$current_opts[0] == '0') {
9555 // make sure we do not concatenate a real ID to 0
9556 $current_opts = array();
9557 }
9558 array_push($current_opts, $pwhereup);
9559 $new_opts = implode(';', $current_opts) . ';';
9560 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9561 $dbo->setQuery($q);
9562 $dbo->execute();
9563 }
9564 }
9565 if (!count($rooms_with_opt)) {
9566 // get all rooms to unset this option (if previously set)
9567 array_push($rooms_with_opt, '0');
9568 }
9569 // unset the option from the other rooms that may have it
9570 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_opt) . ");";
9571 $dbo->setQuery($q);
9572 $dbo->execute();
9573 if ($dbo->getNumRows()) {
9574 $unset_rooms_opt = $dbo->loadAssocList();
9575 foreach ($unset_rooms_opt as $room_data) {
9576 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9577 if (!in_array((string)$pwhereup, $current_opts)) {
9578 // this room is not using this option
9579 continue;
9580 }
9581 $optkey = array_search((string)$pwhereup, $current_opts);
9582 if ($optkey === false) {
9583 // key not found
9584 continue;
9585 }
9586 // unset this option ID from the string
9587 unset($current_opts[$optkey]);
9588 if (!count($current_opts)) {
9589 // a room with no options assigned will be listed as "0;"
9590 $current_opts = array(0);
9591 }
9592 $new_opts = implode(';', $current_opts) . ';';
9593 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9594 $dbo->setQuery($q);
9595 $dbo->execute();
9596 }
9597 }
9598 //
9599
9600 }
9601 $app->redirect("index.php?option=com_vikbooking&task=" . ($stay ? 'editoptional&cid[]=' . $pwhereup : 'optionals'));
9602 }
9603
9604 public function createoptionals()
9605 {
9606 if (!JSession::checkToken()) {
9607 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9608 }
9609
9610 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
9611 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9612 }
9613
9614 $this->do_createoptionals();
9615 }
9616
9617 public function createoptionalsstay()
9618 {
9619 if (!JSession::checkToken()) {
9620 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9621 }
9622
9623 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
9624 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9625 }
9626
9627 $this->do_createoptionals(true);
9628 }
9629
9630 private function do_createoptionals($stay = false)
9631 {
9632 $app = JFactory::getApplication();
9633 $dbo = JFactory::getDbo();
9634
9635 $poptname = VikRequest::getString('optname', '', 'request');
9636 $poptdescr = VikRequest::getString('optdescr', '', 'request', VIKREQUEST_ALLOWHTML);
9637 $poptcost = VikRequest::getFloat('optcost', '', 'request');
9638 $poptperday = VikRequest::getString('optperday', '', 'request');
9639 $poptperperson = VikRequest::getString('optperperson', '', 'request');
9640 $pmaxprice = VikRequest::getFloat('maxprice', '', 'request');
9641 $popthmany = VikRequest::getString('opthmany', '', 'request');
9642 $poptaliq = VikRequest::getInt('optaliq', '', 'request');
9643 $pautoresize = VikRequest::getString('autoresize', '', 'request');
9644 $presizeto = VikRequest::getString('resizeto', '', 'request');
9645 $pifchildren = VikRequest::getString('ifchildren', '', 'request');
9646 $pifchildren = $pifchildren == "1" ? 1 : 0;
9647 $pmaxquant = VikRequest::getString('maxquant', '', 'request');
9648 $pmaxquant = empty($pmaxquant) ? 0 : intval($pmaxquant);
9649 $pforcesel = VikRequest::getString('forcesel', '', 'request');
9650 $pforceval = VikRequest::getString('forceval', '', 'request');
9651 $pforcevalperday = VikRequest::getString('forcevalperday', '', 'request');
9652 $pforcevalperchild = VikRequest::getString('forcevalperchild', '', 'request');
9653 $pforcesummary = VikRequest::getString('forcesummary', '', 'request');
9654 $pforcesel = $pforcesel == "1" ? 1 : 0;
9655 $pis_citytax = VikRequest::getString('is_citytax', '', 'request');
9656 $pis_fee = VikRequest::getString('is_fee', '', 'request');
9657 $pis_citytax = $pis_citytax == "1" && $pis_fee != "1" ? 1 : 0;
9658 $pis_fee = $pis_fee == "1" && $pis_citytax == 0 ? 1 : 0;
9659 $pagefrom = VikRequest::getVar('agefrom', array());
9660 $pageto = VikRequest::getVar('ageto', array());
9661 $pagecost = VikRequest::getVar('agecost', array());
9662 $pagectype = VikRequest::getVar('agectype', array());
9663 $palwaysav = VikRequest::getInt('alwaysav', 0, 'request');
9664 $pavfrom = VikRequest::getString('avfrom', '', 'request');
9665 $pavto = VikRequest::getString('avto', '', 'request');
9666 $ppcentroom = VikRequest::getInt('pcentroom', 0, 'request');
9667 $pidrooms = VikRequest::getVar('idrooms', array());
9668 $optavstr = empty($palwaysav) && !empty($pavfrom) && !empty($pavto) ? VikBooking::getDateTimestamp($pavfrom, 0, 0, 0).';'.VikBooking::getDateTimestamp($pavto, 23, 59, 59) : '';
9669 if ($pforcesel == 1) {
9670 $strforceval = intval($pforceval)."-".($pforcevalperday == "1" ? "1" : "0")."-".($pforcevalperchild == "1" ? "1" : "0")."-".($pforcesummary == "1" ? "1" : "0");
9671 } else {
9672 $strforceval = "";
9673 }
9674 $minguestsnum = VikRequest::getInt('minguestsnum', 0, 'request');
9675 $mingueststype = VikRequest::getString('mingueststype', 'guests', 'request');
9676 $minguestsnum = $minguestsnum < 0 ? 0 : $minguestsnum;
9677 $mingueststype = !empty($mingueststype) && !in_array($mingueststype, array('adults', 'guests')) ? 'guests' : $mingueststype;
9678 $maxguestsnum = VikRequest::getInt('maxguestsnum', 0, 'request');
9679 $maxgueststype = VikRequest::getString('maxgueststype', 'guests', 'request');
9680 $maxguestsnum = $maxguestsnum < 0 ? 0 : $maxguestsnum;
9681 $maxgueststype = !empty($maxgueststype) && !in_array($maxgueststype, array('adults', 'guests')) ? 'guests' : $maxgueststype;
9682 $minguests = VikRequest::getInt('minguests', 0, 'request');
9683 $minguests_conflict = false;
9684 if ($minguests > 0 && $minguestsnum > 0 && $maxguestsnum > 0) {
9685 if ($minguestsnum >= $maxguestsnum) {
9686 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL1');
9687 } elseif (($maxguestsnum - $minguestsnum) < 2) {
9688 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL2');
9689 }
9690 }
9691 if (!$minguests || $minguests_conflict !== false) {
9692 $minguestsnum = 0;
9693 $maxguestsnum = 0;
9694 if ($minguests_conflict !== false) {
9695 // raise warning, but do not stop the process
9696 VikError::raiseWarning('', $minguests_conflict);
9697 }
9698 }
9699 $damagedep = VikRequest::getInt('damagedep', 0, 'request');
9700 $pet_fee = VikRequest::getInt('pet_fee', 0, 'request');
9701 $custom_checkinout = VikRequest::getInt('custom_checkinout', 0, 'request');
9702 $set_checkin = VikRequest::getInt('set_checkin', 0, 'request');
9703 $set_checkout = VikRequest::getInt('set_checkout', 0, 'request');
9704 if (!$custom_checkinout) {
9705 $set_checkin = 0;
9706 $set_checkout = 0;
9707 }
9708 if ((!$set_checkin && !$set_checkout) || $set_checkin == $set_checkout) {
9709 // check-in and check-out times should not be equal or both empty
9710 $custom_checkinout = 0;
9711 }
9712 $damagedep_settings = $damagedep ? ((array) $app->input->get('damagedep_settings', [], 'array')) : [];
9713 $oparams = [
9714 'minguestsnum' => $minguestsnum,
9715 'mingueststype' => $mingueststype,
9716 'maxguestsnum' => $maxguestsnum,
9717 'maxgueststype' => $maxgueststype,
9718 'damagedep' => $damagedep,
9719 'damagedep_settings' => $damagedep_settings,
9720 'pet_fee' => $pet_fee,
9721 'custom_checkinout' => $custom_checkinout,
9722 'set_checkin' => $set_checkin,
9723 'set_checkout' => $set_checkout,
9724 ];
9725
9726 /**
9727 * Ensure options of type city tax never get a tax rate.
9728 *
9729 * @since 1.18.3 (J) - 1.8.3 (WP)
9730 */
9731 if ($pis_citytax) {
9732 $poptaliq = 0;
9733 }
9734
9735 if (!empty($poptname)) {
9736 if (intval($_FILES['optimg']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['optimg']['name'])!="") {
9737 jimport('joomla.filesystem.file');
9738 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9739 if (@is_uploaded_file($_FILES['optimg']['tmp_name'])) {
9740 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['optimg']['name'])));
9741 if (file_exists($updpath.$safename)) {
9742 $j = 1;
9743 while (file_exists($updpath.$j.$safename)) {
9744 $j++;
9745 }
9746 $pwhere = $updpath.$j.$safename;
9747 } else {
9748 $j = "";
9749 $pwhere = $updpath.$safename;
9750 }
9751 if (!getimagesize($_FILES['optimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
9752 @unlink($pwhere);
9753 $picon = "";
9754 } else {
9755 VikBooking::uploadFile($_FILES['optimg']['tmp_name'], $pwhere);
9756 @chmod($pwhere, 0644);
9757 $picon = $j.$safename;
9758 if ($pautoresize == "1" && !empty($presizeto)) {
9759 $eforj = new vikResizer();
9760 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
9761 if ($origmod) {
9762 @unlink($pwhere);
9763 $picon = 'r_'.$j.$safename;
9764 }
9765 }
9766 }
9767 } else {
9768 $picon = "";
9769 }
9770 } else {
9771 $picon = "";
9772 }
9773 $poptperday = ($poptperday == "each" ? "1" : "0");
9774 $poptperperson = ($poptperperson == "each" ? "1" : "0");
9775 ($popthmany == "yes" ? $popthmany = "1" : $popthmany = "0");
9776 $ageintervalstr = '';
9777 if ($pifchildren == 1 && count($pagefrom) > 0 && count($pagecost) > 0 && count($pagefrom) == count($pagecost)) {
9778 foreach ($pagefrom as $kage => $vage) {
9779 $afrom = intval($vage);
9780 $ato = intval($pageto[$kage]);
9781 $acost = floatval($pagecost[$kage]);
9782 if (strlen($vage) > 0 && strlen($pagecost[$kage]) > 0) {
9783 if ($ato < $afrom) $ato = $afrom;
9784 $ageintervalstr .= $afrom.'_'.$ato.'_'.$acost.(array_key_exists($kage, $pagectype) && strpos($pagectype[$kage], '%') !== false ? '_%'.(strpos($pagectype[$kage], '%b') !== false ? 'b' : '') : '').';;';
9785 }
9786 }
9787 $ageintervalstr = rtrim($ageintervalstr, ';;');
9788 if (!empty($ageintervalstr)) {
9789 $pforcesel = 1;
9790 }
9791 }
9792 $q = "SELECT `ordering` FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` DESC LIMIT 1;";
9793 $dbo->setQuery($q);
9794 $dbo->execute();
9795 if ($dbo->getNumRows() == 1) {
9796 $getlast = $dbo->loadResult();
9797 $newsortnum = $getlast + 1;
9798 } else {
9799 $newsortnum = 1;
9800 }
9801 $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)) . ");";
9802 $dbo->setQuery($q);
9803 $dbo->execute();
9804 $newoptid = $dbo->insertid();
9805
9806 if (!empty($newoptid)) {
9807 // assign/unset option-rooms relations
9808 $rooms_with_opt = array();
9809 if (count($pidrooms)) {
9810 // assign this new option to the requested rooms
9811 foreach ($pidrooms as $idroom) {
9812 if (empty($idroom)) {
9813 continue;
9814 }
9815 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
9816 $dbo->setQuery($q);
9817 $dbo->execute();
9818 if (!$dbo->getNumRows()) {
9819 continue;
9820 }
9821 $room_data = $dbo->loadAssoc();
9822 array_push($rooms_with_opt, $room_data['id']);
9823 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9824 if (in_array((string)$newoptid, $current_opts)) {
9825 continue;
9826 }
9827 if (count($current_opts) === 1 && (string)$current_opts[0] == '0') {
9828 // make sure we do not concatenate a real ID to 0
9829 $current_opts = array();
9830 }
9831 array_push($current_opts, $newoptid);
9832 $new_opts = implode(';', $current_opts) . ';';
9833 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9834 $dbo->setQuery($q);
9835 $dbo->execute();
9836 }
9837 }
9838 if (!count($rooms_with_opt)) {
9839 // get all rooms to unset this option (if previously set)
9840 array_push($rooms_with_opt, '0');
9841 }
9842 // unset the option from the other rooms that may have it
9843 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_opt) . ");";
9844 $dbo->setQuery($q);
9845 $dbo->execute();
9846 if ($dbo->getNumRows()) {
9847 $unset_rooms_opt = $dbo->loadAssocList();
9848 foreach ($unset_rooms_opt as $room_data) {
9849 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9850 if (!in_array((string)$newoptid, $current_opts)) {
9851 // this room is not using this option
9852 continue;
9853 }
9854 $optkey = array_search((string)$newoptid, $current_opts);
9855 if ($optkey === false) {
9856 // key not found
9857 continue;
9858 }
9859 // unset this option ID from the string
9860 unset($current_opts[$optkey]);
9861 if (!count($current_opts)) {
9862 // a room with no options assigned will be listed as "0;"
9863 $current_opts = array(0);
9864 }
9865 $new_opts = implode(';', $current_opts) . ';';
9866 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9867 $dbo->setQuery($q);
9868 $dbo->execute();
9869 }
9870 }
9871 //
9872 }
9873
9874 }
9875 $mainframe = JFactory::getApplication();
9876 $mainframe->redirect("index.php?option=com_vikbooking&task=" . ($stay && isset($newoptid) && !empty($newoptid) ? 'editoptional&cid[]=' . $newoptid : 'optionals'));
9877 }
9878
9879 public function removeoptionals()
9880 {
9881 if (!JSession::checkToken()) {
9882 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9883 }
9884
9885 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
9886 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9887 }
9888
9889 $ids = VikRequest::getVar('cid', array(0));
9890 if ($ids) {
9891 $dbo = JFactory::getDbo();
9892 foreach ($ids as $d) {
9893 $q = "SELECT `img` FROM `#__vikbooking_optionals` WHERE `id`=".$dbo->quote($d).";";
9894 $dbo->setQuery($q);
9895 $dbo->execute();
9896 if ($dbo->getNumRows() == 1) {
9897 $rows = $dbo->loadAssocList();
9898 if (!empty($rows[0]['img']) && file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['img'])) {
9899 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['img']);
9900 }
9901 }
9902 $q = "DELETE FROM `#__vikbooking_optionals` WHERE `id`=".$dbo->quote($d).";";
9903 $dbo->setQuery($q);
9904 $dbo->execute();
9905 }
9906 }
9907 $mainframe = JFactory::getApplication();
9908 $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
9909 }
9910
9911 public function sendcustomsms() {
9912 $mainframe = JFactory::getApplication();
9913 $pphone = VikRequest::getString('phone', '', 'request');
9914 $psmscont = VikRequest::getString('smscont', '', 'request');
9915 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
9916 $pgoto = !empty($pgoto) ? urldecode($pgoto) : 'index.php?option=com_vikbooking';
9917 if (!empty($pphone) && !empty($psmscont)) {
9918 $sms_api = VikBooking::getSMSAPIClass();
9919 $sms_api_params = VikBooking::getSMSParams();
9920 if (!empty($sms_api) && file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api) && !empty($sms_api_params)) {
9921 require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
9922 $sms_obj = new VikSmsApi(array(), $sms_api_params);
9923 $response_obj = $sms_obj->sendMessage($pphone, $psmscont);
9924 if ( !$sms_obj->validateResponse($response_obj) ) {
9925 VikError::raiseWarning('', $sms_obj->getLog());
9926 } else {
9927 $mainframe->enqueueMessage(JText::translate('VBSENDSMSOK'));
9928 }
9929 } else {
9930 VikError::raiseWarning('', JText::translate('VBSENDSMSERRMISSAPI'));
9931 }
9932 } else {
9933 VikError::raiseWarning('', JText::translate('VBSENDSMSERRMISSDATA'));
9934 }
9935 $mainframe->redirect($pgoto);
9936 }
9937
9938 public function sendcustomemail() {
9939 $dbo = JFactory::getDbo();
9940 $mainframe = JFactory::getApplication();
9941 $vbo_tn = VikBooking::getTranslator();
9942 $pbid = VikRequest::getInt('bid', '', 'request');
9943 $pemailsubj = VikRequest::getString('emailsubj', '', 'request');
9944 $pemail = VikRequest::getString('email', '', 'request');
9945 $pemailcont = VikRequest::getString('emailcont', '', 'request', VIKREQUEST_ALLOWRAW);
9946 $pemailfrom = VikRequest::getString('emailfrom', '', 'request');
9947 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
9948 $pgoto = !empty($pgoto) ? urldecode($pgoto) : 'index.php?option=com_vikbooking';
9949 if (!empty($pemail) && !empty($pemailcont)) {
9950 $email_attach = null;
9951 jimport('joomla.filesystem.file');
9952 $pemailattch = VikRequest::getVar('emailattch', null, 'files', 'array');
9953 if (isset($pemailattch) && strlen(trim($pemailattch['name']))) {
9954 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pemailattch['name'])));
9955 $src = $pemailattch['tmp_name'];
9956 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9957 $j = "";
9958 if (file_exists($dest.$filename)) {
9959 $j = rand(171, 1717);
9960 while (file_exists($dest.$j.$filename)) {
9961 $j++;
9962 }
9963 }
9964 $finaldest = $dest.$j.$filename;
9965 if (VikBooking::uploadFile($src, $finaldest)) {
9966 $email_attach = $finaldest;
9967 } else {
9968 VikError::raiseWarning('', 'Error uploading the attachment. Email not sent.');
9969 $mainframe->redirect($pgoto);
9970 exit;
9971 }
9972 }
9973 //VBO 1.10 - special tags for the custom email template files and messages
9974 $orig_mail_cont = $pemailcont;
9975 if (strpos($pemailcont, '{') !== false && strpos($pemailcont, '}') !== false) {
9976 // replace any possible placeholder for special tags
9977 $pemailcont = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
9978 return $match[2];
9979 }, $pemailcont);
9980
9981 $booking = array();
9982 $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.";";
9983 $dbo->setQuery($q);
9984 $dbo->execute();
9985 if ($dbo->getNumRows() > 0) {
9986 $booking = $dbo->loadAssoc();
9987 }
9988 $booking_rooms = array();
9989 $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.";";
9990 $dbo->setQuery($q);
9991 $dbo->execute();
9992 if ($dbo->getNumRows() > 0) {
9993 $booking_rooms = $dbo->loadAssocList();
9994 if (!empty($booking['lang'])) {
9995 $vbo_tn->translateContents($booking_rooms, '#__vikbooking_rooms', array('id' => 'idroom', 'room_name' => 'name'), array(), $booking['lang']);
9996 }
9997 }
9998 //we use the same parsing function as the one for the Customer SMS Template
9999 $pemailcont = VikBooking::parseCustomerSMSTemplate($booking, $booking_rooms, null, $pemailcont);
10000 }
10001 //
10002 // allow the use of token {booking_id} in subject
10003 $pemailsubj = str_replace('{booking_id}', $pbid, $pemailsubj);
10004 //
10005 $is_html = (strpos($pemailcont, '<') !== false && strpos($pemailcont, '>') !== false);
10006 $pemailcont = !$is_html ? nl2br($pemailcont) : $pemailcont;
10007 $vbo_app = VikBooking::getVboApplication();
10008 $vbo_app->sendMail($pemailfrom, $pemailfrom, $pemail, $pemailfrom, $pemailsubj, $pemailcont, $is_html, 'base64', $email_attach);
10009 $mainframe->enqueueMessage(JText::translate('VBSENDEMAILOK'));
10010 if ($email_attach !== null) {
10011 @unlink($email_attach);
10012 }
10013 //Booking History
10014 VikBooking::getBookingHistoryInstance()->setBid($pbid)->store('CE', nl2br($pemailsubj . "\n\n" . $pemailcont));
10015 //
10016 //Save email template for future sending
10017 $config_rec_exists = false;
10018 $emtpl = array(
10019 'emailsubj' => $pemailsubj,
10020 'emailcont' => $orig_mail_cont,
10021 'emailfrom' => $pemailfrom
10022 );
10023 $cur_emtpl = array();
10024 $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='customemailtpls';";
10025 $dbo->setQuery($q);
10026 $dbo->execute();
10027 if ($dbo->getNumRows() > 0) {
10028 $config_rec_exists = true;
10029 $cur_emtpl = $dbo->loadResult();
10030 $cur_emtpl = empty($cur_emtpl) ? array() : json_decode($cur_emtpl, true);
10031 $cur_emtpl = is_array($cur_emtpl) ? $cur_emtpl : array();
10032 }
10033 if (count($cur_emtpl) > 0) {
10034 $existing_subj = false;
10035 foreach ($cur_emtpl as $emk => $emv) {
10036 if (array_key_exists('emailsubj', $emv) && $emv['emailsubj'] == $emtpl['emailsubj']) {
10037 $cur_emtpl[$emk] = $emtpl;
10038 $existing_subj = true;
10039 break;
10040 }
10041 }
10042 if ($existing_subj === false) {
10043 $cur_emtpl[] = $emtpl;
10044 }
10045 } else {
10046 $cur_emtpl[] = $emtpl;
10047 }
10048 if (count($cur_emtpl) > 10) {
10049 //Max 10 templates to avoid problems with the size of the field and truncated json strings
10050 $exceed = count($cur_emtpl) - 10;
10051 for ($tl=0; $tl < $exceed; $tl++) {
10052 unset($cur_emtpl[$tl]);
10053 }
10054 $cur_emtpl = array_values($cur_emtpl);
10055 }
10056 if ($config_rec_exists === true) {
10057 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($cur_emtpl))." WHERE `param`='customemailtpls';";
10058 $dbo->setQuery($q);
10059 $dbo->execute();
10060 } else {
10061 $q = "INSERT INTO `#__vikbooking_config` (`param`,`setting`) VALUES ('customemailtpls', ".$dbo->quote(json_encode($cur_emtpl)).");";
10062 $dbo->setQuery($q);
10063 $dbo->execute();
10064 }
10065 //
10066 } else {
10067 VikError::raiseWarning('', JText::translate('VBSENDEMAILERRMISSDATA'));
10068 }
10069 $mainframe->redirect($pgoto);
10070 }
10071
10072 public function rmcustomemailtpl() {
10073 $cid = VikRequest::getVar('cid', array(0));
10074 $oid = $cid[0];
10075 $dbo = JFactory::getDBO();
10076 $mainframe = JFactory::getApplication();
10077 $tplind = VikRequest::getInt('tplind', '', 'request');
10078 if (empty($oid) || !(strlen($tplind) > 0)) {
10079 VikError::raiseWarning('', 'Missing Data.');
10080 $mainframe->redirect('index.php?option=com_vikbooking');
10081 exit;
10082 }
10083 $cur_emtpl = array();
10084 $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='customemailtpls';";
10085 $dbo->setQuery($q);
10086 $dbo->execute();
10087 if ($dbo->getNumRows() > 0) {
10088 $cur_emtpl = $dbo->loadResult();
10089 $cur_emtpl = empty($cur_emtpl) ? array() : json_decode($cur_emtpl, true);
10090 $cur_emtpl = is_array($cur_emtpl) ? $cur_emtpl : array();
10091 } else {
10092 VikError::raiseWarning('', 'Missing Templates Record.');
10093 $mainframe->redirect('index.php?option=com_vikbooking');
10094 exit;
10095 }
10096 if (array_key_exists($tplind, $cur_emtpl)) {
10097 unset($cur_emtpl[$tplind]);
10098 $cur_emtpl = count($cur_emtpl) > 0 ? array_values($cur_emtpl) : array();
10099 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($cur_emtpl))." WHERE `param`='customemailtpls';";
10100 $dbo->setQuery($q);
10101 $dbo->execute();
10102 }
10103 $mainframe->redirect('index.php?option=com_vikbooking&task=editorder&cid[]='.$oid.'&customemail=1');
10104 exit;
10105 }
10106
10107 public function exportcustomers() {
10108 //we do not set the menu for this view
10109
10110 VikRequest::setVar('view', VikRequest::getCmd('view', 'exportcustomers'));
10111
10112 parent::display();
10113
10114 if (VikBooking::showFooter()) {
10115 VikBookingHelper::printFooter();
10116 }
10117 }
10118
10119 public function csvexportprepare() {
10120 //modal box, so we do not set menu or footer
10121
10122 VikRequest::setVar('view', VikRequest::getCmd('view', 'csvexportprepare'));
10123
10124 parent::display();
10125 }
10126
10127 public function icsexportprepare() {
10128 //modal box, so we do not set menu or footer
10129
10130 VikRequest::setVar('view', VikRequest::getCmd('view', 'icsexportprepare'));
10131
10132 parent::display();
10133 }
10134
10135 public function bookingcheckin() {
10136 //modal box, so we do not set menu or footer
10137
10138 VikRequest::setVar('view', VikRequest::getCmd('view', 'bookingcheckin'));
10139
10140 parent::display();
10141 }
10142
10143 public function gencheckindoc() {
10144 //modal box, so we do not set menu or footer
10145
10146 VikRequest::setVar('view', VikRequest::getCmd('view', 'gencheckindoc'));
10147
10148 parent::display();
10149 }
10150
10151 public function checkversion() {
10152 //to be called via ajax
10153 $params = new stdClass;
10154 $params->version = VIKBOOKING_SOFTWARE_VERSION;
10155 $params->alias = 'com_vikbooking';
10156
10157 $result = array();
10158
10159 if (!count($result)) {
10160 $result = new stdClass;
10161 $result->status = 0;
10162 } else {
10163 $result = $result[0];
10164 }
10165
10166 echo json_encode($result);
10167 exit;
10168 }
10169
10170 public function updateprogram() {
10171 $params = new stdClass;
10172 $params->version = VIKBOOKING_SOFTWARE_VERSION;
10173 $params->alias = 'com_vikbooking';
10174
10175 $result = array();
10176
10177 if (!count($result) || !$result[0]) {
10178 if (class_exists('JEventDispatcher')) {
10179 $dispatcher = JEventDispatcher::getInstance();
10180 $result = $dispatcher->trigger('checkVersion', array(&$params));
10181 } else {
10182 $app = JFactory::getApplication();
10183 if (method_exists($app, 'triggerEvent')) {
10184 $result = $app->triggerEvent('checkVersion', array(&$params));
10185 }
10186 }
10187 }
10188
10189 if (!count($result) || !$result[0]->status || !$result[0]->response->status) {
10190 exit('Error, plugin disabled');
10191 }
10192
10193 JToolbarHelper::title(JText::translate('VBMAINTITLEUPDATEPROGRAM'));
10194
10195 VikBookingHelper::pUpdateProgram($result[0]->response);
10196 }
10197
10198 public function updateprogramlaunch() {
10199 $params = new stdClass;
10200 $params->version = VIKBOOKING_SOFTWARE_VERSION;
10201 $params->alias = 'com_vikbooking';
10202
10203 $json = new stdClass;
10204 $json->status = false;
10205
10206 echo json_encode($json);
10207 exit;
10208 }
10209
10210 public function invoke_vcm()
10211 {
10212 $app = JFactory::getApplication();
10213
10214 $oids = VikRequest::getVar('cid', []);
10215 $sync_type = VikRequest::getString('stype', 'new', 'request');
10216 $sync_type = !in_array($sync_type, ['new', 'modify', 'cancel']) ? 'new' : $sync_type;
10217 $original_booking_js = VikRequest::getString('origb', '', 'request', VIKREQUEST_ALLOWRAW);
10218 $return_url = VikRequest::getString('returl', '', 'request', VIKREQUEST_ALLOWRAW);
10219 $return_url = !empty($return_url) ? urldecode($return_url) : $return_url;
10220
10221 if (!$oids || !is_file(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
10222 $app->redirect("index.php?option=com_vikbooking&task=orders");
10223 $app->close();
10224 }
10225
10226 $result = VikBooking::getVcmInvoker()
10227 ->setOids($oids)
10228 ->setSyncType($sync_type)
10229 ->setOriginalBooking($original_booking_js, true)
10230 ->doSync();
10231
10232 if ($result === true) {
10233 $app->enqueueMessage(JText::translate('VBCHANNELMANAGERRESULTOK'));
10234 } else {
10235 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a>');
10236 }
10237
10238 if (!empty($return_url)) {
10239 $app->redirect($return_url);
10240 } else {
10241 $app->redirect("index.php?option=com_vikbooking&task=orders");
10242 }
10243
10244 $app->close();
10245 }
10246
10247 public function multiphotosupload() {
10248 jimport('joomla.filesystem.file');
10249
10250 $dbo = JFactory::getDBO();
10251 $proomid = VikRequest::getInt('roomid', '', 'request');
10252
10253 $resp = array('files' => array());
10254 $error_messages = array(
10255 1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
10256 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
10257 3 => 'The uploaded file was only partially uploaded',
10258 4 => 'No file was uploaded',
10259 6 => 'Missing a temporary folder',
10260 7 => 'Failed to write file to disk',
10261 8 => 'A PHP extension stopped the file upload',
10262 'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini',
10263 'max_file_size' => 'File is too big',
10264 'min_file_size' => 'File is too small',
10265 'accept_file_types' => 'Filetype not allowed',
10266 'max_number_of_files' => 'Maximum number of files exceeded',
10267 'max_width' => 'Image exceeds maximum width',
10268 'min_width' => 'Image requires a minimum width',
10269 'max_height' => 'Image exceeds maximum height',
10270 'min_height' => 'Image requires a minimum height',
10271 'abort' => 'File upload aborted',
10272 'image_resize' => 'Failed to resize image',
10273 'vbo_type' => 'The file type cannot be accepted',
10274 'vbo_jupload' => 'The upload has failed. Check your CMS settings and permissions',
10275 'vbo_perm' => 'Error moving the uploaded files. Check your permissions'
10276 );
10277
10278 $creativik = new vikResizer();
10279 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
10280 $bigsdest = $updpath;
10281 $thumbsdest = $updpath;
10282 $dest = $updpath;
10283 $moreimagestr = '';
10284 $cur_captions = json_encode(array());
10285
10286 $q = "SELECT `moreimgs`,`imgcaptions` FROM `#__vikbooking_rooms` WHERE `id`=".$proomid.";";
10287 $dbo->setQuery($q);
10288 $dbo->execute();
10289 if ($dbo->getNumRows() == 1) {
10290 $photo_data = $dbo->loadAssocList();
10291 $cur_captions = $photo_data[0]['imgcaptions'];
10292 $cur_photos = $photo_data[0]['moreimgs'];
10293 if (!empty($cur_photos)) {
10294 $moreimagestr .= $cur_photos;
10295 }
10296 }
10297
10298 $bulkphotos = VikRequest::getVar('bulkphotos', null, 'files', 'array');
10299
10300 if (is_array($bulkphotos) && count($bulkphotos) > 0 && array_key_exists('name', $bulkphotos) && count($bulkphotos['name']) > 0) {
10301 foreach ($bulkphotos['name'] as $updk => $photoname) {
10302 $uploaded_image = array();
10303 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($photoname)));
10304 $src = $bulkphotos['tmp_name'][$updk];
10305 $j = "";
10306 if (file_exists($dest.$filename)) {
10307 $j = rand(171, 1717);
10308 while (file_exists($dest.$j.$filename)) {
10309 $j++;
10310 }
10311 }
10312 $finaldest=$dest.$j.$filename;
10313 $is_error = false;
10314 $err_key = '';
10315 if (array_key_exists('error', $bulkphotos) && array_key_exists($updk, $bulkphotos['error']) && !empty($bulkphotos['error'][$updk])) {
10316 if (array_key_exists($bulkphotos['error'][$updk], $error_messages)) {
10317 $is_error = true;
10318 $err_key = $bulkphotos['error'][$updk];
10319 }
10320 }
10321 if (!$is_error) {
10322 $check = getimagesize($bulkphotos['tmp_name'][$updk]);
10323 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
10324 if (VikBooking::uploadFile($src, $finaldest)) {
10325 $gimg = $j.$filename;
10326 //orig img
10327 $origmod = true;
10328 VikBooking::uploadFile($finaldest, $bigsdest.'big_'.$j.$filename, true);
10329 //thumb
10330 $thumbsize = VikBooking::getThumbSize();
10331 $thumb = $creativik->proportionalImage($finaldest, $thumbsdest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
10332 if (!$thumb || !$origmod) {
10333 if (file_exists($bigsdest.'big_'.$j.$filename)) @unlink($bigsdest.'big_'.$j.$filename);
10334 if (file_exists($thumbsdest.'thumb_'.$j.$filename)) @unlink($thumbsdest.'thumb_'.$j.$filename);
10335 $is_error = true;
10336 $err_key = 'vbo_perm';
10337 } else {
10338 $moreimagestr.=$j.$filename.";;";
10339 }
10340 @unlink($finaldest);
10341 } else {
10342 $is_error = true;
10343 $err_key = 'vbo_jupload';
10344 }
10345 } else {
10346 $is_error = true;
10347 $err_key = 'vbo_type';
10348 }
10349 }
10350 $img = new stdClass();
10351 if ($is_error) {
10352 $img->name = '';
10353 $img->size = '';
10354 $img->type = '';
10355 $img->url = '';
10356 $img->error = array_key_exists($err_key, $error_messages) ? $error_messages[$err_key] : 'Generic Error for Upload';
10357 } else {
10358 $img->name = $photoname;
10359 $img->size = $bulkphotos['size'][$updk];
10360 $img->type = $bulkphotos['type'][$updk];
10361 $img->url = VBO_SITE_URI.'resources/uploads/big_'.$j.$filename;
10362 }
10363 $resp['files'][] = $img;
10364 }
10365 } else {
10366 $res = new stdClass();
10367 $res->name = '';
10368 $res->size = '';
10369 $res->type = '';
10370 $res->url = '';
10371 $res->error = 'No images received for upload';
10372 $resp['files'][] = $res;
10373 }
10374 //Update current extra images string
10375 $q = "UPDATE `#__vikbooking_rooms` SET `moreimgs`=".$dbo->quote($moreimagestr)." WHERE `id`=".$proomid.";";
10376 $dbo->setQuery($q);
10377 $dbo->execute();
10378 $resp['actmoreimgs'] = $moreimagestr;
10379 //Update current extra images uploaded
10380 $cur_thumbs = '';
10381 $morei=explode(';;', $moreimagestr);
10382 if (@count($morei) > 0) {
10383 $imgcaptions = json_decode($cur_captions, true);
10384 $usecaptions = empty($imgcaptions) || is_null($imgcaptions) || !is_array($imgcaptions) || !(count($imgcaptions) > 0) ? false : true;
10385 $cur_thumbs .= '<ul class="vbo-sortable">';
10386 foreach ($morei as $ki => $mi) {
10387 if (!empty($mi)) {
10388 $cur_thumbs .= '<li class="vbo-editroom-currentphoto">';
10389 $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>';
10390 $cur_thumbs .= '<a class="vbo-toggle-imgcaption" href="javascript: void(0);" onclick="vbOpenImgDetails(\''.$ki.'\', this)"><i class="'.VikBookingIcons::i('cog').'"></i></a>';
10391 $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>';
10392 $cur_thumbs .= '</li>';
10393 }
10394 }
10395 $cur_thumbs .= '</ul>';
10396 $cur_thumbs .= '<br clear="all"/>';
10397 }
10398 $resp['currentthumbs'] = $cur_thumbs;
10399
10400 echo json_encode($resp);
10401 exit;
10402 }
10403
10404 public function loadsmsbalance() {
10405 //to be called via ajax
10406 $html = 'Error1 [N/A]';
10407 $sms_api = VikBooking::getSMSAPIClass();
10408 if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api)) {
10409 require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
10410 $sms_obj = new VikSmsApi(array(), VikBooking::getSMSParams());
10411 if (method_exists('VikSmsApi', 'estimate')) {
10412 $array_result = $sms_obj->estimate("+393711271611", "estimate credit");
10413 if ( $array_result->errorCode != 0 ) {
10414 $html = 'Error3 ['.$array_result->errorMsg.']';
10415 } else {
10416 $html = VikBooking::getCurrencySymb().' '.$array_result->userCredit;
10417 }
10418 } else {
10419 $html = 'Error2 [N/A]';
10420 }
10421 }
10422 echo $html;
10423 exit;
10424 }
10425
10426 public function loadsmsparams() {
10427 //to be called via ajax
10428 $html = '---------';
10429 $phpfile = VikRequest::getString('phpfile', '', 'request');
10430 if (!empty($phpfile)) {
10431 $sms_api = VikBooking::getSMSAPIClass();
10432 $sms_params = $sms_api == $phpfile ? VikBooking::getSMSParams(false) : '';
10433 $html = VikBooking::displaySMSParameters($phpfile, $sms_params);
10434 }
10435 echo $html;
10436 exit;
10437 }
10438
10439 public function loadcronparams() {
10440 //to be called via ajax
10441 $html = '---------';
10442 $phpfile = VikRequest::getString('phpfile', '', 'request');
10443 if (!empty($phpfile)) {
10444 $html = VikBooking::displayCronParameters($phpfile);
10445 }
10446 echo $html;
10447 exit;
10448 }
10449
10450 public function loadpaymentparams() {
10451 //to be called via ajax
10452 $html = '<p>---------</p>';
10453 $phpfile = VikRequest::getString('phpfile', '', 'request');
10454 if (!empty($phpfile)) {
10455 $html = VikBooking::displayPaymentParameters($phpfile);
10456 }
10457 echo $html;
10458 exit;
10459 }
10460
10461 public function setbookingtag() {
10462 //to be called via ajax
10463 $dbo = JFactory::getDBO();
10464 $pidorder = VikRequest::getInt('idorder', '', 'request');
10465 $ptagkey = VikRequest::getInt('tagkey', '', 'request');
10466 if (!empty($pidorder) && $ptagkey >= 0) {
10467 $all_tags = VikBooking::loadBookingsColorTags();
10468 if (array_key_exists($ptagkey, $all_tags)) {
10469 $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `id`=".(int)$pidorder.";";
10470 $dbo->setQuery($q);
10471 $dbo->execute();
10472 if ($dbo->getNumRows() > 0) {
10473 $newcolortag = json_encode($all_tags[$ptagkey]);
10474 $q = "UPDATE `#__vikbooking_orders` SET `colortag`=".$dbo->quote($newcolortag)." WHERE `id`=".(int)$pidorder.";";
10475 $dbo->setQuery($q);
10476 $dbo->execute();
10477 $newcolortag = $all_tags[$ptagkey];
10478 $newcolortag['name'] = JText::translate($newcolortag['name']);
10479 $newcolortag['fontcolor'] = VikBooking::getBestColorContrast($newcolortag['color']);
10480 echo json_encode($newcolortag);
10481 } else {
10482 echo 'e4j.error.Booking ('.$pidorder.') not found';
10483 }
10484 } else {
10485 echo 'e4j.error.Color Tag ('.$ptagkey.') not found';
10486 }
10487 } else {
10488 echo 'e4j.error.Missing Data';
10489 }
10490 exit;
10491 }
10492
10493 public function updatereceiptnum() {
10494 //to be called via ajax
10495 $pnewnum = VikRequest::getInt('newnum', '', 'request');
10496 $pnewnotes = VikRequest::getString('newnotes', '', 'request', VIKREQUEST_ALLOWRAW);
10497 $poid = VikRequest::getInt('oid', '', 'request');
10498 if ($pnewnum > 0) {
10499 VikBooking::getNextReceiptNumber($poid, $pnewnum);
10500 VikBooking::getReceiptNotes($pnewnotes);
10501 //Booking History
10502 VikBooking::getBookingHistoryInstance()->setBid($poid)->store('BR', JText::translate('VBOFISCRECEIPTNUM').': '.$pnewnum);
10503 //
10504 echo 'e4j.ok';
10505 exit;
10506 }
10507 echo 'e4j.error';
10508 exit;
10509 }
10510
10511 /**
10512 * AJAX endpoint to check if a room ID is available on specific dates.
10513 */
10514 public function isroombookable()
10515 {
10516 $app = JFactory::getApplication();
10517 $dbo = JFactory::getDbo();
10518
10519 $prid = $app->input->getUInt('rid', 0);
10520 $pfdate = $app->input->getString('fdate', '');
10521 $ptdate = $app->input->getString('tdate', '');
10522
10523 if (empty($prid) || empty($pfdate) || empty($ptdate)) {
10524 VBOHttpDocument::getInstance($app)->close(400, 'Missing request values.');
10525 }
10526
10527 $res = [
10528 'status' => 0,
10529 'err' => '',
10530 ];
10531
10532 $room_info = VikBooking::getRoomInfo($prid);
10533 if (!$room_info) {
10534 VBOHttpDocument::getInstance($app)->close(404, 'Room not found.');
10535 }
10536
10537 $pcheckinh = 0;
10538 $pcheckinm = 0;
10539 $pcheckouth = 0;
10540 $pcheckoutm = 0;
10541 $timeopst = VikBooking::getTimeOpenStore();
10542 if (is_array($timeopst)) {
10543 $opent = VikBooking::getHoursMinutes($timeopst[0]);
10544 $closet = VikBooking::getHoursMinutes($timeopst[1]);
10545 $pcheckinh = $opent[0];
10546 $pcheckinm = $opent[1];
10547 $pcheckouth = $closet[0];
10548 $pcheckoutm = $closet[1];
10549 }
10550
10551 $from_ts = VikBooking::getDateTimestamp($pfdate, $pcheckinh, $pcheckinm);
10552 $to_ts = VikBooking::getDateTimestamp($ptdate, $pcheckouth, $pcheckoutm);
10553
10554 if (!empty($from_ts) && !empty($to_ts) && VikBooking::roomBookable($room_info['id'], $room_info['units'], $from_ts, $to_ts)) {
10555 $res['status'] = 1;
10556 } else {
10557 if (empty($from_ts) || empty($to_ts)) {
10558 $res['err'] = 'Invalid dates';
10559 } else {
10560 // not available
10561 $res['err'] = JText::sprintf('VBOBOOKADDROOMERR', $room_info['name'], $pfdate, $ptdate);
10562 }
10563 }
10564
10565 // send response to output
10566 VBOHttpDocument::getInstance($app)->json($res);
10567 }
10568
10569 public function uploadsnapshot() {
10570 $snap_base_path = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans';
10571 /**
10572 * We no longer access the uploaded file from php://input, we now retrieve it as a regular file upload.
10573 * The old snapshot collection script with Flash no longer works in 2021.
10574 *
10575 * @since 1.14 (J) - 1.4.0 (WP)
10576 */
10577 $result = null;
10578 try {
10579 $result = VikBooking::uploadFileFromRequest(VikRequest::getVar('snapshot', null, 'files', 'array'), $snap_base_path, 'png,jpg,jpeg');
10580 } catch (RuntimeException $e) {
10581 echo "e4j.error.Error " . $e->getMessage();
10582 exit;
10583 }
10584
10585 if (!is_object($result)) {
10586 echo "e4j.error.Invalid upload response";
10587 exit;
10588 }
10589
10590 echo $result->filename;
10591 exit;
10592 }
10593
10594 public function checkvcmrateschanges() {
10595 //to be called via ajax
10596 $session = JFactory::getSession();
10597 $ret = array('changesCount' => 0, 'changesData' => '');
10598 $updforvcm = $session->get('vbVcmRatesUpd', '');
10599 if (!empty($updforvcm) && is_array($updforvcm) && count($updforvcm) > 0) {
10600 $ret['changesCount'] = $updforvcm['count'];
10601 $ret['changesData'] = $updforvcm;
10602 }
10603
10604 echo json_encode($ret);
10605 exit;
10606 }
10607
10608 /**
10609 * AJAX endpoint to load the details of one or more bookings.
10610 *
10611 * @return void
10612 *
10613 * @since 1.16.0 (J) - 1.6.0 (WP) the method was refactored.
10614 */
10615 public function getbookingsinfo()
10616 {
10617 //to be called via ajax
10618 $dbo = JFactory::getDbo();
10619
10620 $booking_infos = [];
10621 $bookings = [];
10622
10623 $pidorders = VikRequest::getString('idorders', '', 'request');
10624 $psubroom = VikRequest::getString('subroom', '', 'request');
10625 $pstatus = VikRequest::getString('status', '', 'request');
10626 $pstay_date = VikRequest::getString('stay_date', '', 'request');
10627 $pidroom = VikRequest::getInt('idroom', 0, 'request');
10628 $psharedcal = VikRequest::getInt('sharedcal', 0, 'request');
10629
10630 if (!empty($pidorders)) {
10631 $bookings = explode(',', $pidorders);
10632 foreach ($bookings as $k => $v) {
10633 $v = intval(str_replace('-', '', $v));
10634 if (empty($v)) {
10635 unset($bookings[$k]);
10636 continue;
10637 }
10638 $bookings[$k] = $v;
10639 }
10640 }
10641 $bookings = array_values($bookings);
10642
10643 if (!$bookings) {
10644 /**
10645 * AJAX requests made by the page availability overview may contain empty booking IDs
10646 * due to SQL errors that only occupied the room, but could not save the booking record.
10647 * Clean up busy records where the busy relations contain empty booking IDs.
10648 *
10649 * @since 1.14 (J) - 1.4.0 (WP)
10650 */
10651 $hanging_busy_ids = [];
10652
10653 $q = "SELECT `idbusy` FROM `#__vikbooking_ordersbusy` WHERE `idorder` = 0 OR `idorder` IS NULL;";
10654 $dbo->setQuery($q);
10655 $removelist = $dbo->loadAssocList();
10656 if ($removelist) {
10657 foreach ($removelist as $hanging_busy) {
10658 $hanging_busy_id = (int)$hanging_busy['idbusy'];
10659 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
10660 array_push($hanging_busy_ids, $hanging_busy_id);
10661 }
10662 }
10663 }
10664
10665 // let's check also for ghost records that only occupy the room
10666 $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);";
10667 $dbo->setQuery($q);
10668 $removelist = $dbo->loadAssocList();
10669 if ($removelist) {
10670 foreach ($removelist as $hanging_busy) {
10671 $hanging_busy_id = (int)$hanging_busy['id'];
10672 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
10673 array_push($hanging_busy_ids, $hanging_busy_id);
10674 }
10675 }
10676 }
10677
10678 if ($hanging_busy_ids) {
10679 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id` IN (" . implode(', ', $hanging_busy_ids) . ");";
10680 $dbo->setQuery($q);
10681 $dbo->execute();
10682 }
10683 //
10684
10685 // output the error
10686 VBOHttpDocument::getInstance()->close(500, '1 - ' . JText::translate('VBOVWGETBKERRMISSDATA'));
10687 }
10688
10689 $nowdf = VikBooking::getDateFormat(true);
10690 if ($nowdf == "%d/%m/%Y") {
10691 $df = 'd/m/Y';
10692 } elseif ($nowdf == "%m/%d/%Y") {
10693 $df = 'm/d/Y';
10694 } else {
10695 $df = 'Y/m/d';
10696 }
10697 $datesep = VikBooking::getDateSeparator(true);
10698 $currencysymb = VikBooking::getCurrencySymb();
10699 $current_y = date('Y');
10700 $current_ts = time();
10701 $short_meal_enums = VBOMealplanManager::getInstance()->getShortMealPlans();
10702
10703 $query = $dbo->getQuery(true);
10704 $query->select('o.*');
10705 $query->from($dbo->qn('#__vikbooking_orders', 'o'));
10706 if (!empty($pstay_date) && !empty($pidroom) && $pstatus == 'any') {
10707 // include the requested booking IDs and the cancelled reservations for this stay date
10708 $stay_date_info = getdate(strtotime($pstay_date));
10709 $lim_ts_to = mktime(23, 59, 59, $stay_date_info['mon'], $stay_date_info['mday'], $stay_date_info['year']);
10710 $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) . '))');
10711 // exclude the pending reservations
10712 $query->where($dbo->qn('o.status') . ' IN (' . $dbo->q('confirmed') . ', ' . $dbo->q('cancelled') . ')');
10713 } else {
10714 // include only the requested booking IDs
10715 $query->where($dbo->qn('o.id') . ' IN (' . implode(', ', $bookings) . ')');
10716 }
10717 if ($pstatus != 'any') {
10718 $query->where($dbo->qn('o.status') . ' != ' . $dbo->q('cancelled'));
10719 }
10720 if (!empty($pstay_date) && $pstatus == 'any') {
10721 // sort by confirmed status before cancelled status
10722 $query->order('CASE WHEN ' . $dbo->qn('o.status') . ' = ' . $dbo->q('confirmed') . ' THEN 1 ELSE 0 END DESC');
10723 $query->order($dbo->qn('o.id') . ' ASC');
10724 }
10725 $dbo->setQuery($query);
10726 $booking_infos = $dbo->loadAssocList();
10727
10728 foreach ($booking_infos as $k => $row) {
10729 // rooms, amounts and guests information
10730 $rooms = VikBooking::loadOrdersRoomsData($row['id']);
10731 $rids_involved = [];
10732 $room_names = [];
10733 $totadults = 0;
10734 $totchildren = 0;
10735 foreach ($rooms as $rr) {
10736 $rids_involved[] = $rr['idroom'];
10737 $totadults += $rr['adults'];
10738 $totchildren += $rr['children'];
10739 $room_names[] = $rr['room_name'];
10740 if ($row['split_stay']) {
10741 // do not sum guests in case of split stay booking
10742 $totadults = $rr['adults'];
10743 $totchildren = $rr['children'];
10744 }
10745 }
10746
10747 if (!empty($pstay_date) && !empty($pidroom) && $pstatus == 'any') {
10748 // make sure we have fetched a reservation for the correct room (in case of cancellations included)
10749 if (!in_array($pidroom, $rids_involved)) {
10750 $is_out_of_scope = true;
10751 if ($psharedcal && count($bookings) === 1) {
10752 $is_out_of_scope = ($row['id'] != $bookings[0]);
10753 }
10754 if ($is_out_of_scope) {
10755 // out of scope reservation, unset it and go to the next one
10756 unset($booking_infos[$k]);
10757 continue;
10758 }
10759 }
10760 }
10761
10762 // included meal plans to be displayed in case of single-room booking
10763 $included_meals = [];
10764 $rplan_name = '';
10765 if (count($rooms) === 1) {
10766 // rate plan name and ID, if any
10767 $active_rplan_id = 0;
10768 if (!empty($rooms[0]['otarplan'])) {
10769 $rplan_name = $rooms[0]['otarplan'];
10770 } else {
10771 list($rplan_name, $active_rplan_id) = VBOMealplanManager::getInstance()->getPriceData($rooms[0]['idtar']);
10772 }
10773
10774 // find the included meals
10775 if (!empty($rooms[0]['meals'])) {
10776 // display included meals defined at room-reservation record
10777 $included_meals = VBOMealplanManager::getInstance()->roomRateIncludedMeals($rooms[0]);
10778 } else {
10779 // fetch default included meals in the selected rate plan
10780 $included_meals = $active_rplan_id ? VBOMealplanManager::getInstance()->ratePlanIncludedMeals($active_rplan_id) : [];
10781 }
10782 if (!$included_meals && empty($row['meals']) && !empty($row['idorderota']) && !empty($row['channel']) && !empty($row['custdata'])) {
10783 // attempt to fetch the included meal plans from the raw customer data or OTA reservation and room
10784 $included_meals = VBOMealplanManager::getInstance()->otaDataIncludedMeals($row, $rooms[0]);
10785 }
10786 }
10787
10788 if ($included_meals) {
10789 $short_incl_meals = [];
10790 foreach ($included_meals as $meal_enum => $meal_name) {
10791 $short_incl_meals[] = $short_meal_enums[$meal_enum];
10792 }
10793 $booking_infos[$k]['meals_included'] = $short_incl_meals;
10794 }
10795
10796 $booking_infos[$k]['rateplan_name'] = $rplan_name;
10797 $booking_infos[$k]['currency_symb'] = $currencysymb;
10798 if ($row['status'] == 'confirmed') {
10799 $booking_infos[$k]['status_lbl'] = JText::translate('VBCONFIRMED');
10800 if ($row['checkout'] < $current_ts) {
10801 $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSOUT');
10802 } elseif ($row['checkin'] < $current_ts && $row['checkout'] > $current_ts) {
10803 if ($row['checked'] == 1) {
10804 $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSIN');
10805 } elseif ($row['checked'] == -1) {
10806 $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSNOS');
10807 }
10808 }
10809 } elseif ($row['status'] == 'standby') {
10810 $booking_infos[$k]['status_lbl'] = JText::translate('VBSTANDBY');
10811 } elseif ($row['status'] == 'cancelled') {
10812 $booking_infos[$k]['status_lbl'] = JText::translate('VBCANCELLED');
10813 } else {
10814 $booking_infos[$k]['status_lbl'] = $row['status'];
10815 }
10816 $booking_infos[$k]['colortag'] = VikBooking::applyBookingColorTag($row);
10817 if ($booking_infos[$k]['colortag']) {
10818 $booking_infos[$k]['colortag']['name'] = JText::translate($booking_infos[$k]['colortag']['name']);
10819 }
10820 $booking_infos[$k]['room_names'] = implode(', ', $room_names);
10821 $booking_infos[$k]['tot_adults'] = $totadults;
10822 $booking_infos[$k]['tot_children'] = $totchildren;
10823 $booking_infos[$k]['format_tot'] = VikBooking::numberFormat($row['total']);
10824 $booking_infos[$k]['format_totpaid'] = VikBooking::numberFormat($row['totpaid']);
10825
10826 // room indexes
10827 $rindexes = [];
10828 $av_room_indexes = [];
10829 $used_indexes_map = [];
10830 $sub_units_data = [];
10831 $optindexes = [];
10832 $subroomdata = !empty($psubroom) ? explode('-', $psubroom) : array();
10833 $missing_index = false;
10834 foreach ($rooms as $kor => $or) {
10835 if ($row['status'] != "confirmed" || $row['closure'] || empty($or['params'])) {
10836 // cannot build room indexes data
10837 continue;
10838 }
10839
10840 $room_params = json_decode($or['params'], true);
10841 if (!is_array($room_params) || empty($room_params['features']) || !is_array($room_params['features'])) {
10842 // no distinctive features information
10843 continue;
10844 }
10845
10846 if (!strlen($or['roomindex'])) {
10847 // turn flag on for missing index when room does support them
10848 $missing_index = true;
10849 // build array with available room indexes
10850 $av_indexes = [];
10851 $unavailable_indexes = VikBooking::getRoomUnitNumsUnavailable($row, $or['idroom']);
10852 foreach ($room_params['features'] as $rind => $rfeatures) {
10853 if (in_array($rind, $unavailable_indexes) || (isset($used_indexes_map[$or['idroom']]) && in_array($rind, $used_indexes_map[$or['idroom']]))) {
10854 continue;
10855 }
10856 foreach ($rfeatures as $fname => $fval) {
10857 if ($fval) {
10858 $av_indexes[$rind] = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
10859 break;
10860 }
10861 }
10862 }
10863 if ($av_indexes) {
10864 // push available indexes for this room
10865 $av_room_indexes[$kor] = [
10866 'rid' => $or['idroom'],
10867 'name' => $or['room_name'],
10868 'list' => $av_indexes,
10869 ];
10870 }
10871 // do not proceed any further
10872 continue;
10873 }
10874
10875 // parse distinctive features
10876 foreach ($room_params['features'] as $rind => $rfeatures) {
10877 if ($rind != $or['roomindex']) {
10878 continue;
10879 }
10880 $ind_str = '';
10881 $ind_str_short = '';
10882 foreach ($rfeatures as $fname => $fval) {
10883 if (strlen($fval)) {
10884 $ind_str = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
10885 $ind_str_short = $fval;
10886 break;
10887 }
10888 }
10889 if (!isset($rindexes[$or['room_name']])) {
10890 $rindexes[$or['room_name']] = $ind_str;
10891 $sub_units_data[$or['room_name']] = $ind_str_short;
10892 } else {
10893 $rindexes[$or['room_name']] .= ', ' . $ind_str;
10894 $sub_units_data[$or['room_name']] .= ', ' . $ind_str_short;
10895 }
10896 break;
10897 }
10898
10899 // build options to switch sub-unit index
10900 if (count($subroomdata) && !count($optindexes) && $or['idroom'] == (int)$subroomdata[0]) {
10901 // build the options for switching the room index for this room
10902 foreach ($room_params['features'] as $rind => $rfeatures) {
10903 foreach ($rfeatures as $fname => $fval) {
10904 if (strlen((string)$fval)) {
10905 $optindexes[] = '<option value="'.$rind.'"'.($rind == (int)$subroomdata[1] ? ' selected="selected"' : '').'>#'.$rind.' - '.JText::translate($fname).': '.$fval.'</option>';
10906 break;
10907 }
10908 }
10909 }
10910 }
10911 }
10912
10913 if ($rindexes) {
10914 $booking_infos[$k]['rindexes'] = $rindexes;
10915 $booking_infos[$k]['sub_units_data'] = $sub_units_data;
10916 }
10917
10918 if ($optindexes) {
10919 $booking_infos[$k]['optindexes'] = $optindexes;
10920 }
10921
10922 if ($missing_index && $av_room_indexes) {
10923 $booking_infos[$k]['av_room_indexes'] = $av_room_indexes;
10924 }
10925
10926 // include flag for missing room index
10927 $booking_infos[$k]['missing_index'] = $missing_index;
10928
10929 // channel provenience and small logo URL
10930 $ota_logo_img = JText::translate('VBORDFROMSITE');
10931 $booking_avatar_src = null;
10932 $booking_avatar_alt = null;
10933 if (!empty($row['channel'])) {
10934 $channelparts = explode('_', $row['channel']);
10935 $otachannel = array_key_exists(1, $channelparts) && strlen($channelparts[1]) > 0 ? $channelparts[1] : ucwords($channelparts[0]);
10936 $ota_logo_img = VikBooking::getVcmChannelsLogo($row['channel']);
10937 if ($ota_logo_img === false) {
10938 $ota_logo_img = $otachannel;
10939 } else {
10940 $ota_logo_img = '<img src="'.$ota_logo_img.'" class="vbo-channelimg-small"/>';
10941 }
10942 $logo_helper = VikBooking::getVcmChannelsLogo($row['channel'], $get_istance = true);
10943 if ($logo_helper !== false) {
10944 $booking_avatar_src = $logo_helper->getSmallLogoURL();
10945 $booking_avatar_alt = $logo_helper->provenience;
10946 }
10947 }
10948 $booking_infos[$k]['channelimg'] = $ota_logo_img;
10949 $booking_infos[$k]['avatar_src'] = $booking_avatar_src;
10950 $booking_infos[$k]['avatar_alt'] = $booking_avatar_alt;
10951
10952 // Customer Details
10953 $custdata = $row['custdata'];
10954 $custdata_parts = explode("\n", $row['custdata']);
10955 if (count($custdata_parts) > 2 && strpos($custdata_parts[0], ':') !== false && strpos($custdata_parts[1], ':') !== false) {
10956 //get the first two fields
10957 $custvalues = [];
10958 foreach ($custdata_parts as $custdet) {
10959 if (strlen($custdet) < 1) {
10960 continue;
10961 }
10962 $custdet_parts = explode(':', $custdet);
10963 if (count($custdet_parts) >= 2) {
10964 unset($custdet_parts[0]);
10965 array_push($custvalues, trim(implode(':', $custdet_parts)));
10966 }
10967 if (count($custvalues) > 1) {
10968 break;
10969 }
10970 }
10971 if (count($custvalues) > 1) {
10972 $custdata = implode(' ', $custvalues);
10973 }
10974 }
10975 if (strlen($custdata) > 45) {
10976 $custdata = (function_exists('mb_substr') ? mb_substr($custdata, 0, 45, 'UTF-8') : substr($custdata, 0, 45)) . " ...";
10977 }
10978
10979 // customer record details
10980 $customer = [];
10981 $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'];
10982 $dbo->setQuery($q, 0, 1);
10983 $dbo->execute();
10984 if ($dbo->getNumRows()) {
10985 $customer = $dbo->loadAssoc();
10986 if (!empty($customer['first_name'])) {
10987 $custdata = $customer['first_name'].' '.$customer['last_name'];
10988 if (!empty($customer['country'])) {
10989 if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$customer['country'].'.png')) {
10990 $custdata .= '<img src="'.VBO_ADMIN_URI.'resources/countries/'.$customer['country'].'.png'.'" title="'.htmlspecialchars($customer['country']).'" class="vbo-country-flag vbo-country-flag-left"/>';
10991 }
10992 }
10993 }
10994 }
10995 $booking_infos[$k]['customer'] = $customer;
10996
10997 // check if a profile picture is available for the customer
10998 if (!empty($customer['pic'])) {
10999 $booking_avatar_src = strpos($customer['pic'], 'http') === 0 ? $customer['pic'] : VBO_SITE_URI . 'resources/uploads/' . $customer['pic'];
11000 $booking_avatar_alt = basename($booking_avatar_src);
11001 $booking_infos[$k]['avatar_src'] = $booking_avatar_src;
11002 $booking_infos[$k]['avatar_alt'] = $booking_avatar_alt;
11003 }
11004
11005 // whether this is a closure
11006 $booking_infos[$k]['closure'] = (int)$row['closure'];
11007 $booking_infos[$k]['closure_txt'] = $row['closure'] ? JText::translate('VBDBTEXTROOMCLOSED') : null;
11008
11009 // short customer information
11010 $custdata = JText::translate('VBDBTEXTROOMCLOSED') == $row['custdata'] ? '<span class="vbordersroomclosed">'.JText::translate('VBDBTEXTROOMCLOSED').'</span>' : $custdata;
11011 $booking_infos[$k]['cinfo'] = $custdata;
11012
11013 // formatted dates
11014 $booking_infos[$k]['ts'] = date(str_replace("/", $datesep, $df).' H:i', $row['ts']);
11015 $booking_infos[$k]['checkin'] = date(str_replace("/", $datesep, $df).' H:i', $row['checkin']);
11016 $booking_infos[$k]['checkout'] = date(str_replace("/", $datesep, $df).' H:i', $row['checkout']);
11017
11018 // short booking date, check-in, check-out date format
11019 $stay_info_in = getdate($row['checkin']);
11020 $stay_info_out = getdate($row['checkout']);
11021 $str_checkin = date('d', $row['checkin']);
11022 $str_checkin .= $stay_info_in['mon'] != $stay_info_out['mon'] ? ' ' . VikBooking::sayMonth($stay_info_in['mon'], $short = true) : '';
11023 $str_checkout = date('d', $row['checkout']) . ' ' . VikBooking::sayMonth($stay_info_out['mon'], $short = true);
11024 if ($stay_info_in['year'] != $stay_info_out['year'] || $stay_info_in['year'] != $current_y || $stay_info_out['year'] != $current_y) {
11025 $str_checkout .= ' ' . $stay_info_out['year'];
11026 }
11027 $booking_infos[$k]['checkin_short'] = $str_checkin;
11028 $booking_infos[$k]['checkout_short'] = $str_checkout;
11029 $booking_infos[$k]['book_date'] = date(str_replace("/", $datesep, $df), $row['ts']);
11030 $booking_infos[$k]['book_time'] = date('H:i', $row['ts']);
11031 }
11032
11033 if (!$booking_infos) {
11034 if (!empty($pidroom) && $psharedcal) {
11035 // when this flag is enabled, we are excluding bookings not made directly
11036 // for the given room type ID, hence we may get an empty list due to shared calendars
11037 VBOHttpDocument::getInstance()->close(500, 'No bookings made directly for this room-type.');
11038 }
11039 // output the error
11040 VBOHttpDocument::getInstance()->close(500, '2 - ' . JText::translate('VBOVWGETBKERRMISSDATA'));
11041 }
11042
11043 // output the JSON encoded response and exit
11044 VBOHttpDocument::getInstance()->json($booking_infos);
11045 }
11046
11047 /**
11048 * AJAX endpoint to switch a booking room index.
11049 *
11050 * @return void
11051 *
11052 * @since 1.18.2 (J) - 1.8.2 (WP) method refactored.
11053 * @since 1.18.7 (J) - 1.8.7 (WP) introduced history update.
11054 */
11055 public function switchRoomIndex()
11056 {
11057 if (!JSession::checkToken()) {
11058 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
11059 }
11060
11061 $app = JFactory::getApplication();
11062 $dbo = JFactory::getDbo();
11063
11064 $bid = $app->input->getInt('bid', 0);
11065 $rid = $app->input->getInt('rid', 0);
11066 $old_rindex = $app->input->getInt('old_rindex', 0);
11067 $new_rindex = $app->input->getInt('new_rindex', 0);
11068 $is_tmp_row = $app->input->getBool('is_tmp_row', false);
11069 $is_from_tmp_row = $app->input->getBool('is_from_tmp_row', false);
11070
11071 if (empty($bid) || empty($rid) || (empty($old_rindex) && !$is_from_tmp_row) || (empty($new_rindex) && !$is_tmp_row) || $new_rindex < 0) {
11072 // abort for missing or invalid room indexes
11073 VBOHttpDocument::getInstance($app)->close(200, 'e4j.error.#1 Missing Data');
11074 }
11075
11076 // fetch the current booking room record
11077 $dbo->setQuery(
11078 $dbo->getQuery(true)
11079 ->select('*')
11080 ->from($dbo->qn('#__vikbooking_ordersrooms'))
11081 ->where($dbo->qn('idorder') . ' = ' . $bid)
11082 ->where($dbo->qn('idroom') . ' = ' . $rid)
11083 ->where($dbo->qn('roomindex') . ((empty($old_rindex) || $old_rindex == -1) && $is_from_tmp_row ? ' IS NULL' : ' = ' . $old_rindex))
11084 ->order($dbo->qn('id') . ' ASC')
11085 );
11086 $roomRow = $dbo->loadAssoc();
11087
11088 if (!$roomRow) {
11089 // abort for record not found
11090 VBOHttpDocument::getInstance($app)->close(200, 'e4j.error.#2 Record not found');
11091 }
11092
11093 // load booking and booking rooms data for the history before updating
11094 $booking = VikBooking::getBookingInfoFromID($bid);
11095 $prev_booking_rooms = VikBooking::loadOrdersRoomsData($bid);
11096 $current_booking_rooms = $prev_booking_rooms;
11097 // update new room index for current booking rooms
11098 foreach ($current_booking_rooms as $k => $booking_room) {
11099 if ($booking_room['id'] == $roomRow['id']) {
11100 // update new room index
11101 $current_booking_rooms[$k]['roomindex'] = empty($new_rindex) && $is_tmp_row ? null : $new_rindex;
11102 break;
11103 }
11104 }
11105
11106 // update booking room record by switching sub-unit
11107 $dbo->setQuery(
11108 $dbo->getQuery(true)
11109 ->update($dbo->qn('#__vikbooking_ordersrooms'))
11110 ->set($dbo->qn('roomindex') . ' = ' . (empty($new_rindex) && $is_tmp_row ? 'NULL' : $new_rindex))
11111 ->where($dbo->qn('id') . ' = ' . (int) $roomRow['id'])
11112 );
11113 $dbo->execute();
11114
11115 // update history record by setting the proper bookings data
11116 $user = JFactory::getUser();
11117 VikBooking::getBookingHistoryInstance($bid)
11118 ->setPrevBooking(array_merge($booking, ['rooms_info' => $prev_booking_rooms]))
11119 ->setBookingData($booking, $current_booking_rooms)
11120 ->store(
11121 'MB',
11122 sprintf(
11123 '%s [%d → %d]',
11124 JText::translate('VBODEFAULTDISTFEATUREONE'),
11125 (int) $roomRow['roomindex'],
11126 (int) (empty($new_rindex) && $is_tmp_row ? 0 : $new_rindex)
11127 ) . " ({$user->name})"
11128 );
11129
11130 // process completed
11131 VBOHttpDocument::getInstance($app)->close(200, 'e4j.ok');
11132 }
11133
11134 public function searchcustomer()
11135 {
11136 // to be called via ajax
11137 $dbo = JFactory::getDbo();
11138
11139 $kw = VikRequest::getString('kw', '', 'request');
11140 $nopin = VikRequest::getInt('nopin', '', 'request');
11141 $email = VikRequest::getInt('email', 0, 'request');
11142 $selector = VikRequest::getString('selector', 'vbo-custsearchres-entry', 'request');
11143 $no_script = VikRequest::getInt('no_script', 0, 'request');
11144
11145 if (!strlen($kw)) {
11146 VBOHttpDocument::getInstance()->close(200, '');
11147 }
11148
11149 if ($nopin > 0) {
11150 //page all bookings
11151 $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;";
11152 } elseif ($email > 0) {
11153 // page calendar for checking if an email exists
11154 $q = "SELECT `first_name`, `last_name`, `email` FROM `#__vikbooking_customers` WHERE `email`=".$dbo->quote($kw).";";
11155 } else {
11156 //page calendar
11157 $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;";
11158 }
11159 $dbo->setQuery($q);
11160 $customers = $dbo->loadAssocList();
11161
11162 if (!$customers) {
11163 VBOHttpDocument::getInstance()->close(200, '');
11164 }
11165
11166 if ($email > 0) {
11167 VBOHttpDocument::getInstance()->json($customers[0]);
11168 }
11169
11170 $cust_old_fields = array();
11171 $cstring_search = '<div class="vbo-custsearchres-inner">' . "\n";
11172 foreach ($customers as $k => $v) {
11173 $cstring_search .= '<div class="' . $selector . '" data-custid="'.$v['id'].'" data-email="'.$v['email'].'" data-phone="'.htmlspecialchars($v['phone']).'" data-country="'.$v['country'].'" data-pin="'.$v['pin'].'" data-firstname="'.htmlspecialchars($v['first_name']).'" data-lastname="'.htmlspecialchars($v['last_name']).'">'."\n";
11174 $cstring_search .= '<span class="vbo-custsearchres-cflag">';
11175 if (!empty($v['pic'])) {
11176 $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";
11177 } elseif (is_file(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$v['country'].'.png')) {
11178 $cstring_search .= '<img src="'.VBO_ADMIN_URI.'resources/countries/'.$v['country'].'.png'.'" title="'.htmlspecialchars($v['country']).'" class="vbo-country-flag"/>'."\n";
11179 } else {
11180 $cstring_search .= '<i class="' . VikBookingIcons::i('globe') . '"></i>';
11181 }
11182 $cstring_search .= '</span>';
11183 $cstring_search .= '<span class="vbo-custsearchres-name" title="'.htmlspecialchars($v['email']).'">'.$v['first_name'].' '.$v['last_name'].'</span>'."\n";
11184 if (!($nopin > 0)) {
11185 $cstring_search .= '<span class="vbo-custsearchres-pin">'.$v['pin'].'</span>'."\n";
11186 }
11187 $cstring_search .= '</div>'."\n";
11188 if (!empty($v['cfields'])) {
11189 $oldfields = json_decode($v['cfields'], true);
11190 if (is_array($oldfields) && count($oldfields)) {
11191 $cust_old_fields[$v['id']] = $oldfields;
11192 }
11193 }
11194 }
11195 $cstring_search .= '</div>'."\n";
11196
11197 /**
11198 * Add the necessary JS code for the arrow navigation.
11199 */
11200 $cstring_search_js = '<script type="text/javascript">';
11201 $cstring_search_js .= '
11202 var vboCust = jQuery(".' . $selector . '");
11203 var vboCustSelected = null;
11204 var vboCustomerNavigationFn = (e) => {
11205 if (e.which === 40) {
11206 if (vboCustSelected) {
11207 vboCustSelected.removeClass("' . $selector . '-highligthed");
11208 next = vboCustSelected.next();
11209 if (next.length > 0) {
11210 vboCustSelected = next.addClass("' . $selector . '-highligthed");
11211 } else {
11212 vboCustSelected = vboCust.eq(0).addClass("' . $selector . '-highligthed");
11213 }
11214 } else {
11215 vboCustSelected = vboCust.eq(0).addClass("' . $selector . '-highligthed");
11216 }
11217 } else if (e.which === 38) {
11218 if (vboCustSelected) {
11219 vboCustSelected.removeClass("' . $selector . '-highligthed");
11220 next = vboCustSelected.prev();
11221 if (next.length > 0) {
11222 vboCustSelected = next.addClass("' . $selector . '-highligthed");
11223 } else {
11224 vboCustSelected = vboCust.last().addClass("' . $selector . '-highligthed");
11225 }
11226 } else {
11227 vboCustSelected = vboCust.last().addClass("' . $selector . '-highligthed");
11228 }
11229 } else if (e.which === 13) {
11230 if (vboCustSelected) {
11231 vboCustSelected.trigger("click");
11232 }
11233 }
11234 };
11235 jQuery(window).off("keydown", vboCustomerNavigationFn);
11236 jQuery(window).keydown(vboCustomerNavigationFn);
11237 document.addEventListener("vbo-search-customers-navigation-dismissed", (e) => {
11238 jQuery(window).off("keydown", vboCustomerNavigationFn);
11239 })
11240 jQuery(".' . $selector . '").off("hover");
11241 jQuery(".' . $selector . '").hover(function() {
11242 if (vboCustSelected) {
11243 vboCustSelected.removeClass("' . $selector . '-highligthed");
11244 vboCustSelected = null;
11245 }
11246 vboCustSelected = jQuery(this).addClass("' . $selector . '-highligthed");
11247 }, function() {
11248 if (vboCustSelected) {
11249 vboCustSelected.removeClass("' . $selector . '-highligthed");
11250 vboCustSelected = null;
11251 }
11252 jQuery(this).removeClass("' . $selector . '-highligthed");
11253 });';
11254 $cstring_search_js .= '</script>';
11255
11256 if (!$no_script) {
11257 // append JS
11258 $cstring_search .= $cstring_search_js;
11259 }
11260
11261 VBOHttpDocument::getInstance()->json([($nopin > 0 ? '' : $cust_old_fields), $cstring_search]);
11262 }
11263
11264 public function sharesignaturelink() {
11265 //to be called via ajax
11266 $dbo = JFactory::getDBO();
11267 $response = array(
11268 'status' => 0,
11269 'error' => 'Generic Error'
11270 );
11271 $pbid = VikRequest::getInt('bid', '', 'request');
11272 $phow = VikRequest::getString('how', '', 'request');
11273 $pto = VikRequest::getString('to', '', 'request');
11274 $pcustomer = VikRequest::getInt('customer', '', 'request');
11275 $cpin = VikBooking::getCPinIstance();
11276 $customer_info = $cpin->getCustomerByID($pcustomer);
11277 if (!empty($pbid) && !empty($phow) && !empty($pto) && count($customer_info) > 0) {
11278 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$pbid." AND `status`='confirmed' AND `checked` > 0;";
11279 $dbo->setQuery($q);
11280 $dbo->execute();
11281 if ($dbo->getNumRows() > 0) {
11282 $row = $dbo->loadAssoc();
11283
11284 $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'];
11285 if (VBOPlatformDetection::isWordPress()) {
11286 /**
11287 * @wponly Rewrite URI for front-end signature
11288 */
11289 $share_link = str_replace(JUri::root(), '', $share_link);
11290 $model = JModel::getInstance('vikbooking', 'shortcodes');
11291 $itemid = $model->all('post_id', $full = true);
11292 if (count($itemid)) {
11293 $share_link = JRoute::rewrite($share_link . "&Itemid={$itemid[0]->post_id}", false);
11294 }
11295 } else {
11296 /**
11297 * @joomlaonly
11298 */
11299 $best_menuitem_id = VikBooking::findProperItemIdType(['vikbooking', 'booking'], $row['lang']);
11300 if ($best_menuitem_id) {
11301 $share_base = str_replace(JUri::root(), '', $share_link);
11302 $share_link = VikBooking::externalroute($share_base, $xhtml = false, $best_menuitem_id);
11303 }
11304 }
11305
11306 $share_message = JText::sprintf('VBOSIGNSHAREMESSAGE', ltrim($customer_info['first_name'].' '.$customer_info['last_name']), $share_link, VikBooking::getFrontTitle());
11307 if ($phow == 'email') {
11308 $sender = VikBooking::getSenderMail();
11309 $vbo_app = VikBooking::getVboApplication();
11310 $vbo_app->sendMail($sender, $sender, $pto, $sender, JText::translate('VBOSIGNSHARESUBJECT'), $share_message, false);
11311 $response['status'] = 1;
11312 } elseif ($phow == 'sms') {
11313 $share_message = JText::sprintf('VBOSIGNSHAREMESSAGESMS', ltrim($customer_info['first_name'].' '.$customer_info['last_name']), $share_link, VikBooking::getFrontTitle());
11314 $sms_api = VikBooking::getSMSAPIClass();
11315 $sms_api_params = VikBooking::getSMSParams();
11316 if (!empty($sms_api) && file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api) && !empty($sms_api_params)) {
11317 require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
11318 $sms_obj = new VikSmsApi(array(), $sms_api_params);
11319 $response_obj = $sms_obj->sendMessage($pto, $share_message);
11320 if ($sms_obj->validateResponse($response_obj)) {
11321 $response['status'] = 1;
11322 } else {
11323 $response['error'] = $sms_obj->getLog();
11324 }
11325 } else {
11326 $response['error'] = 'No SMS Provider Configured';
11327 }
11328 } else {
11329 $response['error'] = 'Invalid Sending Method';
11330 }
11331 } else {
11332 $response['error'] = 'Invalid Booking ID';
11333 }
11334 } else {
11335 $response['error'] = 'Empty values';
11336 }
11337
11338 echo json_encode($response);
11339 exit;
11340 }
11341
11342 public function dayselectioncount() {
11343 //to be called via ajax
11344 $tsinit = VikRequest::getString('dinit', '', 'request');
11345 $tsend = VikRequest::getString('dend', '', 'request');
11346 if (strlen($tsinit) > 0 && strlen($tsend) > 0) {
11347 $ptsinit=VikBooking::getDateTimestamp($tsinit, '0', '0');
11348 $ptsend=VikBooking::getDateTimestamp($tsend, '23', '59');
11349 $diff = $ptsend - $ptsinit;
11350 if ($diff >= 172800) {
11351 $datef = VikBooking::getDateFormat(true);
11352 if ($datef=="%d/%m/%Y") {
11353 $df = 'd-m-Y';
11354 } else {
11355 $df = 'Y-m-d';
11356 }
11357 //minimum 2 days for excluding some days
11358 $daysdiff = floor($diff / 86400);
11359 $infoinit = getdate($ptsinit);
11360 $select = '';
11361 $select .= '<div style="display: inline-block;"><select name="excludeday[]" multiple="multiple" size="'.($daysdiff > 8 ? 8 : $daysdiff).'" id="vboexclusion">';
11362 for($i = 0; $i <= $daysdiff; $i++) {
11363 $ts = $i > 0 ? mktime(0, 0, 0, $infoinit['mon'], ((int)$infoinit['mday'] + $i), $infoinit['year']) : $ptsinit;
11364 $infots = getdate($ts);
11365 $optval = $infots['mon'].'-'.$infots['mday'].'-'.$infots['year'];
11366 $select .= '<option value="'.$optval.'">'.date($df, $ts).'</option>';
11367 }
11368 $select .= '</select></div>';
11369 //excluded days of the week
11370 if ($daysdiff >= 14) {
11371 $select .= '<div style="display: inline-block; margin-left: 40px;"><select name="excludewdays[]" multiple="multiple" size="8" id="excludewdays" onchange="vboExcludeWDays();">';
11372 $select .= '<optgroup label="'.JText::translate('VBOEXCLWEEKD').'">';
11373 $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>';
11374 $select .= '</optgroup>';
11375 $select .= '</select></div>';
11376 }
11377 //
11378 echo $select;
11379 } else {
11380 echo '';
11381 }
11382 } else {
11383 echo '';
11384 }
11385 exit;
11386 }
11387
11388 public function createcheckindoc()
11389 {
11390 if (!JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking')) {
11391 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
11392 }
11393
11394 $cid = VikRequest::getVar('cid', array(0));
11395 $id = $cid[0];
11396
11397 $dbo = JFactory::getDBO();
11398 $mainframe = JFactory::getApplication();
11399 $vbo_tn = VikBooking::getTranslator();
11400 $lang = JFactory::getLanguage();
11401 $ptmpl = VikRequest::getString('tmpl', '', 'request');
11402 $psignature = VikRequest::getString('signature', '', 'request', VIKREQUEST_ALLOWRAW);
11403 $ppad_width = VikRequest::getInt('pad_width', '', 'request');
11404 $ppad_ratio = VikRequest::getInt('pad_ratio', '', 'request');
11405 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$id." AND `status`='confirmed' AND `checked` > 0;";
11406 $dbo->setQuery($q);
11407 $row = $dbo->loadAssoc();
11408 if (!$row) {
11409 $mainframe->redirect('index.php');
11410 exit;
11411 }
11412 if (!empty($row['lang'])) {
11413 if ($lang->getTag() != $row['lang']) {
11414 if (VBOPlatformDetection::isWordPress()) {
11415 $lang->load('com_vikbooking', VIKBOOKING_LANG, $row['lang'], true);
11416 } else {
11417 $lang->load('com_vikbooking', JPATH_SITE, $row['lang'], true);
11418 $lang->load('com_vikbooking', JPATH_ADMINISTRATOR, $row['lang'], true);
11419 $lang->load('joomla', JPATH_SITE, $row['lang'], true);
11420 $lang->load('joomla', JPATH_ADMINISTRATOR, $row['lang'], true);
11421 }
11422 }
11423 if ($vbo_tn->getDefaultLang() != $row['lang']) {
11424 // force the translation to start because contents should be translated
11425 $vbo_tn::$force_tolang = $row['lang'];
11426 }
11427 }
11428 $customer = array();
11429 $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'].";";
11430 $dbo->setQuery($q);
11431 $dbo->execute();
11432 if ($dbo->getNumRows() > 0) {
11433 $customer = $dbo->loadAssoc();
11434 if (!empty($customer['country'])) {
11435 if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$customer['country'].'.png')) {
11436 $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"/>';
11437 }
11438 }
11439 }
11440 if (!(count($customer) > 0)) {
11441 VikError::raiseWarning('', JText::translate('VBOCHECKINERRNOCUSTOMER'));
11442 $mainframe->redirect('index.php?option=com_vikbooking&task=newcustomer&checkin=1&bid='.$row['id'].($ptmpl == 'component' ? '&tmpl=component' : ''));
11443 exit;
11444 }
11445 $customer['pax_data'] = !empty($customer['pax_data']) ? json_decode($customer['pax_data'], true) : array();
11446 //check if the signature has been submitted
11447 $signature_data = '';
11448 $cont_type = '';
11449 if (!empty($psignature)) {
11450 //check whether the format is accepted
11451 if (strpos($psignature, 'image/png') !== false || strpos($psignature, 'image/jpeg') !== false || strpos($psignature, 'image/svg') !== false) {
11452 $parts = explode(';base64,', $psignature);
11453 $cont_type_parts = explode('image/', $parts[0]);
11454 $cont_type = $cont_type_parts[1];
11455 if (!empty($parts[1])) {
11456 $signature_data = base64_decode($parts[1]);
11457 }
11458 }
11459 }
11460 if (!empty($signature_data)) {
11461 //write file
11462 $sign_fname = $row['id'].'_'.$row['sid'].'_'.$customer['id'].'.'.$cont_type;
11463 $filepath = VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'idscans' . DIRECTORY_SEPARATOR . $sign_fname;
11464 $fp = fopen($filepath, 'w+');
11465 $bytes = fwrite($fp, $signature_data);
11466 fclose($fp);
11467 if ($bytes !== false && $bytes > 0) {
11468 //update the signature in the DB
11469 $q = "UPDATE `#__vikbooking_customers_orders` SET `signature`=".$dbo->quote($sign_fname)." WHERE `idorder`=".(int)$row['id'].";";
11470 $dbo->setQuery($q);
11471 $dbo->execute();
11472 $customer['signature'] = $sign_fname;
11473 //resize image for screens with high resolution
11474 if ($ppad_ratio > 1) {
11475 $new_width = floor(($ppad_width / 2));
11476 $creativik = new vikResizer();
11477 $creativik->proportionalImage($filepath, $filepath, $new_width, $new_width);
11478 } else {
11479 /**
11480 * @wponly - trigger files mirroring
11481 */
11482 VikBookingLoader::import('update.manager');
11483 VikBookingUpdateManager::triggerUploadBackup($filepath);
11484 //
11485 }
11486 //
11487 } else {
11488 VikError::raiseWarning('', JText::translate('VBOERRSTORESIGNFILE'));
11489 }
11490 }
11491 //
11492 //generate PDF for check-in document by parsing the apposite template file
11493 $booking_rooms = array();
11494 $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'].";";
11495 $dbo->setQuery($q);
11496 $dbo->execute();
11497 if ($dbo->getNumRows() > 0) {
11498 $booking_rooms = $dbo->loadAssocList();
11499 if (!empty($row['lang'])) {
11500 $vbo_tn->translateContents($booking_rooms, '#__vikbooking_rooms', array('id' => 'idroom', 'room_name' => 'name'), array(), $row['lang']);
11501 }
11502 }
11503 if (!class_exists('TCPDF')) {
11504 require_once(VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "tcpdf" . DIRECTORY_SEPARATOR . 'tcpdf.php');
11505 }
11506 $usepdffont = is_file(VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "tcpdf" . DIRECTORY_SEPARATOR . "fonts" . DIRECTORY_SEPARATOR . "dejavusans.php") ? 'dejavusans' : 'helvetica';
11507
11508 /**
11509 * Trigger event to allow third party plugins to return a specific font name.
11510 *
11511 * @since 1.16.0 (J) - 1.6.0 (WP)
11512 */
11513 $custom_pdf_font = VBOFactory::getPlatform()->getDispatcher()->filter('onGetPdfFontNameVikBooking', [$usepdffont]);
11514 if (is_array($custom_pdf_font) && !empty($custom_pdf_font[0])) {
11515 $usepdffont = $custom_pdf_font[0];
11516 }
11517
11518 list($checkintpl, $pdfparams) = VikBooking::loadCheckinDocTmpl($row, $booking_rooms, $customer);
11519 $checkin_body = VikBooking::parseCheckinDocTemplate($checkintpl, $row, $booking_rooms, $customer);
11520
11521 // build the proper document SID for bc
11522 $doc_sid = $row['sid'] ?: $row['idorderota'] ?: '';
11523 $pdffname = $row['id'] . '_' . $doc_sid . '.pdf';
11524
11525 $pathpdf = VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "checkins" . DIRECTORY_SEPARATOR . "generated" . DIRECTORY_SEPARATOR . $pdffname;
11526 if (file_exists($pathpdf)) @unlink($pathpdf);
11527 $pdf_page_format = is_array($pdfparams['pdf_page_format']) ? $pdfparams['pdf_page_format'] : constant($pdfparams['pdf_page_format']);
11528 $pdf = new TCPDF(constant($pdfparams['pdf_page_orientation']), constant($pdfparams['pdf_unit']), $pdf_page_format, true, 'UTF-8', false);
11529 $pdf->SetTitle(JText::translate('VBOCHECKINDOCTITLE'));
11530 //Header for each page of the pdf
11531 if ($pdfparams['show_header'] == 1 && count($pdfparams['header_data']) > 0) {
11532 $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]);
11533 }
11534 //header and footer fonts
11535 $pdf->setHeaderFont(array($usepdffont, '', $pdfparams['header_font_size']));
11536 $pdf->setFooterFont(array($usepdffont, '', $pdfparams['footer_font_size']));
11537 //margins
11538 $pdf->SetMargins(constant($pdfparams['pdf_margin_left']), constant($pdfparams['pdf_margin_top']), constant($pdfparams['pdf_margin_right']));
11539 $pdf->SetHeaderMargin(constant($pdfparams['pdf_margin_header']));
11540 $pdf->SetFooterMargin(constant($pdfparams['pdf_margin_footer']));
11541 //
11542 $pdf->SetAutoPageBreak(true, constant($pdfparams['pdf_margin_bottom']));
11543 $pdf->setImageScale(constant($pdfparams['pdf_image_scale_ratio']));
11544 $pdf->SetFont($usepdffont, '', (int)$pdfparams['body_font_size']);
11545 if ($pdfparams['show_header'] == 0 || !(count($pdfparams['header_data']) > 0)) {
11546 $pdf->SetPrintHeader(false);
11547 }
11548 if ($pdfparams['show_footer'] == 0) {
11549 $pdf->SetPrintFooter(false);
11550 }
11551 $pdf->AddPage();
11552 $pdf->writeHTML($checkin_body, true, false, true, false, '');
11553 $pdf->lastPage();
11554 $pdf->Output($pathpdf, 'F');
11555 if (!file_exists($pathpdf)) {
11556 VikError::raiseWarning('', JText::translate('VBOERRGENCHECKINDOC'));
11557 } else {
11558 $q = "UPDATE `#__vikbooking_customers_orders` SET `checkindoc`=".$dbo->quote($pdffname)." WHERE `idorder`=".(int)$row['id'].";";
11559 $dbo->setQuery($q);
11560 $dbo->execute();
11561 $mainframe->enqueueMessage(JText::translate('VBOGENCHECKINDOCSUCCESS'));
11562 /**
11563 * @wponly - trigger files mirroring
11564 */
11565 VikBookingLoader::import('update.manager');
11566 VikBookingUpdateManager::triggerUploadBackup($pathpdf);
11567 //
11568 }
11569 //
11570 /**
11571 * @wponly - this task is executed via Ajax for the Modal forms listener. We cannot redirect to tmpl=component
11572 */
11573 $mainframe->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id']);
11574 exit;
11575 }
11576
11577 public function updatebookingcheckin()
11578 {
11579 if (!JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking')) {
11580 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
11581 }
11582
11583 $cid = VikRequest::getVar('cid', array(0));
11584 $id = $cid[0];
11585
11586 $dbo = JFactory::getDbo();
11587 $app = JFactory::getApplication();
11588
11589 $ptmpl = $app->input->getString('tmpl', '');
11590 $pnewtotpaid = $app->input->getFloat('newtotpaid', 0);
11591 $pguests = $app->input->get('guests', [], 'array');
11592 $pcomments = JComponentHelper::filterText($app->input->get('comments', '', 'raw'));
11593 $pcheckin_action = $app->input->getInt('checkin_action', 0);
11594 $valid_actions = array(-1, 0, 1, 2);
11595 if (!in_array($pcheckin_action, $valid_actions)) {
11596 $app->redirect('index.php');
11597 exit;
11598 }
11599 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$id." AND `status`='confirmed';";
11600 $dbo->setQuery($q);
11601 $dbo->execute();
11602 if ($dbo->getNumRows() < 1) {
11603 $app->redirect('index.php');
11604 exit;
11605 }
11606 $row = $dbo->loadAssoc();
11607 $q = "SELECT * FROM `#__vikbooking_customers_orders` WHERE `idorder`=".$row['id'].";";
11608 $dbo->setQuery($q);
11609 $dbo->execute();
11610 if ($dbo->getNumRows() < 1) {
11611 VikError::raiseWarning('', JText::translate('VBOCHECKINERRNOCUSTOMER'));
11612 $app->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id'].($ptmpl == 'component' ? '&tmpl=component' : ''));
11613 exit;
11614 }
11615 $custorder = $dbo->loadAssoc();
11616 //update checked status and new total paid
11617 $q = "UPDATE `#__vikbooking_orders` SET `checked`=".$pcheckin_action."".($pnewtotpaid > 0 ? ', `totpaid`='.$pnewtotpaid : '')." WHERE `id`=".$row['id'].";";
11618 $dbo->setQuery($q);
11619 $dbo->execute();
11620 // Booking History log for new amount paid (payment update)
11621 if ($pnewtotpaid > 0 && $pnewtotpaid > (float)$row['totpaid']) {
11622 $extra_data = new stdClass;
11623 $extra_data->amount_paid = ($pnewtotpaid - (float)$row['totpaid']);
11624 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->setExtraData($extra_data)->store('PU', JText::sprintf('VBOPREVAMOUNTPAID', VikBooking::numberFormat((float)$row['totpaid'])));
11625 }
11626 //
11627 //Booking History
11628 $hist_type = 'A';
11629 if ($pcheckin_action < 0) {
11630 $hist_type = 'Z';
11631 } elseif ($pcheckin_action == 1) {
11632 $hist_type = 'B';
11633 } elseif ($pcheckin_action == 2) {
11634 $hist_type = 'C';
11635 }
11636 $user = JFactory::getUser();
11637 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('R' . $hist_type, "({$user->name})");
11638 //
11639 //Guests Details
11640 $guests_details = array();
11641 list($pax_fields, $pax_fields_attributes) = VikBooking::getPaxFields();
11642 // grab also the fields for front-end pre check-in
11643 list($pre_pax_fields, $pre_pax_fields_attributes) = VikBooking::getPaxFields(true);
11644 //
11645 foreach ($pguests as $ind => $adults) {
11646 foreach ($adults as $aduind => $details) {
11647 foreach ($pax_fields as $key => $v) {
11648 if (isset($details[$key]) && ((is_scalar($details[$key]) && strlen($details[$key])) || !empty($details[$key]))) {
11649 if (!isset($guests_details[$ind])) {
11650 $guests_details[$ind] = array();
11651 }
11652 if (!isset($guests_details[$ind][$aduind])) {
11653 $guests_details[$ind][$aduind] = array();
11654 }
11655 $guests_details[$ind][$aduind][$key] = $details[$key];
11656 }
11657 }
11658 foreach ($pre_pax_fields as $key => $v) {
11659 if (isset($pax_fields[$key])) {
11660 // we must have parsed this back-end field already
11661 continue;
11662 }
11663 if (isset($details[$key]) && ((is_scalar($details[$key]) && strlen($details[$key])) || !empty($details[$key]))) {
11664 if (!isset($guests_details[$ind])) {
11665 $guests_details[$ind] = array();
11666 }
11667 if (!isset($guests_details[$ind][$aduind])) {
11668 $guests_details[$ind][$aduind] = array();
11669 }
11670 if (!isset($guests_details[$ind][$aduind][$key])) {
11671 $guests_details[$ind][$aduind][$key] = $details[$key];
11672 }
11673 }
11674 }
11675 }
11676 }
11677
11678 if ($guests_details) {
11679 // current pax data may contain some extra information collected via front-end pre-checkin so we need to merge them
11680 $curpaxdata = json_decode($custorder['pax_data'], true);
11681 if (is_array($curpaxdata) && $curpaxdata) {
11682 // scan new guest registration details
11683 foreach ($guests_details as $ind => $groom) {
11684 foreach ($groom as $aduind => $aduinfo) {
11685 if (isset($curpaxdata[$ind][$aduind])) {
11686 $guests_details[$ind][$aduind] = array_merge($curpaxdata[$ind][$aduind], $guests_details[$ind][$aduind]);
11687 // unset some default pax fields that were not specified now, or data cannot be deleted for guests
11688 foreach ($guests_details[$ind][$aduind] as $key => $det) {
11689 if (isset($pguests[$ind][$aduind][$key]) && empty($pguests[$ind][$aduind][$key])) {
11690 // this default pax field was specified as empty now, so we cannot merge it
11691 unset($guests_details[$ind][$aduind][$key]);
11692 }
11693 }
11694 }
11695 }
11696 }
11697
11698 /**
11699 * In order to not lose any custom registration data added through PMS reports,
11700 * we scan the previous registration data to ensure we keep them in the update.
11701 *
11702 * @since 1.16.10 (J) - 1.6.10 (WP)
11703 */
11704 foreach ($curpaxdata as $ind => $groom) {
11705 if (!isset($guests_details[$ind])) {
11706 // ignore deleted room registration
11707 continue;
11708 }
11709 foreach ($groom as $aduind => $aduinfo) {
11710 if (!isset($guests_details[$ind][$aduind]) || !is_array($aduinfo)) {
11711 // ignore deleted room-guest registration
11712 continue;
11713 }
11714 foreach ($aduinfo as $field_key => $field_val) {
11715 if (!isset($guests_details[$ind][$aduind][$field_key]) && !empty($field_val)) {
11716 // merge previous room-guest registration data
11717 $guests_details[$ind][$aduind][$field_key] = $field_val;
11718 }
11719 }
11720 }
11721 }
11722 }
11723
11724 $q = "UPDATE `#__vikbooking_customers_orders` SET `pax_data`=" . $dbo->q(json_encode($guests_details)) . " WHERE `id`=" . (int) $custorder['id'] . ";";
11725 $dbo->setQuery($q);
11726 $dbo->execute();
11727 }
11728
11729 //'checked' status comments
11730 $q = "UPDATE `#__vikbooking_customers_orders` SET `comments`=".$dbo->quote($pcomments)." WHERE `id`=".$custorder['id'].";";
11731 $dbo->setQuery($q);
11732 $dbo->execute();
11733
11734 $app->enqueueMessage(JText::translate('VBOCHECKINSTATUSUPDATED'));
11735 $app->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id'].($pcheckin_action != $row['checked'] ? '&changed=1' : '').($ptmpl == 'component' ? '&tmpl=component' : ''));
11736 exit;
11737 }
11738
11739 public function alterbooking()
11740 {
11741 $dbo = JFactory::getDbo();
11742 $app = JFactory::getApplication();
11743 $user = JFactory::getUser();
11744
11745 $response = array(
11746 'esit' => 1,
11747 'message' => '',
11748 'vcm' => '',
11749 );
11750
11751 // must be a string as it may contain a dash
11752 $pidorder = VikRequest::getString('idorder', '', 'request');
11753 $pidorder = intval(str_replace('-', '', $pidorder));
11754
11755 $poldidroom = VikRequest::getInt('oldidroom', '', 'request');
11756 $pidroom = VikRequest::getInt('idroom', 0, 'request');
11757 $pfromdate = VikRequest::getString('fromdate', '', 'request');
11758 $ptodate = VikRequest::getString('todate', '', 'request');
11759 $pdebug = VikRequest::getInt('e4j_debug', 0, 'request');
11760 if ($pdebug == 1) {
11761 echo 'e4j.error.'.print_r($app->input->post->getArray(), true);
11762 exit;
11763 }
11764
11765 $nowdf = VikBooking::getDateFormat(true);
11766 if ($nowdf == "%d/%m/%Y") {
11767 $df = 'd/m/Y';
11768 } elseif ($nowdf == "%m/%d/%Y") {
11769 $df = 'm/d/Y';
11770 } else {
11771 $df = 'Y/m/d';
11772 }
11773 $pcheckinh = 0;
11774 $pcheckinm = 0;
11775 $pcheckouth = 0;
11776 $pcheckoutm = 0;
11777 $timeopst = VikBooking::getTimeOpenStore();
11778 if (is_array($timeopst)) {
11779 $opent = VikBooking::getHoursMinutes($timeopst[0]);
11780 $closet = VikBooking::getHoursMinutes($timeopst[1]);
11781 $pcheckinh = $opent[0];
11782 $pcheckinm = $opent[1];
11783 $pcheckouth = $closet[0];
11784 $pcheckoutm = $closet[1];
11785 }
11786 $info_tsto = getdate(strtotime($ptodate));
11787 $actualtsto = mktime(0, 0, 0, $info_tsto['mon'], ($info_tsto['mday'] + 1), $info_tsto['year']);
11788 $first = VikBooking::getDateTimestamp(date($df, strtotime($pfromdate)), $pcheckinh, $pcheckinm);
11789 $second = VikBooking::getDateTimestamp(date($df, $actualtsto), $pcheckouth, $pcheckoutm);
11790 $ptodate = date('Y-m-d', $second);
11791 if (!($second > $first)) {
11792 echo 'e4j.error.1 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11793 exit;
11794 }
11795 if (!($pidorder > 0) || !($pidroom > 0) || empty($pfromdate) || empty($ptodate)) {
11796 echo 'e4j.error.2 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11797 exit;
11798 }
11799
11800 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder . " AND `status`='confirmed'";
11801 $dbo->setQuery($q, 0, 1);
11802 $dbo->execute();
11803 if (!$dbo->getNumRows()) {
11804 echo 'e4j.error.3 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11805 exit;
11806 }
11807 $ord = $dbo->loadAssoc();
11808
11809 $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;";
11810 $dbo->setQuery($q);
11811 $dbo->execute();
11812 $ordersrooms = $dbo->loadAssocList();
11813
11814 // store for VCM the current rooms before the modification
11815 $ord['rooms_info'] = $ordersrooms;
11816
11817 // package or custom rate
11818 $is_package = !empty($ord['pkg']) ? true : false;
11819 $is_cust_cost = false;
11820 foreach ($ordersrooms as $kor => $or) {
11821 if ($is_package !== true && !empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
11822 $is_cust_cost = true;
11823 break;
11824 }
11825 }
11826
11827 // availability helper
11828 $av_helper = VikBooking::getAvailabilityInstance();
11829
11830 // room stay dates in case of split stay
11831 $room_stay_dates = [];
11832 if ($ord['split_stay']) {
11833 // no need to get the transient based on booking status, as the booking must be confirmed
11834 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
11835 // immediately count the number of nights of stay for each split room
11836 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
11837 $room_stay_dates[$sps_r_k]['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
11838 }
11839 }
11840
11841 // determine if dates have changed
11842 $dates_changed = false;
11843 if (date('Y-m-d', $ord['checkin']) != $pfromdate || date('Y-m-d', $ord['checkout']) != $ptodate) {
11844 $dates_changed = true;
11845 }
11846
11847 $toswitch = array();
11848 $idbooked = array();
11849 $rooms_units = array();
11850 $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms`;";
11851 $dbo->setQuery($q);
11852 $dbo->execute();
11853 $all_rooms = $dbo->loadAssocList();
11854 foreach ($all_rooms as $rr) {
11855 $rooms_units[$rr['id']]['name'] = $rr['name'];
11856 $rooms_units[$rr['id']]['units'] = $rr['units'];
11857 }
11858
11859 // switch room
11860 if ($poldidroom != $pidroom) {
11861 foreach ($ordersrooms as $ind => $or) {
11862 if ($poldidroom == $or['idroom'] && array_key_exists($pidroom, $rooms_units)) {
11863 if (!isset($idbooked[$or['idroom']])) {
11864 $idbooked[$or['idroom']] = 0;
11865 }
11866 // $idbooked is not really needed as switch is never made for the same room id
11867 $idbooked[$or['idroom']]++;
11868 //
11869 $orkey = count($toswitch);
11870 $toswitch[$orkey]['from'] = $or['idroom'];
11871 $toswitch[$orkey]['to'] = $pidroom;
11872 $toswitch[$orkey]['record'] = $or;
11873 $toswitch[$orkey]['record_ind'] = $ind;
11874 break;
11875 }
11876 }
11877 }
11878 if (count($toswitch)) {
11879 foreach ($toswitch as $ksw => $rsw) {
11880 $plusunit = array_key_exists($rsw['to'], $idbooked) ? $idbooked[$rsw['to']] : 0;
11881 $room_checkin = $ord['checkin'];
11882 $room_checkout = $ord['checkout'];
11883 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
11884 $room_checkin = $room_stay_dates[$rsw['record_ind']]['checkin'];
11885 $room_checkout = $room_stay_dates[$rsw['record_ind']]['checkout'];
11886 }
11887 if (!VikBooking::roomBookable($rsw['to'], ($rooms_units[$rsw['to']]['units'] + $plusunit), $room_checkin, $room_checkout)) {
11888 // the room is not available
11889 unset($toswitch[$ksw]);
11890 echo 'e4j.error.'.JText::sprintf('VBSWITCHRERR', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']);
11891 exit;
11892 }
11893 }
11894 if (count($toswitch)) {
11895 //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)
11896 reset($ordersrooms);
11897 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$ordersrooms[0]['id'].";";
11898 $dbo->setQuery($q);
11899 $dbo->execute();
11900 //
11901 foreach ($toswitch as $ksw => $rsw) {
11902 // update room reservation record
11903 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idroom`=" . $rsw['to'] . ",`idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=" . $rsw['record']['id'] . ";";
11904 $dbo->setQuery($q);
11905 $dbo->execute();
11906 $response['message'] .= JText::sprintf('VBOVWALTBKSWITCHROK', $rsw['record']['name'], $rooms_units[$rsw['to']]['name'])."\n";
11907
11908 // update Notes field for this booking to keep track of the previous room that was assigned
11909 $prev_room_name = array_key_exists($rsw['from'], $rooms_units) ? $rooms_units[$rsw['from']]['name'] : '';
11910 if (!empty($prev_room_name)) {
11911 $new_notes = JText::sprintf('VBOPREVROOMMOVED', $prev_room_name, date($df.' H:i:s'))."\n".$ord['adminnotes'];
11912 $q = "UPDATE `#__vikbooking_orders` SET `adminnotes`=".$dbo->quote($new_notes)." WHERE `id`=".(int)$ord['id'].";";
11913 $dbo->setQuery($q);
11914 $dbo->execute();
11915 }
11916
11917 if ($ord['status'] == 'confirmed') {
11918 // update room record in _busy
11919 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'])) {
11920 // in case of a split stay it is fundamental to update the exact busy record ID
11921 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=" . $rsw['to'] . " WHERE `id`=" . (int)$room_stay_dates[$rsw['record_ind']]['id'];
11922 $dbo->setQuery($q);
11923 $dbo->execute();
11924 } else {
11925 // regular processing of a room ID for a reservation, no matter which one, we switch it
11926 $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;";
11927 $dbo->setQuery($q);
11928 $dbo->execute();
11929 if ($dbo->getNumRows() == 1) {
11930 $cur_busy = $dbo->loadAssocList();
11931 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=".$rsw['to']." WHERE `id`=".$cur_busy[0]['id']." AND `idroom`=".$cur_busy[0]['idroom']." LIMIT 1;";
11932 $dbo->setQuery($q);
11933 $dbo->execute();
11934 }
11935 }
11936
11937 // if automated updates enabled, keep $response['vcm'] empty
11938 // Invoke Channel Manager
11939 if (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
11940 $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>';
11941 }
11942 } elseif ($ord['status'] == 'standby') {
11943 // remove record in _tmplock
11944 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($ord['id']) . ";";
11945 $dbo->setQuery($q);
11946 $dbo->execute();
11947 }
11948 }
11949
11950 // check if sub-units should be assigned again when switching room
11951 if (!$dates_changed && !$ord['split_stay'] && VikBooking::autoRoomUnit()) {
11952 $new_order_rooms = VikBooking::loadOrdersRoomsData($ord['id']);
11953 $room_indexes_usemap = [];
11954 foreach ($new_order_rooms as $kor => $or) {
11955 $num = $kor + 1;
11956 // assign room specific unit
11957 $room_indexes = VikBooking::getRoomUnitNumsAvailable($ord, $or['idroom']);
11958 $use_ind_key = 0;
11959 if ($room_indexes) {
11960 if (!array_key_exists($or['idroom'], $room_indexes_usemap)) {
11961 $room_indexes_usemap[$or['idroom']] = $use_ind_key;
11962 } else {
11963 $use_ind_key = $room_indexes_usemap[$or['idroom']];
11964 }
11965 $q = "UPDATE `#__vikbooking_ordersrooms` SET `roomindex`=".(int)$room_indexes[$use_ind_key]." WHERE `id`=".(int)$or['id'].";";
11966 $dbo->setQuery($q);
11967 $dbo->execute();
11968 $room_indexes_usemap[$or['idroom']]++;
11969 }
11970 }
11971 }
11972
11973 // do not terminate the process when there is a switch, proceed to check the dates.
11974 }
11975 }
11976
11977 // change dates
11978 if ($dates_changed) {
11979 if ($ord['split_stay']) {
11980 // we do not allow to drag and change dates for rooms in a split stay reservation
11981 echo 'e4j.error.' . JText::sprintf('VBO_BOOK_SPLIT_STAY_CANNOTDRAG', $ord['id']);
11982 exit;
11983 }
11984
11985 // total nights of stay
11986 $daysdiff = $ord['days'];
11987
11988 // re-read ordersrooms (as rooms may have been switched)
11989 $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;";
11990 $dbo->setQuery($q);
11991 $dbo->execute();
11992 $ordersrooms = $dbo->loadAssocList();
11993
11994 $groupdays = VikBooking::getGroupDays($first, $second, $daysdiff);
11995 $opertwounits = true;
11996 $units_counter = array();
11997 foreach ($ordersrooms as $ind => $or) {
11998 if (!isset($units_counter[$or['idroom']])) {
11999 $units_counter[$or['idroom']] = -1;
12000 }
12001 $units_counter[$or['idroom']]++;
12002 }
12003
12004 foreach ($ordersrooms as $ind => $or) {
12005 $num = $ind + 1;
12006 $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'] . ";";
12007 $dbo->setQuery($check);
12008 $dbo->execute();
12009 if ($dbo->getNumRows() > 0) {
12010 $busy = $dbo->loadAssocList();
12011 foreach ($groupdays as $gday) {
12012 $bfound = 0;
12013 foreach ($busy as $bu) {
12014 if ($gday >= $bu['checkin'] && $gday <= $bu['realback']) {
12015 $bfound++;
12016 }
12017 }
12018 if ($bfound >= ($or['units'] - $units_counter[$or['idroom']]) || !VikBooking::roomNotLocked($or['idroom'], $or['units'], $first, $second)) {
12019 $opertwounits = false;
12020 break 2;
12021 }
12022 }
12023 }
12024 }
12025 if ($opertwounits !== true) {
12026 $response['esit'] = 0;
12027 $response['message'] = JText::translate('VBROOMNOTRIT')." ".date($df.' H:i', $first)." ".JText::translate('VBROOMNOTCONSTO')." ".date($df.' H:i', $second);
12028 echo json_encode($response);
12029 exit;
12030 }
12031
12032 // update dates and busy records
12033 $realback = VikBooking::getHoursRoomAvail() * 3600;
12034 $realback += $second;
12035 $q = "UPDATE `#__vikbooking_orders` SET `checkin`='".$first."', `checkout`='".$second."' WHERE `id`=".$ord['id'].";";
12036 $dbo->setQuery($q);
12037 $dbo->execute();
12038 if ($ord['status'] == 'confirmed') {
12039 $q = "SELECT `b`.`id` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`id`=`ob`.`idbusy` AND `ob`.`idorder`=".$ord['id'].";";
12040 $dbo->setQuery($q);
12041 $dbo->execute();
12042 $allbusy = $dbo->loadAssocList();
12043 foreach ($allbusy as $bb) {
12044 $q = "UPDATE `#__vikbooking_busy` SET `checkin`='".$first."', `checkout`='".$second."', `realback`='".$realback."' WHERE `id`='".$bb['id']."';";
12045 $dbo->setQuery($q);
12046 $dbo->execute();
12047 }
12048 // if automated updates enabled, keep $response['vcm'] empty
12049 // Invoke Channel Manager
12050 if (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
12051 $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>';
12052 }
12053 }
12054 $response['message'] .= JText::translate('RESUPDATED')."\n";
12055 }
12056
12057 if (count($toswitch)) {
12058 /**
12059 * Rooms have changed so the new rates must be re-calculated.
12060 * Maybe they should be calculated in any case, even if just
12061 * the dates have changed. For the moment the rates are reset.
12062 */
12063 }
12064
12065 // unset any previously booked room due to calendar sharing
12066 VikBooking::cleanSharedCalendarsBusy($ord['id']);
12067 // check if some of the rooms booked have shared calendars
12068 VikBooking::updateSharedCalendars($ord['id']);
12069 //
12070
12071 //Booking History
12072 VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($ord));
12073 //
12074
12075 $vcm_autosync = VikBooking::vcmAutoUpdate();
12076 if ($vcm_autosync > 0 && !empty($response['vcm'])) {
12077 //unset the vcm property as no buttons should be displayed when in auto-sync
12078 $response['vcm'] = '';
12079 $vcm_obj = VikBooking::getVcmInvoker();
12080 $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
12081 $sync_result = $vcm_obj->doSync();
12082 if ($sync_result === false) {
12083 $response['message'] .= JText::translate('VBCHANNELMANAGERRESULTKO')." (".$vcm_obj->getError().")\n";
12084 }
12085 }
12086
12087 // in case of error but not empty VCM message, set an error that will be displayed after the mustReload
12088 if ($response['esit'] < 1 && !empty($response['vcm'])) {
12089 VikError::raiseNotice('', $response['vcm']);
12090 }
12091
12092 $response['message'] = nl2br($response['message']);
12093 echo json_encode($response);
12094 exit;
12095 }
12096
12097 public function modroomrateplans()
12098 {
12099 $dbo = JFactory::getDbo();
12100 $session = JFactory::getSession();
12101
12102 $updforvcm = $session->get('vbVcmRatesUpd', '');
12103 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
12104
12105 $pid_room = VikRequest::getInt('id_room', '', 'request');
12106 $pid_price = VikRequest::getInt('id_price', '', 'request');
12107 $ptype = VikRequest::getString('type', '', 'request');
12108 $pfromdate = VikRequest::getString('fromdate', '', 'request');
12109 $ptodate = VikRequest::getString('todate', '', 'request');
12110
12111 if (empty($pid_room) || empty($pid_price) || empty($ptype) || empty($pfromdate) || empty($ptodate) || !(strtotime($pfromdate) > 0) || !(strtotime($ptodate) > 0)) {
12112 echo 'e4j.error.'.addslashes(JText::translate('VBRATESOVWERRMODRPLANS'));
12113 exit;
12114 }
12115
12116 $q = "SELECT * FROM `#__vikbooking_prices` WHERE `id`=".$pid_price.";";
12117 $dbo->setQuery($q);
12118 $price_record = $dbo->loadAssoc();
12119
12120 if (!$price_record) {
12121 echo 'e4j.error.'.addslashes(JText::translate('VBRATESOVWERRMODRPLANS')).'.';
12122 exit;
12123 }
12124
12125 $current_closed = array();
12126 if (!empty($price_record['closingd'])) {
12127 $current_closed = json_decode($price_record['closingd'], true);
12128 }
12129 $current_closed = !is_array($current_closed) ? array() : $current_closed;
12130
12131 $start_ts = strtotime($pfromdate);
12132 $end_ts = strtotime($ptodate);
12133 $infostart = getdate($start_ts);
12134 $all_days = array();
12135 $output = array();
12136 while ($infostart[0] > 0 && $infostart[0] <= $end_ts) {
12137 $all_days[] = date('Y-m-d', $infostart[0]);
12138 $indkey = $infostart['mday'].'-'.$infostart['mon'].'-'.$infostart['year'].'-'.$pid_price;
12139 $output[$indkey] = array();
12140 $infostart = getdate(mktime(0, 0, 0, $infostart['mon'], ($infostart['mday'] + 1), $infostart['year']));
12141 }
12142
12143 if ($ptype == 'close') {
12144 // close
12145 if (!array_key_exists($pid_room, $current_closed)) {
12146 $current_closed[$pid_room] = array();
12147 }
12148 foreach ($all_days as $daymod) {
12149 if (!in_array($daymod, $current_closed[$pid_room])) {
12150 $current_closed[$pid_room][] = $daymod;
12151 }
12152 }
12153 } else {
12154 // open
12155 if (array_key_exists($pid_room, $current_closed)) {
12156 foreach ($all_days as $daymod) {
12157 if (in_array($daymod, $current_closed[$pid_room])) {
12158 foreach ($current_closed[$pid_room] as $ck => $cv) {
12159 if ($daymod == $cv) {
12160 unset($current_closed[$pid_room][$ck]);
12161 }
12162 }
12163 }
12164 }
12165 } else {
12166 $current_closed[$pid_room] = array();
12167 }
12168 }
12169
12170 if (!$current_closed[$pid_room]) {
12171 unset($current_closed[$pid_room]);
12172 }
12173
12174 $q = "UPDATE `#__vikbooking_prices` SET `closingd`=".(count($current_closed) > 0 ? $dbo->quote(json_encode($current_closed)) : "NULL")." WHERE `id`=".(int)$pid_price.";";
12175 $dbo->setQuery($q);
12176 $dbo->execute();
12177
12178 $oldcsscls = $ptype == 'close' ? 'vbo-roverw-rplan-on' : 'vbo-roverw-rplan-off';
12179 $newcsscls = $ptype == 'close' ? 'vbo-roverw-rplan-off' : 'vbo-roverw-rplan-on';
12180
12181 foreach ($output as $ok => $ov) {
12182 $output[$ok] = array('oldcls' => $oldcsscls, 'newcls' => $newcsscls);
12183 }
12184
12185 // build new session values
12186 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
12187
12188 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
12189 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $start_ts ? $start_ts : $updforvcm['dfrom'];
12190 } else {
12191 $updforvcm['dfrom'] = $start_ts;
12192 }
12193
12194 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
12195 $updforvcm['dto'] = $updforvcm['dto'] < $end_ts ? $end_ts : $updforvcm['dto'];
12196 } else {
12197 $updforvcm['dto'] = $end_ts;
12198 }
12199
12200 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
12201 if (!in_array($pid_room, $updforvcm['rooms'])) {
12202 $updforvcm['rooms'][] = $pid_room;
12203 }
12204 } else {
12205 $updforvcm['rooms'] = array($pid_room);
12206 }
12207
12208 if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
12209 if (array_key_exists($pid_room, $updforvcm['rplans'])) {
12210 if (!in_array($pid_price, $updforvcm['rplans'][$pid_room])) {
12211 $updforvcm['rplans'][$pid_room][] = $pid_price;
12212 }
12213 } else {
12214 $updforvcm['rplans'][$pid_room] = array($pid_price);
12215 }
12216 } else {
12217 $updforvcm['rplans'] = array($pid_room => array($pid_price));
12218 }
12219
12220 /**
12221 * Rather than suggesting the administrator to manually invoke VCM to launch a Bulk Action,
12222 * we try to silently trigger an automatic bulk action before updating the session values.
12223 *
12224 * @since 1.17.1 (J) - 1.7.1 (WP)
12225 * @since 1.17.5 (J) - 1.7.5 (WP) the "rate_id" property is passed along the auto-bulk data.
12226 */
12227 $rates_aligned = false;
12228 try {
12229 if (class_exists('VikChannelManager')) {
12230 $rates_aligned = VikChannelManager::autoBulkActions([
12231 'from_date' => $pfromdate,
12232 'to_date' => $ptodate,
12233 'forced_rooms' => [$pid_room],
12234 'rate_id' => $pid_price,
12235 'update' => 'rates',
12236 ]);
12237 }
12238 } catch (Throwable $e) {
12239 // do nothing
12240 $rates_aligned = false;
12241 }
12242
12243 if (!$rates_aligned) {
12244 // update session values
12245 $session->set('vbVcmRatesUpd', $updforvcm);
12246 }
12247
12248 echo json_encode($output);
12249 exit;
12250 }
12251
12252 public function icsexportlaunch() {
12253 $dbo = JFactory::getDBO();
12254 $pcheckindate = VikRequest::getString('checkindate', '', 'request');
12255 $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
12256 $pstatus = VikRequest::getString('status', '', 'request');
12257 $validstatus = array('confirmed', 'standby', 'cancelled');
12258 $filterstatus = '';
12259 $filterfirst = 0;
12260 $filtersecond = 0;
12261 $nowdf = VikBooking::getDateFormat(true);
12262 if ($nowdf == "%d/%m/%Y") {
12263 $df = 'd/m/Y';
12264 } elseif ($nowdf == "%m/%d/%Y") {
12265 $df = 'm/d/Y';
12266 } else {
12267 $df = 'Y/m/d';
12268 }
12269 $currencyname = VikBooking::getCurrencyName();
12270 if (!empty($pstatus) && in_array($pstatus, $validstatus)) {
12271 $filterstatus = $pstatus;
12272 }
12273 if (!empty($pcheckindate)) {
12274 if (VikBooking::dateIsValid($pcheckindate)) {
12275 $first=VikBooking::getDateTimestamp($pcheckindate, '0', '0');
12276 $filterfirst = $first;
12277 }
12278 }
12279 if (!empty($pcheckoutdate)) {
12280 if (VikBooking::dateIsValid($pcheckoutdate)) {
12281 $second=VikBooking::getDateTimestamp($pcheckoutdate, '23', '59');
12282 if ($second > $first) {
12283 $filtersecond = $second;
12284 }
12285 }
12286 }
12287 $clause = array();
12288 if ($filterfirst > 0) {
12289 $clause[] = "`o`.`checkin` >= ".$filterfirst;
12290 }
12291 if ($filtersecond > 0) {
12292 $clause[] = "`o`.`checkout` <= ".$filtersecond;
12293 }
12294 if (!empty($filterstatus)) {
12295 $clause[] = "`o`.`status` = '".$filterstatus."'";
12296 }
12297 $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;";
12298 $dbo->setQuery($q);
12299 $dbo->execute();
12300 if ($dbo->getNumRows() > 0) {
12301 $orders = $dbo->loadAssocList();
12302 $icscontent = "BEGIN:VCALENDAR\n";
12303 $icscontent .= "VERSION:2.0\n";
12304 $icscontent .= "PRODID:-//e4j//VikBooking//EN\n";
12305 $icscontent .= "CALSCALE:GREGORIAN\n";
12306 $str = "";
12307 foreach ($orders as $kord => $ord) {
12308 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12309 continue;
12310 }
12311 $usecurrencyname = $currencyname;
12312 $usecurrencyname = !empty($ord['idorderota']) && !empty($ord['chcurrency']) ? $ord['chcurrency'] : $usecurrencyname;
12313 $statusstr = '';
12314 if ($ord['status'] == 'confirmed') {
12315 $statusstr = JText::translate('VBCSVSTATUSCONFIRMED');
12316 } elseif ($ord['status'] == 'standby') {
12317 $statusstr = JText::translate('VBCSVSTATUSSTANDBY');
12318 } elseif ($ord['status'] == 'cancelled') {
12319 $statusstr = JText::translate('VBCSVSTATUSCANCELLED');
12320 }
12321 $uri = JURI::root().'index.php?option=com_vikbooking&view=booking&sid='.$ord['sid'].'&ts='.$ord['ts'];
12322 /**
12323 * @wponly Rewrite URI for front-end
12324 */
12325 $uri = str_replace(JUri::root(), '', $uri);
12326 $model = JModel::getInstance('vikbooking', 'shortcodes');
12327 $itemid = $model->best('booking');
12328 if ($itemid) {
12329 $uri = JRoute::rewrite($uri . "&Itemid={$itemid}", false);
12330 }
12331 //
12332 $ordnumbstr = $ord['id'].(!empty($ord['confirmnumber']) ? ' - '.$ord['confirmnumber'] : '').(!empty($ord['idorderota']) ? ' ('.ucwords($ord['channel']).')' : '').' - '.$statusstr;
12333 $peoplestr = ($ord['adults'] + $ord['children']).($ord['children'] > 0 ? ' ('.JText::translate('VBCSVCHILDREN').': '.$ord['children'].')' : '');
12334 $totalstring = ($ord['total'] > 0 ? ($usecurrencyname.' '.VikBooking::numberFormat($ord['total'])) : '');
12335 $totalpaidstring = ($ord['totpaid'] > 0 ? (' ('.VikBooking::numberFormat($ord['totpaid']).')') : '');
12336 $description = JText::sprintf('VBICSEXPDESCRIPTION', $ordnumbstr."\\n", $peoplestr."\\n", $ord['days']."\\n", $totalstring.$totalpaidstring."\\n", "\\n".str_replace("\n", "\\n", trim($ord['custdata'])));
12337 $str .= "BEGIN:VEVENT\n";
12338 $str .= "DTEND:" . JFactory::getDate(date('Y-m-d H:i:s', $ord['checkout']), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12339 $str .= "UID:" . uniqid() . "\n";
12340 $str .= "DTSTAMP:" . JFactory::getDate(date('Y-m-d H:i:s'), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12341 $str .= ((strlen($description) > 0 ) ? "DESCRIPTION:".preg_replace('/([\,;])/','\\\$1', $description)."\n" : "");
12342 $str .= "URL;VALUE=URI:" . preg_replace('/([\,;])/','\\\$1', $uri) . "\n";
12343 $str .= "SUMMARY:" . JText::sprintf('VBICSEXPSUMMARY', date($df, $ord['checkin'])) . "\n";
12344 $str .= "DTSTART:" . JFactory::getDate(date('Y-m-d H:i:s', $ord['checkin']), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12345 $str .= "END:VEVENT\n";
12346 }
12347 $icscontent .= $str;
12348 $icscontent .= "END:VCALENDAR\n";
12349 //download file from buffer
12350 header("Content-Type: application/octet-stream; ");
12351 header("Cache-Control: no-store, no-cache");
12352 header('Content-Disposition: attachment; filename="bookings_export.ics"');
12353 $f = fopen('php://output', "w");
12354 fwrite($f, $icscontent);
12355 fclose($f);
12356 exit;
12357 } else {
12358 VikError::raiseWarning('', JText::translate('VBICSEXPNORECORDS'));
12359 $mainframe = JFactory::getApplication();
12360 $mainframe->redirect("index.php?option=com_vikbooking&task=icsexportprepare&checkindate=".$pcheckindate."&checkoutdate=".$pcheckoutdate."&status=".$pstatus."&tmpl=component");
12361 }
12362 }
12363
12364 public function csvexportlaunch()
12365 {
12366 $dbo = JFactory::getDbo();
12367 $app = JFactory::getApplication();
12368
12369 $pdatefilt = VikRequest::getString('datefilt', '', 'request');
12370 $proomfilt = VikRequest::getString('roomfilt', '', 'request');
12371 $pchfilt = VikRequest::getString('chfilt', '', 'request');
12372 $ppayfilt = VikRequest::getString('payfilt', '', 'request');
12373 $pcheckindate = VikRequest::getString('checkindate', '', 'request');
12374 $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
12375 $pstatus = VikRequest::getString('status', '', 'request');
12376 $pcatfilt = VikRequest::getInt('catfilt', 0, 'request');
12377 $pformat = VikRequest::getString('format', 'csv', 'request');
12378
12379 // let the report class (a generic one) generate the CSV file in the proper format
12380 $report_obj = VikBooking::getReportInstance('revenue')->setExportCSVFormat($pformat);
12381
12382 $validstatus = array('confirmed', 'standby', 'cancelled');
12383 $validdates = array('ts', 'checkin', 'checkout');
12384
12385 $filterdate = '';
12386 $filterstatus = '';
12387 $first = 0;
12388 $filterfirst = 0;
12389 $filtersecond = 0;
12390 $nowdf = VikBooking::getDateFormat(true);
12391 if ($nowdf == "%d/%m/%Y") {
12392 $df = 'd/m/Y';
12393 } elseif ($nowdf == "%m/%d/%Y") {
12394 $df = 'm/d/Y';
12395 } else {
12396 $df = 'Y/m/d';
12397 }
12398 $datesep = VikBooking::getDateSeparator(true);
12399 $currencyname = VikBooking::getCurrencyName();
12400
12401 if (!empty($pstatus) && in_array($pstatus, $validstatus)) {
12402 $filterstatus = $pstatus;
12403 }
12404 if (!empty($pdatefilt) && in_array($pdatefilt, $validdates)) {
12405 $filterdate = $pdatefilt;
12406 }
12407 if (!empty($pcheckindate) && !empty($filterdate)) {
12408 if (VikBooking::dateIsValid($pcheckindate)) {
12409 $first = VikBooking::getDateTimestamp($pcheckindate, '0', '0');
12410 $filterfirst = $first;
12411 }
12412 }
12413 if (!empty($pcheckoutdate) && !empty($filterdate)) {
12414 if (VikBooking::dateIsValid($pcheckoutdate)) {
12415 $second = VikBooking::getDateTimestamp($pcheckoutdate, '23', '59');
12416 if ($second > $first) {
12417 $filtersecond = $second;
12418 }
12419 }
12420 }
12421 $clause = array();
12422 if ($filterfirst > 0) {
12423 $clause[] = "`o`.`".$filterdate."` >= ".$filterfirst;
12424 }
12425 if ($filtersecond > 0) {
12426 $clause[] = "`o`.`".$filterdate."` <= ".$filtersecond;
12427 }
12428 if (!empty($filterstatus)) {
12429 $clause[] = "`o`.`status` = '".$filterstatus."'";
12430 }
12431 if (!empty($pchfilt)) {
12432 $clause[] = "`o`.`channel` LIKE ".$dbo->quote("%".$pchfilt."%");
12433 }
12434 if (!empty($ppayfilt)) {
12435 $clause[] = "`o`.`idpayment` LIKE '".$ppayfilt."=%'";
12436 }
12437 if (!empty($proomfilt)) {
12438 $clause[] = "`or`.`idroom` = '".(int)$proomfilt."'";
12439 }
12440
12441 if (!empty($pcatfilt)) {
12442 $room_cat_ids = array();
12443 $q = "SELECT `id`,`idcat` FROM `#__vikbooking_rooms` WHERE `idcat` LIKE " . $dbo->quote("%$pcatfilt%");
12444 $dbo->setQuery($q);
12445 $dbo->execute();
12446 if ($dbo->getNumRows()) {
12447 $records = $dbo->loadAssocList();
12448 foreach ($records as $rcat) {
12449 $parts = explode(';', $rcat['idcat']);
12450 if (in_array($pcatfilt, $parts)) {
12451 $room_cat_ids[] = $rcat['id'];
12452 }
12453 }
12454 }
12455 if (count($room_cat_ids)) {
12456 $clause[] = "`or`.`idroom` IN (" . implode(', ', $room_cat_ids) . ")";
12457 }
12458 }
12459
12460 $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;";
12461 $dbo->setQuery($q);
12462 $orders = $dbo->loadAssocList();
12463 if (!$orders) {
12464 $app->enqueueMessage(JText::translate('VBCSVEXPNORECORDS'), 'error');
12465 $app->redirect("index.php?option=com_vikbooking&task=csvexportprepare&checkindate=".$pcheckindate."&checkoutdate=".$pcheckoutdate."&status=".$pstatus."&tmpl=component");
12466 $app->close();
12467 }
12468
12469 // options
12470 $all_options = array();
12471 $q = "SELECT * FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
12472 $dbo->setQuery($q);
12473 $options = $dbo->loadAssocList();
12474 if ($options) {
12475 foreach ($options as $ok => $ov) {
12476 $all_options[$ov['id']] = $ov;
12477 }
12478 }
12479
12480 // build columns
12481 $columns = [
12482 [
12483 'label' => JText::translate('VBDASHBOOKINGID'),
12484 ],
12485 [
12486 'label' => JText::translate('VBPVIEWORDERSONE'),
12487 ],
12488 [
12489 'label' => JText::translate('VBCSVCHECKIN'),
12490 ],
12491 [
12492 'label' => JText::translate('VBCSVCHECKOUT'),
12493 ],
12494 [
12495 'label' => JText::translate('VBCSVNIGHTS'),
12496 ],
12497 [
12498 'label' => JText::translate('VBCSVROOM'),
12499 ],
12500 [
12501 'label' => JText::translate('VBCSVPEOPLE'),
12502 ],
12503 [
12504 'label' => JText::translate('VBCSVCUSTINFO'),
12505 ],
12506 [
12507 'label' => JText::translate('ORDER_SPREQUESTS'),
12508 ],
12509 [
12510 'label' => JText::translate('ORDER_NOTES'),
12511 ],
12512 [
12513 'label' => JText::translate('VBCSVCREATEDBY'),
12514 ],
12515 [
12516 'label' => JText::translate('VBCSVCUSTMAIL'),
12517 ],
12518 [
12519 'label' => JText::translate('ORDER_PHONE'),
12520 ],
12521 [
12522 'label' => JText::translate('VBCSVOPTIONS'),
12523 ],
12524 [
12525 'label' => JText::translate('VBCSVPAYMENTMETHOD'),
12526 ],
12527 [
12528 'label' => JText::translate('VBCSVORDIDCONFNUMB'),
12529 ],
12530 [
12531 'label' => JText::translate('VBOCHANNEL'),
12532 ],
12533 [
12534 'label' => JText::translate('VBCSVEXPFILTBSTATUS'),
12535 ],
12536 [
12537 'label' => JText::translate('VBCSVTOTAL'),
12538 ],
12539 [
12540 'label' => JText::translate('VBCSVTOTPAID'),
12541 ],
12542 [
12543 'label' => JText::translate('VBCSVTOTTAXES'),
12544 ],
12545 ];
12546
12547 // booking cancellation details
12548 $cancellation_timestamps = [];
12549
12550 if (empty($filterstatus) || $filterstatus === 'cancelled') {
12551 // insert column for cancellation date at index 2
12552 array_splice($columns, 2, 0, [['label' => JText::translate('VBO_CANC_DATE')]]);
12553 // gather all cancelled bookings, if any
12554 $cancellation_ids = [];
12555 foreach ($orders as $order) {
12556 if ($order['status'] === 'cancelled' && !in_array($order['id'], $cancellation_ids)) {
12557 $cancellation_ids[] = $order['id'];
12558 }
12559 }
12560 if ($cancellation_ids && $cancHistoryEvents = VikBooking::getBookingHistoryInstance(0)->getBookingEventsType('cancelled')) {
12561 // list of booking IDs with cancellation events processed
12562 $cancBidsProcessed = [];
12563
12564 // query the database to fetch the needed history records
12565 $dbo->setQuery(
12566 $dbo->getQuery(true)
12567 ->select([
12568 $dbo->qn('idorder'),
12569 $dbo->qn('dt'),
12570 ])
12571 ->from($dbo->qn('#__vikbooking_orderhistory'))
12572 ->where($dbo->qn('idorder') . ' IN (' . implode(', ', array_map('intval', $cancellation_ids)) . ')')
12573 ->where($dbo->qn('type') . ' IN (' . implode(', ', array_map([$dbo, 'q'], $cancHistoryEvents)) . ')')
12574 ->order($dbo->qn('idorder') . ' ASC')
12575 ->order($dbo->qn('dt') . ' ASC')
12576 );
12577
12578 // scan all booking cancellation records
12579 foreach ($dbo->loadAssocList() as $cancRecord) {
12580 if (!($cancBidsProcessed[$cancRecord['idorder']] ?? 0)) {
12581 // turn flag on to process this booking only once and get the earliest (first) cancellation
12582 $cancBidsProcessed[$cancRecord['idorder']] = 1;
12583
12584 // convert the cancellation date from UTC to local timezone and set booking cancellation timestamp
12585 $cancellation_timestamps[$cancRecord['idorder']] = JHtml::fetch('date', $cancRecord['dt'], 'U');
12586 }
12587 }
12588 }
12589 }
12590
12591 // set CSV columns
12592 $report_obj->setReportCols($columns);
12593
12594 // prepare the container for the CSV rows
12595 $orderscsv = [];
12596
12597 // availability helper
12598 $av_helper = VikBooking::getAvailabilityInstance();
12599
12600 $room_inds = [];
12601 $room_stay_dates = [];
12602 foreach ($orders as $kord => $ord) {
12603 // room index in this booking
12604 if (!isset($room_inds[$ord['id']])) {
12605 $room_inds[$ord['id']] = -1;
12606 }
12607 $room_inds[$ord['id']]++;
12608
12609 /**
12610 * Split stay reservation.
12611 *
12612 * @since 1.16.0 (J) - 1.6.0 (WP)
12613 */
12614 $room_stay_dates = $room_inds[$ord['id']] > 0 ? $room_stay_dates : [];
12615 if ($ord['split_stay']) {
12616 if ($ord['status'] == 'confirmed') {
12617 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
12618 } else {
12619 $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $ord['id'], []);
12620 }
12621 // immediately count the number of nights of stay for each split room
12622 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
12623 if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
12624 // overwrite values for compatibility with non-confirmed bookings
12625 $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
12626 $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
12627 }
12628 $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
12629 // overwrite the whole array
12630 $room_stay_dates[$sps_r_k] = $sps_r_v;
12631 }
12632 }
12633
12634 // determine nights and dates for this room booking
12635 $booking_nights = $ord['days'];
12636 $booking_checkin = $ord['checkin'];
12637 $booking_checkout = $ord['checkout'];
12638 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']) {
12639 $booking_nights = $room_stay_dates[$room_inds[$ord['id']]]['nights'];
12640 $booking_checkin = $room_stay_dates[$room_inds[$ord['id']]]['checkin'];
12641 $booking_checkout = $room_stay_dates[$room_inds[$ord['id']]]['checkout'];
12642 }
12643
12644 $usecurrencyname = $currencyname;
12645 $usecurrencyname = !empty($ord['idorderota']) && !empty($ord['chcurrency']) ? $ord['chcurrency'] : $usecurrencyname;
12646 $peoplestr = ($ord['adults'] + $ord['children']).($ord['children'] > 0 ? ' ('.JText::translate('VBCSVCHILDREN').': '.$ord['children'].')' : '');
12647 $custinfostr = str_replace(",", " ", $ord['custdata']);
12648 $customer = VikBooking::getCPinIstance()->getCustomerFromBooking($ord['id']);
12649 if (count($customer)) {
12650 $custinfostr = $customer['first_name'] . ' ' . $customer['last_name'];
12651 }
12652 $special_requests = '';
12653 if (preg_match("/(?:special requests:\s*)(.*?)$/is", $ord['custdata'], $match)) {
12654 $special_requests = $match[1];
12655 } elseif (preg_match("/(?:special request:\s*)(.*?)$/is", $ord['custdata'], $match)) {
12656 $special_requests = $match[1];
12657 } elseif (preg_match("/(?:special request\s*)(.*?)$/is", $ord['custdata'], $match)) {
12658 $special_requests = $match[1];
12659 } elseif (preg_match("/(?:" . JText::translate('ORDER_SPREQUESTS') . ":\s*)(.*?)$/is", $ord['custdata'], $match)) {
12660 $special_requests = $match[1];
12661 }
12662 $paystr = '';
12663 if (!empty($ord['idpayment'])) {
12664 $payparts = explode('=', $ord['idpayment']);
12665 $paystr = $payparts[1];
12666 }
12667 $ordnumbstr = $ord['id'] . ' - ' . $ord['confirmnumber'] . (!empty($ord['idorderota']) ? ' (' . $ord['idorderota'] . ')' : '');
12668 $bookingSource = JText::translate('VBORDFROMSITE');
12669 if (!empty($ord['channel']) && !empty($ord['idorderota'])) {
12670 $chparts = explode('_', $ord['channel']);
12671 $bookingSource = ($chparts[1] ?? '') ?: $chparts[0];
12672 }
12673 $statusstr = '';
12674 if ($ord['status'] == 'confirmed') {
12675 $statusstr = JText::translate('VBCSVSTATUSCONFIRMED');
12676 } elseif ($ord['status'] == 'standby') {
12677 $statusstr = JText::translate('VBCSVSTATUSSTANDBY');
12678 } elseif ($ord['status'] == 'cancelled') {
12679 $statusstr = JText::translate('VBCSVSTATUSCANCELLED');
12680 }
12681 $totalstring = $usecurrencyname . ' ' . VikBooking::numberFormat($ord['total']);
12682 if ($ord['roomsnum'] > 1) {
12683 // take the cost for the individual room
12684 $totalstring = !empty($ord['cust_cost']) && $ord['cust_cost'] > 0 ? ($usecurrencyname . ' ' . VikBooking::numberFormat($ord['cust_cost'])) : ($usecurrencyname . ' ' . VikBooking::numberFormat($ord['room_cost']));
12685 }
12686 $totalpaidstring = $usecurrencyname . ' ' . VikBooking::numberFormat($ord['totpaid']);
12687 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12688 // total paid will be printed only for the last room booked
12689 $totalpaidstring = '';
12690 }
12691 $options_str = '';
12692 if (!empty($ord['optionals'])) {
12693 $stepo = explode(";", $ord['optionals']);
12694 foreach ($stepo as $roptkey => $oo) {
12695 if (!empty($oo)) {
12696 $stept = explode(":", $oo);
12697 if (array_key_exists($stept[0], $all_options)) {
12698 $actopt = $all_options[$stept[0]];
12699 $optpcent = false;
12700 if (!empty($actopt['ageintervals']) && $ord['children'] > 0 && strstr($stept[1], '-') != false) {
12701 $optagenames = VikBooking::getOptionIntervalsAges($actopt['ageintervals']);
12702 $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt['ageintervals']);
12703 $optageovrct = VikBooking::getOptionIntervalChildOverrides($actopt, $ord['adults'], $ord['children']);
12704 $child_num = VikBooking::getRoomOptionChildNumber($ord['optionals'], $actopt['id'], $roptkey, $ord['children']);
12705 $optagecosts = VikBooking::getOptionIntervalsCosts(isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $actopt['ageintervals']);
12706 $agestept = explode('-', $stept[1]);
12707 $stept[1] = $agestept[0];
12708 $chvar = $agestept[1];
12709 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] > 0) {
12710 $optpcent = true;
12711 }
12712 $actopt['chageintv'] = $chvar;
12713 if (isset($optagenames[($chvar - 1)])) {
12714 $actopt['name'] .= ' ('.$optagenames[($chvar - 1)].')';
12715 }
12716 if (isset($optagecosts[($chvar - 1)])) {
12717 $realcost = (intval($actopt['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $booking_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
12718 } else {
12719 $realcost = 0;
12720 }
12721 } else {
12722 // VBO 1.11 - options percentage cost of the room total fee
12723 $optpcent = (int)$actopt['pcentroom'] ? true : $optpcent;
12724 //
12725 $realcost = (intval($actopt['perday']) == 1 ? ($actopt['cost'] * $booking_nights * $stept[1]) : ($actopt['cost'] * $stept[1]));
12726 }
12727 if ($actopt['maxprice'] > 0 && $realcost > $actopt['maxprice']) {
12728 $realcost=$actopt['maxprice'];
12729 if (intval($actopt['hmany']) == 1 && intval($stept[1]) > 1) {
12730 $realcost = $actopt['maxprice'] * $stept[1];
12731 }
12732 }
12733 $realcost = $actopt['perperson'] == 1 ? ($realcost * $ord['adults']) : $realcost;
12734
12735 /**
12736 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
12737 *
12738 * @since 1.17.7 (J) - 1.7.7 (WP)
12739 */
12740 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt, $ord, $ord]);
12741 if ($custom_calculation) {
12742 $realcost = (float) $custom_calculation[0];
12743 }
12744
12745 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt['idiva']);
12746 $options_str .= ($stept[1] > 1 ? $stept[1]." " : "").$actopt['name'].": ".(!$optpcent ? $currencyname : '')." ".VikBooking::numberFormat($tmpopr).($optpcent ? ' %' : '')." \r\n";
12747 }
12748 }
12749 }
12750 }
12751
12752 // custom extra costs
12753 if (!empty($ord['extracosts'])) {
12754 $cur_extra_costs = json_decode($ord['extracosts'], true);
12755 foreach ($cur_extra_costs as $eck => $ecv) {
12756 $ecplustax = !empty($ecv['idtax']) ? VikBooking::sayOptionalsPlusIva($ecv['cost'], $ecv['idtax']) : $ecv['cost'];
12757 $options_str .= $ecv['name'].": ".$currencyname." ".VikBooking::numberFormat($ecplustax)." \r\n";
12758 }
12759 }
12760
12761 // taxes
12762 $taxes_str = '';
12763 if ($ord['tot_taxes'] > 0.00) {
12764 $taxes_str .= $usecurrencyname.' '.VikBooking::numberFormat($ord['tot_taxes']);
12765 if (!empty($ord['aliq']) && !empty($ord['breakdown'])) {
12766 $tax_breakdown = json_decode($ord['breakdown'], true);
12767 $tax_breakdown = is_array($tax_breakdown) && count($tax_breakdown) > 0 ? $tax_breakdown : array();
12768 if (count($tax_breakdown)) {
12769 foreach ($tax_breakdown as $tbkk => $tbkv) {
12770 $tax_break_cost = $ord['tot_taxes'] * floatval($tbkv['aliq']) / $ord['aliq'];
12771 $taxes_str .= "\r\n".$tbkv['name'].": ".$usecurrencyname.' '.VikBooking::numberFormat($tax_break_cost);
12772 }
12773 }
12774 }
12775 }
12776 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12777 // total taxes will be printed only for the last room booked
12778 $taxes_str = '';
12779 }
12780
12781 // created by
12782 $created_by = '';
12783 if (!empty($ord['ujid'])) {
12784 $creator = new JUser($ord['ujid']);
12785 if (property_exists($creator, 'name')) {
12786 $created_by = $creator->name.' ('.$creator->username.')';
12787 }
12788 }
12789 if (empty($created_by) && !empty($ord['t_first_name'])) {
12790 $created_by = $ord['t_first_name'].' '.$ord['t_last_name'];
12791 }
12792
12793 // build CSV line data
12794 $line_data = [
12795 [
12796 'value' => $ord['id'],
12797 ],
12798 [
12799 'value' => date(str_replace("/", $datesep, $df), $ord['ts']),
12800 ],
12801 [
12802 'value' => date(str_replace("/", $datesep, $df), $booking_checkin),
12803 ],
12804 [
12805 'value' => date(str_replace("/", $datesep, $df), $booking_checkout),
12806 ],
12807 [
12808 'value' => $booking_nights,
12809 ],
12810 [
12811 'value' => $ord['name'],
12812 ],
12813 [
12814 'value' => $peoplestr,
12815 ],
12816 [
12817 'value' => $custinfostr,
12818 ],
12819 [
12820 'value' => $special_requests,
12821 ],
12822 [
12823 'value' => $ord['adminnotes'],
12824 ],
12825 [
12826 'value' => $created_by,
12827 ],
12828 [
12829 'value' => $ord['custmail'],
12830 ],
12831 [
12832 'value' => $ord['phone'],
12833 ],
12834 [
12835 'value' => $options_str,
12836 ],
12837 [
12838 'value' => $paystr,
12839 ],
12840 [
12841 'value' => $ordnumbstr,
12842 ],
12843 [
12844 'value' => $bookingSource,
12845 ],
12846 [
12847 'value' => $statusstr,
12848 ],
12849 [
12850 'value' => $totalstring,
12851 ],
12852 [
12853 'value' => $totalpaidstring,
12854 ],
12855 [
12856 'value' => $taxes_str,
12857 ],
12858 ];
12859
12860 if (empty($filterstatus) || $filterstatus === 'cancelled') {
12861 // obtain cancellation date for this booking
12862 $booking_canc_date = $cancellation_timestamps[$ord['id']] ?? '';
12863 if ($booking_canc_date) {
12864 $booking_canc_date = date(str_replace("/", $datesep, $df), $booking_canc_date);
12865 }
12866 // insert column for cancellation date at index 2
12867 array_splice($line_data, 2, 0, [['value' => $booking_canc_date]]);
12868 }
12869
12870 // push line for export
12871 $orderscsv[] = $line_data;
12872 }
12873
12874 // set CSV rows
12875 $report_obj->setReportRows($orderscsv);
12876
12877 // build lines to export
12878 $csvlines = $report_obj->getExportCSVLines($no_data = true);
12879
12880 // set export file name
12881 $report_obj->setExportCSVFileName('bookings_export_' . date('Y-m-d') . '.csv');
12882
12883 // force the download of the CSV file
12884 $report_obj->outputHeaders();
12885
12886 // send lines to output
12887 $report_obj->outputCSV($csvlines);
12888
12889 exit;
12890 }
12891
12892 public function exportcustomerslaunch() {
12893 $cid = VikRequest::getVar('cid', array(0));
12894 $dbo = JFactory::getDBO();
12895 $pnotes = VikRequest::getInt('notes', '', 'request');
12896 $pscanimg = VikRequest::getInt('scanimg', '', 'request');
12897 $ppin = VikRequest::getInt('pin', '', 'request');
12898 $pcountry = VikRequest::getString('country', '', 'request');
12899 $pfromdate = VikRequest::getString('fromdate', '', 'request');
12900 $ptodate = VikRequest::getString('todate', '', 'request');
12901 $pdatefilt = VikRequest::getInt('datefilt', '', 'request');
12902 $clauses = array();
12903 if (count($cid) > 0 && !empty($cid[0])) {
12904 $clauses[] = "`c`.`id` IN (".implode(', ', $cid).")";
12905 }
12906 if (!empty($pcountry)) {
12907 $clauses[] = "`c`.`country`=".$dbo->quote($pcountry);
12908 }
12909 $datescol = '`bk`.`ts`';
12910 if ($pdatefilt > 0) {
12911 if ($pdatefilt == 1) {
12912 $datescol = '`bk`.`ts`';
12913 } elseif ($pdatefilt == 2) {
12914 $datescol = '`bk`.`checkin`';
12915 } elseif ($pdatefilt == 3) {
12916 $datescol = '`bk`.`checkout`';
12917 }
12918 }
12919 if (!empty($pfromdate)) {
12920 $from_ts = VikBooking::getDateTimestamp($pfromdate, 0, 0);
12921 $clauses[] = $datescol.">=".$from_ts;
12922 }
12923 if (!empty($ptodate)) {
12924 $to_ts = VikBooking::getDateTimestamp($ptodate, 23, 59);
12925 $clauses[] = $datescol."<=".$to_ts;
12926 }
12927 //this query below is safe with the error #1055 when sql_mode=only_full_group_by
12928 $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`,".
12929 "(SELECT COUNT(*) FROM `#__vikbooking_customers_orders` AS `co` WHERE `co`.`idcustomer`=`c`.`id`) AS `tot_bookings`,".
12930 "`cy`.`country_3_code`,`cy`.`country_name` ".
12931 "FROM `#__vikbooking_customers` AS `c` LEFT JOIN `#__vikbooking_countries` `cy` ON `cy`.`country_3_code`=`c`.`country` ".
12932 "LEFT JOIN `#__vikbooking_customers_orders` `co` ON `co`.`idcustomer`=`c`.`id` ".
12933 "LEFT JOIN `#__vikbooking_orders` `bk` ON `bk`.`id`=`co`.`idorder`".
12934 (count($clauses) > 0 ? " WHERE ".implode(' AND ', $clauses) : "")."
12935 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` ".
12936 "ORDER BY `c`.`last_name` ASC;";
12937 $dbo->setQuery($q);
12938 $customers = $dbo->loadAssocList();
12939 if (!$customers) {
12940 VikError::raiseWarning('', JText::translate('VBONORECORDSCSVCUSTOMERS'));
12941 $mainframe = JFactory::getApplication();
12942 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
12943 exit;
12944 }
12945 $csvlines = [];
12946 $csvheadline = [
12947 'ID',
12948 JText::translate('VBCUSTOMERLASTNAME'),
12949 JText::translate('VBCUSTOMERFIRSTNAME'),
12950 JText::translate('VBCUSTOMEREMAIL'),
12951 JText::translate('VBCUSTOMERPHONE'),
12952 JText::translate('VBCUSTOMERADDRESS'),
12953 JText::translate('VBCUSTOMERCITY'),
12954 JText::translate('VBCUSTOMERZIP'),
12955 JText::translate('VBCUSTOMERCOUNTRY'),
12956 JText::translate('VBCUSTOMERGENDER'),
12957 JText::translate('ORDER_DBIRTH'),
12958 JText::translate('VBCUSTOMERTOTBOOKINGS'),
12959 ];
12960 if ($ppin > 0) {
12961 $csvheadline[] = JText::translate('VBCUSTOMERPIN');
12962 }
12963 if ($pscanimg > 0) {
12964 $csvheadline[] = JText::translate('VBCUSTOMERDOCTYPE');
12965 $csvheadline[] = JText::translate('VBCUSTOMERDOCNUM');
12966 $csvheadline[] = JText::translate('VBCUSTOMERDOCIMG');
12967 }
12968 if ($pnotes > 0) {
12969 $csvheadline[] = JText::translate('VBCUSTOMERNOTES');
12970 }
12971 $csvlines[] = $csvheadline;
12972 foreach ($customers as $customer) {
12973 $csvcustomerline = [
12974 $customer['id'],
12975 $customer['last_name'],
12976 $customer['first_name'],
12977 $customer['email'],
12978 $customer['phone'],
12979 $customer['address'],
12980 $customer['city'],
12981 $customer['zip'],
12982 $customer['country_name'],
12983 $customer['gender'],
12984 $customer['bdate'],
12985 $customer['tot_bookings'],
12986 ];
12987 if ($ppin > 0) {
12988 $csvcustomerline[] = $customer['pin'];
12989 }
12990 if ($pscanimg > 0) {
12991 $csvcustomerline[] = $customer['doctype'];
12992 $csvcustomerline[] = $customer['docnum'];
12993 $csvcustomerline[] = (!empty($customer['docimg']) ? VBO_ADMIN_URI.'resources/idscans/'.$customer['docimg'] : '');
12994 }
12995 if ($pnotes > 0) {
12996 $csvcustomerline[] = $customer['notes'];
12997 }
12998 $csvlines[] = $csvcustomerline;
12999 }
13000 header("Content-type: text/csv");
13001 header("Cache-Control: no-store, no-cache");
13002 header('Content-Disposition: attachment; filename="customers_export_'.(!empty($pcountry) ? strtolower($pcountry).'_' : '').date('Y-m-d').'.csv"');
13003 $outstream = fopen("php://output", 'w');
13004 foreach ($csvlines as $csvline) {
13005 fputcsv($outstream, $csvline, $separator = ',', $enclosure = '"', $escape = '');
13006 }
13007 fclose($outstream);
13008 exit;
13009 }
13010
13011 public function renewsession() {
13012 /*
13013 * @wponly
13014 * We just destroy the session
13015 */
13016 JSessionHandler::destroy();
13017 $mainframe = JFactory::getApplication();
13018 $mainframe->redirect("index.php?option=com_vikbooking&task=config");
13019 }
13020
13021 public function trackings() {
13022 VikBookingHelper::printHeader("trackings");
13023
13024 VikRequest::setVar('view', VikRequest::getCmd('view', 'trackings'));
13025
13026 parent::display();
13027
13028 if (VikBooking::showFooter()) {
13029 VikBookingHelper::printFooter();
13030 }
13031 }
13032
13033 public function trkconfig() {
13034 VikBookingHelper::printHeader("trackings");
13035
13036 VikRequest::setVar('view', VikRequest::getCmd('view', 'trkconfig'));
13037
13038 parent::display();
13039
13040 if (VikBooking::showFooter()) {
13041 VikBookingHelper::printFooter();
13042 }
13043 }
13044
13045 public function savetrkconfigstay() {
13046 if (!JSession::checkToken()) {
13047 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13048 }
13049 $this->do_savetrkconfig(true);
13050 }
13051
13052 public function savetrkconfig() {
13053 if (!JSession::checkToken()) {
13054 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13055 }
13056 $this->do_savetrkconfig();
13057 }
13058
13059 private function do_savetrkconfig($stay = false) {
13060 $dbo = JFactory::getDBO();
13061 $trkenabled = VikRequest::getInt('trkenabled', 0, 'request');
13062 $trkenabled = $trkenabled == 1 ? 1 : 0;
13063 $trkcookierfrdur = VikRequest::getFloat('trkcookierfrdur', 1, 'request');
13064 $trkcookierfrdur = $trkcookierfrdur < 0.1 ? 1 : $trkcookierfrdur;
13065 $trkcampname = VikRequest::getVar('trkcampname', array());
13066 $trkcampkey = VikRequest::getVar('trkcampkey', array());
13067 $trkcampval = VikRequest::getVar('trkcampval', array());
13068 $trkcampaigns = array();
13069 foreach ($trkcampname as $k => $v) {
13070 if (empty($trkcampkey[$k])) {
13071 continue;
13072 }
13073 $trkcampkey[$k] = str_replace(' ', '', trim($trkcampkey[$k]));
13074 $name = !empty($v) ? $v : date('Y-m-d').' '.(count($trkcampaigns) + 1);
13075 $trkcampaigns[$trkcampkey[$k]] = array(
13076 'key' => $trkcampkey[$k],
13077 'value' => $trkcampval[$k],
13078 'name' => $name,
13079 );
13080 }
13081
13082 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($trkenabled)." WHERE `param`='trkenabled';";
13083 $dbo->setQuery($q);
13084 $dbo->execute();
13085 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($trkcookierfrdur)." WHERE `param`='trkcookierfrdur';";
13086 $dbo->setQuery($q);
13087 $dbo->execute();
13088 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($trkcampaigns))." WHERE `param`='trkcampaigns';";
13089 $dbo->setQuery($q);
13090 $dbo->execute();
13091
13092 $mainframe = JFactory::getApplication();
13093 $mainframe->redirect("index.php?option=com_vikbooking&task=".($stay ? 'trkconfig' : 'trackings'));
13094 }
13095
13096 public function modtracking() {
13097 $dbo = JFactory::getDbo();
13098 $cid = VikRequest::getVar('cid', array());
13099 foreach ($cid as $id) {
13100 if (!empty($id)) {
13101 $q = "SELECT `id`,`published` FROM `#__vikbooking_trackings` WHERE `id`=".(int)$id.";";
13102 $dbo->setQuery($q);
13103 $dbo->execute();
13104 if ($dbo->getNumRows()) {
13105 $data = $dbo->loadAssoc();
13106 $q = "UPDATE `#__vikbooking_trackings` SET `published`=".($data['published'] ? '0' : '1')." WHERE `id`=".(int)$data['id'].";";
13107 $dbo->setQuery($q);
13108 $dbo->execute();
13109 }
13110 }
13111 }
13112 $mainframe = JFactory::getApplication();
13113 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13114 }
13115
13116 public function removetrackings()
13117 {
13118 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
13119 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13120 }
13121
13122 $ids = VikRequest::getVar('cid', array());
13123 $dbo = JFactory::getDbo();
13124
13125 foreach ($ids as $d) {
13126 $q = "DELETE FROM `#__vikbooking_trackings` WHERE `id`=".(int)$d.";";
13127 $dbo->setQuery($q);
13128 $dbo->execute();
13129 $q = "DELETE FROM `#__vikbooking_tracking_infos` WHERE `idtracking`=".(int)$d.";";
13130 $dbo->setQuery($q);
13131 $dbo->execute();
13132 }
13133
13134 $mainframe = JFactory::getApplication();
13135 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13136 }
13137
13138 /**
13139 * Invokes the Tracker class to obtain
13140 * geo information about the IP addresses.
13141 * This task is called via ajax.
13142 *
13143 * @since 1.11
13144 */
13145 public function getgeoinfo() {
13146 $ips = VikRequest::getVar('ips', array());
13147 if (!count($ips)) {
13148 echo 'e4j.error.empty IPs';
13149 exit;
13150 }
13151
13152 // require the Tracker class without instantiating the object
13153 VikBooking::getTracker(true);
13154 $geo_info = VikBookingTracker::getIpGeoInfo($ips);
13155
13156 if ($geo_info === false) {
13157 echo 'e4j.error.Tracker error, could not get geo info from IPs';
13158 exit;
13159 }
13160
13161 // update db values and compose response
13162 $dbo = JFactory::getDbo();
13163 $resp = array();
13164 foreach ($geo_info as $id => $geo) {
13165 if (is_null($geo) || $geo === false) {
13166 continue;
13167 }
13168 // compose geo info string
13169 $geovals = array();
13170 if (!empty($geo['city'])) {
13171 array_push($geovals, $geo['city']);
13172 }
13173 if (!empty($geo['region'])) {
13174 array_push($geovals, $geo['region']);
13175 }
13176 $threecode = '';
13177 $cname = '';
13178 if (!empty($geo['country'])) {
13179 // returned country is a 2-char code, get the 3-char country code
13180 $q = "SELECT `country_3_code`,`country_name` FROM `#__vikbooking_countries` WHERE `country_2_code`=".$dbo->quote($geo['country']).";";
13181 $dbo->setQuery($q);
13182 $dbo->execute();
13183 if ($dbo->getNumRows()) {
13184 $cinfo = $dbo->loadAssoc();
13185 $threecode = $cinfo['country_3_code'];
13186 $cname = $cinfo['country_name'];
13187 }
13188 array_push($geovals, (empty($cname) ? $geo['country'] : $cname));
13189 }
13190
13191 // full geo information string
13192 $geoinfostr = implode(', ', $geovals);
13193
13194 // push data to the response pool
13195 $resp[$id] = array();
13196 $resp[$id]['geo'] = $geoinfostr;
13197 if (!empty($cname)) {
13198 $resp[$id]['country'] = $cname;
13199 }
13200 if (!empty($threecode)) {
13201 $resp[$id]['country3'] = $threecode;
13202 }
13203
13204 // update main tracking record
13205 $q = "UPDATE `#__vikbooking_trackings` SET `geo`=".$dbo->quote($geoinfostr).(!empty($threecode) ? ', `country`='.$dbo->quote($threecode) : '')." WHERE `id`=".(int)$id.";";
13206 $dbo->setQuery($q);
13207 $dbo->execute();
13208 }
13209
13210 // output the JSON response
13211 echo json_encode($resp);
13212 exit;
13213 }
13214
13215 /**
13216 * Counts the orphan dates for all published rooms
13217 * depending on their restrictions and booked dates.
13218 * By default, the task takes up to 3 months ahead.
13219 * It is possible to filter the request by rooms and months.
13220 * This task should be called via ajax.
13221 *
13222 * @since 1.11
13223 */
13224 public function orphanscount()
13225 {
13226 $dbo = JFactory::getDbo();
13227 $orphans = array();
13228
13229 $nowdf = VikBooking::getDateFormat();
13230 if ($nowdf == "%d/%m/%Y") {
13231 $df = 'd/m/Y';
13232 } elseif ($nowdf == "%m/%d/%Y") {
13233 $df = 'm/d/Y';
13234 } else {
13235 $df = 'Y/m/d';
13236 }
13237
13238 // global min los
13239 $glob_minlos = VikBooking::getDefaultNightsCalendar();
13240 $glob_minlos = $glob_minlos < 1 ? 1 : $glob_minlos;
13241
13242 // rooms and dates
13243 $roomids = VikRequest::getVar('roomids', array(), 'request', 'int');
13244 $months = VikRequest::getInt('months', 3, 'request');
13245 $from = VikRequest::getString('from', '', 'request');
13246 $today = strtotime(date('Y').'-'.date('m').'-'.date('d'));
13247 if (!empty($from)) {
13248 $fromts = VikBooking::getDateTimestamp($from, 0, 0);
13249 if (!empty($fromts)) {
13250 // custom starting date
13251 $today = $fromts;
13252 }
13253 }
13254 $until = strtotime("+{$months} months", $today);
13255
13256 // load all rooms
13257 $rooms = array();
13258 $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms` WHERE `avail`=1".(count($roomids) ? ' AND `id` IN ('.implode(', ', $roomids).')' : '').";";
13259 $dbo->setQuery($q);
13260 $dbo->execute();
13261 if ($dbo->getNumRows()) {
13262 $allrooms = $dbo->loadAssocList();
13263 foreach ($allrooms as $r) {
13264 $rooms[$r['id']] = $r;
13265 }
13266 }
13267 if (!count($rooms)) {
13268 // no rooms found, exit
13269 echo json_encode($orphans);
13270 exit;
13271 }
13272
13273 // load availabilities
13274 $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.");";
13275 $dbo->setQuery($q);
13276 $dbo->execute();
13277 if (!$dbo->getNumRows()) {
13278 // no booked dates found, exit
13279 echo json_encode($orphans);
13280 exit;
13281 }
13282 $busy = $dbo->loadAssocList();
13283
13284 // sort booked dates by room id
13285 $rooms_busy = array();
13286 foreach ($busy as $b) {
13287 if (!isset($rooms_busy[$b['idroom']])) {
13288 $rooms_busy[$b['idroom']] = array();
13289 }
13290 array_push($rooms_busy[$b['idroom']], $b);
13291 }
13292
13293 // load restrictions
13294 $rooms_restr = array();
13295 foreach ($rooms as $rid => $r) {
13296 $restrictions = VikBooking::loadRestrictions(true, array($rid));
13297 if (count($restrictions)) {
13298 $rooms_restr[$rid] = $restrictions;
13299 }
13300 }
13301 if (!count($rooms_restr) && $glob_minlos < 2) {
13302 // no restrictions found and minlos=1, exit
13303 echo json_encode($orphans);
13304 exit;
13305 }
13306
13307 // count availability and minlos per day
13308 $rooms_data = array();
13309 foreach ($rooms as $rid => $r) {
13310 $rooms_data[$rid] = array(
13311 'avail' => array(),
13312 'restr' => array()
13313 );
13314 $nowts = getdate($today);
13315 while ($nowts[0] <= $until) {
13316 $dateind = date('Y-m-d', $nowts[0]);
13317
13318 // remaining availability
13319 if (!isset($rooms_busy[$rid])) {
13320 // no bookings for this room, set full availability for this day
13321 $rooms_data[$rid]['avail'][] = array(
13322 'dt' => $dateind,
13323 'units' => $r['units']
13324 );
13325 } else {
13326 // check remaining availability for this day
13327 $totfound = 0;
13328 foreach ($rooms_busy[$rid] as $b) {
13329 $tmpone = getdate($b['checkin']);
13330 $rit = ($tmpone['mon'] < 10 ? "0".$tmpone['mon'] : $tmpone['mon'])."/".($tmpone['mday'] < 10 ? "0".$tmpone['mday'] : $tmpone['mday'])."/".$tmpone['year'];
13331 $ritts = strtotime($rit);
13332 $tmptwo = getdate($b['checkout']);
13333 $con = ($tmptwo['mon'] < 10 ? "0".$tmptwo['mon'] : $tmptwo['mon'])."/".($tmptwo['mday'] < 10 ? "0".$tmptwo['mday'] : $tmptwo['mday'])."/".$tmptwo['year'];
13334 $conts = strtotime($con);
13335 if ($nowts[0] >= $ritts && $nowts[0] < $conts) {
13336 $totfound++;
13337 }
13338 }
13339 $totfound = $totfound > $r['units'] ? $r['units'] : $totfound;
13340 $rooms_data[$rid]['avail'][] = array(
13341 'dt' => $dateind,
13342 'units' => ($r['units'] - $totfound)
13343 );
13344 }
13345
13346 // restrictions
13347 if (!isset($rooms_restr[$rid])) {
13348 // no restrictions for this room, set global minlos for this day
13349 $rooms_data[$rid]['restr'][] = array(
13350 'dt' => $dateind,
13351 'minlos' => $glob_minlos
13352 );
13353 } else {
13354 // get restriction for this day
13355 $today_tsin = mktime(0, 0, 0, $nowts['mon'], $nowts['mday'], $nowts['year']);
13356 $today_tsout = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
13357
13358 $restr = VikBooking::parseSeasonRestrictions($today_tsin, $today_tsout, 1, $rooms_restr[$rid]);
13359 $minlos = count($restr) ? $restr['minlos'] : $glob_minlos;
13360
13361 $rooms_data[$rid]['restr'][] = array(
13362 'dt' => $dateind,
13363 'minlos' => $minlos
13364 );
13365 }
13366
13367 // next loop
13368 $dayts = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
13369 $nowts = getdate($dayts);
13370 }
13371 }
13372
13373 // week days and months labels
13374 $days_labels = array(
13375 JText::translate('VBSUNDAY'),
13376 JText::translate('VBMONDAY'),
13377 JText::translate('VBTUESDAY'),
13378 JText::translate('VBWEDNESDAY'),
13379 JText::translate('VBTHURSDAY'),
13380 JText::translate('VBFRIDAY'),
13381 JText::translate('VBSATURDAY')
13382 );
13383 $months_labels = array(
13384 JText::translate('VBMONTHONE'),
13385 JText::translate('VBMONTHTWO'),
13386 JText::translate('VBMONTHTHREE'),
13387 JText::translate('VBMONTHFOUR'),
13388 JText::translate('VBMONTHFIVE'),
13389 JText::translate('VBMONTHSIX'),
13390 JText::translate('VBMONTHSEVEN'),
13391 JText::translate('VBMONTHEIGHT'),
13392 JText::translate('VBMONTHNINE'),
13393 JText::translate('VBMONTHTEN'),
13394 JText::translate('VBMONTHELEVEN'),
13395 JText::translate('VBMONTHTWELVE')
13396 );
13397
13398 // orphan dates calculation method
13399 $calc_method = VikBooking::orphansCalculation();
13400
13401 // parse data and build orphans if any
13402 foreach ($rooms_data as $rid => $data) {
13403 foreach ($data['avail'] as $ind => $av) {
13404 if (!isset($data['restr'][$ind]) || $av['units'] < 1) {
13405 // continue, no restriction set or no availability for this day
13406 continue;
13407 }
13408 if ($data['restr'][$ind]['minlos'] < 2) {
13409 // continue, no min los > 1 set for this day
13410 continue;
13411 }
13412 // check if any night after today, until min los, is fully booked
13413 $hasorphans = false;
13414 $forward_count = 0;
13415 for ($i = 1; $i < $data['restr'][$ind]['minlos']; $i++) {
13416 if (!isset($data['avail'][($ind + $i)])) {
13417 // break loop, no info for this day after
13418 break;
13419 }
13420 if ($data['avail'][($ind + $i)]['units'] > 0) {
13421 // continue, availability found for tomorrow, we need a non available next-day
13422 continue;
13423 }
13424 // orphan found
13425 $hasorphans = true;
13426 $forward_count = $i;
13427 break;
13428 }
13429
13430 /**
13431 * Backward calculation method only if "prevnext".
13432 *
13433 * @since 1.3.0
13434 */
13435 $backward_count = 0;
13436 for ($i = 1; $i <= $data['restr'][$ind]['minlos']; $i++) {
13437 if (!isset($data['avail'][($ind - $i)])) {
13438 // break loop, no info for this prev day
13439 break;
13440 }
13441 if ($data['avail'][($ind - $i)]['units'] > 0) {
13442 // increase free nights going backward
13443 $backward_count++;
13444 }
13445 }
13446 if ($calc_method == 'prevnext' && $hasorphans && $backward_count > 0 && ($backward_count >= $data['restr'][$ind]['minlos'] || ($backward_count + $forward_count) >= $data['restr'][$ind]['minlos'])) {
13447 // this should not be an orphan date because of enough free days back, or enough free days in between
13448 $hasorphans = false;
13449 }
13450 //
13451
13452 if ($hasorphans) {
13453 // 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
13454 if (!isset($orphans[$rid])) {
13455 $orphans[$rid] = array(
13456 'name' => $rooms[$rid]['name'],
13457 'dates' => array(),
13458 'rdates' => array(),
13459 'linkd' => date($df, strtotime($av['dt']))
13460 );
13461 }
13462 array_push($orphans[$rid]['dates'], $av['dt']);
13463 // build the value for the readable date
13464 $dtinfo = getdate(strtotime($av['dt']));
13465 $rdate = $days_labels[$dtinfo['wday']] . ', ' . $months_labels[($dtinfo['mon'] - 1)] . ' ' . $dtinfo['mday'] . ' ' . $dtinfo['year'];
13466 array_push($orphans[$rid]['rdates'], $rdate);
13467 }
13468 }
13469 }
13470
13471 // output response
13472 echo json_encode($orphans);
13473 exit;
13474 }
13475
13476 public function tableaux() {
13477 VikBookingHelper::printHeader("tableaux");
13478
13479 VikRequest::setVar('view', VikRequest::getCmd('view', 'tableaux'));
13480
13481 parent::display();
13482
13483 if (VikBooking::showFooter()) {
13484 VikBookingHelper::printFooter();
13485 }
13486 }
13487
13488 public function operators() {
13489 VikBookingHelper::printHeader("operators");
13490
13491 VikRequest::setVar('view', VikRequest::getCmd('view', 'operators'));
13492
13493 parent::display();
13494
13495 if (VikBooking::showFooter()) {
13496 VikBookingHelper::printFooter();
13497 }
13498 }
13499
13500 public function newoperator() {
13501 VikBookingHelper::printHeader("operators");
13502
13503 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoperator'));
13504
13505 parent::display();
13506
13507 if (VikBooking::showFooter()) {
13508 VikBookingHelper::printFooter();
13509 }
13510 }
13511
13512 public function editoperator() {
13513 VikBookingHelper::printHeader("operators");
13514
13515 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoperator'));
13516
13517 parent::display();
13518
13519 if (VikBooking::showFooter()) {
13520 VikBookingHelper::printFooter();
13521 }
13522 }
13523
13524 public function updateoperator()
13525 {
13526 if (!JSession::checkToken()) {
13527 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13528 }
13529
13530 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
13531 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13532 }
13533
13534 $this->do_updateoperator();
13535 }
13536
13537 public function updateoperatorstay()
13538 {
13539 if (!JSession::checkToken()) {
13540 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13541 }
13542
13543 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
13544 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13545 }
13546
13547 $this->do_updateoperator(true);
13548 }
13549
13550 private function do_updateoperator($stay = false)
13551 {
13552 $dbo = JFactory::getDbo();
13553 $app = JFactory::getApplication();
13554 $pfirst_name = VikRequest::getString('first_name', '', 'request');
13555 $plast_name = VikRequest::getString('last_name', '', 'request');
13556 $pemail = VikRequest::getString('email', '', 'request');
13557 $pphone = VikRequest::getString('phone', '', 'request');
13558 $pcode = VikRequest::getString('code', '', 'request');
13559 $pujid = VikRequest::getInt('ujid', '', 'request');
13560 $pwhere = VikRequest::getInt('where', '', 'request');
13561
13562 $work_days_week = (array) $app->input->get('work_days_week', [], 'array');
13563 $work_days_exceptions = (array) $app->input->get('work_days_exceptions', [], 'array');
13564
13565 // normalize to linear arrays
13566 $work_days_week_schedule = array_combine(array_keys($work_days_week), array_values($work_days_week));
13567 $work_days_week = [];
13568 foreach ($work_days_week_schedule as $wday => $whours) {
13569 $work_days_week[] = [
13570 'wday' => $wday,
13571 'hours' => $whours,
13572 ];
13573 }
13574 foreach ($work_days_exceptions as &$wexceptions) {
13575 if (is_scalar($wexceptions)) {
13576 $wexceptions = json_decode($wexceptions, true);
13577 }
13578 }
13579 unset($wexceptions);
13580
13581 if (!empty($pfirst_name) && !empty($pemail) && !empty($pcode)) {
13582 $q = "SELECT * FROM `#__vikbooking_operators` WHERE `id`=".(int)$pwhere." LIMIT 1;";
13583 $dbo->setQuery($q);
13584 $customer = $dbo->loadAssoc();
13585 if (!$customer) {
13586 $app->redirect("index.php?option=com_vikbooking&task=operators");
13587 exit;
13588 }
13589
13590 $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;";
13591 $dbo->setQuery($q);
13592 $ex_operator = $dbo->loadAssoc();
13593 if (!$ex_operator) {
13594 // update fingerprint for the operator
13595 $fingpt = md5($pwhere . $pemail);
13596
13597 /**
13598 * Operator profile picture (URL or uploaded file).
13599 *
13600 * @since 1.16.9 (J) - 1.6.9 (WP)
13601 */
13602 $operator_pic = VikRequest::getString('pic', '', 'request');
13603 $operator_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
13604 if (is_array($operator_pic_img) && !empty($operator_pic_img['name'])) {
13605 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($operator_pic_img['name'])));
13606 $src = $operator_pic_img['tmp_name'];
13607 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
13608 $j = "";
13609 if (is_file($dest.$filename)) {
13610 $j = rand(1, 99999);
13611 while (is_file($dest . $j .$filename)) {
13612 $j++;
13613 }
13614 }
13615 $finaldest = $dest . $j . $filename;
13616 $check = getimagesize($operator_pic_img['tmp_name']);
13617 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
13618 if (VikBooking::uploadFile($src, $finaldest)) {
13619 $operator_pic = $j . $filename;
13620 } else {
13621 VikError::raiseWarning('', 'Error while uploading image');
13622 }
13623 } else {
13624 VikError::raiseWarning('', 'Uploaded file is not an Image');
13625 }
13626 }
13627
13628 // update record
13629 $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;
13630 $dbo->setQuery($q);
13631 $dbo->execute();
13632 $app->enqueueMessage(JText::translate('VBOPERATORSAVED'));
13633 } else {
13634 //email already exists
13635 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>');
13636 $app->redirect("index.php?option=com_vikbooking&task=editoperator&cid[]=".$pwhere);
13637 exit;
13638 }
13639 } else {
13640 VikError::raiseWarning('', JText::translate('VBERROPERATORDATA'));
13641 }
13642
13643 if ($stay) {
13644 $app->redirect("index.php?option=com_vikbooking&task=editoperator&cid[]=".$pwhere);
13645 } else {
13646 $app->redirect("index.php?option=com_vikbooking&task=operators");
13647 }
13648
13649 $app->close();
13650 }
13651
13652 public function saveoperator()
13653 {
13654 if (!JSession::checkToken()) {
13655 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13656 }
13657
13658 $dbo = JFactory::getDbo();
13659 $app = JFactory::getApplication();
13660 $pfirst_name = VikRequest::getString('first_name', '', 'request');
13661 $plast_name = VikRequest::getString('last_name', '', 'request');
13662 $pemail = VikRequest::getString('email', '', 'request');
13663 $pphone = VikRequest::getString('phone', '', 'request');
13664 $pcode = VikRequest::getString('code', '', 'request');
13665 $pujid = VikRequest::getInt('ujid', '', 'request');
13666
13667 $work_days_week = (array) $app->input->get('work_days_week', [], 'array');
13668 $work_days_exceptions = (array) $app->input->get('work_days_exceptions', [], 'array');
13669
13670 // normalize to linear arrays
13671 $work_days_week_schedule = array_combine(array_keys($work_days_week), array_values($work_days_week));
13672 $work_days_week = [];
13673 foreach ($work_days_week_schedule as $wday => $whours) {
13674 $work_days_week[] = [
13675 'wday' => $wday,
13676 'hours' => $whours,
13677 ];
13678 }
13679 foreach ($work_days_exceptions as &$wexceptions) {
13680 if (is_scalar($wexceptions)) {
13681 $wexceptions = json_decode($wexceptions, true);
13682 }
13683 }
13684 unset($wexceptions);
13685
13686 if (!empty($pfirst_name) && !empty($pemail) && !empty($pcode)) {
13687 $q = "SELECT * FROM `#__vikbooking_operators` WHERE `email`=".$dbo->quote($pemail)." OR ".(!empty($pcode) ? "`code`=".$dbo->quote($pcode) : "`ujid`=".$dbo->quote($pujid))." LIMIT 1;";
13688 $dbo->setQuery($q);
13689 $ex_operator = $dbo->loadAssoc();
13690 if (!$ex_operator) {
13691 /**
13692 * Operator profile picture (URL or uploaded file).
13693 *
13694 * @since 1.16.9 (J) - 1.6.9 (WP)
13695 */
13696 $operator_pic = VikRequest::getString('pic', '', 'request');
13697 $operator_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
13698 if (is_array($operator_pic_img) && !empty($operator_pic_img['name'])) {
13699 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($operator_pic_img['name'])));
13700 $src = $operator_pic_img['tmp_name'];
13701 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
13702 $j = "";
13703 if (is_file($dest.$filename)) {
13704 $j = rand(1, 99999);
13705 while (is_file($dest . $j .$filename)) {
13706 $j++;
13707 }
13708 }
13709 $finaldest = $dest . $j . $filename;
13710 $check = getimagesize($operator_pic_img['tmp_name']);
13711 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
13712 if (VikBooking::uploadFile($src, $finaldest)) {
13713 $operator_pic = $j . $filename;
13714 } else {
13715 VikError::raiseWarning('', 'Error while uploading image');
13716 }
13717 } else {
13718 VikError::raiseWarning('', 'Uploaded file is not an Image');
13719 }
13720 }
13721
13722 $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') . ");";
13723 $dbo->setQuery($q);
13724 $dbo->execute();
13725 $lid = $dbo->insertid();
13726 if (!empty($lid)) {
13727 $app->enqueueMessage(JText::translate('VBOPERATORSAVED'));
13728 // generate fingerprint for the operator
13729 $q = "UPDATE `#__vikbooking_operators` SET `fingpt`=".$dbo->q(md5($lid.$pemail))." WHERE `id`=".(int)$lid.";";
13730 $dbo->setQuery($q);
13731 $dbo->execute();
13732 }
13733 } else {
13734 // email already exists
13735 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>');
13736 }
13737 } else {
13738 VikError::raiseWarning('', JText::translate('VBERROPERATORDATA'));
13739 }
13740
13741 $app->redirect("index.php?option=com_vikbooking&task=operators");
13742 $app->close();
13743 }
13744
13745 public function removeoperators()
13746 {
13747 if (!JSession::checkToken()) {
13748 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13749 }
13750
13751 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
13752 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13753 }
13754
13755 $ids = VikRequest::getVar('cid', array(0));
13756 if ($ids) {
13757 $dbo = JFactory::getDBO();
13758 foreach ($ids as $d) {
13759 $q = "DELETE FROM `#__vikbooking_operators` WHERE `id`=".(int)$d.";";
13760 $dbo->setQuery($q);
13761 $dbo->execute();
13762 }
13763 }
13764 $mainframe = JFactory::getApplication();
13765 $mainframe->redirect("index.php?option=com_vikbooking&task=operators");
13766 }
13767
13768 public function canceloperator() {
13769 $mainframe = JFactory::getApplication();
13770 $mainframe->redirect("index.php?option=com_vikbooking&task=operators");
13771 }
13772
13773 public function cancelcrons() {
13774 $mainframe = JFactory::getApplication();
13775 $mainframe->redirect("index.php?option=com_vikbooking&task=crons");
13776 }
13777
13778 public function cancelpackages() {
13779 $mainframe = JFactory::getApplication();
13780 $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
13781 }
13782
13783 public function cancelcustomer() {
13784 $mainframe = JFactory::getApplication();
13785 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
13786 if (!empty($pgoto)) {
13787 $mainframe->redirect(base64_decode($pgoto));
13788 exit;
13789 }
13790 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
13791 }
13792
13793 public function cancelbusyvcm() {
13794 $mainframe = JFactory::getApplication();
13795 $mainframe->redirect("index.php?option=com_vikchannelmanager&task=oversight");
13796 }
13797
13798 public function cancelrestriction() {
13799 $mainframe = JFactory::getApplication();
13800 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
13801 }
13802
13803 public function cancelcoupon() {
13804 $mainframe = JFactory::getApplication();
13805 $mainframe->redirect("index.php?option=com_vikbooking&task=coupons");
13806 }
13807
13808 public function cancelcustomf() {
13809 $mainframe = JFactory::getApplication();
13810 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
13811 }
13812
13813 public function cancelpayment() {
13814 $mainframe = JFactory::getApplication();
13815 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
13816 }
13817
13818 public function cancelseason() {
13819 $mainframe = JFactory::getApplication();
13820 $mainframe->redirect("index.php?option=com_vikbooking&task=seasons");
13821 }
13822
13823 public function goconfig() {
13824 $mainframe = JFactory::getApplication();
13825 $mainframe->redirect("index.php?option=com_vikbooking&task=config");
13826 }
13827
13828 public function canceledorder() {
13829 $pgoto = VikRequest::getString('goto', 'orders', 'request');
13830 $mainframe = JFactory::getApplication();
13831 $mainframe->redirect("index.php?option=com_vikbooking&task=" . $pgoto);
13832 }
13833
13834 public function cancelbusy() {
13835 $pidorder = VikRequest::getString('idorder', '', 'request');
13836 $pgoto = VikRequest::getString('goto', '', 'request');
13837 $mainframe = JFactory::getApplication();
13838 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pidorder.($pgoto == 'overv' ? '&goto=overv' : ''));
13839 }
13840
13841 public function canceloverv() {
13842 $mainframe = JFactory::getApplication();
13843 $mainframe->redirect("index.php?option=com_vikbooking&task=overv");
13844 }
13845
13846 public function canceltableaux() {
13847 $mainframe = JFactory::getApplication();
13848 $mainframe->redirect("index.php?option=com_vikbooking&task=tableaux");
13849 }
13850
13851 public function cancelcalendar() {
13852 $pidroom = VikRequest::getString('idroom', '', 'request');
13853 $mainframe = JFactory::getApplication();
13854 $mainframe->redirect("index.php?option=com_vikbooking&task=calendar&cid[]=".$pidroom);
13855 }
13856
13857 public function canceloptionals() {
13858 $mainframe = JFactory::getApplication();
13859 $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
13860 }
13861
13862 public function cancel() {
13863 $mainframe = JFactory::getApplication();
13864 $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
13865 }
13866
13867 public function cancelcarat() {
13868 $mainframe = JFactory::getApplication();
13869 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
13870 }
13871
13872 public function cancelcat() {
13873 $mainframe = JFactory::getApplication();
13874 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
13875 }
13876
13877 public function cancelprice() {
13878 $mainframe = JFactory::getApplication();
13879 $mainframe->redirect("index.php?option=com_vikbooking&task=prices");
13880 }
13881
13882 public function canceliva() {
13883 $mainframe = JFactory::getApplication();
13884 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
13885 }
13886
13887 public function canceltrk() {
13888 $mainframe = JFactory::getApplication();
13889 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13890 }
13891
13892 public function canceldash() {
13893 $mainframe = JFactory::getApplication();
13894 $mainframe->redirect("index.php?option=com_vikbooking");
13895 }
13896
13897 public function cancelinvoice() {
13898 $mainframe = JFactory::getApplication();
13899 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
13900 if (!empty($pgoto)) {
13901 $mainframe->redirect(base64_decode($pgoto));
13902 exit;
13903 }
13904 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
13905 }
13906
13907 /**
13908 * AJAX upload the customer documents.
13909 *
13910 * @return void
13911 *
13912 * @throws Exception
13913 */
13914 public function upload_customer_document()
13915 {
13916 $input = JFactory::getApplication()->input;
13917 $dbo = JFactory::getDbo();
13918
13919 $customer_id = $input->getUint('customer', 0);
13920
13921 $result = new stdClass;
13922 $result->status = 0;
13923
13924 try
13925 {
13926 $q = $dbo->getQuery(true)
13927 ->select($dbo->qn(array(
13928 'id',
13929 'first_name',
13930 'last_name',
13931 'email',
13932 'docsfolder',
13933 )))
13934 ->from($dbo->qn('#__vikbooking_customers'))
13935 ->where($dbo->qn('id') . ' = ' . $customer_id);
13936
13937 $dbo->setQuery($q, 0, 1);
13938 $dbo->execute();
13939
13940 if (!$dbo->getNumRows())
13941 {
13942 throw new Exception(sprintf('Customer [%d] not found', $customer_id), 404);
13943 }
13944
13945 $customer = $dbo->loadObject();
13946
13947 // fetch documents folder path
13948 $dirpath = VBO_CUSTOMERS_PATH . DIRECTORY_SEPARATOR;
13949
13950 // check if we have a valid directory
13951 if (empty($customer->docsfolder) || !is_dir($dirpath . $customer->docsfolder))
13952 {
13953 // randomize string
13954 $customer->seed = uniqid();
13955
13956 // create blocks for hashed folder
13957 $parts = [
13958 $customer->first_name,
13959 $customer->last_name,
13960 md5(serialize($customer)),
13961 ];
13962
13963 // join fetched parts
13964 $customer->docsfolder = JFilterOutput::stringURLSafe(implode('-', array_filter($parts)));
13965
13966 if (strlen($customer->docsfolder) < 16)
13967 {
13968 throw new Exception('Possible security breach. Please specify the most details as possible.', 400);
13969 }
13970
13971 jimport('joomla.filesystem.folder');
13972
13973 // create a folder for this customer
13974 $created = JFolder::create($dirpath . $customer->docsfolder);
13975
13976 if (!$created)
13977 {
13978 throw new Exception(sprintf('Unable to create the folder [%s]', $dirpath . $customer->docsfolder), 403);
13979 }
13980
13981 unset($customer->seed);
13982
13983 // update docs folder
13984 $dbo->updateObject('#__vikbooking_customers', $customer, 'id');
13985 }
13986
13987 // get file from request
13988 $file = $input->files->get('file', array(), 'array');
13989
13990 // try to upload the file
13991 $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');
13992 $result->status = 1;
13993
13994 $result->size = JHtml::fetch('number.bytes', filesize($result->path), 'auto', 0);
13995 $result->url = str_replace(DIRECTORY_SEPARATOR, '/', str_replace(VBO_CUSTOMERS_PATH . DIRECTORY_SEPARATOR, VBO_CUSTOMERS_URI, $result->path));
13996 }
13997 catch (Exception $e)
13998 {
13999 $result->error = $e->getMessage();
14000 $result->code = $e->getCode();
14001 }
14002
14003 echo json_encode($result);
14004 exit;
14005 }
14006
14007 /**
14008 * AJAX delete the customer documents.
14009 *
14010 * @return void
14011 *
14012 * @throws Exception
14013 */
14014 public function delete_customer_document()
14015 {
14016 $input = JFactory::getApplication()->input;
14017 $dbo = JFactory::getDbo();
14018
14019 $customer_id = $input->getUint('customer', 0);
14020
14021 $result = new stdClass;
14022 $result->status = 0;
14023
14024 $q = $dbo->getQuery(true)
14025 ->select($dbo->qn('docsfolder'))
14026 ->from($dbo->qn('#__vikbooking_customers'))
14027 ->where($dbo->qn('id') . ' = ' . $customer_id);
14028
14029 $dbo->setQuery($q, 0, 1);
14030 $dbo->execute();
14031
14032 if (!$dbo->getNumRows())
14033 {
14034 throw new Exception(sprintf('Customer [%d] not found', $customer_id), 404);
14035 }
14036
14037 $folder = $dbo->loadResult();
14038
14039 if (!$folder)
14040 {
14041 throw new Exception('The customer does not have any documents', 500);
14042 }
14043
14044 $file = $input->getString('file');
14045
14046 if (!$file)
14047 {
14048 throw new Exception('File to remove not specified', 400);
14049 }
14050
14051 $path = implode(DIRECTORY_SEPARATOR, array(VBO_CUSTOMERS_PATH, $folder, $file));
14052
14053 if (!is_file($path))
14054 {
14055 throw new Exception(sprintf('File [%s] not found', $path), 404);
14056 }
14057
14058 jimport('joomla.filesystem.file');
14059
14060 $removed = JFile::delete($path);
14061
14062 echo json_encode(array('status' => (int) $removed));
14063 exit;
14064 }
14065
14066 /**
14067 * AJAX task to invoke a specific report and obtain information.
14068 *
14069 * @since 1.3.0
14070 */
14071 public function get_report_data()
14072 {
14073 $report_name = VikRequest::getString('report_name', '', 'request');
14074 $current_fest = VikRequest::getString('current_fest', '', 'request');
14075 $current_fromdate = VikRequest::getString('current_fromdate', '', 'request');
14076 $current_todate = VikRequest::getString('current_todate', '', 'request');
14077 $step = VikRequest::getString('step', 'weekend', 'request');
14078 $direction = VikRequest::getString('direction', 'load', 'request');
14079 $period = VikRequest::getString('period', 'full', 'request');
14080 $krsort = VikRequest::getString('krsort', 'occupancy', 'request');
14081 $krorder = VikRequest::getString('krorder', 'DESC', 'request');
14082 $chart_datatype = VikRequest::getVar('chart_datatype', array(), 'request');
14083 $chart_meta_data = VikRequest::getString('chart_meta_data', '', 'request', VIKREQUEST_ALLOWRAW);
14084 $chart_meta_data = !empty($chart_meta_data) ? json_decode($chart_meta_data, true) : array();
14085 // idroom can be an array of IDs or just one ID as int/string
14086 $idroom = VikRequest::getVar('idroom', null, 'request');
14087 //
14088
14089 if (empty($report_name) || empty($current_fromdate) || empty($current_todate)) {
14090 throw new Exception("Missing request data", 400);
14091 }
14092
14093 // get requested report instance
14094 $report = VikBooking::getReportInstance($report_name);
14095 if (!$report) {
14096 throw new Exception("Report not found", 404);
14097 }
14098
14099 // chart data
14100 if (empty($chart_datatype)) {
14101 $chart_datatype = array(
14102 'type' => 'doughnut',
14103 'depth' => 1,
14104 'keys' => array($krsort),
14105 );
14106 }
14107
14108 // website date format
14109 $df = $report->getDateFormat();
14110
14111 // prepare request params for the report
14112 $rparams = array(
14113 'fromdate' => $current_fromdate,
14114 'todate' => $current_todate,
14115 'period' => $period,
14116 'krsort' => $krsort,
14117 'krorder' => $krorder,
14118 'idroom' => $idroom,
14119 );
14120
14121 // starting dates info and timestamps
14122 $from_ts = VikBooking::getDateTimestamp($current_fromdate, 0, 0, 0);
14123 $to_ts = VikBooking::getDateTimestamp($current_todate, 23, 59, 59);
14124 $from_info = getdate($from_ts);
14125 $to_info = getdate($to_ts);
14126
14127 // the name of the period requested and whether it's a fest
14128 $period_name = '';
14129 $is_fest = null;
14130
14131 if ($direction == 'prev' || $direction == 'next') {
14132 // calculate prev or next dates
14133 if ($step == 'weekend') {
14134 $period_name = JText::translate('VBOWEEKND');
14135 if ($direction == 'next') {
14136 // next weekend from current end date
14137 $next_ts = strtotime("next friday", $to_ts);
14138 } else {
14139 // prev weekend from current start date
14140 $next_ts = strtotime("previous friday", $from_ts);
14141 }
14142 $next_info = getdate($next_ts);
14143 $new_from_ts = $next_ts;
14144 $new_to_ts = mktime(23, 59, 59, $next_info['mon'], ($next_info['mday'] + 1), $next_info['year']);
14145 $rparams['fromdate'] = date($df, $new_from_ts);
14146 $rparams['todate'] = date($df, $new_to_ts);
14147 } elseif ($step == 'week') {
14148 $period_name = JText::translate('VBOWEEK');
14149 if ($direction == 'next') {
14150 // start next week from the current end date
14151 $new_from_ts = $to_ts;
14152 $new_to_ts = mktime(23, 59, 59, $to_info['mon'], ($to_info['mday'] + 7), $to_info['year']);
14153 $rparams['fromdate'] = $rparams['todate'];
14154 $rparams['todate'] = date($df, $new_to_ts);
14155 } else {
14156 // end prev week from the current from date
14157 $new_from_ts = mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] - 7), $from_info['year']);
14158 $new_to_ts = $from_ts;
14159 $rparams['todate'] = $rparams['fromdate'];
14160 $rparams['fromdate'] = date($df, $new_from_ts);
14161 }
14162 } else {
14163 // month
14164 $period_name = JText::translate('VBPVIEWRESTRICTIONSTWO');
14165 if ($direction == 'next') {
14166 // next month from the current from date
14167 $nextmonts = mktime(0, 0, 0, ($from_info['mon'] + 1), 1, $from_info['year']);
14168 $new_from_ts = $nextmonts;
14169 $new_to_ts = mktime(23, 59, 59, ($from_info['mon'] + 1), date('t', $nextmonts), $from_info['year']);
14170 $rparams['fromdate'] = date($df, $new_from_ts);
14171 $rparams['todate'] = date($df, $new_to_ts);
14172 } else {
14173 // prev month from the current from date
14174 $nextmonts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']);
14175 $new_from_ts = $nextmonts;
14176 $new_to_ts = mktime(23, 59, 59, ($from_info['mon'] - 1), date('t', $nextmonts), $from_info['year']);
14177 $rparams['fromdate'] = date($df, $new_from_ts);
14178 $rparams['todate'] = date($df, $new_to_ts);
14179 }
14180 }
14181
14182 // get the next festivities
14183 $fests = VikBooking::getFestivitiesInstance();
14184 $next_fests = $fests->loadFestDates();
14185 if (count($next_fests)) {
14186 // check whether a festivity should be displayed rather than the calculated period of dates
14187 foreach ($next_fests as $fest) {
14188 $fest_found = false;
14189 if ($direction == 'next' && $fest['festinfo'][0]->from_ts > $from_ts && $fest['festinfo'][0]->from_ts <= $new_to_ts) {
14190 $fest_found = true;
14191 } elseif ($direction == 'prev' && $fest['festinfo'][0]->from_ts < $to_ts && $fest['festinfo'][0]->from_ts >= $new_from_ts) {
14192 $fest_found = true;
14193 }
14194 if ($fest_found && (string)$fest['festinfo'][0]->next_ts != $current_fest) {
14195 // festivity found before next calculated period
14196 $is_fest = $fest['festinfo'][0]->next_ts;
14197 $period_name = $fest['festinfo'][0]->trans_name;
14198 $new_from_ts = $fest['festinfo'][0]->from_ts;
14199 $new_to_ts = $fest['festinfo'][0]->to_ts;
14200 $rparams['fromdate'] = date($df, $new_from_ts);
14201 $rparams['todate'] = date($df, $new_to_ts);
14202 break;
14203 }
14204 }
14205 }
14206 } else {
14207 // load requested dates by skipping the festivities
14208 $new_from_ts = $from_ts;
14209 $new_to_ts = $to_ts;
14210 }
14211
14212 // invoke report
14213 $report->injectParams($rparams);
14214 $report_values = $report->getReportValues(1);
14215 $report_cols = $report->getColumnsValues();
14216 $report_chart = null;
14217 $report_chart_metas = array();
14218 $chart_meta_data = array(
14219 'keys' => array(
14220 'occupancy',
14221 'tot_bookings',
14222 'nights_booked',
14223 ),
14224 );
14225 $error = null;
14226
14227 if (!count($report_values)) {
14228 $error = strlen($report->getError()) ? $report->getError() : JText::translate('VBNOTRACKINGS');
14229 } else {
14230 // get doughnut Chart for the requested key
14231 $report_chart = $report->getChart((array) $chart_datatype);
14232
14233 // get Chart meta data
14234 $all_chart_metas = $report->getChartMetaData(null, $chart_meta_data);
14235 if (count($all_chart_metas)) {
14236 // merge all positions into one array
14237 foreach ($all_chart_metas as $pos_metas) {
14238 $report_chart_metas = array_merge($report_chart_metas, $pos_metas);
14239 }
14240 }
14241
14242 if (empty($period_name)) {
14243 $period_name = $report->getProperty('chartTitle');
14244 }
14245 }
14246
14247 // build response
14248 $response = new stdClass;
14249 $response->error = $error;
14250 $response->fromdate = $rparams['fromdate'];
14251 $response->todate = $rparams['todate'];
14252 $response->in_days = $report->countDaysTo($new_from_ts);
14253 $response->in_days_to = $report->countDaysTo($new_to_ts);
14254 $response->in_days_avg = $report->countAverageDays($response->in_days, $response->in_days_to);
14255 $response->period_name = $period_name;
14256 $response->period_date = count($report_values) && isset($report_values['day']) ? $report_values['day']['display_value'] : '';
14257 $response->is_fest = $is_fest;
14258 $response->report_chart = $report_chart;
14259 $response->report_cols = $report_cols;
14260 $response->report_values = $report_values;
14261 $response->report_script = $report->getScript();
14262 $response->chart_labels = $report->getProperty('chartJsLabels');
14263 $response->dataset_label = $report->getProperty('chartJsDataSetLabel');
14264 $response->chart_colors = $report->getProperty('chartJsColors');
14265 $response->chart_data = $report->getProperty('chartJsData');
14266 $response->report_chart_metas = $report_chart_metas;
14267
14268 echo json_encode($response);
14269 exit;
14270 }
14271
14272 /**
14273 * Go to the previous booking.
14274 *
14275 * @uses navigateToBooking()
14276 *
14277 * @since 1.3.0
14278 */
14279 public function prev_booking()
14280 {
14281 $this->navigateToBooking('prev');
14282 }
14283
14284 /**
14285 * Go to the next booking.
14286 *
14287 * @uses navigateToBooking()
14288 *
14289 * @since 1.3.0
14290 */
14291 public function next_booking()
14292 {
14293 $this->navigateToBooking('next');
14294 }
14295
14296 /**
14297 * Given the current booking ID in the request, we navigate
14298 * either to the next or to the previous reservation (if any).
14299 *
14300 * @param string $direction either next or prev.
14301 *
14302 * @return void
14303 *
14304 * @since 1.3.0
14305 */
14306 private function navigateToBooking($direction = 'next')
14307 {
14308 $bid = VikRequest::getInt('whereup', 0, 'request');
14309 if (empty($bid) || $bid < 1 || !in_array($direction, array('prev', 'next'))) {
14310 throw new Exception("Invalid request", 400);
14311 }
14312
14313 $dbo = JFactory::getDbo();
14314 $app = JFactory::getApplication();
14315
14316 $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `id`" . ($direction == 'next' ? '>' : '<') . "{$bid} ORDER BY `id` " . ($direction == 'next' ? 'ASC' : 'DESC');
14317 $dbo->setQuery($q, 0, 1);
14318 $dbo->execute();
14319 if (!$dbo->getNumRows()) {
14320 VikError::raiseWarning('', JText::translate('VBPEDITBUSYONE'));
14321 $app->redirect("index.php?option=com_vikbooking&task=orders");
14322 exit;
14323 }
14324
14325 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $dbo->loadResult());
14326 exit;
14327 }
14328
14329 /**
14330 * AJAX request: from a list of reservation IDs, we return the ones
14331 * that have a review with the related review ID on VCM.
14332 *
14333 * @since 1.13
14334 */
14335 public function bookings_have_reviews()
14336 {
14337 if (!JSession::checkToken()) {
14338 // missing CSRF-proof token
14339 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14340 }
14341
14342 $dbo = JFactory::getDbo();
14343
14344 $bids = VikRequest::getVar('bids', [], 'request', 'array');
14345 $vcm_installed = class_exists('VikChannelManager');
14346 $withreviews = [];
14347
14348 if ($vcm_installed && $bids) {
14349 $bids = array_filter(array_map('intval', (array) $bids));
14350 $bids = $bids ?: [0];
14351
14352 try {
14353 $q = "SELECT `id`, `idorder` FROM `#__vikchannelmanager_otareviews` WHERE `idorder` IN (" . implode(', ', $bids) . ");";
14354 $dbo->setQuery($q);
14355 $reviews = $dbo->loadAssocList();
14356
14357 foreach ($reviews as $r) {
14358 $withreviews[$r['idorder']] = $r['id'];
14359 }
14360 } catch (Exception $e) {
14361 // do nothing, outdated version
14362 }
14363 }
14364
14365 // output list of booking IDs found, if any
14366 VBOHttpDocument::getInstance()->json($withreviews);
14367 }
14368
14369 /**
14370 * AJAX request for adding a new room-day note.
14371 *
14372 * @return void
14373 *
14374 * @since 1.13.5
14375 */
14376 public function add_roomdaynote()
14377 {
14378 $dt = VikRequest::getString('dt', '', 'request');
14379 $idroom = VikRequest::getInt('idroom', 0, 'request');
14380 $subunit = VikRequest::getInt('subunit', 0, 'request');
14381 $type = VikRequest::getString('type', '', 'request');
14382 $type = empty($type) ? 'custom' : $type;
14383 $name = VikRequest::getString('name', '', 'request');
14384 $descr = VikRequest::getString('descr', '', 'request');
14385 $cdays = VikRequest::getInt('cdays', 0, 'request');
14386 $cdays = $cdays < 0 ? 0 : $cdays;
14387 $cdays = $cdays > 365 ? 365 : $cdays;
14388 if (empty($idroom) || empty($dt) || !strtotime($dt)) {
14389 echo 'e4j.error.1';
14390 exit;
14391 }
14392
14393 // reload end date
14394 $end_date = $dt;
14395
14396 // build critical date object
14397 $new_note = array(
14398 'name' => $name,
14399 'type' => $type,
14400 'descr' => $descr,
14401 );
14402
14403 // get object
14404 $notes = VikBooking::getCriticalDatesInstance();
14405
14406 // store the notes for all consecutive dates
14407 for ($i = 0; $i <= $cdays; $i++) {
14408 $store_dt = $dt;
14409 if ($i > 0) {
14410 $dt_info = getdate(strtotime($store_dt));
14411 $store_dt = date('Y-m-d', mktime(0, 0, 0, $dt_info['mon'], ($dt_info['mday'] + $i), $dt_info['year']));
14412 $end_date = $store_dt;
14413 }
14414 $result = $notes->storeDayNote($new_note, $store_dt, $idroom, $subunit);
14415 if (!$result) {
14416 echo 'e4j.error.2';
14417 exit;
14418 }
14419 }
14420
14421 // reload all room day notes for this day for the AJAX response
14422 $all_notes = $notes->loadRoomDayNotes($dt, $end_date, $idroom, $subunit);
14423
14424 if (!$all_notes || !count($all_notes)) {
14425 // no notes found even after storing it
14426 echo 'e4j.error.3';
14427 exit;
14428 }
14429
14430 echo json_encode($all_notes);
14431 exit;
14432 }
14433
14434 /**
14435 * AJAX request for removing a room day note.
14436 *
14437 * @return void
14438 *
14439 * @since 1.13.5
14440 */
14441 public function remove_roomdaynote()
14442 {
14443 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
14444 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14445 }
14446
14447 $dt = VikRequest::getString('dt', '', 'request');
14448 $idroom = VikRequest::getInt('idroom', 0, 'request');
14449 $subunit = VikRequest::getInt('subunit', 0, 'request');
14450 $type = VikRequest::getString('type', '', 'request');
14451 $type = empty($type) ? 'custom' : $type;
14452 $ind = VikRequest::getInt('ind', 0, 'request');
14453 if (empty($dt) || !strtotime($dt)) {
14454 echo 'e4j.error.1';
14455 exit;
14456 }
14457
14458 $notes = VikBooking::getCriticalDatesInstance();
14459 $result = $notes->deleteDayNote($ind, $dt, $idroom, $subunit, $type);
14460 if (!$result) {
14461 echo 'e4j.error.2';
14462 exit;
14463 }
14464
14465 echo 'e4j.ok';
14466 exit;
14467 }
14468
14469 /**
14470 * AJAX request for storing an event for a booking.
14471 * Firstly developed for the VCM Reporting API - Guest Misconduct,
14472 * but it can be used for any other purpose.
14473 *
14474 * @return void
14475 *
14476 * @since 1.13.5
14477 */
14478 public function store_booking_history_event()
14479 {
14480 $bid = VikRequest::getInt('bid', 0, 'request');
14481 $event = VikRequest::getString('event', '', 'request');
14482 $descr = VikRequest::getString('descr', '', 'request');
14483
14484 if (empty($bid) || empty($event)) {
14485 throw new Exception("Missing required information", 500);
14486 }
14487
14488 // Booking History
14489 VikBooking::getBookingHistoryInstance()->setBid($bid)->store($event, $descr);
14490 //
14491
14492 echo 'e4j.ok';
14493 exit;
14494 }
14495
14496 /**
14497 * AJAX request for updating an option/extra service.
14498 * Firstly developed for the VCM Vacation Rentals Essentials API - Damage Deposit,
14499 * but it can be used for any other purpose.
14500 *
14501 * @return void
14502 *
14503 * @since 1.13.5
14504 */
14505 public function update_option_params()
14506 {
14507 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
14508 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14509 }
14510
14511 $optid = VikRequest::getInt('optid', 0, 'request');
14512 $oparams = VikRequest::getVar('oparams', array(), 'request', 'array');
14513
14514 if (empty($optid) || !is_array($oparams) || empty($oparams)) {
14515 throw new Exception("Missing required information", 500);
14516 }
14517
14518 $dbo = JFactory::getDbo();
14519 $q = "SELECT `oparams` FROM `#__vikbooking_optionals` WHERE `id`=" . (int)$optid . ";";
14520 $dbo->setQuery($q);
14521 $dbo->execute();
14522 if (!$dbo->getNumRows()) {
14523 throw new Exception("Option not found", 404);
14524 }
14525 $cur_params = $dbo->loadResult();
14526 $cur_params = !empty($cur_params) ? json_decode($cur_params, true) : array();
14527 $cur_params = !is_array($cur_params) ? array() : $cur_params;
14528
14529 foreach ($oparams as $k => $v) {
14530 if (empty($k)) {
14531 continue;
14532 }
14533 $cur_params[$k] = $v;
14534 }
14535
14536 $q = "UPDATE `#__vikbooking_optionals` SET `oparams`=" . $dbo->quote(json_encode($cur_params)) ." WHERE `id`=" . (int)$optid . ";";
14537 $dbo->setQuery($q);
14538 $dbo->execute();
14539
14540 echo 'e4j.ok';
14541 exit;
14542 }
14543
14544 /**
14545 * Hidden task to clean up duplicate records in certain database tables
14546 * due to a double execution of the installation queries. Ghost records,
14547 * if any, are also removed to clean up issues with hanging records.
14548 *
14549 * @since November 4th 2020
14550 * @since 1.16.3 (J) - 1.6.3 (WP)
14551 */
14552 public function clean_duplicate_records()
14553 {
14554 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14555 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14556 }
14557
14558 $dbo = JFactory::getDbo();
14559
14560 $tables_with_duplicates = [
14561 '#__vikbooking_config' => [
14562 'id_key' => 'id',
14563 'compare_key' => 'param',
14564 ],
14565 '#__vikbooking_countries' => [
14566 'id_key' => 'id',
14567 'compare_key' => 'country_3_code',
14568 ],
14569 '#__vikbooking_custfields' => [
14570 'id_key' => 'id',
14571 'compare_key' => 'name',
14572 ],
14573 '#__vikbooking_texts' => [
14574 'id_key' => 'id',
14575 'compare_key' => 'param',
14576 ],
14577 ];
14578
14579 foreach ($tables_with_duplicates as $tblname => $data) {
14580 $doubles = [];
14581 $storage = [];
14582 $rmlist = [];
14583
14584 $q = "SELECT * FROM `{$tblname}` ORDER BY `{$data['id_key']}` DESC;";
14585 $dbo->setQuery($q);
14586 $rows = $dbo->loadAssocList();
14587 if (!$rows) {
14588 echo "<p>No records found in table {$tblname}</p>";
14589 continue;
14590 }
14591
14592 foreach ($rows as $row) {
14593 if (!isset($doubles[$row[$data['compare_key']]])) {
14594 $doubles[$row[$data['compare_key']]] = 0;
14595 }
14596 $doubles[$row[$data['compare_key']]]++;
14597 if (!isset($storage[$row[$data['compare_key']]])) {
14598 $storage[$row[$data['compare_key']]] = [];
14599 }
14600 array_push($storage[$row[$data['compare_key']]], $row[$data['id_key']]);
14601 }
14602
14603 foreach ($doubles as $paramkey => $paramcount) {
14604 if ($paramcount < 2 || !isset($storage[$paramkey]) || count($storage[$paramkey]) < 2 || $paramcount != count($storage[$paramkey])) {
14605 continue;
14606 }
14607 $exceeding = $paramcount - 1;
14608 for ($x = 0; $x < $exceeding; $x++) {
14609 array_push($rmlist, $storage[$paramkey][$x]);
14610 }
14611 }
14612
14613 echo "<p>Total records found in table {$tblname}: " . count($rows) . "</p>";
14614 echo '<p>Total records to remove: ' . count($rmlist) . '</p>';
14615 echo '<pre style="display: none;">'.print_r($rmlist, true).'</pre><br/>';
14616
14617 if (count($rmlist)) {
14618 $q = "DELETE FROM `{$tblname}` WHERE `{$data['id_key']}` IN (" . implode(', ', $rmlist) . ");";
14619 $dbo->setQuery($q);
14620 $dbo->execute();
14621 }
14622 }
14623
14624 /**
14625 * Clean up busy records where the busy relations contain empty booking IDs.
14626 */
14627 $hanging_busy_ids = [];
14628
14629 $q = "SELECT `idbusy` FROM `#__vikbooking_ordersbusy` WHERE `idorder` = 0 OR `idorder` IS NULL;";
14630 $dbo->setQuery($q);
14631 $removelist = $dbo->loadAssocList();
14632 if ($removelist) {
14633 foreach ($removelist as $hanging_busy) {
14634 $hanging_busy_id = (int)$hanging_busy['idbusy'];
14635 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
14636 array_push($hanging_busy_ids, $hanging_busy_id);
14637 }
14638 }
14639 }
14640
14641 // let's check also for ghost records that only occupy the room
14642 $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);";
14643 $dbo->setQuery($q);
14644 $removelist = $dbo->loadAssocList();
14645 if ($removelist) {
14646 foreach ($removelist as $hanging_busy) {
14647 $hanging_busy_id = (int)$hanging_busy['id'];
14648 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
14649 array_push($hanging_busy_ids, $hanging_busy_id);
14650 }
14651 }
14652 }
14653
14654 if ($hanging_busy_ids) {
14655 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id` IN (" . implode(', ', $hanging_busy_ids) . ");";
14656 $dbo->setQuery($q);
14657 $dbo->execute();
14658 }
14659
14660 echo "<p>Total ghost records removed: " . count($hanging_busy_ids) . "</p>";
14661
14662 return;
14663 }
14664
14665 /**
14666 * Hidden task to scan all database tables of VikBooking and Vik Channel Manager
14667 * to ensure the column `id` is defined as a primary key and got an auto-increment
14668 * extra flag properly defined and set. We've noticed that some third-party plugins
14669 * used to migrate WP sites may break the primary keys, and so new records won't get an ID.
14670 *
14671 * @since 1.16.8 (J) - 1.6.8 (WP)
14672 */
14673 public function fix_autoincrement_tables()
14674 {
14675 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14676 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14677 }
14678
14679 $dbo = JFactory::getDbo();
14680
14681 // load all the installed database tables
14682 $tables = $dbo->getTableList();
14683
14684 // get current database prefix
14685 $prefix = $dbo->getPrefix();
14686
14687 // replace prefix with placeholder
14688 $tables = array_map(function($table) use ($prefix)
14689 {
14690 return preg_replace("/^{$prefix}/", '#__', $table);
14691 }, $tables);
14692
14693 // remove all the tables that do not belong to VikBooking/VCM
14694 $tables = array_values(array_filter($tables, function($table)
14695 {
14696 if (preg_match("/^#__vik(?:booking|channelmanager)_config$/", $table))
14697 {
14698 // exclude the configuration table, which will be handled in a different way
14699 return false;
14700 }
14701
14702 return preg_match("/^#__vik(?:booking|channelmanager)_/", $table);
14703 }));
14704
14705 foreach ($tables as $table) {
14706 $columns = $dbo->getTableColumns($table, false);
14707 if (!isset($columns['id']) || empty($columns['id']->Type) || !empty($columns['id']->Extra)) {
14708 continue;
14709 }
14710
14711 echo 'Fixing ' . $table. ' for missing auto-increment<br/><pre>' . print_r($columns['id'], true) . '</pre><br/>';
14712
14713 // set auto-increment and primary key
14714 $dbo->setQuery("ALTER TABLE `{$table}` MODIFY `id` " . $columns['id']->Type . " NOT NULL AUTO_INCREMENT PRIMARY KEY;");
14715 $dbo->execute();
14716
14717 // count next auto-increment
14718 $dbo->setQuery("SELECT MAX(`id`) FROM `{$table}`");
14719 $next_ai = (int) $dbo->loadResult() + 1;
14720
14721 // update next auto-increment value
14722 $dbo->setQuery("ALTER TABLE `{$table}` AUTO_INCREMENT = {$next_ai}");
14723 $dbo->execute();
14724 }
14725 }
14726
14727 /**
14728 * Hidden task to (re-)run the update queries from a given plugin version.
14729 * Useful to ensure the database structure is up-to-date and no update queries went lost.
14730 *
14731 * @since 1.17.6 (J) - 1.7.6 (WP)
14732 */
14733 public function run_update_queries()
14734 {
14735 $app = JFactory::getApplication();
14736 $dbo = JFactory::getDbo();
14737
14738 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14739 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14740 }
14741
14742 $from_version = $app->input->getString('from_version');
14743
14744 if (empty($from_version)) {
14745 VBOHttpDocument::getInstance()->close(400, 'Missing from version value.');
14746 }
14747
14748 // determine the SQL updates directory path
14749 $sql_updates_path = '';
14750 if (VBOPlatformDetection::isWordPress()) {
14751 $sql_updates_path = implode(DIRECTORY_SEPARATOR, [VIKBOOKING_BASE, 'sql', 'update', 'mysql']);
14752 } else {
14753 $sql_updates_path = implode(DIRECTORY_SEPARATOR, [VBO_ADMIN_PATH, 'sql', 'updates', 'mysql']);
14754 }
14755
14756 if (!$sql_updates_path || !is_dir($sql_updates_path)) {
14757 VBOHttpDocument::getInstance()->close(500, 'Could not find SQL updates path.');
14758 }
14759
14760 // read all SQL update files
14761 $sql_update_files = JFolder::files($sql_updates_path, '\.sql', $recurse = false, $full = true);
14762
14763 // filter SQL files with just the valid ones
14764 $sql_update_files = array_filter($sql_update_files, function($sql_update_file) use ($from_version) {
14765 $file_version = basename($sql_update_file, '.sql');
14766 return version_compare($file_version, $from_version, '>=');
14767 });
14768
14769 // sort files by version ascending
14770 usort($sql_update_files, function($a, $b) {
14771 return version_compare(basename($a, '.sql'), basename($b, '.sql'));
14772 });
14773
14774 if (!$sql_update_files) {
14775 VBOHttpDocument::getInstance()->close(500, sprintf('Could not find any suitable SQL update file from version %s.', $from_version));
14776 }
14777
14778 $success_queries = 0;
14779
14780 foreach ($sql_update_files as $file) {
14781 $handle = fopen($file, 'r');
14782
14783 $bytes = '';
14784 while (!feof($handle)) {
14785 $bytes .= fread($handle, 8192);
14786 }
14787
14788 fclose($handle);
14789
14790 if (VBOPlatformDetection::isWordPress()) {
14791 $queries_list = JDatabaseHelper::splitSql($bytes);
14792 } else {
14793 try {
14794 if (class_exists('JDatabaseDriver')) {
14795 $queries_list = JDatabaseDriver::splitSql($bytes);
14796 } else {
14797 $queries_list = Joomla\Database\DatabaseDriver::splitSql($bytes);
14798 }
14799 } catch(Throwable $e) {
14800 $app->enqueueMessage(sprintf('Error splitting queries: %s', $e->getMessage()), 'error');
14801 $queries_list = [];
14802 }
14803 }
14804
14805 foreach ($queries_list as $q) {
14806 try {
14807 $dbo->setQuery($q);
14808 $result = $dbo->execute();
14809 } catch (Exception $e) {
14810 $result = false;
14811 $app->enqueueMessage(sprintf('Error executing query: %s', $e->getMessage()), 'warning');
14812 }
14813
14814 if ($result) {
14815 $success_queries++;
14816 }
14817 }
14818 }
14819
14820 if ($success_queries) {
14821 $app->enqueueMessage(sprintf('Successful queries: %d', $success_queries), 'success');
14822 }
14823
14824 // send response to output
14825 echo '<pre>'.print_r($sql_update_files, true).'</pre><br/>';
14826 }
14827
14828 /**
14829 * Loads a specific admin widget ID and executes the requested method.
14830 * Useful for loading a newly added widget, or to execute custom methods.
14831 *
14832 * @see this is an AJAX endpoint.
14833 *
14834 * @since 1.14 (J) - 1.4.0 (WP)
14835 * @since 1.15 (J) - 1.5.0 (WP) widget callback can return values rather than just echoing.
14836 * @since 1.16.5 (J) - 1.6.5 (WP) widgets are rendered within a try-catch statement.
14837 */
14838 public function exec_admin_widget()
14839 {
14840 if (!JSession::checkToken()) {
14841 // missing CSRF-proof token
14842 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14843 }
14844
14845 $widget_id = VikRequest::getString('widget_id', '', 'request');
14846 $call = VikRequest::getString('call', '', 'request');
14847 $return = VikRequest::getInt('return', 0, 'request');
14848 $vbo_page = VikRequest::getString('vbo_page', '', 'request');
14849 $vbo_uri = VikRequest::getString('vbo_uri', '', 'request');
14850 $multitask = VikRequest::getInt('multitask', 0, 'request');
14851
14852 if (empty($widget_id)) {
14853 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget ID');
14854 }
14855
14856 if (empty($call) || !is_string($call)) {
14857 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget Callback');
14858 }
14859
14860 // invoke admin widgets helper
14861 $widgets_helper = VikBooking::getAdminWidgetsInstance();
14862 $widget = $widgets_helper->getWidget($widget_id);
14863
14864 if ($widget === false) {
14865 VBOHttpDocument::getInstance()->close(404, 'Requested Admin Widget not found');
14866 }
14867
14868 if (!method_exists($widget, $call) || !is_callable(array($widget, $call))) {
14869 VBOHttpDocument::getInstance()->close(403, 'Admin Widget Callback not found or not callable');
14870 }
14871
14872 // get the multitask parser object
14873 $parser = VBOMultitaskParser::getInstance($vbo_page, $vbo_uri);
14874
14875 // check if arguments should be passed
14876 $call_args = [];
14877 if ($multitask && $call === 'render') {
14878 // build the multitask data object and inject it to the args as the first index
14879 $call_args[] = $parser->getData();
14880
14881 // bind options within the widget, if any
14882 $widget->bindOptions($call_args[0]);
14883 } else {
14884 // always bind multitask options, if any
14885 $widget->bindOptions($parser->getOptions());
14886 }
14887
14888 try {
14889 if ($return) {
14890 // invoke the widget's method and get the value returned
14891 $widget_response = $call_args ? call_user_func_array([$widget, $call], $call_args) : $widget->{$call}();
14892 } else {
14893 // invoke the widget's method within a buffer
14894 ob_start();
14895 if ($call_args) {
14896 $res = call_user_func_array([$widget, $call], $call_args);
14897 } else {
14898 $widget->{$call}();
14899 }
14900 $widget_response = ob_get_contents();
14901 ob_end_clean();
14902 }
14903 } catch (Throwable $e) {
14904 VBOHttpDocument::getInstance()->close($e->getCode() ?: 500, sprintf("%s\n%s at line %d", $e->getMessage(), $e->getFile(), $e->getLine()));
14905 } catch (Exception $e) {
14906 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
14907 }
14908
14909 // prepare response object with a property equal to the called method
14910 $response = new stdClass;
14911 $response->{$call} = $widget_response;
14912
14913 // output the JSON encoded response and exit
14914 VBOHttpDocument::getInstance()->json($response);
14915 }
14916
14917 /**
14918 * Updates the map of admin widgets.
14919 *
14920 * @throws Exception this is an AJAX endpoint.
14921 *
14922 * @since 1.4.0
14923 */
14924 public function save_admin_widgets()
14925 {
14926 if (!JSession::checkToken()) {
14927 // missing CSRF-proof token
14928 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14929 }
14930
14931 // make sure permissions are sufficient
14932 if (!JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
14933 VBOHttpDocument::getInstance()->close(403, 'You are not authorized to modify the widgets.');
14934 }
14935
14936 $psections = VikRequest::getVar('sections', array(), 'request', 'array');
14937 if (!is_array($psections) || !count($psections)) {
14938 VBOHttpDocument::getInstance()->close(500, 'No sections found in map');
14939 }
14940
14941 // request values are all converted to arrays, so restore the object styling
14942 $psections = json_decode(json_encode($psections));
14943
14944 // update map
14945 $result = VikBooking::getAdminWidgetsInstance()->updateWidgetsMap($psections);
14946
14947 $response = new stdClass;
14948 $response->status = (int)$result;
14949
14950 // output the JSON encoded response and exit
14951 VBOHttpDocument::getInstance()->json($response);
14952 }
14953
14954 /**
14955 * Restores the default admin widgets map.
14956 *
14957 * @since 1.4.0
14958 */
14959 public function reset_admin_widgets()
14960 {
14961 // reset map and redirect to dashboard
14962 VikBooking::getAdminWidgetsInstance()->restoreDefaultWidgetsMap();
14963
14964 JFactory::getApplication()->redirect('index.php?option=com_vikbooking');
14965 exit;
14966 }
14967
14968 /**
14969 * Updates the welcome message status for the widget's customizer via AJAX.
14970 *
14971 * @since 1.4.0
14972 */
14973 public function admin_widgets_welcome()
14974 {
14975 if (!JSession::checkToken()) {
14976 // missing CSRF-proof token
14977 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14978 }
14979
14980 $hide_welcome = VikRequest::getInt('hide_welcome', 0, 'request');
14981 // update configuration value
14982 VikBooking::getAdminWidgetsInstance()->updateWelcome($hide_welcome);
14983
14984 $response = new stdClass;
14985 $response->status = $hide_welcome;
14986
14987 // output the JSON encoded response and exit
14988 VBOHttpDocument::getInstance()->json($response);
14989 }
14990
14991 public function newcondtext()
14992 {
14993 VikBookingHelper::printHeader("11");
14994
14995 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecondtext'));
14996
14997 parent::display();
14998
14999 if (VikBooking::showFooter()) {
15000 VikBookingHelper::printFooter();
15001 }
15002 }
15003
15004 public function editcondtext()
15005 {
15006 VikBookingHelper::printHeader("11");
15007
15008 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecondtext'));
15009
15010 parent::display();
15011
15012 if (VikBooking::showFooter()) {
15013 VikBookingHelper::printFooter();
15014 }
15015 }
15016
15017 public function cancelcondtext()
15018 {
15019 JFactory::getApplication()->redirect('index.php?option=com_vikbooking&task=config&tab=7');
15020 }
15021
15022 public function createcondtext()
15023 {
15024 if (!JSession::checkToken()) {
15025 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15026 }
15027
15028 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
15029 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15030 }
15031
15032 $this->_doCreateCondText();
15033 }
15034
15035 public function createcondtextstay()
15036 {
15037 if (!JSession::checkToken()) {
15038 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15039 }
15040
15041 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
15042 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15043 }
15044
15045 $this->_doCreateCondText(true);
15046 }
15047
15048 private function _doCreateCondText($stay = false)
15049 {
15050 $dbo = JFactory::getDbo();
15051 $app = JFactory::getApplication();
15052 $rules_helper = VikBooking::getConditionalRulesInstance();
15053 $rules_list = $rules_helper->composeRulesParamsFromRequest();
15054
15055 $condtextname = VikRequest::getString('condtextname', '', 'request');
15056 $condtexttkn = VikRequest::getString('condtexttkn', '', 'request');
15057 $msg = VikRequest::getString('msg', '', 'request', VIKREQUEST_ALLOWRAW);
15058 $debug = VikRequest::getInt('debug', 0, 'request');
15059 if (empty($condtextname)) {
15060 $condtextname = date('Y-m-dHis');
15061 $condtexttkn = '{condition: ' . date('YmdHis') . '}';
15062 }
15063
15064 $existing_tokens = $rules_helper->getSpecialTags();
15065 if (count($existing_tokens) && isset($existing_tokens[$condtexttkn])) {
15066 VikError::raiseWarning('', 'Another conditional text with the same special tag already exists');
15067 $app->redirect('index.php?option=com_vikbooking&task=newcondtext');
15068 exit;
15069 }
15070
15071 $data = new stdClass;
15072 $data->name = $condtextname;
15073 $data->token = $condtexttkn;
15074 $data->rules = json_encode($rules_list);
15075 $data->msg = $msg;
15076 $data->lastupd = JDate::getInstance()->toSql();
15077 $data->debug = $debug;
15078
15079 $dbo->insertObject('#__vikbooking_condtexts', $data, 'id');
15080
15081 if (isset($data->id)) {
15082 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
15083 }
15084
15085 if (!$stay || !isset($data->id)) {
15086 $this->cancelcondtext();
15087 exit;
15088 }
15089
15090 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $data->id);
15091 }
15092
15093 public function updatecondtext()
15094 {
15095 if (!JSession::checkToken()) {
15096 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15097 }
15098
15099 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
15100 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15101 }
15102
15103 $this->_doUpdateCondText();
15104 }
15105
15106 public function updatecondtextstay()
15107 {
15108 if (!JSession::checkToken()) {
15109 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15110 }
15111
15112 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
15113 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15114 }
15115
15116 $this->_doUpdateCondText(true);
15117 }
15118
15119 private function _doUpdateCondText($stay = false)
15120 {
15121 $dbo = JFactory::getDbo();
15122 $app = JFactory::getApplication();
15123 $rules_helper = VikBooking::getConditionalRulesInstance();
15124 $rules_list = $rules_helper->composeRulesParamsFromRequest();
15125
15126 $pwhere = VikRequest::getInt('where', '', 'request');
15127 $condtextname = VikRequest::getString('condtextname', '', 'request');
15128 $condtexttkn = VikRequest::getString('condtexttkn', '', 'request');
15129 $msg = VikRequest::getString('msg', '', 'request', VIKREQUEST_ALLOWRAW);
15130 $debug = VikRequest::getInt('debug', 0, 'request');
15131 if (empty($condtextname)) {
15132 $condtextname = date('Y-m-dHis');
15133 $condtexttkn = '{condition: ' . date('YmdHis') . '}';
15134 }
15135
15136 $existing_tokens = $rules_helper->getSpecialTags();
15137 if (count($existing_tokens) && isset($existing_tokens[$condtexttkn]) && ($existing_tokens[$condtexttkn]['id'] != $pwhere)) {
15138 VikError::raiseWarning('', 'Another conditional text with the same special tag already exists (' . $existing_tokens[$condtexttkn]['name'] . ')');
15139 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $pwhere);
15140 exit;
15141 }
15142
15143 $data = new stdClass;
15144 $data->id = $pwhere;
15145 $data->name = $condtextname;
15146 $data->token = $condtexttkn;
15147 $data->rules = json_encode($rules_list);
15148 $data->msg = $msg;
15149 $data->lastupd = JDate::getInstance()->toSql();
15150 $data->debug = $debug;
15151
15152 $dbo->updateObject('#__vikbooking_condtexts', $data, 'id');
15153
15154 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
15155
15156 if (!$stay) {
15157 $this->cancelcondtext();
15158 exit;
15159 }
15160
15161 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $data->id);
15162 }
15163
15164 public function removecondtext()
15165 {
15166 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
15167 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15168 }
15169
15170 $dbo = JFactory::getDbo();
15171 $ids = VikRequest::getVar('cid', array());
15172
15173 VikBooking::getConditionalRulesInstance(true);
15174 $templates = VikBookingHelperConditionalRules::getTemplateFilesPaths();
15175
15176 foreach ($ids as $d) {
15177 $q = "SELECT `token` FROM `#__vikbooking_condtexts` WHERE `id`=" . (int)$d . ";";
15178 $dbo->setQuery($q);
15179 $dbo->execute();
15180 if (!$dbo->getNumRows()) {
15181 continue;
15182 }
15183 $special_tag = $dbo->loadResult();
15184
15185 // remove the token from each template file if it was used before
15186 if (!empty($special_tag)) {
15187 // remove token from all template files
15188 foreach ($templates as $tkey => $tpath) {
15189 // get requested file content
15190 $fcontent = VikBookingHelperConditionalRules::getTemplateFileCode($tkey);
15191 if (empty($fcontent) || !is_string($fcontent)) {
15192 break;
15193 }
15194 // remove tag from code content
15195 $fcontent = str_replace($special_tag, '', $fcontent);
15196 // update the file code
15197 VikBookingHelperConditionalRules::writeTemplateFileCode($tkey, $fcontent);
15198 }
15199 }
15200
15201 // delete the record
15202 $q = "DELETE FROM `#__vikbooking_condtexts` WHERE `id`=" . (int)$d . ";";
15203 $dbo->setQuery($q);
15204 $dbo->execute();
15205 }
15206
15207 $this->cancelcondtext();
15208 }
15209
15210 /**
15211 * AJAX endpoint to update one template file with the given tag or styles.
15212 * A JSON response will be echoed by exiting the process.
15213 */
15214 public function condtext_update_tmpl()
15215 {
15216 VikBooking::getConditionalRulesInstance(true);
15217
15218 $tagaction = VikRequest::getString('tagaction', '', 'request');
15219 $tag = VikRequest::getString('tag', '', 'request');
15220 $file = VikRequest::getString('file', '', 'request', VIKREQUEST_ALLOWRAW);
15221 $newcontent = VikRequest::getString('newcontent', '', 'request', VIKREQUEST_ALLOWRAW);
15222 $custom_classes = VikRequest::getVar('custom_classes', array(), 'request', 'array');
15223
15224 $allowed_actions = array(
15225 'add',
15226 'remove',
15227 'styles',
15228 'restore',
15229 );
15230
15231 if (empty($tagaction) || empty($file) || !in_array($tagaction, $allowed_actions)) {
15232 throw new Exception("Invalid request submitted", 500);
15233 }
15234
15235 if (in_array($tagaction, array('add', 'remove')) && empty($tag)) {
15236 throw new Exception("Invalid request submitted - missing tag", 500);
15237 }
15238
15239 if (in_array($tagaction, array('add', 'styles')) && empty($newcontent)) {
15240 throw new Exception("Invalid request submitted - missing new HTML content", 500);
15241 }
15242
15243 if ($tagaction == 'styles' && (!is_array($custom_classes) || !count($custom_classes))) {
15244 throw new Exception("No custom CSS classes to parse", 500);
15245 }
15246
15247 if ($tagaction == 'restore') {
15248 // immediately restore the requested file to avoid script interruptions
15249 VikBookingHelperConditionalRules::restoreTemplateFileCode($file);
15250 }
15251
15252 // get requested file content
15253 $fcontent = VikBookingHelperConditionalRules::getTemplateFileCode($file);
15254 if (empty($fcontent) || !is_string($fcontent)) {
15255 throw new Exception("File not found or its code is unreadable", 404);
15256 }
15257
15258 if ($tagaction == 'remove') {
15259 // remove tag from code content
15260 $fcontent = str_replace($tag, '', $fcontent);
15261 } elseif ($tagaction == 'add') {
15262 // add tag to code content in the same exact position
15263 $fcontent = VikBookingHelperConditionalRules::addTagByComparingSources($tag, $file, $newcontent, $fcontent);
15264 } elseif ($tagaction == 'styles') {
15265 // apply the same styling rules
15266 $fcontent = VikBookingHelperConditionalRules::addStylesByComparingSources($custom_classes, $file, $newcontent, $fcontent);
15267 }
15268
15269 // update the file code
15270 $res = VikBookingHelperConditionalRules::writeTemplateFileCode($file, $fcontent);
15271
15272 if (!$res) {
15273 throw new Exception("Could not update the source code of the template file", 500);
15274 }
15275
15276 // parse new HTML content
15277 $newhtmls = VikBookingHelperConditionalRules::getTemplateFilesContents($file);
15278 if (!is_array($newhtmls) || !isset($newhtmls[$file])) {
15279 throw new Exception("Could not parse new template file content", 404);
15280 }
15281
15282 // trigger backup/mirroring, if available
15283 if (VBOPlatformDetection::isWordPress()) {
15284 VikBookingUpdateManager::storeTemplateContent($file, $newhtmls[$file]);
15285 }
15286
15287 // build output
15288 $output = new stdClass;
15289 $output->newhtml = $newhtmls[$file];
15290 $output->log = VikBookingHelperConditionalRules::getEditingLog();
15291
15292 echo json_encode($output);
15293 exit;
15294 }
15295
15296 /**
15297 * AJAX endpoint to invoke methods of the geocoding helper.
15298 */
15299 public function geocoding_endpoint()
15300 {
15301 $geo = VikBooking::getGeocodingInstance();
15302 $callback = VikRequest::getString('callback', '', 'request');
15303
15304 if (empty($callback) || !method_exists($geo, $callback) || !is_callable(array($geo, $callback))) {
15305 throw new Exception("Callback not available", 403);
15306 }
15307
15308 // invoke requested method
15309 $res = $geo->{$callback}();
15310
15311 // prepare response
15312 $response = new stdClass;
15313 $response->{$callback} = $res;
15314
15315 echo json_encode($response);
15316 exit;
15317 }
15318
15319 public function refundtn()
15320 {
15321 //modal box, so we do not set menu or footer
15322
15323 VikRequest::setVar('view', VikRequest::getCmd('view', 'refundtn'));
15324
15325 parent::display();
15326 }
15327
15328 public function do_refundtn()
15329 {
15330 $dbo = JFactory::getDbo();
15331 $app = JFactory::getApplication();
15332
15333 $bid = VikRequest::getInt('bid', 0, 'request');
15334 $amount = VikRequest::getFloat('amount', 0, 'request');
15335 $refund_reason = VikRequest::getString('refund_reason', '', 'request');
15336 $tmpl = VikRequest::getString('tmpl', '', 'request');
15337 $nav_suffix = $tmpl == 'component' ? '&tmpl=component' : '';
15338
15339 $currencysymb = VikBooking::getCurrencySymb();
15340
15341 if (empty($bid) || $amount <= 0) {
15342 VikError::raiseWarning('', JText::translate('VBO_PLEASE_FILL_FIELDS'));
15343 $app->redirect('index.php?option=com_vikbooking');
15344 exit;
15345 }
15346
15347 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $bid . " AND `status`!='standby';";
15348 $dbo->setQuery($q);
15349 $row = $dbo->loadAssoc();
15350 if (!$row) {
15351 VikError::raiseWarning('', 'Booking not found');
15352 $app->redirect('index.php?option=com_vikbooking');
15353 exit;
15354 }
15355
15356 // get booking history instance
15357 $history_obj = VikBooking::getBookingHistoryInstance();
15358 $history_obj->setBid($row['id']);
15359
15360 // get payment information
15361 $payment = VikBooking::getPayment($row['idpayment']);
15362 $tn_driver = is_array($payment) ? $payment['file'] : null;
15363
15364 // transaction data validation callback
15365 $tn_data_callback = function($data) use ($tn_driver) {
15366 return (is_object($data) && isset($data->driver) && basename($data->driver, '.php') == basename($tn_driver, '.php'));
15367 };
15368 // get previous transactions
15369 $prev_tn_data = $history_obj->getEventsWithData(array('P0', 'PN'), $tn_data_callback);
15370
15371 if (!is_array($prev_tn_data) || !count($prev_tn_data)) {
15372 // no previous transactions found
15373 VikError::raiseWarning('', 'No previous transactions found, unable to issue the refund');
15374 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15375 exit;
15376 }
15377
15378 // push refund information for the payment gateway
15379 $row['total_to_refund'] = $amount;
15380 $row['transaction'] = $prev_tn_data;
15381 $row['refund_reason'] = $refund_reason;
15382
15383 // push the transaction currency information
15384 $row['transaction_currency'] = VikBooking::getCurrencyCodePp();
15385
15386 /**
15387 * Trigger event to allow third-party plugins to manipulate the transaction data.
15388 *
15389 * @since 1.18.5 (J) - 1.8.5 (WP)
15390 */
15391 VBOFactory::getPlatform()->getDispatcher()->trigger('onInitRefundTransaction', [&$row, &$payment['params']]);
15392
15393 if (VBOPlatformDetection::isWordPress()) {
15394 /**
15395 * @wponly The payment gateway is loaded
15396 * through the apposite dispatcher.
15397 */
15398 JLoader::import('adapter.payment.dispatcher');
15399 $obj = JPaymentDispatcher::getInstance('vikbooking', $payment['file'], $row, $payment['params']);
15400 } else {
15401 /**
15402 * @joomlaonly The Payment Factory library will invoke the gateway.
15403 *
15404 * @since 1.14.3
15405 */
15406 require_once VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'payments' . DIRECTORY_SEPARATOR . 'libraries' . DIRECTORY_SEPARATOR . 'factory.php';
15407 $obj = VBOPaymentFactory::getPaymentInstance($payment['file'], $row, $payment['params']);
15408 }
15409
15410 if (!method_exists($obj, 'isRefundSupported') || !$obj->isRefundSupported()) {
15411 // refund not supported
15412 VikError::raiseWarning('', 'The selected payment method does not support refunds');
15413 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15414 exit;
15415 }
15416
15417 // perform the refund transaction
15418 $array_result = $obj->refund();
15419
15420 if ($array_result['verified'] != 1) {
15421 // raise warning by getting the message
15422 if (!empty($array_result['log']) && is_string($array_result['log'])) {
15423 VikError::raiseWarning('', $array_result['log']);
15424 } else {
15425 VikError::raiseWarning('', 'Operation failed');
15426 }
15427 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15428 exit;
15429 }
15430
15431 /**
15432 * New payment plugins can return the total amount refunded ('tot_paid').
15433 *
15434 * @since 1.15.4 (J) - 1.5.10 (WP)
15435 */
15436 if (!empty($array_result['tot_paid'])) {
15437 // overwrite the requested amount with the returned one
15438 $amount = (float)$array_result['tot_paid'];
15439 }
15440
15441 /**
15442 * The history event extra data will contain the "amount_paid" (refunded).
15443 *
15444 * @since 1.16.9 (J) - 1.6.9 (WP)
15445 */
15446 $history_obj->setExtraData([
15447 'amount_paid' => $amount,
15448 ]);
15449
15450 // update total paid, total and refund columns for the booking
15451 $booking = new stdClass;
15452 $booking->id = $row['id'];
15453 if ($row['totpaid'] > 0) {
15454 $booking->totpaid = $row['totpaid'] - $amount;
15455 }
15456 if ($row['total'] > 0) {
15457 $booking->total = $row['total'] - $amount;
15458 }
15459 $booking->refund = (float)$row['refund'] + $amount;
15460 // update record in db
15461 $dbo->updateObject('#__vikbooking_orders', $booking, 'id');
15462
15463 // store the refund event
15464 $event_descr = [
15465 '(' . $payment['name'] . ')',
15466 $refund_reason,
15467 $currencysymb . ' ' . VikBooking::numberFormat($amount),
15468 ];
15469 $history_obj->store('RF', implode("\n", $event_descr));
15470
15471 // display success message and redirect
15472 $app->enqueueMessage(JText::translate('VBO_REFUND_SUCCESS'));
15473 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . '&success=1' . $nav_suffix);
15474 exit;
15475 }
15476
15477 /**
15478 * AJAX upload endpoint for media files.
15479 *
15480 * @return void
15481 *
15482 * @throws Exception
15483 *
15484 * @since 1.15.0 (J) - 1.5.0 (WP)
15485 */
15486 public function upload_media_file()
15487 {
15488 $input = JFactory::getApplication()->input;
15489
15490 // allowed types
15491 $type = $input->getString('type', '');
15492 $mask = 'png,apng,jpg,jpeg,bmp,heic,webp,gif,ico,svg';
15493
15494 if ($type != 'image') {
15495 $mask .= ',zip,rar,pdf,doc,docx,rtf,odt,pages,xls,xlsx,csv,ods,numbers,txt,md';
15496 }
15497
15498 // response object
15499 $result = new stdClass;
15500 $result->status = 0;
15501
15502 try
15503 {
15504 // get file from request
15505 $file = $input->files->get('file', array(), 'array');
15506
15507 // try to upload the file
15508 $result = VikBooking::uploadFileFromRequest($file, VBO_MEDIA_PATH, $mask);
15509 $result->status = 1;
15510
15511 $result->size = JHtml::fetch('number.bytes', filesize($result->path), 'auto', 0);
15512 $result->url = str_replace(DIRECTORY_SEPARATOR, '/', str_replace(VBO_MEDIA_PATH . DIRECTORY_SEPARATOR, VBO_MEDIA_URI, $result->path));
15513 }
15514 catch (Exception $e)
15515 {
15516 $result->error = $e->getMessage();
15517 $result->code = $e->getCode();
15518 }
15519
15520 echo json_encode($result);
15521 exit;
15522 }
15523
15524 /**
15525 * AJAX endpoint to invoke a report object's method.
15526 *
15527 * @return void
15528 *
15529 * @since 1.15.0 (J) - 1.5.0 (WP)
15530 * @since 1.18.6 (J) - 1.8.6 (WP) added support for "call_args".
15531 */
15532 public function invoke_report()
15533 {
15534 $app = JFactory::getApplication();
15535
15536 $report_name = $app->input->getString('report', '');
15537 $report_call = $app->input->getString('call', '');
15538 $call_args = $app->input->get('call_args', [], 'array');
15539 $params = $app->input->get('params', [], 'array');
15540
15541 if (empty($report_name)) {
15542 VBOHttpDocument::getInstance($app)->close(400, 'Missing report name');
15543 }
15544
15545 if (empty($report_call)) {
15546 VBOHttpDocument::getInstance($app)->close(400, 'Missing report call');
15547 }
15548
15549 // get requested report instance
15550 $report = VikBooking::getReportInstance($report_name);
15551 if (!$report) {
15552 VBOHttpDocument::getInstance($app)->close(404, 'Report not found');
15553 }
15554
15555 if (!method_exists($report, $report_call) || !is_callable(array($report, $report_call))) {
15556 VBOHttpDocument::getInstance($app)->close(403, sprintf('Cannot call [%s] on report', $report_call));
15557 }
15558
15559 try {
15560 // call on report's method
15561 if ($call_args) {
15562 $result = call_user_func_array([$report, $report_call], $call_args);
15563 } else {
15564 $result = $report->{$report_call}($params);
15565 }
15566 } catch (Exception $e) {
15567 VBOHttpDocument::getInstance($app)->close($e->getCode() ?: 500, $e->getMessage());
15568 }
15569
15570 if (is_null($result)) {
15571 VBOHttpDocument::getInstance($app)->close(400, 'Null response');
15572 }
15573
15574 if (is_scalar($result)) {
15575 // wrap result within an array for a JSON encoded response
15576 VBOHttpDocument::getInstance($app)->json([$result]);
15577 }
15578
15579 // output the JSON encoded array/object returned
15580 VBOHttpDocument::getInstance($app)->json($result);
15581 }
15582
15583 /**
15584 * Handles requests for the multitask widgets panel.
15585 *
15586 * @see this is an AJAX endpoint.
15587 *
15588 * @since 1.15.0 (J) - 1.5.0 (WP)
15589 * @since 1.16.5 (J) - 1.6.5 (WP) widgets are rendered within a try-catch statement.
15590 */
15591 public function exec_multitask_widgets()
15592 {
15593 if (!JSession::checkToken()) {
15594 // missing CSRF-proof token
15595 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15596 }
15597
15598 $call = VikRequest::getString('call', '', 'request');
15599 $call_args = VikRequest::getVar('call_args', array(), 'request', 'array');
15600
15601 if (empty($call)) {
15602 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget Callback');
15603 }
15604
15605 // invoke admin widgets helper
15606 $widgets_helper = VikBooking::getAdminWidgetsInstance();
15607
15608 if (!method_exists($widgets_helper, $call) || !is_callable(array($widgets_helper, $call))) {
15609 VBOHttpDocument::getInstance()->close(403, 'Admin Widgets Callback not found or not callable');
15610 }
15611
15612 try {
15613 // invoke the helper's method and get the value returned
15614 if (is_array($call_args) && count($call_args)) {
15615 $result = call_user_func_array(array($widgets_helper, $call), $call_args);
15616 } else {
15617 $result = $widgets_helper->{$call}();
15618 }
15619 } catch (Throwable $e) {
15620 VBOHttpDocument::getInstance()->close($e->getCode() ?: 500, sprintf("%s\n%s at line %d", $e->getMessage(), $e->getFile(), $e->getLine()));
15621 } catch (Exception $e) {
15622 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
15623 }
15624
15625 // prepare response object with the result property
15626 $response = new stdClass;
15627 $response->result = $result;
15628
15629 // output the JSON response and exit
15630 VBOHttpDocument::getInstance()->json($response);
15631 }
15632
15633 /**
15634 * Handles requests for displaying a browser notification being dispatched.
15635 *
15636 * @see this is an AJAX endpoint.
15637 *
15638 * @since 1.15.0 (J) - 1.5.0 (WP)
15639 */
15640 public function notification_displayer()
15641 {
15642 $payload_str = VikRequest::getString('payload', '', 'request', VIKREQUEST_ALLOWRAW);
15643
15644 if (empty($payload_str)) {
15645 VBOHttpDocument::getInstance()->close(500, 'Empty notification payload');
15646 }
15647
15648 // attempt to decode the notification payload
15649 $payload = json_decode($payload_str);
15650
15651 if (!is_object($payload)) {
15652 VBOHttpDocument::getInstance()->close(500, 'Could not decode notification payload: ' . $payload_str);
15653 }
15654
15655 // get notification displayer for this type of notification
15656 $displayer = VBONotificationBuilder::getInstance($payload)->getDisplayer();
15657 if (!$displayer) {
15658 VBOHttpDocument::getInstance()->close(500, 'Could not build notification display data from payload: ' . $payload_str);
15659 }
15660
15661 // compose the notification display data object
15662 try {
15663 $notif_data = $displayer->getData();
15664 if (!$notif_data) {
15665 throw new Exception('Error building the notification display data', 500);
15666 }
15667 } catch (Exception $e) {
15668 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
15669 }
15670
15671 // output the JSON response and exit
15672 VBOHttpDocument::getInstance()->json($notif_data);
15673 }
15674
15675 /**
15676 * Handles requests for watching widgets data and getting
15677 * new events to trigger browser notifications.
15678 *
15679 * @see this is an AJAX endpoint.
15680 *
15681 * @since 1.15.0 (J) - 1.5.0 (WP)
15682 * @since 1.16.8 (J) - 1.6.8 (WP) introduced notification events.
15683 */
15684 public function widgets_watch_data()
15685 {
15686 if (!JSession::checkToken()) {
15687 // missing CSRF-proof token
15688 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15689 }
15690
15691 $app = JFactory::getApplication();
15692
15693 $watch_data_str = $app->input->get('watch_data', '', 'raw');
15694 $pushed_data_str = $app->input->get('pushed_data', '[]', 'raw');
15695
15696 if (empty($watch_data_str)) {
15697 VBOHttpDocument::getInstance()->close(500, 'Empty watch-data payload');
15698 }
15699
15700 // attempt to decode the watch-data payload
15701 $watch_data = json_decode($watch_data_str, true);
15702
15703 if (!$watch_data) {
15704 VBOHttpDocument::getInstance()->close(500, 'Could not decode watch-data payload: ' . $watch_data_str);
15705 }
15706
15707 // check if any pushed data was set
15708 $pushed_data = (array)json_decode($pushed_data_str, true);
15709
15710 // container for new notifications
15711 $notifs_pool = [];
15712
15713 // container for the events data to dispatch
15714 $events_pool = [];
15715
15716 // get admin widgets helper
15717 $widgets_helper = VikBooking::getAdminWidgetsInstance();
15718
15719 foreach ($watch_data as $widget_id => $data) {
15720 // invoke admin widget (with no pre-loading)
15721 $widget_instance = $widgets_helper->getWidget($widget_id);
15722 if (!$widget_instance) {
15723 continue;
15724 }
15725
15726 // build the widget watch data object
15727 $widget_watch_data = VBONotificationWatchdata::getInstance($data)->setPushedData($pushed_data);
15728
15729 // check if the widget needs to emit browser notifications
15730 list($watch_next, $notifications) = $widget_instance->getNotifications($widget_watch_data);
15731
15732 // check if the widget needs to emit JavaScript events
15733 $events = $widget_instance->getNotificationEvents($widget_watch_data);
15734
15735 if ($watch_next) {
15736 // update next watch-data object for this widget
15737 $watch_data[$widget_id] = $watch_next;
15738 }
15739
15740 if (is_array($notifications) && $notifications) {
15741 // merge notifications
15742 $notifs_pool = array_merge($notifs_pool, $notifications);
15743 }
15744
15745 if (is_array($events) && $events) {
15746 // push notification events for this widget
15747 $events_pool[] = $events;
15748 }
15749 }
15750
15751 // build the response object
15752 $response = new stdClass;
15753 $response->watch_data = $watch_data;
15754 $response->notifications = $notifs_pool;
15755 $response->events = $events_pool;
15756
15757 // output the JSON response and exit
15758 VBOHttpDocument::getInstance()->json($response);
15759 }
15760
15761 /**
15762 * Outputs a list of CSS assets required to render the admin widgets
15763 * externally from Vik Booking. Useful i.e. to Vik Channel Manager.
15764 *
15765 * @see this is an AJAX endpoint.
15766 *
15767 * @since 1.16.0 (J) - 1.6.0 (WP)
15768 */
15769 public function widgets_get_assets()
15770 {
15771 // list of needed CSS asset details
15772 $assets_pool = [];
15773
15774 // appearance preference assets (one or none)
15775 $app_pref_asset = VikBooking::loadAppearancePreferenceAssets($get_info = true);
15776
15777 if (VBOPlatformDetection::isWordPress()) {
15778 // WordPress (main CSS)
15779 $assets_pool[] = [
15780 'rel' => 'stylesheet',
15781 'id' => 'vbo-style-css',
15782 'href' => VIKBOOKING_ADMIN_ASSETS_URI . 'vikbooking.css?ver=' . VIKBOOKING_SOFTWARE_VERSION,
15783 'media' => 'all',
15784 ];
15785
15786 if (is_array($app_pref_asset) && !empty($app_pref_asset['href'])) {
15787 // appearance preference CSS
15788 $assets_pool[] = [
15789 'rel' => 'stylesheet',
15790 'id' => (!empty($app_pref_asset['id']) ? $app_pref_asset['id'] : rand()),
15791 'href' => $app_pref_asset['href'] . '?ver=' . VIKBOOKING_SOFTWARE_VERSION,
15792 'media' => 'all',
15793 ];
15794 }
15795 } else {
15796 // Joomla (main CSS)
15797 $assets_pool[] = [
15798 'rel' => 'stylesheet',
15799 'id' => 'vbo-style-css',
15800 'href' => VBO_ADMIN_URI . 'vikbooking.css?' . VIKBOOKING_SOFTWARE_VERSION,
15801 'media' => 'all',
15802 ];
15803
15804 if (is_array($app_pref_asset) && !empty($app_pref_asset['href'])) {
15805 // appearance preference CSS
15806 $assets_pool[] = [
15807 'rel' => 'stylesheet',
15808 'id' => (!empty($app_pref_asset['id']) ? $app_pref_asset['id'] : rand()),
15809 'href' => $app_pref_asset['href'] . '?' . VIKBOOKING_SOFTWARE_VERSION,
15810 'media' => 'all',
15811 ];
15812 }
15813 }
15814
15815 // output the JSON response and exit
15816 VBOHttpDocument::getInstance()->json($assets_pool);
15817 }
15818 }
15819