PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.7
VikBooking Hotel Booking Engine & PMS v1.8.7
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.7, at admin/controller.php

15,769 lines 590.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * @package VikBooking
4 * @subpackage com_vikbooking
5 * @author Alessio Gaggii - e4j - Extensionsforjoomla.com
6 * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
7 * @license GNU General Public License version 2 or later; see LICENSE
8 * @link https://vikwp.com
9 */
10
11 defined('ABSPATH') or die('No script kiddies please!');
12
13 // import Joomla controller library
14 jimport('joomla.application.component.controller');
15
16 class VikBookingController extends JControllerVikBooking
17 {
18 /**
19 * Default controller's method when no task is defined,
20 * or no method exists for that task. If a View is requested.
21 * attempts to set it, otherwise sets the default View.
22 */
23 public function display($cachable = false, $urlparams = array()) {
24
25 $view = VikRequest::getVar('view', '');
26 $header_val = '';
27
28 if (!empty($view)) {
29 $header_val = $view;
30 VikRequest::setVar('view', $view);
31 } else {
32 $header_val = '18';
33 VikRequest::setVar('view', 'dashboard');
34 }
35
36 $hide_menu = JFactory::getApplication()->input->getBool('hide_menu', false);
37
38 if ($hide_menu === false)
39 {
40 VikBookingHelper::printHeader($header_val);
41 }
42
43 parent::display();
44
45 if (VikBooking::showFooter() && $hide_menu === false) {
46 VikBookingHelper::printFooter();
47 }
48 }
49
50 /**
51 * AJAX request for building dynamic donut charts.
52 *
53 * @return void
54 *
55 * @since 1.12.1
56 */
57 public function donut_charts_data() {
58 $fromdt = VikRequest::getString('fromdt', date('Y-m-d'), 'request');
59 $direction = VikRequest::getString('direction', 'next', 'request');
60 $days = VikRequest::getInt('days', 7, 'request');
61 if (empty($fromdt) || !strtotime($fromdt) || empty($days) || $days < 1) {
62 throw new Exception('Missing required data', 400);
63 }
64
65 $from_info = getdate(strtotime($fromdt));
66 if ($direction != 'next') {
67 // fromdt is always the next day after the end of the loop, so the very first next day from the last displayed
68 $from_info = getdate(mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] - ($days * 2)), $from_info['year']));
69 }
70 // always push the start date to the last second (23:59:59)
71 $from_info = getdate(mktime(23, 59, 59, $from_info['mon'], $from_info['mday'], $from_info['year']));
72
73 // months front-end language map
74 $monthsmap = array(
75 JText::translate('VBSHORTMONTHONE'),
76 JText::translate('VBSHORTMONTHTWO'),
77 JText::translate('VBSHORTMONTHTHREE'),
78 JText::translate('VBSHORTMONTHFOUR'),
79 JText::translate('VBSHORTMONTHFIVE'),
80 JText::translate('VBSHORTMONTHSIX'),
81 JText::translate('VBSHORTMONTHSEVEN'),
82 JText::translate('VBSHORTMONTHEIGHT'),
83 JText::translate('VBSHORTMONTHNINE'),
84 JText::translate('VBSHORTMONTHTEN'),
85 JText::translate('VBSHORTMONTHELEVEN'),
86 JText::translate('VBSHORTMONTHTWELVE'),
87 );
88
89 // weekdays front-end language map
90 $wdaysmap = array(
91 JText::translate('VBSUNDAY'),
92 JText::translate('VBMONDAY'),
93 JText::translate('VBTUESDAY'),
94 JText::translate('VBWEDNESDAY'),
95 JText::translate('VBTHURSDAY'),
96 JText::translate('VBFRIDAY'),
97 JText::translate('VBSATURDAY'),
98 );
99
100 // gather information about the rooms and availability
101 $dbo = JFactory::getDbo();
102 $all_rooms_ids = array();
103 $unpublished_rooms = array();
104 $todayymd = date('Y-m-d');
105 $q = "SELECT `id`,`name`,`units`,`params`,`avail` FROM `#__vikbooking_rooms`;";
106 $dbo->setQuery($q);
107 $dbo->execute();
108 if ($dbo->getNumRows()) {
109 $all_rooms = $dbo->loadAssocList();
110 foreach ($all_rooms as $k => $r) {
111 if ($r['avail'] < 1) {
112 $unpublished_rooms[] = $r['id'];
113 }
114 $all_rooms_ids[$r['id']] = $r['name'];
115 }
116 }
117 $q = "SELECT SUM(`units`) FROM `#__vikbooking_rooms` WHERE `avail`=1;";
118 $dbo->setQuery($q);
119 $dbo->execute();
120 $tot_rooms_units = (int)$dbo->loadResult();
121
122 // load busy records
123 $expected_max_ts = mktime(23, 59, 59, $from_info['mon'], ($from_info['mday'] + $days), $from_info['year']);
124 $busy = VikBooking::loadBusyRecordsUnclosed(array_keys($all_rooms_ids), $from_info[0], $expected_max_ts);
125
126 // response body
127 $response = new stdClass;
128 $response->prevweek = ($todayymd != date('Y-m-d', $from_info[0]));
129 $response->nextweek = true;
130 $response->fromd = date('Y-m-d', $from_info[0]);
131 $response->tot_units = $tot_rooms_units;
132 $response->data = array();
133
134 for ($i = 0; $i < $days; $i++) {
135 $tot_booked_today = 0;
136 $today_ts = $from_info[0];
137 $data_obj = new stdClass;
138 $data_obj->ymd = date('Y-m-d', $from_info[0]);
139 $data_obj->lbl = $wdaysmap[(int)$from_info['wday']] . ', ' . $from_info['mday'];
140 $data_obj->lbl = $data_obj->ymd == $todayymd ? JText::translate('VBTODAY') . ', ' . $data_obj->lbl : $data_obj->lbl . ' ' . $monthsmap[($from_info['mon'] - 1)];
141 foreach ($busy as $idroom => $rbusy) {
142 if (in_array($idroom, $unpublished_rooms)) {
143 continue;
144 }
145 foreach ($rbusy as $b) {
146 $tmpone = getdate($b['checkin']);
147 $ritts = mktime(0, 0, 0, $tmpone['mon'], $tmpone['mday'], $tmpone['year']);
148 $tmptwo = getdate($b['checkout']);
149 $conts = mktime(0, 0, 0, $tmptwo['mon'], $tmptwo['mday'], $tmptwo['year']);
150 if ($today_ts >= $ritts && $today_ts < $conts) {
151 $tot_booked_today++;
152 }
153 }
154 }
155
156 $data_obj->tot_booked = $tot_booked_today;
157 $percentage_booked = round((100 * $tot_booked_today / $tot_rooms_units), 2);
158
159 $data_obj->color = '#ff4d4d'; //red
160 if ($percentage_booked > 33 && $percentage_booked <= 66) {
161 $data_obj->color = '#ffa64d'; //orange
162 } elseif ($percentage_booked > 66 && $percentage_booked < 100) {
163 $data_obj->color = '#2a762c'; //green
164 } elseif ($percentage_booked >= 100) {
165 $data_obj->color = '#2482b4'; //light-blue
166 }
167
168 // push today's data
169 array_push($response->data, $data_obj);
170
171 // next day
172 $from_info = getdate(mktime(23, 59, 59, $from_info['mon'], ($from_info['mday'] + 1), $from_info['year']));
173 }
174
175 // update last date (not displayed/included)
176 $response->tod = date('Y-m-d', $from_info[0]);
177
178 echo json_encode($response);
179 exit;
180 }
181
182 /**
183 * AJAX request for adding a new fest.
184 *
185 * @return void
186 *
187 * @since 1.2.0
188 */
189 public function add_fest()
190 {
191 $dt = VikRequest::getString('dt', '', 'request');
192 $type = VikRequest::getString('type', '', 'request');
193 $type = empty($type) ? 'custom' : $type;
194 $name = VikRequest::getString('name', '', 'request');
195 $descr = VikRequest::getString('descr', '', 'request');
196
197 if (empty($name) || empty($dt) || !strtotime($dt)) {
198 VBOHttpDocument::getInstance()->close(400, 'Missing mandatory festivity details');
199 }
200
201 // build fest array
202 $new_fest = [
203 'trans_name' => $name,
204 ];
205
206 $fests = VikBooking::getFestivitiesInstance();
207 $result = $fests->storeFestivity($dt, $new_fest, $type, $descr);
208 if (!$result) {
209 VBOHttpDocument::getInstance()->close(400, 'Could not store festivity details');
210 }
211
212 // reload all festivities for this day for the AJAX response
213 $all_fests = $fests->loadFestDates($dt, $dt);
214 foreach ($all_fests as $k => $v) {
215 // we expect just one record to be returned due to the from/to date limit passed to loadFestDates()
216 VBOHttpDocument::getInstance()->json($v);
217 }
218
219 // no fests found even after storing it
220 VBOHttpDocument::getInstance()->close(404, 'Festivity record not found after saving');
221 }
222
223 /**
224 * AJAX request for removing a fest.
225 *
226 * @return void
227 *
228 * @since 1.2.0
229 */
230 public function remove_fest()
231 {
232 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
233 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
234 }
235
236 $dt = VikRequest::getString('dt', '', 'request');
237 $ind = VikRequest::getInt('ind', 0, 'request');
238 $type = VikRequest::getString('type', '', 'request');
239 $type = empty($type) ? 'custom' : $type;
240 if (empty($dt) || !strtotime($dt)) {
241 echo 'e4j.error.1';
242 exit;
243 }
244
245 $fests = VikBooking::getFestivitiesInstance();
246 $result = $fests->deleteFestivity($dt, $ind, $type);
247 if (!$result) {
248 echo 'e4j.error.2';
249 exit;
250 }
251
252 echo 'e4j.ok';
253 exit;
254 }
255
256 public function einvoicing() {
257 VikBookingHelper::printHeader("einvoicing");
258
259 VikRequest::setVar('view', VikRequest::getCmd('view', 'einvoicing'));
260
261 parent::display();
262
263 if (VikBooking::showFooter()) {
264 VikBookingHelper::printFooter();
265 }
266 }
267
268 public function pmsreports() {
269 if (!JFactory::getUser()->authorise('core.vbo.pms', 'com_vikbooking') && !JFactory::getUser()->authorise('core.vbo.pmsreports', 'com_vikbooking')) {
270 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
271 }
272
273 VikBookingHelper::printHeader("pmsreports");
274
275 VikRequest::setVar('view', VikRequest::getCmd('view', 'pmsreports'));
276
277 parent::display();
278
279 if (VikBooking::showFooter()) {
280 VikBookingHelper::printFooter();
281 }
282 }
283
284 public function ratesoverv() {
285 VikBookingHelper::printHeader("20");
286
287 VikRequest::setVar('view', VikRequest::getCmd('view', 'ratesoverv'));
288
289 parent::display();
290
291 if (VikBooking::showFooter()) {
292 VikBookingHelper::printFooter();
293 }
294 }
295
296 public function stats() {
297 VikBookingHelper::printHeader("stats");
298
299 VikRequest::setVar('view', VikRequest::getCmd('view', 'stats'));
300
301 parent::display();
302
303 if (VikBooking::showFooter()) {
304 VikBookingHelper::printFooter();
305 }
306 }
307
308 /**
309 * AJAX endpoint to calculate the website rates.
310 *
311 * @return void
312 */
313 public function calc_rates()
314 {
315 $response = 'e4j.error.ErrorCode(1) Server is blocking the self-request';
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', '', '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 = array(
383 'hash' => md5('vbo.e4j.vbo'),
384 'req_type' => 'hotel_availability',
385 'nights' => $nights,
386 'num_rooms' => 1,
387 'only_rates' => $only_rates,
388 );
389 $arr_res = $av_helper->getRates($params);
390
391 // pricing pool
392 $price_details = array();
393
394 if (is_array($arr_res)) {
395 if (!strlen($av_helper->getError())) {
396 if (array_key_exists($id_room, $arr_res)) {
397 $response = '';
398 foreach ($arr_res[$id_room] as $rate) {
399 // build pricing object
400 $rplan_details = new stdClass;
401 $rplan_details->idprice = $rate['idprice'];
402 $rplan_details->name = $rate['pricename'];
403 $rplan_details->net = $rate['cost'];
404 $rplan_details->fnet = $currencysymb . ' ' . VikBooking::numberFormat($rate['cost']);
405 $rplan_details->tax = $rate['taxes'];
406 $rplan_details->ftax = $currencysymb . ' ' . VikBooking::numberFormat($rate['taxes']);
407 $rplan_details->tot = $rate['cost'] + $rate['taxes'];
408 $rplan_details->ftot = $currencysymb . ' ' . VikBooking::numberFormat(($rate['cost'] + $rate['taxes']));
409 array_push($price_details, $rplan_details);
410 //
411 $extra_response = '';
412 $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 . '">';
413 $response .= '<span class="vbo-calcrates-ratename">'.$rate['pricename'].'</span>';
414 $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratenet"><span>'.JText::translate('VBCALCRATESNET').'</span>'.$currencysymb.' '.VikBooking::numberFormat($rate['cost']).'</span>';
415 $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratetax"><span>'.JText::translate('VBCALCRATESTAX').'</span>'.$currencysymb.' '.VikBooking::numberFormat($rate['taxes']).'</span>';
416 if (!empty($rate['city_taxes'])) {
417 $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratecitytax"><span>'.JText::translate('VBCALCRATESCITYTAX').'</span>'.$currencysymb.' '.VikBooking::numberFormat($rate['city_taxes']).'</span>';
418 }
419 if (!empty($rate['fees'])) {
420 $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratefees"><span>'.JText::translate('VBCALCRATESFEES').'</span>'.$currencysymb.' '.VikBooking::numberFormat($rate['fees']).'</span>';
421 }
422 if (array_key_exists('affdays', $rate) && $rate['affdays'] > 0) {
423 $extra_response .= '<span class="vbo-calcrates-extrapricedet vbo-calcrates-ratespaffdays"><span>'.JText::translate('VBCALCRATESSPAFFDAYS').'</span>'.$rate['affdays'].'</span>';
424 }
425 if (array_key_exists('diffusagediscount', $rate) && count($rate['diffusagediscount']) > 0) {
426 foreach ($rate['diffusagediscount'] as $roomnumb => $disc) {
427 $extra_response .= '<span class="vbo-calcrates-extrapricedet vbo-calcrates-rateoccupancydisc"><span>'.JText::sprintf('VBCALCRATESADUOCCUPANCY', $rate['diffusage']).'</span>- '.$currencysymb.' '.VikBooking::numberFormat($disc).'</span>';
428 break;
429 }
430 } elseif (array_key_exists('diffusagecost', $rate) && count($rate['diffusagecost']) > 0) {
431 foreach ($rate['diffusagecost'] as $roomnumb => $charge) {
432 $extra_response .= '<span class="vbo-calcrates-extrapricedet vbo-calcrates-rateoccupancycharge"><span>'.JText::sprintf('VBCALCRATESADUOCCUPANCY', $rate['diffusage']).'</span>+ '.$currencysymb.' '.VikBooking::numberFormat($charge).'</span>';
433 break;
434 }
435 }
436 $tot = $rate['cost'] + $rate['taxes'] + $rate['city_taxes'] + $rate['fees'];
437 $tot = round($tot, 2);
438 $response .= '<span class="vbo-calcrates-ratetotal"><span>'.JText::translate('VBCALCRATESTOT').'</span>'.$currencysymb.' '.VikBooking::numberFormat($tot).'</span>';
439 if (!empty($extra_response)) {
440 $response .= '<div class="vbo-calcrates-info">'.$extra_response.'</div>';
441 }
442 $response .= '</div>';
443 }
444 } else {
445 $response = 'e4j.error.'.JText::sprintf('VBCALCRATESROOMNOTAVAILCOMBO', date($df, $checkin_ts), date($df, $checkout_ts));
446 /**
447 * Set a response code so that the View calendar can understand that the room is not available or has no rates.
448 *
449 * @since 1.14 (J) - 1.4.0 (WP)
450 */
451 if (isset($arr_res['fullybooked']) && in_array($id_room, $arr_res['fullybooked'])) {
452 $response_code = -1;
453 }
454 }
455 } else {
456 $response = 'e4j.error.' . $av_helper->getError();
457 /**
458 * Set a response code so that the View calendar can understand that the room is not available or has no rates.
459 *
460 * @since 1.14 (J) - 1.4.0 (WP)
461 */
462 if (isset($arr_res['fullybooked']) && in_array($id_room, $arr_res['fullybooked'])) {
463 $response_code = -1;
464 }
465 }
466 } else {
467 $response = 'e4j.error.' . $av_helper->getError();
468 }
469
470 if ($only_rates && strpos($response, 'e4j.error') === false) {
471 echo json_encode($price_details);
472 exit;
473 }
474
475 // do not do only echo trim($response); or the currency symbol will not be encoded on some servers
476 $safe_response = array(trim($response));
477 if ($only_rates && !empty($response_code)) {
478 array_push($safe_response, $response_code);
479 }
480
481 echo json_encode($safe_response);
482 exit;
483 }
484
485 /**
486 * This is an AJAX endpoint.
487 */
488 public function cron_exec()
489 {
490 ob_start();
491
492 VikRequest::setVar('view', VikRequest::getCmd('view', 'cronexec'));
493
494 parent::display();
495
496 $content = ob_get_contents();
497 ob_end_clean();
498
499 VBOHttpDocument::getInstance()->json([$content]);
500 }
501
502 public function downloadcron()
503 {
504 /**
505 * @wponly no more executable files need to be downloaded for WordPress.
506 */
507 VBOHttpDocument::getInstance()->close(406, 'Cron Jobs must be executed through WPCron');
508 }
509
510 /**
511 * This is an AJAX endpoint.
512 */
513 public function cronlogs()
514 {
515 $dbo = JFactory::getDBO();
516 $pcron_id = VikRequest::getInt('cron_id', '', 'request');
517
518 ob_start();
519
520 $q = "SELECT * FROM `#__vikbooking_cronjobs` WHERE `id`=".(int)$pcron_id.";";
521 $dbo->setQuery($q);
522 $dbo->execute();
523 if ($dbo->getNumRows() == 1) {
524 $cron_data = $dbo->loadAssoc();
525 $cron_data['logs'] = empty($cron_data['logs']) ? '--------' : $cron_data['logs'];
526 echo '<pre>'.print_r($cron_data['logs'], true).'</pre>';
527 }
528
529 $content = ob_get_contents();
530 ob_end_clean();
531
532 VBOHttpDocument::getInstance()->json([$content]);
533 }
534
535 public function packages() {
536 VikBookingHelper::printHeader("packages");
537
538 VikRequest::setVar('view', VikRequest::getCmd('view', 'packages'));
539
540 parent::display();
541
542 if (VikBooking::showFooter()) {
543 VikBookingHelper::printFooter();
544 }
545 }
546
547 public function newpackage() {
548 VikBookingHelper::printHeader("packages");
549
550 VikRequest::setVar('view', VikRequest::getCmd('view', 'managepackage'));
551
552 parent::display();
553
554 if (VikBooking::showFooter()) {
555 VikBookingHelper::printFooter();
556 }
557 }
558
559 public function editpackage() {
560 VikBookingHelper::printHeader("packages");
561
562 VikRequest::setVar('view', VikRequest::getCmd('view', 'managepackage'));
563
564 parent::display();
565
566 if (VikBooking::showFooter()) {
567 VikBookingHelper::printFooter();
568 }
569 }
570
571 public function createpackage()
572 {
573 if (!JSession::checkToken()) {
574 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
575 }
576
577 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
578 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
579 }
580
581 $this->do_createpackage();
582 }
583
584 public function createpackagestay()
585 {
586 if (!JSession::checkToken()) {
587 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
588 }
589
590 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
591 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
592 }
593
594 $this->do_createpackage(true);
595 }
596
597 private function do_createpackage($stay = false) {
598 $dbo = JFactory::getDBO();
599 $mainframe = JFactory::getApplication();
600 $pname = VikRequest::getString('name', '', 'request');
601 $palias = VikRequest::getString('alias', '', 'request');
602 $palias = empty($palias) ? $pname : $palias;
603 $palias = JFilterOutput::stringURLSafe($palias);
604 $pimg = VikRequest::getVar('img', null, 'files', 'array');
605 $pfrom = VikRequest::getString('from', '', 'request');
606 $pto = VikRequest::getString('to', '', 'request');
607 $pexcludeday = VikRequest::getVar('excludeday', array());
608 $strexcldates = array();
609 foreach ($pexcludeday as $exclday) {
610 if (!empty($exclday)) {
611 $strexcldates[] = $exclday;
612 }
613 }
614 $strexcldates = implode(';', $strexcldates);
615 $prooms = VikRequest::getVar('rooms', array());
616 $pminlos = VikRequest::getInt('minlos', '', 'request');
617 $pminlos = $pminlos < 1 ? 1 : $pminlos;
618 $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
619 $pmaxlos = $pmaxlos < 0 ? 0 : $pmaxlos;
620 $pmaxlos = $pmaxlos < $pminlos ? 0 : $pmaxlos;
621 $pcost = VikRequest::getFloat('cost', '', 'request');
622 $paliq = VikRequest::getInt('aliq', '', 'request');
623 $ppernight_total = VikRequest::getInt('pernight_total', '', 'request');
624 $ppernight_total = $ppernight_total == 1 ? 1 : 2;
625 $pperperson = VikRequest::getInt('perperson', '', 'request');
626 $pperperson = $pperperson > 0 ? 1 : 0;
627 $pshowoptions = VikRequest::getInt('showoptions', '', 'request');
628 $pshowoptions = $pshowoptions >= 1 && $pshowoptions <= 3 ? $pshowoptions : 1;
629 $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWRAW);
630 $pshortdescr = VikRequest::getString('shortdescr', '', 'request', VIKREQUEST_ALLOWHTML);
631 $pconditions = VikRequest::getString('conditions', '', 'request', VIKREQUEST_ALLOWRAW);
632 $pbenefits = VikRequest::getString('benefits', '', 'request', VIKREQUEST_ALLOWHTML);
633 $ptsinit = VikBooking::getDateTimestamp($pfrom, '0', '0');
634 $ptsend = VikBooking::getDateTimestamp($pto, '23', '59');
635 $ptsinit = empty($ptsinit) ? time() : $ptsinit;
636 $ptsend = empty($ptsend) || $ptsend < $ptsinit ? $ptsinit : $ptsend;
637 //file upload
638 jimport('joomla.filesystem.file');
639 $gimg = "";
640 if (isset($pimg) && strlen(trim($pimg['name']))) {
641 $pautoresize = VikRequest::getString('autoresize', '', 'request');
642 $presizeto = VikRequest::getInt('resizeto', '', 'request');
643 $creativik = new vikResizer();
644 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimg['name'])));
645 $src = $pimg['tmp_name'];
646 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
647 $j = "";
648 if (file_exists($dest.$filename)) {
649 $j = rand(171, 1717);
650 while (file_exists($dest.$j.$filename)) {
651 $j++;
652 }
653 }
654 $finaldest = $dest.$j.$filename;
655 $check = getimagesize($pimg['tmp_name']);
656 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
657 if (VikBooking::uploadFile($src, $finaldest)) {
658 $gimg = $j.$filename;
659 //orig img
660 $origmod = true;
661 if ($pautoresize == "1" && !empty($presizeto)) {
662 $origmod = $creativik->proportionalImage($finaldest, $dest.'big_'.$j.$filename, $presizeto, $presizeto);
663 } else {
664 VikBooking::uploadFile($finaldest, $dest.'big_'.$j.$filename, true);
665 }
666 //thumb
667 $thumbsize = VikBooking::getThumbSize();
668 $thumb = $creativik->proportionalImage($finaldest, $dest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
669 if (!$thumb || !$origmod) {
670 if (file_exists($dest.'big_'.$j.$filename)) @unlink($dest.'big_'.$j.$filename);
671 if (file_exists($dest.'thumb_'.$j.$filename)) @unlink($dest.'thumb_'.$j.$filename);
672 VikError::raiseWarning('', 'Error Uploading the File: '.$pimg['name']);
673 }
674 @unlink($finaldest);
675 } else {
676 VikError::raiseWarning('', 'Error while uploading image');
677 }
678 } else {
679 VikError::raiseWarning('', 'Uploaded file is not an Image');
680 }
681 }
682 //
683 $goto = "index.php?option=com_vikbooking&task=packages";
684 $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.");";
685 $dbo->setQuery($q);
686 $dbo->execute();
687 $lid = $dbo->insertid();
688 if (!empty($lid)) {
689 $mainframe->enqueueMessage(JText::translate('VBOPKGSAVED'));
690 if ($stay) {
691 $goto = "index.php?option=com_vikbooking&task=editpackage&cid[]=".$lid;
692 }
693 foreach ($prooms as $roomid) {
694 if (!empty($roomid)) {
695 $q = "INSERT INTO `#__vikbooking_packages_rooms` (`idpackage`,`idroom`) VALUES (".(int)$lid.", ".(int)$roomid.");";
696 $dbo->setQuery($q);
697 $dbo->execute();
698 }
699 }
700 }
701 $mainframe->redirect($goto);
702 }
703
704 public function updatepackage()
705 {
706 if (!JSession::checkToken()) {
707 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
708 }
709
710 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
711 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
712 }
713
714 $this->do_updatepackage();
715 }
716
717 public function updatepackagestay()
718 {
719 if (!JSession::checkToken()) {
720 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
721 }
722
723 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
724 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
725 }
726
727 $this->do_updatepackage(true);
728 }
729
730 private function do_updatepackage($stay = false) {
731 $dbo = JFactory::getDBO();
732 $mainframe = JFactory::getApplication();
733 $pwhereup = VikRequest::getInt('whereup', '', 'request');
734 $q = "SELECT * FROM `#__vikbooking_packages` WHERE `id`=".(int)$pwhereup.";";
735 $dbo->setQuery($q);
736 $dbo->execute();
737 if ($dbo->getNumRows() == 1) {
738 $pkg_data = $dbo->loadAssoc();
739 } else {
740 VikError::raiseWarning('', 'Not Found.');
741 $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
742 exit;
743 }
744 $pname = VikRequest::getString('name', '', 'request');
745 $palias = VikRequest::getString('alias', '', 'request');
746 $palias = empty($palias) ? $pname : $palias;
747 $palias = JFilterOutput::stringURLSafe($palias);
748 $pimg = VikRequest::getVar('img', null, 'files', 'array');
749 $pfrom = VikRequest::getString('from', '', 'request');
750 $pto = VikRequest::getString('to', '', 'request');
751 $pexcludeday = VikRequest::getVar('excludeday', array());
752 $strexcldates = array();
753 foreach ($pexcludeday as $exclday) {
754 if (!empty($exclday)) {
755 $strexcldates[] = $exclday;
756 }
757 }
758 $strexcldates = implode(';', $strexcldates);
759 $prooms = VikRequest::getVar('rooms', array());
760 $pminlos = VikRequest::getInt('minlos', '', 'request');
761 $pminlos = $pminlos < 1 ? 1 : $pminlos;
762 $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
763 $pmaxlos = $pmaxlos < 0 ? 0 : $pmaxlos;
764 $pmaxlos = $pmaxlos < $pminlos ? 0 : $pmaxlos;
765 $pcost = VikRequest::getFloat('cost', '', 'request');
766 $paliq = VikRequest::getInt('aliq', '', 'request');
767 $ppernight_total = VikRequest::getInt('pernight_total', '', 'request');
768 $ppernight_total = $ppernight_total == 1 ? 1 : 2;
769 $pperperson = VikRequest::getInt('perperson', '', 'request');
770 $pperperson = $pperperson > 0 ? 1 : 0;
771 $pshowoptions = VikRequest::getInt('showoptions', '', 'request');
772 $pshowoptions = $pshowoptions >= 1 && $pshowoptions <= 3 ? $pshowoptions : 1;
773 $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWRAW);
774 $pshortdescr = VikRequest::getString('shortdescr', '', 'request', VIKREQUEST_ALLOWHTML);
775 $pconditions = VikRequest::getString('conditions', '', 'request', VIKREQUEST_ALLOWRAW);
776 $pbenefits = VikRequest::getString('benefits', '', 'request', VIKREQUEST_ALLOWHTML);
777 $ptsinit = VikBooking::getDateTimestamp($pfrom, '0', '0');
778 $ptsend = VikBooking::getDateTimestamp($pto, '23', '59');
779 $ptsinit = empty($ptsinit) ? time() : $ptsinit;
780 $ptsend = empty($ptsend) || $ptsend < $ptsinit ? $ptsinit : $ptsend;
781 //file upload
782 jimport('joomla.filesystem.file');
783 $gimg = "";
784 if (isset($pimg) && strlen(trim($pimg['name']))) {
785 $pautoresize = VikRequest::getString('autoresize', '', 'request');
786 $presizeto = VikRequest::getInt('resizeto', '', 'request');
787 $creativik = new vikResizer();
788 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimg['name'])));
789 $src = $pimg['tmp_name'];
790 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
791 $j = "";
792 if (file_exists($dest.$filename)) {
793 $j = rand(171, 1717);
794 while (file_exists($dest.$j.$filename)) {
795 $j++;
796 }
797 }
798 $finaldest = $dest.$j.$filename;
799 $check = getimagesize($pimg['tmp_name']);
800 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
801 if (VikBooking::uploadFile($src, $finaldest)) {
802 $gimg = $j.$filename;
803 //orig img
804 $origmod = true;
805 if ($pautoresize == "1" && !empty($presizeto)) {
806 $origmod = $creativik->proportionalImage($finaldest, $dest.'big_'.$j.$filename, $presizeto, $presizeto);
807 } else {
808 VikBooking::uploadFile($finaldest, $dest.'big_'.$j.$filename, true);
809 }
810 //thumb
811 $thumbsize = VikBooking::getThumbSize();
812 $thumb = $creativik->proportionalImage($finaldest, $dest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
813 if (!$thumb || !$origmod) {
814 if (file_exists($dest.'big_'.$j.$filename)) @unlink($dest.'big_'.$j.$filename);
815 if (file_exists($dest.'thumb_'.$j.$filename)) @unlink($dest.'thumb_'.$j.$filename);
816 VikError::raiseWarning('', 'Error Uploading the File: '.$pimg['name']);
817 }
818 @unlink($finaldest);
819 } else {
820 VikError::raiseWarning('', 'Error while uploading image');
821 }
822 } else {
823 VikError::raiseWarning('', 'Uploaded file is not an Image');
824 }
825 }
826 //
827 $goto = "index.php?option=com_vikbooking&task=packages";
828 $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.";";
829 $dbo->setQuery($q);
830 $dbo->execute();
831 $q = "DELETE FROM `#__vikbooking_packages_rooms` WHERE `idpackage`=".(int)$pwhereup.";";
832 $dbo->setQuery($q);
833 $dbo->execute();
834 foreach ($prooms as $roomid) {
835 if (!empty($roomid)) {
836 $q = "INSERT INTO `#__vikbooking_packages_rooms` (`idpackage`,`idroom`) VALUES (".(int)$pwhereup.", ".(int)$roomid.");";
837 $dbo->setQuery($q);
838 $dbo->execute();
839 }
840 }
841 $mainframe->enqueueMessage(JText::translate('VBOPKGUPDATED'));
842 if ($stay) {
843 $goto = "index.php?option=com_vikbooking&task=editpackage&cid[]=".$pwhereup;
844 }
845 $mainframe->redirect($goto);
846 }
847
848 public function removepackages()
849 {
850 if (!JSession::checkToken()) {
851 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
852 }
853
854 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
855 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
856 }
857
858 $ids = VikRequest::getVar('cid', array());
859 $dbo = JFactory::getDbo();
860
861 foreach ($ids as $d) {
862 $q = "DELETE FROM `#__vikbooking_packages` WHERE `id`=".(int)$d.";";
863 $dbo->setQuery($q);
864 $dbo->execute();
865 $q = "DELETE FROM `#__vikbooking_packages_rooms` WHERE `idpackage`=".(int)$d.";";
866 $dbo->setQuery($q);
867 $dbo->execute();
868 }
869
870 $mainframe = JFactory::getApplication();
871 $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
872 }
873
874 public function calendar() {
875 VikBookingHelper::printHeader("19");
876
877 VikRequest::setVar('view', VikRequest::getCmd('view', 'calendar'));
878
879 parent::display();
880
881 if (VikBooking::showFooter()) {
882 VikBookingHelper::printFooter();
883 }
884 }
885
886 public function rooms() {
887 VikBookingHelper::printHeader("7");
888
889 VikRequest::setVar('view', VikRequest::getCmd('view', 'rooms'));
890
891 parent::display();
892
893 if (VikBooking::showFooter()) {
894 VikBookingHelper::printFooter();
895 }
896 }
897
898 public function newroom() {
899 VikBookingHelper::printHeader("7");
900
901 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageroom'));
902
903 parent::display();
904
905 if (VikBooking::showFooter()) {
906 VikBookingHelper::printFooter();
907 }
908 }
909
910 public function editroom() {
911 VikBookingHelper::printHeader("7");
912
913 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageroom'));
914
915 parent::display();
916
917 if (VikBooking::showFooter()) {
918 VikBookingHelper::printFooter();
919 }
920 }
921
922 public function createroom()
923 {
924 if (!JSession::checkToken()) {
925 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
926 }
927
928 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
929 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
930 }
931
932 $this->do_createroom();
933 }
934
935 public function createroomstay()
936 {
937 if (!JSession::checkToken()) {
938 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
939 }
940
941 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
942 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
943 }
944
945 $this->do_createroom(true);
946 }
947
948 private function do_createroom($stay = false) {
949 $app = JFactory::getApplication();
950 $pcname = VikRequest::getString('cname', '', 'request');
951 $pccat = VikRequest::getVar('ccat', array(0));
952 $pcdescr = VikRequest::getString('cdescr', '', 'request', VIKREQUEST_ALLOWRAW);
953 $psmalldesc = VikRequest::getString('smalldesc', '', 'request', VIKREQUEST_ALLOWRAW);
954 $pccarat = VikRequest::getVar('ccarat', array(0));
955 $pcoptional = VikRequest::getVar('coptional', array(0));
956 $pcavail = VikRequest::getString('cavail', '', 'request');
957 $pautoresize = VikRequest::getString('autoresize', '', 'request');
958 $presizeto = VikRequest::getString('resizeto', '', 'request');
959 $pautoresizemore = VikRequest::getString('autoresizemore', '', 'request');
960 $presizetomore = VikRequest::getString('resizetomore', '', 'request');
961 $punits = VikRequest::getInt('units', '', 'request');
962 $pimages = VikRequest::getVar('cimgmore', null, 'files', 'array');
963 $pfromadult = VikRequest::getInt('fromadult', '', 'request');
964 $ptoadult = VikRequest::getInt('toadult', '', 'request');
965 $pfromchild = VikRequest::getInt('fromchild', '', 'request');
966 $ptochild = VikRequest::getInt('tochild', '', 'request');
967 $ptotpeople = VikRequest::getInt('totpeople', '', 'request');
968 $pmintotpeople = VikRequest::getInt('mintotpeople', '', 'request');
969 $pmintotpeople = $pmintotpeople < 1 ? 1 : $pmintotpeople;
970 $plastavail = VikRequest::getString('lastavail', '', 'request');
971 $plastavail = empty($plastavail) ? 0 : intval($plastavail);
972 $psuggocc = VikRequest::getInt('suggocc', 1, 'request');
973 $pcustprice = VikRequest::getString('custprice', '', 'request');
974 $pcustprice = empty($pcustprice) ? '' : floatval($pcustprice);
975 $pcustpricetxt = VikRequest::getString('custpricetxt', '', 'request', VIKREQUEST_ALLOWRAW);
976 $pcustpricesubtxt = VikRequest::getString('custpricesubtxt', '', 'request', VIKREQUEST_ALLOWRAW);
977 $preqinfo = VikRequest::getInt('reqinfo', '', 'request');
978 $ppricecal = VikRequest::getInt('pricecal', '', 'request');
979 $pdefcalcost = VikRequest::getString('defcalcost', '', 'request');
980 $pmaxminpeople = VikRequest::getString('maxminpeople', '', 'request');
981 $pcimgcaption = VikRequest::getVar('cimgcaption', array());
982 $pmaxminpeople = in_array($pmaxminpeople, array('0', '1', '2', '3', '4', '5')) ? $pmaxminpeople : '0';
983 $pseasoncal = VikRequest::getInt('seasoncal', 0, 'request');
984 $pseasoncal = $pseasoncal >= 0 || $pseasoncal <= 3 ? $pseasoncal : 0;
985 $pseasoncal_nights = VikRequest::getString('seasoncal_nights', '', 'request');
986 $pseasoncal_prices = VikRequest::getString('seasoncal_prices', '', 'request');
987 $pseasoncal_restr = VikRequest::getString('seasoncal_restr', '', 'request');
988 $pmulti_units = VikRequest::getInt('multi_units', '', 'request');
989 $pmulti_units = $punits > 1 ? $pmulti_units : 0;
990 $psefalias = VikRequest::getString('sefalias', '', 'request');
991 $psefalias = empty($psefalias) ? JFilterOutput::stringURLSafe($pcname) : JFilterOutput::stringURLSafe($psefalias);
992 $pcustptitle = VikRequest::getString('custptitle', '', 'request');
993 $pcustptitlew = VikRequest::getString('custptitlew', '', 'request');
994 $pcustptitlew = in_array($pcustptitlew, array('before', 'after', 'replace')) ? $pcustptitlew : 'before';
995 $pmetakeywords = VikRequest::getString('metakeywords', '', 'request');
996 $pmetadescription = VikRequest::getString('metadescription', '', 'request');
997 $pshare_with = VikRequest::getVar('share_with', array());
998 $scalnights_arr = array();
999 if (!empty($pseasoncal_nights)) {
1000 $scalnights = explode(',', $pseasoncal_nights);
1001 foreach ($scalnights as $scalnight) {
1002 if (intval(trim($scalnight)) > 0) {
1003 $scalnights_arr[] = intval(trim($scalnight));
1004 }
1005 }
1006 }
1007 if ($scalnights_arr) {
1008 $pseasoncal_nights = implode(', ', $scalnights_arr);
1009 } else {
1010 $pseasoncal_nights = '';
1011 $pseasoncal = 0;
1012 }
1013 $roomparams = [
1014 'lastavail' => $plastavail,
1015 'suggocc' => $psuggocc,
1016 'custprice' => $pcustprice,
1017 'custpricetxt' => $pcustpricetxt,
1018 'custpricesubtxt' => $pcustpricesubtxt,
1019 'reqinfo' => $preqinfo,
1020 'pricecal' => $ppricecal,
1021 'defcalcost' => floatval($pdefcalcost),
1022 'maxminpeople' => $pmaxminpeople,
1023 'seasoncal' => $pseasoncal,
1024 'seasoncal_nights' => $pseasoncal_nights,
1025 'seasoncal_prices' => $pseasoncal_prices,
1026 'seasoncal_restr' => $pseasoncal_restr,
1027 'multi_units' => $pmulti_units,
1028 'custptitle' => $pcustptitle,
1029 'custptitlew' => $pcustptitlew,
1030 'metakeywords' => $pmetakeywords,
1031 'metadescription' => $pmetadescription,
1032 'layout_style' => VikRequest::getString('layout_style', 'default', 'request'),
1033 'checkin' => VikRequest::getString('listing_checkin', '', 'request'),
1034 'checkout' => VikRequest::getString('listing_checkout', '', 'request'),
1035 ];
1036 //distinctive features
1037 $roomparams['features'] = array();
1038 if ($punits > 0) {
1039 for ($i=1; $i <= $punits; $i++) {
1040 $distf_name = VikRequest::getVar('feature-name'.$i, array());
1041 $distf_lang = VikRequest::getVar('feature-lang'.$i, array());
1042 $distf_value = VikRequest::getVar('feature-value'.$i, array());
1043 foreach ($distf_name as $distf_k => $distf) {
1044 if (strlen($distf) > 0 && strlen($distf_value[$distf_k]) > 0) {
1045 $use_key = strlen($distf_lang[$distf_k]) > 0 ? $distf_lang[$distf_k] : $distf;
1046 $roomparams['features'][$i][$use_key] = $distf_value[$distf_k];
1047 }
1048 }
1049 }
1050 }
1051
1052 /**
1053 * Store room geo params information.
1054 *
1055 * @since 1.14 (J) - 1.4.0 (WP)
1056 */
1057 $geo = VikBooking::getGeocodingInstance();
1058 $geo_params = $geo->getRoomGeoTransient(0);
1059 if ($geo_params !== false) {
1060 // make sure the geocoding service was not turned off
1061 $geo_enabled = VikRequest::getInt('geo_enabled', 0, 'request');
1062 if (!$geo_enabled) {
1063 $geo_params->enabled = 0;
1064 }
1065 //
1066 $roomparams['geo'] = $geo_params;
1067 }
1068 //
1069
1070 $roomparamstr = json_encode($roomparams);
1071
1072 if (empty($pcname)) {
1073 $app->enqueueMessage(JText::translate('VBO_PLEASE_FILL_FIELDS'), 'error');
1074 $app->redirect("index.php?option=com_vikbooking&task=rooms");
1075 $app->close();
1076 }
1077
1078 jimport('joomla.filesystem.file');
1079 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
1080
1081 $picon = "";
1082 if (($_FILES['cimg'] ?? null) && !intval($_FILES['cimg']['error']) && VikBooking::caniWrite($updpath) && strlen(trim($_FILES['cimg']['name'])) && @is_uploaded_file($_FILES['cimg']['tmp_name'])) {
1083 $safename = JFile::makeSafe(str_replace(' ', '_', strtolower($_FILES['cimg']['name'])));
1084 $j = '';
1085 $pwhere = $updpath . $safename;
1086 if (file_exists($updpath . $safename)) {
1087 $j = 1;
1088 while (file_exists($updpath . $j . $safename)) {
1089 $j++;
1090 }
1091 $pwhere = $updpath . $j . $safename;
1092 }
1093 if (!getimagesize($_FILES['cimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
1094 @unlink($pwhere);
1095 } elseif (VikBooking::uploadFile($_FILES['cimg']['tmp_name'], $pwhere)) {
1096 $picon = $j . $safename;
1097 if ((int) $pautoresize && !empty($presizeto)) {
1098 $origmod = (new VikResizer)->proportionalImage($pwhere, $updpath . 'r_' . $j . $safename, $presizeto, $presizeto);
1099 if ($origmod) {
1100 @unlink($pwhere);
1101 $picon = 'r_' . $j . $safename;
1102 }
1103 }
1104 /**
1105 * Create a mini-thumbnail of the room/listing main photo.
1106 *
1107 * @since 1.17.5 (J) - 1.7.5 (WP)
1108 */
1109 try {
1110 // resize the original image
1111 (new VikResizer)->proportionalImage($pwhere, $updpath . 'mini_' . $picon, 96, 96);
1112 } catch (Throwable $e) {
1113 // silently catch any PHP GD error and continue
1114 }
1115 }
1116 }
1117
1118 // more images
1119 $creativik = new VikResizer;
1120 $bigsdest = $updpath;
1121 $thumbsdest = $updpath;
1122 $dest = $updpath;
1123 $moreimagestr = "";
1124 $arrimgs = array();
1125 $captiontexts = array();
1126 $imgcaptions = array();
1127 foreach ($pimages['name'] as $kk=>$ci) {
1128 if (!empty($ci)) {
1129 $arrimgs[] = $kk;
1130 $captiontexts[] = isset($pcimgcaption[$kk]) ? $pcimgcaption[$kk] : '';
1131 }
1132 }
1133 foreach ($arrimgs as $ki => $imgk) {
1134 if (strlen(trim($pimages['name'][$imgk]))) {
1135 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimages['name'][$imgk])));
1136 $src = $pimages['tmp_name'][$imgk];
1137 $j = "";
1138 if (file_exists($dest.$filename)) {
1139 $j = rand(171, 1717);
1140 while (file_exists($dest.$j.$filename)) {
1141 $j++;
1142 }
1143 }
1144 $finaldest = $dest.$j.$filename;
1145 $check = getimagesize($pimages['tmp_name'][$imgk]);
1146 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
1147 if (VikBooking::uploadFile($src, $finaldest)) {
1148 $gimg = $j.$filename;
1149 //orig img
1150 $origmod = true;
1151 if ($pautoresizemore == "1" && !empty($presizetomore)) {
1152 $origmod = $creativik->proportionalImage($finaldest, $bigsdest.'big_'.$j.$filename, $presizetomore, $presizetomore);
1153 } else {
1154 VikBooking::uploadFile($finaldest, $bigsdest.'big_'.$j.$filename, true);
1155 }
1156 //thumb
1157 $thumbsize = VikBooking::getThumbSize();
1158 $thumb = $creativik->proportionalImage($finaldest, $thumbsdest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
1159 if (!$thumb || !$origmod) {
1160 if (file_exists($bigsdest.'big_'.$j.$filename)) @unlink($bigsdest.'big_'.$j.$filename);
1161 if (file_exists($thumbsdest.'thumb_'.$j.$filename)) @unlink($thumbsdest.'thumb_'.$j.$filename);
1162 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1163 } else {
1164 $moreimagestr .= $j.$filename.";;";
1165 $imgcaptions[] = $captiontexts[$ki];
1166 }
1167 @unlink($finaldest);
1168 } else {
1169 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1170 }
1171 } else {
1172 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1173 }
1174 }
1175 }
1176 //end more images
1177 if (is_array($pccat) && count($pccat)) {
1178 $pccatdef="";
1179 foreach ($pccat as $ccat) {
1180 if (!empty($ccat)) {
1181 $pccatdef.=$ccat.";";
1182 }
1183 }
1184 } else {
1185 $pccatdef="";
1186 }
1187 if (is_array($pccarat) && count($pccarat)) {
1188 $pccaratdef="";
1189 foreach ($pccarat as $ccarat) {
1190 $pccaratdef.=$ccarat.";";
1191 }
1192 } else {
1193 $pccaratdef="";
1194 }
1195 if (is_array($pcoptional) && count($pcoptional)) {
1196 $pcoptionaldef="";
1197 foreach ($pcoptional as $coptional) {
1198 $pcoptionaldef.=$coptional.";";
1199 }
1200 } else {
1201 $pcoptionaldef="";
1202 }
1203 $pcavaildef=($pcavail=="yes" ? "1" : "0");
1204 if ($pfromadult > $ptoadult) {
1205 $pfromadult = 1;
1206 $ptoadult = 1;
1207 }
1208 if ($pfromchild > $ptochild) {
1209 $pfromchild = 1;
1210 $ptochild = 1;
1211 }
1212 $dbo = JFactory::getDbo();
1213 $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).");";
1214 $dbo->setQuery($q);
1215 $dbo->execute();
1216 $lid = $dbo->insertid();
1217 if (empty($lid)) {
1218 $app->enqueueMessage('Could not store the record on the database', 'error');
1219 $app->redirect("index.php?option=com_vikbooking&task=rooms");
1220 $app->close();
1221 }
1222
1223 /**
1224 * Share availability calendars with other rooms.
1225 *
1226 * @since 1.13
1227 */
1228 // always reset relations for this main room
1229 $q = "DELETE FROM `#__vikbooking_calendars_xref` WHERE `mainroom`={$lid};";
1230 $dbo->setQuery($q);
1231 $dbo->execute();
1232 $newxref = array();
1233 foreach ($pshare_with as $cldroom) {
1234 if (!empty($cldroom)) {
1235 array_push($newxref, (int)$cldroom);
1236 }
1237 }
1238 foreach ($newxref as $cldroom) {
1239 $q = "INSERT INTO `#__vikbooking_calendars_xref` (`mainroom`, `childroom`) VALUES ({$lid}, {$cldroom});";
1240 $dbo->setQuery($q);
1241 $dbo->execute();
1242 }
1243
1244 /**
1245 * Room upgrade options.
1246 *
1247 * @since 1.16.0 (J) - 1.6.0 (WP)
1248 */
1249 $config = VBOFactory::getConfig();
1250 $room_upgrade_options = [];
1251 $room_upgrade = VikRequest::getInt('room_upgrade', 0, 'request');
1252 $upgrade_rooms = VikRequest::getVar('upgrade_rooms', array());
1253 $upgrade_discount = VikRequest::getFloat('upgrade_discount', 0, 'request');
1254 if ($room_upgrade && is_array($upgrade_rooms) && count($upgrade_rooms)) {
1255 $upgrade_rooms = array_map(function($rid) {
1256 return (int)$rid;
1257 }, $upgrade_rooms);
1258
1259 $room_upgrade_options = [
1260 'rooms' => $upgrade_rooms,
1261 'discount' => $upgrade_discount,
1262 ];
1263 }
1264 $config->set('room_upgrade_options_' . $lid, json_encode($room_upgrade_options));
1265
1266 if ($stay === true) {
1267 $app->enqueueMessage(JText::translate('VBOROOMSAVEOK').' - <a href="index.php?option=com_vikbooking&task=tariffs&cid[]='.$lid.'">'.JText::translate('VBOGOTORATES').'</a>');
1268 $app->redirect("index.php?option=com_vikbooking&task=editroom&cid[]=".$lid);
1269 $app->close();
1270 }
1271
1272 $app->redirect("index.php?option=com_vikbooking&task=tariffs&cid[]=".$lid);
1273 $app->close();
1274 }
1275
1276 public function updateroom()
1277 {
1278 if (!JSession::checkToken()) {
1279 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1280 }
1281
1282 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1283 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1284 }
1285
1286 $this->do_updateroom();
1287 }
1288
1289 public function updateroomstay()
1290 {
1291 if (!JSession::checkToken()) {
1292 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1293 }
1294
1295 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1296 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1297 }
1298
1299 $this->do_updateroom(true);
1300 }
1301
1302 private function do_updateroom($stay = false)
1303 {
1304 $app = JFactory::getApplication();
1305 $config = VBOFactory::getConfig();
1306 $dbo = JFactory::getDbo();
1307
1308 $pcname = VikRequest::getString('cname', '', 'request');
1309 $pccat = VikRequest::getVar('ccat', array(0));
1310 $pcdescr = VikRequest::getString('cdescr', '', 'request', VIKREQUEST_ALLOWRAW);
1311 $psmalldesc = VikRequest::getString('smalldesc', '', 'request', VIKREQUEST_ALLOWRAW);
1312 $pccarat = VikRequest::getVar('ccarat', array(0));
1313 $pcoptional = VikRequest::getVar('coptional', array(0));
1314 $pcavail = VikRequest::getString('cavail', '', 'request');
1315 $pwhereup = VikRequest::getInt('whereup', 0, 'request');
1316 $pautoresize = VikRequest::getString('autoresize', '', 'request');
1317 $presizeto = VikRequest::getString('resizeto', '', 'request');
1318 $pautoresizemore = VikRequest::getString('autoresizemore', '', 'request');
1319 $presizetomore = VikRequest::getString('resizetomore', '', 'request');
1320 $punits = VikRequest::getInt('units', '', 'request');
1321 $pimages = VikRequest::getVar('cimgmore', null, 'files', 'array');
1322 $pactmoreimgs = VikRequest::getString('actmoreimgs', '', 'request');
1323 $pfromadult = VikRequest::getInt('fromadult', '', 'request');
1324 $ptoadult = VikRequest::getInt('toadult', '', 'request');
1325 $pfromchild = VikRequest::getInt('fromchild', '', 'request');
1326 $ptochild = VikRequest::getInt('tochild', '', 'request');
1327 $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array(0));
1328 $padultsdiffval = VikRequest::getVar('adultsdiffval', array(0));
1329 $padultsdiffnum = VikRequest::getVar('adultsdiffnum', array(0));
1330 $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array(0));
1331 $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array(0));
1332 $ptotpeople = VikRequest::getInt('totpeople', '', 'request');
1333 $pmintotpeople = VikRequest::getInt('mintotpeople', '', 'request');
1334 $pmintotpeople = $pmintotpeople < 1 ? 1 : $pmintotpeople;
1335 $plastavail = VikRequest::getString('lastavail', '', 'request');
1336 $plastavail = empty($plastavail) ? 0 : intval($plastavail);
1337 $psuggocc = VikRequest::getInt('suggocc', 1, 'request');
1338 $pcustprice = VikRequest::getString('custprice', '', 'request');
1339 $pcustprice = empty($pcustprice) ? '' : floatval($pcustprice);
1340 $pcustpricetxt = VikRequest::getString('custpricetxt', '', 'request', VIKREQUEST_ALLOWRAW);
1341 $pcustpricesubtxt = VikRequest::getString('custpricesubtxt', '', 'request', VIKREQUEST_ALLOWRAW);
1342 $preqinfo = VikRequest::getInt('reqinfo', '', 'request');
1343 $ppricecal = VikRequest::getInt('pricecal', '', 'request');
1344 $pdefcalcost = VikRequest::getString('defcalcost', '', 'request');
1345 $pdefrplan = VikRequest::getInt('defrplan', 0, 'request');
1346 $pmaxminpeople = VikRequest::getString('maxminpeople', '', 'request');
1347 $pcimgcaption = VikRequest::getVar('cimgcaption', array());
1348 $pimgsorting = VikRequest::getVar('imgsorting', array());
1349 $pupdatecaption = VikRequest::getInt('updatecaption', '', 'request');
1350 $pmaxminpeople = in_array($pmaxminpeople, array('0', '1', '2', '3', '4', '5')) ? $pmaxminpeople : '0';
1351 $pseasoncal = VikRequest::getInt('seasoncal', 0, 'request');
1352 $pseasoncal = $pseasoncal >= 0 || $pseasoncal <= 3 ? $pseasoncal : 0;
1353 $pseasoncal_nights = VikRequest::getString('seasoncal_nights', '', 'request');
1354 $pseasoncal_prices = VikRequest::getString('seasoncal_prices', '', 'request');
1355 $pseasoncal_restr = VikRequest::getString('seasoncal_restr', '', 'request');
1356 $pmulti_units = VikRequest::getInt('multi_units', '', 'request');
1357 $pmulti_units = $punits > 1 ? $pmulti_units : 0;
1358 $psefalias = VikRequest::getString('sefalias', '', 'request');
1359 $psefalias = empty($psefalias) ? JFilterOutput::stringURLSafe($pcname) : JFilterOutput::stringURLSafe($psefalias);
1360 $pcustptitle = VikRequest::getString('custptitle', '', 'request');
1361 $pcustptitlew = VikRequest::getString('custptitlew', '', 'request');
1362 $pcustptitlew = in_array($pcustptitlew, array('before', 'after', 'replace')) ? $pcustptitlew : 'before';
1363 $pmetakeywords = VikRequest::getString('metakeywords', '', 'request');
1364 $pmetadescription = VikRequest::getString('metadescription', '', 'request');
1365 $pshare_with = VikRequest::getVar('share_with', array());
1366 $scalnights_arr = array();
1367 if (!empty($pseasoncal_nights)) {
1368 $scalnights = explode(',', $pseasoncal_nights);
1369 foreach ($scalnights as $scalnight) {
1370 if (intval(trim($scalnight)) > 0) {
1371 $scalnights_arr[] = intval(trim($scalnight));
1372 }
1373 }
1374 }
1375 if ($scalnights_arr) {
1376 $pseasoncal_nights = implode(', ', $scalnights_arr);
1377 } else {
1378 $pseasoncal_nights = '';
1379 $pseasoncal = 0;
1380 }
1381 $roomparams = [
1382 'lastavail' => $plastavail,
1383 'suggocc' => $psuggocc,
1384 'custprice' => $pcustprice,
1385 'custpricetxt' => $pcustpricetxt,
1386 'custpricesubtxt' => $pcustpricesubtxt,
1387 'reqinfo' => $preqinfo,
1388 'pricecal' => $ppricecal,
1389 'defcalcost' => floatval($pdefcalcost),
1390 'defrplan' => $pdefrplan,
1391 'maxminpeople' => $pmaxminpeople,
1392 'seasoncal' => $pseasoncal,
1393 'seasoncal_nights' => $pseasoncal_nights,
1394 'seasoncal_prices' => $pseasoncal_prices,
1395 'seasoncal_restr' => $pseasoncal_restr,
1396 'multi_units' => $pmulti_units,
1397 'custptitle' => $pcustptitle,
1398 'custptitlew' => $pcustptitlew,
1399 'metakeywords' => $pmetakeywords,
1400 'metadescription' => $pmetadescription,
1401 'layout_style' => VikRequest::getString('layout_style', 'default', 'request'),
1402 'checkin' => VikRequest::getString('listing_checkin', '', 'request'),
1403 'checkout' => VikRequest::getString('listing_checkout', '', 'request'),
1404 ];
1405 //distinctive features
1406 $roomparams['features'] = array();
1407 $newfeatures = array();
1408 if ($punits > 0) {
1409 for ($i=1; $i <= $punits; $i++) {
1410 $distf_name = VikRequest::getVar('feature-name'.$i, array());
1411 $distf_lang = VikRequest::getVar('feature-lang'.$i, array());
1412 $distf_value = VikRequest::getVar('feature-value'.$i, array());
1413 foreach ($distf_name as $distf_k => $distf) {
1414 if (strlen($distf) > 0 && strlen($distf_value[$distf_k]) > 0) {
1415 $use_key = strlen($distf_lang[$distf_k]) > 0 ? $distf_lang[$distf_k] : $distf;
1416 $roomparams['features'][$i][$use_key] = $distf_value[$distf_k];
1417 if ($distf_k < 1) {
1418 //check only the first feature
1419 $newfeatures[$i][$use_key] = $distf_value[$distf_k];
1420 }
1421 }
1422 }
1423 }
1424 }
1425
1426 // load current room record
1427 $dbo->setQuery(
1428 $dbo->getQuery(true)
1429 ->select('*')
1430 ->from($dbo->qn('#__vikbooking_rooms'))
1431 ->where($dbo->qn('id') . ' = ' . (int) $pwhereup)
1432 );
1433 $prevroom = $dbo->loadAssoc();
1434 if (!$prevroom) {
1435 VBOHttpDocument::getInstance()->close(404, 'Record not found');
1436 }
1437
1438 /**
1439 * Store room geo params information.
1440 *
1441 * @since 1.14 (J) - 1.4.0 (WP)
1442 */
1443 $geo = VikBooking::getGeocodingInstance();
1444 $geo_params = $geo->getRoomGeoTransient($pwhereup);
1445 if ($geo_params !== false) {
1446 // make sure the geocoding service was not turned off
1447 $geo_enabled = VikRequest::getInt('geo_enabled', 0, 'request');
1448 if (!$geo_enabled) {
1449 $geo_params->enabled = 0;
1450 }
1451 //
1452 $roomparams['geo'] = $geo_params;
1453 }
1454 //
1455
1456 $roomparamstr = json_encode($roomparams);
1457
1458 jimport('joomla.filesystem.file');
1459 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
1460
1461 if (!empty($pcname)) {
1462
1463 $picon = "";
1464 if (($_FILES['cimg'] ?? null) && !intval($_FILES['cimg']['error']) && VikBooking::caniWrite($updpath) && strlen(trim($_FILES['cimg']['name'])) && @is_uploaded_file($_FILES['cimg']['tmp_name'])) {
1465 $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['cimg']['name'])));
1466 $j = '';
1467 $pwhere = $updpath . $safename;
1468 if (file_exists($updpath . $safename)) {
1469 $j = 1;
1470 while (file_exists($updpath . $j . $safename)) {
1471 $j++;
1472 }
1473 $pwhere = $updpath . $j . $safename;
1474 }
1475 if (!getimagesize($_FILES['cimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
1476 @unlink($pwhere);
1477 } elseif (VikBooking::uploadFile($_FILES['cimg']['tmp_name'], $pwhere)) {
1478 $picon = $j . $safename;
1479 if ((int) $pautoresize && !empty($presizeto)) {
1480 $origmod = (new VikResizer)->proportionalImage($pwhere, $updpath . 'r_' . $j . $safename, $presizeto, $presizeto);
1481 if ($origmod) {
1482 @unlink($pwhere);
1483 $picon = 'r_' . $j . $safename;
1484 }
1485 }
1486 /**
1487 * Create a mini-thumbnail of the room/listing main photo.
1488 *
1489 * @since 1.17.5 (J) - 1.7.5 (WP)
1490 */
1491 try {
1492 // resize the original image
1493 (new VikResizer)->proportionalImage($pwhere, $updpath . 'mini_' . $picon, 96, 96);
1494 } catch (Throwable $e) {
1495 // silently catch any PHP GD error and continue
1496 }
1497 }
1498 }
1499
1500 /**
1501 * Create a mini-thumbnail of the current room/listing main photo.
1502 *
1503 * @since 1.17.5 (J) - 1.7.5 (WP)
1504 */
1505 if (!$picon && !empty($prevroom['img']) && is_file($updpath . $prevroom['img']) && !is_file($updpath . 'mini_' . $prevroom['img'])) {
1506 try {
1507 // resize the original image
1508 (new VikResizer)->proportionalImage($updpath . $prevroom['img'], $updpath . 'mini_' . $prevroom['img'], 96, 96);
1509 } catch (Throwable $e) {
1510 // silently catch any PHP GD error and continue
1511 }
1512 }
1513
1514 // more images
1515 $creativik = new VikResizer;
1516 $bigsdest = $updpath;
1517 $thumbsdest = $updpath;
1518 $dest = $updpath;
1519 $moreimagestr = $pactmoreimgs;
1520 $arrimgs = array();
1521 $captiontexts = array();
1522 $imgcaptions = array();
1523 //captions of uploaded extra images
1524 if (!empty($pactmoreimgs)) {
1525 $sploimgs = explode(';;', $pactmoreimgs);
1526 foreach ($sploimgs as $ki => $oimg) {
1527 if (!empty($oimg)) {
1528 $oldcaption = VikRequest::getString('caption'.$ki, '', 'request', VIKREQUEST_ALLOWHTML);
1529 $imgcaptions[] = $oldcaption;
1530 }
1531 }
1532 }
1533 //
1534 foreach ($pimages['name'] as $kk=>$ci) {
1535 if (!empty($ci)) {
1536 $arrimgs[] = $kk;
1537 $captiontexts[] = isset($pcimgcaption[$kk]) ? $pcimgcaption[$kk] : '';
1538 }
1539 }
1540 foreach ($arrimgs as $ki => $imgk) {
1541 if (strlen(trim($pimages['name'][$imgk]))) {
1542 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimages['name'][$imgk])));
1543 $src = $pimages['tmp_name'][$imgk];
1544 $j = "";
1545 if (file_exists($dest.$filename)) {
1546 $j = rand(171, 1717);
1547 while (file_exists($dest.$j.$filename)) {
1548 $j++;
1549 }
1550 }
1551 $finaldest = $dest.$j.$filename;
1552 $check = getimagesize($pimages['tmp_name'][$imgk]);
1553 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
1554 if (VikBooking::uploadFile($src, $finaldest)) {
1555 $gimg = $j.$filename;
1556 //orig img
1557 $origmod = true;
1558 if ($pautoresizemore == "1" && !empty($presizetomore)) {
1559 $origmod = $creativik->proportionalImage($finaldest, $bigsdest.'big_'.$j.$filename, $presizetomore, $presizetomore);
1560 } else {
1561 VikBooking::uploadFile($finaldest, $bigsdest.'big_'.$j.$filename, true);
1562 }
1563 //thumb
1564 $thumbsize = VikBooking::getThumbSize();
1565 $thumb = $creativik->proportionalImage($finaldest, $thumbsdest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
1566 if (!$thumb || !$origmod) {
1567 if (file_exists($bigsdest.'big_'.$j.$filename)) @unlink($bigsdest.'big_'.$j.$filename);
1568 if (file_exists($thumbsdest.'thumb_'.$j.$filename)) @unlink($thumbsdest.'thumb_'.$j.$filename);
1569 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1570 } else {
1571 $moreimagestr .= $j.$filename.";;";
1572 $imgcaptions[] = $captiontexts[$ki];
1573 }
1574 @unlink($finaldest);
1575 } else {
1576 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1577 }
1578 } else {
1579 VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1580 }
1581 }
1582 }
1583 //sorting of extra images
1584 $sorted_extraim = array();
1585 $sorted_captions = array();
1586 $extraim_parts = explode(';;', $moreimagestr);
1587 foreach ($pimgsorting as $k => $v) {
1588 $capkey = -1;
1589 if (isset($extraim_parts[$k])) {
1590 $sorted_extraim[] = $v;
1591 foreach ($extraim_parts as $oldk => $oldv) {
1592 if ($oldv == $v) {
1593 $capkey = $oldk;
1594 break;
1595 }
1596 }
1597 }
1598 if (isset($imgcaptions[$capkey])) {
1599 $sorted_captions[] = $imgcaptions[$capkey];
1600 }
1601 }
1602 $tot_sorted_im = count($sorted_extraim);
1603 if ($tot_sorted_im != count($extraim_parts)) {
1604 foreach ($extraim_parts as $k => $v) {
1605 if ($k <= ($tot_sorted_im - 1)) {
1606 continue;
1607 }
1608 $sorted_extraim[] = $v;
1609 if (isset($imgcaptions[$k])) {
1610 $sorted_captions[] = $imgcaptions[$k];
1611 }
1612 }
1613 }
1614 $moreimagestr = implode(';;', $sorted_extraim);
1615 $imgcaptions = $sorted_captions;
1616 //end more images
1617 if (is_array($pccat) && count($pccat)) {
1618 $pccatdef = "";
1619 foreach ($pccat as $ccat) {
1620 if (!empty($ccat)) {
1621 $pccatdef .= $ccat.";";
1622 }
1623 }
1624 } else {
1625 $pccatdef = "";
1626 }
1627 if (is_array($pccarat) && count($pccarat)) {
1628 $pccaratdef = "";
1629 foreach ($pccarat as $ccarat) {
1630 $pccaratdef .= $ccarat.";";
1631 }
1632 } else {
1633 $pccaratdef = "";
1634 }
1635 if (is_array($pcoptional) && count($pcoptional)) {
1636 $pcoptionaldef = "";
1637 foreach ($pcoptional as $coptional) {
1638 $pcoptionaldef .= $coptional.";";
1639 }
1640 } else {
1641 $pcoptionaldef = "";
1642 }
1643 $pcavaildef=($pcavail=="yes" ? "1" : "0");
1644 if ($pfromadult > $ptoadult) {
1645 $pfromadult = 1;
1646 $ptoadult = 1;
1647 }
1648 if ($pfromchild > $ptochild) {
1649 $pfromchild = 1;
1650 $ptochild = 1;
1651 }
1652
1653 //adults charges/discounts
1654 $adchdisctouch = false;
1655 $q = "SELECT * FROM `#__vikbooking_rooms` WHERE `id`='".$pwhereup."';";
1656 $dbo->setQuery($q);
1657 $dbo->execute();
1658 $oldroom = $dbo->loadAssocList();
1659 $oldroom = $oldroom[0];
1660 if ($oldroom['fromadult'] == $pfromadult && $oldroom['toadult'] == $ptoadult) {
1661 if ($oldroom['toadult'] > 1 && $oldroom['fromadult'] < $oldroom['toadult'] && @count($padultsdiffnum) > 0) {
1662 $startadind = $oldroom['fromadult'] > 0 ? $oldroom['fromadult'] : 1;
1663 for($adi = $startadind; $adi <= $oldroom['toadult']; $adi++) {
1664 foreach ($padultsdiffnum as $kad=>$vad) {
1665 if (intval($vad) == intval($adi) && strlen($padultsdiffval[$kad]) > 0) {
1666 $adchdisctouch = true;
1667 $inschdisc = intval($padultsdiffchdisc[$kad]) == 1 ? 1 : 2;
1668 $insvalpcent = intval($padultsdiffvalpcent[$kad]) == 1 ? 1 : 2;
1669 $inspernight = intval($padultsdiffpernight[$kad]) == 1 ? 1 : 0;
1670 $insvalue = floatval($padultsdiffval[$kad]);
1671 //check if it exists
1672 $q = "SELECT `id` FROM `#__vikbooking_adultsdiff` WHERE `idroom`='".$oldroom['id']."' AND `adults`='".$adi."';";
1673 $dbo->setQuery($q);
1674 $dbo->execute();
1675 if ($dbo->getNumRows() > 0) {
1676 if ($insvalue > 0) {
1677 //update
1678 $q = "UPDATE `#__vikbooking_adultsdiff` SET `chdisc`='".$inschdisc."', `valpcent`='".$insvalpcent."', `value`='".$insvalue."', `pernight`='".$inspernight."' WHERE `idroom`='".$oldroom['id']."' AND `adults`='".$adi."';";
1679 $dbo->setQuery($q);
1680 $dbo->execute();
1681 } else {
1682 //delete
1683 $q = "DELETE FROM `#__vikbooking_adultsdiff` WHERE `idroom`='".$oldroom['id']."' AND `adults`='".$adi."';";
1684 $dbo->setQuery($q);
1685 $dbo->execute();
1686 }
1687 } else {
1688 //insert
1689 $q = "INSERT INTO `#__vikbooking_adultsdiff` (`idroom`,`chdisc`,`valpcent`,`value`,`adults`,`pernight`) VALUES('".$oldroom['id']."', '".$inschdisc."', '".$insvalpcent."', '".$insvalue."', '".$adi."', '".$inspernight."');";
1690 $dbo->setQuery($q);
1691 $dbo->execute();
1692 }
1693 }
1694 }
1695 }
1696 }
1697 } else {
1698 //min and max adults num have changed, delete
1699 $q = "DELETE FROM `#__vikbooking_adultsdiff` WHERE `idroom`='".$oldroom['id']."';";
1700 $dbo->setQuery($q);
1701 $dbo->execute();
1702 }
1703 if ($adchdisctouch == true) {
1704 $app->enqueueMessage(JText::translate('VBUPDROOMADCHDISCSAVED'));
1705 }
1706 //
1707 //check distinctive features if there were any changes
1708 $old_rparams = json_decode($oldroom['params'], true);
1709 $old_rparams = is_array($old_rparams) ? $old_rparams : array();
1710 if (array_key_exists('features', $old_rparams)) {
1711 $oldfeatures = array();
1712 foreach ($old_rparams['features'] as $rnumunit => $oldfeat) {
1713 foreach ($oldfeat as $featname => $featval) {
1714 $oldfeatures[$rnumunit][$featname] = $featval;
1715 break;
1716 }
1717 }
1718 /**
1719 * We reset the sub-unit information to all bookings only in case the new
1720 * number of units is reduced. When we add new units or we modify the contents,
1721 * we keep everything as is for the past reservations.
1722 *
1723 * @since 1.15.2 (J) - 1.5.5 (WP)
1724 */
1725 if ($oldfeatures != $newfeatures && count($newfeatures) < count($oldfeatures)) {
1726 // changes were made to the first index (Room Number by default) of the distinctive features
1727 // set to NULL all the already set roomindexes in bookings
1728 $q = "UPDATE `#__vikbooking_ordersrooms` SET `roomindex`=NULL WHERE `idroom`=".(int)$oldroom['id'].";";
1729 $dbo->setQuery($q);
1730 $dbo->execute();
1731 }
1732 }
1733 //
1734 $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).";";
1735 $dbo->setQuery($q);
1736 $dbo->execute();
1737
1738 /**
1739 * Share availability calendars with other rooms.
1740 *
1741 * @since 1.13
1742 */
1743 // always reset relations for this main room
1744 $q = "DELETE FROM `#__vikbooking_calendars_xref` WHERE `mainroom`={$pwhereup};";
1745 $dbo->setQuery($q);
1746 $dbo->execute();
1747 $newxref = array();
1748 foreach ($pshare_with as $cldroom) {
1749 if (!empty($cldroom)) {
1750 array_push($newxref, (int)$cldroom);
1751 }
1752 }
1753 foreach ($newxref as $cldroom) {
1754 $q = "INSERT INTO `#__vikbooking_calendars_xref` (`mainroom`, `childroom`) VALUES ({$pwhereup}, {$cldroom});";
1755 $dbo->setQuery($q);
1756 $dbo->execute();
1757 }
1758
1759 /**
1760 * Room upgrade options.
1761 *
1762 * @since 1.16.0 (J) - 1.6.0 (WP)
1763 */
1764 $room_upgrade_options = [];
1765 $room_upgrade = VikRequest::getInt('room_upgrade', 0, 'request');
1766 $upgrade_rooms = VikRequest::getVar('upgrade_rooms', array());
1767 $upgrade_discount = VikRequest::getFloat('upgrade_discount', 0, 'request');
1768 if ($room_upgrade && is_array($upgrade_rooms) && count($upgrade_rooms)) {
1769 $upgrade_rooms = array_map(function($rid) {
1770 return (int)$rid;
1771 }, $upgrade_rooms);
1772
1773 $room_upgrade_options = [
1774 'rooms' => $upgrade_rooms,
1775 'discount' => $upgrade_discount,
1776 ];
1777 }
1778 $config->set('room_upgrade_options_' . $pwhereup, json_encode($room_upgrade_options));
1779
1780 /**
1781 * Minimum advance booking offset can be defined at room-level (always in hours).
1782 *
1783 * @since 1.18.3 (J) - 1.8.3 (WP)
1784 */
1785 $pmin_adv_notice_room = VikRequest::getInt('min_adv_notice_room', 0, 'request');
1786 $pmindate = VikRequest::getInt('mindate', 0, 'request');
1787 if ($pmin_adv_notice_room && $pmindate > 0) {
1788 // set value
1789 $config->set("room_{$pwhereup}_min_adv_notice", $pmindate);
1790 } else {
1791 // unset value
1792 $config->set("room_{$pwhereup}_min_adv_notice", null);
1793 }
1794
1795 /**
1796 * Maximum advance booking offset can be defined at room-level.
1797 *
1798 * @since 1.16.3 (J) - 1.6.3 (WP)
1799 */
1800 $pmax_adv_notice_room = VikRequest::getInt('max_adv_notice_room', 0, 'request');
1801 $pmaxdate = VikRequest::getInt('maxdate', 0, 'request');
1802 $pmaxdateinterval = VikRequest::getString('maxdateinterval', '', 'request');
1803 $maxdate_str = '';
1804 if ($pmax_adv_notice_room && $pmaxdate > 0) {
1805 $pmaxdateinterval = !in_array($pmaxdateinterval, array('d', 'w', 'm', 'y')) ? 'y' : $pmaxdateinterval;
1806 $maxdate_str = '+' . $pmaxdate . $pmaxdateinterval;
1807 }
1808 $config->set("room_{$pwhereup}_max_adv_notice", $maxdate_str);
1809
1810 $app->enqueueMessage(JText::translate('VBUPDROOMOK'));
1811 }
1812
1813 if ($pupdatecaption == 1 || $stay === true) {
1814 $app->redirect("index.php?option=com_vikbooking&task=editroom&cid[]=".$pwhereup);
1815 } else {
1816 $app->redirect("index.php?option=com_vikbooking&task=rooms");
1817 }
1818 }
1819
1820 public function modavail() {
1821 if (!JSession::checkToken() && !JSession::checkToken('get')) {
1822 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1823 }
1824 $cid = VikRequest::getVar('cid', array(0));
1825 $room = $cid[0];
1826 if (!empty($room)) {
1827 $dbo = JFactory::getDBO();
1828 $q = "SELECT `avail` FROM `#__vikbooking_rooms` WHERE `id`=".$dbo->quote($room).";";
1829 $dbo->setQuery($q);
1830 $dbo->execute();
1831 $get = $dbo->loadAssocList();
1832 $q = "UPDATE `#__vikbooking_rooms` SET `avail`='".(intval($get[0]['avail'])==1 ? 0 : 1)."' WHERE `id`=".$dbo->quote($room).";";
1833 $dbo->setQuery($q);
1834 $dbo->execute();
1835 }
1836 $mainframe = JFactory::getApplication();
1837 $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
1838 }
1839
1840 public function removeroom()
1841 {
1842 if (!JSession::checkToken()) {
1843 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1844 }
1845
1846 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
1847 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1848 }
1849
1850 $ids = VikRequest::getVar('cid', array(0));
1851 if (@count($ids)) {
1852 $dbo = JFactory::getDBO();
1853 foreach ($ids as $d) {
1854 $q = "DELETE FROM `#__vikbooking_rooms` WHERE `id`=".$dbo->quote($d).";";
1855 $dbo->setQuery($q);
1856 $dbo->execute();
1857 $q = "DELETE FROM `#__vikbooking_dispcost` WHERE `idroom`=".$dbo->quote($d).";";
1858 $dbo->setQuery($q);
1859 $dbo->execute();
1860 }
1861 }
1862 $mainframe = JFactory::getApplication();
1863 $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
1864 }
1865
1866 public function tariffs() {
1867 VikBookingHelper::printHeader("fares");
1868
1869 VikRequest::setVar('view', VikRequest::getCmd('view', 'tariffs'));
1870
1871 parent::display();
1872
1873 if (VikBooking::showFooter()) {
1874 VikBookingHelper::printFooter();
1875 }
1876 }
1877
1878 public function removetariffs()
1879 {
1880 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
1881 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1882 }
1883
1884 $ids = VikRequest::getVar('cid', array(0));
1885 $proomid = VikRequest::getInt('roomid', '', 'request');
1886 if (@count($ids)) {
1887 $dbo = JFactory::getDBO();
1888 foreach ($ids as $r) {
1889 $x=explode(";", $r);
1890 foreach ($x as $rm) {
1891 if (!empty($rm)) {
1892 $q = "DELETE FROM `#__vikbooking_dispcost` WHERE `id`=".$dbo->quote($rm).";";
1893 $dbo->setQuery($q);
1894 $dbo->execute();
1895 }
1896 }
1897 }
1898 }
1899 $mainframe = JFactory::getApplication();
1900 $mainframe->redirect("index.php?option=com_vikbooking&task=tariffs&cid[]=".$proomid);
1901 }
1902
1903 public function editbusy() {
1904 VikBookingHelper::printHeader("8");
1905
1906 VikRequest::setVar('view', VikRequest::getCmd('view', 'editbusy'));
1907
1908 parent::display();
1909
1910 if (VikBooking::showFooter()) {
1911 VikBookingHelper::printFooter();
1912 }
1913 }
1914
1915 public function updatebusy()
1916 {
1917 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1918 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1919 }
1920
1921 $this->do_updatebusy();
1922 }
1923
1924 public function updatebusydoinv()
1925 {
1926 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1927 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1928 }
1929
1930 $this->do_updatebusy('geninvoices');
1931 }
1932
1933 private function do_updatebusy($callback = '')
1934 {
1935 $pidorder = VikRequest::getInt('idorder', 0, 'request');
1936 $pcheckindate = VikRequest::getString('checkindate', '', 'request');
1937 $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
1938 $pcheckinh = VikRequest::getString('checkinh', '', 'request');
1939 $pcheckinm = VikRequest::getString('checkinm', '', 'request');
1940 $pcheckouth = VikRequest::getString('checkouth', '', 'request');
1941 $pcheckoutm = VikRequest::getString('checkoutm', '', 'request');
1942 $pcustdata = VikRequest::getString('custdata', '', 'request');
1943 $pareprices = VikRequest::getString('areprices', '', 'request');
1944 $ptotpaid = VikRequest::getString('totpaid', '', 'request');
1945 $prefund = VikRequest::getString('refund', '', 'request');
1946 $pfrominv = VikRequest::getInt('frominv', '', 'request');
1947 $pvcm = VikRequest::getInt('vcm', '', 'request');
1948 $pgoto = VikRequest::getString('goto', '', 'request');
1949 $pextracn = VikRequest::getVar('extracn', []);
1950 $pextracc = VikRequest::getVar('extracc', []);
1951 $pextractx = VikRequest::getVar('extractx', []);
1952 /**
1953 * This is a "foreign key" integer value useful for other Vik plugins
1954 * to store custom extra services within a VBO reservation. Another
1955 * custom value "extra foreign data" (extracdata) is added. We also
1956 * support a "type" string useful for VCM to determine the type of service.
1957 *
1958 * @since 1.16.0 (J) - 1.6.0 (WP)
1959 * @since 1.16.1 (J) - 1.6.1 (WP) added the "type" string.
1960 */
1961 $pextractype = VikRequest::getVar('extractype', []);
1962 $pextracfk = VikRequest::getVar('extracfk', []);
1963 $pextracdata = VikRequest::getVar('extracdata', [], 'request', 'array', VIKREQUEST_ALLOWRAW);
1964
1965 $dbo = JFactory::getDbo();
1966 $user = JFactory::getUser();
1967 $app = JFactory::getApplication();
1968
1969 // availability helper
1970 $av_helper = VikBooking::getAvailabilityInstance();
1971
1972 $actnow = time();
1973 $nowdf = VikBooking::getDateFormat(true);
1974 if ($nowdf == "%d/%m/%Y") {
1975 $df = 'd/m/Y';
1976 } elseif ($nowdf == "%m/%d/%Y") {
1977 $df = 'm/d/Y';
1978 } else {
1979 $df = 'Y/m/d';
1980 }
1981
1982 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder;
1983 $dbo->setQuery($q, 0, 1);
1984 $ord = $dbo->loadAssoc();
1985 if (!$ord) {
1986 $app->redirect("index.php?option=com_vikbooking&task=rooms");
1987 exit;
1988 }
1989
1990 $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;";
1991 $dbo->setQuery($q);
1992 $ordersrooms = $dbo->loadAssocList();
1993
1994 // do not touch this array property because it's used by VCM
1995 $ord['rooms_info'] = $ordersrooms;
1996
1997 // room stay dates in case of split stay
1998 $room_stay_dates = [];
1999 if ($ord['split_stay']) {
2000 if ($ord['status'] == 'confirmed') {
2001 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
2002 } else {
2003 $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $ord['id'], []);
2004 }
2005 // immediately count the number of nights of stay for each split room
2006 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
2007 if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
2008 // overwrite values for compatibility with non-confirmed bookings
2009 $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
2010 $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
2011 }
2012 $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
2013 // overwrite the whole array
2014 $room_stay_dates[$sps_r_k] = $sps_r_v;
2015 }
2016 }
2017
2018 // package or custom rate
2019 $is_package = !empty($ord['pkg']) ? true : false;
2020 $is_cust_cost = false;
2021 foreach ($ordersrooms as $kor => $or) {
2022 if ($is_package !== true && !empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
2023 $is_cust_cost = true;
2024 break;
2025 }
2026 }
2027
2028 // room switching
2029 $toswitch = array();
2030 $idbooked = array();
2031 $rooms_units = array();
2032
2033 $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms`;";
2034 $dbo->setQuery($q);
2035 $all_rooms = $dbo->loadAssocList();
2036 foreach ($all_rooms as $rr) {
2037 $rooms_units[$rr['id']]['name'] = $rr['name'];
2038 $rooms_units[$rr['id']]['units'] = $rr['units'];
2039 }
2040
2041 foreach ($ordersrooms as $ind => $or) {
2042 $switch_command = VikRequest::getString('switch_'.$or['id'], '', 'request');
2043 if (!empty($switch_command) && intval($switch_command) != $or['idroom'] && array_key_exists(intval($switch_command), $rooms_units)) {
2044 if (!isset($idbooked[$or['idroom']])) {
2045 $idbooked[$or['idroom']] = 0;
2046 }
2047 $idbooked[$or['idroom']]++;
2048 $orkey = count($toswitch);
2049 $toswitch[$orkey]['from'] = $or['idroom'];
2050 $toswitch[$orkey]['to'] = intval($switch_command);
2051 $toswitch[$orkey]['record'] = $or;
2052 $toswitch[$orkey]['record_ind'] = $ind;
2053 }
2054 }
2055
2056 if (count($toswitch) && (!empty($ordersrooms[0]['idtar']) || $is_package || $is_cust_cost)) {
2057 foreach ($toswitch as $ksw => $rsw) {
2058 $plusunit = array_key_exists($rsw['to'], $idbooked) ? $idbooked[$rsw['to']] : 0;
2059 $room_checkin = $ord['checkin'];
2060 $room_checkout = $ord['checkout'];
2061 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
2062 $room_checkin = $room_stay_dates[$rsw['record_ind']]['checkin'];
2063 $room_checkout = $room_stay_dates[$rsw['record_ind']]['checkout'];
2064 }
2065 if (!VikBooking::roomBookable($rsw['to'], ($rooms_units[$rsw['to']]['units'] + $plusunit), $room_checkin, $room_checkout)) {
2066 // the room is not available
2067 unset($toswitch[$ksw]);
2068 VikError::raiseWarning('', JText::sprintf('VBSWITCHRERR', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']));
2069 }
2070 }
2071 if (count($toswitch)) {
2072 // reset first record rate
2073 reset($ordersrooms);
2074 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$ordersrooms[0]['id'].";";
2075 $dbo->setQuery($q);
2076 $dbo->execute();
2077
2078 // flag for invoking VCM at a proper time
2079 $vcm_should_run = false;
2080
2081 foreach ($toswitch as $ksw => $rsw) {
2082 // update room reservation record
2083 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idroom`=".$rsw['to'].",`idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$rsw['record']['id'].";";
2084 $dbo->setQuery($q);
2085 $dbo->execute();
2086 $app->enqueueMessage(JText::sprintf('VBSWITCHROK', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']));
2087
2088 // update Notes field for this booking to keep track of the previous room that was assigned
2089 $prev_room_name = array_key_exists($rsw['from'], $rooms_units) ? $rooms_units[$rsw['from']]['name'] : '';
2090 if (!empty($prev_room_name)) {
2091 $new_notes = JText::sprintf('VBOPREVROOMMOVED', $prev_room_name, date($df.' H:i:s'))."\n".$ord['adminnotes'];
2092 $q = "UPDATE `#__vikbooking_orders` SET `adminnotes`=".$dbo->quote($new_notes)." WHERE `id`=".(int)$ord['id'].";";
2093 $dbo->setQuery($q);
2094 $dbo->execute();
2095 }
2096
2097 if ($ord['status'] == 'confirmed') {
2098 // update room record in _busy
2099 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'])) {
2100 // in case of a split stay it is fundamental to update the exact busy record ID
2101 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=" . $rsw['to'] . " WHERE `id`=" . (int)$room_stay_dates[$rsw['record_ind']]['id'];
2102 $dbo->setQuery($q);
2103 $dbo->execute();
2104 } else {
2105 // regular processing of a room ID for a reservation, no matter which one, we switch it
2106 $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'];
2107 $dbo->setQuery($q, 0, 1);
2108 $cur_busy = $dbo->loadAssoc();
2109 if ($cur_busy) {
2110 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=".$rsw['to']." WHERE `id`=".$cur_busy['id']." AND `idroom`=".$cur_busy['idroom']." LIMIT 1;";
2111 $dbo->setQuery($q);
2112 $dbo->execute();
2113 }
2114 }
2115
2116 /**
2117 * Make sure to take care of the shared calendars before invoking VCM.
2118 * Register the flag to run the Channel Manager and leave the booking
2119 * array unchanged to run just one update request.
2120 *
2121 * @since 1.16.0 (J) - 1.6.0 (WP)
2122 */
2123 $vcm_should_run = true;
2124
2125 } elseif ($ord['status'] == 'standby') {
2126 // remove record in _tmplock
2127 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($ord['id']) . ";";
2128 $dbo->setQuery($q);
2129 $dbo->execute();
2130 // check if it's a split stay
2131 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
2132 // update room ID in split stay data
2133 $room_stay_dates[$rsw['record_ind']]['idroom'] = $rsw['to'];
2134 // update configuration record
2135 VBOFactory::getConfig()->set('split_stay_' . $ord['id'], json_encode($room_stay_dates));
2136 }
2137 }
2138 }
2139
2140 // unset any previously booked room due to calendar sharing
2141 VikBooking::cleanSharedCalendarsBusy($ord['id']);
2142 // check if some of the rooms booked have shared calendars
2143 VikBooking::updateSharedCalendars($ord['id']);
2144
2145 if ($vcm_should_run) {
2146 // we can now run the Channel Manager after having updated the shared calendars
2147 $vcm_autosync = VikBooking::vcmAutoUpdate();
2148 if ($vcm_autosync > 0) {
2149 $vcm_obj = VikBooking::getVcmInvoker();
2150 $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
2151 $sync_result = $vcm_obj->doSync();
2152 if ($sync_result === false) {
2153 $vcm_err = $vcm_obj->getError();
2154 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
2155 }
2156 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
2157 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>');
2158 }
2159 }
2160
2161 //Booking History
2162 VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($ord));
2163 //
2164 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2165 exit;
2166 }
2167 }
2168
2169 // update booking data
2170 $first = VikBooking::getDateTimestamp($pcheckindate, $pcheckinh, $pcheckinm);
2171 $second = VikBooking::getDateTimestamp($pcheckoutdate, $pcheckouth, $pcheckoutm);
2172 if ($second <= $first) {
2173 VikError::raiseWarning('', JText::translate('ERRPREV'));
2174 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2175 exit;
2176 }
2177
2178 $secdiff = $second - $first;
2179 $daysdiff = $secdiff / 86400;
2180 if (is_int($daysdiff)) {
2181 if ($daysdiff < 1) {
2182 $daysdiff = 1;
2183 }
2184 } else {
2185 if ($daysdiff < 1) {
2186 $daysdiff = 1;
2187 } else {
2188 $sum = floor($daysdiff) * 86400;
2189 $newdiff = $secdiff - $sum;
2190 $maxhmore = VikBooking::getHoursMoreRb() * 3600;
2191 if ($maxhmore >= $newdiff) {
2192 $daysdiff = floor($daysdiff);
2193 } else {
2194 $daysdiff = ceil($daysdiff);
2195 }
2196 }
2197 }
2198
2199 $groupdays = VikBooking::getGroupDays($first, $second, $daysdiff);
2200 $opertwounits = true;
2201
2202 $units_counter = array();
2203 $prm_room_oid = VikRequest::getInt('rm_room_oid', 0, 'request');
2204 foreach ($ordersrooms as $ind => $or) {
2205 if (!isset($units_counter[$or['idroom']])) {
2206 $units_counter[$or['idroom']] = -1;
2207 }
2208 if ($prm_room_oid != $or['id']) {
2209 $units_counter[$or['idroom']]++;
2210 }
2211 }
2212
2213 /**
2214 * Split stay data for booking and rooms different stay dates.
2215 *
2216 * @since 1.16.0 (J) - 1.6.0 (WP)
2217 */
2218 $split_stay_data = VikRequest::getVar('split_stay_data', array());
2219 $room_modify_dates = VikRequest::getVar('room_modify_dates', array());
2220 $split_stay_checkins = [];
2221 $split_stay_checkouts = [];
2222
2223 if ($ord['split_stay'] && !empty($split_stay_data)) {
2224 // make sure the min/max split stay dates match the booking global dates
2225 foreach ($split_stay_data as $sps_k => $split_stay) {
2226 if (empty($split_stay['checkin']) || empty($split_stay['checkout'])) {
2227 continue;
2228 }
2229 $new_room_checkin = VikBooking::getDateTimestamp($split_stay['checkin'], $pcheckinh, $pcheckinm);
2230 $new_room_checkout = VikBooking::getDateTimestamp($split_stay['checkout'], $pcheckouth, $pcheckoutm);
2231 $split_stay_checkins[] = $new_room_checkin;
2232 $split_stay_checkouts[] = $new_room_checkout;
2233 if (isset($room_stay_dates[$sps_k])) {
2234 $room_stay_dates[$sps_k]['new_checkin'] = $new_room_checkin;
2235 $room_stay_dates[$sps_k]['new_checkout'] = $new_room_checkout;
2236 $room_stay_dates[$sps_k]['new_nights'] = $av_helper->countNightsOfStay($new_room_checkin, $new_room_checkout);
2237 }
2238 }
2239 if (empty($split_stay_checkins) || empty($split_stay_checkouts)) {
2240 // error
2241 VikError::raiseWarning('', 'Error, split stay rooms must have their own stay dates matching the booking check-in and check-out dates');
2242 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2243 exit;
2244 }
2245 if (min($split_stay_checkins) != $first) {
2246 // error
2247 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)));
2248 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2249 exit;
2250 }
2251 if (max($split_stay_checkouts) != $second) {
2252 // error
2253 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)));
2254 $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2255 exit;
2256 }
2257 }
2258
2259 /**
2260 * We need to make sure the sub-units of the rooms involved are not being overbooked.
2261 * In this case, we simply raise an error message by not stopping the process.
2262 *
2263 * @since 1.13.0 (J) - 1.3.0 (WP)
2264 */
2265 $subunits_involved_bids = array();
2266 //
2267
2268 foreach ($ordersrooms as $ind => $or) {
2269 $num = $ind + 1;
2270 $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'] . ";";
2271 $dbo->setQuery($check);
2272 $busy = $dbo->loadAssocList();
2273 if ($busy) {
2274 // determine the days to consider for the count of the availability
2275 $use_groupdays = $groupdays;
2276 $room_checkin = $first;
2277 $room_checkout = $second;
2278 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'])) {
2279 $use_groupdays = VikBooking::getGroupDays($room_stay_dates[$ind]['new_checkin'], $room_stay_dates[$ind]['new_checkout'], $room_stay_dates[$ind]['new_nights']);
2280 $room_checkin = $room_stay_dates[$ind]['new_checkin'];
2281 $room_checkout = $room_stay_dates[$ind]['new_checkout'];
2282 } elseif (!$ord['split_stay'] && !$ord['closure'] && $ord['roomsnum'] > 1 && $ord['days'] > 1 && $ord['status'] == 'confirmed' && VikRequest::getInt('room_modify_dates' . $ind, 0, 'request')) {
2283 // room may have individual stay dates
2284 if (isset($room_modify_dates[$ind]) && !empty($room_modify_dates[$ind]['checkin']) && !empty($room_modify_dates[$ind]['checkout'])) {
2285 // get new stay dates (if changed)
2286 $new_room_checkin = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkin'], $pcheckinh, $pcheckinm);
2287 $new_room_checkout = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkout'], $pcheckouth, $pcheckoutm);
2288 $use_groupdays = VikBooking::getGroupDays($new_room_checkin, $new_room_checkout, $av_helper->countNightsOfStay($new_room_checkin, $new_room_checkout));
2289 $room_checkin = $new_room_checkin;
2290 $room_checkout = $new_room_checkout;
2291 }
2292 }
2293
2294 foreach ($use_groupdays as $gday) {
2295 // count units booked for each stay timestamp
2296 $bfound = 0;
2297 foreach ($busy as $bu) {
2298 if ($gday >= $bu['checkin'] && $gday <= $bu['realback']) {
2299 // increase units booked found
2300 $bfound++;
2301 // keep track of the IDs involved to avoid overbooking for the sub-units
2302 if (!empty($or['roomindex'])) {
2303 if (!isset($subunits_involved_bids[$bu['idorder']])) {
2304 $subunits_involved_bids[$bu['idorder']] = array();
2305 }
2306 array_push($subunits_involved_bids[$bu['idorder']], array(
2307 'idroom' => $or['idroom'],
2308 'roomindex' => $or['roomindex'],
2309 ));
2310 }
2311 }
2312 }
2313
2314 // units booked must be greater than zero in case of split stays involving the same room multiple times
2315 $detract_multi_units = $units_counter[$or['idroom']];
2316 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$ind]) && $room_stay_dates[$ind]['idroom'] == $or['idroom']) {
2317 // split stay bookings never occupy the same room on the same dates
2318 $detract_multi_units = 0;
2319 }
2320 if ($bfound > 0 && $bfound >= ($or['units'] - $detract_multi_units)) {
2321 $opertwounits = false;
2322 break 2;
2323 }
2324
2325 // make sure the room is not temporarily locked while waiting to be paid/confirmed
2326 if ($ord['status'] == 'confirmed' && !VikBooking::roomNotLocked($or['idroom'], $or['units'], $room_checkin, $room_checkout)) {
2327 $opertwounits = false;
2328 break 2;
2329 }
2330 }
2331 }
2332 }
2333
2334 /**
2335 * Make sure no sub-units are overbooked even though the main room is available.
2336 *
2337 * @since 1.13.0 (J) - 1.3.0 (WP)
2338 */
2339 if ($opertwounits === true && $subunits_involved_bids) {
2340 $subunits_involved_bids = array_unique($subunits_involved_bids);
2341 // grab all the information about the bids involved and the related rooms/indexes
2342 $q = "SELECT `or`.`idorder`, `or`.`idroom`, `or`.`roomindex`
2343 FROM `#__vikbooking_ordersrooms` AS `or`
2344 WHERE `or`.`idorder` IN (" . implode(', ', array_keys($subunits_involved_bids)) . ");";
2345 $dbo->setQuery($q);
2346 $involved_data = $dbo->loadAssocList();
2347 foreach ($involved_data as $invb) {
2348 if (empty($invb['roomindex'])) {
2349 continue;
2350 }
2351 foreach ($subunits_involved_bids[$invb['idorder']] as $bookedindex) {
2352 if ($bookedindex['idroom'] == $invb['idroom'] && $bookedindex['roomindex'] == $invb['roomindex']) {
2353 // this same sub-unit is occupied by this booking ID: raise an error message to inform the administrator
2354 $involved_booking = VikBooking::getBookingInfoFromID($invb['idorder']);
2355 $involved_room = VikBooking::getRoomInfo($invb['idroom'], ['name', 'params'], $no_cache = true);
2356 $subunit_name = $invb['roomindex'];
2357 $room_params = (array) json_decode($involved_room['params'] ?? '[]', true);
2358 foreach (($room_params['features'] ?? []) as $rind => $rfeatures) {
2359 if ($rind == $invb['roomindex']) {
2360 foreach ($rfeatures as $fname => $fval) {
2361 if (strlen($fval)) {
2362 $subunit_name = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
2363 break;
2364 }
2365 }
2366 }
2367 }
2368 $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>';
2369 $app->enqueueMessage(
2370 JText::sprintf(
2371 'VBOSUBUNITOVERBOOKEDERR',
2372 $subunit_name,
2373 $involved_room['name'] ?? $invb['idroom'],
2374 date($df, $involved_booking['checkin'] ?? 0),
2375 date($df, $involved_booking['checkout'] ?? 0),
2376 $invb['idorder']
2377 ) . $adjust_link,
2378 'error'
2379 );
2380 }
2381 }
2382 }
2383 }
2384
2385 $forcebooking = VikRequest::getInt('forcebooking', 0, 'request');
2386 if ($opertwounits === true || $forcebooking) {
2387 // update dates, customer information, amount paid and busy records before checking the rates
2388 $turnover_secs = VikBooking::getHoursRoomAvail() * 3600;
2389 $realback = $turnover_secs + $second;
2390
2391 $newtotalpaid = strlen($ptotpaid) > 0 ? floatval($ptotpaid) : "";
2392 $newrefund = strlen($prefund) > 0 ? floatval($prefund) : null;
2393 $roomsnum = $ord['roomsnum'];
2394
2395 // add room to existing booking
2396 $room_added = false;
2397 $padd_room_id = VikRequest::getInt('add_room_id', '', 'request');
2398 $padd_room_adults = VikRequest::getInt('add_room_adults', 2, 'request');
2399 $padd_room_children = VikRequest::getInt('add_room_children', 0, 'request');
2400 $padd_room_fname = VikRequest::getString('add_room_fname', '', 'request');
2401 $padd_room_lname = VikRequest::getString('add_room_lname', '', 'request');
2402 $padd_room_price = VikRequest::getFloat('add_room_price', 0, 'request');
2403 $paliq_add_room = VikRequest::getInt('aliq_add_room', 0, 'request');
2404 if ($padd_room_id > 0 && ($padd_room_adults + $padd_room_children) > 0) {
2405 // no need to re-validate the availability for this new room, as it was made via JS in the View.
2406 // increase the rooms number for later update, and insert the new room record
2407 $roomsnum++;
2408 $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').");";
2409 $dbo->setQuery($q);
2410 $dbo->execute();
2411 $room_added = true;
2412 }
2413
2414 // remove room from existing booking
2415 $room_removed = false;
2416 $room_removed_index = null;
2417 if ($prm_room_oid > 0 && $roomsnum > 1) {
2418 // check if the requested room record exists for removal
2419 $q = "SELECT * FROM `#__vikbooking_ordersrooms` WHERE `id`=".$prm_room_oid." AND `idorder`=".$ord['id'].";";
2420 $dbo->setQuery($q);
2421 $room_before_rm = $dbo->loadAssoc();
2422 if ($room_before_rm) {
2423 // decrease the rooms number for later update, and remove the requested room record
2424 $roomsnum--;
2425 // find the index of this room in the current list before removal
2426 foreach ($ordersrooms as $kor => $or) {
2427 if ($or['id'] == $prm_room_oid) {
2428 $room_removed_index = $kor;
2429 break;
2430 }
2431 }
2432 // go ahead with the deletion of the room record
2433 $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `id`=".$prm_room_oid." AND `idorder`=".$ord['id']." LIMIT 1;";
2434 $dbo->setQuery($q);
2435 $dbo->execute();
2436 $room_removed = $room_before_rm['idroom'];
2437 }
2438 }
2439
2440 if ($ord['split_stay'] && !empty($split_stay_data) && count($split_stay_checkins) && ($room_added !== false || $room_removed !== false)) {
2441 // split stay booking (even if only 1 room left) and one room was either added or removed: set new global stay dates
2442 if ($room_removed !== false && isset($room_removed_index) && isset($split_stay_checkins[$room_removed_index])) {
2443 // exclude the split stay dates of this room that was just removed
2444 unset($split_stay_checkins[$room_removed_index], $split_stay_checkouts[$room_removed_index]);
2445 }
2446 if (count($split_stay_checkins) && count($split_stay_checkouts)) {
2447 // if we still have rooms, and we should, update the booking global stay dates
2448 $first = min($split_stay_checkins);
2449 $second = max($split_stay_checkouts);
2450 $daysdiff = $av_helper->countNightsOfStay($first, $second);
2451 }
2452 }
2453
2454 // update booking's basic information (customer data, dates, tot paid, number of rooms, refund)
2455 $basic_booking = new stdClass;
2456 $basic_booking->id = $ord['id'];
2457 $basic_booking->custdata = $pcustdata;
2458 $basic_booking->days = (int)$daysdiff;
2459 $basic_booking->checkin = $first;
2460 $basic_booking->checkout = $second;
2461 if (strlen($newtotalpaid) > 0) {
2462 $basic_booking->totpaid = $newtotalpaid;
2463 }
2464 $basic_booking->roomsnum = (int)$roomsnum;
2465 if ($newrefund !== null) {
2466 $basic_booking->refund = $newrefund;
2467 }
2468 if ($ord['split_stay'] && $roomsnum < 2 && $room_removed !== false) {
2469 // there is no point in keep treating this reservation as a split stay
2470 $basic_booking->split_stay = 0;
2471 }
2472 $dbo->updateObject('#__vikbooking_orders', $basic_booking, 'id');
2473
2474 // Booking History log for new amount paid (payment update)
2475 if ($newtotalpaid > 0 && $newtotalpaid > (float)$ord['totpaid']) {
2476 $extra_data = new stdClass;
2477 $extra_data->amount_paid = ($newtotalpaid - (float)$ord['totpaid']);
2478 VikBooking::getBookingHistoryInstance()->setBid($ord['id'])->setExtraData($extra_data)->store('PU', JText::sprintf('VBOPREVAMOUNTPAID', VikBooking::numberFormat((float)$ord['totpaid'])));
2479 }
2480
2481 // booking history log for new refund amount
2482 if ($newrefund !== null && $newrefund != (float)$ord['refund']) {
2483 // update current refund value
2484 $ord['refund'] = $newrefund;
2485 // store event
2486 VikBooking::getBookingHistoryInstance()->setBid($ord['id'])->setExtraData(null)->store('RU', JText::sprintf('VBO_NEWREFUND_AMOUNT', VikBooking::numberFormat($ord['refund']), VikBooking::numberFormat($newrefund)));
2487 }
2488
2489 // update busy records
2490 if ($ord['status'] == 'confirmed') {
2491 $allbusy = [];
2492 if ($ord['split_stay'] && !empty($split_stay_data)) {
2493 // in case of split stay we need to update the busy records according to the nights selected
2494 foreach ($split_stay_data as $sps_k => $split_stay) {
2495 if (empty($split_stay['idbusy']) || empty($split_stay['checkin']) || empty($split_stay['checkout'])) {
2496 // missing data
2497 continue;
2498 }
2499 // get selected dates
2500 $room_checkin = VikBooking::getDateTimestamp($split_stay['checkin'], $pcheckinh, $pcheckinm);
2501 $room_checkout = VikBooking::getDateTimestamp($split_stay['checkout'], $pcheckouth, $pcheckoutm);
2502 $room_realback = $turnover_secs + $room_checkout;
2503 // update the exact record
2504 $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $room_checkin . ", `checkout`=" . $room_checkout . ", `realback`=" . $room_realback . " WHERE `id`=" . (int)$split_stay['idbusy'] . ";";
2505 $dbo->setQuery($q);
2506 $dbo->execute();
2507 }
2508 } else {
2509 // regularly update busy records for all rooms involved
2510 $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'] . ";";
2511 $dbo->setQuery($q);
2512 $allbusy = $dbo->loadAssocList();
2513 foreach ($allbusy as $bb) {
2514 $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $first . ", `checkout`=" . $second . ", `realback`=" . $realback . " WHERE `id`=" . $bb['id'] . ";";
2515 $dbo->setQuery($q);
2516 $dbo->execute();
2517 }
2518 }
2519
2520 /**
2521 * Check if some rooms have modified stay dates different than the booking stay dates.
2522 *
2523 * @since 1.16.0 (J) - 1.6.0 (WP)
2524 */
2525 if (!$ord['split_stay'] && !$ord['closure'] && $ord['roomsnum'] > 1 && $ord['days'] > 1) {
2526 // load the occupied stay dates for each room in case they were modified
2527 $room_stay_records = $av_helper->loadSplitStayBusyRecords($ord['id']);
2528 // loop over all rooms to check the requested operations
2529 foreach ($ordersrooms as $ind => $or) {
2530 if (!VikRequest::getInt('room_modify_dates' . $ind, 0, 'request') || !isset($room_stay_records[$ind]) || empty($room_stay_records[$ind]['id'])) {
2531 // toggle is disabled or data is missing
2532 continue;
2533 }
2534 if (isset($room_modify_dates[$ind]) && !empty($room_modify_dates[$ind]['checkin']) && !empty($room_modify_dates[$ind]['checkout'])) {
2535 // calculate the check-in and check-out timestamps, we expect them to be different from the global booking dates
2536 $room_checkin = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkin'], $pcheckinh, $pcheckinm);
2537 $room_checkout = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkout'], $pcheckouth, $pcheckoutm);
2538 $room_realback = $turnover_secs + $room_checkout;
2539 // we don't need to check if the dates are different, we just update the record
2540 $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $room_checkin . ", `checkout`=" . $room_checkout . ", `realback`=" . $room_realback . " WHERE `id`=" . (int)$room_stay_records[$ind]['id'] . ";";
2541 $dbo->setQuery($q);
2542 $dbo->execute();
2543 // inject new room stay timestamps
2544 $ordersrooms[$ind]['modified_checkin'] = $room_checkin;
2545 $ordersrooms[$ind]['modified_checkout'] = $room_checkout;
2546 }
2547 }
2548 }
2549
2550 // add room to existing (confirmed) booking
2551 if ($room_added === true) {
2552 // add busy record for the new room unit
2553 $q = "INSERT INTO `#__vikbooking_busy` (`idroom`,`checkin`,`checkout`,`realback`) VALUES(".$padd_room_id.", ".$dbo->quote($first).", ".$dbo->quote($second).", ".$dbo->quote($realback).");";
2554 $dbo->setQuery($q);
2555 $dbo->execute();
2556 $newbusyid = $dbo->insertid();
2557 $q = "INSERT INTO `#__vikbooking_ordersbusy` (`idorder`,`idbusy`) VALUES(".$ord['id'].", ".(int)$newbusyid.");";
2558 $dbo->setQuery($q);
2559 $dbo->execute();
2560 }
2561
2562 // remove room from existing (confirmed) booking
2563 if ($room_removed !== false) {
2564 // remove busy record for the removed room
2565 if ($ord['split_stay'] && !empty($split_stay_data) && !empty($room_removed_index)) {
2566 // in case of split stay we want to remove the exact dates of the previously booked room
2567 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'])) {
2568 // remove the exact records
2569 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`=" . $room_stay_dates[$room_removed_index]['id'] . " AND `idroom`=" . $room_removed . ";";
2570 $dbo->setQuery($q);
2571 $dbo->execute();
2572 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=" . $ord['id'] . " AND `idbusy`=" . $room_stay_dates[$room_removed_index]['id'] . ";";
2573 $dbo->setQuery($q);
2574 $dbo->execute();
2575 }
2576 } else {
2577 // regularly remove the first matching room
2578 foreach ($allbusy as $bb) {
2579 if ($bb['idroom'] == $room_removed) {
2580 // remove the first room with this ID that was booked
2581 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`=".$bb['id']." AND `idroom`=".$room_removed.";";
2582 $dbo->setQuery($q);
2583 $dbo->execute();
2584 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".$ord['id']." AND `idbusy`=".$bb['id'].";";
2585 $dbo->setQuery($q);
2586 $dbo->execute();
2587 break;
2588 }
2589 }
2590 }
2591 }
2592
2593 if ($ord['checkin'] != $first || $ord['checkout'] != $second || $room_added === true || $room_removed !== false) {
2594 // unset any previously booked room due to calendar sharing
2595 VikBooking::cleanSharedCalendarsBusy($ord['id']);
2596 // check if some of the rooms booked have shared calendars
2597 VikBooking::updateSharedCalendars($ord['id'], array(), $first, $second);
2598
2599 // invoke Channel Manager
2600 $vcm_autosync = VikBooking::vcmAutoUpdate();
2601 if ($vcm_autosync > 0) {
2602 $vcm_obj = VikBooking::getVcmInvoker();
2603 $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
2604 $sync_result = $vcm_obj->doSync();
2605 if ($sync_result === false) {
2606 $vcm_err = $vcm_obj->getError();
2607 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
2608 }
2609 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
2610 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>');
2611 }
2612 //
2613 }
2614 }
2615
2616 $upd_esit = JText::translate('RESUPDATED');
2617
2618 // update the room rates
2619 $isdue = 0;
2620 $tot_taxes = 0;
2621 $tot_city_taxes = 0;
2622 $tot_fees = 0;
2623 $tot_damage_dep = 0;
2624 $doup = true;
2625 $tars = array();
2626 $cust_costs = array();
2627 $rooms_costs_map = array();
2628 $arrpeople = array();
2629 foreach ($ordersrooms as $kor => $or) {
2630 // remove from existing booking
2631 if ($room_removed !== false) {
2632 if ($or['id'] == $prm_room_oid) {
2633 // do not consider this room for the calculation of the new total amount
2634 // we can unset this array for later use, because the channel manager has already been invoked.
2635 unset($ordersrooms[$kor]);
2636 continue;
2637 }
2638 }
2639
2640 // room index starting from 1
2641 $num = $kor + 1;
2642
2643 // default values to be considered
2644 $room_nights = $daysdiff;
2645 $room_checkin = $ord['checkin'];
2646 $room_checkout = $ord['checkout'];
2647 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'])) {
2648 $room_nights = $room_stay_dates[$kor]['new_nights'];
2649 $room_checkin = $room_stay_dates[$kor]['new_checkin'];
2650 $room_checkout = $room_stay_dates[$kor]['new_checkout'];
2651 }
2652
2653 $padults = VikRequest::getString('adults' . $num, '', 'request');
2654 $pchildren = VikRequest::getString('children' . $num, '', 'request');
2655 $ppets = VikRequest::getInt('pets' . $num, 0, 'request');
2656 if (strlen($padults) || strlen($pchildren)) {
2657 $arrpeople[$num]['adults'] = (int)$padults;
2658 $arrpeople[$num]['children'] = (int)$pchildren;
2659 $arrpeople[$num]['pets'] = $ppets;
2660 }
2661 $ppriceid = VikRequest::getString('priceid'.$num, '', 'request');
2662 $polderpriceid = VikRequest::getString('olderpriceid'.$num, '', 'request');
2663 $ppkgid = VikRequest::getString('pkgid'.$num, '', 'request');
2664 $pcust_cost = VikRequest::getString('cust_cost'.$num, '', 'request');
2665 $paliq = VikRequest::getString('aliq'.$num, '', 'request');
2666 $pcust_cpolicy_id = VikRequest::getInt('cust_cpolicy_id'.$num, 0, 'request');
2667 if ($is_package === true && !empty($ppkgid)) {
2668 $pkg_cost = $or['cust_cost'];
2669 $pkg_idiva = $or['cust_idiva'];
2670 $pkg_info = VikBooking::getPackage($ppkgid);
2671 if (is_array($pkg_info) && count($pkg_info) > 0) {
2672 $use_adults = array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num]) ? $arrpeople[$num]['adults'] : $or['adults'];
2673 $pkg_cost = $pkg_info['pernight_total'] == 1 ? ($pkg_info['cost'] * $room_nights) : $pkg_info['cost'];
2674 $pkg_cost = $pkg_info['perperson'] == 1 ? ($pkg_cost * ($use_adults > 0 ? $use_adults : 1)) : $pkg_cost;
2675 $pkg_cost = VikBooking::sayPackagePlusIva($pkg_cost, $pkg_info['idiva']);
2676 }
2677 $cust_costs[$num] = array('pkgid' => $ppkgid, 'cust_cost' => $pkg_cost, 'aliq' => $pkg_idiva);
2678 $isdue += $pkg_cost;
2679 $cost_minus_tax = VikBooking::sayPackageMinusIva($pkg_cost, $pkg_idiva);
2680 $tot_taxes += ($pkg_cost - $cost_minus_tax);
2681 continue;
2682 }
2683 if (empty($ppriceid) && !empty($pcust_cost) && floatval($pcust_cost) > 0) {
2684 $cust_costs[$num] = [
2685 'cust_cost' => $pcust_cost,
2686 'aliq' => $paliq,
2687 'cust_cpolicy_id' => $pcust_cpolicy_id,
2688 ];
2689 $cost_after_tax = VikBooking::sayPackagePlusIva((float)$pcust_cost, (int)$paliq);
2690 $isdue += $cost_after_tax;
2691 $cost_minus_tax = VikBooking::sayPackageMinusIva((float)$pcust_cost, (int)$paliq);
2692 $tot_taxes += ($cost_after_tax - $cost_minus_tax);
2693 continue;
2694 }
2695
2696 // load room rates for the requested rate plan and nights
2697 $q = "SELECT * FROM `#__vikbooking_dispcost` WHERE `idroom`=" . (int)$or['idroom'] . " AND `days`=" . $room_nights . " AND `idprice`=" . (int)$ppriceid . ";";
2698 $dbo->setQuery($q);
2699 $tar = $dbo->loadAssocList();
2700 if (!$tar) {
2701 $doup = false;
2702 break;
2703 }
2704
2705 /**
2706 * The current price may be different from the price paid at the time of booking.
2707 * Check whether it has been asked to keep the old price of the time of booking.
2708 *
2709 * @since 1.13.0 (J) - 1.3.0 (WP)
2710 */
2711 $old_price_used = false;
2712 if (!empty($polderpriceid)) {
2713 $older_info = explode(':', $polderpriceid);
2714 if ((int)$older_info[0] == (int)$ppriceid) {
2715 $old_price = isset($older_info[1]) ? (float)$older_info[1] : 0;
2716 if ($old_price > 0) {
2717 // we override the 'cost' property of the tar array by taking the previous cost
2718 $old_price_used = true;
2719 $tar[0]['cost'] = $old_price;
2720 }
2721 }
2722 }
2723
2724 if (!$old_price_used) {
2725 // apply seasonal rates
2726 $tar = VikBooking::applySeasonsRoom($tar, $room_checkin, $room_checkout);
2727 }
2728
2729 // different usage
2730 if (!$old_price_used && $or['fromadult'] <= $or['adults'] && $or['toadult'] >= $or['adults']) {
2731 // apply OBP rules
2732 $tar = VBORoomHelper::getInstance()->applyOBPRules($tar, $or, $or['adults']);
2733 }
2734
2735 $cost_plus_tax = VikBooking::sayCostPlusIva($tar[0]['cost'], $tar[0]['idprice']);
2736 $isdue += $cost_plus_tax;
2737 if ($cost_plus_tax == $tar[0]['cost']) {
2738 $cost_minus_tax = VikBooking::sayCostMinusIva($tar[0]['cost'], $tar[0]['idprice']);
2739 $tot_taxes += ($tar[0]['cost'] - $cost_minus_tax);
2740 } else {
2741 $tot_taxes += ($cost_plus_tax - $tar[0]['cost']);
2742 }
2743 $tars[$num] = $tar;
2744 $rooms_costs_map[$num] = $tar[0]['cost'];
2745 }
2746
2747 if ($doup === true) {
2748 if ($room_added === true) {
2749 // add room to existing booking may require to increase the total amount, and taxes
2750 $padd_room_price = VikRequest::getFloat('add_room_price', 0, 'request');
2751 $paliq_add_room = VikRequest::getInt('aliq_add_room', 0, 'request');
2752 if (!empty($padd_room_price) && floatval($padd_room_price) > 0) {
2753 $isdue += (float)$padd_room_price;
2754 $cost_minus_tax = VikBooking::sayPackageMinusIva((float)$padd_room_price, (int)$paliq_add_room);
2755 $tot_taxes += ((float)$padd_room_price - $cost_minus_tax);
2756 }
2757 }
2758
2759 // load options
2760 $q = "SELECT * FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
2761 $dbo->setQuery($q);
2762 $toptionals = $dbo->loadAssocList();
2763
2764 foreach ($ordersrooms as $kor => $or) {
2765 $num = $kor + 1;
2766
2767 // default values to be considered
2768 $room_nights = $daysdiff;
2769 $room_checkin = $ord['checkin'];
2770 $room_checkout = $ord['checkout'];
2771 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'])) {
2772 $room_nights = $room_stay_dates[$kor]['new_nights'];
2773 $room_checkin = $room_stay_dates[$kor]['new_checkin'];
2774 $room_checkout = $room_stay_dates[$kor]['new_checkout'];
2775 }
2776
2777 $pt_first_name = VikRequest::getString('t_first_name'.$num, '', 'request');
2778 $pt_last_name = VikRequest::getString('t_last_name'.$num, '', 'request');
2779 $wop = "";
2780
2781 foreach ($toptionals as $opt) {
2782 // option params
2783 $opt_params = !empty($opt['oparams']) ? json_decode($opt['oparams'], true) : [];
2784 $opt_params = is_array($opt_params) ? $opt_params : [];
2785 if (!empty($opt['ageintervals']) && ($or['children'] > 0 || isset($arrpeople[$num]['children']))) {
2786 $tmpvar = VikRequest::getInt('optid'.$num.$opt['id'], []);
2787 if (is_array($tmpvar) && $tmpvar && ($arrpeople[$num]['children'] ?? 0)) {
2788 $opt['quan'] = 1;
2789 $optagenames = VikBooking::getOptionIntervalsAges($opt['ageintervals']);
2790 $optagepcent = VikBooking::getOptionIntervalsPercentage($opt['ageintervals']);
2791 $optageovrct = VikBooking::getOptionIntervalChildOverrides($opt, (isset($arrpeople[$num]) ? $arrpeople[$num]['adults'] : 0), (isset($arrpeople[$num]) ? $arrpeople[$num]['children'] : 0));
2792 $optorigname = $opt['name'];
2793 foreach ($tmpvar as $child_num => $chvar) {
2794 $ageintervals_child_string = isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $opt['ageintervals'];
2795 $optagecosts = VikBooking::getOptionIntervalsCosts($ageintervals_child_string);
2796 $optorigcost = $optagecosts[($chvar - 1)];
2797 $tmp_room_cost = 0;
2798 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 1) {
2799 // percentage value of the adults tariff
2800 if ($is_package !== true && array_key_exists($num, $tars)) {
2801 // type of price
2802 $tmp_room_cost = $tars[$num][0]['cost'];
2803 $optorigcost = $tars[$num][0]['cost'] * $optagecosts[($chvar - 1)] / 100;
2804 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2805 // package
2806 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2807 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2808 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2809 // custom rate + custom tax rate
2810 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2811 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2812 }
2813 } elseif (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 2) {
2814 // percentage value of room base cost
2815 if ($is_package !== true && array_key_exists($num, $tars)) {
2816 // type of price
2817 $usecost = isset($tars[$num][0]['room_base_cost']) ? $tars[$num][0]['room_base_cost'] : $tars[$num][0]['cost'];
2818 $tmp_room_cost = $usecost;
2819 $optorigcost = $usecost * $optagecosts[($chvar - 1)] / 100;
2820 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2821 // package
2822 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2823 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2824 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2825 // custom rate + custom tax rate
2826 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2827 $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2828 }
2829 }
2830 $opt['cost'] = $optorigcost;
2831 $opt['name'] = $optorigname.' ('.$optagenames[($chvar - 1)].')';
2832 $opt['chageintv'] = $chvar;
2833 $wop.=$opt['id'].":".$opt['quan']."-".$chvar.";";
2834 $realcost = (intval($opt['perday']) == 1 ? ($opt['cost'] * $room_nights * $opt['quan']) : ($opt['cost'] * $opt['quan']));
2835 if (!empty($opt['maxprice']) && $opt['maxprice'] > 0 && $realcost > $opt['maxprice']) {
2836 $realcost = $opt['maxprice'];
2837 }
2838
2839 /**
2840 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
2841 *
2842 * @since 1.17.7 (J) - 1.7.7 (WP)
2843 */
2844 $custom_calc_booking = array_merge($ord, ['days' => $room_nights]);
2845 $custom_calc_booking_room = array_merge($or, ($arrpeople[$num] ?? []), ($tmp_room_cost ? ['room_cost' => $tmp_room_cost] : []));
2846 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$opt, $custom_calc_booking, $custom_calc_booking_room]);
2847 if ($custom_calculation) {
2848 $realcost = (float) $custom_calculation[0];
2849 }
2850
2851 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $opt['idiva']);
2852 if ($opt['is_citytax'] == 1) {
2853 $tot_city_taxes += $tmpopr;
2854 } elseif ($opt['is_fee'] == 1) {
2855 $tot_fees += $tmpopr;
2856 } elseif ($opt_params['damagedep'] ?? 0) {
2857 $tot_damage_dep += $tmpopr;
2858 }
2859 // VBO 1.11 - always calculate the amount of tax no matter if this is already a tax or a fee
2860 if ($tmpopr == $realcost) {
2861 $opt_minus_iva = VikBooking::sayOptionalsMinusIva($realcost, $opt['idiva']);
2862 $tot_taxes += ($realcost - $opt_minus_iva);
2863 } else {
2864 $tot_taxes += ($tmpopr - $realcost);
2865 }
2866 //
2867 $isdue += $tmpopr;
2868 }
2869 }
2870 } else {
2871 $tmpvar = VikRequest::getString('optid'.$num.$opt['id'], '', 'request');
2872 $tmp_room_cost = 0;
2873 // options forced per child fix, no age intervals, like children tourist taxes
2874 $forcedquan = 1;
2875 $forceperday = false;
2876 $forceperchild = false;
2877 if (intval($opt['forcesel']) == 1 && strlen($opt['forceval']) > 0 && strlen($tmpvar) > 0) {
2878 $forceparts = explode("-", $opt['forceval']);
2879 $forcedquan = intval($forceparts[0]);
2880 $forceperday = intval($forceparts[1]) == 1 ? true : false;
2881 $forceperchild = intval($forceparts[2]) == 1 ? true : false;
2882 $tmpvar = $forcedquan;
2883 $tmpvar = $forceperchild === true && array_key_exists($num, $arrpeople) && array_key_exists('children', $arrpeople[$num]) ? ($tmpvar * $arrpeople[$num]['children']) : $tmpvar;
2884 }
2885 //
2886 if (!empty($tmpvar)) {
2887 $wop .= $opt['id'].":".$tmpvar.";";
2888 // options percentage cost of the room total fee
2889 if ($is_package !== true && array_key_exists($num, $tars)) {
2890 // type of price
2891 $tmp_room_cost = $tars[$num][0]['cost'];
2892 $deftar_basecosts = $tars[$num][0]['cost'];
2893 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2894 // package
2895 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2896 $deftar_basecosts = $cust_costs[$num]['cust_cost'];
2897 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2898 // custom rate + custom tax rate
2899 $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2900 $deftar_basecosts = $cust_costs[$num]['cust_cost'];
2901 }
2902 $opt['cost'] = (int)$opt['pcentroom'] ? ($deftar_basecosts * $opt['cost'] / 100) : $opt['cost'];
2903 //
2904 $realcost = (intval($opt['perday']) == 1 ? ($opt['cost'] * $room_nights * $tmpvar) : ($opt['cost'] * $tmpvar));
2905 if (!empty($opt['maxprice']) && $opt['maxprice'] > 0 && $realcost > $opt['maxprice']) {
2906 $realcost = $opt['maxprice'];
2907 if (intval($opt['hmany']) == 1 && intval($tmpvar) > 1) {
2908 $realcost = $opt['maxprice'] * $tmpvar;
2909 }
2910 }
2911 if ($opt['perperson'] == 1) {
2912 $num_adults = array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num]) ? $arrpeople[$num]['adults'] : 1;
2913 $realcost = $realcost * $num_adults;
2914 }
2915
2916 /**
2917 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
2918 *
2919 * @since 1.17.7 (J) - 1.7.7 (WP)
2920 */
2921 $custom_calc_booking = array_merge($ord, ['days' => $room_nights]);
2922 $custom_calc_booking_room = array_merge($or, ($arrpeople[$num] ?? []), ($tmp_room_cost ? ['room_cost' => $tmp_room_cost] : []));
2923 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$opt, $custom_calc_booking, $custom_calc_booking_room]);
2924 if ($custom_calculation) {
2925 $realcost = (float) $custom_calculation[0];
2926 }
2927
2928 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $opt['idiva']);
2929 if ($opt['is_citytax'] == 1) {
2930 $tot_city_taxes += $tmpopr;
2931 } elseif ($opt['is_fee'] == 1) {
2932 $tot_fees += $tmpopr;
2933 } elseif ($opt_params['damagedep'] ?? 0) {
2934 $tot_damage_dep += $tmpopr;
2935 }
2936 // VBO 1.11 - always calculate the amount of tax no matter if this is already a tax or a fee
2937 if ($tmpopr == $realcost) {
2938 $opt_minus_iva = VikBooking::sayOptionalsMinusIva($realcost, $opt['idiva']);
2939 $tot_taxes += ($realcost - $opt_minus_iva);
2940 } else {
2941 $tot_taxes += ($tmpopr - $realcost);
2942 }
2943 //
2944 $isdue += $tmpopr;
2945 }
2946 }
2947 }
2948
2949 $upd_fields = array();
2950 if ($is_package !== true && array_key_exists($num, $tars)) {
2951 // type of price
2952 $upd_fields[] = "`idtar`='".$tars[$num][0]['id']."'";
2953 $upd_fields[] = "`cust_cost`=NULL";
2954 $upd_fields[] = "`cust_idiva`=NULL";
2955 $upd_fields[] = "`cust_cpolicy_id`=NULL";
2956 $upd_fields[] = "`room_cost`=".(array_key_exists($num, $rooms_costs_map) ? $dbo->quote($rooms_costs_map[$num]) : "NULL");
2957 } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2958 // packages do not update name or cost, just set again the same package ID to avoid risks of empty upd_fields to update
2959 $upd_fields[] = "`idtar`=NULL";
2960 $upd_fields[] = "`pkg_id`='".$cust_costs[$num]['pkgid']."'";
2961 $upd_fields[] = "`cust_cost`='".$cust_costs[$num]['cust_cost']."'";
2962 $upd_fields[] = "`cust_idiva`='".$cust_costs[$num]['aliq']."'";
2963 $upd_fields[] = "`cust_cpolicy_id`='" . (int) ($cust_costs[$num]['cust_cpolicy_id'] ?? 0) . "'";
2964 $upd_fields[] = "`room_cost`=NULL";
2965 } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2966 // custom rate + custom tax rate
2967 $upd_fields[] = "`idtar`=NULL";
2968 $upd_fields[] = "`cust_cost`='".$cust_costs[$num]['cust_cost']."'";
2969 $upd_fields[] = "`cust_idiva`='".$cust_costs[$num]['aliq']."'";
2970 $upd_fields[] = "`cust_cpolicy_id`='" . (int) ($cust_costs[$num]['cust_cpolicy_id'] ?? 0) . "'";
2971 $upd_fields[] = "`room_cost`=NULL";
2972 // inject new room price
2973 $ordersrooms[$kor]['modified_price'] = $cust_costs[$num]['cust_cost'];
2974 }
2975 if ($toptionals) {
2976 $upd_fields[] = "`optionals`='".$wop."'";
2977 }
2978 if (!empty($pt_first_name) || !empty($pt_last_name)) {
2979 $upd_fields[] = "`t_first_name`=".$dbo->quote($pt_first_name);
2980 $upd_fields[] = "`t_last_name`=".$dbo->quote($pt_last_name);
2981 }
2982 if (array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num])) {
2983 $upd_fields[] = "`adults`=".intval($arrpeople[$num]['adults']);
2984 $upd_fields[] = "`children`=".intval($arrpeople[$num]['children']);
2985 if (isset($arrpeople[$num]['pets'])) {
2986 $upd_fields[] = "`pets`=" . $arrpeople[$num]['pets'];
2987 }
2988 }
2989
2990 /**
2991 * Meal plans at room-reservation level.
2992 *
2993 * @since 1.16.1 (J) - 1.6.1 (WP)
2994 */
2995 $pmealplans = VikRequest::getVar('mealplan' . $num, []);
2996 $upd_fields[] = "`meals`=" . ($pmealplans ? $dbo->q(json_encode($pmealplans)) : 'NULL');
2997
2998 // calculate the extra costs and increase taxes + isdue
2999 $extracosts_arr = array();
3000 if (count($pextracn) && isset($pextracn[$num]) && count($pextracn[$num])) {
3001 foreach ($pextracn[$num] as $eck => $ecn) {
3002 if ($ecn && array_key_exists($eck, $pextracc[$num]) && is_numeric($pextracc[$num][$eck])) {
3003 $ecidtax = array_key_exists($eck, $pextractx[$num]) && intval($pextractx[$num][$eck]) > 0 ? (int)$pextractx[$num][$eck] : '';
3004 $extracosts_arr[] = array(
3005 'name' => $ecn,
3006 'cost' => (float)$pextracc[$num][$eck],
3007 'idtax' => $ecidtax,
3008 'type' => isset($pextractype[$num][$eck]) ? $pextractype[$num][$eck] : '',
3009 'fk' => isset($pextracfk[$num][$eck]) ? (string)$pextracfk[$num][$eck] : '',
3010 'data' => isset($pextracdata[$num][$eck]) ? json_decode($pextracdata[$num][$eck]) : null,
3011 );
3012 $ecplustax = !empty($ecidtax) ? VikBooking::sayOptionalsPlusIva((float)$pextracc[$num][$eck], $ecidtax) : (float)$pextracc[$num][$eck];
3013 $ecminustax = !empty($ecidtax) ? VikBooking::sayOptionalsMinusIva((float)$pextracc[$num][$eck], $ecidtax) : (float)$pextracc[$num][$eck];
3014 $ectottax = (float)$pextracc[$num][$eck] - $ecminustax;
3015 $isdue += $ecplustax;
3016 $tot_taxes += $ectottax;
3017 }
3018 }
3019 }
3020
3021 if ($extracosts_arr) {
3022 $upd_fields[] = "`extracosts`=".$dbo->quote(json_encode($extracosts_arr));
3023 } else {
3024 $upd_fields[] = "`extracosts`=NULL";
3025 }
3026
3027 if ($upd_fields) {
3028 $q = "UPDATE `#__vikbooking_ordersrooms` SET ".implode(', ', $upd_fields)." WHERE `idorder`=".$ord['id']." AND `idroom`='".$or['idroom']."' AND `id`='".$or['id']."';";
3029 $dbo->setQuery($q);
3030 $dbo->execute();
3031 }
3032 }
3033
3034 // update split stay transient record if not confirmed booking
3035 if ($ord['split_stay'] && $ord['status'] != 'confirmed' && !empty($room_stay_dates) && !empty($split_stay_data)) {
3036 /**
3037 * Important: if no rates have been selected for all rooms, we won't enter this inner statement.
3038 * It is necessary to select a rate plan for each room in order to update the split stay data.
3039 */
3040 $new_room_stay_dates = [];
3041 foreach ($room_stay_dates as $kor => $room_stay_info) {
3042 // clone the current information
3043 $clean_room_stay_info = $room_stay_info;
3044 // set new stay values
3045 if (!empty($clean_room_stay_info['checkin_ts'])) {
3046 $clean_room_stay_info['checkin_ts'] = $clean_room_stay_info['new_checkin'];
3047 $clean_room_stay_info['checkout_ts'] = $clean_room_stay_info['new_checkout'];
3048 } else {
3049 $clean_room_stay_info['checkin'] = $clean_room_stay_info['new_checkin'];
3050 $clean_room_stay_info['checkout'] = $clean_room_stay_info['new_checkout'];
3051 }
3052 $clean_room_stay_info['nights'] = $clean_room_stay_info['new_nights'];
3053 // clean up unnecessary keys
3054 unset($clean_room_stay_info['new_checkin'], $clean_room_stay_info['new_checkout'], $clean_room_stay_info['new_nights']);
3055 // push new array info
3056 $new_room_stay_dates[$kor] = $clean_room_stay_info;
3057 }
3058 // update configuration record
3059 VBOFactory::getConfig()->set('split_stay_' . $ord['id'], json_encode($new_room_stay_dates));
3060 }
3061
3062 // make sure to re-apply the discount with the coupon code
3063 if ($ord['coupon']) {
3064 $expcoupon = explode(";", $ord['coupon']);
3065 $isdue -= $expcoupon[1];
3066 }
3067
3068 // make sure to apply any previously refunded amount
3069 if ($ord['refund'] > 0) {
3070 $isdue -= $ord['refund'];
3071 }
3072
3073 // update totals
3074 $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'].";";
3075 $dbo->setQuery($q);
3076 $dbo->execute();
3077 $upd_esit = JText::translate('VBORESRATESUPDATED');
3078
3079 // Customer Booking
3080 if ($ord['status'] == 'confirmed') {
3081 $q = "SELECT `idcustomer` FROM `#__vikbooking_customers_orders` WHERE `idorder`=".$ord['id'].";";
3082 $dbo->setQuery($q);
3083 $customer_id = $dbo->loadResult();
3084 if ($customer_id) {
3085 $cpin = VikBooking::getCPinIstance();
3086 $cpin->is_admin = true;
3087 $cpin->updateBookingCommissions($ord['id'], $customer_id);
3088 }
3089 }
3090
3091 /**
3092 * Check for any OTA reporting action.
3093 *
3094 * @since 1.16.8 (J) - 1.6.8 (WP)
3095 */
3096 if (class_exists('VCMOtaReporting') && VCMOtaReporting::getInstance($ord)->stayChangeAllowed()) {
3097 // check if an OTA reporting action was selected
3098 $ota_stay_change_data = [];
3099 $ota_stay_change_all = $app->input->getInt('ota_stay_change_all', 0);
3100 foreach ($ordersrooms as $kor => $or) {
3101 $ota_stay_change_room = [];
3102 if ($ota_stay_change_all) {
3103 // set room data for stay change
3104 $ota_stay_change_room = [
3105 'idroom' => $or['idroom'],
3106 'checkin' => date('Y-m-d', $first),
3107 'checkout' => date('Y-m-d', $second),
3108 ];
3109 if (isset($or['modified_price'])) {
3110 $ota_stay_change_room['price'] = $or['modified_price'];
3111 }
3112 } elseif ($app->input->getInt('ota_stay_change_room_' . $kor, 0) && !empty($or['modified_checkin']) && !empty($or['modified_checkout'])) {
3113 // set room index data for stay change
3114 $ota_stay_change_room = [
3115 'idroom' => $or['idroom'],
3116 'index' => $kor,
3117 'checkin' => date('Y-m-d', $or['modified_checkin']),
3118 'checkout' => date('Y-m-d', $or['modified_checkout']),
3119 ];
3120 if (isset($or['modified_price'])) {
3121 $ota_stay_change_room['price'] = $or['modified_price'];
3122 }
3123 }
3124 if ($ota_stay_change_room) {
3125 // push room data for stay change
3126 $ota_stay_change_data[] = $ota_stay_change_room;
3127 }
3128 }
3129
3130 if ($ota_stay_change_data) {
3131 // notify the OTA through Vik Channel Manager
3132 $ota_reporting = VCMOtaReporting::getInstance();
3133 $ota_result = $ota_reporting->notifyStayChange($ota_stay_change_data);
3134 if (!$ota_result) {
3135 // enqueue error message
3136 $app->enqueueMessage($ota_reporting->getError(), 'error');
3137 }
3138 }
3139 }
3140 }
3141
3142 // Booking History
3143 $history_descr = "({$user->name}) " . VikBooking::getLogBookingModification($ord, $room_stay_dates);
3144 if (!$opertwounits && $forcebooking) {
3145 $history_descr .= "\n" . JText::translate('VBO_FORCED_BOOKDATES');
3146 }
3147 VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', $history_descr);
3148
3149 // enqueue result message
3150 $app->enqueueMessage($upd_esit);
3151 } else {
3152 VikError::raiseWarning('', JText::translate('VBROOMNOTRIT')." ".date($df.' H:i', $first)." ".JText::translate('VBROOMNOTCONSTO')." ".date($df.' H:i', $second));
3153 $allow_force = 1;
3154 $app->enqueueMessage(JText::translate('VBO_BOOKING_SHOULDFORCE'), 'notice');
3155 }
3156
3157 if ($callback == 'geninvoices') {
3158 $app->redirect("index.php?option=com_vikbooking&task=orders&cid[]=".$ord['id']."&confirmgen=1");
3159 } else {
3160 $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" : ""));
3161 }
3162 }
3163
3164 public function removebusy()
3165 {
3166 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
3167 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
3168 }
3169
3170 $dbo = JFactory::getDbo();
3171 $app = JFactory::getApplication();
3172
3173 $user = JFactory::getUser();
3174 $config = VBOFactory::getConfig();
3175
3176 $prev_conf_ids = [];
3177 $pidorder = VikRequest::getInt('idorder', 0, 'request');
3178 $pgoto = VikRequest::getString('goto', '', 'request');
3179
3180 $purged = false;
3181
3182 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder;
3183 $dbo->setQuery($q, 0, 1);
3184 $row = $dbo->loadAssoc();
3185
3186 // check for any cancellation constraints
3187 $canc_denied = false;
3188 if ($row && class_exists('VCMFeesCancellation')) {
3189 // let VCM detect if there are any constraints for the cancellation
3190 $canc_denied = VCMFeesCancellation::getInstance($row, $anew = true)->isBookingConstrained();
3191 if ($canc_denied) {
3192 // set error message
3193 $canc_deny_error = VCMFeesCancellation::getInstance()->getError();
3194 if ($canc_deny_error) {
3195 $app->enqueueMessage($canc_deny_error, 'error');
3196 }
3197 }
3198 }
3199
3200 if ($row && !$canc_denied) {
3201 // set status to cancelled
3202 if ($row['status'] != 'cancelled') {
3203 $q = "UPDATE `#__vikbooking_orders` SET `status`='cancelled' WHERE `id`=".(int)$row['id'].";";
3204 $dbo->setQuery($q);
3205 $dbo->execute();
3206 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($row['id']) . ";";
3207 $dbo->setQuery($q);
3208 $dbo->execute();
3209 if ($row['status'] == 'confirmed') {
3210 $prev_conf_ids[] = $row['id'];
3211 }
3212 // Booking History
3213 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('CB', "({$user->name})");
3214 }
3215
3216 // free records up
3217 $q = "SELECT * FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
3218 $dbo->setQuery($q);
3219 $ordbusy = $dbo->loadAssocList();
3220 if ($ordbusy) {
3221 foreach ($ordbusy as $ob) {
3222 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`='".$ob['idbusy']."';";
3223 $dbo->setQuery($q);
3224 $dbo->execute();
3225 }
3226 }
3227
3228 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
3229 $dbo->setQuery($q);
3230 $dbo->execute();
3231
3232 // check for purge removal
3233 if ($row['status'] == 'cancelled') {
3234 $q = "DELETE FROM `#__vikbooking_customers_orders` WHERE `idorder`=" . intval($row['id']) . ";";
3235 $dbo->setQuery($q);
3236 $dbo->execute();
3237 $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `idorder`=".(int)$row['id'].";";
3238 $dbo->setQuery($q);
3239 $dbo->execute();
3240 $q = "DELETE FROM `#__vikbooking_orderhistory` WHERE `idorder`=".(int)$row['id'].";";
3241 $dbo->setQuery($q);
3242 $dbo->execute();
3243 $q = "DELETE FROM `#__vikbooking_orders` WHERE `id`=".(int)$row['id'].";";
3244 $dbo->setQuery($q);
3245 $dbo->execute();
3246 // in case of split stay booking, remove the transient
3247 if ($row['split_stay']) {
3248 $config->remove('split_stay_' . $row['id']);
3249 }
3250 // turn flag on
3251 $purged = true;
3252 }
3253
3254 // enqueue message
3255 $app->enqueueMessage(JText::translate('VBMESSDELBUSY'));
3256 }
3257
3258 if ($prev_conf_ids) {
3259 $prev_conf_ids_str = '';
3260 foreach ($prev_conf_ids as $prev_id) {
3261 $prev_conf_ids_str .= '&cid[]='.$prev_id;
3262 }
3263 //Invoke Channel Manager
3264 $vcm_autosync = VikBooking::vcmAutoUpdate();
3265 if ($vcm_autosync > 0) {
3266 $vcm_obj = VikBooking::getVcmInvoker();
3267 $vcm_obj->setOids($prev_conf_ids)->setSyncType('cancel');
3268 $sync_result = $vcm_obj->doSync();
3269 if ($sync_result === false) {
3270 $vcm_err = $vcm_obj->getError();
3271 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
3272 }
3273 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
3274 $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');
3275 VikError::raiseNotice('', JText::translate('VBCHANNELMANAGERINVOKEASK').' <button type="button" class="btn btn-primary" onclick="document.location.href=\''.$vcm_sync_url.'\';">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button>');
3276 }
3277 //
3278 }
3279
3280 if ($pgoto == 'overv') {
3281 $app->redirect("index.php?option=com_vikbooking&task=overv");
3282 } elseif (!$purged) {
3283 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $pidorder);
3284 } else {
3285 $app->redirect("index.php?option=com_vikbooking&task=orders");
3286 }
3287
3288 $app->close();
3289 }
3290
3291 public function unlockrecords()
3292 {
3293 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
3294 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
3295 }
3296
3297 $ids = VikRequest::getVar('cid', array(0));
3298 if (@count($ids)) {
3299 $dbo = JFactory::getDBO();
3300 foreach ($ids as $d) {
3301 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `id`=".$dbo->quote($d).";";
3302 $dbo->setQuery($q);
3303 $dbo->execute();
3304 }
3305 }
3306 $mainframe = JFactory::getApplication();
3307 $mainframe->redirect("index.php?option=com_vikbooking");
3308 }
3309
3310 public function sortoption() {
3311 if (!JSession::checkToken('get')) {
3312 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3313 }
3314 $sortid = VikRequest::getVar('cid', array(0));
3315 $pmode = VikRequest::getString('mode', '', 'request');
3316 $dbo = JFactory::getDBO();
3317 $mainframe = JFactory::getApplication();
3318 if (!empty($pmode)) {
3319 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
3320 $dbo->setQuery($q);
3321 $dbo->execute();
3322 $totr = $dbo->getNumRows();
3323 if ($totr > 1) {
3324 $data = $dbo->loadAssocList();
3325 if ($pmode == "up") {
3326 foreach ($data as $v) {
3327 if ($v['id'] == $sortid[0]) {
3328 $y = $v['ordering'];
3329 }
3330 }
3331 if ($y && $y > 1) {
3332 $vik = $y - 1;
3333 $found = false;
3334 foreach ($data as $v) {
3335 if (intval($v['ordering']) == intval($vik)) {
3336 $found = true;
3337 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3338 $dbo->setQuery($q);
3339 $dbo->execute();
3340 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3341 $dbo->setQuery($q);
3342 $dbo->execute();
3343 break;
3344 }
3345 }
3346 if (!$found) {
3347 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3348 $dbo->setQuery($q);
3349 $dbo->execute();
3350 }
3351 }
3352 } elseif ($pmode == "down") {
3353 foreach ($data as $v) {
3354 if ($v['id'] == $sortid[0]) {
3355 $y = $v['ordering'];
3356 }
3357 }
3358 if ($y) {
3359 $vik = $y + 1;
3360 $found = false;
3361 foreach ($data as $v) {
3362 if (intval($v['ordering']) == intval($vik)) {
3363 $found = true;
3364 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3365 $dbo->setQuery($q);
3366 $dbo->execute();
3367 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3368 $dbo->setQuery($q);
3369 $dbo->execute();
3370 break;
3371 }
3372 }
3373 if (!$found) {
3374 $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3375 $dbo->setQuery($q);
3376 $dbo->execute();
3377 }
3378 }
3379 }
3380 }
3381 $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
3382 } else {
3383 $mainframe->redirect("index.php?option=com_vikbooking");
3384 }
3385 }
3386
3387 public function sortpayment() {
3388 if (!JSession::checkToken('get')) {
3389 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3390 }
3391 $cid = VikRequest::getVar('cid', array(0));
3392 $sortid = $cid[0];
3393 $dbo = JFactory::getDBO();
3394 $mainframe = JFactory::getApplication();
3395 $pmode = VikRequest::getString('mode', '', 'request');
3396 if (!empty($pmode) && !empty($sortid)) {
3397 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_gpayments` ORDER BY `#__vikbooking_gpayments`.`ordering` ASC;";
3398 $dbo->setQuery($q);
3399 $dbo->execute();
3400 $totr=$dbo->getNumRows();
3401 if ($totr > 1) {
3402 $data = $dbo->loadAssocList();
3403 if ($pmode == "up") {
3404 foreach ($data as $v) {
3405 if ($v['id'] == $sortid) {
3406 $y = $v['ordering'];
3407 }
3408 }
3409 if ($y && $y > 1) {
3410 $vik = $y - 1;
3411 $found = false;
3412 foreach ($data as $v) {
3413 if (intval($v['ordering']) == intval($vik)) {
3414 $found = true;
3415 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3416 $dbo->setQuery($q);
3417 $dbo->execute();
3418 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3419 $dbo->setQuery($q);
3420 $dbo->execute();
3421 break;
3422 }
3423 }
3424 if (!$found) {
3425 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3426 $dbo->setQuery($q);
3427 $dbo->execute();
3428 }
3429 }
3430 } elseif ($pmode == "down") {
3431 foreach ($data as $v) {
3432 if ($v['id'] == $sortid) {
3433 $y = $v['ordering'];
3434 }
3435 }
3436 if ($y) {
3437 $vik = $y + 1;
3438 $found = false;
3439 foreach ($data as $v) {
3440 if (intval($v['ordering']) == intval($vik)) {
3441 $found=true;
3442 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3443 $dbo->setQuery($q);
3444 $dbo->execute();
3445 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3446 $dbo->setQuery($q);
3447 $dbo->execute();
3448 break;
3449 }
3450 }
3451 if (!$found) {
3452 $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3453 $dbo->setQuery($q);
3454 $dbo->execute();
3455 }
3456 }
3457 }
3458 }
3459 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
3460 } else {
3461 $mainframe->redirect("index.php?option=com_vikbooking");
3462 }
3463 }
3464
3465 public function sortcarat() {
3466 if (!JSession::checkToken('get')) {
3467 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3468 }
3469 $sortid = VikRequest::getVar('cid', array(0));
3470 $pmode = VikRequest::getString('mode', '', 'request');
3471 $dbo = JFactory::getDBO();
3472 $mainframe = JFactory::getApplication();
3473 if (!empty($pmode)) {
3474 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_characteristics` ORDER BY `#__vikbooking_characteristics`.`ordering` ASC;";
3475 $dbo->setQuery($q);
3476 $dbo->execute();
3477 $totr = $dbo->getNumRows();
3478 if ($totr > 1) {
3479 $data = $dbo->loadAssocList();
3480 if ($pmode == "up") {
3481 foreach ($data as $v) {
3482 if ($v['id'] == $sortid[0]) {
3483 $y = $v['ordering'];
3484 }
3485 }
3486 if ($y && $y > 1) {
3487 $vik = $y - 1;
3488 $found = false;
3489 foreach ($data as $v) {
3490 if (intval($v['ordering']) == intval($vik)) {
3491 $found = true;
3492 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3493 $dbo->setQuery($q);
3494 $dbo->execute();
3495 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3496 $dbo->setQuery($q);
3497 $dbo->execute();
3498 break;
3499 }
3500 }
3501 if (!$found) {
3502 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3503 $dbo->setQuery($q);
3504 $dbo->execute();
3505 }
3506 }
3507 } elseif ($pmode == "down") {
3508 foreach ($data as $v) {
3509 if ($v['id'] == $sortid[0]) {
3510 $y = $v['ordering'];
3511 }
3512 }
3513 if ($y) {
3514 $vik = $y + 1;
3515 $found = false;
3516 foreach ($data as $v) {
3517 if (intval($v['ordering']) == intval($vik)) {
3518 $found = true;
3519 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3520 $dbo->setQuery($q);
3521 $dbo->execute();
3522 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3523 $dbo->setQuery($q);
3524 $dbo->execute();
3525 break;
3526 }
3527 }
3528 if (!$found) {
3529 $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3530 $dbo->setQuery($q);
3531 $dbo->execute();
3532 }
3533 }
3534 }
3535 }
3536 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
3537 } else {
3538 $mainframe->redirect("index.php?option=com_vikbooking");
3539 }
3540 }
3541
3542 public function resendordemail() {
3543 $this->do_resendorderemail();
3544 }
3545
3546 public function sendcancordemail() {
3547 $this->do_resendorderemail(true);
3548 }
3549
3550 private function do_resendorderemail($cancellation = false)
3551 {
3552 $dbo = JFactory::getDbo();
3553 $app = JFactory::getApplication();
3554 $vbo_tn = VikBooking::getTranslator();
3555
3556 $cid = VikRequest::getVar('cid', array(0));
3557 $oid = (int)$cid[0];
3558
3559 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $oid . ";";
3560 $dbo->setQuery($q);
3561 $dbo->execute();
3562 if (!$dbo->getNumRows()) {
3563 $app->redirect("index.php?option=com_vikbooking&task=orders");
3564 $app->close();
3565 }
3566 $order = $dbo->loadAssoc();
3567
3568 // check if the language in use is the same as the one used during the checkout
3569 if (!empty($order['lang'])) {
3570 $lang = JFactory::getLanguage();
3571 if ($lang->getTag() != $order['lang']) {
3572 $lang->load('com_vikbooking', (VBOPlatformDetection::isWordPress() ? VIKBOOKING_LANG : JPATH_ADMINISTRATOR), $order['lang'], true);
3573 if (defined('_JEXEC') && !defined('ABSPATH')) {
3574 $lang->load('joomla', JPATH_ADMINISTRATOR, $order['lang'], true);
3575 }
3576 }
3577 if ($vbo_tn->getDefaultLang() != $order['lang']) {
3578 // force the translation to start because contents should be translated
3579 $vbo_tn::$force_tolang = $order['lang'];
3580 }
3581 }
3582
3583 // availability helper
3584 $av_helper = VikBooking::getAvailabilityInstance();
3585
3586 /**
3587 * Split stay reservation.
3588 *
3589 * @since 1.16.0 (J) - 1.6.0 (WP)
3590 */
3591 $room_stay_dates = [];
3592 if ($order['split_stay']) {
3593 if ($order['status'] == 'confirmed') {
3594 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($order['id']);
3595 } else {
3596 $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $order['id'], []);
3597 }
3598 // immediately count the number of nights of stay for each split room
3599 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
3600 if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
3601 // overwrite values for compatibility with non-confirmed bookings
3602 $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
3603 $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
3604 }
3605 $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
3606 // overwrite the whole array
3607 $room_stay_dates[$sps_r_k] = $sps_r_v;
3608 }
3609 }
3610
3611 // load rooms booked
3612 $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;";
3613 $dbo->setQuery($q);
3614 $dbo->execute();
3615 $ordersrooms = $dbo->loadAssocList();
3616 $vbo_tn->translateContents($ordersrooms, '#__vikbooking_rooms', array('id' => 'r_reference_id'));
3617
3618 $ftitle = VikBooking::getFrontTitle();
3619 $currencyname = VikBooking::getCurrencyName();
3620
3621 $turnover_secs = VikBooking::getHoursRoomAvail() * 3600;
3622 $realback = $turnover_secs + $order['checkout'];
3623
3624 $rooms = array();
3625 $tars = array();
3626 $arrpeople = array();
3627 $is_package = !empty($order['pkg']) ? true : false;
3628 $nowts = time();
3629 foreach ($ordersrooms as $kor => $or) {
3630 $num = $kor + 1;
3631 $rooms[$num] = $or;
3632 $arrpeople[$num]['adults'] = $or['adults'];
3633 $arrpeople[$num]['children'] = $or['children'];
3634 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3635 // package or custom cost set from the back-end
3636 continue;
3637 }
3638
3639 // determine the proper values for this room
3640 $room_nights = $order['days'];
3641 $room_checkin = $order['checkin'];
3642 $room_checkout = $order['checkout'];
3643 if ($order['split_stay'] && !empty($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) {
3644 $room_nights = $room_stay_dates[$kor]['nights'];
3645 $room_checkin = $room_stay_dates[$kor]['checkin'];
3646 $room_checkout = $room_stay_dates[$kor]['checkout'];
3647 }
3648
3649 // load tariff
3650 $q = "SELECT * FROM `#__vikbooking_dispcost` WHERE `id`=" . (int)$or['idtar'] . ";";
3651 $dbo->setQuery($q);
3652 $dbo->execute();
3653 if ($dbo->getNumRows() > 0) {
3654 $tar = $dbo->loadAssocList();
3655 $tar = VikBooking::applySeasonsRoom($tar, $room_checkin, $room_checkout);
3656
3657 // different usage
3658 $tar = VBORoomHelper::getInstance()->applyOBPRules($tar, $or, $or['adults']);
3659
3660 $tars[$num] = $tar[0];
3661 } else {
3662 VikError::raiseWarning('', JText::translate('VBERRNOFAREFOUND'));
3663 }
3664 }
3665
3666 $secdiff = $order['checkout'] - $order['checkin'];
3667 $daysdiff = $secdiff / 86400;
3668 if (is_int($daysdiff)) {
3669 if ($daysdiff < 1) {
3670 $daysdiff = 1;
3671 }
3672 } else {
3673 if ($daysdiff < 1) {
3674 $daysdiff = 1;
3675 } else {
3676 $sum = floor($daysdiff) * 86400;
3677 $newdiff = $secdiff - $sum;
3678 $maxhmore = VikBooking::getHoursMoreRb() * 3600;
3679 if ($maxhmore >= $newdiff) {
3680 $daysdiff = floor($daysdiff);
3681 } else {
3682 $daysdiff = ceil($daysdiff);
3683 }
3684 }
3685 }
3686
3687 $isdue = 0;
3688 $pricestr = array();
3689 $optstr = array();
3690 foreach ($ordersrooms as $kor => $or) {
3691 $num = $kor + 1;
3692
3693 // determine the proper values for this room
3694 $room_nights = $order['days'];
3695 $room_checkin = $order['checkin'];
3696 $room_checkout = $order['checkout'];
3697 if ($order['split_stay'] && !empty($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) {
3698 $room_nights = $room_stay_dates[$kor]['nights'];
3699 $room_checkin = $room_stay_dates[$kor]['checkin'];
3700 $room_checkout = $room_stay_dates[$kor]['checkout'];
3701 }
3702
3703 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3704 // package cost or cust_cost may not be inclusive of taxes if prices tax included is off
3705 $calctar = VikBooking::sayPackagePlusIva($or['cust_cost'], $or['cust_idiva']);
3706 $isdue += $calctar;
3707 $pricestr[$num] = (!empty($or['pkg_name']) ? $or['pkg_name'] : (!empty($or['otarplan']) ? ucwords($or['otarplan']) : JText::translate('VBOROOMCUSTRATEPLAN'))).": ".$calctar." ".$currencyname;
3708 } elseif (array_key_exists($num, $tars) && is_array($tars[$num])) {
3709 $display_rate = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3710 $calctar = VikBooking::sayCostPlusIva($display_rate, $tars[$num]['idprice']);
3711 $tars[$num]['calctar'] = $calctar;
3712 $isdue += $calctar;
3713 $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'] : "");
3714 }
3715 if (!empty($or['optionals'])) {
3716 $stepo = explode(";", $or['optionals']);
3717 foreach ($stepo as $roptkey => $oo) {
3718 if (empty($oo)) {
3719 continue;
3720 }
3721 $stept = explode(":", $oo);
3722 $q = "SELECT * FROM `#__vikbooking_optionals` WHERE `id`=" . $dbo->quote($stept[0]) . ";";
3723 $dbo->setQuery($q);
3724 $dbo->execute();
3725 if (!$dbo->getNumRows()) {
3726 continue;
3727 }
3728 $actopt = $dbo->loadAssocList();
3729 $vbo_tn->translateContents($actopt, '#__vikbooking_optionals', array(), array(), (!empty($order['lang']) ? $order['lang'] : null));
3730 $chvar = '';
3731 if (!empty($actopt[0]['ageintervals']) && $or['children'] > 0 && strstr($stept[1], '-') != false) {
3732 $optagenames = VikBooking::getOptionIntervalsAges($actopt[0]['ageintervals']);
3733 $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt[0]['ageintervals']);
3734 $optageovrct = VikBooking::getOptionIntervalChildOverrides($actopt[0], $or['adults'], $or['children']);
3735 $child_num = VikBooking::getRoomOptionChildNumber($or['optionals'], $actopt[0]['id'], $roptkey, $or['children']);
3736 $optagecosts = VikBooking::getOptionIntervalsCosts(isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $actopt[0]['ageintervals']);
3737 $agestept = explode('-', $stept[1]);
3738 $stept[1] = $agestept[0];
3739 $chvar = $agestept[1];
3740 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 1) {
3741 //percentage value of the adults tariff
3742 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3743 $optagecosts[($chvar - 1)] = $or['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
3744 } else {
3745 $display_rate = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3746 $optagecosts[($chvar - 1)] = $display_rate * $optagecosts[($chvar - 1)] / 100;
3747 }
3748 } elseif (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 2) {
3749 //VBO 1.10 - percentage value of room base cost
3750 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3751 $optagecosts[($chvar - 1)] = $or['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
3752 } else {
3753 $display_rate = isset($tars[$num]['room_base_cost']) ? $tars[$num]['room_base_cost'] : (!empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost']);
3754 $optagecosts[($chvar - 1)] = $display_rate * $optagecosts[($chvar - 1)] / 100;
3755 }
3756 }
3757 $actopt[0]['chageintv'] = $chvar;
3758 $actopt[0]['name'] .= ' ('.$optagenames[($chvar - 1)].')';
3759 $actopt[0]['quan'] = $stept[1];
3760 $realcost = (intval($actopt[0]['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $room_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
3761 } else {
3762 $actopt[0]['quan'] = $stept[1];
3763 // VBO 1.11 - options percentage cost of the room total fee
3764 if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3765 $deftar_basecosts = $or['cust_cost'];
3766 } else {
3767 $deftar_basecosts = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3768 }
3769 $actopt[0]['cost'] = (int)$actopt[0]['pcentroom'] ? ($deftar_basecosts * $actopt[0]['cost'] / 100) : $actopt[0]['cost'];
3770 //
3771 $realcost = (intval($actopt[0]['perday']) == 1 ? ($actopt[0]['cost'] * $room_nights * $stept[1]) : ($actopt[0]['cost'] * $stept[1]));
3772 }
3773 if (!empty($actopt[0]['maxprice']) && $actopt[0]['maxprice'] > 0 && $realcost > $actopt[0]['maxprice']) {
3774 $realcost = $actopt[0]['maxprice'];
3775 if (intval($actopt[0]['hmany']) == 1 && intval($stept[1]) > 1) {
3776 $realcost = $actopt[0]['maxprice'] * $stept[1];
3777 }
3778 }
3779 if ($actopt[0]['perperson'] == 1) {
3780 $realcost = $realcost * $or['adults'];
3781 }
3782
3783 /**
3784 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
3785 *
3786 * @since 1.17.7 (J) - 1.7.7 (WP)
3787 */
3788 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt[0], $order, $or]);
3789 if ($custom_calculation) {
3790 $realcost = (float) $custom_calculation[0];
3791 }
3792
3793 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt[0]['idiva']);
3794 $isdue += $tmpopr;
3795 $optstr[$num][] = ($stept[1] > 1 ? $stept[1] . " " : "") . $actopt[0]['name'] . ": " . $tmpopr . " " . $currencyname . "\n";
3796 }
3797 }
3798
3799 // custom extra costs
3800 if (!empty($or['extracosts'])) {
3801 $cur_extra_costs = json_decode($or['extracosts'], true);
3802 foreach ($cur_extra_costs as $eck => $ecv) {
3803 $ecplustax = !empty($ecv['idtax']) ? VikBooking::sayOptionalsPlusIva($ecv['cost'], $ecv['idtax']) : $ecv['cost'];
3804 $isdue += $ecplustax;
3805 $optstr[$num][] = $ecv['name'] . ": " . $ecplustax . " " . $currencyname."\n";
3806 }
3807 }
3808 }
3809
3810 // coupon
3811 $usedcoupon = false;
3812 $origisdue = $isdue;
3813 if (strlen($order['coupon']) > 0) {
3814 $usedcoupon = true;
3815 $expcoupon = explode(";", $order['coupon']);
3816 $isdue = $isdue - $expcoupon[1];
3817 }
3818
3819 // make sure to apply any previously refunded amount
3820 if ($order['refund'] > 0) {
3821 $isdue -= $order['refund'];
3822 }
3823
3824 // ConfirmationNumber
3825 $confirmnumber = $order['confirmnumber'];
3826
3827 $esit_mess = JText::sprintf('VBORDEREMAILRESENT', $order['custmail']);
3828 $status_str = JText::translate('VBCOMPLETED');
3829 if ($cancellation) {
3830 $confirmnumber = '';
3831 $esit_mess = JText::sprintf('VBCANCORDEREMAILSENT', $order['custmail']);
3832 $status_str = JText::translate('VBCANCELLED');
3833 } elseif ($order['status'] == 'standby') {
3834 $confirmnumber = '';
3835 $status_str = JText::translate('VBWAITINGFORPAYMENT');
3836 }
3837 $app->enqueueMessage($esit_mess);
3838
3839 // force the original total amount if rates have changed
3840 if (number_format($isdue, 2) != number_format($order['total'], 2)) {
3841 $isdue = $order['total'];
3842 }
3843
3844 // send email notification to guest (by ignoring the configuration settings)
3845 VikBooking::sendBookingEmail($order['id'], ['guest'], $send = true, $no_config = true);
3846
3847 if ($cancellation) {
3848 /**
3849 * If "send cancellation email", we log the event in the history.
3850 *
3851 * @since 1.14 (J) - 1.4.0 (WP)
3852 */
3853 VikBooking::getBookingHistoryInstance()->setBid($order['id'])->store('EC');
3854 } else {
3855 /**
3856 * Instead, we store an event log to remind that the email was re-sent to the guest
3857 *
3858 * @since 1.16.3 (J) - 1.6.3 (WP)
3859 */
3860 VikBooking::getBookingHistoryInstance()->setBid($order['id'])->store('ER', $esit_mess);
3861 }
3862
3863 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$oid);
3864 $app->close();
3865 }
3866
3867 public function setordconfirmed()
3868 {
3869 $app = JFactory::getApplication();
3870
3871 // the booking ID to confirm
3872 $cid = VikRequest::getVar('cid', array(0));
3873 $oid = (int) $cid[0];
3874
3875 // notify the customer unless it was a re-confirmation
3876 $pskip = $app->input->getInt('skip_notification', 0);
3877
3878 // access the reservation model
3879 $model = VBOModelReservation::getInstance();
3880
3881 // set the booking to confirmed
3882 $confirmed = $model->setConfirmed([
3883 'booking_id' => $oid,
3884 'notify' => (bool) (!$pskip),
3885 ]);
3886
3887 if (!$confirmed) {
3888 $error = $model->getError();
3889 if (!is_string($error) || !$error) {
3890 $error = 'Could not confirm the reservation';
3891 }
3892
3893 // enqueue error message
3894 $app->enqueueMessage($error, 'error');
3895 } else {
3896 // enqueue success message
3897 $app->enqueueMessage(JText::translate('VBORDERSETASCONF'));
3898 }
3899
3900 // redirect
3901 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $oid);
3902 $app->close();
3903 }
3904
3905 public function payments() {
3906 VikBookingHelper::printHeader("14");
3907
3908 VikRequest::setVar('view', VikRequest::getCmd('view', 'payments'));
3909
3910 parent::display();
3911
3912 if (VikBooking::showFooter()) {
3913 VikBookingHelper::printFooter();
3914 }
3915 }
3916
3917 public function newpayment() {
3918 VikBookingHelper::printHeader("14");
3919
3920 VikRequest::setVar('view', VikRequest::getCmd('view', 'managepayment'));
3921
3922 parent::display();
3923
3924 if (VikBooking::showFooter()) {
3925 VikBookingHelper::printFooter();
3926 }
3927 }
3928
3929 public function editpayment() {
3930 VikBookingHelper::printHeader("14");
3931
3932 VikRequest::setVar('view', VikRequest::getCmd('view', 'managepayment'));
3933
3934 parent::display();
3935
3936 if (VikBooking::showFooter()) {
3937 VikBookingHelper::printFooter();
3938 }
3939 }
3940
3941 public function createpayment()
3942 {
3943 if (!JSession::checkToken()) {
3944 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3945 }
3946
3947 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
3948 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
3949 }
3950
3951 $mainframe = JFactory::getApplication();
3952 $pname = VikRequest::getString('name', '', 'request');
3953 $ppayment = VikRequest::getString('payment', '', 'request');
3954 $ppublished = VikRequest::getString('published', '', 'request');
3955 $pcharge = VikRequest::getFloat('charge', '', 'request');
3956 $psetconfirmed = VikRequest::getString('setconfirmed', '', 'request');
3957 $phidenonrefund = VikRequest::getInt('hidenonrefund', '', 'request');
3958 $ponlynonrefund = VikRequest::getInt('onlynonrefund', '', 'request');
3959 $pshownotealw = VikRequest::getString('shownotealw', '', 'request');
3960 $pnote = VikRequest::getString('note', '', 'request', VIKREQUEST_ALLOWHTML);
3961 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
3962 $pval_pcent = !in_array($pval_pcent, array('1', '2')) ? 1 : $pval_pcent;
3963 $pch_disc = VikRequest::getString('ch_disc', '', 'request');
3964 $pch_disc = !in_array($pch_disc, array('1', '2')) ? 1 : $pch_disc;
3965 $poutposition = VikRequest::getString('outposition', 'top', 'request');
3966 $plogo = VikRequest::getString('logo', '', 'request');
3967 $pall_rooms = VikRequest::getInt('all_rooms', 0, 'request');
3968 $pidrooms = VikRequest::getVar('idrooms', array());
3969 $vikpaymentparams = VikRequest::getVar('vikpaymentparams', array(0));
3970 $payparamarr = array();
3971 $payparamstr = '';
3972 if (count($vikpaymentparams) > 0) {
3973 foreach ($vikpaymentparams as $setting => $cont) {
3974 if (strlen($setting) > 0) {
3975 $payparamarr[$setting] = $cont;
3976 }
3977 }
3978 if (count($payparamarr) > 0) {
3979 $payparamstr = json_encode($payparamarr);
3980 }
3981 }
3982
3983 $dbo = JFactory::getDbo();
3984
3985 $set_idrooms = [];
3986 if (empty($pall_rooms) && !empty($pidrooms)) {
3987 $pidrooms = array_map(function($idroom) {
3988 return (int)$idroom;
3989 }, $pidrooms);
3990 foreach ($pidrooms as $idroom) {
3991 if (empty($idroom) || in_array($idroom, $set_idrooms)) {
3992 continue;
3993 }
3994 $set_idrooms[] = $idroom;
3995 }
3996 }
3997
3998 if (!empty($pname) && !empty($ppayment)) {
3999 $setpub = $ppublished == "1" ? 1 : 0;
4000 $psetconfirmed = $psetconfirmed == "1" ? 1 : 0;
4001 $pshownotealw = $pshownotealw == "1" ? 1 : 0;
4002 $q = "SELECT `id` FROM `#__vikbooking_gpayments` WHERE `file`=".$dbo->quote($ppayment).";";
4003 $dbo->setQuery($q);
4004 $dbo->execute();
4005 if ($dbo->getNumRows() >= 0) {
4006 $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') . ");";
4007 $dbo->setQuery($q);
4008 $dbo->execute();
4009 $mainframe->enqueueMessage(JText::translate('VBPAYMENTSAVED'));
4010 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4011 } else {
4012 VikError::raiseWarning('', JText::translate('ERRINVFILEPAYMENT'));
4013 $mainframe->redirect("index.php?option=com_vikbooking&task=newpayment");
4014 }
4015 } else {
4016 $mainframe->redirect("index.php?option=com_vikbooking&task=newpayment");
4017 }
4018 }
4019
4020 public function updatepayment()
4021 {
4022 if (!JSession::checkToken()) {
4023 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4024 }
4025
4026 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4027 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4028 }
4029
4030 $this->do_updatepayment($stay = false);
4031 }
4032
4033 public function updatepaymentstay()
4034 {
4035 if (!JSession::checkToken()) {
4036 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4037 }
4038
4039 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4040 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4041 }
4042
4043 $this->do_updatepayment($stay = true);
4044 }
4045
4046 protected function do_updatepayment($stay = false)
4047 {
4048 $mainframe = JFactory::getApplication();
4049
4050 $pwhere = VikRequest::getString('where', '', 'request');
4051 $pname = VikRequest::getString('name', '', 'request');
4052 $ppayment = VikRequest::getString('payment', '', 'request');
4053 $ppublished = VikRequest::getString('published', '', 'request');
4054 $pcharge = VikRequest::getFloat('charge', '', 'request');
4055 $psetconfirmed = VikRequest::getString('setconfirmed', '', 'request');
4056 $phidenonrefund = VikRequest::getInt('hidenonrefund', '', 'request');
4057 $ponlynonrefund = VikRequest::getInt('onlynonrefund', '', 'request');
4058 $pshownotealw = VikRequest::getString('shownotealw', '', 'request');
4059 $pnote = VikRequest::getString('note', '', 'request', VIKREQUEST_ALLOWRAW);
4060 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4061 $pval_pcent = !in_array($pval_pcent, array('1', '2')) ? 1 : $pval_pcent;
4062 $pch_disc = VikRequest::getString('ch_disc', '', 'request');
4063 $pch_disc = !in_array($pch_disc, array('1', '2')) ? 1 : $pch_disc;
4064 $poutposition = VikRequest::getString('outposition', 'top', 'request');
4065 $plogo = VikRequest::getString('logo', '', 'request');
4066 $pall_rooms = VikRequest::getInt('all_rooms', 0, 'request');
4067 $pidrooms = VikRequest::getVar('idrooms', array());
4068 $vikpaymentparams = VikRequest::getVar('vikpaymentparams', array(0));
4069 $payparamarr = array();
4070 $payparamstr = '';
4071 if (count($vikpaymentparams) > 0) {
4072 foreach ($vikpaymentparams as $setting => $cont) {
4073 if (strlen($setting) > 0) {
4074 $payparamarr[$setting] = $cont;
4075 }
4076 }
4077 if (count($payparamarr) > 0) {
4078 $payparamstr = json_encode($payparamarr);
4079 }
4080 }
4081
4082 $dbo = JFactory::getDbo();
4083
4084 $set_idrooms = [];
4085 if (empty($pall_rooms) && !empty($pidrooms)) {
4086 $pidrooms = array_map(function($idroom) {
4087 return (int)$idroom;
4088 }, $pidrooms);
4089 foreach ($pidrooms as $idroom) {
4090 if (empty($idroom) || in_array($idroom, $set_idrooms)) {
4091 continue;
4092 }
4093 $set_idrooms[] = $idroom;
4094 }
4095 }
4096
4097 if (!empty($pname) && !empty($ppayment) && !empty($pwhere)) {
4098 $setpub = $ppublished == "1" ? 1 : 0;
4099 $psetconfirmed = $psetconfirmed == "1" ? 1 : 0;
4100 $pshownotealw = $pshownotealw == "1" ? 1 : 0;
4101 $q = "SELECT `id` FROM `#__vikbooking_gpayments` WHERE `file`=".$dbo->quote($ppayment)." AND `id`!='".$pwhere."';";
4102 $dbo->setQuery($q);
4103 $dbo->execute();
4104 if ($dbo->getNumRows() >= 0) {
4105 $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).";";
4106 $dbo->setQuery($q);
4107 $dbo->execute();
4108
4109 $mainframe->enqueueMessage(JText::translate('VBPAYMENTUPDATED'));
4110 if ($stay) {
4111 $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4112 } else {
4113 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4114 }
4115 } else {
4116 VikError::raiseWarning('', JText::translate('ERRINVFILEPAYMENT'));
4117 $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4118 }
4119 } else {
4120 $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4121 }
4122 }
4123
4124 public function removepayments()
4125 {
4126 if (!JSession::checkToken()) {
4127 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4128 }
4129
4130 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
4131 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4132 }
4133
4134 $ids = VikRequest::getVar('cid', array(0));
4135 if ($ids) {
4136 $dbo = JFactory::getDBO();
4137 foreach ($ids as $d) {
4138 $q = "DELETE FROM `#__vikbooking_gpayments` WHERE `id`=".$dbo->quote($d).";";
4139 $dbo->setQuery($q);
4140 $dbo->execute();
4141 }
4142 }
4143 $mainframe = JFactory::getApplication();
4144 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4145 }
4146
4147 public function modavailpayment() {
4148 if (!JSession::checkToken('get')) {
4149 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4150 }
4151 $cid = VikRequest::getVar('cid', array(0));
4152 $idp = $cid[0];
4153 if (!empty($idp)) {
4154 $dbo = JFactory::getDBO();
4155 $q = "SELECT `published` FROM `#__vikbooking_gpayments` WHERE `id`=".intval($idp).";";
4156 $dbo->setQuery($q);
4157 $dbo->execute();
4158 $get = $dbo->loadAssocList();
4159 $q = "UPDATE `#__vikbooking_gpayments` SET `published`=".(intval($get[0]['published']) == 1 ? '0' : '1')." WHERE `id`=".intval($idp).";";
4160 $dbo->setQuery($q);
4161 $dbo->execute();
4162 }
4163 $mainframe = JFactory::getApplication();
4164 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4165 }
4166
4167 public function seasons() {
4168 VikBookingHelper::printHeader("13");
4169
4170 VikRequest::setVar('view', VikRequest::getCmd('view', 'seasons'));
4171
4172 parent::display();
4173
4174 if (VikBooking::showFooter()) {
4175 VikBookingHelper::printFooter();
4176 }
4177 }
4178
4179 public function newseason() {
4180 VikBookingHelper::printHeader("13");
4181
4182 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageseason'));
4183
4184 parent::display();
4185
4186 if (VikBooking::showFooter()) {
4187 VikBookingHelper::printFooter();
4188 }
4189 }
4190
4191 public function editseason() {
4192 VikBookingHelper::printHeader("13");
4193
4194 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageseason'));
4195
4196 parent::display();
4197
4198 if (VikBooking::showFooter()) {
4199 VikBookingHelper::printFooter();
4200 }
4201 }
4202
4203 public function updateseason()
4204 {
4205 if (!JSession::checkToken()) {
4206 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4207 }
4208
4209 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4210 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4211 }
4212
4213 $this->do_updateseason();
4214 }
4215
4216 public function updateseasonstay()
4217 {
4218 if (!JSession::checkToken()) {
4219 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4220 }
4221
4222 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4223 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4224 }
4225
4226 $this->do_updateseason(true);
4227 }
4228
4229 private function do_updateseason($stay = false)
4230 {
4231 $app = JFactory::getApplication();
4232 $dbo = JFactory::getDbo();
4233 $session = JFactory::getSession();
4234
4235 $pwhere = VikRequest::getInt('where', 0, 'request');
4236
4237 $pfrom = VikRequest::getString('from', '', 'request');
4238 $pto = VikRequest::getString('to', '', 'request');
4239 $ptype = VikRequest::getString('type', '', 'request');
4240 $pdiffcost = VikRequest::getFloat('diffcost', '', 'request');
4241 $pidrooms = VikRequest::getVar('idrooms', array());
4242 $pidprices = VikRequest::getVar('idprices', array());
4243 $pwdays = VikRequest::getVar('wdays', array());
4244 $pspname = VikRequest::getString('spname', '', 'request');
4245 $pcheckinincl = VikRequest::getString('checkinincl', '', 'request');
4246 $pcheckinincl = $pcheckinincl == 1 ? 1 : 0;
4247 $pyeartied = VikRequest::getInt('yeartied', 0, 'request');
4248 $pyeartied = $pyeartied == 1 ? 1 : 0;
4249 $tieyear = 0;
4250 $ppromo = VikRequest::getInt('promo', 0, 'request');
4251 $ppromo = $ppromo == 1 ? 1 : 0;
4252 $ppromodaysadv = VikRequest::getInt('promodaysadv', '', 'request');
4253 $ppromominlos = VikRequest::getInt('promominlos', '', 'request');
4254 $ppromotxt = VikRequest::getString('promotxt', '', 'request', VIKREQUEST_ALLOWHTML);
4255 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4256 $pval_pcent = $pval_pcent == "1" ? 1 : 2;
4257 $proundmode = VikRequest::getString('roundmode', '', 'request');
4258 $proundmode = (!empty($proundmode) && in_array($proundmode, array('PHP_ROUND_HALF_UP', 'PHP_ROUND_HALF_DOWN')) ? $proundmode : '');
4259 $pnightsoverrides = VikRequest::getVar('nightsoverrides', array());
4260 $pvaluesoverrides = VikRequest::getVar('valuesoverrides', array());
4261 $pandmoreoverride = VikRequest::getVar('andmoreoverride', array());
4262 $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array());
4263 $padultsdiffval = VikRequest::getVar('adultsdiffval', array());
4264 $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array());
4265 $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array());
4266 $ppromolastmind = VikRequest::getInt('promolastmind', 0, 'request');
4267 $ppromolastminh = VikRequest::getInt('promolastminh', 0, 'request');
4268 $promolastmin = ($ppromolastmind * 86400) + ($ppromolastminh * 3600);
4269 $ppromofinalprice = VikRequest::getInt('promofinalprice', 0, 'request');
4270 $ppromofinalprice = $ppromo ? $ppromofinalprice : 0;
4271 $occupancy_ovr = array();
4272 $losverridestr = "";
4273
4274 $updforvcm = $session->get('vbVcmRatesUpd', '');
4275 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
4276
4277 // check null dates
4278 if ($dbo->getNullDate() == $pfrom) {
4279 $pfrom = '';
4280 }
4281 if ($dbo->getNullDate() == $pto) {
4282 $pto = '';
4283 }
4284
4285 if ((empty($pfrom) || empty($pto)) && !$pwdays) {
4286 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4287 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4288 exit;
4289 }
4290
4291 $skipseason = false;
4292 if (empty($pfrom) || empty($pto)) {
4293 $skipseason = true;
4294 }
4295 $skipdays = false;
4296 $wdaystr = null;
4297 if (count($pwdays) == 0) {
4298 $skipdays = true;
4299 } else {
4300 $wdaystr = "";
4301 foreach ($pwdays as $wd) {
4302 $wdaystr .= $wd.';';
4303 }
4304 }
4305 $roomstr = "";
4306 $roomids = array();
4307 foreach ($pidrooms as $room) {
4308 if (empty($room)) {
4309 continue;
4310 }
4311 $roomstr .= "-".$room."-,";
4312 $roomids[] = (int)$room;
4313 }
4314 $pricestr = "";
4315 $priceids = array();
4316 foreach ($pidprices as $price) {
4317 if (empty($price)) {
4318 continue;
4319 }
4320 $pricestr .= "-".$price."-,";
4321 $priceids[] = (int)$price;
4322 }
4323 $valid = true;
4324 $double_records = array();
4325 $sfrom = null;
4326 $sto = null;
4327
4328 // value overrides
4329 if ($pnightsoverrides && $pvaluesoverrides) {
4330 foreach ($pnightsoverrides as $ko => $no) {
4331 if (!empty($no) && strlen(trim($pvaluesoverrides[$ko])) > 0) {
4332 $infiniteclause = intval($pandmoreoverride[$ko]) == 1 ? '-i' : '';
4333 $losverridestr .= intval($no).$infiniteclause.':'.trim($pvaluesoverrides[$ko]).'_';
4334 }
4335 }
4336 }
4337
4338 if (!$skipseason) {
4339 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
4340 $second = VikBooking::getDateTimestamp($pto, 0, 0);
4341
4342 if ($second > 0 && $second == $first) {
4343 $second += 86399;
4344 }
4345
4346 if (!($second > $first)) {
4347 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4348 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4349 exit;
4350 }
4351
4352 $baseone = getdate($first);
4353 $basets = mktime(0, 0, 0, 1, 1, $baseone['year']);
4354 $sfrom = $baseone[0] - $basets;
4355 $basetwo = getdate($second);
4356 $basets = mktime(0, 0, 0, 1, 1, $basetwo['year']);
4357 $sto = $basetwo[0] - $basets;
4358
4359 // check leap year
4360 if ($baseone['year'] % 4 == 0 && ($baseone['year'] % 100 != 0 || $baseone['year'] % 400 == 0)) {
4361 $leapts = mktime(0, 0, 0, 2, 29, $baseone['year']);
4362 if ($baseone[0] > $leapts) {
4363 $sfrom -= 86400;
4364 /**
4365 * To avoid issue with leap years and dates near Feb 29th, we only reduce the seconds if these were reduced
4366 * for the from-date of the seasons. Doing it just for the to-date in 2019 for 2020 (leap) produced invalid results.
4367 *
4368 * @since July 2nd 2019
4369 */
4370 if ($basetwo['year'] % 4 == 0 && ($basetwo['year'] % 100 != 0 || $basetwo['year'] % 400 == 0)) {
4371 $leapts = mktime(0, 0, 0, 2, 29, $basetwo['year']);
4372 if ($basetwo[0] > $leapts) {
4373 $sto -= date('d-m', $baseone[0]) != '31-12' && date('d-m', $basetwo[0]) == '31-12' ? 1 : 86400;
4374 }
4375 }
4376 }
4377 }
4378
4379 // tied to the year
4380 if ($pyeartied == 1) {
4381 $tieyear = $baseone['year'];
4382 }
4383
4384 // Occupancy Override
4385 if (count($padultsdiffval) > 0) {
4386 foreach ($padultsdiffval as $rid => $valovr_arr) {
4387 if (!is_array($valovr_arr) || !is_array($padultsdiffchdisc[$rid]) || !is_array($padultsdiffvalpcent[$rid]) || !is_array($padultsdiffpernight[$rid])) {
4388 continue;
4389 }
4390 foreach ($valovr_arr as $occ => $valovr) {
4391 if (!(strlen($valovr) > 0) || !(strlen($padultsdiffchdisc[$rid][$occ]) > 0) || !(strlen($padultsdiffvalpcent[$rid][$occ]) > 0) || !(strlen($padultsdiffpernight[$rid][$occ]) > 0)) {
4392 continue;
4393 }
4394 if (!array_key_exists($rid, $occupancy_ovr)) {
4395 $occupancy_ovr[$rid] = array();
4396 }
4397 $occupancy_ovr[$rid][$occ] = array('chdisc' => (int)$padultsdiffchdisc[$rid][$occ], 'valpcent' => (int)$padultsdiffvalpcent[$rid][$occ], 'pernight' => (int)$padultsdiffpernight[$rid][$occ], 'value' => (float)$valovr);
4398 }
4399 }
4400 }
4401
4402 // check if seasons dates are valid
4403 $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").";";
4404 $dbo->setQuery($q);
4405 $similar = $dbo->loadAssocList();
4406 if ($similar) {
4407 $valid = false;
4408 foreach ($similar as $sim) {
4409 $double_records[] = $sim['spname'];
4410 }
4411 }
4412
4413 $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").";";
4414 $dbo->setQuery($q);
4415 $similar = $dbo->loadAssocList();
4416 if ($similar) {
4417 $valid = false;
4418 foreach ($similar as $sim) {
4419 $double_records[] = $sim['spname'];
4420 }
4421 }
4422
4423 $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").";";
4424 $dbo->setQuery($q);
4425 $dbo->execute();
4426 $similar = $dbo->loadAssocList();
4427 if ($similar) {
4428 $valid = false;
4429 foreach ($similar as $sim) {
4430 $double_records[] = $sim['spname'];
4431 }
4432 }
4433 }
4434
4435 // fetch previous record before the update
4436 $q = $dbo->getQuery(true)
4437 ->select('*')
4438 ->from($dbo->qn('#__vikbooking_seasons'))
4439 ->where($dbo->qn('id') . ' = ' . $pwhere);
4440 $dbo->setQuery($q, 0, 1);
4441 $prev_record = $dbo->loadAssoc();
4442
4443 if (!$valid || !$prev_record) {
4444 VikError::raiseWarning('', JText::translate('ERRINVDATEROOMSLOCSEASON').($double_records ? ' ('.implode(', ', array_unique($double_records)).')' : ''));
4445 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4446 exit;
4447 }
4448
4449 /**
4450 * Attempt to access the promotion handlers in advance to perform additional validations.
4451 *
4452 * @since 1.16.4 (J) - 1.6.4 (WP)
4453 */
4454 try {
4455 $promo_handlers = VikBooking::getPromotionHandlers();
4456 } catch (Exception $e) {
4457 // reset the value
4458 $promo_handlers = [];
4459 }
4460
4461 if (!$prev_record['promo'] && $ppromo && $promo_handlers) {
4462 // channels supporting promotions are available, and a regular special price is
4463 // being converted into a promotion - this is not allowed so we make it a non-promotion.
4464 $ppromo = 0;
4465 $app->enqueueMessage(JText::translate('VBO_NOPROMO_UPD_CHANNELS'), 'warning');
4466 }
4467
4468 if ($promo_handlers && $proundmode) {
4469 /**
4470 * Always disallow rounding when channels supporting promotions are available.
4471 *
4472 * @since 1.18.3 (J) - 1.8.3 (WP)
4473 */
4474 $proundmode = '';
4475 $app->enqueueMessage(sprintf('%s: %s.', JText::translate('VBNEWSEASONROUNDCOST'), JText::translate('VBPARAMPRICECALENDARDISABLED')), 'warning');
4476 }
4477
4478 // update record
4479 $upd_record = new stdClass;
4480 $upd_record->id = $prev_record['id'];
4481 $upd_record->type = $ptype == "1" ? 1 : 2;
4482 $upd_record->from = $sfrom;
4483 $upd_record->to = $sto;
4484 $upd_record->diffcost = $pdiffcost;
4485 $upd_record->idrooms = $roomstr;
4486 $upd_record->spname = $pspname;
4487 $upd_record->wdays = $wdaystr;
4488 $upd_record->checkinincl = $pcheckinincl;
4489 $upd_record->val_pcent = $pval_pcent;
4490 $upd_record->losoverride = $losverridestr;
4491 $upd_record->roundmode = !empty($proundmode) ? $proundmode : null;
4492 $upd_record->year = $pyeartied == 1 ? $tieyear : null;
4493 $upd_record->idprices = $pricestr;
4494 $upd_record->promo = $ppromo;
4495 $upd_record->promodaysadv = !empty($ppromodaysadv) ? $ppromodaysadv : null;
4496 $upd_record->promotxt = $ppromotxt;
4497 $upd_record->promominlos = !empty($ppromominlos) ? $ppromominlos : 0;
4498 $upd_record->occupancy_ovr = $occupancy_ovr ? json_encode($occupancy_ovr) : null;
4499 $upd_record->promolastmin = (int)$promolastmin;
4500 $upd_record->promofinalprice = $ppromofinalprice;
4501
4502 $dbo->updateObject('#__vikbooking_seasons', $upd_record, 'id', $nulls = true);
4503
4504 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
4505
4506 // update session values
4507 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
4508 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
4509 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $first ? $first : $updforvcm['dfrom'];
4510 } else {
4511 $updforvcm['dfrom'] = $first;
4512 }
4513 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
4514 $updforvcm['dto'] = $updforvcm['dto'] < $second ? $second : $updforvcm['dto'];
4515 } else {
4516 $updforvcm['dto'] = $second;
4517 }
4518 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
4519 foreach ($roomids as $rid) {
4520 if (!in_array($rid, $updforvcm['rooms'])) {
4521 $updforvcm['rooms'][] = $rid;
4522 }
4523 }
4524 } else {
4525 $updforvcm['rooms'] = $roomids;
4526 }
4527 if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
4528 foreach ($roomids as $rid) {
4529 if (array_key_exists($rid, $updforvcm['rplans'])) {
4530 $updforvcm['rplans'][$rid] = $updforvcm['rplans'][$rid] + $priceids;
4531 } else {
4532 $updforvcm['rplans'][$rid] = $priceids;
4533 }
4534 }
4535 } else {
4536 $updforvcm['rplans'] = array();
4537 foreach ($roomids as $rid) {
4538 $updforvcm['rplans'][$rid] = $priceids;
4539 }
4540 }
4541 $session->set('vbVcmRatesUpd', $updforvcm);
4542
4543 /**
4544 * Query promotion handlers, if any, to trigger the update/delete promotion event.
4545 *
4546 * @since 1.15.0 (J) - 1.5.0 (WP)
4547 * @since 1.16.4 (J) - 1.6.4 (WP) added control to perform a delete operation.
4548 */
4549 $promo_update_type = $prev_record['promo'] && !$ppromo ? 'triggerDelete' : 'triggerUpdate';
4550 $promo_method_type = $prev_record['promo'] && !$ppromo ? 'delete' : 'update';
4551 try {
4552 if ($ppromo && is_array($promo_handlers) && $promo_handlers) {
4553 foreach ($promo_handlers as $promo_handler) {
4554 if (!isset($promo_handler->instance) || !is_object($promo_handler->instance) || !method_exists($promo_handler->instance, $promo_update_type)) {
4555 // outdated handler object
4556 continue;
4557 }
4558 if (!is_callable(array($promo_handler->instance, $promo_update_type)) || !$promo_handler->instance->{$promo_update_type}()) {
4559 // promotion handler does not support update/delete promotion event
4560 continue;
4561 }
4562 // invoke the update/delete promotion event for this handler
4563 $ch_result = $promo_handler->instance->createPromotion(['vbo_promo_id' => $pwhere], $promo_method_type);
4564 if (!$ch_result) {
4565 VikError::raiseWarning('', $promo_handler->instance->getName() . ': ' . $promo_handler->instance->getError());
4566 }
4567 }
4568 }
4569 } catch (Exception $e) {
4570 // do nothing
4571 }
4572
4573 if ($stay) {
4574 $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4575 } else {
4576 $app->redirect("index.php?option=com_vikbooking&task=seasons");
4577 }
4578 $app->close();
4579 }
4580
4581 public function createseason()
4582 {
4583 if (!JSession::checkToken()) {
4584 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4585 }
4586
4587 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
4588 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4589 }
4590
4591 $this->do_createseason();
4592 }
4593
4594 public function createseason_new()
4595 {
4596 if (!JSession::checkToken()) {
4597 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4598 }
4599
4600 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
4601 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4602 }
4603
4604 $this->do_createseason(true);
4605 }
4606
4607 private function do_createseason($andnew = false)
4608 {
4609 $app = JFactory::getApplication();
4610 $dbo = JFactory::getDbo();
4611 $session = JFactory::getSession();
4612
4613 $pfrom = VikRequest::getString('from', '', 'request');
4614 $pto = VikRequest::getString('to', '', 'request');
4615 $ptype = VikRequest::getString('type', '', 'request');
4616 $pdiffcost = VikRequest::getFloat('diffcost', '', 'request');
4617 $pidrooms = VikRequest::getVar('idrooms', array());
4618 $pidprices = VikRequest::getVar('idprices', array());
4619 $pwdays = VikRequest::getVar('wdays', array());
4620 $pspname = VikRequest::getString('spname', '', 'request');
4621 $pcheckinincl = VikRequest::getString('checkinincl', '', 'request');
4622 $pcheckinincl = $pcheckinincl == 1 ? 1 : 0;
4623 $pyeartied = VikRequest::getInt('yeartied', 0, 'request');
4624 $pyeartied = $pyeartied == 1 ? 1 : 0;
4625 $tieyear = 0;
4626 $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4627 $pval_pcent = $pval_pcent == "1" ? 1 : 2;
4628 $proundmode = VikRequest::getString('roundmode', '', 'request');
4629 $proundmode = (!empty($proundmode) && in_array($proundmode, array('PHP_ROUND_HALF_UP', 'PHP_ROUND_HALF_DOWN')) ? $proundmode : '');
4630 $ppromo = VikRequest::getInt('promo', 0, 'request');
4631 $ppromodaysadv = VikRequest::getInt('promodaysadv', '', 'request');
4632 $ppromominlos = VikRequest::getInt('promominlos', '', 'request');
4633 $ppromotxt = VikRequest::getString('promotxt', '', 'request', VIKREQUEST_ALLOWHTML);
4634 $pnightsoverrides = VikRequest::getVar('nightsoverrides', array());
4635 $pvaluesoverrides = VikRequest::getVar('valuesoverrides', array());
4636 $pandmoreoverride = VikRequest::getVar('andmoreoverride', array());
4637 $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array());
4638 $padultsdiffval = VikRequest::getVar('adultsdiffval', array());
4639 $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array());
4640 $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array());
4641 $ppromolastmind = VikRequest::getInt('promolastmind', 0, 'request');
4642 $ppromolastminh = VikRequest::getInt('promolastminh', 0, 'request');
4643 $promolastmin = ($ppromolastmind * 86400) + ($ppromolastminh * 3600);
4644 $ppromofinalprice = VikRequest::getInt('promofinalprice', 0, 'request');
4645 $ppromofinalprice = $ppromo ? $ppromofinalprice : 0;
4646 $pchannels = VikRequest::getVar('channels', array());
4647 $occupancy_ovr = array();
4648 $losverridestr = "";
4649
4650 $updforvcm = $session->get('vbVcmRatesUpd', '');
4651 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
4652
4653 // check null dates
4654 if ($dbo->getNullDate() == $pfrom) {
4655 $pfrom = '';
4656 }
4657 if ($dbo->getNullDate() == $pto) {
4658 $pto = '';
4659 }
4660
4661 if ((empty($pfrom) || empty($pto)) && !$pwdays) {
4662 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4663 $app->redirect("index.php?option=com_vikbooking&task=newseason");
4664 exit;
4665 }
4666
4667 $skipseason = false;
4668 if (empty($pfrom) || empty($pto)) {
4669 $skipseason = true;
4670 }
4671 $skipdays = false;
4672 $wdaystr = null;
4673 if (!$pwdays) {
4674 $skipdays = true;
4675 } else {
4676 $wdaystr = "";
4677 foreach ($pwdays as $wd) {
4678 $wdaystr .= $wd.';';
4679 }
4680 }
4681 $roomstr = "";
4682 $roomids = array();
4683 foreach ($pidrooms as $room) {
4684 if (empty($room)) {
4685 continue;
4686 }
4687 $roomstr .= "-".$room."-,";
4688 $roomids[] = (int)$room;
4689 }
4690 $pricestr = "";
4691 $priceids = array();
4692 foreach ($pidprices as $price) {
4693 if (empty($price)) {
4694 continue;
4695 }
4696 $pricestr .= "-".$price."-,";
4697 $priceids[] = (int)$price;
4698 }
4699 $valid = true;
4700 $double_records = array();
4701 $sfrom = null;
4702 $sto = null;
4703
4704 // value overrides
4705 if ($pnightsoverrides && $pvaluesoverrides) {
4706 foreach ($pnightsoverrides as $ko => $no) {
4707 if (!empty($no) && strlen(trim($pvaluesoverrides[$ko])) > 0) {
4708 $infiniteclause = intval($pandmoreoverride[$ko]) == 1 ? '-i' : '';
4709 $losverridestr .= intval($no).$infiniteclause.':'.trim($pvaluesoverrides[$ko]).'_';
4710 }
4711 }
4712 }
4713
4714 if (!$skipseason) {
4715 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
4716 $second = VikBooking::getDateTimestamp($pto, 0, 0);
4717
4718 if ($second > 0 && $second == $first) {
4719 $second += 86399;
4720 }
4721
4722 if (!($second > $first)) {
4723 VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4724 $app->redirect("index.php?option=com_vikbooking&task=newseason");
4725 exit;
4726 }
4727
4728 $baseone = getdate($first);
4729 $basets = mktime(0, 0, 0, 1, 1, $baseone['year']);
4730 $sfrom = $baseone[0] - $basets;
4731 $basetwo = getdate($second);
4732 $basets = mktime(0, 0, 0, 1, 1, $basetwo['year']);
4733 $sto = $basetwo[0] - $basets;
4734
4735 // check leap year
4736 if ($baseone['year'] % 4 == 0 && ($baseone['year'] % 100 != 0 || $baseone['year'] % 400 == 0)) {
4737 $leapts = mktime(0, 0, 0, 2, 29, $baseone['year']);
4738 if ($baseone[0] > $leapts) {
4739 $sfrom -= 86400;
4740 /**
4741 * To avoid issue with leap years and dates near Feb 29th, we only reduce the seconds if these were reduced
4742 * for the from-date of the seasons. Doing it just for the to-date in 2019 for 2020 (leap) produced invalid results.
4743 *
4744 * @since July 2nd 2019
4745 */
4746 if ($basetwo['year'] % 4 == 0 && ($basetwo['year'] % 100 != 0 || $basetwo['year'] % 400 == 0)) {
4747 $leapts = mktime(0, 0, 0, 2, 29, $basetwo['year']);
4748 if ($basetwo[0] > $leapts) {
4749 $sto -= date('d-m', $baseone[0]) != '31-12' && date('d-m', $basetwo[0]) == '31-12' ? 1 : 86400;
4750 }
4751 }
4752 }
4753 }
4754
4755 // tied to the year
4756 if ($pyeartied == 1) {
4757 $tieyear = $baseone['year'];
4758 }
4759
4760 // Occupancy Override
4761 if ($padultsdiffval) {
4762 foreach ($padultsdiffval as $rid => $valovr_arr) {
4763 if (!is_array($valovr_arr) || !is_array($padultsdiffchdisc[$rid]) || !is_array($padultsdiffvalpcent[$rid]) || !is_array($padultsdiffpernight[$rid])) {
4764 continue;
4765 }
4766 foreach ($valovr_arr as $occ => $valovr) {
4767 if (!(strlen($valovr) > 0) || !(strlen($padultsdiffchdisc[$rid][$occ]) > 0) || !(strlen($padultsdiffvalpcent[$rid][$occ]) > 0) || !(strlen($padultsdiffpernight[$rid][$occ]) > 0)) {
4768 continue;
4769 }
4770 if (!array_key_exists($rid, $occupancy_ovr)) {
4771 $occupancy_ovr[$rid] = array();
4772 }
4773 $occupancy_ovr[$rid][$occ] = array('chdisc' => (int)$padultsdiffchdisc[$rid][$occ], 'valpcent' => (int)$padultsdiffvalpcent[$rid][$occ], 'pernight' => (int)$padultsdiffpernight[$rid][$occ], 'value' => (float)$valovr);
4774 }
4775 }
4776 }
4777
4778 // check if seasons dates are valid
4779 $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").";";
4780 $dbo->setQuery($q);
4781 $similar = $dbo->loadAssocList();
4782 if ($similar) {
4783 $valid = false;
4784 foreach ($similar as $sim) {
4785 $double_records[] = $sim['spname'];
4786 }
4787 }
4788
4789 $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").";";
4790 $dbo->setQuery($q);
4791 $similar = $dbo->loadAssocList();
4792 if ($similar) {
4793 $valid = false;
4794 foreach ($similar as $sim) {
4795 $double_records[] = $sim['spname'];
4796 }
4797 }
4798
4799 $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").";";
4800 $dbo->setQuery($q);
4801 $similar = $dbo->loadAssocList();
4802 if ($similar) {
4803 $valid = false;
4804 foreach ($similar as $sim) {
4805 $double_records[] = $sim['spname'];
4806 }
4807 }
4808 }
4809
4810 if (!$valid && !$ppromo) {
4811 VikError::raiseWarning('', JText::translate('ERRINVDATEROOMSLOCSEASON').(count($double_records) ? ' ('.implode(', ', array_unique($double_records)).')' : ''));
4812 $app->redirect("index.php?option=com_vikbooking&task=newseason");
4813 exit;
4814 }
4815
4816 if ($pchannels && $proundmode) {
4817 /**
4818 * Always disallow rounding when channels supporting promotions are available.
4819 *
4820 * @since 1.18.3 (J) - 1.8.3 (WP)
4821 */
4822 $proundmode = '';
4823 $app->enqueueMessage(sprintf('%s: %s.', JText::translate('VBNEWSEASONROUNDCOST'), JText::translate('VBPARAMPRICECALENDARDISABLED')), 'warning');
4824 }
4825
4826 // insert new record
4827 $sea_record = new stdClass;
4828 $sea_record->type = $ptype == "1" ? 1 : 2;
4829 $sea_record->from = $sfrom;
4830 $sea_record->to = $sto;
4831 $sea_record->diffcost = $pdiffcost;
4832 $sea_record->idrooms = $roomstr;
4833 $sea_record->spname = $pspname;
4834 $sea_record->wdays = $wdaystr;
4835 $sea_record->checkinincl = $pcheckinincl;
4836 $sea_record->val_pcent = $pval_pcent;
4837 $sea_record->losoverride = $losverridestr;
4838 $sea_record->roundmode = !empty($proundmode) ? $proundmode : null;
4839 $sea_record->year = $pyeartied == 1 ? $tieyear : null;
4840 $sea_record->idprices = $pricestr;
4841 $sea_record->promo = $ppromo == 1 ? 1 : 0;
4842 $sea_record->promodaysadv = !empty($ppromodaysadv) ? $ppromodaysadv : null;
4843 $sea_record->promotxt = $ppromotxt;
4844 $sea_record->promominlos = !empty($ppromominlos) ? $ppromominlos : 0;
4845 $sea_record->occupancy_ovr = $occupancy_ovr ? json_encode($occupancy_ovr) : null;
4846 $sea_record->promolastmin = (int)$promolastmin;
4847 $sea_record->promofinalprice = $ppromofinalprice;
4848
4849 $dbo->insertObject('#__vikbooking_seasons', $sea_record, 'id');
4850
4851 $vbo_promo_id = $sea_record->id;
4852
4853 $app->enqueueMessage(JText::translate('VBSEASONSAVED'));
4854
4855 // update session values
4856 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
4857 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
4858 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $first ? $first : $updforvcm['dfrom'];
4859 } else {
4860 $updforvcm['dfrom'] = $first;
4861 }
4862 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
4863 $updforvcm['dto'] = $updforvcm['dto'] < $second ? $second : $updforvcm['dto'];
4864 } else {
4865 $updforvcm['dto'] = $second;
4866 }
4867 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
4868 foreach ($roomids as $rid) {
4869 if (!in_array($rid, $updforvcm['rooms'])) {
4870 $updforvcm['rooms'][] = $rid;
4871 }
4872 }
4873 } else {
4874 $updforvcm['rooms'] = $roomids;
4875 }
4876 if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
4877 foreach ($roomids as $rid) {
4878 if (array_key_exists($rid, $updforvcm['rplans'])) {
4879 $updforvcm['rplans'][$rid] = $updforvcm['rplans'][$rid] + $priceids;
4880 } else {
4881 $updforvcm['rplans'][$rid] = $priceids;
4882 }
4883 }
4884 } else {
4885 $updforvcm['rplans'] = array();
4886 foreach ($roomids as $rid) {
4887 $updforvcm['rplans'][$rid] = $priceids;
4888 }
4889 }
4890 if (!$ppromo) {
4891 $session->set('vbVcmRatesUpd', $updforvcm);
4892 }
4893
4894 /**
4895 * Create the promotion also on the selected channels
4896 *
4897 * @since 1.13.0 (J) - 1.3.0 (WP)
4898 */
4899 if ($ppromo && $pchannels) {
4900 foreach ($pchannels as $channel_key) {
4901 $promo_obj = VikBooking::getPromotionHandlers($channel_key);
4902 if (!is_object($promo_obj)) {
4903 continue;
4904 }
4905 /**
4906 * We inject for VCM the ID of the newly created promotion in VBO.
4907 *
4908 * @since 1.15.0 (J) - 1.5.0 (WP)
4909 */
4910 $ch_result = $promo_obj->createPromotion(array('vbo_promo_id' => $vbo_promo_id), 'new');
4911 if (!$ch_result) {
4912 VikError::raiseWarning('', $promo_obj->getName() . ': ' . $promo_obj->getError());
4913 } else {
4914 $resp = $promo_obj->getResponse();
4915 $app->enqueueMessage($promo_obj->getName() . ': ' . JText::translate('VBOCHPROMOSUCCESS') . (!empty($resp) ? ' (' . str_replace('e4j.ok.', '', $resp) . ')' : ''));
4916 // in case of success, unset the current session values in VCM
4917 $session->set('vcmBPromo', '');
4918 }
4919 }
4920 }
4921
4922 $app->redirect("index.php?option=com_vikbooking&task=".($andnew ? 'newseason' : 'seasons'));
4923 $app->close();
4924 }
4925
4926 public function removeseasons()
4927 {
4928 if (!JSession::checkToken()) {
4929 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4930 }
4931
4932 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
4933 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4934 }
4935
4936 $app = JFactory::getApplication();
4937 $dbo = JFactory::getDbo();
4938
4939 $ids = VikRequest::getVar('cid', array(0));
4940 $pidroom = VikRequest::getInt('idroom', '', 'request');
4941 $pwhere = VikRequest::getInt('where', '', 'request');
4942 if (!empty($pwhere)) {
4943 $ids[] = $pwhere;
4944 }
4945 $tot_removed = array();
4946 $prev_promos = array();
4947 foreach ($ids as $d) {
4948 if (empty($d)) {
4949 continue;
4950 }
4951 // check if it was a promotion
4952 $q = "SELECT `id` FROM `#__vikbooking_seasons` WHERE `id`=" . (int)$d . " AND `promo`=1;";
4953 $dbo->setQuery($q);
4954 $dbo->execute();
4955 if ($dbo->getNumRows()) {
4956 // push it as a previous promo
4957 array_push($prev_promos, $d);
4958 }
4959
4960 // delete the record
4961 $q = "DELETE FROM `#__vikbooking_seasons` WHERE `id`=".$dbo->quote($d).";";
4962 $dbo->setQuery($q);
4963 $dbo->execute();
4964 $tot_removed[] = $d;
4965 }
4966
4967 /**
4968 * Query promotion handlers, if any, to trigger the delete promotion event.
4969 *
4970 * @since 1.15.0 (J) - 1.5.0 (WP)
4971 */
4972 $promo_handlers = VikBooking::getPromotionHandlers();
4973 foreach ($prev_promos as $vbo_promo_id) {
4974 try {
4975 if (is_array($promo_handlers)) {
4976 foreach ($promo_handlers as $promo_handler) {
4977 if (!isset($promo_handler->instance) || !is_object($promo_handler->instance) || !method_exists($promo_handler->instance, 'triggerDelete')) {
4978 // outdated handler object
4979 continue;
4980 }
4981 if (!is_callable(array($promo_handler->instance, 'triggerDelete')) || !$promo_handler->instance->triggerDelete()) {
4982 // promotion handler does not support delete promotion event
4983 continue;
4984 }
4985 // invoke the delete promotion event for this handler
4986 $ch_result = $promo_handler->instance->createPromotion(array('vbo_promo_id' => $vbo_promo_id), 'delete');
4987 if (!$ch_result) {
4988 VikError::raiseWarning('', $promo_handler->instance->getName() . ': ' . $promo_handler->instance->getError());
4989 }
4990 }
4991 }
4992 } catch (Exception $e) {
4993 // do nothing
4994 }
4995 }
4996
4997 $app->enqueueMessage(JText::sprintf('VBRECORDSREMOVED', count($tot_removed)));
4998 $app->redirect("index.php?option=com_vikbooking&task=seasons".(!empty($pidroom) ? '&idroom='.$pidroom : ''));
4999 $app->close();
5000 }
5001
5002 public function updatecustomer()
5003 {
5004 if (!JSession::checkToken()) {
5005 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5006 }
5007
5008 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5009 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5010 }
5011
5012 $this->do_updatecustomer();
5013 }
5014
5015 public function updatecustomerstay()
5016 {
5017 if (!JSession::checkToken()) {
5018 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5019 }
5020
5021 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5022 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5023 }
5024
5025 $this->do_updatecustomer(true);
5026 }
5027
5028 private function do_updatecustomer($stay = false) {
5029 $dbo = JFactory::getDbo();
5030 $mainframe = JFactory::getApplication();
5031 $pfirst_name = VikRequest::getString('first_name', '', 'request');
5032 $plast_name = VikRequest::getString('last_name', '', 'request');
5033 $pcompany = VikRequest::getString('company', '', 'request');
5034 $pvat = VikRequest::getString('vat', '', 'request');
5035 $pemail = VikRequest::getString('email', '', 'request');
5036 $pphone = VikRequest::getString('phone', '', 'request');
5037 $pcountry = VikRequest::getString('country', '', 'request');
5038 $pstate = VikRequest::getString('state', '', 'request');
5039 $ppin = VikRequest::getString('pin', '', 'request');
5040 $pujid = VikRequest::getInt('ujid', '', 'request');
5041 $paddress = VikRequest::getString('address', '', 'request');
5042 $pcity = VikRequest::getString('city', '', 'request');
5043 $pzip = VikRequest::getString('zip', '', 'request');
5044 $pfisccode = VikRequest::getString('fisccode', '', 'request');
5045 $ppec = VikRequest::getString('pec', '', 'request');
5046 $precipcode = VikRequest::getString('recipcode', '', 'request');
5047 $pgender = VikRequest::getString('gender', '', 'request');
5048 $pgender = in_array($pgender, array('F', 'M')) ? $pgender : '';
5049 $pbdate = VikRequest::getString('bdate', '', 'request');
5050 $ppbirth = VikRequest::getString('pbirth', '', 'request');
5051 $pdoctype = VikRequest::getString('doctype', '', 'request');
5052 $pdocnum = VikRequest::getString('docnum', '', 'request');
5053 $pnotes = VikRequest::getString('notes', '', 'request');
5054 $pscandocimg = VikRequest::getString('scandocimg', '', 'request');
5055 $pischannel = VikRequest::getInt('ischannel', '', 'request');
5056 $pcommission = VikRequest::getFloat('commission', '', 'request');
5057 $pcalccmmon = VikRequest::getInt('calccmmon', '', 'request');
5058 $papplycmmon = VikRequest::getInt('applycmmon', '', 'request');
5059 $pchname = VikRequest::getString('chname', '', 'request');
5060 $pchcolor = VikRequest::getString('chcolor', '', 'request');
5061 $pwhere = VikRequest::getInt('where', '', 'request');
5062 $ptmpl = VikRequest::getString('tmpl', '', 'request');
5063 $pcheckin = VikRequest::getInt('checkin', '', 'request');
5064 $pbid = VikRequest::getInt('bid', '', 'request');
5065 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
5066 if (!empty($pwhere) && !empty($pfirst_name) && !empty($plast_name) && !empty($pemail)) {
5067 $q = "SELECT * FROM `#__vikbooking_customers` WHERE `id`=".(int)$pwhere." LIMIT 1;";
5068 $dbo->setQuery($q);
5069 $dbo->execute();
5070 if ($dbo->getNumRows() == 1) {
5071 $customer = $dbo->loadAssoc();
5072 } else {
5073 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5074 exit;
5075 }
5076 /**
5077 * Existing customers are recognized by equal first name, last name and email address.
5078 *
5079 * @since 1.3.0
5080 */
5081 $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;";
5082 $dbo->setQuery($q);
5083 $dbo->execute();
5084 if ($dbo->getNumRows() == 0) {
5085 $cpin = VikBooking::getCPinIstance();
5086 if (empty($ppin)) {
5087 $ppin = $customer['pin'];
5088 } elseif ($cpin->pinExists($ppin, $customer['pin'])) {
5089 $ppin = $cpin->generateUniquePin();
5090 }
5091 //file upload
5092 jimport('joomla.filesystem.file');
5093 $pimg = VikRequest::getVar('docimg', null, 'files', 'array');
5094 $gimg = "";
5095 if (isset($pimg) && strlen(trim($pimg['name']))) {
5096 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($pimg['name'])));
5097 $src = $pimg['tmp_name'];
5098 $dest = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans'.DIRECTORY_SEPARATOR;
5099 $j = "";
5100 if (file_exists($dest.$filename)) {
5101 $j = rand(171, 1717);
5102 while (file_exists($dest.$j.$filename)) {
5103 $j++;
5104 }
5105 }
5106 $finaldest = $dest.$j.$filename;
5107 $check = getimagesize($pimg['tmp_name']);
5108 if (($check[2] & imagetypes()) || preg_match("/application\/(zip|pdf)$/", $pimg['type'])) {
5109 if (VikBooking::uploadFile($src, $finaldest)) {
5110 $gimg = $j.$filename;
5111 } else {
5112 VikError::raiseWarning('', 'Error while uploading image');
5113 }
5114 } else {
5115 VikError::raiseWarning('', 'Uploaded file is not an Image');
5116 }
5117 } elseif (!empty($pscandocimg)) {
5118 $gimg = $pscandocimg;
5119 }
5120 //
5121 $pischannel = $pischannel > 0 ? 1 : 0;
5122 $pcalccmmon = $pcalccmmon > 0 ? 1 : 0;
5123 $papplycmmon = $papplycmmon > 0 ? 1 : 0;
5124 $pchname = str_replace(' ', '', trim($pchname));
5125 $pchname = strlen($pchname) <= 0 && $pischannel > 0 ? str_replace(' ', '', trim($pfirst_name.' '.$plast_name)) : $pchname;
5126 $chparams = array(
5127 'commission' => ($pcommission > 0.00 ? $pcommission : 0),
5128 'calccmmon' => $pcalccmmon,
5129 'applycmmon' => $papplycmmon,
5130 'chcolor' => $pchcolor,
5131 'chname' => $pchname
5132 );
5133
5134 /**
5135 * Customer profile picture (URL or uploaded file).
5136 *
5137 * @since 1.15.3 (J) - 1.5.5 (WP)
5138 */
5139 $customer_pic = VikRequest::getString('pic', '', 'request');
5140 $customer_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
5141 if (is_array($customer_pic_img) && !empty($customer_pic_img['name'])) {
5142 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($customer_pic_img['name'])));
5143 $src = $customer_pic_img['tmp_name'];
5144 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
5145 $j = "";
5146 if (is_file($dest.$filename)) {
5147 $j = rand(1, 99999);
5148 while (is_file($dest . $j .$filename)) {
5149 $j++;
5150 }
5151 }
5152 $finaldest = $dest . $j . $filename;
5153 $check = getimagesize($customer_pic_img['tmp_name']);
5154 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
5155 if (VikBooking::uploadFile($src, $finaldest)) {
5156 $customer_pic = $j . $filename;
5157 } else {
5158 VikError::raiseWarning('', 'Error while uploading image');
5159 }
5160 } else {
5161 VikError::raiseWarning('', 'Uploaded file is not an Image');
5162 }
5163 }
5164
5165 // update customer object
5166 $new_customer = new stdClass;
5167 $new_customer->id = (int)$pwhere;
5168 $new_customer->first_name = $pfirst_name;
5169 $new_customer->last_name = $plast_name;
5170 $new_customer->email = $pemail;
5171 $new_customer->phone = $pphone;
5172 $new_customer->country = $pcountry;
5173 $new_customer->pin = $ppin;
5174 $new_customer->ujid = $pujid;
5175 $new_customer->address = $paddress;
5176 $new_customer->city = $pcity;
5177 $new_customer->zip = $pzip;
5178 $new_customer->state = $pstate;
5179 $new_customer->doctype = $pdoctype;
5180 $new_customer->docnum = $pdocnum;
5181 if (!empty($gimg)) {
5182 $new_customer->docimg = $gimg;
5183 }
5184 $new_customer->notes = $pnotes;
5185 $new_customer->ischannel = $pischannel;
5186 $new_customer->chdata = json_encode($chparams);
5187 $new_customer->company = $pcompany;
5188 $new_customer->vat = $pvat;
5189 $new_customer->gender = $pgender;
5190 $new_customer->bdate = $pbdate;
5191 $new_customer->pbirth = $ppbirth;
5192 $new_customer->fisccode = $pfisccode;
5193 $new_customer->pec = $ppec;
5194 $new_customer->recipcode = $precipcode;
5195 $new_customer->pic = $customer_pic;
5196 /**
5197 * We need to update the previous information stored through
5198 * the custom fields when making a reservation for/by this client.
5199 *
5200 * @since 1.13
5201 */
5202 $skip_prev_fields = array(
5203 'id',
5204 'ujid',
5205 'docimg',
5206 'ischannel',
5207 'chdata',
5208 'notes',
5209 );
5210 if (!empty($customer['cfields'])) {
5211 $custf_info = json_decode($customer['cfields'], true);
5212 foreach ($new_customer as $fname => $fnewval) {
5213 if (!isset($customer[$fname]) || in_array($fname, $skip_prev_fields)) {
5214 continue;
5215 }
5216 // seek for old value in custom fields submitted
5217 foreach ($custf_info as $k => $v) {
5218 if (!empty($customer[$fname]) && $v == $customer[$fname]) {
5219 // field found, replace it with the new value
5220 $custf_info[$k] = $fnewval;
5221 }
5222 }
5223 }
5224 // update value on db
5225 $new_customer->cfields = json_encode($custf_info);
5226 }
5227
5228 // trigger the customer before-update event
5229 $cpin->pluginCustomerSync($new_customer->id, 'update', (array)$new_customer, $before = true);
5230
5231 // update customer record
5232 $dbo->updateObject('#__vikbooking_customers', $new_customer, 'id');
5233
5234 // trigger the customer after-save event
5235 $cpin->pluginCustomerSync($new_customer->id, 'update', (array)$new_customer, $before = false);
5236
5237 // update all the bookings affected by this Customer ID as a sales channel
5238 $source_name = 'customer'.$pwhere.'_'.$pchname;
5239 if ($pischannel > 0) {
5240 $oid_clause = '';
5241 if ($customer['ischannel'] < 1) {
5242 //Was not a sales channel but now it is, so update all his bookings
5243 $q = "SELECT `o`.`idorderota`, `co`.`idorder`
5244 FROM `#__vikbooking_customers_orders` AS `co`
5245 LEFT JOIN `#__vikbooking_orders` AS `o` ON `co`.`idorder`=`o`.`id`
5246 WHERE `co`.`idcustomer`=".$customer['id'].";";
5247 $dbo->setQuery($q);
5248 $all_bids = $dbo->loadAssocList();
5249 if ($all_bids) {
5250 $bids = array();
5251 foreach ($all_bids as $bid) {
5252 if (empty($idorderota) && !in_array($bid['idorder'], $bids)) {
5253 $bids[] = $bid['idorder'];
5254 }
5255 }
5256 if ($bids) {
5257 $oid_clause = " OR `id` IN (".implode(',', $bids).")";
5258 }
5259 }
5260 }
5261 $q = "UPDATE `#__vikbooking_orders` SET `channel`=".$dbo->quote($source_name)." WHERE `channel` LIKE 'customer".$pwhere."%'".$oid_clause.";";
5262 } else {
5263 $q = "UPDATE `#__vikbooking_orders` SET `channel`=NULL,`cmms`=NULL WHERE `channel` LIKE 'customer".$pwhere."%';";
5264 }
5265 $dbo->setQuery($q);
5266 $dbo->execute();
5267 //
5268 $mainframe->enqueueMessage(JText::translate('VBCUSTOMERSAVED'));
5269 } else {
5270 //email already exists
5271 $ex_customer = $dbo->loadAssoc();
5272 //check if coming from the Check-in view or not
5273 if (!empty($pcheckin) && !empty($pbid)) {
5274 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5275 /**
5276 * @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
5277 */
5278 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5279 //
5280 exit;
5281 } elseif (!empty($pgoto)) {
5282 // check if coming from a specific task
5283 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5284 $mainframe->redirect(base64_decode($pgoto));
5285 exit;
5286 } else {
5287 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>');
5288 $mainframe->redirect("index.php?option=com_vikbooking&task=editcustomer&cid[]=".$pwhere);
5289 exit;
5290 }
5291 }
5292 }
5293
5294 //check if coming from the Check-in view
5295 if (!empty($pcheckin) && !empty($pbid)) {
5296 /**
5297 * @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
5298 */
5299 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $pbid);
5300 exit;
5301 }
5302
5303 if ($stay) {
5304 $mainframe->redirect("index.php?option=com_vikbooking&task=editcustomer&cid[]=" . $pwhere . (!empty($pgoto) ? '&goto=' . $pgoto : ''));
5305 exit;
5306 }
5307
5308 // check if coming from a specific task
5309 if (!empty($pgoto)) {
5310 $mainframe->redirect(base64_decode($pgoto));
5311 exit;
5312 }
5313
5314 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5315 }
5316
5317 public function savecustomer() {
5318 if (!JSession::checkToken()) {
5319 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5320 }
5321 $dbo = JFactory::getDbo();
5322 $mainframe = JFactory::getApplication();
5323 $pfirst_name = VikRequest::getString('first_name', '', 'request');
5324 $plast_name = VikRequest::getString('last_name', '', 'request');
5325 $pcompany = VikRequest::getString('company', '', 'request');
5326 $pvat = VikRequest::getString('vat', '', 'request');
5327 $pemail = VikRequest::getString('email', '', 'request');
5328 $pphone = VikRequest::getString('phone', '', 'request');
5329 $pcountry = VikRequest::getString('country', '', 'request');
5330 $pstate = VikRequest::getString('state', '', 'request');
5331 $ppin = VikRequest::getString('pin', '', 'request');
5332 $pujid = VikRequest::getInt('ujid', '', 'request');
5333 $paddress = VikRequest::getString('address', '', 'request');
5334 $pcity = VikRequest::getString('city', '', 'request');
5335 $pzip = VikRequest::getString('zip', '', 'request');
5336 $pfisccode = VikRequest::getString('fisccode', '', 'request');
5337 $ppec = VikRequest::getString('pec', '', 'request');
5338 $precipcode = VikRequest::getString('recipcode', '', 'request');
5339 $pgender = VikRequest::getString('gender', '', 'request');
5340 $pgender = in_array($pgender, array('F', 'M')) ? $pgender : '';
5341 $pbdate = VikRequest::getString('bdate', '', 'request');
5342 $ppbirth = VikRequest::getString('pbirth', '', 'request');
5343 $pdoctype = VikRequest::getString('doctype', '', 'request');
5344 $pdocnum = VikRequest::getString('docnum', '', 'request');
5345 $pnotes = VikRequest::getString('notes', '', 'request');
5346 $pscandocimg = VikRequest::getString('scandocimg', '', 'request');
5347 $pischannel = VikRequest::getInt('ischannel', '', 'request');
5348 $pcommission = VikRequest::getFloat('commission', '', 'request');
5349 $pcalccmmon = VikRequest::getInt('calccmmon', '', 'request');
5350 $papplycmmon = VikRequest::getInt('applycmmon', '', 'request');
5351 $pchname = VikRequest::getString('chname', '', 'request');
5352 $pchcolor = VikRequest::getString('chcolor', '', 'request');
5353 $ptmpl = VikRequest::getString('tmpl', '', 'request');
5354 $pcheckin = VikRequest::getInt('checkin', '', 'request');
5355 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
5356 $pbid = VikRequest::getInt('bid', '', 'request');
5357 if (!empty($pfirst_name) && !empty($plast_name) && !empty($pemail)) {
5358 $cpin = VikBooking::getCPinIstance();
5359 /**
5360 * Existing customers are recognized by equal first name, last name and email address.
5361 *
5362 * @since 1.3.0
5363 */
5364 $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;";
5365 $dbo->setQuery($q);
5366 $dbo->execute();
5367 if ($dbo->getNumRows() == 0) {
5368 if (empty($ppin)) {
5369 $ppin = $cpin->generateUniquePin();
5370 } elseif ($cpin->pinExists($ppin)) {
5371 $ppin = $cpin->generateUniquePin();
5372 }
5373 //file upload
5374 jimport('joomla.filesystem.file');
5375 $pimg = VikRequest::getVar('docimg', null, 'files', 'array');
5376 $gimg = "";
5377 if (isset($pimg) && strlen(trim($pimg['name']))) {
5378 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($pimg['name'])));
5379 $src = $pimg['tmp_name'];
5380 $dest = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans'.DIRECTORY_SEPARATOR;
5381 $j = "";
5382 if (file_exists($dest.$filename)) {
5383 $j = rand(171, 1717);
5384 while (file_exists($dest.$j.$filename)) {
5385 $j++;
5386 }
5387 }
5388 $finaldest = $dest.$j.$filename;
5389 $check = getimagesize($pimg['tmp_name']);
5390 if (($check[2] & imagetypes()) || preg_match("/application\/(zip|pdf)$/", $pimg['type'])) {
5391 if (VikBooking::uploadFile($src, $finaldest)) {
5392 $gimg = $j.$filename;
5393 } else {
5394 VikError::raiseWarning('', 'Error while uploading image');
5395 }
5396 } else {
5397 VikError::raiseWarning('', 'Uploaded file is not an Image');
5398 }
5399 } elseif (!empty($pscandocimg)) {
5400 $gimg = $pscandocimg;
5401 }
5402 //
5403 $pischannel = $pischannel > 0 ? 1 : 0;
5404 $pcalccmmon = $pcalccmmon > 0 ? 1 : 0;
5405 $papplycmmon = $papplycmmon > 0 ? 1 : 0;
5406 $pchname = str_replace(' ', '', trim($pchname));
5407 $pchname = strlen($pchname) <= 0 && $pischannel > 0 ? str_replace(' ', '', trim($pfirst_name.' '.$plast_name)) : $pchname;
5408 $chparams = array(
5409 'commission' => ($pcommission > 0.00 ? $pcommission : 0),
5410 'calccmmon' => $pcalccmmon,
5411 'applycmmon' => $papplycmmon,
5412 'chcolor' => $pchcolor,
5413 'chname' => $pchname
5414 );
5415
5416 /**
5417 * Customer profile picture (URL or uploaded file).
5418 *
5419 * @since 1.15.3 (J) - 1.5.5 (WP)
5420 */
5421 $customer_pic = VikRequest::getString('pic', '', 'request');
5422 $customer_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
5423 if (is_array($customer_pic_img) && !empty($customer_pic_img['name'])) {
5424 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($customer_pic_img['name'])));
5425 $src = $customer_pic_img['tmp_name'];
5426 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
5427 $j = "";
5428 if (is_file($dest.$filename)) {
5429 $j = rand(1, 99999);
5430 while (is_file($dest . $j .$filename)) {
5431 $j++;
5432 }
5433 }
5434 $finaldest = $dest . $j . $filename;
5435 $check = getimagesize($customer_pic_img['tmp_name']);
5436 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
5437 if (VikBooking::uploadFile($src, $finaldest)) {
5438 $customer_pic = $j . $filename;
5439 } else {
5440 VikError::raiseWarning('', 'Error while uploading image');
5441 }
5442 } else {
5443 VikError::raiseWarning('', 'Uploaded file is not an Image');
5444 }
5445 }
5446
5447 // build customer record
5448 $customer_obj = new stdClass;
5449 $customer_obj->first_name = $pfirst_name;
5450 $customer_obj->last_name = $plast_name;
5451 $customer_obj->email = $pemail;
5452 $customer_obj->phone = $pphone;
5453 $customer_obj->country = $pcountry;
5454 $customer_obj->pin = $ppin;
5455 $customer_obj->ujid = $pujid;
5456 $customer_obj->address = $paddress;
5457 $customer_obj->city = $pcity;
5458 $customer_obj->zip = $pzip;
5459 $customer_obj->state = $pstate;
5460 $customer_obj->doctype = $pdoctype;
5461 $customer_obj->docnum = $pdocnum;
5462 $customer_obj->docimg = $gimg;
5463 $customer_obj->notes = $pnotes;
5464 $customer_obj->ischannel = $pischannel;
5465 $customer_obj->chdata = json_encode($chparams);
5466 $customer_obj->company = $pcompany;
5467 $customer_obj->vat = $pvat;
5468 $customer_obj->gender = $pgender;
5469 $customer_obj->bdate = $pbdate;
5470 $customer_obj->pbirth = $ppbirth;
5471 $customer_obj->fisccode = $pfisccode;
5472 $customer_obj->pec = $ppec;
5473 $customer_obj->recipcode = $precipcode;
5474 $customer_obj->pic = !empty($customer_pic) ? $customer_pic : null;
5475
5476 // trigger the customer before-insert event
5477 $cpin->pluginCustomerSync(0, 'insert', (array)$customer_obj, $before = true);
5478
5479 // insert the new customer record
5480 $dbo->insertObject('#__vikbooking_customers', $customer_obj, 'id');
5481 $lid = isset($customer_obj->id) ? $customer_obj->id : null;
5482
5483 // trigger the customer after-save event
5484 $cpin->pluginCustomerSync($lid, 'insert', (array)$customer_obj, $before = false);
5485
5486 if (!empty($lid)) {
5487 $mainframe->enqueueMessage(JText::translate('VBCUSTOMERSAVED'));
5488 //check if coming from the Check-in view
5489 if (!empty($pcheckin) && !empty($pbid)) {
5490 $cpin->setNewPin($ppin);
5491 $cpin->setNewCustomerId($lid);
5492 $cpin->saveCustomerBooking($pbid);
5493 /**
5494 * @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
5495 */
5496 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5497 //
5498 exit;
5499 }
5500 // check if coming from a specific task
5501 if (!empty($pgoto) && !empty($pbid)) {
5502 $cpin->setNewPin($ppin);
5503 $cpin->setNewCustomerId($lid);
5504 $cpin->saveCustomerBooking($pbid);
5505 $mainframe->redirect(base64_decode($pgoto));
5506 exit;
5507 }
5508 }
5509 } else {
5510 //email already exists
5511 $ex_customer = $dbo->loadAssoc();
5512 //check if coming from the Check-in view or not
5513 if (!empty($pcheckin) && !empty($pbid)) {
5514 $cpin->setNewPin($ex_customer['pin']);
5515 $cpin->setNewCustomerId($ex_customer['id']);
5516 $cpin->saveCustomerBooking($pbid);
5517 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5518 /**
5519 * @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
5520 */
5521 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5522 //
5523 exit;
5524 } elseif (!empty($pgoto) && !empty($pbid)) {
5525 // check if coming from a specific task
5526 $cpin->setNewPin($ex_customer['pin']);
5527 $cpin->setNewCustomerId($ex_customer['id']);
5528 $cpin->saveCustomerBooking($pbid);
5529 VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5530 $mainframe->redirect(base64_decode($pgoto));
5531 exit;
5532 } else {
5533 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>');
5534 }
5535 }
5536 }
5537 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5538 }
5539
5540 public function customers() {
5541 VikBookingHelper::printHeader("22");
5542
5543 VikRequest::setVar('view', VikRequest::getCmd('view', 'customers'));
5544
5545 parent::display();
5546
5547 if (VikBooking::showFooter()) {
5548 VikBookingHelper::printFooter();
5549 }
5550 }
5551
5552 public function newcustomer() {
5553 VikBookingHelper::printHeader("22");
5554
5555 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomer'));
5556
5557 parent::display();
5558
5559 if (VikBooking::showFooter()) {
5560 VikBookingHelper::printFooter();
5561 }
5562 }
5563
5564 public function editcustomer() {
5565 VikBookingHelper::printHeader("22");
5566
5567 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomer'));
5568
5569 parent::display();
5570
5571 if (VikBooking::showFooter()) {
5572 VikBookingHelper::printFooter();
5573 }
5574 }
5575
5576 public function removecustomers()
5577 {
5578 if (!JSession::checkToken()) {
5579 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5580 }
5581
5582 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
5583 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5584 }
5585
5586 $ids = VikRequest::getVar('cid', array(0));
5587 if ($ids) {
5588 $dbo = JFactory::getDBO();
5589 $cpin = VikBooking::getCPinIstance();
5590 foreach ($ids as $d) {
5591 $cpin->pluginCustomerSync($d, 'delete');
5592 $q = "DELETE FROM `#__vikbooking_customers` WHERE `id`=".(int)$d.";";
5593 $dbo->setQuery($q);
5594 $dbo->execute();
5595 }
5596 }
5597 $mainframe = JFactory::getApplication();
5598 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5599 }
5600
5601 public function restrictions() {
5602 VikBookingHelper::printHeader("restrictions");
5603
5604 VikRequest::setVar('view', VikRequest::getCmd('view', 'restrictions'));
5605
5606 parent::display();
5607
5608 if (VikBooking::showFooter()) {
5609 VikBookingHelper::printFooter();
5610 }
5611 }
5612
5613 public function newrestriction() {
5614 VikBookingHelper::printHeader("restrictions");
5615
5616 VikRequest::setVar('view', VikRequest::getCmd('view', 'managerestriction'));
5617
5618 parent::display();
5619
5620 if (VikBooking::showFooter()) {
5621 VikBookingHelper::printFooter();
5622 }
5623 }
5624
5625 public function editrestriction() {
5626 VikBookingHelper::printHeader("restrictions");
5627
5628 VikRequest::setVar('view', VikRequest::getCmd('view', 'managerestriction'));
5629
5630 parent::display();
5631
5632 if (VikBooking::showFooter()) {
5633 VikBookingHelper::printFooter();
5634 }
5635 }
5636
5637 public function createrestriction()
5638 {
5639 if (!JSession::checkToken()) {
5640 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5641 }
5642
5643 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
5644 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5645 }
5646
5647 $dbo = JFactory::getDBO();
5648 $session = JFactory::getSession();
5649 $mainframe = JFactory::getApplication();
5650 $updforvcm = $session->get('vbVcmRatesUpd', '');
5651 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
5652 $pname = VikRequest::getString('name', '', 'request');
5653 $pmonth = VikRequest::getInt('month', '', 'request');
5654 $pmonth = empty($pmonth) ? 0 : $pmonth;
5655 $pname = empty($pname) ? 'Restriction '.$pmonth : $pname;
5656 $pdfrom = VikRequest::getString('dfrom', '', 'request');
5657 $pdto = VikRequest::getString('dto', '', 'request');
5658 $pwday = VikRequest::getString('wday', '', 'request');
5659 $pwdaytwo = VikRequest::getString('wdaytwo', '', 'request');
5660 $pwdaytwo = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday == $pwdaytwo ? '' : $pwdaytwo;
5661 $pcomboa = VikRequest::getString('comboa', '', 'request');
5662 $pcomboa = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboa : '';
5663 $pcombob = VikRequest::getString('combob', '', 'request');
5664 $pcombob = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombob : '';
5665 $pcomboc = VikRequest::getString('comboc', '', 'request');
5666 $pcomboc = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboc : '';
5667 $pcombod = VikRequest::getString('combod', '', 'request');
5668 $pcombod = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombod : '';
5669 $combostr = '';
5670 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboa) ? $pcomboa.':' : ':';
5671 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombob) ? $pcombob.':' : ':';
5672 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboc) ? $pcomboc.':' : ':';
5673 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombod) ? $pcombod : '';
5674 $pminlos = VikRequest::getInt('minlos', '', 'request');
5675 $pminlos = $pminlos < 1 ? 1 : $pminlos;
5676 $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
5677 $pmaxlos = empty($pmaxlos) ? 0 : $pmaxlos;
5678 $pmultiplyminlos = VikRequest::getString('multiplyminlos', '', 'request');
5679 $pmultiplyminlos = empty($pmultiplyminlos) ? 0 : 1;
5680 $pallrooms = VikRequest::getString('allrooms', '', 'request');
5681 $pallrooms = $pallrooms == "1" ? 1 : 0;
5682 $pidrooms = VikRequest::getVar('idrooms', array(0));
5683 $ridr = '';
5684 $roomidsforsess = array();
5685 if (!empty($pidrooms) && @count($pidrooms) && $pallrooms == 0) {
5686 foreach ($pidrooms as $idr) {
5687 if (empty($idr)) {
5688 continue;
5689 }
5690 $ridr .= '-'.$idr.'-;';
5691 $roomidsforsess[] = (int)$idr;
5692 }
5693 } elseif ($pallrooms > 0) {
5694 $q = "SELECT `id` FROM `#__vikbooking_rooms`;";
5695 $dbo->setQuery($q);
5696 $dbo->execute();
5697 if ($dbo->getNumRows() > 0) {
5698 $fetchids = $dbo->loadAssocList();
5699 foreach ($fetchids as $fetchid) {
5700 $roomidsforsess[] = (int)$fetchid['id'];
5701 }
5702 }
5703 }
5704 $pcta = VikRequest::getInt('cta', '', 'request');
5705 $pctd = VikRequest::getInt('ctd', '', 'request');
5706 $pctad = VikRequest::getVar('ctad', array());
5707 $pctdd = VikRequest::getVar('ctdd', array());
5708 if ($pminlos == 1 && strlen($pwday) == 0 && empty($pctad) && empty($pctdd) && $pmaxlos < 1) {
5709 // VBO 1.11 - we now allow restrictions with just 1 night of stay
5710 // VikError::raiseWarning('', JText::translate('VBUSELESSRESTRICTION'));
5711 // $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5712 // exit;
5713 }
5714
5715 //check if there are restrictions for this month
5716 if ($pmonth > 0) {
5717 $q = "SELECT `id` FROM `#__vikbooking_restrictions` WHERE `month`='".$pmonth."';";
5718 $dbo->setQuery($q);
5719 $dbo->execute();
5720 if ($dbo->getNumRows() > 0) {
5721 VikError::raiseWarning('', JText::translate('VBRESTRICTIONMONTHEXISTS'));
5722 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5723 exit;
5724 }
5725 $pdfrom = 0;
5726 $pdto = 0;
5727 } else {
5728 //dates range
5729 if (empty($pdfrom) || empty($pdto)) {
5730 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
5731 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5732 exit;
5733 } else {
5734 $housto = $pdfrom == $pdto ? 23 : 0;
5735 $minsto = $pdfrom == $pdto ? 59 : 0;
5736 $secsto = $pdfrom == $pdto ? 59 : 0;
5737 $pdfrom = VikBooking::getDateTimestamp($pdfrom, 0, 0);
5738 $pdto = VikBooking::getDateTimestamp($pdto, $housto, $minsto, $secsto);
5739 }
5740 if ($pdfrom > $pdto) {
5741 // invalid dates in the past
5742 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
5743 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5744 exit;
5745 }
5746 }
5747 //CTA and CTD
5748 $setcta = array();
5749 $setctd = array();
5750 if ($pcta > 0 && count($pctad) > 0) {
5751 foreach ($pctad as $ctwd) {
5752 if (strlen($ctwd)) {
5753 $setcta[] = '-'.(int)$ctwd.'-';
5754 }
5755 }
5756 }
5757 if ($pctd > 0 && count($pctdd) > 0) {
5758 foreach ($pctdd as $ctwd) {
5759 if (strlen($ctwd)) {
5760 $setctd[] = '-'.(int)$ctwd.'-';
5761 }
5762 }
5763 }
5764 //
5765 //update session values
5766 if (!($pdfrom > 0)) {
5767 $attemptyear = (int)date('Y');
5768 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
5769 if ($attemptfrom < time()) {
5770 $attemptyear++;
5771 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
5772 }
5773 $attemptto = mktime(0, 0, 0, $pmonth, date('t', $attemptfrom), $attemptyear);
5774 } else {
5775 $attemptfrom = $pdfrom;
5776 $attemptto = $pdto;
5777 }
5778 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
5779 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
5780 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $attemptfrom ? $attemptfrom : $updforvcm['dfrom'];
5781 } else {
5782 $updforvcm['dfrom'] = $attemptfrom;
5783 }
5784 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
5785 $updforvcm['dto'] = $updforvcm['dto'] < $attemptto ? $attemptto : $updforvcm['dto'];
5786 } else {
5787 $updforvcm['dto'] = $attemptto;
5788 }
5789 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
5790 foreach ($roomidsforsess as $rid) {
5791 if (!in_array($rid, $updforvcm['rooms'])) {
5792 $updforvcm['rooms'][] = $rid;
5793 }
5794 }
5795 } else {
5796 $updforvcm['rooms'] = $roomidsforsess;
5797 }
5798 if (!array_key_exists('rplans', $updforvcm) || !is_array($updforvcm['rplans'])) {
5799 $updforvcm['rplans'] = array();
5800 }
5801 $session->set('vbVcmRatesUpd', $updforvcm);
5802 //
5803 $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").");";
5804 $dbo->setQuery($q);
5805 $dbo->execute();
5806 $lid = $dbo->insertid();
5807 if (!empty($lid)) {
5808 /**
5809 * Repeat restriction on the selected week days until the limit
5810 *
5811 * @since 1.13
5812 */
5813 $prepeat = VikRequest::getInt('repeat', 0, 'request');
5814 $prepeatuntil = VikRequest::getString('repeatuntil', '', 'request');
5815 if ($prepeat > 0 && !empty($prepeatuntil) && $pdfrom > 0 && $pdto > 0) {
5816 $repeat_intervals = array();
5817 $start = getdate($pdfrom);
5818 $end = getdate($pdto);
5819 $wdays = array();
5820 while ($start[0] <= $end[0]) {
5821 // push requested week day
5822 array_push($wdays, $start['wday']);
5823 // next day
5824 $start = getdate(mktime($start['hours'], $start['minutes'], $start['seconds'], $start['mon'], ($start['mday'] + 1), $start['year']));
5825 }
5826 $dtuntil = VikBooking::getDateTimestamp($prepeatuntil, 23, 59, 59);
5827 if (count($wdays) < 7 && $dtuntil > $pdto) {
5828 // increment end date for the repeat
5829 $end = getdate(mktime($end['hours'], $end['minutes'], $end['seconds'], $end['mon'], ($end['mday'] + 1), $end['year']));
5830 //
5831 $until_info = getdate($dtuntil);
5832 $interval = array();
5833 while ($end[0] <= $until_info[0]) {
5834 if (in_array($end['wday'], $wdays)) {
5835 if (!isset($interval['from'])) {
5836 $interval['from'] = $end[0];
5837 }
5838 $interval['to'] = $end[0];
5839 } else {
5840 if (isset($interval['from'])) {
5841 // append interval
5842 array_push($repeat_intervals, $interval);
5843 // reset interval
5844 $interval = array();
5845 }
5846 }
5847 // next day
5848 $end = getdate(mktime($end['hours'], $end['minutes'], $end['seconds'], $end['mon'], ($end['mday'] + 1), $end['year']));
5849 }
5850 if (isset($interval['from'])) {
5851 // append last hanging interval
5852 array_push($repeat_intervals, $interval);
5853 }
5854 if (count($repeat_intervals)) {
5855 // create the repeated records for the calculated intervals
5856 $repeat_count = 2;
5857 foreach ($repeat_intervals as $rp) {
5858 if (date('Y-m-d', $rp['from']) == date('Y-m-d', $rp['to'])) {
5859 // adjust time in case of equal dates (1 single day restriction)
5860 $rpfrom = getdate($rp['from']);
5861 $rpto = getdate($rp['to']);
5862 $rp['from'] = mktime(0, 0, 0, $rpfrom['mon'], $rpfrom['mday'], $rpfrom['year']);
5863 /**
5864 * The end date of the restriction must cover the whole day until 23:59:59.
5865 *
5866 * @since 1.15.4 (J) - 1.5.4 (WP)
5867 */
5868 $rp['to'] = mktime(23, 59, 59, $rpto['mon'], $rpto['mday'], $rpto['year']);
5869 }
5870 // adjust name
5871 $restr_rp_name = $pname . " #{$repeat_count}";
5872 //
5873 $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").");";
5874 $dbo->setQuery($q);
5875 $dbo->execute();
5876 $lid = $dbo->insertid();
5877 if (!empty($lid)) {
5878 $repeat_count++;
5879 }
5880 }
5881 }
5882 }
5883 }
5884 //
5885 $mainframe->enqueueMessage(JText::translate('VBRESTRICTIONSAVED'));
5886 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
5887 } else {
5888 VikError::raiseWarning('', 'Error while saving');
5889 $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5890 }
5891 }
5892
5893 public function updaterestriction()
5894 {
5895 if (!JSession::checkToken()) {
5896 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5897 }
5898
5899 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5900 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5901 }
5902
5903 $dbo = JFactory::getDBO();
5904 $session = JFactory::getSession();
5905 $mainframe = JFactory::getApplication();
5906 $updforvcm = $session->get('vbVcmRatesUpd', '');
5907 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
5908 $pwhere = VikRequest::getInt('where', '', 'request');
5909 $pname = VikRequest::getString('name', '', 'request');
5910 $pmonth = VikRequest::getInt('month', '', 'request');
5911 $pmonth = empty($pmonth) ? 0 : $pmonth;
5912 $pname = empty($pname) ? 'Restriction '.$pmonth : $pname;
5913 $pdfrom = VikRequest::getString('dfrom', '', 'request');
5914 $pdto = VikRequest::getString('dto', '', 'request');
5915 $pwday = VikRequest::getString('wday', '', 'request');
5916 $pwdaytwo = VikRequest::getString('wdaytwo', '', 'request');
5917 $pwdaytwo = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday == $pwdaytwo ? '' : $pwdaytwo;
5918 $pcomboa = VikRequest::getString('comboa', '', 'request');
5919 $pcomboa = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboa : '';
5920 $pcombob = VikRequest::getString('combob', '', 'request');
5921 $pcombob = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombob : '';
5922 $pcomboc = VikRequest::getString('comboc', '', 'request');
5923 $pcomboc = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboc : '';
5924 $pcombod = VikRequest::getString('combod', '', 'request');
5925 $pcombod = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombod : '';
5926 $combostr = '';
5927 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboa) ? $pcomboa.':' : ':';
5928 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombob) ? $pcombob.':' : ':';
5929 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboc) ? $pcomboc.':' : ':';
5930 $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombod) ? $pcombod : '';
5931 $pminlos = VikRequest::getInt('minlos', '', 'request');
5932 $pminlos = $pminlos < 1 ? 1 : $pminlos;
5933 $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
5934 $pmaxlos = empty($pmaxlos) ? 0 : $pmaxlos;
5935 $pmultiplyminlos = VikRequest::getString('multiplyminlos', '', 'request');
5936 $pmultiplyminlos = empty($pmultiplyminlos) ? 0 : 1;
5937 $pallrooms = VikRequest::getString('allrooms', '', 'request');
5938 $pallrooms = $pallrooms == "1" ? 1 : 0;
5939 $pidrooms = VikRequest::getVar('idrooms', array(0));
5940 $ridr = '';
5941 $roomidsforsess = array();
5942 if (!empty($pidrooms) && @count($pidrooms) && $pallrooms == 0) {
5943 foreach ($pidrooms as $idr) {
5944 if (empty($idr)) {
5945 continue;
5946 }
5947 $ridr .= '-'.$idr.'-;';
5948 $roomidsforsess[] = (int)$idr;
5949 }
5950 } elseif ($pallrooms > 0) {
5951 $q = "SELECT `id` FROM `#__vikbooking_rooms`;";
5952 $dbo->setQuery($q);
5953 $dbo->execute();
5954 if ($dbo->getNumRows() > 0) {
5955 $fetchids = $dbo->loadAssocList();
5956 foreach ($fetchids as $fetchid) {
5957 $roomidsforsess[] = (int)$fetchid['id'];
5958 }
5959 }
5960 }
5961 $pcta = VikRequest::getInt('cta', '', 'request');
5962 $pctd = VikRequest::getInt('ctd', '', 'request');
5963 $pctad = VikRequest::getVar('ctad', array());
5964 $pctdd = VikRequest::getVar('ctdd', array());
5965 if ($pminlos == 1 && strlen($pwday) == 0 && empty($pctad) && empty($pctdd) && $pmaxlos < 1) {
5966 // VBO 1.11 - we now allow restrictions with just 1 night of stay
5967 // VikError::raiseWarning('', JText::translate('VBUSELESSRESTRICTION'));
5968 // $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
5969 // exit;
5970 }
5971 //check if there are restrictions for this month
5972 if ($pmonth > 0) {
5973 $q = "SELECT `id` FROM `#__vikbooking_restrictions` WHERE `month`='".$pmonth."' AND `id`!='".$pwhere."';";
5974 $dbo->setQuery($q);
5975 $dbo->execute();
5976 if ($dbo->getNumRows() > 0) {
5977 VikError::raiseWarning('', JText::translate('VBRESTRICTIONMONTHEXISTS'));
5978 $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
5979 exit;
5980 }
5981 $pdfrom = 0;
5982 $pdto = 0;
5983 } else {
5984 //dates range
5985 if (empty($pdfrom) || empty($pdto)) {
5986 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
5987 $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
5988 exit;
5989 } else {
5990 $housto = $pdfrom == $pdto ? 23 : 0;
5991 $minsto = $pdfrom == $pdto ? 59 : 0;
5992 $secsto = $pdfrom == $pdto ? 59 : 0;
5993 $pdfrom = VikBooking::getDateTimestamp($pdfrom, 0, 0);
5994 $pdto = VikBooking::getDateTimestamp($pdto, $housto, $minsto, $secsto);
5995 }
5996 if ($pdfrom > $pdto) {
5997 // invalid dates in the past
5998 VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
5999 $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6000 exit;
6001 }
6002 }
6003 //CTA and CTD
6004 $setcta = array();
6005 $setctd = array();
6006 if ($pcta > 0 && count($pctad) > 0) {
6007 foreach ($pctad as $ctwd) {
6008 if (strlen($ctwd)) {
6009 $setcta[] = '-'.(int)$ctwd.'-';
6010 }
6011 }
6012 }
6013 if ($pctd > 0 && count($pctdd) > 0) {
6014 foreach ($pctdd as $ctwd) {
6015 if (strlen($ctwd)) {
6016 $setctd[] = '-'.(int)$ctwd.'-';
6017 }
6018 }
6019 }
6020 //
6021 //update session values
6022 if (!($pdfrom > 0)) {
6023 $attemptyear = (int)date('Y');
6024 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
6025 if ($attemptfrom < time()) {
6026 $attemptyear++;
6027 $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
6028 }
6029 $attemptto = mktime(0, 0, 0, $pmonth, date('t', $attemptfrom), $attemptyear);
6030 } else {
6031 $attemptfrom = $pdfrom;
6032 $attemptto = $pdto;
6033 }
6034 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
6035 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
6036 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $attemptfrom ? $attemptfrom : $updforvcm['dfrom'];
6037 } else {
6038 $updforvcm['dfrom'] = $attemptfrom;
6039 }
6040 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
6041 $updforvcm['dto'] = $updforvcm['dto'] < $attemptto ? $attemptto : $updforvcm['dto'];
6042 } else {
6043 $updforvcm['dto'] = $attemptto;
6044 }
6045 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
6046 foreach ($roomidsforsess as $rid) {
6047 if (!in_array($rid, $updforvcm['rooms'])) {
6048 $updforvcm['rooms'][] = $rid;
6049 }
6050 }
6051 } else {
6052 $updforvcm['rooms'] = $roomidsforsess;
6053 }
6054 if (!array_key_exists('rplans', $updforvcm) || !is_array($updforvcm['rplans'])) {
6055 $updforvcm['rplans'] = array();
6056 }
6057 $session->set('vbVcmRatesUpd', $updforvcm);
6058 //
6059 $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."';";
6060 $dbo->setQuery($q);
6061 $dbo->execute();
6062 $mainframe->enqueueMessage(JText::translate('VBRESTRICTIONSAVED'));
6063 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
6064 }
6065
6066 public function removerestrictions()
6067 {
6068 if (!JSession::checkToken()) {
6069 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6070 }
6071
6072 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6073 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6074 }
6075
6076 $ids = VikRequest::getVar('cid', array(0));
6077 if ($ids) {
6078 $dbo = JFactory::getDBO();
6079 foreach ($ids as $d) {
6080 $q = "DELETE FROM `#__vikbooking_restrictions` WHERE `id`=".(int)$d.";";
6081 $dbo->setQuery($q);
6082 $dbo->execute();
6083 }
6084 }
6085 $mainframe = JFactory::getApplication();
6086 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
6087 }
6088
6089 public function prices() {
6090 VikBookingHelper::printHeader("1");
6091
6092 VikRequest::setVar('view', VikRequest::getCmd('view', 'prices'));
6093
6094 parent::display();
6095
6096 if (VikBooking::showFooter()) {
6097 VikBookingHelper::printFooter();
6098 }
6099 }
6100
6101 public function newprice() {
6102 VikBookingHelper::printHeader("1");
6103
6104 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageprice'));
6105
6106 parent::display();
6107
6108 if (VikBooking::showFooter()) {
6109 VikBookingHelper::printFooter();
6110 }
6111 }
6112
6113 public function editprice() {
6114 VikBookingHelper::printHeader("1");
6115
6116 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageprice'));
6117
6118 parent::display();
6119
6120 if (VikBooking::showFooter()) {
6121 VikBookingHelper::printFooter();
6122 }
6123 }
6124
6125 public function createprice()
6126 {
6127 if (!JSession::checkToken()) {
6128 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6129 }
6130
6131 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6132 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6133 }
6134
6135 $this->do_createprice();
6136 }
6137
6138 public function createprice_new()
6139 {
6140 if (!JSession::checkToken()) {
6141 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6142 }
6143
6144 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6145 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6146 }
6147
6148 $this->do_createprice(true);
6149 }
6150
6151 private function do_createprice($new = false)
6152 {
6153 $app = JFactory::getApplication();
6154 $dbo = JFactory::getDbo();
6155
6156 $pprice = VikRequest::getString('price', '', 'request');
6157 $pattr = VikRequest::getString('attr', '', 'request');
6158 $ppraliq = VikRequest::getInt('praliq', '', 'request');
6159 $pmeal_plans = (array)VikRequest::getVar('meal_plans', []);
6160 $pbreakfast_included = in_array('breakfast', $pmeal_plans) ? 1 : 0;
6161 $pfree_cancellation = VikRequest::getInt('free_cancellation', 0, 'request');
6162 $pfree_cancellation = $pfree_cancellation == 1 ? 1 : 0;
6163 $pcanc_deadline = VikRequest::getInt('canc_deadline', '', 'request');
6164 $pminlos = VikRequest::getInt('minlos', 0, 'request');
6165 $pminlos = $pminlos < 0 ? 0 : $pminlos;
6166 $pminhadv = VikRequest::getInt('minhadv', '', 'request');
6167 $pminhadv = $pminhadv < 0 ? 0 : $pminhadv;
6168 $pcanc_policy = VikRequest::getString('canc_policy', '', 'request', VIKREQUEST_ALLOWHTML);
6169
6170 $is_derived = $app->input->getInt('is_derived', 0);
6171 $derived_id = $app->input->getUInt('derived_id', 0);
6172 $derived_data = $app->input->get('derived_data', [], 'array');
6173
6174 $parent_id = 0;
6175 $derived_info = null;
6176
6177 if ($is_derived && $derived_id && $derived_data) {
6178 $parent_id = $derived_id;
6179 $derived_info = $derived_data;
6180 $derived_info['mode'] = ($derived_info['mode'] ?? '') == 'charge' ? 'charge' : 'discount';
6181 $derived_info['type'] = ($derived_info['type'] ?? '') == 'absolute' ? 'absolute' : 'percent';
6182 $derived_info['value'] = (float) ($derived_info['value'] ?? 0);
6183 $derived_info['follow_restr'] = (int) ($derived_info['follow_restr'] ?? 1);
6184 if (!$derived_info['value']) {
6185 $parent_id = 0;
6186 $derived_info = null;
6187 }
6188 }
6189
6190 if (!empty($pprice)) {
6191 $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') . ");";
6192 $dbo->setQuery($q);
6193 $dbo->execute();
6194
6195 $new_rplan_id = $dbo->insertid();
6196
6197 /**
6198 * Allow to populate base rates for newly created rate plan for all room types using the parent rate.
6199 *
6200 * @since 1.18.6 (J) - 1.8.6 (WP)
6201 */
6202 if ($app->input->getBool('set_derived_rates', false) && $is_derived && $derived_id && $derived_info) {
6203 // find all rooms with base rates defined for the parent rate plan
6204 $dbo->setQuery(
6205 $dbo->getQuery(true)
6206 ->select($dbo->qn('idroom'))
6207 ->from($dbo->qn('#__vikbooking_dispcost'))
6208 ->where($dbo->qn('idprice') . ' = ' . $derived_id)
6209 ->group($dbo->qn('idroom'))
6210 ->order($dbo->qn('idroom') . ' ASC')
6211 );
6212 $populateRoomIds = array_map('intval', $dbo->loadColumn());
6213
6214 // determine rates table range of nights of stays
6215 $fromNights = $pminlos ?: 1;
6216 $maxNights = $app->input->getUInt('set_max_nights') ?: $pminlos ?: 1;
6217 $maxNights = $maxNights < $fromNights ? $fromNights : $maxNights;
6218
6219 // fetch base rates for all the involved room types
6220 $dbo->setQuery(
6221 $dbo->getQuery(true)
6222 ->select([
6223 $dbo->qn('idroom'),
6224 $dbo->qn('days'),
6225 $dbo->qn('cost'),
6226 ])
6227 ->from($dbo->qn('#__vikbooking_dispcost'))
6228 ->where($dbo->qn('idroom') . ' IN (' . implode(', ', $populateRoomIds) . ')')
6229 ->where($dbo->qn('idprice') . ' = ' . $derived_id)
6230 ->order($dbo->qn('idroom') . ' ASC')
6231 ->order($dbo->qn('days') . ' ASC')
6232 );
6233 $roomBaseRates = $dbo->loadAssocList();
6234
6235 // iterate all rooms involved
6236 foreach ($populateRoomIds as $roomId) {
6237 // loop through the interval of nights of stay
6238 for ($n = $fromNights; $n <= $maxNights; $n++) {
6239 // fetch current room rate in parent rate plan
6240 $roomParentNightlyRate = 0;
6241 $roomParentExactRate = 0;
6242 foreach ($roomBaseRates as $roomBaseRate) {
6243 if ($roomBaseRate['idroom'] != $roomId) {
6244 // ignore room
6245 continue;
6246 }
6247 if (!$roomParentNightlyRate) {
6248 // set rate for the lowest number of nights of stay
6249 $roomParentNightlyRate = $roomBaseRate['cost'] / ($roomBaseRate['days'] ?: 1);
6250 }
6251 if ($roomBaseRate['days'] == $n) {
6252 // set room exact rate for this number of nights of stay
6253 $roomParentExactRate = $roomBaseRate['cost'];
6254 // do not proceed
6255 break;
6256 }
6257 }
6258
6259 if (!$roomParentNightlyRate) {
6260 // missing pricing information from parent rate plan
6261 continue;
6262 }
6263
6264 // determine the cost to apply for the newly created derived rate plan
6265 $nightlyDerivedRate = $roomParentExactRate ?: $roomParentNightlyRate;
6266
6267 // check how the new rate was derived
6268 if ($derived_info['mode'] == 'charge') {
6269 // increase rate
6270 if ($derived_info['type'] == 'absolute') {
6271 // fixed increase
6272 $nightlyDerivedRate += $derived_info['value'];
6273 } else {
6274 // percent increase
6275 $nightlyDerivedRate *= (100 + $derived_info['value']) / 100;
6276 }
6277 } else {
6278 // discount rate
6279 if ($derived_info['type'] == 'absolute') {
6280 // fixed discount
6281 $nightlyDerivedRate -= $derived_info['value'];
6282 } else {
6283 // percent discount
6284 $nightlyDerivedRate *= (100 - $derived_info['value']) / 100;
6285 }
6286 }
6287
6288 if (!$roomParentExactRate) {
6289 // multiply rate by number of nights of stay if started from the parent lowest number of nights
6290 $nightlyDerivedRate *= $n;
6291 }
6292
6293 // build new room base rate record
6294 $rateRecord = [
6295 'idroom' => $roomId,
6296 'days' => $n,
6297 'idprice' => $new_rplan_id,
6298 'cost' => round($nightlyDerivedRate, 2),
6299 ];
6300
6301 // cast to object
6302 $rateRecord = (object) $rateRecord;
6303
6304 // insert record
6305 $dbo->insertObject('#__vikbooking_dispcost', $rateRecord, 'id');
6306 }
6307 }
6308 }
6309 }
6310
6311 $app->redirect("index.php?option=com_vikbooking&task=" . ($new ? 'newprice' : 'prices'));
6312 $app->close();
6313 }
6314
6315 public function updateprice()
6316 {
6317 if (!JSession::checkToken()) {
6318 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6319 }
6320
6321 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6322 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6323 }
6324
6325 $this->do_updateprice();
6326 }
6327
6328 public function updatepricestay()
6329 {
6330 if (!JSession::checkToken()) {
6331 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6332 }
6333
6334 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6335 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6336 }
6337
6338 $this->do_updateprice(true);
6339 }
6340
6341 private function do_updateprice($stay = false)
6342 {
6343 $app = JFactory::getApplication();
6344 $dbo = JFactory::getDbo();
6345
6346 $pprice = VikRequest::getString('price', '', 'request');
6347 $pattr = VikRequest::getString('attr', '', 'request');
6348 $ppraliq = VikRequest::getInt('praliq', '', 'request');
6349 $pmeal_plans = (array)VikRequest::getVar('meal_plans', []);
6350 $pbreakfast_included = in_array('breakfast', $pmeal_plans) ? 1 : 0;
6351 $pfree_cancellation = VikRequest::getInt('free_cancellation', '', 'request');
6352 $pfree_cancellation = $pfree_cancellation == 1 ? 1 : 0;
6353 $pcanc_deadline = VikRequest::getInt('canc_deadline', '', 'request');
6354 $pminlos = VikRequest::getInt('minlos', 0, 'request');
6355 $pminlos = $pminlos < 0 ? 0 : $pminlos;
6356 $pminhadv = VikRequest::getInt('minhadv', '', 'request');
6357 $pminhadv = $pminhadv < 0 ? 0 : $pminhadv;
6358 $pcanc_policy = VikRequest::getString('canc_policy', '', 'request', VIKREQUEST_ALLOWHTML);
6359 $pwhereup = VikRequest::getInt('whereup', 0, 'request');
6360
6361 $is_derived = $app->input->getInt('is_derived', 0);
6362 $derived_id = $app->input->getUInt('derived_id', 0);
6363 $derived_data = $app->input->get('derived_data', [], 'array');
6364
6365 $parent_id = 0;
6366 $derived_info = null;
6367
6368 if ($is_derived && $derived_id && $derived_data) {
6369 $parent_id = $derived_id;
6370 $derived_info = $derived_data;
6371 $derived_info['mode'] = ($derived_info['mode'] ?? '') == 'charge' ? 'charge' : 'discount';
6372 $derived_info['type'] = ($derived_info['type'] ?? '') == 'absolute' ? 'absolute' : 'percent';
6373 $derived_info['value'] = (float) ($derived_info['value'] ?? 0);
6374 $derived_info['follow_restr'] = (int) ($derived_info['follow_restr'] ?? 1);
6375 if (!$derived_info['value']) {
6376 $parent_id = 0;
6377 $derived_info = null;
6378 }
6379 }
6380
6381 if (!empty($pprice) && $pwhereup) {
6382 $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) . ";";
6383 $dbo->setQuery($q);
6384 $dbo->execute();
6385 }
6386
6387 $app->redirect("index.php?option=com_vikbooking&task=" . ($stay ? 'editprice&cid[]=' . $pwhereup : 'prices'));
6388 $app->close();
6389 }
6390
6391 public function removeprice()
6392 {
6393 if (!JSession::checkToken()) {
6394 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6395 }
6396
6397 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6398 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6399 }
6400
6401 $ids = VikRequest::getVar('cid', array(0));
6402 if ($ids) {
6403 $dbo = JFactory::getDBO();
6404 foreach ($ids as $d) {
6405 $q = "DELETE FROM `#__vikbooking_prices` WHERE `id`=".$dbo->quote($d).";";
6406 $dbo->setQuery($q);
6407 $dbo->execute();
6408 $q = "DELETE FROM `#__vikbooking_dispcost` WHERE `idprice`=".intval($d).";";
6409 $dbo->setQuery($q);
6410 $dbo->execute();
6411 }
6412 }
6413 $mainframe = JFactory::getApplication();
6414 $mainframe->redirect("index.php?option=com_vikbooking&task=prices");
6415 }
6416
6417 public function iva() {
6418 VikBookingHelper::printHeader("2");
6419
6420 VikRequest::setVar('view', VikRequest::getCmd('view', 'iva'));
6421
6422 parent::display();
6423
6424 if (VikBooking::showFooter()) {
6425 VikBookingHelper::printFooter();
6426 }
6427 }
6428
6429 public function newiva() {
6430 VikBookingHelper::printHeader("2");
6431
6432 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageiva'));
6433
6434 parent::display();
6435
6436 if (VikBooking::showFooter()) {
6437 VikBookingHelper::printFooter();
6438 }
6439 }
6440
6441 public function editiva() {
6442 VikBookingHelper::printHeader("2");
6443
6444 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageiva'));
6445
6446 parent::display();
6447
6448 if (VikBooking::showFooter()) {
6449 VikBookingHelper::printFooter();
6450 }
6451 }
6452
6453 public function createiva()
6454 {
6455 if (!JSession::checkToken()) {
6456 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6457 }
6458
6459 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6460 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6461 }
6462
6463 $paliqname = VikRequest::getString('aliqname', '', 'request');
6464 $paliqperc = VikRequest::getFloat('aliqperc', '', 'request');
6465 $pbreakdown_name = VikRequest::getVar('breakdown_name', array());
6466 $pbreakdown_rate = VikRequest::getVar('breakdown_rate', array());
6467 $ptaxcap = VikRequest::getFloat('taxcap', 0, 'request');
6468 if (!empty($paliqperc)) {
6469 $dbo = JFactory::getDBO();
6470 $breakdown_str = '';
6471 if (count($pbreakdown_name) > 0) {
6472 $breakdown_values = array();
6473 $bkcount = 0;
6474 $tot_sub_aliq = 0;
6475 foreach ($pbreakdown_name as $key => $subtax) {
6476 if (!empty($subtax) && floatval($pbreakdown_rate[$key]) > 0) {
6477 $breakdown_values[$bkcount]['name'] = $subtax;
6478 $breakdown_values[$bkcount]['aliq'] = (float)$pbreakdown_rate[$key];
6479 $tot_sub_aliq += (float)$pbreakdown_rate[$key];
6480 $bkcount++;
6481 }
6482 }
6483 if (count($breakdown_values) > 0) {
6484 $breakdown_str = json_encode($breakdown_values);
6485 if ($tot_sub_aliq < (float)$paliqperc || $tot_sub_aliq > (float)$paliqperc) {
6486 VikError::raiseWarning('', JText::translate('VBOTAXBKDWNERRNOMATCH'));
6487 }
6488 }
6489 }
6490 $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').");";
6491 $dbo->setQuery($q);
6492 $dbo->execute();
6493 }
6494 $mainframe = JFactory::getApplication();
6495 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6496 }
6497
6498 public function updateiva()
6499 {
6500 if (!JSession::checkToken()) {
6501 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6502 }
6503
6504 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6505 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6506 }
6507
6508 $paliqname = VikRequest::getString('aliqname', '', 'request');
6509 $paliqperc = VikRequest::getFloat('aliqperc', '', 'request');
6510 $pbreakdown_name = VikRequest::getVar('breakdown_name', array());
6511 $pbreakdown_rate = VikRequest::getVar('breakdown_rate', array());
6512 $ptaxcap = VikRequest::getFloat('taxcap', 0, 'request');
6513 $pwhereup = VikRequest::getInt('whereup', 0, 'request');
6514 if (!empty($paliqperc)) {
6515 $dbo = JFactory::getDBO();
6516 $breakdown_str = '';
6517 if (count($pbreakdown_name) > 0) {
6518 $breakdown_values = array();
6519 $bkcount = 0;
6520 $tot_sub_aliq = 0;
6521 foreach ($pbreakdown_name as $key => $subtax) {
6522 if (!empty($subtax) && floatval($pbreakdown_rate[$key]) > 0) {
6523 $breakdown_values[$bkcount]['name'] = $subtax;
6524 $breakdown_values[$bkcount]['aliq'] = (float)$pbreakdown_rate[$key];
6525 $tot_sub_aliq += (float)$pbreakdown_rate[$key];
6526 $bkcount++;
6527 }
6528 }
6529 if (count($breakdown_values) > 0) {
6530 $breakdown_str = json_encode($breakdown_values);
6531 if ($tot_sub_aliq < (float)$paliqperc || $tot_sub_aliq > (float)$paliqperc) {
6532 VikError::raiseWarning('', JText::translate('VBOTAXBKDWNERRNOMATCH'));
6533 }
6534 }
6535 }
6536 $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).";";
6537 $dbo->setQuery($q);
6538 $dbo->execute();
6539 }
6540 $mainframe = JFactory::getApplication();
6541 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6542 }
6543
6544 public function removeiva()
6545 {
6546 if (!JSession::checkToken()) {
6547 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6548 }
6549
6550 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6551 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6552 }
6553
6554 $ids = VikRequest::getVar('cid', array(0));
6555 if ($ids) {
6556 $dbo = JFactory::getDBO();
6557 foreach ($ids as $d) {
6558 $q = "DELETE FROM `#__vikbooking_iva` WHERE `id`=".$dbo->quote($d).";";
6559 $dbo->setQuery($q);
6560 $dbo->execute();
6561 }
6562 }
6563 $mainframe = JFactory::getApplication();
6564 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6565 }
6566
6567 public function categories() {
6568 VikBookingHelper::printHeader("4");
6569
6570 VikRequest::setVar('view', VikRequest::getCmd('view', 'categories'));
6571
6572 parent::display();
6573
6574 if (VikBooking::showFooter()) {
6575 VikBookingHelper::printFooter();
6576 }
6577 }
6578
6579 public function newcat() {
6580 VikBookingHelper::printHeader("4");
6581
6582 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecategory'));
6583
6584 parent::display();
6585
6586 if (VikBooking::showFooter()) {
6587 VikBookingHelper::printFooter();
6588 }
6589 }
6590
6591 public function editcat() {
6592 VikBookingHelper::printHeader("4");
6593
6594 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecategory'));
6595
6596 parent::display();
6597
6598 if (VikBooking::showFooter()) {
6599 VikBookingHelper::printFooter();
6600 }
6601 }
6602
6603 public function createcat()
6604 {
6605 if (!JSession::checkToken()) {
6606 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6607 }
6608
6609 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6610 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6611 }
6612
6613 $pcatname = VikRequest::getString('catname', '', 'request');
6614 $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWHTML);
6615 if (!empty($pcatname)) {
6616 $dbo = JFactory::getDBO();
6617 $q = "INSERT INTO `#__vikbooking_categories` (`name`,`descr`) VALUES(".$dbo->quote($pcatname).", ".$dbo->quote($pdescr).");";
6618 $dbo->setQuery($q);
6619 $dbo->execute();
6620 }
6621 $mainframe = JFactory::getApplication();
6622 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6623 }
6624
6625 public function updatecat()
6626 {
6627 if (!JSession::checkToken()) {
6628 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6629 }
6630
6631 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6632 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6633 }
6634
6635 $pcatname = VikRequest::getString('catname', '', 'request');
6636 $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWHTML);
6637 $pwhereup = VikRequest::getString('whereup', '', 'request');
6638 if (!empty($pcatname)) {
6639 $dbo = JFactory::getDBO();
6640 $q = "UPDATE `#__vikbooking_categories` SET `name`=".$dbo->quote($pcatname).", `descr`=".$dbo->quote($pdescr)." WHERE `id`=".$dbo->quote($pwhereup).";";
6641 $dbo->setQuery($q);
6642 $dbo->execute();
6643 }
6644 $mainframe = JFactory::getApplication();
6645 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6646 }
6647
6648 public function removecat()
6649 {
6650 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6651 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6652 }
6653
6654 $ids = VikRequest::getVar('cid', array(0));
6655 if ($ids) {
6656 $dbo = JFactory::getDBO();
6657 foreach ($ids as $d) {
6658 $q = "DELETE FROM `#__vikbooking_categories` WHERE `id`=".$dbo->quote($d).";";
6659 $dbo->setQuery($q);
6660 $dbo->execute();
6661 }
6662 }
6663 $mainframe = JFactory::getApplication();
6664 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6665 }
6666
6667 public function carat() {
6668 VikBookingHelper::printHeader("5");
6669
6670 VikRequest::setVar('view', VikRequest::getCmd('view', 'carat'));
6671
6672 parent::display();
6673
6674 if (VikBooking::showFooter()) {
6675 VikBookingHelper::printFooter();
6676 }
6677 }
6678
6679 public function newcarat() {
6680 VikBookingHelper::printHeader("5");
6681
6682 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecarat'));
6683
6684 parent::display();
6685
6686 if (VikBooking::showFooter()) {
6687 VikBookingHelper::printFooter();
6688 }
6689 }
6690
6691 public function editcarat() {
6692 VikBookingHelper::printHeader("5");
6693
6694 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecarat'));
6695
6696 parent::display();
6697
6698 if (VikBooking::showFooter()) {
6699 VikBookingHelper::printFooter();
6700 }
6701 }
6702
6703 public function createcarat()
6704 {
6705 if (!JSession::checkToken()) {
6706 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6707 }
6708
6709 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6710 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6711 }
6712
6713 $pcaratname = VikRequest::getString('caratname', '', 'request');
6714 $pcarattextimg = VikRequest::getString('carattextimg', '', 'request', VIKREQUEST_ALLOWRAW);
6715 $pautoresize = VikRequest::getString('autoresize', '', 'request');
6716 $presizeto = VikRequest::getString('resizeto', '', 'request');
6717 $pidrooms = VikRequest::getVar('idrooms', array());
6718 if (!empty($pcaratname)) {
6719 if (intval($_FILES['caraticon']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['caraticon']['name'])!="") {
6720 jimport('joomla.filesystem.file');
6721 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
6722 if (@is_uploaded_file($_FILES['caraticon']['tmp_name'])) {
6723 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['caraticon']['name'])));
6724 if (file_exists($updpath.$safename)) {
6725 $j=1;
6726 while (file_exists($updpath.$j.$safename)) {
6727 $j++;
6728 }
6729 $pwhere=$updpath.$j.$safename;
6730 } else {
6731 $j="";
6732 $pwhere=$updpath.$safename;
6733 }
6734 if (!getimagesize($_FILES['caraticon']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
6735 @unlink($pwhere);
6736 $picon="";
6737 } else {
6738 VikBooking::uploadFile($_FILES['caraticon']['tmp_name'], $pwhere);
6739 @chmod($pwhere, 0644);
6740 $picon=$j.$safename;
6741 if ($pautoresize=="1" && !empty($presizeto)) {
6742 $eforj = new vikResizer();
6743 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
6744 if ($origmod) {
6745 @unlink($pwhere);
6746 $picon='r_'.$j.$safename;
6747 }
6748 }
6749 }
6750 } else {
6751 $picon="";
6752 }
6753 } else {
6754 $picon="";
6755 }
6756 $dbo = JFactory::getDbo();
6757 // get new ordering
6758 $q = "SELECT `ordering` FROM `#__vikbooking_characteristics` ORDER BY `#__vikbooking_characteristics`.`ordering` DESC LIMIT 1;";
6759 $dbo->setQuery($q);
6760 $dbo->execute();
6761 if ($dbo->getNumRows()) {
6762 $newsortnum = $dbo->loadResult() + 1;
6763 } else {
6764 $newsortnum = 1;
6765 }
6766 $pordering = VikRequest::getInt('ordering', 0, 'request');
6767 $newsortnum = !empty($pordering) ? $pordering : $newsortnum;
6768 //
6769 $q = "INSERT INTO `#__vikbooking_characteristics` (`name`,`icon`,`textimg`,`ordering`) VALUES(".$dbo->quote($pcaratname).", ".$dbo->quote($picon).", ".$dbo->quote($pcarattextimg).", {$newsortnum});";
6770 $dbo->setQuery($q);
6771 $dbo->execute();
6772
6773 $new_carat_id = $dbo->insertid();
6774 if (!empty($new_carat_id)) {
6775 // assign/unset carat-rooms relations
6776 $rooms_with_carat = array();
6777 if (count($pidrooms)) {
6778 // assign this new carat to the requested rooms
6779 foreach ($pidrooms as $idroom) {
6780 if (empty($idroom)) {
6781 continue;
6782 }
6783 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
6784 $dbo->setQuery($q);
6785 $dbo->execute();
6786 if (!$dbo->getNumRows()) {
6787 continue;
6788 }
6789 $room_data = $dbo->loadAssoc();
6790 array_push($rooms_with_carat, $room_data['id']);
6791 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6792 if (in_array((string)$new_carat_id, $current_carats)) {
6793 continue;
6794 }
6795 if (count($current_carats) === 1 && (string)$current_carats[0] == '0') {
6796 // make sure we do not concatenate a real ID to 0
6797 $current_carats = array();
6798 }
6799 array_push($current_carats, $new_carat_id);
6800 $new_opts = implode(';', $current_carats) . ';';
6801 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
6802 $dbo->setQuery($q);
6803 $dbo->execute();
6804 }
6805 }
6806 if (!count($rooms_with_carat)) {
6807 // get all rooms to unset this carat (if previously set)
6808 array_push($rooms_with_carat, '0');
6809 }
6810 // unset the carat from the other rooms that may have it
6811 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_carat) . ");";
6812 $dbo->setQuery($q);
6813 $dbo->execute();
6814 if ($dbo->getNumRows()) {
6815 $unset_rooms_carat = $dbo->loadAssocList();
6816 foreach ($unset_rooms_carat as $room_data) {
6817 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6818 if (!in_array((string)$new_carat_id, $current_carats)) {
6819 // this room is not using this carat
6820 continue;
6821 }
6822 $caratkey = array_search((string)$new_carat_id, $current_carats);
6823 if ($caratkey === false) {
6824 // key not found
6825 continue;
6826 }
6827 // unset this carat ID from the string
6828 unset($current_carats[$caratkey]);
6829 if (!count($current_carats)) {
6830 // a room with no carats assigned will be listed as "0;"
6831 $current_carats = array(0);
6832 }
6833 $new_opts = implode(';', $current_carats) . ';';
6834 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
6835 $dbo->setQuery($q);
6836 $dbo->execute();
6837 }
6838 }
6839 //
6840 }
6841 }
6842 $mainframe = JFactory::getApplication();
6843 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
6844 }
6845
6846 public function updatecarat()
6847 {
6848 if (!JSession::checkToken()) {
6849 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6850 }
6851
6852 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6853 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6854 }
6855
6856 $pcaratname = VikRequest::getString('caratname', '', 'request');
6857 $pcarattextimg = VikRequest::getString('carattextimg', '', 'request', VIKREQUEST_ALLOWRAW);
6858 $pwhereup = VikRequest::getString('whereup', '', 'request');
6859 $pautoresize = VikRequest::getString('autoresize', '', 'request');
6860 $presizeto = VikRequest::getString('resizeto', '', 'request');
6861 $pidrooms = VikRequest::getVar('idrooms', array());
6862 $pordering = VikRequest::getInt('ordering', 1, 'request');
6863 if (!empty($pcaratname)) {
6864 if (intval($_FILES['caraticon']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['caraticon']['name'])!="") {
6865 jimport('joomla.filesystem.file');
6866 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
6867 if (@is_uploaded_file($_FILES['caraticon']['tmp_name'])) {
6868 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['caraticon']['name'])));
6869 if (file_exists($updpath.$safename)) {
6870 $j=1;
6871 while (file_exists($updpath.$j.$safename)) {
6872 $j++;
6873 }
6874 $pwhere=$updpath.$j.$safename;
6875 } else {
6876 $j="";
6877 $pwhere=$updpath.$safename;
6878 }
6879 if (!getimagesize($_FILES['caraticon']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
6880 @unlink($pwhere);
6881 $picon="";
6882 } else {
6883 VikBooking::uploadFile($_FILES['caraticon']['tmp_name'], $pwhere);
6884 @chmod($pwhere, 0644);
6885 $picon=$j.$safename;
6886 if ($pautoresize=="1" && !empty($presizeto)) {
6887 $eforj = new vikResizer();
6888 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
6889 if ($origmod) {
6890 @unlink($pwhere);
6891 $picon='r_'.$j.$safename;
6892 }
6893 }
6894 }
6895 } else {
6896 $picon="";
6897 }
6898 } else {
6899 $picon="";
6900 }
6901 $dbo = JFactory::getDbo();
6902 $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).";";
6903 $dbo->setQuery($q);
6904 $dbo->execute();
6905
6906 // assign/unset carat-rooms relations
6907 $rooms_with_carat = array();
6908 if (count($pidrooms)) {
6909 // assign this new carat to the requested rooms
6910 foreach ($pidrooms as $idroom) {
6911 if (empty($idroom)) {
6912 continue;
6913 }
6914 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
6915 $dbo->setQuery($q);
6916 $dbo->execute();
6917 if (!$dbo->getNumRows()) {
6918 continue;
6919 }
6920 $room_data = $dbo->loadAssoc();
6921 array_push($rooms_with_carat, $room_data['id']);
6922 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6923 if (in_array((string)$pwhereup, $current_carats)) {
6924 continue;
6925 }
6926 if (count($current_carats) === 1 && (string)$current_carats[0] == '0') {
6927 // make sure we do not concatenate a real ID to 0
6928 $current_carats = array();
6929 }
6930 array_push($current_carats, $pwhereup);
6931 $new_carats = implode(';', $current_carats) . ';';
6932 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_carats) . " WHERE `id`={$room_data['id']};";
6933 $dbo->setQuery($q);
6934 $dbo->execute();
6935 }
6936 }
6937 if (!count($rooms_with_carat)) {
6938 // get all rooms to unset this carat (if previously set)
6939 array_push($rooms_with_carat, '0');
6940 }
6941 // unset the carat from the other rooms that may have it
6942 $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_carat) . ");";
6943 $dbo->setQuery($q);
6944 $dbo->execute();
6945 if ($dbo->getNumRows()) {
6946 $unset_rooms_carat = $dbo->loadAssocList();
6947 foreach ($unset_rooms_carat as $room_data) {
6948 $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6949 if (!in_array((string)$pwhereup, $current_carats)) {
6950 // this room is not using this carat
6951 continue;
6952 }
6953 $caratkey = array_search((string)$pwhereup, $current_carats);
6954 if ($caratkey === false) {
6955 // key not found
6956 continue;
6957 }
6958 // unset this carat ID from the string
6959 unset($current_carats[$caratkey]);
6960 if (!count($current_carats)) {
6961 // a room with no carats assigned will be listed as "0;"
6962 $current_carats = array(0);
6963 }
6964 $new_carats = implode(';', $current_carats) . ';';
6965 $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_carats) . " WHERE `id`={$room_data['id']};";
6966 $dbo->setQuery($q);
6967 $dbo->execute();
6968 }
6969 }
6970 //
6971 }
6972 $mainframe = JFactory::getApplication();
6973 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
6974 }
6975
6976 public function removecarat()
6977 {
6978 if (!JSession::checkToken()) {
6979 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6980 }
6981
6982 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6983 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6984 }
6985
6986 $ids = VikRequest::getVar('cid', array(0));
6987 if ($ids) {
6988 $dbo = JFactory::getDBO();
6989 foreach ($ids as $d) {
6990 $q = "SELECT `icon` FROM `#__vikbooking_characteristics` WHERE `id`=".$dbo->quote($d).";";
6991 $dbo->setQuery($q);
6992 $dbo->execute();
6993 if ($dbo->getNumRows() == 1) {
6994 $rows = $dbo->loadAssocList();
6995 if (!empty($rows[0]['icon']) && file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['icon'])) {
6996 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['icon']);
6997 }
6998 }
6999 $q = "DELETE FROM `#__vikbooking_characteristics` WHERE `id`=".$dbo->quote($d).";";
7000 $dbo->setQuery($q);
7001 $dbo->execute();
7002 }
7003 }
7004 $mainframe = JFactory::getApplication();
7005 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
7006 }
7007
7008 public function coupons() {
7009 VikBookingHelper::printHeader("17");
7010
7011 VikRequest::setVar('view', VikRequest::getCmd('view', 'coupons'));
7012
7013 parent::display();
7014
7015 if (VikBooking::showFooter()) {
7016 VikBookingHelper::printFooter();
7017 }
7018 }
7019
7020 public function newcoupon() {
7021 VikBookingHelper::printHeader("17");
7022
7023 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecoupon'));
7024
7025 parent::display();
7026
7027 if (VikBooking::showFooter()) {
7028 VikBookingHelper::printFooter();
7029 }
7030 }
7031
7032 public function editcoupon() {
7033 VikBookingHelper::printHeader("17");
7034
7035 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecoupon'));
7036
7037 parent::display();
7038
7039 if (VikBooking::showFooter()) {
7040 VikBookingHelper::printFooter();
7041 }
7042 }
7043
7044 public function createcoupon()
7045 {
7046 if (!JSession::checkToken()) {
7047 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7048 }
7049
7050 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
7051 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7052 }
7053
7054 $pcode = VikRequest::getString('code', '', 'request');
7055 $pvalue = VikRequest::getString('value', '', 'request');
7056 $pfrom = VikRequest::getString('from', '', 'request');
7057 $pto = VikRequest::getString('to', '', 'request');
7058 $pidrooms = VikRequest::getVar('idrooms', array(0));
7059 $ptype = VikRequest::getString('type', '', 'request');
7060 $ptype = $ptype == "1" ? 1 : 2;
7061 $ppercentot = VikRequest::getString('percentot', '', 'request');
7062 $ppercentot = $ppercentot == "1" ? 1 : 2;
7063 $pallvehicles = VikRequest::getString('allvehicles', '', 'request');
7064 $pallvehicles = $pallvehicles == "1" ? 1 : 0;
7065 $pmintotord = VikRequest::getFloat('mintotord', 0, 'request');
7066 $pmaxtotord = VikRequest::getFloat('maxtotord', 0, 'request');
7067 $pexcludetaxes = VikRequest::getInt('excludetaxes', 0, 'request');
7068 $pminlos = VikRequest::getInt('minlos', 0, 'request');
7069 $pcustomers = VikRequest::getVar('customers', array());
7070 $pautomatic = VikRequest::getInt('automatic', 0, 'request');
7071 $stridrooms = "";
7072 if (count($pidrooms) > 0 && $pallvehicles != 1) {
7073 foreach ($pidrooms as $ch) {
7074 if (!empty($ch)) {
7075 $stridrooms .= ";".$ch.";";
7076 }
7077 }
7078 }
7079 $strdatevalid = "";
7080 if (strlen($pfrom) > 0 && strlen($pto) > 0) {
7081 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
7082 $second = VikBooking::getDateTimestamp($pto, 0, 0);
7083 if ($first < $second) {
7084 $strdatevalid .= $first."-".$second;
7085 }
7086 }
7087
7088 $dbo = JFactory::getDbo();
7089 $app = JFactory::getApplication();
7090
7091 $q = "SELECT * FROM `#__vikbooking_coupons` WHERE `code`=".$dbo->quote($pcode).";";
7092 $dbo->setQuery($q);
7093 $dbo->execute();
7094 if ($dbo->getNumRows() > 0) {
7095 VikError::raiseWarning('', JText::translate('VBCOUPONEXISTS'));
7096 } else {
7097 $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) . ");";
7098 $dbo->setQuery($q);
7099 $dbo->execute();
7100
7101 $id_coupon = $dbo->insertid();
7102
7103 $app->enqueueMessage(JText::translate('VBCOUPONSAVEOK'));
7104
7105 // check if this coupon should be assigned to specific customers
7106 foreach ($pcustomers as $id_customer) {
7107 $customer_coupon = new stdClass;
7108 $customer_coupon->idcustomer = (int)$id_customer;
7109 $customer_coupon->idcoupon = (int)$id_coupon;
7110 $customer_coupon->automatic = $pautomatic ? 1 : 0;
7111
7112 $dbo->insertObject('#__vikbooking_customers_coupons', $customer_coupon, 'id');
7113 }
7114 }
7115 $app->redirect("index.php?option=com_vikbooking&task=coupons");
7116 }
7117
7118 public function updatecoupon()
7119 {
7120 if (!JSession::checkToken()) {
7121 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7122 }
7123
7124 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7125 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7126 }
7127
7128 $this->do_updatecoupon($stay = false);
7129 }
7130
7131 public function updatecoupon_stay()
7132 {
7133 if (!JSession::checkToken()) {
7134 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7135 }
7136
7137 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7138 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7139 }
7140
7141 $this->do_updatecoupon($stay = true);
7142 }
7143
7144 protected function do_updatecoupon($stay = false)
7145 {
7146 if (!JSession::checkToken()) {
7147 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7148 }
7149 $pcode = VikRequest::getString('code', '', 'request');
7150 $pvalue = VikRequest::getString('value', '', 'request');
7151 $pfrom = VikRequest::getString('from', '', 'request');
7152 $pto = VikRequest::getString('to', '', 'request');
7153 $pidrooms = VikRequest::getVar('idrooms', array(0));
7154 $pwhere = VikRequest::getInt('where', 0, 'request');
7155 $ptype = VikRequest::getString('type', '', 'request');
7156 $ptype = $ptype == "1" ? 1 : 2;
7157 $ppercentot = VikRequest::getString('percentot', '', 'request');
7158 $ppercentot = $ppercentot == "1" ? 1 : 2;
7159 $pallvehicles = VikRequest::getString('allvehicles', '', 'request');
7160 $pallvehicles = $pallvehicles == "1" ? 1 : 0;
7161 $pmintotord = VikRequest::getFloat('mintotord', 0, 'request');
7162 $pmaxtotord = VikRequest::getFloat('maxtotord', 0, 'request');
7163 $pexcludetaxes = VikRequest::getInt('excludetaxes', 0, 'request');
7164 $pminlos = VikRequest::getInt('minlos', 0, 'request');
7165 $pcustomers = VikRequest::getVar('customers', array());
7166 $pautomatic = VikRequest::getInt('automatic', 0, 'request');
7167 $stridrooms = "";
7168 if (count($pidrooms) > 0 && $pallvehicles != 1) {
7169 foreach ($pidrooms as $ch) {
7170 if (!empty($ch)) {
7171 $stridrooms .= ";".$ch.";";
7172 }
7173 }
7174 }
7175 $strdatevalid = "";
7176 if (strlen($pfrom) > 0 && strlen($pto) > 0) {
7177 $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
7178 $second = VikBooking::getDateTimestamp($pto, 0, 0);
7179 if ($first < $second) {
7180 $strdatevalid .= $first."-".$second;
7181 }
7182 }
7183
7184 $dbo = JFactory::getDbo();
7185 $app = JFactory::getApplication();
7186
7187 $q = "SELECT * FROM `#__vikbooking_coupons` WHERE `code`=".$dbo->quote($pcode)." AND `id`!='".$pwhere."';";
7188 $dbo->setQuery($q);
7189 $dbo->execute();
7190 if ($dbo->getNumRows() > 0) {
7191 VikError::raiseWarning('', JText::translate('VBCOUPONEXISTS'));
7192 } else {
7193 $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 . ";";
7194 $dbo->setQuery($q);
7195 $dbo->execute();
7196
7197 $app->enqueueMessage(JText::translate('VBCOUPONSAVEOK'));
7198
7199 // clean up any previously created record with customers
7200 $q = "DELETE FROM `#__vikbooking_customers_coupons` WHERE `idcoupon`=" . $pwhere;
7201 $dbo->setQuery($q);
7202 $dbo->execute();
7203
7204 // check if this coupon should be assigned to specific customers
7205 foreach ($pcustomers as $id_customer) {
7206 $customer_coupon = new stdClass;
7207 $customer_coupon->idcustomer = (int)$id_customer;
7208 $customer_coupon->idcoupon = (int)$pwhere;
7209 $customer_coupon->automatic = $pautomatic ? 1 : 0;
7210
7211 $dbo->insertObject('#__vikbooking_customers_coupons', $customer_coupon, 'id');
7212 }
7213 }
7214
7215 if ($stay) {
7216 $app->redirect("index.php?option=com_vikbooking&task=editcoupon&cid[]=$pwhere");
7217 } else {
7218 $app->redirect("index.php?option=com_vikbooking&task=coupons");
7219 }
7220 }
7221
7222 public function removecoupons()
7223 {
7224 if (!JSession::checkToken()) {
7225 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7226 }
7227
7228 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7229 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7230 }
7231
7232 $dbo = JFactory::getDbo();
7233
7234 $ids = VikRequest::getVar('cid', array(0));
7235
7236 if ($ids) {
7237 foreach ($ids as $d) {
7238 // delete coupon record
7239 $q = "DELETE FROM `#__vikbooking_coupons` WHERE `id`=".$dbo->quote($d).";";
7240 $dbo->setQuery($q);
7241 $dbo->execute();
7242
7243 // clean up any previously created record with customers
7244 $q = "DELETE FROM `#__vikbooking_customers_coupons` WHERE `idcoupon`=" . (int)$d;
7245 $dbo->setQuery($q);
7246 $dbo->execute();
7247 }
7248 }
7249
7250 JFactory::getApplication()->redirect("index.php?option=com_vikbooking&task=coupons");
7251 }
7252
7253 public function removemoreimgs()
7254 {
7255 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7256 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7257 }
7258
7259 $mainframe = JFactory::getApplication();
7260 $proomid = VikRequest::getInt('roomid', '', 'request');
7261 $pimgind = VikRequest::getInt('imgind', '', 'request');
7262 if (!strlen($pimgind)) {
7263 $mainframe->redirect("index.php?option=com_vikbooking");
7264 exit;
7265 }
7266 $dbo = JFactory::getDBO();
7267 $q = "SELECT `moreimgs`,`imgcaptions` FROM `#__vikbooking_rooms` WHERE `id`='".$proomid."';";
7268 $dbo->setQuery($q);
7269 $dbo->execute();
7270 $row = $dbo->loadAssoc();
7271 $actmore = $row['moreimgs'];
7272 if (!empty($actmore)) {
7273 $actsplit = explode(';;', $actmore);
7274 $captions = json_decode($row['imgcaptions'], true);
7275 $captions = !is_array($captions) ? array() : $captions;
7276 if ($pimgind < 0) {
7277 foreach ($actsplit as $img) {
7278 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'big_'.$img);
7279 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'thumb_'.$img);
7280 }
7281 // reset images and captions
7282 $actsplit = array();
7283 $captions = array();
7284 } else {
7285 if (array_key_exists($pimgind, $actsplit)) {
7286 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'big_'.$actsplit[$pimgind]);
7287 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'thumb_'.$actsplit[$pimgind]);
7288 // unset current image
7289 unset($actsplit[$pimgind]);
7290 // unset caption if exists
7291 if (isset($captions[$pimgind])) {
7292 unset($captions[$pimgind]);
7293 $captions = array_values($captions);
7294 }
7295 }
7296 }
7297 $newstr = "";
7298 foreach ($actsplit as $oi) {
7299 if (!empty($oi)) {
7300 $newstr .= $oi.';;';
7301 }
7302 }
7303 $q = "UPDATE `#__vikbooking_rooms` SET `moreimgs`=".$dbo->quote($newstr).", `imgcaptions`=".$dbo->quote(json_encode($captions))." WHERE `id`='".$proomid."';";
7304 $dbo->setQuery($q);
7305 $dbo->execute();
7306 }
7307 $mainframe->redirect("index.php?option=com_vikbooking&task=editroom&cid[]=".$proomid);
7308 }
7309
7310 public function sortfield() {
7311 if (!JSession::checkToken('get')) {
7312 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7313 }
7314 $mainframe = JFactory::getApplication();
7315 $sortid = VikRequest::getVar('cid', array(0));
7316 $pmode = VikRequest::getString('mode', '', 'request');
7317 $dbo = JFactory::getDBO();
7318 if (!empty($pmode)) {
7319 $q = "SELECT `id`,`ordering` FROM `#__vikbooking_custfields` ORDER BY `#__vikbooking_custfields`.`ordering` ASC;";
7320 $dbo->setQuery($q);
7321 $dbo->execute();
7322 $totr=$dbo->getNumRows();
7323 if ($totr > 1) {
7324 $data = $dbo->loadAssocList();
7325 if ($pmode == "up") {
7326 foreach ($data as $v) {
7327 if ($v['id'] == $sortid[0]) {
7328 $y = $v['ordering'];
7329 }
7330 }
7331 if ($y && $y > 1) {
7332 $vik = $y - 1;
7333 $found = false;
7334 foreach ($data as $v) {
7335 if (intval($v['ordering']) == intval($vik)) {
7336 $found=true;
7337 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
7338 $dbo->setQuery($q);
7339 $dbo->execute();
7340 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7341 $dbo->setQuery($q);
7342 $dbo->execute();
7343 break;
7344 }
7345 }
7346 if (!$found) {
7347 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7348 $dbo->setQuery($q);
7349 $dbo->execute();
7350 }
7351 }
7352 } elseif ($pmode == "down") {
7353 foreach ($data as $v) {
7354 if ($v['id'] == $sortid[0]) {
7355 $y = $v['ordering'];
7356 }
7357 }
7358 if ($y) {
7359 $vik = $y + 1;
7360 $found = false;
7361 foreach ($data as $v) {
7362 if (intval($v['ordering']) == intval($vik)) {
7363 $found=true;
7364 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
7365 $dbo->setQuery($q);
7366 $dbo->execute();
7367 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7368 $dbo->setQuery($q);
7369 $dbo->execute();
7370 break;
7371 }
7372 }
7373 if (!$found) {
7374 $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7375 $dbo->setQuery($q);
7376 $dbo->execute();
7377 }
7378 }
7379 }
7380 }
7381 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7382 } else {
7383 $mainframe->redirect("index.php?option=com_vikbooking");
7384 }
7385 }
7386
7387 public function customf() {
7388 VikBookingHelper::printHeader("16");
7389
7390 VikRequest::setVar('view', VikRequest::getCmd('view', 'customf'));
7391
7392 parent::display();
7393
7394 if (VikBooking::showFooter()) {
7395 VikBookingHelper::printFooter();
7396 }
7397 }
7398
7399 public function newcustomf() {
7400 VikBookingHelper::printHeader("16");
7401
7402 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomf'));
7403
7404 parent::display();
7405
7406 if (VikBooking::showFooter()) {
7407 VikBookingHelper::printFooter();
7408 }
7409 }
7410
7411 public function editcustomf() {
7412 VikBookingHelper::printHeader("16");
7413
7414 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomf'));
7415
7416 parent::display();
7417
7418 if (VikBooking::showFooter()) {
7419 VikBookingHelper::printFooter();
7420 }
7421 }
7422
7423 public function createcustomf()
7424 {
7425 if (!JSession::checkToken()) {
7426 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7427 }
7428
7429 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
7430 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7431 }
7432
7433 $pname = VikRequest::getString('name', '', 'request', VIKREQUEST_ALLOWHTML);
7434 $ptype = VikRequest::getString('type', '', 'request');
7435 $pchoose = VikRequest::getVar('choose', array(0));
7436 $prequired = VikRequest::getString('required', '', 'request');
7437 $prequired = $prequired == "1" ? 1 : 0;
7438 $pflag = VikRequest::getString('flag', '', 'request');
7439 $pisemail = $pflag == 'isemail' ? 1 : 0;
7440 $pisnominative = $pflag == 'isnominative' && $ptype == 'text' ? 1 : 0;
7441 $pisphone = $pflag == 'isphone' && $ptype == 'text' ? 1 : 0;
7442 $pisaddress = $pflag == 'isaddress' && $ptype == 'text' ? 1 : 0;
7443 $piscity = $pflag == 'iscity' && $ptype == 'text' ? 1 : 0;
7444 $piszip = $pflag == 'iszip' && $ptype == 'text' ? 1 : 0;
7445 $piscompany = $pflag == 'iscompany' && $ptype == 'text' ? 1 : 0;
7446 $pisvat = $pflag == 'isvat' && $ptype == 'text' ? 1 : 0;
7447 $pisfisccode = $pflag == 'isfisccode' && $ptype == 'text' ? 1 : 0;
7448 $pispec = $pflag == 'ispec' && $ptype == 'text' ? 1 : 0;
7449 $pisrecipcode = $pflag == 'isrecipcode' && $ptype == 'text' ? 1 : 0;
7450 $fieldflag = '';
7451 if ($pisaddress == 1) {
7452 $fieldflag = 'address';
7453 } elseif ($piscity == 1) {
7454 $fieldflag = 'city';
7455 } elseif ($piszip == 1) {
7456 $fieldflag = 'zip';
7457 } elseif ($piscompany == 1) {
7458 $fieldflag = 'company';
7459 } elseif ($pisvat == 1) {
7460 $fieldflag = 'vat';
7461 } elseif ($pisfisccode == 1) {
7462 $fieldflag = 'fisccode';
7463 } elseif ($pispec == 1) {
7464 $fieldflag = 'pec';
7465 } elseif ($pisrecipcode == 1) {
7466 $fieldflag = 'recipcode';
7467 }
7468 $ppoplink = VikRequest::getString('poplink', '', 'request');
7469 $choosestr = "";
7470 if (is_array($pchoose)) {
7471 foreach ($pchoose as $ch) {
7472 if (!empty($ch)) {
7473 $choosestr .= $ch.";;__;;";
7474 }
7475 }
7476 }
7477 $defvalue = VikRequest::getString('defvalue', '', 'request');
7478
7479 $dbo = JFactory::getDbo();
7480
7481 $q = "SELECT `ordering` FROM `#__vikbooking_custfields` ORDER BY `#__vikbooking_custfields`.`ordering` DESC LIMIT 1;";
7482 $dbo->setQuery($q);
7483 $dbo->execute();
7484 if ($dbo->getNumRows() == 1) {
7485 $getlast = $dbo->loadResult();
7486 $newsortnum = $getlast + 1;
7487 } else {
7488 $newsortnum = 1;
7489 }
7490 $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).");";
7491 $dbo->setQuery($q);
7492 $dbo->execute();
7493 $mainframe = JFactory::getApplication();
7494 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7495 }
7496
7497 public function updatecustomf()
7498 {
7499 if (!JSession::checkToken()) {
7500 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7501 }
7502
7503 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7504 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7505 }
7506
7507 $pname = VikRequest::getString('name', '', 'request', VIKREQUEST_ALLOWHTML);
7508 $ptype = VikRequest::getString('type', '', 'request');
7509 $pchoose = VikRequest::getVar('choose', array(0));
7510 $prequired = VikRequest::getString('required', '', 'request');
7511 $prequired = $prequired == "1" ? 1 : 0;
7512 $pflag = VikRequest::getString('flag', '', 'request');
7513 $pisemail = $pflag == 'isemail' ? 1 : 0;
7514 $pisnominative = $pflag == 'isnominative' && $ptype == 'text' ? 1 : 0;
7515 $pisphone = $pflag == 'isphone' && $ptype == 'text' ? 1 : 0;
7516 $pisaddress = $pflag == 'isaddress' && $ptype == 'text' ? 1 : 0;
7517 $piscity = $pflag == 'iscity' && $ptype == 'text' ? 1 : 0;
7518 $piszip = $pflag == 'iszip' && $ptype == 'text' ? 1 : 0;
7519 $piscompany = $pflag == 'iscompany' && $ptype == 'text' ? 1 : 0;
7520 $pisvat = $pflag == 'isvat' && $ptype == 'text' ? 1 : 0;
7521 $pisfisccode = $pflag == 'isfisccode' && $ptype == 'text' ? 1 : 0;
7522 $pispec = $pflag == 'ispec' && $ptype == 'text' ? 1 : 0;
7523 $pisrecipcode = $pflag == 'isrecipcode' && $ptype == 'text' ? 1 : 0;
7524 $fieldflag = '';
7525 if ($pisaddress == 1) {
7526 $fieldflag = 'address';
7527 } elseif ($piscity == 1) {
7528 $fieldflag = 'city';
7529 } elseif ($piszip == 1) {
7530 $fieldflag = 'zip';
7531 } elseif ($piscompany == 1) {
7532 $fieldflag = 'company';
7533 } elseif ($pisvat == 1) {
7534 $fieldflag = 'vat';
7535 } elseif ($pisfisccode == 1) {
7536 $fieldflag = 'fisccode';
7537 } elseif ($pispec == 1) {
7538 $fieldflag = 'pec';
7539 } elseif ($pisrecipcode == 1) {
7540 $fieldflag = 'recipcode';
7541 }
7542 $ppoplink = VikRequest::getString('poplink', '', 'request');
7543 $pwhere = VikRequest::getInt('where', '', 'request');
7544 $choosestr = "";
7545 if (is_array($pchoose)) {
7546 foreach ($pchoose as $ch) {
7547 if (!empty($ch)) {
7548 $choosestr .= $ch.";;__;;";
7549 }
7550 }
7551 }
7552 $defvalue = VikRequest::getString('defvalue', '', 'request');
7553
7554 $dbo = JFactory::getDbo();
7555
7556 $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).";";
7557 $dbo->setQuery($q);
7558 $dbo->execute();
7559 $mainframe = JFactory::getApplication();
7560 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7561 }
7562
7563 public function removecustomf()
7564 {
7565 if (!JSession::checkToken()) {
7566 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7567 }
7568
7569 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7570 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7571 }
7572
7573 $ids = VikRequest::getVar('cid', array(0));
7574 if ($ids) {
7575 $dbo = JFactory::getDBO();
7576 foreach ($ids as $d) {
7577 $q = "DELETE FROM `#__vikbooking_custfields` WHERE `id`=".$dbo->quote($d).";";
7578 $dbo->setQuery($q);
7579 $dbo->execute();
7580 }
7581 }
7582 $mainframe = JFactory::getApplication();
7583 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7584 }
7585
7586 public function overv() {
7587 VikBookingHelper::printHeader("15");
7588
7589 VikRequest::setVar('view', VikRequest::getCmd('view', 'overv'));
7590
7591 parent::display();
7592
7593 if (VikBooking::showFooter()) {
7594 VikBookingHelper::printFooter();
7595 }
7596 }
7597
7598 public function translations() {
7599 VikBookingHelper::printHeader("21");
7600
7601 VikRequest::setVar('view', VikRequest::getCmd('view', 'translations'));
7602
7603 parent::display();
7604
7605 if (VikBooking::showFooter()) {
7606 VikBookingHelper::printFooter();
7607 }
7608 }
7609
7610 public function savetranslation() {
7611 if (!JSession::checkToken()) {
7612 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7613 }
7614 $this->do_savetranslation();
7615 }
7616
7617 public function savetranslationstay() {
7618 if (!JSession::checkToken()) {
7619 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7620 }
7621 $this->do_savetranslation(true);
7622 }
7623
7624 private function do_savetranslation($stay = false) {
7625 $dbo = JFactory::getDBO();
7626 $mainframe = JFactory::getApplication();
7627 $vbo_tn = VikBooking::getTranslator();
7628 $table = VikRequest::getString('vbo_table', '', 'request');
7629 $cur_langtab = VikRequest::getString('vbo_lang', '', 'request');
7630 $langs = $vbo_tn->getLanguagesList();
7631 $xml_tables = $vbo_tn->getTranslationTables();
7632 if (!empty($table) && array_key_exists($table, $xml_tables)) {
7633 $tn = VikRequest::getVar('tn', array(), 'request', 'array', VIKREQUEST_ALLOWRAW);
7634 $tn_saved = 0;
7635 $table_cols = $vbo_tn->getTableColumns($table);
7636 foreach ($langs as $ltag => $lang) {
7637 if ($ltag == $vbo_tn->default_lang) {
7638 continue;
7639 }
7640 if (array_key_exists($ltag, $tn) && count($tn[$ltag]) > 0) {
7641 foreach ($tn[$ltag] as $reference_id => $translation) {
7642 $lang_translation = array();
7643 foreach ($table_cols as $field => $fdetails) {
7644 if (!array_key_exists($field, $translation)) {
7645 continue;
7646 }
7647 $ftype = $fdetails['type'];
7648 if ($ftype == 'skip') {
7649 continue;
7650 }
7651
7652 if (is_array($translation[$field])) {
7653 foreach ($translation[$field] as $tn_field_k => $tn_field_v) {
7654 if (!is_string($tn_field_v)) {
7655 continue;
7656 }
7657 // replace any possible placeholder for special tags
7658 $translation[$field][$tn_field_k] = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
7659 return $match[2];
7660 }, $translation[$field][$tn_field_k]);
7661 }
7662 } elseif (!empty($translation[$field])) {
7663 // replace any possible placeholder for special tags
7664 $translation[$field] = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
7665 return $match[2];
7666 }, $translation[$field]);
7667 }
7668
7669 if ($ftype == 'json' && !is_scalar($translation[$field])) {
7670 $translation[$field] = json_encode($translation[$field]);
7671 }
7672 $lang_translation[$field] = $translation[$field];
7673 }
7674 if (count($lang_translation) > 0) {
7675 $q = "SELECT `id` FROM `#__vikbooking_translations` WHERE `table`=".$dbo->quote($table)." AND `lang`=".$dbo->quote($ltag)." AND `reference_id`=".$dbo->quote((int)$reference_id).";";
7676 $dbo->setQuery($q);
7677 $dbo->execute();
7678 if ($dbo->getNumRows() > 0) {
7679 $last_id = $dbo->loadResult();
7680 $q = "UPDATE `#__vikbooking_translations` SET `content`=".$dbo->quote(json_encode($lang_translation))." WHERE `id`=".(int)$last_id.";";
7681 } else {
7682 $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)).");";
7683 }
7684 $dbo->setQuery($q);
7685 $dbo->execute();
7686 $tn_saved++;
7687 }
7688 }
7689 }
7690 }
7691 if ($tn_saved > 0) {
7692 $mainframe->enqueueMessage(JText::translate('VBOTRANSLSAVEDOK'));
7693 }
7694 } else {
7695 VikError::raiseWarning('', JText::translate('VBTRANSLATIONERRINVTABLE'));
7696 }
7697 $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);
7698 }
7699
7700 public function choosebusy() {
7701 VikBookingHelper::printHeader("8");
7702
7703 VikRequest::setVar('view', VikRequest::getCmd('view', 'choosebusy'));
7704
7705 parent::display();
7706
7707 if (VikBooking::showFooter()) {
7708 VikBookingHelper::printFooter();
7709 }
7710 }
7711
7712 public function orders() {
7713 VikBookingHelper::printHeader("8");
7714
7715 VikRequest::setVar('view', VikRequest::getCmd('view', 'orders'));
7716
7717 parent::display();
7718
7719 if (VikBooking::showFooter()) {
7720 VikBookingHelper::printFooter();
7721 }
7722 }
7723
7724 public function vieworders() {
7725 //alias method of orders() for backward compatibility with VCM
7726 $this->orders();
7727 }
7728
7729 public function editorder() {
7730 VikBookingHelper::printHeader("8");
7731
7732 VikRequest::setVar('view', VikRequest::getCmd('view', 'editorder'));
7733
7734 parent::display();
7735
7736 if (VikBooking::showFooter()) {
7737 VikBookingHelper::printFooter();
7738 }
7739 }
7740
7741 public function removeorders()
7742 {
7743 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7744 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7745 }
7746
7747 $dbo = JFactory::getDbo();
7748 $app = JFactory::getApplication();
7749
7750 $ids = VikRequest::getVar('cid', array(0));
7751 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
7752
7753 $user = JFactory::getUser();
7754 $config = VBOFactory::getConfig();
7755
7756 $prev_conf_ids = [];
7757 $purged = false;
7758
7759 $tot_cancs = 0;
7760
7761 if (is_array($ids) && count($ids)) {
7762 foreach ($ids as $d) {
7763 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $dbo->quote($d);
7764 $dbo->setQuery($q, 0, 1);
7765 $row = $dbo->loadAssoc();
7766
7767 // check for any cancellation constraints
7768 $canc_denied = false;
7769 if ($row && class_exists('VCMFeesCancellation')) {
7770 // let VCM detect if there are any constraints for the cancellation
7771 $canc_denied = VCMFeesCancellation::getInstance($row, $anew = true)->isBookingConstrained();
7772 if ($canc_denied) {
7773 // set error message
7774 $canc_deny_error = VCMFeesCancellation::getInstance()->getError();
7775 if ($canc_deny_error) {
7776 $app->enqueueMessage($canc_deny_error, 'error');
7777 }
7778 }
7779 }
7780
7781 if ($row && !$canc_denied) {
7782 // increase counter
7783 $tot_cancs++;
7784
7785 // set status to cancelled
7786 if ($row['status'] != 'cancelled') {
7787 $q = "UPDATE `#__vikbooking_orders` SET `status`='cancelled' WHERE `id`=".(int)$row['id'].";";
7788 $dbo->setQuery($q);
7789 $dbo->execute();
7790 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($row['id']) . ";";
7791 $dbo->setQuery($q);
7792 $dbo->execute();
7793 if ($row['status'] == 'confirmed') {
7794 $prev_conf_ids[] = $row['id'];
7795 }
7796 // Booking History
7797 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('CB', "({$user->name})");
7798 }
7799
7800 // free records up
7801 $q = "SELECT * FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
7802 $dbo->setQuery($q);
7803 $ordbusy = $dbo->loadAssocList();
7804 if ($ordbusy) {
7805 foreach ($ordbusy as $ob) {
7806 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`='".$ob['idbusy']."';";
7807 $dbo->setQuery($q);
7808 $dbo->execute();
7809 }
7810 }
7811
7812 $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
7813 $dbo->setQuery($q);
7814 $dbo->execute();
7815
7816 // check for purge removal
7817 if ($row['status'] == 'cancelled') {
7818 $q = "DELETE FROM `#__vikbooking_customers_orders` WHERE `idorder`=" . intval($row['id']) . ";";
7819 $dbo->setQuery($q);
7820 $dbo->execute();
7821 $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `idorder`=".(int)$row['id'].";";
7822 $dbo->setQuery($q);
7823 $dbo->execute();
7824 $q = "DELETE FROM `#__vikbooking_orderhistory` WHERE `idorder`=".(int)$row['id'].";";
7825 $dbo->setQuery($q);
7826 $dbo->execute();
7827 $q = "DELETE FROM `#__vikbooking_orders` WHERE `id`=".(int)$row['id'].";";
7828 $dbo->setQuery($q);
7829 $dbo->execute();
7830 // in case of split stay booking, remove the transient
7831 if ($row['split_stay']) {
7832 $config->remove('split_stay_' . $row['id']);
7833 }
7834 // turn flag on
7835 $purged = true;
7836 }
7837 }
7838 }
7839
7840 if ($tot_cancs) {
7841 // enqueue system message
7842 $app->enqueueMessage(JText::translate('VBMESSDELBUSY'));
7843 }
7844 }
7845
7846 if ($prev_conf_ids) {
7847 $prev_conf_ids_str = '';
7848 foreach ($prev_conf_ids as $prev_id) {
7849 $prev_conf_ids_str .= '&cid[]='.$prev_id;
7850 }
7851 //Invoke Channel Manager
7852 $vcm_autosync = VikBooking::vcmAutoUpdate();
7853 if ($vcm_autosync > 0) {
7854 $vcm_obj = VikBooking::getVcmInvoker();
7855 $vcm_obj->setOids($prev_conf_ids)->setSyncType('cancel');
7856 $sync_result = $vcm_obj->doSync();
7857 if ($sync_result === false) {
7858 $vcm_err = $vcm_obj->getError();
7859 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
7860 }
7861 } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
7862 $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');
7863 VikError::raiseNotice('', JText::translate('VBCHANNELMANAGERINVOKEASK').' <button type="button" class="btn btn-primary" onclick="document.location.href=\''.$vcm_sync_url.'\';">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button>');
7864 }
7865 //
7866 }
7867
7868 if (!empty($pgoto)) {
7869 if (is_numeric($pgoto) && is_array($ids) && count($ids) === 1) {
7870 if ($purged) {
7871 // go back to the bookings list page
7872 $app->redirect("index.php?option=com_vikbooking&task=orders");
7873 } else {
7874 // go back to the booking details page
7875 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . (int)$ids[0]);
7876 }
7877 exit;
7878 }
7879 // we expect the goto URL to be base64 encoded
7880 $app->redirect(base64_decode($pgoto));
7881 exit;
7882 }
7883
7884 // go back to the bookings list page
7885 $app->redirect("index.php?option=com_vikbooking&task=orders");
7886 }
7887
7888 public function config() {
7889 VikBookingHelper::printHeader("11");
7890
7891 VikRequest::setVar('view', VikRequest::getCmd('view', 'config'));
7892
7893 parent::display();
7894
7895 if (VikBooking::showFooter()) {
7896 VikBookingHelper::printFooter();
7897 }
7898 }
7899
7900 public function saveconfig()
7901 {
7902 if (!JSession::checkToken()) {
7903 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7904 }
7905
7906 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking') || !JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
7907 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7908 }
7909
7910 $dbo = JFactory::getDbo();
7911 $app = JFactory::getApplication();
7912
7913 $config = VBOFactory::getConfig();
7914
7915 $pallowbooking = VikRequest::getString('allowbooking', '', 'request');
7916 $pdisabledbookingmsg = VikRequest::getString('disabledbookingmsg', '', 'request', VIKREQUEST_ALLOWHTML);
7917 $ptimeopenstorefh = VikRequest::getString('timeopenstorefh', '', 'request');
7918 $ptimeopenstorefm = VikRequest::getString('timeopenstorefm', '', 'request');
7919 $ptimeopenstoreth = VikRequest::getString('timeopenstoreth', '', 'request');
7920 $ptimeopenstoretm = VikRequest::getString('timeopenstoretm', '', 'request');
7921 $phoursmorebookingback = VikRequest::getString('hoursmorebookingback', '', 'request');
7922 $pdateformat = VikRequest::getString('dateformat', '', 'request');
7923 $pdatesep = VikRequest::getString('datesep', '', 'request');
7924 $pdatesep = empty($pdatesep) ? "/" : $pdatesep;
7925 $presmodcanc = VikRequest::getInt('resmodcanc', 1, 'request');
7926 $presmodcancmin = VikRequest::getInt('resmodcancmin', 1, 'request');
7927 $pshowcategories = VikRequest::getString('showcategories', '', 'request');
7928 $pshowchildren = VikRequest::getString('showchildren', '', 'request');
7929 $psearchsuggestions = VikRequest::getInt('searchsuggestions', '', 'request');
7930 $ptokenform = VikRequest::getString('tokenform', '', 'request');
7931 $padminemail = VikRequest::getString('adminemail', '', 'request');
7932 $psenderemail = VikRequest::getString('senderemail', '', 'request');
7933 $pminuteslock = VikRequest::getString('minuteslock', '', 'request');
7934 $pminautoremove = VikRequest::getInt('minautoremove', '', 'request');
7935 $pfooterordmail = VikRequest::getString('footerordmail', '', 'request', VIKREQUEST_ALLOWHTML);
7936 $ptermsconds = VikRequest::getString('termsconds', '', 'request', VIKREQUEST_ALLOWHTML);
7937 $prequirelogin = VikRequest::getString('requirelogin', '', 'request');
7938 $pautoroomunit = VikRequest::getInt('autoroomunit', '', 'request');
7939 $ptodaybookings = VikRequest::getInt('todaybookings', '', 'request');
7940 $ptodaybookings = $ptodaybookings === 1 ? 1 : 0;
7941 $ploadbootstrap = VikRequest::getInt('loadbootstrap', '', 'request');
7942 $ploadbootstrap = $ploadbootstrap === 1 ? 1 : 0;
7943 $pusefa = VikRequest::getInt('usefa', '', 'request');
7944 $pusefa = $pusefa > 0 ? 1 : 0;
7945 $ploadjquery = VikRequest::getString('loadjquery', '', 'request');
7946 $ploadjquery = $ploadjquery == "yes" ? "1" : "0";
7947 $pcalendar = VikRequest::getString('calendar', '', 'request');
7948 $pcalendar = $pcalendar == "joomla" ? "joomla" : "jqueryui";
7949 $penablecoupons = VikRequest::getString('enablecoupons', '', 'request');
7950 $penablecoupons = $penablecoupons == "1" ? 1 : 0;
7951 $penablepin = VikRequest::getString('enablepin', '', 'request');
7952 $penablepin = $penablepin == "1" ? 1 : 0;
7953 $pmindaysadvance = VikRequest::getInt('mindaysadvance', '', 'request');
7954 $pmindaysadvance = $pmindaysadvance < 0 ? 0 : $pmindaysadvance;
7955 $pautodefcalnights = VikRequest::getInt('autodefcalnights', '', 'request');
7956 $pautodefcalnights = $pautodefcalnights >= 1 ? $pautodefcalnights : '1';
7957 $pnumrooms = VikRequest::getInt('numrooms', '', 'request');
7958 $pnumrooms = $pnumrooms > 0 ? $pnumrooms : '5';
7959 $pnumadultsfrom = VikRequest::getString('numadultsfrom', '', 'request');
7960 $pnumadultsfrom = intval($pnumadultsfrom) >= 0 ? $pnumadultsfrom : '1';
7961 $pnumadultsto = VikRequest::getString('numadultsto', '', 'request');
7962 $pnumadultsto = intval($pnumadultsto) > 0 ? $pnumadultsto : '10';
7963 if (intval($pnumadultsfrom) > intval($pnumadultsto)) {
7964 $pnumadultsfrom = '1';
7965 $pnumadultsto = '10';
7966 }
7967 $pnumchildrenfrom = VikRequest::getString('numchildrenfrom', '', 'request');
7968 $pnumchildrenfrom = intval($pnumchildrenfrom) >= 0 ? $pnumchildrenfrom : '1';
7969 $pnumchildrento = VikRequest::getString('numchildrento', '', 'request');
7970 $pnumchildrento = intval($pnumchildrento) > 0 ? $pnumchildrento : '4';
7971 if (intval($pnumchildrenfrom) > intval($pnumchildrento)) {
7972 $pnumadultsfrom = '1';
7973 $pnumadultsto = '4';
7974 }
7975 $confnumadults = $pnumadultsfrom.'-'.$pnumadultsto;
7976 $confnumchildren = $pnumchildrenfrom.'-'.$pnumchildrento;
7977 $pmaxdate = VikRequest::getString('maxdate', '', 'request');
7978 $pmaxdate = intval($pmaxdate) < 1 ? 2 : $pmaxdate;
7979 $pmaxdateinterval = VikRequest::getString('maxdateinterval', '', 'request');
7980 $pmaxdateinterval = !in_array($pmaxdateinterval, array('d', 'w', 'm', 'y')) ? 'y' : $pmaxdateinterval;
7981 $maxdate_str = '+'.$pmaxdate.$pmaxdateinterval;
7982 $pcronkey = VikRequest::getString('cronkey', '', 'request');
7983 $pcdsfrom = VikRequest::getVar('cdsfrom', array());
7984 $pcdsto = VikRequest::getVar('cdsto', array());
7985 $closing_dates = array();
7986 if (count($pcdsfrom)) {
7987 foreach ($pcdsfrom as $kcd => $vcdfrom) {
7988 if (!empty($vcdfrom) && array_key_exists($kcd, $pcdsto) && !empty($pcdsto[$kcd])) {
7989 $tscdfrom = VikBooking::getDateTimestamp($vcdfrom, '0', '0');
7990 $tscdto = VikBooking::getDateTimestamp($pcdsto[$kcd], '0', '0');
7991 if (!empty($tscdfrom) && !empty($tscdto) && $tscdto >= $tscdfrom) {
7992 $cdval = array('from' => $tscdfrom, 'to' => $tscdto);
7993 if (!in_array($cdval, $closing_dates)) {
7994 $closing_dates[] = $cdval;
7995 }
7996 }
7997 }
7998 }
7999 }
8000 $psmartsearch = VikRequest::getString('smartsearch', '', 'request');
8001 $psmartsearch = $psmartsearch == "dynamic" ? "dynamic" : "automatic";
8002 $pvbosef = VikRequest::getInt('vbosef', '', 'request');
8003 $vbosef = file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php');
8004 if ($pvbosef === 1) {
8005 if (!$vbosef) {
8006 rename(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'_router.php', VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php');
8007 }
8008 } else {
8009 if ($vbosef) {
8010 rename(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php', VBO_SITE_PATH.DIRECTORY_SEPARATOR.'_router.php');
8011 }
8012 }
8013 $pmultilang = VikRequest::getString('multilang', '', 'request');
8014 $pmultilang = $pmultilang == "1" ? 1 : 0;
8015 $pvcmautoupd = VikRequest::getInt('vcmautoupd', '', 'request');
8016 $pvcmautoupd = $pvcmautoupd > 0 ? 1 : 0;
8017 /**
8018 * Chat params and configuration settings
8019 *
8020 * @since 1.12
8021 */
8022 $pchatenabled = VikRequest::getInt('chatenabled', 0, 'request');
8023 if (is_file(VCM_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'lib.vikchannelmanager.php')) {
8024 $config->set('chatenabled', $pchatenabled);
8025
8026 // chat params
8027 $pchat_res_status = explode(';', VikRequest::getString('chat_res_status', '', 'request'));
8028 $chat_res_status = array();
8029 foreach ($pchat_res_status as $chatrs) {
8030 if (!empty($chatrs)) {
8031 array_push($chat_res_status, $chatrs);
8032 }
8033 }
8034 $chatparams = new stdClass;
8035 $chatparams->res_status = $chat_res_status;
8036 $chatparams->av_type = VikRequest::getString('chat_av_type', '', 'request');
8037 $chatparams->av_days = VikRequest::getInt('chat_av_days', 0, 'request');
8038
8039 $config->set('chatparams', $chatparams);
8040 }
8041
8042 /**
8043 * Pre check-in configuration settings
8044 *
8045 * @since 1.12
8046 */
8047 $pprecheckinenabled = VikRequest::getInt('precheckinenabled', 0, 'request');
8048 $pprecheckinenabled = $pprecheckinenabled > 0 ? 1 : 0;
8049
8050 $config->set('precheckinenabled', $pprecheckinenabled);
8051 // this may be a negative integer, it should not be unsigned
8052 $config->set('precheckinminoffset', VikRequest::getInt('precheckinminoffset', 0, 'request'));
8053
8054 $pupsellingenabled = VikRequest::getInt('upsellingenabled', 0, 'request');
8055 $pupsellingenabled = $pupsellingenabled > 0 ? 1 : 0;
8056 $config->set('upselling', $pupsellingenabled);
8057
8058 $porphanscal = VikRequest::getString('orphanscal', 'next', 'request');
8059 $porphanscal = $porphanscal == 'prevnext' ? 'prevnext' : 'next';
8060 $config->set('orphanscalculation', $porphanscal);
8061
8062 $psrcrtpl = VikRequest::getString('srcrtpl', 'compact', 'request');
8063 $config->set('searchrestmpl', $psrcrtpl);
8064
8065 /**
8066 * Guest Reviews settings
8067 *
8068 * @since 1.13
8069 */
8070 $pgrenabled = VikRequest::getInt('grenabled', 0, 'request');
8071 $pgrminchars = VikRequest::getInt('grminchars', 0, 'request');
8072 $pgrappr = VikRequest::getString('grappr', 'auto', 'request');
8073 $pgrappr = $pgrappr == 'auto' ? 'auto' : 'manual';
8074 $pgrtype = VikRequest::getString('grtype', 'service', 'request');
8075 $pgrtype = $pgrtype == 'service' ? 'service' : 'global';
8076 $pgrsrv = VikRequest::getVar('grsrv', array(), 'request', 'array');
8077 $config->set('grenabled', $pgrenabled);
8078 $config->set('grminchars', $pgrminchars);
8079 $config->set('grappr', $pgrappr);
8080 $config->set('grtype', $pgrtype);
8081 try {
8082 // always truncate service names (this query may require special permissions)
8083 $q = "TRUNCATE TABLE `#__vikbooking_greview_service`;";
8084 $dbo->setQuery($q);
8085 $dbo->execute();
8086 } catch (Exception $e) {
8087 // do nothing
8088 }
8089 foreach ($pgrsrv as $srvname) {
8090 $q = "INSERT INTO `#__vikbooking_greview_service` (`service_name`) VALUES (" . $dbo->quote($srvname) . ");";
8091 $dbo->setQuery($q);
8092 $dbo->execute();
8093 }
8094
8095 /**
8096 * Preferred countries ordering, or custom countries.
8097 *
8098 * @since 1.14 (J) - 1.3.11 (WP)
8099 * @since 1.14.1 (J) - 1.4.1 (WP) we also support "cust_pref_countries"
8100 */
8101 $pref_countries = VikRequest::getVar('pref_countries', array());
8102 $cust_pref_countries = VikRequest::getString('cust_pref_countries', '', 'request');
8103 $pref_countries = !is_array($pref_countries) || empty($pref_countries[0]) ? VikBooking::preferredCountriesOrdering() : $pref_countries;
8104 if (!empty($cust_pref_countries)) {
8105 $all_custom_prefcountries = array();
8106 $cust_pref_countries = explode(',', $cust_pref_countries);
8107 foreach ($cust_pref_countries as $cust_pref_country) {
8108 $cust_pref_country = trim(strtolower($cust_pref_country));
8109 if (empty($cust_pref_country) || strlen($cust_pref_country) != 2) {
8110 continue;
8111 }
8112 array_push($all_custom_prefcountries, $cust_pref_country);
8113 }
8114 if (count($all_custom_prefcountries)) {
8115 $pref_countries = $all_custom_prefcountries;
8116 }
8117 }
8118 $config->set('preferred_countries', $pref_countries);
8119 //
8120
8121 $gmapskey = VikRequest::getString('gmapskey', '', 'request');
8122 $config->set('gmapskey', $gmapskey);
8123
8124 $pref_textcolor = VikRequest::getString('pref_textcolor', '', 'request');
8125 $pref_bgcolor = VikRequest::getString('pref_bgcolor', '', 'request');
8126 $pref_fontcolor = VikRequest::getString('pref_fontcolor', '', 'request');
8127 $pref_bgcolorhov = VikRequest::getString('pref_bgcolorhov', '', 'request');
8128 $pref_fontcolorhov = VikRequest::getString('pref_fontcolorhov', '', 'request');
8129 $pref_colors = array(
8130 'textcolor' => $pref_textcolor,
8131 'bgcolor' => $pref_bgcolor,
8132 'fontcolor' => $pref_fontcolor,
8133 'bgcolorhov' => $pref_bgcolorhov,
8134 'fontcolorhov' => $pref_fontcolorhov,
8135 );
8136 $config->set('pref_colors', $pref_colors);
8137
8138 $interactive_map = VikRequest::getInt('interactive_map', 0, 'request');
8139 $config->set('interactive_map', $interactive_map);
8140 $config->set('search_filters', VikRequest::getInt('search_filters', 0, 'request'));
8141
8142 $noemptydecimals = VikRequest::getInt('noemptydecimals', 0, 'request');
8143 $config->set('noemptydecimals', $noemptydecimals);
8144
8145 /**
8146 * Appearance preferences (light, auto, dark mode).
8147 *
8148 * @since 1.15.0 (J) - 1.5.0 (WP)
8149 * @since 1.16.10 (J) - 1.6.10 (WP) mirrored on VCM.
8150 */
8151 $appearance_pref = VikRequest::getString('appearance_pref', '');
8152 $config->set('appearance_pref', $appearance_pref);
8153 if (class_exists('VCMFactory')) {
8154 VCMFactory::getConfig()->set('appearance_pref', $appearance_pref);
8155 }
8156
8157 $res_backend_path = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR;
8158 $picon = "";
8159 if (intval($_FILES['sitelogo']['error']) == 0 && trim($_FILES['sitelogo']['name'])!="") {
8160 jimport('joomla.filesystem.file');
8161 if (@is_uploaded_file($_FILES['sitelogo']['tmp_name'])) {
8162 $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['sitelogo']['name'])));
8163 if (file_exists($res_backend_path.$safename)) {
8164 $j = 1;
8165 while (file_exists($res_backend_path.$j.$safename)) {
8166 $j++;
8167 }
8168 $pwhere = $res_backend_path.$j.$safename;
8169 } else {
8170 $j = "";
8171 $pwhere = $res_backend_path.$safename;
8172 }
8173 if (!getimagesize($_FILES['sitelogo']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
8174 @unlink($pwhere);
8175 $picon = "";
8176 } else {
8177 VikBooking::uploadFile($_FILES['sitelogo']['tmp_name'], $pwhere);
8178 @chmod($pwhere, 0644);
8179 $picon = $j.$safename;
8180 }
8181 }
8182 if (!empty($picon)) {
8183 $config->set('sitelogo', $picon);
8184 }
8185 }
8186 $pbackicon = "";
8187 if (intval($_FILES['backlogo']['error']) == 0 && trim($_FILES['backlogo']['name'])!="") {
8188 jimport('joomla.filesystem.file');
8189 if (@is_uploaded_file($_FILES['backlogo']['tmp_name'])) {
8190 $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['backlogo']['name'])));
8191 if (file_exists($res_backend_path.$safename)) {
8192 $j = 1;
8193 while (file_exists($res_backend_path.$j.$safename)) {
8194 $j++;
8195 }
8196 $pwhere = $res_backend_path.$j.$safename;
8197 } else {
8198 $j = "";
8199 $pwhere = $res_backend_path.$safename;
8200 }
8201 if (!getimagesize($_FILES['backlogo']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
8202 @unlink($pwhere);
8203 $pbackicon = "";
8204 } else {
8205 VikBooking::uploadFile($_FILES['backlogo']['tmp_name'], $pwhere);
8206 @chmod($pwhere, 0644);
8207 $pbackicon = $j.$safename;
8208 }
8209 }
8210 if (!empty($pbackicon)) {
8211 $config->set('backlogo', $pbackicon);
8212 }
8213 }
8214 $config->set('vcmautoupd', $pvcmautoupd);
8215 $config->set('allowbooking', empty($pallowbooking) || $pallowbooking != "1" ? 0 : 1);
8216 $config->set('showcategories', empty($pshowcategories) || $pshowcategories != "yes" ? 0 : 1);
8217 $config->set('showchildren', empty($pshowchildren) || $pshowchildren != "yes" ? 0 : 1);
8218 $config->set('searchsuggestions', $psearchsuggestions);
8219 $config->set('tokenform', empty($ptokenform) || $ptokenform != "yes" ? 0 : 1);
8220 $config->set('guests_label', $app->input->getString('guests_label', 'adults'));
8221 $config->set('search_show_busy_listings', $app->input->getInt('search_show_busy_listings', 0));
8222 $config->set('search_link_roomdetails', $app->input->getInt('search_link_roomdetails', 0));
8223
8224 // translatable text
8225 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pfooterordmail)." WHERE `param`='footerordmail';";
8226 $dbo->setQuery($q);
8227 $dbo->execute();
8228
8229 // translatable text
8230 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pdisabledbookingmsg)." WHERE `param`='disabledbookingmsg';";
8231 $dbo->setQuery($q);
8232 $dbo->execute();
8233
8234 // translatable text
8235 $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($app->input->getString('guests_allowed_policy', '', 'raw')) . " WHERE `param`='guests_allowed_policy';";
8236 $dbo->setQuery($q);
8237 $dbo->execute();
8238
8239 // terms and conditions
8240 $q = "SELECT `id`,`setting` FROM `#__vikbooking_texts` WHERE `param`='termsconds';";
8241 $dbo->setQuery($q);
8242 $dbo->execute();
8243 if ($dbo->getNumRows() > 0) {
8244 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($ptermsconds)." WHERE `param`='termsconds';";
8245 $dbo->setQuery($q);
8246 $dbo->execute();
8247 } else {
8248 $q = "INSERT INTO `#__vikbooking_texts` (`param`,`exp`,`setting`) VALUES ('termsconds','Terms and Conditions',".$dbo->quote($ptermsconds).");";
8249 $dbo->setQuery($q);
8250 $dbo->execute();
8251 }
8252
8253 $config->set('adminemail', $padminemail);
8254 $config->set('senderemail', $psenderemail);
8255 $config->set('dateformat', empty($pdateformat) ? "%d/%m/%Y" : $pdateformat);
8256 $config->set('datesep', $pdatesep);
8257 $config->set('resmodcanc', $presmodcanc);
8258 $config->set('resmodcancmin', $presmodcancmin);
8259 $config->set('minuteslock', $pminuteslock);
8260 $config->set('minautoremove', $pminautoremove);
8261
8262 $openingh = $ptimeopenstorefh * 3600;
8263 $openingm = $ptimeopenstorefm * 60;
8264 $openingts = $openingh + $openingm;
8265 $closingh = $ptimeopenstoreth * 3600;
8266 $closingm = $ptimeopenstoretm * 60;
8267 $closingts = $closingh + $closingm;
8268 // check if the check-in/out times have changed and if there are future bookings with the old time to prevent availability errors
8269 $prevtimes = $config->get('timeopenstore', '');
8270 if ($prevtimes != $openingts . "-" . $closingts) {
8271 $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `checkout`>".time().";";
8272 $dbo->setQuery($q);
8273 $dbo->execute();
8274 if ($dbo->getNumRows() > 0) {
8275 VikError::raiseWarning('', JText::translate('VBOCONFIGWARNDIFFCHECKINOUT'));
8276 /**
8277 * VBO 1.10 Patch - we concatenate a button to unify the check-in/out times
8278 * for all reservations to avoid issues with the availability.
8279 *
8280 * @since August 29th 2018
8281 */
8282 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>');
8283 //
8284 }
8285 }
8286 $config->set('timeopenstore', $openingts . "-" . $closingts);
8287
8288 // set the hours of extended gratuity period to the difference between checkin and checkout if checkout is later
8289 $phoursmorebookingback = "0";
8290 if ($closingts > $openingts) {
8291 $diffcheck = ($closingts - $openingts) / 3600;
8292 $phoursmorebookingback = ceil($diffcheck);
8293 }
8294 $config->set('hoursmorebookingback', $phoursmorebookingback);
8295 $config->set('hoursmoreroomavail', '0');
8296 $config->set('multilang', $pmultilang);
8297 $config->set('requirelogin', $prequirelogin == "1" ? 1 : 0);
8298 $config->set('autoroomunit', $pautoroomunit ? 1 : 0);
8299 $config->set('todaybookings', $ptodaybookings);
8300 $config->set('bootstrap', $ploadbootstrap);
8301 $config->set('usefa', $pusefa);
8302 $config->set('loadjquery', $ploadjquery);
8303 $config->set('calendar', $pcalendar ?: 'jqueryui');
8304 $config->set('dboptimizetime', $app->input->getString('dboptimizetime', ''));
8305 $config->set('enablecoupons', $penablecoupons);
8306 $config->set('enablepin', $penablepin);
8307 $config->set('mindaysadvance', $pmindaysadvance);
8308 $config->set('autodefcalnights', $pautodefcalnights);
8309 $config->set('numrooms', $pnumrooms);
8310 $config->set('numadults', $confnumadults);
8311 $config->set('numchildren', $confnumchildren);
8312 $config->set('closingdates', $closing_dates);
8313 $config->set('smartsearch', $psmartsearch);
8314 $config->set('maxdate', $maxdate_str);
8315 $config->set('cronkey', $pcronkey);
8316
8317 $pfronttitle = VikRequest::getString('fronttitle', '', 'request');
8318 $pfronttitletag = VikRequest::getString('fronttitletag', '', 'request');
8319 $pfronttitletagclass = VikRequest::getString('fronttitletagclass', '', 'request');
8320 $pshowfooter = VikRequest::getString('showfooter', '', 'request');
8321 $pintromain = VikRequest::getString('intromain', '', 'request', VIKREQUEST_ALLOWHTML);
8322 $pclosingmain = VikRequest::getString('closingmain', '', 'request', VIKREQUEST_ALLOWHTML);
8323 $pcurrencyname = VikRequest::getString('currencyname', '', 'request', VIKREQUEST_ALLOWHTML);
8324 $pcurrencysymb = VikRequest::getString('currencysymb', '', 'request', VIKREQUEST_ALLOWHTML);
8325 $pcurrencycodepp = VikRequest::getString('currencycodepp', '', 'request');
8326 $pnumdecimals = VikRequest::getString('numdecimals', '', 'request');
8327 $pnumdecimals = intval($pnumdecimals);
8328 $pdecseparator = VikRequest::getString('decseparator', '', 'request');
8329 $pdecseparator = empty($pdecseparator) ? '.' : $pdecseparator;
8330 $pthoseparator = VikRequest::getString('thoseparator', '', 'request');
8331 $numberformatstr = $pnumdecimals.':'.$pdecseparator.':'.$pthoseparator;
8332 $pshowpartlyreserved = VikRequest::getString('showpartlyreserved', '', 'request');
8333 $pshowpartlyreserved = $pshowpartlyreserved == "yes" ? 1 : 0;
8334 $pshowcheckinoutonly = VikRequest::getInt('showcheckinoutonly', '', 'request');
8335 $pshowcheckinoutonly = $pshowcheckinoutonly > 0 ? 1 : 0;
8336 $pnumcalendars = VikRequest::getInt('numcalendars', '', 'request');
8337 $pnumcalendars = $pnumcalendars > -1 ? $pnumcalendars : 3;
8338 $pthumbsize = VikRequest::getInt('thumbsize', 0, 'request');
8339 $pfirstwday = VikRequest::getString('firstwday', '', 'request');
8340 $pfirstwday = intval($pfirstwday) >= 0 && intval($pfirstwday) <= 6 ? $pfirstwday : '0';
8341 $pbctagname = VikRequest::getVar('bctagname', array());
8342 $pbctagcolor = VikRequest::getVar('bctagcolor', array());
8343 $pbctagrule = VikRequest::getVar('bctagrule', array());
8344 $bctags_arr = array();
8345 $bctags_rules = array();
8346 if (count($pbctagname) > 0) {
8347 foreach ($pbctagname as $bctk => $bctv) {
8348 if (!empty($bctv) && !empty($pbctagcolor[$bctk]) && strlen($pbctagrule[$bctk]) > 0) {
8349 if (intval($pbctagrule[$bctk]) == 0 || !in_array($pbctagrule[$bctk], $bctags_rules)) {
8350 $bctags_rules[] = $pbctagrule[$bctk];
8351 $bctags_arr[] = array('color' => $pbctagcolor[$bctk], 'name' => $bctv, 'rule' => $pbctagrule[$bctk]);
8352 }
8353 }
8354 }
8355 }
8356 //theme
8357 $ptheme = VikRequest::getString('theme', '', 'request');
8358 if (empty($ptheme) || $ptheme == 'default') {
8359 $ptheme = 'default';
8360 } else {
8361 $validtheme = false;
8362 $themes = glob(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'themes'.DIRECTORY_SEPARATOR.'*');
8363 if (count($themes) > 0) {
8364 $strip = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'themes'.DIRECTORY_SEPARATOR;
8365 foreach ($themes as $th) {
8366 if (is_dir($th)) {
8367 $tname = str_replace($strip, '', $th);
8368 if ($tname == $ptheme) {
8369 $validtheme = true;
8370 break;
8371 }
8372 }
8373 }
8374 }
8375 if ($validtheme == false) {
8376 $ptheme = 'default';
8377 }
8378 }
8379 $config->set('theme', $ptheme);
8380 //
8381 $config->set('showpartlyreserved', $pshowpartlyreserved);
8382 $config->set('showcheckinoutonly', $pshowcheckinoutonly);
8383 $config->set('numcalendars', $pnumcalendars);
8384
8385 // record may not be set
8386 $config->set('thumbsize', $pthumbsize);
8387
8388 $config->set('firstwday', $pfirstwday);
8389
8390 // translatable text
8391 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pfronttitle)." WHERE `param`='fronttitle';";
8392 $dbo->setQuery($q);
8393 $dbo->execute();
8394
8395 $config->set('fronttitletag', $pfronttitletag);
8396 $config->set('fronttitletagclass', $pfronttitletagclass);
8397 $config->set('showfooter', empty($pshowfooter) || $pshowfooter != "yes" ? 0 : 1);
8398
8399 // translatable texts
8400 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pintromain)." WHERE `param`='intromain';";
8401 $dbo->setQuery($q);
8402 $dbo->execute();
8403 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pclosingmain)." WHERE `param`='closingmain';";
8404 $dbo->setQuery($q);
8405 $dbo->execute();
8406
8407 $config->set('currencyname', $pcurrencyname);
8408 $config->set('currencysymb', $pcurrencysymb);
8409 $config->set('currencypos', $app->input->getAlnum('currencypos', 'before'));
8410 $config->set('currencycodepp', $pcurrencycodepp);
8411 $config->set('numberformat', $numberformatstr);
8412 // bookings color tags
8413 $config->set('bookingsctags', $bctags_arr);
8414
8415 $pivainclusa = VikRequest::getString('ivainclusa', '', 'request');
8416 $ptaxsummary = VikRequest::getString('taxsummary', '', 'request');
8417 $ptaxsummary = empty($ptaxsummary) || $ptaxsummary != "yes" ? "0" : "1";
8418 $pccpaypal = VikRequest::getString('ccpaypal', '', 'request');
8419 $ppaytotal = VikRequest::getString('paytotal', '', 'request');
8420 $ppayaccpercent = VikRequest::getString('payaccpercent', '', 'request');
8421 $ptypedeposit = VikRequest::getString('typedeposit', '', 'request');
8422 $ptypedeposit = $ptypedeposit == 'fixed' ? 'fixed' : 'pcent';
8423 $pdepoverrides = VikRequest::getString('depoverrides', '', 'request');
8424 $ppaymentname = VikRequest::getString('paymentname', '', 'request');
8425 $pdisclaimer = VikRequest::getString('disclaimer', '', 'request', VIKREQUEST_ALLOWHTML);
8426 $pmultipay = VikRequest::getString('multipay', '', 'request');
8427 $pmultipay = $pmultipay == "yes" ? 1 : 0;
8428 $pdepifdaysadv = VikRequest::getInt('depifdaysadv', '', 'request');
8429 $pnodepnonrefund = VikRequest::getInt('nodepnonrefund', '', 'request');
8430 $pdepcustchoice = VikRequest::getString('depcustchoice', '', 'request');
8431 $pdepcustchoice = $pdepcustchoice == "yes" ? 1 : 0;
8432
8433 // translatable text
8434 $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($ppaymentname) . " WHERE `param`='paymentname';";
8435 $dbo->setQuery($q);
8436 $dbo->execute();
8437 $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($pdisclaimer) . " WHERE `param`='disclaimer';";
8438 $dbo->setQuery($q);
8439 $dbo->execute();
8440
8441 $config->set('ivainclusa', empty($pivainclusa) || $pivainclusa != "yes" ? 0 : 1);
8442 $config->set('taxsummary', $ptaxsummary);
8443 $config->set('paytotal', empty($ppaytotal) || $ppaytotal != "yes" ? 0 : 1);
8444
8445 $config->set('ccpaypal', $pccpaypal);
8446 $config->set('payaccpercent', $ppayaccpercent);
8447 $config->set('typedeposit', $ptypedeposit);
8448 $config->set('depoverrides', $pdepoverrides);
8449 $config->set('multipay', $pmultipay);
8450 $config->set('depifdaysadv', $pdepifdaysadv);
8451 $config->set('nodepnonrefund', $pnodepnonrefund);
8452 $config->set('depcustchoice', $pdepcustchoice);
8453 $config->set('depbalancedays', $app->input->getInt('depbalancedays', null));
8454
8455 $psendemailwhen = VikRequest::getInt('sendemailwhen', '', 'request');
8456 $psendemailwhen = $psendemailwhen > 1 ? 2 : 1;
8457 $pattachical = VikRequest::getInt('attachical', 0, 'request');
8458 $pattachical = $pattachical >= 0 && $pattachical <= 3 ? $pattachical : 1;
8459 $config->set('emailsendwhen', $psendemailwhen);
8460 $config->set('attachical', $pattachical);
8461
8462 // SMS APIs
8463 $psmsapi = VikRequest::getString('smsapi', '', 'request');
8464 $psmsautosend = VikRequest::getString('smsautosend', '', 'request');
8465 $psmsautosend = intval($psmsautosend) > 0 ? 1 : 0;
8466 $psmssendto = VikRequest::getVar('smssendto', array());
8467 $sms_sendto = array();
8468 foreach ($psmssendto as $sto) {
8469 if (in_array($sto, array('admin', 'customer'))) {
8470 $sms_sendto[] = $sto;
8471 }
8472 }
8473 $psmssendwhen = VikRequest::getInt('smssendwhen', '', 'request');
8474 $psmssendwhen = $psmssendwhen > 1 ? 2 : 1;
8475 $psmsadminphone = VikRequest::getString('smsadminphone', '', 'request');
8476 $psmsadmintpl = VikRequest::getString('smsadmintpl', '', 'request', VIKREQUEST_ALLOWRAW);
8477 $psmscustomertpl = VikRequest::getString('smscustomertpl', '', 'request', VIKREQUEST_ALLOWRAW);
8478 $psmsadmintplpend = VikRequest::getString('smsadmintplpend', '', 'request', VIKREQUEST_ALLOWRAW);
8479 $psmscustomertplpend = VikRequest::getString('smscustomertplpend', '', 'request', VIKREQUEST_ALLOWRAW);
8480 $psmsadmintplcanc = VikRequest::getString('smsadmintplcanc', '', 'request', VIKREQUEST_ALLOWRAW);
8481 $psmscustomertplcanc = VikRequest::getString('smscustomertplcanc', '', 'request', VIKREQUEST_ALLOWRAW);
8482 $viksmsparams = VikRequest::getVar('viksmsparams', array());
8483 $smsparamarr = array();
8484 if (count($viksmsparams) > 0) {
8485 foreach ($viksmsparams as $setting => $cont) {
8486 if (strlen($setting) > 0) {
8487 $smsparamarr[$setting] = $cont;
8488 }
8489 }
8490 }
8491 $config->set('smsapi', $psmsapi);
8492 $config->set('smsautosend', $psmsautosend);
8493 $config->set('smssendto', $sms_sendto);
8494 $config->set('smssendwhen', $psmssendwhen);
8495 $config->set('smsadminphone', $psmsadminphone);
8496 $config->set('smsparams', $smsparamarr);
8497
8498 // translatable texts
8499 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintpl)." WHERE `param`='smsadmintpl';";
8500 $dbo->setQuery($q);
8501 $dbo->execute();
8502 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertpl)." WHERE `param`='smscustomertpl';";
8503 $dbo->setQuery($q);
8504 $dbo->execute();
8505 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintplpend)." WHERE `param`='smsadmintplpend';";
8506 $dbo->setQuery($q);
8507 $dbo->execute();
8508 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertplpend)." WHERE `param`='smscustomertplpend';";
8509 $dbo->setQuery($q);
8510 $dbo->execute();
8511 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintplcanc)." WHERE `param`='smsadmintplcanc';";
8512 $dbo->setQuery($q);
8513 $dbo->execute();
8514 $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertplcanc)." WHERE `param`='smscustomertplcanc';";
8515 $dbo->setQuery($q);
8516 $dbo->execute();
8517
8518 /**
8519 * Backup settings
8520 *
8521 * @since 1.15.0 (J) - 1.5.0 (WP)
8522 */
8523 $backup_type = $app->input->getString('backuptype', 'full');
8524 $backup_folder = $app->input->getString('backupfolder', '');
8525
8526 $tmp = $app->get('tmp_path');
8527
8528 if (!$backup_folder)
8529 {
8530 // path not specified, use temporary folder
8531 $backup_folder = $tmp;
8532 }
8533
8534 $current = $config->get('backupfolder');
8535
8536 if (!$current)
8537 {
8538 // path was missing, use temporary folder
8539 $current = $tmp;
8540 }
8541
8542 // check whether the backup folder has been moved
8543 if ($current && $backup_folder && rtrim($current, DIRECTORY_SEPARATOR) !== rtrim($backup_folder, DIRECTORY_SEPARATOR))
8544 {
8545 $backupModel = new VBOModelBackup();
8546
8547 // backup folder moved, try to copy all the existing overrides
8548 if (!$backupModel->moveArchives($backup_folder))
8549 {
8550 // iterate all errors and display them
8551 foreach ($backupModel->getErrors() as $error)
8552 {
8553 $app->enqueueMessage($error, 'warning');
8554 }
8555 }
8556 }
8557
8558 // save configuration
8559 $config->set('backuptype', $backup_type);
8560 $config->set('backupfolder', $backup_folder);
8561
8562 /**
8563 * Check-in data collection type.
8564 *
8565 * @since 1.15.0 (J) - 1.5.0 (WP)
8566 */
8567 $config->set('checkindata', VikRequest::getString('checkindata', 'basic', 'request'));
8568
8569 /**
8570 * Front-end appearance.
8571 *
8572 * @since 1.15.0 (J) - 1.5.0 (WP) (patch)
8573 */
8574 $config->set('appearance_front', VikRequest::getInt('appearance_front', 0, 'request'));
8575
8576 /**
8577 * Split stays.
8578 *
8579 * @since 1.16.0 (J) - 1.6.0 (WP)
8580 */
8581 $glob_split_stay = VikRequest::getInt('split_stay', 0, 'request');
8582 $split_stay_ratio = VikRequest::getFloat('split_stay_ratio', 0, 'request');
8583 $split_stay_ratio = $split_stay_ratio > 100 ? 100 : $split_stay_ratio;
8584 $config->set('split_stay_ratio', ($glob_split_stay && $split_stay_ratio > 0 ? $split_stay_ratio : 0));
8585
8586 /**
8587 * Re-build Web App manifest file to let the event trigger.
8588 *
8589 * @since 1.16.5 (J) - 1.6.5 (WP)
8590 */
8591 try {
8592 VBOWebappManifest::build();
8593 } catch (Exception $e) {
8594 // do nothing
8595 }
8596
8597 // redirect
8598 $app->enqueueMessage(JText::translate('VBSETTINGSAVED'));
8599 $app->redirect('index.php?option=com_vikbooking&task=config');
8600 $app->close();
8601 }
8602
8603 /**
8604 * Task to unify the check-in and check-out times for all reservations.
8605 */
8606 public function unifycheckinout()
8607 {
8608 $dbo = JFactory::getDbo();
8609 $app = JFactory::getApplication();
8610 $user = JFactory::getUser();
8611
8612 $fh = VikRequest::getInt('fh', 12, 'request');
8613 $fm = VikRequest::getInt('fm', 0, 'request');
8614 $th = VikRequest::getInt('th', 10, 'request');
8615 $tm = VikRequest::getInt('tm', 0, 'request');
8616
8617 $now = time();
8618 $totmod = 0;
8619 $totbookmod = 0;
8620
8621 // query all busy records
8622 $q = $dbo->getQuery(true)
8623 ->select('*')
8624 ->from($dbo->qn('#__vikbooking_busy'));
8625
8626 $dbo->setQuery($q);
8627 $records = $dbo->loadAssocList();
8628
8629 foreach ($records as $v) {
8630 $info_start = getdate($v['checkin']);
8631 $info_end = getdate($v['checkout']);
8632 $new_start = mktime($fh, $fm, 0, $info_start['mon'], $info_start['mday'], $info_start['year']);
8633 $new_end = mktime($th, $tm, 0, $info_end['mon'], $info_end['mday'], $info_end['year']);
8634
8635 $q = $dbo->getQuery(true)
8636 ->update($dbo->qn('#__vikbooking_busy'))
8637 ->set($dbo->qn('checkin') . ' = ' . $new_start)
8638 ->set($dbo->qn('checkout') . ' = ' . $new_end)
8639 ->set($dbo->qn('realback') . ' = ' . $new_end)
8640 ->where($dbo->qn('id') . ' = ' . (int)$v['id']);
8641
8642 $dbo->setQuery($q, 0, 1);
8643 $dbo->execute();
8644
8645 $totmod++;
8646 }
8647
8648 // query all bookings
8649 $q = $dbo->getQuery(true)
8650 ->select($dbo->qn([
8651 'id',
8652 'days',
8653 'checkin',
8654 'checkout',
8655 'total',
8656 ]))
8657 ->from($dbo->qn('#__vikbooking_orders'))
8658 ->order($dbo->qn('checkin') . ' DESC');
8659
8660 $dbo->setQuery($q);
8661 $records = $dbo->loadAssocList();
8662
8663 foreach ($records as $v) {
8664 $info_start = getdate($v['checkin']);
8665 $info_end = getdate($v['checkout']);
8666 $new_start = mktime($fh, $fm, 0, $info_start['mon'], $info_start['mday'], $info_start['year']);
8667 $new_end = mktime($th, $tm, 0, $info_end['mon'], $info_end['mday'], $info_end['year']);
8668
8669 $q = $dbo->getQuery(true)
8670 ->update($dbo->qn('#__vikbooking_orders'))
8671 ->set($dbo->qn('checkin') . ' = ' . $new_start)
8672 ->set($dbo->qn('checkout') . ' = ' . $new_end)
8673 ->where($dbo->qn('id') . ' = ' . (int)$v['id']);
8674
8675 $dbo->setQuery($q, 0, 1);
8676 $dbo->execute();
8677
8678 /**
8679 * In case the operation changed the check-in/check-out time for this booking,
8680 * store a new history record for a booking modification.
8681 *
8682 * @since 1.16.6 (J) - 1.6.6 (WP)
8683 */
8684 if ($v['checkout'] > $now && ($info_start['hours'] != $fh || $info_end['hours'] != $th)) {
8685 // Booking History
8686 VikBooking::getBookingHistoryInstance($v['id'])->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($v));
8687 }
8688
8689 $totbookmod++;
8690 }
8691
8692 $app->enqueueMessage('OK: ' . $totbookmod);
8693 $app->redirect("index.php?option=com_vikbooking&task=config");
8694 $app->close();
8695 }
8696
8697 public function savetmplfile()
8698 {
8699 $app = JFactory::getApplication();
8700
8701 if (!JSession::checkToken()) {
8702 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
8703 }
8704
8705 if (!JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
8706 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8707 }
8708
8709 $fpath = VikRequest::getString('path', '', 'request', VIKREQUEST_ALLOWRAW);
8710 $pcont = VikRequest::getString('cont', '', 'request', VIKREQUEST_ALLOWRAW);
8711 $pajax = VikRequest::getInt('ajax', 0, 'request');
8712
8713 // default status
8714 $result = [
8715 'status' => 0,
8716 'message' => 'Generic error',
8717 ];
8718
8719 $exists = file_exists($fpath) ? true : false;
8720 if (!$exists) {
8721 $fpath = urldecode($fpath);
8722 }
8723 $fpath = file_exists($fpath) ? $fpath : '';
8724 if (!empty($fpath)) {
8725 $fp = fopen($fpath, 'wb');
8726 $byt = (int) fwrite($fp, $pcont);
8727 fclose($fp);
8728 if ($byt > 0) {
8729 // success
8730 $result = [
8731 'status' => 1,
8732 'message' => JText::translate('VBOUPDTMPLFILEOK'),
8733 ];
8734
8735 if (VBOPlatformDetection::isWordPress()) {
8736 /**
8737 * @wponly call the UpdateManager Class to temporarily store modifications made to template files
8738 */
8739 VikBookingUpdateManager::storeTemplateContent($fpath, $pcont);
8740 }
8741 } else {
8742 // error
8743 $result = [
8744 'status' => 0,
8745 'message' => JText::translate('VBOUPDTMPLFILENOBYTES'),
8746 ];
8747 }
8748 } else {
8749 // error
8750 $result = [
8751 'status' => 0,
8752 'message' => JText::translate('VBOUPDTMPLFILEERR'),
8753 ];
8754 }
8755
8756 if ($pajax) {
8757 if ($result['status']) {
8758 VBOHttpDocument::getInstance($app)->json($result);
8759 } else {
8760 VBOHttpDocument::getInstance($app)->close(500, $result['message']);
8761 }
8762 } else {
8763 if ($result['status']) {
8764 $app->enqueueMessage($result['message']);
8765 } else {
8766 VikError::raiseWarning('', $result['message']);
8767 }
8768 }
8769
8770 $app->redirect("index.php?option=com_vikbooking&task=edittmplfile&path=".$fpath."&tmpl=component");
8771 $app->close();
8772 }
8773
8774 public function edittmplfile()
8775 {
8776 // this view should be rendered through AJAX
8777 VikRequest::setVar('view', VikRequest::getCmd('view', 'edittmplfile'));
8778
8779 if (JFactory::getApplication()->input->getBool('ajax') && VBOPlatformDetection::isJoomla()) {
8780 /**
8781 * @todo This needs to be changed for Joomla in the future versions.
8782 * Right now no HTML document tree is being added as an AJAX response, because
8783 * the View output is captured within a buffer, but the CodeMirror will not work.
8784 * In case the View was rendered normally and sent to output, the CodeMirror would
8785 * work fine, but the response appended to the modal body would contain HTML head tags
8786 * and so accessing the language definitions through JS would fail after the first response.
8787 * The solution for both Joomla and WordPress is probably to use a completely different endpoint
8788 * that returns just the file buffer/content, and maybe the file type, so that who makes the requests
8789 * can set the content and render the proper CodeMirror editor manually at runtime.
8790 */
8791
8792 // start output buffer
8793 ob_start();
8794
8795 try {
8796 // display view
8797 parent::display();
8798 } catch (Exception $e) {
8799 // clear output buffer
8800 ob_end_clean();
8801
8802 // raise error
8803 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
8804 }
8805
8806 // obtain view HTML from buffer
8807 $html = ob_get_contents();
8808
8809 // clear output buffer
8810 ob_end_clean();
8811
8812 // encode HTML in JSON to avoid encoding issues
8813 VBOHttpDocument::getInstance()->json(json_encode($html));
8814
8815 } else {
8816 // regular view display
8817 parent::display();
8818 }
8819 }
8820
8821 public function tmplfileprew() {
8822 //modal box, so we do not set menu or footer
8823
8824 VikRequest::setVar('view', VikRequest::getCmd('view', 'tmplfileprew'));
8825
8826 parent::display();
8827 }
8828
8829 public function invoices() {
8830 VikBookingHelper::printHeader("invoices");
8831
8832 VikRequest::setVar('view', VikRequest::getCmd('view', 'invoices'));
8833
8834 parent::display();
8835
8836 if (VikBooking::showFooter()) {
8837 VikBookingHelper::printFooter();
8838 }
8839 }
8840
8841 public function newmaninvoice() {
8842 VikBookingHelper::printHeader("invoices");
8843
8844 VikRequest::setVar('view', VikRequest::getCmd('view', 'managemaninvoice'));
8845
8846 parent::display();
8847
8848 if (VikBooking::showFooter()) {
8849 VikBookingHelper::printFooter();
8850 }
8851 }
8852
8853 public function editmaninvoice() {
8854 VikBookingHelper::printHeader("invoices");
8855
8856 VikRequest::setVar('view', VikRequest::getCmd('view', 'managemaninvoice'));
8857
8858 parent::display();
8859
8860 if (VikBooking::showFooter()) {
8861 VikBookingHelper::printFooter();
8862 }
8863 }
8864
8865 public function savemaninvoice() {
8866 if (!JSession::checkToken()) {
8867 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
8868 }
8869 $this->do_storemaninvoice('save');
8870 $mainframe = JFactory::getApplication();
8871 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8872 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8873 if (!empty($pgoto)) {
8874 $mainframe->redirect(base64_decode($pgoto));
8875 exit;
8876 }
8877 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
8878 }
8879
8880 public function updatemaninvoice()
8881 {
8882 if (!JSession::checkToken()) {
8883 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
8884 }
8885
8886 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
8887 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8888 }
8889
8890 $invid = VikRequest::getInt('whereup', 0, 'request');
8891 $this->do_storemaninvoice('update', $invid);
8892 $mainframe = JFactory::getApplication();
8893 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8894 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8895 if (!empty($pgoto)) {
8896 $mainframe->redirect(base64_decode($pgoto));
8897 exit;
8898 }
8899 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
8900 }
8901
8902 public function updatemaninvoicestay()
8903 {
8904 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
8905 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8906 }
8907
8908 $invid = VikRequest::getInt('whereup', 0, 'request');
8909 $this->do_storemaninvoice('updatestay', $invid);
8910 $mainframe = JFactory::getApplication();
8911 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8912 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8913 if (!empty($pgoto)) {
8914 $mainframe->redirect(base64_decode($pgoto));
8915 exit;
8916 }
8917 $mainframe->redirect("index.php?option=com_vikbooking&task=editmaninvoice&cid[]=".$invid);
8918 }
8919
8920 private function do_storemaninvoice($action, $invid = 0) {
8921 $dbo = JFactory::getDBO();
8922 $mainframe = JFactory::getApplication();
8923 $pinvoice_num = VikRequest::getInt('invoice_num', '', 'request');
8924 $pinvoice_num = $pinvoice_num <= 0 ? 1 : $pinvoice_num;
8925 $pinvoice_suff = VikRequest::getString('invoice_suff', '', 'request');
8926 $pcompany_info = VikRequest::getString('company_info', '', 'request', VIKREQUEST_ALLOWHTML);
8927 $pcompany_info = strpos($pcompany_info, '<') !== false ? $pcompany_info : nl2br($pcompany_info);
8928 $pinvoice_notes = VikRequest::getString('invoice_notes', '', 'request', VIKREQUEST_ALLOWHTML);
8929 $pinvoice_notes = strpos($pinvoice_notes, '<') !== false ? $pinvoice_notes : nl2br($pinvoice_notes);
8930 $pidcustomer = VikRequest::getInt('idcustomer', '', 'request');
8931 $error_uri = strpos($action, 'update') !== false && !empty($invid) ? 'index.php?option=com_vikbooking&task=editmaninvoice&cid[]='.$invid : 'index.php?option=com_vikbooking&task=newmaninvoice';
8932 if (empty($pidcustomer)) {
8933 VikError::raiseWarning('', JText::translate('VBNOCUSTOMERS'));
8934 $mainframe->redirect($error_uri);
8935 exit;
8936 }
8937 $services = VikRequest::getVar('service', array());
8938 $nets = VikRequest::getVar('net', array());
8939 $aliqs = VikRequest::getVar('aliq', array());
8940 $taxs = VikRequest::getVar('tax', array());
8941 $tots = VikRequest::getVar('tot', array());
8942 $ptotalnet = VikRequest::getFloat('totalnet', 0, 'request');
8943 $ptotaltax = VikRequest::getFloat('totaltax', 0, 'request');
8944 $ptotaltot = VikRequest::getFloat('totaltot', 0, 'request');
8945 if (!count($services) || count($services) != count($nets) || count($services) != count($taxs) || count($services) != count($tots)) {
8946 VikError::raiseWarning('', 'Missing data.');
8947 $mainframe->redirect($error_uri);
8948 exit;
8949 }
8950 $rawcont = array(
8951 'rows' => array(),
8952 'totalnet' => $ptotalnet,
8953 'totaltax' => $ptotaltax,
8954 'totaltot' => $ptotaltot,
8955 'notes' => $pinvoice_notes,
8956 );
8957 foreach ($services as $k => $service) {
8958 if (empty($service)) {
8959 continue;
8960 }
8961 array_push($rawcont['rows'], array(
8962 'service' => $service,
8963 'net' => (float)$nets[$k],
8964 'aliq' => (isset($aliqs[$k]) ? (float)$aliqs[$k] : 0),
8965 'tax' => (float)$taxs[$k],
8966 'tot' => (float)$tots[$k],
8967 ));
8968 }
8969 // store/update manual invoice
8970 $nowts = time();
8971 $retval = 0;
8972 if (strpos($action, 'save') !== false) {
8973 $pdffname = $nowts . '_' . rand() . '.pdf';
8974 $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)).");";
8975 $dbo->setQuery($q);
8976 $dbo->execute();
8977 $retval = $dbo->insertid();
8978 } else {
8979 // fetch old record
8980 $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id`=".(int)$invid.";";
8981 $dbo->setQuery($q);
8982 $dbo->execute();
8983 if (!$dbo->getNumRows()) {
8984 VikError::raiseWarning('', JText::translate('VBNOINVOICESFOUND'));
8985 $mainframe->redirect($error_uri);
8986 exit;
8987 }
8988 $previnvoice = $dbo->loadAssoc();
8989 //
8990 $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'].";";
8991 $dbo->setQuery($q);
8992 $dbo->execute();
8993 $retval = $previnvoice['id'];
8994 }
8995 // update config values for the invoice
8996 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pcompany_info)." WHERE `param`='invcompanyinfo';";
8997 $dbo->setQuery($q);
8998 $dbo->execute();
8999 // generate the custom invoice
9000 $result = VikBooking::generateCustomInvoice($retval);
9001 //
9002 $nextinv = VikBooking::getNextInvoiceNumber();
9003 $updatenum = ($pinvoice_num >= $nextinv);
9004 if ($updatenum) {
9005 /**
9006 * IMPORTANT: update the next invoice number after calling the e-Invocing drivers
9007 * to avoid conflicts with the drivers for the e-invoices generation.
9008 */
9009 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(($pinvoice_num - 1))." WHERE `param`='invoiceinum';";
9010 $dbo->setQuery($q);
9011 $dbo->execute();
9012 }
9013
9014 return $retval;
9015 }
9016
9017 public function downloadinvoices() {
9018 $ids = VikRequest::getVar('cid', array(0));
9019 if (@count($ids) > 0) {
9020 $dbo = JFactory::getDBO();
9021 $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id` IN (".implode(', ', $ids).");";
9022 $dbo->setQuery($q);
9023 $dbo->execute();
9024 if ($dbo->getNumRows() > 0) {
9025 $invoices = $dbo->loadAssocList();
9026 if (!(count($invoices) > 1)) {
9027 //Single Invoice Download
9028 if (file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoices[0]['file_name'])) {
9029 header("Content-type:application/pdf");
9030 header("Content-Disposition:attachment;filename=".$invoices[0]['file_name']);
9031 readfile(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoices[0]['file_name']);
9032 exit;
9033 }
9034 } else {
9035 //Multiple Invoices Download
9036 $to_zip = array();
9037 foreach ($invoices as $k => $invoice) {
9038 $to_zip[$k]['name'] = $invoice['file_name'];
9039 $to_zip[$k]['path'] = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoice['file_name'];
9040 }
9041 if (class_exists('ZipArchive')) {
9042 $zip_path = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.date('Y-m-d').'-invoices.zip';
9043 $zip = new ZipArchive;
9044 $zip->open($zip_path, ZipArchive::CREATE);
9045 foreach ($to_zip as $k => $zipv) {
9046 $zip->addFile($zipv['path'], $zipv['name']);
9047 }
9048 $zip->close();
9049 header("Content-type:application/zip");
9050 header("Content-Disposition:attachment;filename=".date('Y-m-d').'-invoices.zip');
9051 header("Content-Length:".filesize($zip_path));
9052 readfile($zip_path);
9053 unlink($zip_path);
9054 exit;
9055 } else {
9056 //Class ZipArchive does not exist
9057 VikError::raiseWarning('', 'Class ZipArchive does not exist on your server. Download the files one by one.');
9058 }
9059 }
9060 }
9061 }
9062 $mainframe = JFactory::getApplication();
9063 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9064 }
9065
9066 public function resendinvoices() {
9067 $ids = VikRequest::getVar('cid', array(0));
9068 $mainframe = JFactory::getApplication();
9069 if (!(count($ids) > 0)) {
9070 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9071 exit;
9072 }
9073 $dbo = JFactory::getDBO();
9074 $invoices = array();
9075 $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` ".
9076 "FROM `#__vikbooking_invoices` AS `i` " .
9077 "LEFT JOIN `#__vikbooking_orders` `o` ON `o`.`id`=`i`.`idorder` " .
9078 "LEFT JOIN `#__vikbooking_customers` `c` ON `c`.`id`=`i`.`idcustomer` " .
9079 "LEFT JOIN `#__vikbooking_countries` `nat` ON `nat`.`country_3_code`=`c`.`country` " .
9080 "WHERE `i`.`id` IN (".implode(', ', $ids).") AND (`i`.`idorder` < 0 OR (`o`.`status`='confirmed' AND `o`.`total` > 0)) ORDER BY `o`.`id` ASC;";
9081 $dbo->setQuery($q);
9082 $dbo->execute();
9083 if ($dbo->getNumRows() > 0) {
9084 $invoices = $dbo->loadAssocList();
9085 }
9086 if (!(count($invoices) > 0)) {
9087 VikError::raiseWarning('', JText::translate('VBOGENINVERRNOBOOKINGS'));
9088 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9089 exit;
9090 }
9091 $tot_generated = 0;
9092 $tot_sent = 0;
9093 foreach ($invoices as $bkey => $invoice) {
9094 $invoice['custmail'] = empty($invoice['custmail']) && !empty($invoice['customer_email']) ? $invoice['customer_email'] : $invoice['custmail'];
9095 $invoices[$bkey] = $invoice;
9096 $send_res = VikBooking::sendBookingInvoice($invoice['id'], $invoice);
9097 if ($send_res !== false) {
9098 $tot_sent++;
9099 }
9100 }
9101 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', $tot_generated, $tot_sent));
9102 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9103 }
9104
9105 public function removeinvoices()
9106 {
9107 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
9108 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9109 }
9110
9111 $ids = VikRequest::getVar('cid', array());
9112 $tot_removed = 0;
9113 $dbo = JFactory::getDbo();
9114
9115 foreach ($ids as $d) {
9116 $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id`=".(int)$d.";";
9117 $dbo->setQuery($q);
9118 $dbo->execute();
9119 if ($dbo->getNumRows() == 1) {
9120 $cur_invoice = $dbo->loadAssoc();
9121 if (file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$cur_invoice['file_name'])) {
9122 unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$cur_invoice['file_name']);
9123 }
9124 $q = "DELETE FROM `#__vikbooking_invoices` WHERE `id`=".(int)$d.";";
9125 $dbo->setQuery($q);
9126 $dbo->execute();
9127 $tot_removed++;
9128 }
9129 }
9130
9131 $mainframe = JFactory::getApplication();
9132 $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESRMVD', $tot_removed));
9133 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9134 }
9135
9136 public function geninvoices()
9137 {
9138 $dbo = JFactory::getDbo();
9139 $app = JFactory::getApplication();
9140
9141 $ids = VikRequest::getVar('cid', array());
9142
9143 if (!$ids) {
9144 $app->redirect("index.php?option=com_vikbooking&task=orders");
9145 exit;
9146 }
9147
9148 $pinvoice_num = VikRequest::getInt('invoice_num', '', 'request');
9149 $pinvoice_num = $pinvoice_num <= 0 ? 1 : $pinvoice_num;
9150 $pinvoice_suff = VikRequest::getString('invoice_suff', '', 'request');
9151 $pinvoice_date = VikRequest::getString('invoice_date', '', 'request');
9152 $pcompany_info = VikRequest::getString('company_info', '', 'request', VIKREQUEST_ALLOWHTML);
9153 $pcompany_info = strpos($pcompany_info, '<') !== false ? $pcompany_info : nl2br($pcompany_info);
9154 $pinvoice_send = VikRequest::getInt('invoice_send', '', 'request');
9155 $pinvoice_send = $pinvoice_send > 0 ? true : false;
9156 $increment_inv = true;
9157 $pconfirmgen = VikRequest::getInt('confirmgen', '', 'request');
9158
9159 // if editing an invoice (re-creating an existing invoice for a booking), do not increment the invoice number
9160 if (count($ids) === 1) {
9161 $q = "SELECT `number` FROM `#__vikbooking_invoices` WHERE `idorder`=".(int)$ids[0].";";
9162 $dbo->setQuery($q);
9163 $dbo->execute();
9164 if ($dbo->getNumRows() == 1) {
9165 $increment_inv = false;
9166 }
9167 }
9168
9169 // get bookings
9170 $dbo->setQuery(
9171 $dbo->getQuery(true)
9172 ->select($dbo->qn('o') . '.*')
9173 ->select($dbo->qn('co.idcustomer'))
9174 ->select('CONCAT_WS(\' \', ' . $dbo->qn('c.first_name') . ', ' . $dbo->qn('c.last_name') . ') AS ' . $dbo->qn('customer_name'))
9175 ->select([
9176 $dbo->qn('c.pin', 'customer_pin'),
9177 $dbo->qn('nat.country_name'),
9178 ])
9179 ->from($dbo->qn('#__vikbooking_orders', 'o'))
9180 ->leftJoin($dbo->qn('#__vikbooking_customers_orders', 'co') . ' ON ' . $dbo->qn('co.idorder') . ' = ' . $dbo->qn('o.id'))
9181 ->leftJoin($dbo->qn('#__vikbooking_customers', 'c') . ' ON ' . $dbo->qn('c.id') . ' = ' . $dbo->qn('co.idcustomer'))
9182 ->leftJoin($dbo->qn('#__vikbooking_countries', 'nat') . ' ON ' . $dbo->qn('nat.country_3_code') . ' = ' . $dbo->qn('o.country'))
9183 ->where($dbo->qn('o.id') . ' IN (' . implode(', ', array_map('intval', $ids)) . ')')
9184 ->where($dbo->qn('o.status') . ' = ' . $dbo->q('confirmed'))
9185 ->where($dbo->qn('o.total') . ' > 0')
9186 ->order($dbo->qn('o.id') . ' ASC')
9187 );
9188
9189 $bookings = $dbo->loadAssocList();
9190
9191 if (!$bookings) {
9192 VikError::raiseWarning('', JText::translate('VBOGENINVERRNOBOOKINGS'));
9193 $app->redirect("index.php?option=com_vikbooking&task=orders");
9194 exit;
9195 }
9196
9197 $tot_generated = 0;
9198 $tot_sent = 0;
9199 foreach ($bookings as $bkey => $booking) {
9200 $gen_res = VikBooking::generateBookingInvoice($booking, $pinvoice_num, $pinvoice_suff, $pinvoice_date, $pcompany_info);
9201 if ($gen_res !== false && $gen_res > 0) {
9202 $tot_generated++;
9203 $pinvoice_num++;
9204 if ($pinvoice_send) {
9205 $send_res = VikBooking::sendBookingInvoice($gen_res, $booking);
9206 if ($send_res !== false) {
9207 $tot_sent++;
9208 }
9209 }
9210 } else {
9211 VikError::raiseWarning('', JText::sprintf('VBOGENINVERRBOOKING', $booking['id']));
9212 }
9213 }
9214
9215 if ($tot_generated > 0 && $increment_inv === true) {
9216 /**
9217 * IMPORTANT: update the next invoice number after calling generateBookingInvoice()
9218 * to avoid conflicts with the drivers for the e-invoices generation.
9219 */
9220 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(($pinvoice_num - 1))." WHERE `param`='invoiceinum';";
9221 $dbo->setQuery($q);
9222 $dbo->execute();
9223 }
9224
9225 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pinvoice_suff)." WHERE `param`='invoicesuffix';";
9226 $dbo->setQuery($q);
9227 $dbo->execute();
9228
9229 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pcompany_info)." WHERE `param`='invcompanyinfo';";
9230 $dbo->setQuery($q);
9231 $dbo->execute();
9232
9233 $app->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', $tot_generated, $tot_sent));
9234
9235 if ($pconfirmgen > 0) {
9236 $app->redirect("index.php?option=com_vikbooking&task=invoices&show=".$pconfirmgen);
9237 } elseif (count($bookings) === 1) {
9238 // go to the back-end booking details page
9239 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $bookings[0]['id']);
9240 } else {
9241 $app->redirect("index.php?option=com_vikbooking&task=orders");
9242 }
9243 }
9244
9245 public function optionals() {
9246 VikBookingHelper::printHeader("6");
9247
9248 VikRequest::setVar('view', VikRequest::getCmd('view', 'optionals'));
9249
9250 parent::display();
9251
9252 if (VikBooking::showFooter()) {
9253 VikBookingHelper::printFooter();
9254 }
9255 }
9256
9257 public function newoptionals() {
9258 VikBookingHelper::printHeader("6");
9259
9260 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoptional'));
9261
9262 parent::display();
9263
9264 if (VikBooking::showFooter()) {
9265 VikBookingHelper::printFooter();
9266 }
9267 }
9268
9269 public function editoptional() {
9270 VikBookingHelper::printHeader("6");
9271
9272 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoptional'));
9273
9274 parent::display();
9275
9276 if (VikBooking::showFooter()) {
9277 VikBookingHelper::printFooter();
9278 }
9279 }
9280
9281 public function updateoptional()
9282 {
9283 if (!JSession::checkToken()) {
9284 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9285 }
9286
9287 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
9288 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9289 }
9290
9291 $this->do_updateoptional();
9292 }
9293
9294 public function updateoptionalstay()
9295 {
9296 if (!JSession::checkToken()) {
9297 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9298 }
9299
9300 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
9301 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9302 }
9303
9304 $this->do_updateoptional(true);
9305 }
9306
9307 private function do_updateoptional($stay = false) {
9308 $dbo = JFactory::getDbo();
9309 $app = JFactory::getApplication();
9310 $poptname = VikRequest::getString('optname', '', 'request');
9311 $poptdescr = VikRequest::getString('optdescr', '', 'request', VIKREQUEST_ALLOWHTML);
9312 $poptcost = VikRequest::getFloat('optcost', '', 'request');
9313 $poptperday = VikRequest::getString('optperday', '', 'request');
9314 $poptperperson = VikRequest::getString('optperperson', '', 'request');
9315 $pmaxprice = VikRequest::getFloat('maxprice', '', 'request');
9316 $popthmany = VikRequest::getString('opthmany', '', 'request');
9317 $poptaliq = VikRequest::getInt('optaliq', '', 'request');
9318 $pwhereup = VikRequest::getString('whereup', '', 'request');
9319 $pautoresize = VikRequest::getString('autoresize', '', 'request');
9320 $presizeto = VikRequest::getString('resizeto', '', 'request');
9321 $pifchildren = VikRequest::getString('ifchildren', '', 'request');
9322 $pifchildren = $pifchildren == "1" ? 1 : 0;
9323 $pmaxquant = VikRequest::getString('maxquant', '', 'request');
9324 $pmaxquant = empty($pmaxquant) ? 0 : intval($pmaxquant);
9325 $pforcesel = VikRequest::getString('forcesel', '', 'request');
9326 $pforceval = VikRequest::getString('forceval', '', 'request');
9327 $pforcevalperday = VikRequest::getString('forcevalperday', '', 'request');
9328 $pforcevalperchild = VikRequest::getString('forcevalperchild', '', 'request');
9329 $pforcesummary = VikRequest::getString('forcesummary', '', 'request');
9330 $pforcesel = $pforcesel == "1" ? 1 : 0;
9331 $pis_citytax = VikRequest::getString('is_citytax', '', 'request');
9332 $pis_fee = VikRequest::getString('is_fee', '', 'request');
9333 $pis_citytax = $pis_citytax == "1" && $pis_fee != "1" ? 1 : 0;
9334 $pis_fee = $pis_fee == "1" && $pis_citytax == 0 ? 1 : 0;
9335 $pagefrom = VikRequest::getVar('agefrom', array());
9336 $pageto = VikRequest::getVar('ageto', array());
9337 $pagecost = VikRequest::getVar('agecost', array());
9338 $pagectype = VikRequest::getVar('agectype', array());
9339 $palwaysav = VikRequest::getInt('alwaysav', 0, 'request');
9340 $pavfrom = VikRequest::getString('avfrom', '', 'request');
9341 $pavto = VikRequest::getString('avto', '', 'request');
9342 $ppcentroom = VikRequest::getInt('pcentroom', 0, 'request');
9343 $pidrooms = VikRequest::getVar('idrooms', array());
9344 $optavstr = empty($palwaysav) && !empty($pavfrom) && !empty($pavto) ? VikBooking::getDateTimestamp($pavfrom, 0, 0, 0).';'.VikBooking::getDateTimestamp($pavto, 23, 59, 59) : '';
9345 if ($pforcesel == 1) {
9346 $strforceval = intval($pforceval)."-".($pforcevalperday == "1" ? "1" : "0")."-".($pforcevalperchild == "1" ? "1" : "0")."-".($pforcesummary == "1" ? "1" : "0");
9347 } else {
9348 $strforceval = "";
9349 }
9350 $minguestsnum = VikRequest::getInt('minguestsnum', 0, 'request');
9351 $mingueststype = VikRequest::getString('mingueststype', 'guests', 'request');
9352 $minguestsnum = $minguestsnum < 0 ? 0 : $minguestsnum;
9353 $mingueststype = !empty($mingueststype) && !in_array($mingueststype, array('adults', 'guests')) ? 'guests' : $mingueststype;
9354 $maxguestsnum = VikRequest::getInt('maxguestsnum', 0, 'request');
9355 $maxgueststype = VikRequest::getString('maxgueststype', 'guests', 'request');
9356 $maxguestsnum = $maxguestsnum < 0 ? 0 : $maxguestsnum;
9357 $maxgueststype = !empty($maxgueststype) && !in_array($maxgueststype, array('adults', 'guests')) ? 'guests' : $maxgueststype;
9358 $minguests = VikRequest::getInt('minguests', 0, 'request');
9359 $minguests_conflict = false;
9360 if ($minguests > 0 && $minguestsnum > 0 && $maxguestsnum > 0) {
9361 if ($minguestsnum >= $maxguestsnum) {
9362 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL1');
9363 } elseif (($maxguestsnum - $minguestsnum) < 2) {
9364 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL2');
9365 }
9366 }
9367 if (!$minguests || $minguests_conflict !== false) {
9368 $minguestsnum = 0;
9369 $maxguestsnum = 0;
9370 if ($minguests_conflict !== false) {
9371 // raise warning, but do not stop the process
9372 VikError::raiseWarning('', $minguests_conflict);
9373 }
9374 }
9375 $damagedep = VikRequest::getInt('damagedep', 0, 'request');
9376 $pet_fee = VikRequest::getInt('pet_fee', 0, 'request');
9377 $custom_checkinout = VikRequest::getInt('custom_checkinout', 0, 'request');
9378 $set_checkin = VikRequest::getInt('set_checkin', 0, 'request');
9379 $set_checkout = VikRequest::getInt('set_checkout', 0, 'request');
9380 if (!$custom_checkinout) {
9381 $set_checkin = 0;
9382 $set_checkout = 0;
9383 }
9384 if ((!$set_checkin && !$set_checkout) || $set_checkin == $set_checkout) {
9385 // check-in and check-out times should not be equal or both empty
9386 $custom_checkinout = 0;
9387 }
9388 $damagedep_settings = $damagedep ? ((array) $app->input->get('damagedep_settings', [], 'array')) : [];
9389 $oparams = [
9390 'minguestsnum' => $minguestsnum,
9391 'mingueststype' => $mingueststype,
9392 'maxguestsnum' => $maxguestsnum,
9393 'maxgueststype' => $maxgueststype,
9394 'damagedep' => $damagedep,
9395 'damagedep_settings' => $damagedep_settings,
9396 'pet_fee' => $pet_fee,
9397 'custom_checkinout' => $custom_checkinout,
9398 'set_checkin' => $set_checkin,
9399 'set_checkout' => $set_checkout,
9400 ];
9401 /**
9402 * We fetch the previous params to merge them with the new ones
9403 * in case some properties have been set somewhere else.
9404 * For example, the damage deposit transmission to Booking.com.
9405 */
9406 $cur_oparams = array();
9407 $q = "SELECT `oparams` FROM `#__vikbooking_optionals` WHERE `id`=" . (int)$pwhereup . ";";
9408 $dbo->setQuery($q);
9409 $dbo->execute();
9410 if ($dbo->getNumRows()) {
9411 $cur_oparams = $dbo->loadResult();
9412 $cur_oparams = !empty($cur_oparams) ? json_decode($cur_oparams, true) : array();
9413 $cur_oparams = !is_array($cur_oparams) ? array() : $cur_oparams;
9414 // merge previous params with the new ones to get the new values
9415 $oparams = array_merge($cur_oparams, $oparams);
9416 }
9417
9418 /**
9419 * Ensure options of type city tax never get a tax rate.
9420 *
9421 * @since 1.18.3 (J) - 1.8.3 (WP)
9422 */
9423 if ($pis_citytax) {
9424 $poptaliq = 0;
9425 }
9426
9427 if (!empty($poptname)) {
9428 if (intval($_FILES['optimg']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['optimg']['name'])!="") {
9429 jimport('joomla.filesystem.file');
9430 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9431 if (@is_uploaded_file($_FILES['optimg']['tmp_name'])) {
9432 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['optimg']['name'])));
9433 if (file_exists($updpath.$safename)) {
9434 $j=1;
9435 while (file_exists($updpath.$j.$safename)) {
9436 $j++;
9437 }
9438 $pwhere=$updpath.$j.$safename;
9439 } else {
9440 $j="";
9441 $pwhere=$updpath.$safename;
9442 }
9443 if (!getimagesize($_FILES['optimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
9444 @unlink($pwhere);
9445 $picon="";
9446 } else {
9447 VikBooking::uploadFile($_FILES['optimg']['tmp_name'], $pwhere);
9448 @chmod($pwhere, 0644);
9449 $picon=$j.$safename;
9450 if ($pautoresize=="1" && !empty($presizeto)) {
9451 $eforj = new vikResizer();
9452 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
9453 if ($origmod) {
9454 @unlink($pwhere);
9455 $picon='r_'.$j.$safename;
9456 }
9457 }
9458 }
9459 } else {
9460 $picon="";
9461 }
9462 } else {
9463 $picon="";
9464 }
9465 ($poptperday=="each" ? $poptperday="1" : $poptperday="0");
9466 $poptperperson=($poptperperson=="each" ? "1" : "0");
9467 ($popthmany=="yes" ? $popthmany="1" : $popthmany="0");
9468 $ageintervalstr = '';
9469 if ($pifchildren == 1 && count($pagefrom) > 0 && count($pagecost) > 0 && count($pagefrom) == count($pagecost)) {
9470 foreach ($pagefrom as $kage => $vage) {
9471 $afrom = intval($vage);
9472 $ato = intval($pageto[$kage]);
9473 $acost = floatval($pagecost[$kage]);
9474 if (strlen($vage) > 0 && strlen($pagecost[$kage]) > 0) {
9475 if ($ato < $afrom) $ato = $afrom;
9476 $ageintervalstr .= $afrom.'_'.$ato.'_'.$acost.(array_key_exists($kage, $pagectype) && strpos($pagectype[$kage], '%') !== false ? '_%'.(strpos($pagectype[$kage], '%b') !== false ? 'b' : '') : '').';;';
9477 }
9478 }
9479 $ageintervalstr = rtrim($ageintervalstr, ';;');
9480 if (!empty($ageintervalstr)) {
9481 $pforcesel = 1;
9482 }
9483 }
9484 $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).";";
9485 $dbo->setQuery($q);
9486 $dbo->execute();
9487 $app->enqueueMessage(JText::translate('VBOSUCCUPDOPTION'));
9488
9489 // assign/unset option-rooms relations
9490 $rooms_with_opt = array();
9491 if (count($pidrooms)) {
9492 // assign this new option to the requested rooms
9493 foreach ($pidrooms as $idroom) {
9494 if (empty($idroom)) {
9495 continue;
9496 }
9497 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
9498 $dbo->setQuery($q);
9499 $dbo->execute();
9500 if (!$dbo->getNumRows()) {
9501 continue;
9502 }
9503 $room_data = $dbo->loadAssoc();
9504 array_push($rooms_with_opt, $room_data['id']);
9505 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9506 if (in_array((string)$pwhereup, $current_opts)) {
9507 continue;
9508 }
9509 if (count($current_opts) === 1 && (string)$current_opts[0] == '0') {
9510 // make sure we do not concatenate a real ID to 0
9511 $current_opts = array();
9512 }
9513 array_push($current_opts, $pwhereup);
9514 $new_opts = implode(';', $current_opts) . ';';
9515 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9516 $dbo->setQuery($q);
9517 $dbo->execute();
9518 }
9519 }
9520 if (!count($rooms_with_opt)) {
9521 // get all rooms to unset this option (if previously set)
9522 array_push($rooms_with_opt, '0');
9523 }
9524 // unset the option from the other rooms that may have it
9525 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_opt) . ");";
9526 $dbo->setQuery($q);
9527 $dbo->execute();
9528 if ($dbo->getNumRows()) {
9529 $unset_rooms_opt = $dbo->loadAssocList();
9530 foreach ($unset_rooms_opt as $room_data) {
9531 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9532 if (!in_array((string)$pwhereup, $current_opts)) {
9533 // this room is not using this option
9534 continue;
9535 }
9536 $optkey = array_search((string)$pwhereup, $current_opts);
9537 if ($optkey === false) {
9538 // key not found
9539 continue;
9540 }
9541 // unset this option ID from the string
9542 unset($current_opts[$optkey]);
9543 if (!count($current_opts)) {
9544 // a room with no options assigned will be listed as "0;"
9545 $current_opts = array(0);
9546 }
9547 $new_opts = implode(';', $current_opts) . ';';
9548 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9549 $dbo->setQuery($q);
9550 $dbo->execute();
9551 }
9552 }
9553 //
9554
9555 }
9556 $app->redirect("index.php?option=com_vikbooking&task=" . ($stay ? 'editoptional&cid[]=' . $pwhereup : 'optionals'));
9557 }
9558
9559 public function createoptionals()
9560 {
9561 if (!JSession::checkToken()) {
9562 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9563 }
9564
9565 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
9566 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9567 }
9568
9569 $this->do_createoptionals();
9570 }
9571
9572 public function createoptionalsstay()
9573 {
9574 if (!JSession::checkToken()) {
9575 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9576 }
9577
9578 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
9579 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9580 }
9581
9582 $this->do_createoptionals(true);
9583 }
9584
9585 private function do_createoptionals($stay = false)
9586 {
9587 $app = JFactory::getApplication();
9588 $dbo = JFactory::getDbo();
9589
9590 $poptname = VikRequest::getString('optname', '', 'request');
9591 $poptdescr = VikRequest::getString('optdescr', '', 'request', VIKREQUEST_ALLOWHTML);
9592 $poptcost = VikRequest::getFloat('optcost', '', 'request');
9593 $poptperday = VikRequest::getString('optperday', '', 'request');
9594 $poptperperson = VikRequest::getString('optperperson', '', 'request');
9595 $pmaxprice = VikRequest::getFloat('maxprice', '', 'request');
9596 $popthmany = VikRequest::getString('opthmany', '', 'request');
9597 $poptaliq = VikRequest::getInt('optaliq', '', 'request');
9598 $pautoresize = VikRequest::getString('autoresize', '', 'request');
9599 $presizeto = VikRequest::getString('resizeto', '', 'request');
9600 $pifchildren = VikRequest::getString('ifchildren', '', 'request');
9601 $pifchildren = $pifchildren == "1" ? 1 : 0;
9602 $pmaxquant = VikRequest::getString('maxquant', '', 'request');
9603 $pmaxquant = empty($pmaxquant) ? 0 : intval($pmaxquant);
9604 $pforcesel = VikRequest::getString('forcesel', '', 'request');
9605 $pforceval = VikRequest::getString('forceval', '', 'request');
9606 $pforcevalperday = VikRequest::getString('forcevalperday', '', 'request');
9607 $pforcevalperchild = VikRequest::getString('forcevalperchild', '', 'request');
9608 $pforcesummary = VikRequest::getString('forcesummary', '', 'request');
9609 $pforcesel = $pforcesel == "1" ? 1 : 0;
9610 $pis_citytax = VikRequest::getString('is_citytax', '', 'request');
9611 $pis_fee = VikRequest::getString('is_fee', '', 'request');
9612 $pis_citytax = $pis_citytax == "1" && $pis_fee != "1" ? 1 : 0;
9613 $pis_fee = $pis_fee == "1" && $pis_citytax == 0 ? 1 : 0;
9614 $pagefrom = VikRequest::getVar('agefrom', array());
9615 $pageto = VikRequest::getVar('ageto', array());
9616 $pagecost = VikRequest::getVar('agecost', array());
9617 $pagectype = VikRequest::getVar('agectype', array());
9618 $palwaysav = VikRequest::getInt('alwaysav', 0, 'request');
9619 $pavfrom = VikRequest::getString('avfrom', '', 'request');
9620 $pavto = VikRequest::getString('avto', '', 'request');
9621 $ppcentroom = VikRequest::getInt('pcentroom', 0, 'request');
9622 $pidrooms = VikRequest::getVar('idrooms', array());
9623 $optavstr = empty($palwaysav) && !empty($pavfrom) && !empty($pavto) ? VikBooking::getDateTimestamp($pavfrom, 0, 0, 0).';'.VikBooking::getDateTimestamp($pavto, 23, 59, 59) : '';
9624 if ($pforcesel == 1) {
9625 $strforceval = intval($pforceval)."-".($pforcevalperday == "1" ? "1" : "0")."-".($pforcevalperchild == "1" ? "1" : "0")."-".($pforcesummary == "1" ? "1" : "0");
9626 } else {
9627 $strforceval = "";
9628 }
9629 $minguestsnum = VikRequest::getInt('minguestsnum', 0, 'request');
9630 $mingueststype = VikRequest::getString('mingueststype', 'guests', 'request');
9631 $minguestsnum = $minguestsnum < 0 ? 0 : $minguestsnum;
9632 $mingueststype = !empty($mingueststype) && !in_array($mingueststype, array('adults', 'guests')) ? 'guests' : $mingueststype;
9633 $maxguestsnum = VikRequest::getInt('maxguestsnum', 0, 'request');
9634 $maxgueststype = VikRequest::getString('maxgueststype', 'guests', 'request');
9635 $maxguestsnum = $maxguestsnum < 0 ? 0 : $maxguestsnum;
9636 $maxgueststype = !empty($maxgueststype) && !in_array($maxgueststype, array('adults', 'guests')) ? 'guests' : $maxgueststype;
9637 $minguests = VikRequest::getInt('minguests', 0, 'request');
9638 $minguests_conflict = false;
9639 if ($minguests > 0 && $minguestsnum > 0 && $maxguestsnum > 0) {
9640 if ($minguestsnum >= $maxguestsnum) {
9641 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL1');
9642 } elseif (($maxguestsnum - $minguestsnum) < 2) {
9643 $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL2');
9644 }
9645 }
9646 if (!$minguests || $minguests_conflict !== false) {
9647 $minguestsnum = 0;
9648 $maxguestsnum = 0;
9649 if ($minguests_conflict !== false) {
9650 // raise warning, but do not stop the process
9651 VikError::raiseWarning('', $minguests_conflict);
9652 }
9653 }
9654 $damagedep = VikRequest::getInt('damagedep', 0, 'request');
9655 $pet_fee = VikRequest::getInt('pet_fee', 0, 'request');
9656 $custom_checkinout = VikRequest::getInt('custom_checkinout', 0, 'request');
9657 $set_checkin = VikRequest::getInt('set_checkin', 0, 'request');
9658 $set_checkout = VikRequest::getInt('set_checkout', 0, 'request');
9659 if (!$custom_checkinout) {
9660 $set_checkin = 0;
9661 $set_checkout = 0;
9662 }
9663 if ((!$set_checkin && !$set_checkout) || $set_checkin == $set_checkout) {
9664 // check-in and check-out times should not be equal or both empty
9665 $custom_checkinout = 0;
9666 }
9667 $damagedep_settings = $damagedep ? ((array) $app->input->get('damagedep_settings', [], 'array')) : [];
9668 $oparams = [
9669 'minguestsnum' => $minguestsnum,
9670 'mingueststype' => $mingueststype,
9671 'maxguestsnum' => $maxguestsnum,
9672 'maxgueststype' => $maxgueststype,
9673 'damagedep' => $damagedep,
9674 'damagedep_settings' => $damagedep_settings,
9675 'pet_fee' => $pet_fee,
9676 'custom_checkinout' => $custom_checkinout,
9677 'set_checkin' => $set_checkin,
9678 'set_checkout' => $set_checkout,
9679 ];
9680
9681 /**
9682 * Ensure options of type city tax never get a tax rate.
9683 *
9684 * @since 1.18.3 (J) - 1.8.3 (WP)
9685 */
9686 if ($pis_citytax) {
9687 $poptaliq = 0;
9688 }
9689
9690 if (!empty($poptname)) {
9691 if (intval($_FILES['optimg']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['optimg']['name'])!="") {
9692 jimport('joomla.filesystem.file');
9693 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9694 if (@is_uploaded_file($_FILES['optimg']['tmp_name'])) {
9695 $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['optimg']['name'])));
9696 if (file_exists($updpath.$safename)) {
9697 $j = 1;
9698 while (file_exists($updpath.$j.$safename)) {
9699 $j++;
9700 }
9701 $pwhere = $updpath.$j.$safename;
9702 } else {
9703 $j = "";
9704 $pwhere = $updpath.$safename;
9705 }
9706 if (!getimagesize($_FILES['optimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
9707 @unlink($pwhere);
9708 $picon = "";
9709 } else {
9710 VikBooking::uploadFile($_FILES['optimg']['tmp_name'], $pwhere);
9711 @chmod($pwhere, 0644);
9712 $picon = $j.$safename;
9713 if ($pautoresize == "1" && !empty($presizeto)) {
9714 $eforj = new vikResizer();
9715 $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
9716 if ($origmod) {
9717 @unlink($pwhere);
9718 $picon = 'r_'.$j.$safename;
9719 }
9720 }
9721 }
9722 } else {
9723 $picon = "";
9724 }
9725 } else {
9726 $picon = "";
9727 }
9728 $poptperday = ($poptperday == "each" ? "1" : "0");
9729 $poptperperson = ($poptperperson == "each" ? "1" : "0");
9730 ($popthmany == "yes" ? $popthmany = "1" : $popthmany = "0");
9731 $ageintervalstr = '';
9732 if ($pifchildren == 1 && count($pagefrom) > 0 && count($pagecost) > 0 && count($pagefrom) == count($pagecost)) {
9733 foreach ($pagefrom as $kage => $vage) {
9734 $afrom = intval($vage);
9735 $ato = intval($pageto[$kage]);
9736 $acost = floatval($pagecost[$kage]);
9737 if (strlen($vage) > 0 && strlen($pagecost[$kage]) > 0) {
9738 if ($ato < $afrom) $ato = $afrom;
9739 $ageintervalstr .= $afrom.'_'.$ato.'_'.$acost.(array_key_exists($kage, $pagectype) && strpos($pagectype[$kage], '%') !== false ? '_%'.(strpos($pagectype[$kage], '%b') !== false ? 'b' : '') : '').';;';
9740 }
9741 }
9742 $ageintervalstr = rtrim($ageintervalstr, ';;');
9743 if (!empty($ageintervalstr)) {
9744 $pforcesel = 1;
9745 }
9746 }
9747 $q = "SELECT `ordering` FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` DESC LIMIT 1;";
9748 $dbo->setQuery($q);
9749 $dbo->execute();
9750 if ($dbo->getNumRows() == 1) {
9751 $getlast = $dbo->loadResult();
9752 $newsortnum = $getlast + 1;
9753 } else {
9754 $newsortnum = 1;
9755 }
9756 $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)) . ");";
9757 $dbo->setQuery($q);
9758 $dbo->execute();
9759 $newoptid = $dbo->insertid();
9760
9761 if (!empty($newoptid)) {
9762 // assign/unset option-rooms relations
9763 $rooms_with_opt = array();
9764 if (count($pidrooms)) {
9765 // assign this new option to the requested rooms
9766 foreach ($pidrooms as $idroom) {
9767 if (empty($idroom)) {
9768 continue;
9769 }
9770 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
9771 $dbo->setQuery($q);
9772 $dbo->execute();
9773 if (!$dbo->getNumRows()) {
9774 continue;
9775 }
9776 $room_data = $dbo->loadAssoc();
9777 array_push($rooms_with_opt, $room_data['id']);
9778 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9779 if (in_array((string)$newoptid, $current_opts)) {
9780 continue;
9781 }
9782 if (count($current_opts) === 1 && (string)$current_opts[0] == '0') {
9783 // make sure we do not concatenate a real ID to 0
9784 $current_opts = array();
9785 }
9786 array_push($current_opts, $newoptid);
9787 $new_opts = implode(';', $current_opts) . ';';
9788 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9789 $dbo->setQuery($q);
9790 $dbo->execute();
9791 }
9792 }
9793 if (!count($rooms_with_opt)) {
9794 // get all rooms to unset this option (if previously set)
9795 array_push($rooms_with_opt, '0');
9796 }
9797 // unset the option from the other rooms that may have it
9798 $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_opt) . ");";
9799 $dbo->setQuery($q);
9800 $dbo->execute();
9801 if ($dbo->getNumRows()) {
9802 $unset_rooms_opt = $dbo->loadAssocList();
9803 foreach ($unset_rooms_opt as $room_data) {
9804 $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9805 if (!in_array((string)$newoptid, $current_opts)) {
9806 // this room is not using this option
9807 continue;
9808 }
9809 $optkey = array_search((string)$newoptid, $current_opts);
9810 if ($optkey === false) {
9811 // key not found
9812 continue;
9813 }
9814 // unset this option ID from the string
9815 unset($current_opts[$optkey]);
9816 if (!count($current_opts)) {
9817 // a room with no options assigned will be listed as "0;"
9818 $current_opts = array(0);
9819 }
9820 $new_opts = implode(';', $current_opts) . ';';
9821 $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9822 $dbo->setQuery($q);
9823 $dbo->execute();
9824 }
9825 }
9826 //
9827 }
9828
9829 }
9830 $mainframe = JFactory::getApplication();
9831 $mainframe->redirect("index.php?option=com_vikbooking&task=" . ($stay && isset($newoptid) && !empty($newoptid) ? 'editoptional&cid[]=' . $newoptid : 'optionals'));
9832 }
9833
9834 public function removeoptionals()
9835 {
9836 if (!JSession::checkToken()) {
9837 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9838 }
9839
9840 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
9841 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9842 }
9843
9844 $ids = VikRequest::getVar('cid', array(0));
9845 if ($ids) {
9846 $dbo = JFactory::getDbo();
9847 foreach ($ids as $d) {
9848 $q = "SELECT `img` FROM `#__vikbooking_optionals` WHERE `id`=".$dbo->quote($d).";";
9849 $dbo->setQuery($q);
9850 $dbo->execute();
9851 if ($dbo->getNumRows() == 1) {
9852 $rows = $dbo->loadAssocList();
9853 if (!empty($rows[0]['img']) && file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['img'])) {
9854 @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['img']);
9855 }
9856 }
9857 $q = "DELETE FROM `#__vikbooking_optionals` WHERE `id`=".$dbo->quote($d).";";
9858 $dbo->setQuery($q);
9859 $dbo->execute();
9860 }
9861 }
9862 $mainframe = JFactory::getApplication();
9863 $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
9864 }
9865
9866 public function sendcustomsms() {
9867 $mainframe = JFactory::getApplication();
9868 $pphone = VikRequest::getString('phone', '', 'request');
9869 $psmscont = VikRequest::getString('smscont', '', 'request');
9870 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
9871 $pgoto = !empty($pgoto) ? urldecode($pgoto) : 'index.php?option=com_vikbooking';
9872 if (!empty($pphone) && !empty($psmscont)) {
9873 $sms_api = VikBooking::getSMSAPIClass();
9874 $sms_api_params = VikBooking::getSMSParams();
9875 if (!empty($sms_api) && file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api) && !empty($sms_api_params)) {
9876 require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
9877 $sms_obj = new VikSmsApi(array(), $sms_api_params);
9878 $response_obj = $sms_obj->sendMessage($pphone, $psmscont);
9879 if ( !$sms_obj->validateResponse($response_obj) ) {
9880 VikError::raiseWarning('', $sms_obj->getLog());
9881 } else {
9882 $mainframe->enqueueMessage(JText::translate('VBSENDSMSOK'));
9883 }
9884 } else {
9885 VikError::raiseWarning('', JText::translate('VBSENDSMSERRMISSAPI'));
9886 }
9887 } else {
9888 VikError::raiseWarning('', JText::translate('VBSENDSMSERRMISSDATA'));
9889 }
9890 $mainframe->redirect($pgoto);
9891 }
9892
9893 public function sendcustomemail() {
9894 $dbo = JFactory::getDbo();
9895 $mainframe = JFactory::getApplication();
9896 $vbo_tn = VikBooking::getTranslator();
9897 $pbid = VikRequest::getInt('bid', '', 'request');
9898 $pemailsubj = VikRequest::getString('emailsubj', '', 'request');
9899 $pemail = VikRequest::getString('email', '', 'request');
9900 $pemailcont = VikRequest::getString('emailcont', '', 'request', VIKREQUEST_ALLOWRAW);
9901 $pemailfrom = VikRequest::getString('emailfrom', '', 'request');
9902 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
9903 $pgoto = !empty($pgoto) ? urldecode($pgoto) : 'index.php?option=com_vikbooking';
9904 if (!empty($pemail) && !empty($pemailcont)) {
9905 $email_attach = null;
9906 jimport('joomla.filesystem.file');
9907 $pemailattch = VikRequest::getVar('emailattch', null, 'files', 'array');
9908 if (isset($pemailattch) && strlen(trim($pemailattch['name']))) {
9909 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pemailattch['name'])));
9910 $src = $pemailattch['tmp_name'];
9911 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9912 $j = "";
9913 if (file_exists($dest.$filename)) {
9914 $j = rand(171, 1717);
9915 while (file_exists($dest.$j.$filename)) {
9916 $j++;
9917 }
9918 }
9919 $finaldest = $dest.$j.$filename;
9920 if (VikBooking::uploadFile($src, $finaldest)) {
9921 $email_attach = $finaldest;
9922 } else {
9923 VikError::raiseWarning('', 'Error uploading the attachment. Email not sent.');
9924 $mainframe->redirect($pgoto);
9925 exit;
9926 }
9927 }
9928 //VBO 1.10 - special tags for the custom email template files and messages
9929 $orig_mail_cont = $pemailcont;
9930 if (strpos($pemailcont, '{') !== false && strpos($pemailcont, '}') !== false) {
9931 // replace any possible placeholder for special tags
9932 $pemailcont = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
9933 return $match[2];
9934 }, $pemailcont);
9935
9936 $booking = array();
9937 $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.";";
9938 $dbo->setQuery($q);
9939 $dbo->execute();
9940 if ($dbo->getNumRows() > 0) {
9941 $booking = $dbo->loadAssoc();
9942 }
9943 $booking_rooms = array();
9944 $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.";";
9945 $dbo->setQuery($q);
9946 $dbo->execute();
9947 if ($dbo->getNumRows() > 0) {
9948 $booking_rooms = $dbo->loadAssocList();
9949 if (!empty($booking['lang'])) {
9950 $vbo_tn->translateContents($booking_rooms, '#__vikbooking_rooms', array('id' => 'idroom', 'room_name' => 'name'), array(), $booking['lang']);
9951 }
9952 }
9953 //we use the same parsing function as the one for the Customer SMS Template
9954 $pemailcont = VikBooking::parseCustomerSMSTemplate($booking, $booking_rooms, null, $pemailcont);
9955 }
9956 //
9957 // allow the use of token {booking_id} in subject
9958 $pemailsubj = str_replace('{booking_id}', $pbid, $pemailsubj);
9959 //
9960 $is_html = (strpos($pemailcont, '<') !== false && strpos($pemailcont, '>') !== false);
9961 $pemailcont = !$is_html ? nl2br($pemailcont) : $pemailcont;
9962 $vbo_app = VikBooking::getVboApplication();
9963 $vbo_app->sendMail($pemailfrom, $pemailfrom, $pemail, $pemailfrom, $pemailsubj, $pemailcont, $is_html, 'base64', $email_attach);
9964 $mainframe->enqueueMessage(JText::translate('VBSENDEMAILOK'));
9965 if ($email_attach !== null) {
9966 @unlink($email_attach);
9967 }
9968 //Booking History
9969 VikBooking::getBookingHistoryInstance()->setBid($pbid)->store('CE', nl2br($pemailsubj . "\n\n" . $pemailcont));
9970 //
9971 //Save email template for future sending
9972 $config_rec_exists = false;
9973 $emtpl = array(
9974 'emailsubj' => $pemailsubj,
9975 'emailcont' => $orig_mail_cont,
9976 'emailfrom' => $pemailfrom
9977 );
9978 $cur_emtpl = array();
9979 $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='customemailtpls';";
9980 $dbo->setQuery($q);
9981 $dbo->execute();
9982 if ($dbo->getNumRows() > 0) {
9983 $config_rec_exists = true;
9984 $cur_emtpl = $dbo->loadResult();
9985 $cur_emtpl = empty($cur_emtpl) ? array() : json_decode($cur_emtpl, true);
9986 $cur_emtpl = is_array($cur_emtpl) ? $cur_emtpl : array();
9987 }
9988 if (count($cur_emtpl) > 0) {
9989 $existing_subj = false;
9990 foreach ($cur_emtpl as $emk => $emv) {
9991 if (array_key_exists('emailsubj', $emv) && $emv['emailsubj'] == $emtpl['emailsubj']) {
9992 $cur_emtpl[$emk] = $emtpl;
9993 $existing_subj = true;
9994 break;
9995 }
9996 }
9997 if ($existing_subj === false) {
9998 $cur_emtpl[] = $emtpl;
9999 }
10000 } else {
10001 $cur_emtpl[] = $emtpl;
10002 }
10003 if (count($cur_emtpl) > 10) {
10004 //Max 10 templates to avoid problems with the size of the field and truncated json strings
10005 $exceed = count($cur_emtpl) - 10;
10006 for ($tl=0; $tl < $exceed; $tl++) {
10007 unset($cur_emtpl[$tl]);
10008 }
10009 $cur_emtpl = array_values($cur_emtpl);
10010 }
10011 if ($config_rec_exists === true) {
10012 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($cur_emtpl))." WHERE `param`='customemailtpls';";
10013 $dbo->setQuery($q);
10014 $dbo->execute();
10015 } else {
10016 $q = "INSERT INTO `#__vikbooking_config` (`param`,`setting`) VALUES ('customemailtpls', ".$dbo->quote(json_encode($cur_emtpl)).");";
10017 $dbo->setQuery($q);
10018 $dbo->execute();
10019 }
10020 //
10021 } else {
10022 VikError::raiseWarning('', JText::translate('VBSENDEMAILERRMISSDATA'));
10023 }
10024 $mainframe->redirect($pgoto);
10025 }
10026
10027 public function rmcustomemailtpl() {
10028 $cid = VikRequest::getVar('cid', array(0));
10029 $oid = $cid[0];
10030 $dbo = JFactory::getDBO();
10031 $mainframe = JFactory::getApplication();
10032 $tplind = VikRequest::getInt('tplind', '', 'request');
10033 if (empty($oid) || !(strlen($tplind) > 0)) {
10034 VikError::raiseWarning('', 'Missing Data.');
10035 $mainframe->redirect('index.php?option=com_vikbooking');
10036 exit;
10037 }
10038 $cur_emtpl = array();
10039 $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='customemailtpls';";
10040 $dbo->setQuery($q);
10041 $dbo->execute();
10042 if ($dbo->getNumRows() > 0) {
10043 $cur_emtpl = $dbo->loadResult();
10044 $cur_emtpl = empty($cur_emtpl) ? array() : json_decode($cur_emtpl, true);
10045 $cur_emtpl = is_array($cur_emtpl) ? $cur_emtpl : array();
10046 } else {
10047 VikError::raiseWarning('', 'Missing Templates Record.');
10048 $mainframe->redirect('index.php?option=com_vikbooking');
10049 exit;
10050 }
10051 if (array_key_exists($tplind, $cur_emtpl)) {
10052 unset($cur_emtpl[$tplind]);
10053 $cur_emtpl = count($cur_emtpl) > 0 ? array_values($cur_emtpl) : array();
10054 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($cur_emtpl))." WHERE `param`='customemailtpls';";
10055 $dbo->setQuery($q);
10056 $dbo->execute();
10057 }
10058 $mainframe->redirect('index.php?option=com_vikbooking&task=editorder&cid[]='.$oid.'&customemail=1');
10059 exit;
10060 }
10061
10062 public function exportcustomers() {
10063 //we do not set the menu for this view
10064
10065 VikRequest::setVar('view', VikRequest::getCmd('view', 'exportcustomers'));
10066
10067 parent::display();
10068
10069 if (VikBooking::showFooter()) {
10070 VikBookingHelper::printFooter();
10071 }
10072 }
10073
10074 public function csvexportprepare() {
10075 //modal box, so we do not set menu or footer
10076
10077 VikRequest::setVar('view', VikRequest::getCmd('view', 'csvexportprepare'));
10078
10079 parent::display();
10080 }
10081
10082 public function icsexportprepare() {
10083 //modal box, so we do not set menu or footer
10084
10085 VikRequest::setVar('view', VikRequest::getCmd('view', 'icsexportprepare'));
10086
10087 parent::display();
10088 }
10089
10090 public function bookingcheckin() {
10091 //modal box, so we do not set menu or footer
10092
10093 VikRequest::setVar('view', VikRequest::getCmd('view', 'bookingcheckin'));
10094
10095 parent::display();
10096 }
10097
10098 public function gencheckindoc() {
10099 //modal box, so we do not set menu or footer
10100
10101 VikRequest::setVar('view', VikRequest::getCmd('view', 'gencheckindoc'));
10102
10103 parent::display();
10104 }
10105
10106 public function checkversion() {
10107 //to be called via ajax
10108 $params = new stdClass;
10109 $params->version = VIKBOOKING_SOFTWARE_VERSION;
10110 $params->alias = 'com_vikbooking';
10111
10112 $result = array();
10113
10114 if (!count($result)) {
10115 $result = new stdClass;
10116 $result->status = 0;
10117 } else {
10118 $result = $result[0];
10119 }
10120
10121 echo json_encode($result);
10122 exit;
10123 }
10124
10125 public function updateprogram() {
10126 $params = new stdClass;
10127 $params->version = VIKBOOKING_SOFTWARE_VERSION;
10128 $params->alias = 'com_vikbooking';
10129
10130 $result = array();
10131
10132 if (!count($result) || !$result[0]) {
10133 if (class_exists('JEventDispatcher')) {
10134 $dispatcher = JEventDispatcher::getInstance();
10135 $result = $dispatcher->trigger('checkVersion', array(&$params));
10136 } else {
10137 $app = JFactory::getApplication();
10138 if (method_exists($app, 'triggerEvent')) {
10139 $result = $app->triggerEvent('checkVersion', array(&$params));
10140 }
10141 }
10142 }
10143
10144 if (!count($result) || !$result[0]->status || !$result[0]->response->status) {
10145 exit('Error, plugin disabled');
10146 }
10147
10148 JToolbarHelper::title(JText::translate('VBMAINTITLEUPDATEPROGRAM'));
10149
10150 VikBookingHelper::pUpdateProgram($result[0]->response);
10151 }
10152
10153 public function updateprogramlaunch() {
10154 $params = new stdClass;
10155 $params->version = VIKBOOKING_SOFTWARE_VERSION;
10156 $params->alias = 'com_vikbooking';
10157
10158 $json = new stdClass;
10159 $json->status = false;
10160
10161 echo json_encode($json);
10162 exit;
10163 }
10164
10165 public function invoke_vcm()
10166 {
10167 $app = JFactory::getApplication();
10168
10169 $oids = VikRequest::getVar('cid', []);
10170 $sync_type = VikRequest::getString('stype', 'new', 'request');
10171 $sync_type = !in_array($sync_type, ['new', 'modify', 'cancel']) ? 'new' : $sync_type;
10172 $original_booking_js = VikRequest::getString('origb', '', 'request', VIKREQUEST_ALLOWRAW);
10173 $return_url = VikRequest::getString('returl', '', 'request', VIKREQUEST_ALLOWRAW);
10174 $return_url = !empty($return_url) ? urldecode($return_url) : $return_url;
10175
10176 if (!$oids || !is_file(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
10177 $app->redirect("index.php?option=com_vikbooking&task=orders");
10178 $app->close();
10179 }
10180
10181 $result = VikBooking::getVcmInvoker()
10182 ->setOids($oids)
10183 ->setSyncType($sync_type)
10184 ->setOriginalBooking($original_booking_js, true)
10185 ->doSync();
10186
10187 if ($result === true) {
10188 $app->enqueueMessage(JText::translate('VBCHANNELMANAGERRESULTOK'));
10189 } else {
10190 VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a>');
10191 }
10192
10193 if (!empty($return_url)) {
10194 $app->redirect($return_url);
10195 } else {
10196 $app->redirect("index.php?option=com_vikbooking&task=orders");
10197 }
10198
10199 $app->close();
10200 }
10201
10202 public function multiphotosupload() {
10203 jimport('joomla.filesystem.file');
10204
10205 $dbo = JFactory::getDBO();
10206 $proomid = VikRequest::getInt('roomid', '', 'request');
10207
10208 $resp = array('files' => array());
10209 $error_messages = array(
10210 1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
10211 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
10212 3 => 'The uploaded file was only partially uploaded',
10213 4 => 'No file was uploaded',
10214 6 => 'Missing a temporary folder',
10215 7 => 'Failed to write file to disk',
10216 8 => 'A PHP extension stopped the file upload',
10217 'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini',
10218 'max_file_size' => 'File is too big',
10219 'min_file_size' => 'File is too small',
10220 'accept_file_types' => 'Filetype not allowed',
10221 'max_number_of_files' => 'Maximum number of files exceeded',
10222 'max_width' => 'Image exceeds maximum width',
10223 'min_width' => 'Image requires a minimum width',
10224 'max_height' => 'Image exceeds maximum height',
10225 'min_height' => 'Image requires a minimum height',
10226 'abort' => 'File upload aborted',
10227 'image_resize' => 'Failed to resize image',
10228 'vbo_type' => 'The file type cannot be accepted',
10229 'vbo_jupload' => 'The upload has failed. Check your CMS settings and permissions',
10230 'vbo_perm' => 'Error moving the uploaded files. Check your permissions'
10231 );
10232
10233 $creativik = new vikResizer();
10234 $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
10235 $bigsdest = $updpath;
10236 $thumbsdest = $updpath;
10237 $dest = $updpath;
10238 $moreimagestr = '';
10239 $cur_captions = json_encode(array());
10240
10241 $q = "SELECT `moreimgs`,`imgcaptions` FROM `#__vikbooking_rooms` WHERE `id`=".$proomid.";";
10242 $dbo->setQuery($q);
10243 $dbo->execute();
10244 if ($dbo->getNumRows() == 1) {
10245 $photo_data = $dbo->loadAssocList();
10246 $cur_captions = $photo_data[0]['imgcaptions'];
10247 $cur_photos = $photo_data[0]['moreimgs'];
10248 if (!empty($cur_photos)) {
10249 $moreimagestr .= $cur_photos;
10250 }
10251 }
10252
10253 $bulkphotos = VikRequest::getVar('bulkphotos', null, 'files', 'array');
10254
10255 if (is_array($bulkphotos) && count($bulkphotos) > 0 && array_key_exists('name', $bulkphotos) && count($bulkphotos['name']) > 0) {
10256 foreach ($bulkphotos['name'] as $updk => $photoname) {
10257 $uploaded_image = array();
10258 $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($photoname)));
10259 $src = $bulkphotos['tmp_name'][$updk];
10260 $j = "";
10261 if (file_exists($dest.$filename)) {
10262 $j = rand(171, 1717);
10263 while (file_exists($dest.$j.$filename)) {
10264 $j++;
10265 }
10266 }
10267 $finaldest=$dest.$j.$filename;
10268 $is_error = false;
10269 $err_key = '';
10270 if (array_key_exists('error', $bulkphotos) && array_key_exists($updk, $bulkphotos['error']) && !empty($bulkphotos['error'][$updk])) {
10271 if (array_key_exists($bulkphotos['error'][$updk], $error_messages)) {
10272 $is_error = true;
10273 $err_key = $bulkphotos['error'][$updk];
10274 }
10275 }
10276 if (!$is_error) {
10277 $check = getimagesize($bulkphotos['tmp_name'][$updk]);
10278 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
10279 if (VikBooking::uploadFile($src, $finaldest)) {
10280 $gimg = $j.$filename;
10281 //orig img
10282 $origmod = true;
10283 VikBooking::uploadFile($finaldest, $bigsdest.'big_'.$j.$filename, true);
10284 //thumb
10285 $thumbsize = VikBooking::getThumbSize();
10286 $thumb = $creativik->proportionalImage($finaldest, $thumbsdest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
10287 if (!$thumb || !$origmod) {
10288 if (file_exists($bigsdest.'big_'.$j.$filename)) @unlink($bigsdest.'big_'.$j.$filename);
10289 if (file_exists($thumbsdest.'thumb_'.$j.$filename)) @unlink($thumbsdest.'thumb_'.$j.$filename);
10290 $is_error = true;
10291 $err_key = 'vbo_perm';
10292 } else {
10293 $moreimagestr.=$j.$filename.";;";
10294 }
10295 @unlink($finaldest);
10296 } else {
10297 $is_error = true;
10298 $err_key = 'vbo_jupload';
10299 }
10300 } else {
10301 $is_error = true;
10302 $err_key = 'vbo_type';
10303 }
10304 }
10305 $img = new stdClass();
10306 if ($is_error) {
10307 $img->name = '';
10308 $img->size = '';
10309 $img->type = '';
10310 $img->url = '';
10311 $img->error = array_key_exists($err_key, $error_messages) ? $error_messages[$err_key] : 'Generic Error for Upload';
10312 } else {
10313 $img->name = $photoname;
10314 $img->size = $bulkphotos['size'][$updk];
10315 $img->type = $bulkphotos['type'][$updk];
10316 $img->url = VBO_SITE_URI.'resources/uploads/big_'.$j.$filename;
10317 }
10318 $resp['files'][] = $img;
10319 }
10320 } else {
10321 $res = new stdClass();
10322 $res->name = '';
10323 $res->size = '';
10324 $res->type = '';
10325 $res->url = '';
10326 $res->error = 'No images received for upload';
10327 $resp['files'][] = $res;
10328 }
10329 //Update current extra images string
10330 $q = "UPDATE `#__vikbooking_rooms` SET `moreimgs`=".$dbo->quote($moreimagestr)." WHERE `id`=".$proomid.";";
10331 $dbo->setQuery($q);
10332 $dbo->execute();
10333 $resp['actmoreimgs'] = $moreimagestr;
10334 //Update current extra images uploaded
10335 $cur_thumbs = '';
10336 $morei=explode(';;', $moreimagestr);
10337 if (@count($morei) > 0) {
10338 $imgcaptions = json_decode($cur_captions, true);
10339 $usecaptions = empty($imgcaptions) || is_null($imgcaptions) || !is_array($imgcaptions) || !(count($imgcaptions) > 0) ? false : true;
10340 $cur_thumbs .= '<ul class="vbo-sortable">';
10341 foreach ($morei as $ki => $mi) {
10342 if (!empty($mi)) {
10343 $cur_thumbs .= '<li class="vbo-editroom-currentphoto">';
10344 $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>';
10345 $cur_thumbs .= '<a class="vbo-toggle-imgcaption" href="javascript: void(0);" onclick="vbOpenImgDetails(\''.$ki.'\', this)"><i class="'.VikBookingIcons::i('cog').'"></i></a>';
10346 $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>';
10347 $cur_thumbs .= '</li>';
10348 }
10349 }
10350 $cur_thumbs .= '</ul>';
10351 $cur_thumbs .= '<br clear="all"/>';
10352 }
10353 $resp['currentthumbs'] = $cur_thumbs;
10354
10355 echo json_encode($resp);
10356 exit;
10357 }
10358
10359 public function loadsmsbalance() {
10360 //to be called via ajax
10361 $html = 'Error1 [N/A]';
10362 $sms_api = VikBooking::getSMSAPIClass();
10363 if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api)) {
10364 require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
10365 $sms_obj = new VikSmsApi(array(), VikBooking::getSMSParams());
10366 if (method_exists('VikSmsApi', 'estimate')) {
10367 $array_result = $sms_obj->estimate("+393711271611", "estimate credit");
10368 if ( $array_result->errorCode != 0 ) {
10369 $html = 'Error3 ['.$array_result->errorMsg.']';
10370 } else {
10371 $html = VikBooking::getCurrencySymb().' '.$array_result->userCredit;
10372 }
10373 } else {
10374 $html = 'Error2 [N/A]';
10375 }
10376 }
10377 echo $html;
10378 exit;
10379 }
10380
10381 public function loadsmsparams() {
10382 //to be called via ajax
10383 $html = '---------';
10384 $phpfile = VikRequest::getString('phpfile', '', 'request');
10385 if (!empty($phpfile)) {
10386 $sms_api = VikBooking::getSMSAPIClass();
10387 $sms_params = $sms_api == $phpfile ? VikBooking::getSMSParams(false) : '';
10388 $html = VikBooking::displaySMSParameters($phpfile, $sms_params);
10389 }
10390 echo $html;
10391 exit;
10392 }
10393
10394 public function loadcronparams() {
10395 //to be called via ajax
10396 $html = '---------';
10397 $phpfile = VikRequest::getString('phpfile', '', 'request');
10398 if (!empty($phpfile)) {
10399 $html = VikBooking::displayCronParameters($phpfile);
10400 }
10401 echo $html;
10402 exit;
10403 }
10404
10405 public function loadpaymentparams() {
10406 //to be called via ajax
10407 $html = '<p>---------</p>';
10408 $phpfile = VikRequest::getString('phpfile', '', 'request');
10409 if (!empty($phpfile)) {
10410 $html = VikBooking::displayPaymentParameters($phpfile);
10411 }
10412 echo $html;
10413 exit;
10414 }
10415
10416 public function setbookingtag() {
10417 //to be called via ajax
10418 $dbo = JFactory::getDBO();
10419 $pidorder = VikRequest::getInt('idorder', '', 'request');
10420 $ptagkey = VikRequest::getInt('tagkey', '', 'request');
10421 if (!empty($pidorder) && $ptagkey >= 0) {
10422 $all_tags = VikBooking::loadBookingsColorTags();
10423 if (array_key_exists($ptagkey, $all_tags)) {
10424 $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `id`=".(int)$pidorder.";";
10425 $dbo->setQuery($q);
10426 $dbo->execute();
10427 if ($dbo->getNumRows() > 0) {
10428 $newcolortag = json_encode($all_tags[$ptagkey]);
10429 $q = "UPDATE `#__vikbooking_orders` SET `colortag`=".$dbo->quote($newcolortag)." WHERE `id`=".(int)$pidorder.";";
10430 $dbo->setQuery($q);
10431 $dbo->execute();
10432 $newcolortag = $all_tags[$ptagkey];
10433 $newcolortag['name'] = JText::translate($newcolortag['name']);
10434 $newcolortag['fontcolor'] = VikBooking::getBestColorContrast($newcolortag['color']);
10435 echo json_encode($newcolortag);
10436 } else {
10437 echo 'e4j.error.Booking ('.$pidorder.') not found';
10438 }
10439 } else {
10440 echo 'e4j.error.Color Tag ('.$ptagkey.') not found';
10441 }
10442 } else {
10443 echo 'e4j.error.Missing Data';
10444 }
10445 exit;
10446 }
10447
10448 public function updatereceiptnum() {
10449 //to be called via ajax
10450 $pnewnum = VikRequest::getInt('newnum', '', 'request');
10451 $pnewnotes = VikRequest::getString('newnotes', '', 'request', VIKREQUEST_ALLOWRAW);
10452 $poid = VikRequest::getInt('oid', '', 'request');
10453 if ($pnewnum > 0) {
10454 VikBooking::getNextReceiptNumber($poid, $pnewnum);
10455 VikBooking::getReceiptNotes($pnewnotes);
10456 //Booking History
10457 VikBooking::getBookingHistoryInstance()->setBid($poid)->store('BR', JText::translate('VBOFISCRECEIPTNUM').': '.$pnewnum);
10458 //
10459 echo 'e4j.ok';
10460 exit;
10461 }
10462 echo 'e4j.error';
10463 exit;
10464 }
10465
10466 public function isroombookable() {
10467 //to be called via ajax
10468 $dbo = JFactory::getDBO();
10469 $res = array(
10470 'status' => 0,
10471 'err' => ''
10472 );
10473 $prid = VikRequest::getInt('rid', '', 'request');
10474 $pfdate = VikRequest::getString('fdate', '', 'request');
10475 $ptdate = VikRequest::getString('tdate', '', 'request');
10476 $room_info = array();
10477 $q = "SELECT * FROM `#__vikbooking_rooms` WHERE `id`=".(int)$prid.";";
10478 $dbo->setQuery($q);
10479 $dbo->execute();
10480 if ($dbo->getNumRows() > 0) {
10481 $room_info = $dbo->loadAssoc();
10482 }
10483 $pcheckinh = 0;
10484 $pcheckinm = 0;
10485 $pcheckouth = 0;
10486 $pcheckoutm = 0;
10487 $timeopst = VikBooking::getTimeOpenStore();
10488 if (is_array($timeopst)) {
10489 $opent = VikBooking::getHoursMinutes($timeopst[0]);
10490 $closet = VikBooking::getHoursMinutes($timeopst[1]);
10491 $pcheckinh = $opent[0];
10492 $pcheckinm = $opent[1];
10493 $pcheckouth = $closet[0];
10494 $pcheckoutm = $closet[1];
10495 }
10496 $from_ts = VikBooking::getDateTimestamp($pfdate, $pcheckinh, $pcheckinm);
10497 $to_ts = VikBooking::getDateTimestamp($ptdate, $pcheckouth, $pcheckoutm);
10498 if (
10499 count($room_info) > 0 &&
10500 (!empty($pfdate) && !empty($ptdate) && !empty($from_ts) && !empty($to_ts)) &&
10501 VikBooking::roomBookable($room_info['id'], $room_info['units'], $from_ts, $to_ts))
10502 {
10503 $res['status'] = 1;
10504 } else {
10505 if (!(count($room_info) > 0)) {
10506 $res['err'] = 'Room not found';
10507 } elseif (empty($pfdate) || empty($ptdate) || empty($from_ts) || empty($to_ts)) {
10508 $res['err'] = 'Invalid dates';
10509 } else {
10510 //not available
10511 $res['err'] = JText::sprintf('VBOBOOKADDROOMERR', $room_info['name'], $pfdate, $ptdate);
10512 }
10513 }
10514
10515 echo json_encode($res);
10516 exit;
10517 }
10518
10519 public function uploadsnapshot() {
10520 $snap_base_path = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans';
10521 /**
10522 * We no longer access the uploaded file from php://input, we now retrieve it as a regular file upload.
10523 * The old snapshot collection script with Flash no longer works in 2021.
10524 *
10525 * @since 1.14 (J) - 1.4.0 (WP)
10526 */
10527 $result = null;
10528 try {
10529 $result = VikBooking::uploadFileFromRequest(VikRequest::getVar('snapshot', null, 'files', 'array'), $snap_base_path, 'png,jpg,jpeg');
10530 } catch (RuntimeException $e) {
10531 echo "e4j.error.Error " . $e->getMessage();
10532 exit;
10533 }
10534
10535 if (!is_object($result)) {
10536 echo "e4j.error.Invalid upload response";
10537 exit;
10538 }
10539
10540 echo $result->filename;
10541 exit;
10542 }
10543
10544 public function checkvcmrateschanges() {
10545 //to be called via ajax
10546 $session = JFactory::getSession();
10547 $ret = array('changesCount' => 0, 'changesData' => '');
10548 $updforvcm = $session->get('vbVcmRatesUpd', '');
10549 if (!empty($updforvcm) && is_array($updforvcm) && count($updforvcm) > 0) {
10550 $ret['changesCount'] = $updforvcm['count'];
10551 $ret['changesData'] = $updforvcm;
10552 }
10553
10554 echo json_encode($ret);
10555 exit;
10556 }
10557
10558 /**
10559 * AJAX endpoint to load the details of one or more bookings.
10560 *
10561 * @return void
10562 *
10563 * @since 1.16.0 (J) - 1.6.0 (WP) the method was refactored.
10564 */
10565 public function getbookingsinfo()
10566 {
10567 //to be called via ajax
10568 $dbo = JFactory::getDbo();
10569
10570 $booking_infos = [];
10571 $bookings = [];
10572
10573 $pidorders = VikRequest::getString('idorders', '', 'request');
10574 $psubroom = VikRequest::getString('subroom', '', 'request');
10575 $pstatus = VikRequest::getString('status', '', 'request');
10576 $pstay_date = VikRequest::getString('stay_date', '', 'request');
10577 $pidroom = VikRequest::getInt('idroom', 0, 'request');
10578 $psharedcal = VikRequest::getInt('sharedcal', 0, 'request');
10579
10580 if (!empty($pidorders)) {
10581 $bookings = explode(',', $pidorders);
10582 foreach ($bookings as $k => $v) {
10583 $v = intval(str_replace('-', '', $v));
10584 if (empty($v)) {
10585 unset($bookings[$k]);
10586 continue;
10587 }
10588 $bookings[$k] = $v;
10589 }
10590 }
10591 $bookings = array_values($bookings);
10592
10593 if (!$bookings) {
10594 /**
10595 * AJAX requests made by the page availability overview may contain empty booking IDs
10596 * due to SQL errors that only occupied the room, but could not save the booking record.
10597 * Clean up busy records where the busy relations contain empty booking IDs.
10598 *
10599 * @since 1.14 (J) - 1.4.0 (WP)
10600 */
10601 $hanging_busy_ids = [];
10602
10603 $q = "SELECT `idbusy` FROM `#__vikbooking_ordersbusy` WHERE `idorder` = 0 OR `idorder` IS NULL;";
10604 $dbo->setQuery($q);
10605 $removelist = $dbo->loadAssocList();
10606 if ($removelist) {
10607 foreach ($removelist as $hanging_busy) {
10608 $hanging_busy_id = (int)$hanging_busy['idbusy'];
10609 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
10610 array_push($hanging_busy_ids, $hanging_busy_id);
10611 }
10612 }
10613 }
10614
10615 // let's check also for ghost records that only occupy the room
10616 $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);";
10617 $dbo->setQuery($q);
10618 $removelist = $dbo->loadAssocList();
10619 if ($removelist) {
10620 foreach ($removelist as $hanging_busy) {
10621 $hanging_busy_id = (int)$hanging_busy['id'];
10622 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
10623 array_push($hanging_busy_ids, $hanging_busy_id);
10624 }
10625 }
10626 }
10627
10628 if ($hanging_busy_ids) {
10629 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id` IN (" . implode(', ', $hanging_busy_ids) . ");";
10630 $dbo->setQuery($q);
10631 $dbo->execute();
10632 }
10633 //
10634
10635 // output the error
10636 VBOHttpDocument::getInstance()->close(500, '1 - ' . JText::translate('VBOVWGETBKERRMISSDATA'));
10637 }
10638
10639 $nowdf = VikBooking::getDateFormat(true);
10640 if ($nowdf == "%d/%m/%Y") {
10641 $df = 'd/m/Y';
10642 } elseif ($nowdf == "%m/%d/%Y") {
10643 $df = 'm/d/Y';
10644 } else {
10645 $df = 'Y/m/d';
10646 }
10647 $datesep = VikBooking::getDateSeparator(true);
10648 $currencysymb = VikBooking::getCurrencySymb();
10649 $current_y = date('Y');
10650 $current_ts = time();
10651 $short_meal_enums = VBOMealplanManager::getInstance()->getShortMealPlans();
10652
10653 $query = $dbo->getQuery(true);
10654 $query->select('o.*');
10655 $query->from($dbo->qn('#__vikbooking_orders', 'o'));
10656 if (!empty($pstay_date) && !empty($pidroom) && $pstatus == 'any') {
10657 // include the requested booking IDs and the cancelled reservations for this stay date
10658 $stay_date_info = getdate(strtotime($pstay_date));
10659 $lim_ts_to = mktime(23, 59, 59, $stay_date_info['mon'], $stay_date_info['mday'], $stay_date_info['year']);
10660 $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) . '))');
10661 // exclude the pending reservations
10662 $query->where($dbo->qn('o.status') . ' IN (' . $dbo->q('confirmed') . ', ' . $dbo->q('cancelled') . ')');
10663 } else {
10664 // include only the requested booking IDs
10665 $query->where($dbo->qn('o.id') . ' IN (' . implode(', ', $bookings) . ')');
10666 }
10667 if ($pstatus != 'any') {
10668 $query->where($dbo->qn('o.status') . ' != ' . $dbo->q('cancelled'));
10669 }
10670 if (!empty($pstay_date) && $pstatus == 'any') {
10671 // sort by confirmed status before cancelled status
10672 $query->order('CASE WHEN ' . $dbo->qn('o.status') . ' = ' . $dbo->q('confirmed') . ' THEN 1 ELSE 0 END DESC');
10673 $query->order($dbo->qn('o.id') . ' ASC');
10674 }
10675 $dbo->setQuery($query);
10676 $booking_infos = $dbo->loadAssocList();
10677
10678 foreach ($booking_infos as $k => $row) {
10679 // rooms, amounts and guests information
10680 $rooms = VikBooking::loadOrdersRoomsData($row['id']);
10681 $rids_involved = [];
10682 $room_names = [];
10683 $totadults = 0;
10684 $totchildren = 0;
10685 foreach ($rooms as $rr) {
10686 $rids_involved[] = $rr['idroom'];
10687 $totadults += $rr['adults'];
10688 $totchildren += $rr['children'];
10689 $room_names[] = $rr['room_name'];
10690 if ($row['split_stay']) {
10691 // do not sum guests in case of split stay booking
10692 $totadults = $rr['adults'];
10693 $totchildren = $rr['children'];
10694 }
10695 }
10696
10697 if (!empty($pstay_date) && !empty($pidroom) && $pstatus == 'any') {
10698 // make sure we have fetched a reservation for the correct room (in case of cancellations included)
10699 if (!in_array($pidroom, $rids_involved)) {
10700 $is_out_of_scope = true;
10701 if ($psharedcal && count($bookings) === 1) {
10702 $is_out_of_scope = ($row['id'] != $bookings[0]);
10703 }
10704 if ($is_out_of_scope) {
10705 // out of scope reservation, unset it and go to the next one
10706 unset($booking_infos[$k]);
10707 continue;
10708 }
10709 }
10710 }
10711
10712 // included meal plans to be displayed in case of single-room booking
10713 $included_meals = [];
10714 $rplan_name = '';
10715 if (count($rooms) === 1) {
10716 // rate plan name and ID, if any
10717 $active_rplan_id = 0;
10718 if (!empty($rooms[0]['otarplan'])) {
10719 $rplan_name = $rooms[0]['otarplan'];
10720 } else {
10721 list($rplan_name, $active_rplan_id) = VBOMealplanManager::getInstance()->getPriceData($rooms[0]['idtar']);
10722 }
10723
10724 // find the included meals
10725 if (!empty($rooms[0]['meals'])) {
10726 // display included meals defined at room-reservation record
10727 $included_meals = VBOMealplanManager::getInstance()->roomRateIncludedMeals($rooms[0]);
10728 } else {
10729 // fetch default included meals in the selected rate plan
10730 $included_meals = $active_rplan_id ? VBOMealplanManager::getInstance()->ratePlanIncludedMeals($active_rplan_id) : [];
10731 }
10732 if (!$included_meals && empty($row['meals']) && !empty($row['idorderota']) && !empty($row['channel']) && !empty($row['custdata'])) {
10733 // attempt to fetch the included meal plans from the raw customer data or OTA reservation and room
10734 $included_meals = VBOMealplanManager::getInstance()->otaDataIncludedMeals($row, $rooms[0]);
10735 }
10736 }
10737
10738 if ($included_meals) {
10739 $short_incl_meals = [];
10740 foreach ($included_meals as $meal_enum => $meal_name) {
10741 $short_incl_meals[] = $short_meal_enums[$meal_enum];
10742 }
10743 $booking_infos[$k]['meals_included'] = $short_incl_meals;
10744 }
10745
10746 $booking_infos[$k]['rateplan_name'] = $rplan_name;
10747 $booking_infos[$k]['currency_symb'] = $currencysymb;
10748 if ($row['status'] == 'confirmed') {
10749 $booking_infos[$k]['status_lbl'] = JText::translate('VBCONFIRMED');
10750 if ($row['checkout'] < $current_ts) {
10751 $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSOUT');
10752 } elseif ($row['checkin'] < $current_ts && $row['checkout'] > $current_ts) {
10753 if ($row['checked'] == 1) {
10754 $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSIN');
10755 } elseif ($row['checked'] == -1) {
10756 $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSNOS');
10757 }
10758 }
10759 } elseif ($row['status'] == 'standby') {
10760 $booking_infos[$k]['status_lbl'] = JText::translate('VBSTANDBY');
10761 } elseif ($row['status'] == 'cancelled') {
10762 $booking_infos[$k]['status_lbl'] = JText::translate('VBCANCELLED');
10763 } else {
10764 $booking_infos[$k]['status_lbl'] = $row['status'];
10765 }
10766 $booking_infos[$k]['colortag'] = VikBooking::applyBookingColorTag($row);
10767 if ($booking_infos[$k]['colortag']) {
10768 $booking_infos[$k]['colortag']['name'] = JText::translate($booking_infos[$k]['colortag']['name']);
10769 }
10770 $booking_infos[$k]['room_names'] = implode(', ', $room_names);
10771 $booking_infos[$k]['tot_adults'] = $totadults;
10772 $booking_infos[$k]['tot_children'] = $totchildren;
10773 $booking_infos[$k]['format_tot'] = VikBooking::numberFormat($row['total']);
10774 $booking_infos[$k]['format_totpaid'] = VikBooking::numberFormat($row['totpaid']);
10775
10776 // room indexes
10777 $rindexes = [];
10778 $av_room_indexes = [];
10779 $used_indexes_map = [];
10780 $sub_units_data = [];
10781 $optindexes = [];
10782 $subroomdata = !empty($psubroom) ? explode('-', $psubroom) : array();
10783 $missing_index = false;
10784 foreach ($rooms as $kor => $or) {
10785 if ($row['status'] != "confirmed" || $row['closure'] || empty($or['params'])) {
10786 // cannot build room indexes data
10787 continue;
10788 }
10789
10790 $room_params = json_decode($or['params'], true);
10791 if (!is_array($room_params) || empty($room_params['features']) || !is_array($room_params['features'])) {
10792 // no distinctive features information
10793 continue;
10794 }
10795
10796 if (!strlen($or['roomindex'])) {
10797 // turn flag on for missing index when room does support them
10798 $missing_index = true;
10799 // build array with available room indexes
10800 $av_indexes = [];
10801 $unavailable_indexes = VikBooking::getRoomUnitNumsUnavailable($row, $or['idroom']);
10802 foreach ($room_params['features'] as $rind => $rfeatures) {
10803 if (in_array($rind, $unavailable_indexes) || (isset($used_indexes_map[$or['idroom']]) && in_array($rind, $used_indexes_map[$or['idroom']]))) {
10804 continue;
10805 }
10806 foreach ($rfeatures as $fname => $fval) {
10807 if ($fval) {
10808 $av_indexes[$rind] = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
10809 break;
10810 }
10811 }
10812 }
10813 if ($av_indexes) {
10814 // push available indexes for this room
10815 $av_room_indexes[$kor] = [
10816 'rid' => $or['idroom'],
10817 'name' => $or['room_name'],
10818 'list' => $av_indexes,
10819 ];
10820 }
10821 // do not proceed any further
10822 continue;
10823 }
10824
10825 // parse distinctive features
10826 foreach ($room_params['features'] as $rind => $rfeatures) {
10827 if ($rind != $or['roomindex']) {
10828 continue;
10829 }
10830 $ind_str = '';
10831 $ind_str_short = '';
10832 foreach ($rfeatures as $fname => $fval) {
10833 if (strlen($fval)) {
10834 $ind_str = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
10835 $ind_str_short = $fval;
10836 break;
10837 }
10838 }
10839 if (!isset($rindexes[$or['room_name']])) {
10840 $rindexes[$or['room_name']] = $ind_str;
10841 $sub_units_data[$or['room_name']] = $ind_str_short;
10842 } else {
10843 $rindexes[$or['room_name']] .= ', ' . $ind_str;
10844 $sub_units_data[$or['room_name']] .= ', ' . $ind_str_short;
10845 }
10846 break;
10847 }
10848
10849 // build options to switch sub-unit index
10850 if (count($subroomdata) && !count($optindexes) && $or['idroom'] == (int)$subroomdata[0]) {
10851 // build the options for switching the room index for this room
10852 foreach ($room_params['features'] as $rind => $rfeatures) {
10853 foreach ($rfeatures as $fname => $fval) {
10854 if (strlen((string)$fval)) {
10855 $optindexes[] = '<option value="'.$rind.'"'.($rind == (int)$subroomdata[1] ? ' selected="selected"' : '').'>#'.$rind.' - '.JText::translate($fname).': '.$fval.'</option>';
10856 break;
10857 }
10858 }
10859 }
10860 }
10861 }
10862
10863 if ($rindexes) {
10864 $booking_infos[$k]['rindexes'] = $rindexes;
10865 $booking_infos[$k]['sub_units_data'] = $sub_units_data;
10866 }
10867
10868 if ($optindexes) {
10869 $booking_infos[$k]['optindexes'] = $optindexes;
10870 }
10871
10872 if ($missing_index && $av_room_indexes) {
10873 $booking_infos[$k]['av_room_indexes'] = $av_room_indexes;
10874 }
10875
10876 // include flag for missing room index
10877 $booking_infos[$k]['missing_index'] = $missing_index;
10878
10879 // channel provenience and small logo URL
10880 $ota_logo_img = JText::translate('VBORDFROMSITE');
10881 $booking_avatar_src = null;
10882 $booking_avatar_alt = null;
10883 if (!empty($row['channel'])) {
10884 $channelparts = explode('_', $row['channel']);
10885 $otachannel = array_key_exists(1, $channelparts) && strlen($channelparts[1]) > 0 ? $channelparts[1] : ucwords($channelparts[0]);
10886 $ota_logo_img = VikBooking::getVcmChannelsLogo($row['channel']);
10887 if ($ota_logo_img === false) {
10888 $ota_logo_img = $otachannel;
10889 } else {
10890 $ota_logo_img = '<img src="'.$ota_logo_img.'" class="vbo-channelimg-small"/>';
10891 }
10892 $logo_helper = VikBooking::getVcmChannelsLogo($row['channel'], $get_istance = true);
10893 if ($logo_helper !== false) {
10894 $booking_avatar_src = $logo_helper->getSmallLogoURL();
10895 $booking_avatar_alt = $logo_helper->provenience;
10896 }
10897 }
10898 $booking_infos[$k]['channelimg'] = $ota_logo_img;
10899 $booking_infos[$k]['avatar_src'] = $booking_avatar_src;
10900 $booking_infos[$k]['avatar_alt'] = $booking_avatar_alt;
10901
10902 // Customer Details
10903 $custdata = $row['custdata'];
10904 $custdata_parts = explode("\n", $row['custdata']);
10905 if (count($custdata_parts) > 2 && strpos($custdata_parts[0], ':') !== false && strpos($custdata_parts[1], ':') !== false) {
10906 //get the first two fields
10907 $custvalues = [];
10908 foreach ($custdata_parts as $custdet) {
10909 if (strlen($custdet) < 1) {
10910 continue;
10911 }
10912 $custdet_parts = explode(':', $custdet);
10913 if (count($custdet_parts) >= 2) {
10914 unset($custdet_parts[0]);
10915 array_push($custvalues, trim(implode(':', $custdet_parts)));
10916 }
10917 if (count($custvalues) > 1) {
10918 break;
10919 }
10920 }
10921 if (count($custvalues) > 1) {
10922 $custdata = implode(' ', $custvalues);
10923 }
10924 }
10925 if (strlen($custdata) > 45) {
10926 $custdata = (function_exists('mb_substr') ? mb_substr($custdata, 0, 45, 'UTF-8') : substr($custdata, 0, 45)) . " ...";
10927 }
10928
10929 // customer record details
10930 $customer = [];
10931 $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'];
10932 $dbo->setQuery($q, 0, 1);
10933 $dbo->execute();
10934 if ($dbo->getNumRows()) {
10935 $customer = $dbo->loadAssoc();
10936 if (!empty($customer['first_name'])) {
10937 $custdata = $customer['first_name'].' '.$customer['last_name'];
10938 if (!empty($customer['country'])) {
10939 if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$customer['country'].'.png')) {
10940 $custdata .= '<img src="'.VBO_ADMIN_URI.'resources/countries/'.$customer['country'].'.png'.'" title="'.htmlspecialchars($customer['country']).'" class="vbo-country-flag vbo-country-flag-left"/>';
10941 }
10942 }
10943 }
10944 }
10945 $booking_infos[$k]['customer'] = $customer;
10946
10947 // check if a profile picture is available for the customer
10948 if (!empty($customer['pic'])) {
10949 $booking_avatar_src = strpos($customer['pic'], 'http') === 0 ? $customer['pic'] : VBO_SITE_URI . 'resources/uploads/' . $customer['pic'];
10950 $booking_avatar_alt = basename($booking_avatar_src);
10951 $booking_infos[$k]['avatar_src'] = $booking_avatar_src;
10952 $booking_infos[$k]['avatar_alt'] = $booking_avatar_alt;
10953 }
10954
10955 // whether this is a closure
10956 $booking_infos[$k]['closure'] = (int)$row['closure'];
10957 $booking_infos[$k]['closure_txt'] = $row['closure'] ? JText::translate('VBDBTEXTROOMCLOSED') : null;
10958
10959 // short customer information
10960 $custdata = JText::translate('VBDBTEXTROOMCLOSED') == $row['custdata'] ? '<span class="vbordersroomclosed">'.JText::translate('VBDBTEXTROOMCLOSED').'</span>' : $custdata;
10961 $booking_infos[$k]['cinfo'] = $custdata;
10962
10963 // formatted dates
10964 $booking_infos[$k]['ts'] = date(str_replace("/", $datesep, $df).' H:i', $row['ts']);
10965 $booking_infos[$k]['checkin'] = date(str_replace("/", $datesep, $df).' H:i', $row['checkin']);
10966 $booking_infos[$k]['checkout'] = date(str_replace("/", $datesep, $df).' H:i', $row['checkout']);
10967
10968 // short booking date, check-in, check-out date format
10969 $stay_info_in = getdate($row['checkin']);
10970 $stay_info_out = getdate($row['checkout']);
10971 $str_checkin = date('d', $row['checkin']);
10972 $str_checkin .= $stay_info_in['mon'] != $stay_info_out['mon'] ? ' ' . VikBooking::sayMonth($stay_info_in['mon'], $short = true) : '';
10973 $str_checkout = date('d', $row['checkout']) . ' ' . VikBooking::sayMonth($stay_info_out['mon'], $short = true);
10974 if ($stay_info_in['year'] != $stay_info_out['year'] || $stay_info_in['year'] != $current_y || $stay_info_out['year'] != $current_y) {
10975 $str_checkout .= ' ' . $stay_info_out['year'];
10976 }
10977 $booking_infos[$k]['checkin_short'] = $str_checkin;
10978 $booking_infos[$k]['checkout_short'] = $str_checkout;
10979 $booking_infos[$k]['book_date'] = date(str_replace("/", $datesep, $df), $row['ts']);
10980 $booking_infos[$k]['book_time'] = date('H:i', $row['ts']);
10981 }
10982
10983 if (!$booking_infos) {
10984 if (!empty($pidroom) && $psharedcal) {
10985 // when this flag is enabled, we are excluding bookings not made directly
10986 // for the given room type ID, hence we may get an empty list due to shared calendars
10987 VBOHttpDocument::getInstance()->close(500, 'No bookings made directly for this room-type.');
10988 }
10989 // output the error
10990 VBOHttpDocument::getInstance()->close(500, '2 - ' . JText::translate('VBOVWGETBKERRMISSDATA'));
10991 }
10992
10993 // output the JSON encoded response and exit
10994 VBOHttpDocument::getInstance()->json($booking_infos);
10995 }
10996
10997 /**
10998 * AJAX endpoint to switch a booking room index.
10999 *
11000 * @return void
11001 *
11002 * @since 1.18.2 (J) - 1.8.2 (WP) method refactored.
11003 * @since 1.18.7 (J) - 1.8.7 (WP) introduced history update.
11004 */
11005 public function switchRoomIndex()
11006 {
11007 if (!JSession::checkToken()) {
11008 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
11009 }
11010
11011 $app = JFactory::getApplication();
11012 $dbo = JFactory::getDbo();
11013
11014 $bid = $app->input->getInt('bid', 0);
11015 $rid = $app->input->getInt('rid', 0);
11016 $old_rindex = $app->input->getInt('old_rindex', 0);
11017 $new_rindex = $app->input->getInt('new_rindex', 0);
11018 $is_tmp_row = $app->input->getBool('is_tmp_row', false);
11019 $is_from_tmp_row = $app->input->getBool('is_from_tmp_row', false);
11020
11021 if (empty($bid) || empty($rid) || (empty($old_rindex) && !$is_from_tmp_row) || (empty($new_rindex) && !$is_tmp_row) || $new_rindex < 0) {
11022 // abort for missing or invalid room indexes
11023 VBOHttpDocument::getInstance($app)->close(200, 'e4j.error.#1 Missing Data');
11024 }
11025
11026 // fetch the current booking room record
11027 $dbo->setQuery(
11028 $dbo->getQuery(true)
11029 ->select('*')
11030 ->from($dbo->qn('#__vikbooking_ordersrooms'))
11031 ->where($dbo->qn('idorder') . ' = ' . $bid)
11032 ->where($dbo->qn('idroom') . ' = ' . $rid)
11033 ->where($dbo->qn('roomindex') . ((empty($old_rindex) || $old_rindex == -1) && $is_from_tmp_row ? ' IS NULL' : ' = ' . $old_rindex))
11034 ->order($dbo->qn('id') . ' ASC')
11035 );
11036 $roomRow = $dbo->loadAssoc();
11037
11038 if (!$roomRow) {
11039 // abort for record not found
11040 VBOHttpDocument::getInstance($app)->close(200, 'e4j.error.#2 Record not found');
11041 }
11042
11043 // load booking and booking rooms data for the history before updating
11044 $booking = VikBooking::getBookingInfoFromID($bid);
11045 $prev_booking_rooms = VikBooking::loadOrdersRoomsData($bid);
11046 $current_booking_rooms = $prev_booking_rooms;
11047 // update new room index for current booking rooms
11048 foreach ($current_booking_rooms as $k => $booking_room) {
11049 if ($booking_room['id'] == $roomRow['id']) {
11050 // update new room index
11051 $current_booking_rooms[$k]['roomindex'] = empty($new_rindex) && $is_tmp_row ? null : $new_rindex;
11052 break;
11053 }
11054 }
11055
11056 // update booking room record by switching sub-unit
11057 $dbo->setQuery(
11058 $dbo->getQuery(true)
11059 ->update($dbo->qn('#__vikbooking_ordersrooms'))
11060 ->set($dbo->qn('roomindex') . ' = ' . (empty($new_rindex) && $is_tmp_row ? 'NULL' : $new_rindex))
11061 ->where($dbo->qn('id') . ' = ' . (int) $roomRow['id'])
11062 );
11063 $dbo->execute();
11064
11065 // update history record by setting the proper bookings data
11066 $user = JFactory::getUser();
11067 VikBooking::getBookingHistoryInstance($bid)
11068 ->setPrevBooking(array_merge($booking, ['rooms_info' => $prev_booking_rooms]))
11069 ->setBookingData($booking, $current_booking_rooms)
11070 ->store(
11071 'MB',
11072 sprintf(
11073 '%s [%d → %d]',
11074 JText::translate('VBODEFAULTDISTFEATUREONE'),
11075 (int) $roomRow['roomindex'],
11076 (int) (empty($new_rindex) && $is_tmp_row ? 0 : $new_rindex)
11077 ) . " ({$user->name})"
11078 );
11079
11080 // process completed
11081 VBOHttpDocument::getInstance($app)->close(200, 'e4j.ok');
11082 }
11083
11084 public function searchcustomer()
11085 {
11086 // to be called via ajax
11087 $dbo = JFactory::getDbo();
11088
11089 $kw = VikRequest::getString('kw', '', 'request');
11090 $nopin = VikRequest::getInt('nopin', '', 'request');
11091 $email = VikRequest::getInt('email', 0, 'request');
11092 $selector = VikRequest::getString('selector', 'vbo-custsearchres-entry', 'request');
11093 $no_script = VikRequest::getInt('no_script', 0, 'request');
11094
11095 if (!strlen($kw)) {
11096 VBOHttpDocument::getInstance()->close(200, '');
11097 }
11098
11099 if ($nopin > 0) {
11100 //page all bookings
11101 $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;";
11102 } elseif ($email > 0) {
11103 // page calendar for checking if an email exists
11104 $q = "SELECT `first_name`, `last_name`, `email` FROM `#__vikbooking_customers` WHERE `email`=".$dbo->quote($kw).";";
11105 } else {
11106 //page calendar
11107 $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;";
11108 }
11109 $dbo->setQuery($q);
11110 $customers = $dbo->loadAssocList();
11111
11112 if (!$customers) {
11113 VBOHttpDocument::getInstance()->close(200, '');
11114 }
11115
11116 if ($email > 0) {
11117 VBOHttpDocument::getInstance()->json($customers[0]);
11118 }
11119
11120 $cust_old_fields = array();
11121 $cstring_search = '<div class="vbo-custsearchres-inner">' . "\n";
11122 foreach ($customers as $k => $v) {
11123 $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";
11124 $cstring_search .= '<span class="vbo-custsearchres-cflag">';
11125 if (!empty($v['pic'])) {
11126 $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";
11127 } elseif (is_file(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$v['country'].'.png')) {
11128 $cstring_search .= '<img src="'.VBO_ADMIN_URI.'resources/countries/'.$v['country'].'.png'.'" title="'.htmlspecialchars($v['country']).'" class="vbo-country-flag"/>'."\n";
11129 } else {
11130 $cstring_search .= '<i class="' . VikBookingIcons::i('globe') . '"></i>';
11131 }
11132 $cstring_search .= '</span>';
11133 $cstring_search .= '<span class="vbo-custsearchres-name" title="'.htmlspecialchars($v['email']).'">'.$v['first_name'].' '.$v['last_name'].'</span>'."\n";
11134 if (!($nopin > 0)) {
11135 $cstring_search .= '<span class="vbo-custsearchres-pin">'.$v['pin'].'</span>'."\n";
11136 }
11137 $cstring_search .= '</div>'."\n";
11138 if (!empty($v['cfields'])) {
11139 $oldfields = json_decode($v['cfields'], true);
11140 if (is_array($oldfields) && count($oldfields)) {
11141 $cust_old_fields[$v['id']] = $oldfields;
11142 }
11143 }
11144 }
11145 $cstring_search .= '</div>'."\n";
11146
11147 /**
11148 * Add the necessary JS code for the arrow navigation.
11149 */
11150 $cstring_search_js = '<script type="text/javascript">';
11151 $cstring_search_js .= '
11152 var vboCust = jQuery(".' . $selector . '");
11153 var vboCustSelected = null;
11154 var vboCustomerNavigationFn = (e) => {
11155 if (e.which === 40) {
11156 if (vboCustSelected) {
11157 vboCustSelected.removeClass("' . $selector . '-highligthed");
11158 next = vboCustSelected.next();
11159 if (next.length > 0) {
11160 vboCustSelected = next.addClass("' . $selector . '-highligthed");
11161 } else {
11162 vboCustSelected = vboCust.eq(0).addClass("' . $selector . '-highligthed");
11163 }
11164 } else {
11165 vboCustSelected = vboCust.eq(0).addClass("' . $selector . '-highligthed");
11166 }
11167 } else if (e.which === 38) {
11168 if (vboCustSelected) {
11169 vboCustSelected.removeClass("' . $selector . '-highligthed");
11170 next = vboCustSelected.prev();
11171 if (next.length > 0) {
11172 vboCustSelected = next.addClass("' . $selector . '-highligthed");
11173 } else {
11174 vboCustSelected = vboCust.last().addClass("' . $selector . '-highligthed");
11175 }
11176 } else {
11177 vboCustSelected = vboCust.last().addClass("' . $selector . '-highligthed");
11178 }
11179 } else if (e.which === 13) {
11180 if (vboCustSelected) {
11181 vboCustSelected.trigger("click");
11182 }
11183 }
11184 };
11185 jQuery(window).off("keydown", vboCustomerNavigationFn);
11186 jQuery(window).keydown(vboCustomerNavigationFn);
11187 document.addEventListener("vbo-search-customers-navigation-dismissed", (e) => {
11188 jQuery(window).off("keydown", vboCustomerNavigationFn);
11189 })
11190 jQuery(".' . $selector . '").off("hover");
11191 jQuery(".' . $selector . '").hover(function() {
11192 if (vboCustSelected) {
11193 vboCustSelected.removeClass("' . $selector . '-highligthed");
11194 vboCustSelected = null;
11195 }
11196 vboCustSelected = jQuery(this).addClass("' . $selector . '-highligthed");
11197 }, function() {
11198 if (vboCustSelected) {
11199 vboCustSelected.removeClass("' . $selector . '-highligthed");
11200 vboCustSelected = null;
11201 }
11202 jQuery(this).removeClass("' . $selector . '-highligthed");
11203 });';
11204 $cstring_search_js .= '</script>';
11205
11206 if (!$no_script) {
11207 // append JS
11208 $cstring_search .= $cstring_search_js;
11209 }
11210
11211 VBOHttpDocument::getInstance()->json([($nopin > 0 ? '' : $cust_old_fields), $cstring_search]);
11212 }
11213
11214 public function sharesignaturelink() {
11215 //to be called via ajax
11216 $dbo = JFactory::getDBO();
11217 $response = array(
11218 'status' => 0,
11219 'error' => 'Generic Error'
11220 );
11221 $pbid = VikRequest::getInt('bid', '', 'request');
11222 $phow = VikRequest::getString('how', '', 'request');
11223 $pto = VikRequest::getString('to', '', 'request');
11224 $pcustomer = VikRequest::getInt('customer', '', 'request');
11225 $cpin = VikBooking::getCPinIstance();
11226 $customer_info = $cpin->getCustomerByID($pcustomer);
11227 if (!empty($pbid) && !empty($phow) && !empty($pto) && count($customer_info) > 0) {
11228 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$pbid." AND `status`='confirmed' AND `checked` > 0;";
11229 $dbo->setQuery($q);
11230 $dbo->execute();
11231 if ($dbo->getNumRows() > 0) {
11232 $row = $dbo->loadAssoc();
11233
11234 $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'];
11235 if (VBOPlatformDetection::isWordPress()) {
11236 /**
11237 * @wponly Rewrite URI for front-end signature
11238 */
11239 $share_link = str_replace(JUri::root(), '', $share_link);
11240 $model = JModel::getInstance('vikbooking', 'shortcodes');
11241 $itemid = $model->all('post_id', $full = true);
11242 if (count($itemid)) {
11243 $share_link = JRoute::rewrite($share_link . "&Itemid={$itemid[0]->post_id}", false);
11244 }
11245 } else {
11246 /**
11247 * @joomlaonly
11248 */
11249 $best_menuitem_id = VikBooking::findProperItemIdType(['vikbooking', 'booking'], $row['lang']);
11250 if ($best_menuitem_id) {
11251 $share_base = str_replace(JUri::root(), '', $share_link);
11252 $share_link = VikBooking::externalroute($share_base, $xhtml = false, $best_menuitem_id);
11253 }
11254 }
11255
11256 $share_message = JText::sprintf('VBOSIGNSHAREMESSAGE', ltrim($customer_info['first_name'].' '.$customer_info['last_name']), $share_link, VikBooking::getFrontTitle());
11257 if ($phow == 'email') {
11258 $sender = VikBooking::getSenderMail();
11259 $vbo_app = VikBooking::getVboApplication();
11260 $vbo_app->sendMail($sender, $sender, $pto, $sender, JText::translate('VBOSIGNSHARESUBJECT'), $share_message, false);
11261 $response['status'] = 1;
11262 } elseif ($phow == 'sms') {
11263 $share_message = JText::sprintf('VBOSIGNSHAREMESSAGESMS', ltrim($customer_info['first_name'].' '.$customer_info['last_name']), $share_link, VikBooking::getFrontTitle());
11264 $sms_api = VikBooking::getSMSAPIClass();
11265 $sms_api_params = VikBooking::getSMSParams();
11266 if (!empty($sms_api) && file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api) && !empty($sms_api_params)) {
11267 require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
11268 $sms_obj = new VikSmsApi(array(), $sms_api_params);
11269 $response_obj = $sms_obj->sendMessage($pto, $share_message);
11270 if ($sms_obj->validateResponse($response_obj)) {
11271 $response['status'] = 1;
11272 } else {
11273 $response['error'] = $sms_obj->getLog();
11274 }
11275 } else {
11276 $response['error'] = 'No SMS Provider Configured';
11277 }
11278 } else {
11279 $response['error'] = 'Invalid Sending Method';
11280 }
11281 } else {
11282 $response['error'] = 'Invalid Booking ID';
11283 }
11284 } else {
11285 $response['error'] = 'Empty values';
11286 }
11287
11288 echo json_encode($response);
11289 exit;
11290 }
11291
11292 public function dayselectioncount() {
11293 //to be called via ajax
11294 $tsinit = VikRequest::getString('dinit', '', 'request');
11295 $tsend = VikRequest::getString('dend', '', 'request');
11296 if (strlen($tsinit) > 0 && strlen($tsend) > 0) {
11297 $ptsinit=VikBooking::getDateTimestamp($tsinit, '0', '0');
11298 $ptsend=VikBooking::getDateTimestamp($tsend, '23', '59');
11299 $diff = $ptsend - $ptsinit;
11300 if ($diff >= 172800) {
11301 $datef = VikBooking::getDateFormat(true);
11302 if ($datef=="%d/%m/%Y") {
11303 $df = 'd-m-Y';
11304 } else {
11305 $df = 'Y-m-d';
11306 }
11307 //minimum 2 days for excluding some days
11308 $daysdiff = floor($diff / 86400);
11309 $infoinit = getdate($ptsinit);
11310 $select = '';
11311 $select .= '<div style="display: inline-block;"><select name="excludeday[]" multiple="multiple" size="'.($daysdiff > 8 ? 8 : $daysdiff).'" id="vboexclusion">';
11312 for($i = 0; $i <= $daysdiff; $i++) {
11313 $ts = $i > 0 ? mktime(0, 0, 0, $infoinit['mon'], ((int)$infoinit['mday'] + $i), $infoinit['year']) : $ptsinit;
11314 $infots = getdate($ts);
11315 $optval = $infots['mon'].'-'.$infots['mday'].'-'.$infots['year'];
11316 $select .= '<option value="'.$optval.'">'.date($df, $ts).'</option>';
11317 }
11318 $select .= '</select></div>';
11319 //excluded days of the week
11320 if ($daysdiff >= 14) {
11321 $select .= '<div style="display: inline-block; margin-left: 40px;"><select name="excludewdays[]" multiple="multiple" size="8" id="excludewdays" onchange="vboExcludeWDays();">';
11322 $select .= '<optgroup label="'.JText::translate('VBOEXCLWEEKD').'">';
11323 $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>';
11324 $select .= '</optgroup>';
11325 $select .= '</select></div>';
11326 }
11327 //
11328 echo $select;
11329 } else {
11330 echo '';
11331 }
11332 } else {
11333 echo '';
11334 }
11335 exit;
11336 }
11337
11338 public function createcheckindoc()
11339 {
11340 if (!JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking')) {
11341 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
11342 }
11343
11344 $cid = VikRequest::getVar('cid', array(0));
11345 $id = $cid[0];
11346
11347 $dbo = JFactory::getDBO();
11348 $mainframe = JFactory::getApplication();
11349 $vbo_tn = VikBooking::getTranslator();
11350 $lang = JFactory::getLanguage();
11351 $ptmpl = VikRequest::getString('tmpl', '', 'request');
11352 $psignature = VikRequest::getString('signature', '', 'request', VIKREQUEST_ALLOWRAW);
11353 $ppad_width = VikRequest::getInt('pad_width', '', 'request');
11354 $ppad_ratio = VikRequest::getInt('pad_ratio', '', 'request');
11355 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$id." AND `status`='confirmed' AND `checked` > 0;";
11356 $dbo->setQuery($q);
11357 $row = $dbo->loadAssoc();
11358 if (!$row) {
11359 $mainframe->redirect('index.php');
11360 exit;
11361 }
11362 if (!empty($row['lang'])) {
11363 if ($lang->getTag() != $row['lang']) {
11364 if (VBOPlatformDetection::isWordPress()) {
11365 $lang->load('com_vikbooking', VIKBOOKING_LANG, $row['lang'], true);
11366 } else {
11367 $lang->load('com_vikbooking', JPATH_SITE, $row['lang'], true);
11368 $lang->load('com_vikbooking', JPATH_ADMINISTRATOR, $row['lang'], true);
11369 $lang->load('joomla', JPATH_SITE, $row['lang'], true);
11370 $lang->load('joomla', JPATH_ADMINISTRATOR, $row['lang'], true);
11371 }
11372 }
11373 if ($vbo_tn->getDefaultLang() != $row['lang']) {
11374 // force the translation to start because contents should be translated
11375 $vbo_tn::$force_tolang = $row['lang'];
11376 }
11377 }
11378 $customer = array();
11379 $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'].";";
11380 $dbo->setQuery($q);
11381 $dbo->execute();
11382 if ($dbo->getNumRows() > 0) {
11383 $customer = $dbo->loadAssoc();
11384 if (!empty($customer['country'])) {
11385 if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$customer['country'].'.png')) {
11386 $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"/>';
11387 }
11388 }
11389 }
11390 if (!(count($customer) > 0)) {
11391 VikError::raiseWarning('', JText::translate('VBOCHECKINERRNOCUSTOMER'));
11392 $mainframe->redirect('index.php?option=com_vikbooking&task=newcustomer&checkin=1&bid='.$row['id'].($ptmpl == 'component' ? '&tmpl=component' : ''));
11393 exit;
11394 }
11395 $customer['pax_data'] = !empty($customer['pax_data']) ? json_decode($customer['pax_data'], true) : array();
11396 //check if the signature has been submitted
11397 $signature_data = '';
11398 $cont_type = '';
11399 if (!empty($psignature)) {
11400 //check whether the format is accepted
11401 if (strpos($psignature, 'image/png') !== false || strpos($psignature, 'image/jpeg') !== false || strpos($psignature, 'image/svg') !== false) {
11402 $parts = explode(';base64,', $psignature);
11403 $cont_type_parts = explode('image/', $parts[0]);
11404 $cont_type = $cont_type_parts[1];
11405 if (!empty($parts[1])) {
11406 $signature_data = base64_decode($parts[1]);
11407 }
11408 }
11409 }
11410 if (!empty($signature_data)) {
11411 //write file
11412 $sign_fname = $row['id'].'_'.$row['sid'].'_'.$customer['id'].'.'.$cont_type;
11413 $filepath = VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'idscans' . DIRECTORY_SEPARATOR . $sign_fname;
11414 $fp = fopen($filepath, 'w+');
11415 $bytes = fwrite($fp, $signature_data);
11416 fclose($fp);
11417 if ($bytes !== false && $bytes > 0) {
11418 //update the signature in the DB
11419 $q = "UPDATE `#__vikbooking_customers_orders` SET `signature`=".$dbo->quote($sign_fname)." WHERE `idorder`=".(int)$row['id'].";";
11420 $dbo->setQuery($q);
11421 $dbo->execute();
11422 $customer['signature'] = $sign_fname;
11423 //resize image for screens with high resolution
11424 if ($ppad_ratio > 1) {
11425 $new_width = floor(($ppad_width / 2));
11426 $creativik = new vikResizer();
11427 $creativik->proportionalImage($filepath, $filepath, $new_width, $new_width);
11428 } else {
11429 /**
11430 * @wponly - trigger files mirroring
11431 */
11432 VikBookingLoader::import('update.manager');
11433 VikBookingUpdateManager::triggerUploadBackup($filepath);
11434 //
11435 }
11436 //
11437 } else {
11438 VikError::raiseWarning('', JText::translate('VBOERRSTORESIGNFILE'));
11439 }
11440 }
11441 //
11442 //generate PDF for check-in document by parsing the apposite template file
11443 $booking_rooms = array();
11444 $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'].";";
11445 $dbo->setQuery($q);
11446 $dbo->execute();
11447 if ($dbo->getNumRows() > 0) {
11448 $booking_rooms = $dbo->loadAssocList();
11449 if (!empty($row['lang'])) {
11450 $vbo_tn->translateContents($booking_rooms, '#__vikbooking_rooms', array('id' => 'idroom', 'room_name' => 'name'), array(), $row['lang']);
11451 }
11452 }
11453 if (!class_exists('TCPDF')) {
11454 require_once(VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "tcpdf" . DIRECTORY_SEPARATOR . 'tcpdf.php');
11455 }
11456 $usepdffont = is_file(VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "tcpdf" . DIRECTORY_SEPARATOR . "fonts" . DIRECTORY_SEPARATOR . "dejavusans.php") ? 'dejavusans' : 'helvetica';
11457
11458 /**
11459 * Trigger event to allow third party plugins to return a specific font name.
11460 *
11461 * @since 1.16.0 (J) - 1.6.0 (WP)
11462 */
11463 $custom_pdf_font = VBOFactory::getPlatform()->getDispatcher()->filter('onGetPdfFontNameVikBooking', [$usepdffont]);
11464 if (is_array($custom_pdf_font) && !empty($custom_pdf_font[0])) {
11465 $usepdffont = $custom_pdf_font[0];
11466 }
11467
11468 list($checkintpl, $pdfparams) = VikBooking::loadCheckinDocTmpl($row, $booking_rooms, $customer);
11469 $checkin_body = VikBooking::parseCheckinDocTemplate($checkintpl, $row, $booking_rooms, $customer);
11470
11471 // build the proper document SID for bc
11472 $doc_sid = $row['sid'] ?: $row['idorderota'] ?: '';
11473 $pdffname = $row['id'] . '_' . $doc_sid . '.pdf';
11474
11475 $pathpdf = VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "checkins" . DIRECTORY_SEPARATOR . "generated" . DIRECTORY_SEPARATOR . $pdffname;
11476 if (file_exists($pathpdf)) @unlink($pathpdf);
11477 $pdf_page_format = is_array($pdfparams['pdf_page_format']) ? $pdfparams['pdf_page_format'] : constant($pdfparams['pdf_page_format']);
11478 $pdf = new TCPDF(constant($pdfparams['pdf_page_orientation']), constant($pdfparams['pdf_unit']), $pdf_page_format, true, 'UTF-8', false);
11479 $pdf->SetTitle(JText::translate('VBOCHECKINDOCTITLE'));
11480 //Header for each page of the pdf
11481 if ($pdfparams['show_header'] == 1 && count($pdfparams['header_data']) > 0) {
11482 $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]);
11483 }
11484 //header and footer fonts
11485 $pdf->setHeaderFont(array($usepdffont, '', $pdfparams['header_font_size']));
11486 $pdf->setFooterFont(array($usepdffont, '', $pdfparams['footer_font_size']));
11487 //margins
11488 $pdf->SetMargins(constant($pdfparams['pdf_margin_left']), constant($pdfparams['pdf_margin_top']), constant($pdfparams['pdf_margin_right']));
11489 $pdf->SetHeaderMargin(constant($pdfparams['pdf_margin_header']));
11490 $pdf->SetFooterMargin(constant($pdfparams['pdf_margin_footer']));
11491 //
11492 $pdf->SetAutoPageBreak(true, constant($pdfparams['pdf_margin_bottom']));
11493 $pdf->setImageScale(constant($pdfparams['pdf_image_scale_ratio']));
11494 $pdf->SetFont($usepdffont, '', (int)$pdfparams['body_font_size']);
11495 if ($pdfparams['show_header'] == 0 || !(count($pdfparams['header_data']) > 0)) {
11496 $pdf->SetPrintHeader(false);
11497 }
11498 if ($pdfparams['show_footer'] == 0) {
11499 $pdf->SetPrintFooter(false);
11500 }
11501 $pdf->AddPage();
11502 $pdf->writeHTML($checkin_body, true, false, true, false, '');
11503 $pdf->lastPage();
11504 $pdf->Output($pathpdf, 'F');
11505 if (!file_exists($pathpdf)) {
11506 VikError::raiseWarning('', JText::translate('VBOERRGENCHECKINDOC'));
11507 } else {
11508 $q = "UPDATE `#__vikbooking_customers_orders` SET `checkindoc`=".$dbo->quote($pdffname)." WHERE `idorder`=".(int)$row['id'].";";
11509 $dbo->setQuery($q);
11510 $dbo->execute();
11511 $mainframe->enqueueMessage(JText::translate('VBOGENCHECKINDOCSUCCESS'));
11512 /**
11513 * @wponly - trigger files mirroring
11514 */
11515 VikBookingLoader::import('update.manager');
11516 VikBookingUpdateManager::triggerUploadBackup($pathpdf);
11517 //
11518 }
11519 //
11520 /**
11521 * @wponly - this task is executed via Ajax for the Modal forms listener. We cannot redirect to tmpl=component
11522 */
11523 $mainframe->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id']);
11524 exit;
11525 }
11526
11527 public function updatebookingcheckin()
11528 {
11529 if (!JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking')) {
11530 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
11531 }
11532
11533 $cid = VikRequest::getVar('cid', array(0));
11534 $id = $cid[0];
11535
11536 $dbo = JFactory::getDbo();
11537 $app = JFactory::getApplication();
11538
11539 $ptmpl = $app->input->getString('tmpl', '');
11540 $pnewtotpaid = $app->input->getFloat('newtotpaid', 0);
11541 $pguests = $app->input->get('guests', [], 'array');
11542 $pcomments = JComponentHelper::filterText($app->input->get('comments', '', 'raw'));
11543 $pcheckin_action = $app->input->getInt('checkin_action', 0);
11544 $valid_actions = array(-1, 0, 1, 2);
11545 if (!in_array($pcheckin_action, $valid_actions)) {
11546 $app->redirect('index.php');
11547 exit;
11548 }
11549 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$id." AND `status`='confirmed';";
11550 $dbo->setQuery($q);
11551 $dbo->execute();
11552 if ($dbo->getNumRows() < 1) {
11553 $app->redirect('index.php');
11554 exit;
11555 }
11556 $row = $dbo->loadAssoc();
11557 $q = "SELECT * FROM `#__vikbooking_customers_orders` WHERE `idorder`=".$row['id'].";";
11558 $dbo->setQuery($q);
11559 $dbo->execute();
11560 if ($dbo->getNumRows() < 1) {
11561 VikError::raiseWarning('', JText::translate('VBOCHECKINERRNOCUSTOMER'));
11562 $app->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id'].($ptmpl == 'component' ? '&tmpl=component' : ''));
11563 exit;
11564 }
11565 $custorder = $dbo->loadAssoc();
11566 //update checked status and new total paid
11567 $q = "UPDATE `#__vikbooking_orders` SET `checked`=".$pcheckin_action."".($pnewtotpaid > 0 ? ', `totpaid`='.$pnewtotpaid : '')." WHERE `id`=".$row['id'].";";
11568 $dbo->setQuery($q);
11569 $dbo->execute();
11570 // Booking History log for new amount paid (payment update)
11571 if ($pnewtotpaid > 0 && $pnewtotpaid > (float)$row['totpaid']) {
11572 $extra_data = new stdClass;
11573 $extra_data->amount_paid = ($pnewtotpaid - (float)$row['totpaid']);
11574 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->setExtraData($extra_data)->store('PU', JText::sprintf('VBOPREVAMOUNTPAID', VikBooking::numberFormat((float)$row['totpaid'])));
11575 }
11576 //
11577 //Booking History
11578 $hist_type = 'A';
11579 if ($pcheckin_action < 0) {
11580 $hist_type = 'Z';
11581 } elseif ($pcheckin_action == 1) {
11582 $hist_type = 'B';
11583 } elseif ($pcheckin_action == 2) {
11584 $hist_type = 'C';
11585 }
11586 $user = JFactory::getUser();
11587 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('R' . $hist_type, "({$user->name})");
11588 //
11589 //Guests Details
11590 $guests_details = array();
11591 list($pax_fields, $pax_fields_attributes) = VikBooking::getPaxFields();
11592 // grab also the fields for front-end pre check-in
11593 list($pre_pax_fields, $pre_pax_fields_attributes) = VikBooking::getPaxFields(true);
11594 //
11595 foreach ($pguests as $ind => $adults) {
11596 foreach ($adults as $aduind => $details) {
11597 foreach ($pax_fields as $key => $v) {
11598 if (isset($details[$key]) && ((is_scalar($details[$key]) && strlen($details[$key])) || !empty($details[$key]))) {
11599 if (!isset($guests_details[$ind])) {
11600 $guests_details[$ind] = array();
11601 }
11602 if (!isset($guests_details[$ind][$aduind])) {
11603 $guests_details[$ind][$aduind] = array();
11604 }
11605 $guests_details[$ind][$aduind][$key] = $details[$key];
11606 }
11607 }
11608 foreach ($pre_pax_fields as $key => $v) {
11609 if (isset($pax_fields[$key])) {
11610 // we must have parsed this back-end field already
11611 continue;
11612 }
11613 if (isset($details[$key]) && ((is_scalar($details[$key]) && strlen($details[$key])) || !empty($details[$key]))) {
11614 if (!isset($guests_details[$ind])) {
11615 $guests_details[$ind] = array();
11616 }
11617 if (!isset($guests_details[$ind][$aduind])) {
11618 $guests_details[$ind][$aduind] = array();
11619 }
11620 if (!isset($guests_details[$ind][$aduind][$key])) {
11621 $guests_details[$ind][$aduind][$key] = $details[$key];
11622 }
11623 }
11624 }
11625 }
11626 }
11627
11628 if ($guests_details) {
11629 // current pax data may contain some extra information collected via front-end pre-checkin so we need to merge them
11630 $curpaxdata = json_decode($custorder['pax_data'], true);
11631 if (is_array($curpaxdata) && $curpaxdata) {
11632 // scan new guest registration details
11633 foreach ($guests_details as $ind => $groom) {
11634 foreach ($groom as $aduind => $aduinfo) {
11635 if (isset($curpaxdata[$ind][$aduind])) {
11636 $guests_details[$ind][$aduind] = array_merge($curpaxdata[$ind][$aduind], $guests_details[$ind][$aduind]);
11637 // unset some default pax fields that were not specified now, or data cannot be deleted for guests
11638 foreach ($guests_details[$ind][$aduind] as $key => $det) {
11639 if (isset($pguests[$ind][$aduind][$key]) && empty($pguests[$ind][$aduind][$key])) {
11640 // this default pax field was specified as empty now, so we cannot merge it
11641 unset($guests_details[$ind][$aduind][$key]);
11642 }
11643 }
11644 }
11645 }
11646 }
11647
11648 /**
11649 * In order to not lose any custom registration data added through PMS reports,
11650 * we scan the previous registration data to ensure we keep them in the update.
11651 *
11652 * @since 1.16.10 (J) - 1.6.10 (WP)
11653 */
11654 foreach ($curpaxdata as $ind => $groom) {
11655 if (!isset($guests_details[$ind])) {
11656 // ignore deleted room registration
11657 continue;
11658 }
11659 foreach ($groom as $aduind => $aduinfo) {
11660 if (!isset($guests_details[$ind][$aduind]) || !is_array($aduinfo)) {
11661 // ignore deleted room-guest registration
11662 continue;
11663 }
11664 foreach ($aduinfo as $field_key => $field_val) {
11665 if (!isset($guests_details[$ind][$aduind][$field_key]) && !empty($field_val)) {
11666 // merge previous room-guest registration data
11667 $guests_details[$ind][$aduind][$field_key] = $field_val;
11668 }
11669 }
11670 }
11671 }
11672 }
11673
11674 $q = "UPDATE `#__vikbooking_customers_orders` SET `pax_data`=" . $dbo->q(json_encode($guests_details)) . " WHERE `id`=" . (int) $custorder['id'] . ";";
11675 $dbo->setQuery($q);
11676 $dbo->execute();
11677 }
11678
11679 //'checked' status comments
11680 $q = "UPDATE `#__vikbooking_customers_orders` SET `comments`=".$dbo->quote($pcomments)." WHERE `id`=".$custorder['id'].";";
11681 $dbo->setQuery($q);
11682 $dbo->execute();
11683
11684 $app->enqueueMessage(JText::translate('VBOCHECKINSTATUSUPDATED'));
11685 $app->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id'].($pcheckin_action != $row['checked'] ? '&changed=1' : '').($ptmpl == 'component' ? '&tmpl=component' : ''));
11686 exit;
11687 }
11688
11689 public function alterbooking()
11690 {
11691 $dbo = JFactory::getDbo();
11692 $app = JFactory::getApplication();
11693 $user = JFactory::getUser();
11694
11695 $response = array(
11696 'esit' => 1,
11697 'message' => '',
11698 'vcm' => '',
11699 );
11700
11701 // must be a string as it may contain a dash
11702 $pidorder = VikRequest::getString('idorder', '', 'request');
11703 $pidorder = intval(str_replace('-', '', $pidorder));
11704
11705 $poldidroom = VikRequest::getInt('oldidroom', '', 'request');
11706 $pidroom = VikRequest::getInt('idroom', 0, 'request');
11707 $pfromdate = VikRequest::getString('fromdate', '', 'request');
11708 $ptodate = VikRequest::getString('todate', '', 'request');
11709 $pdebug = VikRequest::getInt('e4j_debug', 0, 'request');
11710 if ($pdebug == 1) {
11711 echo 'e4j.error.'.print_r($app->input->post->getArray(), true);
11712 exit;
11713 }
11714
11715 $nowdf = VikBooking::getDateFormat(true);
11716 if ($nowdf == "%d/%m/%Y") {
11717 $df = 'd/m/Y';
11718 } elseif ($nowdf == "%m/%d/%Y") {
11719 $df = 'm/d/Y';
11720 } else {
11721 $df = 'Y/m/d';
11722 }
11723 $pcheckinh = 0;
11724 $pcheckinm = 0;
11725 $pcheckouth = 0;
11726 $pcheckoutm = 0;
11727 $timeopst = VikBooking::getTimeOpenStore();
11728 if (is_array($timeopst)) {
11729 $opent = VikBooking::getHoursMinutes($timeopst[0]);
11730 $closet = VikBooking::getHoursMinutes($timeopst[1]);
11731 $pcheckinh = $opent[0];
11732 $pcheckinm = $opent[1];
11733 $pcheckouth = $closet[0];
11734 $pcheckoutm = $closet[1];
11735 }
11736 $info_tsto = getdate(strtotime($ptodate));
11737 $actualtsto = mktime(0, 0, 0, $info_tsto['mon'], ($info_tsto['mday'] + 1), $info_tsto['year']);
11738 $first = VikBooking::getDateTimestamp(date($df, strtotime($pfromdate)), $pcheckinh, $pcheckinm);
11739 $second = VikBooking::getDateTimestamp(date($df, $actualtsto), $pcheckouth, $pcheckoutm);
11740 $ptodate = date('Y-m-d', $second);
11741 if (!($second > $first)) {
11742 echo 'e4j.error.1 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11743 exit;
11744 }
11745 if (!($pidorder > 0) || !($pidroom > 0) || empty($pfromdate) || empty($ptodate)) {
11746 echo 'e4j.error.2 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11747 exit;
11748 }
11749
11750 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder . " AND `status`='confirmed'";
11751 $dbo->setQuery($q, 0, 1);
11752 $dbo->execute();
11753 if (!$dbo->getNumRows()) {
11754 echo 'e4j.error.3 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11755 exit;
11756 }
11757 $ord = $dbo->loadAssoc();
11758
11759 $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;";
11760 $dbo->setQuery($q);
11761 $dbo->execute();
11762 $ordersrooms = $dbo->loadAssocList();
11763
11764 // store for VCM the current rooms before the modification
11765 $ord['rooms_info'] = $ordersrooms;
11766
11767 // package or custom rate
11768 $is_package = !empty($ord['pkg']) ? true : false;
11769 $is_cust_cost = false;
11770 foreach ($ordersrooms as $kor => $or) {
11771 if ($is_package !== true && !empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
11772 $is_cust_cost = true;
11773 break;
11774 }
11775 }
11776
11777 // availability helper
11778 $av_helper = VikBooking::getAvailabilityInstance();
11779
11780 // room stay dates in case of split stay
11781 $room_stay_dates = [];
11782 if ($ord['split_stay']) {
11783 // no need to get the transient based on booking status, as the booking must be confirmed
11784 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
11785 // immediately count the number of nights of stay for each split room
11786 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
11787 $room_stay_dates[$sps_r_k]['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
11788 }
11789 }
11790
11791 // determine if dates have changed
11792 $dates_changed = false;
11793 if (date('Y-m-d', $ord['checkin']) != $pfromdate || date('Y-m-d', $ord['checkout']) != $ptodate) {
11794 $dates_changed = true;
11795 }
11796
11797 $toswitch = array();
11798 $idbooked = array();
11799 $rooms_units = array();
11800 $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms`;";
11801 $dbo->setQuery($q);
11802 $dbo->execute();
11803 $all_rooms = $dbo->loadAssocList();
11804 foreach ($all_rooms as $rr) {
11805 $rooms_units[$rr['id']]['name'] = $rr['name'];
11806 $rooms_units[$rr['id']]['units'] = $rr['units'];
11807 }
11808
11809 // switch room
11810 if ($poldidroom != $pidroom) {
11811 foreach ($ordersrooms as $ind => $or) {
11812 if ($poldidroom == $or['idroom'] && array_key_exists($pidroom, $rooms_units)) {
11813 if (!isset($idbooked[$or['idroom']])) {
11814 $idbooked[$or['idroom']] = 0;
11815 }
11816 // $idbooked is not really needed as switch is never made for the same room id
11817 $idbooked[$or['idroom']]++;
11818 //
11819 $orkey = count($toswitch);
11820 $toswitch[$orkey]['from'] = $or['idroom'];
11821 $toswitch[$orkey]['to'] = $pidroom;
11822 $toswitch[$orkey]['record'] = $or;
11823 $toswitch[$orkey]['record_ind'] = $ind;
11824 break;
11825 }
11826 }
11827 }
11828 if (count($toswitch)) {
11829 foreach ($toswitch as $ksw => $rsw) {
11830 $plusunit = array_key_exists($rsw['to'], $idbooked) ? $idbooked[$rsw['to']] : 0;
11831 $room_checkin = $ord['checkin'];
11832 $room_checkout = $ord['checkout'];
11833 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
11834 $room_checkin = $room_stay_dates[$rsw['record_ind']]['checkin'];
11835 $room_checkout = $room_stay_dates[$rsw['record_ind']]['checkout'];
11836 }
11837 if (!VikBooking::roomBookable($rsw['to'], ($rooms_units[$rsw['to']]['units'] + $plusunit), $room_checkin, $room_checkout)) {
11838 // the room is not available
11839 unset($toswitch[$ksw]);
11840 echo 'e4j.error.'.JText::sprintf('VBSWITCHRERR', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']);
11841 exit;
11842 }
11843 }
11844 if (count($toswitch)) {
11845 //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)
11846 reset($ordersrooms);
11847 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$ordersrooms[0]['id'].";";
11848 $dbo->setQuery($q);
11849 $dbo->execute();
11850 //
11851 foreach ($toswitch as $ksw => $rsw) {
11852 // update room reservation record
11853 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idroom`=" . $rsw['to'] . ",`idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=" . $rsw['record']['id'] . ";";
11854 $dbo->setQuery($q);
11855 $dbo->execute();
11856 $response['message'] .= JText::sprintf('VBOVWALTBKSWITCHROK', $rsw['record']['name'], $rooms_units[$rsw['to']]['name'])."\n";
11857
11858 // update Notes field for this booking to keep track of the previous room that was assigned
11859 $prev_room_name = array_key_exists($rsw['from'], $rooms_units) ? $rooms_units[$rsw['from']]['name'] : '';
11860 if (!empty($prev_room_name)) {
11861 $new_notes = JText::sprintf('VBOPREVROOMMOVED', $prev_room_name, date($df.' H:i:s'))."\n".$ord['adminnotes'];
11862 $q = "UPDATE `#__vikbooking_orders` SET `adminnotes`=".$dbo->quote($new_notes)." WHERE `id`=".(int)$ord['id'].";";
11863 $dbo->setQuery($q);
11864 $dbo->execute();
11865 }
11866
11867 if ($ord['status'] == 'confirmed') {
11868 // update room record in _busy
11869 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'])) {
11870 // in case of a split stay it is fundamental to update the exact busy record ID
11871 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=" . $rsw['to'] . " WHERE `id`=" . (int)$room_stay_dates[$rsw['record_ind']]['id'];
11872 $dbo->setQuery($q);
11873 $dbo->execute();
11874 } else {
11875 // regular processing of a room ID for a reservation, no matter which one, we switch it
11876 $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;";
11877 $dbo->setQuery($q);
11878 $dbo->execute();
11879 if ($dbo->getNumRows() == 1) {
11880 $cur_busy = $dbo->loadAssocList();
11881 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=".$rsw['to']." WHERE `id`=".$cur_busy[0]['id']." AND `idroom`=".$cur_busy[0]['idroom']." LIMIT 1;";
11882 $dbo->setQuery($q);
11883 $dbo->execute();
11884 }
11885 }
11886
11887 // if automated updates enabled, keep $response['vcm'] empty
11888 // Invoke Channel Manager
11889 if (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
11890 $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>';
11891 }
11892 } elseif ($ord['status'] == 'standby') {
11893 // remove record in _tmplock
11894 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($ord['id']) . ";";
11895 $dbo->setQuery($q);
11896 $dbo->execute();
11897 }
11898 }
11899
11900 // check if sub-units should be assigned again when switching room
11901 if (!$dates_changed && !$ord['split_stay'] && VikBooking::autoRoomUnit()) {
11902 $new_order_rooms = VikBooking::loadOrdersRoomsData($ord['id']);
11903 $room_indexes_usemap = [];
11904 foreach ($new_order_rooms as $kor => $or) {
11905 $num = $kor + 1;
11906 // assign room specific unit
11907 $room_indexes = VikBooking::getRoomUnitNumsAvailable($ord, $or['idroom']);
11908 $use_ind_key = 0;
11909 if ($room_indexes) {
11910 if (!array_key_exists($or['idroom'], $room_indexes_usemap)) {
11911 $room_indexes_usemap[$or['idroom']] = $use_ind_key;
11912 } else {
11913 $use_ind_key = $room_indexes_usemap[$or['idroom']];
11914 }
11915 $q = "UPDATE `#__vikbooking_ordersrooms` SET `roomindex`=".(int)$room_indexes[$use_ind_key]." WHERE `id`=".(int)$or['id'].";";
11916 $dbo->setQuery($q);
11917 $dbo->execute();
11918 $room_indexes_usemap[$or['idroom']]++;
11919 }
11920 }
11921 }
11922
11923 // do not terminate the process when there is a switch, proceed to check the dates.
11924 }
11925 }
11926
11927 // change dates
11928 if ($dates_changed) {
11929 if ($ord['split_stay']) {
11930 // we do not allow to drag and change dates for rooms in a split stay reservation
11931 echo 'e4j.error.' . JText::sprintf('VBO_BOOK_SPLIT_STAY_CANNOTDRAG', $ord['id']);
11932 exit;
11933 }
11934
11935 // total nights of stay
11936 $daysdiff = $ord['days'];
11937
11938 // re-read ordersrooms (as rooms may have been switched)
11939 $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;";
11940 $dbo->setQuery($q);
11941 $dbo->execute();
11942 $ordersrooms = $dbo->loadAssocList();
11943
11944 $groupdays = VikBooking::getGroupDays($first, $second, $daysdiff);
11945 $opertwounits = true;
11946 $units_counter = array();
11947 foreach ($ordersrooms as $ind => $or) {
11948 if (!isset($units_counter[$or['idroom']])) {
11949 $units_counter[$or['idroom']] = -1;
11950 }
11951 $units_counter[$or['idroom']]++;
11952 }
11953
11954 foreach ($ordersrooms as $ind => $or) {
11955 $num = $ind + 1;
11956 $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'] . ";";
11957 $dbo->setQuery($check);
11958 $dbo->execute();
11959 if ($dbo->getNumRows() > 0) {
11960 $busy = $dbo->loadAssocList();
11961 foreach ($groupdays as $gday) {
11962 $bfound = 0;
11963 foreach ($busy as $bu) {
11964 if ($gday >= $bu['checkin'] && $gday <= $bu['realback']) {
11965 $bfound++;
11966 }
11967 }
11968 if ($bfound >= ($or['units'] - $units_counter[$or['idroom']]) || !VikBooking::roomNotLocked($or['idroom'], $or['units'], $first, $second)) {
11969 $opertwounits = false;
11970 break 2;
11971 }
11972 }
11973 }
11974 }
11975 if ($opertwounits !== true) {
11976 $response['esit'] = 0;
11977 $response['message'] = JText::translate('VBROOMNOTRIT')." ".date($df.' H:i', $first)." ".JText::translate('VBROOMNOTCONSTO')." ".date($df.' H:i', $second);
11978 echo json_encode($response);
11979 exit;
11980 }
11981
11982 // update dates and busy records
11983 $realback = VikBooking::getHoursRoomAvail() * 3600;
11984 $realback += $second;
11985 $q = "UPDATE `#__vikbooking_orders` SET `checkin`='".$first."', `checkout`='".$second."' WHERE `id`=".$ord['id'].";";
11986 $dbo->setQuery($q);
11987 $dbo->execute();
11988 if ($ord['status'] == 'confirmed') {
11989 $q = "SELECT `b`.`id` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`id`=`ob`.`idbusy` AND `ob`.`idorder`=".$ord['id'].";";
11990 $dbo->setQuery($q);
11991 $dbo->execute();
11992 $allbusy = $dbo->loadAssocList();
11993 foreach ($allbusy as $bb) {
11994 $q = "UPDATE `#__vikbooking_busy` SET `checkin`='".$first."', `checkout`='".$second."', `realback`='".$realback."' WHERE `id`='".$bb['id']."';";
11995 $dbo->setQuery($q);
11996 $dbo->execute();
11997 }
11998 // if automated updates enabled, keep $response['vcm'] empty
11999 // Invoke Channel Manager
12000 if (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
12001 $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>';
12002 }
12003 }
12004 $response['message'] .= JText::translate('RESUPDATED')."\n";
12005 }
12006
12007 if (count($toswitch)) {
12008 /**
12009 * Rooms have changed so the new rates must be re-calculated.
12010 * Maybe they should be calculated in any case, even if just
12011 * the dates have changed. For the moment the rates are reset.
12012 */
12013 }
12014
12015 // unset any previously booked room due to calendar sharing
12016 VikBooking::cleanSharedCalendarsBusy($ord['id']);
12017 // check if some of the rooms booked have shared calendars
12018 VikBooking::updateSharedCalendars($ord['id']);
12019 //
12020
12021 //Booking History
12022 VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($ord));
12023 //
12024
12025 $vcm_autosync = VikBooking::vcmAutoUpdate();
12026 if ($vcm_autosync > 0 && !empty($response['vcm'])) {
12027 //unset the vcm property as no buttons should be displayed when in auto-sync
12028 $response['vcm'] = '';
12029 $vcm_obj = VikBooking::getVcmInvoker();
12030 $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
12031 $sync_result = $vcm_obj->doSync();
12032 if ($sync_result === false) {
12033 $response['message'] .= JText::translate('VBCHANNELMANAGERRESULTKO')." (".$vcm_obj->getError().")\n";
12034 }
12035 }
12036
12037 // in case of error but not empty VCM message, set an error that will be displayed after the mustReload
12038 if ($response['esit'] < 1 && !empty($response['vcm'])) {
12039 VikError::raiseNotice('', $response['vcm']);
12040 }
12041
12042 $response['message'] = nl2br($response['message']);
12043 echo json_encode($response);
12044 exit;
12045 }
12046
12047 public function modroomrateplans()
12048 {
12049 $dbo = JFactory::getDbo();
12050 $session = JFactory::getSession();
12051
12052 $updforvcm = $session->get('vbVcmRatesUpd', '');
12053 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
12054
12055 $pid_room = VikRequest::getInt('id_room', '', 'request');
12056 $pid_price = VikRequest::getInt('id_price', '', 'request');
12057 $ptype = VikRequest::getString('type', '', 'request');
12058 $pfromdate = VikRequest::getString('fromdate', '', 'request');
12059 $ptodate = VikRequest::getString('todate', '', 'request');
12060
12061 if (empty($pid_room) || empty($pid_price) || empty($ptype) || empty($pfromdate) || empty($ptodate) || !(strtotime($pfromdate) > 0) || !(strtotime($ptodate) > 0)) {
12062 echo 'e4j.error.'.addslashes(JText::translate('VBRATESOVWERRMODRPLANS'));
12063 exit;
12064 }
12065
12066 $q = "SELECT * FROM `#__vikbooking_prices` WHERE `id`=".$pid_price.";";
12067 $dbo->setQuery($q);
12068 $price_record = $dbo->loadAssoc();
12069
12070 if (!$price_record) {
12071 echo 'e4j.error.'.addslashes(JText::translate('VBRATESOVWERRMODRPLANS')).'.';
12072 exit;
12073 }
12074
12075 $current_closed = array();
12076 if (!empty($price_record['closingd'])) {
12077 $current_closed = json_decode($price_record['closingd'], true);
12078 }
12079 $current_closed = !is_array($current_closed) ? array() : $current_closed;
12080
12081 $start_ts = strtotime($pfromdate);
12082 $end_ts = strtotime($ptodate);
12083 $infostart = getdate($start_ts);
12084 $all_days = array();
12085 $output = array();
12086 while ($infostart[0] > 0 && $infostart[0] <= $end_ts) {
12087 $all_days[] = date('Y-m-d', $infostart[0]);
12088 $indkey = $infostart['mday'].'-'.$infostart['mon'].'-'.$infostart['year'].'-'.$pid_price;
12089 $output[$indkey] = array();
12090 $infostart = getdate(mktime(0, 0, 0, $infostart['mon'], ($infostart['mday'] + 1), $infostart['year']));
12091 }
12092
12093 if ($ptype == 'close') {
12094 // close
12095 if (!array_key_exists($pid_room, $current_closed)) {
12096 $current_closed[$pid_room] = array();
12097 }
12098 foreach ($all_days as $daymod) {
12099 if (!in_array($daymod, $current_closed[$pid_room])) {
12100 $current_closed[$pid_room][] = $daymod;
12101 }
12102 }
12103 } else {
12104 // open
12105 if (array_key_exists($pid_room, $current_closed)) {
12106 foreach ($all_days as $daymod) {
12107 if (in_array($daymod, $current_closed[$pid_room])) {
12108 foreach ($current_closed[$pid_room] as $ck => $cv) {
12109 if ($daymod == $cv) {
12110 unset($current_closed[$pid_room][$ck]);
12111 }
12112 }
12113 }
12114 }
12115 } else {
12116 $current_closed[$pid_room] = array();
12117 }
12118 }
12119
12120 if (!$current_closed[$pid_room]) {
12121 unset($current_closed[$pid_room]);
12122 }
12123
12124 $q = "UPDATE `#__vikbooking_prices` SET `closingd`=".(count($current_closed) > 0 ? $dbo->quote(json_encode($current_closed)) : "NULL")." WHERE `id`=".(int)$pid_price.";";
12125 $dbo->setQuery($q);
12126 $dbo->execute();
12127
12128 $oldcsscls = $ptype == 'close' ? 'vbo-roverw-rplan-on' : 'vbo-roverw-rplan-off';
12129 $newcsscls = $ptype == 'close' ? 'vbo-roverw-rplan-off' : 'vbo-roverw-rplan-on';
12130
12131 foreach ($output as $ok => $ov) {
12132 $output[$ok] = array('oldcls' => $oldcsscls, 'newcls' => $newcsscls);
12133 }
12134
12135 // build new session values
12136 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
12137
12138 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
12139 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $start_ts ? $start_ts : $updforvcm['dfrom'];
12140 } else {
12141 $updforvcm['dfrom'] = $start_ts;
12142 }
12143
12144 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
12145 $updforvcm['dto'] = $updforvcm['dto'] < $end_ts ? $end_ts : $updforvcm['dto'];
12146 } else {
12147 $updforvcm['dto'] = $end_ts;
12148 }
12149
12150 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
12151 if (!in_array($pid_room, $updforvcm['rooms'])) {
12152 $updforvcm['rooms'][] = $pid_room;
12153 }
12154 } else {
12155 $updforvcm['rooms'] = array($pid_room);
12156 }
12157
12158 if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
12159 if (array_key_exists($pid_room, $updforvcm['rplans'])) {
12160 if (!in_array($pid_price, $updforvcm['rplans'][$pid_room])) {
12161 $updforvcm['rplans'][$pid_room][] = $pid_price;
12162 }
12163 } else {
12164 $updforvcm['rplans'][$pid_room] = array($pid_price);
12165 }
12166 } else {
12167 $updforvcm['rplans'] = array($pid_room => array($pid_price));
12168 }
12169
12170 /**
12171 * Rather than suggesting the administrator to manually invoke VCM to launch a Bulk Action,
12172 * we try to silently trigger an automatic bulk action before updating the session values.
12173 *
12174 * @since 1.17.1 (J) - 1.7.1 (WP)
12175 * @since 1.17.5 (J) - 1.7.5 (WP) the "rate_id" property is passed along the auto-bulk data.
12176 */
12177 $rates_aligned = false;
12178 try {
12179 if (class_exists('VikChannelManager')) {
12180 $rates_aligned = VikChannelManager::autoBulkActions([
12181 'from_date' => $pfromdate,
12182 'to_date' => $ptodate,
12183 'forced_rooms' => [$pid_room],
12184 'rate_id' => $pid_price,
12185 'update' => 'rates',
12186 ]);
12187 }
12188 } catch (Throwable $e) {
12189 // do nothing
12190 $rates_aligned = false;
12191 }
12192
12193 if (!$rates_aligned) {
12194 // update session values
12195 $session->set('vbVcmRatesUpd', $updforvcm);
12196 }
12197
12198 echo json_encode($output);
12199 exit;
12200 }
12201
12202 public function icsexportlaunch() {
12203 $dbo = JFactory::getDBO();
12204 $pcheckindate = VikRequest::getString('checkindate', '', 'request');
12205 $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
12206 $pstatus = VikRequest::getString('status', '', 'request');
12207 $validstatus = array('confirmed', 'standby', 'cancelled');
12208 $filterstatus = '';
12209 $filterfirst = 0;
12210 $filtersecond = 0;
12211 $nowdf = VikBooking::getDateFormat(true);
12212 if ($nowdf == "%d/%m/%Y") {
12213 $df = 'd/m/Y';
12214 } elseif ($nowdf == "%m/%d/%Y") {
12215 $df = 'm/d/Y';
12216 } else {
12217 $df = 'Y/m/d';
12218 }
12219 $currencyname = VikBooking::getCurrencyName();
12220 if (!empty($pstatus) && in_array($pstatus, $validstatus)) {
12221 $filterstatus = $pstatus;
12222 }
12223 if (!empty($pcheckindate)) {
12224 if (VikBooking::dateIsValid($pcheckindate)) {
12225 $first=VikBooking::getDateTimestamp($pcheckindate, '0', '0');
12226 $filterfirst = $first;
12227 }
12228 }
12229 if (!empty($pcheckoutdate)) {
12230 if (VikBooking::dateIsValid($pcheckoutdate)) {
12231 $second=VikBooking::getDateTimestamp($pcheckoutdate, '23', '59');
12232 if ($second > $first) {
12233 $filtersecond = $second;
12234 }
12235 }
12236 }
12237 $clause = array();
12238 if ($filterfirst > 0) {
12239 $clause[] = "`o`.`checkin` >= ".$filterfirst;
12240 }
12241 if ($filtersecond > 0) {
12242 $clause[] = "`o`.`checkout` <= ".$filtersecond;
12243 }
12244 if (!empty($filterstatus)) {
12245 $clause[] = "`o`.`status` = '".$filterstatus."'";
12246 }
12247 $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;";
12248 $dbo->setQuery($q);
12249 $dbo->execute();
12250 if ($dbo->getNumRows() > 0) {
12251 $orders = $dbo->loadAssocList();
12252 $icscontent = "BEGIN:VCALENDAR\n";
12253 $icscontent .= "VERSION:2.0\n";
12254 $icscontent .= "PRODID:-//e4j//VikBooking//EN\n";
12255 $icscontent .= "CALSCALE:GREGORIAN\n";
12256 $str = "";
12257 foreach ($orders as $kord => $ord) {
12258 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12259 continue;
12260 }
12261 $usecurrencyname = $currencyname;
12262 $usecurrencyname = !empty($ord['idorderota']) && !empty($ord['chcurrency']) ? $ord['chcurrency'] : $usecurrencyname;
12263 $statusstr = '';
12264 if ($ord['status'] == 'confirmed') {
12265 $statusstr = JText::translate('VBCSVSTATUSCONFIRMED');
12266 } elseif ($ord['status'] == 'standby') {
12267 $statusstr = JText::translate('VBCSVSTATUSSTANDBY');
12268 } elseif ($ord['status'] == 'cancelled') {
12269 $statusstr = JText::translate('VBCSVSTATUSCANCELLED');
12270 }
12271 $uri = JURI::root().'index.php?option=com_vikbooking&view=booking&sid='.$ord['sid'].'&ts='.$ord['ts'];
12272 /**
12273 * @wponly Rewrite URI for front-end
12274 */
12275 $uri = str_replace(JUri::root(), '', $uri);
12276 $model = JModel::getInstance('vikbooking', 'shortcodes');
12277 $itemid = $model->best('booking');
12278 if ($itemid) {
12279 $uri = JRoute::rewrite($uri . "&Itemid={$itemid}", false);
12280 }
12281 //
12282 $ordnumbstr = $ord['id'].(!empty($ord['confirmnumber']) ? ' - '.$ord['confirmnumber'] : '').(!empty($ord['idorderota']) ? ' ('.ucwords($ord['channel']).')' : '').' - '.$statusstr;
12283 $peoplestr = ($ord['adults'] + $ord['children']).($ord['children'] > 0 ? ' ('.JText::translate('VBCSVCHILDREN').': '.$ord['children'].')' : '');
12284 $totalstring = ($ord['total'] > 0 ? ($usecurrencyname.' '.VikBooking::numberFormat($ord['total'])) : '');
12285 $totalpaidstring = ($ord['totpaid'] > 0 ? (' ('.VikBooking::numberFormat($ord['totpaid']).')') : '');
12286 $description = JText::sprintf('VBICSEXPDESCRIPTION', $ordnumbstr."\\n", $peoplestr."\\n", $ord['days']."\\n", $totalstring.$totalpaidstring."\\n", "\\n".str_replace("\n", "\\n", trim($ord['custdata'])));
12287 $str .= "BEGIN:VEVENT\n";
12288 $str .= "DTEND:" . JFactory::getDate(date('Y-m-d H:i:s', $ord['checkout']), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12289 $str .= "UID:" . uniqid() . "\n";
12290 $str .= "DTSTAMP:" . JFactory::getDate(date('Y-m-d H:i:s'), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12291 $str .= ((strlen($description) > 0 ) ? "DESCRIPTION:".preg_replace('/([\,;])/','\\\$1', $description)."\n" : "");
12292 $str .= "URL;VALUE=URI:" . preg_replace('/([\,;])/','\\\$1', $uri) . "\n";
12293 $str .= "SUMMARY:" . JText::sprintf('VBICSEXPSUMMARY', date($df, $ord['checkin'])) . "\n";
12294 $str .= "DTSTART:" . JFactory::getDate(date('Y-m-d H:i:s', $ord['checkin']), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12295 $str .= "END:VEVENT\n";
12296 }
12297 $icscontent .= $str;
12298 $icscontent .= "END:VCALENDAR\n";
12299 //download file from buffer
12300 header("Content-Type: application/octet-stream; ");
12301 header("Cache-Control: no-store, no-cache");
12302 header('Content-Disposition: attachment; filename="bookings_export.ics"');
12303 $f = fopen('php://output', "w");
12304 fwrite($f, $icscontent);
12305 fclose($f);
12306 exit;
12307 } else {
12308 VikError::raiseWarning('', JText::translate('VBICSEXPNORECORDS'));
12309 $mainframe = JFactory::getApplication();
12310 $mainframe->redirect("index.php?option=com_vikbooking&task=icsexportprepare&checkindate=".$pcheckindate."&checkoutdate=".$pcheckoutdate."&status=".$pstatus."&tmpl=component");
12311 }
12312 }
12313
12314 public function csvexportlaunch()
12315 {
12316 $dbo = JFactory::getDbo();
12317 $app = JFactory::getApplication();
12318
12319 $pdatefilt = VikRequest::getString('datefilt', '', 'request');
12320 $proomfilt = VikRequest::getString('roomfilt', '', 'request');
12321 $pchfilt = VikRequest::getString('chfilt', '', 'request');
12322 $ppayfilt = VikRequest::getString('payfilt', '', 'request');
12323 $pcheckindate = VikRequest::getString('checkindate', '', 'request');
12324 $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
12325 $pstatus = VikRequest::getString('status', '', 'request');
12326 $pcatfilt = VikRequest::getInt('catfilt', 0, 'request');
12327 $pformat = VikRequest::getString('format', 'csv', 'request');
12328
12329 // let the report class (a generic one) generate the CSV file in the proper format
12330 $report_obj = VikBooking::getReportInstance('revenue')->setExportCSVFormat($pformat);
12331
12332 $validstatus = array('confirmed', 'standby', 'cancelled');
12333 $validdates = array('ts', 'checkin', 'checkout');
12334
12335 $filterdate = '';
12336 $filterstatus = '';
12337 $first = 0;
12338 $filterfirst = 0;
12339 $filtersecond = 0;
12340 $nowdf = VikBooking::getDateFormat(true);
12341 if ($nowdf == "%d/%m/%Y") {
12342 $df = 'd/m/Y';
12343 } elseif ($nowdf == "%m/%d/%Y") {
12344 $df = 'm/d/Y';
12345 } else {
12346 $df = 'Y/m/d';
12347 }
12348 $datesep = VikBooking::getDateSeparator(true);
12349 $currencyname = VikBooking::getCurrencyName();
12350
12351 if (!empty($pstatus) && in_array($pstatus, $validstatus)) {
12352 $filterstatus = $pstatus;
12353 }
12354 if (!empty($pdatefilt) && in_array($pdatefilt, $validdates)) {
12355 $filterdate = $pdatefilt;
12356 }
12357 if (!empty($pcheckindate) && !empty($filterdate)) {
12358 if (VikBooking::dateIsValid($pcheckindate)) {
12359 $first = VikBooking::getDateTimestamp($pcheckindate, '0', '0');
12360 $filterfirst = $first;
12361 }
12362 }
12363 if (!empty($pcheckoutdate) && !empty($filterdate)) {
12364 if (VikBooking::dateIsValid($pcheckoutdate)) {
12365 $second = VikBooking::getDateTimestamp($pcheckoutdate, '23', '59');
12366 if ($second > $first) {
12367 $filtersecond = $second;
12368 }
12369 }
12370 }
12371 $clause = array();
12372 if ($filterfirst > 0) {
12373 $clause[] = "`o`.`".$filterdate."` >= ".$filterfirst;
12374 }
12375 if ($filtersecond > 0) {
12376 $clause[] = "`o`.`".$filterdate."` <= ".$filtersecond;
12377 }
12378 if (!empty($filterstatus)) {
12379 $clause[] = "`o`.`status` = '".$filterstatus."'";
12380 }
12381 if (!empty($pchfilt)) {
12382 $clause[] = "`o`.`channel` LIKE ".$dbo->quote("%".$pchfilt."%");
12383 }
12384 if (!empty($ppayfilt)) {
12385 $clause[] = "`o`.`idpayment` LIKE '".$ppayfilt."=%'";
12386 }
12387 if (!empty($proomfilt)) {
12388 $clause[] = "`or`.`idroom` = '".(int)$proomfilt."'";
12389 }
12390
12391 if (!empty($pcatfilt)) {
12392 $room_cat_ids = array();
12393 $q = "SELECT `id`,`idcat` FROM `#__vikbooking_rooms` WHERE `idcat` LIKE " . $dbo->quote("%$pcatfilt%");
12394 $dbo->setQuery($q);
12395 $dbo->execute();
12396 if ($dbo->getNumRows()) {
12397 $records = $dbo->loadAssocList();
12398 foreach ($records as $rcat) {
12399 $parts = explode(';', $rcat['idcat']);
12400 if (in_array($pcatfilt, $parts)) {
12401 $room_cat_ids[] = $rcat['id'];
12402 }
12403 }
12404 }
12405 if (count($room_cat_ids)) {
12406 $clause[] = "`or`.`idroom` IN (" . implode(', ', $room_cat_ids) . ")";
12407 }
12408 }
12409
12410 $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;";
12411 $dbo->setQuery($q);
12412 $orders = $dbo->loadAssocList();
12413 if (!$orders) {
12414 $app->enqueueMessage(JText::translate('VBCSVEXPNORECORDS'), 'error');
12415 $app->redirect("index.php?option=com_vikbooking&task=csvexportprepare&checkindate=".$pcheckindate."&checkoutdate=".$pcheckoutdate."&status=".$pstatus."&tmpl=component");
12416 $app->close();
12417 }
12418
12419 // options
12420 $all_options = array();
12421 $q = "SELECT * FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
12422 $dbo->setQuery($q);
12423 $options = $dbo->loadAssocList();
12424 if ($options) {
12425 foreach ($options as $ok => $ov) {
12426 $all_options[$ov['id']] = $ov;
12427 }
12428 }
12429
12430 // build columns
12431 $columns = [
12432 [
12433 'label' => JText::translate('VBDASHBOOKINGID'),
12434 ],
12435 [
12436 'label' => JText::translate('VBPVIEWORDERSONE'),
12437 ],
12438 [
12439 'label' => JText::translate('VBCSVCHECKIN'),
12440 ],
12441 [
12442 'label' => JText::translate('VBCSVCHECKOUT'),
12443 ],
12444 [
12445 'label' => JText::translate('VBCSVNIGHTS'),
12446 ],
12447 [
12448 'label' => JText::translate('VBCSVROOM'),
12449 ],
12450 [
12451 'label' => JText::translate('VBCSVPEOPLE'),
12452 ],
12453 [
12454 'label' => JText::translate('VBCSVCUSTINFO'),
12455 ],
12456 [
12457 'label' => JText::translate('ORDER_SPREQUESTS'),
12458 ],
12459 [
12460 'label' => JText::translate('ORDER_NOTES'),
12461 ],
12462 [
12463 'label' => JText::translate('VBCSVCREATEDBY'),
12464 ],
12465 [
12466 'label' => JText::translate('VBCSVCUSTMAIL'),
12467 ],
12468 [
12469 'label' => JText::translate('ORDER_PHONE'),
12470 ],
12471 [
12472 'label' => JText::translate('VBCSVOPTIONS'),
12473 ],
12474 [
12475 'label' => JText::translate('VBCSVPAYMENTMETHOD'),
12476 ],
12477 [
12478 'label' => JText::translate('VBCSVORDIDCONFNUMB'),
12479 ],
12480 [
12481 'label' => JText::translate('VBOCHANNEL'),
12482 ],
12483 [
12484 'label' => JText::translate('VBCSVEXPFILTBSTATUS'),
12485 ],
12486 [
12487 'label' => JText::translate('VBCSVTOTAL'),
12488 ],
12489 [
12490 'label' => JText::translate('VBCSVTOTPAID'),
12491 ],
12492 [
12493 'label' => JText::translate('VBCSVTOTTAXES'),
12494 ],
12495 ];
12496
12497 // booking cancellation details
12498 $cancellation_timestamps = [];
12499
12500 if (empty($filterstatus) || $filterstatus === 'cancelled') {
12501 // insert column for cancellation date at index 2
12502 array_splice($columns, 2, 0, [['label' => JText::translate('VBO_CANC_DATE')]]);
12503 // gather all cancelled bookings, if any
12504 $cancellation_ids = [];
12505 foreach ($orders as $order) {
12506 if ($order['status'] === 'cancelled' && !in_array($order['id'], $cancellation_ids)) {
12507 $cancellation_ids[] = $order['id'];
12508 }
12509 }
12510 if ($cancellation_ids && $cancHistoryEvents = VikBooking::getBookingHistoryInstance(0)->getBookingEventsType('cancelled')) {
12511 // list of booking IDs with cancellation events processed
12512 $cancBidsProcessed = [];
12513
12514 // query the database to fetch the needed history records
12515 $dbo->setQuery(
12516 $dbo->getQuery(true)
12517 ->select([
12518 $dbo->qn('idorder'),
12519 $dbo->qn('dt'),
12520 ])
12521 ->from($dbo->qn('#__vikbooking_orderhistory'))
12522 ->where($dbo->qn('idorder') . ' IN (' . implode(', ', array_map('intval', $cancellation_ids)) . ')')
12523 ->where($dbo->qn('type') . ' IN (' . implode(', ', array_map([$dbo, 'q'], $cancHistoryEvents)) . ')')
12524 ->order($dbo->qn('idorder') . ' ASC')
12525 ->order($dbo->qn('dt') . ' ASC')
12526 );
12527
12528 // scan all booking cancellation records
12529 foreach ($dbo->loadAssocList() as $cancRecord) {
12530 if (!($cancBidsProcessed[$cancRecord['idorder']] ?? 0)) {
12531 // turn flag on to process this booking only once and get the earliest (first) cancellation
12532 $cancBidsProcessed[$cancRecord['idorder']] = 1;
12533
12534 // convert the cancellation date from UTC to local timezone and set booking cancellation timestamp
12535 $cancellation_timestamps[$cancRecord['idorder']] = JHtml::fetch('date', $cancRecord['dt'], 'U');
12536 }
12537 }
12538 }
12539 }
12540
12541 // set CSV columns
12542 $report_obj->setReportCols($columns);
12543
12544 // prepare the container for the CSV rows
12545 $orderscsv = [];
12546
12547 // availability helper
12548 $av_helper = VikBooking::getAvailabilityInstance();
12549
12550 $room_inds = [];
12551 $room_stay_dates = [];
12552 foreach ($orders as $kord => $ord) {
12553 // room index in this booking
12554 if (!isset($room_inds[$ord['id']])) {
12555 $room_inds[$ord['id']] = -1;
12556 }
12557 $room_inds[$ord['id']]++;
12558
12559 /**
12560 * Split stay reservation.
12561 *
12562 * @since 1.16.0 (J) - 1.6.0 (WP)
12563 */
12564 $room_stay_dates = $room_inds[$ord['id']] > 0 ? $room_stay_dates : [];
12565 if ($ord['split_stay']) {
12566 if ($ord['status'] == 'confirmed') {
12567 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
12568 } else {
12569 $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $ord['id'], []);
12570 }
12571 // immediately count the number of nights of stay for each split room
12572 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
12573 if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
12574 // overwrite values for compatibility with non-confirmed bookings
12575 $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
12576 $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
12577 }
12578 $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
12579 // overwrite the whole array
12580 $room_stay_dates[$sps_r_k] = $sps_r_v;
12581 }
12582 }
12583
12584 // determine nights and dates for this room booking
12585 $booking_nights = $ord['days'];
12586 $booking_checkin = $ord['checkin'];
12587 $booking_checkout = $ord['checkout'];
12588 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']) {
12589 $booking_nights = $room_stay_dates[$room_inds[$ord['id']]]['nights'];
12590 $booking_checkin = $room_stay_dates[$room_inds[$ord['id']]]['checkin'];
12591 $booking_checkout = $room_stay_dates[$room_inds[$ord['id']]]['checkout'];
12592 }
12593
12594 $usecurrencyname = $currencyname;
12595 $usecurrencyname = !empty($ord['idorderota']) && !empty($ord['chcurrency']) ? $ord['chcurrency'] : $usecurrencyname;
12596 $peoplestr = ($ord['adults'] + $ord['children']).($ord['children'] > 0 ? ' ('.JText::translate('VBCSVCHILDREN').': '.$ord['children'].')' : '');
12597 $custinfostr = str_replace(",", " ", $ord['custdata']);
12598 $customer = VikBooking::getCPinIstance()->getCustomerFromBooking($ord['id']);
12599 if (count($customer)) {
12600 $custinfostr = $customer['first_name'] . ' ' . $customer['last_name'];
12601 }
12602 $special_requests = '';
12603 if (preg_match("/(?:special requests:\s*)(.*?)$/is", $ord['custdata'], $match)) {
12604 $special_requests = $match[1];
12605 } elseif (preg_match("/(?:special request:\s*)(.*?)$/is", $ord['custdata'], $match)) {
12606 $special_requests = $match[1];
12607 } elseif (preg_match("/(?:special request\s*)(.*?)$/is", $ord['custdata'], $match)) {
12608 $special_requests = $match[1];
12609 } elseif (preg_match("/(?:" . JText::translate('ORDER_SPREQUESTS') . ":\s*)(.*?)$/is", $ord['custdata'], $match)) {
12610 $special_requests = $match[1];
12611 }
12612 $paystr = '';
12613 if (!empty($ord['idpayment'])) {
12614 $payparts = explode('=', $ord['idpayment']);
12615 $paystr = $payparts[1];
12616 }
12617 $ordnumbstr = $ord['id'] . ' - ' . $ord['confirmnumber'] . (!empty($ord['idorderota']) ? ' (' . $ord['idorderota'] . ')' : '');
12618 $bookingSource = JText::translate('VBORDFROMSITE');
12619 if (!empty($ord['channel']) && !empty($ord['idorderota'])) {
12620 $chparts = explode('_', $ord['channel']);
12621 $bookingSource = ($chparts[1] ?? '') ?: $chparts[0];
12622 }
12623 $statusstr = '';
12624 if ($ord['status'] == 'confirmed') {
12625 $statusstr = JText::translate('VBCSVSTATUSCONFIRMED');
12626 } elseif ($ord['status'] == 'standby') {
12627 $statusstr = JText::translate('VBCSVSTATUSSTANDBY');
12628 } elseif ($ord['status'] == 'cancelled') {
12629 $statusstr = JText::translate('VBCSVSTATUSCANCELLED');
12630 }
12631 $totalstring = $usecurrencyname . ' ' . VikBooking::numberFormat($ord['total']);
12632 if ($ord['roomsnum'] > 1) {
12633 // take the cost for the individual room
12634 $totalstring = !empty($ord['cust_cost']) && $ord['cust_cost'] > 0 ? ($usecurrencyname . ' ' . VikBooking::numberFormat($ord['cust_cost'])) : ($usecurrencyname . ' ' . VikBooking::numberFormat($ord['room_cost']));
12635 }
12636 $totalpaidstring = $usecurrencyname . ' ' . VikBooking::numberFormat($ord['totpaid']);
12637 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12638 // total paid will be printed only for the last room booked
12639 $totalpaidstring = '';
12640 }
12641 $options_str = '';
12642 if (!empty($ord['optionals'])) {
12643 $stepo = explode(";", $ord['optionals']);
12644 foreach ($stepo as $roptkey => $oo) {
12645 if (!empty($oo)) {
12646 $stept = explode(":", $oo);
12647 if (array_key_exists($stept[0], $all_options)) {
12648 $actopt = $all_options[$stept[0]];
12649 $optpcent = false;
12650 if (!empty($actopt['ageintervals']) && $ord['children'] > 0 && strstr($stept[1], '-') != false) {
12651 $optagenames = VikBooking::getOptionIntervalsAges($actopt['ageintervals']);
12652 $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt['ageintervals']);
12653 $optageovrct = VikBooking::getOptionIntervalChildOverrides($actopt, $ord['adults'], $ord['children']);
12654 $child_num = VikBooking::getRoomOptionChildNumber($ord['optionals'], $actopt['id'], $roptkey, $ord['children']);
12655 $optagecosts = VikBooking::getOptionIntervalsCosts(isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $actopt['ageintervals']);
12656 $agestept = explode('-', $stept[1]);
12657 $stept[1] = $agestept[0];
12658 $chvar = $agestept[1];
12659 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] > 0) {
12660 $optpcent = true;
12661 }
12662 $actopt['chageintv'] = $chvar;
12663 if (isset($optagenames[($chvar - 1)])) {
12664 $actopt['name'] .= ' ('.$optagenames[($chvar - 1)].')';
12665 }
12666 if (isset($optagecosts[($chvar - 1)])) {
12667 $realcost = (intval($actopt['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $booking_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
12668 } else {
12669 $realcost = 0;
12670 }
12671 } else {
12672 // VBO 1.11 - options percentage cost of the room total fee
12673 $optpcent = (int)$actopt['pcentroom'] ? true : $optpcent;
12674 //
12675 $realcost = (intval($actopt['perday']) == 1 ? ($actopt['cost'] * $booking_nights * $stept[1]) : ($actopt['cost'] * $stept[1]));
12676 }
12677 if ($actopt['maxprice'] > 0 && $realcost > $actopt['maxprice']) {
12678 $realcost=$actopt['maxprice'];
12679 if (intval($actopt['hmany']) == 1 && intval($stept[1]) > 1) {
12680 $realcost = $actopt['maxprice'] * $stept[1];
12681 }
12682 }
12683 $realcost = $actopt['perperson'] == 1 ? ($realcost * $ord['adults']) : $realcost;
12684
12685 /**
12686 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
12687 *
12688 * @since 1.17.7 (J) - 1.7.7 (WP)
12689 */
12690 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt, $ord, $ord]);
12691 if ($custom_calculation) {
12692 $realcost = (float) $custom_calculation[0];
12693 }
12694
12695 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt['idiva']);
12696 $options_str .= ($stept[1] > 1 ? $stept[1]." " : "").$actopt['name'].": ".(!$optpcent ? $currencyname : '')." ".VikBooking::numberFormat($tmpopr).($optpcent ? ' %' : '')." \r\n";
12697 }
12698 }
12699 }
12700 }
12701
12702 // custom extra costs
12703 if (!empty($ord['extracosts'])) {
12704 $cur_extra_costs = json_decode($ord['extracosts'], true);
12705 foreach ($cur_extra_costs as $eck => $ecv) {
12706 $ecplustax = !empty($ecv['idtax']) ? VikBooking::sayOptionalsPlusIva($ecv['cost'], $ecv['idtax']) : $ecv['cost'];
12707 $options_str .= $ecv['name'].": ".$currencyname." ".VikBooking::numberFormat($ecplustax)." \r\n";
12708 }
12709 }
12710
12711 // taxes
12712 $taxes_str = '';
12713 if ($ord['tot_taxes'] > 0.00) {
12714 $taxes_str .= $usecurrencyname.' '.VikBooking::numberFormat($ord['tot_taxes']);
12715 if (!empty($ord['aliq']) && !empty($ord['breakdown'])) {
12716 $tax_breakdown = json_decode($ord['breakdown'], true);
12717 $tax_breakdown = is_array($tax_breakdown) && count($tax_breakdown) > 0 ? $tax_breakdown : array();
12718 if (count($tax_breakdown)) {
12719 foreach ($tax_breakdown as $tbkk => $tbkv) {
12720 $tax_break_cost = $ord['tot_taxes'] * floatval($tbkv['aliq']) / $ord['aliq'];
12721 $taxes_str .= "\r\n".$tbkv['name'].": ".$usecurrencyname.' '.VikBooking::numberFormat($tax_break_cost);
12722 }
12723 }
12724 }
12725 }
12726 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12727 // total taxes will be printed only for the last room booked
12728 $taxes_str = '';
12729 }
12730
12731 // created by
12732 $created_by = '';
12733 if (!empty($ord['ujid'])) {
12734 $creator = new JUser($ord['ujid']);
12735 if (property_exists($creator, 'name')) {
12736 $created_by = $creator->name.' ('.$creator->username.')';
12737 }
12738 }
12739 if (empty($created_by) && !empty($ord['t_first_name'])) {
12740 $created_by = $ord['t_first_name'].' '.$ord['t_last_name'];
12741 }
12742
12743 // build CSV line data
12744 $line_data = [
12745 [
12746 'value' => $ord['id'],
12747 ],
12748 [
12749 'value' => date(str_replace("/", $datesep, $df), $ord['ts']),
12750 ],
12751 [
12752 'value' => date(str_replace("/", $datesep, $df), $booking_checkin),
12753 ],
12754 [
12755 'value' => date(str_replace("/", $datesep, $df), $booking_checkout),
12756 ],
12757 [
12758 'value' => $booking_nights,
12759 ],
12760 [
12761 'value' => $ord['name'],
12762 ],
12763 [
12764 'value' => $peoplestr,
12765 ],
12766 [
12767 'value' => $custinfostr,
12768 ],
12769 [
12770 'value' => $special_requests,
12771 ],
12772 [
12773 'value' => $ord['adminnotes'],
12774 ],
12775 [
12776 'value' => $created_by,
12777 ],
12778 [
12779 'value' => $ord['custmail'],
12780 ],
12781 [
12782 'value' => $ord['phone'],
12783 ],
12784 [
12785 'value' => $options_str,
12786 ],
12787 [
12788 'value' => $paystr,
12789 ],
12790 [
12791 'value' => $ordnumbstr,
12792 ],
12793 [
12794 'value' => $bookingSource,
12795 ],
12796 [
12797 'value' => $statusstr,
12798 ],
12799 [
12800 'value' => $totalstring,
12801 ],
12802 [
12803 'value' => $totalpaidstring,
12804 ],
12805 [
12806 'value' => $taxes_str,
12807 ],
12808 ];
12809
12810 if (empty($filterstatus) || $filterstatus === 'cancelled') {
12811 // obtain cancellation date for this booking
12812 $booking_canc_date = $cancellation_timestamps[$ord['id']] ?? '';
12813 if ($booking_canc_date) {
12814 $booking_canc_date = date(str_replace("/", $datesep, $df), $booking_canc_date);
12815 }
12816 // insert column for cancellation date at index 2
12817 array_splice($line_data, 2, 0, [['value' => $booking_canc_date]]);
12818 }
12819
12820 // push line for export
12821 $orderscsv[] = $line_data;
12822 }
12823
12824 // set CSV rows
12825 $report_obj->setReportRows($orderscsv);
12826
12827 // build lines to export
12828 $csvlines = $report_obj->getExportCSVLines($no_data = true);
12829
12830 // set export file name
12831 $report_obj->setExportCSVFileName('bookings_export_' . date('Y-m-d') . '.csv');
12832
12833 // force the download of the CSV file
12834 $report_obj->outputHeaders();
12835
12836 // send lines to output
12837 $report_obj->outputCSV($csvlines);
12838
12839 exit;
12840 }
12841
12842 public function exportcustomerslaunch() {
12843 $cid = VikRequest::getVar('cid', array(0));
12844 $dbo = JFactory::getDBO();
12845 $pnotes = VikRequest::getInt('notes', '', 'request');
12846 $pscanimg = VikRequest::getInt('scanimg', '', 'request');
12847 $ppin = VikRequest::getInt('pin', '', 'request');
12848 $pcountry = VikRequest::getString('country', '', 'request');
12849 $pfromdate = VikRequest::getString('fromdate', '', 'request');
12850 $ptodate = VikRequest::getString('todate', '', 'request');
12851 $pdatefilt = VikRequest::getInt('datefilt', '', 'request');
12852 $clauses = array();
12853 if (count($cid) > 0 && !empty($cid[0])) {
12854 $clauses[] = "`c`.`id` IN (".implode(', ', $cid).")";
12855 }
12856 if (!empty($pcountry)) {
12857 $clauses[] = "`c`.`country`=".$dbo->quote($pcountry);
12858 }
12859 $datescol = '`bk`.`ts`';
12860 if ($pdatefilt > 0) {
12861 if ($pdatefilt == 1) {
12862 $datescol = '`bk`.`ts`';
12863 } elseif ($pdatefilt == 2) {
12864 $datescol = '`bk`.`checkin`';
12865 } elseif ($pdatefilt == 3) {
12866 $datescol = '`bk`.`checkout`';
12867 }
12868 }
12869 if (!empty($pfromdate)) {
12870 $from_ts = VikBooking::getDateTimestamp($pfromdate, 0, 0);
12871 $clauses[] = $datescol.">=".$from_ts;
12872 }
12873 if (!empty($ptodate)) {
12874 $to_ts = VikBooking::getDateTimestamp($ptodate, 23, 59);
12875 $clauses[] = $datescol."<=".$to_ts;
12876 }
12877 //this query below is safe with the error #1055 when sql_mode=only_full_group_by
12878 $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`,".
12879 "(SELECT COUNT(*) FROM `#__vikbooking_customers_orders` AS `co` WHERE `co`.`idcustomer`=`c`.`id`) AS `tot_bookings`,".
12880 "`cy`.`country_3_code`,`cy`.`country_name` ".
12881 "FROM `#__vikbooking_customers` AS `c` LEFT JOIN `#__vikbooking_countries` `cy` ON `cy`.`country_3_code`=`c`.`country` ".
12882 "LEFT JOIN `#__vikbooking_customers_orders` `co` ON `co`.`idcustomer`=`c`.`id` ".
12883 "LEFT JOIN `#__vikbooking_orders` `bk` ON `bk`.`id`=`co`.`idorder`".
12884 (count($clauses) > 0 ? " WHERE ".implode(' AND ', $clauses) : "")."
12885 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` ".
12886 "ORDER BY `c`.`last_name` ASC;";
12887 $dbo->setQuery($q);
12888 $customers = $dbo->loadAssocList();
12889 if (!$customers) {
12890 VikError::raiseWarning('', JText::translate('VBONORECORDSCSVCUSTOMERS'));
12891 $mainframe = JFactory::getApplication();
12892 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
12893 exit;
12894 }
12895 $csvlines = [];
12896 $csvheadline = [
12897 'ID',
12898 JText::translate('VBCUSTOMERLASTNAME'),
12899 JText::translate('VBCUSTOMERFIRSTNAME'),
12900 JText::translate('VBCUSTOMEREMAIL'),
12901 JText::translate('VBCUSTOMERPHONE'),
12902 JText::translate('VBCUSTOMERADDRESS'),
12903 JText::translate('VBCUSTOMERCITY'),
12904 JText::translate('VBCUSTOMERZIP'),
12905 JText::translate('VBCUSTOMERCOUNTRY'),
12906 JText::translate('VBCUSTOMERGENDER'),
12907 JText::translate('ORDER_DBIRTH'),
12908 JText::translate('VBCUSTOMERTOTBOOKINGS'),
12909 ];
12910 if ($ppin > 0) {
12911 $csvheadline[] = JText::translate('VBCUSTOMERPIN');
12912 }
12913 if ($pscanimg > 0) {
12914 $csvheadline[] = JText::translate('VBCUSTOMERDOCTYPE');
12915 $csvheadline[] = JText::translate('VBCUSTOMERDOCNUM');
12916 $csvheadline[] = JText::translate('VBCUSTOMERDOCIMG');
12917 }
12918 if ($pnotes > 0) {
12919 $csvheadline[] = JText::translate('VBCUSTOMERNOTES');
12920 }
12921 $csvlines[] = $csvheadline;
12922 foreach ($customers as $customer) {
12923 $csvcustomerline = [
12924 $customer['id'],
12925 $customer['last_name'],
12926 $customer['first_name'],
12927 $customer['email'],
12928 $customer['phone'],
12929 $customer['address'],
12930 $customer['city'],
12931 $customer['zip'],
12932 $customer['country_name'],
12933 $customer['gender'],
12934 $customer['bdate'],
12935 $customer['tot_bookings'],
12936 ];
12937 if ($ppin > 0) {
12938 $csvcustomerline[] = $customer['pin'];
12939 }
12940 if ($pscanimg > 0) {
12941 $csvcustomerline[] = $customer['doctype'];
12942 $csvcustomerline[] = $customer['docnum'];
12943 $csvcustomerline[] = (!empty($customer['docimg']) ? VBO_ADMIN_URI.'resources/idscans/'.$customer['docimg'] : '');
12944 }
12945 if ($pnotes > 0) {
12946 $csvcustomerline[] = $customer['notes'];
12947 }
12948 $csvlines[] = $csvcustomerline;
12949 }
12950 header("Content-type: text/csv");
12951 header("Cache-Control: no-store, no-cache");
12952 header('Content-Disposition: attachment; filename="customers_export_'.(!empty($pcountry) ? strtolower($pcountry).'_' : '').date('Y-m-d').'.csv"');
12953 $outstream = fopen("php://output", 'w');
12954 foreach ($csvlines as $csvline) {
12955 fputcsv($outstream, $csvline, $separator = ',', $enclosure = '"', $escape = '');
12956 }
12957 fclose($outstream);
12958 exit;
12959 }
12960
12961 public function renewsession() {
12962 /*
12963 * @wponly
12964 * We just destroy the session
12965 */
12966 JSessionHandler::destroy();
12967 $mainframe = JFactory::getApplication();
12968 $mainframe->redirect("index.php?option=com_vikbooking&task=config");
12969 }
12970
12971 public function trackings() {
12972 VikBookingHelper::printHeader("trackings");
12973
12974 VikRequest::setVar('view', VikRequest::getCmd('view', 'trackings'));
12975
12976 parent::display();
12977
12978 if (VikBooking::showFooter()) {
12979 VikBookingHelper::printFooter();
12980 }
12981 }
12982
12983 public function trkconfig() {
12984 VikBookingHelper::printHeader("trackings");
12985
12986 VikRequest::setVar('view', VikRequest::getCmd('view', 'trkconfig'));
12987
12988 parent::display();
12989
12990 if (VikBooking::showFooter()) {
12991 VikBookingHelper::printFooter();
12992 }
12993 }
12994
12995 public function savetrkconfigstay() {
12996 if (!JSession::checkToken()) {
12997 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
12998 }
12999 $this->do_savetrkconfig(true);
13000 }
13001
13002 public function savetrkconfig() {
13003 if (!JSession::checkToken()) {
13004 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13005 }
13006 $this->do_savetrkconfig();
13007 }
13008
13009 private function do_savetrkconfig($stay = false) {
13010 $dbo = JFactory::getDBO();
13011 $trkenabled = VikRequest::getInt('trkenabled', 0, 'request');
13012 $trkenabled = $trkenabled == 1 ? 1 : 0;
13013 $trkcookierfrdur = VikRequest::getFloat('trkcookierfrdur', 1, 'request');
13014 $trkcookierfrdur = $trkcookierfrdur < 0.1 ? 1 : $trkcookierfrdur;
13015 $trkcampname = VikRequest::getVar('trkcampname', array());
13016 $trkcampkey = VikRequest::getVar('trkcampkey', array());
13017 $trkcampval = VikRequest::getVar('trkcampval', array());
13018 $trkcampaigns = array();
13019 foreach ($trkcampname as $k => $v) {
13020 if (empty($trkcampkey[$k])) {
13021 continue;
13022 }
13023 $trkcampkey[$k] = str_replace(' ', '', trim($trkcampkey[$k]));
13024 $name = !empty($v) ? $v : date('Y-m-d').' '.(count($trkcampaigns) + 1);
13025 $trkcampaigns[$trkcampkey[$k]] = array(
13026 'key' => $trkcampkey[$k],
13027 'value' => $trkcampval[$k],
13028 'name' => $name,
13029 );
13030 }
13031
13032 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($trkenabled)." WHERE `param`='trkenabled';";
13033 $dbo->setQuery($q);
13034 $dbo->execute();
13035 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($trkcookierfrdur)." WHERE `param`='trkcookierfrdur';";
13036 $dbo->setQuery($q);
13037 $dbo->execute();
13038 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($trkcampaigns))." WHERE `param`='trkcampaigns';";
13039 $dbo->setQuery($q);
13040 $dbo->execute();
13041
13042 $mainframe = JFactory::getApplication();
13043 $mainframe->redirect("index.php?option=com_vikbooking&task=".($stay ? 'trkconfig' : 'trackings'));
13044 }
13045
13046 public function modtracking() {
13047 $dbo = JFactory::getDbo();
13048 $cid = VikRequest::getVar('cid', array());
13049 foreach ($cid as $id) {
13050 if (!empty($id)) {
13051 $q = "SELECT `id`,`published` FROM `#__vikbooking_trackings` WHERE `id`=".(int)$id.";";
13052 $dbo->setQuery($q);
13053 $dbo->execute();
13054 if ($dbo->getNumRows()) {
13055 $data = $dbo->loadAssoc();
13056 $q = "UPDATE `#__vikbooking_trackings` SET `published`=".($data['published'] ? '0' : '1')." WHERE `id`=".(int)$data['id'].";";
13057 $dbo->setQuery($q);
13058 $dbo->execute();
13059 }
13060 }
13061 }
13062 $mainframe = JFactory::getApplication();
13063 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13064 }
13065
13066 public function removetrackings()
13067 {
13068 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
13069 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13070 }
13071
13072 $ids = VikRequest::getVar('cid', array());
13073 $dbo = JFactory::getDbo();
13074
13075 foreach ($ids as $d) {
13076 $q = "DELETE FROM `#__vikbooking_trackings` WHERE `id`=".(int)$d.";";
13077 $dbo->setQuery($q);
13078 $dbo->execute();
13079 $q = "DELETE FROM `#__vikbooking_tracking_infos` WHERE `idtracking`=".(int)$d.";";
13080 $dbo->setQuery($q);
13081 $dbo->execute();
13082 }
13083
13084 $mainframe = JFactory::getApplication();
13085 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13086 }
13087
13088 /**
13089 * Invokes the Tracker class to obtain
13090 * geo information about the IP addresses.
13091 * This task is called via ajax.
13092 *
13093 * @since 1.11
13094 */
13095 public function getgeoinfo() {
13096 $ips = VikRequest::getVar('ips', array());
13097 if (!count($ips)) {
13098 echo 'e4j.error.empty IPs';
13099 exit;
13100 }
13101
13102 // require the Tracker class without instantiating the object
13103 VikBooking::getTracker(true);
13104 $geo_info = VikBookingTracker::getIpGeoInfo($ips);
13105
13106 if ($geo_info === false) {
13107 echo 'e4j.error.Tracker error, could not get geo info from IPs';
13108 exit;
13109 }
13110
13111 // update db values and compose response
13112 $dbo = JFactory::getDbo();
13113 $resp = array();
13114 foreach ($geo_info as $id => $geo) {
13115 if (is_null($geo) || $geo === false) {
13116 continue;
13117 }
13118 // compose geo info string
13119 $geovals = array();
13120 if (!empty($geo['city'])) {
13121 array_push($geovals, $geo['city']);
13122 }
13123 if (!empty($geo['region'])) {
13124 array_push($geovals, $geo['region']);
13125 }
13126 $threecode = '';
13127 $cname = '';
13128 if (!empty($geo['country'])) {
13129 // returned country is a 2-char code, get the 3-char country code
13130 $q = "SELECT `country_3_code`,`country_name` FROM `#__vikbooking_countries` WHERE `country_2_code`=".$dbo->quote($geo['country']).";";
13131 $dbo->setQuery($q);
13132 $dbo->execute();
13133 if ($dbo->getNumRows()) {
13134 $cinfo = $dbo->loadAssoc();
13135 $threecode = $cinfo['country_3_code'];
13136 $cname = $cinfo['country_name'];
13137 }
13138 array_push($geovals, (empty($cname) ? $geo['country'] : $cname));
13139 }
13140
13141 // full geo information string
13142 $geoinfostr = implode(', ', $geovals);
13143
13144 // push data to the response pool
13145 $resp[$id] = array();
13146 $resp[$id]['geo'] = $geoinfostr;
13147 if (!empty($cname)) {
13148 $resp[$id]['country'] = $cname;
13149 }
13150 if (!empty($threecode)) {
13151 $resp[$id]['country3'] = $threecode;
13152 }
13153
13154 // update main tracking record
13155 $q = "UPDATE `#__vikbooking_trackings` SET `geo`=".$dbo->quote($geoinfostr).(!empty($threecode) ? ', `country`='.$dbo->quote($threecode) : '')." WHERE `id`=".(int)$id.";";
13156 $dbo->setQuery($q);
13157 $dbo->execute();
13158 }
13159
13160 // output the JSON response
13161 echo json_encode($resp);
13162 exit;
13163 }
13164
13165 /**
13166 * Counts the orphan dates for all published rooms
13167 * depending on their restrictions and booked dates.
13168 * By default, the task takes up to 3 months ahead.
13169 * It is possible to filter the request by rooms and months.
13170 * This task should be called via ajax.
13171 *
13172 * @since 1.11
13173 */
13174 public function orphanscount()
13175 {
13176 $dbo = JFactory::getDbo();
13177 $orphans = array();
13178
13179 $nowdf = VikBooking::getDateFormat();
13180 if ($nowdf == "%d/%m/%Y") {
13181 $df = 'd/m/Y';
13182 } elseif ($nowdf == "%m/%d/%Y") {
13183 $df = 'm/d/Y';
13184 } else {
13185 $df = 'Y/m/d';
13186 }
13187
13188 // global min los
13189 $glob_minlos = VikBooking::getDefaultNightsCalendar();
13190 $glob_minlos = $glob_minlos < 1 ? 1 : $glob_minlos;
13191
13192 // rooms and dates
13193 $roomids = VikRequest::getVar('roomids', array(), 'request', 'int');
13194 $months = VikRequest::getInt('months', 3, 'request');
13195 $from = VikRequest::getString('from', '', 'request');
13196 $today = strtotime(date('Y').'-'.date('m').'-'.date('d'));
13197 if (!empty($from)) {
13198 $fromts = VikBooking::getDateTimestamp($from, 0, 0);
13199 if (!empty($fromts)) {
13200 // custom starting date
13201 $today = $fromts;
13202 }
13203 }
13204 $until = strtotime("+{$months} months", $today);
13205
13206 // load all rooms
13207 $rooms = array();
13208 $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms` WHERE `avail`=1".(count($roomids) ? ' AND `id` IN ('.implode(', ', $roomids).')' : '').";";
13209 $dbo->setQuery($q);
13210 $dbo->execute();
13211 if ($dbo->getNumRows()) {
13212 $allrooms = $dbo->loadAssocList();
13213 foreach ($allrooms as $r) {
13214 $rooms[$r['id']] = $r;
13215 }
13216 }
13217 if (!count($rooms)) {
13218 // no rooms found, exit
13219 echo json_encode($orphans);
13220 exit;
13221 }
13222
13223 // load availabilities
13224 $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.");";
13225 $dbo->setQuery($q);
13226 $dbo->execute();
13227 if (!$dbo->getNumRows()) {
13228 // no booked dates found, exit
13229 echo json_encode($orphans);
13230 exit;
13231 }
13232 $busy = $dbo->loadAssocList();
13233
13234 // sort booked dates by room id
13235 $rooms_busy = array();
13236 foreach ($busy as $b) {
13237 if (!isset($rooms_busy[$b['idroom']])) {
13238 $rooms_busy[$b['idroom']] = array();
13239 }
13240 array_push($rooms_busy[$b['idroom']], $b);
13241 }
13242
13243 // load restrictions
13244 $rooms_restr = array();
13245 foreach ($rooms as $rid => $r) {
13246 $restrictions = VikBooking::loadRestrictions(true, array($rid));
13247 if (count($restrictions)) {
13248 $rooms_restr[$rid] = $restrictions;
13249 }
13250 }
13251 if (!count($rooms_restr) && $glob_minlos < 2) {
13252 // no restrictions found and minlos=1, exit
13253 echo json_encode($orphans);
13254 exit;
13255 }
13256
13257 // count availability and minlos per day
13258 $rooms_data = array();
13259 foreach ($rooms as $rid => $r) {
13260 $rooms_data[$rid] = array(
13261 'avail' => array(),
13262 'restr' => array()
13263 );
13264 $nowts = getdate($today);
13265 while ($nowts[0] <= $until) {
13266 $dateind = date('Y-m-d', $nowts[0]);
13267
13268 // remaining availability
13269 if (!isset($rooms_busy[$rid])) {
13270 // no bookings for this room, set full availability for this day
13271 $rooms_data[$rid]['avail'][] = array(
13272 'dt' => $dateind,
13273 'units' => $r['units']
13274 );
13275 } else {
13276 // check remaining availability for this day
13277 $totfound = 0;
13278 foreach ($rooms_busy[$rid] as $b) {
13279 $tmpone = getdate($b['checkin']);
13280 $rit = ($tmpone['mon'] < 10 ? "0".$tmpone['mon'] : $tmpone['mon'])."/".($tmpone['mday'] < 10 ? "0".$tmpone['mday'] : $tmpone['mday'])."/".$tmpone['year'];
13281 $ritts = strtotime($rit);
13282 $tmptwo = getdate($b['checkout']);
13283 $con = ($tmptwo['mon'] < 10 ? "0".$tmptwo['mon'] : $tmptwo['mon'])."/".($tmptwo['mday'] < 10 ? "0".$tmptwo['mday'] : $tmptwo['mday'])."/".$tmptwo['year'];
13284 $conts = strtotime($con);
13285 if ($nowts[0] >= $ritts && $nowts[0] < $conts) {
13286 $totfound++;
13287 }
13288 }
13289 $totfound = $totfound > $r['units'] ? $r['units'] : $totfound;
13290 $rooms_data[$rid]['avail'][] = array(
13291 'dt' => $dateind,
13292 'units' => ($r['units'] - $totfound)
13293 );
13294 }
13295
13296 // restrictions
13297 if (!isset($rooms_restr[$rid])) {
13298 // no restrictions for this room, set global minlos for this day
13299 $rooms_data[$rid]['restr'][] = array(
13300 'dt' => $dateind,
13301 'minlos' => $glob_minlos
13302 );
13303 } else {
13304 // get restriction for this day
13305 $today_tsin = mktime(0, 0, 0, $nowts['mon'], $nowts['mday'], $nowts['year']);
13306 $today_tsout = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
13307
13308 $restr = VikBooking::parseSeasonRestrictions($today_tsin, $today_tsout, 1, $rooms_restr[$rid]);
13309 $minlos = count($restr) ? $restr['minlos'] : $glob_minlos;
13310
13311 $rooms_data[$rid]['restr'][] = array(
13312 'dt' => $dateind,
13313 'minlos' => $minlos
13314 );
13315 }
13316
13317 // next loop
13318 $dayts = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
13319 $nowts = getdate($dayts);
13320 }
13321 }
13322
13323 // week days and months labels
13324 $days_labels = array(
13325 JText::translate('VBSUNDAY'),
13326 JText::translate('VBMONDAY'),
13327 JText::translate('VBTUESDAY'),
13328 JText::translate('VBWEDNESDAY'),
13329 JText::translate('VBTHURSDAY'),
13330 JText::translate('VBFRIDAY'),
13331 JText::translate('VBSATURDAY')
13332 );
13333 $months_labels = array(
13334 JText::translate('VBMONTHONE'),
13335 JText::translate('VBMONTHTWO'),
13336 JText::translate('VBMONTHTHREE'),
13337 JText::translate('VBMONTHFOUR'),
13338 JText::translate('VBMONTHFIVE'),
13339 JText::translate('VBMONTHSIX'),
13340 JText::translate('VBMONTHSEVEN'),
13341 JText::translate('VBMONTHEIGHT'),
13342 JText::translate('VBMONTHNINE'),
13343 JText::translate('VBMONTHTEN'),
13344 JText::translate('VBMONTHELEVEN'),
13345 JText::translate('VBMONTHTWELVE')
13346 );
13347
13348 // orphan dates calculation method
13349 $calc_method = VikBooking::orphansCalculation();
13350
13351 // parse data and build orphans if any
13352 foreach ($rooms_data as $rid => $data) {
13353 foreach ($data['avail'] as $ind => $av) {
13354 if (!isset($data['restr'][$ind]) || $av['units'] < 1) {
13355 // continue, no restriction set or no availability for this day
13356 continue;
13357 }
13358 if ($data['restr'][$ind]['minlos'] < 2) {
13359 // continue, no min los > 1 set for this day
13360 continue;
13361 }
13362 // check if any night after today, until min los, is fully booked
13363 $hasorphans = false;
13364 $forward_count = 0;
13365 for ($i = 1; $i < $data['restr'][$ind]['minlos']; $i++) {
13366 if (!isset($data['avail'][($ind + $i)])) {
13367 // break loop, no info for this day after
13368 break;
13369 }
13370 if ($data['avail'][($ind + $i)]['units'] > 0) {
13371 // continue, availability found for tomorrow, we need a non available next-day
13372 continue;
13373 }
13374 // orphan found
13375 $hasorphans = true;
13376 $forward_count = $i;
13377 break;
13378 }
13379
13380 /**
13381 * Backward calculation method only if "prevnext".
13382 *
13383 * @since 1.3.0
13384 */
13385 $backward_count = 0;
13386 for ($i = 1; $i <= $data['restr'][$ind]['minlos']; $i++) {
13387 if (!isset($data['avail'][($ind - $i)])) {
13388 // break loop, no info for this prev day
13389 break;
13390 }
13391 if ($data['avail'][($ind - $i)]['units'] > 0) {
13392 // increase free nights going backward
13393 $backward_count++;
13394 }
13395 }
13396 if ($calc_method == 'prevnext' && $hasorphans && $backward_count > 0 && ($backward_count >= $data['restr'][$ind]['minlos'] || ($backward_count + $forward_count) >= $data['restr'][$ind]['minlos'])) {
13397 // this should not be an orphan date because of enough free days back, or enough free days in between
13398 $hasorphans = false;
13399 }
13400 //
13401
13402 if ($hasorphans) {
13403 // 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
13404 if (!isset($orphans[$rid])) {
13405 $orphans[$rid] = array(
13406 'name' => $rooms[$rid]['name'],
13407 'dates' => array(),
13408 'rdates' => array(),
13409 'linkd' => date($df, strtotime($av['dt']))
13410 );
13411 }
13412 array_push($orphans[$rid]['dates'], $av['dt']);
13413 // build the value for the readable date
13414 $dtinfo = getdate(strtotime($av['dt']));
13415 $rdate = $days_labels[$dtinfo['wday']] . ', ' . $months_labels[($dtinfo['mon'] - 1)] . ' ' . $dtinfo['mday'] . ' ' . $dtinfo['year'];
13416 array_push($orphans[$rid]['rdates'], $rdate);
13417 }
13418 }
13419 }
13420
13421 // output response
13422 echo json_encode($orphans);
13423 exit;
13424 }
13425
13426 public function tableaux() {
13427 VikBookingHelper::printHeader("tableaux");
13428
13429 VikRequest::setVar('view', VikRequest::getCmd('view', 'tableaux'));
13430
13431 parent::display();
13432
13433 if (VikBooking::showFooter()) {
13434 VikBookingHelper::printFooter();
13435 }
13436 }
13437
13438 public function operators() {
13439 VikBookingHelper::printHeader("operators");
13440
13441 VikRequest::setVar('view', VikRequest::getCmd('view', 'operators'));
13442
13443 parent::display();
13444
13445 if (VikBooking::showFooter()) {
13446 VikBookingHelper::printFooter();
13447 }
13448 }
13449
13450 public function newoperator() {
13451 VikBookingHelper::printHeader("operators");
13452
13453 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoperator'));
13454
13455 parent::display();
13456
13457 if (VikBooking::showFooter()) {
13458 VikBookingHelper::printFooter();
13459 }
13460 }
13461
13462 public function editoperator() {
13463 VikBookingHelper::printHeader("operators");
13464
13465 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoperator'));
13466
13467 parent::display();
13468
13469 if (VikBooking::showFooter()) {
13470 VikBookingHelper::printFooter();
13471 }
13472 }
13473
13474 public function updateoperator()
13475 {
13476 if (!JSession::checkToken()) {
13477 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13478 }
13479
13480 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
13481 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13482 }
13483
13484 $this->do_updateoperator();
13485 }
13486
13487 public function updateoperatorstay()
13488 {
13489 if (!JSession::checkToken()) {
13490 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13491 }
13492
13493 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
13494 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13495 }
13496
13497 $this->do_updateoperator(true);
13498 }
13499
13500 private function do_updateoperator($stay = false)
13501 {
13502 $dbo = JFactory::getDbo();
13503 $app = JFactory::getApplication();
13504 $pfirst_name = VikRequest::getString('first_name', '', 'request');
13505 $plast_name = VikRequest::getString('last_name', '', 'request');
13506 $pemail = VikRequest::getString('email', '', 'request');
13507 $pphone = VikRequest::getString('phone', '', 'request');
13508 $pcode = VikRequest::getString('code', '', 'request');
13509 $pujid = VikRequest::getInt('ujid', '', 'request');
13510 $pwhere = VikRequest::getInt('where', '', 'request');
13511
13512 $work_days_week = (array) $app->input->get('work_days_week', [], 'array');
13513 $work_days_exceptions = (array) $app->input->get('work_days_exceptions', [], 'array');
13514
13515 // normalize to linear arrays
13516 $work_days_week_schedule = array_combine(array_keys($work_days_week), array_values($work_days_week));
13517 $work_days_week = [];
13518 foreach ($work_days_week_schedule as $wday => $whours) {
13519 $work_days_week[] = [
13520 'wday' => $wday,
13521 'hours' => $whours,
13522 ];
13523 }
13524 foreach ($work_days_exceptions as &$wexceptions) {
13525 if (is_scalar($wexceptions)) {
13526 $wexceptions = json_decode($wexceptions, true);
13527 }
13528 }
13529 unset($wexceptions);
13530
13531 if (!empty($pfirst_name) && !empty($pemail) && !empty($pcode)) {
13532 $q = "SELECT * FROM `#__vikbooking_operators` WHERE `id`=".(int)$pwhere." LIMIT 1;";
13533 $dbo->setQuery($q);
13534 $customer = $dbo->loadAssoc();
13535 if (!$customer) {
13536 $app->redirect("index.php?option=com_vikbooking&task=operators");
13537 exit;
13538 }
13539
13540 $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;";
13541 $dbo->setQuery($q);
13542 $ex_operator = $dbo->loadAssoc();
13543 if (!$ex_operator) {
13544 // update fingerprint for the operator
13545 $fingpt = md5($pwhere . $pemail);
13546
13547 /**
13548 * Operator profile picture (URL or uploaded file).
13549 *
13550 * @since 1.16.9 (J) - 1.6.9 (WP)
13551 */
13552 $operator_pic = VikRequest::getString('pic', '', 'request');
13553 $operator_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
13554 if (is_array($operator_pic_img) && !empty($operator_pic_img['name'])) {
13555 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($operator_pic_img['name'])));
13556 $src = $operator_pic_img['tmp_name'];
13557 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
13558 $j = "";
13559 if (is_file($dest.$filename)) {
13560 $j = rand(1, 99999);
13561 while (is_file($dest . $j .$filename)) {
13562 $j++;
13563 }
13564 }
13565 $finaldest = $dest . $j . $filename;
13566 $check = getimagesize($operator_pic_img['tmp_name']);
13567 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
13568 if (VikBooking::uploadFile($src, $finaldest)) {
13569 $operator_pic = $j . $filename;
13570 } else {
13571 VikError::raiseWarning('', 'Error while uploading image');
13572 }
13573 } else {
13574 VikError::raiseWarning('', 'Uploaded file is not an Image');
13575 }
13576 }
13577
13578 // update record
13579 $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;
13580 $dbo->setQuery($q);
13581 $dbo->execute();
13582 $app->enqueueMessage(JText::translate('VBOPERATORSAVED'));
13583 } else {
13584 //email already exists
13585 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>');
13586 $app->redirect("index.php?option=com_vikbooking&task=editoperator&cid[]=".$pwhere);
13587 exit;
13588 }
13589 } else {
13590 VikError::raiseWarning('', JText::translate('VBERROPERATORDATA'));
13591 }
13592
13593 if ($stay) {
13594 $app->redirect("index.php?option=com_vikbooking&task=editoperator&cid[]=".$pwhere);
13595 } else {
13596 $app->redirect("index.php?option=com_vikbooking&task=operators");
13597 }
13598
13599 $app->close();
13600 }
13601
13602 public function saveoperator()
13603 {
13604 if (!JSession::checkToken()) {
13605 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13606 }
13607
13608 $dbo = JFactory::getDbo();
13609 $app = JFactory::getApplication();
13610 $pfirst_name = VikRequest::getString('first_name', '', 'request');
13611 $plast_name = VikRequest::getString('last_name', '', 'request');
13612 $pemail = VikRequest::getString('email', '', 'request');
13613 $pphone = VikRequest::getString('phone', '', 'request');
13614 $pcode = VikRequest::getString('code', '', 'request');
13615 $pujid = VikRequest::getInt('ujid', '', 'request');
13616
13617 $work_days_week = (array) $app->input->get('work_days_week', [], 'array');
13618 $work_days_exceptions = (array) $app->input->get('work_days_exceptions', [], 'array');
13619
13620 // normalize to linear arrays
13621 $work_days_week_schedule = array_combine(array_keys($work_days_week), array_values($work_days_week));
13622 $work_days_week = [];
13623 foreach ($work_days_week_schedule as $wday => $whours) {
13624 $work_days_week[] = [
13625 'wday' => $wday,
13626 'hours' => $whours,
13627 ];
13628 }
13629 foreach ($work_days_exceptions as &$wexceptions) {
13630 if (is_scalar($wexceptions)) {
13631 $wexceptions = json_decode($wexceptions, true);
13632 }
13633 }
13634 unset($wexceptions);
13635
13636 if (!empty($pfirst_name) && !empty($pemail) && !empty($pcode)) {
13637 $q = "SELECT * FROM `#__vikbooking_operators` WHERE `email`=".$dbo->quote($pemail)." OR ".(!empty($pcode) ? "`code`=".$dbo->quote($pcode) : "`ujid`=".$dbo->quote($pujid))." LIMIT 1;";
13638 $dbo->setQuery($q);
13639 $ex_operator = $dbo->loadAssoc();
13640 if (!$ex_operator) {
13641 /**
13642 * Operator profile picture (URL or uploaded file).
13643 *
13644 * @since 1.16.9 (J) - 1.6.9 (WP)
13645 */
13646 $operator_pic = VikRequest::getString('pic', '', 'request');
13647 $operator_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
13648 if (is_array($operator_pic_img) && !empty($operator_pic_img['name'])) {
13649 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($operator_pic_img['name'])));
13650 $src = $operator_pic_img['tmp_name'];
13651 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
13652 $j = "";
13653 if (is_file($dest.$filename)) {
13654 $j = rand(1, 99999);
13655 while (is_file($dest . $j .$filename)) {
13656 $j++;
13657 }
13658 }
13659 $finaldest = $dest . $j . $filename;
13660 $check = getimagesize($operator_pic_img['tmp_name']);
13661 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
13662 if (VikBooking::uploadFile($src, $finaldest)) {
13663 $operator_pic = $j . $filename;
13664 } else {
13665 VikError::raiseWarning('', 'Error while uploading image');
13666 }
13667 } else {
13668 VikError::raiseWarning('', 'Uploaded file is not an Image');
13669 }
13670 }
13671
13672 $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') . ");";
13673 $dbo->setQuery($q);
13674 $dbo->execute();
13675 $lid = $dbo->insertid();
13676 if (!empty($lid)) {
13677 $app->enqueueMessage(JText::translate('VBOPERATORSAVED'));
13678 // generate fingerprint for the operator
13679 $q = "UPDATE `#__vikbooking_operators` SET `fingpt`=".$dbo->q(md5($lid.$pemail))." WHERE `id`=".(int)$lid.";";
13680 $dbo->setQuery($q);
13681 $dbo->execute();
13682 }
13683 } else {
13684 // email already exists
13685 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>');
13686 }
13687 } else {
13688 VikError::raiseWarning('', JText::translate('VBERROPERATORDATA'));
13689 }
13690
13691 $app->redirect("index.php?option=com_vikbooking&task=operators");
13692 $app->close();
13693 }
13694
13695 public function removeoperators()
13696 {
13697 if (!JSession::checkToken()) {
13698 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13699 }
13700
13701 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
13702 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13703 }
13704
13705 $ids = VikRequest::getVar('cid', array(0));
13706 if ($ids) {
13707 $dbo = JFactory::getDBO();
13708 foreach ($ids as $d) {
13709 $q = "DELETE FROM `#__vikbooking_operators` WHERE `id`=".(int)$d.";";
13710 $dbo->setQuery($q);
13711 $dbo->execute();
13712 }
13713 }
13714 $mainframe = JFactory::getApplication();
13715 $mainframe->redirect("index.php?option=com_vikbooking&task=operators");
13716 }
13717
13718 public function canceloperator() {
13719 $mainframe = JFactory::getApplication();
13720 $mainframe->redirect("index.php?option=com_vikbooking&task=operators");
13721 }
13722
13723 public function cancelcrons() {
13724 $mainframe = JFactory::getApplication();
13725 $mainframe->redirect("index.php?option=com_vikbooking&task=crons");
13726 }
13727
13728 public function cancelpackages() {
13729 $mainframe = JFactory::getApplication();
13730 $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
13731 }
13732
13733 public function cancelcustomer() {
13734 $mainframe = JFactory::getApplication();
13735 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
13736 if (!empty($pgoto)) {
13737 $mainframe->redirect(base64_decode($pgoto));
13738 exit;
13739 }
13740 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
13741 }
13742
13743 public function cancelbusyvcm() {
13744 $mainframe = JFactory::getApplication();
13745 $mainframe->redirect("index.php?option=com_vikchannelmanager&task=oversight");
13746 }
13747
13748 public function cancelrestriction() {
13749 $mainframe = JFactory::getApplication();
13750 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
13751 }
13752
13753 public function cancelcoupon() {
13754 $mainframe = JFactory::getApplication();
13755 $mainframe->redirect("index.php?option=com_vikbooking&task=coupons");
13756 }
13757
13758 public function cancelcustomf() {
13759 $mainframe = JFactory::getApplication();
13760 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
13761 }
13762
13763 public function cancelpayment() {
13764 $mainframe = JFactory::getApplication();
13765 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
13766 }
13767
13768 public function cancelseason() {
13769 $mainframe = JFactory::getApplication();
13770 $mainframe->redirect("index.php?option=com_vikbooking&task=seasons");
13771 }
13772
13773 public function goconfig() {
13774 $mainframe = JFactory::getApplication();
13775 $mainframe->redirect("index.php?option=com_vikbooking&task=config");
13776 }
13777
13778 public function canceledorder() {
13779 $pgoto = VikRequest::getString('goto', 'orders', 'request');
13780 $mainframe = JFactory::getApplication();
13781 $mainframe->redirect("index.php?option=com_vikbooking&task=" . $pgoto);
13782 }
13783
13784 public function cancelbusy() {
13785 $pidorder = VikRequest::getString('idorder', '', 'request');
13786 $pgoto = VikRequest::getString('goto', '', 'request');
13787 $mainframe = JFactory::getApplication();
13788 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pidorder.($pgoto == 'overv' ? '&goto=overv' : ''));
13789 }
13790
13791 public function canceloverv() {
13792 $mainframe = JFactory::getApplication();
13793 $mainframe->redirect("index.php?option=com_vikbooking&task=overv");
13794 }
13795
13796 public function canceltableaux() {
13797 $mainframe = JFactory::getApplication();
13798 $mainframe->redirect("index.php?option=com_vikbooking&task=tableaux");
13799 }
13800
13801 public function cancelcalendar() {
13802 $pidroom = VikRequest::getString('idroom', '', 'request');
13803 $mainframe = JFactory::getApplication();
13804 $mainframe->redirect("index.php?option=com_vikbooking&task=calendar&cid[]=".$pidroom);
13805 }
13806
13807 public function canceloptionals() {
13808 $mainframe = JFactory::getApplication();
13809 $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
13810 }
13811
13812 public function cancel() {
13813 $mainframe = JFactory::getApplication();
13814 $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
13815 }
13816
13817 public function cancelcarat() {
13818 $mainframe = JFactory::getApplication();
13819 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
13820 }
13821
13822 public function cancelcat() {
13823 $mainframe = JFactory::getApplication();
13824 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
13825 }
13826
13827 public function cancelprice() {
13828 $mainframe = JFactory::getApplication();
13829 $mainframe->redirect("index.php?option=com_vikbooking&task=prices");
13830 }
13831
13832 public function canceliva() {
13833 $mainframe = JFactory::getApplication();
13834 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
13835 }
13836
13837 public function canceltrk() {
13838 $mainframe = JFactory::getApplication();
13839 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13840 }
13841
13842 public function canceldash() {
13843 $mainframe = JFactory::getApplication();
13844 $mainframe->redirect("index.php?option=com_vikbooking");
13845 }
13846
13847 public function cancelinvoice() {
13848 $mainframe = JFactory::getApplication();
13849 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
13850 if (!empty($pgoto)) {
13851 $mainframe->redirect(base64_decode($pgoto));
13852 exit;
13853 }
13854 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
13855 }
13856
13857 /**
13858 * AJAX upload the customer documents.
13859 *
13860 * @return void
13861 *
13862 * @throws Exception
13863 */
13864 public function upload_customer_document()
13865 {
13866 $input = JFactory::getApplication()->input;
13867 $dbo = JFactory::getDbo();
13868
13869 $customer_id = $input->getUint('customer', 0);
13870
13871 $result = new stdClass;
13872 $result->status = 0;
13873
13874 try
13875 {
13876 $q = $dbo->getQuery(true)
13877 ->select($dbo->qn(array(
13878 'id',
13879 'first_name',
13880 'last_name',
13881 'email',
13882 'docsfolder',
13883 )))
13884 ->from($dbo->qn('#__vikbooking_customers'))
13885 ->where($dbo->qn('id') . ' = ' . $customer_id);
13886
13887 $dbo->setQuery($q, 0, 1);
13888 $dbo->execute();
13889
13890 if (!$dbo->getNumRows())
13891 {
13892 throw new Exception(sprintf('Customer [%d] not found', $customer_id), 404);
13893 }
13894
13895 $customer = $dbo->loadObject();
13896
13897 // fetch documents folder path
13898 $dirpath = VBO_CUSTOMERS_PATH . DIRECTORY_SEPARATOR;
13899
13900 // check if we have a valid directory
13901 if (empty($customer->docsfolder) || !is_dir($dirpath . $customer->docsfolder))
13902 {
13903 // randomize string
13904 $customer->seed = uniqid();
13905
13906 // create blocks for hashed folder
13907 $parts = [
13908 $customer->first_name,
13909 $customer->last_name,
13910 md5(serialize($customer)),
13911 ];
13912
13913 // join fetched parts
13914 $customer->docsfolder = JFilterOutput::stringURLSafe(implode('-', array_filter($parts)));
13915
13916 if (strlen($customer->docsfolder) < 16)
13917 {
13918 throw new Exception('Possible security breach. Please specify the most details as possible.', 400);
13919 }
13920
13921 jimport('joomla.filesystem.folder');
13922
13923 // create a folder for this customer
13924 $created = JFolder::create($dirpath . $customer->docsfolder);
13925
13926 if (!$created)
13927 {
13928 throw new Exception(sprintf('Unable to create the folder [%s]', $dirpath . $customer->docsfolder), 403);
13929 }
13930
13931 unset($customer->seed);
13932
13933 // update docs folder
13934 $dbo->updateObject('#__vikbooking_customers', $customer, 'id');
13935 }
13936
13937 // get file from request
13938 $file = $input->files->get('file', array(), 'array');
13939
13940 // try to upload the file
13941 $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');
13942 $result->status = 1;
13943
13944 $result->size = JHtml::fetch('number.bytes', filesize($result->path), 'auto', 0);
13945 $result->url = str_replace(DIRECTORY_SEPARATOR, '/', str_replace(VBO_CUSTOMERS_PATH . DIRECTORY_SEPARATOR, VBO_CUSTOMERS_URI, $result->path));
13946 }
13947 catch (Exception $e)
13948 {
13949 $result->error = $e->getMessage();
13950 $result->code = $e->getCode();
13951 }
13952
13953 echo json_encode($result);
13954 exit;
13955 }
13956
13957 /**
13958 * AJAX delete the customer documents.
13959 *
13960 * @return void
13961 *
13962 * @throws Exception
13963 */
13964 public function delete_customer_document()
13965 {
13966 $input = JFactory::getApplication()->input;
13967 $dbo = JFactory::getDbo();
13968
13969 $customer_id = $input->getUint('customer', 0);
13970
13971 $result = new stdClass;
13972 $result->status = 0;
13973
13974 $q = $dbo->getQuery(true)
13975 ->select($dbo->qn('docsfolder'))
13976 ->from($dbo->qn('#__vikbooking_customers'))
13977 ->where($dbo->qn('id') . ' = ' . $customer_id);
13978
13979 $dbo->setQuery($q, 0, 1);
13980 $dbo->execute();
13981
13982 if (!$dbo->getNumRows())
13983 {
13984 throw new Exception(sprintf('Customer [%d] not found', $customer_id), 404);
13985 }
13986
13987 $folder = $dbo->loadResult();
13988
13989 if (!$folder)
13990 {
13991 throw new Exception('The customer does not have any documents', 500);
13992 }
13993
13994 $file = $input->getString('file');
13995
13996 if (!$file)
13997 {
13998 throw new Exception('File to remove not specified', 400);
13999 }
14000
14001 $path = implode(DIRECTORY_SEPARATOR, array(VBO_CUSTOMERS_PATH, $folder, $file));
14002
14003 if (!is_file($path))
14004 {
14005 throw new Exception(sprintf('File [%s] not found', $path), 404);
14006 }
14007
14008 jimport('joomla.filesystem.file');
14009
14010 $removed = JFile::delete($path);
14011
14012 echo json_encode(array('status' => (int) $removed));
14013 exit;
14014 }
14015
14016 /**
14017 * AJAX task to invoke a specific report and obtain information.
14018 *
14019 * @since 1.3.0
14020 */
14021 public function get_report_data()
14022 {
14023 $report_name = VikRequest::getString('report_name', '', 'request');
14024 $current_fest = VikRequest::getString('current_fest', '', 'request');
14025 $current_fromdate = VikRequest::getString('current_fromdate', '', 'request');
14026 $current_todate = VikRequest::getString('current_todate', '', 'request');
14027 $step = VikRequest::getString('step', 'weekend', 'request');
14028 $direction = VikRequest::getString('direction', 'load', 'request');
14029 $period = VikRequest::getString('period', 'full', 'request');
14030 $krsort = VikRequest::getString('krsort', 'occupancy', 'request');
14031 $krorder = VikRequest::getString('krorder', 'DESC', 'request');
14032 $chart_datatype = VikRequest::getVar('chart_datatype', array(), 'request');
14033 $chart_meta_data = VikRequest::getString('chart_meta_data', '', 'request', VIKREQUEST_ALLOWRAW);
14034 $chart_meta_data = !empty($chart_meta_data) ? json_decode($chart_meta_data, true) : array();
14035 // idroom can be an array of IDs or just one ID as int/string
14036 $idroom = VikRequest::getVar('idroom', null, 'request');
14037 //
14038
14039 if (empty($report_name) || empty($current_fromdate) || empty($current_todate)) {
14040 throw new Exception("Missing request data", 400);
14041 }
14042
14043 // get requested report instance
14044 $report = VikBooking::getReportInstance($report_name);
14045 if (!$report) {
14046 throw new Exception("Report not found", 404);
14047 }
14048
14049 // chart data
14050 if (empty($chart_datatype)) {
14051 $chart_datatype = array(
14052 'type' => 'doughnut',
14053 'depth' => 1,
14054 'keys' => array($krsort),
14055 );
14056 }
14057
14058 // website date format
14059 $df = $report->getDateFormat();
14060
14061 // prepare request params for the report
14062 $rparams = array(
14063 'fromdate' => $current_fromdate,
14064 'todate' => $current_todate,
14065 'period' => $period,
14066 'krsort' => $krsort,
14067 'krorder' => $krorder,
14068 'idroom' => $idroom,
14069 );
14070
14071 // starting dates info and timestamps
14072 $from_ts = VikBooking::getDateTimestamp($current_fromdate, 0, 0, 0);
14073 $to_ts = VikBooking::getDateTimestamp($current_todate, 23, 59, 59);
14074 $from_info = getdate($from_ts);
14075 $to_info = getdate($to_ts);
14076
14077 // the name of the period requested and whether it's a fest
14078 $period_name = '';
14079 $is_fest = null;
14080
14081 if ($direction == 'prev' || $direction == 'next') {
14082 // calculate prev or next dates
14083 if ($step == 'weekend') {
14084 $period_name = JText::translate('VBOWEEKND');
14085 if ($direction == 'next') {
14086 // next weekend from current end date
14087 $next_ts = strtotime("next friday", $to_ts);
14088 } else {
14089 // prev weekend from current start date
14090 $next_ts = strtotime("previous friday", $from_ts);
14091 }
14092 $next_info = getdate($next_ts);
14093 $new_from_ts = $next_ts;
14094 $new_to_ts = mktime(23, 59, 59, $next_info['mon'], ($next_info['mday'] + 1), $next_info['year']);
14095 $rparams['fromdate'] = date($df, $new_from_ts);
14096 $rparams['todate'] = date($df, $new_to_ts);
14097 } elseif ($step == 'week') {
14098 $period_name = JText::translate('VBOWEEK');
14099 if ($direction == 'next') {
14100 // start next week from the current end date
14101 $new_from_ts = $to_ts;
14102 $new_to_ts = mktime(23, 59, 59, $to_info['mon'], ($to_info['mday'] + 7), $to_info['year']);
14103 $rparams['fromdate'] = $rparams['todate'];
14104 $rparams['todate'] = date($df, $new_to_ts);
14105 } else {
14106 // end prev week from the current from date
14107 $new_from_ts = mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] - 7), $from_info['year']);
14108 $new_to_ts = $from_ts;
14109 $rparams['todate'] = $rparams['fromdate'];
14110 $rparams['fromdate'] = date($df, $new_from_ts);
14111 }
14112 } else {
14113 // month
14114 $period_name = JText::translate('VBPVIEWRESTRICTIONSTWO');
14115 if ($direction == 'next') {
14116 // next month from the current from date
14117 $nextmonts = mktime(0, 0, 0, ($from_info['mon'] + 1), 1, $from_info['year']);
14118 $new_from_ts = $nextmonts;
14119 $new_to_ts = mktime(23, 59, 59, ($from_info['mon'] + 1), date('t', $nextmonts), $from_info['year']);
14120 $rparams['fromdate'] = date($df, $new_from_ts);
14121 $rparams['todate'] = date($df, $new_to_ts);
14122 } else {
14123 // prev month from the current from date
14124 $nextmonts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']);
14125 $new_from_ts = $nextmonts;
14126 $new_to_ts = mktime(23, 59, 59, ($from_info['mon'] - 1), date('t', $nextmonts), $from_info['year']);
14127 $rparams['fromdate'] = date($df, $new_from_ts);
14128 $rparams['todate'] = date($df, $new_to_ts);
14129 }
14130 }
14131
14132 // get the next festivities
14133 $fests = VikBooking::getFestivitiesInstance();
14134 $next_fests = $fests->loadFestDates();
14135 if (count($next_fests)) {
14136 // check whether a festivity should be displayed rather than the calculated period of dates
14137 foreach ($next_fests as $fest) {
14138 $fest_found = false;
14139 if ($direction == 'next' && $fest['festinfo'][0]->from_ts > $from_ts && $fest['festinfo'][0]->from_ts <= $new_to_ts) {
14140 $fest_found = true;
14141 } elseif ($direction == 'prev' && $fest['festinfo'][0]->from_ts < $to_ts && $fest['festinfo'][0]->from_ts >= $new_from_ts) {
14142 $fest_found = true;
14143 }
14144 if ($fest_found && (string)$fest['festinfo'][0]->next_ts != $current_fest) {
14145 // festivity found before next calculated period
14146 $is_fest = $fest['festinfo'][0]->next_ts;
14147 $period_name = $fest['festinfo'][0]->trans_name;
14148 $new_from_ts = $fest['festinfo'][0]->from_ts;
14149 $new_to_ts = $fest['festinfo'][0]->to_ts;
14150 $rparams['fromdate'] = date($df, $new_from_ts);
14151 $rparams['todate'] = date($df, $new_to_ts);
14152 break;
14153 }
14154 }
14155 }
14156 } else {
14157 // load requested dates by skipping the festivities
14158 $new_from_ts = $from_ts;
14159 $new_to_ts = $to_ts;
14160 }
14161
14162 // invoke report
14163 $report->injectParams($rparams);
14164 $report_values = $report->getReportValues(1);
14165 $report_cols = $report->getColumnsValues();
14166 $report_chart = null;
14167 $report_chart_metas = array();
14168 $chart_meta_data = array(
14169 'keys' => array(
14170 'occupancy',
14171 'tot_bookings',
14172 'nights_booked',
14173 ),
14174 );
14175 $error = null;
14176
14177 if (!count($report_values)) {
14178 $error = strlen($report->getError()) ? $report->getError() : JText::translate('VBNOTRACKINGS');
14179 } else {
14180 // get doughnut Chart for the requested key
14181 $report_chart = $report->getChart((array) $chart_datatype);
14182
14183 // get Chart meta data
14184 $all_chart_metas = $report->getChartMetaData(null, $chart_meta_data);
14185 if (count($all_chart_metas)) {
14186 // merge all positions into one array
14187 foreach ($all_chart_metas as $pos_metas) {
14188 $report_chart_metas = array_merge($report_chart_metas, $pos_metas);
14189 }
14190 }
14191
14192 if (empty($period_name)) {
14193 $period_name = $report->getProperty('chartTitle');
14194 }
14195 }
14196
14197 // build response
14198 $response = new stdClass;
14199 $response->error = $error;
14200 $response->fromdate = $rparams['fromdate'];
14201 $response->todate = $rparams['todate'];
14202 $response->in_days = $report->countDaysTo($new_from_ts);
14203 $response->in_days_to = $report->countDaysTo($new_to_ts);
14204 $response->in_days_avg = $report->countAverageDays($response->in_days, $response->in_days_to);
14205 $response->period_name = $period_name;
14206 $response->period_date = count($report_values) && isset($report_values['day']) ? $report_values['day']['display_value'] : '';
14207 $response->is_fest = $is_fest;
14208 $response->report_chart = $report_chart;
14209 $response->report_cols = $report_cols;
14210 $response->report_values = $report_values;
14211 $response->report_script = $report->getScript();
14212 $response->chart_labels = $report->getProperty('chartJsLabels');
14213 $response->dataset_label = $report->getProperty('chartJsDataSetLabel');
14214 $response->chart_colors = $report->getProperty('chartJsColors');
14215 $response->chart_data = $report->getProperty('chartJsData');
14216 $response->report_chart_metas = $report_chart_metas;
14217
14218 echo json_encode($response);
14219 exit;
14220 }
14221
14222 /**
14223 * Go to the previous booking.
14224 *
14225 * @uses navigateToBooking()
14226 *
14227 * @since 1.3.0
14228 */
14229 public function prev_booking()
14230 {
14231 $this->navigateToBooking('prev');
14232 }
14233
14234 /**
14235 * Go to the next booking.
14236 *
14237 * @uses navigateToBooking()
14238 *
14239 * @since 1.3.0
14240 */
14241 public function next_booking()
14242 {
14243 $this->navigateToBooking('next');
14244 }
14245
14246 /**
14247 * Given the current booking ID in the request, we navigate
14248 * either to the next or to the previous reservation (if any).
14249 *
14250 * @param string $direction either next or prev.
14251 *
14252 * @return void
14253 *
14254 * @since 1.3.0
14255 */
14256 private function navigateToBooking($direction = 'next')
14257 {
14258 $bid = VikRequest::getInt('whereup', 0, 'request');
14259 if (empty($bid) || $bid < 1 || !in_array($direction, array('prev', 'next'))) {
14260 throw new Exception("Invalid request", 400);
14261 }
14262
14263 $dbo = JFactory::getDbo();
14264 $app = JFactory::getApplication();
14265
14266 $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `id`" . ($direction == 'next' ? '>' : '<') . "{$bid} ORDER BY `id` " . ($direction == 'next' ? 'ASC' : 'DESC');
14267 $dbo->setQuery($q, 0, 1);
14268 $dbo->execute();
14269 if (!$dbo->getNumRows()) {
14270 VikError::raiseWarning('', JText::translate('VBPEDITBUSYONE'));
14271 $app->redirect("index.php?option=com_vikbooking&task=orders");
14272 exit;
14273 }
14274
14275 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $dbo->loadResult());
14276 exit;
14277 }
14278
14279 /**
14280 * AJAX request: from a list of reservation IDs, we return the ones
14281 * that have a review with the related review ID on VCM.
14282 *
14283 * @since 1.13
14284 */
14285 public function bookings_have_reviews()
14286 {
14287 if (!JSession::checkToken()) {
14288 // missing CSRF-proof token
14289 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14290 }
14291
14292 $dbo = JFactory::getDbo();
14293
14294 $bids = VikRequest::getVar('bids', [], 'request', 'array');
14295 $vcm_installed = class_exists('VikChannelManager');
14296 $withreviews = [];
14297
14298 if ($vcm_installed && $bids) {
14299 $bids = array_filter(array_map('intval', (array) $bids));
14300 $bids = $bids ?: [0];
14301
14302 try {
14303 $q = "SELECT `id`, `idorder` FROM `#__vikchannelmanager_otareviews` WHERE `idorder` IN (" . implode(', ', $bids) . ");";
14304 $dbo->setQuery($q);
14305 $reviews = $dbo->loadAssocList();
14306
14307 foreach ($reviews as $r) {
14308 $withreviews[$r['idorder']] = $r['id'];
14309 }
14310 } catch (Exception $e) {
14311 // do nothing, outdated version
14312 }
14313 }
14314
14315 // output list of booking IDs found, if any
14316 VBOHttpDocument::getInstance()->json($withreviews);
14317 }
14318
14319 /**
14320 * AJAX request for adding a new room-day note.
14321 *
14322 * @return void
14323 *
14324 * @since 1.13.5
14325 */
14326 public function add_roomdaynote()
14327 {
14328 $dt = VikRequest::getString('dt', '', 'request');
14329 $idroom = VikRequest::getInt('idroom', 0, 'request');
14330 $subunit = VikRequest::getInt('subunit', 0, 'request');
14331 $type = VikRequest::getString('type', '', 'request');
14332 $type = empty($type) ? 'custom' : $type;
14333 $name = VikRequest::getString('name', '', 'request');
14334 $descr = VikRequest::getString('descr', '', 'request');
14335 $cdays = VikRequest::getInt('cdays', 0, 'request');
14336 $cdays = $cdays < 0 ? 0 : $cdays;
14337 $cdays = $cdays > 365 ? 365 : $cdays;
14338 if (empty($idroom) || empty($dt) || !strtotime($dt)) {
14339 echo 'e4j.error.1';
14340 exit;
14341 }
14342
14343 // reload end date
14344 $end_date = $dt;
14345
14346 // build critical date object
14347 $new_note = array(
14348 'name' => $name,
14349 'type' => $type,
14350 'descr' => $descr,
14351 );
14352
14353 // get object
14354 $notes = VikBooking::getCriticalDatesInstance();
14355
14356 // store the notes for all consecutive dates
14357 for ($i = 0; $i <= $cdays; $i++) {
14358 $store_dt = $dt;
14359 if ($i > 0) {
14360 $dt_info = getdate(strtotime($store_dt));
14361 $store_dt = date('Y-m-d', mktime(0, 0, 0, $dt_info['mon'], ($dt_info['mday'] + $i), $dt_info['year']));
14362 $end_date = $store_dt;
14363 }
14364 $result = $notes->storeDayNote($new_note, $store_dt, $idroom, $subunit);
14365 if (!$result) {
14366 echo 'e4j.error.2';
14367 exit;
14368 }
14369 }
14370
14371 // reload all room day notes for this day for the AJAX response
14372 $all_notes = $notes->loadRoomDayNotes($dt, $end_date, $idroom, $subunit);
14373
14374 if (!$all_notes || !count($all_notes)) {
14375 // no notes found even after storing it
14376 echo 'e4j.error.3';
14377 exit;
14378 }
14379
14380 echo json_encode($all_notes);
14381 exit;
14382 }
14383
14384 /**
14385 * AJAX request for removing a room day note.
14386 *
14387 * @return void
14388 *
14389 * @since 1.13.5
14390 */
14391 public function remove_roomdaynote()
14392 {
14393 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
14394 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14395 }
14396
14397 $dt = VikRequest::getString('dt', '', 'request');
14398 $idroom = VikRequest::getInt('idroom', 0, 'request');
14399 $subunit = VikRequest::getInt('subunit', 0, 'request');
14400 $type = VikRequest::getString('type', '', 'request');
14401 $type = empty($type) ? 'custom' : $type;
14402 $ind = VikRequest::getInt('ind', 0, 'request');
14403 if (empty($dt) || !strtotime($dt)) {
14404 echo 'e4j.error.1';
14405 exit;
14406 }
14407
14408 $notes = VikBooking::getCriticalDatesInstance();
14409 $result = $notes->deleteDayNote($ind, $dt, $idroom, $subunit, $type);
14410 if (!$result) {
14411 echo 'e4j.error.2';
14412 exit;
14413 }
14414
14415 echo 'e4j.ok';
14416 exit;
14417 }
14418
14419 /**
14420 * AJAX request for storing an event for a booking.
14421 * Firstly developed for the VCM Reporting API - Guest Misconduct,
14422 * but it can be used for any other purpose.
14423 *
14424 * @return void
14425 *
14426 * @since 1.13.5
14427 */
14428 public function store_booking_history_event()
14429 {
14430 $bid = VikRequest::getInt('bid', 0, 'request');
14431 $event = VikRequest::getString('event', '', 'request');
14432 $descr = VikRequest::getString('descr', '', 'request');
14433
14434 if (empty($bid) || empty($event)) {
14435 throw new Exception("Missing required information", 500);
14436 }
14437
14438 // Booking History
14439 VikBooking::getBookingHistoryInstance()->setBid($bid)->store($event, $descr);
14440 //
14441
14442 echo 'e4j.ok';
14443 exit;
14444 }
14445
14446 /**
14447 * AJAX request for updating an option/extra service.
14448 * Firstly developed for the VCM Vacation Rentals Essentials API - Damage Deposit,
14449 * but it can be used for any other purpose.
14450 *
14451 * @return void
14452 *
14453 * @since 1.13.5
14454 */
14455 public function update_option_params()
14456 {
14457 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
14458 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14459 }
14460
14461 $optid = VikRequest::getInt('optid', 0, 'request');
14462 $oparams = VikRequest::getVar('oparams', array(), 'request', 'array');
14463
14464 if (empty($optid) || !is_array($oparams) || empty($oparams)) {
14465 throw new Exception("Missing required information", 500);
14466 }
14467
14468 $dbo = JFactory::getDbo();
14469 $q = "SELECT `oparams` FROM `#__vikbooking_optionals` WHERE `id`=" . (int)$optid . ";";
14470 $dbo->setQuery($q);
14471 $dbo->execute();
14472 if (!$dbo->getNumRows()) {
14473 throw new Exception("Option not found", 404);
14474 }
14475 $cur_params = $dbo->loadResult();
14476 $cur_params = !empty($cur_params) ? json_decode($cur_params, true) : array();
14477 $cur_params = !is_array($cur_params) ? array() : $cur_params;
14478
14479 foreach ($oparams as $k => $v) {
14480 if (empty($k)) {
14481 continue;
14482 }
14483 $cur_params[$k] = $v;
14484 }
14485
14486 $q = "UPDATE `#__vikbooking_optionals` SET `oparams`=" . $dbo->quote(json_encode($cur_params)) ." WHERE `id`=" . (int)$optid . ";";
14487 $dbo->setQuery($q);
14488 $dbo->execute();
14489
14490 echo 'e4j.ok';
14491 exit;
14492 }
14493
14494 /**
14495 * Hidden task to clean up duplicate records in certain database tables
14496 * due to a double execution of the installation queries. Ghost records,
14497 * if any, are also removed to clean up issues with hanging records.
14498 *
14499 * @since November 4th 2020
14500 * @since 1.16.3 (J) - 1.6.3 (WP)
14501 */
14502 public function clean_duplicate_records()
14503 {
14504 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14505 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14506 }
14507
14508 $dbo = JFactory::getDbo();
14509
14510 $tables_with_duplicates = [
14511 '#__vikbooking_config' => [
14512 'id_key' => 'id',
14513 'compare_key' => 'param',
14514 ],
14515 '#__vikbooking_countries' => [
14516 'id_key' => 'id',
14517 'compare_key' => 'country_3_code',
14518 ],
14519 '#__vikbooking_custfields' => [
14520 'id_key' => 'id',
14521 'compare_key' => 'name',
14522 ],
14523 '#__vikbooking_texts' => [
14524 'id_key' => 'id',
14525 'compare_key' => 'param',
14526 ],
14527 ];
14528
14529 foreach ($tables_with_duplicates as $tblname => $data) {
14530 $doubles = [];
14531 $storage = [];
14532 $rmlist = [];
14533
14534 $q = "SELECT * FROM `{$tblname}` ORDER BY `{$data['id_key']}` DESC;";
14535 $dbo->setQuery($q);
14536 $rows = $dbo->loadAssocList();
14537 if (!$rows) {
14538 echo "<p>No records found in table {$tblname}</p>";
14539 continue;
14540 }
14541
14542 foreach ($rows as $row) {
14543 if (!isset($doubles[$row[$data['compare_key']]])) {
14544 $doubles[$row[$data['compare_key']]] = 0;
14545 }
14546 $doubles[$row[$data['compare_key']]]++;
14547 if (!isset($storage[$row[$data['compare_key']]])) {
14548 $storage[$row[$data['compare_key']]] = [];
14549 }
14550 array_push($storage[$row[$data['compare_key']]], $row[$data['id_key']]);
14551 }
14552
14553 foreach ($doubles as $paramkey => $paramcount) {
14554 if ($paramcount < 2 || !isset($storage[$paramkey]) || count($storage[$paramkey]) < 2 || $paramcount != count($storage[$paramkey])) {
14555 continue;
14556 }
14557 $exceeding = $paramcount - 1;
14558 for ($x = 0; $x < $exceeding; $x++) {
14559 array_push($rmlist, $storage[$paramkey][$x]);
14560 }
14561 }
14562
14563 echo "<p>Total records found in table {$tblname}: " . count($rows) . "</p>";
14564 echo '<p>Total records to remove: ' . count($rmlist) . '</p>';
14565 echo '<pre style="display: none;">'.print_r($rmlist, true).'</pre><br/>';
14566
14567 if (count($rmlist)) {
14568 $q = "DELETE FROM `{$tblname}` WHERE `{$data['id_key']}` IN (" . implode(', ', $rmlist) . ");";
14569 $dbo->setQuery($q);
14570 $dbo->execute();
14571 }
14572 }
14573
14574 /**
14575 * Clean up busy records where the busy relations contain empty booking IDs.
14576 */
14577 $hanging_busy_ids = [];
14578
14579 $q = "SELECT `idbusy` FROM `#__vikbooking_ordersbusy` WHERE `idorder` = 0 OR `idorder` IS NULL;";
14580 $dbo->setQuery($q);
14581 $removelist = $dbo->loadAssocList();
14582 if ($removelist) {
14583 foreach ($removelist as $hanging_busy) {
14584 $hanging_busy_id = (int)$hanging_busy['idbusy'];
14585 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
14586 array_push($hanging_busy_ids, $hanging_busy_id);
14587 }
14588 }
14589 }
14590
14591 // let's check also for ghost records that only occupy the room
14592 $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);";
14593 $dbo->setQuery($q);
14594 $removelist = $dbo->loadAssocList();
14595 if ($removelist) {
14596 foreach ($removelist as $hanging_busy) {
14597 $hanging_busy_id = (int)$hanging_busy['id'];
14598 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
14599 array_push($hanging_busy_ids, $hanging_busy_id);
14600 }
14601 }
14602 }
14603
14604 if ($hanging_busy_ids) {
14605 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id` IN (" . implode(', ', $hanging_busy_ids) . ");";
14606 $dbo->setQuery($q);
14607 $dbo->execute();
14608 }
14609
14610 echo "<p>Total ghost records removed: " . count($hanging_busy_ids) . "</p>";
14611
14612 return;
14613 }
14614
14615 /**
14616 * Hidden task to scan all database tables of VikBooking and Vik Channel Manager
14617 * to ensure the column `id` is defined as a primary key and got an auto-increment
14618 * extra flag properly defined and set. We've noticed that some third-party plugins
14619 * used to migrate WP sites may break the primary keys, and so new records won't get an ID.
14620 *
14621 * @since 1.16.8 (J) - 1.6.8 (WP)
14622 */
14623 public function fix_autoincrement_tables()
14624 {
14625 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14626 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14627 }
14628
14629 $dbo = JFactory::getDbo();
14630
14631 // load all the installed database tables
14632 $tables = $dbo->getTableList();
14633
14634 // get current database prefix
14635 $prefix = $dbo->getPrefix();
14636
14637 // replace prefix with placeholder
14638 $tables = array_map(function($table) use ($prefix)
14639 {
14640 return preg_replace("/^{$prefix}/", '#__', $table);
14641 }, $tables);
14642
14643 // remove all the tables that do not belong to VikBooking/VCM
14644 $tables = array_values(array_filter($tables, function($table)
14645 {
14646 if (preg_match("/^#__vik(?:booking|channelmanager)_config$/", $table))
14647 {
14648 // exclude the configuration table, which will be handled in a different way
14649 return false;
14650 }
14651
14652 return preg_match("/^#__vik(?:booking|channelmanager)_/", $table);
14653 }));
14654
14655 foreach ($tables as $table) {
14656 $columns = $dbo->getTableColumns($table, false);
14657 if (!isset($columns['id']) || empty($columns['id']->Type) || !empty($columns['id']->Extra)) {
14658 continue;
14659 }
14660
14661 echo 'Fixing ' . $table. ' for missing auto-increment<br/><pre>' . print_r($columns['id'], true) . '</pre><br/>';
14662
14663 // set auto-increment and primary key
14664 $dbo->setQuery("ALTER TABLE `{$table}` MODIFY `id` " . $columns['id']->Type . " NOT NULL AUTO_INCREMENT PRIMARY KEY;");
14665 $dbo->execute();
14666
14667 // count next auto-increment
14668 $dbo->setQuery("SELECT MAX(`id`) FROM `{$table}`");
14669 $next_ai = (int) $dbo->loadResult() + 1;
14670
14671 // update next auto-increment value
14672 $dbo->setQuery("ALTER TABLE `{$table}` AUTO_INCREMENT = {$next_ai}");
14673 $dbo->execute();
14674 }
14675 }
14676
14677 /**
14678 * Hidden task to (re-)run the update queries from a given plugin version.
14679 * Useful to ensure the database structure is up-to-date and no update queries went lost.
14680 *
14681 * @since 1.17.6 (J) - 1.7.6 (WP)
14682 */
14683 public function run_update_queries()
14684 {
14685 $app = JFactory::getApplication();
14686 $dbo = JFactory::getDbo();
14687
14688 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14689 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14690 }
14691
14692 $from_version = $app->input->getString('from_version');
14693
14694 if (empty($from_version)) {
14695 VBOHttpDocument::getInstance()->close(400, 'Missing from version value.');
14696 }
14697
14698 // determine the SQL updates directory path
14699 $sql_updates_path = '';
14700 if (VBOPlatformDetection::isWordPress()) {
14701 $sql_updates_path = implode(DIRECTORY_SEPARATOR, [VIKBOOKING_BASE, 'sql', 'update', 'mysql']);
14702 } else {
14703 $sql_updates_path = implode(DIRECTORY_SEPARATOR, [VBO_ADMIN_PATH, 'sql', 'updates', 'mysql']);
14704 }
14705
14706 if (!$sql_updates_path || !is_dir($sql_updates_path)) {
14707 VBOHttpDocument::getInstance()->close(500, 'Could not find SQL updates path.');
14708 }
14709
14710 // read all SQL update files
14711 $sql_update_files = JFolder::files($sql_updates_path, '\.sql', $recurse = false, $full = true);
14712
14713 // filter SQL files with just the valid ones
14714 $sql_update_files = array_filter($sql_update_files, function($sql_update_file) use ($from_version) {
14715 $file_version = basename($sql_update_file, '.sql');
14716 return version_compare($file_version, $from_version, '>=');
14717 });
14718
14719 // sort files by version ascending
14720 usort($sql_update_files, function($a, $b) {
14721 return version_compare(basename($a, '.sql'), basename($b, '.sql'));
14722 });
14723
14724 if (!$sql_update_files) {
14725 VBOHttpDocument::getInstance()->close(500, sprintf('Could not find any suitable SQL update file from version %s.', $from_version));
14726 }
14727
14728 $success_queries = 0;
14729
14730 foreach ($sql_update_files as $file) {
14731 $handle = fopen($file, 'r');
14732
14733 $bytes = '';
14734 while (!feof($handle)) {
14735 $bytes .= fread($handle, 8192);
14736 }
14737
14738 fclose($handle);
14739
14740 if (VBOPlatformDetection::isWordPress()) {
14741 $queries_list = JDatabaseHelper::splitSql($bytes);
14742 } else {
14743 try {
14744 if (class_exists('JDatabaseDriver')) {
14745 $queries_list = JDatabaseDriver::splitSql($bytes);
14746 } else {
14747 $queries_list = Joomla\Database\DatabaseDriver::splitSql($bytes);
14748 }
14749 } catch(Throwable $e) {
14750 $app->enqueueMessage(sprintf('Error splitting queries: %s', $e->getMessage()), 'error');
14751 $queries_list = [];
14752 }
14753 }
14754
14755 foreach ($queries_list as $q) {
14756 try {
14757 $dbo->setQuery($q);
14758 $result = $dbo->execute();
14759 } catch (Exception $e) {
14760 $result = false;
14761 $app->enqueueMessage(sprintf('Error executing query: %s', $e->getMessage()), 'warning');
14762 }
14763
14764 if ($result) {
14765 $success_queries++;
14766 }
14767 }
14768 }
14769
14770 if ($success_queries) {
14771 $app->enqueueMessage(sprintf('Successful queries: %d', $success_queries), 'success');
14772 }
14773
14774 // send response to output
14775 echo '<pre>'.print_r($sql_update_files, true).'</pre><br/>';
14776 }
14777
14778 /**
14779 * Loads a specific admin widget ID and executes the requested method.
14780 * Useful for loading a newly added widget, or to execute custom methods.
14781 *
14782 * @see this is an AJAX endpoint.
14783 *
14784 * @since 1.14 (J) - 1.4.0 (WP)
14785 * @since 1.15 (J) - 1.5.0 (WP) widget callback can return values rather than just echoing.
14786 * @since 1.16.5 (J) - 1.6.5 (WP) widgets are rendered within a try-catch statement.
14787 */
14788 public function exec_admin_widget()
14789 {
14790 if (!JSession::checkToken()) {
14791 // missing CSRF-proof token
14792 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14793 }
14794
14795 $widget_id = VikRequest::getString('widget_id', '', 'request');
14796 $call = VikRequest::getString('call', '', 'request');
14797 $return = VikRequest::getInt('return', 0, 'request');
14798 $vbo_page = VikRequest::getString('vbo_page', '', 'request');
14799 $vbo_uri = VikRequest::getString('vbo_uri', '', 'request');
14800 $multitask = VikRequest::getInt('multitask', 0, 'request');
14801
14802 if (empty($widget_id)) {
14803 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget ID');
14804 }
14805
14806 if (empty($call) || !is_string($call)) {
14807 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget Callback');
14808 }
14809
14810 // invoke admin widgets helper
14811 $widgets_helper = VikBooking::getAdminWidgetsInstance();
14812 $widget = $widgets_helper->getWidget($widget_id);
14813
14814 if ($widget === false) {
14815 VBOHttpDocument::getInstance()->close(404, 'Requested Admin Widget not found');
14816 }
14817
14818 if (!method_exists($widget, $call) || !is_callable(array($widget, $call))) {
14819 VBOHttpDocument::getInstance()->close(403, 'Admin Widget Callback not found or not callable');
14820 }
14821
14822 // get the multitask parser object
14823 $parser = VBOMultitaskParser::getInstance($vbo_page, $vbo_uri);
14824
14825 // check if arguments should be passed
14826 $call_args = [];
14827 if ($multitask && $call === 'render') {
14828 // build the multitask data object and inject it to the args as the first index
14829 $call_args[] = $parser->getData();
14830
14831 // bind options within the widget, if any
14832 $widget->bindOptions($call_args[0]);
14833 } else {
14834 // always bind multitask options, if any
14835 $widget->bindOptions($parser->getOptions());
14836 }
14837
14838 try {
14839 if ($return) {
14840 // invoke the widget's method and get the value returned
14841 $widget_response = $call_args ? call_user_func_array([$widget, $call], $call_args) : $widget->{$call}();
14842 } else {
14843 // invoke the widget's method within a buffer
14844 ob_start();
14845 if ($call_args) {
14846 $res = call_user_func_array([$widget, $call], $call_args);
14847 } else {
14848 $widget->{$call}();
14849 }
14850 $widget_response = ob_get_contents();
14851 ob_end_clean();
14852 }
14853 } catch (Throwable $e) {
14854 VBOHttpDocument::getInstance()->close($e->getCode() ?: 500, $e->getMessage());
14855 } catch (Exception $e) {
14856 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
14857 }
14858
14859 // prepare response object with a property equal to the called method
14860 $response = new stdClass;
14861 $response->{$call} = $widget_response;
14862
14863 // output the JSON encoded response and exit
14864 VBOHttpDocument::getInstance()->json($response);
14865 }
14866
14867 /**
14868 * Updates the map of admin widgets.
14869 *
14870 * @throws Exception this is an AJAX endpoint.
14871 *
14872 * @since 1.4.0
14873 */
14874 public function save_admin_widgets()
14875 {
14876 if (!JSession::checkToken()) {
14877 // missing CSRF-proof token
14878 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14879 }
14880
14881 // make sure permissions are sufficient
14882 if (!JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
14883 VBOHttpDocument::getInstance()->close(403, 'You are not authorized to modify the widgets.');
14884 }
14885
14886 $psections = VikRequest::getVar('sections', array(), 'request', 'array');
14887 if (!is_array($psections) || !count($psections)) {
14888 VBOHttpDocument::getInstance()->close(500, 'No sections found in map');
14889 }
14890
14891 // request values are all converted to arrays, so restore the object styling
14892 $psections = json_decode(json_encode($psections));
14893
14894 // update map
14895 $result = VikBooking::getAdminWidgetsInstance()->updateWidgetsMap($psections);
14896
14897 $response = new stdClass;
14898 $response->status = (int)$result;
14899
14900 // output the JSON encoded response and exit
14901 VBOHttpDocument::getInstance()->json($response);
14902 }
14903
14904 /**
14905 * Restores the default admin widgets map.
14906 *
14907 * @since 1.4.0
14908 */
14909 public function reset_admin_widgets()
14910 {
14911 // reset map and redirect to dashboard
14912 VikBooking::getAdminWidgetsInstance()->restoreDefaultWidgetsMap();
14913
14914 JFactory::getApplication()->redirect('index.php?option=com_vikbooking');
14915 exit;
14916 }
14917
14918 /**
14919 * Updates the welcome message status for the widget's customizer via AJAX.
14920 *
14921 * @since 1.4.0
14922 */
14923 public function admin_widgets_welcome()
14924 {
14925 if (!JSession::checkToken()) {
14926 // missing CSRF-proof token
14927 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14928 }
14929
14930 $hide_welcome = VikRequest::getInt('hide_welcome', 0, 'request');
14931 // update configuration value
14932 VikBooking::getAdminWidgetsInstance()->updateWelcome($hide_welcome);
14933
14934 $response = new stdClass;
14935 $response->status = $hide_welcome;
14936
14937 // output the JSON encoded response and exit
14938 VBOHttpDocument::getInstance()->json($response);
14939 }
14940
14941 public function newcondtext()
14942 {
14943 VikBookingHelper::printHeader("11");
14944
14945 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecondtext'));
14946
14947 parent::display();
14948
14949 if (VikBooking::showFooter()) {
14950 VikBookingHelper::printFooter();
14951 }
14952 }
14953
14954 public function editcondtext()
14955 {
14956 VikBookingHelper::printHeader("11");
14957
14958 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecondtext'));
14959
14960 parent::display();
14961
14962 if (VikBooking::showFooter()) {
14963 VikBookingHelper::printFooter();
14964 }
14965 }
14966
14967 public function cancelcondtext()
14968 {
14969 JFactory::getApplication()->redirect('index.php?option=com_vikbooking&task=config&tab=7');
14970 }
14971
14972 public function createcondtext()
14973 {
14974 if (!JSession::checkToken()) {
14975 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
14976 }
14977
14978 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
14979 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14980 }
14981
14982 $this->_doCreateCondText();
14983 }
14984
14985 public function createcondtextstay()
14986 {
14987 if (!JSession::checkToken()) {
14988 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
14989 }
14990
14991 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
14992 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14993 }
14994
14995 $this->_doCreateCondText(true);
14996 }
14997
14998 private function _doCreateCondText($stay = false)
14999 {
15000 $dbo = JFactory::getDbo();
15001 $app = JFactory::getApplication();
15002 $rules_helper = VikBooking::getConditionalRulesInstance();
15003 $rules_list = $rules_helper->composeRulesParamsFromRequest();
15004
15005 $condtextname = VikRequest::getString('condtextname', '', 'request');
15006 $condtexttkn = VikRequest::getString('condtexttkn', '', 'request');
15007 $msg = VikRequest::getString('msg', '', 'request', VIKREQUEST_ALLOWRAW);
15008 $debug = VikRequest::getInt('debug', 0, 'request');
15009 if (empty($condtextname)) {
15010 $condtextname = date('Y-m-dHis');
15011 $condtexttkn = '{condition: ' . date('YmdHis') . '}';
15012 }
15013
15014 $existing_tokens = $rules_helper->getSpecialTags();
15015 if (count($existing_tokens) && isset($existing_tokens[$condtexttkn])) {
15016 VikError::raiseWarning('', 'Another conditional text with the same special tag already exists');
15017 $app->redirect('index.php?option=com_vikbooking&task=newcondtext');
15018 exit;
15019 }
15020
15021 $data = new stdClass;
15022 $data->name = $condtextname;
15023 $data->token = $condtexttkn;
15024 $data->rules = json_encode($rules_list);
15025 $data->msg = $msg;
15026 $data->lastupd = JDate::getInstance()->toSql();
15027 $data->debug = $debug;
15028
15029 $dbo->insertObject('#__vikbooking_condtexts', $data, 'id');
15030
15031 if (isset($data->id)) {
15032 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
15033 }
15034
15035 if (!$stay || !isset($data->id)) {
15036 $this->cancelcondtext();
15037 exit;
15038 }
15039
15040 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $data->id);
15041 }
15042
15043 public function updatecondtext()
15044 {
15045 if (!JSession::checkToken()) {
15046 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15047 }
15048
15049 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
15050 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15051 }
15052
15053 $this->_doUpdateCondText();
15054 }
15055
15056 public function updatecondtextstay()
15057 {
15058 if (!JSession::checkToken()) {
15059 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15060 }
15061
15062 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
15063 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15064 }
15065
15066 $this->_doUpdateCondText(true);
15067 }
15068
15069 private function _doUpdateCondText($stay = false)
15070 {
15071 $dbo = JFactory::getDbo();
15072 $app = JFactory::getApplication();
15073 $rules_helper = VikBooking::getConditionalRulesInstance();
15074 $rules_list = $rules_helper->composeRulesParamsFromRequest();
15075
15076 $pwhere = VikRequest::getInt('where', '', 'request');
15077 $condtextname = VikRequest::getString('condtextname', '', 'request');
15078 $condtexttkn = VikRequest::getString('condtexttkn', '', 'request');
15079 $msg = VikRequest::getString('msg', '', 'request', VIKREQUEST_ALLOWRAW);
15080 $debug = VikRequest::getInt('debug', 0, 'request');
15081 if (empty($condtextname)) {
15082 $condtextname = date('Y-m-dHis');
15083 $condtexttkn = '{condition: ' . date('YmdHis') . '}';
15084 }
15085
15086 $existing_tokens = $rules_helper->getSpecialTags();
15087 if (count($existing_tokens) && isset($existing_tokens[$condtexttkn]) && ($existing_tokens[$condtexttkn]['id'] != $pwhere)) {
15088 VikError::raiseWarning('', 'Another conditional text with the same special tag already exists (' . $existing_tokens[$condtexttkn]['name'] . ')');
15089 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $pwhere);
15090 exit;
15091 }
15092
15093 $data = new stdClass;
15094 $data->id = $pwhere;
15095 $data->name = $condtextname;
15096 $data->token = $condtexttkn;
15097 $data->rules = json_encode($rules_list);
15098 $data->msg = $msg;
15099 $data->lastupd = JDate::getInstance()->toSql();
15100 $data->debug = $debug;
15101
15102 $dbo->updateObject('#__vikbooking_condtexts', $data, 'id');
15103
15104 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
15105
15106 if (!$stay) {
15107 $this->cancelcondtext();
15108 exit;
15109 }
15110
15111 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $data->id);
15112 }
15113
15114 public function removecondtext()
15115 {
15116 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
15117 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15118 }
15119
15120 $dbo = JFactory::getDbo();
15121 $ids = VikRequest::getVar('cid', array());
15122
15123 VikBooking::getConditionalRulesInstance(true);
15124 $templates = VikBookingHelperConditionalRules::getTemplateFilesPaths();
15125
15126 foreach ($ids as $d) {
15127 $q = "SELECT `token` FROM `#__vikbooking_condtexts` WHERE `id`=" . (int)$d . ";";
15128 $dbo->setQuery($q);
15129 $dbo->execute();
15130 if (!$dbo->getNumRows()) {
15131 continue;
15132 }
15133 $special_tag = $dbo->loadResult();
15134
15135 // remove the token from each template file if it was used before
15136 if (!empty($special_tag)) {
15137 // remove token from all template files
15138 foreach ($templates as $tkey => $tpath) {
15139 // get requested file content
15140 $fcontent = VikBookingHelperConditionalRules::getTemplateFileCode($tkey);
15141 if (empty($fcontent) || !is_string($fcontent)) {
15142 break;
15143 }
15144 // remove tag from code content
15145 $fcontent = str_replace($special_tag, '', $fcontent);
15146 // update the file code
15147 VikBookingHelperConditionalRules::writeTemplateFileCode($tkey, $fcontent);
15148 }
15149 }
15150
15151 // delete the record
15152 $q = "DELETE FROM `#__vikbooking_condtexts` WHERE `id`=" . (int)$d . ";";
15153 $dbo->setQuery($q);
15154 $dbo->execute();
15155 }
15156
15157 $this->cancelcondtext();
15158 }
15159
15160 /**
15161 * AJAX endpoint to update one template file with the given tag or styles.
15162 * A JSON response will be echoed by exiting the process.
15163 */
15164 public function condtext_update_tmpl()
15165 {
15166 VikBooking::getConditionalRulesInstance(true);
15167
15168 $tagaction = VikRequest::getString('tagaction', '', 'request');
15169 $tag = VikRequest::getString('tag', '', 'request');
15170 $file = VikRequest::getString('file', '', 'request', VIKREQUEST_ALLOWRAW);
15171 $newcontent = VikRequest::getString('newcontent', '', 'request', VIKREQUEST_ALLOWRAW);
15172 $custom_classes = VikRequest::getVar('custom_classes', array(), 'request', 'array');
15173
15174 $allowed_actions = array(
15175 'add',
15176 'remove',
15177 'styles',
15178 'restore',
15179 );
15180
15181 if (empty($tagaction) || empty($file) || !in_array($tagaction, $allowed_actions)) {
15182 throw new Exception("Invalid request submitted", 500);
15183 }
15184
15185 if (in_array($tagaction, array('add', 'remove')) && empty($tag)) {
15186 throw new Exception("Invalid request submitted - missing tag", 500);
15187 }
15188
15189 if (in_array($tagaction, array('add', 'styles')) && empty($newcontent)) {
15190 throw new Exception("Invalid request submitted - missing new HTML content", 500);
15191 }
15192
15193 if ($tagaction == 'styles' && (!is_array($custom_classes) || !count($custom_classes))) {
15194 throw new Exception("No custom CSS classes to parse", 500);
15195 }
15196
15197 if ($tagaction == 'restore') {
15198 // immediately restore the requested file to avoid script interruptions
15199 VikBookingHelperConditionalRules::restoreTemplateFileCode($file);
15200 }
15201
15202 // get requested file content
15203 $fcontent = VikBookingHelperConditionalRules::getTemplateFileCode($file);
15204 if (empty($fcontent) || !is_string($fcontent)) {
15205 throw new Exception("File not found or its code is unreadable", 404);
15206 }
15207
15208 if ($tagaction == 'remove') {
15209 // remove tag from code content
15210 $fcontent = str_replace($tag, '', $fcontent);
15211 } elseif ($tagaction == 'add') {
15212 // add tag to code content in the same exact position
15213 $fcontent = VikBookingHelperConditionalRules::addTagByComparingSources($tag, $file, $newcontent, $fcontent);
15214 } elseif ($tagaction == 'styles') {
15215 // apply the same styling rules
15216 $fcontent = VikBookingHelperConditionalRules::addStylesByComparingSources($custom_classes, $file, $newcontent, $fcontent);
15217 }
15218
15219 // update the file code
15220 $res = VikBookingHelperConditionalRules::writeTemplateFileCode($file, $fcontent);
15221
15222 if (!$res) {
15223 throw new Exception("Could not update the source code of the template file", 500);
15224 }
15225
15226 // parse new HTML content
15227 $newhtmls = VikBookingHelperConditionalRules::getTemplateFilesContents($file);
15228 if (!is_array($newhtmls) || !isset($newhtmls[$file])) {
15229 throw new Exception("Could not parse new template file content", 404);
15230 }
15231
15232 // trigger backup/mirroring, if available
15233 if (VBOPlatformDetection::isWordPress()) {
15234 VikBookingUpdateManager::storeTemplateContent($file, $newhtmls[$file]);
15235 }
15236
15237 // build output
15238 $output = new stdClass;
15239 $output->newhtml = $newhtmls[$file];
15240 $output->log = VikBookingHelperConditionalRules::getEditingLog();
15241
15242 echo json_encode($output);
15243 exit;
15244 }
15245
15246 /**
15247 * AJAX endpoint to invoke methods of the geocoding helper.
15248 */
15249 public function geocoding_endpoint()
15250 {
15251 $geo = VikBooking::getGeocodingInstance();
15252 $callback = VikRequest::getString('callback', '', 'request');
15253
15254 if (empty($callback) || !method_exists($geo, $callback) || !is_callable(array($geo, $callback))) {
15255 throw new Exception("Callback not available", 403);
15256 }
15257
15258 // invoke requested method
15259 $res = $geo->{$callback}();
15260
15261 // prepare response
15262 $response = new stdClass;
15263 $response->{$callback} = $res;
15264
15265 echo json_encode($response);
15266 exit;
15267 }
15268
15269 public function refundtn()
15270 {
15271 //modal box, so we do not set menu or footer
15272
15273 VikRequest::setVar('view', VikRequest::getCmd('view', 'refundtn'));
15274
15275 parent::display();
15276 }
15277
15278 public function do_refundtn()
15279 {
15280 $dbo = JFactory::getDbo();
15281 $app = JFactory::getApplication();
15282
15283 $bid = VikRequest::getInt('bid', 0, 'request');
15284 $amount = VikRequest::getFloat('amount', 0, 'request');
15285 $refund_reason = VikRequest::getString('refund_reason', '', 'request');
15286 $tmpl = VikRequest::getString('tmpl', '', 'request');
15287 $nav_suffix = $tmpl == 'component' ? '&tmpl=component' : '';
15288
15289 $currencysymb = VikBooking::getCurrencySymb();
15290
15291 if (empty($bid) || $amount <= 0) {
15292 VikError::raiseWarning('', JText::translate('VBO_PLEASE_FILL_FIELDS'));
15293 $app->redirect('index.php?option=com_vikbooking');
15294 exit;
15295 }
15296
15297 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $bid . " AND `status`!='standby';";
15298 $dbo->setQuery($q);
15299 $row = $dbo->loadAssoc();
15300 if (!$row) {
15301 VikError::raiseWarning('', 'Booking not found');
15302 $app->redirect('index.php?option=com_vikbooking');
15303 exit;
15304 }
15305
15306 // get booking history instance
15307 $history_obj = VikBooking::getBookingHistoryInstance();
15308 $history_obj->setBid($row['id']);
15309
15310 // get payment information
15311 $payment = VikBooking::getPayment($row['idpayment']);
15312 $tn_driver = is_array($payment) ? $payment['file'] : null;
15313
15314 // transaction data validation callback
15315 $tn_data_callback = function($data) use ($tn_driver) {
15316 return (is_object($data) && isset($data->driver) && basename($data->driver, '.php') == basename($tn_driver, '.php'));
15317 };
15318 // get previous transactions
15319 $prev_tn_data = $history_obj->getEventsWithData(array('P0', 'PN'), $tn_data_callback);
15320
15321 if (!is_array($prev_tn_data) || !count($prev_tn_data)) {
15322 // no previous transactions found
15323 VikError::raiseWarning('', 'No previous transactions found, unable to issue the refund');
15324 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15325 exit;
15326 }
15327
15328 // push refund information for the payment gateway
15329 $row['total_to_refund'] = $amount;
15330 $row['transaction'] = $prev_tn_data;
15331 $row['refund_reason'] = $refund_reason;
15332
15333 // push the transaction currency information
15334 $row['transaction_currency'] = VikBooking::getCurrencyCodePp();
15335
15336 /**
15337 * Trigger event to allow third-party plugins to manipulate the transaction data.
15338 *
15339 * @since 1.18.5 (J) - 1.8.5 (WP)
15340 */
15341 VBOFactory::getPlatform()->getDispatcher()->trigger('onInitRefundTransaction', [&$row, &$payment['params']]);
15342
15343 if (VBOPlatformDetection::isWordPress()) {
15344 /**
15345 * @wponly The payment gateway is loaded
15346 * through the apposite dispatcher.
15347 */
15348 JLoader::import('adapter.payment.dispatcher');
15349 $obj = JPaymentDispatcher::getInstance('vikbooking', $payment['file'], $row, $payment['params']);
15350 } else {
15351 /**
15352 * @joomlaonly The Payment Factory library will invoke the gateway.
15353 *
15354 * @since 1.14.3
15355 */
15356 require_once VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'payments' . DIRECTORY_SEPARATOR . 'libraries' . DIRECTORY_SEPARATOR . 'factory.php';
15357 $obj = VBOPaymentFactory::getPaymentInstance($payment['file'], $row, $payment['params']);
15358 }
15359
15360 if (!method_exists($obj, 'isRefundSupported') || !$obj->isRefundSupported()) {
15361 // refund not supported
15362 VikError::raiseWarning('', 'The selected payment method does not support refunds');
15363 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15364 exit;
15365 }
15366
15367 // perform the refund transaction
15368 $array_result = $obj->refund();
15369
15370 if ($array_result['verified'] != 1) {
15371 // raise warning by getting the message
15372 if (!empty($array_result['log']) && is_string($array_result['log'])) {
15373 VikError::raiseWarning('', $array_result['log']);
15374 } else {
15375 VikError::raiseWarning('', 'Operation failed');
15376 }
15377 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15378 exit;
15379 }
15380
15381 /**
15382 * New payment plugins can return the total amount refunded ('tot_paid').
15383 *
15384 * @since 1.15.4 (J) - 1.5.10 (WP)
15385 */
15386 if (!empty($array_result['tot_paid'])) {
15387 // overwrite the requested amount with the returned one
15388 $amount = (float)$array_result['tot_paid'];
15389 }
15390
15391 /**
15392 * The history event extra data will contain the "amount_paid" (refunded).
15393 *
15394 * @since 1.16.9 (J) - 1.6.9 (WP)
15395 */
15396 $history_obj->setExtraData([
15397 'amount_paid' => $amount,
15398 ]);
15399
15400 // update total paid, total and refund columns for the booking
15401 $booking = new stdClass;
15402 $booking->id = $row['id'];
15403 if ($row['totpaid'] > 0) {
15404 $booking->totpaid = $row['totpaid'] - $amount;
15405 }
15406 if ($row['total'] > 0) {
15407 $booking->total = $row['total'] - $amount;
15408 }
15409 $booking->refund = (float)$row['refund'] + $amount;
15410 // update record in db
15411 $dbo->updateObject('#__vikbooking_orders', $booking, 'id');
15412
15413 // store the refund event
15414 $event_descr = [
15415 '(' . $payment['name'] . ')',
15416 $refund_reason,
15417 $currencysymb . ' ' . VikBooking::numberFormat($amount),
15418 ];
15419 $history_obj->store('RF', implode("\n", $event_descr));
15420
15421 // display success message and redirect
15422 $app->enqueueMessage(JText::translate('VBO_REFUND_SUCCESS'));
15423 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . '&success=1' . $nav_suffix);
15424 exit;
15425 }
15426
15427 /**
15428 * AJAX upload endpoint for media files.
15429 *
15430 * @return void
15431 *
15432 * @throws Exception
15433 *
15434 * @since 1.15.0 (J) - 1.5.0 (WP)
15435 */
15436 public function upload_media_file()
15437 {
15438 $input = JFactory::getApplication()->input;
15439
15440 // allowed types
15441 $type = $input->getString('type', '');
15442 $mask = 'png,apng,jpg,jpeg,bmp,heic,webp,gif,ico,svg';
15443
15444 if ($type != 'image') {
15445 $mask .= ',zip,rar,pdf,doc,docx,rtf,odt,pages,xls,xlsx,csv,ods,numbers,txt,md';
15446 }
15447
15448 // response object
15449 $result = new stdClass;
15450 $result->status = 0;
15451
15452 try
15453 {
15454 // get file from request
15455 $file = $input->files->get('file', array(), 'array');
15456
15457 // try to upload the file
15458 $result = VikBooking::uploadFileFromRequest($file, VBO_MEDIA_PATH, $mask);
15459 $result->status = 1;
15460
15461 $result->size = JHtml::fetch('number.bytes', filesize($result->path), 'auto', 0);
15462 $result->url = str_replace(DIRECTORY_SEPARATOR, '/', str_replace(VBO_MEDIA_PATH . DIRECTORY_SEPARATOR, VBO_MEDIA_URI, $result->path));
15463 }
15464 catch (Exception $e)
15465 {
15466 $result->error = $e->getMessage();
15467 $result->code = $e->getCode();
15468 }
15469
15470 echo json_encode($result);
15471 exit;
15472 }
15473
15474 /**
15475 * AJAX endpoint to invoke a report object's method.
15476 *
15477 * @return void
15478 *
15479 * @since 1.15.0 (J) - 1.5.0 (WP)
15480 * @since 1.18.6 (J) - 1.8.6 (WP) added support for "call_args".
15481 */
15482 public function invoke_report()
15483 {
15484 $app = JFactory::getApplication();
15485
15486 $report_name = $app->input->getString('report', '');
15487 $report_call = $app->input->getString('call', '');
15488 $call_args = $app->input->get('call_args', [], 'array');
15489 $params = $app->input->get('params', [], 'array');
15490
15491 if (empty($report_name)) {
15492 VBOHttpDocument::getInstance($app)->close(400, 'Missing report name');
15493 }
15494
15495 if (empty($report_call)) {
15496 VBOHttpDocument::getInstance($app)->close(400, 'Missing report call');
15497 }
15498
15499 // get requested report instance
15500 $report = VikBooking::getReportInstance($report_name);
15501 if (!$report) {
15502 VBOHttpDocument::getInstance($app)->close(404, 'Report not found');
15503 }
15504
15505 if (!method_exists($report, $report_call) || !is_callable(array($report, $report_call))) {
15506 VBOHttpDocument::getInstance($app)->close(403, sprintf('Cannot call [%s] on report', $report_call));
15507 }
15508
15509 try {
15510 // call on report's method
15511 if ($call_args) {
15512 $result = call_user_func_array([$report, $report_call], $call_args);
15513 } else {
15514 $result = $report->{$report_call}($params);
15515 }
15516 } catch (Exception $e) {
15517 VBOHttpDocument::getInstance($app)->close($e->getCode() ?: 500, $e->getMessage());
15518 }
15519
15520 if (is_null($result)) {
15521 VBOHttpDocument::getInstance($app)->close(400, 'Null response');
15522 }
15523
15524 if (is_scalar($result)) {
15525 // wrap result within an array for a JSON encoded response
15526 VBOHttpDocument::getInstance($app)->json([$result]);
15527 }
15528
15529 // output the JSON encoded array/object returned
15530 VBOHttpDocument::getInstance($app)->json($result);
15531 }
15532
15533 /**
15534 * Handles requests for the multitask widgets panel.
15535 *
15536 * @see this is an AJAX endpoint.
15537 *
15538 * @since 1.15.0 (J) - 1.5.0 (WP)
15539 * @since 1.16.5 (J) - 1.6.5 (WP) widgets are rendered within a try-catch statement.
15540 */
15541 public function exec_multitask_widgets()
15542 {
15543 if (!JSession::checkToken()) {
15544 // missing CSRF-proof token
15545 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15546 }
15547
15548 $call = VikRequest::getString('call', '', 'request');
15549 $call_args = VikRequest::getVar('call_args', array(), 'request', 'array');
15550
15551 if (empty($call)) {
15552 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget Callback');
15553 }
15554
15555 // invoke admin widgets helper
15556 $widgets_helper = VikBooking::getAdminWidgetsInstance();
15557
15558 if (!method_exists($widgets_helper, $call) || !is_callable(array($widgets_helper, $call))) {
15559 VBOHttpDocument::getInstance()->close(403, 'Admin Widgets Callback not found or not callable');
15560 }
15561
15562 try {
15563 // invoke the helper's method and get the value returned
15564 if (is_array($call_args) && count($call_args)) {
15565 $result = call_user_func_array(array($widgets_helper, $call), $call_args);
15566 } else {
15567 $result = $widgets_helper->{$call}();
15568 }
15569 } catch (Throwable $e) {
15570 VBOHttpDocument::getInstance()->close($e->getCode() ?: 500, $e->getMessage());
15571 } catch (Exception $e) {
15572 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
15573 }
15574
15575 // prepare response object with the result property
15576 $response = new stdClass;
15577 $response->result = $result;
15578
15579 // output the JSON response and exit
15580 VBOHttpDocument::getInstance()->json($response);
15581 }
15582
15583 /**
15584 * Handles requests for displaying a browser notification being dispatched.
15585 *
15586 * @see this is an AJAX endpoint.
15587 *
15588 * @since 1.15.0 (J) - 1.5.0 (WP)
15589 */
15590 public function notification_displayer()
15591 {
15592 $payload_str = VikRequest::getString('payload', '', 'request', VIKREQUEST_ALLOWRAW);
15593
15594 if (empty($payload_str)) {
15595 VBOHttpDocument::getInstance()->close(500, 'Empty notification payload');
15596 }
15597
15598 // attempt to decode the notification payload
15599 $payload = json_decode($payload_str);
15600
15601 if (!is_object($payload)) {
15602 VBOHttpDocument::getInstance()->close(500, 'Could not decode notification payload: ' . $payload_str);
15603 }
15604
15605 // get notification displayer for this type of notification
15606 $displayer = VBONotificationBuilder::getInstance($payload)->getDisplayer();
15607 if (!$displayer) {
15608 VBOHttpDocument::getInstance()->close(500, 'Could not build notification display data from payload: ' . $payload_str);
15609 }
15610
15611 // compose the notification display data object
15612 try {
15613 $notif_data = $displayer->getData();
15614 if (!$notif_data) {
15615 throw new Exception('Error building the notification display data', 500);
15616 }
15617 } catch (Exception $e) {
15618 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
15619 }
15620
15621 // output the JSON response and exit
15622 VBOHttpDocument::getInstance()->json($notif_data);
15623 }
15624
15625 /**
15626 * Handles requests for watching widgets data and getting
15627 * new events to trigger browser notifications.
15628 *
15629 * @see this is an AJAX endpoint.
15630 *
15631 * @since 1.15.0 (J) - 1.5.0 (WP)
15632 * @since 1.16.8 (J) - 1.6.8 (WP) introduced notification events.
15633 */
15634 public function widgets_watch_data()
15635 {
15636 if (!JSession::checkToken()) {
15637 // missing CSRF-proof token
15638 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15639 }
15640
15641 $app = JFactory::getApplication();
15642
15643 $watch_data_str = $app->input->get('watch_data', '', 'raw');
15644 $pushed_data_str = $app->input->get('pushed_data', '[]', 'raw');
15645
15646 if (empty($watch_data_str)) {
15647 VBOHttpDocument::getInstance()->close(500, 'Empty watch-data payload');
15648 }
15649
15650 // attempt to decode the watch-data payload
15651 $watch_data = json_decode($watch_data_str, true);
15652
15653 if (!$watch_data) {
15654 VBOHttpDocument::getInstance()->close(500, 'Could not decode watch-data payload: ' . $watch_data_str);
15655 }
15656
15657 // check if any pushed data was set
15658 $pushed_data = (array)json_decode($pushed_data_str, true);
15659
15660 // container for new notifications
15661 $notifs_pool = [];
15662
15663 // container for the events data to dispatch
15664 $events_pool = [];
15665
15666 // get admin widgets helper
15667 $widgets_helper = VikBooking::getAdminWidgetsInstance();
15668
15669 foreach ($watch_data as $widget_id => $data) {
15670 // invoke admin widget (with no pre-loading)
15671 $widget_instance = $widgets_helper->getWidget($widget_id);
15672 if (!$widget_instance) {
15673 continue;
15674 }
15675
15676 // build the widget watch data object
15677 $widget_watch_data = VBONotificationWatchdata::getInstance($data)->setPushedData($pushed_data);
15678
15679 // check if the widget needs to emit browser notifications
15680 list($watch_next, $notifications) = $widget_instance->getNotifications($widget_watch_data);
15681
15682 // check if the widget needs to emit JavaScript events
15683 $events = $widget_instance->getNotificationEvents($widget_watch_data);
15684
15685 if ($watch_next) {
15686 // update next watch-data object for this widget
15687 $watch_data[$widget_id] = $watch_next;
15688 }
15689
15690 if (is_array($notifications) && $notifications) {
15691 // merge notifications
15692 $notifs_pool = array_merge($notifs_pool, $notifications);
15693 }
15694
15695 if (is_array($events) && $events) {
15696 // push notification events for this widget
15697 $events_pool[] = $events;
15698 }
15699 }
15700
15701 // build the response object
15702 $response = new stdClass;
15703 $response->watch_data = $watch_data;
15704 $response->notifications = $notifs_pool;
15705 $response->events = $events_pool;
15706
15707 // output the JSON response and exit
15708 VBOHttpDocument::getInstance()->json($response);
15709 }
15710
15711 /**
15712 * Outputs a list of CSS assets required to render the admin widgets
15713 * externally from Vik Booking. Useful i.e. to Vik Channel Manager.
15714 *
15715 * @see this is an AJAX endpoint.
15716 *
15717 * @since 1.16.0 (J) - 1.6.0 (WP)
15718 */
15719 public function widgets_get_assets()
15720 {
15721 // list of needed CSS asset details
15722 $assets_pool = [];
15723
15724 // appearance preference assets (one or none)
15725 $app_pref_asset = VikBooking::loadAppearancePreferenceAssets($get_info = true);
15726
15727 if (VBOPlatformDetection::isWordPress()) {
15728 // WordPress (main CSS)
15729 $assets_pool[] = [
15730 'rel' => 'stylesheet',
15731 'id' => 'vbo-style-css',
15732 'href' => VIKBOOKING_ADMIN_ASSETS_URI . 'vikbooking.css?ver=' . VIKBOOKING_SOFTWARE_VERSION,
15733 'media' => 'all',
15734 ];
15735
15736 if (is_array($app_pref_asset) && !empty($app_pref_asset['href'])) {
15737 // appearance preference CSS
15738 $assets_pool[] = [
15739 'rel' => 'stylesheet',
15740 'id' => (!empty($app_pref_asset['id']) ? $app_pref_asset['id'] : rand()),
15741 'href' => $app_pref_asset['href'] . '?ver=' . VIKBOOKING_SOFTWARE_VERSION,
15742 'media' => 'all',
15743 ];
15744 }
15745 } else {
15746 // Joomla (main CSS)
15747 $assets_pool[] = [
15748 'rel' => 'stylesheet',
15749 'id' => 'vbo-style-css',
15750 'href' => VBO_ADMIN_URI . 'vikbooking.css?' . VIKBOOKING_SOFTWARE_VERSION,
15751 'media' => 'all',
15752 ];
15753
15754 if (is_array($app_pref_asset) && !empty($app_pref_asset['href'])) {
15755 // appearance preference CSS
15756 $assets_pool[] = [
15757 'rel' => 'stylesheet',
15758 'id' => (!empty($app_pref_asset['id']) ? $app_pref_asset['id'] : rand()),
15759 'href' => $app_pref_asset['href'] . '?' . VIKBOOKING_SOFTWARE_VERSION,
15760 'media' => 'all',
15761 ];
15762 }
15763 }
15764
15765 // output the JSON response and exit
15766 VBOHttpDocument::getInstance()->json($assets_pool);
15767 }
15768 }
15769