PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.6
VikBooking Hotel Booking Engine & PMS v1.8.6
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 1.7.3 All 36 releases
vikbooking / admin / controller.php

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

15,731 lines 589.2 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 // output the error
10985 VBOHttpDocument::getInstance()->close(500, '2 - ' . JText::translate('VBOVWGETBKERRMISSDATA'));
10986 }
10987
10988 // output the JSON encoded response and exit
10989 VBOHttpDocument::getInstance()->json($booking_infos);
10990 }
10991
10992 /**
10993 * AJAX endpoint to switch a booking room index.
10994 *
10995 * @return void
10996 *
10997 * @since 1.18.2 (J) - 1.8.2 (WP) method refactored.
10998 */
10999 public function switchRoomIndex()
11000 {
11001 if (!JSession::checkToken()) {
11002 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
11003 }
11004
11005 $app = JFactory::getApplication();
11006 $dbo = JFactory::getDbo();
11007
11008 $bid = $app->input->getInt('bid', 0);
11009 $rid = $app->input->getInt('rid', 0);
11010 $old_rindex = $app->input->getInt('old_rindex', 0);
11011 $new_rindex = $app->input->getInt('new_rindex', 0);
11012 $is_tmp_row = $app->input->getBool('is_tmp_row', false);
11013 $is_from_tmp_row = $app->input->getBool('is_from_tmp_row', false);
11014
11015 if (empty($bid) || empty($rid) || (empty($old_rindex) && !$is_from_tmp_row) || (empty($new_rindex) && !$is_tmp_row) || $new_rindex < 0) {
11016 // abort for missing or invalid room indexes
11017 VBOHttpDocument::getInstance($app)->close(200, 'e4j.error.#1 Missing Data');
11018 }
11019
11020 // fetch the current booking room record
11021 $dbo->setQuery(
11022 $dbo->getQuery(true)
11023 ->select('*')
11024 ->from($dbo->qn('#__vikbooking_ordersrooms'))
11025 ->where($dbo->qn('idorder') . ' = ' . $bid)
11026 ->where($dbo->qn('idroom') . ' = ' . $rid)
11027 ->where($dbo->qn('roomindex') . (empty($old_rindex) && $is_from_tmp_row ? ' IS NULL' : ' = ' . $old_rindex))
11028 ->order($dbo->qn('id') . ' ASC')
11029 );
11030 $roomRow = $dbo->loadAssoc();
11031
11032 if (!$roomRow) {
11033 // abort for record not found
11034 VBOHttpDocument::getInstance($app)->close(200, 'e4j.error.#2 Record not found');
11035 }
11036
11037 // update booking room record by switching sub-unit
11038 $dbo->setQuery(
11039 $dbo->getQuery(true)
11040 ->update($dbo->qn('#__vikbooking_ordersrooms'))
11041 ->set($dbo->qn('roomindex') . ' = ' . (empty($new_rindex) && $is_tmp_row ? 'NULL' : $new_rindex))
11042 ->where($dbo->qn('id') . ' = ' . (int) $roomRow['id'])
11043 );
11044 $dbo->execute();
11045
11046 // process completed
11047 VBOHttpDocument::getInstance($app)->close(200, 'e4j.ok');
11048 }
11049
11050 public function searchcustomer()
11051 {
11052 // to be called via ajax
11053 $dbo = JFactory::getDbo();
11054
11055 $kw = VikRequest::getString('kw', '', 'request');
11056 $nopin = VikRequest::getInt('nopin', '', 'request');
11057 $email = VikRequest::getInt('email', 0, 'request');
11058 $selector = VikRequest::getString('selector', 'vbo-custsearchres-entry', 'request');
11059 $no_script = VikRequest::getInt('no_script', 0, 'request');
11060
11061 if (!strlen($kw)) {
11062 VBOHttpDocument::getInstance()->close(200, '');
11063 }
11064
11065 if ($nopin > 0) {
11066 //page all bookings
11067 $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;";
11068 } elseif ($email > 0) {
11069 // page calendar for checking if an email exists
11070 $q = "SELECT `first_name`, `last_name`, `email` FROM `#__vikbooking_customers` WHERE `email`=".$dbo->quote($kw).";";
11071 } else {
11072 //page calendar
11073 $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;";
11074 }
11075 $dbo->setQuery($q);
11076 $customers = $dbo->loadAssocList();
11077
11078 if (!$customers) {
11079 VBOHttpDocument::getInstance()->close(200, '');
11080 }
11081
11082 if ($email > 0) {
11083 VBOHttpDocument::getInstance()->json($customers[0]);
11084 }
11085
11086 $cust_old_fields = array();
11087 $cstring_search = '<div class="vbo-custsearchres-inner">' . "\n";
11088 foreach ($customers as $k => $v) {
11089 $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";
11090 $cstring_search .= '<span class="vbo-custsearchres-cflag">';
11091 if (!empty($v['pic'])) {
11092 $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";
11093 } elseif (is_file(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$v['country'].'.png')) {
11094 $cstring_search .= '<img src="'.VBO_ADMIN_URI.'resources/countries/'.$v['country'].'.png'.'" title="'.htmlspecialchars($v['country']).'" class="vbo-country-flag"/>'."\n";
11095 } else {
11096 $cstring_search .= '<i class="' . VikBookingIcons::i('globe') . '"></i>';
11097 }
11098 $cstring_search .= '</span>';
11099 $cstring_search .= '<span class="vbo-custsearchres-name" title="'.htmlspecialchars($v['email']).'">'.$v['first_name'].' '.$v['last_name'].'</span>'."\n";
11100 if (!($nopin > 0)) {
11101 $cstring_search .= '<span class="vbo-custsearchres-pin">'.$v['pin'].'</span>'."\n";
11102 }
11103 $cstring_search .= '</div>'."\n";
11104 if (!empty($v['cfields'])) {
11105 $oldfields = json_decode($v['cfields'], true);
11106 if (is_array($oldfields) && count($oldfields)) {
11107 $cust_old_fields[$v['id']] = $oldfields;
11108 }
11109 }
11110 }
11111 $cstring_search .= '</div>'."\n";
11112
11113 /**
11114 * Add the necessary JS code for the arrow navigation.
11115 */
11116 $cstring_search_js = '<script type="text/javascript">';
11117 $cstring_search_js .= '
11118 var vboCust = jQuery(".' . $selector . '");
11119 var vboCustSelected = null;
11120 var vboCustomerNavigationFn = (e) => {
11121 if (e.which === 40) {
11122 if (vboCustSelected) {
11123 vboCustSelected.removeClass("' . $selector . '-highligthed");
11124 next = vboCustSelected.next();
11125 if (next.length > 0) {
11126 vboCustSelected = next.addClass("' . $selector . '-highligthed");
11127 } else {
11128 vboCustSelected = vboCust.eq(0).addClass("' . $selector . '-highligthed");
11129 }
11130 } else {
11131 vboCustSelected = vboCust.eq(0).addClass("' . $selector . '-highligthed");
11132 }
11133 } else if (e.which === 38) {
11134 if (vboCustSelected) {
11135 vboCustSelected.removeClass("' . $selector . '-highligthed");
11136 next = vboCustSelected.prev();
11137 if (next.length > 0) {
11138 vboCustSelected = next.addClass("' . $selector . '-highligthed");
11139 } else {
11140 vboCustSelected = vboCust.last().addClass("' . $selector . '-highligthed");
11141 }
11142 } else {
11143 vboCustSelected = vboCust.last().addClass("' . $selector . '-highligthed");
11144 }
11145 } else if (e.which === 13) {
11146 if (vboCustSelected) {
11147 vboCustSelected.trigger("click");
11148 }
11149 }
11150 };
11151 jQuery(window).off("keydown", vboCustomerNavigationFn);
11152 jQuery(window).keydown(vboCustomerNavigationFn);
11153 document.addEventListener("vbo-search-customers-navigation-dismissed", (e) => {
11154 jQuery(window).off("keydown", vboCustomerNavigationFn);
11155 })
11156 jQuery(".' . $selector . '").off("hover");
11157 jQuery(".' . $selector . '").hover(function() {
11158 if (vboCustSelected) {
11159 vboCustSelected.removeClass("' . $selector . '-highligthed");
11160 vboCustSelected = null;
11161 }
11162 vboCustSelected = jQuery(this).addClass("' . $selector . '-highligthed");
11163 }, function() {
11164 if (vboCustSelected) {
11165 vboCustSelected.removeClass("' . $selector . '-highligthed");
11166 vboCustSelected = null;
11167 }
11168 jQuery(this).removeClass("' . $selector . '-highligthed");
11169 });';
11170 $cstring_search_js .= '</script>';
11171
11172 if (!$no_script) {
11173 // append JS
11174 $cstring_search .= $cstring_search_js;
11175 }
11176
11177 VBOHttpDocument::getInstance()->json([($nopin > 0 ? '' : $cust_old_fields), $cstring_search]);
11178 }
11179
11180 public function sharesignaturelink() {
11181 //to be called via ajax
11182 $dbo = JFactory::getDBO();
11183 $response = array(
11184 'status' => 0,
11185 'error' => 'Generic Error'
11186 );
11187 $pbid = VikRequest::getInt('bid', '', 'request');
11188 $phow = VikRequest::getString('how', '', 'request');
11189 $pto = VikRequest::getString('to', '', 'request');
11190 $pcustomer = VikRequest::getInt('customer', '', 'request');
11191 $cpin = VikBooking::getCPinIstance();
11192 $customer_info = $cpin->getCustomerByID($pcustomer);
11193 if (!empty($pbid) && !empty($phow) && !empty($pto) && count($customer_info) > 0) {
11194 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$pbid." AND `status`='confirmed' AND `checked` > 0;";
11195 $dbo->setQuery($q);
11196 $dbo->execute();
11197 if ($dbo->getNumRows() > 0) {
11198 $row = $dbo->loadAssoc();
11199
11200 $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'];
11201 if (VBOPlatformDetection::isWordPress()) {
11202 /**
11203 * @wponly Rewrite URI for front-end signature
11204 */
11205 $share_link = str_replace(JUri::root(), '', $share_link);
11206 $model = JModel::getInstance('vikbooking', 'shortcodes');
11207 $itemid = $model->all('post_id', $full = true);
11208 if (count($itemid)) {
11209 $share_link = JRoute::rewrite($share_link . "&Itemid={$itemid[0]->post_id}", false);
11210 }
11211 } else {
11212 /**
11213 * @joomlaonly
11214 */
11215 $best_menuitem_id = VikBooking::findProperItemIdType(['vikbooking', 'booking'], $row['lang']);
11216 if ($best_menuitem_id) {
11217 $share_base = str_replace(JUri::root(), '', $share_link);
11218 $share_link = VikBooking::externalroute($share_base, $xhtml = false, $best_menuitem_id);
11219 }
11220 }
11221
11222 $share_message = JText::sprintf('VBOSIGNSHAREMESSAGE', ltrim($customer_info['first_name'].' '.$customer_info['last_name']), $share_link, VikBooking::getFrontTitle());
11223 if ($phow == 'email') {
11224 $sender = VikBooking::getSenderMail();
11225 $vbo_app = VikBooking::getVboApplication();
11226 $vbo_app->sendMail($sender, $sender, $pto, $sender, JText::translate('VBOSIGNSHARESUBJECT'), $share_message, false);
11227 $response['status'] = 1;
11228 } elseif ($phow == 'sms') {
11229 $share_message = JText::sprintf('VBOSIGNSHAREMESSAGESMS', ltrim($customer_info['first_name'].' '.$customer_info['last_name']), $share_link, VikBooking::getFrontTitle());
11230 $sms_api = VikBooking::getSMSAPIClass();
11231 $sms_api_params = VikBooking::getSMSParams();
11232 if (!empty($sms_api) && file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api) && !empty($sms_api_params)) {
11233 require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
11234 $sms_obj = new VikSmsApi(array(), $sms_api_params);
11235 $response_obj = $sms_obj->sendMessage($pto, $share_message);
11236 if ($sms_obj->validateResponse($response_obj)) {
11237 $response['status'] = 1;
11238 } else {
11239 $response['error'] = $sms_obj->getLog();
11240 }
11241 } else {
11242 $response['error'] = 'No SMS Provider Configured';
11243 }
11244 } else {
11245 $response['error'] = 'Invalid Sending Method';
11246 }
11247 } else {
11248 $response['error'] = 'Invalid Booking ID';
11249 }
11250 } else {
11251 $response['error'] = 'Empty values';
11252 }
11253
11254 echo json_encode($response);
11255 exit;
11256 }
11257
11258 public function dayselectioncount() {
11259 //to be called via ajax
11260 $tsinit = VikRequest::getString('dinit', '', 'request');
11261 $tsend = VikRequest::getString('dend', '', 'request');
11262 if (strlen($tsinit) > 0 && strlen($tsend) > 0) {
11263 $ptsinit=VikBooking::getDateTimestamp($tsinit, '0', '0');
11264 $ptsend=VikBooking::getDateTimestamp($tsend, '23', '59');
11265 $diff = $ptsend - $ptsinit;
11266 if ($diff >= 172800) {
11267 $datef = VikBooking::getDateFormat(true);
11268 if ($datef=="%d/%m/%Y") {
11269 $df = 'd-m-Y';
11270 } else {
11271 $df = 'Y-m-d';
11272 }
11273 //minimum 2 days for excluding some days
11274 $daysdiff = floor($diff / 86400);
11275 $infoinit = getdate($ptsinit);
11276 $select = '';
11277 $select .= '<div style="display: inline-block;"><select name="excludeday[]" multiple="multiple" size="'.($daysdiff > 8 ? 8 : $daysdiff).'" id="vboexclusion">';
11278 for($i = 0; $i <= $daysdiff; $i++) {
11279 $ts = $i > 0 ? mktime(0, 0, 0, $infoinit['mon'], ((int)$infoinit['mday'] + $i), $infoinit['year']) : $ptsinit;
11280 $infots = getdate($ts);
11281 $optval = $infots['mon'].'-'.$infots['mday'].'-'.$infots['year'];
11282 $select .= '<option value="'.$optval.'">'.date($df, $ts).'</option>';
11283 }
11284 $select .= '</select></div>';
11285 //excluded days of the week
11286 if ($daysdiff >= 14) {
11287 $select .= '<div style="display: inline-block; margin-left: 40px;"><select name="excludewdays[]" multiple="multiple" size="8" id="excludewdays" onchange="vboExcludeWDays();">';
11288 $select .= '<optgroup label="'.JText::translate('VBOEXCLWEEKD').'">';
11289 $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>';
11290 $select .= '</optgroup>';
11291 $select .= '</select></div>';
11292 }
11293 //
11294 echo $select;
11295 } else {
11296 echo '';
11297 }
11298 } else {
11299 echo '';
11300 }
11301 exit;
11302 }
11303
11304 public function createcheckindoc()
11305 {
11306 if (!JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking')) {
11307 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
11308 }
11309
11310 $cid = VikRequest::getVar('cid', array(0));
11311 $id = $cid[0];
11312
11313 $dbo = JFactory::getDBO();
11314 $mainframe = JFactory::getApplication();
11315 $vbo_tn = VikBooking::getTranslator();
11316 $lang = JFactory::getLanguage();
11317 $ptmpl = VikRequest::getString('tmpl', '', 'request');
11318 $psignature = VikRequest::getString('signature', '', 'request', VIKREQUEST_ALLOWRAW);
11319 $ppad_width = VikRequest::getInt('pad_width', '', 'request');
11320 $ppad_ratio = VikRequest::getInt('pad_ratio', '', 'request');
11321 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$id." AND `status`='confirmed' AND `checked` > 0;";
11322 $dbo->setQuery($q);
11323 $row = $dbo->loadAssoc();
11324 if (!$row) {
11325 $mainframe->redirect('index.php');
11326 exit;
11327 }
11328 if (!empty($row['lang'])) {
11329 if ($lang->getTag() != $row['lang']) {
11330 if (VBOPlatformDetection::isWordPress()) {
11331 $lang->load('com_vikbooking', VIKBOOKING_LANG, $row['lang'], true);
11332 } else {
11333 $lang->load('com_vikbooking', JPATH_SITE, $row['lang'], true);
11334 $lang->load('com_vikbooking', JPATH_ADMINISTRATOR, $row['lang'], true);
11335 $lang->load('joomla', JPATH_SITE, $row['lang'], true);
11336 $lang->load('joomla', JPATH_ADMINISTRATOR, $row['lang'], true);
11337 }
11338 }
11339 if ($vbo_tn->getDefaultLang() != $row['lang']) {
11340 // force the translation to start because contents should be translated
11341 $vbo_tn::$force_tolang = $row['lang'];
11342 }
11343 }
11344 $customer = array();
11345 $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'].";";
11346 $dbo->setQuery($q);
11347 $dbo->execute();
11348 if ($dbo->getNumRows() > 0) {
11349 $customer = $dbo->loadAssoc();
11350 if (!empty($customer['country'])) {
11351 if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$customer['country'].'.png')) {
11352 $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"/>';
11353 }
11354 }
11355 }
11356 if (!(count($customer) > 0)) {
11357 VikError::raiseWarning('', JText::translate('VBOCHECKINERRNOCUSTOMER'));
11358 $mainframe->redirect('index.php?option=com_vikbooking&task=newcustomer&checkin=1&bid='.$row['id'].($ptmpl == 'component' ? '&tmpl=component' : ''));
11359 exit;
11360 }
11361 $customer['pax_data'] = !empty($customer['pax_data']) ? json_decode($customer['pax_data'], true) : array();
11362 //check if the signature has been submitted
11363 $signature_data = '';
11364 $cont_type = '';
11365 if (!empty($psignature)) {
11366 //check whether the format is accepted
11367 if (strpos($psignature, 'image/png') !== false || strpos($psignature, 'image/jpeg') !== false || strpos($psignature, 'image/svg') !== false) {
11368 $parts = explode(';base64,', $psignature);
11369 $cont_type_parts = explode('image/', $parts[0]);
11370 $cont_type = $cont_type_parts[1];
11371 if (!empty($parts[1])) {
11372 $signature_data = base64_decode($parts[1]);
11373 }
11374 }
11375 }
11376 if (!empty($signature_data)) {
11377 //write file
11378 $sign_fname = $row['id'].'_'.$row['sid'].'_'.$customer['id'].'.'.$cont_type;
11379 $filepath = VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'idscans' . DIRECTORY_SEPARATOR . $sign_fname;
11380 $fp = fopen($filepath, 'w+');
11381 $bytes = fwrite($fp, $signature_data);
11382 fclose($fp);
11383 if ($bytes !== false && $bytes > 0) {
11384 //update the signature in the DB
11385 $q = "UPDATE `#__vikbooking_customers_orders` SET `signature`=".$dbo->quote($sign_fname)." WHERE `idorder`=".(int)$row['id'].";";
11386 $dbo->setQuery($q);
11387 $dbo->execute();
11388 $customer['signature'] = $sign_fname;
11389 //resize image for screens with high resolution
11390 if ($ppad_ratio > 1) {
11391 $new_width = floor(($ppad_width / 2));
11392 $creativik = new vikResizer();
11393 $creativik->proportionalImage($filepath, $filepath, $new_width, $new_width);
11394 } else {
11395 /**
11396 * @wponly - trigger files mirroring
11397 */
11398 VikBookingLoader::import('update.manager');
11399 VikBookingUpdateManager::triggerUploadBackup($filepath);
11400 //
11401 }
11402 //
11403 } else {
11404 VikError::raiseWarning('', JText::translate('VBOERRSTORESIGNFILE'));
11405 }
11406 }
11407 //
11408 //generate PDF for check-in document by parsing the apposite template file
11409 $booking_rooms = array();
11410 $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'].";";
11411 $dbo->setQuery($q);
11412 $dbo->execute();
11413 if ($dbo->getNumRows() > 0) {
11414 $booking_rooms = $dbo->loadAssocList();
11415 if (!empty($row['lang'])) {
11416 $vbo_tn->translateContents($booking_rooms, '#__vikbooking_rooms', array('id' => 'idroom', 'room_name' => 'name'), array(), $row['lang']);
11417 }
11418 }
11419 if (!class_exists('TCPDF')) {
11420 require_once(VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "tcpdf" . DIRECTORY_SEPARATOR . 'tcpdf.php');
11421 }
11422 $usepdffont = is_file(VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "tcpdf" . DIRECTORY_SEPARATOR . "fonts" . DIRECTORY_SEPARATOR . "dejavusans.php") ? 'dejavusans' : 'helvetica';
11423
11424 /**
11425 * Trigger event to allow third party plugins to return a specific font name.
11426 *
11427 * @since 1.16.0 (J) - 1.6.0 (WP)
11428 */
11429 $custom_pdf_font = VBOFactory::getPlatform()->getDispatcher()->filter('onGetPdfFontNameVikBooking', [$usepdffont]);
11430 if (is_array($custom_pdf_font) && !empty($custom_pdf_font[0])) {
11431 $usepdffont = $custom_pdf_font[0];
11432 }
11433
11434 list($checkintpl, $pdfparams) = VikBooking::loadCheckinDocTmpl($row, $booking_rooms, $customer);
11435 $checkin_body = VikBooking::parseCheckinDocTemplate($checkintpl, $row, $booking_rooms, $customer);
11436
11437 // build the proper document SID for bc
11438 $doc_sid = $row['sid'] ?: $row['idorderota'] ?: '';
11439 $pdffname = $row['id'] . '_' . $doc_sid . '.pdf';
11440
11441 $pathpdf = VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "checkins" . DIRECTORY_SEPARATOR . "generated" . DIRECTORY_SEPARATOR . $pdffname;
11442 if (file_exists($pathpdf)) @unlink($pathpdf);
11443 $pdf_page_format = is_array($pdfparams['pdf_page_format']) ? $pdfparams['pdf_page_format'] : constant($pdfparams['pdf_page_format']);
11444 $pdf = new TCPDF(constant($pdfparams['pdf_page_orientation']), constant($pdfparams['pdf_unit']), $pdf_page_format, true, 'UTF-8', false);
11445 $pdf->SetTitle(JText::translate('VBOCHECKINDOCTITLE'));
11446 //Header for each page of the pdf
11447 if ($pdfparams['show_header'] == 1 && count($pdfparams['header_data']) > 0) {
11448 $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]);
11449 }
11450 //header and footer fonts
11451 $pdf->setHeaderFont(array($usepdffont, '', $pdfparams['header_font_size']));
11452 $pdf->setFooterFont(array($usepdffont, '', $pdfparams['footer_font_size']));
11453 //margins
11454 $pdf->SetMargins(constant($pdfparams['pdf_margin_left']), constant($pdfparams['pdf_margin_top']), constant($pdfparams['pdf_margin_right']));
11455 $pdf->SetHeaderMargin(constant($pdfparams['pdf_margin_header']));
11456 $pdf->SetFooterMargin(constant($pdfparams['pdf_margin_footer']));
11457 //
11458 $pdf->SetAutoPageBreak(true, constant($pdfparams['pdf_margin_bottom']));
11459 $pdf->setImageScale(constant($pdfparams['pdf_image_scale_ratio']));
11460 $pdf->SetFont($usepdffont, '', (int)$pdfparams['body_font_size']);
11461 if ($pdfparams['show_header'] == 0 || !(count($pdfparams['header_data']) > 0)) {
11462 $pdf->SetPrintHeader(false);
11463 }
11464 if ($pdfparams['show_footer'] == 0) {
11465 $pdf->SetPrintFooter(false);
11466 }
11467 $pdf->AddPage();
11468 $pdf->writeHTML($checkin_body, true, false, true, false, '');
11469 $pdf->lastPage();
11470 $pdf->Output($pathpdf, 'F');
11471 if (!file_exists($pathpdf)) {
11472 VikError::raiseWarning('', JText::translate('VBOERRGENCHECKINDOC'));
11473 } else {
11474 $q = "UPDATE `#__vikbooking_customers_orders` SET `checkindoc`=".$dbo->quote($pdffname)." WHERE `idorder`=".(int)$row['id'].";";
11475 $dbo->setQuery($q);
11476 $dbo->execute();
11477 $mainframe->enqueueMessage(JText::translate('VBOGENCHECKINDOCSUCCESS'));
11478 /**
11479 * @wponly - trigger files mirroring
11480 */
11481 VikBookingLoader::import('update.manager');
11482 VikBookingUpdateManager::triggerUploadBackup($pathpdf);
11483 //
11484 }
11485 //
11486 /**
11487 * @wponly - this task is executed via Ajax for the Modal forms listener. We cannot redirect to tmpl=component
11488 */
11489 $mainframe->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id']);
11490 exit;
11491 }
11492
11493 public function updatebookingcheckin()
11494 {
11495 if (!JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking')) {
11496 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
11497 }
11498
11499 $cid = VikRequest::getVar('cid', array(0));
11500 $id = $cid[0];
11501
11502 $dbo = JFactory::getDbo();
11503 $app = JFactory::getApplication();
11504
11505 $ptmpl = $app->input->getString('tmpl', '');
11506 $pnewtotpaid = $app->input->getFloat('newtotpaid', 0);
11507 $pguests = $app->input->get('guests', [], 'array');
11508 $pcomments = JComponentHelper::filterText($app->input->get('comments', '', 'raw'));
11509 $pcheckin_action = $app->input->getInt('checkin_action', 0);
11510 $valid_actions = array(-1, 0, 1, 2);
11511 if (!in_array($pcheckin_action, $valid_actions)) {
11512 $app->redirect('index.php');
11513 exit;
11514 }
11515 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$id." AND `status`='confirmed';";
11516 $dbo->setQuery($q);
11517 $dbo->execute();
11518 if ($dbo->getNumRows() < 1) {
11519 $app->redirect('index.php');
11520 exit;
11521 }
11522 $row = $dbo->loadAssoc();
11523 $q = "SELECT * FROM `#__vikbooking_customers_orders` WHERE `idorder`=".$row['id'].";";
11524 $dbo->setQuery($q);
11525 $dbo->execute();
11526 if ($dbo->getNumRows() < 1) {
11527 VikError::raiseWarning('', JText::translate('VBOCHECKINERRNOCUSTOMER'));
11528 $app->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id'].($ptmpl == 'component' ? '&tmpl=component' : ''));
11529 exit;
11530 }
11531 $custorder = $dbo->loadAssoc();
11532 //update checked status and new total paid
11533 $q = "UPDATE `#__vikbooking_orders` SET `checked`=".$pcheckin_action."".($pnewtotpaid > 0 ? ', `totpaid`='.$pnewtotpaid : '')." WHERE `id`=".$row['id'].";";
11534 $dbo->setQuery($q);
11535 $dbo->execute();
11536 // Booking History log for new amount paid (payment update)
11537 if ($pnewtotpaid > 0 && $pnewtotpaid > (float)$row['totpaid']) {
11538 $extra_data = new stdClass;
11539 $extra_data->amount_paid = ($pnewtotpaid - (float)$row['totpaid']);
11540 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->setExtraData($extra_data)->store('PU', JText::sprintf('VBOPREVAMOUNTPAID', VikBooking::numberFormat((float)$row['totpaid'])));
11541 }
11542 //
11543 //Booking History
11544 $hist_type = 'A';
11545 if ($pcheckin_action < 0) {
11546 $hist_type = 'Z';
11547 } elseif ($pcheckin_action == 1) {
11548 $hist_type = 'B';
11549 } elseif ($pcheckin_action == 2) {
11550 $hist_type = 'C';
11551 }
11552 $user = JFactory::getUser();
11553 VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('R' . $hist_type, "({$user->name})");
11554 //
11555 //Guests Details
11556 $guests_details = array();
11557 list($pax_fields, $pax_fields_attributes) = VikBooking::getPaxFields();
11558 // grab also the fields for front-end pre check-in
11559 list($pre_pax_fields, $pre_pax_fields_attributes) = VikBooking::getPaxFields(true);
11560 //
11561 foreach ($pguests as $ind => $adults) {
11562 foreach ($adults as $aduind => $details) {
11563 foreach ($pax_fields as $key => $v) {
11564 if (isset($details[$key]) && ((is_scalar($details[$key]) && strlen($details[$key])) || !empty($details[$key]))) {
11565 if (!isset($guests_details[$ind])) {
11566 $guests_details[$ind] = array();
11567 }
11568 if (!isset($guests_details[$ind][$aduind])) {
11569 $guests_details[$ind][$aduind] = array();
11570 }
11571 $guests_details[$ind][$aduind][$key] = $details[$key];
11572 }
11573 }
11574 foreach ($pre_pax_fields as $key => $v) {
11575 if (isset($pax_fields[$key])) {
11576 // we must have parsed this back-end field already
11577 continue;
11578 }
11579 if (isset($details[$key]) && ((is_scalar($details[$key]) && strlen($details[$key])) || !empty($details[$key]))) {
11580 if (!isset($guests_details[$ind])) {
11581 $guests_details[$ind] = array();
11582 }
11583 if (!isset($guests_details[$ind][$aduind])) {
11584 $guests_details[$ind][$aduind] = array();
11585 }
11586 if (!isset($guests_details[$ind][$aduind][$key])) {
11587 $guests_details[$ind][$aduind][$key] = $details[$key];
11588 }
11589 }
11590 }
11591 }
11592 }
11593
11594 if ($guests_details) {
11595 // current pax data may contain some extra information collected via front-end pre-checkin so we need to merge them
11596 $curpaxdata = json_decode($custorder['pax_data'], true);
11597 if (is_array($curpaxdata) && $curpaxdata) {
11598 // scan new guest registration details
11599 foreach ($guests_details as $ind => $groom) {
11600 foreach ($groom as $aduind => $aduinfo) {
11601 if (isset($curpaxdata[$ind][$aduind])) {
11602 $guests_details[$ind][$aduind] = array_merge($curpaxdata[$ind][$aduind], $guests_details[$ind][$aduind]);
11603 // unset some default pax fields that were not specified now, or data cannot be deleted for guests
11604 foreach ($guests_details[$ind][$aduind] as $key => $det) {
11605 if (isset($pguests[$ind][$aduind][$key]) && empty($pguests[$ind][$aduind][$key])) {
11606 // this default pax field was specified as empty now, so we cannot merge it
11607 unset($guests_details[$ind][$aduind][$key]);
11608 }
11609 }
11610 }
11611 }
11612 }
11613
11614 /**
11615 * In order to not lose any custom registration data added through PMS reports,
11616 * we scan the previous registration data to ensure we keep them in the update.
11617 *
11618 * @since 1.16.10 (J) - 1.6.10 (WP)
11619 */
11620 foreach ($curpaxdata as $ind => $groom) {
11621 if (!isset($guests_details[$ind])) {
11622 // ignore deleted room registration
11623 continue;
11624 }
11625 foreach ($groom as $aduind => $aduinfo) {
11626 if (!isset($guests_details[$ind][$aduind]) || !is_array($aduinfo)) {
11627 // ignore deleted room-guest registration
11628 continue;
11629 }
11630 foreach ($aduinfo as $field_key => $field_val) {
11631 if (!isset($guests_details[$ind][$aduind][$field_key]) && !empty($field_val)) {
11632 // merge previous room-guest registration data
11633 $guests_details[$ind][$aduind][$field_key] = $field_val;
11634 }
11635 }
11636 }
11637 }
11638 }
11639
11640 $q = "UPDATE `#__vikbooking_customers_orders` SET `pax_data`=" . $dbo->q(json_encode($guests_details)) . " WHERE `id`=" . (int) $custorder['id'] . ";";
11641 $dbo->setQuery($q);
11642 $dbo->execute();
11643 }
11644
11645 //'checked' status comments
11646 $q = "UPDATE `#__vikbooking_customers_orders` SET `comments`=".$dbo->quote($pcomments)." WHERE `id`=".$custorder['id'].";";
11647 $dbo->setQuery($q);
11648 $dbo->execute();
11649
11650 $app->enqueueMessage(JText::translate('VBOCHECKINSTATUSUPDATED'));
11651 $app->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id'].($pcheckin_action != $row['checked'] ? '&changed=1' : '').($ptmpl == 'component' ? '&tmpl=component' : ''));
11652 exit;
11653 }
11654
11655 public function alterbooking()
11656 {
11657 $dbo = JFactory::getDbo();
11658 $app = JFactory::getApplication();
11659 $user = JFactory::getUser();
11660
11661 $response = array(
11662 'esit' => 1,
11663 'message' => '',
11664 'vcm' => '',
11665 );
11666
11667 // must be a string as it may contain a dash
11668 $pidorder = VikRequest::getString('idorder', '', 'request');
11669 $pidorder = intval(str_replace('-', '', $pidorder));
11670
11671 $poldidroom = VikRequest::getInt('oldidroom', '', 'request');
11672 $pidroom = VikRequest::getInt('idroom', 0, 'request');
11673 $pfromdate = VikRequest::getString('fromdate', '', 'request');
11674 $ptodate = VikRequest::getString('todate', '', 'request');
11675 $pdebug = VikRequest::getInt('e4j_debug', 0, 'request');
11676 if ($pdebug == 1) {
11677 echo 'e4j.error.'.print_r($app->input->post->getArray(), true);
11678 exit;
11679 }
11680
11681 $nowdf = VikBooking::getDateFormat(true);
11682 if ($nowdf == "%d/%m/%Y") {
11683 $df = 'd/m/Y';
11684 } elseif ($nowdf == "%m/%d/%Y") {
11685 $df = 'm/d/Y';
11686 } else {
11687 $df = 'Y/m/d';
11688 }
11689 $pcheckinh = 0;
11690 $pcheckinm = 0;
11691 $pcheckouth = 0;
11692 $pcheckoutm = 0;
11693 $timeopst = VikBooking::getTimeOpenStore();
11694 if (is_array($timeopst)) {
11695 $opent = VikBooking::getHoursMinutes($timeopst[0]);
11696 $closet = VikBooking::getHoursMinutes($timeopst[1]);
11697 $pcheckinh = $opent[0];
11698 $pcheckinm = $opent[1];
11699 $pcheckouth = $closet[0];
11700 $pcheckoutm = $closet[1];
11701 }
11702 $info_tsto = getdate(strtotime($ptodate));
11703 $actualtsto = mktime(0, 0, 0, $info_tsto['mon'], ($info_tsto['mday'] + 1), $info_tsto['year']);
11704 $first = VikBooking::getDateTimestamp(date($df, strtotime($pfromdate)), $pcheckinh, $pcheckinm);
11705 $second = VikBooking::getDateTimestamp(date($df, $actualtsto), $pcheckouth, $pcheckoutm);
11706 $ptodate = date('Y-m-d', $second);
11707 if (!($second > $first)) {
11708 echo 'e4j.error.1 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11709 exit;
11710 }
11711 if (!($pidorder > 0) || !($pidroom > 0) || empty($pfromdate) || empty($ptodate)) {
11712 echo 'e4j.error.2 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11713 exit;
11714 }
11715
11716 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder . " AND `status`='confirmed'";
11717 $dbo->setQuery($q, 0, 1);
11718 $dbo->execute();
11719 if (!$dbo->getNumRows()) {
11720 echo 'e4j.error.3 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11721 exit;
11722 }
11723 $ord = $dbo->loadAssoc();
11724
11725 $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;";
11726 $dbo->setQuery($q);
11727 $dbo->execute();
11728 $ordersrooms = $dbo->loadAssocList();
11729
11730 // store for VCM the current rooms before the modification
11731 $ord['rooms_info'] = $ordersrooms;
11732
11733 // package or custom rate
11734 $is_package = !empty($ord['pkg']) ? true : false;
11735 $is_cust_cost = false;
11736 foreach ($ordersrooms as $kor => $or) {
11737 if ($is_package !== true && !empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
11738 $is_cust_cost = true;
11739 break;
11740 }
11741 }
11742
11743 // availability helper
11744 $av_helper = VikBooking::getAvailabilityInstance();
11745
11746 // room stay dates in case of split stay
11747 $room_stay_dates = [];
11748 if ($ord['split_stay']) {
11749 // no need to get the transient based on booking status, as the booking must be confirmed
11750 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
11751 // immediately count the number of nights of stay for each split room
11752 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
11753 $room_stay_dates[$sps_r_k]['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
11754 }
11755 }
11756
11757 // determine if dates have changed
11758 $dates_changed = false;
11759 if (date('Y-m-d', $ord['checkin']) != $pfromdate || date('Y-m-d', $ord['checkout']) != $ptodate) {
11760 $dates_changed = true;
11761 }
11762
11763 $toswitch = array();
11764 $idbooked = array();
11765 $rooms_units = array();
11766 $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms`;";
11767 $dbo->setQuery($q);
11768 $dbo->execute();
11769 $all_rooms = $dbo->loadAssocList();
11770 foreach ($all_rooms as $rr) {
11771 $rooms_units[$rr['id']]['name'] = $rr['name'];
11772 $rooms_units[$rr['id']]['units'] = $rr['units'];
11773 }
11774
11775 // switch room
11776 if ($poldidroom != $pidroom) {
11777 foreach ($ordersrooms as $ind => $or) {
11778 if ($poldidroom == $or['idroom'] && array_key_exists($pidroom, $rooms_units)) {
11779 if (!isset($idbooked[$or['idroom']])) {
11780 $idbooked[$or['idroom']] = 0;
11781 }
11782 // $idbooked is not really needed as switch is never made for the same room id
11783 $idbooked[$or['idroom']]++;
11784 //
11785 $orkey = count($toswitch);
11786 $toswitch[$orkey]['from'] = $or['idroom'];
11787 $toswitch[$orkey]['to'] = $pidroom;
11788 $toswitch[$orkey]['record'] = $or;
11789 $toswitch[$orkey]['record_ind'] = $ind;
11790 break;
11791 }
11792 }
11793 }
11794 if (count($toswitch)) {
11795 foreach ($toswitch as $ksw => $rsw) {
11796 $plusunit = array_key_exists($rsw['to'], $idbooked) ? $idbooked[$rsw['to']] : 0;
11797 $room_checkin = $ord['checkin'];
11798 $room_checkout = $ord['checkout'];
11799 if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
11800 $room_checkin = $room_stay_dates[$rsw['record_ind']]['checkin'];
11801 $room_checkout = $room_stay_dates[$rsw['record_ind']]['checkout'];
11802 }
11803 if (!VikBooking::roomBookable($rsw['to'], ($rooms_units[$rsw['to']]['units'] + $plusunit), $room_checkin, $room_checkout)) {
11804 // the room is not available
11805 unset($toswitch[$ksw]);
11806 echo 'e4j.error.'.JText::sprintf('VBSWITCHRERR', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']);
11807 exit;
11808 }
11809 }
11810 if (count($toswitch)) {
11811 //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)
11812 reset($ordersrooms);
11813 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$ordersrooms[0]['id'].";";
11814 $dbo->setQuery($q);
11815 $dbo->execute();
11816 //
11817 foreach ($toswitch as $ksw => $rsw) {
11818 // update room reservation record
11819 $q = "UPDATE `#__vikbooking_ordersrooms` SET `idroom`=" . $rsw['to'] . ",`idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=" . $rsw['record']['id'] . ";";
11820 $dbo->setQuery($q);
11821 $dbo->execute();
11822 $response['message'] .= JText::sprintf('VBOVWALTBKSWITCHROK', $rsw['record']['name'], $rooms_units[$rsw['to']]['name'])."\n";
11823
11824 // update Notes field for this booking to keep track of the previous room that was assigned
11825 $prev_room_name = array_key_exists($rsw['from'], $rooms_units) ? $rooms_units[$rsw['from']]['name'] : '';
11826 if (!empty($prev_room_name)) {
11827 $new_notes = JText::sprintf('VBOPREVROOMMOVED', $prev_room_name, date($df.' H:i:s'))."\n".$ord['adminnotes'];
11828 $q = "UPDATE `#__vikbooking_orders` SET `adminnotes`=".$dbo->quote($new_notes)." WHERE `id`=".(int)$ord['id'].";";
11829 $dbo->setQuery($q);
11830 $dbo->execute();
11831 }
11832
11833 if ($ord['status'] == 'confirmed') {
11834 // update room record in _busy
11835 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'])) {
11836 // in case of a split stay it is fundamental to update the exact busy record ID
11837 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=" . $rsw['to'] . " WHERE `id`=" . (int)$room_stay_dates[$rsw['record_ind']]['id'];
11838 $dbo->setQuery($q);
11839 $dbo->execute();
11840 } else {
11841 // regular processing of a room ID for a reservation, no matter which one, we switch it
11842 $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;";
11843 $dbo->setQuery($q);
11844 $dbo->execute();
11845 if ($dbo->getNumRows() == 1) {
11846 $cur_busy = $dbo->loadAssocList();
11847 $q = "UPDATE `#__vikbooking_busy` SET `idroom`=".$rsw['to']." WHERE `id`=".$cur_busy[0]['id']." AND `idroom`=".$cur_busy[0]['idroom']." LIMIT 1;";
11848 $dbo->setQuery($q);
11849 $dbo->execute();
11850 }
11851 }
11852
11853 // if automated updates enabled, keep $response['vcm'] empty
11854 // Invoke Channel Manager
11855 if (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
11856 $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>';
11857 }
11858 } elseif ($ord['status'] == 'standby') {
11859 // remove record in _tmplock
11860 $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($ord['id']) . ";";
11861 $dbo->setQuery($q);
11862 $dbo->execute();
11863 }
11864 }
11865
11866 // check if sub-units should be assigned again when switching room
11867 if (!$dates_changed && !$ord['split_stay'] && VikBooking::autoRoomUnit()) {
11868 $new_order_rooms = VikBooking::loadOrdersRoomsData($ord['id']);
11869 $room_indexes_usemap = [];
11870 foreach ($new_order_rooms as $kor => $or) {
11871 $num = $kor + 1;
11872 // assign room specific unit
11873 $room_indexes = VikBooking::getRoomUnitNumsAvailable($ord, $or['idroom']);
11874 $use_ind_key = 0;
11875 if ($room_indexes) {
11876 if (!array_key_exists($or['idroom'], $room_indexes_usemap)) {
11877 $room_indexes_usemap[$or['idroom']] = $use_ind_key;
11878 } else {
11879 $use_ind_key = $room_indexes_usemap[$or['idroom']];
11880 }
11881 $q = "UPDATE `#__vikbooking_ordersrooms` SET `roomindex`=".(int)$room_indexes[$use_ind_key]." WHERE `id`=".(int)$or['id'].";";
11882 $dbo->setQuery($q);
11883 $dbo->execute();
11884 $room_indexes_usemap[$or['idroom']]++;
11885 }
11886 }
11887 }
11888
11889 // do not terminate the process when there is a switch, proceed to check the dates.
11890 }
11891 }
11892
11893 // change dates
11894 if ($dates_changed) {
11895 if ($ord['split_stay']) {
11896 // we do not allow to drag and change dates for rooms in a split stay reservation
11897 echo 'e4j.error.' . JText::sprintf('VBO_BOOK_SPLIT_STAY_CANNOTDRAG', $ord['id']);
11898 exit;
11899 }
11900
11901 // total nights of stay
11902 $daysdiff = $ord['days'];
11903
11904 // re-read ordersrooms (as rooms may have been switched)
11905 $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;";
11906 $dbo->setQuery($q);
11907 $dbo->execute();
11908 $ordersrooms = $dbo->loadAssocList();
11909
11910 $groupdays = VikBooking::getGroupDays($first, $second, $daysdiff);
11911 $opertwounits = true;
11912 $units_counter = array();
11913 foreach ($ordersrooms as $ind => $or) {
11914 if (!isset($units_counter[$or['idroom']])) {
11915 $units_counter[$or['idroom']] = -1;
11916 }
11917 $units_counter[$or['idroom']]++;
11918 }
11919
11920 foreach ($ordersrooms as $ind => $or) {
11921 $num = $ind + 1;
11922 $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'] . ";";
11923 $dbo->setQuery($check);
11924 $dbo->execute();
11925 if ($dbo->getNumRows() > 0) {
11926 $busy = $dbo->loadAssocList();
11927 foreach ($groupdays as $gday) {
11928 $bfound = 0;
11929 foreach ($busy as $bu) {
11930 if ($gday >= $bu['checkin'] && $gday <= $bu['realback']) {
11931 $bfound++;
11932 }
11933 }
11934 if ($bfound >= ($or['units'] - $units_counter[$or['idroom']]) || !VikBooking::roomNotLocked($or['idroom'], $or['units'], $first, $second)) {
11935 $opertwounits = false;
11936 break 2;
11937 }
11938 }
11939 }
11940 }
11941 if ($opertwounits !== true) {
11942 $response['esit'] = 0;
11943 $response['message'] = JText::translate('VBROOMNOTRIT')." ".date($df.' H:i', $first)." ".JText::translate('VBROOMNOTCONSTO')." ".date($df.' H:i', $second);
11944 echo json_encode($response);
11945 exit;
11946 }
11947
11948 // update dates and busy records
11949 $realback = VikBooking::getHoursRoomAvail() * 3600;
11950 $realback += $second;
11951 $q = "UPDATE `#__vikbooking_orders` SET `checkin`='".$first."', `checkout`='".$second."' WHERE `id`=".$ord['id'].";";
11952 $dbo->setQuery($q);
11953 $dbo->execute();
11954 if ($ord['status'] == 'confirmed') {
11955 $q = "SELECT `b`.`id` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`id`=`ob`.`idbusy` AND `ob`.`idorder`=".$ord['id'].";";
11956 $dbo->setQuery($q);
11957 $dbo->execute();
11958 $allbusy = $dbo->loadAssocList();
11959 foreach ($allbusy as $bb) {
11960 $q = "UPDATE `#__vikbooking_busy` SET `checkin`='".$first."', `checkout`='".$second."', `realback`='".$realback."' WHERE `id`='".$bb['id']."';";
11961 $dbo->setQuery($q);
11962 $dbo->execute();
11963 }
11964 // if automated updates enabled, keep $response['vcm'] empty
11965 // Invoke Channel Manager
11966 if (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
11967 $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>';
11968 }
11969 }
11970 $response['message'] .= JText::translate('RESUPDATED')."\n";
11971 }
11972
11973 if (count($toswitch)) {
11974 /**
11975 * Rooms have changed so the new rates must be re-calculated.
11976 * Maybe they should be calculated in any case, even if just
11977 * the dates have changed. For the moment the rates are reset.
11978 */
11979 }
11980
11981 // unset any previously booked room due to calendar sharing
11982 VikBooking::cleanSharedCalendarsBusy($ord['id']);
11983 // check if some of the rooms booked have shared calendars
11984 VikBooking::updateSharedCalendars($ord['id']);
11985 //
11986
11987 //Booking History
11988 VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($ord));
11989 //
11990
11991 $vcm_autosync = VikBooking::vcmAutoUpdate();
11992 if ($vcm_autosync > 0 && !empty($response['vcm'])) {
11993 //unset the vcm property as no buttons should be displayed when in auto-sync
11994 $response['vcm'] = '';
11995 $vcm_obj = VikBooking::getVcmInvoker();
11996 $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
11997 $sync_result = $vcm_obj->doSync();
11998 if ($sync_result === false) {
11999 $response['message'] .= JText::translate('VBCHANNELMANAGERRESULTKO')." (".$vcm_obj->getError().")\n";
12000 }
12001 }
12002
12003 // in case of error but not empty VCM message, set an error that will be displayed after the mustReload
12004 if ($response['esit'] < 1 && !empty($response['vcm'])) {
12005 VikError::raiseNotice('', $response['vcm']);
12006 }
12007
12008 $response['message'] = nl2br($response['message']);
12009 echo json_encode($response);
12010 exit;
12011 }
12012
12013 public function modroomrateplans()
12014 {
12015 $dbo = JFactory::getDbo();
12016 $session = JFactory::getSession();
12017
12018 $updforvcm = $session->get('vbVcmRatesUpd', '');
12019 $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
12020
12021 $pid_room = VikRequest::getInt('id_room', '', 'request');
12022 $pid_price = VikRequest::getInt('id_price', '', 'request');
12023 $ptype = VikRequest::getString('type', '', 'request');
12024 $pfromdate = VikRequest::getString('fromdate', '', 'request');
12025 $ptodate = VikRequest::getString('todate', '', 'request');
12026
12027 if (empty($pid_room) || empty($pid_price) || empty($ptype) || empty($pfromdate) || empty($ptodate) || !(strtotime($pfromdate) > 0) || !(strtotime($ptodate) > 0)) {
12028 echo 'e4j.error.'.addslashes(JText::translate('VBRATESOVWERRMODRPLANS'));
12029 exit;
12030 }
12031
12032 $q = "SELECT * FROM `#__vikbooking_prices` WHERE `id`=".$pid_price.";";
12033 $dbo->setQuery($q);
12034 $price_record = $dbo->loadAssoc();
12035
12036 if (!$price_record) {
12037 echo 'e4j.error.'.addslashes(JText::translate('VBRATESOVWERRMODRPLANS')).'.';
12038 exit;
12039 }
12040
12041 $current_closed = array();
12042 if (!empty($price_record['closingd'])) {
12043 $current_closed = json_decode($price_record['closingd'], true);
12044 }
12045 $current_closed = !is_array($current_closed) ? array() : $current_closed;
12046
12047 $start_ts = strtotime($pfromdate);
12048 $end_ts = strtotime($ptodate);
12049 $infostart = getdate($start_ts);
12050 $all_days = array();
12051 $output = array();
12052 while ($infostart[0] > 0 && $infostart[0] <= $end_ts) {
12053 $all_days[] = date('Y-m-d', $infostart[0]);
12054 $indkey = $infostart['mday'].'-'.$infostart['mon'].'-'.$infostart['year'].'-'.$pid_price;
12055 $output[$indkey] = array();
12056 $infostart = getdate(mktime(0, 0, 0, $infostart['mon'], ($infostart['mday'] + 1), $infostart['year']));
12057 }
12058
12059 if ($ptype == 'close') {
12060 // close
12061 if (!array_key_exists($pid_room, $current_closed)) {
12062 $current_closed[$pid_room] = array();
12063 }
12064 foreach ($all_days as $daymod) {
12065 if (!in_array($daymod, $current_closed[$pid_room])) {
12066 $current_closed[$pid_room][] = $daymod;
12067 }
12068 }
12069 } else {
12070 // open
12071 if (array_key_exists($pid_room, $current_closed)) {
12072 foreach ($all_days as $daymod) {
12073 if (in_array($daymod, $current_closed[$pid_room])) {
12074 foreach ($current_closed[$pid_room] as $ck => $cv) {
12075 if ($daymod == $cv) {
12076 unset($current_closed[$pid_room][$ck]);
12077 }
12078 }
12079 }
12080 }
12081 } else {
12082 $current_closed[$pid_room] = array();
12083 }
12084 }
12085
12086 if (!$current_closed[$pid_room]) {
12087 unset($current_closed[$pid_room]);
12088 }
12089
12090 $q = "UPDATE `#__vikbooking_prices` SET `closingd`=".(count($current_closed) > 0 ? $dbo->quote(json_encode($current_closed)) : "NULL")." WHERE `id`=".(int)$pid_price.";";
12091 $dbo->setQuery($q);
12092 $dbo->execute();
12093
12094 $oldcsscls = $ptype == 'close' ? 'vbo-roverw-rplan-on' : 'vbo-roverw-rplan-off';
12095 $newcsscls = $ptype == 'close' ? 'vbo-roverw-rplan-off' : 'vbo-roverw-rplan-on';
12096
12097 foreach ($output as $ok => $ov) {
12098 $output[$ok] = array('oldcls' => $oldcsscls, 'newcls' => $newcsscls);
12099 }
12100
12101 // build new session values
12102 $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
12103
12104 if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
12105 $updforvcm['dfrom'] = $updforvcm['dfrom'] > $start_ts ? $start_ts : $updforvcm['dfrom'];
12106 } else {
12107 $updforvcm['dfrom'] = $start_ts;
12108 }
12109
12110 if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
12111 $updforvcm['dto'] = $updforvcm['dto'] < $end_ts ? $end_ts : $updforvcm['dto'];
12112 } else {
12113 $updforvcm['dto'] = $end_ts;
12114 }
12115
12116 if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
12117 if (!in_array($pid_room, $updforvcm['rooms'])) {
12118 $updforvcm['rooms'][] = $pid_room;
12119 }
12120 } else {
12121 $updforvcm['rooms'] = array($pid_room);
12122 }
12123
12124 if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
12125 if (array_key_exists($pid_room, $updforvcm['rplans'])) {
12126 if (!in_array($pid_price, $updforvcm['rplans'][$pid_room])) {
12127 $updforvcm['rplans'][$pid_room][] = $pid_price;
12128 }
12129 } else {
12130 $updforvcm['rplans'][$pid_room] = array($pid_price);
12131 }
12132 } else {
12133 $updforvcm['rplans'] = array($pid_room => array($pid_price));
12134 }
12135
12136 /**
12137 * Rather than suggesting the administrator to manually invoke VCM to launch a Bulk Action,
12138 * we try to silently trigger an automatic bulk action before updating the session values.
12139 *
12140 * @since 1.17.1 (J) - 1.7.1 (WP)
12141 * @since 1.17.5 (J) - 1.7.5 (WP) the "rate_id" property is passed along the auto-bulk data.
12142 */
12143 $rates_aligned = false;
12144 try {
12145 if (class_exists('VikChannelManager')) {
12146 $rates_aligned = VikChannelManager::autoBulkActions([
12147 'from_date' => $pfromdate,
12148 'to_date' => $ptodate,
12149 'forced_rooms' => [$pid_room],
12150 'rate_id' => $pid_price,
12151 'update' => 'rates',
12152 ]);
12153 }
12154 } catch (Throwable $e) {
12155 // do nothing
12156 $rates_aligned = false;
12157 }
12158
12159 if (!$rates_aligned) {
12160 // update session values
12161 $session->set('vbVcmRatesUpd', $updforvcm);
12162 }
12163
12164 echo json_encode($output);
12165 exit;
12166 }
12167
12168 public function icsexportlaunch() {
12169 $dbo = JFactory::getDBO();
12170 $pcheckindate = VikRequest::getString('checkindate', '', 'request');
12171 $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
12172 $pstatus = VikRequest::getString('status', '', 'request');
12173 $validstatus = array('confirmed', 'standby', 'cancelled');
12174 $filterstatus = '';
12175 $filterfirst = 0;
12176 $filtersecond = 0;
12177 $nowdf = VikBooking::getDateFormat(true);
12178 if ($nowdf == "%d/%m/%Y") {
12179 $df = 'd/m/Y';
12180 } elseif ($nowdf == "%m/%d/%Y") {
12181 $df = 'm/d/Y';
12182 } else {
12183 $df = 'Y/m/d';
12184 }
12185 $currencyname = VikBooking::getCurrencyName();
12186 if (!empty($pstatus) && in_array($pstatus, $validstatus)) {
12187 $filterstatus = $pstatus;
12188 }
12189 if (!empty($pcheckindate)) {
12190 if (VikBooking::dateIsValid($pcheckindate)) {
12191 $first=VikBooking::getDateTimestamp($pcheckindate, '0', '0');
12192 $filterfirst = $first;
12193 }
12194 }
12195 if (!empty($pcheckoutdate)) {
12196 if (VikBooking::dateIsValid($pcheckoutdate)) {
12197 $second=VikBooking::getDateTimestamp($pcheckoutdate, '23', '59');
12198 if ($second > $first) {
12199 $filtersecond = $second;
12200 }
12201 }
12202 }
12203 $clause = array();
12204 if ($filterfirst > 0) {
12205 $clause[] = "`o`.`checkin` >= ".$filterfirst;
12206 }
12207 if ($filtersecond > 0) {
12208 $clause[] = "`o`.`checkout` <= ".$filtersecond;
12209 }
12210 if (!empty($filterstatus)) {
12211 $clause[] = "`o`.`status` = '".$filterstatus."'";
12212 }
12213 $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;";
12214 $dbo->setQuery($q);
12215 $dbo->execute();
12216 if ($dbo->getNumRows() > 0) {
12217 $orders = $dbo->loadAssocList();
12218 $icscontent = "BEGIN:VCALENDAR\n";
12219 $icscontent .= "VERSION:2.0\n";
12220 $icscontent .= "PRODID:-//e4j//VikBooking//EN\n";
12221 $icscontent .= "CALSCALE:GREGORIAN\n";
12222 $str = "";
12223 foreach ($orders as $kord => $ord) {
12224 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12225 continue;
12226 }
12227 $usecurrencyname = $currencyname;
12228 $usecurrencyname = !empty($ord['idorderota']) && !empty($ord['chcurrency']) ? $ord['chcurrency'] : $usecurrencyname;
12229 $statusstr = '';
12230 if ($ord['status'] == 'confirmed') {
12231 $statusstr = JText::translate('VBCSVSTATUSCONFIRMED');
12232 } elseif ($ord['status'] == 'standby') {
12233 $statusstr = JText::translate('VBCSVSTATUSSTANDBY');
12234 } elseif ($ord['status'] == 'cancelled') {
12235 $statusstr = JText::translate('VBCSVSTATUSCANCELLED');
12236 }
12237 $uri = JURI::root().'index.php?option=com_vikbooking&view=booking&sid='.$ord['sid'].'&ts='.$ord['ts'];
12238 /**
12239 * @wponly Rewrite URI for front-end
12240 */
12241 $uri = str_replace(JUri::root(), '', $uri);
12242 $model = JModel::getInstance('vikbooking', 'shortcodes');
12243 $itemid = $model->best('booking');
12244 if ($itemid) {
12245 $uri = JRoute::rewrite($uri . "&Itemid={$itemid}", false);
12246 }
12247 //
12248 $ordnumbstr = $ord['id'].(!empty($ord['confirmnumber']) ? ' - '.$ord['confirmnumber'] : '').(!empty($ord['idorderota']) ? ' ('.ucwords($ord['channel']).')' : '').' - '.$statusstr;
12249 $peoplestr = ($ord['adults'] + $ord['children']).($ord['children'] > 0 ? ' ('.JText::translate('VBCSVCHILDREN').': '.$ord['children'].')' : '');
12250 $totalstring = ($ord['total'] > 0 ? ($usecurrencyname.' '.VikBooking::numberFormat($ord['total'])) : '');
12251 $totalpaidstring = ($ord['totpaid'] > 0 ? (' ('.VikBooking::numberFormat($ord['totpaid']).')') : '');
12252 $description = JText::sprintf('VBICSEXPDESCRIPTION', $ordnumbstr."\\n", $peoplestr."\\n", $ord['days']."\\n", $totalstring.$totalpaidstring."\\n", "\\n".str_replace("\n", "\\n", trim($ord['custdata'])));
12253 $str .= "BEGIN:VEVENT\n";
12254 $str .= "DTEND:" . JFactory::getDate(date('Y-m-d H:i:s', $ord['checkout']), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12255 $str .= "UID:" . uniqid() . "\n";
12256 $str .= "DTSTAMP:" . JFactory::getDate(date('Y-m-d H:i:s'), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12257 $str .= ((strlen($description) > 0 ) ? "DESCRIPTION:".preg_replace('/([\,;])/','\\\$1', $description)."\n" : "");
12258 $str .= "URL;VALUE=URI:" . preg_replace('/([\,;])/','\\\$1', $uri) . "\n";
12259 $str .= "SUMMARY:" . JText::sprintf('VBICSEXPSUMMARY', date($df, $ord['checkin'])) . "\n";
12260 $str .= "DTSTART:" . JFactory::getDate(date('Y-m-d H:i:s', $ord['checkin']), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12261 $str .= "END:VEVENT\n";
12262 }
12263 $icscontent .= $str;
12264 $icscontent .= "END:VCALENDAR\n";
12265 //download file from buffer
12266 header("Content-Type: application/octet-stream; ");
12267 header("Cache-Control: no-store, no-cache");
12268 header('Content-Disposition: attachment; filename="bookings_export.ics"');
12269 $f = fopen('php://output', "w");
12270 fwrite($f, $icscontent);
12271 fclose($f);
12272 exit;
12273 } else {
12274 VikError::raiseWarning('', JText::translate('VBICSEXPNORECORDS'));
12275 $mainframe = JFactory::getApplication();
12276 $mainframe->redirect("index.php?option=com_vikbooking&task=icsexportprepare&checkindate=".$pcheckindate."&checkoutdate=".$pcheckoutdate."&status=".$pstatus."&tmpl=component");
12277 }
12278 }
12279
12280 public function csvexportlaunch()
12281 {
12282 $dbo = JFactory::getDbo();
12283 $app = JFactory::getApplication();
12284
12285 $pdatefilt = VikRequest::getString('datefilt', '', 'request');
12286 $proomfilt = VikRequest::getString('roomfilt', '', 'request');
12287 $pchfilt = VikRequest::getString('chfilt', '', 'request');
12288 $ppayfilt = VikRequest::getString('payfilt', '', 'request');
12289 $pcheckindate = VikRequest::getString('checkindate', '', 'request');
12290 $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
12291 $pstatus = VikRequest::getString('status', '', 'request');
12292 $pcatfilt = VikRequest::getInt('catfilt', 0, 'request');
12293 $pformat = VikRequest::getString('format', 'csv', 'request');
12294
12295 // let the report class (a generic one) generate the CSV file in the proper format
12296 $report_obj = VikBooking::getReportInstance('revenue')->setExportCSVFormat($pformat);
12297
12298 $validstatus = array('confirmed', 'standby', 'cancelled');
12299 $validdates = array('ts', 'checkin', 'checkout');
12300
12301 $filterdate = '';
12302 $filterstatus = '';
12303 $first = 0;
12304 $filterfirst = 0;
12305 $filtersecond = 0;
12306 $nowdf = VikBooking::getDateFormat(true);
12307 if ($nowdf == "%d/%m/%Y") {
12308 $df = 'd/m/Y';
12309 } elseif ($nowdf == "%m/%d/%Y") {
12310 $df = 'm/d/Y';
12311 } else {
12312 $df = 'Y/m/d';
12313 }
12314 $datesep = VikBooking::getDateSeparator(true);
12315 $currencyname = VikBooking::getCurrencyName();
12316
12317 if (!empty($pstatus) && in_array($pstatus, $validstatus)) {
12318 $filterstatus = $pstatus;
12319 }
12320 if (!empty($pdatefilt) && in_array($pdatefilt, $validdates)) {
12321 $filterdate = $pdatefilt;
12322 }
12323 if (!empty($pcheckindate) && !empty($filterdate)) {
12324 if (VikBooking::dateIsValid($pcheckindate)) {
12325 $first = VikBooking::getDateTimestamp($pcheckindate, '0', '0');
12326 $filterfirst = $first;
12327 }
12328 }
12329 if (!empty($pcheckoutdate) && !empty($filterdate)) {
12330 if (VikBooking::dateIsValid($pcheckoutdate)) {
12331 $second = VikBooking::getDateTimestamp($pcheckoutdate, '23', '59');
12332 if ($second > $first) {
12333 $filtersecond = $second;
12334 }
12335 }
12336 }
12337 $clause = array();
12338 if ($filterfirst > 0) {
12339 $clause[] = "`o`.`".$filterdate."` >= ".$filterfirst;
12340 }
12341 if ($filtersecond > 0) {
12342 $clause[] = "`o`.`".$filterdate."` <= ".$filtersecond;
12343 }
12344 if (!empty($filterstatus)) {
12345 $clause[] = "`o`.`status` = '".$filterstatus."'";
12346 }
12347 if (!empty($pchfilt)) {
12348 $clause[] = "`o`.`channel` LIKE ".$dbo->quote("%".$pchfilt."%");
12349 }
12350 if (!empty($ppayfilt)) {
12351 $clause[] = "`o`.`idpayment` LIKE '".$ppayfilt."=%'";
12352 }
12353 if (!empty($proomfilt)) {
12354 $clause[] = "`or`.`idroom` = '".(int)$proomfilt."'";
12355 }
12356
12357 if (!empty($pcatfilt)) {
12358 $room_cat_ids = array();
12359 $q = "SELECT `id`,`idcat` FROM `#__vikbooking_rooms` WHERE `idcat` LIKE " . $dbo->quote("%$pcatfilt%");
12360 $dbo->setQuery($q);
12361 $dbo->execute();
12362 if ($dbo->getNumRows()) {
12363 $records = $dbo->loadAssocList();
12364 foreach ($records as $rcat) {
12365 $parts = explode(';', $rcat['idcat']);
12366 if (in_array($pcatfilt, $parts)) {
12367 $room_cat_ids[] = $rcat['id'];
12368 }
12369 }
12370 }
12371 if (count($room_cat_ids)) {
12372 $clause[] = "`or`.`idroom` IN (" . implode(', ', $room_cat_ids) . ")";
12373 }
12374 }
12375
12376 $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;";
12377 $dbo->setQuery($q);
12378 $orders = $dbo->loadAssocList();
12379 if (!$orders) {
12380 $app->enqueueMessage(JText::translate('VBCSVEXPNORECORDS'), 'error');
12381 $app->redirect("index.php?option=com_vikbooking&task=csvexportprepare&checkindate=".$pcheckindate."&checkoutdate=".$pcheckoutdate."&status=".$pstatus."&tmpl=component");
12382 $app->close();
12383 }
12384
12385 // options
12386 $all_options = array();
12387 $q = "SELECT * FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
12388 $dbo->setQuery($q);
12389 $options = $dbo->loadAssocList();
12390 if ($options) {
12391 foreach ($options as $ok => $ov) {
12392 $all_options[$ov['id']] = $ov;
12393 }
12394 }
12395
12396 // build columns
12397 $columns = [
12398 [
12399 'label' => JText::translate('VBDASHBOOKINGID'),
12400 ],
12401 [
12402 'label' => JText::translate('VBPVIEWORDERSONE'),
12403 ],
12404 [
12405 'label' => JText::translate('VBCSVCHECKIN'),
12406 ],
12407 [
12408 'label' => JText::translate('VBCSVCHECKOUT'),
12409 ],
12410 [
12411 'label' => JText::translate('VBCSVNIGHTS'),
12412 ],
12413 [
12414 'label' => JText::translate('VBCSVROOM'),
12415 ],
12416 [
12417 'label' => JText::translate('VBCSVPEOPLE'),
12418 ],
12419 [
12420 'label' => JText::translate('VBCSVCUSTINFO'),
12421 ],
12422 [
12423 'label' => JText::translate('ORDER_SPREQUESTS'),
12424 ],
12425 [
12426 'label' => JText::translate('ORDER_NOTES'),
12427 ],
12428 [
12429 'label' => JText::translate('VBCSVCREATEDBY'),
12430 ],
12431 [
12432 'label' => JText::translate('VBCSVCUSTMAIL'),
12433 ],
12434 [
12435 'label' => JText::translate('ORDER_PHONE'),
12436 ],
12437 [
12438 'label' => JText::translate('VBCSVOPTIONS'),
12439 ],
12440 [
12441 'label' => JText::translate('VBCSVPAYMENTMETHOD'),
12442 ],
12443 [
12444 'label' => JText::translate('VBCSVORDIDCONFNUMB'),
12445 ],
12446 [
12447 'label' => JText::translate('VBOCHANNEL'),
12448 ],
12449 [
12450 'label' => JText::translate('VBCSVEXPFILTBSTATUS'),
12451 ],
12452 [
12453 'label' => JText::translate('VBCSVTOTAL'),
12454 ],
12455 [
12456 'label' => JText::translate('VBCSVTOTPAID'),
12457 ],
12458 [
12459 'label' => JText::translate('VBCSVTOTTAXES'),
12460 ],
12461 ];
12462
12463 // booking cancellation details
12464 $cancellation_timestamps = [];
12465
12466 if (empty($filterstatus) || $filterstatus === 'cancelled') {
12467 // insert column for cancellation date at index 2
12468 array_splice($columns, 2, 0, [['label' => JText::translate('VBO_CANC_DATE')]]);
12469 // gather all cancelled bookings, if any
12470 $cancellation_ids = [];
12471 foreach ($orders as $order) {
12472 if ($order['status'] === 'cancelled' && !in_array($order['id'], $cancellation_ids)) {
12473 $cancellation_ids[] = $order['id'];
12474 }
12475 }
12476 if ($cancellation_ids && $cancHistoryEvents = VikBooking::getBookingHistoryInstance(0)->getBookingEventsType('cancelled')) {
12477 // list of booking IDs with cancellation events processed
12478 $cancBidsProcessed = [];
12479
12480 // query the database to fetch the needed history records
12481 $dbo->setQuery(
12482 $dbo->getQuery(true)
12483 ->select([
12484 $dbo->qn('idorder'),
12485 $dbo->qn('dt'),
12486 ])
12487 ->from($dbo->qn('#__vikbooking_orderhistory'))
12488 ->where($dbo->qn('idorder') . ' IN (' . implode(', ', array_map('intval', $cancellation_ids)) . ')')
12489 ->where($dbo->qn('type') . ' IN (' . implode(', ', array_map([$dbo, 'q'], $cancHistoryEvents)) . ')')
12490 ->order($dbo->qn('idorder') . ' ASC')
12491 ->order($dbo->qn('dt') . ' ASC')
12492 );
12493
12494 // scan all booking cancellation records
12495 foreach ($dbo->loadAssocList() as $cancRecord) {
12496 if (!($cancBidsProcessed[$cancRecord['idorder']] ?? 0)) {
12497 // turn flag on to process this booking only once and get the earliest (first) cancellation
12498 $cancBidsProcessed[$cancRecord['idorder']] = 1;
12499
12500 // convert the cancellation date from UTC to local timezone and set booking cancellation timestamp
12501 $cancellation_timestamps[$cancRecord['idorder']] = JHtml::fetch('date', $cancRecord['dt'], 'U');
12502 }
12503 }
12504 }
12505 }
12506
12507 // set CSV columns
12508 $report_obj->setReportCols($columns);
12509
12510 // prepare the container for the CSV rows
12511 $orderscsv = [];
12512
12513 // availability helper
12514 $av_helper = VikBooking::getAvailabilityInstance();
12515
12516 $room_inds = [];
12517 $room_stay_dates = [];
12518 foreach ($orders as $kord => $ord) {
12519 // room index in this booking
12520 if (!isset($room_inds[$ord['id']])) {
12521 $room_inds[$ord['id']] = -1;
12522 }
12523 $room_inds[$ord['id']]++;
12524
12525 /**
12526 * Split stay reservation.
12527 *
12528 * @since 1.16.0 (J) - 1.6.0 (WP)
12529 */
12530 $room_stay_dates = $room_inds[$ord['id']] > 0 ? $room_stay_dates : [];
12531 if ($ord['split_stay']) {
12532 if ($ord['status'] == 'confirmed') {
12533 $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
12534 } else {
12535 $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $ord['id'], []);
12536 }
12537 // immediately count the number of nights of stay for each split room
12538 foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
12539 if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
12540 // overwrite values for compatibility with non-confirmed bookings
12541 $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
12542 $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
12543 }
12544 $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
12545 // overwrite the whole array
12546 $room_stay_dates[$sps_r_k] = $sps_r_v;
12547 }
12548 }
12549
12550 // determine nights and dates for this room booking
12551 $booking_nights = $ord['days'];
12552 $booking_checkin = $ord['checkin'];
12553 $booking_checkout = $ord['checkout'];
12554 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']) {
12555 $booking_nights = $room_stay_dates[$room_inds[$ord['id']]]['nights'];
12556 $booking_checkin = $room_stay_dates[$room_inds[$ord['id']]]['checkin'];
12557 $booking_checkout = $room_stay_dates[$room_inds[$ord['id']]]['checkout'];
12558 }
12559
12560 $usecurrencyname = $currencyname;
12561 $usecurrencyname = !empty($ord['idorderota']) && !empty($ord['chcurrency']) ? $ord['chcurrency'] : $usecurrencyname;
12562 $peoplestr = ($ord['adults'] + $ord['children']).($ord['children'] > 0 ? ' ('.JText::translate('VBCSVCHILDREN').': '.$ord['children'].')' : '');
12563 $custinfostr = str_replace(",", " ", $ord['custdata']);
12564 $customer = VikBooking::getCPinIstance()->getCustomerFromBooking($ord['id']);
12565 if (count($customer)) {
12566 $custinfostr = $customer['first_name'] . ' ' . $customer['last_name'];
12567 }
12568 $special_requests = '';
12569 if (preg_match("/(?:special requests:\s*)(.*?)$/is", $ord['custdata'], $match)) {
12570 $special_requests = $match[1];
12571 } elseif (preg_match("/(?:special request:\s*)(.*?)$/is", $ord['custdata'], $match)) {
12572 $special_requests = $match[1];
12573 } elseif (preg_match("/(?:special request\s*)(.*?)$/is", $ord['custdata'], $match)) {
12574 $special_requests = $match[1];
12575 } elseif (preg_match("/(?:" . JText::translate('ORDER_SPREQUESTS') . ":\s*)(.*?)$/is", $ord['custdata'], $match)) {
12576 $special_requests = $match[1];
12577 }
12578 $paystr = '';
12579 if (!empty($ord['idpayment'])) {
12580 $payparts = explode('=', $ord['idpayment']);
12581 $paystr = $payparts[1];
12582 }
12583 $ordnumbstr = $ord['id'] . ' - ' . $ord['confirmnumber'] . (!empty($ord['idorderota']) ? ' (' . $ord['idorderota'] . ')' : '');
12584 $bookingSource = JText::translate('VBORDFROMSITE');
12585 if (!empty($ord['channel']) && !empty($ord['idorderota'])) {
12586 $chparts = explode('_', $ord['channel']);
12587 $bookingSource = ($chparts[1] ?? '') ?: $chparts[0];
12588 }
12589 $statusstr = '';
12590 if ($ord['status'] == 'confirmed') {
12591 $statusstr = JText::translate('VBCSVSTATUSCONFIRMED');
12592 } elseif ($ord['status'] == 'standby') {
12593 $statusstr = JText::translate('VBCSVSTATUSSTANDBY');
12594 } elseif ($ord['status'] == 'cancelled') {
12595 $statusstr = JText::translate('VBCSVSTATUSCANCELLED');
12596 }
12597 $totalstring = $usecurrencyname . ' ' . VikBooking::numberFormat($ord['total']);
12598 if ($ord['roomsnum'] > 1) {
12599 // take the cost for the individual room
12600 $totalstring = !empty($ord['cust_cost']) && $ord['cust_cost'] > 0 ? ($usecurrencyname . ' ' . VikBooking::numberFormat($ord['cust_cost'])) : ($usecurrencyname . ' ' . VikBooking::numberFormat($ord['room_cost']));
12601 }
12602 $totalpaidstring = $usecurrencyname . ' ' . VikBooking::numberFormat($ord['totpaid']);
12603 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12604 // total paid will be printed only for the last room booked
12605 $totalpaidstring = '';
12606 }
12607 $options_str = '';
12608 if (!empty($ord['optionals'])) {
12609 $stepo = explode(";", $ord['optionals']);
12610 foreach ($stepo as $roptkey => $oo) {
12611 if (!empty($oo)) {
12612 $stept = explode(":", $oo);
12613 if (array_key_exists($stept[0], $all_options)) {
12614 $actopt = $all_options[$stept[0]];
12615 $optpcent = false;
12616 if (!empty($actopt['ageintervals']) && $ord['children'] > 0 && strstr($stept[1], '-') != false) {
12617 $optagenames = VikBooking::getOptionIntervalsAges($actopt['ageintervals']);
12618 $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt['ageintervals']);
12619 $optageovrct = VikBooking::getOptionIntervalChildOverrides($actopt, $ord['adults'], $ord['children']);
12620 $child_num = VikBooking::getRoomOptionChildNumber($ord['optionals'], $actopt['id'], $roptkey, $ord['children']);
12621 $optagecosts = VikBooking::getOptionIntervalsCosts(isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $actopt['ageintervals']);
12622 $agestept = explode('-', $stept[1]);
12623 $stept[1] = $agestept[0];
12624 $chvar = $agestept[1];
12625 if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] > 0) {
12626 $optpcent = true;
12627 }
12628 $actopt['chageintv'] = $chvar;
12629 if (isset($optagenames[($chvar - 1)])) {
12630 $actopt['name'] .= ' ('.$optagenames[($chvar - 1)].')';
12631 }
12632 if (isset($optagecosts[($chvar - 1)])) {
12633 $realcost = (intval($actopt['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $booking_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
12634 } else {
12635 $realcost = 0;
12636 }
12637 } else {
12638 // VBO 1.11 - options percentage cost of the room total fee
12639 $optpcent = (int)$actopt['pcentroom'] ? true : $optpcent;
12640 //
12641 $realcost = (intval($actopt['perday']) == 1 ? ($actopt['cost'] * $booking_nights * $stept[1]) : ($actopt['cost'] * $stept[1]));
12642 }
12643 if ($actopt['maxprice'] > 0 && $realcost > $actopt['maxprice']) {
12644 $realcost=$actopt['maxprice'];
12645 if (intval($actopt['hmany']) == 1 && intval($stept[1]) > 1) {
12646 $realcost = $actopt['maxprice'] * $stept[1];
12647 }
12648 }
12649 $realcost = $actopt['perperson'] == 1 ? ($realcost * $ord['adults']) : $realcost;
12650
12651 /**
12652 * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
12653 *
12654 * @since 1.17.7 (J) - 1.7.7 (WP)
12655 */
12656 $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt, $ord, $ord]);
12657 if ($custom_calculation) {
12658 $realcost = (float) $custom_calculation[0];
12659 }
12660
12661 $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt['idiva']);
12662 $options_str .= ($stept[1] > 1 ? $stept[1]." " : "").$actopt['name'].": ".(!$optpcent ? $currencyname : '')." ".VikBooking::numberFormat($tmpopr).($optpcent ? ' %' : '')." \r\n";
12663 }
12664 }
12665 }
12666 }
12667
12668 // custom extra costs
12669 if (!empty($ord['extracosts'])) {
12670 $cur_extra_costs = json_decode($ord['extracosts'], true);
12671 foreach ($cur_extra_costs as $eck => $ecv) {
12672 $ecplustax = !empty($ecv['idtax']) ? VikBooking::sayOptionalsPlusIva($ecv['cost'], $ecv['idtax']) : $ecv['cost'];
12673 $options_str .= $ecv['name'].": ".$currencyname." ".VikBooking::numberFormat($ecplustax)." \r\n";
12674 }
12675 }
12676
12677 // taxes
12678 $taxes_str = '';
12679 if ($ord['tot_taxes'] > 0.00) {
12680 $taxes_str .= $usecurrencyname.' '.VikBooking::numberFormat($ord['tot_taxes']);
12681 if (!empty($ord['aliq']) && !empty($ord['breakdown'])) {
12682 $tax_breakdown = json_decode($ord['breakdown'], true);
12683 $tax_breakdown = is_array($tax_breakdown) && count($tax_breakdown) > 0 ? $tax_breakdown : array();
12684 if (count($tax_breakdown)) {
12685 foreach ($tax_breakdown as $tbkk => $tbkv) {
12686 $tax_break_cost = $ord['tot_taxes'] * floatval($tbkv['aliq']) / $ord['aliq'];
12687 $taxes_str .= "\r\n".$tbkv['name'].": ".$usecurrencyname.' '.VikBooking::numberFormat($tax_break_cost);
12688 }
12689 }
12690 }
12691 }
12692 if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12693 // total taxes will be printed only for the last room booked
12694 $taxes_str = '';
12695 }
12696
12697 // created by
12698 $created_by = '';
12699 if (!empty($ord['ujid'])) {
12700 $creator = new JUser($ord['ujid']);
12701 if (property_exists($creator, 'name')) {
12702 $created_by = $creator->name.' ('.$creator->username.')';
12703 }
12704 }
12705 if (empty($created_by) && !empty($ord['t_first_name'])) {
12706 $created_by = $ord['t_first_name'].' '.$ord['t_last_name'];
12707 }
12708
12709 // build CSV line data
12710 $line_data = [
12711 [
12712 'value' => $ord['id'],
12713 ],
12714 [
12715 'value' => date(str_replace("/", $datesep, $df), $ord['ts']),
12716 ],
12717 [
12718 'value' => date(str_replace("/", $datesep, $df), $booking_checkin),
12719 ],
12720 [
12721 'value' => date(str_replace("/", $datesep, $df), $booking_checkout),
12722 ],
12723 [
12724 'value' => $booking_nights,
12725 ],
12726 [
12727 'value' => $ord['name'],
12728 ],
12729 [
12730 'value' => $peoplestr,
12731 ],
12732 [
12733 'value' => $custinfostr,
12734 ],
12735 [
12736 'value' => $special_requests,
12737 ],
12738 [
12739 'value' => $ord['adminnotes'],
12740 ],
12741 [
12742 'value' => $created_by,
12743 ],
12744 [
12745 'value' => $ord['custmail'],
12746 ],
12747 [
12748 'value' => $ord['phone'],
12749 ],
12750 [
12751 'value' => $options_str,
12752 ],
12753 [
12754 'value' => $paystr,
12755 ],
12756 [
12757 'value' => $ordnumbstr,
12758 ],
12759 [
12760 'value' => $bookingSource,
12761 ],
12762 [
12763 'value' => $statusstr,
12764 ],
12765 [
12766 'value' => $totalstring,
12767 ],
12768 [
12769 'value' => $totalpaidstring,
12770 ],
12771 [
12772 'value' => $taxes_str,
12773 ],
12774 ];
12775
12776 if (empty($filterstatus) || $filterstatus === 'cancelled') {
12777 // obtain cancellation date for this booking
12778 $booking_canc_date = $cancellation_timestamps[$ord['id']] ?? '';
12779 if ($booking_canc_date) {
12780 $booking_canc_date = date(str_replace("/", $datesep, $df), $booking_canc_date);
12781 }
12782 // insert column for cancellation date at index 2
12783 array_splice($line_data, 2, 0, [['value' => $booking_canc_date]]);
12784 }
12785
12786 // push line for export
12787 $orderscsv[] = $line_data;
12788 }
12789
12790 // set CSV rows
12791 $report_obj->setReportRows($orderscsv);
12792
12793 // build lines to export
12794 $csvlines = $report_obj->getExportCSVLines($no_data = true);
12795
12796 // set export file name
12797 $report_obj->setExportCSVFileName('bookings_export_' . date('Y-m-d') . '.csv');
12798
12799 // force the download of the CSV file
12800 $report_obj->outputHeaders();
12801
12802 // send lines to output
12803 $report_obj->outputCSV($csvlines);
12804
12805 exit;
12806 }
12807
12808 public function exportcustomerslaunch() {
12809 $cid = VikRequest::getVar('cid', array(0));
12810 $dbo = JFactory::getDBO();
12811 $pnotes = VikRequest::getInt('notes', '', 'request');
12812 $pscanimg = VikRequest::getInt('scanimg', '', 'request');
12813 $ppin = VikRequest::getInt('pin', '', 'request');
12814 $pcountry = VikRequest::getString('country', '', 'request');
12815 $pfromdate = VikRequest::getString('fromdate', '', 'request');
12816 $ptodate = VikRequest::getString('todate', '', 'request');
12817 $pdatefilt = VikRequest::getInt('datefilt', '', 'request');
12818 $clauses = array();
12819 if (count($cid) > 0 && !empty($cid[0])) {
12820 $clauses[] = "`c`.`id` IN (".implode(', ', $cid).")";
12821 }
12822 if (!empty($pcountry)) {
12823 $clauses[] = "`c`.`country`=".$dbo->quote($pcountry);
12824 }
12825 $datescol = '`bk`.`ts`';
12826 if ($pdatefilt > 0) {
12827 if ($pdatefilt == 1) {
12828 $datescol = '`bk`.`ts`';
12829 } elseif ($pdatefilt == 2) {
12830 $datescol = '`bk`.`checkin`';
12831 } elseif ($pdatefilt == 3) {
12832 $datescol = '`bk`.`checkout`';
12833 }
12834 }
12835 if (!empty($pfromdate)) {
12836 $from_ts = VikBooking::getDateTimestamp($pfromdate, 0, 0);
12837 $clauses[] = $datescol.">=".$from_ts;
12838 }
12839 if (!empty($ptodate)) {
12840 $to_ts = VikBooking::getDateTimestamp($ptodate, 23, 59);
12841 $clauses[] = $datescol."<=".$to_ts;
12842 }
12843 //this query below is safe with the error #1055 when sql_mode=only_full_group_by
12844 $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`,".
12845 "(SELECT COUNT(*) FROM `#__vikbooking_customers_orders` AS `co` WHERE `co`.`idcustomer`=`c`.`id`) AS `tot_bookings`,".
12846 "`cy`.`country_3_code`,`cy`.`country_name` ".
12847 "FROM `#__vikbooking_customers` AS `c` LEFT JOIN `#__vikbooking_countries` `cy` ON `cy`.`country_3_code`=`c`.`country` ".
12848 "LEFT JOIN `#__vikbooking_customers_orders` `co` ON `co`.`idcustomer`=`c`.`id` ".
12849 "LEFT JOIN `#__vikbooking_orders` `bk` ON `bk`.`id`=`co`.`idorder`".
12850 (count($clauses) > 0 ? " WHERE ".implode(' AND ', $clauses) : "")."
12851 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` ".
12852 "ORDER BY `c`.`last_name` ASC;";
12853 $dbo->setQuery($q);
12854 $customers = $dbo->loadAssocList();
12855 if (!$customers) {
12856 VikError::raiseWarning('', JText::translate('VBONORECORDSCSVCUSTOMERS'));
12857 $mainframe = JFactory::getApplication();
12858 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
12859 exit;
12860 }
12861 $csvlines = [];
12862 $csvheadline = [
12863 'ID',
12864 JText::translate('VBCUSTOMERLASTNAME'),
12865 JText::translate('VBCUSTOMERFIRSTNAME'),
12866 JText::translate('VBCUSTOMEREMAIL'),
12867 JText::translate('VBCUSTOMERPHONE'),
12868 JText::translate('VBCUSTOMERADDRESS'),
12869 JText::translate('VBCUSTOMERCITY'),
12870 JText::translate('VBCUSTOMERZIP'),
12871 JText::translate('VBCUSTOMERCOUNTRY'),
12872 JText::translate('VBCUSTOMERGENDER'),
12873 JText::translate('ORDER_DBIRTH'),
12874 JText::translate('VBCUSTOMERTOTBOOKINGS'),
12875 ];
12876 if ($ppin > 0) {
12877 $csvheadline[] = JText::translate('VBCUSTOMERPIN');
12878 }
12879 if ($pscanimg > 0) {
12880 $csvheadline[] = JText::translate('VBCUSTOMERDOCTYPE');
12881 $csvheadline[] = JText::translate('VBCUSTOMERDOCNUM');
12882 $csvheadline[] = JText::translate('VBCUSTOMERDOCIMG');
12883 }
12884 if ($pnotes > 0) {
12885 $csvheadline[] = JText::translate('VBCUSTOMERNOTES');
12886 }
12887 $csvlines[] = $csvheadline;
12888 foreach ($customers as $customer) {
12889 $csvcustomerline = [
12890 $customer['id'],
12891 $customer['last_name'],
12892 $customer['first_name'],
12893 $customer['email'],
12894 $customer['phone'],
12895 $customer['address'],
12896 $customer['city'],
12897 $customer['zip'],
12898 $customer['country_name'],
12899 $customer['gender'],
12900 $customer['bdate'],
12901 $customer['tot_bookings'],
12902 ];
12903 if ($ppin > 0) {
12904 $csvcustomerline[] = $customer['pin'];
12905 }
12906 if ($pscanimg > 0) {
12907 $csvcustomerline[] = $customer['doctype'];
12908 $csvcustomerline[] = $customer['docnum'];
12909 $csvcustomerline[] = (!empty($customer['docimg']) ? VBO_ADMIN_URI.'resources/idscans/'.$customer['docimg'] : '');
12910 }
12911 if ($pnotes > 0) {
12912 $csvcustomerline[] = $customer['notes'];
12913 }
12914 $csvlines[] = $csvcustomerline;
12915 }
12916 header("Content-type: text/csv");
12917 header("Cache-Control: no-store, no-cache");
12918 header('Content-Disposition: attachment; filename="customers_export_'.(!empty($pcountry) ? strtolower($pcountry).'_' : '').date('Y-m-d').'.csv"');
12919 $outstream = fopen("php://output", 'w');
12920 foreach ($csvlines as $csvline) {
12921 fputcsv($outstream, $csvline, $separator = ',', $enclosure = '"', $escape = '');
12922 }
12923 fclose($outstream);
12924 exit;
12925 }
12926
12927 public function renewsession() {
12928 /*
12929 * @wponly
12930 * We just destroy the session
12931 */
12932 JSessionHandler::destroy();
12933 $mainframe = JFactory::getApplication();
12934 $mainframe->redirect("index.php?option=com_vikbooking&task=config");
12935 }
12936
12937 public function trackings() {
12938 VikBookingHelper::printHeader("trackings");
12939
12940 VikRequest::setVar('view', VikRequest::getCmd('view', 'trackings'));
12941
12942 parent::display();
12943
12944 if (VikBooking::showFooter()) {
12945 VikBookingHelper::printFooter();
12946 }
12947 }
12948
12949 public function trkconfig() {
12950 VikBookingHelper::printHeader("trackings");
12951
12952 VikRequest::setVar('view', VikRequest::getCmd('view', 'trkconfig'));
12953
12954 parent::display();
12955
12956 if (VikBooking::showFooter()) {
12957 VikBookingHelper::printFooter();
12958 }
12959 }
12960
12961 public function savetrkconfigstay() {
12962 if (!JSession::checkToken()) {
12963 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
12964 }
12965 $this->do_savetrkconfig(true);
12966 }
12967
12968 public function savetrkconfig() {
12969 if (!JSession::checkToken()) {
12970 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
12971 }
12972 $this->do_savetrkconfig();
12973 }
12974
12975 private function do_savetrkconfig($stay = false) {
12976 $dbo = JFactory::getDBO();
12977 $trkenabled = VikRequest::getInt('trkenabled', 0, 'request');
12978 $trkenabled = $trkenabled == 1 ? 1 : 0;
12979 $trkcookierfrdur = VikRequest::getFloat('trkcookierfrdur', 1, 'request');
12980 $trkcookierfrdur = $trkcookierfrdur < 0.1 ? 1 : $trkcookierfrdur;
12981 $trkcampname = VikRequest::getVar('trkcampname', array());
12982 $trkcampkey = VikRequest::getVar('trkcampkey', array());
12983 $trkcampval = VikRequest::getVar('trkcampval', array());
12984 $trkcampaigns = array();
12985 foreach ($trkcampname as $k => $v) {
12986 if (empty($trkcampkey[$k])) {
12987 continue;
12988 }
12989 $trkcampkey[$k] = str_replace(' ', '', trim($trkcampkey[$k]));
12990 $name = !empty($v) ? $v : date('Y-m-d').' '.(count($trkcampaigns) + 1);
12991 $trkcampaigns[$trkcampkey[$k]] = array(
12992 'key' => $trkcampkey[$k],
12993 'value' => $trkcampval[$k],
12994 'name' => $name,
12995 );
12996 }
12997
12998 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($trkenabled)." WHERE `param`='trkenabled';";
12999 $dbo->setQuery($q);
13000 $dbo->execute();
13001 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($trkcookierfrdur)." WHERE `param`='trkcookierfrdur';";
13002 $dbo->setQuery($q);
13003 $dbo->execute();
13004 $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($trkcampaigns))." WHERE `param`='trkcampaigns';";
13005 $dbo->setQuery($q);
13006 $dbo->execute();
13007
13008 $mainframe = JFactory::getApplication();
13009 $mainframe->redirect("index.php?option=com_vikbooking&task=".($stay ? 'trkconfig' : 'trackings'));
13010 }
13011
13012 public function modtracking() {
13013 $dbo = JFactory::getDbo();
13014 $cid = VikRequest::getVar('cid', array());
13015 foreach ($cid as $id) {
13016 if (!empty($id)) {
13017 $q = "SELECT `id`,`published` FROM `#__vikbooking_trackings` WHERE `id`=".(int)$id.";";
13018 $dbo->setQuery($q);
13019 $dbo->execute();
13020 if ($dbo->getNumRows()) {
13021 $data = $dbo->loadAssoc();
13022 $q = "UPDATE `#__vikbooking_trackings` SET `published`=".($data['published'] ? '0' : '1')." WHERE `id`=".(int)$data['id'].";";
13023 $dbo->setQuery($q);
13024 $dbo->execute();
13025 }
13026 }
13027 }
13028 $mainframe = JFactory::getApplication();
13029 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13030 }
13031
13032 public function removetrackings()
13033 {
13034 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
13035 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13036 }
13037
13038 $ids = VikRequest::getVar('cid', array());
13039 $dbo = JFactory::getDbo();
13040
13041 foreach ($ids as $d) {
13042 $q = "DELETE FROM `#__vikbooking_trackings` WHERE `id`=".(int)$d.";";
13043 $dbo->setQuery($q);
13044 $dbo->execute();
13045 $q = "DELETE FROM `#__vikbooking_tracking_infos` WHERE `idtracking`=".(int)$d.";";
13046 $dbo->setQuery($q);
13047 $dbo->execute();
13048 }
13049
13050 $mainframe = JFactory::getApplication();
13051 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13052 }
13053
13054 /**
13055 * Invokes the Tracker class to obtain
13056 * geo information about the IP addresses.
13057 * This task is called via ajax.
13058 *
13059 * @since 1.11
13060 */
13061 public function getgeoinfo() {
13062 $ips = VikRequest::getVar('ips', array());
13063 if (!count($ips)) {
13064 echo 'e4j.error.empty IPs';
13065 exit;
13066 }
13067
13068 // require the Tracker class without instantiating the object
13069 VikBooking::getTracker(true);
13070 $geo_info = VikBookingTracker::getIpGeoInfo($ips);
13071
13072 if ($geo_info === false) {
13073 echo 'e4j.error.Tracker error, could not get geo info from IPs';
13074 exit;
13075 }
13076
13077 // update db values and compose response
13078 $dbo = JFactory::getDbo();
13079 $resp = array();
13080 foreach ($geo_info as $id => $geo) {
13081 if (is_null($geo) || $geo === false) {
13082 continue;
13083 }
13084 // compose geo info string
13085 $geovals = array();
13086 if (!empty($geo['city'])) {
13087 array_push($geovals, $geo['city']);
13088 }
13089 if (!empty($geo['region'])) {
13090 array_push($geovals, $geo['region']);
13091 }
13092 $threecode = '';
13093 $cname = '';
13094 if (!empty($geo['country'])) {
13095 // returned country is a 2-char code, get the 3-char country code
13096 $q = "SELECT `country_3_code`,`country_name` FROM `#__vikbooking_countries` WHERE `country_2_code`=".$dbo->quote($geo['country']).";";
13097 $dbo->setQuery($q);
13098 $dbo->execute();
13099 if ($dbo->getNumRows()) {
13100 $cinfo = $dbo->loadAssoc();
13101 $threecode = $cinfo['country_3_code'];
13102 $cname = $cinfo['country_name'];
13103 }
13104 array_push($geovals, (empty($cname) ? $geo['country'] : $cname));
13105 }
13106
13107 // full geo information string
13108 $geoinfostr = implode(', ', $geovals);
13109
13110 // push data to the response pool
13111 $resp[$id] = array();
13112 $resp[$id]['geo'] = $geoinfostr;
13113 if (!empty($cname)) {
13114 $resp[$id]['country'] = $cname;
13115 }
13116 if (!empty($threecode)) {
13117 $resp[$id]['country3'] = $threecode;
13118 }
13119
13120 // update main tracking record
13121 $q = "UPDATE `#__vikbooking_trackings` SET `geo`=".$dbo->quote($geoinfostr).(!empty($threecode) ? ', `country`='.$dbo->quote($threecode) : '')." WHERE `id`=".(int)$id.";";
13122 $dbo->setQuery($q);
13123 $dbo->execute();
13124 }
13125
13126 // output the JSON response
13127 echo json_encode($resp);
13128 exit;
13129 }
13130
13131 /**
13132 * Counts the orphan dates for all published rooms
13133 * depending on their restrictions and booked dates.
13134 * By default, the task takes up to 3 months ahead.
13135 * It is possible to filter the request by rooms and months.
13136 * This task should be called via ajax.
13137 *
13138 * @since 1.11
13139 */
13140 public function orphanscount()
13141 {
13142 $dbo = JFactory::getDbo();
13143 $orphans = array();
13144
13145 $nowdf = VikBooking::getDateFormat();
13146 if ($nowdf == "%d/%m/%Y") {
13147 $df = 'd/m/Y';
13148 } elseif ($nowdf == "%m/%d/%Y") {
13149 $df = 'm/d/Y';
13150 } else {
13151 $df = 'Y/m/d';
13152 }
13153
13154 // global min los
13155 $glob_minlos = VikBooking::getDefaultNightsCalendar();
13156 $glob_minlos = $glob_minlos < 1 ? 1 : $glob_minlos;
13157
13158 // rooms and dates
13159 $roomids = VikRequest::getVar('roomids', array(), 'request', 'int');
13160 $months = VikRequest::getInt('months', 3, 'request');
13161 $from = VikRequest::getString('from', '', 'request');
13162 $today = strtotime(date('Y').'-'.date('m').'-'.date('d'));
13163 if (!empty($from)) {
13164 $fromts = VikBooking::getDateTimestamp($from, 0, 0);
13165 if (!empty($fromts)) {
13166 // custom starting date
13167 $today = $fromts;
13168 }
13169 }
13170 $until = strtotime("+{$months} months", $today);
13171
13172 // load all rooms
13173 $rooms = array();
13174 $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms` WHERE `avail`=1".(count($roomids) ? ' AND `id` IN ('.implode(', ', $roomids).')' : '').";";
13175 $dbo->setQuery($q);
13176 $dbo->execute();
13177 if ($dbo->getNumRows()) {
13178 $allrooms = $dbo->loadAssocList();
13179 foreach ($allrooms as $r) {
13180 $rooms[$r['id']] = $r;
13181 }
13182 }
13183 if (!count($rooms)) {
13184 // no rooms found, exit
13185 echo json_encode($orphans);
13186 exit;
13187 }
13188
13189 // load availabilities
13190 $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.");";
13191 $dbo->setQuery($q);
13192 $dbo->execute();
13193 if (!$dbo->getNumRows()) {
13194 // no booked dates found, exit
13195 echo json_encode($orphans);
13196 exit;
13197 }
13198 $busy = $dbo->loadAssocList();
13199
13200 // sort booked dates by room id
13201 $rooms_busy = array();
13202 foreach ($busy as $b) {
13203 if (!isset($rooms_busy[$b['idroom']])) {
13204 $rooms_busy[$b['idroom']] = array();
13205 }
13206 array_push($rooms_busy[$b['idroom']], $b);
13207 }
13208
13209 // load restrictions
13210 $rooms_restr = array();
13211 foreach ($rooms as $rid => $r) {
13212 $restrictions = VikBooking::loadRestrictions(true, array($rid));
13213 if (count($restrictions)) {
13214 $rooms_restr[$rid] = $restrictions;
13215 }
13216 }
13217 if (!count($rooms_restr) && $glob_minlos < 2) {
13218 // no restrictions found and minlos=1, exit
13219 echo json_encode($orphans);
13220 exit;
13221 }
13222
13223 // count availability and minlos per day
13224 $rooms_data = array();
13225 foreach ($rooms as $rid => $r) {
13226 $rooms_data[$rid] = array(
13227 'avail' => array(),
13228 'restr' => array()
13229 );
13230 $nowts = getdate($today);
13231 while ($nowts[0] <= $until) {
13232 $dateind = date('Y-m-d', $nowts[0]);
13233
13234 // remaining availability
13235 if (!isset($rooms_busy[$rid])) {
13236 // no bookings for this room, set full availability for this day
13237 $rooms_data[$rid]['avail'][] = array(
13238 'dt' => $dateind,
13239 'units' => $r['units']
13240 );
13241 } else {
13242 // check remaining availability for this day
13243 $totfound = 0;
13244 foreach ($rooms_busy[$rid] as $b) {
13245 $tmpone = getdate($b['checkin']);
13246 $rit = ($tmpone['mon'] < 10 ? "0".$tmpone['mon'] : $tmpone['mon'])."/".($tmpone['mday'] < 10 ? "0".$tmpone['mday'] : $tmpone['mday'])."/".$tmpone['year'];
13247 $ritts = strtotime($rit);
13248 $tmptwo = getdate($b['checkout']);
13249 $con = ($tmptwo['mon'] < 10 ? "0".$tmptwo['mon'] : $tmptwo['mon'])."/".($tmptwo['mday'] < 10 ? "0".$tmptwo['mday'] : $tmptwo['mday'])."/".$tmptwo['year'];
13250 $conts = strtotime($con);
13251 if ($nowts[0] >= $ritts && $nowts[0] < $conts) {
13252 $totfound++;
13253 }
13254 }
13255 $totfound = $totfound > $r['units'] ? $r['units'] : $totfound;
13256 $rooms_data[$rid]['avail'][] = array(
13257 'dt' => $dateind,
13258 'units' => ($r['units'] - $totfound)
13259 );
13260 }
13261
13262 // restrictions
13263 if (!isset($rooms_restr[$rid])) {
13264 // no restrictions for this room, set global minlos for this day
13265 $rooms_data[$rid]['restr'][] = array(
13266 'dt' => $dateind,
13267 'minlos' => $glob_minlos
13268 );
13269 } else {
13270 // get restriction for this day
13271 $today_tsin = mktime(0, 0, 0, $nowts['mon'], $nowts['mday'], $nowts['year']);
13272 $today_tsout = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
13273
13274 $restr = VikBooking::parseSeasonRestrictions($today_tsin, $today_tsout, 1, $rooms_restr[$rid]);
13275 $minlos = count($restr) ? $restr['minlos'] : $glob_minlos;
13276
13277 $rooms_data[$rid]['restr'][] = array(
13278 'dt' => $dateind,
13279 'minlos' => $minlos
13280 );
13281 }
13282
13283 // next loop
13284 $dayts = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
13285 $nowts = getdate($dayts);
13286 }
13287 }
13288
13289 // week days and months labels
13290 $days_labels = array(
13291 JText::translate('VBSUNDAY'),
13292 JText::translate('VBMONDAY'),
13293 JText::translate('VBTUESDAY'),
13294 JText::translate('VBWEDNESDAY'),
13295 JText::translate('VBTHURSDAY'),
13296 JText::translate('VBFRIDAY'),
13297 JText::translate('VBSATURDAY')
13298 );
13299 $months_labels = array(
13300 JText::translate('VBMONTHONE'),
13301 JText::translate('VBMONTHTWO'),
13302 JText::translate('VBMONTHTHREE'),
13303 JText::translate('VBMONTHFOUR'),
13304 JText::translate('VBMONTHFIVE'),
13305 JText::translate('VBMONTHSIX'),
13306 JText::translate('VBMONTHSEVEN'),
13307 JText::translate('VBMONTHEIGHT'),
13308 JText::translate('VBMONTHNINE'),
13309 JText::translate('VBMONTHTEN'),
13310 JText::translate('VBMONTHELEVEN'),
13311 JText::translate('VBMONTHTWELVE')
13312 );
13313
13314 // orphan dates calculation method
13315 $calc_method = VikBooking::orphansCalculation();
13316
13317 // parse data and build orphans if any
13318 foreach ($rooms_data as $rid => $data) {
13319 foreach ($data['avail'] as $ind => $av) {
13320 if (!isset($data['restr'][$ind]) || $av['units'] < 1) {
13321 // continue, no restriction set or no availability for this day
13322 continue;
13323 }
13324 if ($data['restr'][$ind]['minlos'] < 2) {
13325 // continue, no min los > 1 set for this day
13326 continue;
13327 }
13328 // check if any night after today, until min los, is fully booked
13329 $hasorphans = false;
13330 $forward_count = 0;
13331 for ($i = 1; $i < $data['restr'][$ind]['minlos']; $i++) {
13332 if (!isset($data['avail'][($ind + $i)])) {
13333 // break loop, no info for this day after
13334 break;
13335 }
13336 if ($data['avail'][($ind + $i)]['units'] > 0) {
13337 // continue, availability found for tomorrow, we need a non available next-day
13338 continue;
13339 }
13340 // orphan found
13341 $hasorphans = true;
13342 $forward_count = $i;
13343 break;
13344 }
13345
13346 /**
13347 * Backward calculation method only if "prevnext".
13348 *
13349 * @since 1.3.0
13350 */
13351 $backward_count = 0;
13352 for ($i = 1; $i <= $data['restr'][$ind]['minlos']; $i++) {
13353 if (!isset($data['avail'][($ind - $i)])) {
13354 // break loop, no info for this prev day
13355 break;
13356 }
13357 if ($data['avail'][($ind - $i)]['units'] > 0) {
13358 // increase free nights going backward
13359 $backward_count++;
13360 }
13361 }
13362 if ($calc_method == 'prevnext' && $hasorphans && $backward_count > 0 && ($backward_count >= $data['restr'][$ind]['minlos'] || ($backward_count + $forward_count) >= $data['restr'][$ind]['minlos'])) {
13363 // this should not be an orphan date because of enough free days back, or enough free days in between
13364 $hasorphans = false;
13365 }
13366 //
13367
13368 if ($hasorphans) {
13369 // 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
13370 if (!isset($orphans[$rid])) {
13371 $orphans[$rid] = array(
13372 'name' => $rooms[$rid]['name'],
13373 'dates' => array(),
13374 'rdates' => array(),
13375 'linkd' => date($df, strtotime($av['dt']))
13376 );
13377 }
13378 array_push($orphans[$rid]['dates'], $av['dt']);
13379 // build the value for the readable date
13380 $dtinfo = getdate(strtotime($av['dt']));
13381 $rdate = $days_labels[$dtinfo['wday']] . ', ' . $months_labels[($dtinfo['mon'] - 1)] . ' ' . $dtinfo['mday'] . ' ' . $dtinfo['year'];
13382 array_push($orphans[$rid]['rdates'], $rdate);
13383 }
13384 }
13385 }
13386
13387 // output response
13388 echo json_encode($orphans);
13389 exit;
13390 }
13391
13392 public function tableaux() {
13393 VikBookingHelper::printHeader("tableaux");
13394
13395 VikRequest::setVar('view', VikRequest::getCmd('view', 'tableaux'));
13396
13397 parent::display();
13398
13399 if (VikBooking::showFooter()) {
13400 VikBookingHelper::printFooter();
13401 }
13402 }
13403
13404 public function operators() {
13405 VikBookingHelper::printHeader("operators");
13406
13407 VikRequest::setVar('view', VikRequest::getCmd('view', 'operators'));
13408
13409 parent::display();
13410
13411 if (VikBooking::showFooter()) {
13412 VikBookingHelper::printFooter();
13413 }
13414 }
13415
13416 public function newoperator() {
13417 VikBookingHelper::printHeader("operators");
13418
13419 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoperator'));
13420
13421 parent::display();
13422
13423 if (VikBooking::showFooter()) {
13424 VikBookingHelper::printFooter();
13425 }
13426 }
13427
13428 public function editoperator() {
13429 VikBookingHelper::printHeader("operators");
13430
13431 VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoperator'));
13432
13433 parent::display();
13434
13435 if (VikBooking::showFooter()) {
13436 VikBookingHelper::printFooter();
13437 }
13438 }
13439
13440 public function updateoperator()
13441 {
13442 if (!JSession::checkToken()) {
13443 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13444 }
13445
13446 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
13447 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13448 }
13449
13450 $this->do_updateoperator();
13451 }
13452
13453 public function updateoperatorstay()
13454 {
13455 if (!JSession::checkToken()) {
13456 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13457 }
13458
13459 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
13460 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13461 }
13462
13463 $this->do_updateoperator(true);
13464 }
13465
13466 private function do_updateoperator($stay = false)
13467 {
13468 $dbo = JFactory::getDbo();
13469 $app = JFactory::getApplication();
13470 $pfirst_name = VikRequest::getString('first_name', '', 'request');
13471 $plast_name = VikRequest::getString('last_name', '', 'request');
13472 $pemail = VikRequest::getString('email', '', 'request');
13473 $pphone = VikRequest::getString('phone', '', 'request');
13474 $pcode = VikRequest::getString('code', '', 'request');
13475 $pujid = VikRequest::getInt('ujid', '', 'request');
13476 $pwhere = VikRequest::getInt('where', '', 'request');
13477
13478 $work_days_week = (array) $app->input->get('work_days_week', [], 'array');
13479 $work_days_exceptions = (array) $app->input->get('work_days_exceptions', [], 'array');
13480
13481 // normalize to linear arrays
13482 $work_days_week_schedule = array_combine(array_keys($work_days_week), array_values($work_days_week));
13483 $work_days_week = [];
13484 foreach ($work_days_week_schedule as $wday => $whours) {
13485 $work_days_week[] = [
13486 'wday' => $wday,
13487 'hours' => $whours,
13488 ];
13489 }
13490 foreach ($work_days_exceptions as &$wexceptions) {
13491 if (is_scalar($wexceptions)) {
13492 $wexceptions = json_decode($wexceptions, true);
13493 }
13494 }
13495 unset($wexceptions);
13496
13497 if (!empty($pfirst_name) && !empty($pemail) && !empty($pcode)) {
13498 $q = "SELECT * FROM `#__vikbooking_operators` WHERE `id`=".(int)$pwhere." LIMIT 1;";
13499 $dbo->setQuery($q);
13500 $customer = $dbo->loadAssoc();
13501 if (!$customer) {
13502 $app->redirect("index.php?option=com_vikbooking&task=operators");
13503 exit;
13504 }
13505
13506 $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;";
13507 $dbo->setQuery($q);
13508 $ex_operator = $dbo->loadAssoc();
13509 if (!$ex_operator) {
13510 // update fingerprint for the operator
13511 $fingpt = md5($pwhere . $pemail);
13512
13513 /**
13514 * Operator profile picture (URL or uploaded file).
13515 *
13516 * @since 1.16.9 (J) - 1.6.9 (WP)
13517 */
13518 $operator_pic = VikRequest::getString('pic', '', 'request');
13519 $operator_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
13520 if (is_array($operator_pic_img) && !empty($operator_pic_img['name'])) {
13521 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($operator_pic_img['name'])));
13522 $src = $operator_pic_img['tmp_name'];
13523 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
13524 $j = "";
13525 if (is_file($dest.$filename)) {
13526 $j = rand(1, 99999);
13527 while (is_file($dest . $j .$filename)) {
13528 $j++;
13529 }
13530 }
13531 $finaldest = $dest . $j . $filename;
13532 $check = getimagesize($operator_pic_img['tmp_name']);
13533 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
13534 if (VikBooking::uploadFile($src, $finaldest)) {
13535 $operator_pic = $j . $filename;
13536 } else {
13537 VikError::raiseWarning('', 'Error while uploading image');
13538 }
13539 } else {
13540 VikError::raiseWarning('', 'Uploaded file is not an Image');
13541 }
13542 }
13543
13544 // update record
13545 $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;
13546 $dbo->setQuery($q);
13547 $dbo->execute();
13548 $app->enqueueMessage(JText::translate('VBOPERATORSAVED'));
13549 } else {
13550 //email already exists
13551 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>');
13552 $app->redirect("index.php?option=com_vikbooking&task=editoperator&cid[]=".$pwhere);
13553 exit;
13554 }
13555 } else {
13556 VikError::raiseWarning('', JText::translate('VBERROPERATORDATA'));
13557 }
13558
13559 if ($stay) {
13560 $app->redirect("index.php?option=com_vikbooking&task=editoperator&cid[]=".$pwhere);
13561 } else {
13562 $app->redirect("index.php?option=com_vikbooking&task=operators");
13563 }
13564
13565 $app->close();
13566 }
13567
13568 public function saveoperator()
13569 {
13570 if (!JSession::checkToken()) {
13571 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13572 }
13573
13574 $dbo = JFactory::getDbo();
13575 $app = JFactory::getApplication();
13576 $pfirst_name = VikRequest::getString('first_name', '', 'request');
13577 $plast_name = VikRequest::getString('last_name', '', 'request');
13578 $pemail = VikRequest::getString('email', '', 'request');
13579 $pphone = VikRequest::getString('phone', '', 'request');
13580 $pcode = VikRequest::getString('code', '', 'request');
13581 $pujid = VikRequest::getInt('ujid', '', 'request');
13582
13583 $work_days_week = (array) $app->input->get('work_days_week', [], 'array');
13584 $work_days_exceptions = (array) $app->input->get('work_days_exceptions', [], 'array');
13585
13586 // normalize to linear arrays
13587 $work_days_week_schedule = array_combine(array_keys($work_days_week), array_values($work_days_week));
13588 $work_days_week = [];
13589 foreach ($work_days_week_schedule as $wday => $whours) {
13590 $work_days_week[] = [
13591 'wday' => $wday,
13592 'hours' => $whours,
13593 ];
13594 }
13595 foreach ($work_days_exceptions as &$wexceptions) {
13596 if (is_scalar($wexceptions)) {
13597 $wexceptions = json_decode($wexceptions, true);
13598 }
13599 }
13600 unset($wexceptions);
13601
13602 if (!empty($pfirst_name) && !empty($pemail) && !empty($pcode)) {
13603 $q = "SELECT * FROM `#__vikbooking_operators` WHERE `email`=".$dbo->quote($pemail)." OR ".(!empty($pcode) ? "`code`=".$dbo->quote($pcode) : "`ujid`=".$dbo->quote($pujid))." LIMIT 1;";
13604 $dbo->setQuery($q);
13605 $ex_operator = $dbo->loadAssoc();
13606 if (!$ex_operator) {
13607 /**
13608 * Operator profile picture (URL or uploaded file).
13609 *
13610 * @since 1.16.9 (J) - 1.6.9 (WP)
13611 */
13612 $operator_pic = VikRequest::getString('pic', '', 'request');
13613 $operator_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
13614 if (is_array($operator_pic_img) && !empty($operator_pic_img['name'])) {
13615 $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($operator_pic_img['name'])));
13616 $src = $operator_pic_img['tmp_name'];
13617 $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
13618 $j = "";
13619 if (is_file($dest.$filename)) {
13620 $j = rand(1, 99999);
13621 while (is_file($dest . $j .$filename)) {
13622 $j++;
13623 }
13624 }
13625 $finaldest = $dest . $j . $filename;
13626 $check = getimagesize($operator_pic_img['tmp_name']);
13627 if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
13628 if (VikBooking::uploadFile($src, $finaldest)) {
13629 $operator_pic = $j . $filename;
13630 } else {
13631 VikError::raiseWarning('', 'Error while uploading image');
13632 }
13633 } else {
13634 VikError::raiseWarning('', 'Uploaded file is not an Image');
13635 }
13636 }
13637
13638 $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') . ");";
13639 $dbo->setQuery($q);
13640 $dbo->execute();
13641 $lid = $dbo->insertid();
13642 if (!empty($lid)) {
13643 $app->enqueueMessage(JText::translate('VBOPERATORSAVED'));
13644 // generate fingerprint for the operator
13645 $q = "UPDATE `#__vikbooking_operators` SET `fingpt`=".$dbo->q(md5($lid.$pemail))." WHERE `id`=".(int)$lid.";";
13646 $dbo->setQuery($q);
13647 $dbo->execute();
13648 }
13649 } else {
13650 // email already exists
13651 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>');
13652 }
13653 } else {
13654 VikError::raiseWarning('', JText::translate('VBERROPERATORDATA'));
13655 }
13656
13657 $app->redirect("index.php?option=com_vikbooking&task=operators");
13658 $app->close();
13659 }
13660
13661 public function removeoperators()
13662 {
13663 if (!JSession::checkToken()) {
13664 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13665 }
13666
13667 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
13668 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13669 }
13670
13671 $ids = VikRequest::getVar('cid', array(0));
13672 if ($ids) {
13673 $dbo = JFactory::getDBO();
13674 foreach ($ids as $d) {
13675 $q = "DELETE FROM `#__vikbooking_operators` WHERE `id`=".(int)$d.";";
13676 $dbo->setQuery($q);
13677 $dbo->execute();
13678 }
13679 }
13680 $mainframe = JFactory::getApplication();
13681 $mainframe->redirect("index.php?option=com_vikbooking&task=operators");
13682 }
13683
13684 public function canceloperator() {
13685 $mainframe = JFactory::getApplication();
13686 $mainframe->redirect("index.php?option=com_vikbooking&task=operators");
13687 }
13688
13689 public function cancelcrons() {
13690 $mainframe = JFactory::getApplication();
13691 $mainframe->redirect("index.php?option=com_vikbooking&task=crons");
13692 }
13693
13694 public function cancelpackages() {
13695 $mainframe = JFactory::getApplication();
13696 $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
13697 }
13698
13699 public function cancelcustomer() {
13700 $mainframe = JFactory::getApplication();
13701 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
13702 if (!empty($pgoto)) {
13703 $mainframe->redirect(base64_decode($pgoto));
13704 exit;
13705 }
13706 $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
13707 }
13708
13709 public function cancelbusyvcm() {
13710 $mainframe = JFactory::getApplication();
13711 $mainframe->redirect("index.php?option=com_vikchannelmanager&task=oversight");
13712 }
13713
13714 public function cancelrestriction() {
13715 $mainframe = JFactory::getApplication();
13716 $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
13717 }
13718
13719 public function cancelcoupon() {
13720 $mainframe = JFactory::getApplication();
13721 $mainframe->redirect("index.php?option=com_vikbooking&task=coupons");
13722 }
13723
13724 public function cancelcustomf() {
13725 $mainframe = JFactory::getApplication();
13726 $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
13727 }
13728
13729 public function cancelpayment() {
13730 $mainframe = JFactory::getApplication();
13731 $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
13732 }
13733
13734 public function cancelseason() {
13735 $mainframe = JFactory::getApplication();
13736 $mainframe->redirect("index.php?option=com_vikbooking&task=seasons");
13737 }
13738
13739 public function goconfig() {
13740 $mainframe = JFactory::getApplication();
13741 $mainframe->redirect("index.php?option=com_vikbooking&task=config");
13742 }
13743
13744 public function canceledorder() {
13745 $pgoto = VikRequest::getString('goto', 'orders', 'request');
13746 $mainframe = JFactory::getApplication();
13747 $mainframe->redirect("index.php?option=com_vikbooking&task=" . $pgoto);
13748 }
13749
13750 public function cancelbusy() {
13751 $pidorder = VikRequest::getString('idorder', '', 'request');
13752 $pgoto = VikRequest::getString('goto', '', 'request');
13753 $mainframe = JFactory::getApplication();
13754 $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pidorder.($pgoto == 'overv' ? '&goto=overv' : ''));
13755 }
13756
13757 public function canceloverv() {
13758 $mainframe = JFactory::getApplication();
13759 $mainframe->redirect("index.php?option=com_vikbooking&task=overv");
13760 }
13761
13762 public function canceltableaux() {
13763 $mainframe = JFactory::getApplication();
13764 $mainframe->redirect("index.php?option=com_vikbooking&task=tableaux");
13765 }
13766
13767 public function cancelcalendar() {
13768 $pidroom = VikRequest::getString('idroom', '', 'request');
13769 $mainframe = JFactory::getApplication();
13770 $mainframe->redirect("index.php?option=com_vikbooking&task=calendar&cid[]=".$pidroom);
13771 }
13772
13773 public function canceloptionals() {
13774 $mainframe = JFactory::getApplication();
13775 $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
13776 }
13777
13778 public function cancel() {
13779 $mainframe = JFactory::getApplication();
13780 $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
13781 }
13782
13783 public function cancelcarat() {
13784 $mainframe = JFactory::getApplication();
13785 $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
13786 }
13787
13788 public function cancelcat() {
13789 $mainframe = JFactory::getApplication();
13790 $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
13791 }
13792
13793 public function cancelprice() {
13794 $mainframe = JFactory::getApplication();
13795 $mainframe->redirect("index.php?option=com_vikbooking&task=prices");
13796 }
13797
13798 public function canceliva() {
13799 $mainframe = JFactory::getApplication();
13800 $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
13801 }
13802
13803 public function canceltrk() {
13804 $mainframe = JFactory::getApplication();
13805 $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13806 }
13807
13808 public function canceldash() {
13809 $mainframe = JFactory::getApplication();
13810 $mainframe->redirect("index.php?option=com_vikbooking");
13811 }
13812
13813 public function cancelinvoice() {
13814 $mainframe = JFactory::getApplication();
13815 $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
13816 if (!empty($pgoto)) {
13817 $mainframe->redirect(base64_decode($pgoto));
13818 exit;
13819 }
13820 $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
13821 }
13822
13823 /**
13824 * AJAX upload the customer documents.
13825 *
13826 * @return void
13827 *
13828 * @throws Exception
13829 */
13830 public function upload_customer_document()
13831 {
13832 $input = JFactory::getApplication()->input;
13833 $dbo = JFactory::getDbo();
13834
13835 $customer_id = $input->getUint('customer', 0);
13836
13837 $result = new stdClass;
13838 $result->status = 0;
13839
13840 try
13841 {
13842 $q = $dbo->getQuery(true)
13843 ->select($dbo->qn(array(
13844 'id',
13845 'first_name',
13846 'last_name',
13847 'email',
13848 'docsfolder',
13849 )))
13850 ->from($dbo->qn('#__vikbooking_customers'))
13851 ->where($dbo->qn('id') . ' = ' . $customer_id);
13852
13853 $dbo->setQuery($q, 0, 1);
13854 $dbo->execute();
13855
13856 if (!$dbo->getNumRows())
13857 {
13858 throw new Exception(sprintf('Customer [%d] not found', $customer_id), 404);
13859 }
13860
13861 $customer = $dbo->loadObject();
13862
13863 // fetch documents folder path
13864 $dirpath = VBO_CUSTOMERS_PATH . DIRECTORY_SEPARATOR;
13865
13866 // check if we have a valid directory
13867 if (empty($customer->docsfolder) || !is_dir($dirpath . $customer->docsfolder))
13868 {
13869 // randomize string
13870 $customer->seed = uniqid();
13871
13872 // create blocks for hashed folder
13873 $parts = [
13874 $customer->first_name,
13875 $customer->last_name,
13876 md5(serialize($customer)),
13877 ];
13878
13879 // join fetched parts
13880 $customer->docsfolder = JFilterOutput::stringURLSafe(implode('-', array_filter($parts)));
13881
13882 if (strlen($customer->docsfolder) < 16)
13883 {
13884 throw new Exception('Possible security breach. Please specify the most details as possible.', 400);
13885 }
13886
13887 jimport('joomla.filesystem.folder');
13888
13889 // create a folder for this customer
13890 $created = JFolder::create($dirpath . $customer->docsfolder);
13891
13892 if (!$created)
13893 {
13894 throw new Exception(sprintf('Unable to create the folder [%s]', $dirpath . $customer->docsfolder), 403);
13895 }
13896
13897 unset($customer->seed);
13898
13899 // update docs folder
13900 $dbo->updateObject('#__vikbooking_customers', $customer, 'id');
13901 }
13902
13903 // get file from request
13904 $file = $input->files->get('file', array(), 'array');
13905
13906 // try to upload the file
13907 $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');
13908 $result->status = 1;
13909
13910 $result->size = JHtml::fetch('number.bytes', filesize($result->path), 'auto', 0);
13911 $result->url = str_replace(DIRECTORY_SEPARATOR, '/', str_replace(VBO_CUSTOMERS_PATH . DIRECTORY_SEPARATOR, VBO_CUSTOMERS_URI, $result->path));
13912 }
13913 catch (Exception $e)
13914 {
13915 $result->error = $e->getMessage();
13916 $result->code = $e->getCode();
13917 }
13918
13919 echo json_encode($result);
13920 exit;
13921 }
13922
13923 /**
13924 * AJAX delete the customer documents.
13925 *
13926 * @return void
13927 *
13928 * @throws Exception
13929 */
13930 public function delete_customer_document()
13931 {
13932 $input = JFactory::getApplication()->input;
13933 $dbo = JFactory::getDbo();
13934
13935 $customer_id = $input->getUint('customer', 0);
13936
13937 $result = new stdClass;
13938 $result->status = 0;
13939
13940 $q = $dbo->getQuery(true)
13941 ->select($dbo->qn('docsfolder'))
13942 ->from($dbo->qn('#__vikbooking_customers'))
13943 ->where($dbo->qn('id') . ' = ' . $customer_id);
13944
13945 $dbo->setQuery($q, 0, 1);
13946 $dbo->execute();
13947
13948 if (!$dbo->getNumRows())
13949 {
13950 throw new Exception(sprintf('Customer [%d] not found', $customer_id), 404);
13951 }
13952
13953 $folder = $dbo->loadResult();
13954
13955 if (!$folder)
13956 {
13957 throw new Exception('The customer does not have any documents', 500);
13958 }
13959
13960 $file = $input->getString('file');
13961
13962 if (!$file)
13963 {
13964 throw new Exception('File to remove not specified', 400);
13965 }
13966
13967 $path = implode(DIRECTORY_SEPARATOR, array(VBO_CUSTOMERS_PATH, $folder, $file));
13968
13969 if (!is_file($path))
13970 {
13971 throw new Exception(sprintf('File [%s] not found', $path), 404);
13972 }
13973
13974 jimport('joomla.filesystem.file');
13975
13976 $removed = JFile::delete($path);
13977
13978 echo json_encode(array('status' => (int) $removed));
13979 exit;
13980 }
13981
13982 /**
13983 * AJAX task to invoke a specific report and obtain information.
13984 *
13985 * @since 1.3.0
13986 */
13987 public function get_report_data()
13988 {
13989 $report_name = VikRequest::getString('report_name', '', 'request');
13990 $current_fest = VikRequest::getString('current_fest', '', 'request');
13991 $current_fromdate = VikRequest::getString('current_fromdate', '', 'request');
13992 $current_todate = VikRequest::getString('current_todate', '', 'request');
13993 $step = VikRequest::getString('step', 'weekend', 'request');
13994 $direction = VikRequest::getString('direction', 'load', 'request');
13995 $period = VikRequest::getString('period', 'full', 'request');
13996 $krsort = VikRequest::getString('krsort', 'occupancy', 'request');
13997 $krorder = VikRequest::getString('krorder', 'DESC', 'request');
13998 $chart_datatype = VikRequest::getVar('chart_datatype', array(), 'request');
13999 $chart_meta_data = VikRequest::getString('chart_meta_data', '', 'request', VIKREQUEST_ALLOWRAW);
14000 $chart_meta_data = !empty($chart_meta_data) ? json_decode($chart_meta_data, true) : array();
14001 // idroom can be an array of IDs or just one ID as int/string
14002 $idroom = VikRequest::getVar('idroom', null, 'request');
14003 //
14004
14005 if (empty($report_name) || empty($current_fromdate) || empty($current_todate)) {
14006 throw new Exception("Missing request data", 400);
14007 }
14008
14009 // get requested report instance
14010 $report = VikBooking::getReportInstance($report_name);
14011 if (!$report) {
14012 throw new Exception("Report not found", 404);
14013 }
14014
14015 // chart data
14016 if (empty($chart_datatype)) {
14017 $chart_datatype = array(
14018 'type' => 'doughnut',
14019 'depth' => 1,
14020 'keys' => array($krsort),
14021 );
14022 }
14023
14024 // website date format
14025 $df = $report->getDateFormat();
14026
14027 // prepare request params for the report
14028 $rparams = array(
14029 'fromdate' => $current_fromdate,
14030 'todate' => $current_todate,
14031 'period' => $period,
14032 'krsort' => $krsort,
14033 'krorder' => $krorder,
14034 'idroom' => $idroom,
14035 );
14036
14037 // starting dates info and timestamps
14038 $from_ts = VikBooking::getDateTimestamp($current_fromdate, 0, 0, 0);
14039 $to_ts = VikBooking::getDateTimestamp($current_todate, 23, 59, 59);
14040 $from_info = getdate($from_ts);
14041 $to_info = getdate($to_ts);
14042
14043 // the name of the period requested and whether it's a fest
14044 $period_name = '';
14045 $is_fest = null;
14046
14047 if ($direction == 'prev' || $direction == 'next') {
14048 // calculate prev or next dates
14049 if ($step == 'weekend') {
14050 $period_name = JText::translate('VBOWEEKND');
14051 if ($direction == 'next') {
14052 // next weekend from current end date
14053 $next_ts = strtotime("next friday", $to_ts);
14054 } else {
14055 // prev weekend from current start date
14056 $next_ts = strtotime("previous friday", $from_ts);
14057 }
14058 $next_info = getdate($next_ts);
14059 $new_from_ts = $next_ts;
14060 $new_to_ts = mktime(23, 59, 59, $next_info['mon'], ($next_info['mday'] + 1), $next_info['year']);
14061 $rparams['fromdate'] = date($df, $new_from_ts);
14062 $rparams['todate'] = date($df, $new_to_ts);
14063 } elseif ($step == 'week') {
14064 $period_name = JText::translate('VBOWEEK');
14065 if ($direction == 'next') {
14066 // start next week from the current end date
14067 $new_from_ts = $to_ts;
14068 $new_to_ts = mktime(23, 59, 59, $to_info['mon'], ($to_info['mday'] + 7), $to_info['year']);
14069 $rparams['fromdate'] = $rparams['todate'];
14070 $rparams['todate'] = date($df, $new_to_ts);
14071 } else {
14072 // end prev week from the current from date
14073 $new_from_ts = mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] - 7), $from_info['year']);
14074 $new_to_ts = $from_ts;
14075 $rparams['todate'] = $rparams['fromdate'];
14076 $rparams['fromdate'] = date($df, $new_from_ts);
14077 }
14078 } else {
14079 // month
14080 $period_name = JText::translate('VBPVIEWRESTRICTIONSTWO');
14081 if ($direction == 'next') {
14082 // next month from the current from date
14083 $nextmonts = mktime(0, 0, 0, ($from_info['mon'] + 1), 1, $from_info['year']);
14084 $new_from_ts = $nextmonts;
14085 $new_to_ts = mktime(23, 59, 59, ($from_info['mon'] + 1), date('t', $nextmonts), $from_info['year']);
14086 $rparams['fromdate'] = date($df, $new_from_ts);
14087 $rparams['todate'] = date($df, $new_to_ts);
14088 } else {
14089 // prev month from the current from date
14090 $nextmonts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']);
14091 $new_from_ts = $nextmonts;
14092 $new_to_ts = mktime(23, 59, 59, ($from_info['mon'] - 1), date('t', $nextmonts), $from_info['year']);
14093 $rparams['fromdate'] = date($df, $new_from_ts);
14094 $rparams['todate'] = date($df, $new_to_ts);
14095 }
14096 }
14097
14098 // get the next festivities
14099 $fests = VikBooking::getFestivitiesInstance();
14100 $next_fests = $fests->loadFestDates();
14101 if (count($next_fests)) {
14102 // check whether a festivity should be displayed rather than the calculated period of dates
14103 foreach ($next_fests as $fest) {
14104 $fest_found = false;
14105 if ($direction == 'next' && $fest['festinfo'][0]->from_ts > $from_ts && $fest['festinfo'][0]->from_ts <= $new_to_ts) {
14106 $fest_found = true;
14107 } elseif ($direction == 'prev' && $fest['festinfo'][0]->from_ts < $to_ts && $fest['festinfo'][0]->from_ts >= $new_from_ts) {
14108 $fest_found = true;
14109 }
14110 if ($fest_found && (string)$fest['festinfo'][0]->next_ts != $current_fest) {
14111 // festivity found before next calculated period
14112 $is_fest = $fest['festinfo'][0]->next_ts;
14113 $period_name = $fest['festinfo'][0]->trans_name;
14114 $new_from_ts = $fest['festinfo'][0]->from_ts;
14115 $new_to_ts = $fest['festinfo'][0]->to_ts;
14116 $rparams['fromdate'] = date($df, $new_from_ts);
14117 $rparams['todate'] = date($df, $new_to_ts);
14118 break;
14119 }
14120 }
14121 }
14122 } else {
14123 // load requested dates by skipping the festivities
14124 $new_from_ts = $from_ts;
14125 $new_to_ts = $to_ts;
14126 }
14127
14128 // invoke report
14129 $report->injectParams($rparams);
14130 $report_values = $report->getReportValues(1);
14131 $report_cols = $report->getColumnsValues();
14132 $report_chart = null;
14133 $report_chart_metas = array();
14134 $chart_meta_data = array(
14135 'keys' => array(
14136 'occupancy',
14137 'tot_bookings',
14138 'nights_booked',
14139 ),
14140 );
14141 $error = null;
14142
14143 if (!count($report_values)) {
14144 $error = strlen($report->getError()) ? $report->getError() : JText::translate('VBNOTRACKINGS');
14145 } else {
14146 // get doughnut Chart for the requested key
14147 $report_chart = $report->getChart((array) $chart_datatype);
14148
14149 // get Chart meta data
14150 $all_chart_metas = $report->getChartMetaData(null, $chart_meta_data);
14151 if (count($all_chart_metas)) {
14152 // merge all positions into one array
14153 foreach ($all_chart_metas as $pos_metas) {
14154 $report_chart_metas = array_merge($report_chart_metas, $pos_metas);
14155 }
14156 }
14157
14158 if (empty($period_name)) {
14159 $period_name = $report->getProperty('chartTitle');
14160 }
14161 }
14162
14163 // build response
14164 $response = new stdClass;
14165 $response->error = $error;
14166 $response->fromdate = $rparams['fromdate'];
14167 $response->todate = $rparams['todate'];
14168 $response->in_days = $report->countDaysTo($new_from_ts);
14169 $response->in_days_to = $report->countDaysTo($new_to_ts);
14170 $response->in_days_avg = $report->countAverageDays($response->in_days, $response->in_days_to);
14171 $response->period_name = $period_name;
14172 $response->period_date = count($report_values) && isset($report_values['day']) ? $report_values['day']['display_value'] : '';
14173 $response->is_fest = $is_fest;
14174 $response->report_chart = $report_chart;
14175 $response->report_cols = $report_cols;
14176 $response->report_values = $report_values;
14177 $response->report_script = $report->getScript();
14178 $response->chart_labels = $report->getProperty('chartJsLabels');
14179 $response->dataset_label = $report->getProperty('chartJsDataSetLabel');
14180 $response->chart_colors = $report->getProperty('chartJsColors');
14181 $response->chart_data = $report->getProperty('chartJsData');
14182 $response->report_chart_metas = $report_chart_metas;
14183
14184 echo json_encode($response);
14185 exit;
14186 }
14187
14188 /**
14189 * Go to the previous booking.
14190 *
14191 * @uses navigateToBooking()
14192 *
14193 * @since 1.3.0
14194 */
14195 public function prev_booking()
14196 {
14197 $this->navigateToBooking('prev');
14198 }
14199
14200 /**
14201 * Go to the next booking.
14202 *
14203 * @uses navigateToBooking()
14204 *
14205 * @since 1.3.0
14206 */
14207 public function next_booking()
14208 {
14209 $this->navigateToBooking('next');
14210 }
14211
14212 /**
14213 * Given the current booking ID in the request, we navigate
14214 * either to the next or to the previous reservation (if any).
14215 *
14216 * @param string $direction either next or prev.
14217 *
14218 * @return void
14219 *
14220 * @since 1.3.0
14221 */
14222 private function navigateToBooking($direction = 'next')
14223 {
14224 $bid = VikRequest::getInt('whereup', 0, 'request');
14225 if (empty($bid) || $bid < 1 || !in_array($direction, array('prev', 'next'))) {
14226 throw new Exception("Invalid request", 400);
14227 }
14228
14229 $dbo = JFactory::getDbo();
14230 $app = JFactory::getApplication();
14231
14232 $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `id`" . ($direction == 'next' ? '>' : '<') . "{$bid} ORDER BY `id` " . ($direction == 'next' ? 'ASC' : 'DESC');
14233 $dbo->setQuery($q, 0, 1);
14234 $dbo->execute();
14235 if (!$dbo->getNumRows()) {
14236 VikError::raiseWarning('', JText::translate('VBPEDITBUSYONE'));
14237 $app->redirect("index.php?option=com_vikbooking&task=orders");
14238 exit;
14239 }
14240
14241 $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $dbo->loadResult());
14242 exit;
14243 }
14244
14245 /**
14246 * AJAX request: from a list of reservation IDs, we return the ones
14247 * that have a review with the related review ID on VCM.
14248 *
14249 * @since 1.13
14250 */
14251 public function bookings_have_reviews()
14252 {
14253 if (!JSession::checkToken()) {
14254 // missing CSRF-proof token
14255 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14256 }
14257
14258 $dbo = JFactory::getDbo();
14259
14260 $bids = VikRequest::getVar('bids', [], 'request', 'array');
14261 $vcm_installed = class_exists('VikChannelManager');
14262 $withreviews = [];
14263
14264 if ($vcm_installed && $bids) {
14265 $bids = array_filter(array_map('intval', (array) $bids));
14266 $bids = $bids ?: [0];
14267
14268 try {
14269 $q = "SELECT `id`, `idorder` FROM `#__vikchannelmanager_otareviews` WHERE `idorder` IN (" . implode(', ', $bids) . ");";
14270 $dbo->setQuery($q);
14271 $reviews = $dbo->loadAssocList();
14272
14273 foreach ($reviews as $r) {
14274 $withreviews[$r['idorder']] = $r['id'];
14275 }
14276 } catch (Exception $e) {
14277 // do nothing, outdated version
14278 }
14279 }
14280
14281 // output list of booking IDs found, if any
14282 VBOHttpDocument::getInstance()->json($withreviews);
14283 }
14284
14285 /**
14286 * AJAX request for adding a new room-day note.
14287 *
14288 * @return void
14289 *
14290 * @since 1.13.5
14291 */
14292 public function add_roomdaynote()
14293 {
14294 $dt = VikRequest::getString('dt', '', 'request');
14295 $idroom = VikRequest::getInt('idroom', 0, 'request');
14296 $subunit = VikRequest::getInt('subunit', 0, 'request');
14297 $type = VikRequest::getString('type', '', 'request');
14298 $type = empty($type) ? 'custom' : $type;
14299 $name = VikRequest::getString('name', '', 'request');
14300 $descr = VikRequest::getString('descr', '', 'request');
14301 $cdays = VikRequest::getInt('cdays', 0, 'request');
14302 $cdays = $cdays < 0 ? 0 : $cdays;
14303 $cdays = $cdays > 365 ? 365 : $cdays;
14304 if (empty($idroom) || empty($dt) || !strtotime($dt)) {
14305 echo 'e4j.error.1';
14306 exit;
14307 }
14308
14309 // reload end date
14310 $end_date = $dt;
14311
14312 // build critical date object
14313 $new_note = array(
14314 'name' => $name,
14315 'type' => $type,
14316 'descr' => $descr,
14317 );
14318
14319 // get object
14320 $notes = VikBooking::getCriticalDatesInstance();
14321
14322 // store the notes for all consecutive dates
14323 for ($i = 0; $i <= $cdays; $i++) {
14324 $store_dt = $dt;
14325 if ($i > 0) {
14326 $dt_info = getdate(strtotime($store_dt));
14327 $store_dt = date('Y-m-d', mktime(0, 0, 0, $dt_info['mon'], ($dt_info['mday'] + $i), $dt_info['year']));
14328 $end_date = $store_dt;
14329 }
14330 $result = $notes->storeDayNote($new_note, $store_dt, $idroom, $subunit);
14331 if (!$result) {
14332 echo 'e4j.error.2';
14333 exit;
14334 }
14335 }
14336
14337 // reload all room day notes for this day for the AJAX response
14338 $all_notes = $notes->loadRoomDayNotes($dt, $end_date, $idroom, $subunit);
14339
14340 if (!$all_notes || !count($all_notes)) {
14341 // no notes found even after storing it
14342 echo 'e4j.error.3';
14343 exit;
14344 }
14345
14346 echo json_encode($all_notes);
14347 exit;
14348 }
14349
14350 /**
14351 * AJAX request for removing a room day note.
14352 *
14353 * @return void
14354 *
14355 * @since 1.13.5
14356 */
14357 public function remove_roomdaynote()
14358 {
14359 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
14360 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14361 }
14362
14363 $dt = VikRequest::getString('dt', '', 'request');
14364 $idroom = VikRequest::getInt('idroom', 0, 'request');
14365 $subunit = VikRequest::getInt('subunit', 0, 'request');
14366 $type = VikRequest::getString('type', '', 'request');
14367 $type = empty($type) ? 'custom' : $type;
14368 $ind = VikRequest::getInt('ind', 0, 'request');
14369 if (empty($dt) || !strtotime($dt)) {
14370 echo 'e4j.error.1';
14371 exit;
14372 }
14373
14374 $notes = VikBooking::getCriticalDatesInstance();
14375 $result = $notes->deleteDayNote($ind, $dt, $idroom, $subunit, $type);
14376 if (!$result) {
14377 echo 'e4j.error.2';
14378 exit;
14379 }
14380
14381 echo 'e4j.ok';
14382 exit;
14383 }
14384
14385 /**
14386 * AJAX request for storing an event for a booking.
14387 * Firstly developed for the VCM Reporting API - Guest Misconduct,
14388 * but it can be used for any other purpose.
14389 *
14390 * @return void
14391 *
14392 * @since 1.13.5
14393 */
14394 public function store_booking_history_event()
14395 {
14396 $bid = VikRequest::getInt('bid', 0, 'request');
14397 $event = VikRequest::getString('event', '', 'request');
14398 $descr = VikRequest::getString('descr', '', 'request');
14399
14400 if (empty($bid) || empty($event)) {
14401 throw new Exception("Missing required information", 500);
14402 }
14403
14404 // Booking History
14405 VikBooking::getBookingHistoryInstance()->setBid($bid)->store($event, $descr);
14406 //
14407
14408 echo 'e4j.ok';
14409 exit;
14410 }
14411
14412 /**
14413 * AJAX request for updating an option/extra service.
14414 * Firstly developed for the VCM Vacation Rentals Essentials API - Damage Deposit,
14415 * but it can be used for any other purpose.
14416 *
14417 * @return void
14418 *
14419 * @since 1.13.5
14420 */
14421 public function update_option_params()
14422 {
14423 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
14424 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14425 }
14426
14427 $optid = VikRequest::getInt('optid', 0, 'request');
14428 $oparams = VikRequest::getVar('oparams', array(), 'request', 'array');
14429
14430 if (empty($optid) || !is_array($oparams) || empty($oparams)) {
14431 throw new Exception("Missing required information", 500);
14432 }
14433
14434 $dbo = JFactory::getDbo();
14435 $q = "SELECT `oparams` FROM `#__vikbooking_optionals` WHERE `id`=" . (int)$optid . ";";
14436 $dbo->setQuery($q);
14437 $dbo->execute();
14438 if (!$dbo->getNumRows()) {
14439 throw new Exception("Option not found", 404);
14440 }
14441 $cur_params = $dbo->loadResult();
14442 $cur_params = !empty($cur_params) ? json_decode($cur_params, true) : array();
14443 $cur_params = !is_array($cur_params) ? array() : $cur_params;
14444
14445 foreach ($oparams as $k => $v) {
14446 if (empty($k)) {
14447 continue;
14448 }
14449 $cur_params[$k] = $v;
14450 }
14451
14452 $q = "UPDATE `#__vikbooking_optionals` SET `oparams`=" . $dbo->quote(json_encode($cur_params)) ." WHERE `id`=" . (int)$optid . ";";
14453 $dbo->setQuery($q);
14454 $dbo->execute();
14455
14456 echo 'e4j.ok';
14457 exit;
14458 }
14459
14460 /**
14461 * Hidden task to clean up duplicate records in certain database tables
14462 * due to a double execution of the installation queries. Ghost records,
14463 * if any, are also removed to clean up issues with hanging records.
14464 *
14465 * @since November 4th 2020
14466 * @since 1.16.3 (J) - 1.6.3 (WP)
14467 */
14468 public function clean_duplicate_records()
14469 {
14470 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14471 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14472 }
14473
14474 $dbo = JFactory::getDbo();
14475
14476 $tables_with_duplicates = [
14477 '#__vikbooking_config' => [
14478 'id_key' => 'id',
14479 'compare_key' => 'param',
14480 ],
14481 '#__vikbooking_countries' => [
14482 'id_key' => 'id',
14483 'compare_key' => 'country_3_code',
14484 ],
14485 '#__vikbooking_custfields' => [
14486 'id_key' => 'id',
14487 'compare_key' => 'name',
14488 ],
14489 '#__vikbooking_texts' => [
14490 'id_key' => 'id',
14491 'compare_key' => 'param',
14492 ],
14493 ];
14494
14495 foreach ($tables_with_duplicates as $tblname => $data) {
14496 $doubles = [];
14497 $storage = [];
14498 $rmlist = [];
14499
14500 $q = "SELECT * FROM `{$tblname}` ORDER BY `{$data['id_key']}` DESC;";
14501 $dbo->setQuery($q);
14502 $rows = $dbo->loadAssocList();
14503 if (!$rows) {
14504 echo "<p>No records found in table {$tblname}</p>";
14505 continue;
14506 }
14507
14508 foreach ($rows as $row) {
14509 if (!isset($doubles[$row[$data['compare_key']]])) {
14510 $doubles[$row[$data['compare_key']]] = 0;
14511 }
14512 $doubles[$row[$data['compare_key']]]++;
14513 if (!isset($storage[$row[$data['compare_key']]])) {
14514 $storage[$row[$data['compare_key']]] = [];
14515 }
14516 array_push($storage[$row[$data['compare_key']]], $row[$data['id_key']]);
14517 }
14518
14519 foreach ($doubles as $paramkey => $paramcount) {
14520 if ($paramcount < 2 || !isset($storage[$paramkey]) || count($storage[$paramkey]) < 2 || $paramcount != count($storage[$paramkey])) {
14521 continue;
14522 }
14523 $exceeding = $paramcount - 1;
14524 for ($x = 0; $x < $exceeding; $x++) {
14525 array_push($rmlist, $storage[$paramkey][$x]);
14526 }
14527 }
14528
14529 echo "<p>Total records found in table {$tblname}: " . count($rows) . "</p>";
14530 echo '<p>Total records to remove: ' . count($rmlist) . '</p>';
14531 echo '<pre style="display: none;">'.print_r($rmlist, true).'</pre><br/>';
14532
14533 if (count($rmlist)) {
14534 $q = "DELETE FROM `{$tblname}` WHERE `{$data['id_key']}` IN (" . implode(', ', $rmlist) . ");";
14535 $dbo->setQuery($q);
14536 $dbo->execute();
14537 }
14538 }
14539
14540 /**
14541 * Clean up busy records where the busy relations contain empty booking IDs.
14542 */
14543 $hanging_busy_ids = [];
14544
14545 $q = "SELECT `idbusy` FROM `#__vikbooking_ordersbusy` WHERE `idorder` = 0 OR `idorder` IS NULL;";
14546 $dbo->setQuery($q);
14547 $removelist = $dbo->loadAssocList();
14548 if ($removelist) {
14549 foreach ($removelist as $hanging_busy) {
14550 $hanging_busy_id = (int)$hanging_busy['idbusy'];
14551 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
14552 array_push($hanging_busy_ids, $hanging_busy_id);
14553 }
14554 }
14555 }
14556
14557 // let's check also for ghost records that only occupy the room
14558 $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);";
14559 $dbo->setQuery($q);
14560 $removelist = $dbo->loadAssocList();
14561 if ($removelist) {
14562 foreach ($removelist as $hanging_busy) {
14563 $hanging_busy_id = (int)$hanging_busy['id'];
14564 if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
14565 array_push($hanging_busy_ids, $hanging_busy_id);
14566 }
14567 }
14568 }
14569
14570 if ($hanging_busy_ids) {
14571 $q = "DELETE FROM `#__vikbooking_busy` WHERE `id` IN (" . implode(', ', $hanging_busy_ids) . ");";
14572 $dbo->setQuery($q);
14573 $dbo->execute();
14574 }
14575
14576 echo "<p>Total ghost records removed: " . count($hanging_busy_ids) . "</p>";
14577
14578 return;
14579 }
14580
14581 /**
14582 * Hidden task to scan all database tables of VikBooking and Vik Channel Manager
14583 * to ensure the column `id` is defined as a primary key and got an auto-increment
14584 * extra flag properly defined and set. We've noticed that some third-party plugins
14585 * used to migrate WP sites may break the primary keys, and so new records won't get an ID.
14586 *
14587 * @since 1.16.8 (J) - 1.6.8 (WP)
14588 */
14589 public function fix_autoincrement_tables()
14590 {
14591 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14592 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14593 }
14594
14595 $dbo = JFactory::getDbo();
14596
14597 // load all the installed database tables
14598 $tables = $dbo->getTableList();
14599
14600 // get current database prefix
14601 $prefix = $dbo->getPrefix();
14602
14603 // replace prefix with placeholder
14604 $tables = array_map(function($table) use ($prefix)
14605 {
14606 return preg_replace("/^{$prefix}/", '#__', $table);
14607 }, $tables);
14608
14609 // remove all the tables that do not belong to VikBooking/VCM
14610 $tables = array_values(array_filter($tables, function($table)
14611 {
14612 if (preg_match("/^#__vik(?:booking|channelmanager)_config$/", $table))
14613 {
14614 // exclude the configuration table, which will be handled in a different way
14615 return false;
14616 }
14617
14618 return preg_match("/^#__vik(?:booking|channelmanager)_/", $table);
14619 }));
14620
14621 foreach ($tables as $table) {
14622 $columns = $dbo->getTableColumns($table, false);
14623 if (!isset($columns['id']) || empty($columns['id']->Type) || !empty($columns['id']->Extra)) {
14624 continue;
14625 }
14626
14627 echo 'Fixing ' . $table. ' for missing auto-increment<br/><pre>' . print_r($columns['id'], true) . '</pre><br/>';
14628
14629 // set auto-increment and primary key
14630 $dbo->setQuery("ALTER TABLE `{$table}` MODIFY `id` " . $columns['id']->Type . " NOT NULL AUTO_INCREMENT PRIMARY KEY;");
14631 $dbo->execute();
14632
14633 // count next auto-increment
14634 $dbo->setQuery("SELECT MAX(`id`) FROM `{$table}`");
14635 $next_ai = (int) $dbo->loadResult() + 1;
14636
14637 // update next auto-increment value
14638 $dbo->setQuery("ALTER TABLE `{$table}` AUTO_INCREMENT = {$next_ai}");
14639 $dbo->execute();
14640 }
14641 }
14642
14643 /**
14644 * Hidden task to (re-)run the update queries from a given plugin version.
14645 * Useful to ensure the database structure is up-to-date and no update queries went lost.
14646 *
14647 * @since 1.17.6 (J) - 1.7.6 (WP)
14648 */
14649 public function run_update_queries()
14650 {
14651 $app = JFactory::getApplication();
14652 $dbo = JFactory::getDbo();
14653
14654 if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14655 VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14656 }
14657
14658 $from_version = $app->input->getString('from_version');
14659
14660 if (empty($from_version)) {
14661 VBOHttpDocument::getInstance()->close(400, 'Missing from version value.');
14662 }
14663
14664 // determine the SQL updates directory path
14665 $sql_updates_path = '';
14666 if (VBOPlatformDetection::isWordPress()) {
14667 $sql_updates_path = implode(DIRECTORY_SEPARATOR, [VIKBOOKING_BASE, 'sql', 'update', 'mysql']);
14668 } else {
14669 $sql_updates_path = implode(DIRECTORY_SEPARATOR, [VBO_ADMIN_PATH, 'sql', 'updates', 'mysql']);
14670 }
14671
14672 if (!$sql_updates_path || !is_dir($sql_updates_path)) {
14673 VBOHttpDocument::getInstance()->close(500, 'Could not find SQL updates path.');
14674 }
14675
14676 // read all SQL update files
14677 $sql_update_files = JFolder::files($sql_updates_path, '\.sql', $recurse = false, $full = true);
14678
14679 // filter SQL files with just the valid ones
14680 $sql_update_files = array_filter($sql_update_files, function($sql_update_file) use ($from_version) {
14681 $file_version = basename($sql_update_file, '.sql');
14682 return version_compare($file_version, $from_version, '>=');
14683 });
14684
14685 // sort files by version ascending
14686 usort($sql_update_files, function($a, $b) {
14687 return version_compare(basename($a, '.sql'), basename($b, '.sql'));
14688 });
14689
14690 if (!$sql_update_files) {
14691 VBOHttpDocument::getInstance()->close(500, sprintf('Could not find any suitable SQL update file from version %s.', $from_version));
14692 }
14693
14694 $success_queries = 0;
14695
14696 foreach ($sql_update_files as $file) {
14697 $handle = fopen($file, 'r');
14698
14699 $bytes = '';
14700 while (!feof($handle)) {
14701 $bytes .= fread($handle, 8192);
14702 }
14703
14704 fclose($handle);
14705
14706 if (VBOPlatformDetection::isWordPress()) {
14707 $queries_list = JDatabaseHelper::splitSql($bytes);
14708 } else {
14709 try {
14710 $queries_list = Joomla\Database\DatabaseDriver::splitSql($bytes);
14711 } catch(Throwable $e) {
14712 $app->enqueueMessage(sprintf('Error splitting queries: %s', $e->getMessage()), 'error');
14713 $queries_list = [];
14714 }
14715 }
14716
14717 foreach ($queries_list as $q) {
14718 try {
14719 $dbo->setQuery($q);
14720 $result = $dbo->execute();
14721 } catch (Exception $e) {
14722 $result = false;
14723 $app->enqueueMessage(sprintf('Error executing query: %s', $e->getMessage()), 'warning');
14724 }
14725
14726 if ($result) {
14727 $success_queries++;
14728 }
14729 }
14730 }
14731
14732 if ($success_queries) {
14733 $app->enqueueMessage(sprintf('Successful queries: %d', $success_queries), 'success');
14734 }
14735
14736 // send response to output
14737 echo '<pre>'.print_r($sql_update_files, true).'</pre><br/>';
14738 }
14739
14740 /**
14741 * Loads a specific admin widget ID and executes the requested method.
14742 * Useful for loading a newly added widget, or to execute custom methods.
14743 *
14744 * @see this is an AJAX endpoint.
14745 *
14746 * @since 1.14 (J) - 1.4.0 (WP)
14747 * @since 1.15 (J) - 1.5.0 (WP) widget callback can return values rather than just echoing.
14748 * @since 1.16.5 (J) - 1.6.5 (WP) widgets are rendered within a try-catch statement.
14749 */
14750 public function exec_admin_widget()
14751 {
14752 if (!JSession::checkToken()) {
14753 // missing CSRF-proof token
14754 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14755 }
14756
14757 $widget_id = VikRequest::getString('widget_id', '', 'request');
14758 $call = VikRequest::getString('call', '', 'request');
14759 $return = VikRequest::getInt('return', 0, 'request');
14760 $vbo_page = VikRequest::getString('vbo_page', '', 'request');
14761 $vbo_uri = VikRequest::getString('vbo_uri', '', 'request');
14762 $multitask = VikRequest::getInt('multitask', 0, 'request');
14763
14764 if (empty($widget_id)) {
14765 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget ID');
14766 }
14767
14768 if (empty($call) || !is_string($call)) {
14769 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget Callback');
14770 }
14771
14772 // invoke admin widgets helper
14773 $widgets_helper = VikBooking::getAdminWidgetsInstance();
14774 $widget = $widgets_helper->getWidget($widget_id);
14775
14776 if ($widget === false) {
14777 VBOHttpDocument::getInstance()->close(404, 'Requested Admin Widget not found');
14778 }
14779
14780 if (!method_exists($widget, $call) || !is_callable(array($widget, $call))) {
14781 VBOHttpDocument::getInstance()->close(403, 'Admin Widget Callback not found or not callable');
14782 }
14783
14784 // get the multitask parser object
14785 $parser = VBOMultitaskParser::getInstance($vbo_page, $vbo_uri);
14786
14787 // check if arguments should be passed
14788 $call_args = [];
14789 if ($multitask && $call === 'render') {
14790 // build the multitask data object and inject it to the args as the first index
14791 $call_args[] = $parser->getData();
14792
14793 // bind options within the widget, if any
14794 $widget->bindOptions($call_args[0]);
14795 } else {
14796 // always bind multitask options, if any
14797 $widget->bindOptions($parser->getOptions());
14798 }
14799
14800 try {
14801 if ($return) {
14802 // invoke the widget's method and get the value returned
14803 $widget_response = $call_args ? call_user_func_array([$widget, $call], $call_args) : $widget->{$call}();
14804 } else {
14805 // invoke the widget's method within a buffer
14806 ob_start();
14807 if ($call_args) {
14808 $res = call_user_func_array([$widget, $call], $call_args);
14809 } else {
14810 $widget->{$call}();
14811 }
14812 $widget_response = ob_get_contents();
14813 ob_end_clean();
14814 }
14815 } catch (Throwable $e) {
14816 VBOHttpDocument::getInstance()->close($e->getCode() ?: 500, $e->getMessage());
14817 } catch (Exception $e) {
14818 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
14819 }
14820
14821 // prepare response object with a property equal to the called method
14822 $response = new stdClass;
14823 $response->{$call} = $widget_response;
14824
14825 // output the JSON encoded response and exit
14826 VBOHttpDocument::getInstance()->json($response);
14827 }
14828
14829 /**
14830 * Updates the map of admin widgets.
14831 *
14832 * @throws Exception this is an AJAX endpoint.
14833 *
14834 * @since 1.4.0
14835 */
14836 public function save_admin_widgets()
14837 {
14838 if (!JSession::checkToken()) {
14839 // missing CSRF-proof token
14840 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14841 }
14842
14843 // make sure permissions are sufficient
14844 if (!JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
14845 VBOHttpDocument::getInstance()->close(403, 'You are not authorized to modify the widgets.');
14846 }
14847
14848 $psections = VikRequest::getVar('sections', array(), 'request', 'array');
14849 if (!is_array($psections) || !count($psections)) {
14850 VBOHttpDocument::getInstance()->close(500, 'No sections found in map');
14851 }
14852
14853 // request values are all converted to arrays, so restore the object styling
14854 $psections = json_decode(json_encode($psections));
14855
14856 // update map
14857 $result = VikBooking::getAdminWidgetsInstance()->updateWidgetsMap($psections);
14858
14859 $response = new stdClass;
14860 $response->status = (int)$result;
14861
14862 // output the JSON encoded response and exit
14863 VBOHttpDocument::getInstance()->json($response);
14864 }
14865
14866 /**
14867 * Restores the default admin widgets map.
14868 *
14869 * @since 1.4.0
14870 */
14871 public function reset_admin_widgets()
14872 {
14873 // reset map and redirect to dashboard
14874 VikBooking::getAdminWidgetsInstance()->restoreDefaultWidgetsMap();
14875
14876 JFactory::getApplication()->redirect('index.php?option=com_vikbooking');
14877 exit;
14878 }
14879
14880 /**
14881 * Updates the welcome message status for the widget's customizer via AJAX.
14882 *
14883 * @since 1.4.0
14884 */
14885 public function admin_widgets_welcome()
14886 {
14887 if (!JSession::checkToken()) {
14888 // missing CSRF-proof token
14889 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14890 }
14891
14892 $hide_welcome = VikRequest::getInt('hide_welcome', 0, 'request');
14893 // update configuration value
14894 VikBooking::getAdminWidgetsInstance()->updateWelcome($hide_welcome);
14895
14896 $response = new stdClass;
14897 $response->status = $hide_welcome;
14898
14899 // output the JSON encoded response and exit
14900 VBOHttpDocument::getInstance()->json($response);
14901 }
14902
14903 public function newcondtext()
14904 {
14905 VikBookingHelper::printHeader("11");
14906
14907 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecondtext'));
14908
14909 parent::display();
14910
14911 if (VikBooking::showFooter()) {
14912 VikBookingHelper::printFooter();
14913 }
14914 }
14915
14916 public function editcondtext()
14917 {
14918 VikBookingHelper::printHeader("11");
14919
14920 VikRequest::setVar('view', VikRequest::getCmd('view', 'managecondtext'));
14921
14922 parent::display();
14923
14924 if (VikBooking::showFooter()) {
14925 VikBookingHelper::printFooter();
14926 }
14927 }
14928
14929 public function cancelcondtext()
14930 {
14931 JFactory::getApplication()->redirect('index.php?option=com_vikbooking&task=config&tab=7');
14932 }
14933
14934 public function createcondtext()
14935 {
14936 if (!JSession::checkToken()) {
14937 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
14938 }
14939
14940 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
14941 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14942 }
14943
14944 $this->_doCreateCondText();
14945 }
14946
14947 public function createcondtextstay()
14948 {
14949 if (!JSession::checkToken()) {
14950 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
14951 }
14952
14953 if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
14954 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14955 }
14956
14957 $this->_doCreateCondText(true);
14958 }
14959
14960 private function _doCreateCondText($stay = false)
14961 {
14962 $dbo = JFactory::getDbo();
14963 $app = JFactory::getApplication();
14964 $rules_helper = VikBooking::getConditionalRulesInstance();
14965 $rules_list = $rules_helper->composeRulesParamsFromRequest();
14966
14967 $condtextname = VikRequest::getString('condtextname', '', 'request');
14968 $condtexttkn = VikRequest::getString('condtexttkn', '', 'request');
14969 $msg = VikRequest::getString('msg', '', 'request', VIKREQUEST_ALLOWRAW);
14970 $debug = VikRequest::getInt('debug', 0, 'request');
14971 if (empty($condtextname)) {
14972 $condtextname = date('Y-m-dHis');
14973 $condtexttkn = '{condition: ' . date('YmdHis') . '}';
14974 }
14975
14976 $existing_tokens = $rules_helper->getSpecialTags();
14977 if (count($existing_tokens) && isset($existing_tokens[$condtexttkn])) {
14978 VikError::raiseWarning('', 'Another conditional text with the same special tag already exists');
14979 $app->redirect('index.php?option=com_vikbooking&task=newcondtext');
14980 exit;
14981 }
14982
14983 $data = new stdClass;
14984 $data->name = $condtextname;
14985 $data->token = $condtexttkn;
14986 $data->rules = json_encode($rules_list);
14987 $data->msg = $msg;
14988 $data->lastupd = JDate::getInstance()->toSql();
14989 $data->debug = $debug;
14990
14991 $dbo->insertObject('#__vikbooking_condtexts', $data, 'id');
14992
14993 if (isset($data->id)) {
14994 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
14995 }
14996
14997 if (!$stay || !isset($data->id)) {
14998 $this->cancelcondtext();
14999 exit;
15000 }
15001
15002 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $data->id);
15003 }
15004
15005 public function updatecondtext()
15006 {
15007 if (!JSession::checkToken()) {
15008 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15009 }
15010
15011 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
15012 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15013 }
15014
15015 $this->_doUpdateCondText();
15016 }
15017
15018 public function updatecondtextstay()
15019 {
15020 if (!JSession::checkToken()) {
15021 throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15022 }
15023
15024 if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
15025 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15026 }
15027
15028 $this->_doUpdateCondText(true);
15029 }
15030
15031 private function _doUpdateCondText($stay = false)
15032 {
15033 $dbo = JFactory::getDbo();
15034 $app = JFactory::getApplication();
15035 $rules_helper = VikBooking::getConditionalRulesInstance();
15036 $rules_list = $rules_helper->composeRulesParamsFromRequest();
15037
15038 $pwhere = VikRequest::getInt('where', '', 'request');
15039 $condtextname = VikRequest::getString('condtextname', '', 'request');
15040 $condtexttkn = VikRequest::getString('condtexttkn', '', 'request');
15041 $msg = VikRequest::getString('msg', '', 'request', VIKREQUEST_ALLOWRAW);
15042 $debug = VikRequest::getInt('debug', 0, 'request');
15043 if (empty($condtextname)) {
15044 $condtextname = date('Y-m-dHis');
15045 $condtexttkn = '{condition: ' . date('YmdHis') . '}';
15046 }
15047
15048 $existing_tokens = $rules_helper->getSpecialTags();
15049 if (count($existing_tokens) && isset($existing_tokens[$condtexttkn]) && ($existing_tokens[$condtexttkn]['id'] != $pwhere)) {
15050 VikError::raiseWarning('', 'Another conditional text with the same special tag already exists (' . $existing_tokens[$condtexttkn]['name'] . ')');
15051 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $pwhere);
15052 exit;
15053 }
15054
15055 $data = new stdClass;
15056 $data->id = $pwhere;
15057 $data->name = $condtextname;
15058 $data->token = $condtexttkn;
15059 $data->rules = json_encode($rules_list);
15060 $data->msg = $msg;
15061 $data->lastupd = JDate::getInstance()->toSql();
15062 $data->debug = $debug;
15063
15064 $dbo->updateObject('#__vikbooking_condtexts', $data, 'id');
15065
15066 $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
15067
15068 if (!$stay) {
15069 $this->cancelcondtext();
15070 exit;
15071 }
15072
15073 $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $data->id);
15074 }
15075
15076 public function removecondtext()
15077 {
15078 if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
15079 VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15080 }
15081
15082 $dbo = JFactory::getDbo();
15083 $ids = VikRequest::getVar('cid', array());
15084
15085 VikBooking::getConditionalRulesInstance(true);
15086 $templates = VikBookingHelperConditionalRules::getTemplateFilesPaths();
15087
15088 foreach ($ids as $d) {
15089 $q = "SELECT `token` FROM `#__vikbooking_condtexts` WHERE `id`=" . (int)$d . ";";
15090 $dbo->setQuery($q);
15091 $dbo->execute();
15092 if (!$dbo->getNumRows()) {
15093 continue;
15094 }
15095 $special_tag = $dbo->loadResult();
15096
15097 // remove the token from each template file if it was used before
15098 if (!empty($special_tag)) {
15099 // remove token from all template files
15100 foreach ($templates as $tkey => $tpath) {
15101 // get requested file content
15102 $fcontent = VikBookingHelperConditionalRules::getTemplateFileCode($tkey);
15103 if (empty($fcontent) || !is_string($fcontent)) {
15104 break;
15105 }
15106 // remove tag from code content
15107 $fcontent = str_replace($special_tag, '', $fcontent);
15108 // update the file code
15109 VikBookingHelperConditionalRules::writeTemplateFileCode($tkey, $fcontent);
15110 }
15111 }
15112
15113 // delete the record
15114 $q = "DELETE FROM `#__vikbooking_condtexts` WHERE `id`=" . (int)$d . ";";
15115 $dbo->setQuery($q);
15116 $dbo->execute();
15117 }
15118
15119 $this->cancelcondtext();
15120 }
15121
15122 /**
15123 * AJAX endpoint to update one template file with the given tag or styles.
15124 * A JSON response will be echoed by exiting the process.
15125 */
15126 public function condtext_update_tmpl()
15127 {
15128 VikBooking::getConditionalRulesInstance(true);
15129
15130 $tagaction = VikRequest::getString('tagaction', '', 'request');
15131 $tag = VikRequest::getString('tag', '', 'request');
15132 $file = VikRequest::getString('file', '', 'request', VIKREQUEST_ALLOWRAW);
15133 $newcontent = VikRequest::getString('newcontent', '', 'request', VIKREQUEST_ALLOWRAW);
15134 $custom_classes = VikRequest::getVar('custom_classes', array(), 'request', 'array');
15135
15136 $allowed_actions = array(
15137 'add',
15138 'remove',
15139 'styles',
15140 'restore',
15141 );
15142
15143 if (empty($tagaction) || empty($file) || !in_array($tagaction, $allowed_actions)) {
15144 throw new Exception("Invalid request submitted", 500);
15145 }
15146
15147 if (in_array($tagaction, array('add', 'remove')) && empty($tag)) {
15148 throw new Exception("Invalid request submitted - missing tag", 500);
15149 }
15150
15151 if (in_array($tagaction, array('add', 'styles')) && empty($newcontent)) {
15152 throw new Exception("Invalid request submitted - missing new HTML content", 500);
15153 }
15154
15155 if ($tagaction == 'styles' && (!is_array($custom_classes) || !count($custom_classes))) {
15156 throw new Exception("No custom CSS classes to parse", 500);
15157 }
15158
15159 if ($tagaction == 'restore') {
15160 // immediately restore the requested file to avoid script interruptions
15161 VikBookingHelperConditionalRules::restoreTemplateFileCode($file);
15162 }
15163
15164 // get requested file content
15165 $fcontent = VikBookingHelperConditionalRules::getTemplateFileCode($file);
15166 if (empty($fcontent) || !is_string($fcontent)) {
15167 throw new Exception("File not found or its code is unreadable", 404);
15168 }
15169
15170 if ($tagaction == 'remove') {
15171 // remove tag from code content
15172 $fcontent = str_replace($tag, '', $fcontent);
15173 } elseif ($tagaction == 'add') {
15174 // add tag to code content in the same exact position
15175 $fcontent = VikBookingHelperConditionalRules::addTagByComparingSources($tag, $file, $newcontent, $fcontent);
15176 } elseif ($tagaction == 'styles') {
15177 // apply the same styling rules
15178 $fcontent = VikBookingHelperConditionalRules::addStylesByComparingSources($custom_classes, $file, $newcontent, $fcontent);
15179 }
15180
15181 // update the file code
15182 $res = VikBookingHelperConditionalRules::writeTemplateFileCode($file, $fcontent);
15183
15184 if (!$res) {
15185 throw new Exception("Could not update the source code of the template file", 500);
15186 }
15187
15188 // parse new HTML content
15189 $newhtmls = VikBookingHelperConditionalRules::getTemplateFilesContents($file);
15190 if (!is_array($newhtmls) || !isset($newhtmls[$file])) {
15191 throw new Exception("Could not parse new template file content", 404);
15192 }
15193
15194 // trigger backup/mirroring, if available
15195 if (VBOPlatformDetection::isWordPress()) {
15196 VikBookingUpdateManager::storeTemplateContent($file, $newhtmls[$file]);
15197 }
15198
15199 // build output
15200 $output = new stdClass;
15201 $output->newhtml = $newhtmls[$file];
15202 $output->log = VikBookingHelperConditionalRules::getEditingLog();
15203
15204 echo json_encode($output);
15205 exit;
15206 }
15207
15208 /**
15209 * AJAX endpoint to invoke methods of the geocoding helper.
15210 */
15211 public function geocoding_endpoint()
15212 {
15213 $geo = VikBooking::getGeocodingInstance();
15214 $callback = VikRequest::getString('callback', '', 'request');
15215
15216 if (empty($callback) || !method_exists($geo, $callback) || !is_callable(array($geo, $callback))) {
15217 throw new Exception("Callback not available", 403);
15218 }
15219
15220 // invoke requested method
15221 $res = $geo->{$callback}();
15222
15223 // prepare response
15224 $response = new stdClass;
15225 $response->{$callback} = $res;
15226
15227 echo json_encode($response);
15228 exit;
15229 }
15230
15231 public function refundtn()
15232 {
15233 //modal box, so we do not set menu or footer
15234
15235 VikRequest::setVar('view', VikRequest::getCmd('view', 'refundtn'));
15236
15237 parent::display();
15238 }
15239
15240 public function do_refundtn()
15241 {
15242 $dbo = JFactory::getDbo();
15243 $app = JFactory::getApplication();
15244
15245 $bid = VikRequest::getInt('bid', 0, 'request');
15246 $amount = VikRequest::getFloat('amount', 0, 'request');
15247 $refund_reason = VikRequest::getString('refund_reason', '', 'request');
15248 $tmpl = VikRequest::getString('tmpl', '', 'request');
15249 $nav_suffix = $tmpl == 'component' ? '&tmpl=component' : '';
15250
15251 $currencysymb = VikBooking::getCurrencySymb();
15252
15253 if (empty($bid) || $amount <= 0) {
15254 VikError::raiseWarning('', JText::translate('VBO_PLEASE_FILL_FIELDS'));
15255 $app->redirect('index.php?option=com_vikbooking');
15256 exit;
15257 }
15258
15259 $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $bid . " AND `status`!='standby';";
15260 $dbo->setQuery($q);
15261 $row = $dbo->loadAssoc();
15262 if (!$row) {
15263 VikError::raiseWarning('', 'Booking not found');
15264 $app->redirect('index.php?option=com_vikbooking');
15265 exit;
15266 }
15267
15268 // get booking history instance
15269 $history_obj = VikBooking::getBookingHistoryInstance();
15270 $history_obj->setBid($row['id']);
15271
15272 // get payment information
15273 $payment = VikBooking::getPayment($row['idpayment']);
15274 $tn_driver = is_array($payment) ? $payment['file'] : null;
15275
15276 // transaction data validation callback
15277 $tn_data_callback = function($data) use ($tn_driver) {
15278 return (is_object($data) && isset($data->driver) && basename($data->driver, '.php') == basename($tn_driver, '.php'));
15279 };
15280 // get previous transactions
15281 $prev_tn_data = $history_obj->getEventsWithData(array('P0', 'PN'), $tn_data_callback);
15282
15283 if (!is_array($prev_tn_data) || !count($prev_tn_data)) {
15284 // no previous transactions found
15285 VikError::raiseWarning('', 'No previous transactions found, unable to issue the refund');
15286 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15287 exit;
15288 }
15289
15290 // push refund information for the payment gateway
15291 $row['total_to_refund'] = $amount;
15292 $row['transaction'] = $prev_tn_data;
15293 $row['refund_reason'] = $refund_reason;
15294
15295 // push the transaction currency information
15296 $row['transaction_currency'] = VikBooking::getCurrencyCodePp();
15297
15298 /**
15299 * Trigger event to allow third-party plugins to manipulate the transaction data.
15300 *
15301 * @since 1.18.5 (J) - 1.8.5 (WP)
15302 */
15303 VBOFactory::getPlatform()->getDispatcher()->trigger('onInitRefundTransaction', [&$row, &$payment['params']]);
15304
15305 if (VBOPlatformDetection::isWordPress()) {
15306 /**
15307 * @wponly The payment gateway is loaded
15308 * through the apposite dispatcher.
15309 */
15310 JLoader::import('adapter.payment.dispatcher');
15311 $obj = JPaymentDispatcher::getInstance('vikbooking', $payment['file'], $row, $payment['params']);
15312 } else {
15313 /**
15314 * @joomlaonly The Payment Factory library will invoke the gateway.
15315 *
15316 * @since 1.14.3
15317 */
15318 require_once VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'payments' . DIRECTORY_SEPARATOR . 'libraries' . DIRECTORY_SEPARATOR . 'factory.php';
15319 $obj = VBOPaymentFactory::getPaymentInstance($payment['file'], $row, $payment['params']);
15320 }
15321
15322 if (!method_exists($obj, 'isRefundSupported') || !$obj->isRefundSupported()) {
15323 // refund not supported
15324 VikError::raiseWarning('', 'The selected payment method does not support refunds');
15325 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15326 exit;
15327 }
15328
15329 // perform the refund transaction
15330 $array_result = $obj->refund();
15331
15332 if ($array_result['verified'] != 1) {
15333 // raise warning by getting the message
15334 if (!empty($array_result['log']) && is_string($array_result['log'])) {
15335 VikError::raiseWarning('', $array_result['log']);
15336 } else {
15337 VikError::raiseWarning('', 'Operation failed');
15338 }
15339 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15340 exit;
15341 }
15342
15343 /**
15344 * New payment plugins can return the total amount refunded ('tot_paid').
15345 *
15346 * @since 1.15.4 (J) - 1.5.10 (WP)
15347 */
15348 if (!empty($array_result['tot_paid'])) {
15349 // overwrite the requested amount with the returned one
15350 $amount = (float)$array_result['tot_paid'];
15351 }
15352
15353 /**
15354 * The history event extra data will contain the "amount_paid" (refunded).
15355 *
15356 * @since 1.16.9 (J) - 1.6.9 (WP)
15357 */
15358 $history_obj->setExtraData([
15359 'amount_paid' => $amount,
15360 ]);
15361
15362 // update total paid, total and refund columns for the booking
15363 $booking = new stdClass;
15364 $booking->id = $row['id'];
15365 if ($row['totpaid'] > 0) {
15366 $booking->totpaid = $row['totpaid'] - $amount;
15367 }
15368 if ($row['total'] > 0) {
15369 $booking->total = $row['total'] - $amount;
15370 }
15371 $booking->refund = (float)$row['refund'] + $amount;
15372 // update record in db
15373 $dbo->updateObject('#__vikbooking_orders', $booking, 'id');
15374
15375 // store the refund event
15376 $event_descr = [
15377 '(' . $payment['name'] . ')',
15378 $refund_reason,
15379 $currencysymb . ' ' . VikBooking::numberFormat($amount),
15380 ];
15381 $history_obj->store('RF', implode("\n", $event_descr));
15382
15383 // display success message and redirect
15384 $app->enqueueMessage(JText::translate('VBO_REFUND_SUCCESS'));
15385 $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . '&success=1' . $nav_suffix);
15386 exit;
15387 }
15388
15389 /**
15390 * AJAX upload endpoint for media files.
15391 *
15392 * @return void
15393 *
15394 * @throws Exception
15395 *
15396 * @since 1.15.0 (J) - 1.5.0 (WP)
15397 */
15398 public function upload_media_file()
15399 {
15400 $input = JFactory::getApplication()->input;
15401
15402 // allowed types
15403 $type = $input->getString('type', '');
15404 $mask = 'png,apng,jpg,jpeg,bmp,heic,webp,gif,ico,svg';
15405
15406 if ($type != 'image') {
15407 $mask .= ',zip,rar,pdf,doc,docx,rtf,odt,pages,xls,xlsx,csv,ods,numbers,txt,md';
15408 }
15409
15410 // response object
15411 $result = new stdClass;
15412 $result->status = 0;
15413
15414 try
15415 {
15416 // get file from request
15417 $file = $input->files->get('file', array(), 'array');
15418
15419 // try to upload the file
15420 $result = VikBooking::uploadFileFromRequest($file, VBO_MEDIA_PATH, $mask);
15421 $result->status = 1;
15422
15423 $result->size = JHtml::fetch('number.bytes', filesize($result->path), 'auto', 0);
15424 $result->url = str_replace(DIRECTORY_SEPARATOR, '/', str_replace(VBO_MEDIA_PATH . DIRECTORY_SEPARATOR, VBO_MEDIA_URI, $result->path));
15425 }
15426 catch (Exception $e)
15427 {
15428 $result->error = $e->getMessage();
15429 $result->code = $e->getCode();
15430 }
15431
15432 echo json_encode($result);
15433 exit;
15434 }
15435
15436 /**
15437 * AJAX endpoint to invoke a report object's method.
15438 *
15439 * @return void
15440 *
15441 * @since 1.15.0 (J) - 1.5.0 (WP)
15442 * @since 1.18.6 (J) - 1.8.6 (WP) added support for "call_args".
15443 */
15444 public function invoke_report()
15445 {
15446 $app = JFactory::getApplication();
15447
15448 $report_name = $app->input->getString('report', '');
15449 $report_call = $app->input->getString('call', '');
15450 $call_args = $app->input->get('call_args', [], 'array');
15451 $params = $app->input->get('params', [], 'array');
15452
15453 if (empty($report_name)) {
15454 VBOHttpDocument::getInstance($app)->close(400, 'Missing report name');
15455 }
15456
15457 if (empty($report_call)) {
15458 VBOHttpDocument::getInstance($app)->close(400, 'Missing report call');
15459 }
15460
15461 // get requested report instance
15462 $report = VikBooking::getReportInstance($report_name);
15463 if (!$report) {
15464 VBOHttpDocument::getInstance($app)->close(404, 'Report not found');
15465 }
15466
15467 if (!method_exists($report, $report_call) || !is_callable(array($report, $report_call))) {
15468 VBOHttpDocument::getInstance($app)->close(403, sprintf('Cannot call [%s] on report', $report_call));
15469 }
15470
15471 try {
15472 // call on report's method
15473 if ($call_args) {
15474 $result = call_user_func_array([$report, $report_call], $call_args);
15475 } else {
15476 $result = $report->{$report_call}($params);
15477 }
15478 } catch (Exception $e) {
15479 VBOHttpDocument::getInstance($app)->close($e->getCode() ?: 500, $e->getMessage());
15480 }
15481
15482 if (is_null($result)) {
15483 VBOHttpDocument::getInstance($app)->close(400, 'Null response');
15484 }
15485
15486 if (is_scalar($result)) {
15487 // wrap result within an array for a JSON encoded response
15488 VBOHttpDocument::getInstance($app)->json([$result]);
15489 }
15490
15491 // output the JSON encoded array/object returned
15492 VBOHttpDocument::getInstance($app)->json($result);
15493 }
15494
15495 /**
15496 * Handles requests for the multitask widgets panel.
15497 *
15498 * @see this is an AJAX endpoint.
15499 *
15500 * @since 1.15.0 (J) - 1.5.0 (WP)
15501 * @since 1.16.5 (J) - 1.6.5 (WP) widgets are rendered within a try-catch statement.
15502 */
15503 public function exec_multitask_widgets()
15504 {
15505 if (!JSession::checkToken()) {
15506 // missing CSRF-proof token
15507 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15508 }
15509
15510 $call = VikRequest::getString('call', '', 'request');
15511 $call_args = VikRequest::getVar('call_args', array(), 'request', 'array');
15512
15513 if (empty($call)) {
15514 VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget Callback');
15515 }
15516
15517 // invoke admin widgets helper
15518 $widgets_helper = VikBooking::getAdminWidgetsInstance();
15519
15520 if (!method_exists($widgets_helper, $call) || !is_callable(array($widgets_helper, $call))) {
15521 VBOHttpDocument::getInstance()->close(403, 'Admin Widgets Callback not found or not callable');
15522 }
15523
15524 try {
15525 // invoke the helper's method and get the value returned
15526 if (is_array($call_args) && count($call_args)) {
15527 $result = call_user_func_array(array($widgets_helper, $call), $call_args);
15528 } else {
15529 $result = $widgets_helper->{$call}();
15530 }
15531 } catch (Throwable $e) {
15532 VBOHttpDocument::getInstance()->close($e->getCode() ?: 500, $e->getMessage());
15533 } catch (Exception $e) {
15534 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
15535 }
15536
15537 // prepare response object with the result property
15538 $response = new stdClass;
15539 $response->result = $result;
15540
15541 // output the JSON response and exit
15542 VBOHttpDocument::getInstance()->json($response);
15543 }
15544
15545 /**
15546 * Handles requests for displaying a browser notification being dispatched.
15547 *
15548 * @see this is an AJAX endpoint.
15549 *
15550 * @since 1.15.0 (J) - 1.5.0 (WP)
15551 */
15552 public function notification_displayer()
15553 {
15554 $payload_str = VikRequest::getString('payload', '', 'request', VIKREQUEST_ALLOWRAW);
15555
15556 if (empty($payload_str)) {
15557 VBOHttpDocument::getInstance()->close(500, 'Empty notification payload');
15558 }
15559
15560 // attempt to decode the notification payload
15561 $payload = json_decode($payload_str);
15562
15563 if (!is_object($payload)) {
15564 VBOHttpDocument::getInstance()->close(500, 'Could not decode notification payload: ' . $payload_str);
15565 }
15566
15567 // get notification displayer for this type of notification
15568 $displayer = VBONotificationBuilder::getInstance($payload)->getDisplayer();
15569 if (!$displayer) {
15570 VBOHttpDocument::getInstance()->close(500, 'Could not build notification display data from payload: ' . $payload_str);
15571 }
15572
15573 // compose the notification display data object
15574 try {
15575 $notif_data = $displayer->getData();
15576 if (!$notif_data) {
15577 throw new Exception('Error building the notification display data', 500);
15578 }
15579 } catch (Exception $e) {
15580 VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
15581 }
15582
15583 // output the JSON response and exit
15584 VBOHttpDocument::getInstance()->json($notif_data);
15585 }
15586
15587 /**
15588 * Handles requests for watching widgets data and getting
15589 * new events to trigger browser notifications.
15590 *
15591 * @see this is an AJAX endpoint.
15592 *
15593 * @since 1.15.0 (J) - 1.5.0 (WP)
15594 * @since 1.16.8 (J) - 1.6.8 (WP) introduced notification events.
15595 */
15596 public function widgets_watch_data()
15597 {
15598 if (!JSession::checkToken()) {
15599 // missing CSRF-proof token
15600 VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15601 }
15602
15603 $app = JFactory::getApplication();
15604
15605 $watch_data_str = $app->input->get('watch_data', '', 'raw');
15606 $pushed_data_str = $app->input->get('pushed_data', '[]', 'raw');
15607
15608 if (empty($watch_data_str)) {
15609 VBOHttpDocument::getInstance()->close(500, 'Empty watch-data payload');
15610 }
15611
15612 // attempt to decode the watch-data payload
15613 $watch_data = json_decode($watch_data_str, true);
15614
15615 if (!$watch_data) {
15616 VBOHttpDocument::getInstance()->close(500, 'Could not decode watch-data payload: ' . $watch_data_str);
15617 }
15618
15619 // check if any pushed data was set
15620 $pushed_data = (array)json_decode($pushed_data_str, true);
15621
15622 // container for new notifications
15623 $notifs_pool = [];
15624
15625 // container for the events data to dispatch
15626 $events_pool = [];
15627
15628 // get admin widgets helper
15629 $widgets_helper = VikBooking::getAdminWidgetsInstance();
15630
15631 foreach ($watch_data as $widget_id => $data) {
15632 // invoke admin widget (with no pre-loading)
15633 $widget_instance = $widgets_helper->getWidget($widget_id);
15634 if (!$widget_instance) {
15635 continue;
15636 }
15637
15638 // build the widget watch data object
15639 $widget_watch_data = VBONotificationWatchdata::getInstance($data)->setPushedData($pushed_data);
15640
15641 // check if the widget needs to emit browser notifications
15642 list($watch_next, $notifications) = $widget_instance->getNotifications($widget_watch_data);
15643
15644 // check if the widget needs to emit JavaScript events
15645 $events = $widget_instance->getNotificationEvents($widget_watch_data);
15646
15647 if ($watch_next) {
15648 // update next watch-data object for this widget
15649 $watch_data[$widget_id] = $watch_next;
15650 }
15651
15652 if (is_array($notifications) && $notifications) {
15653 // merge notifications
15654 $notifs_pool = array_merge($notifs_pool, $notifications);
15655 }
15656
15657 if (is_array($events) && $events) {
15658 // push notification events for this widget
15659 $events_pool[] = $events;
15660 }
15661 }
15662
15663 // build the response object
15664 $response = new stdClass;
15665 $response->watch_data = $watch_data;
15666 $response->notifications = $notifs_pool;
15667 $response->events = $events_pool;
15668
15669 // output the JSON response and exit
15670 VBOHttpDocument::getInstance()->json($response);
15671 }
15672
15673 /**
15674 * Outputs a list of CSS assets required to render the admin widgets
15675 * externally from Vik Booking. Useful i.e. to Vik Channel Manager.
15676 *
15677 * @see this is an AJAX endpoint.
15678 *
15679 * @since 1.16.0 (J) - 1.6.0 (WP)
15680 */
15681 public function widgets_get_assets()
15682 {
15683 // list of needed CSS asset details
15684 $assets_pool = [];
15685
15686 // appearance preference assets (one or none)
15687 $app_pref_asset = VikBooking::loadAppearancePreferenceAssets($get_info = true);
15688
15689 if (VBOPlatformDetection::isWordPress()) {
15690 // WordPress (main CSS)
15691 $assets_pool[] = [
15692 'rel' => 'stylesheet',
15693 'id' => 'vbo-style-css',
15694 'href' => VIKBOOKING_ADMIN_ASSETS_URI . 'vikbooking.css?ver=' . VIKBOOKING_SOFTWARE_VERSION,
15695 'media' => 'all',
15696 ];
15697
15698 if (is_array($app_pref_asset) && !empty($app_pref_asset['href'])) {
15699 // appearance preference CSS
15700 $assets_pool[] = [
15701 'rel' => 'stylesheet',
15702 'id' => (!empty($app_pref_asset['id']) ? $app_pref_asset['id'] : rand()),
15703 'href' => $app_pref_asset['href'] . '?ver=' . VIKBOOKING_SOFTWARE_VERSION,
15704 'media' => 'all',
15705 ];
15706 }
15707 } else {
15708 // Joomla (main CSS)
15709 $assets_pool[] = [
15710 'rel' => 'stylesheet',
15711 'id' => 'vbo-style-css',
15712 'href' => VBO_ADMIN_URI . 'vikbooking.css?' . VIKBOOKING_SOFTWARE_VERSION,
15713 'media' => 'all',
15714 ];
15715
15716 if (is_array($app_pref_asset) && !empty($app_pref_asset['href'])) {
15717 // appearance preference CSS
15718 $assets_pool[] = [
15719 'rel' => 'stylesheet',
15720 'id' => (!empty($app_pref_asset['id']) ? $app_pref_asset['id'] : rand()),
15721 'href' => $app_pref_asset['href'] . '?' . VIKBOOKING_SOFTWARE_VERSION,
15722 'media' => 'all',
15723 ];
15724 }
15725 }
15726
15727 // output the JSON response and exit
15728 VBOHttpDocument::getInstance()->json($assets_pool);
15729 }
15730 }
15731