PluginProbe
VikBooking Hotel Booking Engine & PMS / 1.8.16
VikBooking Hotel Booking Engine & PMS v1.8.16
1.8.16 1.8.15 1.8.14 1.8.13 1.8.12 1.8.11 1.8.10 1.8.9 1.8.6 1.8.7 1.8.8 trunk 1.6.0 1.6.1 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 All 37 releases
← All changes | admin/controller.php +15886 -0 1.6.3 → 1.8.16 View file →
@@ -1,0 +1,15886 @@
1 +<?php
2 +/**
3 + * @package VikBooking
4 + * @subpackage com_vikbooking
5 + * @author Alessio Gaggii - e4j - Extensionsforjoomla.com
6 + * @copyright Copyright (C) 2018 e4j - Extensionsforjoomla.com. All rights reserved.
7 + * @license GNU General Public License version 2 or later; see LICENSE
8 + * @link https://vikwp.com
9 + */
10 +
11 +defined('ABSPATH') or die('No script kiddies please!');
12 +
13 +// import Joomla controller library
14 +jimport('joomla.application.component.controller');
15 +
16 +class VikBookingController extends JControllerVikBooking
17 +{
18 + /**
19 + * Default controller's method when no task is defined,
20 + * or no method exists for that task. If a View is requested.
21 + * attempts to set it, otherwise sets the default View.
22 + */
23 + public function display($cachable = false, $urlparams = array()) {
24 +
25 + $view = VikRequest::getVar('view', '');
26 + $header_val = '';
27 +
28 + if (!empty($view)) {
29 + $header_val = $view;
30 + VikRequest::setVar('view', $view);
31 + } else {
32 + $header_val = '18';
33 + VikRequest::setVar('view', 'dashboard');
34 + }
35 +
36 + $hide_menu = JFactory::getApplication()->input->getBool('hide_menu', false);
37 +
38 + if ($hide_menu === false)
39 + {
40 + VikBookingHelper::printHeader($header_val);
41 + }
42 +
43 + parent::display();
44 +
45 + if (VikBooking::showFooter() && $hide_menu === false) {
46 + VikBookingHelper::printFooter();
47 + }
48 + }
49 +
50 + /**
51 + * AJAX request for building dynamic donut charts.
52 + *
53 + * @return void
54 + *
55 + * @since 1.12.1
56 + */
57 + public function donut_charts_data() {
58 + $fromdt = VikRequest::getString('fromdt', date('Y-m-d'), 'request');
59 + $direction = VikRequest::getString('direction', 'next', 'request');
60 + $days = VikRequest::getInt('days', 7, 'request');
61 + if (empty($fromdt) || !strtotime($fromdt) || empty($days) || $days < 1) {
62 + throw new Exception('Missing required data', 400);
63 + }
64 +
65 + $from_info = getdate(strtotime($fromdt));
66 + if ($direction != 'next') {
67 + // fromdt is always the next day after the end of the loop, so the very first next day from the last displayed
68 + $from_info = getdate(mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] - ($days * 2)), $from_info['year']));
69 + }
70 + // always push the start date to the last second (23:59:59)
71 + $from_info = getdate(mktime(23, 59, 59, $from_info['mon'], $from_info['mday'], $from_info['year']));
72 +
73 + // months front-end language map
74 + $monthsmap = array(
75 + JText::translate('VBSHORTMONTHONE'),
76 + JText::translate('VBSHORTMONTHTWO'),
77 + JText::translate('VBSHORTMONTHTHREE'),
78 + JText::translate('VBSHORTMONTHFOUR'),
79 + JText::translate('VBSHORTMONTHFIVE'),
80 + JText::translate('VBSHORTMONTHSIX'),
81 + JText::translate('VBSHORTMONTHSEVEN'),
82 + JText::translate('VBSHORTMONTHEIGHT'),
83 + JText::translate('VBSHORTMONTHNINE'),
84 + JText::translate('VBSHORTMONTHTEN'),
85 + JText::translate('VBSHORTMONTHELEVEN'),
86 + JText::translate('VBSHORTMONTHTWELVE'),
87 + );
88 +
89 + // weekdays front-end language map
90 + $wdaysmap = array(
91 + JText::translate('VBSUNDAY'),
92 + JText::translate('VBMONDAY'),
93 + JText::translate('VBTUESDAY'),
94 + JText::translate('VBWEDNESDAY'),
95 + JText::translate('VBTHURSDAY'),
96 + JText::translate('VBFRIDAY'),
97 + JText::translate('VBSATURDAY'),
98 + );
99 +
100 + // gather information about the rooms and availability
101 + $dbo = JFactory::getDbo();
102 + $all_rooms_ids = array();
103 + $unpublished_rooms = array();
104 + $todayymd = date('Y-m-d');
105 + $q = "SELECT `id`,`name`,`units`,`params`,`avail` FROM `#__vikbooking_rooms`;";
106 + $dbo->setQuery($q);
107 + $dbo->execute();
108 + if ($dbo->getNumRows()) {
109 + $all_rooms = $dbo->loadAssocList();
110 + foreach ($all_rooms as $k => $r) {
111 + if ($r['avail'] < 1) {
112 + $unpublished_rooms[] = $r['id'];
113 + }
114 + $all_rooms_ids[$r['id']] = $r['name'];
115 + }
116 + }
117 + $q = "SELECT SUM(`units`) FROM `#__vikbooking_rooms` WHERE `avail`=1;";
118 + $dbo->setQuery($q);
119 + $dbo->execute();
120 + $tot_rooms_units = (int)$dbo->loadResult();
121 +
122 + // load busy records
123 + $expected_max_ts = mktime(23, 59, 59, $from_info['mon'], ($from_info['mday'] + $days), $from_info['year']);
124 + $busy = VikBooking::loadBusyRecordsUnclosed(array_keys($all_rooms_ids), $from_info[0], $expected_max_ts);
125 +
126 + // response body
127 + $response = new stdClass;
128 + $response->prevweek = ($todayymd != date('Y-m-d', $from_info[0]));
129 + $response->nextweek = true;
130 + $response->fromd = date('Y-m-d', $from_info[0]);
131 + $response->tot_units = $tot_rooms_units;
132 + $response->data = array();
133 +
134 + for ($i = 0; $i < $days; $i++) {
135 + $tot_booked_today = 0;
136 + $today_ts = $from_info[0];
137 + $data_obj = new stdClass;
138 + $data_obj->ymd = date('Y-m-d', $from_info[0]);
139 + $data_obj->lbl = $wdaysmap[(int)$from_info['wday']] . ', ' . $from_info['mday'];
140 + $data_obj->lbl = $data_obj->ymd == $todayymd ? JText::translate('VBTODAY') . ', ' . $data_obj->lbl : $data_obj->lbl . ' ' . $monthsmap[($from_info['mon'] - 1)];
141 + foreach ($busy as $idroom => $rbusy) {
142 + if (in_array($idroom, $unpublished_rooms)) {
143 + continue;
144 + }
145 + foreach ($rbusy as $b) {
146 + $tmpone = getdate($b['checkin']);
147 + $ritts = mktime(0, 0, 0, $tmpone['mon'], $tmpone['mday'], $tmpone['year']);
148 + $tmptwo = getdate($b['checkout']);
149 + $conts = mktime(0, 0, 0, $tmptwo['mon'], $tmptwo['mday'], $tmptwo['year']);
150 + if ($today_ts >= $ritts && $today_ts < $conts) {
151 + $tot_booked_today++;
152 + }
153 + }
154 + }
155 +
156 + $data_obj->tot_booked = $tot_booked_today;
157 + $percentage_booked = round((100 * $tot_booked_today / $tot_rooms_units), 2);
158 +
159 + $data_obj->color = '#ff4d4d'; //red
160 + if ($percentage_booked > 33 && $percentage_booked <= 66) {
161 + $data_obj->color = '#ffa64d'; //orange
162 + } elseif ($percentage_booked > 66 && $percentage_booked < 100) {
163 + $data_obj->color = '#2a762c'; //green
164 + } elseif ($percentage_booked >= 100) {
165 + $data_obj->color = '#2482b4'; //light-blue
166 + }
167 +
168 + // push today's data
169 + array_push($response->data, $data_obj);
170 +
171 + // next day
172 + $from_info = getdate(mktime(23, 59, 59, $from_info['mon'], ($from_info['mday'] + 1), $from_info['year']));
173 + }
174 +
175 + // update last date (not displayed/included)
176 + $response->tod = date('Y-m-d', $from_info[0]);
177 +
178 + echo json_encode($response);
179 + exit;
180 + }
181 +
182 + /**
183 + * AJAX request for adding a new fest.
184 + *
185 + * @return void
186 + *
187 + * @since 1.2.0
188 + */
189 + public function add_fest()
190 + {
191 + $dt = VikRequest::getString('dt', '', 'request');
192 + $type = VikRequest::getString('type', '', 'request');
193 + $type = empty($type) ? 'custom' : $type;
194 + $name = VikRequest::getString('name', '', 'request');
195 + $descr = VikRequest::getString('descr', '', 'request');
196 +
197 + if (empty($name) || empty($dt) || !strtotime($dt)) {
198 + VBOHttpDocument::getInstance()->close(400, 'Missing mandatory festivity details');
199 + }
200 +
201 + // build fest array
202 + $new_fest = [
203 + 'trans_name' => $name,
204 + ];
205 +
206 + $fests = VikBooking::getFestivitiesInstance();
207 + $result = $fests->storeFestivity($dt, $new_fest, $type, $descr);
208 + if (!$result) {
209 + VBOHttpDocument::getInstance()->close(400, 'Could not store festivity details');
210 + }
211 +
212 + // reload all festivities for this day for the AJAX response
213 + $all_fests = $fests->loadFestDates($dt, $dt);
214 + foreach ($all_fests as $k => $v) {
215 + // we expect just one record to be returned due to the from/to date limit passed to loadFestDates()
216 + VBOHttpDocument::getInstance()->json($v);
217 + }
218 +
219 + // no fests found even after storing it
220 + VBOHttpDocument::getInstance()->close(404, 'Festivity record not found after saving');
221 + }
222 +
223 + /**
224 + * AJAX request for removing a fest.
225 + *
226 + * @return void
227 + *
228 + * @since 1.2.0
229 + */
230 + public function remove_fest()
231 + {
232 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
233 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
234 + }
235 +
236 + $dt = VikRequest::getString('dt', '', 'request');
237 + $ind = VikRequest::getInt('ind', 0, 'request');
238 + $type = VikRequest::getString('type', '', 'request');
239 + $type = empty($type) ? 'custom' : $type;
240 + if (empty($dt) || !strtotime($dt)) {
241 + echo 'e4j.error.1';
242 + exit;
243 + }
244 +
245 + $fests = VikBooking::getFestivitiesInstance();
246 + $result = $fests->deleteFestivity($dt, $ind, $type);
247 + if (!$result) {
248 + echo 'e4j.error.2';
249 + exit;
250 + }
251 +
252 + echo 'e4j.ok';
253 + exit;
254 + }
255 +
256 + public function einvoicing() {
257 + VikBookingHelper::printHeader("einvoicing");
258 +
259 + VikRequest::setVar('view', VikRequest::getCmd('view', 'einvoicing'));
260 +
261 + parent::display();
262 +
263 + if (VikBooking::showFooter()) {
264 + VikBookingHelper::printFooter();
265 + }
266 + }
267 +
268 + public function pmsreports() {
269 + if (!JFactory::getUser()->authorise('core.vbo.pms', 'com_vikbooking') && !JFactory::getUser()->authorise('core.vbo.pmsreports', 'com_vikbooking')) {
270 + VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
271 + }
272 +
273 + VikBookingHelper::printHeader("pmsreports");
274 +
275 + VikRequest::setVar('view', VikRequest::getCmd('view', 'pmsreports'));
276 +
277 + parent::display();
278 +
279 + if (VikBooking::showFooter()) {
280 + VikBookingHelper::printFooter();
281 + }
282 + }
283 +
284 + public function ratesoverv() {
285 + VikBookingHelper::printHeader("20");
286 +
287 + VikRequest::setVar('view', VikRequest::getCmd('view', 'ratesoverv'));
288 +
289 + parent::display();
290 +
291 + if (VikBooking::showFooter()) {
292 + VikBookingHelper::printFooter();
293 + }
294 + }
295 +
296 + public function stats() {
297 + VikBookingHelper::printHeader("stats");
298 +
299 + VikRequest::setVar('view', VikRequest::getCmd('view', 'stats'));
300 +
301 + parent::display();
302 +
303 + if (VikBooking::showFooter()) {
304 + VikBookingHelper::printFooter();
305 + }
306 + }
307 +
308 + /**
309 + * AJAX endpoint to calculate the website rates.
310 + *
311 + * @return void
312 + */
313 + public function calc_rates()
314 + {
315 + $response = 'e4j.error.ErrorCode(1) default error';
316 + $response_code = 0;
317 +
318 + // availability helper
319 + $av_helper = VikBooking::getAvailabilityInstance();
320 +
321 + $currencysymb = VikBooking::getCurrencySymb();
322 + $vbo_df = VikBooking::getDateFormat();
323 + $df = $vbo_df == "%d/%m/%Y" ? 'd/m/Y' : ($vbo_df == "%m/%d/%Y" ? 'm/d/Y' : 'Y/m/d');
324 + $id_room = VikRequest::getInt('id_room', 0, 'request');
325 + $checkin = VikRequest::getString('checkin', '', 'request');
326 + $nights = VikRequest::getInt('num_nights', 1, 'request');
327 + $adults = VikRequest::getInt('num_adults', 0, 'request');
328 + $children = VikRequest::getInt('num_children', 0, 'request');
329 + /**
330 + * The page Calendar may call this task via AJAX to obtain information
331 + * about the various rate plans and final costs associated.
332 + *
333 + * @since 1.13 (J) - 1.3.0 (WP)
334 + */
335 + $only_rates = VikRequest::getInt('only_rates', 0, 'request');
336 + $units = VikRequest::getInt('units', 1, 'request');
337 + $checkinfdate = VikRequest::getString('checkinfdate', '', 'request');
338 + $checkoutfdate = VikRequest::getString('checkoutfdate', '', 'request');
339 + if (!empty($checkinfdate) && empty($checkin)) {
340 + $checkin = date('Y-m-d', VikBooking::getDateTimestamp($checkinfdate, 0, 0, 0));
341 + }
342 +
343 + $checkin_ts = strtotime($checkin);
344 + if (empty($checkin_ts)) {
345 + $checkin = date('Y-m-d');
346 + $checkin_ts = strtotime($checkin);
347 + }
348 +
349 + if (!empty($checkoutfdate) && !empty($checkinfdate) && $nights < 2) {
350 + // checkout date was given rather than number of nights
351 + $checkout_ts = VikBooking::getDateTimestamp($checkoutfdate, 0, 0, 0);
352 + $checkout = date('Y-m-d', $checkout_ts);
353 + $nights = $av_helper->countNightsOfStay($checkin_ts, $checkout_ts);
354 + } else {
355 + // calculate checkout depending on number of nights of stay
356 + $is_dst = date('I', $checkin_ts);
357 + $checkout_ts = $checkin_ts;
358 + for ($i = 1; $i <= $nights; $i++) {
359 + $checkout_ts += 86400;
360 + $is_now_dst = date('I', $checkout_ts);
361 + if ($is_dst != $is_now_dst) {
362 + if ((int)$is_dst == 1) {
363 + $checkout_ts += 3600;
364 + } else {
365 + $checkout_ts -= 3600;
366 + }
367 + $is_dst = $is_now_dst;
368 + }
369 + }
370 + $checkout = date('Y-m-d', $checkout_ts);
371 + }
372 +
373 + /**
374 + * We got rid of the CURL request to the front-end task of VBO "tac_av_l"
375 + * by replacing the call with the new helper class VikBookingAvailability.
376 + *
377 + * @since 1.15.0 (J) - 1.5.0 (WP)
378 + */
379 + $av_helper->setStayDates($checkin, $checkout);
380 + $av_helper->setRoomParty($adults, $children);
381 + // build extra params to obtain the necessary data
382 + $params = [
383 + 'hash' => md5('vbo.e4j.vbo'),
384 + 'req_type' => 'hotel_availability',
385 + 'nights' => $nights,
386 + 'num_rooms' => 1,
387 + 'only_rates' => $only_rates,
388 + 'forced_room_ids' => $id_room ? [$id_room] : null,
389 + ];
390 + $arr_res = $av_helper->getRates($params);
391 +
392 + // pricing pool
393 + $price_details = [];
394 +
395 + if (is_array($arr_res)) {
396 + if (!strlen($av_helper->getError())) {
397 + if (array_key_exists($id_room, $arr_res)) {
398 + $response = '';
399 + foreach ($arr_res[$id_room] as $rate) {
400 + // build pricing object
401 + $rplan_details = new stdClass;
402 + $rplan_details->idprice = $rate['idprice'];
403 + $rplan_details->name = $rate['pricename'];
404 + $rplan_details->net = $rate['cost'];
405 + $rplan_details->fnet = VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['cost']), $currencysymb);
406 + $rplan_details->tax = $rate['taxes'];
407 + $rplan_details->ftax = VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['taxes']), $currencysymb);
408 + $rplan_details->tot = $rate['cost'] + $rate['taxes'];
409 + $rplan_details->ftot = VikBooking::formatCurrencyNumber(VikBooking::numberFormat(($rate['cost'] + $rate['taxes'])), $currencysymb);
410 + array_push($price_details, $rplan_details);
411 + //
412 + $extra_response = '';
413 + $response .= '<div class="vbo-calcrates-rateblock" data-idprice="' . $rate['idprice'] . '" data-idroom="' . $id_room . '" data-checkin="' . $checkin . '" data-checkout="' . $checkout . '" data-adults="' . $adults . '" data-children="' . $children . '">';
414 + $response .= '<span class="vbo-calcrates-ratename">'.$rate['pricename'].'</span>';
415 + $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratenet"><span>'.JText::translate('VBCALCRATESNET').'</span>'.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['cost']), $currencysymb).'</span>';
416 + $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratetax"><span>'.JText::translate('VBCALCRATESTAX').'</span>'.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['taxes']), $currencysymb).'</span>';
417 + if (!empty($rate['city_taxes'])) {
418 + $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratecitytax"><span>'.JText::translate('VBCALCRATESCITYTAX').'</span>'.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['city_taxes']), $currencysymb).'</span>';
419 + }
420 + if (!empty($rate['fees'])) {
421 + $response .= '<span class="vbo-calcrates-pricedet vbo-calcrates-ratefees"><span>'.JText::translate('VBCALCRATESFEES').'</span>'.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($rate['fees']), $currencysymb).'</span>';
422 + }
423 + if (array_key_exists('affdays', $rate) && $rate['affdays'] > 0) {
424 + $extra_response .= '<span class="vbo-calcrates-extrapricedet vbo-calcrates-ratespaffdays"><span>'.JText::translate('VBCALCRATESSPAFFDAYS').'</span>'.$rate['affdays'].'</span>';
425 + }
426 + if (array_key_exists('diffusagediscount', $rate) && count($rate['diffusagediscount']) > 0) {
427 + foreach ($rate['diffusagediscount'] as $roomnumb => $disc) {
428 + $extra_response .= '<span class="vbo-calcrates-extrapricedet vbo-calcrates-rateoccupancydisc"><span>'.JText::sprintf('VBCALCRATESADUOCCUPANCY', $rate['diffusage']).'</span>- '.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($disc), $currencysymb).'</span>';
429 + break;
430 + }
431 + } elseif (array_key_exists('diffusagecost', $rate) && count($rate['diffusagecost']) > 0) {
432 + foreach ($rate['diffusagecost'] as $roomnumb => $charge) {
433 + $extra_response .= '<span class="vbo-calcrates-extrapricedet vbo-calcrates-rateoccupancycharge"><span>'.JText::sprintf('VBCALCRATESADUOCCUPANCY', $rate['diffusage']).'</span>+ '.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($charge), $currencysymb).'</span>';
434 + break;
435 + }
436 + }
437 + $tot = $rate['cost'] + $rate['taxes'] + $rate['city_taxes'] + $rate['fees'];
438 + $tot = round($tot, 2);
439 + $response .= '<span class="vbo-calcrates-ratetotal"><span>'.JText::translate('VBCALCRATESTOT').'</span>'.VikBooking::formatCurrencyNumber(VikBooking::numberFormat($tot), $currencysymb).'</span>';
440 + if (!empty($extra_response)) {
441 + $response .= '<div class="vbo-calcrates-info">'.$extra_response.'</div>';
442 + }
443 + $response .= '</div>';
444 + }
445 + } else {
446 + $response = 'e4j.error.'.JText::sprintf('VBCALCRATESROOMNOTAVAILCOMBO', date($df, $checkin_ts), date($df, $checkout_ts));
447 + /**
448 + * Set a response code so that the View calendar can understand that the room is not available or has no rates.
449 + *
450 + * @since 1.14 (J) - 1.4.0 (WP)
451 + */
452 + if (isset($arr_res['fullybooked']) && in_array($id_room, $arr_res['fullybooked'])) {
453 + $response_code = -1;
454 + }
455 + }
456 + } else {
457 + $response = 'e4j.error.' . $av_helper->getError();
458 + /**
459 + * Set a response code so that the View calendar can understand that the room is not available or has no rates.
460 + *
461 + * @since 1.14 (J) - 1.4.0 (WP)
462 + */
463 + if (isset($arr_res['fullybooked']) && in_array($id_room, $arr_res['fullybooked'])) {
464 + $response_code = -1;
465 + }
466 + }
467 + } else {
468 + $response = 'e4j.error.' . $av_helper->getError();
469 + }
470 +
471 + if (!$arr_res && $id_room && in_array($id_room, $av_helper->getFullyBooked())) {
472 + // set the response code to indicate the room is fully booked
473 + $response_code = -1;
474 + }
475 +
476 + if ($only_rates && strpos($response, 'e4j.error') === false) {
477 + VBOHttpDocument::getInstance()->json($price_details);
478 + }
479 +
480 + // do not do only echo trim($response); or the currency symbol will not be encoded on some servers
481 + $safe_response = array(trim($response));
482 + if ($only_rates && !empty($response_code)) {
483 + array_push($safe_response, $response_code);
484 + }
485 +
486 + VBOHttpDocument::getInstance()->json($safe_response);
487 + }
488 +
489 + /**
490 + * This is an AJAX endpoint.
491 + */
492 + public function cron_exec()
493 + {
494 + ob_start();
495 +
496 + VikRequest::setVar('view', VikRequest::getCmd('view', 'cronexec'));
497 +
498 + parent::display();
499 +
500 + $content = ob_get_contents();
501 + ob_end_clean();
502 +
503 + VBOHttpDocument::getInstance()->json([$content]);
504 + }
505 +
506 + public function downloadcron()
507 + {
508 + /**
509 + * @wponly no more executable files need to be downloaded for WordPress.
510 + */
511 + VBOHttpDocument::getInstance()->close(406, 'Cron Jobs must be executed through WPCron');
512 + }
513 +
514 + /**
515 + * This is an AJAX endpoint.
516 + */
517 + public function cronlogs()
518 + {
519 + $dbo = JFactory::getDBO();
520 + $pcron_id = VikRequest::getInt('cron_id', '', 'request');
521 +
522 + ob_start();
523 +
524 + $q = "SELECT * FROM `#__vikbooking_cronjobs` WHERE `id`=".(int)$pcron_id.";";
525 + $dbo->setQuery($q);
526 + $dbo->execute();
527 + if ($dbo->getNumRows() == 1) {
528 + $cron_data = $dbo->loadAssoc();
529 + $cron_data['logs'] = empty($cron_data['logs']) ? '--------' : $cron_data['logs'];
530 + echo '<pre>'.print_r($cron_data['logs'], true).'</pre>';
531 + }
532 +
533 + $content = ob_get_contents();
534 + ob_end_clean();
535 +
536 + VBOHttpDocument::getInstance()->json([$content]);
537 + }
538 +
539 + public function packages() {
540 + VikBookingHelper::printHeader("packages");
541 +
542 + VikRequest::setVar('view', VikRequest::getCmd('view', 'packages'));
543 +
544 + parent::display();
545 +
546 + if (VikBooking::showFooter()) {
547 + VikBookingHelper::printFooter();
548 + }
549 + }
550 +
551 + public function newpackage() {
552 + VikBookingHelper::printHeader("packages");
553 +
554 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managepackage'));
555 +
556 + parent::display();
557 +
558 + if (VikBooking::showFooter()) {
559 + VikBookingHelper::printFooter();
560 + }
561 + }
562 +
563 + public function editpackage() {
564 + VikBookingHelper::printHeader("packages");
565 +
566 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managepackage'));
567 +
568 + parent::display();
569 +
570 + if (VikBooking::showFooter()) {
571 + VikBookingHelper::printFooter();
572 + }
573 + }
574 +
575 + public function createpackage()
576 + {
577 + if (!JSession::checkToken()) {
578 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
579 + }
580 +
581 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
582 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
583 + }
584 +
585 + $this->do_createpackage();
586 + }
587 +
588 + public function createpackagestay()
589 + {
590 + if (!JSession::checkToken()) {
591 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
592 + }
593 +
594 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
595 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
596 + }
597 +
598 + $this->do_createpackage(true);
599 + }
600 +
601 + private function do_createpackage($stay = false) {
602 + $dbo = JFactory::getDBO();
603 + $mainframe = JFactory::getApplication();
604 + $pname = VikRequest::getString('name', '', 'request');
605 + $palias = VikRequest::getString('alias', '', 'request');
606 + $palias = empty($palias) ? $pname : $palias;
607 + $palias = JFilterOutput::stringURLSafe($palias);
608 + $pimg = VikRequest::getVar('img', null, 'files', 'array');
609 + $pfrom = VikRequest::getString('from', '', 'request');
610 + $pto = VikRequest::getString('to', '', 'request');
611 + $pexcludeday = VikRequest::getVar('excludeday', array());
612 + $strexcldates = array();
613 + foreach ($pexcludeday as $exclday) {
614 + if (!empty($exclday)) {
615 + $strexcldates[] = $exclday;
616 + }
617 + }
618 + $strexcldates = implode(';', $strexcldates);
619 + $prooms = VikRequest::getVar('rooms', array());
620 + $pminlos = VikRequest::getInt('minlos', '', 'request');
621 + $pminlos = $pminlos < 1 ? 1 : $pminlos;
622 + $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
623 + $pmaxlos = $pmaxlos < 0 ? 0 : $pmaxlos;
624 + $pmaxlos = $pmaxlos < $pminlos ? 0 : $pmaxlos;
625 + $pcost = VikRequest::getFloat('cost', '', 'request');
626 + $paliq = VikRequest::getInt('aliq', '', 'request');
627 + $ppernight_total = VikRequest::getInt('pernight_total', '', 'request');
628 + $ppernight_total = $ppernight_total == 1 ? 1 : 2;
629 + $pperperson = VikRequest::getInt('perperson', '', 'request');
630 + $pperperson = $pperperson > 0 ? 1 : 0;
631 + $pshowoptions = VikRequest::getInt('showoptions', '', 'request');
632 + $pshowoptions = $pshowoptions >= 1 && $pshowoptions <= 3 ? $pshowoptions : 1;
633 + $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWRAW);
634 + $pshortdescr = VikRequest::getString('shortdescr', '', 'request', VIKREQUEST_ALLOWHTML);
635 + $pconditions = VikRequest::getString('conditions', '', 'request', VIKREQUEST_ALLOWRAW);
636 + $pbenefits = VikRequest::getString('benefits', '', 'request', VIKREQUEST_ALLOWHTML);
637 + $ptsinit = VikBooking::getDateTimestamp($pfrom, '0', '0');
638 + $ptsend = VikBooking::getDateTimestamp($pto, '23', '59');
639 + $ptsinit = empty($ptsinit) ? time() : $ptsinit;
640 + $ptsend = empty($ptsend) || $ptsend < $ptsinit ? $ptsinit : $ptsend;
641 + //file upload
642 + jimport('joomla.filesystem.file');
643 + $gimg = "";
644 + if (isset($pimg) && strlen(trim($pimg['name']))) {
645 + $pautoresize = VikRequest::getString('autoresize', '', 'request');
646 + $presizeto = VikRequest::getInt('resizeto', '', 'request');
647 + $creativik = new vikResizer();
648 + $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimg['name'])));
649 + $src = $pimg['tmp_name'];
650 + $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
651 + $j = "";
652 + if (file_exists($dest.$filename)) {
653 + $j = rand(171, 1717);
654 + while (file_exists($dest.$j.$filename)) {
655 + $j++;
656 + }
657 + }
658 + $finaldest = $dest.$j.$filename;
659 + $check = getimagesize($pimg['tmp_name']);
660 + if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
661 + if (VikBooking::uploadFile($src, $finaldest)) {
662 + $gimg = $j.$filename;
663 + //orig img
664 + $origmod = true;
665 + if ($pautoresize == "1" && !empty($presizeto)) {
666 + $origmod = $creativik->proportionalImage($finaldest, $dest.'big_'.$j.$filename, $presizeto, $presizeto);
667 + } else {
668 + VikBooking::uploadFile($finaldest, $dest.'big_'.$j.$filename, true);
669 + }
670 + //thumb
671 + $thumbsize = VikBooking::getThumbSize();
672 + $thumb = $creativik->proportionalImage($finaldest, $dest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
673 + if (!$thumb || !$origmod) {
674 + if (file_exists($dest.'big_'.$j.$filename)) @unlink($dest.'big_'.$j.$filename);
675 + if (file_exists($dest.'thumb_'.$j.$filename)) @unlink($dest.'thumb_'.$j.$filename);
676 + VikError::raiseWarning('', 'Error Uploading the File: '.$pimg['name']);
677 + }
678 + @unlink($finaldest);
679 + } else {
680 + VikError::raiseWarning('', 'Error while uploading image');
681 + }
682 + } else {
683 + VikError::raiseWarning('', 'Uploaded file is not an Image');
684 + }
685 + }
686 + //
687 + $goto = "index.php?option=com_vikbooking&task=packages";
688 + $q = "INSERT INTO `#__vikbooking_packages` (`name`,`alias`,`img`,`dfrom`,`dto`,`excldates`,`minlos`,`maxlos`,`cost`,`idiva`,`pernight_total`,`perperson`,`descr`,`shortdescr`,`benefits`,`conditions`,`showoptions`) VALUES (".$dbo->quote($pname).", ".$dbo->quote($palias).", ".$dbo->quote($gimg).", ".(int)$ptsinit.", ".(int)$ptsend.", ".$dbo->quote($strexcldates).", ".(int)$pminlos.", ".(int)$pmaxlos.", ".$dbo->quote($pcost).",'".$paliq."', ".(int)$ppernight_total.", ".(int)$pperperson.", ".$dbo->quote($pdescr).", ".$dbo->quote($pshortdescr).", ".$dbo->quote($pbenefits).", ".$dbo->quote($pconditions).", ".(int)$pshowoptions.");";
689 + $dbo->setQuery($q);
690 + $dbo->execute();
691 + $lid = $dbo->insertid();
692 + if (!empty($lid)) {
693 + $mainframe->enqueueMessage(JText::translate('VBOPKGSAVED'));
694 + if ($stay) {
695 + $goto = "index.php?option=com_vikbooking&task=editpackage&cid[]=".$lid;
696 + }
697 + foreach ($prooms as $roomid) {
698 + if (!empty($roomid)) {
699 + $q = "INSERT INTO `#__vikbooking_packages_rooms` (`idpackage`,`idroom`) VALUES (".(int)$lid.", ".(int)$roomid.");";
700 + $dbo->setQuery($q);
701 + $dbo->execute();
702 + }
703 + }
704 + }
705 + $mainframe->redirect($goto);
706 + }
707 +
708 + public function updatepackage()
709 + {
710 + if (!JSession::checkToken()) {
711 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
712 + }
713 +
714 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
715 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
716 + }
717 +
718 + $this->do_updatepackage();
719 + }
720 +
721 + public function updatepackagestay()
722 + {
723 + if (!JSession::checkToken()) {
724 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
725 + }
726 +
727 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
728 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
729 + }
730 +
731 + $this->do_updatepackage(true);
732 + }
733 +
734 + private function do_updatepackage($stay = false) {
735 + $dbo = JFactory::getDBO();
736 + $mainframe = JFactory::getApplication();
737 + $pwhereup = VikRequest::getInt('whereup', '', 'request');
738 + $q = "SELECT * FROM `#__vikbooking_packages` WHERE `id`=".(int)$pwhereup.";";
739 + $dbo->setQuery($q);
740 + $dbo->execute();
741 + if ($dbo->getNumRows() == 1) {
742 + $pkg_data = $dbo->loadAssoc();
743 + } else {
744 + VikError::raiseWarning('', 'Not Found.');
745 + $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
746 + exit;
747 + }
748 + $pname = VikRequest::getString('name', '', 'request');
749 + $palias = VikRequest::getString('alias', '', 'request');
750 + $palias = empty($palias) ? $pname : $palias;
751 + $palias = JFilterOutput::stringURLSafe($palias);
752 + $pimg = VikRequest::getVar('img', null, 'files', 'array');
753 + $pfrom = VikRequest::getString('from', '', 'request');
754 + $pto = VikRequest::getString('to', '', 'request');
755 + $pexcludeday = VikRequest::getVar('excludeday', array());
756 + $strexcldates = array();
757 + foreach ($pexcludeday as $exclday) {
758 + if (!empty($exclday)) {
759 + $strexcldates[] = $exclday;
760 + }
761 + }
762 + $strexcldates = implode(';', $strexcldates);
763 + $prooms = VikRequest::getVar('rooms', array());
764 + $pminlos = VikRequest::getInt('minlos', '', 'request');
765 + $pminlos = $pminlos < 1 ? 1 : $pminlos;
766 + $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
767 + $pmaxlos = $pmaxlos < 0 ? 0 : $pmaxlos;
768 + $pmaxlos = $pmaxlos < $pminlos ? 0 : $pmaxlos;
769 + $pcost = VikRequest::getFloat('cost', '', 'request');
770 + $paliq = VikRequest::getInt('aliq', '', 'request');
771 + $ppernight_total = VikRequest::getInt('pernight_total', '', 'request');
772 + $ppernight_total = $ppernight_total == 1 ? 1 : 2;
773 + $pperperson = VikRequest::getInt('perperson', '', 'request');
774 + $pperperson = $pperperson > 0 ? 1 : 0;
775 + $pshowoptions = VikRequest::getInt('showoptions', '', 'request');
776 + $pshowoptions = $pshowoptions >= 1 && $pshowoptions <= 3 ? $pshowoptions : 1;
777 + $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWRAW);
778 + $pshortdescr = VikRequest::getString('shortdescr', '', 'request', VIKREQUEST_ALLOWHTML);
779 + $pconditions = VikRequest::getString('conditions', '', 'request', VIKREQUEST_ALLOWRAW);
780 + $pbenefits = VikRequest::getString('benefits', '', 'request', VIKREQUEST_ALLOWHTML);
781 + $ptsinit = VikBooking::getDateTimestamp($pfrom, '0', '0');
782 + $ptsend = VikBooking::getDateTimestamp($pto, '23', '59');
783 + $ptsinit = empty($ptsinit) ? time() : $ptsinit;
784 + $ptsend = empty($ptsend) || $ptsend < $ptsinit ? $ptsinit : $ptsend;
785 + //file upload
786 + jimport('joomla.filesystem.file');
787 + $gimg = "";
788 + if (isset($pimg) && strlen(trim($pimg['name']))) {
789 + $pautoresize = VikRequest::getString('autoresize', '', 'request');
790 + $presizeto = VikRequest::getInt('resizeto', '', 'request');
791 + $creativik = new vikResizer();
792 + $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimg['name'])));
793 + $src = $pimg['tmp_name'];
794 + $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
795 + $j = "";
796 + if (file_exists($dest.$filename)) {
797 + $j = rand(171, 1717);
798 + while (file_exists($dest.$j.$filename)) {
799 + $j++;
800 + }
801 + }
802 + $finaldest = $dest.$j.$filename;
803 + $check = getimagesize($pimg['tmp_name']);
804 + if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
805 + if (VikBooking::uploadFile($src, $finaldest)) {
806 + $gimg = $j.$filename;
807 + //orig img
808 + $origmod = true;
809 + if ($pautoresize == "1" && !empty($presizeto)) {
810 + $origmod = $creativik->proportionalImage($finaldest, $dest.'big_'.$j.$filename, $presizeto, $presizeto);
811 + } else {
812 + VikBooking::uploadFile($finaldest, $dest.'big_'.$j.$filename, true);
813 + }
814 + //thumb
815 + $thumbsize = VikBooking::getThumbSize();
816 + $thumb = $creativik->proportionalImage($finaldest, $dest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
817 + if (!$thumb || !$origmod) {
818 + if (file_exists($dest.'big_'.$j.$filename)) @unlink($dest.'big_'.$j.$filename);
819 + if (file_exists($dest.'thumb_'.$j.$filename)) @unlink($dest.'thumb_'.$j.$filename);
820 + VikError::raiseWarning('', 'Error Uploading the File: '.$pimg['name']);
821 + }
822 + @unlink($finaldest);
823 + } else {
824 + VikError::raiseWarning('', 'Error while uploading image');
825 + }
826 + } else {
827 + VikError::raiseWarning('', 'Uploaded file is not an Image');
828 + }
829 + }
830 + //
831 + $goto = "index.php?option=com_vikbooking&task=packages";
832 + $q = "UPDATE `#__vikbooking_packages` SET `name`=".$dbo->quote($pname).",`alias`=".$dbo->quote($palias)."".(!empty($gimg) ? ",`img`=".$dbo->quote($gimg) : "").",`dfrom`=".(int)$ptsinit.",`dto`=".(int)$ptsend.",`excldates`=".$dbo->quote($strexcldates).",`minlos`=".(int)$pminlos.",`maxlos`=".(int)$pmaxlos.",`cost`=".$dbo->quote($pcost).",`idiva`='".$paliq."',`pernight_total`=".(int)$ppernight_total.",`perperson`=".(int)$pperperson.",`descr`=".$dbo->quote($pdescr).",`shortdescr`=".$dbo->quote($pshortdescr).",`benefits`=".$dbo->quote($pbenefits).",`conditions`=".$dbo->quote($pconditions).",`showoptions`=".(int)$pshowoptions." WHERE `id`=".(int)$pwhereup.";";
833 + $dbo->setQuery($q);
834 + $dbo->execute();
835 + $q = "DELETE FROM `#__vikbooking_packages_rooms` WHERE `idpackage`=".(int)$pwhereup.";";
836 + $dbo->setQuery($q);
837 + $dbo->execute();
838 + foreach ($prooms as $roomid) {
839 + if (!empty($roomid)) {
840 + $q = "INSERT INTO `#__vikbooking_packages_rooms` (`idpackage`,`idroom`) VALUES (".(int)$pwhereup.", ".(int)$roomid.");";
841 + $dbo->setQuery($q);
842 + $dbo->execute();
843 + }
844 + }
845 + $mainframe->enqueueMessage(JText::translate('VBOPKGUPDATED'));
846 + if ($stay) {
847 + $goto = "index.php?option=com_vikbooking&task=editpackage&cid[]=".$pwhereup;
848 + }
849 + $mainframe->redirect($goto);
850 + }
851 +
852 + public function removepackages()
853 + {
854 + if (!JSession::checkToken()) {
855 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
856 + }
857 +
858 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
859 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
860 + }
861 +
862 + $ids = VikRequest::getVar('cid', array());
863 + $dbo = JFactory::getDbo();
864 +
865 + foreach ($ids as $d) {
866 + $q = "DELETE FROM `#__vikbooking_packages` WHERE `id`=".(int)$d.";";
867 + $dbo->setQuery($q);
868 + $dbo->execute();
869 + $q = "DELETE FROM `#__vikbooking_packages_rooms` WHERE `idpackage`=".(int)$d.";";
870 + $dbo->setQuery($q);
871 + $dbo->execute();
872 + }
873 +
874 + $mainframe = JFactory::getApplication();
875 + $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
876 + }
877 +
878 + public function calendar() {
879 + VikBookingHelper::printHeader("19");
880 +
881 + VikRequest::setVar('view', VikRequest::getCmd('view', 'calendar'));
882 +
883 + parent::display();
884 +
885 + if (VikBooking::showFooter()) {
886 + VikBookingHelper::printFooter();
887 + }
888 + }
889 +
890 + public function rooms() {
891 + VikBookingHelper::printHeader("7");
892 +
893 + VikRequest::setVar('view', VikRequest::getCmd('view', 'rooms'));
894 +
895 + parent::display();
896 +
897 + if (VikBooking::showFooter()) {
898 + VikBookingHelper::printFooter();
899 + }
900 + }
901 +
902 + public function newroom() {
903 + VikBookingHelper::printHeader("7");
904 +
905 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageroom'));
906 +
907 + parent::display();
908 +
909 + if (VikBooking::showFooter()) {
910 + VikBookingHelper::printFooter();
911 + }
912 + }
913 +
914 + public function editroom() {
915 + VikBookingHelper::printHeader("7");
916 +
917 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageroom'));
918 +
919 + parent::display();
920 +
921 + if (VikBooking::showFooter()) {
922 + VikBookingHelper::printFooter();
923 + }
924 + }
925 +
926 + public function createroom()
927 + {
928 + if (!JSession::checkToken()) {
929 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
930 + }
931 +
932 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
933 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
934 + }
935 +
936 + $this->do_createroom();
937 + }
938 +
939 + public function createroomstay()
940 + {
941 + if (!JSession::checkToken()) {
942 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
943 + }
944 +
945 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
946 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
947 + }
948 +
949 + $this->do_createroom(true);
950 + }
951 +
952 + private function do_createroom($stay = false) {
953 + $app = JFactory::getApplication();
954 + $pcname = VikRequest::getString('cname', '', 'request');
955 + $pccat = VikRequest::getVar('ccat', array(0));
956 + $pcdescr = VikRequest::getString('cdescr', '', 'request', VIKREQUEST_ALLOWRAW);
957 + $psmalldesc = VikRequest::getString('smalldesc', '', 'request', VIKREQUEST_ALLOWRAW);
958 + $pccarat = VikRequest::getVar('ccarat', array(0));
959 + $pcoptional = VikRequest::getVar('coptional', array(0));
960 + $pcavail = VikRequest::getString('cavail', '', 'request');
961 + $pautoresize = VikRequest::getString('autoresize', '', 'request');
962 + $presizeto = VikRequest::getString('resizeto', '', 'request');
963 + $pautoresizemore = VikRequest::getString('autoresizemore', '', 'request');
964 + $presizetomore = VikRequest::getString('resizetomore', '', 'request');
965 + $punits = VikRequest::getInt('units', '', 'request');
966 + $pimages = VikRequest::getVar('cimgmore', null, 'files', 'array');
967 + $pfromadult = VikRequest::getInt('fromadult', '', 'request');
968 + $ptoadult = VikRequest::getInt('toadult', '', 'request');
969 + $pfromchild = VikRequest::getInt('fromchild', '', 'request');
970 + $ptochild = VikRequest::getInt('tochild', '', 'request');
971 + $ptotpeople = VikRequest::getInt('totpeople', '', 'request');
972 + $pmintotpeople = VikRequest::getInt('mintotpeople', '', 'request');
973 + $pmintotpeople = $pmintotpeople < 1 ? 1 : $pmintotpeople;
974 + $plastavail = VikRequest::getString('lastavail', '', 'request');
975 + $plastavail = empty($plastavail) ? 0 : intval($plastavail);
976 + $psuggocc = VikRequest::getInt('suggocc', 1, 'request');
977 + $pcustprice = VikRequest::getString('custprice', '', 'request');
978 + $pcustprice = empty($pcustprice) ? '' : floatval($pcustprice);
979 + $pcustpricetxt = VikRequest::getString('custpricetxt', '', 'request', VIKREQUEST_ALLOWRAW);
980 + $pcustpricesubtxt = VikRequest::getString('custpricesubtxt', '', 'request', VIKREQUEST_ALLOWRAW);
981 + $preqinfo = VikRequest::getInt('reqinfo', '', 'request');
982 + $ppricecal = VikRequest::getInt('pricecal', '', 'request');
983 + $pdefcalcost = VikRequest::getString('defcalcost', '', 'request');
984 + $pmaxminpeople = VikRequest::getString('maxminpeople', '', 'request');
985 + $pcimgcaption = VikRequest::getVar('cimgcaption', array());
986 + $pmaxminpeople = in_array($pmaxminpeople, array('0', '1', '2', '3', '4', '5')) ? $pmaxminpeople : '0';
987 + $pseasoncal = VikRequest::getInt('seasoncal', 0, 'request');
988 + $pseasoncal = $pseasoncal >= 0 || $pseasoncal <= 3 ? $pseasoncal : 0;
989 + $pseasoncal_nights = VikRequest::getString('seasoncal_nights', '', 'request');
990 + $pseasoncal_prices = VikRequest::getString('seasoncal_prices', '', 'request');
991 + $pseasoncal_restr = VikRequest::getString('seasoncal_restr', '', 'request');
992 + $pmulti_units = VikRequest::getInt('multi_units', '', 'request');
993 + $pmulti_units = $punits > 1 ? $pmulti_units : 0;
994 + $psefalias = VikRequest::getString('sefalias', '', 'request');
995 + $psefalias = empty($psefalias) ? JFilterOutput::stringURLSafe($pcname) : JFilterOutput::stringURLSafe($psefalias);
996 + $pcustptitle = VikRequest::getString('custptitle', '', 'request');
997 + $pcustptitlew = VikRequest::getString('custptitlew', '', 'request');
998 + $pcustptitlew = in_array($pcustptitlew, array('before', 'after', 'replace')) ? $pcustptitlew : 'before';
999 + $pmetakeywords = VikRequest::getString('metakeywords', '', 'request');
1000 + $pmetadescription = VikRequest::getString('metadescription', '', 'request');
1001 + $pshare_with = VikRequest::getVar('share_with', array());
1002 + $scalnights_arr = array();
1003 + if (!empty($pseasoncal_nights)) {
1004 + $scalnights = explode(',', $pseasoncal_nights);
1005 + foreach ($scalnights as $scalnight) {
1006 + if (intval(trim($scalnight)) > 0) {
1007 + $scalnights_arr[] = intval(trim($scalnight));
1008 + }
1009 + }
1010 + }
1011 + if ($scalnights_arr) {
1012 + $pseasoncal_nights = implode(', ', $scalnights_arr);
1013 + } else {
1014 + $pseasoncal_nights = '';
1015 + $pseasoncal = 0;
1016 + }
1017 + $roomparams = [
1018 + 'lastavail' => $plastavail,
1019 + 'suggocc' => $psuggocc,
1020 + 'custprice' => $pcustprice,
1021 + 'custpricetxt' => $pcustpricetxt,
1022 + 'custpricesubtxt' => $pcustpricesubtxt,
1023 + 'reqinfo' => $preqinfo,
1024 + 'pricecal' => $ppricecal,
1025 + 'defcalcost' => floatval($pdefcalcost),
1026 + 'maxminpeople' => $pmaxminpeople,
1027 + 'seasoncal' => $pseasoncal,
1028 + 'seasoncal_nights' => $pseasoncal_nights,
1029 + 'seasoncal_prices' => $pseasoncal_prices,
1030 + 'seasoncal_restr' => $pseasoncal_restr,
1031 + 'multi_units' => $pmulti_units,
1032 + 'custptitle' => $pcustptitle,
1033 + 'custptitlew' => $pcustptitlew,
1034 + 'metakeywords' => $pmetakeywords,
1035 + 'metadescription' => $pmetadescription,
1036 + 'layout_style' => VikRequest::getString('layout_style', 'default', 'request'),
1037 + 'checkin' => VikRequest::getString('listing_checkin', '', 'request'),
1038 + 'checkout' => VikRequest::getString('listing_checkout', '', 'request'),
1039 + 'instructions' => VikRequest::getString('listing_instructions', '', 'request', VIKREQUEST_ALLOWRAW),
1040 + ];
1041 + //distinctive features
1042 + $roomparams['features'] = array();
1043 + if ($punits > 0) {
1044 + for ($i=1; $i <= $punits; $i++) {
1045 + $distf_name = VikRequest::getVar('feature-name'.$i, array());
1046 + $distf_lang = VikRequest::getVar('feature-lang'.$i, array());
1047 + $distf_value = VikRequest::getVar('feature-value'.$i, array());
1048 + foreach ($distf_name as $distf_k => $distf) {
1049 + if (strlen($distf) > 0 && strlen($distf_value[$distf_k]) > 0) {
1050 + $use_key = strlen($distf_lang[$distf_k]) > 0 ? $distf_lang[$distf_k] : $distf;
1051 + $roomparams['features'][$i][$use_key] = $distf_value[$distf_k];
1052 + }
1053 + }
1054 + }
1055 + }
1056 +
1057 + /**
1058 + * Store room geo params information.
1059 + *
1060 + * @since 1.14 (J) - 1.4.0 (WP)
1061 + */
1062 + $geo = VikBooking::getGeocodingInstance();
1063 + $geo_params = $geo->getRoomGeoTransient(0);
1064 + if ($geo_params !== false) {
1065 + // make sure the geocoding service was not turned off
1066 + $geo_enabled = VikRequest::getInt('geo_enabled', 0, 'request');
1067 + if (!$geo_enabled) {
1068 + $geo_params->enabled = 0;
1069 + }
1070 + //
1071 + $roomparams['geo'] = $geo_params;
1072 + }
1073 + //
1074 +
1075 + $roomparamstr = json_encode($roomparams);
1076 +
1077 + if (empty($pcname)) {
1078 + $app->enqueueMessage(JText::translate('VBO_PLEASE_FILL_FIELDS'), 'error');
1079 + $app->redirect("index.php?option=com_vikbooking&task=rooms");
1080 + $app->close();
1081 + }
1082 +
1083 + jimport('joomla.filesystem.file');
1084 + $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
1085 +
1086 + $picon = "";
1087 + if (($_FILES['cimg'] ?? null) && !intval($_FILES['cimg']['error']) && VikBooking::caniWrite($updpath) && strlen(trim($_FILES['cimg']['name'])) && @is_uploaded_file($_FILES['cimg']['tmp_name'])) {
1088 + $safename = JFile::makeSafe(str_replace(' ', '_', strtolower($_FILES['cimg']['name'])));
1089 + $j = '';
1090 + $pwhere = $updpath . $safename;
1091 + if (file_exists($updpath . $safename)) {
1092 + $j = 1;
1093 + while (file_exists($updpath . $j . $safename)) {
1094 + $j++;
1095 + }
1096 + $pwhere = $updpath . $j . $safename;
1097 + }
1098 + if (!getimagesize($_FILES['cimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
1099 + @unlink($pwhere);
1100 + } elseif (VikBooking::uploadFile($_FILES['cimg']['tmp_name'], $pwhere)) {
1101 + $picon = $j . $safename;
1102 + if ((int) $pautoresize && !empty($presizeto)) {
1103 + $origmod = (new VikResizer)->proportionalImage($pwhere, $updpath . 'r_' . $j . $safename, $presizeto, $presizeto);
1104 + if ($origmod) {
1105 + @unlink($pwhere);
1106 + $picon = 'r_' . $j . $safename;
1107 + }
1108 + }
1109 + /**
1110 + * Create a mini-thumbnail of the room/listing main photo.
1111 + *
1112 + * @since 1.17.5 (J) - 1.7.5 (WP)
1113 + */
1114 + try {
1115 + // resize the original image
1116 + (new VikResizer)->proportionalImage($pwhere, $updpath . 'mini_' . $picon, 96, 96);
1117 + } catch (Throwable $e) {
1118 + // silently catch any PHP GD error and continue
1119 + }
1120 + }
1121 + }
1122 +
1123 + // more images
1124 + $creativik = new VikResizer;
1125 + $bigsdest = $updpath;
1126 + $thumbsdest = $updpath;
1127 + $dest = $updpath;
1128 + $moreimagestr = "";
1129 + $arrimgs = array();
1130 + $captiontexts = array();
1131 + $imgcaptions = array();
1132 + foreach ($pimages['name'] as $kk=>$ci) {
1133 + if (!empty($ci)) {
1134 + $arrimgs[] = $kk;
1135 + $captiontexts[] = isset($pcimgcaption[$kk]) ? $pcimgcaption[$kk] : '';
1136 + }
1137 + }
1138 + foreach ($arrimgs as $ki => $imgk) {
1139 + if (strlen(trim($pimages['name'][$imgk]))) {
1140 + $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimages['name'][$imgk])));
1141 + $src = $pimages['tmp_name'][$imgk];
1142 + $j = "";
1143 + if (file_exists($dest.$filename)) {
1144 + $j = rand(171, 1717);
1145 + while (file_exists($dest.$j.$filename)) {
1146 + $j++;
1147 + }
1148 + }
1149 + $finaldest = $dest.$j.$filename;
1150 + $check = getimagesize($pimages['tmp_name'][$imgk]);
1151 + if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
1152 + if (VikBooking::uploadFile($src, $finaldest)) {
1153 + $gimg = $j.$filename;
1154 + //orig img
1155 + $origmod = true;
1156 + if ($pautoresizemore == "1" && !empty($presizetomore)) {
1157 + $origmod = $creativik->proportionalImage($finaldest, $bigsdest.'big_'.$j.$filename, $presizetomore, $presizetomore);
1158 + } else {
1159 + VikBooking::uploadFile($finaldest, $bigsdest.'big_'.$j.$filename, true);
1160 + }
1161 + //thumb
1162 + $thumbsize = VikBooking::getThumbSize();
1163 + $thumb = $creativik->proportionalImage($finaldest, $thumbsdest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
1164 + if (!$thumb || !$origmod) {
1165 + if (file_exists($bigsdest.'big_'.$j.$filename)) @unlink($bigsdest.'big_'.$j.$filename);
1166 + if (file_exists($thumbsdest.'thumb_'.$j.$filename)) @unlink($thumbsdest.'thumb_'.$j.$filename);
1167 + VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1168 + } else {
1169 + $moreimagestr .= $j.$filename.";;";
1170 + $imgcaptions[] = $captiontexts[$ki];
1171 + }
1172 + @unlink($finaldest);
1173 + } else {
1174 + VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1175 + }
1176 + } else {
1177 + VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1178 + }
1179 + }
1180 + }
1181 + //end more images
1182 + if (is_array($pccat) && count($pccat)) {
1183 + $pccatdef="";
1184 + foreach ($pccat as $ccat) {
1185 + if (!empty($ccat)) {
1186 + $pccatdef.=$ccat.";";
1187 + }
1188 + }
1189 + } else {
1190 + $pccatdef="";
1191 + }
1192 + if (is_array($pccarat) && count($pccarat)) {
1193 + $pccaratdef="";
1194 + foreach ($pccarat as $ccarat) {
1195 + $pccaratdef.=$ccarat.";";
1196 + }
1197 + } else {
1198 + $pccaratdef="";
1199 + }
1200 + if (is_array($pcoptional) && count($pcoptional)) {
1201 + $pcoptionaldef="";
1202 + foreach ($pcoptional as $coptional) {
1203 + $pcoptionaldef.=$coptional.";";
1204 + }
1205 + } else {
1206 + $pcoptionaldef="";
1207 + }
1208 + $pcavaildef=($pcavail=="yes" ? "1" : "0");
1209 + if ($pfromadult > $ptoadult) {
1210 + $pfromadult = 1;
1211 + $ptoadult = 1;
1212 + }
1213 + if ($pfromchild > $ptochild) {
1214 + $pfromchild = 1;
1215 + $ptochild = 1;
1216 + }
1217 + $dbo = JFactory::getDbo();
1218 + $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).");";
1219 + $dbo->setQuery($q);
1220 + $dbo->execute();
1221 + $lid = $dbo->insertid();
1222 + if (empty($lid)) {
1223 + $app->enqueueMessage('Could not store the record on the database', 'error');
1224 + $app->redirect("index.php?option=com_vikbooking&task=rooms");
1225 + $app->close();
1226 + }
1227 +
1228 + /**
1229 + * Share availability calendars with other rooms.
1230 + *
1231 + * @since 1.13
1232 + */
1233 + // always reset relations for this main room
1234 + $q = "DELETE FROM `#__vikbooking_calendars_xref` WHERE `mainroom`={$lid};";
1235 + $dbo->setQuery($q);
1236 + $dbo->execute();
1237 + $newxref = array();
1238 + foreach ($pshare_with as $cldroom) {
1239 + if (!empty($cldroom)) {
1240 + array_push($newxref, (int)$cldroom);
1241 + }
1242 + }
1243 + foreach ($newxref as $cldroom) {
1244 + $q = "INSERT INTO `#__vikbooking_calendars_xref` (`mainroom`, `childroom`) VALUES ({$lid}, {$cldroom});";
1245 + $dbo->setQuery($q);
1246 + $dbo->execute();
1247 + }
1248 +
1249 + /**
1250 + * Room upgrade options.
1251 + *
1252 + * @since 1.16.0 (J) - 1.6.0 (WP)
1253 + */
1254 + $config = VBOFactory::getConfig();
1255 + $room_upgrade_options = [];
1256 + $room_upgrade = VikRequest::getInt('room_upgrade', 0, 'request');
1257 + $upgrade_rooms = VikRequest::getVar('upgrade_rooms', array());
1258 + $upgrade_discount = VikRequest::getFloat('upgrade_discount', 0, 'request');
1259 + if ($room_upgrade && is_array($upgrade_rooms) && count($upgrade_rooms)) {
1260 + $upgrade_rooms = array_map(function($rid) {
1261 + return (int)$rid;
1262 + }, $upgrade_rooms);
1263 +
1264 + $room_upgrade_options = [
1265 + 'rooms' => $upgrade_rooms,
1266 + 'discount' => $upgrade_discount,
1267 + ];
1268 + }
1269 + $config->set('room_upgrade_options_' . $lid, json_encode($room_upgrade_options));
1270 +
1271 + if ($stay === true) {
1272 + $app->enqueueMessage(JText::translate('VBOROOMSAVEOK').' - <a href="index.php?option=com_vikbooking&task=tariffs&cid[]='.$lid.'">'.JText::translate('VBOGOTORATES').'</a>');
1273 + $app->redirect("index.php?option=com_vikbooking&task=editroom&cid[]=".$lid);
1274 + $app->close();
1275 + }
1276 +
1277 + $app->redirect("index.php?option=com_vikbooking&task=tariffs&cid[]=".$lid);
1278 + $app->close();
1279 + }
1280 +
1281 + public function updateroom()
1282 + {
1283 + if (!JSession::checkToken()) {
1284 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1285 + }
1286 +
1287 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1288 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1289 + }
1290 +
1291 + $this->do_updateroom();
1292 + }
1293 +
1294 + public function updateroomstay()
1295 + {
1296 + if (!JSession::checkToken()) {
1297 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1298 + }
1299 +
1300 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1301 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1302 + }
1303 +
1304 + $this->do_updateroom(true);
1305 + }
1306 +
1307 + private function do_updateroom($stay = false)
1308 + {
1309 + $app = JFactory::getApplication();
1310 + $config = VBOFactory::getConfig();
1311 + $dbo = JFactory::getDbo();
1312 +
1313 + $pcname = VikRequest::getString('cname', '', 'request');
1314 + $pccat = VikRequest::getVar('ccat', array(0));
1315 + $pcdescr = VikRequest::getString('cdescr', '', 'request', VIKREQUEST_ALLOWRAW);
1316 + $psmalldesc = VikRequest::getString('smalldesc', '', 'request', VIKREQUEST_ALLOWRAW);
1317 + $pccarat = VikRequest::getVar('ccarat', array(0));
1318 + $pcoptional = VikRequest::getVar('coptional', array(0));
1319 + $pcavail = VikRequest::getString('cavail', '', 'request');
1320 + $pwhereup = VikRequest::getInt('whereup', 0, 'request');
1321 + $pautoresize = VikRequest::getString('autoresize', '', 'request');
1322 + $presizeto = VikRequest::getString('resizeto', '', 'request');
1323 + $pautoresizemore = VikRequest::getString('autoresizemore', '', 'request');
1324 + $presizetomore = VikRequest::getString('resizetomore', '', 'request');
1325 + $punits = VikRequest::getInt('units', '', 'request');
1326 + $pimages = VikRequest::getVar('cimgmore', null, 'files', 'array');
1327 + $pactmoreimgs = VikRequest::getString('actmoreimgs', '', 'request');
1328 + $pfromadult = VikRequest::getInt('fromadult', '', 'request');
1329 + $ptoadult = VikRequest::getInt('toadult', '', 'request');
1330 + $pfromchild = VikRequest::getInt('fromchild', '', 'request');
1331 + $ptochild = VikRequest::getInt('tochild', '', 'request');
1332 + $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array(0));
1333 + $padultsdiffval = VikRequest::getVar('adultsdiffval', array(0));
1334 + $padultsdiffnum = VikRequest::getVar('adultsdiffnum', array(0));
1335 + $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array(0));
1336 + $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array(0));
1337 + $ptotpeople = VikRequest::getInt('totpeople', '', 'request');
1338 + $pmintotpeople = VikRequest::getInt('mintotpeople', '', 'request');
1339 + $pmintotpeople = $pmintotpeople < 1 ? 1 : $pmintotpeople;
1340 + $plastavail = VikRequest::getString('lastavail', '', 'request');
1341 + $plastavail = empty($plastavail) ? 0 : intval($plastavail);
1342 + $psuggocc = VikRequest::getInt('suggocc', 1, 'request');
1343 + $pcustprice = VikRequest::getString('custprice', '', 'request');
1344 + $pcustprice = empty($pcustprice) ? '' : floatval($pcustprice);
1345 + $pcustpricetxt = VikRequest::getString('custpricetxt', '', 'request', VIKREQUEST_ALLOWRAW);
1346 + $pcustpricesubtxt = VikRequest::getString('custpricesubtxt', '', 'request', VIKREQUEST_ALLOWRAW);
1347 + $preqinfo = VikRequest::getInt('reqinfo', '', 'request');
1348 + $ppricecal = VikRequest::getInt('pricecal', '', 'request');
1349 + $pdefcalcost = VikRequest::getString('defcalcost', '', 'request');
1350 + $pdefrplan = VikRequest::getInt('defrplan', 0, 'request');
1351 + $pmaxminpeople = VikRequest::getString('maxminpeople', '', 'request');
1352 + $pcimgcaption = VikRequest::getVar('cimgcaption', array());
1353 + $pimgsorting = VikRequest::getVar('imgsorting', array());
1354 + $pupdatecaption = VikRequest::getInt('updatecaption', '', 'request');
1355 + $pmaxminpeople = in_array($pmaxminpeople, array('0', '1', '2', '3', '4', '5')) ? $pmaxminpeople : '0';
1356 + $pseasoncal = VikRequest::getInt('seasoncal', 0, 'request');
1357 + $pseasoncal = $pseasoncal >= 0 || $pseasoncal <= 3 ? $pseasoncal : 0;
1358 + $pseasoncal_nights = VikRequest::getString('seasoncal_nights', '', 'request');
1359 + $pseasoncal_prices = VikRequest::getString('seasoncal_prices', '', 'request');
1360 + $pseasoncal_restr = VikRequest::getString('seasoncal_restr', '', 'request');
1361 + $pmulti_units = VikRequest::getInt('multi_units', '', 'request');
1362 + $pmulti_units = $punits > 1 ? $pmulti_units : 0;
1363 + $psefalias = VikRequest::getString('sefalias', '', 'request');
1364 + $psefalias = empty($psefalias) ? JFilterOutput::stringURLSafe($pcname) : JFilterOutput::stringURLSafe($psefalias);
1365 + $pcustptitle = VikRequest::getString('custptitle', '', 'request');
1366 + $pcustptitlew = VikRequest::getString('custptitlew', '', 'request');
1367 + $pcustptitlew = in_array($pcustptitlew, array('before', 'after', 'replace')) ? $pcustptitlew : 'before';
1368 + $pmetakeywords = VikRequest::getString('metakeywords', '', 'request');
1369 + $pmetadescription = VikRequest::getString('metadescription', '', 'request');
1370 + $pshare_with = VikRequest::getVar('share_with', array());
1371 + $scalnights_arr = array();
1372 + if (!empty($pseasoncal_nights)) {
1373 + $scalnights = explode(',', $pseasoncal_nights);
1374 + foreach ($scalnights as $scalnight) {
1375 + if (intval(trim($scalnight)) > 0) {
1376 + $scalnights_arr[] = intval(trim($scalnight));
1377 + }
1378 + }
1379 + }
1380 + if ($scalnights_arr) {
1381 + $pseasoncal_nights = implode(', ', $scalnights_arr);
1382 + } else {
1383 + $pseasoncal_nights = '';
1384 + $pseasoncal = 0;
1385 + }
1386 + $roomparams = [
1387 + 'lastavail' => $plastavail,
1388 + 'suggocc' => $psuggocc,
1389 + 'custprice' => $pcustprice,
1390 + 'custpricetxt' => $pcustpricetxt,
1391 + 'custpricesubtxt' => $pcustpricesubtxt,
1392 + 'reqinfo' => $preqinfo,
1393 + 'pricecal' => $ppricecal,
1394 + 'defcalcost' => floatval($pdefcalcost),
1395 + 'defrplan' => $pdefrplan,
1396 + 'maxminpeople' => $pmaxminpeople,
1397 + 'seasoncal' => $pseasoncal,
1398 + 'seasoncal_nights' => $pseasoncal_nights,
1399 + 'seasoncal_prices' => $pseasoncal_prices,
1400 + 'seasoncal_restr' => $pseasoncal_restr,
1401 + 'multi_units' => $pmulti_units,
1402 + 'custptitle' => $pcustptitle,
1403 + 'custptitlew' => $pcustptitlew,
1404 + 'metakeywords' => $pmetakeywords,
1405 + 'metadescription' => $pmetadescription,
1406 + 'layout_style' => VikRequest::getString('layout_style', 'default', 'request'),
1407 + 'checkin' => VikRequest::getString('listing_checkin', '', 'request'),
1408 + 'checkout' => VikRequest::getString('listing_checkout', '', 'request'),
1409 + 'instructions' => VikRequest::getString('listing_instructions', '', 'request', VIKREQUEST_ALLOWRAW),
1410 + ];
1411 + //distinctive features
1412 + $roomparams['features'] = array();
1413 + $newfeatures = array();
1414 + if ($punits > 0) {
1415 + for ($i=1; $i <= $punits; $i++) {
1416 + $distf_name = VikRequest::getVar('feature-name'.$i, array());
1417 + $distf_lang = VikRequest::getVar('feature-lang'.$i, array());
1418 + $distf_value = VikRequest::getVar('feature-value'.$i, array());
1419 + foreach ($distf_name as $distf_k => $distf) {
1420 + if (strlen($distf) > 0 && strlen($distf_value[$distf_k]) > 0) {
1421 + $use_key = strlen($distf_lang[$distf_k]) > 0 ? $distf_lang[$distf_k] : $distf;
1422 + $roomparams['features'][$i][$use_key] = $distf_value[$distf_k];
1423 + if ($distf_k < 1) {
1424 + //check only the first feature
1425 + $newfeatures[$i][$use_key] = $distf_value[$distf_k];
1426 + }
1427 + }
1428 + }
1429 + }
1430 + }
1431 +
1432 + // load current room record
1433 + $dbo->setQuery(
1434 + $dbo->getQuery(true)
1435 + ->select('*')
1436 + ->from($dbo->qn('#__vikbooking_rooms'))
1437 + ->where($dbo->qn('id') . ' = ' . (int) $pwhereup)
1438 + );
1439 + $prevroom = $dbo->loadAssoc();
1440 + if (!$prevroom) {
1441 + VBOHttpDocument::getInstance()->close(404, 'Record not found');
1442 + }
1443 +
1444 + /**
1445 + * Store room geo params information.
1446 + *
1447 + * @since 1.14 (J) - 1.4.0 (WP)
1448 + */
1449 + $geo = VikBooking::getGeocodingInstance();
1450 + $geo_params = $geo->getRoomGeoTransient($pwhereup);
1451 + if ($geo_params !== false) {
1452 + // make sure the geocoding service was not turned off
1453 + $geo_enabled = VikRequest::getInt('geo_enabled', 0, 'request');
1454 + if (!$geo_enabled) {
1455 + $geo_params->enabled = 0;
1456 + }
1457 + //
1458 + $roomparams['geo'] = $geo_params;
1459 + }
1460 + //
1461 +
1462 + $roomparamstr = json_encode($roomparams);
1463 +
1464 + jimport('joomla.filesystem.file');
1465 + $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
1466 +
1467 + if (!empty($pcname)) {
1468 +
1469 + $picon = "";
1470 + if (($_FILES['cimg'] ?? null) && !intval($_FILES['cimg']['error']) && VikBooking::caniWrite($updpath) && strlen(trim($_FILES['cimg']['name'])) && @is_uploaded_file($_FILES['cimg']['tmp_name'])) {
1471 + $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['cimg']['name'])));
1472 + $j = '';
1473 + $pwhere = $updpath . $safename;
1474 + if (file_exists($updpath . $safename)) {
1475 + $j = 1;
1476 + while (file_exists($updpath . $j . $safename)) {
1477 + $j++;
1478 + }
1479 + $pwhere = $updpath . $j . $safename;
1480 + }
1481 + if (!getimagesize($_FILES['cimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
1482 + @unlink($pwhere);
1483 + } elseif (VikBooking::uploadFile($_FILES['cimg']['tmp_name'], $pwhere)) {
1484 + $picon = $j . $safename;
1485 + if ((int) $pautoresize && !empty($presizeto)) {
1486 + $origmod = (new VikResizer)->proportionalImage($pwhere, $updpath . 'r_' . $j . $safename, $presizeto, $presizeto);
1487 + if ($origmod) {
1488 + @unlink($pwhere);
1489 + $picon = 'r_' . $j . $safename;
1490 + }
1491 + }
1492 + /**
1493 + * Create a mini-thumbnail of the room/listing main photo.
1494 + *
1495 + * @since 1.17.5 (J) - 1.7.5 (WP)
1496 + */
1497 + try {
1498 + // resize the original image
1499 + (new VikResizer)->proportionalImage($pwhere, $updpath . 'mini_' . $picon, 96, 96);
1500 + } catch (Throwable $e) {
1501 + // silently catch any PHP GD error and continue
1502 + }
1503 + }
1504 + }
1505 +
1506 + /**
1507 + * Create a mini-thumbnail of the current room/listing main photo.
1508 + *
1509 + * @since 1.17.5 (J) - 1.7.5 (WP)
1510 + */
1511 + if (!$picon && !empty($prevroom['img']) && is_file($updpath . $prevroom['img']) && !is_file($updpath . 'mini_' . $prevroom['img'])) {
1512 + try {
1513 + // resize the original image
1514 + (new VikResizer)->proportionalImage($updpath . $prevroom['img'], $updpath . 'mini_' . $prevroom['img'], 96, 96);
1515 + } catch (Throwable $e) {
1516 + // silently catch any PHP GD error and continue
1517 + }
1518 + }
1519 +
1520 + // more images
1521 + $creativik = new VikResizer;
1522 + $bigsdest = $updpath;
1523 + $thumbsdest = $updpath;
1524 + $dest = $updpath;
1525 + $moreimagestr = $pactmoreimgs;
1526 + $arrimgs = array();
1527 + $captiontexts = array();
1528 + $imgcaptions = array();
1529 + //captions of uploaded extra images
1530 + if (!empty($pactmoreimgs)) {
1531 + $sploimgs = explode(';;', $pactmoreimgs);
1532 + foreach ($sploimgs as $ki => $oimg) {
1533 + if (!empty($oimg)) {
1534 + $oldcaption = VikRequest::getString('caption'.$ki, '', 'request', VIKREQUEST_ALLOWHTML);
1535 + $imgcaptions[] = $oldcaption;
1536 + }
1537 + }
1538 + }
1539 + //
1540 + foreach ($pimages['name'] as $kk=>$ci) {
1541 + if (!empty($ci)) {
1542 + $arrimgs[] = $kk;
1543 + $captiontexts[] = isset($pcimgcaption[$kk]) ? $pcimgcaption[$kk] : '';
1544 + }
1545 + }
1546 + foreach ($arrimgs as $ki => $imgk) {
1547 + if (strlen(trim($pimages['name'][$imgk]))) {
1548 + $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pimages['name'][$imgk])));
1549 + $src = $pimages['tmp_name'][$imgk];
1550 + $j = "";
1551 + if (file_exists($dest.$filename)) {
1552 + $j = rand(171, 1717);
1553 + while (file_exists($dest.$j.$filename)) {
1554 + $j++;
1555 + }
1556 + }
1557 + $finaldest = $dest.$j.$filename;
1558 + $check = getimagesize($pimages['tmp_name'][$imgk]);
1559 + if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
1560 + if (VikBooking::uploadFile($src, $finaldest)) {
1561 + $gimg = $j.$filename;
1562 + //orig img
1563 + $origmod = true;
1564 + if ($pautoresizemore == "1" && !empty($presizetomore)) {
1565 + $origmod = $creativik->proportionalImage($finaldest, $bigsdest.'big_'.$j.$filename, $presizetomore, $presizetomore);
1566 + } else {
1567 + VikBooking::uploadFile($finaldest, $bigsdest.'big_'.$j.$filename, true);
1568 + }
1569 + //thumb
1570 + $thumbsize = VikBooking::getThumbSize();
1571 + $thumb = $creativik->proportionalImage($finaldest, $thumbsdest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
1572 + if (!$thumb || !$origmod) {
1573 + if (file_exists($bigsdest.'big_'.$j.$filename)) @unlink($bigsdest.'big_'.$j.$filename);
1574 + if (file_exists($thumbsdest.'thumb_'.$j.$filename)) @unlink($thumbsdest.'thumb_'.$j.$filename);
1575 + VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1576 + } else {
1577 + $moreimagestr .= $j.$filename.";;";
1578 + $imgcaptions[] = $captiontexts[$ki];
1579 + }
1580 + @unlink($finaldest);
1581 + } else {
1582 + VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1583 + }
1584 + } else {
1585 + VikError::raiseWarning('', 'Error While Uploading the File: '.$pimages['name'][$imgk]);
1586 + }
1587 + }
1588 + }
1589 + //sorting of extra images
1590 + $sorted_extraim = array();
1591 + $sorted_captions = array();
1592 + $extraim_parts = explode(';;', $moreimagestr);
1593 + foreach ($pimgsorting as $k => $v) {
1594 + $capkey = -1;
1595 + if (isset($extraim_parts[$k])) {
1596 + $sorted_extraim[] = $v;
1597 + foreach ($extraim_parts as $oldk => $oldv) {
1598 + if ($oldv == $v) {
1599 + $capkey = $oldk;
1600 + break;
1601 + }
1602 + }
1603 + }
1604 + if (isset($imgcaptions[$capkey])) {
1605 + $sorted_captions[] = $imgcaptions[$capkey];
1606 + }
1607 + }
1608 + $tot_sorted_im = count($sorted_extraim);
1609 + if ($tot_sorted_im != count($extraim_parts)) {
1610 + foreach ($extraim_parts as $k => $v) {
1611 + if ($k <= ($tot_sorted_im - 1)) {
1612 + continue;
1613 + }
1614 + $sorted_extraim[] = $v;
1615 + if (isset($imgcaptions[$k])) {
1616 + $sorted_captions[] = $imgcaptions[$k];
1617 + }
1618 + }
1619 + }
1620 + $moreimagestr = implode(';;', $sorted_extraim);
1621 + $imgcaptions = $sorted_captions;
1622 + //end more images
1623 + if (is_array($pccat) && count($pccat)) {
1624 + $pccatdef = "";
1625 + foreach ($pccat as $ccat) {
1626 + if (!empty($ccat)) {
1627 + $pccatdef .= $ccat.";";
1628 + }
1629 + }
1630 + } else {
1631 + $pccatdef = "";
1632 + }
1633 + if (is_array($pccarat) && count($pccarat)) {
1634 + $pccaratdef = "";
1635 + foreach ($pccarat as $ccarat) {
1636 + $pccaratdef .= $ccarat.";";
1637 + }
1638 + } else {
1639 + $pccaratdef = "";
1640 + }
1641 + if (is_array($pcoptional) && count($pcoptional)) {
1642 + $pcoptionaldef = "";
1643 + foreach ($pcoptional as $coptional) {
1644 + $pcoptionaldef .= $coptional.";";
1645 + }
1646 + } else {
1647 + $pcoptionaldef = "";
1648 + }
1649 + $pcavaildef=($pcavail=="yes" ? "1" : "0");
1650 + if ($pfromadult > $ptoadult) {
1651 + $pfromadult = 1;
1652 + $ptoadult = 1;
1653 + }
1654 + if ($pfromchild > $ptochild) {
1655 + $pfromchild = 1;
1656 + $ptochild = 1;
1657 + }
1658 +
1659 + //adults charges/discounts
1660 + $adchdisctouch = false;
1661 + $q = "SELECT * FROM `#__vikbooking_rooms` WHERE `id`='".$pwhereup."';";
1662 + $dbo->setQuery($q);
1663 + $dbo->execute();
1664 + $oldroom = $dbo->loadAssocList();
1665 + $oldroom = $oldroom[0];
1666 + if ($oldroom['fromadult'] == $pfromadult && $oldroom['toadult'] == $ptoadult) {
1667 + if ($oldroom['toadult'] > 1 && $oldroom['fromadult'] < $oldroom['toadult'] && @count($padultsdiffnum) > 0) {
1668 + $startadind = $oldroom['fromadult'] > 0 ? $oldroom['fromadult'] : 1;
1669 + for($adi = $startadind; $adi <= $oldroom['toadult']; $adi++) {
1670 + foreach ($padultsdiffnum as $kad=>$vad) {
1671 + if (intval($vad) == intval($adi) && strlen($padultsdiffval[$kad]) > 0) {
1672 + $adchdisctouch = true;
1673 + $inschdisc = intval($padultsdiffchdisc[$kad]) == 1 ? 1 : 2;
1674 + $insvalpcent = intval($padultsdiffvalpcent[$kad]) == 1 ? 1 : 2;
1675 + $inspernight = intval($padultsdiffpernight[$kad]) == 1 ? 1 : 0;
1676 + $insvalue = floatval($padultsdiffval[$kad]);
1677 + //check if it exists
1678 + $q = "SELECT `id` FROM `#__vikbooking_adultsdiff` WHERE `idroom`='".$oldroom['id']."' AND `adults`='".$adi."';";
1679 + $dbo->setQuery($q);
1680 + $dbo->execute();
1681 + if ($dbo->getNumRows() > 0) {
1682 + if ($insvalue > 0) {
1683 + //update
1684 + $q = "UPDATE `#__vikbooking_adultsdiff` SET `chdisc`='".$inschdisc."', `valpcent`='".$insvalpcent."', `value`='".$insvalue."', `pernight`='".$inspernight."' WHERE `idroom`='".$oldroom['id']."' AND `adults`='".$adi."';";
1685 + $dbo->setQuery($q);
1686 + $dbo->execute();
1687 + } else {
1688 + //delete
1689 + $q = "DELETE FROM `#__vikbooking_adultsdiff` WHERE `idroom`='".$oldroom['id']."' AND `adults`='".$adi."';";
1690 + $dbo->setQuery($q);
1691 + $dbo->execute();
1692 + }
1693 + } else {
1694 + //insert
1695 + $q = "INSERT INTO `#__vikbooking_adultsdiff` (`idroom`,`chdisc`,`valpcent`,`value`,`adults`,`pernight`) VALUES('".$oldroom['id']."', '".$inschdisc."', '".$insvalpcent."', '".$insvalue."', '".$adi."', '".$inspernight."');";
1696 + $dbo->setQuery($q);
1697 + $dbo->execute();
1698 + }
1699 + }
1700 + }
1701 + }
1702 + }
1703 + } else {
1704 + //min and max adults num have changed, delete
1705 + $q = "DELETE FROM `#__vikbooking_adultsdiff` WHERE `idroom`='".$oldroom['id']."';";
1706 + $dbo->setQuery($q);
1707 + $dbo->execute();
1708 + }
1709 + if ($adchdisctouch == true) {
1710 + $app->enqueueMessage(JText::translate('VBUPDROOMADCHDISCSAVED'));
1711 + }
1712 + //
1713 + //check distinctive features if there were any changes
1714 + $old_rparams = json_decode($oldroom['params'], true);
1715 + $old_rparams = is_array($old_rparams) ? $old_rparams : array();
1716 + if (array_key_exists('features', $old_rparams)) {
1717 + $oldfeatures = array();
1718 + foreach ($old_rparams['features'] as $rnumunit => $oldfeat) {
1719 + foreach ($oldfeat as $featname => $featval) {
1720 + $oldfeatures[$rnumunit][$featname] = $featval;
1721 + break;
1722 + }
1723 + }
1724 + /**
1725 + * We reset the sub-unit information to all bookings only in case the new
1726 + * number of units is reduced. When we add new units or we modify the contents,
1727 + * we keep everything as is for the past reservations.
1728 + *
1729 + * @since 1.15.2 (J) - 1.5.5 (WP)
1730 + */
1731 + if ($oldfeatures != $newfeatures && count($newfeatures) < count($oldfeatures)) {
1732 + // changes were made to the first index (Room Number by default) of the distinctive features
1733 + // set to NULL all the already set roomindexes in bookings
1734 + $q = "UPDATE `#__vikbooking_ordersrooms` SET `roomindex`=NULL WHERE `idroom`=".(int)$oldroom['id'].";";
1735 + $dbo->setQuery($q);
1736 + $dbo->execute();
1737 + }
1738 + }
1739 + //
1740 + $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).";";
1741 + $dbo->setQuery($q);
1742 + $dbo->execute();
1743 +
1744 + /**
1745 + * Share availability calendars with other rooms.
1746 + *
1747 + * @since 1.13
1748 + */
1749 + // always reset relations for this main room
1750 + $q = "DELETE FROM `#__vikbooking_calendars_xref` WHERE `mainroom`={$pwhereup};";
1751 + $dbo->setQuery($q);
1752 + $dbo->execute();
1753 + $newxref = array();
1754 + foreach ($pshare_with as $cldroom) {
1755 + if (!empty($cldroom)) {
1756 + array_push($newxref, (int)$cldroom);
1757 + }
1758 + }
1759 + foreach ($newxref as $cldroom) {
1760 + $q = "INSERT INTO `#__vikbooking_calendars_xref` (`mainroom`, `childroom`) VALUES ({$pwhereup}, {$cldroom});";
1761 + $dbo->setQuery($q);
1762 + $dbo->execute();
1763 + }
1764 +
1765 + /**
1766 + * Room upgrade options.
1767 + *
1768 + * @since 1.16.0 (J) - 1.6.0 (WP)
1769 + */
1770 + $room_upgrade_options = [];
1771 + $room_upgrade = VikRequest::getInt('room_upgrade', 0, 'request');
1772 + $upgrade_rooms = VikRequest::getVar('upgrade_rooms', array());
1773 + $upgrade_discount = VikRequest::getFloat('upgrade_discount', 0, 'request');
1774 + if ($room_upgrade && is_array($upgrade_rooms) && count($upgrade_rooms)) {
1775 + $upgrade_rooms = array_map(function($rid) {
1776 + return (int)$rid;
1777 + }, $upgrade_rooms);
1778 +
1779 + $room_upgrade_options = [
1780 + 'rooms' => $upgrade_rooms,
1781 + 'discount' => $upgrade_discount,
1782 + ];
1783 + }
1784 + $config->set('room_upgrade_options_' . $pwhereup, json_encode($room_upgrade_options));
1785 +
1786 + /**
1787 + * Minimum advance booking offset can be defined at room-level (always in hours).
1788 + *
1789 + * @since 1.18.3 (J) - 1.8.3 (WP)
1790 + */
1791 + $pmin_adv_notice_room = VikRequest::getInt('min_adv_notice_room', 0, 'request');
1792 + $pmindate = VikRequest::getInt('mindate', 0, 'request');
1793 + if ($pmin_adv_notice_room && $pmindate >= 0) {
1794 + // set value
1795 + $config->set("room_{$pwhereup}_min_adv_notice", $pmindate);
1796 + } else {
1797 + // unset value
1798 + $config->set("room_{$pwhereup}_min_adv_notice", null);
1799 + }
1800 +
1801 + /**
1802 + * Maximum advance booking offset can be defined at room-level.
1803 + *
1804 + * @since 1.16.3 (J) - 1.6.3 (WP)
1805 + */
1806 + $pmax_adv_notice_room = VikRequest::getInt('max_adv_notice_room', 0, 'request');
1807 + $pmaxdate = VikRequest::getInt('maxdate', 0, 'request');
1808 + $pmaxdateinterval = VikRequest::getString('maxdateinterval', '', 'request');
1809 + $maxdate_str = '';
1810 + if ($pmax_adv_notice_room && $pmaxdate > 0) {
1811 + $pmaxdateinterval = !in_array($pmaxdateinterval, array('d', 'w', 'm', 'y')) ? 'y' : $pmaxdateinterval;
1812 + $maxdate_str = '+' . $pmaxdate . $pmaxdateinterval;
1813 + }
1814 + $config->set("room_{$pwhereup}_max_adv_notice", $maxdate_str);
1815 +
1816 + $app->enqueueMessage(JText::translate('VBUPDROOMOK'));
1817 + }
1818 +
1819 + if ($pupdatecaption == 1 || $stay === true) {
1820 + $app->redirect("index.php?option=com_vikbooking&task=editroom&cid[]=".$pwhereup);
1821 + } else {
1822 + $app->redirect("index.php?option=com_vikbooking&task=rooms");
1823 + }
1824 + }
1825 +
1826 + public function modavail() {
1827 + if (!JSession::checkToken() && !JSession::checkToken('get')) {
1828 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1829 + }
1830 + $cid = VikRequest::getVar('cid', array(0));
1831 + $room = $cid[0];
1832 + if (!empty($room)) {
1833 + $dbo = JFactory::getDBO();
1834 + $q = "SELECT `avail` FROM `#__vikbooking_rooms` WHERE `id`=".$dbo->quote($room).";";
1835 + $dbo->setQuery($q);
1836 + $dbo->execute();
1837 + $get = $dbo->loadAssocList();
1838 + $q = "UPDATE `#__vikbooking_rooms` SET `avail`='".(intval($get[0]['avail'])==1 ? 0 : 1)."' WHERE `id`=".$dbo->quote($room).";";
1839 + $dbo->setQuery($q);
1840 + $dbo->execute();
1841 + }
1842 + $mainframe = JFactory::getApplication();
1843 + $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
1844 + }
1845 +
1846 + public function removeroom()
1847 + {
1848 + if (!JSession::checkToken()) {
1849 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
1850 + }
1851 +
1852 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
1853 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1854 + }
1855 +
1856 + $ids = VikRequest::getVar('cid', array(0));
1857 + if (@count($ids)) {
1858 + $dbo = JFactory::getDBO();
1859 + foreach ($ids as $d) {
1860 + $q = "DELETE FROM `#__vikbooking_rooms` WHERE `id`=".$dbo->quote($d).";";
1861 + $dbo->setQuery($q);
1862 + $dbo->execute();
1863 + $q = "DELETE FROM `#__vikbooking_dispcost` WHERE `idroom`=".$dbo->quote($d).";";
1864 + $dbo->setQuery($q);
1865 + $dbo->execute();
1866 + }
1867 + }
1868 + $mainframe = JFactory::getApplication();
1869 + $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
1870 + }
1871 +
1872 + public function tariffs() {
1873 + VikBookingHelper::printHeader("fares");
1874 +
1875 + VikRequest::setVar('view', VikRequest::getCmd('view', 'tariffs'));
1876 +
1877 + parent::display();
1878 +
1879 + if (VikBooking::showFooter()) {
1880 + VikBookingHelper::printFooter();
1881 + }
1882 + }
1883 +
1884 + public function removetariffs()
1885 + {
1886 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
1887 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1888 + }
1889 +
1890 + $ids = VikRequest::getVar('cid', array(0));
1891 + $proomid = VikRequest::getInt('roomid', '', 'request');
1892 + if (@count($ids)) {
1893 + $dbo = JFactory::getDBO();
1894 + foreach ($ids as $r) {
1895 + $x=explode(";", $r);
1896 + foreach ($x as $rm) {
1897 + if (!empty($rm)) {
1898 + $q = "DELETE FROM `#__vikbooking_dispcost` WHERE `id`=".$dbo->quote($rm).";";
1899 + $dbo->setQuery($q);
1900 + $dbo->execute();
1901 + }
1902 + }
1903 + }
1904 + }
1905 + $mainframe = JFactory::getApplication();
1906 + $mainframe->redirect("index.php?option=com_vikbooking&task=tariffs&cid[]=".$proomid);
1907 + }
1908 +
1909 + public function editbusy() {
1910 + VikBookingHelper::printHeader("8");
1911 +
1912 + VikRequest::setVar('view', VikRequest::getCmd('view', 'editbusy'));
1913 +
1914 + parent::display();
1915 +
1916 + if (VikBooking::showFooter()) {
1917 + VikBookingHelper::printFooter();
1918 + }
1919 + }
1920 +
1921 + public function updatebusy()
1922 + {
1923 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1924 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1925 + }
1926 +
1927 + $this->do_updatebusy();
1928 + }
1929 +
1930 + public function updatebusydoinv()
1931 + {
1932 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
1933 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
1934 + }
1935 +
1936 + $this->do_updatebusy('geninvoices');
1937 + }
1938 +
1939 + private function do_updatebusy($callback = '')
1940 + {
1941 + $pidorder = VikRequest::getInt('idorder', 0, 'request');
1942 + $pcheckindate = VikRequest::getString('checkindate', '', 'request');
1943 + $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
1944 + $pcheckinh = VikRequest::getString('checkinh', '', 'request');
1945 + $pcheckinm = VikRequest::getString('checkinm', '', 'request');
1946 + $pcheckouth = VikRequest::getString('checkouth', '', 'request');
1947 + $pcheckoutm = VikRequest::getString('checkoutm', '', 'request');
1948 + $pcustdata = VikRequest::getString('custdata', '', 'request');
1949 + $pareprices = VikRequest::getString('areprices', '', 'request');
1950 + $ptotpaid = VikRequest::getString('totpaid', '', 'request');
1951 + $prefund = VikRequest::getString('refund', '', 'request');
1952 + $pfrominv = VikRequest::getInt('frominv', '', 'request');
1953 + $pvcm = VikRequest::getInt('vcm', '', 'request');
1954 + $pgoto = VikRequest::getString('goto', '', 'request');
1955 + $pextracn = VikRequest::getVar('extracn', []);
1956 + $pextracc = VikRequest::getVar('extracc', []);
1957 + $pextractx = VikRequest::getVar('extractx', []);
1958 + /**
1959 + * This is a "foreign key" integer value useful for other Vik plugins
1960 + * to store custom extra services within a VBO reservation. Another
1961 + * custom value "extra foreign data" (extracdata) is added. We also
1962 + * support a "type" string useful for VCM to determine the type of service.
1963 + *
1964 + * @since 1.16.0 (J) - 1.6.0 (WP)
1965 + * @since 1.16.1 (J) - 1.6.1 (WP) added the "type" string.
1966 + */
1967 + $pextractype = VikRequest::getVar('extractype', []);
1968 + $pextracfk = VikRequest::getVar('extracfk', []);
1969 + $pextracdata = VikRequest::getVar('extracdata', [], 'request', 'array', VIKREQUEST_ALLOWRAW);
1970 +
1971 + $dbo = JFactory::getDbo();
1972 + $user = JFactory::getUser();
1973 + $app = JFactory::getApplication();
1974 +
1975 + // availability helper
1976 + $av_helper = VikBooking::getAvailabilityInstance();
1977 +
1978 + $actnow = time();
1979 + $nowdf = VikBooking::getDateFormat(true);
1980 + if ($nowdf == "%d/%m/%Y") {
1981 + $df = 'd/m/Y';
1982 + } elseif ($nowdf == "%m/%d/%Y") {
1983 + $df = 'm/d/Y';
1984 + } else {
1985 + $df = 'Y/m/d';
1986 + }
1987 +
1988 + $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder;
1989 + $dbo->setQuery($q, 0, 1);
1990 + $ord = $dbo->loadAssoc();
1991 + if (!$ord) {
1992 + $app->redirect("index.php?option=com_vikbooking&task=rooms");
1993 + exit;
1994 + }
1995 +
1996 + $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;";
1997 + $dbo->setQuery($q);
1998 + $ordersrooms = $dbo->loadAssocList();
1999 +
2000 + // do not touch this array property because it's used by VCM
2001 + $ord['rooms_info'] = $ordersrooms;
2002 +
2003 + // room stay dates in case of split stay
2004 + $room_stay_dates = [];
2005 + if ($ord['split_stay']) {
2006 + if ($ord['status'] == 'confirmed') {
2007 + $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
2008 + } else {
2009 + $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $ord['id'], []);
2010 + }
2011 + // immediately count the number of nights of stay for each split room
2012 + foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
2013 + if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
2014 + // overwrite values for compatibility with non-confirmed bookings
2015 + $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
2016 + $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
2017 + }
2018 + $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
2019 + // overwrite the whole array
2020 + $room_stay_dates[$sps_r_k] = $sps_r_v;
2021 + }
2022 + }
2023 +
2024 + // package or custom rate
2025 + $is_package = !empty($ord['pkg']) ? true : false;
2026 + $is_cust_cost = false;
2027 + foreach ($ordersrooms as $kor => $or) {
2028 + if ($is_package !== true && !empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
2029 + $is_cust_cost = true;
2030 + break;
2031 + }
2032 + }
2033 +
2034 + // room switching
2035 + $toswitch = array();
2036 + $idbooked = array();
2037 + $rooms_units = array();
2038 +
2039 + $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms`;";
2040 + $dbo->setQuery($q);
2041 + $all_rooms = $dbo->loadAssocList();
2042 + foreach ($all_rooms as $rr) {
2043 + $rooms_units[$rr['id']]['name'] = $rr['name'];
2044 + $rooms_units[$rr['id']]['units'] = $rr['units'];
2045 + }
2046 +
2047 + foreach ($ordersrooms as $ind => $or) {
2048 + $switch_command = VikRequest::getString('switch_'.$or['id'], '', 'request');
2049 + if (!empty($switch_command) && intval($switch_command) != $or['idroom'] && array_key_exists(intval($switch_command), $rooms_units)) {
2050 + if (!isset($idbooked[$or['idroom']])) {
2051 + $idbooked[$or['idroom']] = 0;
2052 + }
2053 + $idbooked[$or['idroom']]++;
2054 + $orkey = count($toswitch);
2055 + $toswitch[$orkey]['from'] = $or['idroom'];
2056 + $toswitch[$orkey]['to'] = intval($switch_command);
2057 + $toswitch[$orkey]['record'] = $or;
2058 + $toswitch[$orkey]['record_ind'] = $ind;
2059 + }
2060 + }
2061 +
2062 + if (count($toswitch) && (!empty($ordersrooms[0]['idtar']) || $is_package || $is_cust_cost)) {
2063 + foreach ($toswitch as $ksw => $rsw) {
2064 + $plusunit = array_key_exists($rsw['to'], $idbooked) ? $idbooked[$rsw['to']] : 0;
2065 + $room_checkin = $ord['checkin'];
2066 + $room_checkout = $ord['checkout'];
2067 + if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
2068 + $room_checkin = $room_stay_dates[$rsw['record_ind']]['checkin'];
2069 + $room_checkout = $room_stay_dates[$rsw['record_ind']]['checkout'];
2070 + }
2071 + if (!VikBooking::roomBookable($rsw['to'], ($rooms_units[$rsw['to']]['units'] + $plusunit), $room_checkin, $room_checkout)) {
2072 + // the room is not available
2073 + unset($toswitch[$ksw]);
2074 + VikError::raiseWarning('', JText::sprintf('VBSWITCHRERR', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']));
2075 + }
2076 + }
2077 + if (count($toswitch)) {
2078 + // reset first record rate
2079 + reset($ordersrooms);
2080 + $q = "UPDATE `#__vikbooking_ordersrooms` SET `idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$ordersrooms[0]['id'].";";
2081 + $dbo->setQuery($q);
2082 + $dbo->execute();
2083 +
2084 + // flag for invoking VCM at a proper time
2085 + $vcm_should_run = false;
2086 +
2087 + foreach ($toswitch as $ksw => $rsw) {
2088 + // update room reservation record
2089 + $q = "UPDATE `#__vikbooking_ordersrooms` SET `idroom`=".$rsw['to'].",`idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$rsw['record']['id'].";";
2090 + $dbo->setQuery($q);
2091 + $dbo->execute();
2092 + $app->enqueueMessage(JText::sprintf('VBSWITCHROK', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']));
2093 +
2094 + // update Notes field for this booking to keep track of the previous room that was assigned
2095 + $prev_room_name = array_key_exists($rsw['from'], $rooms_units) ? $rooms_units[$rsw['from']]['name'] : '';
2096 + if (!empty($prev_room_name)) {
2097 + $new_notes = JText::sprintf('VBOPREVROOMMOVED', $prev_room_name, date($df.' H:i:s'))."\n".$ord['adminnotes'];
2098 + $q = "UPDATE `#__vikbooking_orders` SET `adminnotes`=".$dbo->quote($new_notes)." WHERE `id`=".(int)$ord['id'].";";
2099 + $dbo->setQuery($q);
2100 + $dbo->execute();
2101 + }
2102 +
2103 + if ($ord['status'] == 'confirmed') {
2104 + // update room record in _busy
2105 + 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'])) {
2106 + // in case of a split stay it is fundamental to update the exact busy record ID
2107 + $q = "UPDATE `#__vikbooking_busy` SET `idroom`=" . $rsw['to'] . " WHERE `id`=" . (int)$room_stay_dates[$rsw['record_ind']]['id'];
2108 + $dbo->setQuery($q);
2109 + $dbo->execute();
2110 + } else {
2111 + // regular processing of a room ID for a reservation, no matter which one, we switch it
2112 + $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'];
2113 + $dbo->setQuery($q, 0, 1);
2114 + $cur_busy = $dbo->loadAssoc();
2115 + if ($cur_busy) {
2116 + $q = "UPDATE `#__vikbooking_busy` SET `idroom`=".$rsw['to']." WHERE `id`=".$cur_busy['id']." AND `idroom`=".$cur_busy['idroom']." LIMIT 1;";
2117 + $dbo->setQuery($q);
2118 + $dbo->execute();
2119 + }
2120 + }
2121 +
2122 + /**
2123 + * Make sure to take care of the shared calendars before invoking VCM.
2124 + * Register the flag to run the Channel Manager and leave the booking
2125 + * array unchanged to run just one update request.
2126 + *
2127 + * @since 1.16.0 (J) - 1.6.0 (WP)
2128 + */
2129 + $vcm_should_run = true;
2130 +
2131 + } elseif ($ord['status'] == 'standby') {
2132 + // remove record in _tmplock
2133 + $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($ord['id']) . ";";
2134 + $dbo->setQuery($q);
2135 + $dbo->execute();
2136 + // check if it's a split stay
2137 + if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
2138 + // update room ID in split stay data
2139 + $room_stay_dates[$rsw['record_ind']]['idroom'] = $rsw['to'];
2140 + // update configuration record
2141 + VBOFactory::getConfig()->set('split_stay_' . $ord['id'], json_encode($room_stay_dates));
2142 + }
2143 + }
2144 + }
2145 +
2146 + // unset any previously booked room due to calendar sharing
2147 + VikBooking::cleanSharedCalendarsBusy($ord['id']);
2148 + // check if some of the rooms booked have shared calendars
2149 + VikBooking::updateSharedCalendars($ord['id']);
2150 +
2151 + if ($vcm_should_run) {
2152 + // we can now run the Channel Manager after having updated the shared calendars
2153 + $vcm_autosync = VikBooking::vcmAutoUpdate();
2154 + if ($vcm_autosync > 0) {
2155 + $vcm_obj = VikBooking::getVcmInvoker();
2156 + $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
2157 + $sync_result = $vcm_obj->doSync();
2158 + if ($sync_result === false) {
2159 + $vcm_err = $vcm_obj->getError();
2160 + VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
2161 + }
2162 + } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
2163 + 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>');
2164 + }
2165 + }
2166 +
2167 + //Booking History
2168 + VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($ord));
2169 + //
2170 + $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2171 + exit;
2172 + }
2173 + }
2174 +
2175 + // update booking data
2176 + $first = VikBooking::getDateTimestamp($pcheckindate, $pcheckinh, $pcheckinm);
2177 + $second = VikBooking::getDateTimestamp($pcheckoutdate, $pcheckouth, $pcheckoutm);
2178 + if ($second <= $first) {
2179 + VikError::raiseWarning('', JText::translate('ERRPREV'));
2180 + $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2181 + exit;
2182 + }
2183 +
2184 + $secdiff = $second - $first;
2185 + $daysdiff = $secdiff / 86400;
2186 + if (is_int($daysdiff)) {
2187 + if ($daysdiff < 1) {
2188 + $daysdiff = 1;
2189 + }
2190 + } else {
2191 + if ($daysdiff < 1) {
2192 + $daysdiff = 1;
2193 + } else {
2194 + $sum = floor($daysdiff) * 86400;
2195 + $newdiff = $secdiff - $sum;
2196 + $maxhmore = VikBooking::getHoursMoreRb() * 3600;
2197 + if ($maxhmore >= $newdiff) {
2198 + $daysdiff = floor($daysdiff);
2199 + } else {
2200 + $daysdiff = ceil($daysdiff);
2201 + }
2202 + }
2203 + }
2204 +
2205 + $groupdays = VikBooking::getGroupDays($first, $second, $daysdiff);
2206 + $opertwounits = true;
2207 +
2208 + $units_counter = array();
2209 + $prm_room_oid = VikRequest::getInt('rm_room_oid', 0, 'request');
2210 + foreach ($ordersrooms as $ind => $or) {
2211 + if (!isset($units_counter[$or['idroom']])) {
2212 + $units_counter[$or['idroom']] = -1;
2213 + }
2214 + if ($prm_room_oid != $or['id']) {
2215 + $units_counter[$or['idroom']]++;
2216 + }
2217 + }
2218 +
2219 + /**
2220 + * Split stay data for booking and rooms different stay dates.
2221 + *
2222 + * @since 1.16.0 (J) - 1.6.0 (WP)
2223 + */
2224 + $split_stay_data = VikRequest::getVar('split_stay_data', array());
2225 + $room_modify_dates = VikRequest::getVar('room_modify_dates', array());
2226 + $split_stay_checkins = [];
2227 + $split_stay_checkouts = [];
2228 +
2229 + if ($ord['split_stay'] && !empty($split_stay_data)) {
2230 + // make sure the min/max split stay dates match the booking global dates
2231 + foreach ($split_stay_data as $sps_k => $split_stay) {
2232 + if (empty($split_stay['checkin']) || empty($split_stay['checkout'])) {
2233 + continue;
2234 + }
2235 + $new_room_checkin = VikBooking::getDateTimestamp($split_stay['checkin'], $pcheckinh, $pcheckinm);
2236 + $new_room_checkout = VikBooking::getDateTimestamp($split_stay['checkout'], $pcheckouth, $pcheckoutm);
2237 + $split_stay_checkins[] = $new_room_checkin;
2238 + $split_stay_checkouts[] = $new_room_checkout;
2239 + if (isset($room_stay_dates[$sps_k])) {
2240 + $room_stay_dates[$sps_k]['new_checkin'] = $new_room_checkin;
2241 + $room_stay_dates[$sps_k]['new_checkout'] = $new_room_checkout;
2242 + $room_stay_dates[$sps_k]['new_nights'] = $av_helper->countNightsOfStay($new_room_checkin, $new_room_checkout);
2243 + }
2244 + }
2245 + if (empty($split_stay_checkins) || empty($split_stay_checkouts)) {
2246 + // error
2247 + VikError::raiseWarning('', 'Error, split stay rooms must have their own stay dates matching the booking check-in and check-out dates');
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 (min($split_stay_checkins) != $first) {
2252 + // error
2253 + 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)));
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 + if (max($split_stay_checkouts) != $second) {
2258 + // error
2259 + 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)));
2260 + $app->redirect("index.php?option=com_vikbooking&task=editbusy".($pvcm == 1 ? '&vcm=1' : '').($pfrominv == 1 ? '&frominv=1' : '')."&cid[]=".$ord['id'].($pgoto == 'overv' ? "&goto=overv" : ""));
2261 + exit;
2262 + }
2263 + }
2264 +
2265 + /**
2266 + * We need to make sure the sub-units of the rooms involved are not being overbooked.
2267 + * In this case, we simply raise an error message by not stopping the process.
2268 + *
2269 + * @since 1.13.0 (J) - 1.3.0 (WP)
2270 + */
2271 + $subunits_involved_bids = array();
2272 + //
2273 +
2274 + foreach ($ordersrooms as $ind => $or) {
2275 + $num = $ind + 1;
2276 + $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'] . ";";
2277 + $dbo->setQuery($check);
2278 + $busy = $dbo->loadAssocList();
2279 + if ($busy) {
2280 + // determine the days to consider for the count of the availability
2281 + $use_groupdays = $groupdays;
2282 + $room_checkin = $first;
2283 + $room_checkout = $second;
2284 + 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'])) {
2285 + $use_groupdays = VikBooking::getGroupDays($room_stay_dates[$ind]['new_checkin'], $room_stay_dates[$ind]['new_checkout'], $room_stay_dates[$ind]['new_nights']);
2286 + $room_checkin = $room_stay_dates[$ind]['new_checkin'];
2287 + $room_checkout = $room_stay_dates[$ind]['new_checkout'];
2288 + } elseif (!$ord['split_stay'] && !$ord['closure'] && $ord['roomsnum'] > 1 && $ord['days'] > 1 && $ord['status'] == 'confirmed' && VikRequest::getInt('room_modify_dates' . $ind, 0, 'request')) {
2289 + // room may have individual stay dates
2290 + if (isset($room_modify_dates[$ind]) && !empty($room_modify_dates[$ind]['checkin']) && !empty($room_modify_dates[$ind]['checkout'])) {
2291 + // get new stay dates (if changed)
2292 + $new_room_checkin = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkin'], $pcheckinh, $pcheckinm);
2293 + $new_room_checkout = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkout'], $pcheckouth, $pcheckoutm);
2294 + $new_room_staynights = $av_helper->countNightsOfStay($new_room_checkin, $new_room_checkout);
2295 + $use_groupdays = VikBooking::getGroupDays($new_room_checkin, $new_room_checkout, $new_room_staynights);
2296 + $room_checkin = $new_room_checkin;
2297 + $room_checkout = $new_room_checkout;
2298 + // inject room stay nights and timestamps
2299 + $room_modify_dates[$ind]['stay_nights'] = $new_room_staynights;
2300 + $room_modify_dates[$ind]['checkin_ts'] = $new_room_checkin;
2301 + $room_modify_dates[$ind]['checkout_ts'] = $new_room_checkout;
2302 + }
2303 + }
2304 +
2305 + foreach ($use_groupdays as $gday) {
2306 + // count units booked for each stay timestamp
2307 + $bfound = 0;
2308 + foreach ($busy as $bu) {
2309 + if ($gday >= $bu['checkin'] && $gday <= $bu['realback']) {
2310 + // increase units booked found
2311 + $bfound++;
2312 + // keep track of the IDs involved to avoid overbooking for the sub-units
2313 + if (!empty($or['roomindex'])) {
2314 + if (!isset($subunits_involved_bids[$bu['idorder']])) {
2315 + $subunits_involved_bids[$bu['idorder']] = array();
2316 + }
2317 + array_push($subunits_involved_bids[$bu['idorder']], array(
2318 + 'idroom' => $or['idroom'],
2319 + 'roomindex' => $or['roomindex'],
2320 + ));
2321 + }
2322 + }
2323 + }
2324 +
2325 + // units booked must be greater than zero in case of split stays involving the same room multiple times
2326 + $detract_multi_units = $units_counter[$or['idroom']];
2327 + if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$ind]) && $room_stay_dates[$ind]['idroom'] == $or['idroom']) {
2328 + // split stay bookings never occupy the same room on the same dates
2329 + $detract_multi_units = 0;
2330 + }
2331 + if ($bfound > 0 && $bfound >= ($or['units'] - $detract_multi_units)) {
2332 + $opertwounits = false;
2333 + break 2;
2334 + }
2335 +
2336 + // make sure the room is not temporarily locked while waiting to be paid/confirmed
2337 + if ($ord['status'] == 'confirmed' && !VikBooking::roomNotLocked($or['idroom'], $or['units'], $room_checkin, $room_checkout)) {
2338 + $opertwounits = false;
2339 + break 2;
2340 + }
2341 + }
2342 + }
2343 + }
2344 +
2345 + /**
2346 + * Make sure no sub-units are overbooked even though the main room is available.
2347 + *
2348 + * @since 1.13.0 (J) - 1.3.0 (WP)
2349 + */
2350 + if ($opertwounits === true && $subunits_involved_bids) {
2351 + $subunits_involved_bids = array_unique($subunits_involved_bids);
2352 + // grab all the information about the bids involved and the related rooms/indexes
2353 + $q = "SELECT `or`.`idorder`, `or`.`idroom`, `or`.`roomindex`
2354 + FROM `#__vikbooking_ordersrooms` AS `or`
2355 + WHERE `or`.`idorder` IN (" . implode(', ', array_keys($subunits_involved_bids)) . ");";
2356 + $dbo->setQuery($q);
2357 + $involved_data = $dbo->loadAssocList();
2358 + foreach ($involved_data as $invb) {
2359 + if (empty($invb['roomindex'])) {
2360 + continue;
2361 + }
2362 + foreach ($subunits_involved_bids[$invb['idorder']] as $bookedindex) {
2363 + if ($bookedindex['idroom'] == $invb['idroom'] && $bookedindex['roomindex'] == $invb['roomindex']) {
2364 + // this same sub-unit is occupied by this booking ID: raise an error message to inform the administrator
2365 + $involved_booking = VikBooking::getBookingInfoFromID($invb['idorder']);
2366 + $involved_room = VikBooking::getRoomInfo($invb['idroom'], ['name', 'params'], $no_cache = true);
2367 + $subunit_name = $invb['roomindex'];
2368 + $room_params = (array) json_decode($involved_room['params'] ?? '[]', true);
2369 + foreach (($room_params['features'] ?? []) as $rind => $rfeatures) {
2370 + if ($rind == $invb['roomindex']) {
2371 + foreach ($rfeatures as $fname => $fval) {
2372 + if (strlen($fval)) {
2373 + $subunit_name = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
2374 + break;
2375 + }
2376 + }
2377 + }
2378 + }
2379 + $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>';
2380 + $app->enqueueMessage(
2381 + JText::sprintf(
2382 + 'VBOSUBUNITOVERBOOKEDERR',
2383 + $subunit_name,
2384 + $involved_room['name'] ?? $invb['idroom'],
2385 + date($df, $involved_booking['checkin'] ?? 0),
2386 + date($df, $involved_booking['checkout'] ?? 0),
2387 + $invb['idorder']
2388 + ) . $adjust_link,
2389 + 'error'
2390 + );
2391 + }
2392 + }
2393 + }
2394 + }
2395 +
2396 + $forcebooking = VikRequest::getInt('forcebooking', 0, 'request');
2397 + if ($opertwounits === true || $forcebooking) {
2398 + // update dates, customer information, amount paid and busy records before checking the rates
2399 + $turnover_secs = VikBooking::getHoursRoomAvail() * 3600;
2400 + $realback = $turnover_secs + $second;
2401 +
2402 + $newtotalpaid = strlen($ptotpaid) > 0 ? floatval($ptotpaid) : "";
2403 + $newrefund = strlen($prefund) > 0 ? floatval($prefund) : null;
2404 + $roomsnum = $ord['roomsnum'];
2405 +
2406 + // add room to existing booking
2407 + $room_added = false;
2408 + $padd_room_id = VikRequest::getInt('add_room_id', '', 'request');
2409 + $padd_room_adults = VikRequest::getInt('add_room_adults', 2, 'request');
2410 + $padd_room_children = VikRequest::getInt('add_room_children', 0, 'request');
2411 + $padd_room_fname = VikRequest::getString('add_room_fname', '', 'request');
2412 + $padd_room_lname = VikRequest::getString('add_room_lname', '', 'request');
2413 + $padd_room_price = VikRequest::getFloat('add_room_price', 0, 'request');
2414 + $paliq_add_room = VikRequest::getInt('aliq_add_room', 0, 'request');
2415 + if ($padd_room_id > 0 && ($padd_room_adults + $padd_room_children) > 0) {
2416 + // no need to re-validate the availability for this new room, as it was made via JS in the View.
2417 + // increase the rooms number for later update, and insert the new room record
2418 + $roomsnum++;
2419 + $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').");";
2420 + $dbo->setQuery($q);
2421 + $dbo->execute();
2422 + $room_added = true;
2423 + }
2424 +
2425 + // remove room from existing booking
2426 + $room_removed = false;
2427 + $room_removed_index = null;
2428 + if ($prm_room_oid > 0 && $roomsnum > 1) {
2429 + // check if the requested room record exists for removal
2430 + $q = "SELECT * FROM `#__vikbooking_ordersrooms` WHERE `id`=".$prm_room_oid." AND `idorder`=".$ord['id'].";";
2431 + $dbo->setQuery($q);
2432 + $room_before_rm = $dbo->loadAssoc();
2433 + if ($room_before_rm) {
2434 + // decrease the rooms number for later update, and remove the requested room record
2435 + $roomsnum--;
2436 + // find the index of this room in the current list before removal
2437 + foreach ($ordersrooms as $kor => $or) {
2438 + if ($or['id'] == $prm_room_oid) {
2439 + $room_removed_index = $kor;
2440 + break;
2441 + }
2442 + }
2443 + // go ahead with the deletion of the room record
2444 + $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `id`=".$prm_room_oid." AND `idorder`=".$ord['id']." LIMIT 1;";
2445 + $dbo->setQuery($q);
2446 + $dbo->execute();
2447 + $room_removed = $room_before_rm['idroom'];
2448 + }
2449 + }
2450 +
2451 + if ($ord['split_stay'] && !empty($split_stay_data) && count($split_stay_checkins) && ($room_added !== false || $room_removed !== false)) {
2452 + // split stay booking (even if only 1 room left) and one room was either added or removed: set new global stay dates
2453 + if ($room_removed !== false && isset($room_removed_index) && isset($split_stay_checkins[$room_removed_index])) {
2454 + // exclude the split stay dates of this room that was just removed
2455 + unset($split_stay_checkins[$room_removed_index], $split_stay_checkouts[$room_removed_index]);
2456 + }
2457 + if (count($split_stay_checkins) && count($split_stay_checkouts)) {
2458 + // if we still have rooms, and we should, update the booking global stay dates
2459 + $first = min($split_stay_checkins);
2460 + $second = max($split_stay_checkouts);
2461 + $daysdiff = $av_helper->countNightsOfStay($first, $second);
2462 + }
2463 + }
2464 +
2465 + // update booking's basic information (customer data, dates, tot paid, number of rooms, refund)
2466 + $basic_booking = new stdClass;
2467 + $basic_booking->id = $ord['id'];
2468 + $basic_booking->custdata = $pcustdata;
2469 + $basic_booking->days = (int)$daysdiff;
2470 + $basic_booking->checkin = $first;
2471 + $basic_booking->checkout = $second;
2472 + if (strlen($newtotalpaid) > 0) {
2473 + $basic_booking->totpaid = $newtotalpaid;
2474 + }
2475 + $basic_booking->roomsnum = (int)$roomsnum;
2476 + if ($newrefund !== null) {
2477 + $basic_booking->refund = $newrefund;
2478 + }
2479 + if ($ord['split_stay'] && $roomsnum < 2 && $room_removed !== false) {
2480 + // there is no point in keep treating this reservation as a split stay
2481 + $basic_booking->split_stay = 0;
2482 + }
2483 + $dbo->updateObject('#__vikbooking_orders', $basic_booking, 'id');
2484 +
2485 + // Booking History log for new amount paid (payment update)
2486 + if ($newtotalpaid > 0 && $newtotalpaid > (float)$ord['totpaid']) {
2487 + $extra_data = new stdClass;
2488 + $extra_data->amount_paid = ($newtotalpaid - (float)$ord['totpaid']);
2489 + VikBooking::getBookingHistoryInstance()->setBid($ord['id'])->setExtraData($extra_data)->store('PU', JText::sprintf('VBOPREVAMOUNTPAID', VikBooking::numberFormat((float)$ord['totpaid'])));
2490 + }
2491 +
2492 + // booking history log for new refund amount
2493 + if ($newrefund !== null && $newrefund != (float)$ord['refund']) {
2494 + // update current refund value
2495 + $ord['refund'] = $newrefund;
2496 + // store event
2497 + VikBooking::getBookingHistoryInstance()->setBid($ord['id'])->setExtraData(null)->store('RU', JText::sprintf('VBO_NEWREFUND_AMOUNT', VikBooking::numberFormat($ord['refund']), VikBooking::numberFormat($newrefund)));
2498 + }
2499 +
2500 + // update busy records
2501 + if ($ord['status'] == 'confirmed') {
2502 + $allbusy = [];
2503 + if ($ord['split_stay'] && !empty($split_stay_data)) {
2504 + // in case of split stay we need to update the busy records according to the nights selected
2505 + foreach ($split_stay_data as $sps_k => $split_stay) {
2506 + if (empty($split_stay['idbusy']) || empty($split_stay['checkin']) || empty($split_stay['checkout'])) {
2507 + // missing data
2508 + continue;
2509 + }
2510 + // get selected dates
2511 + $room_checkin = VikBooking::getDateTimestamp($split_stay['checkin'], $pcheckinh, $pcheckinm);
2512 + $room_checkout = VikBooking::getDateTimestamp($split_stay['checkout'], $pcheckouth, $pcheckoutm);
2513 + $room_realback = $turnover_secs + $room_checkout;
2514 + // update the exact record
2515 + $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $room_checkin . ", `checkout`=" . $room_checkout . ", `realback`=" . $room_realback . " WHERE `id`=" . (int)$split_stay['idbusy'] . ";";
2516 + $dbo->setQuery($q);
2517 + $dbo->execute();
2518 + }
2519 + } else {
2520 + // regularly update busy records for all rooms involved
2521 + $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'] . ";";
2522 + $dbo->setQuery($q);
2523 + $allbusy = $dbo->loadAssocList();
2524 +
2525 + foreach ($allbusy as $bb) {
2526 + $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $first . ", `checkout`=" . $second . ", `realback`=" . $realback . " WHERE `id`=" . $bb['id'] . ";";
2527 + $dbo->setQuery($q);
2528 + $dbo->execute();
2529 + }
2530 +
2531 + if (!$allbusy) {
2532 + /**
2533 + * If no existing busy records were fetched, it means we have some missing or broken
2534 + * records in the database. Proceed with restoring them to occupy the room(s).
2535 + *
2536 + * @since 1.8.19 (J) - 1.8.9 (WP)
2537 + */
2538 + $dbo->setQuery(
2539 + $dbo->getQuery(true)
2540 + ->delete($dbo->qn('#__vikbooking_ordersbusy'))
2541 + ->where($dbo->qn('idorder') . ' = ' . (int) $ord['id'])
2542 + );
2543 + $dbo->execute();
2544 + foreach ($ordersrooms as $or) {
2545 + if ($room_removed !== false && $or['id'] == $prm_room_oid) {
2546 + continue;
2547 + }
2548 + $restoreBusyRecord = (object) [
2549 + 'idroom' => $or['idroom'],
2550 + 'checkin' => $first,
2551 + 'checkout' => $second,
2552 + 'realback' => $realback,
2553 + ];
2554 + $dbo->insertObject('#__vikbooking_busy', $restoreBusyRecord, 'id');
2555 + if (!empty($restoreBusyRecord->id)) {
2556 + $restoreBusyRelation = (object) [
2557 + 'idorder' => $ord['id'],
2558 + 'idbusy' => $restoreBusyRecord->id,
2559 + ];
2560 + $dbo->insertObject('#__vikbooking_ordersbusy', $restoreBusyRelation, 'id');
2561 + }
2562 + }
2563 + }
2564 + }
2565 +
2566 + /**
2567 + * Check if some rooms have modified stay dates different than the booking stay dates.
2568 + *
2569 + * @since 1.16.0 (J) - 1.6.0 (WP)
2570 + */
2571 + if (!$ord['split_stay'] && !$ord['closure'] && $ord['roomsnum'] > 1 && $ord['days'] > 1) {
2572 + // load the occupied stay dates for each room in case they were modified
2573 + $room_stay_records = $av_helper->loadSplitStayBusyRecords($ord['id']);
2574 + // loop over all rooms to check the requested operations
2575 + foreach ($ordersrooms as $ind => $or) {
2576 + if (!VikRequest::getInt('room_modify_dates' . $ind, 0, 'request') || !isset($room_stay_records[$ind]) || empty($room_stay_records[$ind]['id'])) {
2577 + // toggle is disabled or data is missing
2578 + continue;
2579 + }
2580 + if (isset($room_modify_dates[$ind]) && !empty($room_modify_dates[$ind]['checkin']) && !empty($room_modify_dates[$ind]['checkout'])) {
2581 + // calculate the check-in and check-out timestamps, we expect them to be different from the global booking dates
2582 + $room_checkin = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkin'], $pcheckinh, $pcheckinm);
2583 + $room_checkout = VikBooking::getDateTimestamp($room_modify_dates[$ind]['checkout'], $pcheckouth, $pcheckoutm);
2584 + $room_realback = $turnover_secs + $room_checkout;
2585 + // we don't need to check if the dates are different, we just update the record
2586 + $q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $room_checkin . ", `checkout`=" . $room_checkout . ", `realback`=" . $room_realback . " WHERE `id`=" . (int)$room_stay_records[$ind]['id'] . ";";
2587 + $dbo->setQuery($q);
2588 + $dbo->execute();
2589 + // inject new room stay timestamps
2590 + $ordersrooms[$ind]['modified_checkin'] = $room_checkin;
2591 + $ordersrooms[$ind]['modified_checkout'] = $room_checkout;
2592 + }
2593 + }
2594 + }
2595 +
2596 + // add room to existing (confirmed) booking
2597 + if ($room_added === true) {
2598 + // add busy record for the new room unit
2599 + $q = "INSERT INTO `#__vikbooking_busy` (`idroom`,`checkin`,`checkout`,`realback`) VALUES(".$padd_room_id.", ".$dbo->quote($first).", ".$dbo->quote($second).", ".$dbo->quote($realback).");";
2600 + $dbo->setQuery($q);
2601 + $dbo->execute();
2602 + $newbusyid = $dbo->insertid();
2603 + $q = "INSERT INTO `#__vikbooking_ordersbusy` (`idorder`,`idbusy`) VALUES(".$ord['id'].", ".(int)$newbusyid.");";
2604 + $dbo->setQuery($q);
2605 + $dbo->execute();
2606 + }
2607 +
2608 + // remove room from existing (confirmed) booking
2609 + if ($room_removed !== false) {
2610 + // remove busy record for the removed room
2611 + if ($ord['split_stay'] && !empty($split_stay_data) && !empty($room_removed_index)) {
2612 + // in case of split stay we want to remove the exact dates of the previously booked room
2613 + 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'])) {
2614 + // remove the exact records
2615 + $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`=" . $room_stay_dates[$room_removed_index]['id'] . " AND `idroom`=" . $room_removed . ";";
2616 + $dbo->setQuery($q);
2617 + $dbo->execute();
2618 + $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=" . $ord['id'] . " AND `idbusy`=" . $room_stay_dates[$room_removed_index]['id'] . ";";
2619 + $dbo->setQuery($q);
2620 + $dbo->execute();
2621 + }
2622 + } else {
2623 + // regularly remove the first matching room
2624 + foreach ($allbusy as $bb) {
2625 + if ($bb['idroom'] == $room_removed) {
2626 + // remove the first room with this ID that was booked
2627 + $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`=".$bb['id']." AND `idroom`=".$room_removed.";";
2628 + $dbo->setQuery($q);
2629 + $dbo->execute();
2630 + $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".$ord['id']." AND `idbusy`=".$bb['id'].";";
2631 + $dbo->setQuery($q);
2632 + $dbo->execute();
2633 + break;
2634 + }
2635 + }
2636 + }
2637 + }
2638 +
2639 + if ($ord['checkin'] != $first || $ord['checkout'] != $second || $room_added === true || $room_removed !== false) {
2640 + // unset any previously booked room due to calendar sharing
2641 + VikBooking::cleanSharedCalendarsBusy($ord['id']);
2642 + // check if some of the rooms booked have shared calendars
2643 + VikBooking::updateSharedCalendars($ord['id'], array(), $first, $second);
2644 +
2645 + // invoke Channel Manager
2646 + $vcm_autosync = VikBooking::vcmAutoUpdate();
2647 + if ($vcm_autosync > 0) {
2648 + $vcm_obj = VikBooking::getVcmInvoker();
2649 + $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
2650 + $sync_result = $vcm_obj->doSync();
2651 + if ($sync_result === false) {
2652 + $vcm_err = $vcm_obj->getError();
2653 + VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
2654 + }
2655 + } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
2656 + 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>');
2657 + }
2658 + //
2659 + }
2660 + }
2661 +
2662 + $upd_esit = JText::translate('RESUPDATED');
2663 +
2664 + // update the room rates
2665 + $isdue = 0;
2666 + $tot_taxes = 0;
2667 + $tot_city_taxes = 0;
2668 + $tot_fees = 0;
2669 + $tot_damage_dep = 0;
2670 + $doup = true;
2671 + $tars = array();
2672 + $cust_costs = array();
2673 + $rooms_costs_map = array();
2674 + $arrpeople = array();
2675 + foreach ($ordersrooms as $kor => $or) {
2676 + // remove from existing booking
2677 + if ($room_removed !== false) {
2678 + if ($or['id'] == $prm_room_oid) {
2679 + // do not consider this room for the calculation of the new total amount
2680 + // we can unset this array for later use, because the channel manager has already been invoked.
2681 + unset($ordersrooms[$kor]);
2682 + continue;
2683 + }
2684 + }
2685 +
2686 + // room index starting from 1
2687 + $num = $kor + 1;
2688 +
2689 + // default values to be considered
2690 + $room_nights = $daysdiff;
2691 + $room_checkin = $ord['checkin'];
2692 + $room_checkout = $ord['checkout'];
2693 + 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'])) {
2694 + // overwrite default values in case of split-stay booking
2695 + $room_nights = $room_stay_dates[$kor]['new_nights'];
2696 + $room_checkin = $room_stay_dates[$kor]['new_checkin'];
2697 + $room_checkout = $room_stay_dates[$kor]['new_checkout'];
2698 + } elseif (!$ord['split_stay'] && ($room_modify_dates[$kor]['checkin_ts'] ?? null)) {
2699 + // overwrite default values in case of multi-room reservation with different stay dates
2700 + $room_nights = $room_modify_dates[$kor]['stay_nights'] ?? $room_nights;
2701 + $room_checkin = $room_modify_dates[$kor]['checkin_ts'];
2702 + $room_checkout = $room_modify_dates[$kor]['checkout_ts'];
2703 + }
2704 +
2705 + $padults = VikRequest::getString('adults' . $num, '', 'request');
2706 + $pchildren = VikRequest::getString('children' . $num, '', 'request');
2707 + $ppets = VikRequest::getInt('pets' . $num, 0, 'request');
2708 + if (strlen($padults) || strlen($pchildren)) {
2709 + $arrpeople[$num]['adults'] = (int)$padults;
2710 + $arrpeople[$num]['children'] = (int)$pchildren;
2711 + $arrpeople[$num]['pets'] = $ppets;
2712 + }
2713 + $ppriceid = VikRequest::getString('priceid'.$num, '', 'request');
2714 + $polderpriceid = VikRequest::getString('olderpriceid'.$num, '', 'request');
2715 + $ppkgid = VikRequest::getString('pkgid'.$num, '', 'request');
2716 + $pcust_cost = VikRequest::getString('cust_cost'.$num, '', 'request');
2717 + $paliq = VikRequest::getString('aliq'.$num, '', 'request');
2718 + $pcust_cpolicy_id = VikRequest::getInt('cust_cpolicy_id'.$num, 0, 'request');
2719 + if ($is_package === true && !empty($ppkgid)) {
2720 + $pkg_cost = $or['cust_cost'];
2721 + $pkg_idiva = $or['cust_idiva'];
2722 + $pkg_info = VikBooking::getPackage($ppkgid);
2723 + if (is_array($pkg_info) && count($pkg_info) > 0) {
2724 + $use_adults = array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num]) ? $arrpeople[$num]['adults'] : $or['adults'];
2725 + $pkg_cost = $pkg_info['pernight_total'] == 1 ? ($pkg_info['cost'] * $room_nights) : $pkg_info['cost'];
2726 + $pkg_cost = $pkg_info['perperson'] == 1 ? ($pkg_cost * ($use_adults > 0 ? $use_adults : 1)) : $pkg_cost;
2727 + $pkg_cost = VikBooking::sayPackagePlusIva($pkg_cost, $pkg_info['idiva']);
2728 + }
2729 + $cust_costs[$num] = array('pkgid' => $ppkgid, 'cust_cost' => $pkg_cost, 'aliq' => $pkg_idiva);
2730 + $isdue += $pkg_cost;
2731 + $cost_minus_tax = VikBooking::sayPackageMinusIva($pkg_cost, $pkg_idiva);
2732 + $tot_taxes += ($pkg_cost - $cost_minus_tax);
2733 + continue;
2734 + }
2735 + if (empty($ppriceid) && !empty($pcust_cost) && floatval($pcust_cost) > 0) {
2736 + $cust_costs[$num] = [
2737 + 'cust_cost' => $pcust_cost,
2738 + 'aliq' => $paliq,
2739 + 'cust_cpolicy_id' => $pcust_cpolicy_id,
2740 + ];
2741 + $cost_after_tax = VikBooking::sayPackagePlusIva((float)$pcust_cost, (int)$paliq);
2742 + $isdue += $cost_after_tax;
2743 + $cost_minus_tax = VikBooking::sayPackageMinusIva((float)$pcust_cost, (int)$paliq);
2744 + $tot_taxes += ($cost_after_tax - $cost_minus_tax);
2745 + continue;
2746 + }
2747 +
2748 + // load room rates for the requested rate plan and nights
2749 + $q = "SELECT * FROM `#__vikbooking_dispcost` WHERE `idroom`=" . (int)$or['idroom'] . " AND `days`=" . $room_nights . " AND `idprice`=" . (int)$ppriceid . ";";
2750 + $dbo->setQuery($q);
2751 + $tar = $dbo->loadAssocList();
2752 + if (!$tar) {
2753 + $doup = false;
2754 + break;
2755 + }
2756 +
2757 + /**
2758 + * The current price may be different from the price paid at the time of booking.
2759 + * Check whether it has been asked to keep the old price of the time of booking.
2760 + *
2761 + * @since 1.13.0 (J) - 1.3.0 (WP)
2762 + */
2763 + $old_price_used = false;
2764 + if (!empty($polderpriceid)) {
2765 + $older_info = explode(':', $polderpriceid);
2766 + if ((int)$older_info[0] == (int)$ppriceid) {
2767 + $old_price = isset($older_info[1]) ? (float)$older_info[1] : 0;
2768 + if ($old_price > 0) {
2769 + // we override the 'cost' property of the tar array by taking the previous cost
2770 + $old_price_used = true;
2771 + $tar[0]['cost'] = $old_price;
2772 + }
2773 + }
2774 + }
2775 +
2776 + if (!$old_price_used) {
2777 + // apply seasonal rates
2778 + $tar = VikBooking::applySeasonsRoom($tar, $room_checkin, $room_checkout);
2779 + }
2780 +
2781 + // different usage
2782 + if (!$old_price_used && $or['fromadult'] <= $or['adults'] && $or['toadult'] >= $or['adults']) {
2783 + // apply OBP rules
2784 + $tar = VBORoomHelper::getInstance()->applyOBPRules($tar, $or, $or['adults']);
2785 + }
2786 +
2787 + $cost_plus_tax = VikBooking::sayCostPlusIva($tar[0]['cost'], $tar[0]['idprice']);
2788 + $isdue += $cost_plus_tax;
2789 + if ($cost_plus_tax == $tar[0]['cost']) {
2790 + $cost_minus_tax = VikBooking::sayCostMinusIva($tar[0]['cost'], $tar[0]['idprice']);
2791 + $tot_taxes += ($tar[0]['cost'] - $cost_minus_tax);
2792 + } else {
2793 + $tot_taxes += ($cost_plus_tax - $tar[0]['cost']);
2794 + }
2795 + $tars[$num] = $tar;
2796 + $rooms_costs_map[$num] = $tar[0]['cost'];
2797 + }
2798 +
2799 + if ($doup === true) {
2800 + if ($room_added === true) {
2801 + // add room to existing booking may require to increase the total amount, and taxes
2802 + $padd_room_price = VikRequest::getFloat('add_room_price', 0, 'request');
2803 + $paliq_add_room = VikRequest::getInt('aliq_add_room', 0, 'request');
2804 + if (!empty($padd_room_price) && floatval($padd_room_price) > 0) {
2805 + $isdue += (float)$padd_room_price;
2806 + $cost_minus_tax = VikBooking::sayPackageMinusIva((float)$padd_room_price, (int)$paliq_add_room);
2807 + $tot_taxes += ((float)$padd_room_price - $cost_minus_tax);
2808 + }
2809 + }
2810 +
2811 + // load options
2812 + $q = "SELECT * FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
2813 + $dbo->setQuery($q);
2814 + $toptionals = $dbo->loadAssocList();
2815 +
2816 + foreach ($ordersrooms as $kor => $or) {
2817 + $num = $kor + 1;
2818 +
2819 + // default values to be considered
2820 + $room_nights = $daysdiff;
2821 + $room_checkin = $ord['checkin'];
2822 + $room_checkout = $ord['checkout'];
2823 + 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'])) {
2824 + // overwrite default values in case of split-stay booking
2825 + $room_nights = $room_stay_dates[$kor]['new_nights'];
2826 + $room_checkin = $room_stay_dates[$kor]['new_checkin'];
2827 + $room_checkout = $room_stay_dates[$kor]['new_checkout'];
2828 + } elseif (!$ord['split_stay'] && ($room_modify_dates[$kor]['checkin_ts'] ?? null)) {
2829 + // overwrite default values in case of multi-room reservation with different stay dates
2830 + $room_nights = $room_modify_dates[$kor]['stay_nights'] ?? $room_nights;
2831 + $room_checkin = $room_modify_dates[$kor]['checkin_ts'];
2832 + $room_checkout = $room_modify_dates[$kor]['checkout_ts'];
2833 + }
2834 +
2835 + $pt_first_name = VikRequest::getString('t_first_name'.$num, '', 'request');
2836 + $pt_last_name = VikRequest::getString('t_last_name'.$num, '', 'request');
2837 + $wop = "";
2838 +
2839 + foreach ($toptionals as $opt) {
2840 + // option params
2841 + $opt_params = !empty($opt['oparams']) ? json_decode($opt['oparams'], true) : [];
2842 + $opt_params = is_array($opt_params) ? $opt_params : [];
2843 + if (!empty($opt['ageintervals']) && ($or['children'] > 0 || isset($arrpeople[$num]['children']))) {
2844 + $tmpvar = VikRequest::getInt('optid'.$num.$opt['id'], []);
2845 + if (is_array($tmpvar) && $tmpvar && ($arrpeople[$num]['children'] ?? 0)) {
2846 + $opt['quan'] = 1;
2847 + $optagenames = VikBooking::getOptionIntervalsAges($opt['ageintervals']);
2848 + $optagepcent = VikBooking::getOptionIntervalsPercentage($opt['ageintervals']);
2849 + $optageovrct = VikBooking::getOptionIntervalChildOverrides($opt, (isset($arrpeople[$num]) ? $arrpeople[$num]['adults'] : 0), (isset($arrpeople[$num]) ? $arrpeople[$num]['children'] : 0));
2850 + $optorigname = $opt['name'];
2851 + foreach ($tmpvar as $child_num => $chvar) {
2852 + $ageintervals_child_string = isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $opt['ageintervals'];
2853 + $optagecosts = VikBooking::getOptionIntervalsCosts($ageintervals_child_string);
2854 + $optorigcost = $optagecosts[($chvar - 1)];
2855 + $tmp_room_cost = 0;
2856 + if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 1) {
2857 + // percentage value of the adults tariff
2858 + if ($is_package !== true && array_key_exists($num, $tars)) {
2859 + // type of price
2860 + $tmp_room_cost = $tars[$num][0]['cost'];
2861 + $optorigcost = $tars[$num][0]['cost'] * $optagecosts[($chvar - 1)] / 100;
2862 + } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2863 + // package
2864 + $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2865 + $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2866 + } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2867 + // custom rate + custom tax rate
2868 + $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2869 + $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2870 + }
2871 + } elseif (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 2) {
2872 + // percentage value of room base cost
2873 + if ($is_package !== true && array_key_exists($num, $tars)) {
2874 + // type of price
2875 + $usecost = isset($tars[$num][0]['room_base_cost']) ? $tars[$num][0]['room_base_cost'] : $tars[$num][0]['cost'];
2876 + $tmp_room_cost = $usecost;
2877 + $optorigcost = $usecost * $optagecosts[($chvar - 1)] / 100;
2878 + } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2879 + // package
2880 + $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2881 + $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2882 + } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2883 + // custom rate + custom tax rate
2884 + $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2885 + $optorigcost = $cust_costs[$num]['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
2886 + }
2887 + }
2888 + $opt['cost'] = $optorigcost;
2889 + $opt['name'] = $optorigname.' ('.$optagenames[($chvar - 1)].')';
2890 + $opt['chageintv'] = $chvar;
2891 + $wop.=$opt['id'].":".$opt['quan']."-".$chvar.";";
2892 + $realcost = (intval($opt['perday']) == 1 ? ($opt['cost'] * $room_nights * $opt['quan']) : ($opt['cost'] * $opt['quan']));
2893 + if (!empty($opt['maxprice']) && $opt['maxprice'] > 0 && $realcost > $opt['maxprice']) {
2894 + $realcost = $opt['maxprice'];
2895 + }
2896 +
2897 + /**
2898 + * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
2899 + *
2900 + * @since 1.17.7 (J) - 1.7.7 (WP)
2901 + */
2902 + $custom_calc_booking = array_merge($ord, ['days' => $room_nights]);
2903 + $custom_calc_booking_room = array_merge($or, ($arrpeople[$num] ?? []), ($tmp_room_cost ? ['room_cost' => $tmp_room_cost] : []));
2904 + $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$opt, $custom_calc_booking, $custom_calc_booking_room]);
2905 + if ($custom_calculation) {
2906 + $realcost = (float) $custom_calculation[0];
2907 + }
2908 +
2909 + $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $opt['idiva']);
2910 + if ($opt['is_citytax'] == 1) {
2911 + $tot_city_taxes += $tmpopr;
2912 + } elseif ($opt['is_fee'] == 1) {
2913 + $tot_fees += $tmpopr;
2914 + } elseif ($opt_params['damagedep'] ?? 0) {
2915 + $tot_damage_dep += $tmpopr;
2916 + }
2917 + // VBO 1.11 - always calculate the amount of tax no matter if this is already a tax or a fee
2918 + if ($tmpopr == $realcost) {
2919 + $opt_minus_iva = VikBooking::sayOptionalsMinusIva($realcost, $opt['idiva']);
2920 + $tot_taxes += ($realcost - $opt_minus_iva);
2921 + } else {
2922 + $tot_taxes += ($tmpopr - $realcost);
2923 + }
2924 + //
2925 + $isdue += $tmpopr;
2926 + }
2927 + }
2928 + } else {
2929 + $tmpvar = VikRequest::getString('optid'.$num.$opt['id'], '', 'request');
2930 + if (is_array($tmpvar)) {
2931 + // prevent errors for unexpected option configuration, probably missing age intervals
2932 + continue;
2933 + }
2934 + $tmp_room_cost = 0;
2935 + // options forced per child fix, no age intervals, like children tourist taxes
2936 + $forcedquan = 1;
2937 + $forceperday = false;
2938 + $forceperchild = false;
2939 + if (intval($opt['forcesel']) == 1 && strlen($opt['forceval']) > 0 && strlen($tmpvar) > 0) {
2940 + $forceparts = explode("-", $opt['forceval']);
2941 + $forcedquan = intval($forceparts[0]);
2942 + $forceperday = intval($forceparts[1]) == 1 ? true : false;
2943 + $forceperchild = intval($forceparts[2]) == 1 ? true : false;
2944 + $tmpvar = $forcedquan;
2945 + $tmpvar = $forceperchild === true && array_key_exists($num, $arrpeople) && array_key_exists('children', $arrpeople[$num]) ? ($tmpvar * $arrpeople[$num]['children']) : $tmpvar;
2946 + }
2947 + //
2948 + if (!empty($tmpvar)) {
2949 + $wop .= $opt['id'].":".$tmpvar.";";
2950 + // options percentage cost of the room total fee
2951 + if ($is_package !== true && array_key_exists($num, $tars)) {
2952 + // type of price
2953 + $tmp_room_cost = $tars[$num][0]['cost'];
2954 + $deftar_basecosts = $tars[$num][0]['cost'];
2955 + } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
2956 + // package
2957 + $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2958 + $deftar_basecosts = $cust_costs[$num]['cust_cost'];
2959 + } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
2960 + // custom rate + custom tax rate
2961 + $tmp_room_cost = $cust_costs[$num]['cust_cost'];
2962 + $deftar_basecosts = $cust_costs[$num]['cust_cost'];
2963 + }
2964 + $opt['cost'] = (int)$opt['pcentroom'] ? ($deftar_basecosts * $opt['cost'] / 100) : $opt['cost'];
2965 + //
2966 + $realcost = (intval($opt['perday']) == 1 ? ($opt['cost'] * $room_nights * $tmpvar) : ($opt['cost'] * $tmpvar));
2967 + if (!empty($opt['maxprice']) && $opt['maxprice'] > 0 && $realcost > $opt['maxprice']) {
2968 + $realcost = $opt['maxprice'];
2969 + if (intval($opt['hmany']) == 1 && intval($tmpvar) > 1) {
2970 + $realcost = $opt['maxprice'] * $tmpvar;
2971 + }
2972 + }
2973 + if ($opt['perperson'] == 1) {
2974 + $num_adults = array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num]) ? $arrpeople[$num]['adults'] : 1;
2975 + $realcost = $realcost * $num_adults;
2976 + }
2977 +
2978 + /**
2979 + * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
2980 + *
2981 + * @since 1.17.7 (J) - 1.7.7 (WP)
2982 + */
2983 + $custom_calc_booking = array_merge($ord, ['days' => $room_nights]);
2984 + $custom_calc_booking_room = array_merge($or, ($arrpeople[$num] ?? []), ($tmp_room_cost ? ['room_cost' => $tmp_room_cost] : []));
2985 + $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$opt, $custom_calc_booking, $custom_calc_booking_room]);
2986 + if ($custom_calculation) {
2987 + $realcost = (float) $custom_calculation[0];
2988 + }
2989 +
2990 + $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $opt['idiva']);
2991 + if ($opt['is_citytax'] == 1) {
2992 + $tot_city_taxes += $tmpopr;
2993 + } elseif ($opt['is_fee'] == 1) {
2994 + $tot_fees += $tmpopr;
2995 + } elseif ($opt_params['damagedep'] ?? 0) {
2996 + $tot_damage_dep += $tmpopr;
2997 + }
2998 + // VBO 1.11 - always calculate the amount of tax no matter if this is already a tax or a fee
2999 + if ($tmpopr == $realcost) {
3000 + $opt_minus_iva = VikBooking::sayOptionalsMinusIva($realcost, $opt['idiva']);
3001 + $tot_taxes += ($realcost - $opt_minus_iva);
3002 + } else {
3003 + $tot_taxes += ($tmpopr - $realcost);
3004 + }
3005 + //
3006 + $isdue += $tmpopr;
3007 + }
3008 + }
3009 + }
3010 +
3011 + $upd_fields = array();
3012 + if ($is_package !== true && array_key_exists($num, $tars)) {
3013 + // type of price
3014 + $upd_fields[] = "`idtar`='".$tars[$num][0]['id']."'";
3015 + $upd_fields[] = "`cust_cost`=NULL";
3016 + $upd_fields[] = "`cust_idiva`=NULL";
3017 + $upd_fields[] = "`cust_cpolicy_id`=NULL";
3018 + $upd_fields[] = "`room_cost`=".(array_key_exists($num, $rooms_costs_map) ? $dbo->quote($rooms_costs_map[$num]) : "NULL");
3019 + } elseif ($is_package === true && array_key_exists($num, $cust_costs)) {
3020 + // packages do not update name or cost, just set again the same package ID to avoid risks of empty upd_fields to update
3021 + $upd_fields[] = "`idtar`=NULL";
3022 + $upd_fields[] = "`pkg_id`='".$cust_costs[$num]['pkgid']."'";
3023 + $upd_fields[] = "`cust_cost`='".$cust_costs[$num]['cust_cost']."'";
3024 + $upd_fields[] = "`cust_idiva`='".$cust_costs[$num]['aliq']."'";
3025 + $upd_fields[] = "`cust_cpolicy_id`='" . (int) ($cust_costs[$num]['cust_cpolicy_id'] ?? 0) . "'";
3026 + $upd_fields[] = "`room_cost`=NULL";
3027 + } elseif (array_key_exists($num, $cust_costs) && array_key_exists('cust_cost', $cust_costs[$num])) {
3028 + // custom rate + custom tax rate
3029 + $upd_fields[] = "`idtar`=NULL";
3030 + $upd_fields[] = "`cust_cost`='".$cust_costs[$num]['cust_cost']."'";
3031 + $upd_fields[] = "`cust_idiva`='".$cust_costs[$num]['aliq']."'";
3032 + $upd_fields[] = "`cust_cpolicy_id`='" . (int) ($cust_costs[$num]['cust_cpolicy_id'] ?? 0) . "'";
3033 + $upd_fields[] = "`room_cost`=NULL";
3034 + // inject new room price
3035 + $ordersrooms[$kor]['modified_price'] = $cust_costs[$num]['cust_cost'];
3036 + }
3037 + if ($toptionals) {
3038 + $upd_fields[] = "`optionals`='".$wop."'";
3039 + }
3040 + if (!empty($pt_first_name) || !empty($pt_last_name)) {
3041 + $upd_fields[] = "`t_first_name`=".$dbo->quote($pt_first_name);
3042 + $upd_fields[] = "`t_last_name`=".$dbo->quote($pt_last_name);
3043 + }
3044 + if (array_key_exists($num, $arrpeople) && array_key_exists('adults', $arrpeople[$num])) {
3045 + $upd_fields[] = "`adults`=".intval($arrpeople[$num]['adults']);
3046 + $upd_fields[] = "`children`=".intval($arrpeople[$num]['children']);
3047 + if (isset($arrpeople[$num]['pets'])) {
3048 + $upd_fields[] = "`pets`=" . $arrpeople[$num]['pets'];
3049 + }
3050 + }
3051 +
3052 + /**
3053 + * Meal plans at room-reservation level.
3054 + *
3055 + * @since 1.16.1 (J) - 1.6.1 (WP)
3056 + */
3057 + $pmealplans = VikRequest::getVar('mealplan' . $num, []);
3058 + $upd_fields[] = "`meals`=" . ($pmealplans ? $dbo->q(json_encode($pmealplans)) : 'NULL');
3059 +
3060 + // calculate the extra costs and increase taxes + isdue
3061 + $extracosts_arr = array();
3062 + if (count($pextracn) && isset($pextracn[$num]) && count($pextracn[$num])) {
3063 + foreach ($pextracn[$num] as $eck => $ecn) {
3064 + if ($ecn && array_key_exists($eck, $pextracc[$num]) && is_numeric($pextracc[$num][$eck])) {
3065 + $ecidtax = array_key_exists($eck, $pextractx[$num]) && intval($pextractx[$num][$eck]) > 0 ? (int)$pextractx[$num][$eck] : '';
3066 + $extracosts_arr[] = array(
3067 + 'name' => $ecn,
3068 + 'cost' => (float)$pextracc[$num][$eck],
3069 + 'idtax' => $ecidtax,
3070 + 'type' => isset($pextractype[$num][$eck]) ? $pextractype[$num][$eck] : '',
3071 + 'fk' => isset($pextracfk[$num][$eck]) ? (string)$pextracfk[$num][$eck] : '',
3072 + 'data' => isset($pextracdata[$num][$eck]) ? json_decode($pextracdata[$num][$eck]) : null,
3073 + );
3074 + $ecplustax = !empty($ecidtax) ? VikBooking::sayOptionalsPlusIva((float)$pextracc[$num][$eck], $ecidtax) : (float)$pextracc[$num][$eck];
3075 + $ecminustax = !empty($ecidtax) ? VikBooking::sayOptionalsMinusIva((float)$pextracc[$num][$eck], $ecidtax) : (float)$pextracc[$num][$eck];
3076 + $ectottax = (float)$pextracc[$num][$eck] - $ecminustax;
3077 + $isdue += $ecplustax;
3078 + $tot_taxes += $ectottax;
3079 + }
3080 + }
3081 + }
3082 +
3083 + if ($extracosts_arr) {
3084 + $upd_fields[] = "`extracosts`=".$dbo->quote(json_encode($extracosts_arr));
3085 + } else {
3086 + $upd_fields[] = "`extracosts`=NULL";
3087 + }
3088 +
3089 + if ($upd_fields) {
3090 + $q = "UPDATE `#__vikbooking_ordersrooms` SET ".implode(', ', $upd_fields)." WHERE `idorder`=".$ord['id']." AND `idroom`='".$or['idroom']."' AND `id`='".$or['id']."';";
3091 + $dbo->setQuery($q);
3092 + $dbo->execute();
3093 + }
3094 + }
3095 +
3096 + // update split stay transient record if not confirmed booking
3097 + if ($ord['split_stay'] && $ord['status'] != 'confirmed' && !empty($room_stay_dates) && !empty($split_stay_data)) {
3098 + /**
3099 + * Important: if no rates have been selected for all rooms, we won't enter this inner statement.
3100 + * It is necessary to select a rate plan for each room in order to update the split stay data.
3101 + */
3102 + $new_room_stay_dates = [];
3103 + foreach ($room_stay_dates as $kor => $room_stay_info) {
3104 + // clone the current information
3105 + $clean_room_stay_info = $room_stay_info;
3106 + // set new stay values
3107 + if (!empty($clean_room_stay_info['checkin_ts'])) {
3108 + $clean_room_stay_info['checkin_ts'] = $clean_room_stay_info['new_checkin'];
3109 + $clean_room_stay_info['checkout_ts'] = $clean_room_stay_info['new_checkout'];
3110 + } else {
3111 + $clean_room_stay_info['checkin'] = $clean_room_stay_info['new_checkin'];
3112 + $clean_room_stay_info['checkout'] = $clean_room_stay_info['new_checkout'];
3113 + }
3114 + $clean_room_stay_info['nights'] = $clean_room_stay_info['new_nights'];
3115 + // clean up unnecessary keys
3116 + unset($clean_room_stay_info['new_checkin'], $clean_room_stay_info['new_checkout'], $clean_room_stay_info['new_nights']);
3117 + // push new array info
3118 + $new_room_stay_dates[$kor] = $clean_room_stay_info;
3119 + }
3120 + // update configuration record
3121 + VBOFactory::getConfig()->set('split_stay_' . $ord['id'], json_encode($new_room_stay_dates));
3122 + }
3123 +
3124 + // make sure to re-apply the discount with the coupon code
3125 + if ($ord['coupon']) {
3126 + $expcoupon = explode(";", $ord['coupon']);
3127 + $isdue -= $expcoupon[1];
3128 + }
3129 +
3130 + // make sure to apply any previously refunded amount
3131 + if ($ord['refund'] > 0) {
3132 + $isdue -= $ord['refund'];
3133 + }
3134 +
3135 + // update totals
3136 + $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'].";";
3137 + $dbo->setQuery($q);
3138 + $dbo->execute();
3139 + $upd_esit = JText::translate('VBORESRATESUPDATED');
3140 +
3141 + // Customer Booking
3142 + if ($ord['status'] == 'confirmed') {
3143 + $q = "SELECT `idcustomer` FROM `#__vikbooking_customers_orders` WHERE `idorder`=".$ord['id'].";";
3144 + $dbo->setQuery($q);
3145 + $customer_id = $dbo->loadResult();
3146 + if ($customer_id) {
3147 + $cpin = VikBooking::getCPinIstance();
3148 + $cpin->is_admin = true;
3149 + $cpin->updateBookingCommissions($ord['id'], $customer_id);
3150 + }
3151 + }
3152 +
3153 + /**
3154 + * Check for any OTA reporting action.
3155 + *
3156 + * @since 1.16.8 (J) - 1.6.8 (WP)
3157 + */
3158 + if (class_exists('VCMOtaReporting') && VCMOtaReporting::getInstance($ord)->stayChangeAllowed()) {
3159 + // check if an OTA reporting action was selected
3160 + $ota_stay_change_data = [];
3161 + $ota_stay_change_all = $app->input->getInt('ota_stay_change_all', 0);
3162 + foreach ($ordersrooms as $kor => $or) {
3163 + $ota_stay_change_room = [];
3164 + if ($ota_stay_change_all) {
3165 + // set room data for stay change
3166 + $ota_stay_change_room = [
3167 + 'idroom' => $or['idroom'],
3168 + 'checkin' => date('Y-m-d', $first),
3169 + 'checkout' => date('Y-m-d', $second),
3170 + ];
3171 + if (isset($or['modified_price'])) {
3172 + $ota_stay_change_room['price'] = $or['modified_price'];
3173 + }
3174 + } elseif ($app->input->getInt('ota_stay_change_room_' . $kor, 0) && !empty($or['modified_checkin']) && !empty($or['modified_checkout'])) {
3175 + // set room index data for stay change
3176 + $ota_stay_change_room = [
3177 + 'idroom' => $or['idroom'],
3178 + 'index' => $kor,
3179 + 'checkin' => date('Y-m-d', $or['modified_checkin']),
3180 + 'checkout' => date('Y-m-d', $or['modified_checkout']),
3181 + ];
3182 + if (isset($or['modified_price'])) {
3183 + $ota_stay_change_room['price'] = $or['modified_price'];
3184 + }
3185 + }
3186 + if ($ota_stay_change_room) {
3187 + // push room data for stay change
3188 + $ota_stay_change_data[] = $ota_stay_change_room;
3189 + }
3190 + }
3191 +
3192 + if ($ota_stay_change_data) {
3193 + // notify the OTA through Vik Channel Manager
3194 + $ota_reporting = VCMOtaReporting::getInstance();
3195 + $ota_result = $ota_reporting->notifyStayChange($ota_stay_change_data);
3196 + if (!$ota_result) {
3197 + // enqueue error message
3198 + $app->enqueueMessage($ota_reporting->getError(), 'error');
3199 + }
3200 + }
3201 + }
3202 + }
3203 +
3204 + // Booking History
3205 + $history_descr = "({$user->name}) " . VikBooking::getLogBookingModification($ord, $room_stay_dates);
3206 + if (!$opertwounits && $forcebooking) {
3207 + $history_descr .= "\n" . JText::translate('VBO_FORCED_BOOKDATES');
3208 + }
3209 + VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', $history_descr);
3210 +
3211 + // enqueue result message
3212 + $app->enqueueMessage($upd_esit);
3213 + } else {
3214 + VikError::raiseWarning('', JText::translate('VBROOMNOTRIT')." ".date($df.' H:i', $first)." ".JText::translate('VBROOMNOTCONSTO')." ".date($df.' H:i', $second));
3215 + $allow_force = 1;
3216 + $app->enqueueMessage(JText::translate('VBO_BOOKING_SHOULDFORCE'), 'notice');
3217 + }
3218 +
3219 + if ($callback == 'geninvoices') {
3220 + $app->redirect("index.php?option=com_vikbooking&task=orders&cid[]=".$ord['id']."&confirmgen=1");
3221 + } else {
3222 + $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" : ""));
3223 + }
3224 + }
3225 +
3226 + public function removebusy()
3227 + {
3228 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
3229 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
3230 + }
3231 +
3232 + $dbo = JFactory::getDbo();
3233 + $app = JFactory::getApplication();
3234 +
3235 + $user = JFactory::getUser();
3236 + $config = VBOFactory::getConfig();
3237 +
3238 + $prev_conf_ids = [];
3239 + $pidorder = VikRequest::getInt('idorder', 0, 'request');
3240 + $pgoto = VikRequest::getString('goto', '', 'request');
3241 +
3242 + $purged = false;
3243 +
3244 + $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder;
3245 + $dbo->setQuery($q, 0, 1);
3246 + $row = $dbo->loadAssoc();
3247 +
3248 + // check for any cancellation constraints
3249 + $canc_denied = false;
3250 + if ($row && class_exists('VCMFeesCancellation')) {
3251 + // let VCM detect if there are any constraints for the cancellation
3252 + $canc_denied = VCMFeesCancellation::getInstance($row, $anew = true)->isBookingConstrained();
3253 + if ($canc_denied) {
3254 + // set error message
3255 + $canc_deny_error = VCMFeesCancellation::getInstance()->getError();
3256 + if ($canc_deny_error) {
3257 + $app->enqueueMessage($canc_deny_error, 'error');
3258 + }
3259 + }
3260 + }
3261 +
3262 + if ($row && !$canc_denied) {
3263 + // set status to cancelled
3264 + if ($row['status'] != 'cancelled') {
3265 + $q = "UPDATE `#__vikbooking_orders` SET `status`='cancelled' WHERE `id`=".(int)$row['id'].";";
3266 + $dbo->setQuery($q);
3267 + $dbo->execute();
3268 + $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($row['id']) . ";";
3269 + $dbo->setQuery($q);
3270 + $dbo->execute();
3271 + if ($row['status'] == 'confirmed') {
3272 + $prev_conf_ids[] = $row['id'];
3273 + }
3274 + // Booking History
3275 + VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('CB', "({$user->name})");
3276 + }
3277 +
3278 + /**
3279 + * In case of pending bookings being cancelled, schedule the release through VCM.
3280 + *
3281 + * @since 1.18.8 (J) - 1.8.8 (WP)
3282 + */
3283 + if ($row['status'] == 'standby' && method_exists('VCMRequestAvailability', 'setForRelease')) {
3284 + // let the CM schedule the release of the involved and unconfirmed booking IDs, if needed
3285 + VCMRequestAvailability::getInstance()->setForRelease([$row['id']]);
3286 + }
3287 +
3288 + // free records up
3289 + $q = "SELECT * FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
3290 + $dbo->setQuery($q);
3291 + $ordbusy = $dbo->loadAssocList();
3292 + if ($ordbusy) {
3293 + foreach ($ordbusy as $ob) {
3294 + $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`='".$ob['idbusy']."';";
3295 + $dbo->setQuery($q);
3296 + $dbo->execute();
3297 + }
3298 + }
3299 +
3300 + $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
3301 + $dbo->setQuery($q);
3302 + $dbo->execute();
3303 +
3304 + // check for purge removal
3305 + if ($row['status'] == 'cancelled') {
3306 + $q = "DELETE FROM `#__vikbooking_customers_orders` WHERE `idorder`=" . intval($row['id']) . ";";
3307 + $dbo->setQuery($q);
3308 + $dbo->execute();
3309 + $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `idorder`=".(int)$row['id'].";";
3310 + $dbo->setQuery($q);
3311 + $dbo->execute();
3312 + $q = "DELETE FROM `#__vikbooking_orderhistory` WHERE `idorder`=".(int)$row['id'].";";
3313 + $dbo->setQuery($q);
3314 + $dbo->execute();
3315 + $q = "DELETE FROM `#__vikbooking_orders` WHERE `id`=".(int)$row['id'].";";
3316 + $dbo->setQuery($q);
3317 + $dbo->execute();
3318 + // in case of split stay booking, remove the transient
3319 + if ($row['split_stay']) {
3320 + $config->remove('split_stay_' . $row['id']);
3321 + }
3322 + // turn flag on
3323 + $purged = true;
3324 + }
3325 +
3326 + // enqueue message
3327 + $app->enqueueMessage(JText::translate('VBMESSDELBUSY'));
3328 + }
3329 +
3330 + if ($prev_conf_ids) {
3331 + $prev_conf_ids_str = '';
3332 + foreach ($prev_conf_ids as $prev_id) {
3333 + $prev_conf_ids_str .= '&cid[]='.$prev_id;
3334 + }
3335 + //Invoke Channel Manager
3336 + $vcm_autosync = VikBooking::vcmAutoUpdate();
3337 + if ($vcm_autosync > 0) {
3338 + $vcm_obj = VikBooking::getVcmInvoker();
3339 + $vcm_obj->setOids($prev_conf_ids)->setSyncType('cancel');
3340 + $sync_result = $vcm_obj->doSync();
3341 + if ($sync_result === false) {
3342 + $vcm_err = $vcm_obj->getError();
3343 + VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
3344 + }
3345 + } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
3346 + $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');
3347 + VikError::raiseNotice('', JText::translate('VBCHANNELMANAGERINVOKEASK').' <button type="button" class="btn btn-primary" onclick="document.location.href=\''.$vcm_sync_url.'\';">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button>');
3348 + }
3349 + //
3350 + }
3351 +
3352 + if ($pgoto == 'overv') {
3353 + $app->redirect("index.php?option=com_vikbooking&task=overv");
3354 + } elseif (!$purged) {
3355 + $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $pidorder);
3356 + } else {
3357 + $app->redirect("index.php?option=com_vikbooking&task=orders");
3358 + }
3359 +
3360 + $app->close();
3361 + }
3362 +
3363 + public function unlockrecords()
3364 + {
3365 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
3366 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
3367 + }
3368 +
3369 + $ids = VikRequest::getVar('cid', array(0));
3370 + if (@count($ids)) {
3371 + $dbo = JFactory::getDBO();
3372 + foreach ($ids as $d) {
3373 + $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `id`=".$dbo->quote($d).";";
3374 + $dbo->setQuery($q);
3375 + $dbo->execute();
3376 + }
3377 + }
3378 + $mainframe = JFactory::getApplication();
3379 + $mainframe->redirect("index.php?option=com_vikbooking");
3380 + }
3381 +
3382 + public function sortoption() {
3383 + if (!JSession::checkToken('get')) {
3384 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3385 + }
3386 + $sortid = VikRequest::getVar('cid', array(0));
3387 + $pmode = VikRequest::getString('mode', '', 'request');
3388 + $dbo = JFactory::getDBO();
3389 + $mainframe = JFactory::getApplication();
3390 + if (!empty($pmode)) {
3391 + $q = "SELECT `id`,`ordering` FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
3392 + $dbo->setQuery($q);
3393 + $dbo->execute();
3394 + $totr = $dbo->getNumRows();
3395 + if ($totr > 1) {
3396 + $data = $dbo->loadAssocList();
3397 + if ($pmode == "up") {
3398 + foreach ($data as $v) {
3399 + if ($v['id'] == $sortid[0]) {
3400 + $y = $v['ordering'];
3401 + }
3402 + }
3403 + if ($y && $y > 1) {
3404 + $vik = $y - 1;
3405 + $found = false;
3406 + foreach ($data as $v) {
3407 + if (intval($v['ordering']) == intval($vik)) {
3408 + $found = true;
3409 + $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3410 + $dbo->setQuery($q);
3411 + $dbo->execute();
3412 + $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3413 + $dbo->setQuery($q);
3414 + $dbo->execute();
3415 + break;
3416 + }
3417 + }
3418 + if (!$found) {
3419 + $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3420 + $dbo->setQuery($q);
3421 + $dbo->execute();
3422 + }
3423 + }
3424 + } elseif ($pmode == "down") {
3425 + foreach ($data as $v) {
3426 + if ($v['id'] == $sortid[0]) {
3427 + $y = $v['ordering'];
3428 + }
3429 + }
3430 + if ($y) {
3431 + $vik = $y + 1;
3432 + $found = false;
3433 + foreach ($data as $v) {
3434 + if (intval($v['ordering']) == intval($vik)) {
3435 + $found = true;
3436 + $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3437 + $dbo->setQuery($q);
3438 + $dbo->execute();
3439 + $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3440 + $dbo->setQuery($q);
3441 + $dbo->execute();
3442 + break;
3443 + }
3444 + }
3445 + if (!$found) {
3446 + $q = "UPDATE `#__vikbooking_optionals` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3447 + $dbo->setQuery($q);
3448 + $dbo->execute();
3449 + }
3450 + }
3451 + }
3452 + }
3453 + $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
3454 + } else {
3455 + $mainframe->redirect("index.php?option=com_vikbooking");
3456 + }
3457 + }
3458 +
3459 + public function sortpayment() {
3460 + if (!JSession::checkToken('get')) {
3461 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3462 + }
3463 + $cid = VikRequest::getVar('cid', array(0));
3464 + $sortid = $cid[0];
3465 + $dbo = JFactory::getDBO();
3466 + $mainframe = JFactory::getApplication();
3467 + $pmode = VikRequest::getString('mode', '', 'request');
3468 + if (!empty($pmode) && !empty($sortid)) {
3469 + $q = "SELECT `id`,`ordering` FROM `#__vikbooking_gpayments` ORDER BY `#__vikbooking_gpayments`.`ordering` ASC;";
3470 + $dbo->setQuery($q);
3471 + $dbo->execute();
3472 + $totr=$dbo->getNumRows();
3473 + if ($totr > 1) {
3474 + $data = $dbo->loadAssocList();
3475 + if ($pmode == "up") {
3476 + foreach ($data as $v) {
3477 + if ($v['id'] == $sortid) {
3478 + $y = $v['ordering'];
3479 + }
3480 + }
3481 + if ($y && $y > 1) {
3482 + $vik = $y - 1;
3483 + $found = false;
3484 + foreach ($data as $v) {
3485 + if (intval($v['ordering']) == intval($vik)) {
3486 + $found = true;
3487 + $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3488 + $dbo->setQuery($q);
3489 + $dbo->execute();
3490 + $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3491 + $dbo->setQuery($q);
3492 + $dbo->execute();
3493 + break;
3494 + }
3495 + }
3496 + if (!$found) {
3497 + $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3498 + $dbo->setQuery($q);
3499 + $dbo->execute();
3500 + }
3501 + }
3502 + } elseif ($pmode == "down") {
3503 + foreach ($data as $v) {
3504 + if ($v['id'] == $sortid) {
3505 + $y = $v['ordering'];
3506 + }
3507 + }
3508 + if ($y) {
3509 + $vik = $y + 1;
3510 + $found = false;
3511 + foreach ($data as $v) {
3512 + if (intval($v['ordering']) == intval($vik)) {
3513 + $found=true;
3514 + $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3515 + $dbo->setQuery($q);
3516 + $dbo->execute();
3517 + $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3518 + $dbo->setQuery($q);
3519 + $dbo->execute();
3520 + break;
3521 + }
3522 + }
3523 + if (!$found) {
3524 + $q = "UPDATE `#__vikbooking_gpayments` SET `ordering`='".$vik."' WHERE `id`='".$sortid."' LIMIT 1;";
3525 + $dbo->setQuery($q);
3526 + $dbo->execute();
3527 + }
3528 + }
3529 + }
3530 + }
3531 + $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
3532 + } else {
3533 + $mainframe->redirect("index.php?option=com_vikbooking");
3534 + }
3535 + }
3536 +
3537 + public function sortcarat() {
3538 + if (!JSession::checkToken('get')) {
3539 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
3540 + }
3541 + $sortid = VikRequest::getVar('cid', array(0));
3542 + $pmode = VikRequest::getString('mode', '', 'request');
3543 + $dbo = JFactory::getDBO();
3544 + $mainframe = JFactory::getApplication();
3545 + if (!empty($pmode)) {
3546 + $q = "SELECT `id`,`ordering` FROM `#__vikbooking_characteristics` ORDER BY `#__vikbooking_characteristics`.`ordering` ASC;";
3547 + $dbo->setQuery($q);
3548 + $dbo->execute();
3549 + $totr = $dbo->getNumRows();
3550 + if ($totr > 1) {
3551 + $data = $dbo->loadAssocList();
3552 + if ($pmode == "up") {
3553 + foreach ($data as $v) {
3554 + if ($v['id'] == $sortid[0]) {
3555 + $y = $v['ordering'];
3556 + }
3557 + }
3558 + if ($y && $y > 1) {
3559 + $vik = $y - 1;
3560 + $found = false;
3561 + foreach ($data as $v) {
3562 + if (intval($v['ordering']) == intval($vik)) {
3563 + $found = true;
3564 + $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3565 + $dbo->setQuery($q);
3566 + $dbo->execute();
3567 + $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3568 + $dbo->setQuery($q);
3569 + $dbo->execute();
3570 + break;
3571 + }
3572 + }
3573 + if (!$found) {
3574 + $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3575 + $dbo->setQuery($q);
3576 + $dbo->execute();
3577 + }
3578 + }
3579 + } elseif ($pmode == "down") {
3580 + foreach ($data as $v) {
3581 + if ($v['id'] == $sortid[0]) {
3582 + $y = $v['ordering'];
3583 + }
3584 + }
3585 + if ($y) {
3586 + $vik = $y + 1;
3587 + $found = false;
3588 + foreach ($data as $v) {
3589 + if (intval($v['ordering']) == intval($vik)) {
3590 + $found = true;
3591 + $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
3592 + $dbo->setQuery($q);
3593 + $dbo->execute();
3594 + $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3595 + $dbo->setQuery($q);
3596 + $dbo->execute();
3597 + break;
3598 + }
3599 + }
3600 + if (!$found) {
3601 + $q = "UPDATE `#__vikbooking_characteristics` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
3602 + $dbo->setQuery($q);
3603 + $dbo->execute();
3604 + }
3605 + }
3606 + }
3607 + }
3608 + $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
3609 + } else {
3610 + $mainframe->redirect("index.php?option=com_vikbooking");
3611 + }
3612 + }
3613 +
3614 + public function resendordemail() {
3615 + $this->do_resendorderemail();
3616 + }
3617 +
3618 + public function sendcancordemail() {
3619 + $this->do_resendorderemail(true);
3620 + }
3621 +
3622 + private function do_resendorderemail($cancellation = false)
3623 + {
3624 + $dbo = JFactory::getDbo();
3625 + $app = JFactory::getApplication();
3626 + $vbo_tn = VikBooking::getTranslator();
3627 +
3628 + $cid = VikRequest::getVar('cid', array(0));
3629 + $oid = (int)$cid[0];
3630 +
3631 + $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $oid . ";";
3632 + $dbo->setQuery($q);
3633 + $dbo->execute();
3634 + if (!$dbo->getNumRows()) {
3635 + $app->redirect("index.php?option=com_vikbooking&task=orders");
3636 + $app->close();
3637 + }
3638 + $order = $dbo->loadAssoc();
3639 +
3640 + // check if the language in use is the same as the one used during the checkout
3641 + if (!empty($order['lang'])) {
3642 + $lang = JFactory::getLanguage();
3643 + if ($lang->getTag() != $order['lang']) {
3644 + $lang->load('com_vikbooking', (VBOPlatformDetection::isWordPress() ? VIKBOOKING_LANG : JPATH_ADMINISTRATOR), $order['lang'], true);
3645 + if (defined('_JEXEC') && !defined('ABSPATH')) {
3646 + $lang->load('joomla', JPATH_ADMINISTRATOR, $order['lang'], true);
3647 + }
3648 + }
3649 + if ($vbo_tn->getDefaultLang() != $order['lang']) {
3650 + // force the translation to start because contents should be translated
3651 + $vbo_tn::$force_tolang = $order['lang'];
3652 + }
3653 + }
3654 +
3655 + // availability helper
3656 + $av_helper = VikBooking::getAvailabilityInstance();
3657 +
3658 + /**
3659 + * Split stay reservation.
3660 + *
3661 + * @since 1.16.0 (J) - 1.6.0 (WP)
3662 + */
3663 + $room_stay_dates = [];
3664 + if ($order['split_stay']) {
3665 + if ($order['status'] == 'confirmed') {
3666 + $room_stay_dates = $av_helper->loadSplitStayBusyRecords($order['id']);
3667 + } else {
3668 + $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $order['id'], []);
3669 + }
3670 + // immediately count the number of nights of stay for each split room
3671 + foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
3672 + if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
3673 + // overwrite values for compatibility with non-confirmed bookings
3674 + $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
3675 + $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
3676 + }
3677 + $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
3678 + // overwrite the whole array
3679 + $room_stay_dates[$sps_r_k] = $sps_r_v;
3680 + }
3681 + }
3682 +
3683 + // load rooms booked
3684 + $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;";
3685 + $dbo->setQuery($q);
3686 + $dbo->execute();
3687 + $ordersrooms = $dbo->loadAssocList();
3688 + $vbo_tn->translateContents($ordersrooms, '#__vikbooking_rooms', array('id' => 'r_reference_id'));
3689 +
3690 + $ftitle = VikBooking::getFrontTitle();
3691 + $currencyname = VikBooking::getCurrencyName();
3692 +
3693 + $turnover_secs = VikBooking::getHoursRoomAvail() * 3600;
3694 + $realback = $turnover_secs + $order['checkout'];
3695 +
3696 + $rooms = array();
3697 + $tars = array();
3698 + $arrpeople = array();
3699 + $is_package = !empty($order['pkg']) ? true : false;
3700 + $nowts = time();
3701 + foreach ($ordersrooms as $kor => $or) {
3702 + $num = $kor + 1;
3703 + $rooms[$num] = $or;
3704 + $arrpeople[$num]['adults'] = $or['adults'];
3705 + $arrpeople[$num]['children'] = $or['children'];
3706 + if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3707 + // package or custom cost set from the back-end
3708 + continue;
3709 + }
3710 +
3711 + // determine the proper values for this room
3712 + $room_nights = $order['days'];
3713 + $room_checkin = $order['checkin'];
3714 + $room_checkout = $order['checkout'];
3715 + if ($order['split_stay'] && !empty($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) {
3716 + $room_nights = $room_stay_dates[$kor]['nights'];
3717 + $room_checkin = $room_stay_dates[$kor]['checkin'];
3718 + $room_checkout = $room_stay_dates[$kor]['checkout'];
3719 + }
3720 +
3721 + // load tariff
3722 + $q = "SELECT * FROM `#__vikbooking_dispcost` WHERE `id`=" . (int)$or['idtar'] . ";";
3723 + $dbo->setQuery($q);
3724 + $dbo->execute();
3725 + if ($dbo->getNumRows() > 0) {
3726 + $tar = $dbo->loadAssocList();
3727 + $tar = VikBooking::applySeasonsRoom($tar, $room_checkin, $room_checkout);
3728 +
3729 + // different usage
3730 + $tar = VBORoomHelper::getInstance()->applyOBPRules($tar, $or, $or['adults']);
3731 +
3732 + $tars[$num] = $tar[0];
3733 + } else {
3734 + VikError::raiseWarning('', JText::translate('VBERRNOFAREFOUND'));
3735 + }
3736 + }
3737 +
3738 + $secdiff = $order['checkout'] - $order['checkin'];
3739 + $daysdiff = $secdiff / 86400;
3740 + if (is_int($daysdiff)) {
3741 + if ($daysdiff < 1) {
3742 + $daysdiff = 1;
3743 + }
3744 + } else {
3745 + if ($daysdiff < 1) {
3746 + $daysdiff = 1;
3747 + } else {
3748 + $sum = floor($daysdiff) * 86400;
3749 + $newdiff = $secdiff - $sum;
3750 + $maxhmore = VikBooking::getHoursMoreRb() * 3600;
3751 + if ($maxhmore >= $newdiff) {
3752 + $daysdiff = floor($daysdiff);
3753 + } else {
3754 + $daysdiff = ceil($daysdiff);
3755 + }
3756 + }
3757 + }
3758 +
3759 + $isdue = 0;
3760 + $pricestr = array();
3761 + $optstr = array();
3762 + foreach ($ordersrooms as $kor => $or) {
3763 + $num = $kor + 1;
3764 +
3765 + // determine the proper values for this room
3766 + $room_nights = $order['days'];
3767 + $room_checkin = $order['checkin'];
3768 + $room_checkout = $order['checkout'];
3769 + if ($order['split_stay'] && !empty($room_stay_dates) && isset($room_stay_dates[$kor]) && $room_stay_dates[$kor]['idroom'] == $or['idroom']) {
3770 + $room_nights = $room_stay_dates[$kor]['nights'];
3771 + $room_checkin = $room_stay_dates[$kor]['checkin'];
3772 + $room_checkout = $room_stay_dates[$kor]['checkout'];
3773 + }
3774 +
3775 + if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3776 + // package cost or cust_cost may not be inclusive of taxes if prices tax included is off
3777 + $calctar = VikBooking::sayPackagePlusIva($or['cust_cost'], $or['cust_idiva']);
3778 + $isdue += $calctar;
3779 + $pricestr[$num] = (!empty($or['pkg_name']) ? $or['pkg_name'] : (!empty($or['otarplan']) ? ucwords($or['otarplan']) : JText::translate('VBOROOMCUSTRATEPLAN'))).": ".$calctar." ".$currencyname;
3780 + } elseif (array_key_exists($num, $tars) && is_array($tars[$num])) {
3781 + $display_rate = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3782 + $calctar = VikBooking::sayCostPlusIva($display_rate, $tars[$num]['idprice']);
3783 + $tars[$num]['calctar'] = $calctar;
3784 + $isdue += $calctar;
3785 + $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'] : "");
3786 + }
3787 + if (!empty($or['optionals'])) {
3788 + $stepo = explode(";", $or['optionals']);
3789 + foreach ($stepo as $roptkey => $oo) {
3790 + if (empty($oo)) {
3791 + continue;
3792 + }
3793 + $stept = explode(":", $oo);
3794 + $q = "SELECT * FROM `#__vikbooking_optionals` WHERE `id`=" . $dbo->quote($stept[0]) . ";";
3795 + $dbo->setQuery($q);
3796 + $dbo->execute();
3797 + if (!$dbo->getNumRows()) {
3798 + continue;
3799 + }
3800 + $actopt = $dbo->loadAssocList();
3801 + $vbo_tn->translateContents($actopt, '#__vikbooking_optionals', array(), array(), (!empty($order['lang']) ? $order['lang'] : null));
3802 + $chvar = '';
3803 + if (!empty($actopt[0]['ageintervals']) && $or['children'] > 0 && strstr($stept[1], '-') != false) {
3804 + $optagenames = VikBooking::getOptionIntervalsAges($actopt[0]['ageintervals']);
3805 + $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt[0]['ageintervals']);
3806 + $optageovrct = VikBooking::getOptionIntervalChildOverrides($actopt[0], $or['adults'], $or['children']);
3807 + $child_num = VikBooking::getRoomOptionChildNumber($or['optionals'], $actopt[0]['id'], $roptkey, $or['children']);
3808 + $optagecosts = VikBooking::getOptionIntervalsCosts(isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $actopt[0]['ageintervals']);
3809 + $agestept = explode('-', $stept[1]);
3810 + $stept[1] = $agestept[0];
3811 + $chvar = $agestept[1];
3812 + if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 1) {
3813 + //percentage value of the adults tariff
3814 + if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3815 + $optagecosts[($chvar - 1)] = $or['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
3816 + } else {
3817 + $display_rate = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3818 + $optagecosts[($chvar - 1)] = $display_rate * $optagecosts[($chvar - 1)] / 100;
3819 + }
3820 + } elseif (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] == 2) {
3821 + //VBO 1.10 - percentage value of room base cost
3822 + if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3823 + $optagecosts[($chvar - 1)] = $or['cust_cost'] * $optagecosts[($chvar - 1)] / 100;
3824 + } else {
3825 + $display_rate = isset($tars[$num]['room_base_cost']) ? $tars[$num]['room_base_cost'] : (!empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost']);
3826 + $optagecosts[($chvar - 1)] = $display_rate * $optagecosts[($chvar - 1)] / 100;
3827 + }
3828 + }
3829 + $actopt[0]['chageintv'] = $chvar;
3830 + $actopt[0]['name'] .= ' ('.$optagenames[($chvar - 1)].')';
3831 + $actopt[0]['quan'] = $stept[1];
3832 + $realcost = (intval($actopt[0]['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $room_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
3833 + } else {
3834 + $actopt[0]['quan'] = $stept[1];
3835 + // VBO 1.11 - options percentage cost of the room total fee
3836 + if ($is_package === true || (!empty($or['cust_cost']) && $or['cust_cost'] > 0.00)) {
3837 + $deftar_basecosts = $or['cust_cost'];
3838 + } else {
3839 + $deftar_basecosts = !empty($or['room_cost']) ? $or['room_cost'] : $tars[$num]['cost'];
3840 + }
3841 + $actopt[0]['cost'] = (int)$actopt[0]['pcentroom'] ? ($deftar_basecosts * $actopt[0]['cost'] / 100) : $actopt[0]['cost'];
3842 + //
3843 + $realcost = (intval($actopt[0]['perday']) == 1 ? ($actopt[0]['cost'] * $room_nights * $stept[1]) : ($actopt[0]['cost'] * $stept[1]));
3844 + }
3845 + if (!empty($actopt[0]['maxprice']) && $actopt[0]['maxprice'] > 0 && $realcost > $actopt[0]['maxprice']) {
3846 + $realcost = $actopt[0]['maxprice'];
3847 + if (intval($actopt[0]['hmany']) == 1 && intval($stept[1]) > 1) {
3848 + $realcost = $actopt[0]['maxprice'] * $stept[1];
3849 + }
3850 + }
3851 + if ($actopt[0]['perperson'] == 1) {
3852 + $realcost = $realcost * $or['adults'];
3853 + }
3854 +
3855 + /**
3856 + * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
3857 + *
3858 + * @since 1.17.7 (J) - 1.7.7 (WP)
3859 + */
3860 + $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt[0], $order, $or]);
3861 + if ($custom_calculation) {
3862 + $realcost = (float) $custom_calculation[0];
3863 + }
3864 +
3865 + $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt[0]['idiva']);
3866 + $isdue += $tmpopr;
3867 + $optstr[$num][] = ($stept[1] > 1 ? $stept[1] . " " : "") . $actopt[0]['name'] . ": " . $tmpopr . " " . $currencyname . "\n";
3868 + }
3869 + }
3870 +
3871 + // custom extra costs
3872 + if (!empty($or['extracosts'])) {
3873 + $cur_extra_costs = json_decode($or['extracosts'], true);
3874 + foreach ($cur_extra_costs as $eck => $ecv) {
3875 + $ecplustax = !empty($ecv['idtax']) ? VikBooking::sayOptionalsPlusIva($ecv['cost'], $ecv['idtax']) : $ecv['cost'];
3876 + $isdue += $ecplustax;
3877 + $optstr[$num][] = $ecv['name'] . ": " . $ecplustax . " " . $currencyname."\n";
3878 + }
3879 + }
3880 + }
3881 +
3882 + // coupon
3883 + $usedcoupon = false;
3884 + $origisdue = $isdue;
3885 + if (strlen($order['coupon']) > 0) {
3886 + $usedcoupon = true;
3887 + $expcoupon = explode(";", $order['coupon']);
3888 + $isdue = $isdue - $expcoupon[1];
3889 + }
3890 +
3891 + // make sure to apply any previously refunded amount
3892 + if ($order['refund'] > 0) {
3893 + $isdue -= $order['refund'];
3894 + }
3895 +
3896 + // ConfirmationNumber
3897 + $confirmnumber = $order['confirmnumber'];
3898 +
3899 + $esit_mess = JText::sprintf('VBORDEREMAILRESENT', $order['custmail']);
3900 + $status_str = JText::translate('VBCOMPLETED');
3901 + if ($cancellation) {
3902 + $confirmnumber = '';
3903 + $esit_mess = JText::sprintf('VBCANCORDEREMAILSENT', $order['custmail']);
3904 + $status_str = JText::translate('VBCANCELLED');
3905 + } elseif ($order['status'] == 'standby') {
3906 + $confirmnumber = '';
3907 + $status_str = JText::translate('VBWAITINGFORPAYMENT');
3908 + }
3909 + $app->enqueueMessage($esit_mess);
3910 +
3911 + // force the original total amount if rates have changed
3912 + if (number_format($isdue, 2) != number_format($order['total'], 2)) {
3913 + $isdue = $order['total'];
3914 + }
3915 +
3916 + // send email notification to guest (by ignoring the configuration settings)
3917 + VikBooking::sendBookingEmail($order['id'], ['guest'], $send = true, $no_config = true);
3918 +
3919 + if ($cancellation) {
3920 + /**
3921 + * If "send cancellation email", we log the event in the history.
3922 + *
3923 + * @since 1.14 (J) - 1.4.0 (WP)
3924 + */
3925 + VikBooking::getBookingHistoryInstance()->setBid($order['id'])->store('EC');
3926 + } else {
3927 + /**
3928 + * Instead, we store an event log to remind that the email was re-sent to the guest
3929 + *
3930 + * @since 1.16.3 (J) - 1.6.3 (WP)
3931 + */
3932 + VikBooking::getBookingHistoryInstance()->setBid($order['id'])->store('ER', $esit_mess);
3933 + }
3934 +
3935 + $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$oid);
3936 + $app->close();
3937 + }
3938 +
3939 + public function setordconfirmed()
3940 + {
3941 + $app = JFactory::getApplication();
3942 +
3943 + // the booking ID to confirm
3944 + $cid = VikRequest::getVar('cid', array(0));
3945 + $oid = (int) $cid[0];
3946 +
3947 + // notify the customer unless it was a re-confirmation
3948 + $pskip = $app->input->getInt('skip_notification', 0);
3949 +
3950 + // access the reservation model
3951 + $model = VBOModelReservation::getInstance();
3952 +
3953 + // set the booking to confirmed
3954 + $confirmed = $model->setConfirmed([
3955 + 'booking_id' => $oid,
3956 + 'notify' => (bool) (!$pskip),
3957 + ]);
3958 +
3959 + if (!$confirmed) {
3960 + $error = $model->getError();
3961 + if (!is_string($error) || !$error) {
3962 + $error = 'Could not confirm the reservation';
3963 + }
3964 +
3965 + // enqueue error message
3966 + $app->enqueueMessage($error, 'error');
3967 + } else {
3968 + // enqueue success message
3969 + $app->enqueueMessage(JText::translate('VBORDERSETASCONF'));
3970 + }
3971 +
3972 + // redirect
3973 + $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $oid);
3974 + $app->close();
3975 + }
3976 +
3977 + public function payments() {
3978 + VikBookingHelper::printHeader("14");
3979 +
3980 + VikRequest::setVar('view', VikRequest::getCmd('view', 'payments'));
3981 +
3982 + parent::display();
3983 +
3984 + if (VikBooking::showFooter()) {
3985 + VikBookingHelper::printFooter();
3986 + }
3987 + }
3988 +
3989 + public function newpayment() {
3990 + VikBookingHelper::printHeader("14");
3991 +
3992 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managepayment'));
3993 +
3994 + parent::display();
3995 +
3996 + if (VikBooking::showFooter()) {
3997 + VikBookingHelper::printFooter();
3998 + }
3999 + }
4000 +
4001 + public function editpayment() {
4002 + VikBookingHelper::printHeader("14");
4003 +
4004 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managepayment'));
4005 +
4006 + parent::display();
4007 +
4008 + if (VikBooking::showFooter()) {
4009 + VikBookingHelper::printFooter();
4010 + }
4011 + }
4012 +
4013 + public function createpayment()
4014 + {
4015 + if (!JSession::checkToken()) {
4016 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4017 + }
4018 +
4019 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
4020 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4021 + }
4022 +
4023 + $mainframe = JFactory::getApplication();
4024 + $pname = VikRequest::getString('name', '', 'request');
4025 + $ppayment = VikRequest::getString('payment', '', 'request');
4026 + $ppublished = VikRequest::getString('published', '', 'request');
4027 + $pcharge = VikRequest::getFloat('charge', '', 'request');
4028 + $psetconfirmed = VikRequest::getString('setconfirmed', '', 'request');
4029 + $phidenonrefund = VikRequest::getInt('hidenonrefund', '', 'request');
4030 + $ponlynonrefund = VikRequest::getInt('onlynonrefund', '', 'request');
4031 + $pshownotealw = VikRequest::getString('shownotealw', '', 'request');
4032 + $pnote = VikRequest::getString('note', '', 'request', VIKREQUEST_ALLOWHTML);
4033 + $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4034 + $pval_pcent = !in_array($pval_pcent, array('1', '2')) ? 1 : $pval_pcent;
4035 + $pch_disc = VikRequest::getString('ch_disc', '', 'request');
4036 + $pch_disc = !in_array($pch_disc, array('1', '2')) ? 1 : $pch_disc;
4037 + $poutposition = VikRequest::getString('outposition', 'top', 'request');
4038 + $plogo = VikRequest::getString('logo', '', 'request');
4039 + $pall_rooms = VikRequest::getInt('all_rooms', 0, 'request');
4040 + $pidrooms = VikRequest::getVar('idrooms', array());
4041 + $vikpaymentparams = VikRequest::getVar('vikpaymentparams', array(0));
4042 + $payparamarr = array();
4043 + $payparamstr = '';
4044 + if (count($vikpaymentparams) > 0) {
4045 + foreach ($vikpaymentparams as $setting => $cont) {
4046 + if (strlen($setting) > 0) {
4047 + $payparamarr[$setting] = $cont;
4048 + }
4049 + }
4050 + if (count($payparamarr) > 0) {
4051 + $payparamstr = json_encode($payparamarr);
4052 + }
4053 + }
4054 +
4055 + $dbo = JFactory::getDbo();
4056 +
4057 + $set_idrooms = [];
4058 + if (empty($pall_rooms) && !empty($pidrooms)) {
4059 + $pidrooms = array_map(function($idroom) {
4060 + return (int)$idroom;
4061 + }, $pidrooms);
4062 + foreach ($pidrooms as $idroom) {
4063 + if (empty($idroom) || in_array($idroom, $set_idrooms)) {
4064 + continue;
4065 + }
4066 + $set_idrooms[] = $idroom;
4067 + }
4068 + }
4069 +
4070 + if (!empty($pname) && !empty($ppayment)) {
4071 + $setpub = $ppublished == "1" ? 1 : 0;
4072 + $psetconfirmed = $psetconfirmed == "1" ? 1 : 0;
4073 + $pshownotealw = $pshownotealw == "1" ? 1 : 0;
4074 + $q = "SELECT `id` FROM `#__vikbooking_gpayments` WHERE `file`=".$dbo->quote($ppayment).";";
4075 + $dbo->setQuery($q);
4076 + $dbo->execute();
4077 + if ($dbo->getNumRows() >= 0) {
4078 + $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') . ");";
4079 + $dbo->setQuery($q);
4080 + $dbo->execute();
4081 + $mainframe->enqueueMessage(JText::translate('VBPAYMENTSAVED'));
4082 + $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4083 + } else {
4084 + VikError::raiseWarning('', JText::translate('ERRINVFILEPAYMENT'));
4085 + $mainframe->redirect("index.php?option=com_vikbooking&task=newpayment");
4086 + }
4087 + } else {
4088 + $mainframe->redirect("index.php?option=com_vikbooking&task=newpayment");
4089 + }
4090 + }
4091 +
4092 + public function updatepayment()
4093 + {
4094 + if (!JSession::checkToken()) {
4095 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4096 + }
4097 +
4098 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4099 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4100 + }
4101 +
4102 + $this->do_updatepayment($stay = false);
4103 + }
4104 +
4105 + public function updatepaymentstay()
4106 + {
4107 + if (!JSession::checkToken()) {
4108 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4109 + }
4110 +
4111 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4112 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4113 + }
4114 +
4115 + $this->do_updatepayment($stay = true);
4116 + }
4117 +
4118 + protected function do_updatepayment($stay = false)
4119 + {
4120 + $mainframe = JFactory::getApplication();
4121 +
4122 + $pwhere = VikRequest::getString('where', '', 'request');
4123 + $pname = VikRequest::getString('name', '', 'request');
4124 + $ppayment = VikRequest::getString('payment', '', 'request');
4125 + $ppublished = VikRequest::getString('published', '', 'request');
4126 + $pcharge = VikRequest::getFloat('charge', '', 'request');
4127 + $psetconfirmed = VikRequest::getString('setconfirmed', '', 'request');
4128 + $phidenonrefund = VikRequest::getInt('hidenonrefund', '', 'request');
4129 + $ponlynonrefund = VikRequest::getInt('onlynonrefund', '', 'request');
4130 + $pshownotealw = VikRequest::getString('shownotealw', '', 'request');
4131 + $pnote = VikRequest::getString('note', '', 'request', VIKREQUEST_ALLOWRAW);
4132 + $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4133 + $pval_pcent = !in_array($pval_pcent, array('1', '2')) ? 1 : $pval_pcent;
4134 + $pch_disc = VikRequest::getString('ch_disc', '', 'request');
4135 + $pch_disc = !in_array($pch_disc, array('1', '2')) ? 1 : $pch_disc;
4136 + $poutposition = VikRequest::getString('outposition', 'top', 'request');
4137 + $plogo = VikRequest::getString('logo', '', 'request');
4138 + $pall_rooms = VikRequest::getInt('all_rooms', 0, 'request');
4139 + $pidrooms = VikRequest::getVar('idrooms', array());
4140 + $vikpaymentparams = VikRequest::getVar('vikpaymentparams', array(0));
4141 + $payparamarr = array();
4142 + $payparamstr = '';
4143 + if (count($vikpaymentparams) > 0) {
4144 + foreach ($vikpaymentparams as $setting => $cont) {
4145 + if (strlen($setting) > 0) {
4146 + $payparamarr[$setting] = $cont;
4147 + }
4148 + }
4149 + if (count($payparamarr) > 0) {
4150 + $payparamstr = json_encode($payparamarr);
4151 + }
4152 + }
4153 +
4154 + $dbo = JFactory::getDbo();
4155 +
4156 + $set_idrooms = [];
4157 + if (empty($pall_rooms) && !empty($pidrooms)) {
4158 + $pidrooms = array_map(function($idroom) {
4159 + return (int)$idroom;
4160 + }, $pidrooms);
4161 + foreach ($pidrooms as $idroom) {
4162 + if (empty($idroom) || in_array($idroom, $set_idrooms)) {
4163 + continue;
4164 + }
4165 + $set_idrooms[] = $idroom;
4166 + }
4167 + }
4168 +
4169 + if (!empty($pname) && !empty($ppayment) && !empty($pwhere)) {
4170 + $setpub = $ppublished == "1" ? 1 : 0;
4171 + $psetconfirmed = $psetconfirmed == "1" ? 1 : 0;
4172 + $pshownotealw = $pshownotealw == "1" ? 1 : 0;
4173 + $q = "SELECT `id` FROM `#__vikbooking_gpayments` WHERE `file`=".$dbo->quote($ppayment)." AND `id`!='".$pwhere."';";
4174 + $dbo->setQuery($q);
4175 + $dbo->execute();
4176 + if ($dbo->getNumRows() >= 0) {
4177 + $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).";";
4178 + $dbo->setQuery($q);
4179 + $dbo->execute();
4180 +
4181 + $mainframe->enqueueMessage(JText::translate('VBPAYMENTUPDATED'));
4182 + if ($stay) {
4183 + $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4184 + } else {
4185 + $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4186 + }
4187 + } else {
4188 + VikError::raiseWarning('', JText::translate('ERRINVFILEPAYMENT'));
4189 + $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4190 + }
4191 + } else {
4192 + $mainframe->redirect("index.php?option=com_vikbooking&task=editpayment&cid[]=".$pwhere);
4193 + }
4194 + }
4195 +
4196 + public function removepayments()
4197 + {
4198 + if (!JSession::checkToken()) {
4199 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4200 + }
4201 +
4202 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
4203 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4204 + }
4205 +
4206 + $ids = VikRequest::getVar('cid', array(0));
4207 + if ($ids) {
4208 + $dbo = JFactory::getDBO();
4209 + foreach ($ids as $d) {
4210 + $q = "DELETE FROM `#__vikbooking_gpayments` WHERE `id`=".$dbo->quote($d).";";
4211 + $dbo->setQuery($q);
4212 + $dbo->execute();
4213 + }
4214 + }
4215 + $mainframe = JFactory::getApplication();
4216 + $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4217 + }
4218 +
4219 + public function modavailpayment() {
4220 + if (!JSession::checkToken('get')) {
4221 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4222 + }
4223 + $cid = VikRequest::getVar('cid', array(0));
4224 + $idp = $cid[0];
4225 + if (!empty($idp)) {
4226 + $dbo = JFactory::getDBO();
4227 + $q = "SELECT `published` FROM `#__vikbooking_gpayments` WHERE `id`=".intval($idp).";";
4228 + $dbo->setQuery($q);
4229 + $dbo->execute();
4230 + $get = $dbo->loadAssocList();
4231 + $q = "UPDATE `#__vikbooking_gpayments` SET `published`=".(intval($get[0]['published']) == 1 ? '0' : '1')." WHERE `id`=".intval($idp).";";
4232 + $dbo->setQuery($q);
4233 + $dbo->execute();
4234 + }
4235 + $mainframe = JFactory::getApplication();
4236 + $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
4237 + }
4238 +
4239 + public function seasons() {
4240 + VikBookingHelper::printHeader("13");
4241 +
4242 + VikRequest::setVar('view', VikRequest::getCmd('view', 'seasons'));
4243 +
4244 + parent::display();
4245 +
4246 + if (VikBooking::showFooter()) {
4247 + VikBookingHelper::printFooter();
4248 + }
4249 + }
4250 +
4251 + public function newseason() {
4252 + VikBookingHelper::printHeader("13");
4253 +
4254 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageseason'));
4255 +
4256 + parent::display();
4257 +
4258 + if (VikBooking::showFooter()) {
4259 + VikBookingHelper::printFooter();
4260 + }
4261 + }
4262 +
4263 + public function editseason() {
4264 + VikBookingHelper::printHeader("13");
4265 +
4266 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageseason'));
4267 +
4268 + parent::display();
4269 +
4270 + if (VikBooking::showFooter()) {
4271 + VikBookingHelper::printFooter();
4272 + }
4273 + }
4274 +
4275 + public function updateseason()
4276 + {
4277 + if (!JSession::checkToken()) {
4278 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4279 + }
4280 +
4281 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4282 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4283 + }
4284 +
4285 + $this->do_updateseason();
4286 + }
4287 +
4288 + public function updateseasonstay()
4289 + {
4290 + if (!JSession::checkToken()) {
4291 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4292 + }
4293 +
4294 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
4295 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4296 + }
4297 +
4298 + $this->do_updateseason(true);
4299 + }
4300 +
4301 + private function do_updateseason($stay = false)
4302 + {
4303 + $app = JFactory::getApplication();
4304 + $dbo = JFactory::getDbo();
4305 + $session = JFactory::getSession();
4306 +
4307 + $pwhere = VikRequest::getInt('where', 0, 'request');
4308 +
4309 + $pfrom = VikRequest::getString('from', '', 'request');
4310 + $pto = VikRequest::getString('to', '', 'request');
4311 + $ptype = VikRequest::getString('type', '', 'request');
4312 + $pdiffcost = VikRequest::getFloat('diffcost', '', 'request');
4313 + $pidrooms = VikRequest::getVar('idrooms', array());
4314 + $pidprices = VikRequest::getVar('idprices', array());
4315 + $pwdays = VikRequest::getVar('wdays', array());
4316 + $pspname = VikRequest::getString('spname', '', 'request');
4317 + $pcheckinincl = VikRequest::getString('checkinincl', '', 'request');
4318 + $pcheckinincl = $pcheckinincl == 1 ? 1 : 0;
4319 + $pyeartied = VikRequest::getInt('yeartied', 0, 'request');
4320 + $pyeartied = $pyeartied == 1 ? 1 : 0;
4321 + $tieyear = 0;
4322 + $ppromo = VikRequest::getInt('promo', 0, 'request');
4323 + $ppromo = $ppromo == 1 ? 1 : 0;
4324 + $ppromodaysadv = VikRequest::getInt('promodaysadv', '', 'request');
4325 + $ppromominlos = VikRequest::getInt('promominlos', '', 'request');
4326 + $ppromotxt = VikRequest::getString('promotxt', '', 'request', VIKREQUEST_ALLOWHTML);
4327 + $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4328 + $pval_pcent = $pval_pcent == "1" ? 1 : 2;
4329 + $proundmode = VikRequest::getString('roundmode', '', 'request');
4330 + $proundmode = (!empty($proundmode) && in_array($proundmode, array('PHP_ROUND_HALF_UP', 'PHP_ROUND_HALF_DOWN')) ? $proundmode : '');
4331 + $pnightsoverrides = VikRequest::getVar('nightsoverrides', array());
4332 + $pvaluesoverrides = VikRequest::getVar('valuesoverrides', array());
4333 + $pandmoreoverride = VikRequest::getVar('andmoreoverride', array());
4334 + $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array());
4335 + $padultsdiffval = VikRequest::getVar('adultsdiffval', array());
4336 + $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array());
4337 + $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array());
4338 + $ppromolastmind = VikRequest::getInt('promolastmind', 0, 'request');
4339 + $ppromolastminh = VikRequest::getInt('promolastminh', 0, 'request');
4340 + $promolastmin = ($ppromolastmind * 86400) + ($ppromolastminh * 3600);
4341 + $ppromofinalprice = VikRequest::getInt('promofinalprice', 0, 'request');
4342 + $ppromofinalprice = $ppromo ? $ppromofinalprice : 0;
4343 + $occupancy_ovr = array();
4344 + $losverridestr = "";
4345 +
4346 + $updforvcm = $session->get('vbVcmRatesUpd', '');
4347 + $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
4348 +
4349 + // check null dates
4350 + if ($dbo->getNullDate() == $pfrom) {
4351 + $pfrom = '';
4352 + }
4353 + if ($dbo->getNullDate() == $pto) {
4354 + $pto = '';
4355 + }
4356 +
4357 + if ((empty($pfrom) || empty($pto)) && !$pwdays) {
4358 + VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4359 + $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4360 + exit;
4361 + }
4362 +
4363 + $skipseason = false;
4364 + if (empty($pfrom) || empty($pto)) {
4365 + $skipseason = true;
4366 + }
4367 + $skipdays = false;
4368 + $wdaystr = null;
4369 + if (count($pwdays) == 0) {
4370 + $skipdays = true;
4371 + } else {
4372 + $wdaystr = "";
4373 + foreach ($pwdays as $wd) {
4374 + $wdaystr .= $wd.';';
4375 + }
4376 + }
4377 + $roomstr = "";
4378 + $roomids = array();
4379 + foreach ($pidrooms as $room) {
4380 + if (empty($room)) {
4381 + continue;
4382 + }
4383 + $roomstr .= "-".$room."-,";
4384 + $roomids[] = (int)$room;
4385 + }
4386 + $pricestr = "";
4387 + $priceids = array();
4388 + foreach ($pidprices as $price) {
4389 + if (empty($price)) {
4390 + continue;
4391 + }
4392 + $pricestr .= "-".$price."-,";
4393 + $priceids[] = (int)$price;
4394 + }
4395 + $valid = true;
4396 + $double_records = array();
4397 + $sfrom = null;
4398 + $sto = null;
4399 +
4400 + // value overrides
4401 + if ($pnightsoverrides && $pvaluesoverrides) {
4402 + foreach ($pnightsoverrides as $ko => $no) {
4403 + if (!empty($no) && strlen(trim($pvaluesoverrides[$ko])) > 0) {
4404 + $infiniteclause = intval($pandmoreoverride[$ko]) == 1 ? '-i' : '';
4405 + $losverridestr .= intval($no).$infiniteclause.':'.trim($pvaluesoverrides[$ko]).'_';
4406 + }
4407 + }
4408 + }
4409 +
4410 + if (!$skipseason) {
4411 + $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
4412 + $second = VikBooking::getDateTimestamp($pto, 0, 0);
4413 +
4414 + if ($second > 0 && $second == $first) {
4415 + $second += 86399;
4416 + }
4417 +
4418 + if (!($second > $first)) {
4419 + VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4420 + $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4421 + exit;
4422 + }
4423 +
4424 + $baseone = getdate($first);
4425 + $basets = mktime(0, 0, 0, 1, 1, $baseone['year']);
4426 + $sfrom = $baseone[0] - $basets;
4427 + $basetwo = getdate($second);
4428 + $basets = mktime(0, 0, 0, 1, 1, $basetwo['year']);
4429 + $sto = $basetwo[0] - $basets;
4430 +
4431 + // check leap year
4432 + if ($baseone['year'] % 4 == 0 && ($baseone['year'] % 100 != 0 || $baseone['year'] % 400 == 0)) {
4433 + $leapts = mktime(0, 0, 0, 2, 29, $baseone['year']);
4434 + if ($baseone[0] > $leapts) {
4435 + $sfrom -= 86400;
4436 + /**
4437 + * To avoid issue with leap years and dates near Feb 29th, we only reduce the seconds if these were reduced
4438 + * for the from-date of the seasons. Doing it just for the to-date in 2019 for 2020 (leap) produced invalid results.
4439 + *
4440 + * @since July 2nd 2019
4441 + */
4442 + if ($basetwo['year'] % 4 == 0 && ($basetwo['year'] % 100 != 0 || $basetwo['year'] % 400 == 0)) {
4443 + $leapts = mktime(0, 0, 0, 2, 29, $basetwo['year']);
4444 + if ($basetwo[0] > $leapts) {
4445 + $sto -= date('d-m', $baseone[0]) != '31-12' && date('d-m', $basetwo[0]) == '31-12' ? 1 : 86400;
4446 + }
4447 + }
4448 + }
4449 + }
4450 +
4451 + // tied to the year
4452 + if ($pyeartied == 1) {
4453 + $tieyear = $baseone['year'];
4454 + }
4455 +
4456 + // Occupancy Override
4457 + if (count($padultsdiffval) > 0) {
4458 + foreach ($padultsdiffval as $rid => $valovr_arr) {
4459 + if (!is_array($valovr_arr) || !is_array($padultsdiffchdisc[$rid]) || !is_array($padultsdiffvalpcent[$rid]) || !is_array($padultsdiffpernight[$rid])) {
4460 + continue;
4461 + }
4462 + foreach ($valovr_arr as $occ => $valovr) {
4463 + if (!(strlen($valovr) > 0) || !(strlen($padultsdiffchdisc[$rid][$occ]) > 0) || !(strlen($padultsdiffvalpcent[$rid][$occ]) > 0) || !(strlen($padultsdiffpernight[$rid][$occ]) > 0)) {
4464 + continue;
4465 + }
4466 + if (!array_key_exists($rid, $occupancy_ovr)) {
4467 + $occupancy_ovr[$rid] = array();
4468 + }
4469 + $occupancy_ovr[$rid][$occ] = array('chdisc' => (int)$padultsdiffchdisc[$rid][$occ], 'valpcent' => (int)$padultsdiffvalpcent[$rid][$occ], 'pernight' => (int)$padultsdiffpernight[$rid][$occ], 'value' => (float)$valovr);
4470 + }
4471 + }
4472 + }
4473 +
4474 + // check if seasons dates are valid
4475 + $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").";";
4476 + $dbo->setQuery($q);
4477 + $similar = $dbo->loadAssocList();
4478 + if ($similar) {
4479 + $valid = false;
4480 + foreach ($similar as $sim) {
4481 + $double_records[] = $sim['spname'];
4482 + }
4483 + }
4484 +
4485 + $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").";";
4486 + $dbo->setQuery($q);
4487 + $similar = $dbo->loadAssocList();
4488 + if ($similar) {
4489 + $valid = false;
4490 + foreach ($similar as $sim) {
4491 + $double_records[] = $sim['spname'];
4492 + }
4493 + }
4494 +
4495 + $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").";";
4496 + $dbo->setQuery($q);
4497 + $dbo->execute();
4498 + $similar = $dbo->loadAssocList();
4499 + if ($similar) {
4500 + $valid = false;
4501 + foreach ($similar as $sim) {
4502 + $double_records[] = $sim['spname'];
4503 + }
4504 + }
4505 + }
4506 +
4507 + // fetch previous record before the update
4508 + $q = $dbo->getQuery(true)
4509 + ->select('*')
4510 + ->from($dbo->qn('#__vikbooking_seasons'))
4511 + ->where($dbo->qn('id') . ' = ' . $pwhere);
4512 + $dbo->setQuery($q, 0, 1);
4513 + $prev_record = $dbo->loadAssoc();
4514 +
4515 + if (!$valid || !$prev_record) {
4516 + VikError::raiseWarning('', JText::translate('ERRINVDATEROOMSLOCSEASON').($double_records ? ' ('.implode(', ', array_unique($double_records)).')' : ''));
4517 + $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4518 + exit;
4519 + }
4520 +
4521 + /**
4522 + * Attempt to access the promotion handlers in advance to perform additional validations.
4523 + *
4524 + * @since 1.16.4 (J) - 1.6.4 (WP)
4525 + */
4526 + try {
4527 + $promo_handlers = VikBooking::getPromotionHandlers();
4528 + } catch (Exception $e) {
4529 + // reset the value
4530 + $promo_handlers = [];
4531 + }
4532 +
4533 + if (!$prev_record['promo'] && $ppromo && $promo_handlers) {
4534 + // channels supporting promotions are available, and a regular special price is
4535 + // being converted into a promotion - this is not allowed so we make it a non-promotion.
4536 + $ppromo = 0;
4537 + $app->enqueueMessage(JText::translate('VBO_NOPROMO_UPD_CHANNELS'), 'warning');
4538 + }
4539 +
4540 + if ($promo_handlers && $proundmode) {
4541 + /**
4542 + * Always disallow rounding when channels supporting promotions are available.
4543 + *
4544 + * @since 1.18.3 (J) - 1.8.3 (WP)
4545 + */
4546 + $proundmode = '';
4547 + $app->enqueueMessage(sprintf('%s: %s.', JText::translate('VBNEWSEASONROUNDCOST'), JText::translate('VBPARAMPRICECALENDARDISABLED')), 'warning');
4548 + }
4549 +
4550 + // update record
4551 + $upd_record = new stdClass;
4552 + $upd_record->id = $prev_record['id'];
4553 + $upd_record->type = $ptype == "1" ? 1 : 2;
4554 + $upd_record->from = $sfrom;
4555 + $upd_record->to = $sto;
4556 + $upd_record->diffcost = $pdiffcost;
4557 + $upd_record->idrooms = $roomstr;
4558 + $upd_record->spname = $pspname;
4559 + $upd_record->wdays = $wdaystr;
4560 + $upd_record->checkinincl = $pcheckinincl;
4561 + $upd_record->val_pcent = $pval_pcent;
4562 + $upd_record->losoverride = $losverridestr;
4563 + $upd_record->roundmode = !empty($proundmode) ? $proundmode : null;
4564 + $upd_record->year = $pyeartied == 1 ? $tieyear : null;
4565 + $upd_record->idprices = $pricestr;
4566 + $upd_record->promo = $ppromo;
4567 + $upd_record->promodaysadv = !empty($ppromodaysadv) ? $ppromodaysadv : null;
4568 + $upd_record->promotxt = $ppromotxt;
4569 + $upd_record->promominlos = !empty($ppromominlos) ? $ppromominlos : 0;
4570 + $upd_record->occupancy_ovr = $occupancy_ovr ? json_encode($occupancy_ovr) : null;
4571 + $upd_record->promolastmin = (int)$promolastmin;
4572 + $upd_record->promofinalprice = $ppromofinalprice;
4573 +
4574 + $dbo->updateObject('#__vikbooking_seasons', $upd_record, 'id', $nulls = true);
4575 +
4576 + $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
4577 +
4578 + // update session values
4579 + $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
4580 + if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
4581 + $updforvcm['dfrom'] = $updforvcm['dfrom'] > $first ? $first : $updforvcm['dfrom'];
4582 + } else {
4583 + $updforvcm['dfrom'] = $first;
4584 + }
4585 + if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
4586 + $updforvcm['dto'] = $updforvcm['dto'] < $second ? $second : $updforvcm['dto'];
4587 + } else {
4588 + $updforvcm['dto'] = $second;
4589 + }
4590 + if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
4591 + foreach ($roomids as $rid) {
4592 + if (!in_array($rid, $updforvcm['rooms'])) {
4593 + $updforvcm['rooms'][] = $rid;
4594 + }
4595 + }
4596 + } else {
4597 + $updforvcm['rooms'] = $roomids;
4598 + }
4599 + if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
4600 + foreach ($roomids as $rid) {
4601 + if (array_key_exists($rid, $updforvcm['rplans'])) {
4602 + $updforvcm['rplans'][$rid] = $updforvcm['rplans'][$rid] + $priceids;
4603 + } else {
4604 + $updforvcm['rplans'][$rid] = $priceids;
4605 + }
4606 + }
4607 + } else {
4608 + $updforvcm['rplans'] = array();
4609 + foreach ($roomids as $rid) {
4610 + $updforvcm['rplans'][$rid] = $priceids;
4611 + }
4612 + }
4613 + $session->set('vbVcmRatesUpd', $updforvcm);
4614 +
4615 + /**
4616 + * Query promotion handlers, if any, to trigger the update/delete promotion event.
4617 + *
4618 + * @since 1.15.0 (J) - 1.5.0 (WP)
4619 + * @since 1.16.4 (J) - 1.6.4 (WP) added control to perform a delete operation.
4620 + */
4621 + $promo_update_type = $prev_record['promo'] && !$ppromo ? 'triggerDelete' : 'triggerUpdate';
4622 + $promo_method_type = $prev_record['promo'] && !$ppromo ? 'delete' : 'update';
4623 + try {
4624 + if ($ppromo && is_array($promo_handlers) && $promo_handlers) {
4625 + foreach ($promo_handlers as $promo_handler) {
4626 + if (!isset($promo_handler->instance) || !is_object($promo_handler->instance) || !method_exists($promo_handler->instance, $promo_update_type)) {
4627 + // outdated handler object
4628 + continue;
4629 + }
4630 + if (!is_callable(array($promo_handler->instance, $promo_update_type)) || !$promo_handler->instance->{$promo_update_type}()) {
4631 + // promotion handler does not support update/delete promotion event
4632 + continue;
4633 + }
4634 + // invoke the update/delete promotion event for this handler
4635 + $ch_result = $promo_handler->instance->createPromotion(['vbo_promo_id' => $pwhere], $promo_method_type);
4636 + if (!$ch_result) {
4637 + VikError::raiseWarning('', $promo_handler->instance->getName() . ': ' . $promo_handler->instance->getError());
4638 + }
4639 + }
4640 + }
4641 + } catch (Exception $e) {
4642 + // do nothing
4643 + }
4644 +
4645 + if ($stay) {
4646 + $app->redirect("index.php?option=com_vikbooking&task=editseason&cid[]=" . $pwhere);
4647 + } else {
4648 + $app->redirect("index.php?option=com_vikbooking&task=seasons");
4649 + }
4650 + $app->close();
4651 + }
4652 +
4653 + public function createseason()
4654 + {
4655 + if (!JSession::checkToken()) {
4656 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4657 + }
4658 +
4659 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
4660 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4661 + }
4662 +
4663 + $this->do_createseason();
4664 + }
4665 +
4666 + public function createseason_new()
4667 + {
4668 + if (!JSession::checkToken()) {
4669 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
4670 + }
4671 +
4672 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
4673 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
4674 + }
4675 +
4676 + $this->do_createseason(true);
4677 + }
4678 +
4679 + private function do_createseason($andnew = false)
4680 + {
4681 + $app = JFactory::getApplication();
4682 + $dbo = JFactory::getDbo();
4683 + $session = JFactory::getSession();
4684 +
4685 + $pfrom = VikRequest::getString('from', '', 'request');
4686 + $pto = VikRequest::getString('to', '', 'request');
4687 + $ptype = VikRequest::getString('type', '', 'request');
4688 + $pdiffcost = VikRequest::getFloat('diffcost', '', 'request');
4689 + $pidrooms = VikRequest::getVar('idrooms', array());
4690 + $pidprices = VikRequest::getVar('idprices', array());
4691 + $pwdays = VikRequest::getVar('wdays', array());
4692 + $pspname = VikRequest::getString('spname', '', 'request');
4693 + $pcheckinincl = VikRequest::getString('checkinincl', '', 'request');
4694 + $pcheckinincl = $pcheckinincl == 1 ? 1 : 0;
4695 + $pyeartied = VikRequest::getInt('yeartied', 0, 'request');
4696 + $pyeartied = $pyeartied == 1 ? 1 : 0;
4697 + $tieyear = 0;
4698 + $pval_pcent = VikRequest::getString('val_pcent', '', 'request');
4699 + $pval_pcent = $pval_pcent == "1" ? 1 : 2;
4700 + $proundmode = VikRequest::getString('roundmode', '', 'request');
4701 + $proundmode = (!empty($proundmode) && in_array($proundmode, array('PHP_ROUND_HALF_UP', 'PHP_ROUND_HALF_DOWN')) ? $proundmode : '');
4702 + $ppromo = VikRequest::getInt('promo', 0, 'request');
4703 + $ppromodaysadv = VikRequest::getInt('promodaysadv', '', 'request');
4704 + $ppromominlos = VikRequest::getInt('promominlos', '', 'request');
4705 + $ppromotxt = VikRequest::getString('promotxt', '', 'request', VIKREQUEST_ALLOWHTML);
4706 + $pnightsoverrides = VikRequest::getVar('nightsoverrides', array());
4707 + $pvaluesoverrides = VikRequest::getVar('valuesoverrides', array());
4708 + $pandmoreoverride = VikRequest::getVar('andmoreoverride', array());
4709 + $padultsdiffchdisc = VikRequest::getVar('adultsdiffchdisc', array());
4710 + $padultsdiffval = VikRequest::getVar('adultsdiffval', array());
4711 + $padultsdiffvalpcent = VikRequest::getVar('adultsdiffvalpcent', array());
4712 + $padultsdiffpernight = VikRequest::getVar('adultsdiffpernight', array());
4713 + $ppromolastmind = VikRequest::getInt('promolastmind', 0, 'request');
4714 + $ppromolastminh = VikRequest::getInt('promolastminh', 0, 'request');
4715 + $promolastmin = ($ppromolastmind * 86400) + ($ppromolastminh * 3600);
4716 + $ppromofinalprice = VikRequest::getInt('promofinalprice', 0, 'request');
4717 + $ppromofinalprice = $ppromo ? $ppromofinalprice : 0;
4718 + $pchannels = VikRequest::getVar('channels', array());
4719 + $occupancy_ovr = array();
4720 + $losverridestr = "";
4721 +
4722 + $updforvcm = $session->get('vbVcmRatesUpd', '');
4723 + $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
4724 +
4725 + // check null dates
4726 + if ($dbo->getNullDate() == $pfrom) {
4727 + $pfrom = '';
4728 + }
4729 + if ($dbo->getNullDate() == $pto) {
4730 + $pto = '';
4731 + }
4732 +
4733 + if ((empty($pfrom) || empty($pto)) && !$pwdays) {
4734 + VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4735 + $app->redirect("index.php?option=com_vikbooking&task=newseason");
4736 + exit;
4737 + }
4738 +
4739 + $skipseason = false;
4740 + if (empty($pfrom) || empty($pto)) {
4741 + $skipseason = true;
4742 + }
4743 + $skipdays = false;
4744 + $wdaystr = null;
4745 + if (!$pwdays) {
4746 + $skipdays = true;
4747 + } else {
4748 + $wdaystr = "";
4749 + foreach ($pwdays as $wd) {
4750 + $wdaystr .= $wd.';';
4751 + }
4752 + }
4753 + $roomstr = "";
4754 + $roomids = array();
4755 + foreach ($pidrooms as $room) {
4756 + if (empty($room)) {
4757 + continue;
4758 + }
4759 + $roomstr .= "-".$room."-,";
4760 + $roomids[] = (int)$room;
4761 + }
4762 + $pricestr = "";
4763 + $priceids = array();
4764 + foreach ($pidprices as $price) {
4765 + if (empty($price)) {
4766 + continue;
4767 + }
4768 + $pricestr .= "-".$price."-,";
4769 + $priceids[] = (int)$price;
4770 + }
4771 + $valid = true;
4772 + $double_records = array();
4773 + $sfrom = null;
4774 + $sto = null;
4775 +
4776 + // value overrides
4777 + if ($pnightsoverrides && $pvaluesoverrides) {
4778 + foreach ($pnightsoverrides as $ko => $no) {
4779 + if (!empty($no) && strlen(trim($pvaluesoverrides[$ko])) > 0) {
4780 + $infiniteclause = intval($pandmoreoverride[$ko]) == 1 ? '-i' : '';
4781 + $losverridestr .= intval($no).$infiniteclause.':'.trim($pvaluesoverrides[$ko]).'_';
4782 + }
4783 + }
4784 + }
4785 +
4786 + if (!$skipseason) {
4787 + $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
4788 + $second = VikBooking::getDateTimestamp($pto, 0, 0);
4789 +
4790 + if ($second > 0 && $second == $first) {
4791 + $second += 86399;
4792 + }
4793 +
4794 + if (!($second > $first)) {
4795 + VikError::raiseWarning('', JText::translate('ERRINVDATESEASON'));
4796 + $app->redirect("index.php?option=com_vikbooking&task=newseason");
4797 + exit;
4798 + }
4799 +
4800 + $baseone = getdate($first);
4801 + $basets = mktime(0, 0, 0, 1, 1, $baseone['year']);
4802 + $sfrom = $baseone[0] - $basets;
4803 + $basetwo = getdate($second);
4804 + $basets = mktime(0, 0, 0, 1, 1, $basetwo['year']);
4805 + $sto = $basetwo[0] - $basets;
4806 +
4807 + // check leap year
4808 + if ($baseone['year'] % 4 == 0 && ($baseone['year'] % 100 != 0 || $baseone['year'] % 400 == 0)) {
4809 + $leapts = mktime(0, 0, 0, 2, 29, $baseone['year']);
4810 + if ($baseone[0] > $leapts) {
4811 + $sfrom -= 86400;
4812 + /**
4813 + * To avoid issue with leap years and dates near Feb 29th, we only reduce the seconds if these were reduced
4814 + * for the from-date of the seasons. Doing it just for the to-date in 2019 for 2020 (leap) produced invalid results.
4815 + *
4816 + * @since July 2nd 2019
4817 + */
4818 + if ($basetwo['year'] % 4 == 0 && ($basetwo['year'] % 100 != 0 || $basetwo['year'] % 400 == 0)) {
4819 + $leapts = mktime(0, 0, 0, 2, 29, $basetwo['year']);
4820 + if ($basetwo[0] > $leapts) {
4821 + $sto -= date('d-m', $baseone[0]) != '31-12' && date('d-m', $basetwo[0]) == '31-12' ? 1 : 86400;
4822 + }
4823 + }
4824 + }
4825 + }
4826 +
4827 + // tied to the year
4828 + if ($pyeartied == 1) {
4829 + $tieyear = $baseone['year'];
4830 + }
4831 +
4832 + // Occupancy Override
4833 + if ($padultsdiffval) {
4834 + foreach ($padultsdiffval as $rid => $valovr_arr) {
4835 + if (!is_array($valovr_arr) || !is_array($padultsdiffchdisc[$rid]) || !is_array($padultsdiffvalpcent[$rid]) || !is_array($padultsdiffpernight[$rid])) {
4836 + continue;
4837 + }
4838 + foreach ($valovr_arr as $occ => $valovr) {
4839 + if (!(strlen($valovr) > 0) || !(strlen($padultsdiffchdisc[$rid][$occ]) > 0) || !(strlen($padultsdiffvalpcent[$rid][$occ]) > 0) || !(strlen($padultsdiffpernight[$rid][$occ]) > 0)) {
4840 + continue;
4841 + }
4842 + if (!array_key_exists($rid, $occupancy_ovr)) {
4843 + $occupancy_ovr[$rid] = array();
4844 + }
4845 + $occupancy_ovr[$rid][$occ] = array('chdisc' => (int)$padultsdiffchdisc[$rid][$occ], 'valpcent' => (int)$padultsdiffvalpcent[$rid][$occ], 'pernight' => (int)$padultsdiffpernight[$rid][$occ], 'value' => (float)$valovr);
4846 + }
4847 + }
4848 + }
4849 +
4850 + // check if seasons dates are valid
4851 + $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").";";
4852 + $dbo->setQuery($q);
4853 + $similar = $dbo->loadAssocList();
4854 + if ($similar) {
4855 + $valid = false;
4856 + foreach ($similar as $sim) {
4857 + $double_records[] = $sim['spname'];
4858 + }
4859 + }
4860 +
4861 + $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").";";
4862 + $dbo->setQuery($q);
4863 + $similar = $dbo->loadAssocList();
4864 + if ($similar) {
4865 + $valid = false;
4866 + foreach ($similar as $sim) {
4867 + $double_records[] = $sim['spname'];
4868 + }
4869 + }
4870 +
4871 + $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").";";
4872 + $dbo->setQuery($q);
4873 + $similar = $dbo->loadAssocList();
4874 + if ($similar) {
4875 + $valid = false;
4876 + foreach ($similar as $sim) {
4877 + $double_records[] = $sim['spname'];
4878 + }
4879 + }
4880 + }
4881 +
4882 + if (!$valid && !$ppromo) {
4883 + VikError::raiseWarning('', JText::translate('ERRINVDATEROOMSLOCSEASON').(count($double_records) ? ' ('.implode(', ', array_unique($double_records)).')' : ''));
4884 + $app->redirect("index.php?option=com_vikbooking&task=newseason");
4885 + exit;
4886 + }
4887 +
4888 + if ($pchannels && $proundmode) {
4889 + /**
4890 + * Always disallow rounding when channels supporting promotions are available.
4891 + *
4892 + * @since 1.18.3 (J) - 1.8.3 (WP)
4893 + */
4894 + $proundmode = '';
4895 + $app->enqueueMessage(sprintf('%s: %s.', JText::translate('VBNEWSEASONROUNDCOST'), JText::translate('VBPARAMPRICECALENDARDISABLED')), 'warning');
4896 + }
4897 +
4898 + // insert new record
4899 + $sea_record = new stdClass;
4900 + $sea_record->type = $ptype == "1" ? 1 : 2;
4901 + $sea_record->from = $sfrom;
4902 + $sea_record->to = $sto;
4903 + $sea_record->diffcost = $pdiffcost;
4904 + $sea_record->idrooms = $roomstr;
4905 + $sea_record->spname = $pspname;
4906 + $sea_record->wdays = $wdaystr;
4907 + $sea_record->checkinincl = $pcheckinincl;
4908 + $sea_record->val_pcent = $pval_pcent;
4909 + $sea_record->losoverride = $losverridestr;
4910 + $sea_record->roundmode = !empty($proundmode) ? $proundmode : null;
4911 + $sea_record->year = $pyeartied == 1 ? $tieyear : null;
4912 + $sea_record->idprices = $pricestr;
4913 + $sea_record->promo = $ppromo == 1 ? 1 : 0;
4914 + $sea_record->promodaysadv = !empty($ppromodaysadv) ? $ppromodaysadv : null;
4915 + $sea_record->promotxt = $ppromotxt;
4916 + $sea_record->promominlos = !empty($ppromominlos) ? $ppromominlos : 0;
4917 + $sea_record->occupancy_ovr = $occupancy_ovr ? json_encode($occupancy_ovr) : null;
4918 + $sea_record->promolastmin = (int)$promolastmin;
4919 + $sea_record->promofinalprice = $ppromofinalprice;
4920 +
4921 + $dbo->insertObject('#__vikbooking_seasons', $sea_record, 'id');
4922 +
4923 + $vbo_promo_id = $sea_record->id;
4924 +
4925 + $app->enqueueMessage(JText::translate('VBSEASONSAVED'));
4926 +
4927 + // update session values
4928 + $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
4929 + if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
4930 + $updforvcm['dfrom'] = $updforvcm['dfrom'] > $first ? $first : $updforvcm['dfrom'];
4931 + } else {
4932 + $updforvcm['dfrom'] = $first;
4933 + }
4934 + if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
4935 + $updforvcm['dto'] = $updforvcm['dto'] < $second ? $second : $updforvcm['dto'];
4936 + } else {
4937 + $updforvcm['dto'] = $second;
4938 + }
4939 + if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
4940 + foreach ($roomids as $rid) {
4941 + if (!in_array($rid, $updforvcm['rooms'])) {
4942 + $updforvcm['rooms'][] = $rid;
4943 + }
4944 + }
4945 + } else {
4946 + $updforvcm['rooms'] = $roomids;
4947 + }
4948 + if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
4949 + foreach ($roomids as $rid) {
4950 + if (array_key_exists($rid, $updforvcm['rplans'])) {
4951 + $updforvcm['rplans'][$rid] = $updforvcm['rplans'][$rid] + $priceids;
4952 + } else {
4953 + $updforvcm['rplans'][$rid] = $priceids;
4954 + }
4955 + }
4956 + } else {
4957 + $updforvcm['rplans'] = array();
4958 + foreach ($roomids as $rid) {
4959 + $updforvcm['rplans'][$rid] = $priceids;
4960 + }
4961 + }
4962 + if (!$ppromo) {
4963 + $session->set('vbVcmRatesUpd', $updforvcm);
4964 + }
4965 +
4966 + /**
4967 + * Create the promotion also on the selected channels
4968 + *
4969 + * @since 1.13.0 (J) - 1.3.0 (WP)
4970 + */
4971 + if ($ppromo && $pchannels) {
4972 + foreach ($pchannels as $channel_key) {
4973 + $promo_obj = VikBooking::getPromotionHandlers($channel_key);
4974 + if (!is_object($promo_obj)) {
4975 + continue;
4976 + }
4977 + /**
4978 + * We inject for VCM the ID of the newly created promotion in VBO.
4979 + *
4980 + * @since 1.15.0 (J) - 1.5.0 (WP)
4981 + */
4982 + $ch_result = $promo_obj->createPromotion(array('vbo_promo_id' => $vbo_promo_id), 'new');
4983 + if (!$ch_result) {
4984 + VikError::raiseWarning('', $promo_obj->getName() . ': ' . $promo_obj->getError());
4985 + } else {
4986 + $resp = $promo_obj->getResponse();
4987 + $app->enqueueMessage($promo_obj->getName() . ': ' . JText::translate('VBOCHPROMOSUCCESS') . (!empty($resp) ? ' (' . str_replace('e4j.ok.', '', $resp) . ')' : ''));
4988 + // in case of success, unset the current session values in VCM
4989 + $session->set('vcmBPromo', '');
4990 + }
4991 + }
4992 + }
4993 +
4994 + $app->redirect("index.php?option=com_vikbooking&task=".($andnew ? 'newseason' : 'seasons'));
4995 + $app->close();
4996 + }
4997 +
4998 + public function removeseasons()
4999 + {
5000 + if (!JSession::checkToken()) {
5001 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5002 + }
5003 +
5004 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
5005 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5006 + }
5007 +
5008 + $app = JFactory::getApplication();
5009 + $dbo = JFactory::getDbo();
5010 +
5011 + $ids = VikRequest::getVar('cid', array(0));
5012 + $pidroom = VikRequest::getInt('idroom', '', 'request');
5013 + $pwhere = VikRequest::getInt('where', '', 'request');
5014 + if (!empty($pwhere)) {
5015 + $ids[] = $pwhere;
5016 + }
5017 + $tot_removed = array();
5018 + $prev_promos = array();
5019 + foreach ($ids as $d) {
5020 + if (empty($d)) {
5021 + continue;
5022 + }
5023 + // check if it was a promotion
5024 + $q = "SELECT `id` FROM `#__vikbooking_seasons` WHERE `id`=" . (int)$d . " AND `promo`=1;";
5025 + $dbo->setQuery($q);
5026 + $dbo->execute();
5027 + if ($dbo->getNumRows()) {
5028 + // push it as a previous promo
5029 + array_push($prev_promos, $d);
5030 + }
5031 +
5032 + // delete the record
5033 + $q = "DELETE FROM `#__vikbooking_seasons` WHERE `id`=".$dbo->quote($d).";";
5034 + $dbo->setQuery($q);
5035 + $dbo->execute();
5036 + $tot_removed[] = $d;
5037 + }
5038 +
5039 + /**
5040 + * Query promotion handlers, if any, to trigger the delete promotion event.
5041 + *
5042 + * @since 1.15.0 (J) - 1.5.0 (WP)
5043 + */
5044 + $promo_handlers = VikBooking::getPromotionHandlers();
5045 + foreach ($prev_promos as $vbo_promo_id) {
5046 + try {
5047 + if (is_array($promo_handlers)) {
5048 + foreach ($promo_handlers as $promo_handler) {
5049 + if (!isset($promo_handler->instance) || !is_object($promo_handler->instance) || !method_exists($promo_handler->instance, 'triggerDelete')) {
5050 + // outdated handler object
5051 + continue;
5052 + }
5053 + if (!is_callable(array($promo_handler->instance, 'triggerDelete')) || !$promo_handler->instance->triggerDelete()) {
5054 + // promotion handler does not support delete promotion event
5055 + continue;
5056 + }
5057 + // invoke the delete promotion event for this handler
5058 + $ch_result = $promo_handler->instance->createPromotion(array('vbo_promo_id' => $vbo_promo_id), 'delete');
5059 + if (!$ch_result) {
5060 + VikError::raiseWarning('', $promo_handler->instance->getName() . ': ' . $promo_handler->instance->getError());
5061 + }
5062 + }
5063 + }
5064 + } catch (Exception $e) {
5065 + // do nothing
5066 + }
5067 + }
5068 +
5069 + $app->enqueueMessage(JText::sprintf('VBRECORDSREMOVED', count($tot_removed)));
5070 + $app->redirect("index.php?option=com_vikbooking&task=seasons".(!empty($pidroom) ? '&idroom='.$pidroom : ''));
5071 + $app->close();
5072 + }
5073 +
5074 + public function updatecustomer()
5075 + {
5076 + if (!JSession::checkToken()) {
5077 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5078 + }
5079 +
5080 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5081 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5082 + }
5083 +
5084 + $this->do_updatecustomer();
5085 + }
5086 +
5087 + public function updatecustomerstay()
5088 + {
5089 + if (!JSession::checkToken()) {
5090 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5091 + }
5092 +
5093 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5094 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5095 + }
5096 +
5097 + $this->do_updatecustomer(true);
5098 + }
5099 +
5100 + private function do_updatecustomer($stay = false) {
5101 + $dbo = JFactory::getDbo();
5102 + $mainframe = JFactory::getApplication();
5103 + $pfirst_name = VikRequest::getString('first_name', '', 'request');
5104 + $plast_name = VikRequest::getString('last_name', '', 'request');
5105 + $pcompany = VikRequest::getString('company', '', 'request');
5106 + $pvat = VikRequest::getString('vat', '', 'request');
5107 + $pemail = VikRequest::getString('email', '', 'request');
5108 + $pphone = VikRequest::getString('phone', '', 'request');
5109 + $pcountry = VikRequest::getString('country', '', 'request');
5110 + $pstate = VikRequest::getString('state', '', 'request');
5111 + $ppin = VikRequest::getString('pin', '', 'request');
5112 + $pujid = VikRequest::getInt('ujid', '', 'request');
5113 + $paddress = VikRequest::getString('address', '', 'request');
5114 + $pcity = VikRequest::getString('city', '', 'request');
5115 + $pzip = VikRequest::getString('zip', '', 'request');
5116 + $pfisccode = VikRequest::getString('fisccode', '', 'request');
5117 + $ppec = VikRequest::getString('pec', '', 'request');
5118 + $precipcode = VikRequest::getString('recipcode', '', 'request');
5119 + $pgender = VikRequest::getString('gender', '', 'request');
5120 + $pgender = in_array($pgender, array('F', 'M')) ? $pgender : '';
5121 + $pbdate = VikRequest::getString('bdate', '', 'request');
5122 + $ppbirth = VikRequest::getString('pbirth', '', 'request');
5123 + $pdoctype = VikRequest::getString('doctype', '', 'request');
5124 + $pdocnum = VikRequest::getString('docnum', '', 'request');
5125 + $pnotes = VikRequest::getString('notes', '', 'request');
5126 + $pscandocimg = VikRequest::getString('scandocimg', '', 'request');
5127 + $pischannel = VikRequest::getInt('ischannel', '', 'request');
5128 + $pcommission = VikRequest::getFloat('commission', '', 'request');
5129 + $pcalccmmon = VikRequest::getInt('calccmmon', '', 'request');
5130 + $papplycmmon = VikRequest::getInt('applycmmon', '', 'request');
5131 + $pchname = VikRequest::getString('chname', '', 'request');
5132 + $pchcolor = VikRequest::getString('chcolor', '', 'request');
5133 + $pwhere = VikRequest::getInt('where', '', 'request');
5134 + $ptmpl = VikRequest::getString('tmpl', '', 'request');
5135 + $pcheckin = VikRequest::getInt('checkin', '', 'request');
5136 + $pbid = VikRequest::getInt('bid', '', 'request');
5137 + $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
5138 + if (!empty($pwhere) && !empty($pfirst_name) && !empty($plast_name) && !empty($pemail)) {
5139 + $q = "SELECT * FROM `#__vikbooking_customers` WHERE `id`=".(int)$pwhere." LIMIT 1;";
5140 + $dbo->setQuery($q);
5141 + $dbo->execute();
5142 + if ($dbo->getNumRows() == 1) {
5143 + $customer = $dbo->loadAssoc();
5144 + } else {
5145 + $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5146 + exit;
5147 + }
5148 + /**
5149 + * Existing customers are recognized by equal first name, last name and email address.
5150 + *
5151 + * @since 1.3.0
5152 + */
5153 + $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;";
5154 + $dbo->setQuery($q);
5155 + $dbo->execute();
5156 + if ($dbo->getNumRows() == 0) {
5157 + $cpin = VikBooking::getCPinIstance();
5158 + if (empty($ppin)) {
5159 + $ppin = $customer['pin'];
5160 + } elseif ($cpin->pinExists($ppin, $customer['pin'])) {
5161 + $ppin = $cpin->generateUniquePin();
5162 + }
5163 + //file upload
5164 + jimport('joomla.filesystem.file');
5165 + $pimg = VikRequest::getVar('docimg', null, 'files', 'array');
5166 + $gimg = "";
5167 + if (isset($pimg) && strlen(trim($pimg['name']))) {
5168 + $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($pimg['name'])));
5169 + $src = $pimg['tmp_name'];
5170 + $dest = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans'.DIRECTORY_SEPARATOR;
5171 + $j = "";
5172 + if (file_exists($dest.$filename)) {
5173 + $j = rand(171, 1717);
5174 + while (file_exists($dest.$j.$filename)) {
5175 + $j++;
5176 + }
5177 + }
5178 + $finaldest = $dest.$j.$filename;
5179 + $check = getimagesize($pimg['tmp_name']);
5180 + if (($check[2] & imagetypes()) || preg_match("/application\/(zip|pdf)$/", $pimg['type'])) {
5181 + if (VikBooking::uploadFile($src, $finaldest)) {
5182 + $gimg = $j.$filename;
5183 + } else {
5184 + VikError::raiseWarning('', 'Error while uploading image');
5185 + }
5186 + } else {
5187 + VikError::raiseWarning('', 'Uploaded file is not an Image');
5188 + }
5189 + } elseif (!empty($pscandocimg)) {
5190 + $gimg = $pscandocimg;
5191 + }
5192 + //
5193 + $pischannel = $pischannel > 0 ? 1 : 0;
5194 + $pcalccmmon = $pcalccmmon > 0 ? 1 : 0;
5195 + $papplycmmon = $papplycmmon > 0 ? 1 : 0;
5196 + $pchname = str_replace(' ', '', trim($pchname));
5197 + $pchname = strlen($pchname) <= 0 && $pischannel > 0 ? str_replace(' ', '', trim($pfirst_name.' '.$plast_name)) : $pchname;
5198 + $chparams = array(
5199 + 'commission' => ($pcommission > 0.00 ? $pcommission : 0),
5200 + 'calccmmon' => $pcalccmmon,
5201 + 'applycmmon' => $papplycmmon,
5202 + 'chcolor' => $pchcolor,
5203 + 'chname' => $pchname
5204 + );
5205 +
5206 + /**
5207 + * Customer profile picture (URL or uploaded file).
5208 + *
5209 + * @since 1.15.3 (J) - 1.5.5 (WP)
5210 + */
5211 + $customer_pic = VikRequest::getString('pic', '', 'request');
5212 + $customer_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
5213 + if (is_array($customer_pic_img) && !empty($customer_pic_img['name'])) {
5214 + $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($customer_pic_img['name'])));
5215 + $src = $customer_pic_img['tmp_name'];
5216 + $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
5217 + $j = "";
5218 + if (is_file($dest.$filename)) {
5219 + $j = rand(1, 99999);
5220 + while (is_file($dest . $j .$filename)) {
5221 + $j++;
5222 + }
5223 + }
5224 + $finaldest = $dest . $j . $filename;
5225 + $check = getimagesize($customer_pic_img['tmp_name']);
5226 + if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
5227 + if (VikBooking::uploadFile($src, $finaldest)) {
5228 + $customer_pic = $j . $filename;
5229 + } else {
5230 + VikError::raiseWarning('', 'Error while uploading image');
5231 + }
5232 + } else {
5233 + VikError::raiseWarning('', 'Uploaded file is not an Image');
5234 + }
5235 + }
5236 +
5237 + // update customer object
5238 + $new_customer = new stdClass;
5239 + $new_customer->id = (int)$pwhere;
5240 + $new_customer->first_name = $pfirst_name;
5241 + $new_customer->last_name = $plast_name;
5242 + $new_customer->email = $pemail;
5243 + $new_customer->phone = $pphone;
5244 + $new_customer->country = $pcountry;
5245 + $new_customer->pin = $ppin;
5246 + $new_customer->ujid = $pujid;
5247 + $new_customer->address = $paddress;
5248 + $new_customer->city = $pcity;
5249 + $new_customer->zip = $pzip;
5250 + $new_customer->state = $pstate;
5251 + $new_customer->doctype = $pdoctype;
5252 + $new_customer->docnum = $pdocnum;
5253 + if (!empty($gimg)) {
5254 + $new_customer->docimg = $gimg;
5255 + }
5256 + $new_customer->notes = $pnotes;
5257 + $new_customer->ischannel = $pischannel;
5258 + $new_customer->chdata = json_encode($chparams);
5259 + $new_customer->company = $pcompany;
5260 + $new_customer->vat = $pvat;
5261 + $new_customer->gender = $pgender;
5262 + $new_customer->bdate = $pbdate;
5263 + $new_customer->pbirth = $ppbirth;
5264 + $new_customer->fisccode = $pfisccode;
5265 + $new_customer->pec = $ppec;
5266 + $new_customer->recipcode = $precipcode;
5267 + $new_customer->pic = $customer_pic;
5268 + /**
5269 + * We need to update the previous information stored through
5270 + * the custom fields when making a reservation for/by this client.
5271 + *
5272 + * @since 1.13
5273 + */
5274 + $skip_prev_fields = array(
5275 + 'id',
5276 + 'ujid',
5277 + 'docimg',
5278 + 'ischannel',
5279 + 'chdata',
5280 + 'notes',
5281 + );
5282 + if (!empty($customer['cfields'])) {
5283 + $custf_info = json_decode($customer['cfields'], true);
5284 + foreach ($new_customer as $fname => $fnewval) {
5285 + if (!isset($customer[$fname]) || in_array($fname, $skip_prev_fields)) {
5286 + continue;
5287 + }
5288 + // seek for old value in custom fields submitted
5289 + foreach ($custf_info as $k => $v) {
5290 + if (!empty($customer[$fname]) && $v == $customer[$fname]) {
5291 + // field found, replace it with the new value
5292 + $custf_info[$k] = $fnewval;
5293 + }
5294 + }
5295 + }
5296 + // update value on db
5297 + $new_customer->cfields = json_encode($custf_info);
5298 + }
5299 +
5300 + // trigger the customer before-update event
5301 + $cpin->pluginCustomerSync($new_customer->id, 'update', (array)$new_customer, $before = true);
5302 +
5303 + // update customer record
5304 + $dbo->updateObject('#__vikbooking_customers', $new_customer, 'id');
5305 +
5306 + // trigger the customer after-save event
5307 + $cpin->pluginCustomerSync($new_customer->id, 'update', (array)$new_customer, $before = false);
5308 +
5309 + // update all the bookings affected by this Customer ID as a sales channel
5310 + $source_name = 'customer'.$pwhere.'_'.$pchname;
5311 + if ($pischannel > 0) {
5312 + $oid_clause = '';
5313 + if ($customer['ischannel'] < 1) {
5314 + //Was not a sales channel but now it is, so update all his bookings
5315 + $q = "SELECT `o`.`idorderota`, `co`.`idorder`
5316 + FROM `#__vikbooking_customers_orders` AS `co`
5317 + LEFT JOIN `#__vikbooking_orders` AS `o` ON `co`.`idorder`=`o`.`id`
5318 + WHERE `co`.`idcustomer`=".$customer['id'].";";
5319 + $dbo->setQuery($q);
5320 + $all_bids = $dbo->loadAssocList();
5321 + if ($all_bids) {
5322 + $bids = array();
5323 + foreach ($all_bids as $bid) {
5324 + if (empty($idorderota) && !in_array($bid['idorder'], $bids)) {
5325 + $bids[] = $bid['idorder'];
5326 + }
5327 + }
5328 + if ($bids) {
5329 + $oid_clause = " OR `id` IN (".implode(',', $bids).")";
5330 + }
5331 + }
5332 + }
5333 + $q = "UPDATE `#__vikbooking_orders` SET `channel`=".$dbo->quote($source_name)." WHERE `channel` LIKE 'customer".$pwhere."%'".$oid_clause.";";
5334 + } else {
5335 + $q = "UPDATE `#__vikbooking_orders` SET `channel`=NULL,`cmms`=NULL WHERE `channel` LIKE 'customer".$pwhere."%';";
5336 + }
5337 + $dbo->setQuery($q);
5338 + $dbo->execute();
5339 + //
5340 + $mainframe->enqueueMessage(JText::translate('VBCUSTOMERSAVED'));
5341 + } else {
5342 + //email already exists
5343 + $ex_customer = $dbo->loadAssoc();
5344 + //check if coming from the Check-in view or not
5345 + if (!empty($pcheckin) && !empty($pbid)) {
5346 + VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5347 + /**
5348 + * @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
5349 + */
5350 + $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5351 + //
5352 + exit;
5353 + } elseif (!empty($pgoto)) {
5354 + // check if coming from a specific task
5355 + VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5356 + $mainframe->redirect(base64_decode($pgoto));
5357 + exit;
5358 + } else {
5359 + 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>');
5360 + $mainframe->redirect("index.php?option=com_vikbooking&task=editcustomer&cid[]=".$pwhere);
5361 + exit;
5362 + }
5363 + }
5364 + }
5365 +
5366 + //check if coming from the Check-in view
5367 + if (!empty($pcheckin) && !empty($pbid)) {
5368 + /**
5369 + * @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
5370 + */
5371 + $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $pbid);
5372 + exit;
5373 + }
5374 +
5375 + if ($stay) {
5376 + $mainframe->redirect("index.php?option=com_vikbooking&task=editcustomer&cid[]=" . $pwhere . (!empty($pgoto) ? '&goto=' . $pgoto : ''));
5377 + exit;
5378 + }
5379 +
5380 + // check if coming from a specific task
5381 + if (!empty($pgoto)) {
5382 + $mainframe->redirect(base64_decode($pgoto));
5383 + exit;
5384 + }
5385 +
5386 + $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5387 + }
5388 +
5389 + public function savecustomer() {
5390 + if (!JSession::checkToken()) {
5391 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5392 + }
5393 + $dbo = JFactory::getDbo();
5394 + $mainframe = JFactory::getApplication();
5395 + $pfirst_name = VikRequest::getString('first_name', '', 'request');
5396 + $plast_name = VikRequest::getString('last_name', '', 'request');
5397 + $pcompany = VikRequest::getString('company', '', 'request');
5398 + $pvat = VikRequest::getString('vat', '', 'request');
5399 + $pemail = VikRequest::getString('email', '', 'request');
5400 + $pphone = VikRequest::getString('phone', '', 'request');
5401 + $pcountry = VikRequest::getString('country', '', 'request');
5402 + $pstate = VikRequest::getString('state', '', 'request');
5403 + $ppin = VikRequest::getString('pin', '', 'request');
5404 + $pujid = VikRequest::getInt('ujid', '', 'request');
5405 + $paddress = VikRequest::getString('address', '', 'request');
5406 + $pcity = VikRequest::getString('city', '', 'request');
5407 + $pzip = VikRequest::getString('zip', '', 'request');
5408 + $pfisccode = VikRequest::getString('fisccode', '', 'request');
5409 + $ppec = VikRequest::getString('pec', '', 'request');
5410 + $precipcode = VikRequest::getString('recipcode', '', 'request');
5411 + $pgender = VikRequest::getString('gender', '', 'request');
5412 + $pgender = in_array($pgender, array('F', 'M')) ? $pgender : '';
5413 + $pbdate = VikRequest::getString('bdate', '', 'request');
5414 + $ppbirth = VikRequest::getString('pbirth', '', 'request');
5415 + $pdoctype = VikRequest::getString('doctype', '', 'request');
5416 + $pdocnum = VikRequest::getString('docnum', '', 'request');
5417 + $pnotes = VikRequest::getString('notes', '', 'request');
5418 + $pscandocimg = VikRequest::getString('scandocimg', '', 'request');
5419 + $pischannel = VikRequest::getInt('ischannel', '', 'request');
5420 + $pcommission = VikRequest::getFloat('commission', '', 'request');
5421 + $pcalccmmon = VikRequest::getInt('calccmmon', '', 'request');
5422 + $papplycmmon = VikRequest::getInt('applycmmon', '', 'request');
5423 + $pchname = VikRequest::getString('chname', '', 'request');
5424 + $pchcolor = VikRequest::getString('chcolor', '', 'request');
5425 + $ptmpl = VikRequest::getString('tmpl', '', 'request');
5426 + $pcheckin = VikRequest::getInt('checkin', '', 'request');
5427 + $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
5428 + $pbid = VikRequest::getInt('bid', '', 'request');
5429 + if (!empty($pfirst_name) && !empty($plast_name) && !empty($pemail)) {
5430 + $cpin = VikBooking::getCPinIstance();
5431 + /**
5432 + * Existing customers are recognized by equal first name, last name and email address.
5433 + *
5434 + * @since 1.3.0
5435 + */
5436 + $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;";
5437 + $dbo->setQuery($q);
5438 + $dbo->execute();
5439 + if ($dbo->getNumRows() == 0) {
5440 + if (empty($ppin)) {
5441 + $ppin = $cpin->generateUniquePin();
5442 + } elseif ($cpin->pinExists($ppin)) {
5443 + $ppin = $cpin->generateUniquePin();
5444 + }
5445 + //file upload
5446 + jimport('joomla.filesystem.file');
5447 + $pimg = VikRequest::getVar('docimg', null, 'files', 'array');
5448 + $gimg = "";
5449 + if (isset($pimg) && strlen(trim($pimg['name']))) {
5450 + $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($pimg['name'])));
5451 + $src = $pimg['tmp_name'];
5452 + $dest = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans'.DIRECTORY_SEPARATOR;
5453 + $j = "";
5454 + if (file_exists($dest.$filename)) {
5455 + $j = rand(171, 1717);
5456 + while (file_exists($dest.$j.$filename)) {
5457 + $j++;
5458 + }
5459 + }
5460 + $finaldest = $dest.$j.$filename;
5461 + $check = getimagesize($pimg['tmp_name']);
5462 + if (($check[2] & imagetypes()) || preg_match("/application\/(zip|pdf)$/", $pimg['type'])) {
5463 + if (VikBooking::uploadFile($src, $finaldest)) {
5464 + $gimg = $j.$filename;
5465 + } else {
5466 + VikError::raiseWarning('', 'Error while uploading image');
5467 + }
5468 + } else {
5469 + VikError::raiseWarning('', 'Uploaded file is not an Image');
5470 + }
5471 + } elseif (!empty($pscandocimg)) {
5472 + $gimg = $pscandocimg;
5473 + }
5474 + //
5475 + $pischannel = $pischannel > 0 ? 1 : 0;
5476 + $pcalccmmon = $pcalccmmon > 0 ? 1 : 0;
5477 + $papplycmmon = $papplycmmon > 0 ? 1 : 0;
5478 + $pchname = str_replace(' ', '', trim($pchname));
5479 + $pchname = strlen($pchname) <= 0 && $pischannel > 0 ? str_replace(' ', '', trim($pfirst_name.' '.$plast_name)) : $pchname;
5480 + $chparams = array(
5481 + 'commission' => ($pcommission > 0.00 ? $pcommission : 0),
5482 + 'calccmmon' => $pcalccmmon,
5483 + 'applycmmon' => $papplycmmon,
5484 + 'chcolor' => $pchcolor,
5485 + 'chname' => $pchname
5486 + );
5487 +
5488 + /**
5489 + * Customer profile picture (URL or uploaded file).
5490 + *
5491 + * @since 1.15.3 (J) - 1.5.5 (WP)
5492 + */
5493 + $customer_pic = VikRequest::getString('pic', '', 'request');
5494 + $customer_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
5495 + if (is_array($customer_pic_img) && !empty($customer_pic_img['name'])) {
5496 + $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($customer_pic_img['name'])));
5497 + $src = $customer_pic_img['tmp_name'];
5498 + $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
5499 + $j = "";
5500 + if (is_file($dest.$filename)) {
5501 + $j = rand(1, 99999);
5502 + while (is_file($dest . $j .$filename)) {
5503 + $j++;
5504 + }
5505 + }
5506 + $finaldest = $dest . $j . $filename;
5507 + $check = getimagesize($customer_pic_img['tmp_name']);
5508 + if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
5509 + if (VikBooking::uploadFile($src, $finaldest)) {
5510 + $customer_pic = $j . $filename;
5511 + } else {
5512 + VikError::raiseWarning('', 'Error while uploading image');
5513 + }
5514 + } else {
5515 + VikError::raiseWarning('', 'Uploaded file is not an Image');
5516 + }
5517 + }
5518 +
5519 + // build customer record
5520 + $customer_obj = new stdClass;
5521 + $customer_obj->first_name = $pfirst_name;
5522 + $customer_obj->last_name = $plast_name;
5523 + $customer_obj->email = $pemail;
5524 + $customer_obj->phone = $pphone;
5525 + $customer_obj->country = $pcountry;
5526 + $customer_obj->pin = $ppin;
5527 + $customer_obj->ujid = $pujid;
5528 + $customer_obj->address = $paddress;
5529 + $customer_obj->city = $pcity;
5530 + $customer_obj->zip = $pzip;
5531 + $customer_obj->state = $pstate;
5532 + $customer_obj->doctype = $pdoctype;
5533 + $customer_obj->docnum = $pdocnum;
5534 + $customer_obj->docimg = $gimg;
5535 + $customer_obj->notes = $pnotes;
5536 + $customer_obj->ischannel = $pischannel;
5537 + $customer_obj->chdata = json_encode($chparams);
5538 + $customer_obj->company = $pcompany;
5539 + $customer_obj->vat = $pvat;
5540 + $customer_obj->gender = $pgender;
5541 + $customer_obj->bdate = $pbdate;
5542 + $customer_obj->pbirth = $ppbirth;
5543 + $customer_obj->fisccode = $pfisccode;
5544 + $customer_obj->pec = $ppec;
5545 + $customer_obj->recipcode = $precipcode;
5546 + $customer_obj->pic = !empty($customer_pic) ? $customer_pic : null;
5547 +
5548 + // trigger the customer before-insert event
5549 + $cpin->pluginCustomerSync(0, 'insert', (array)$customer_obj, $before = true);
5550 +
5551 + // insert the new customer record
5552 + $dbo->insertObject('#__vikbooking_customers', $customer_obj, 'id');
5553 + $lid = isset($customer_obj->id) ? $customer_obj->id : null;
5554 +
5555 + // trigger the customer after-save event
5556 + $cpin->pluginCustomerSync($lid, 'insert', (array)$customer_obj, $before = false);
5557 +
5558 + if (!empty($lid)) {
5559 + $mainframe->enqueueMessage(JText::translate('VBCUSTOMERSAVED'));
5560 + //check if coming from the Check-in view
5561 + if (!empty($pcheckin) && !empty($pbid)) {
5562 + $cpin->setNewPin($ppin);
5563 + $cpin->setNewCustomerId($lid);
5564 + $cpin->saveCustomerBooking($pbid);
5565 + /**
5566 + * @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
5567 + */
5568 + $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5569 + //
5570 + exit;
5571 + }
5572 + // check if coming from a specific task
5573 + if (!empty($pgoto) && !empty($pbid)) {
5574 + $cpin->setNewPin($ppin);
5575 + $cpin->setNewCustomerId($lid);
5576 + $cpin->saveCustomerBooking($pbid);
5577 + $mainframe->redirect(base64_decode($pgoto));
5578 + exit;
5579 + }
5580 + }
5581 + } else {
5582 + //email already exists
5583 + $ex_customer = $dbo->loadAssoc();
5584 + //check if coming from the Check-in view or not
5585 + if (!empty($pcheckin) && !empty($pbid)) {
5586 + $cpin->setNewPin($ex_customer['pin']);
5587 + $cpin->setNewCustomerId($ex_customer['id']);
5588 + $cpin->saveCustomerBooking($pbid);
5589 + VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5590 + /**
5591 + * @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
5592 + */
5593 + $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pbid);
5594 + //
5595 + exit;
5596 + } elseif (!empty($pgoto) && !empty($pbid)) {
5597 + // check if coming from a specific task
5598 + $cpin->setNewPin($ex_customer['pin']);
5599 + $cpin->setNewCustomerId($ex_customer['id']);
5600 + $cpin->saveCustomerBooking($pbid);
5601 + VikError::raiseWarning('', JText::translate('VBERRCUSTOMEREMAILEXISTS').' ('.$ex_customer['first_name'].' '.$ex_customer['last_name'].')');
5602 + $mainframe->redirect(base64_decode($pgoto));
5603 + exit;
5604 + } else {
5605 + 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>');
5606 + }
5607 + }
5608 + }
5609 + $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5610 + }
5611 +
5612 + public function customers() {
5613 + VikBookingHelper::printHeader("22");
5614 +
5615 + VikRequest::setVar('view', VikRequest::getCmd('view', 'customers'));
5616 +
5617 + parent::display();
5618 +
5619 + if (VikBooking::showFooter()) {
5620 + VikBookingHelper::printFooter();
5621 + }
5622 + }
5623 +
5624 + public function newcustomer() {
5625 + VikBookingHelper::printHeader("22");
5626 +
5627 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomer'));
5628 +
5629 + parent::display();
5630 +
5631 + if (VikBooking::showFooter()) {
5632 + VikBookingHelper::printFooter();
5633 + }
5634 + }
5635 +
5636 + public function editcustomer() {
5637 + VikBookingHelper::printHeader("22");
5638 +
5639 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomer'));
5640 +
5641 + parent::display();
5642 +
5643 + if (VikBooking::showFooter()) {
5644 + VikBookingHelper::printFooter();
5645 + }
5646 + }
5647 +
5648 + public function removecustomers()
5649 + {
5650 + if (!JSession::checkToken()) {
5651 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5652 + }
5653 +
5654 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
5655 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5656 + }
5657 +
5658 + $ids = VikRequest::getVar('cid', array(0));
5659 + if ($ids) {
5660 + $dbo = JFactory::getDBO();
5661 + $cpin = VikBooking::getCPinIstance();
5662 + foreach ($ids as $d) {
5663 + $cpin->pluginCustomerSync($d, 'delete');
5664 + $q = "DELETE FROM `#__vikbooking_customers` WHERE `id`=".(int)$d.";";
5665 + $dbo->setQuery($q);
5666 + $dbo->execute();
5667 + }
5668 + }
5669 + $mainframe = JFactory::getApplication();
5670 + $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
5671 + }
5672 +
5673 + public function restrictions() {
5674 + VikBookingHelper::printHeader("restrictions");
5675 +
5676 + VikRequest::setVar('view', VikRequest::getCmd('view', 'restrictions'));
5677 +
5678 + parent::display();
5679 +
5680 + if (VikBooking::showFooter()) {
5681 + VikBookingHelper::printFooter();
5682 + }
5683 + }
5684 +
5685 + public function newrestriction() {
5686 + VikBookingHelper::printHeader("restrictions");
5687 +
5688 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managerestriction'));
5689 +
5690 + parent::display();
5691 +
5692 + if (VikBooking::showFooter()) {
5693 + VikBookingHelper::printFooter();
5694 + }
5695 + }
5696 +
5697 + public function editrestriction() {
5698 + VikBookingHelper::printHeader("restrictions");
5699 +
5700 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managerestriction'));
5701 +
5702 + parent::display();
5703 +
5704 + if (VikBooking::showFooter()) {
5705 + VikBookingHelper::printFooter();
5706 + }
5707 + }
5708 +
5709 + public function createrestriction()
5710 + {
5711 + if (!JSession::checkToken()) {
5712 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5713 + }
5714 +
5715 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
5716 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5717 + }
5718 +
5719 + $dbo = JFactory::getDBO();
5720 + $session = JFactory::getSession();
5721 + $mainframe = JFactory::getApplication();
5722 + $updforvcm = $session->get('vbVcmRatesUpd', '');
5723 + $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
5724 + $pname = VikRequest::getString('name', '', 'request');
5725 + $pmonth = VikRequest::getInt('month', '', 'request');
5726 + $pmonth = empty($pmonth) ? 0 : $pmonth;
5727 + $pname = empty($pname) ? 'Restriction '.$pmonth : $pname;
5728 + $pdfrom = VikRequest::getString('dfrom', '', 'request');
5729 + $pdto = VikRequest::getString('dto', '', 'request');
5730 + $pwday = VikRequest::getString('wday', '', 'request');
5731 + $pwdaytwo = VikRequest::getString('wdaytwo', '', 'request');
5732 + $pwdaytwo = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday == $pwdaytwo ? '' : $pwdaytwo;
5733 + $pcomboa = VikRequest::getString('comboa', '', 'request');
5734 + $pcomboa = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboa : '';
5735 + $pcombob = VikRequest::getString('combob', '', 'request');
5736 + $pcombob = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombob : '';
5737 + $pcomboc = VikRequest::getString('comboc', '', 'request');
5738 + $pcomboc = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboc : '';
5739 + $pcombod = VikRequest::getString('combod', '', 'request');
5740 + $pcombod = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombod : '';
5741 + $combostr = '';
5742 + $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboa) ? $pcomboa.':' : ':';
5743 + $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombob) ? $pcombob.':' : ':';
5744 + $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboc) ? $pcomboc.':' : ':';
5745 + $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombod) ? $pcombod : '';
5746 + $pminlos = VikRequest::getInt('minlos', '', 'request');
5747 + $pminlos = $pminlos < 1 ? 1 : $pminlos;
5748 + $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
5749 + $pmaxlos = empty($pmaxlos) ? 0 : $pmaxlos;
5750 + $pmultiplyminlos = VikRequest::getString('multiplyminlos', '', 'request');
5751 + $pmultiplyminlos = empty($pmultiplyminlos) ? 0 : 1;
5752 + $pallrooms = VikRequest::getString('allrooms', '', 'request');
5753 + $pallrooms = $pallrooms == "1" ? 1 : 0;
5754 + $pidrooms = VikRequest::getVar('idrooms', array(0));
5755 + $ridr = '';
5756 + $roomidsforsess = array();
5757 + if (!empty($pidrooms) && @count($pidrooms) && $pallrooms == 0) {
5758 + foreach ($pidrooms as $idr) {
5759 + if (empty($idr)) {
5760 + continue;
5761 + }
5762 + $ridr .= '-'.$idr.'-;';
5763 + $roomidsforsess[] = (int)$idr;
5764 + }
5765 + } elseif ($pallrooms > 0) {
5766 + $q = "SELECT `id` FROM `#__vikbooking_rooms`;";
5767 + $dbo->setQuery($q);
5768 + $dbo->execute();
5769 + if ($dbo->getNumRows() > 0) {
5770 + $fetchids = $dbo->loadAssocList();
5771 + foreach ($fetchids as $fetchid) {
5772 + $roomidsforsess[] = (int)$fetchid['id'];
5773 + }
5774 + }
5775 + }
5776 + $pcta = VikRequest::getInt('cta', '', 'request');
5777 + $pctd = VikRequest::getInt('ctd', '', 'request');
5778 + $pctad = VikRequest::getVar('ctad', array());
5779 + $pctdd = VikRequest::getVar('ctdd', array());
5780 + if ($pminlos == 1 && strlen($pwday) == 0 && empty($pctad) && empty($pctdd) && $pmaxlos < 1) {
5781 + // VBO 1.11 - we now allow restrictions with just 1 night of stay
5782 + // VikError::raiseWarning('', JText::translate('VBUSELESSRESTRICTION'));
5783 + // $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5784 + // exit;
5785 + }
5786 +
5787 + //check if there are restrictions for this month
5788 + if ($pmonth > 0) {
5789 + $q = "SELECT `id` FROM `#__vikbooking_restrictions` WHERE `month`='".$pmonth."';";
5790 + $dbo->setQuery($q);
5791 + $dbo->execute();
5792 + if ($dbo->getNumRows() > 0) {
5793 + VikError::raiseWarning('', JText::translate('VBRESTRICTIONMONTHEXISTS'));
5794 + $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5795 + exit;
5796 + }
5797 + $pdfrom = 0;
5798 + $pdto = 0;
5799 + } else {
5800 + //dates range
5801 + if (empty($pdfrom) || empty($pdto)) {
5802 + VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
5803 + $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5804 + exit;
5805 + } else {
5806 + $housto = $pdfrom == $pdto ? 23 : 0;
5807 + $minsto = $pdfrom == $pdto ? 59 : 0;
5808 + $secsto = $pdfrom == $pdto ? 59 : 0;
5809 + $pdfrom = VikBooking::getDateTimestamp($pdfrom, 0, 0);
5810 + $pdto = VikBooking::getDateTimestamp($pdto, $housto, $minsto, $secsto);
5811 + }
5812 + if ($pdfrom > $pdto) {
5813 + // invalid dates in the past
5814 + VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
5815 + $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5816 + exit;
5817 + }
5818 + }
5819 + //CTA and CTD
5820 + $setcta = array();
5821 + $setctd = array();
5822 + if ($pcta > 0 && count($pctad) > 0) {
5823 + foreach ($pctad as $ctwd) {
5824 + if (strlen($ctwd)) {
5825 + $setcta[] = '-'.(int)$ctwd.'-';
5826 + }
5827 + }
5828 + }
5829 + if ($pctd > 0 && count($pctdd) > 0) {
5830 + foreach ($pctdd as $ctwd) {
5831 + if (strlen($ctwd)) {
5832 + $setctd[] = '-'.(int)$ctwd.'-';
5833 + }
5834 + }
5835 + }
5836 + //
5837 + //update session values
5838 + if (!($pdfrom > 0)) {
5839 + $attemptyear = (int)date('Y');
5840 + $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
5841 + if ($attemptfrom < time()) {
5842 + $attemptyear++;
5843 + $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
5844 + }
5845 + $attemptto = mktime(0, 0, 0, $pmonth, date('t', $attemptfrom), $attemptyear);
5846 + } else {
5847 + $attemptfrom = $pdfrom;
5848 + $attemptto = $pdto;
5849 + }
5850 + $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
5851 + if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
5852 + $updforvcm['dfrom'] = $updforvcm['dfrom'] > $attemptfrom ? $attemptfrom : $updforvcm['dfrom'];
5853 + } else {
5854 + $updforvcm['dfrom'] = $attemptfrom;
5855 + }
5856 + if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
5857 + $updforvcm['dto'] = $updforvcm['dto'] < $attemptto ? $attemptto : $updforvcm['dto'];
5858 + } else {
5859 + $updforvcm['dto'] = $attemptto;
5860 + }
5861 + if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
5862 + foreach ($roomidsforsess as $rid) {
5863 + if (!in_array($rid, $updforvcm['rooms'])) {
5864 + $updforvcm['rooms'][] = $rid;
5865 + }
5866 + }
5867 + } else {
5868 + $updforvcm['rooms'] = $roomidsforsess;
5869 + }
5870 + if (!array_key_exists('rplans', $updforvcm) || !is_array($updforvcm['rplans'])) {
5871 + $updforvcm['rplans'] = array();
5872 + }
5873 + $session->set('vbVcmRatesUpd', $updforvcm);
5874 + //
5875 + $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").");";
5876 + $dbo->setQuery($q);
5877 + $dbo->execute();
5878 + $lid = $dbo->insertid();
5879 + if (!empty($lid)) {
5880 + /**
5881 + * Repeat restriction on the selected week days until the limit
5882 + *
5883 + * @since 1.13
5884 + */
5885 + $prepeat = VikRequest::getInt('repeat', 0, 'request');
5886 + $prepeatuntil = VikRequest::getString('repeatuntil', '', 'request');
5887 + if ($prepeat > 0 && !empty($prepeatuntil) && $pdfrom > 0 && $pdto > 0) {
5888 + $repeat_intervals = array();
5889 + $start = getdate($pdfrom);
5890 + $end = getdate($pdto);
5891 + $wdays = array();
5892 + while ($start[0] <= $end[0]) {
5893 + // push requested week day
5894 + array_push($wdays, $start['wday']);
5895 + // next day
5896 + $start = getdate(mktime($start['hours'], $start['minutes'], $start['seconds'], $start['mon'], ($start['mday'] + 1), $start['year']));
5897 + }
5898 + $dtuntil = VikBooking::getDateTimestamp($prepeatuntil, 23, 59, 59);
5899 + if (count($wdays) < 7 && $dtuntil > $pdto) {
5900 + // increment end date for the repeat
5901 + $end = getdate(mktime($end['hours'], $end['minutes'], $end['seconds'], $end['mon'], ($end['mday'] + 1), $end['year']));
5902 + //
5903 + $until_info = getdate($dtuntil);
5904 + $interval = array();
5905 + while ($end[0] <= $until_info[0]) {
5906 + if (in_array($end['wday'], $wdays)) {
5907 + if (!isset($interval['from'])) {
5908 + $interval['from'] = $end[0];
5909 + }
5910 + $interval['to'] = $end[0];
5911 + } else {
5912 + if (isset($interval['from'])) {
5913 + // append interval
5914 + array_push($repeat_intervals, $interval);
5915 + // reset interval
5916 + $interval = array();
5917 + }
5918 + }
5919 + // next day
5920 + $end = getdate(mktime($end['hours'], $end['minutes'], $end['seconds'], $end['mon'], ($end['mday'] + 1), $end['year']));
5921 + }
5922 + if (isset($interval['from'])) {
5923 + // append last hanging interval
5924 + array_push($repeat_intervals, $interval);
5925 + }
5926 + if (count($repeat_intervals)) {
5927 + // create the repeated records for the calculated intervals
5928 + $repeat_count = 2;
5929 + foreach ($repeat_intervals as $rp) {
5930 + if (date('Y-m-d', $rp['from']) == date('Y-m-d', $rp['to'])) {
5931 + // adjust time in case of equal dates (1 single day restriction)
5932 + $rpfrom = getdate($rp['from']);
5933 + $rpto = getdate($rp['to']);
5934 + $rp['from'] = mktime(0, 0, 0, $rpfrom['mon'], $rpfrom['mday'], $rpfrom['year']);
5935 + /**
5936 + * The end date of the restriction must cover the whole day until 23:59:59.
5937 + *
5938 + * @since 1.15.4 (J) - 1.5.4 (WP)
5939 + */
5940 + $rp['to'] = mktime(23, 59, 59, $rpto['mon'], $rpto['mday'], $rpto['year']);
5941 + }
5942 + // adjust name
5943 + $restr_rp_name = $pname . " #{$repeat_count}";
5944 + //
5945 + $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").");";
5946 + $dbo->setQuery($q);
5947 + $dbo->execute();
5948 + $lid = $dbo->insertid();
5949 + if (!empty($lid)) {
5950 + $repeat_count++;
5951 + }
5952 + }
5953 + }
5954 + }
5955 + }
5956 + //
5957 + $mainframe->enqueueMessage(JText::translate('VBRESTRICTIONSAVED'));
5958 + $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
5959 + } else {
5960 + VikError::raiseWarning('', 'Error while saving');
5961 + $mainframe->redirect("index.php?option=com_vikbooking&task=newrestriction");
5962 + }
5963 + }
5964 +
5965 + public function updaterestriction()
5966 + {
5967 + if (!JSession::checkToken()) {
5968 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
5969 + }
5970 +
5971 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
5972 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
5973 + }
5974 +
5975 + $dbo = JFactory::getDBO();
5976 + $session = JFactory::getSession();
5977 + $mainframe = JFactory::getApplication();
5978 + $updforvcm = $session->get('vbVcmRatesUpd', '');
5979 + $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
5980 + $pwhere = VikRequest::getInt('where', '', 'request');
5981 + $pname = VikRequest::getString('name', '', 'request');
5982 + $pmonth = VikRequest::getInt('month', '', 'request');
5983 + $pmonth = empty($pmonth) ? 0 : $pmonth;
5984 + $pname = empty($pname) ? 'Restriction '.$pmonth : $pname;
5985 + $pdfrom = VikRequest::getString('dfrom', '', 'request');
5986 + $pdto = VikRequest::getString('dto', '', 'request');
5987 + $pwday = VikRequest::getString('wday', '', 'request');
5988 + $pwdaytwo = VikRequest::getString('wdaytwo', '', 'request');
5989 + $pwdaytwo = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday == $pwdaytwo ? '' : $pwdaytwo;
5990 + $pcomboa = VikRequest::getString('comboa', '', 'request');
5991 + $pcomboa = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboa : '';
5992 + $pcombob = VikRequest::getString('combob', '', 'request');
5993 + $pcombob = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombob : '';
5994 + $pcomboc = VikRequest::getString('comboc', '', 'request');
5995 + $pcomboc = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcomboc : '';
5996 + $pcombod = VikRequest::getString('combod', '', 'request');
5997 + $pcombod = strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo ? $pcombod : '';
5998 + $combostr = '';
5999 + $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboa) ? $pcomboa.':' : ':';
6000 + $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombob) ? $pcombob.':' : ':';
6001 + $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcomboc) ? $pcomboc.':' : ':';
6002 + $combostr .= strlen($pwday) > 0 && strlen($pwdaytwo) > 0 && $pwday != $pwdaytwo && !empty($pcombod) ? $pcombod : '';
6003 + $pminlos = VikRequest::getInt('minlos', '', 'request');
6004 + $pminlos = $pminlos < 1 ? 1 : $pminlos;
6005 + $pmaxlos = VikRequest::getInt('maxlos', '', 'request');
6006 + $pmaxlos = empty($pmaxlos) ? 0 : $pmaxlos;
6007 + $pmultiplyminlos = VikRequest::getString('multiplyminlos', '', 'request');
6008 + $pmultiplyminlos = empty($pmultiplyminlos) ? 0 : 1;
6009 + $pallrooms = VikRequest::getString('allrooms', '', 'request');
6010 + $pallrooms = $pallrooms == "1" ? 1 : 0;
6011 + $pidrooms = VikRequest::getVar('idrooms', array(0));
6012 + $ridr = '';
6013 + $roomidsforsess = array();
6014 + if (!empty($pidrooms) && @count($pidrooms) && $pallrooms == 0) {
6015 + foreach ($pidrooms as $idr) {
6016 + if (empty($idr)) {
6017 + continue;
6018 + }
6019 + $ridr .= '-'.$idr.'-;';
6020 + $roomidsforsess[] = (int)$idr;
6021 + }
6022 + } elseif ($pallrooms > 0) {
6023 + $q = "SELECT `id` FROM `#__vikbooking_rooms`;";
6024 + $dbo->setQuery($q);
6025 + $dbo->execute();
6026 + if ($dbo->getNumRows() > 0) {
6027 + $fetchids = $dbo->loadAssocList();
6028 + foreach ($fetchids as $fetchid) {
6029 + $roomidsforsess[] = (int)$fetchid['id'];
6030 + }
6031 + }
6032 + }
6033 + $pcta = VikRequest::getInt('cta', '', 'request');
6034 + $pctd = VikRequest::getInt('ctd', '', 'request');
6035 + $pctad = VikRequest::getVar('ctad', array());
6036 + $pctdd = VikRequest::getVar('ctdd', array());
6037 + if ($pminlos == 1 && strlen($pwday) == 0 && empty($pctad) && empty($pctdd) && $pmaxlos < 1) {
6038 + // VBO 1.11 - we now allow restrictions with just 1 night of stay
6039 + // VikError::raiseWarning('', JText::translate('VBUSELESSRESTRICTION'));
6040 + // $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6041 + // exit;
6042 + }
6043 + //check if there are restrictions for this month
6044 + if ($pmonth > 0) {
6045 + $q = "SELECT `id` FROM `#__vikbooking_restrictions` WHERE `month`='".$pmonth."' AND `id`!='".$pwhere."';";
6046 + $dbo->setQuery($q);
6047 + $dbo->execute();
6048 + if ($dbo->getNumRows() > 0) {
6049 + VikError::raiseWarning('', JText::translate('VBRESTRICTIONMONTHEXISTS'));
6050 + $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6051 + exit;
6052 + }
6053 + $pdfrom = 0;
6054 + $pdto = 0;
6055 + } else {
6056 + //dates range
6057 + if (empty($pdfrom) || empty($pdto)) {
6058 + VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
6059 + $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6060 + exit;
6061 + } else {
6062 + $housto = $pdfrom == $pdto ? 23 : 0;
6063 + $minsto = $pdfrom == $pdto ? 59 : 0;
6064 + $secsto = $pdfrom == $pdto ? 59 : 0;
6065 + $pdfrom = VikBooking::getDateTimestamp($pdfrom, 0, 0);
6066 + $pdto = VikBooking::getDateTimestamp($pdto, $housto, $minsto, $secsto);
6067 + }
6068 + if ($pdfrom > $pdto) {
6069 + // invalid dates in the past
6070 + VikError::raiseWarning('', JText::translate('VBRESTRICTIONERRDRANGE'));
6071 + $mainframe->redirect("index.php?option=com_vikbooking&task=editrestriction&cid[]=".$pwhere);
6072 + exit;
6073 + }
6074 + }
6075 + //CTA and CTD
6076 + $setcta = array();
6077 + $setctd = array();
6078 + if ($pcta > 0 && count($pctad) > 0) {
6079 + foreach ($pctad as $ctwd) {
6080 + if (strlen($ctwd)) {
6081 + $setcta[] = '-'.(int)$ctwd.'-';
6082 + }
6083 + }
6084 + }
6085 + if ($pctd > 0 && count($pctdd) > 0) {
6086 + foreach ($pctdd as $ctwd) {
6087 + if (strlen($ctwd)) {
6088 + $setctd[] = '-'.(int)$ctwd.'-';
6089 + }
6090 + }
6091 + }
6092 + //
6093 + //update session values
6094 + if (!($pdfrom > 0)) {
6095 + $attemptyear = (int)date('Y');
6096 + $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
6097 + if ($attemptfrom < time()) {
6098 + $attemptyear++;
6099 + $attemptfrom = mktime(0, 0, 0, $pmonth, 1, $attemptyear);
6100 + }
6101 + $attemptto = mktime(0, 0, 0, $pmonth, date('t', $attemptfrom), $attemptyear);
6102 + } else {
6103 + $attemptfrom = $pdfrom;
6104 + $attemptto = $pdto;
6105 + }
6106 + $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
6107 + if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
6108 + $updforvcm['dfrom'] = $updforvcm['dfrom'] > $attemptfrom ? $attemptfrom : $updforvcm['dfrom'];
6109 + } else {
6110 + $updforvcm['dfrom'] = $attemptfrom;
6111 + }
6112 + if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
6113 + $updforvcm['dto'] = $updforvcm['dto'] < $attemptto ? $attemptto : $updforvcm['dto'];
6114 + } else {
6115 + $updforvcm['dto'] = $attemptto;
6116 + }
6117 + if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
6118 + foreach ($roomidsforsess as $rid) {
6119 + if (!in_array($rid, $updforvcm['rooms'])) {
6120 + $updforvcm['rooms'][] = $rid;
6121 + }
6122 + }
6123 + } else {
6124 + $updforvcm['rooms'] = $roomidsforsess;
6125 + }
6126 + if (!array_key_exists('rplans', $updforvcm) || !is_array($updforvcm['rplans'])) {
6127 + $updforvcm['rplans'] = array();
6128 + }
6129 + $session->set('vbVcmRatesUpd', $updforvcm);
6130 + //
6131 + $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."';";
6132 + $dbo->setQuery($q);
6133 + $dbo->execute();
6134 + $mainframe->enqueueMessage(JText::translate('VBRESTRICTIONSAVED'));
6135 + $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
6136 + }
6137 +
6138 + public function removerestrictions()
6139 + {
6140 + if (!JSession::checkToken()) {
6141 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6142 + }
6143 +
6144 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6145 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6146 + }
6147 +
6148 + $ids = VikRequest::getVar('cid', array(0));
6149 + if ($ids) {
6150 + $dbo = JFactory::getDBO();
6151 + foreach ($ids as $d) {
6152 + $q = "DELETE FROM `#__vikbooking_restrictions` WHERE `id`=".(int)$d.";";
6153 + $dbo->setQuery($q);
6154 + $dbo->execute();
6155 + }
6156 + }
6157 + $mainframe = JFactory::getApplication();
6158 + $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
6159 + }
6160 +
6161 + public function prices() {
6162 + VikBookingHelper::printHeader("1");
6163 +
6164 + VikRequest::setVar('view', VikRequest::getCmd('view', 'prices'));
6165 +
6166 + parent::display();
6167 +
6168 + if (VikBooking::showFooter()) {
6169 + VikBookingHelper::printFooter();
6170 + }
6171 + }
6172 +
6173 + public function newprice() {
6174 + VikBookingHelper::printHeader("1");
6175 +
6176 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageprice'));
6177 +
6178 + parent::display();
6179 +
6180 + if (VikBooking::showFooter()) {
6181 + VikBookingHelper::printFooter();
6182 + }
6183 + }
6184 +
6185 + public function editprice() {
6186 + VikBookingHelper::printHeader("1");
6187 +
6188 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageprice'));
6189 +
6190 + parent::display();
6191 +
6192 + if (VikBooking::showFooter()) {
6193 + VikBookingHelper::printFooter();
6194 + }
6195 + }
6196 +
6197 + public function createprice()
6198 + {
6199 + if (!JSession::checkToken()) {
6200 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6201 + }
6202 +
6203 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6204 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6205 + }
6206 +
6207 + $this->do_createprice();
6208 + }
6209 +
6210 + public function createprice_new()
6211 + {
6212 + if (!JSession::checkToken()) {
6213 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6214 + }
6215 +
6216 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6217 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6218 + }
6219 +
6220 + $this->do_createprice(true);
6221 + }
6222 +
6223 + private function do_createprice($new = false)
6224 + {
6225 + $app = JFactory::getApplication();
6226 + $dbo = JFactory::getDbo();
6227 +
6228 + $pprice = VikRequest::getString('price', '', 'request');
6229 + $pattr = VikRequest::getString('attr', '', 'request');
6230 + $ppraliq = VikRequest::getInt('praliq', '', 'request');
6231 + $pmeal_plans = (array)VikRequest::getVar('meal_plans', []);
6232 + $pbreakfast_included = in_array('breakfast', $pmeal_plans) ? 1 : 0;
6233 + $pfree_cancellation = VikRequest::getInt('free_cancellation', 0, 'request');
6234 + $pfree_cancellation = $pfree_cancellation == 1 ? 1 : 0;
6235 + $pcanc_deadline = VikRequest::getInt('canc_deadline', '', 'request');
6236 + $pminlos = VikRequest::getInt('minlos', 0, 'request');
6237 + $pminlos = $pminlos < 0 ? 0 : $pminlos;
6238 + $pminhadv = VikRequest::getInt('minhadv', '', 'request');
6239 + $pminhadv = $pminhadv < 0 ? 0 : $pminhadv;
6240 + $pcanc_policy = VikRequest::getString('canc_policy', '', 'request', VIKREQUEST_ALLOWHTML);
6241 +
6242 + $is_derived = $app->input->getInt('is_derived', 0);
6243 + $derived_id = $app->input->getUInt('derived_id', 0);
6244 + $derived_data = $app->input->get('derived_data', [], 'array');
6245 +
6246 + $parent_id = 0;
6247 + $derived_info = null;
6248 +
6249 + if ($is_derived && $derived_id && $derived_data) {
6250 + $parent_id = $derived_id;
6251 + $derived_info = $derived_data;
6252 + $derived_info['mode'] = ($derived_info['mode'] ?? '') == 'charge' ? 'charge' : 'discount';
6253 + $derived_info['type'] = ($derived_info['type'] ?? '') == 'absolute' ? 'absolute' : 'percent';
6254 + $derived_info['value'] = (float) ($derived_info['value'] ?? 0);
6255 + $derived_info['follow_restr'] = isset($derived_info['follow_restr']) ? 1 : 0;
6256 + if (!$derived_info['value']) {
6257 + $parent_id = 0;
6258 + $derived_info = null;
6259 + }
6260 + }
6261 +
6262 + if (!empty($pprice)) {
6263 + $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') . ");";
6264 + $dbo->setQuery($q);
6265 + $dbo->execute();
6266 +
6267 + $new_rplan_id = $dbo->insertid();
6268 +
6269 + /**
6270 + * Allow to populate base rates for newly created rate plan for all room types using the parent rate.
6271 + *
6272 + * @since 1.18.6 (J) - 1.8.6 (WP)
6273 + */
6274 + if ($app->input->getBool('set_derived_rates', false) && $is_derived && $derived_id && $derived_info) {
6275 + // find all rooms with base rates defined for the parent rate plan
6276 + $dbo->setQuery(
6277 + $dbo->getQuery(true)
6278 + ->select($dbo->qn('idroom'))
6279 + ->from($dbo->qn('#__vikbooking_dispcost'))
6280 + ->where($dbo->qn('idprice') . ' = ' . $derived_id)
6281 + ->group($dbo->qn('idroom'))
6282 + ->order($dbo->qn('idroom') . ' ASC')
6283 + );
6284 + $populateRoomIds = array_map('intval', $dbo->loadColumn());
6285 +
6286 + // determine rates table range of nights of stays
6287 + $fromNights = $pminlos ?: 1;
6288 + $maxNights = $app->input->getUInt('set_max_nights') ?: $pminlos ?: 1;
6289 + $maxNights = $maxNights < $fromNights ? $fromNights : $maxNights;
6290 +
6291 + // fetch base rates for all the involved room types
6292 + $dbo->setQuery(
6293 + $dbo->getQuery(true)
6294 + ->select([
6295 + $dbo->qn('idroom'),
6296 + $dbo->qn('days'),
6297 + $dbo->qn('cost'),
6298 + ])
6299 + ->from($dbo->qn('#__vikbooking_dispcost'))
6300 + ->where($dbo->qn('idroom') . ' IN (' . implode(', ', $populateRoomIds) . ')')
6301 + ->where($dbo->qn('idprice') . ' = ' . $derived_id)
6302 + ->order($dbo->qn('idroom') . ' ASC')
6303 + ->order($dbo->qn('days') . ' ASC')
6304 + );
6305 + $roomBaseRates = $dbo->loadAssocList();
6306 +
6307 + // iterate all rooms involved
6308 + foreach ($populateRoomIds as $roomId) {
6309 + // loop through the interval of nights of stay
6310 + for ($n = $fromNights; $n <= $maxNights; $n++) {
6311 + // fetch current room rate in parent rate plan
6312 + $roomParentNightlyRate = 0;
6313 + $roomParentExactRate = 0;
6314 + foreach ($roomBaseRates as $roomBaseRate) {
6315 + if ($roomBaseRate['idroom'] != $roomId) {
6316 + // ignore room
6317 + continue;
6318 + }
6319 + if (!$roomParentNightlyRate) {
6320 + // set rate for the lowest number of nights of stay
6321 + $roomParentNightlyRate = $roomBaseRate['cost'] / ($roomBaseRate['days'] ?: 1);
6322 + }
6323 + if ($roomBaseRate['days'] == $n) {
6324 + // set room exact rate for this number of nights of stay
6325 + $roomParentExactRate = $roomBaseRate['cost'];
6326 + // do not proceed
6327 + break;
6328 + }
6329 + }
6330 +
6331 + if (!$roomParentNightlyRate) {
6332 + // missing pricing information from parent rate plan
6333 + continue;
6334 + }
6335 +
6336 + // determine the cost to apply for the newly created derived rate plan
6337 + $nightlyDerivedRate = $roomParentExactRate ?: $roomParentNightlyRate;
6338 +
6339 + // check how the new rate was derived
6340 + if ($derived_info['mode'] == 'charge') {
6341 + // increase rate
6342 + if ($derived_info['type'] == 'absolute') {
6343 + // fixed increase
6344 + $nightlyDerivedRate += $derived_info['value'];
6345 + } else {
6346 + // percent increase
6347 + $nightlyDerivedRate *= (100 + $derived_info['value']) / 100;
6348 + }
6349 + } else {
6350 + // discount rate
6351 + if ($derived_info['type'] == 'absolute') {
6352 + // fixed discount
6353 + $nightlyDerivedRate -= $derived_info['value'];
6354 + } else {
6355 + // percent discount
6356 + $nightlyDerivedRate *= (100 - $derived_info['value']) / 100;
6357 + }
6358 + }
6359 +
6360 + if (!$roomParentExactRate) {
6361 + // multiply rate by number of nights of stay if started from the parent lowest number of nights
6362 + $nightlyDerivedRate *= $n;
6363 + }
6364 +
6365 + // build new room base rate record
6366 + $rateRecord = [
6367 + 'idroom' => $roomId,
6368 + 'days' => $n,
6369 + 'idprice' => $new_rplan_id,
6370 + 'cost' => round($nightlyDerivedRate, 2),
6371 + ];
6372 +
6373 + // cast to object
6374 + $rateRecord = (object) $rateRecord;
6375 +
6376 + // insert record
6377 + $dbo->insertObject('#__vikbooking_dispcost', $rateRecord, 'id');
6378 + }
6379 + }
6380 + }
6381 + }
6382 +
6383 + $app->redirect("index.php?option=com_vikbooking&task=" . ($new ? 'newprice' : 'prices'));
6384 + $app->close();
6385 + }
6386 +
6387 + public function updateprice()
6388 + {
6389 + if (!JSession::checkToken()) {
6390 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6391 + }
6392 +
6393 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6394 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6395 + }
6396 +
6397 + $this->do_updateprice();
6398 + }
6399 +
6400 + public function updatepricestay()
6401 + {
6402 + if (!JSession::checkToken()) {
6403 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6404 + }
6405 +
6406 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6407 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6408 + }
6409 +
6410 + $this->do_updateprice(true);
6411 + }
6412 +
6413 + private function do_updateprice($stay = false)
6414 + {
6415 + $app = JFactory::getApplication();
6416 + $dbo = JFactory::getDbo();
6417 +
6418 + $pprice = VikRequest::getString('price', '', 'request');
6419 + $pattr = VikRequest::getString('attr', '', 'request');
6420 + $ppraliq = VikRequest::getInt('praliq', '', 'request');
6421 + $pmeal_plans = (array)VikRequest::getVar('meal_plans', []);
6422 + $pbreakfast_included = in_array('breakfast', $pmeal_plans) ? 1 : 0;
6423 + $pfree_cancellation = VikRequest::getInt('free_cancellation', '', 'request');
6424 + $pfree_cancellation = $pfree_cancellation == 1 ? 1 : 0;
6425 + $pcanc_deadline = VikRequest::getInt('canc_deadline', '', 'request');
6426 + $pminlos = VikRequest::getInt('minlos', 0, 'request');
6427 + $pminlos = $pminlos < 0 ? 0 : $pminlos;
6428 + $pminhadv = VikRequest::getInt('minhadv', '', 'request');
6429 + $pminhadv = $pminhadv < 0 ? 0 : $pminhadv;
6430 + $pcanc_policy = VikRequest::getString('canc_policy', '', 'request', VIKREQUEST_ALLOWHTML);
6431 + $pwhereup = VikRequest::getInt('whereup', 0, 'request');
6432 +
6433 + $is_derived = $app->input->getInt('is_derived', 0);
6434 + $derived_id = $app->input->getUInt('derived_id', 0);
6435 + $derived_data = $app->input->get('derived_data', [], 'array');
6436 +
6437 + $parent_id = 0;
6438 + $derived_info = null;
6439 +
6440 + if ($is_derived && $derived_id && $derived_data) {
6441 + $parent_id = $derived_id;
6442 + $derived_info = $derived_data;
6443 + $derived_info['mode'] = ($derived_info['mode'] ?? '') == 'charge' ? 'charge' : 'discount';
6444 + $derived_info['type'] = ($derived_info['type'] ?? '') == 'absolute' ? 'absolute' : 'percent';
6445 + $derived_info['value'] = (float) ($derived_info['value'] ?? 0);
6446 + $derived_info['follow_restr'] = isset($derived_info['follow_restr']) ? 1 : 0;
6447 + if (!$derived_info['value']) {
6448 + $parent_id = 0;
6449 + $derived_info = null;
6450 + }
6451 + }
6452 +
6453 + if (!empty($pprice) && $pwhereup) {
6454 + $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) . ";";
6455 + $dbo->setQuery($q);
6456 + $dbo->execute();
6457 + }
6458 +
6459 + $app->redirect("index.php?option=com_vikbooking&task=" . ($stay ? 'editprice&cid[]=' . $pwhereup : 'prices'));
6460 + $app->close();
6461 + }
6462 +
6463 + public function removeprice()
6464 + {
6465 + if (!JSession::checkToken()) {
6466 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6467 + }
6468 +
6469 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6470 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6471 + }
6472 +
6473 + $ids = VikRequest::getVar('cid', array(0));
6474 + if ($ids) {
6475 + $dbo = JFactory::getDBO();
6476 + foreach ($ids as $d) {
6477 + $q = "DELETE FROM `#__vikbooking_prices` WHERE `id`=".$dbo->quote($d).";";
6478 + $dbo->setQuery($q);
6479 + $dbo->execute();
6480 + $q = "DELETE FROM `#__vikbooking_dispcost` WHERE `idprice`=".intval($d).";";
6481 + $dbo->setQuery($q);
6482 + $dbo->execute();
6483 + }
6484 + }
6485 + $mainframe = JFactory::getApplication();
6486 + $mainframe->redirect("index.php?option=com_vikbooking&task=prices");
6487 + }
6488 +
6489 + public function iva() {
6490 + VikBookingHelper::printHeader("2");
6491 +
6492 + VikRequest::setVar('view', VikRequest::getCmd('view', 'iva'));
6493 +
6494 + parent::display();
6495 +
6496 + if (VikBooking::showFooter()) {
6497 + VikBookingHelper::printFooter();
6498 + }
6499 + }
6500 +
6501 + public function newiva() {
6502 + VikBookingHelper::printHeader("2");
6503 +
6504 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageiva'));
6505 +
6506 + parent::display();
6507 +
6508 + if (VikBooking::showFooter()) {
6509 + VikBookingHelper::printFooter();
6510 + }
6511 + }
6512 +
6513 + public function editiva() {
6514 + VikBookingHelper::printHeader("2");
6515 +
6516 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageiva'));
6517 +
6518 + parent::display();
6519 +
6520 + if (VikBooking::showFooter()) {
6521 + VikBookingHelper::printFooter();
6522 + }
6523 + }
6524 +
6525 + public function createiva()
6526 + {
6527 + if (!JSession::checkToken()) {
6528 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6529 + }
6530 +
6531 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6532 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6533 + }
6534 +
6535 + $paliqname = VikRequest::getString('aliqname', '', 'request');
6536 + $paliqperc = VikRequest::getFloat('aliqperc', '', 'request');
6537 + $pbreakdown_name = VikRequest::getVar('breakdown_name', array());
6538 + $pbreakdown_rate = VikRequest::getVar('breakdown_rate', array());
6539 + $ptaxcap = VikRequest::getFloat('taxcap', 0, 'request');
6540 + if (!empty($paliqperc)) {
6541 + $dbo = JFactory::getDBO();
6542 + $breakdown_str = '';
6543 + if (count($pbreakdown_name) > 0) {
6544 + $breakdown_values = array();
6545 + $bkcount = 0;
6546 + $tot_sub_aliq = 0;
6547 + foreach ($pbreakdown_name as $key => $subtax) {
6548 + if (!empty($subtax) && floatval($pbreakdown_rate[$key]) > 0) {
6549 + $breakdown_values[$bkcount]['name'] = $subtax;
6550 + $breakdown_values[$bkcount]['aliq'] = (float)$pbreakdown_rate[$key];
6551 + $tot_sub_aliq += (float)$pbreakdown_rate[$key];
6552 + $bkcount++;
6553 + }
6554 + }
6555 + if (count($breakdown_values) > 0) {
6556 + $breakdown_str = json_encode($breakdown_values);
6557 + if ($tot_sub_aliq < (float)$paliqperc || $tot_sub_aliq > (float)$paliqperc) {
6558 + VikError::raiseWarning('', JText::translate('VBOTAXBKDWNERRNOMATCH'));
6559 + }
6560 + }
6561 + }
6562 + $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').");";
6563 + $dbo->setQuery($q);
6564 + $dbo->execute();
6565 + }
6566 + $mainframe = JFactory::getApplication();
6567 + $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6568 + }
6569 +
6570 + public function updateiva()
6571 + {
6572 + if (!JSession::checkToken()) {
6573 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6574 + }
6575 +
6576 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6577 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6578 + }
6579 +
6580 + $paliqname = VikRequest::getString('aliqname', '', 'request');
6581 + $paliqperc = VikRequest::getFloat('aliqperc', '', 'request');
6582 + $pbreakdown_name = VikRequest::getVar('breakdown_name', array());
6583 + $pbreakdown_rate = VikRequest::getVar('breakdown_rate', array());
6584 + $ptaxcap = VikRequest::getFloat('taxcap', 0, 'request');
6585 + $pwhereup = VikRequest::getInt('whereup', 0, 'request');
6586 + if (!empty($paliqperc)) {
6587 + $dbo = JFactory::getDBO();
6588 + $breakdown_str = '';
6589 + if (count($pbreakdown_name) > 0) {
6590 + $breakdown_values = array();
6591 + $bkcount = 0;
6592 + $tot_sub_aliq = 0;
6593 + foreach ($pbreakdown_name as $key => $subtax) {
6594 + if (!empty($subtax) && floatval($pbreakdown_rate[$key]) > 0) {
6595 + $breakdown_values[$bkcount]['name'] = $subtax;
6596 + $breakdown_values[$bkcount]['aliq'] = (float)$pbreakdown_rate[$key];
6597 + $tot_sub_aliq += (float)$pbreakdown_rate[$key];
6598 + $bkcount++;
6599 + }
6600 + }
6601 + if (count($breakdown_values) > 0) {
6602 + $breakdown_str = json_encode($breakdown_values);
6603 + if ($tot_sub_aliq < (float)$paliqperc || $tot_sub_aliq > (float)$paliqperc) {
6604 + VikError::raiseWarning('', JText::translate('VBOTAXBKDWNERRNOMATCH'));
6605 + }
6606 + }
6607 + }
6608 + $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).";";
6609 + $dbo->setQuery($q);
6610 + $dbo->execute();
6611 + }
6612 + $mainframe = JFactory::getApplication();
6613 + $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6614 + }
6615 +
6616 + public function removeiva()
6617 + {
6618 + if (!JSession::checkToken()) {
6619 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6620 + }
6621 +
6622 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6623 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6624 + }
6625 +
6626 + $ids = VikRequest::getVar('cid', array(0));
6627 + if ($ids) {
6628 + $dbo = JFactory::getDBO();
6629 + foreach ($ids as $d) {
6630 + $q = "DELETE FROM `#__vikbooking_iva` WHERE `id`=".$dbo->quote($d).";";
6631 + $dbo->setQuery($q);
6632 + $dbo->execute();
6633 + }
6634 + }
6635 + $mainframe = JFactory::getApplication();
6636 + $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
6637 + }
6638 +
6639 + public function categories() {
6640 + VikBookingHelper::printHeader("4");
6641 +
6642 + VikRequest::setVar('view', VikRequest::getCmd('view', 'categories'));
6643 +
6644 + parent::display();
6645 +
6646 + if (VikBooking::showFooter()) {
6647 + VikBookingHelper::printFooter();
6648 + }
6649 + }
6650 +
6651 + public function newcat() {
6652 + VikBookingHelper::printHeader("4");
6653 +
6654 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecategory'));
6655 +
6656 + parent::display();
6657 +
6658 + if (VikBooking::showFooter()) {
6659 + VikBookingHelper::printFooter();
6660 + }
6661 + }
6662 +
6663 + public function editcat() {
6664 + VikBookingHelper::printHeader("4");
6665 +
6666 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecategory'));
6667 +
6668 + parent::display();
6669 +
6670 + if (VikBooking::showFooter()) {
6671 + VikBookingHelper::printFooter();
6672 + }
6673 + }
6674 +
6675 + public function createcat()
6676 + {
6677 + if (!JSession::checkToken()) {
6678 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6679 + }
6680 +
6681 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6682 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6683 + }
6684 +
6685 + $pcatname = VikRequest::getString('catname', '', 'request');
6686 + $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWHTML);
6687 + if (!empty($pcatname)) {
6688 + $dbo = JFactory::getDBO();
6689 + $q = "INSERT INTO `#__vikbooking_categories` (`name`,`descr`) VALUES(".$dbo->quote($pcatname).", ".$dbo->quote($pdescr).");";
6690 + $dbo->setQuery($q);
6691 + $dbo->execute();
6692 + }
6693 + $mainframe = JFactory::getApplication();
6694 + $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6695 + }
6696 +
6697 + public function updatecat()
6698 + {
6699 + if (!JSession::checkToken()) {
6700 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6701 + }
6702 +
6703 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6704 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6705 + }
6706 +
6707 + $pcatname = VikRequest::getString('catname', '', 'request');
6708 + $pdescr = VikRequest::getString('descr', '', 'request', VIKREQUEST_ALLOWHTML);
6709 + $pwhereup = VikRequest::getString('whereup', '', 'request');
6710 + if (!empty($pcatname)) {
6711 + $dbo = JFactory::getDBO();
6712 + $q = "UPDATE `#__vikbooking_categories` SET `name`=".$dbo->quote($pcatname).", `descr`=".$dbo->quote($pdescr)." WHERE `id`=".$dbo->quote($pwhereup).";";
6713 + $dbo->setQuery($q);
6714 + $dbo->execute();
6715 + }
6716 + $mainframe = JFactory::getApplication();
6717 + $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6718 + }
6719 +
6720 + public function removecat()
6721 + {
6722 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
6723 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6724 + }
6725 +
6726 + $ids = VikRequest::getVar('cid', array(0));
6727 + if ($ids) {
6728 + $dbo = JFactory::getDBO();
6729 + foreach ($ids as $d) {
6730 + $q = "DELETE FROM `#__vikbooking_categories` WHERE `id`=".$dbo->quote($d).";";
6731 + $dbo->setQuery($q);
6732 + $dbo->execute();
6733 + }
6734 + }
6735 + $mainframe = JFactory::getApplication();
6736 + $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
6737 + }
6738 +
6739 + public function carat() {
6740 + VikBookingHelper::printHeader("5");
6741 +
6742 + VikRequest::setVar('view', VikRequest::getCmd('view', 'carat'));
6743 +
6744 + parent::display();
6745 +
6746 + if (VikBooking::showFooter()) {
6747 + VikBookingHelper::printFooter();
6748 + }
6749 + }
6750 +
6751 + public function newcarat() {
6752 + VikBookingHelper::printHeader("5");
6753 +
6754 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecarat'));
6755 +
6756 + parent::display();
6757 +
6758 + if (VikBooking::showFooter()) {
6759 + VikBookingHelper::printFooter();
6760 + }
6761 + }
6762 +
6763 + public function editcarat() {
6764 + VikBookingHelper::printHeader("5");
6765 +
6766 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecarat'));
6767 +
6768 + parent::display();
6769 +
6770 + if (VikBooking::showFooter()) {
6771 + VikBookingHelper::printFooter();
6772 + }
6773 + }
6774 +
6775 + public function createcarat()
6776 + {
6777 + if (!JSession::checkToken()) {
6778 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6779 + }
6780 +
6781 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
6782 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6783 + }
6784 +
6785 + $pcaratname = VikRequest::getString('caratname', '', 'request');
6786 + $pcarattextimg = VikRequest::getString('carattextimg', '', 'request', VIKREQUEST_ALLOWRAW);
6787 + $pautoresize = VikRequest::getString('autoresize', '', 'request');
6788 + $presizeto = VikRequest::getString('resizeto', '', 'request');
6789 + $pidrooms = VikRequest::getVar('idrooms', array());
6790 + if (!empty($pcaratname)) {
6791 + if (intval($_FILES['caraticon']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['caraticon']['name'])!="") {
6792 + jimport('joomla.filesystem.file');
6793 + $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
6794 + if (@is_uploaded_file($_FILES['caraticon']['tmp_name'])) {
6795 + $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['caraticon']['name'])));
6796 + if (file_exists($updpath.$safename)) {
6797 + $j=1;
6798 + while (file_exists($updpath.$j.$safename)) {
6799 + $j++;
6800 + }
6801 + $pwhere=$updpath.$j.$safename;
6802 + } else {
6803 + $j="";
6804 + $pwhere=$updpath.$safename;
6805 + }
6806 + if (!getimagesize($_FILES['caraticon']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
6807 + @unlink($pwhere);
6808 + $picon="";
6809 + } else {
6810 + VikBooking::uploadFile($_FILES['caraticon']['tmp_name'], $pwhere);
6811 + @chmod($pwhere, 0644);
6812 + $picon=$j.$safename;
6813 + if ($pautoresize=="1" && !empty($presizeto)) {
6814 + $eforj = new vikResizer();
6815 + $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
6816 + if ($origmod) {
6817 + @unlink($pwhere);
6818 + $picon='r_'.$j.$safename;
6819 + }
6820 + }
6821 + }
6822 + } else {
6823 + $picon="";
6824 + }
6825 + } else {
6826 + $picon="";
6827 + }
6828 + $dbo = JFactory::getDbo();
6829 + // get new ordering
6830 + $q = "SELECT `ordering` FROM `#__vikbooking_characteristics` ORDER BY `#__vikbooking_characteristics`.`ordering` DESC LIMIT 1;";
6831 + $dbo->setQuery($q);
6832 + $dbo->execute();
6833 + if ($dbo->getNumRows()) {
6834 + $newsortnum = $dbo->loadResult() + 1;
6835 + } else {
6836 + $newsortnum = 1;
6837 + }
6838 + $pordering = VikRequest::getInt('ordering', 0, 'request');
6839 + $newsortnum = !empty($pordering) ? $pordering : $newsortnum;
6840 + //
6841 + $q = "INSERT INTO `#__vikbooking_characteristics` (`name`,`icon`,`textimg`,`ordering`) VALUES(".$dbo->quote($pcaratname).", ".$dbo->quote($picon).", ".$dbo->quote($pcarattextimg).", {$newsortnum});";
6842 + $dbo->setQuery($q);
6843 + $dbo->execute();
6844 +
6845 + $new_carat_id = $dbo->insertid();
6846 + if (!empty($new_carat_id)) {
6847 + // assign/unset carat-rooms relations
6848 + $rooms_with_carat = array();
6849 + if (count($pidrooms)) {
6850 + // assign this new carat to the requested rooms
6851 + foreach ($pidrooms as $idroom) {
6852 + if (empty($idroom)) {
6853 + continue;
6854 + }
6855 + $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
6856 + $dbo->setQuery($q);
6857 + $dbo->execute();
6858 + if (!$dbo->getNumRows()) {
6859 + continue;
6860 + }
6861 + $room_data = $dbo->loadAssoc();
6862 + array_push($rooms_with_carat, $room_data['id']);
6863 + $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6864 + if (in_array((string)$new_carat_id, $current_carats)) {
6865 + continue;
6866 + }
6867 + if (count($current_carats) === 1 && (string)$current_carats[0] == '0') {
6868 + // make sure we do not concatenate a real ID to 0
6869 + $current_carats = array();
6870 + }
6871 + array_push($current_carats, $new_carat_id);
6872 + $new_opts = implode(';', $current_carats) . ';';
6873 + $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
6874 + $dbo->setQuery($q);
6875 + $dbo->execute();
6876 + }
6877 + }
6878 + if (!count($rooms_with_carat)) {
6879 + // get all rooms to unset this carat (if previously set)
6880 + array_push($rooms_with_carat, '0');
6881 + }
6882 + // unset the carat from the other rooms that may have it
6883 + $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_carat) . ");";
6884 + $dbo->setQuery($q);
6885 + $dbo->execute();
6886 + if ($dbo->getNumRows()) {
6887 + $unset_rooms_carat = $dbo->loadAssocList();
6888 + foreach ($unset_rooms_carat as $room_data) {
6889 + $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6890 + if (!in_array((string)$new_carat_id, $current_carats)) {
6891 + // this room is not using this carat
6892 + continue;
6893 + }
6894 + $caratkey = array_search((string)$new_carat_id, $current_carats);
6895 + if ($caratkey === false) {
6896 + // key not found
6897 + continue;
6898 + }
6899 + // unset this carat ID from the string
6900 + unset($current_carats[$caratkey]);
6901 + if (!count($current_carats)) {
6902 + // a room with no carats assigned will be listed as "0;"
6903 + $current_carats = array(0);
6904 + }
6905 + $new_opts = implode(';', $current_carats) . ';';
6906 + $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
6907 + $dbo->setQuery($q);
6908 + $dbo->execute();
6909 + }
6910 + }
6911 + //
6912 + }
6913 + }
6914 + $mainframe = JFactory::getApplication();
6915 + $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
6916 + }
6917 +
6918 + public function updatecarat()
6919 + {
6920 + if (!JSession::checkToken()) {
6921 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
6922 + }
6923 +
6924 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
6925 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
6926 + }
6927 +
6928 + $pcaratname = VikRequest::getString('caratname', '', 'request');
6929 + $pcarattextimg = VikRequest::getString('carattextimg', '', 'request', VIKREQUEST_ALLOWRAW);
6930 + $pwhereup = VikRequest::getString('whereup', '', 'request');
6931 + $pautoresize = VikRequest::getString('autoresize', '', 'request');
6932 + $presizeto = VikRequest::getString('resizeto', '', 'request');
6933 + $pidrooms = VikRequest::getVar('idrooms', array());
6934 + $pordering = VikRequest::getInt('ordering', 1, 'request');
6935 + if (!empty($pcaratname)) {
6936 + if (intval($_FILES['caraticon']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['caraticon']['name'])!="") {
6937 + jimport('joomla.filesystem.file');
6938 + $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
6939 + if (@is_uploaded_file($_FILES['caraticon']['tmp_name'])) {
6940 + $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['caraticon']['name'])));
6941 + if (file_exists($updpath.$safename)) {
6942 + $j=1;
6943 + while (file_exists($updpath.$j.$safename)) {
6944 + $j++;
6945 + }
6946 + $pwhere=$updpath.$j.$safename;
6947 + } else {
6948 + $j="";
6949 + $pwhere=$updpath.$safename;
6950 + }
6951 + if (!getimagesize($_FILES['caraticon']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
6952 + @unlink($pwhere);
6953 + $picon="";
6954 + } else {
6955 + VikBooking::uploadFile($_FILES['caraticon']['tmp_name'], $pwhere);
6956 + @chmod($pwhere, 0644);
6957 + $picon=$j.$safename;
6958 + if ($pautoresize=="1" && !empty($presizeto)) {
6959 + $eforj = new vikResizer();
6960 + $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
6961 + if ($origmod) {
6962 + @unlink($pwhere);
6963 + $picon='r_'.$j.$safename;
6964 + }
6965 + }
6966 + }
6967 + } else {
6968 + $picon="";
6969 + }
6970 + } else {
6971 + $picon="";
6972 + }
6973 + $dbo = JFactory::getDbo();
6974 + $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).";";
6975 + $dbo->setQuery($q);
6976 + $dbo->execute();
6977 +
6978 + // assign/unset carat-rooms relations
6979 + $rooms_with_carat = array();
6980 + if (count($pidrooms)) {
6981 + // assign this new carat to the requested rooms
6982 + foreach ($pidrooms as $idroom) {
6983 + if (empty($idroom)) {
6984 + continue;
6985 + }
6986 + $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
6987 + $dbo->setQuery($q);
6988 + $dbo->execute();
6989 + if (!$dbo->getNumRows()) {
6990 + continue;
6991 + }
6992 + $room_data = $dbo->loadAssoc();
6993 + array_push($rooms_with_carat, $room_data['id']);
6994 + $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
6995 + if (in_array((string)$pwhereup, $current_carats)) {
6996 + continue;
6997 + }
6998 + if (count($current_carats) === 1 && (string)$current_carats[0] == '0') {
6999 + // make sure we do not concatenate a real ID to 0
7000 + $current_carats = array();
7001 + }
7002 + array_push($current_carats, $pwhereup);
7003 + $new_carats = implode(';', $current_carats) . ';';
7004 + $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_carats) . " WHERE `id`={$room_data['id']};";
7005 + $dbo->setQuery($q);
7006 + $dbo->execute();
7007 + }
7008 + }
7009 + if (!count($rooms_with_carat)) {
7010 + // get all rooms to unset this carat (if previously set)
7011 + array_push($rooms_with_carat, '0');
7012 + }
7013 + // unset the carat from the other rooms that may have it
7014 + $q = "SELECT `id`, `idcarat` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_carat) . ");";
7015 + $dbo->setQuery($q);
7016 + $dbo->execute();
7017 + if ($dbo->getNumRows()) {
7018 + $unset_rooms_carat = $dbo->loadAssocList();
7019 + foreach ($unset_rooms_carat as $room_data) {
7020 + $current_carats = empty($room_data['idcarat']) ? array() : explode(';', rtrim($room_data['idcarat'], ';'));
7021 + if (!in_array((string)$pwhereup, $current_carats)) {
7022 + // this room is not using this carat
7023 + continue;
7024 + }
7025 + $caratkey = array_search((string)$pwhereup, $current_carats);
7026 + if ($caratkey === false) {
7027 + // key not found
7028 + continue;
7029 + }
7030 + // unset this carat ID from the string
7031 + unset($current_carats[$caratkey]);
7032 + if (!count($current_carats)) {
7033 + // a room with no carats assigned will be listed as "0;"
7034 + $current_carats = array(0);
7035 + }
7036 + $new_carats = implode(';', $current_carats) . ';';
7037 + $q = "UPDATE `#__vikbooking_rooms` SET `idcarat`=" . $dbo->quote($new_carats) . " WHERE `id`={$room_data['id']};";
7038 + $dbo->setQuery($q);
7039 + $dbo->execute();
7040 + }
7041 + }
7042 + //
7043 + }
7044 + $mainframe = JFactory::getApplication();
7045 + $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
7046 + }
7047 +
7048 + public function removecarat()
7049 + {
7050 + if (!JSession::checkToken()) {
7051 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7052 + }
7053 +
7054 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7055 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7056 + }
7057 +
7058 + $ids = VikRequest::getVar('cid', array(0));
7059 + if ($ids) {
7060 + $dbo = JFactory::getDBO();
7061 + foreach ($ids as $d) {
7062 + $q = "SELECT `icon` FROM `#__vikbooking_characteristics` WHERE `id`=".$dbo->quote($d).";";
7063 + $dbo->setQuery($q);
7064 + $dbo->execute();
7065 + if ($dbo->getNumRows() == 1) {
7066 + $rows = $dbo->loadAssocList();
7067 + if (!empty($rows[0]['icon']) && file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['icon'])) {
7068 + @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['icon']);
7069 + }
7070 + }
7071 + $q = "DELETE FROM `#__vikbooking_characteristics` WHERE `id`=".$dbo->quote($d).";";
7072 + $dbo->setQuery($q);
7073 + $dbo->execute();
7074 + }
7075 + }
7076 + $mainframe = JFactory::getApplication();
7077 + $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
7078 + }
7079 +
7080 + public function coupons() {
7081 + VikBookingHelper::printHeader("17");
7082 +
7083 + VikRequest::setVar('view', VikRequest::getCmd('view', 'coupons'));
7084 +
7085 + parent::display();
7086 +
7087 + if (VikBooking::showFooter()) {
7088 + VikBookingHelper::printFooter();
7089 + }
7090 + }
7091 +
7092 + public function newcoupon() {
7093 + VikBookingHelper::printHeader("17");
7094 +
7095 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecoupon'));
7096 +
7097 + parent::display();
7098 +
7099 + if (VikBooking::showFooter()) {
7100 + VikBookingHelper::printFooter();
7101 + }
7102 + }
7103 +
7104 + public function editcoupon() {
7105 + VikBookingHelper::printHeader("17");
7106 +
7107 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecoupon'));
7108 +
7109 + parent::display();
7110 +
7111 + if (VikBooking::showFooter()) {
7112 + VikBookingHelper::printFooter();
7113 + }
7114 + }
7115 +
7116 + public function createcoupon()
7117 + {
7118 + if (!JSession::checkToken()) {
7119 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7120 + }
7121 +
7122 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
7123 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7124 + }
7125 +
7126 + $pcode = VikRequest::getString('code', '', 'request');
7127 + $pvalue = VikRequest::getString('value', '', 'request');
7128 + $pfrom = VikRequest::getString('from', '', 'request');
7129 + $pto = VikRequest::getString('to', '', 'request');
7130 + $pidrooms = VikRequest::getVar('idrooms', array(0));
7131 + $ptype = VikRequest::getString('type', '', 'request');
7132 + $ptype = $ptype == "1" ? 1 : 2;
7133 + $ppercentot = VikRequest::getString('percentot', '', 'request');
7134 + $ppercentot = $ppercentot == "1" ? 1 : 2;
7135 + $pallvehicles = VikRequest::getString('allvehicles', '', 'request');
7136 + $pallvehicles = $pallvehicles == "1" ? 1 : 0;
7137 + $pmintotord = VikRequest::getFloat('mintotord', 0, 'request');
7138 + $pmaxtotord = VikRequest::getFloat('maxtotord', 0, 'request');
7139 + $pexcludetaxes = VikRequest::getInt('excludetaxes', 0, 'request');
7140 + $pminlos = VikRequest::getInt('minlos', 0, 'request');
7141 + $pcustomers = VikRequest::getVar('customers', array());
7142 + $pautomatic = VikRequest::getInt('automatic', 0, 'request');
7143 + $stridrooms = "";
7144 + if (count($pidrooms) > 0 && $pallvehicles != 1) {
7145 + foreach ($pidrooms as $ch) {
7146 + if (!empty($ch)) {
7147 + $stridrooms .= ";".$ch.";";
7148 + }
7149 + }
7150 + }
7151 + $strdatevalid = "";
7152 + if (strlen($pfrom) > 0 && strlen($pto) > 0) {
7153 + $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
7154 + $second = VikBooking::getDateTimestamp($pto, 0, 0);
7155 + if ($first < $second) {
7156 + $strdatevalid .= $first."-".$second;
7157 + }
7158 + }
7159 +
7160 + $dbo = JFactory::getDbo();
7161 + $app = JFactory::getApplication();
7162 +
7163 + $q = "SELECT * FROM `#__vikbooking_coupons` WHERE `code`=".$dbo->quote($pcode).";";
7164 + $dbo->setQuery($q);
7165 + $dbo->execute();
7166 + if ($dbo->getNumRows() > 0) {
7167 + VikError::raiseWarning('', JText::translate('VBCOUPONEXISTS'));
7168 + } else {
7169 + $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) . ");";
7170 + $dbo->setQuery($q);
7171 + $dbo->execute();
7172 +
7173 + $id_coupon = $dbo->insertid();
7174 +
7175 + $app->enqueueMessage(JText::translate('VBCOUPONSAVEOK'));
7176 +
7177 + // check if this coupon should be assigned to specific customers
7178 + foreach ($pcustomers as $id_customer) {
7179 + $customer_coupon = new stdClass;
7180 + $customer_coupon->idcustomer = (int)$id_customer;
7181 + $customer_coupon->idcoupon = (int)$id_coupon;
7182 + $customer_coupon->automatic = $pautomatic ? 1 : 0;
7183 +
7184 + $dbo->insertObject('#__vikbooking_customers_coupons', $customer_coupon, 'id');
7185 + }
7186 + }
7187 + $app->redirect("index.php?option=com_vikbooking&task=coupons");
7188 + }
7189 +
7190 + public function updatecoupon()
7191 + {
7192 + if (!JSession::checkToken()) {
7193 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7194 + }
7195 +
7196 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7197 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7198 + }
7199 +
7200 + $this->do_updatecoupon($stay = false);
7201 + }
7202 +
7203 + public function updatecoupon_stay()
7204 + {
7205 + if (!JSession::checkToken()) {
7206 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7207 + }
7208 +
7209 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7210 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7211 + }
7212 +
7213 + $this->do_updatecoupon($stay = true);
7214 + }
7215 +
7216 + protected function do_updatecoupon($stay = false)
7217 + {
7218 + if (!JSession::checkToken()) {
7219 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7220 + }
7221 + $pcode = VikRequest::getString('code', '', 'request');
7222 + $pvalue = VikRequest::getString('value', '', 'request');
7223 + $pfrom = VikRequest::getString('from', '', 'request');
7224 + $pto = VikRequest::getString('to', '', 'request');
7225 + $pidrooms = VikRequest::getVar('idrooms', array(0));
7226 + $pwhere = VikRequest::getInt('where', 0, 'request');
7227 + $ptype = VikRequest::getString('type', '', 'request');
7228 + $ptype = $ptype == "1" ? 1 : 2;
7229 + $ppercentot = VikRequest::getString('percentot', '', 'request');
7230 + $ppercentot = $ppercentot == "1" ? 1 : 2;
7231 + $pallvehicles = VikRequest::getString('allvehicles', '', 'request');
7232 + $pallvehicles = $pallvehicles == "1" ? 1 : 0;
7233 + $pmintotord = VikRequest::getFloat('mintotord', 0, 'request');
7234 + $pmaxtotord = VikRequest::getFloat('maxtotord', 0, 'request');
7235 + $pexcludetaxes = VikRequest::getInt('excludetaxes', 0, 'request');
7236 + $pminlos = VikRequest::getInt('minlos', 0, 'request');
7237 + $pcustomers = VikRequest::getVar('customers', array());
7238 + $pautomatic = VikRequest::getInt('automatic', 0, 'request');
7239 + $stridrooms = "";
7240 + if (count($pidrooms) > 0 && $pallvehicles != 1) {
7241 + foreach ($pidrooms as $ch) {
7242 + if (!empty($ch)) {
7243 + $stridrooms .= ";".$ch.";";
7244 + }
7245 + }
7246 + }
7247 + $strdatevalid = "";
7248 + if (strlen($pfrom) > 0 && strlen($pto) > 0) {
7249 + $first = VikBooking::getDateTimestamp($pfrom, 0, 0);
7250 + $second = VikBooking::getDateTimestamp($pto, 0, 0);
7251 + if ($first < $second) {
7252 + $strdatevalid .= $first."-".$second;
7253 + }
7254 + }
7255 +
7256 + $dbo = JFactory::getDbo();
7257 + $app = JFactory::getApplication();
7258 +
7259 + $q = "SELECT * FROM `#__vikbooking_coupons` WHERE `code`=".$dbo->quote($pcode)." AND `id`!='".$pwhere."';";
7260 + $dbo->setQuery($q);
7261 + $dbo->execute();
7262 + if ($dbo->getNumRows() > 0) {
7263 + VikError::raiseWarning('', JText::translate('VBCOUPONEXISTS'));
7264 + } else {
7265 + $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 . ";";
7266 + $dbo->setQuery($q);
7267 + $dbo->execute();
7268 +
7269 + $app->enqueueMessage(JText::translate('VBCOUPONSAVEOK'));
7270 +
7271 + // clean up any previously created record with customers
7272 + $q = "DELETE FROM `#__vikbooking_customers_coupons` WHERE `idcoupon`=" . $pwhere;
7273 + $dbo->setQuery($q);
7274 + $dbo->execute();
7275 +
7276 + // check if this coupon should be assigned to specific customers
7277 + foreach ($pcustomers as $id_customer) {
7278 + $customer_coupon = new stdClass;
7279 + $customer_coupon->idcustomer = (int)$id_customer;
7280 + $customer_coupon->idcoupon = (int)$pwhere;
7281 + $customer_coupon->automatic = $pautomatic ? 1 : 0;
7282 +
7283 + $dbo->insertObject('#__vikbooking_customers_coupons', $customer_coupon, 'id');
7284 + }
7285 + }
7286 +
7287 + if ($stay) {
7288 + $app->redirect("index.php?option=com_vikbooking&task=editcoupon&cid[]=$pwhere");
7289 + } else {
7290 + $app->redirect("index.php?option=com_vikbooking&task=coupons");
7291 + }
7292 + }
7293 +
7294 + public function removecoupons()
7295 + {
7296 + if (!JSession::checkToken()) {
7297 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7298 + }
7299 +
7300 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7301 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7302 + }
7303 +
7304 + $dbo = JFactory::getDbo();
7305 +
7306 + $ids = VikRequest::getVar('cid', array(0));
7307 +
7308 + if ($ids) {
7309 + foreach ($ids as $d) {
7310 + // delete coupon record
7311 + $q = "DELETE FROM `#__vikbooking_coupons` WHERE `id`=".$dbo->quote($d).";";
7312 + $dbo->setQuery($q);
7313 + $dbo->execute();
7314 +
7315 + // clean up any previously created record with customers
7316 + $q = "DELETE FROM `#__vikbooking_customers_coupons` WHERE `idcoupon`=" . (int)$d;
7317 + $dbo->setQuery($q);
7318 + $dbo->execute();
7319 + }
7320 + }
7321 +
7322 + JFactory::getApplication()->redirect("index.php?option=com_vikbooking&task=coupons");
7323 + }
7324 +
7325 + public function removemoreimgs()
7326 + {
7327 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7328 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7329 + }
7330 +
7331 + $mainframe = JFactory::getApplication();
7332 + $proomid = VikRequest::getInt('roomid', '', 'request');
7333 + $pimgind = VikRequest::getInt('imgind', '', 'request');
7334 + if (!strlen($pimgind)) {
7335 + $mainframe->redirect("index.php?option=com_vikbooking");
7336 + exit;
7337 + }
7338 + $dbo = JFactory::getDBO();
7339 + $q = "SELECT `moreimgs`,`imgcaptions` FROM `#__vikbooking_rooms` WHERE `id`='".$proomid."';";
7340 + $dbo->setQuery($q);
7341 + $dbo->execute();
7342 + $row = $dbo->loadAssoc();
7343 + $actmore = $row['moreimgs'];
7344 + if (!empty($actmore)) {
7345 + $actsplit = explode(';;', $actmore);
7346 + $captions = json_decode($row['imgcaptions'], true);
7347 + $captions = !is_array($captions) ? array() : $captions;
7348 + if ($pimgind < 0) {
7349 + foreach ($actsplit as $img) {
7350 + @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'big_'.$img);
7351 + @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'thumb_'.$img);
7352 + }
7353 + // reset images and captions
7354 + $actsplit = array();
7355 + $captions = array();
7356 + } else {
7357 + if (array_key_exists($pimgind, $actsplit)) {
7358 + @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'big_'.$actsplit[$pimgind]);
7359 + @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.'thumb_'.$actsplit[$pimgind]);
7360 + // unset current image
7361 + unset($actsplit[$pimgind]);
7362 + // unset caption if exists
7363 + if (isset($captions[$pimgind])) {
7364 + unset($captions[$pimgind]);
7365 + $captions = array_values($captions);
7366 + }
7367 + }
7368 + }
7369 + $newstr = "";
7370 + foreach ($actsplit as $oi) {
7371 + if (!empty($oi)) {
7372 + $newstr .= $oi.';;';
7373 + }
7374 + }
7375 + $q = "UPDATE `#__vikbooking_rooms` SET `moreimgs`=".$dbo->quote($newstr).", `imgcaptions`=".$dbo->quote(json_encode($captions))." WHERE `id`='".$proomid."';";
7376 + $dbo->setQuery($q);
7377 + $dbo->execute();
7378 + }
7379 + $mainframe->redirect("index.php?option=com_vikbooking&task=editroom&cid[]=".$proomid);
7380 + }
7381 +
7382 + public function sortfield() {
7383 + if (!JSession::checkToken('get')) {
7384 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7385 + }
7386 + $mainframe = JFactory::getApplication();
7387 + $sortid = VikRequest::getVar('cid', array(0));
7388 + $pmode = VikRequest::getString('mode', '', 'request');
7389 + $dbo = JFactory::getDBO();
7390 + if (!empty($pmode)) {
7391 + $q = "SELECT `id`,`ordering` FROM `#__vikbooking_custfields` ORDER BY `#__vikbooking_custfields`.`ordering` ASC;";
7392 + $dbo->setQuery($q);
7393 + $dbo->execute();
7394 + $totr=$dbo->getNumRows();
7395 + if ($totr > 1) {
7396 + $data = $dbo->loadAssocList();
7397 + if ($pmode == "up") {
7398 + foreach ($data as $v) {
7399 + if ($v['id'] == $sortid[0]) {
7400 + $y = $v['ordering'];
7401 + }
7402 + }
7403 + if ($y && $y > 1) {
7404 + $vik = $y - 1;
7405 + $found = false;
7406 + foreach ($data as $v) {
7407 + if (intval($v['ordering']) == intval($vik)) {
7408 + $found=true;
7409 + $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
7410 + $dbo->setQuery($q);
7411 + $dbo->execute();
7412 + $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7413 + $dbo->setQuery($q);
7414 + $dbo->execute();
7415 + break;
7416 + }
7417 + }
7418 + if (!$found) {
7419 + $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7420 + $dbo->setQuery($q);
7421 + $dbo->execute();
7422 + }
7423 + }
7424 + } elseif ($pmode == "down") {
7425 + foreach ($data as $v) {
7426 + if ($v['id'] == $sortid[0]) {
7427 + $y = $v['ordering'];
7428 + }
7429 + }
7430 + if ($y) {
7431 + $vik = $y + 1;
7432 + $found = false;
7433 + foreach ($data as $v) {
7434 + if (intval($v['ordering']) == intval($vik)) {
7435 + $found=true;
7436 + $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$y."' WHERE `id`='".$v['id']."' LIMIT 1;";
7437 + $dbo->setQuery($q);
7438 + $dbo->execute();
7439 + $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7440 + $dbo->setQuery($q);
7441 + $dbo->execute();
7442 + break;
7443 + }
7444 + }
7445 + if (!$found) {
7446 + $q = "UPDATE `#__vikbooking_custfields` SET `ordering`='".$vik."' WHERE `id`='".$sortid[0]."' LIMIT 1;";
7447 + $dbo->setQuery($q);
7448 + $dbo->execute();
7449 + }
7450 + }
7451 + }
7452 + }
7453 + $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7454 + } else {
7455 + $mainframe->redirect("index.php?option=com_vikbooking");
7456 + }
7457 + }
7458 +
7459 + public function customf() {
7460 + VikBookingHelper::printHeader("16");
7461 +
7462 + VikRequest::setVar('view', VikRequest::getCmd('view', 'customf'));
7463 +
7464 + parent::display();
7465 +
7466 + if (VikBooking::showFooter()) {
7467 + VikBookingHelper::printFooter();
7468 + }
7469 + }
7470 +
7471 + public function newcustomf() {
7472 + VikBookingHelper::printHeader("16");
7473 +
7474 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomf'));
7475 +
7476 + parent::display();
7477 +
7478 + if (VikBooking::showFooter()) {
7479 + VikBookingHelper::printFooter();
7480 + }
7481 + }
7482 +
7483 + public function editcustomf() {
7484 + VikBookingHelper::printHeader("16");
7485 +
7486 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecustomf'));
7487 +
7488 + parent::display();
7489 +
7490 + if (VikBooking::showFooter()) {
7491 + VikBookingHelper::printFooter();
7492 + }
7493 + }
7494 +
7495 + public function createcustomf()
7496 + {
7497 + if (!JSession::checkToken()) {
7498 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7499 + }
7500 +
7501 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
7502 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7503 + }
7504 +
7505 + $pname = VikRequest::getString('name', '', 'request', VIKREQUEST_ALLOWHTML);
7506 + $ptype = VikRequest::getString('type', '', 'request');
7507 + $pchoose = VikRequest::getVar('choose', array(0));
7508 + $prequired = VikRequest::getString('required', '', 'request');
7509 + $prequired = $prequired == "1" ? 1 : 0;
7510 + $pflag = VikRequest::getString('flag', '', 'request');
7511 + $pisemail = $pflag == 'isemail' ? 1 : 0;
7512 + $pisnominative = $pflag == 'isnominative' && $ptype == 'text' ? 1 : 0;
7513 + $pisphone = $pflag == 'isphone' && $ptype == 'text' ? 1 : 0;
7514 + $pisaddress = $pflag == 'isaddress' && $ptype == 'text' ? 1 : 0;
7515 + $piscity = $pflag == 'iscity' && $ptype == 'text' ? 1 : 0;
7516 + $piszip = $pflag == 'iszip' && $ptype == 'text' ? 1 : 0;
7517 + $piscompany = $pflag == 'iscompany' && $ptype == 'text' ? 1 : 0;
7518 + $pisvat = $pflag == 'isvat' && $ptype == 'text' ? 1 : 0;
7519 + $pisfisccode = $pflag == 'isfisccode' && $ptype == 'text' ? 1 : 0;
7520 + $pispec = $pflag == 'ispec' && $ptype == 'text' ? 1 : 0;
7521 + $pisrecipcode = $pflag == 'isrecipcode' && $ptype == 'text' ? 1 : 0;
7522 + $fieldflag = '';
7523 + if ($pisaddress == 1) {
7524 + $fieldflag = 'address';
7525 + } elseif ($piscity == 1) {
7526 + $fieldflag = 'city';
7527 + } elseif ($piszip == 1) {
7528 + $fieldflag = 'zip';
7529 + } elseif ($piscompany == 1) {
7530 + $fieldflag = 'company';
7531 + } elseif ($pisvat == 1) {
7532 + $fieldflag = 'vat';
7533 + } elseif ($pisfisccode == 1) {
7534 + $fieldflag = 'fisccode';
7535 + } elseif ($pispec == 1) {
7536 + $fieldflag = 'pec';
7537 + } elseif ($pisrecipcode == 1) {
7538 + $fieldflag = 'recipcode';
7539 + }
7540 + $ppoplink = VikRequest::getString('poplink', '', 'request');
7541 + $choosestr = "";
7542 + if (is_array($pchoose)) {
7543 + foreach ($pchoose as $ch) {
7544 + if (!empty($ch)) {
7545 + $choosestr .= $ch.";;__;;";
7546 + }
7547 + }
7548 + }
7549 + $defvalue = VikRequest::getString('defvalue', '', 'request');
7550 +
7551 + $dbo = JFactory::getDbo();
7552 +
7553 + $q = "SELECT `ordering` FROM `#__vikbooking_custfields` ORDER BY `#__vikbooking_custfields`.`ordering` DESC LIMIT 1;";
7554 + $dbo->setQuery($q);
7555 + $dbo->execute();
7556 + if ($dbo->getNumRows() == 1) {
7557 + $getlast = $dbo->loadResult();
7558 + $newsortnum = $getlast + 1;
7559 + } else {
7560 + $newsortnum = 1;
7561 + }
7562 + $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).");";
7563 + $dbo->setQuery($q);
7564 + $dbo->execute();
7565 + $mainframe = JFactory::getApplication();
7566 + $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7567 + }
7568 +
7569 + public function updatecustomf()
7570 + {
7571 + if (!JSession::checkToken()) {
7572 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7573 + }
7574 +
7575 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
7576 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7577 + }
7578 +
7579 + $pname = VikRequest::getString('name', '', 'request', VIKREQUEST_ALLOWHTML);
7580 + $ptype = VikRequest::getString('type', '', 'request');
7581 + $pchoose = VikRequest::getVar('choose', array(0));
7582 + $prequired = VikRequest::getString('required', '', 'request');
7583 + $prequired = $prequired == "1" ? 1 : 0;
7584 + $pflag = VikRequest::getString('flag', '', 'request');
7585 + $pisemail = $pflag == 'isemail' ? 1 : 0;
7586 + $pisnominative = $pflag == 'isnominative' && $ptype == 'text' ? 1 : 0;
7587 + $pisphone = $pflag == 'isphone' && $ptype == 'text' ? 1 : 0;
7588 + $pisaddress = $pflag == 'isaddress' && $ptype == 'text' ? 1 : 0;
7589 + $piscity = $pflag == 'iscity' && $ptype == 'text' ? 1 : 0;
7590 + $piszip = $pflag == 'iszip' && $ptype == 'text' ? 1 : 0;
7591 + $piscompany = $pflag == 'iscompany' && $ptype == 'text' ? 1 : 0;
7592 + $pisvat = $pflag == 'isvat' && $ptype == 'text' ? 1 : 0;
7593 + $pisfisccode = $pflag == 'isfisccode' && $ptype == 'text' ? 1 : 0;
7594 + $pispec = $pflag == 'ispec' && $ptype == 'text' ? 1 : 0;
7595 + $pisrecipcode = $pflag == 'isrecipcode' && $ptype == 'text' ? 1 : 0;
7596 + $fieldflag = '';
7597 + if ($pisaddress == 1) {
7598 + $fieldflag = 'address';
7599 + } elseif ($piscity == 1) {
7600 + $fieldflag = 'city';
7601 + } elseif ($piszip == 1) {
7602 + $fieldflag = 'zip';
7603 + } elseif ($piscompany == 1) {
7604 + $fieldflag = 'company';
7605 + } elseif ($pisvat == 1) {
7606 + $fieldflag = 'vat';
7607 + } elseif ($pisfisccode == 1) {
7608 + $fieldflag = 'fisccode';
7609 + } elseif ($pispec == 1) {
7610 + $fieldflag = 'pec';
7611 + } elseif ($pisrecipcode == 1) {
7612 + $fieldflag = 'recipcode';
7613 + }
7614 + $ppoplink = VikRequest::getString('poplink', '', 'request');
7615 + $pwhere = VikRequest::getInt('where', '', 'request');
7616 + $choosestr = "";
7617 + if (is_array($pchoose)) {
7618 + foreach ($pchoose as $ch) {
7619 + if (!empty($ch)) {
7620 + $choosestr .= $ch.";;__;;";
7621 + }
7622 + }
7623 + }
7624 + $defvalue = VikRequest::getString('defvalue', '', 'request');
7625 +
7626 + $dbo = JFactory::getDbo();
7627 +
7628 + $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).";";
7629 + $dbo->setQuery($q);
7630 + $dbo->execute();
7631 + $mainframe = JFactory::getApplication();
7632 + $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7633 + }
7634 +
7635 + public function removecustomf()
7636 + {
7637 + if (!JSession::checkToken()) {
7638 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7639 + }
7640 +
7641 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7642 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7643 + }
7644 +
7645 + $ids = VikRequest::getVar('cid', array(0));
7646 + if ($ids) {
7647 + $dbo = JFactory::getDBO();
7648 + foreach ($ids as $d) {
7649 + $q = "DELETE FROM `#__vikbooking_custfields` WHERE `id`=".$dbo->quote($d).";";
7650 + $dbo->setQuery($q);
7651 + $dbo->execute();
7652 + }
7653 + }
7654 + $mainframe = JFactory::getApplication();
7655 + $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
7656 + }
7657 +
7658 + public function overv() {
7659 + VikBookingHelper::printHeader("15");
7660 +
7661 + VikRequest::setVar('view', VikRequest::getCmd('view', 'overv'));
7662 +
7663 + parent::display();
7664 +
7665 + if (VikBooking::showFooter()) {
7666 + VikBookingHelper::printFooter();
7667 + }
7668 + }
7669 +
7670 + public function translations() {
7671 + VikBookingHelper::printHeader("21");
7672 +
7673 + VikRequest::setVar('view', VikRequest::getCmd('view', 'translations'));
7674 +
7675 + parent::display();
7676 +
7677 + if (VikBooking::showFooter()) {
7678 + VikBookingHelper::printFooter();
7679 + }
7680 + }
7681 +
7682 + public function savetranslation() {
7683 + if (!JSession::checkToken()) {
7684 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7685 + }
7686 + $this->do_savetranslation();
7687 + }
7688 +
7689 + public function savetranslationstay() {
7690 + if (!JSession::checkToken()) {
7691 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7692 + }
7693 + $this->do_savetranslation(true);
7694 + }
7695 +
7696 + private function do_savetranslation($stay = false) {
7697 + $dbo = JFactory::getDBO();
7698 + $mainframe = JFactory::getApplication();
7699 + $vbo_tn = VikBooking::getTranslator();
7700 + $table = VikRequest::getString('vbo_table', '', 'request');
7701 + $cur_langtab = VikRequest::getString('vbo_lang', '', 'request');
7702 + $langs = $vbo_tn->getLanguagesList();
7703 + $xml_tables = $vbo_tn->getTranslationTables();
7704 + if (!empty($table) && array_key_exists($table, $xml_tables)) {
7705 + $tn = VikRequest::getVar('tn', array(), 'request', 'array', VIKREQUEST_ALLOWRAW);
7706 + $tn_saved = 0;
7707 + $table_cols = $vbo_tn->getTableColumns($table);
7708 + foreach ($langs as $ltag => $lang) {
7709 + if ($ltag == $vbo_tn->default_lang) {
7710 + continue;
7711 + }
7712 + if (array_key_exists($ltag, $tn) && count($tn[$ltag]) > 0) {
7713 + foreach ($tn[$ltag] as $reference_id => $translation) {
7714 + $lang_translation = array();
7715 + foreach ($table_cols as $field => $fdetails) {
7716 + if (!array_key_exists($field, $translation)) {
7717 + continue;
7718 + }
7719 + $ftype = $fdetails['type'];
7720 + if ($ftype == 'skip') {
7721 + continue;
7722 + }
7723 +
7724 + if (is_array($translation[$field])) {
7725 + foreach ($translation[$field] as $tn_field_k => $tn_field_v) {
7726 + if (!is_string($tn_field_v)) {
7727 + continue;
7728 + }
7729 + // replace any possible placeholder for special tags
7730 + $translation[$field][$tn_field_k] = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
7731 + return $match[2];
7732 + }, $translation[$field][$tn_field_k]);
7733 + }
7734 + } elseif (!empty($translation[$field])) {
7735 + // replace any possible placeholder for special tags
7736 + $translation[$field] = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
7737 + return $match[2];
7738 + }, $translation[$field]);
7739 + }
7740 +
7741 + if ($ftype == 'json' && !is_scalar($translation[$field])) {
7742 + $translation[$field] = json_encode($translation[$field]);
7743 + }
7744 + $lang_translation[$field] = $translation[$field];
7745 + }
7746 + if (count($lang_translation) > 0) {
7747 + $q = "SELECT `id` FROM `#__vikbooking_translations` WHERE `table`=".$dbo->quote($table)." AND `lang`=".$dbo->quote($ltag)." AND `reference_id`=".$dbo->quote((int)$reference_id).";";
7748 + $dbo->setQuery($q);
7749 + $dbo->execute();
7750 + if ($dbo->getNumRows() > 0) {
7751 + $last_id = $dbo->loadResult();
7752 + $q = "UPDATE `#__vikbooking_translations` SET `content`=".$dbo->quote(json_encode($lang_translation))." WHERE `id`=".(int)$last_id.";";
7753 + } else {
7754 + $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)).");";
7755 + }
7756 + $dbo->setQuery($q);
7757 + $dbo->execute();
7758 + $tn_saved++;
7759 + }
7760 + }
7761 + }
7762 + }
7763 + if ($tn_saved > 0) {
7764 + $mainframe->enqueueMessage(JText::translate('VBOTRANSLSAVEDOK'));
7765 + }
7766 + } else {
7767 + VikError::raiseWarning('', JText::translate('VBTRANSLATIONERRINVTABLE'));
7768 + }
7769 + $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);
7770 + }
7771 +
7772 + public function choosebusy() {
7773 + VikBookingHelper::printHeader("8");
7774 +
7775 + VikRequest::setVar('view', VikRequest::getCmd('view', 'choosebusy'));
7776 +
7777 + parent::display();
7778 +
7779 + if (VikBooking::showFooter()) {
7780 + VikBookingHelper::printFooter();
7781 + }
7782 + }
7783 +
7784 + public function orders() {
7785 + VikBookingHelper::printHeader("8");
7786 +
7787 + VikRequest::setVar('view', VikRequest::getCmd('view', 'orders'));
7788 +
7789 + parent::display();
7790 +
7791 + if (VikBooking::showFooter()) {
7792 + VikBookingHelper::printFooter();
7793 + }
7794 + }
7795 +
7796 + public function vieworders() {
7797 + //alias method of orders() for backward compatibility with VCM
7798 + $this->orders();
7799 + }
7800 +
7801 + public function editorder() {
7802 + VikBookingHelper::printHeader("8");
7803 +
7804 + VikRequest::setVar('view', VikRequest::getCmd('view', 'editorder'));
7805 +
7806 + parent::display();
7807 +
7808 + if (VikBooking::showFooter()) {
7809 + VikBookingHelper::printFooter();
7810 + }
7811 + }
7812 +
7813 + public function removeorders()
7814 + {
7815 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
7816 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7817 + }
7818 +
7819 + $dbo = JFactory::getDbo();
7820 + $app = JFactory::getApplication();
7821 +
7822 + $ids = VikRequest::getVar('cid', array(0));
7823 + $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
7824 +
7825 + $user = JFactory::getUser();
7826 + $config = VBOFactory::getConfig();
7827 +
7828 + $prev_conf_ids = [];
7829 + $purged = false;
7830 +
7831 + $tot_cancs = 0;
7832 +
7833 + if (is_array($ids) && count($ids)) {
7834 + foreach ($ids as $d) {
7835 + $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $dbo->quote($d);
7836 + $dbo->setQuery($q, 0, 1);
7837 + $row = $dbo->loadAssoc();
7838 +
7839 + // check for any cancellation constraints
7840 + $canc_denied = false;
7841 + if ($row && class_exists('VCMFeesCancellation')) {
7842 + // let VCM detect if there are any constraints for the cancellation
7843 + $canc_denied = VCMFeesCancellation::getInstance($row, $anew = true)->isBookingConstrained();
7844 + if ($canc_denied) {
7845 + // set error message
7846 + $canc_deny_error = VCMFeesCancellation::getInstance()->getError();
7847 + if ($canc_deny_error) {
7848 + $app->enqueueMessage($canc_deny_error, 'error');
7849 + }
7850 + }
7851 + }
7852 +
7853 + if ($row && !$canc_denied) {
7854 + // increase counter
7855 + $tot_cancs++;
7856 +
7857 + // set status to cancelled
7858 + if ($row['status'] != 'cancelled') {
7859 + $q = "UPDATE `#__vikbooking_orders` SET `status`='cancelled' WHERE `id`=".(int)$row['id'].";";
7860 + $dbo->setQuery($q);
7861 + $dbo->execute();
7862 + $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($row['id']) . ";";
7863 + $dbo->setQuery($q);
7864 + $dbo->execute();
7865 + if ($row['status'] == 'confirmed') {
7866 + $prev_conf_ids[] = $row['id'];
7867 + }
7868 + // Booking History
7869 + VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('CB', "({$user->name})");
7870 + }
7871 +
7872 + /**
7873 + * In case of pending bookings being cancelled, schedule the release through VCM.
7874 + *
7875 + * @since 1.18.8 (J) - 1.8.8 (WP)
7876 + */
7877 + if ($row['status'] == 'standby' && method_exists('VCMRequestAvailability', 'setForRelease')) {
7878 + // let the CM schedule the release of the involved and unconfirmed booking IDs, if needed
7879 + VCMRequestAvailability::getInstance()->setForRelease([$row['id']]);
7880 + }
7881 +
7882 + // free records up
7883 + $q = "SELECT * FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
7884 + $dbo->setQuery($q);
7885 + $ordbusy = $dbo->loadAssocList();
7886 + if ($ordbusy) {
7887 + foreach ($ordbusy as $ob) {
7888 + $q = "DELETE FROM `#__vikbooking_busy` WHERE `id`='".$ob['idbusy']."';";
7889 + $dbo->setQuery($q);
7890 + $dbo->execute();
7891 + }
7892 + }
7893 +
7894 + $q = "DELETE FROM `#__vikbooking_ordersbusy` WHERE `idorder`=".(int)$row['id'].";";
7895 + $dbo->setQuery($q);
7896 + $dbo->execute();
7897 +
7898 + // check for purge removal
7899 + if ($row['status'] == 'cancelled') {
7900 + $q = "DELETE FROM `#__vikbooking_customers_orders` WHERE `idorder`=" . intval($row['id']) . ";";
7901 + $dbo->setQuery($q);
7902 + $dbo->execute();
7903 + $q = "DELETE FROM `#__vikbooking_ordersrooms` WHERE `idorder`=".(int)$row['id'].";";
7904 + $dbo->setQuery($q);
7905 + $dbo->execute();
7906 + $q = "DELETE FROM `#__vikbooking_orderhistory` WHERE `idorder`=".(int)$row['id'].";";
7907 + $dbo->setQuery($q);
7908 + $dbo->execute();
7909 + $q = "DELETE FROM `#__vikbooking_orders` WHERE `id`=".(int)$row['id'].";";
7910 + $dbo->setQuery($q);
7911 + $dbo->execute();
7912 + // in case of split stay booking, remove the transient
7913 + if ($row['split_stay']) {
7914 + $config->remove('split_stay_' . $row['id']);
7915 + }
7916 + // turn flag on
7917 + $purged = true;
7918 + }
7919 + }
7920 + }
7921 +
7922 + if ($tot_cancs) {
7923 + // enqueue system message
7924 + $app->enqueueMessage(JText::translate('VBMESSDELBUSY'));
7925 + }
7926 + }
7927 +
7928 + if ($prev_conf_ids) {
7929 + $prev_conf_ids_str = '';
7930 + foreach ($prev_conf_ids as $prev_id) {
7931 + $prev_conf_ids_str .= '&cid[]='.$prev_id;
7932 + }
7933 + //Invoke Channel Manager
7934 + $vcm_autosync = VikBooking::vcmAutoUpdate();
7935 + if ($vcm_autosync > 0) {
7936 + $vcm_obj = VikBooking::getVcmInvoker();
7937 + $vcm_obj->setOids($prev_conf_ids)->setSyncType('cancel');
7938 + $sync_result = $vcm_obj->doSync();
7939 + if ($sync_result === false) {
7940 + $vcm_err = $vcm_obj->getError();
7941 + VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a> '.(strlen($vcm_err) > 0 ? '('.$vcm_err.')' : ''));
7942 + }
7943 + } elseif (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
7944 + $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');
7945 + VikError::raiseNotice('', JText::translate('VBCHANNELMANAGERINVOKEASK').' <button type="button" class="btn btn-primary" onclick="document.location.href=\''.$vcm_sync_url.'\';">'.JText::translate('VBCHANNELMANAGERSENDRQ').'</button>');
7946 + }
7947 + //
7948 + }
7949 +
7950 + if (!empty($pgoto)) {
7951 + if (is_numeric($pgoto) && is_array($ids) && count($ids) === 1) {
7952 + if ($purged) {
7953 + // go back to the bookings list page
7954 + $app->redirect("index.php?option=com_vikbooking&task=orders");
7955 + } else {
7956 + // go back to the booking details page
7957 + $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . (int)$ids[0]);
7958 + }
7959 + exit;
7960 + }
7961 + // we expect the goto URL to be base64 encoded
7962 + $app->redirect(base64_decode($pgoto));
7963 + exit;
7964 + }
7965 +
7966 + // go back to the bookings list page
7967 + $app->redirect("index.php?option=com_vikbooking&task=orders");
7968 + }
7969 +
7970 + public function config() {
7971 + VikBookingHelper::printHeader("11");
7972 +
7973 + VikRequest::setVar('view', VikRequest::getCmd('view', 'config'));
7974 +
7975 + parent::display();
7976 +
7977 + if (VikBooking::showFooter()) {
7978 + VikBookingHelper::printFooter();
7979 + }
7980 + }
7981 +
7982 + public function saveconfig()
7983 + {
7984 + if (!JSession::checkToken()) {
7985 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
7986 + }
7987 +
7988 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking') || !JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
7989 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
7990 + }
7991 +
7992 + $dbo = JFactory::getDbo();
7993 + $app = JFactory::getApplication();
7994 +
7995 + $config = VBOFactory::getConfig();
7996 +
7997 + $pallowbooking = VikRequest::getString('allowbooking', '', 'request');
7998 + $pdisabledbookingmsg = VikRequest::getString('disabledbookingmsg', '', 'request', VIKREQUEST_ALLOWHTML);
7999 + $ptimeopenstorefh = VikRequest::getString('timeopenstorefh', '', 'request');
8000 + $ptimeopenstorefm = VikRequest::getString('timeopenstorefm', '', 'request');
8001 + $ptimeopenstoreth = VikRequest::getString('timeopenstoreth', '', 'request');
8002 + $ptimeopenstoretm = VikRequest::getString('timeopenstoretm', '', 'request');
8003 + $phoursmorebookingback = VikRequest::getString('hoursmorebookingback', '', 'request');
8004 + $pdateformat = VikRequest::getString('dateformat', '', 'request');
8005 + $pdatesep = VikRequest::getString('datesep', '', 'request');
8006 + $pdatesep = empty($pdatesep) ? "/" : $pdatesep;
8007 + $presmodcanc = VikRequest::getInt('resmodcanc', 1, 'request');
8008 + $presmodcancmin = VikRequest::getInt('resmodcancmin', 1, 'request');
8009 + $pshowcategories = VikRequest::getString('showcategories', '', 'request');
8010 + $pshowchildren = VikRequest::getString('showchildren', '', 'request');
8011 + $psearchsuggestions = VikRequest::getInt('searchsuggestions', '', 'request');
8012 + $ptokenform = VikRequest::getString('tokenform', '', 'request');
8013 + $padminemail = VikRequest::getString('adminemail', '', 'request');
8014 + $psenderemail = VikRequest::getString('senderemail', '', 'request');
8015 + $pminuteslock = VikRequest::getString('minuteslock', '', 'request');
8016 + $pminautoremove = VikRequest::getInt('minautoremove', '', 'request');
8017 + $pfooterordmail = VikRequest::getString('footerordmail', '', 'request', VIKREQUEST_ALLOWHTML);
8018 + $ptermsconds = VikRequest::getString('termsconds', '', 'request', VIKREQUEST_ALLOWHTML);
8019 + $prequirelogin = VikRequest::getString('requirelogin', '', 'request');
8020 + $pautoroomunit = VikRequest::getInt('autoroomunit', '', 'request');
8021 + $ptodaybookings = VikRequest::getInt('todaybookings', '', 'request');
8022 + $ptodaybookings = $ptodaybookings === 1 ? 1 : 0;
8023 + $ploadbootstrap = VikRequest::getInt('loadbootstrap', '', 'request');
8024 + $ploadbootstrap = $ploadbootstrap === 1 ? 1 : 0;
8025 + $pusefa = VikRequest::getInt('usefa', '', 'request');
8026 + $pusefa = $pusefa > 0 ? 1 : 0;
8027 + $ploadjquery = VikRequest::getString('loadjquery', '', 'request');
8028 + $ploadjquery = $ploadjquery == "yes" ? "1" : "0";
8029 + $pcalendar = VikRequest::getString('calendar', '', 'request');
8030 + $pcalendar = $pcalendar == "joomla" ? "joomla" : "jqueryui";
8031 + $penablecoupons = VikRequest::getString('enablecoupons', '', 'request');
8032 + $penablecoupons = $penablecoupons == "1" ? 1 : 0;
8033 + $penablepin = VikRequest::getString('enablepin', '', 'request');
8034 + $penablepin = $penablepin == "1" ? 1 : 0;
8035 + $pmindaysadvance = VikRequest::getInt('mindaysadvance', '', 'request');
8036 + $pmindaysadvance = $pmindaysadvance < 0 ? 0 : $pmindaysadvance;
8037 + $pautodefcalnights = VikRequest::getInt('autodefcalnights', '', 'request');
8038 + $pautodefcalnights = $pautodefcalnights >= 1 ? $pautodefcalnights : '1';
8039 + $pnumrooms = VikRequest::getInt('numrooms', '', 'request');
8040 + $pnumrooms = $pnumrooms > 0 ? $pnumrooms : '5';
8041 + $pnumadultsfrom = VikRequest::getString('numadultsfrom', '', 'request');
8042 + $pnumadultsfrom = intval($pnumadultsfrom) >= 0 ? $pnumadultsfrom : '1';
8043 + $pnumadultsto = VikRequest::getString('numadultsto', '', 'request');
8044 + $pnumadultsto = intval($pnumadultsto) > 0 ? $pnumadultsto : '10';
8045 + if (intval($pnumadultsfrom) > intval($pnumadultsto)) {
8046 + $pnumadultsfrom = '1';
8047 + $pnumadultsto = '10';
8048 + }
8049 + $pnumchildrenfrom = VikRequest::getString('numchildrenfrom', '', 'request');
8050 + $pnumchildrenfrom = intval($pnumchildrenfrom) >= 0 ? $pnumchildrenfrom : '1';
8051 + $pnumchildrento = VikRequest::getString('numchildrento', '', 'request');
8052 + $pnumchildrento = intval($pnumchildrento) > 0 ? $pnumchildrento : '4';
8053 + if (intval($pnumchildrenfrom) > intval($pnumchildrento)) {
8054 + $pnumadultsfrom = '1';
8055 + $pnumadultsto = '4';
8056 + }
8057 + $confnumadults = $pnumadultsfrom.'-'.$pnumadultsto;
8058 + $confnumchildren = $pnumchildrenfrom.'-'.$pnumchildrento;
8059 + $pmaxdate = VikRequest::getString('maxdate', '', 'request');
8060 + $pmaxdate = intval($pmaxdate) < 1 ? 2 : $pmaxdate;
8061 + $pmaxdateinterval = VikRequest::getString('maxdateinterval', '', 'request');
8062 + $pmaxdateinterval = !in_array($pmaxdateinterval, array('d', 'w', 'm', 'y')) ? 'y' : $pmaxdateinterval;
8063 + $maxdate_str = '+'.$pmaxdate.$pmaxdateinterval;
8064 + $pcronkey = VikRequest::getString('cronkey', '', 'request');
8065 + $pcdsfrom = VikRequest::getVar('cdsfrom', array());
8066 + $pcdsto = VikRequest::getVar('cdsto', array());
8067 + $closing_dates = array();
8068 + if (count($pcdsfrom)) {
8069 + foreach ($pcdsfrom as $kcd => $vcdfrom) {
8070 + if (!empty($vcdfrom) && array_key_exists($kcd, $pcdsto) && !empty($pcdsto[$kcd])) {
8071 + $tscdfrom = VikBooking::getDateTimestamp($vcdfrom, '0', '0');
8072 + $tscdto = VikBooking::getDateTimestamp($pcdsto[$kcd], '0', '0');
8073 + if (!empty($tscdfrom) && !empty($tscdto) && $tscdto >= $tscdfrom) {
8074 + $cdval = array('from' => $tscdfrom, 'to' => $tscdto);
8075 + if (!in_array($cdval, $closing_dates)) {
8076 + $closing_dates[] = $cdval;
8077 + }
8078 + }
8079 + }
8080 + }
8081 + }
8082 + $psmartsearch = VikRequest::getString('smartsearch', '', 'request');
8083 + $psmartsearch = $psmartsearch == "dynamic" ? "dynamic" : "automatic";
8084 + $pvbosef = VikRequest::getInt('vbosef', '', 'request');
8085 + $vbosef = file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php');
8086 + if ($pvbosef === 1) {
8087 + if (!$vbosef) {
8088 + rename(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'_router.php', VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php');
8089 + }
8090 + } else {
8091 + if ($vbosef) {
8092 + rename(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'router.php', VBO_SITE_PATH.DIRECTORY_SEPARATOR.'_router.php');
8093 + }
8094 + }
8095 + $pmultilang = VikRequest::getString('multilang', '', 'request');
8096 + $pmultilang = $pmultilang == "1" ? 1 : 0;
8097 + $pvcmautoupd = VikRequest::getInt('vcmautoupd', '', 'request');
8098 + $pvcmautoupd = $pvcmautoupd > 0 ? 1 : 0;
8099 + /**
8100 + * Chat params and configuration settings
8101 + *
8102 + * @since 1.12
8103 + */
8104 + $pchatenabled = VikRequest::getInt('chatenabled', 0, 'request');
8105 + if (is_file(VCM_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'lib.vikchannelmanager.php')) {
8106 + $config->set('chatenabled', $pchatenabled);
8107 +
8108 + // chat params
8109 + $pchat_res_status = explode(';', VikRequest::getString('chat_res_status', '', 'request'));
8110 + $chat_res_status = array();
8111 + foreach ($pchat_res_status as $chatrs) {
8112 + if (!empty($chatrs)) {
8113 + array_push($chat_res_status, $chatrs);
8114 + }
8115 + }
8116 + $chatparams = new stdClass;
8117 + $chatparams->res_status = $chat_res_status;
8118 + $chatparams->av_type = VikRequest::getString('chat_av_type', '', 'request');
8119 + $chatparams->av_days = VikRequest::getInt('chat_av_days', 0, 'request');
8120 +
8121 + $config->set('chatparams', $chatparams);
8122 + }
8123 +
8124 + /**
8125 + * Pre check-in configuration settings
8126 + *
8127 + * @since 1.12
8128 + */
8129 + $pprecheckinenabled = VikRequest::getInt('precheckinenabled', 0, 'request');
8130 + $pprecheckinenabled = $pprecheckinenabled > 0 ? 1 : 0;
8131 +
8132 + $config->set('precheckinenabled', $pprecheckinenabled);
8133 + // this may be a negative integer, it should not be unsigned
8134 + $config->set('precheckinminoffset', VikRequest::getInt('precheckinminoffset', 0, 'request'));
8135 +
8136 + $pupsellingenabled = VikRequest::getInt('upsellingenabled', 0, 'request');
8137 + $pupsellingenabled = $pupsellingenabled > 0 ? 1 : 0;
8138 + $config->set('upselling', $pupsellingenabled);
8139 +
8140 + $porphanscal = VikRequest::getString('orphanscal', 'next', 'request');
8141 + $porphanscal = $porphanscal == 'prevnext' ? 'prevnext' : 'next';
8142 + $config->set('orphanscalculation', $porphanscal);
8143 +
8144 + $psrcrtpl = VikRequest::getString('srcrtpl', 'compact', 'request');
8145 + $config->set('searchrestmpl', $psrcrtpl);
8146 +
8147 + /**
8148 + * Guest Reviews settings
8149 + *
8150 + * @since 1.13
8151 + */
8152 + $pgrenabled = VikRequest::getInt('grenabled', 0, 'request');
8153 + $pgrminchars = VikRequest::getInt('grminchars', 0, 'request');
8154 + $pgrappr = VikRequest::getString('grappr', 'auto', 'request');
8155 + $pgrappr = $pgrappr == 'auto' ? 'auto' : 'manual';
8156 + $pgrtype = VikRequest::getString('grtype', 'service', 'request');
8157 + $pgrtype = $pgrtype == 'service' ? 'service' : 'global';
8158 + $pgrsrv = VikRequest::getVar('grsrv', array(), 'request', 'array');
8159 + $config->set('grenabled', $pgrenabled);
8160 + $config->set('grminchars', $pgrminchars);
8161 + $config->set('grappr', $pgrappr);
8162 + $config->set('grtype', $pgrtype);
8163 + try {
8164 + // always truncate service names (this query may require special permissions)
8165 + $q = "TRUNCATE TABLE `#__vikbooking_greview_service`;";
8166 + $dbo->setQuery($q);
8167 + $dbo->execute();
8168 + } catch (Exception $e) {
8169 + // do nothing
8170 + }
8171 + foreach ($pgrsrv as $srvname) {
8172 + $q = "INSERT INTO `#__vikbooking_greview_service` (`service_name`) VALUES (" . $dbo->quote($srvname) . ");";
8173 + $dbo->setQuery($q);
8174 + $dbo->execute();
8175 + }
8176 +
8177 + /**
8178 + * Preferred countries ordering, or custom countries.
8179 + *
8180 + * @since 1.14 (J) - 1.3.11 (WP)
8181 + * @since 1.14.1 (J) - 1.4.1 (WP) we also support "cust_pref_countries"
8182 + */
8183 + $pref_countries = VikRequest::getVar('pref_countries', array());
8184 + $cust_pref_countries = VikRequest::getString('cust_pref_countries', '', 'request');
8185 + $pref_countries = !is_array($pref_countries) || empty($pref_countries[0]) ? VikBooking::preferredCountriesOrdering() : $pref_countries;
8186 + if (!empty($cust_pref_countries)) {
8187 + $all_custom_prefcountries = array();
8188 + $cust_pref_countries = explode(',', $cust_pref_countries);
8189 + foreach ($cust_pref_countries as $cust_pref_country) {
8190 + $cust_pref_country = trim(strtolower($cust_pref_country));
8191 + if (empty($cust_pref_country) || strlen($cust_pref_country) != 2) {
8192 + continue;
8193 + }
8194 + array_push($all_custom_prefcountries, $cust_pref_country);
8195 + }
8196 + if (count($all_custom_prefcountries)) {
8197 + $pref_countries = $all_custom_prefcountries;
8198 + }
8199 + }
8200 + $config->set('preferred_countries', $pref_countries);
8201 + //
8202 +
8203 + $gmapskey = VikRequest::getString('gmapskey', '', 'request');
8204 + $config->set('gmapskey', $gmapskey);
8205 +
8206 + $pref_textcolor = VikRequest::getString('pref_textcolor', '', 'request');
8207 + $pref_bgcolor = VikRequest::getString('pref_bgcolor', '', 'request');
8208 + $pref_fontcolor = VikRequest::getString('pref_fontcolor', '', 'request');
8209 + $pref_bgcolorhov = VikRequest::getString('pref_bgcolorhov', '', 'request');
8210 + $pref_fontcolorhov = VikRequest::getString('pref_fontcolorhov', '', 'request');
8211 + $pref_colors = array(
8212 + 'textcolor' => $pref_textcolor,
8213 + 'bgcolor' => $pref_bgcolor,
8214 + 'fontcolor' => $pref_fontcolor,
8215 + 'bgcolorhov' => $pref_bgcolorhov,
8216 + 'fontcolorhov' => $pref_fontcolorhov,
8217 + );
8218 + $config->set('pref_colors', $pref_colors);
8219 +
8220 + $interactive_map = VikRequest::getInt('interactive_map', 0, 'request');
8221 + $config->set('interactive_map', $interactive_map);
8222 + $config->set('search_filters', VikRequest::getInt('search_filters', 0, 'request'));
8223 +
8224 + $noemptydecimals = VikRequest::getInt('noemptydecimals', 0, 'request');
8225 + $config->set('noemptydecimals', $noemptydecimals);
8226 +
8227 + /**
8228 + * Appearance preferences (light, auto, dark mode).
8229 + *
8230 + * @since 1.15.0 (J) - 1.5.0 (WP)
8231 + * @since 1.16.10 (J) - 1.6.10 (WP) mirrored on VCM.
8232 + */
8233 + $appearance_pref = VikRequest::getString('appearance_pref', '');
8234 + $config->set('appearance_pref', $appearance_pref);
8235 + if (class_exists('VCMFactory')) {
8236 + VCMFactory::getConfig()->set('appearance_pref', $appearance_pref);
8237 + }
8238 +
8239 + $res_backend_path = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR;
8240 + $picon = "";
8241 + if (intval($_FILES['sitelogo']['error']) == 0 && trim($_FILES['sitelogo']['name'])!="") {
8242 + jimport('joomla.filesystem.file');
8243 + if (@is_uploaded_file($_FILES['sitelogo']['tmp_name'])) {
8244 + $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['sitelogo']['name'])));
8245 + if (file_exists($res_backend_path.$safename)) {
8246 + $j = 1;
8247 + while (file_exists($res_backend_path.$j.$safename)) {
8248 + $j++;
8249 + }
8250 + $pwhere = $res_backend_path.$j.$safename;
8251 + } else {
8252 + $j = "";
8253 + $pwhere = $res_backend_path.$safename;
8254 + }
8255 + if (!getimagesize($_FILES['sitelogo']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
8256 + @unlink($pwhere);
8257 + $picon = "";
8258 + } else {
8259 + VikBooking::uploadFile($_FILES['sitelogo']['tmp_name'], $pwhere);
8260 + @chmod($pwhere, 0644);
8261 + $picon = $j.$safename;
8262 + }
8263 + }
8264 + if (!empty($picon)) {
8265 + $config->set('sitelogo', $picon);
8266 + }
8267 + }
8268 + $pbackicon = "";
8269 + if (intval($_FILES['backlogo']['error']) == 0 && trim($_FILES['backlogo']['name'])!="") {
8270 + jimport('joomla.filesystem.file');
8271 + if (@is_uploaded_file($_FILES['backlogo']['tmp_name'])) {
8272 + $safename = JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['backlogo']['name'])));
8273 + if (file_exists($res_backend_path.$safename)) {
8274 + $j = 1;
8275 + while (file_exists($res_backend_path.$j.$safename)) {
8276 + $j++;
8277 + }
8278 + $pwhere = $res_backend_path.$j.$safename;
8279 + } else {
8280 + $j = "";
8281 + $pwhere = $res_backend_path.$safename;
8282 + }
8283 + if (!getimagesize($_FILES['backlogo']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
8284 + @unlink($pwhere);
8285 + $pbackicon = "";
8286 + } else {
8287 + VikBooking::uploadFile($_FILES['backlogo']['tmp_name'], $pwhere);
8288 + @chmod($pwhere, 0644);
8289 + $pbackicon = $j.$safename;
8290 + }
8291 + }
8292 + if (!empty($pbackicon)) {
8293 + $config->set('backlogo', $pbackicon);
8294 + }
8295 + }
8296 + $config->set('vcmautoupd', $pvcmautoupd);
8297 + $config->set('allowbooking', empty($pallowbooking) || $pallowbooking != "1" ? 0 : 1);
8298 + $config->set('showcategories', empty($pshowcategories) || $pshowcategories != "yes" ? 0 : 1);
8299 + $config->set('showchildren', empty($pshowchildren) || $pshowchildren != "yes" ? 0 : 1);
8300 + $config->set('searchsuggestions', $psearchsuggestions);
8301 + $config->set('tokenform', empty($ptokenform) || $ptokenform != "yes" ? 0 : 1);
8302 + $config->set('guests_label', $app->input->getString('guests_label', 'adults'));
8303 + $config->set('search_show_busy_listings', $app->input->getInt('search_show_busy_listings', 0));
8304 + $config->set('search_link_roomdetails', $app->input->getInt('search_link_roomdetails', 0));
8305 +
8306 + // translatable text
8307 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pfooterordmail)." WHERE `param`='footerordmail';";
8308 + $dbo->setQuery($q);
8309 + $dbo->execute();
8310 +
8311 + // translatable text
8312 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pdisabledbookingmsg)." WHERE `param`='disabledbookingmsg';";
8313 + $dbo->setQuery($q);
8314 + $dbo->execute();
8315 +
8316 + // translatable text
8317 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($app->input->getString('guests_allowed_policy', '', 'raw')) . " WHERE `param`='guests_allowed_policy';";
8318 + $dbo->setQuery($q);
8319 + $dbo->execute();
8320 +
8321 + // terms and conditions
8322 + $q = "SELECT `id`,`setting` FROM `#__vikbooking_texts` WHERE `param`='termsconds';";
8323 + $dbo->setQuery($q);
8324 + $dbo->execute();
8325 + if ($dbo->getNumRows() > 0) {
8326 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($ptermsconds)." WHERE `param`='termsconds';";
8327 + $dbo->setQuery($q);
8328 + $dbo->execute();
8329 + } else {
8330 + $q = "INSERT INTO `#__vikbooking_texts` (`param`,`exp`,`setting`) VALUES ('termsconds','Terms and Conditions',".$dbo->quote($ptermsconds).");";
8331 + $dbo->setQuery($q);
8332 + $dbo->execute();
8333 + }
8334 +
8335 + $config->set('adminemail', $padminemail);
8336 + $config->set('senderemail', $psenderemail);
8337 + $config->set('dateformat', empty($pdateformat) ? "%d/%m/%Y" : $pdateformat);
8338 + $config->set('datesep', $pdatesep);
8339 + $config->set('resmodcanc', $presmodcanc);
8340 + $config->set('resmodcancmin', $presmodcancmin);
8341 + $config->set('minuteslock', $pminuteslock);
8342 + $config->set('minautoremove', $pminautoremove);
8343 +
8344 + $openingh = $ptimeopenstorefh * 3600;
8345 + $openingm = $ptimeopenstorefm * 60;
8346 + $openingts = $openingh + $openingm;
8347 + $closingh = $ptimeopenstoreth * 3600;
8348 + $closingm = $ptimeopenstoretm * 60;
8349 + $closingts = $closingh + $closingm;
8350 + // check if the check-in/out times have changed and if there are future bookings with the old time to prevent availability errors
8351 + $prevtimes = $config->get('timeopenstore', '');
8352 + if ($prevtimes != $openingts . "-" . $closingts) {
8353 + $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `checkout`>".time().";";
8354 + $dbo->setQuery($q);
8355 + $dbo->execute();
8356 + if ($dbo->getNumRows() > 0) {
8357 + VikError::raiseWarning('', JText::translate('VBOCONFIGWARNDIFFCHECKINOUT'));
8358 + /**
8359 + * VBO 1.10 Patch - we concatenate a button to unify the check-in/out times
8360 + * for all reservations to avoid issues with the availability.
8361 + *
8362 + * @since August 29th 2018
8363 + */
8364 + 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>');
8365 + //
8366 + }
8367 + }
8368 + $config->set('timeopenstore', $openingts . "-" . $closingts);
8369 +
8370 + // set the hours of extended gratuity period to the difference between checkin and checkout if checkout is later
8371 + $phoursmorebookingback = "0";
8372 + if ($closingts > $openingts) {
8373 + $diffcheck = ($closingts - $openingts) / 3600;
8374 + $phoursmorebookingback = ceil($diffcheck);
8375 + }
8376 + $config->set('hoursmorebookingback', $phoursmorebookingback);
8377 + $config->set('hoursmoreroomavail', '0');
8378 + $config->set('multilang', $pmultilang);
8379 + $config->set('requirelogin', $prequirelogin == "1" ? 1 : 0);
8380 + $config->set('autoroomunit', $pautoroomunit ? 1 : 0);
8381 + $config->set('todaybookings', $ptodaybookings);
8382 + $config->set('bootstrap', $ploadbootstrap);
8383 + $config->set('usefa', $pusefa);
8384 + $config->set('loadjquery', $ploadjquery);
8385 + $config->set('calendar', $pcalendar ?: 'jqueryui');
8386 + $config->set('dboptimizetime', $app->input->getString('dboptimizetime', ''));
8387 + $config->set('enablecoupons', $penablecoupons);
8388 + $config->set('enablepin', $penablepin);
8389 + $config->set('mindaysadvance', $pmindaysadvance);
8390 + $config->set('autodefcalnights', $pautodefcalnights);
8391 + $config->set('numrooms', $pnumrooms);
8392 + $config->set('numadults', $confnumadults);
8393 + $config->set('numchildren', $confnumchildren);
8394 + $config->set('closingdates', $closing_dates);
8395 + $config->set('smartsearch', $psmartsearch);
8396 + $config->set('maxdate', $maxdate_str);
8397 + $config->set('cronkey', $pcronkey);
8398 +
8399 + $pfronttitle = VikRequest::getString('fronttitle', '', 'request');
8400 + $pfronttitletag = VikRequest::getString('fronttitletag', '', 'request');
8401 + $pfronttitletagclass = VikRequest::getString('fronttitletagclass', '', 'request');
8402 + $pshowfooter = VikRequest::getString('showfooter', '', 'request');
8403 + $pintromain = VikRequest::getString('intromain', '', 'request', VIKREQUEST_ALLOWHTML);
8404 + $pclosingmain = VikRequest::getString('closingmain', '', 'request', VIKREQUEST_ALLOWHTML);
8405 + $pcurrencyname = VikRequest::getString('currencyname', '', 'request', VIKREQUEST_ALLOWHTML);
8406 + $pcurrencysymb = VikRequest::getString('currencysymb', '', 'request', VIKREQUEST_ALLOWHTML);
8407 + $pcurrencycodepp = VikRequest::getString('currencycodepp', '', 'request');
8408 + $pnumdecimals = VikRequest::getString('numdecimals', '', 'request');
8409 + $pnumdecimals = intval($pnumdecimals);
8410 + $pdecseparator = VikRequest::getString('decseparator', '', 'request');
8411 + $pdecseparator = empty($pdecseparator) ? '.' : $pdecseparator;
8412 + $pthoseparator = VikRequest::getString('thoseparator', '', 'request');
8413 + $numberformatstr = $pnumdecimals.':'.$pdecseparator.':'.$pthoseparator;
8414 + $pshowpartlyreserved = VikRequest::getString('showpartlyreserved', '', 'request');
8415 + $pshowpartlyreserved = $pshowpartlyreserved == "yes" ? 1 : 0;
8416 + $pshowcheckinoutonly = VikRequest::getInt('showcheckinoutonly', '', 'request');
8417 + $pshowcheckinoutonly = $pshowcheckinoutonly > 0 ? 1 : 0;
8418 + $pnumcalendars = VikRequest::getInt('numcalendars', '', 'request');
8419 + $pnumcalendars = $pnumcalendars > -1 ? $pnumcalendars : 3;
8420 + $pthumbsize = VikRequest::getInt('thumbsize', 0, 'request');
8421 + $pfirstwday = VikRequest::getString('firstwday', '', 'request');
8422 + $pfirstwday = intval($pfirstwday) >= 0 && intval($pfirstwday) <= 6 ? $pfirstwday : '0';
8423 + $pbctagname = VikRequest::getVar('bctagname', array());
8424 + $pbctagcolor = VikRequest::getVar('bctagcolor', array());
8425 + $pbctagrule = VikRequest::getVar('bctagrule', array());
8426 + $bctags_arr = array();
8427 + $bctags_rules = array();
8428 + if (count($pbctagname) > 0) {
8429 + foreach ($pbctagname as $bctk => $bctv) {
8430 + if (!empty($bctv) && !empty($pbctagcolor[$bctk]) && strlen($pbctagrule[$bctk]) > 0) {
8431 + if (intval($pbctagrule[$bctk]) == 0 || !in_array($pbctagrule[$bctk], $bctags_rules)) {
8432 + $bctags_rules[] = $pbctagrule[$bctk];
8433 + $bctags_arr[] = array('color' => $pbctagcolor[$bctk], 'name' => $bctv, 'rule' => $pbctagrule[$bctk]);
8434 + }
8435 + }
8436 + }
8437 + }
8438 + //theme
8439 + $ptheme = VikRequest::getString('theme', '', 'request');
8440 + if (empty($ptheme) || $ptheme == 'default') {
8441 + $ptheme = 'default';
8442 + } else {
8443 + $validtheme = false;
8444 + $themes = glob(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'themes'.DIRECTORY_SEPARATOR.'*');
8445 + if (count($themes) > 0) {
8446 + $strip = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'themes'.DIRECTORY_SEPARATOR;
8447 + foreach ($themes as $th) {
8448 + if (is_dir($th)) {
8449 + $tname = str_replace($strip, '', $th);
8450 + if ($tname == $ptheme) {
8451 + $validtheme = true;
8452 + break;
8453 + }
8454 + }
8455 + }
8456 + }
8457 + if ($validtheme == false) {
8458 + $ptheme = 'default';
8459 + }
8460 + }
8461 + $config->set('theme', $ptheme);
8462 + //
8463 + $config->set('showpartlyreserved', $pshowpartlyreserved);
8464 + $config->set('showcheckinoutonly', $pshowcheckinoutonly);
8465 + $config->set('numcalendars', $pnumcalendars);
8466 +
8467 + // record may not be set
8468 + $config->set('thumbsize', $pthumbsize);
8469 +
8470 + $config->set('firstwday', $pfirstwday);
8471 +
8472 + // translatable text
8473 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pfronttitle)." WHERE `param`='fronttitle';";
8474 + $dbo->setQuery($q);
8475 + $dbo->execute();
8476 +
8477 + $config->set('fronttitletag', $pfronttitletag);
8478 + $config->set('fronttitletagclass', $pfronttitletagclass);
8479 + $config->set('showfooter', empty($pshowfooter) || $pshowfooter != "yes" ? 0 : 1);
8480 +
8481 + // translatable texts
8482 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pintromain)." WHERE `param`='intromain';";
8483 + $dbo->setQuery($q);
8484 + $dbo->execute();
8485 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($pclosingmain)." WHERE `param`='closingmain';";
8486 + $dbo->setQuery($q);
8487 + $dbo->execute();
8488 +
8489 + $config->set('currencyname', $pcurrencyname);
8490 + $config->set('currencysymb', $pcurrencysymb);
8491 + $config->set('currencypos', $app->input->getAlnum('currencypos', 'before'));
8492 + $config->set('currencycodepp', $pcurrencycodepp);
8493 + $config->set('numberformat', $numberformatstr);
8494 + // bookings color tags
8495 + $config->set('bookingsctags', $bctags_arr);
8496 +
8497 + $pivainclusa = VikRequest::getString('ivainclusa', '', 'request');
8498 + $ptaxsummary = VikRequest::getString('taxsummary', '', 'request');
8499 + $ptaxsummary = empty($ptaxsummary) || $ptaxsummary != "yes" ? "0" : "1";
8500 + $pccpaypal = VikRequest::getString('ccpaypal', '', 'request');
8501 + $ppaytotal = VikRequest::getString('paytotal', '', 'request');
8502 + $ppayaccpercent = VikRequest::getString('payaccpercent', '', 'request');
8503 + $ptypedeposit = VikRequest::getString('typedeposit', '', 'request');
8504 + $ptypedeposit = $ptypedeposit == 'fixed' ? 'fixed' : 'pcent';
8505 + $pdepoverrides = VikRequest::getString('depoverrides', '', 'request');
8506 + $ppaymentname = VikRequest::getString('paymentname', '', 'request');
8507 + $pdisclaimer = VikRequest::getString('disclaimer', '', 'request', VIKREQUEST_ALLOWHTML);
8508 + $pmultipay = VikRequest::getString('multipay', '', 'request');
8509 + $pmultipay = $pmultipay == "yes" ? 1 : 0;
8510 + $pdepifdaysadv = VikRequest::getInt('depifdaysadv', '', 'request');
8511 + $pnodepnonrefund = VikRequest::getInt('nodepnonrefund', '', 'request');
8512 + $pdepcustchoice = VikRequest::getString('depcustchoice', '', 'request');
8513 + $pdepcustchoice = $pdepcustchoice == "yes" ? 1 : 0;
8514 +
8515 + // translatable text
8516 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($ppaymentname) . " WHERE `param`='paymentname';";
8517 + $dbo->setQuery($q);
8518 + $dbo->execute();
8519 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=" . $dbo->q($pdisclaimer) . " WHERE `param`='disclaimer';";
8520 + $dbo->setQuery($q);
8521 + $dbo->execute();
8522 +
8523 + $config->set('ivainclusa', empty($pivainclusa) || $pivainclusa != "yes" ? 0 : 1);
8524 + $config->set('taxsummary', $ptaxsummary);
8525 + $config->set('paytotal', empty($ppaytotal) || $ppaytotal != "yes" ? 0 : 1);
8526 +
8527 + $config->set('ccpaypal', $pccpaypal);
8528 + $config->set('payaccpercent', $ppayaccpercent);
8529 + $config->set('typedeposit', $ptypedeposit);
8530 + $config->set('depoverrides', $pdepoverrides);
8531 + $config->set('multipay', $pmultipay);
8532 + $config->set('depifdaysadv', $pdepifdaysadv);
8533 + $config->set('nodepnonrefund', $pnodepnonrefund);
8534 + $config->set('depcustchoice', $pdepcustchoice);
8535 + $config->set('depbalancedays', $app->input->getInt('depbalancedays', null));
8536 +
8537 + $psendemailwhen = VikRequest::getInt('sendemailwhen', '', 'request');
8538 + $psendemailwhen = $psendemailwhen > 1 ? 2 : 1;
8539 + $pattachical = VikRequest::getInt('attachical', 0, 'request');
8540 + $pattachical = $pattachical >= 0 && $pattachical <= 3 ? $pattachical : 1;
8541 + $config->set('emailsendwhen', $psendemailwhen);
8542 + $config->set('attachical', $pattachical);
8543 +
8544 + // SMS APIs
8545 + $psmsapi = VikRequest::getString('smsapi', '', 'request');
8546 + $psmsautosend = VikRequest::getString('smsautosend', '', 'request');
8547 + $psmsautosend = intval($psmsautosend) > 0 ? 1 : 0;
8548 + $psmssendto = VikRequest::getVar('smssendto', array());
8549 + $sms_sendto = array();
8550 + foreach ($psmssendto as $sto) {
8551 + if (in_array($sto, array('admin', 'customer'))) {
8552 + $sms_sendto[] = $sto;
8553 + }
8554 + }
8555 + $psmssendwhen = VikRequest::getInt('smssendwhen', '', 'request');
8556 + $psmssendwhen = $psmssendwhen > 1 ? 2 : 1;
8557 + $psmsadminphone = VikRequest::getString('smsadminphone', '', 'request');
8558 + $psmsadmintpl = VikRequest::getString('smsadmintpl', '', 'request', VIKREQUEST_ALLOWRAW);
8559 + $psmscustomertpl = VikRequest::getString('smscustomertpl', '', 'request', VIKREQUEST_ALLOWRAW);
8560 + $psmsadmintplpend = VikRequest::getString('smsadmintplpend', '', 'request', VIKREQUEST_ALLOWRAW);
8561 + $psmscustomertplpend = VikRequest::getString('smscustomertplpend', '', 'request', VIKREQUEST_ALLOWRAW);
8562 + $psmsadmintplcanc = VikRequest::getString('smsadmintplcanc', '', 'request', VIKREQUEST_ALLOWRAW);
8563 + $psmscustomertplcanc = VikRequest::getString('smscustomertplcanc', '', 'request', VIKREQUEST_ALLOWRAW);
8564 + $viksmsparams = VikRequest::getVar('viksmsparams', array());
8565 + $smsparamarr = array();
8566 + if (count($viksmsparams) > 0) {
8567 + foreach ($viksmsparams as $setting => $cont) {
8568 + if (strlen($setting) > 0) {
8569 + $smsparamarr[$setting] = $cont;
8570 + }
8571 + }
8572 + }
8573 + $config->set('smsapi', $psmsapi);
8574 + $config->set('smsautosend', $psmsautosend);
8575 + $config->set('smssendto', $sms_sendto);
8576 + $config->set('smssendwhen', $psmssendwhen);
8577 + $config->set('smsadminphone', $psmsadminphone);
8578 + $config->set('smsparams', $smsparamarr);
8579 +
8580 + // translatable texts
8581 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintpl)." WHERE `param`='smsadmintpl';";
8582 + $dbo->setQuery($q);
8583 + $dbo->execute();
8584 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertpl)." WHERE `param`='smscustomertpl';";
8585 + $dbo->setQuery($q);
8586 + $dbo->execute();
8587 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintplpend)." WHERE `param`='smsadmintplpend';";
8588 + $dbo->setQuery($q);
8589 + $dbo->execute();
8590 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertplpend)." WHERE `param`='smscustomertplpend';";
8591 + $dbo->setQuery($q);
8592 + $dbo->execute();
8593 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmsadmintplcanc)." WHERE `param`='smsadmintplcanc';";
8594 + $dbo->setQuery($q);
8595 + $dbo->execute();
8596 + $q = "UPDATE `#__vikbooking_texts` SET `setting`=".$dbo->quote($psmscustomertplcanc)." WHERE `param`='smscustomertplcanc';";
8597 + $dbo->setQuery($q);
8598 + $dbo->execute();
8599 +
8600 + /**
8601 + * Backup settings
8602 + *
8603 + * @since 1.15.0 (J) - 1.5.0 (WP)
8604 + */
8605 + $backup_type = $app->input->getString('backuptype', 'full');
8606 + $backup_folder = $app->input->getString('backupfolder', '');
8607 +
8608 + $tmp = $app->get('tmp_path');
8609 +
8610 + if (!$backup_folder)
8611 + {
8612 + // path not specified, use temporary folder
8613 + $backup_folder = $tmp;
8614 + }
8615 +
8616 + $current = $config->get('backupfolder');
8617 +
8618 + if (!$current)
8619 + {
8620 + // path was missing, use temporary folder
8621 + $current = $tmp;
8622 + }
8623 +
8624 + // check whether the backup folder has been moved
8625 + if ($current && $backup_folder && rtrim($current, DIRECTORY_SEPARATOR) !== rtrim($backup_folder, DIRECTORY_SEPARATOR))
8626 + {
8627 + $backupModel = new VBOModelBackup();
8628 +
8629 + // backup folder moved, try to copy all the existing overrides
8630 + if (!$backupModel->moveArchives($backup_folder))
8631 + {
8632 + // iterate all errors and display them
8633 + foreach ($backupModel->getErrors() as $error)
8634 + {
8635 + $app->enqueueMessage($error, 'warning');
8636 + }
8637 + }
8638 + }
8639 +
8640 + // save configuration
8641 + $config->set('backuptype', $backup_type);
8642 + $config->set('backupfolder', $backup_folder);
8643 +
8644 + /**
8645 + * Check-in data collection type.
8646 + *
8647 + * @since 1.15.0 (J) - 1.5.0 (WP)
8648 + */
8649 + $config->set('checkindata', VikRequest::getString('checkindata', 'basic', 'request'));
8650 +
8651 + /**
8652 + * Front-end appearance.
8653 + *
8654 + * @since 1.15.0 (J) - 1.5.0 (WP) (patch)
8655 + */
8656 + $config->set('appearance_front', VikRequest::getInt('appearance_front', 0, 'request'));
8657 +
8658 + /**
8659 + * Split stays.
8660 + *
8661 + * @since 1.16.0 (J) - 1.6.0 (WP)
8662 + */
8663 + $glob_split_stay = VikRequest::getInt('split_stay', 0, 'request');
8664 + $split_stay_ratio = VikRequest::getFloat('split_stay_ratio', 0, 'request');
8665 + $split_stay_ratio = $split_stay_ratio > 100 ? 100 : $split_stay_ratio;
8666 + $config->set('split_stay_ratio', ($glob_split_stay && $split_stay_ratio > 0 ? $split_stay_ratio : 0));
8667 +
8668 + /**
8669 + * Re-build Web App manifest file to let the event trigger.
8670 + *
8671 + * @since 1.16.5 (J) - 1.6.5 (WP)
8672 + */
8673 + try {
8674 + VBOWebappManifest::build();
8675 + } catch (Exception $e) {
8676 + // do nothing
8677 + }
8678 +
8679 + // redirect
8680 + $app->enqueueMessage(JText::translate('VBSETTINGSAVED'));
8681 + $app->redirect('index.php?option=com_vikbooking&task=config');
8682 + $app->close();
8683 + }
8684 +
8685 + /**
8686 + * Task to unify the check-in and check-out times for all reservations.
8687 + */
8688 + public function unifycheckinout()
8689 + {
8690 + $dbo = JFactory::getDbo();
8691 + $app = JFactory::getApplication();
8692 + $user = JFactory::getUser();
8693 +
8694 + $fh = VikRequest::getInt('fh', 12, 'request');
8695 + $fm = VikRequest::getInt('fm', 0, 'request');
8696 + $th = VikRequest::getInt('th', 10, 'request');
8697 + $tm = VikRequest::getInt('tm', 0, 'request');
8698 +
8699 + $now = time();
8700 + $totmod = 0;
8701 + $totbookmod = 0;
8702 +
8703 + // query all busy records
8704 + $q = $dbo->getQuery(true)
8705 + ->select('*')
8706 + ->from($dbo->qn('#__vikbooking_busy'));
8707 +
8708 + $dbo->setQuery($q);
8709 + $records = $dbo->loadAssocList();
8710 +
8711 + foreach ($records as $v) {
8712 + $info_start = getdate($v['checkin']);
8713 + $info_end = getdate($v['checkout']);
8714 + $new_start = mktime($fh, $fm, 0, $info_start['mon'], $info_start['mday'], $info_start['year']);
8715 + $new_end = mktime($th, $tm, 0, $info_end['mon'], $info_end['mday'], $info_end['year']);
8716 +
8717 + $q = $dbo->getQuery(true)
8718 + ->update($dbo->qn('#__vikbooking_busy'))
8719 + ->set($dbo->qn('checkin') . ' = ' . $new_start)
8720 + ->set($dbo->qn('checkout') . ' = ' . $new_end)
8721 + ->set($dbo->qn('realback') . ' = ' . $new_end)
8722 + ->where($dbo->qn('id') . ' = ' . (int)$v['id']);
8723 +
8724 + $dbo->setQuery($q, 0, 1);
8725 + $dbo->execute();
8726 +
8727 + $totmod++;
8728 + }
8729 +
8730 + // query all bookings
8731 + $q = $dbo->getQuery(true)
8732 + ->select($dbo->qn([
8733 + 'id',
8734 + 'days',
8735 + 'checkin',
8736 + 'checkout',
8737 + 'total',
8738 + ]))
8739 + ->from($dbo->qn('#__vikbooking_orders'))
8740 + ->order($dbo->qn('checkin') . ' DESC');
8741 +
8742 + $dbo->setQuery($q);
8743 + $records = $dbo->loadAssocList();
8744 +
8745 + foreach ($records as $v) {
8746 + $info_start = getdate($v['checkin']);
8747 + $info_end = getdate($v['checkout']);
8748 + $new_start = mktime($fh, $fm, 0, $info_start['mon'], $info_start['mday'], $info_start['year']);
8749 + $new_end = mktime($th, $tm, 0, $info_end['mon'], $info_end['mday'], $info_end['year']);
8750 +
8751 + $q = $dbo->getQuery(true)
8752 + ->update($dbo->qn('#__vikbooking_orders'))
8753 + ->set($dbo->qn('checkin') . ' = ' . $new_start)
8754 + ->set($dbo->qn('checkout') . ' = ' . $new_end)
8755 + ->where($dbo->qn('id') . ' = ' . (int)$v['id']);
8756 +
8757 + $dbo->setQuery($q, 0, 1);
8758 + $dbo->execute();
8759 +
8760 + /**
8761 + * In case the operation changed the check-in/check-out time for this booking,
8762 + * store a new history record for a booking modification.
8763 + *
8764 + * @since 1.16.6 (J) - 1.6.6 (WP)
8765 + */
8766 + if ($v['checkout'] > $now && ($info_start['hours'] != $fh || $info_end['hours'] != $th)) {
8767 + // Booking History
8768 + VikBooking::getBookingHistoryInstance($v['id'])->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($v));
8769 + }
8770 +
8771 + $totbookmod++;
8772 + }
8773 +
8774 + $app->enqueueMessage('OK: ' . $totbookmod);
8775 + $app->redirect("index.php?option=com_vikbooking&task=config");
8776 + $app->close();
8777 + }
8778 +
8779 + public function savetmplfile()
8780 + {
8781 + $app = JFactory::getApplication();
8782 +
8783 + if (!JSession::checkToken()) {
8784 + VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
8785 + }
8786 +
8787 + if (!JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
8788 + VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8789 + }
8790 +
8791 + $fpath = VikRequest::getString('path', '', 'request', VIKREQUEST_ALLOWRAW);
8792 + $pcont = VikRequest::getString('cont', '', 'request', VIKREQUEST_ALLOWRAW);
8793 + $pajax = VikRequest::getInt('ajax', 0, 'request');
8794 +
8795 + // default status
8796 + $result = [
8797 + 'status' => 0,
8798 + 'message' => 'Generic error',
8799 + ];
8800 +
8801 + $exists = file_exists($fpath) ? true : false;
8802 + if (!$exists) {
8803 + $fpath = urldecode($fpath);
8804 + }
8805 + $fpath = file_exists($fpath) ? $fpath : '';
8806 + if (!empty($fpath)) {
8807 + $fp = fopen($fpath, 'wb');
8808 + $byt = (int) fwrite($fp, $pcont);
8809 + fclose($fp);
8810 + if ($byt > 0) {
8811 + // success
8812 + $result = [
8813 + 'status' => 1,
8814 + 'message' => JText::translate('VBOUPDTMPLFILEOK'),
8815 + ];
8816 +
8817 + if (VBOPlatformDetection::isWordPress()) {
8818 + /**
8819 + * @wponly call the UpdateManager Class to temporarily store modifications made to template files
8820 + */
8821 + VikBookingUpdateManager::storeTemplateContent($fpath, $pcont);
8822 + }
8823 + } else {
8824 + // error
8825 + $result = [
8826 + 'status' => 0,
8827 + 'message' => JText::translate('VBOUPDTMPLFILENOBYTES'),
8828 + ];
8829 + }
8830 + } else {
8831 + // error
8832 + $result = [
8833 + 'status' => 0,
8834 + 'message' => JText::translate('VBOUPDTMPLFILEERR'),
8835 + ];
8836 + }
8837 +
8838 + if ($pajax) {
8839 + if ($result['status']) {
8840 + VBOHttpDocument::getInstance($app)->json($result);
8841 + } else {
8842 + VBOHttpDocument::getInstance($app)->close(500, $result['message']);
8843 + }
8844 + } else {
8845 + if ($result['status']) {
8846 + $app->enqueueMessage($result['message']);
8847 + } else {
8848 + VikError::raiseWarning('', $result['message']);
8849 + }
8850 + }
8851 +
8852 + $app->redirect("index.php?option=com_vikbooking&task=edittmplfile&path=".$fpath."&tmpl=component");
8853 + $app->close();
8854 + }
8855 +
8856 + public function edittmplfile()
8857 + {
8858 + // this view should be rendered through AJAX
8859 + VikRequest::setVar('view', VikRequest::getCmd('view', 'edittmplfile'));
8860 +
8861 + if (JFactory::getApplication()->input->getBool('ajax') && VBOPlatformDetection::isJoomla()) {
8862 + /**
8863 + * @todo This needs to be changed for Joomla in the future versions.
8864 + * Right now no HTML document tree is being added as an AJAX response, because
8865 + * the View output is captured within a buffer, but the CodeMirror will not work.
8866 + * In case the View was rendered normally and sent to output, the CodeMirror would
8867 + * work fine, but the response appended to the modal body would contain HTML head tags
8868 + * and so accessing the language definitions through JS would fail after the first response.
8869 + * The solution for both Joomla and WordPress is probably to use a completely different endpoint
8870 + * that returns just the file buffer/content, and maybe the file type, so that who makes the requests
8871 + * can set the content and render the proper CodeMirror editor manually at runtime.
8872 + */
8873 +
8874 + // start output buffer
8875 + ob_start();
8876 +
8877 + try {
8878 + // display view
8879 + parent::display();
8880 + } catch (Exception $e) {
8881 + // clear output buffer
8882 + ob_end_clean();
8883 +
8884 + // raise error
8885 + VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
8886 + }
8887 +
8888 + // obtain view HTML from buffer
8889 + $html = ob_get_contents();
8890 +
8891 + // clear output buffer
8892 + ob_end_clean();
8893 +
8894 + // encode HTML in JSON to avoid encoding issues
8895 + VBOHttpDocument::getInstance()->json(json_encode($html));
8896 +
8897 + } else {
8898 + // regular view display
8899 + parent::display();
8900 + }
8901 + }
8902 +
8903 + public function tmplfileprew() {
8904 + //modal box, so we do not set menu or footer
8905 +
8906 + VikRequest::setVar('view', VikRequest::getCmd('view', 'tmplfileprew'));
8907 +
8908 + parent::display();
8909 + }
8910 +
8911 + public function invoices() {
8912 + VikBookingHelper::printHeader("invoices");
8913 +
8914 + VikRequest::setVar('view', VikRequest::getCmd('view', 'invoices'));
8915 +
8916 + parent::display();
8917 +
8918 + if (VikBooking::showFooter()) {
8919 + VikBookingHelper::printFooter();
8920 + }
8921 + }
8922 +
8923 + public function newmaninvoice() {
8924 + VikBookingHelper::printHeader("invoices");
8925 +
8926 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managemaninvoice'));
8927 +
8928 + parent::display();
8929 +
8930 + if (VikBooking::showFooter()) {
8931 + VikBookingHelper::printFooter();
8932 + }
8933 + }
8934 +
8935 + public function editmaninvoice() {
8936 + VikBookingHelper::printHeader("invoices");
8937 +
8938 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managemaninvoice'));
8939 +
8940 + parent::display();
8941 +
8942 + if (VikBooking::showFooter()) {
8943 + VikBookingHelper::printFooter();
8944 + }
8945 + }
8946 +
8947 + public function savemaninvoice() {
8948 + if (!JSession::checkToken()) {
8949 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
8950 + }
8951 + $this->do_storemaninvoice('save');
8952 + $mainframe = JFactory::getApplication();
8953 + $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8954 + $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8955 + if (!empty($pgoto)) {
8956 + $mainframe->redirect(base64_decode($pgoto));
8957 + exit;
8958 + }
8959 + $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
8960 + }
8961 +
8962 + public function updatemaninvoice()
8963 + {
8964 + if (!JSession::checkToken()) {
8965 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
8966 + }
8967 +
8968 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
8969 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8970 + }
8971 +
8972 + $invid = VikRequest::getInt('whereup', 0, 'request');
8973 + $this->do_storemaninvoice('update', $invid);
8974 + $mainframe = JFactory::getApplication();
8975 + $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8976 + $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8977 + if (!empty($pgoto)) {
8978 + $mainframe->redirect(base64_decode($pgoto));
8979 + exit;
8980 + }
8981 + $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
8982 + }
8983 +
8984 + public function updatemaninvoicestay()
8985 + {
8986 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
8987 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
8988 + }
8989 +
8990 + $invid = VikRequest::getInt('whereup', 0, 'request');
8991 + $this->do_storemaninvoice('updatestay', $invid);
8992 + $mainframe = JFactory::getApplication();
8993 + $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', 1, 0));
8994 + $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
8995 + if (!empty($pgoto)) {
8996 + $mainframe->redirect(base64_decode($pgoto));
8997 + exit;
8998 + }
8999 + $mainframe->redirect("index.php?option=com_vikbooking&task=editmaninvoice&cid[]=".$invid);
9000 + }
9001 +
9002 + private function do_storemaninvoice($action, $invid = 0) {
9003 + $dbo = JFactory::getDBO();
9004 + $mainframe = JFactory::getApplication();
9005 + $pinvoice_num = VikRequest::getInt('invoice_num', '', 'request');
9006 + $pinvoice_num = $pinvoice_num <= 0 ? 1 : $pinvoice_num;
9007 + $pinvoice_suff = VikRequest::getString('invoice_suff', '', 'request');
9008 + $pcompany_info = VikRequest::getString('company_info', '', 'request', VIKREQUEST_ALLOWHTML);
9009 + $pcompany_info = strpos($pcompany_info, '<') !== false ? $pcompany_info : nl2br($pcompany_info);
9010 + $pinvoice_notes = VikRequest::getString('invoice_notes', '', 'request', VIKREQUEST_ALLOWHTML);
9011 + $pinvoice_notes = strpos($pinvoice_notes, '<') !== false ? $pinvoice_notes : nl2br($pinvoice_notes);
9012 + $pidcustomer = VikRequest::getInt('idcustomer', '', 'request');
9013 + $error_uri = strpos($action, 'update') !== false && !empty($invid) ? 'index.php?option=com_vikbooking&task=editmaninvoice&cid[]='.$invid : 'index.php?option=com_vikbooking&task=newmaninvoice';
9014 + if (empty($pidcustomer)) {
9015 + VikError::raiseWarning('', JText::translate('VBNOCUSTOMERS'));
9016 + $mainframe->redirect($error_uri);
9017 + exit;
9018 + }
9019 + $services = VikRequest::getVar('service', array());
9020 + $nets = VikRequest::getVar('net', array());
9021 + $aliqs = VikRequest::getVar('aliq', array());
9022 + $taxs = VikRequest::getVar('tax', array());
9023 + $tots = VikRequest::getVar('tot', array());
9024 + $ptotalnet = VikRequest::getFloat('totalnet', 0, 'request');
9025 + $ptotaltax = VikRequest::getFloat('totaltax', 0, 'request');
9026 + $ptotaltot = VikRequest::getFloat('totaltot', 0, 'request');
9027 + if (!count($services) || count($services) != count($nets) || count($services) != count($taxs) || count($services) != count($tots)) {
9028 + VikError::raiseWarning('', 'Missing data.');
9029 + $mainframe->redirect($error_uri);
9030 + exit;
9031 + }
9032 + $rawcont = array(
9033 + 'rows' => array(),
9034 + 'totalnet' => $ptotalnet,
9035 + 'totaltax' => $ptotaltax,
9036 + 'totaltot' => $ptotaltot,
9037 + 'notes' => $pinvoice_notes,
9038 + );
9039 + foreach ($services as $k => $service) {
9040 + if (empty($service)) {
9041 + continue;
9042 + }
9043 + array_push($rawcont['rows'], array(
9044 + 'service' => $service,
9045 + 'net' => (float)$nets[$k],
9046 + 'aliq' => (isset($aliqs[$k]) ? (float)$aliqs[$k] : 0),
9047 + 'tax' => (float)$taxs[$k],
9048 + 'tot' => (float)$tots[$k],
9049 + ));
9050 + }
9051 + // store/update manual invoice
9052 + $nowts = time();
9053 + $retval = 0;
9054 + if (strpos($action, 'save') !== false) {
9055 + $pdffname = $nowts . '_' . rand() . '.pdf';
9056 + $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)).");";
9057 + $dbo->setQuery($q);
9058 + $dbo->execute();
9059 + $retval = $dbo->insertid();
9060 + } else {
9061 + // fetch old record
9062 + $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id`=".(int)$invid.";";
9063 + $dbo->setQuery($q);
9064 + $dbo->execute();
9065 + if (!$dbo->getNumRows()) {
9066 + VikError::raiseWarning('', JText::translate('VBNOINVOICESFOUND'));
9067 + $mainframe->redirect($error_uri);
9068 + exit;
9069 + }
9070 + $previnvoice = $dbo->loadAssoc();
9071 + //
9072 + $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'].";";
9073 + $dbo->setQuery($q);
9074 + $dbo->execute();
9075 + $retval = $previnvoice['id'];
9076 + }
9077 + // update config values for the invoice
9078 + $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pcompany_info)." WHERE `param`='invcompanyinfo';";
9079 + $dbo->setQuery($q);
9080 + $dbo->execute();
9081 + // generate the custom invoice
9082 + $result = VikBooking::generateCustomInvoice($retval);
9083 + //
9084 + $nextinv = VikBooking::getNextInvoiceNumber();
9085 + $updatenum = ($pinvoice_num >= $nextinv);
9086 + if ($updatenum) {
9087 + /**
9088 + * IMPORTANT: update the next invoice number after calling the e-Invocing drivers
9089 + * to avoid conflicts with the drivers for the e-invoices generation.
9090 + */
9091 + $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(($pinvoice_num - 1))." WHERE `param`='invoiceinum';";
9092 + $dbo->setQuery($q);
9093 + $dbo->execute();
9094 + }
9095 +
9096 + return $retval;
9097 + }
9098 +
9099 + public function downloadinvoices() {
9100 + $ids = VikRequest::getVar('cid', array(0));
9101 + if (@count($ids) > 0) {
9102 + $dbo = JFactory::getDBO();
9103 + $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id` IN (".implode(', ', $ids).");";
9104 + $dbo->setQuery($q);
9105 + $dbo->execute();
9106 + if ($dbo->getNumRows() > 0) {
9107 + $invoices = $dbo->loadAssocList();
9108 + if (!(count($invoices) > 1)) {
9109 + //Single Invoice Download
9110 + if (file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoices[0]['file_name'])) {
9111 + header("Content-type:application/pdf");
9112 + header("Content-Disposition:attachment;filename=".$invoices[0]['file_name']);
9113 + readfile(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoices[0]['file_name']);
9114 + exit;
9115 + }
9116 + } else {
9117 + //Multiple Invoices Download
9118 + $to_zip = array();
9119 + foreach ($invoices as $k => $invoice) {
9120 + $to_zip[$k]['name'] = $invoice['file_name'];
9121 + $to_zip[$k]['path'] = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$invoice['file_name'];
9122 + }
9123 + if (class_exists('ZipArchive')) {
9124 + $zip_path = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.date('Y-m-d').'-invoices.zip';
9125 + $zip = new ZipArchive;
9126 + $zip->open($zip_path, ZipArchive::CREATE);
9127 + foreach ($to_zip as $k => $zipv) {
9128 + $zip->addFile($zipv['path'], $zipv['name']);
9129 + }
9130 + $zip->close();
9131 + header("Content-type:application/zip");
9132 + header("Content-Disposition:attachment;filename=".date('Y-m-d').'-invoices.zip');
9133 + header("Content-Length:".filesize($zip_path));
9134 + readfile($zip_path);
9135 + unlink($zip_path);
9136 + exit;
9137 + } else {
9138 + //Class ZipArchive does not exist
9139 + VikError::raiseWarning('', 'Class ZipArchive does not exist on your server. Download the files one by one.');
9140 + }
9141 + }
9142 + }
9143 + }
9144 + $mainframe = JFactory::getApplication();
9145 + $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9146 + }
9147 +
9148 + public function resendinvoices() {
9149 + $ids = VikRequest::getVar('cid', array(0));
9150 + $mainframe = JFactory::getApplication();
9151 + if (!(count($ids) > 0)) {
9152 + $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9153 + exit;
9154 + }
9155 + $dbo = JFactory::getDBO();
9156 + $invoices = array();
9157 + $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` ".
9158 + "FROM `#__vikbooking_invoices` AS `i` " .
9159 + "LEFT JOIN `#__vikbooking_orders` `o` ON `o`.`id`=`i`.`idorder` " .
9160 + "LEFT JOIN `#__vikbooking_customers` `c` ON `c`.`id`=`i`.`idcustomer` " .
9161 + "LEFT JOIN `#__vikbooking_countries` `nat` ON `nat`.`country_3_code`=`c`.`country` " .
9162 + "WHERE `i`.`id` IN (".implode(', ', $ids).") AND (`i`.`idorder` < 0 OR (`o`.`status`='confirmed' AND `o`.`total` > 0)) ORDER BY `o`.`id` ASC;";
9163 + $dbo->setQuery($q);
9164 + $dbo->execute();
9165 + if ($dbo->getNumRows() > 0) {
9166 + $invoices = $dbo->loadAssocList();
9167 + }
9168 + if (!(count($invoices) > 0)) {
9169 + VikError::raiseWarning('', JText::translate('VBOGENINVERRNOBOOKINGS'));
9170 + $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9171 + exit;
9172 + }
9173 + $tot_generated = 0;
9174 + $tot_sent = 0;
9175 + foreach ($invoices as $bkey => $invoice) {
9176 + $invoice['custmail'] = empty($invoice['custmail']) && !empty($invoice['customer_email']) ? $invoice['customer_email'] : $invoice['custmail'];
9177 + $invoices[$bkey] = $invoice;
9178 + $send_res = VikBooking::sendBookingInvoice($invoice['id'], $invoice);
9179 + if ($send_res !== false) {
9180 + $tot_sent++;
9181 + }
9182 + }
9183 + $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', $tot_generated, $tot_sent));
9184 + $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9185 + }
9186 +
9187 + public function removeinvoices()
9188 + {
9189 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
9190 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9191 + }
9192 +
9193 + $ids = VikRequest::getVar('cid', array());
9194 + $tot_removed = 0;
9195 + $dbo = JFactory::getDbo();
9196 +
9197 + foreach ($ids as $d) {
9198 + $q = "SELECT * FROM `#__vikbooking_invoices` WHERE `id`=".(int)$d.";";
9199 + $dbo->setQuery($q);
9200 + $cur_invoice = $dbo->loadAssoc();
9201 + if ($cur_invoice) {
9202 + $invoice_fpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'helpers'.DIRECTORY_SEPARATOR.'invoices'.DIRECTORY_SEPARATOR.'generated'.DIRECTORY_SEPARATOR.$cur_invoice['file_name'];
9203 + if (is_file($invoice_fpath)) {
9204 + unlink($invoice_fpath);
9205 + }
9206 + if (VBOPlatformDetection::isWordPress()) {
9207 + /**
9208 + * @wponly - trigger files mirroring for deletion
9209 + */
9210 + VikBookingLoader::import('update.manager');
9211 + VikBookingUpdateManager::triggerDeletionBackup($invoice_fpath);
9212 + }
9213 + $q = "DELETE FROM `#__vikbooking_invoices` WHERE `id`=".(int)$d.";";
9214 + $dbo->setQuery($q);
9215 + $dbo->execute();
9216 + $tot_removed++;
9217 + }
9218 + }
9219 +
9220 + $mainframe = JFactory::getApplication();
9221 + $mainframe->enqueueMessage(JText::sprintf('VBOTOTINVOICESRMVD', $tot_removed));
9222 + $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
9223 + }
9224 +
9225 + public function geninvoices()
9226 + {
9227 + $dbo = JFactory::getDbo();
9228 + $app = JFactory::getApplication();
9229 +
9230 + $ids = VikRequest::getVar('cid', array());
9231 +
9232 + if (!$ids) {
9233 + $app->redirect("index.php?option=com_vikbooking&task=orders");
9234 + exit;
9235 + }
9236 +
9237 + $pinvoice_num = VikRequest::getInt('invoice_num', '', 'request');
9238 + $pinvoice_num = $pinvoice_num <= 0 ? 1 : $pinvoice_num;
9239 + $pinvoice_suff = VikRequest::getString('invoice_suff', '', 'request');
9240 + $pinvoice_date = VikRequest::getString('invoice_date', '', 'request');
9241 + $pcompany_info = VikRequest::getString('company_info', '', 'request', VIKREQUEST_ALLOWHTML);
9242 + $pcompany_info = strpos($pcompany_info, '<') !== false ? $pcompany_info : nl2br($pcompany_info);
9243 + $pinvoice_send = VikRequest::getInt('invoice_send', '', 'request');
9244 + $pinvoice_send = $pinvoice_send > 0 ? true : false;
9245 + $increment_inv = true;
9246 + $pconfirmgen = VikRequest::getInt('confirmgen', '', 'request');
9247 +
9248 + // if editing an invoice (re-creating an existing invoice for a booking), do not increment the invoice number
9249 + if (count($ids) === 1) {
9250 + $q = "SELECT `number` FROM `#__vikbooking_invoices` WHERE `idorder`=".(int)$ids[0].";";
9251 + $dbo->setQuery($q);
9252 + $dbo->execute();
9253 + if ($dbo->getNumRows() == 1) {
9254 + $increment_inv = false;
9255 + }
9256 + }
9257 +
9258 + // get bookings
9259 + $dbo->setQuery(
9260 + $dbo->getQuery(true)
9261 + ->select($dbo->qn('o') . '.*')
9262 + ->select($dbo->qn('co.idcustomer'))
9263 + ->select('CONCAT_WS(\' \', ' . $dbo->qn('c.first_name') . ', ' . $dbo->qn('c.last_name') . ') AS ' . $dbo->qn('customer_name'))
9264 + ->select([
9265 + $dbo->qn('c.pin', 'customer_pin'),
9266 + $dbo->qn('nat.country_name'),
9267 + ])
9268 + ->from($dbo->qn('#__vikbooking_orders', 'o'))
9269 + ->leftJoin($dbo->qn('#__vikbooking_customers_orders', 'co') . ' ON ' . $dbo->qn('co.idorder') . ' = ' . $dbo->qn('o.id'))
9270 + ->leftJoin($dbo->qn('#__vikbooking_customers', 'c') . ' ON ' . $dbo->qn('c.id') . ' = ' . $dbo->qn('co.idcustomer'))
9271 + ->leftJoin($dbo->qn('#__vikbooking_countries', 'nat') . ' ON ' . $dbo->qn('nat.country_3_code') . ' = ' . $dbo->qn('o.country'))
9272 + ->where($dbo->qn('o.id') . ' IN (' . implode(', ', array_map('intval', $ids)) . ')')
9273 + ->where($dbo->qn('o.status') . ' = ' . $dbo->q('confirmed'))
9274 + ->where($dbo->qn('o.total') . ' > 0')
9275 + ->order($dbo->qn('o.id') . ' ASC')
9276 + );
9277 +
9278 + $bookings = $dbo->loadAssocList();
9279 +
9280 + if (!$bookings) {
9281 + VikError::raiseWarning('', JText::translate('VBOGENINVERRNOBOOKINGS'));
9282 + $app->redirect("index.php?option=com_vikbooking&task=orders");
9283 + exit;
9284 + }
9285 +
9286 + $tot_generated = 0;
9287 + $tot_sent = 0;
9288 + foreach ($bookings as $bkey => $booking) {
9289 + $gen_res = VikBooking::generateBookingInvoice($booking, $pinvoice_num, $pinvoice_suff, $pinvoice_date, $pcompany_info);
9290 + if ($gen_res !== false && $gen_res > 0) {
9291 + $tot_generated++;
9292 + $pinvoice_num++;
9293 + if ($pinvoice_send) {
9294 + $send_res = VikBooking::sendBookingInvoice($gen_res, $booking);
9295 + if ($send_res !== false) {
9296 + $tot_sent++;
9297 + }
9298 + }
9299 + } else {
9300 + VikError::raiseWarning('', JText::sprintf('VBOGENINVERRBOOKING', $booking['id']));
9301 + }
9302 + }
9303 +
9304 + if ($tot_generated > 0 && $increment_inv === true) {
9305 + /**
9306 + * IMPORTANT: update the next invoice number after calling generateBookingInvoice()
9307 + * to avoid conflicts with the drivers for the e-invoices generation.
9308 + */
9309 + $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(($pinvoice_num - 1))." WHERE `param`='invoiceinum';";
9310 + $dbo->setQuery($q);
9311 + $dbo->execute();
9312 + }
9313 +
9314 + $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pinvoice_suff)." WHERE `param`='invoicesuffix';";
9315 + $dbo->setQuery($q);
9316 + $dbo->execute();
9317 +
9318 + $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($pcompany_info)." WHERE `param`='invcompanyinfo';";
9319 + $dbo->setQuery($q);
9320 + $dbo->execute();
9321 +
9322 + $app->enqueueMessage(JText::sprintf('VBOTOTINVOICESGEND', $tot_generated, $tot_sent));
9323 +
9324 + if ($pconfirmgen > 0) {
9325 + $app->redirect("index.php?option=com_vikbooking&task=invoices&show=".$pconfirmgen);
9326 + } elseif (count($bookings) === 1) {
9327 + // go to the back-end booking details page
9328 + $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $bookings[0]['id']);
9329 + } else {
9330 + $app->redirect("index.php?option=com_vikbooking&task=orders");
9331 + }
9332 + }
9333 +
9334 + public function optionals() {
9335 + VikBookingHelper::printHeader("6");
9336 +
9337 + VikRequest::setVar('view', VikRequest::getCmd('view', 'optionals'));
9338 +
9339 + parent::display();
9340 +
9341 + if (VikBooking::showFooter()) {
9342 + VikBookingHelper::printFooter();
9343 + }
9344 + }
9345 +
9346 + public function newoptionals() {
9347 + VikBookingHelper::printHeader("6");
9348 +
9349 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoptional'));
9350 +
9351 + parent::display();
9352 +
9353 + if (VikBooking::showFooter()) {
9354 + VikBookingHelper::printFooter();
9355 + }
9356 + }
9357 +
9358 + public function editoptional() {
9359 + VikBookingHelper::printHeader("6");
9360 +
9361 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoptional'));
9362 +
9363 + parent::display();
9364 +
9365 + if (VikBooking::showFooter()) {
9366 + VikBookingHelper::printFooter();
9367 + }
9368 + }
9369 +
9370 + public function updateoptional()
9371 + {
9372 + if (!JSession::checkToken()) {
9373 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9374 + }
9375 +
9376 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
9377 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9378 + }
9379 +
9380 + $this->do_updateoptional();
9381 + }
9382 +
9383 + public function updateoptionalstay()
9384 + {
9385 + if (!JSession::checkToken()) {
9386 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9387 + }
9388 +
9389 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
9390 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9391 + }
9392 +
9393 + $this->do_updateoptional(true);
9394 + }
9395 +
9396 + private function do_updateoptional($stay = false) {
9397 + $dbo = JFactory::getDbo();
9398 + $app = JFactory::getApplication();
9399 + $poptname = VikRequest::getString('optname', '', 'request');
9400 + $poptdescr = VikRequest::getString('optdescr', '', 'request', VIKREQUEST_ALLOWHTML);
9401 + $poptcost = VikRequest::getFloat('optcost', '', 'request');
9402 + $poptperday = VikRequest::getString('optperday', '', 'request');
9403 + $poptperperson = VikRequest::getString('optperperson', '', 'request');
9404 + $pmaxprice = VikRequest::getFloat('maxprice', '', 'request');
9405 + $popthmany = VikRequest::getString('opthmany', '', 'request');
9406 + $poptaliq = VikRequest::getInt('optaliq', '', 'request');
9407 + $pwhereup = VikRequest::getString('whereup', '', 'request');
9408 + $pautoresize = VikRequest::getString('autoresize', '', 'request');
9409 + $presizeto = VikRequest::getString('resizeto', '', 'request');
9410 + $pifchildren = VikRequest::getString('ifchildren', '', 'request');
9411 + $pifchildren = $pifchildren == "1" ? 1 : 0;
9412 + $pmaxquant = VikRequest::getString('maxquant', '', 'request');
9413 + $pmaxquant = empty($pmaxquant) ? 0 : intval($pmaxquant);
9414 + $pforcesel = VikRequest::getString('forcesel', '', 'request');
9415 + $pforceval = VikRequest::getString('forceval', '', 'request');
9416 + $pforcevalperday = VikRequest::getString('forcevalperday', '', 'request');
9417 + $pforcevalperchild = VikRequest::getString('forcevalperchild', '', 'request');
9418 + $pforcesummary = VikRequest::getString('forcesummary', '', 'request');
9419 + $pforcesel = $pforcesel == "1" ? 1 : 0;
9420 + $pis_citytax = VikRequest::getString('is_citytax', '', 'request');
9421 + $pis_fee = VikRequest::getString('is_fee', '', 'request');
9422 + $pis_citytax = $pis_citytax == "1" && $pis_fee != "1" ? 1 : 0;
9423 + $pis_fee = $pis_fee == "1" && $pis_citytax == 0 ? 1 : 0;
9424 + $pagefrom = VikRequest::getVar('agefrom', array());
9425 + $pageto = VikRequest::getVar('ageto', array());
9426 + $pagecost = VikRequest::getVar('agecost', array());
9427 + $pagectype = VikRequest::getVar('agectype', array());
9428 + $palwaysav = VikRequest::getInt('alwaysav', 0, 'request');
9429 + $pavfrom = VikRequest::getString('avfrom', '', 'request');
9430 + $pavto = VikRequest::getString('avto', '', 'request');
9431 + $ppcentroom = VikRequest::getInt('pcentroom', 0, 'request');
9432 + $pidrooms = VikRequest::getVar('idrooms', array());
9433 + $optavstr = empty($palwaysav) && !empty($pavfrom) && !empty($pavto) ? VikBooking::getDateTimestamp($pavfrom, 0, 0, 0).';'.VikBooking::getDateTimestamp($pavto, 23, 59, 59) : '';
9434 + if ($pforcesel == 1) {
9435 + $strforceval = intval($pforceval)."-".($pforcevalperday == "1" ? "1" : "0")."-".($pforcevalperchild == "1" ? "1" : "0")."-".($pforcesummary == "1" ? "1" : "0");
9436 + } else {
9437 + $strforceval = "";
9438 + }
9439 + $minguestsnum = VikRequest::getInt('minguestsnum', 0, 'request');
9440 + $mingueststype = VikRequest::getString('mingueststype', 'guests', 'request');
9441 + $minguestsnum = $minguestsnum < 0 ? 0 : $minguestsnum;
9442 + $mingueststype = !empty($mingueststype) && !in_array($mingueststype, array('adults', 'guests')) ? 'guests' : $mingueststype;
9443 + $maxguestsnum = VikRequest::getInt('maxguestsnum', 0, 'request');
9444 + $maxgueststype = VikRequest::getString('maxgueststype', 'guests', 'request');
9445 + $maxguestsnum = $maxguestsnum < 0 ? 0 : $maxguestsnum;
9446 + $maxgueststype = !empty($maxgueststype) && !in_array($maxgueststype, array('adults', 'guests')) ? 'guests' : $maxgueststype;
9447 + $minguests = VikRequest::getInt('minguests', 0, 'request');
9448 + $minguests_conflict = false;
9449 + if ($minguests > 0 && $minguestsnum > 0 && $maxguestsnum > 0) {
9450 + if ($minguestsnum >= $maxguestsnum) {
9451 + $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL1');
9452 + } elseif (($maxguestsnum - $minguestsnum) < 2) {
9453 + $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL2');
9454 + }
9455 + }
9456 + if (!$minguests || $minguests_conflict !== false) {
9457 + $minguestsnum = 0;
9458 + $maxguestsnum = 0;
9459 + if ($minguests_conflict !== false) {
9460 + // raise warning, but do not stop the process
9461 + VikError::raiseWarning('', $minguests_conflict);
9462 + }
9463 + }
9464 + $damagedep = VikRequest::getInt('damagedep', 0, 'request');
9465 + $pet_fee = VikRequest::getInt('pet_fee', 0, 'request');
9466 + $custom_checkinout = VikRequest::getInt('custom_checkinout', 0, 'request');
9467 + $set_checkin = VikRequest::getInt('set_checkin', 0, 'request');
9468 + $set_checkout = VikRequest::getInt('set_checkout', 0, 'request');
9469 + if (!$custom_checkinout) {
9470 + $set_checkin = 0;
9471 + $set_checkout = 0;
9472 + }
9473 + if ((!$set_checkin && !$set_checkout) || $set_checkin == $set_checkout) {
9474 + // check-in and check-out times should not be equal or both empty
9475 + $custom_checkinout = 0;
9476 + }
9477 + $damagedep_settings = $damagedep ? ((array) $app->input->get('damagedep_settings', [], 'array')) : [];
9478 + $oparams = [
9479 + 'minguestsnum' => $minguestsnum,
9480 + 'mingueststype' => $mingueststype,
9481 + 'maxguestsnum' => $maxguestsnum,
9482 + 'maxgueststype' => $maxgueststype,
9483 + 'damagedep' => $damagedep,
9484 + 'damagedep_settings' => $damagedep_settings,
9485 + 'pet_fee' => $pet_fee,
9486 + 'custom_checkinout' => $custom_checkinout,
9487 + 'set_checkin' => $set_checkin,
9488 + 'set_checkout' => $set_checkout,
9489 + ];
9490 + /**
9491 + * We fetch the previous params to merge them with the new ones
9492 + * in case some properties have been set somewhere else.
9493 + * For example, the damage deposit transmission to Booking.com.
9494 + */
9495 + $cur_oparams = array();
9496 + $q = "SELECT `oparams` FROM `#__vikbooking_optionals` WHERE `id`=" . (int)$pwhereup . ";";
9497 + $dbo->setQuery($q);
9498 + $dbo->execute();
9499 + if ($dbo->getNumRows()) {
9500 + $cur_oparams = $dbo->loadResult();
9501 + $cur_oparams = !empty($cur_oparams) ? json_decode($cur_oparams, true) : array();
9502 + $cur_oparams = !is_array($cur_oparams) ? array() : $cur_oparams;
9503 + // merge previous params with the new ones to get the new values
9504 + $oparams = array_merge($cur_oparams, $oparams);
9505 + }
9506 +
9507 + /**
9508 + * Ensure options of type city tax never get a tax rate.
9509 + *
9510 + * @since 1.18.3 (J) - 1.8.3 (WP)
9511 + */
9512 + if ($pis_citytax) {
9513 + $poptaliq = 0;
9514 + }
9515 +
9516 + if (!empty($poptname)) {
9517 + if (intval($_FILES['optimg']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['optimg']['name'])!="") {
9518 + jimport('joomla.filesystem.file');
9519 + $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9520 + if (@is_uploaded_file($_FILES['optimg']['tmp_name'])) {
9521 + $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['optimg']['name'])));
9522 + if (file_exists($updpath.$safename)) {
9523 + $j=1;
9524 + while (file_exists($updpath.$j.$safename)) {
9525 + $j++;
9526 + }
9527 + $pwhere=$updpath.$j.$safename;
9528 + } else {
9529 + $j="";
9530 + $pwhere=$updpath.$safename;
9531 + }
9532 + if (!getimagesize($_FILES['optimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
9533 + @unlink($pwhere);
9534 + $picon="";
9535 + } else {
9536 + VikBooking::uploadFile($_FILES['optimg']['tmp_name'], $pwhere);
9537 + @chmod($pwhere, 0644);
9538 + $picon=$j.$safename;
9539 + if ($pautoresize=="1" && !empty($presizeto)) {
9540 + $eforj = new vikResizer();
9541 + $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
9542 + if ($origmod) {
9543 + @unlink($pwhere);
9544 + $picon='r_'.$j.$safename;
9545 + }
9546 + }
9547 + }
9548 + } else {
9549 + $picon="";
9550 + }
9551 + } else {
9552 + $picon="";
9553 + }
9554 + ($poptperday=="each" ? $poptperday="1" : $poptperday="0");
9555 + $poptperperson=($poptperperson=="each" ? "1" : "0");
9556 + ($popthmany=="yes" ? $popthmany="1" : $popthmany="0");
9557 + $ageintervalstr = '';
9558 + if ($pifchildren == 1 && count($pagefrom) > 0 && count($pagecost) > 0 && count($pagefrom) == count($pagecost)) {
9559 + foreach ($pagefrom as $kage => $vage) {
9560 + $afrom = intval($vage);
9561 + $ato = intval($pageto[$kage]);
9562 + $acost = floatval($pagecost[$kage]);
9563 + if (strlen($vage) > 0 && strlen($pagecost[$kage]) > 0) {
9564 + if ($ato < $afrom) $ato = $afrom;
9565 + $ageintervalstr .= $afrom.'_'.$ato.'_'.$acost.(array_key_exists($kage, $pagectype) && strpos($pagectype[$kage], '%') !== false ? '_%'.(strpos($pagectype[$kage], '%b') !== false ? 'b' : '') : '').';;';
9566 + }
9567 + }
9568 + $ageintervalstr = rtrim($ageintervalstr, ';;');
9569 + if (!empty($ageintervalstr)) {
9570 + $pforcesel = 1;
9571 + }
9572 + }
9573 + $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).";";
9574 + $dbo->setQuery($q);
9575 + $dbo->execute();
9576 + $app->enqueueMessage(JText::translate('VBOSUCCUPDOPTION'));
9577 +
9578 + // assign/unset option-rooms relations
9579 + $rooms_with_opt = array();
9580 + if (count($pidrooms)) {
9581 + // assign this new option to the requested rooms
9582 + foreach ($pidrooms as $idroom) {
9583 + if (empty($idroom)) {
9584 + continue;
9585 + }
9586 + $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
9587 + $dbo->setQuery($q);
9588 + $dbo->execute();
9589 + if (!$dbo->getNumRows()) {
9590 + continue;
9591 + }
9592 + $room_data = $dbo->loadAssoc();
9593 + array_push($rooms_with_opt, $room_data['id']);
9594 + $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9595 + if (in_array((string)$pwhereup, $current_opts)) {
9596 + continue;
9597 + }
9598 + if (count($current_opts) === 1 && (string)$current_opts[0] == '0') {
9599 + // make sure we do not concatenate a real ID to 0
9600 + $current_opts = array();
9601 + }
9602 + array_push($current_opts, $pwhereup);
9603 + $new_opts = implode(';', $current_opts) . ';';
9604 + $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9605 + $dbo->setQuery($q);
9606 + $dbo->execute();
9607 + }
9608 + }
9609 + if (!count($rooms_with_opt)) {
9610 + // get all rooms to unset this option (if previously set)
9611 + array_push($rooms_with_opt, '0');
9612 + }
9613 + // unset the option from the other rooms that may have it
9614 + $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_opt) . ");";
9615 + $dbo->setQuery($q);
9616 + $dbo->execute();
9617 + if ($dbo->getNumRows()) {
9618 + $unset_rooms_opt = $dbo->loadAssocList();
9619 + foreach ($unset_rooms_opt as $room_data) {
9620 + $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9621 + if (!in_array((string)$pwhereup, $current_opts)) {
9622 + // this room is not using this option
9623 + continue;
9624 + }
9625 + $optkey = array_search((string)$pwhereup, $current_opts);
9626 + if ($optkey === false) {
9627 + // key not found
9628 + continue;
9629 + }
9630 + // unset this option ID from the string
9631 + unset($current_opts[$optkey]);
9632 + if (!count($current_opts)) {
9633 + // a room with no options assigned will be listed as "0;"
9634 + $current_opts = array(0);
9635 + }
9636 + $new_opts = implode(';', $current_opts) . ';';
9637 + $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9638 + $dbo->setQuery($q);
9639 + $dbo->execute();
9640 + }
9641 + }
9642 + //
9643 +
9644 + }
9645 + $app->redirect("index.php?option=com_vikbooking&task=" . ($stay ? 'editoptional&cid[]=' . $pwhereup : 'optionals'));
9646 + }
9647 +
9648 + public function createoptionals()
9649 + {
9650 + if (!JSession::checkToken()) {
9651 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9652 + }
9653 +
9654 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
9655 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9656 + }
9657 +
9658 + $this->do_createoptionals();
9659 + }
9660 +
9661 + public function createoptionalsstay()
9662 + {
9663 + if (!JSession::checkToken()) {
9664 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9665 + }
9666 +
9667 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
9668 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9669 + }
9670 +
9671 + $this->do_createoptionals(true);
9672 + }
9673 +
9674 + private function do_createoptionals($stay = false)
9675 + {
9676 + $app = JFactory::getApplication();
9677 + $dbo = JFactory::getDbo();
9678 +
9679 + $poptname = VikRequest::getString('optname', '', 'request');
9680 + $poptdescr = VikRequest::getString('optdescr', '', 'request', VIKREQUEST_ALLOWHTML);
9681 + $poptcost = VikRequest::getFloat('optcost', '', 'request');
9682 + $poptperday = VikRequest::getString('optperday', '', 'request');
9683 + $poptperperson = VikRequest::getString('optperperson', '', 'request');
9684 + $pmaxprice = VikRequest::getFloat('maxprice', '', 'request');
9685 + $popthmany = VikRequest::getString('opthmany', '', 'request');
9686 + $poptaliq = VikRequest::getInt('optaliq', '', 'request');
9687 + $pautoresize = VikRequest::getString('autoresize', '', 'request');
9688 + $presizeto = VikRequest::getString('resizeto', '', 'request');
9689 + $pifchildren = VikRequest::getString('ifchildren', '', 'request');
9690 + $pifchildren = $pifchildren == "1" ? 1 : 0;
9691 + $pmaxquant = VikRequest::getString('maxquant', '', 'request');
9692 + $pmaxquant = empty($pmaxquant) ? 0 : intval($pmaxquant);
9693 + $pforcesel = VikRequest::getString('forcesel', '', 'request');
9694 + $pforceval = VikRequest::getString('forceval', '', 'request');
9695 + $pforcevalperday = VikRequest::getString('forcevalperday', '', 'request');
9696 + $pforcevalperchild = VikRequest::getString('forcevalperchild', '', 'request');
9697 + $pforcesummary = VikRequest::getString('forcesummary', '', 'request');
9698 + $pforcesel = $pforcesel == "1" ? 1 : 0;
9699 + $pis_citytax = VikRequest::getString('is_citytax', '', 'request');
9700 + $pis_fee = VikRequest::getString('is_fee', '', 'request');
9701 + $pis_citytax = $pis_citytax == "1" && $pis_fee != "1" ? 1 : 0;
9702 + $pis_fee = $pis_fee == "1" && $pis_citytax == 0 ? 1 : 0;
9703 + $pagefrom = VikRequest::getVar('agefrom', array());
9704 + $pageto = VikRequest::getVar('ageto', array());
9705 + $pagecost = VikRequest::getVar('agecost', array());
9706 + $pagectype = VikRequest::getVar('agectype', array());
9707 + $palwaysav = VikRequest::getInt('alwaysav', 0, 'request');
9708 + $pavfrom = VikRequest::getString('avfrom', '', 'request');
9709 + $pavto = VikRequest::getString('avto', '', 'request');
9710 + $ppcentroom = VikRequest::getInt('pcentroom', 0, 'request');
9711 + $pidrooms = VikRequest::getVar('idrooms', array());
9712 + $optavstr = empty($palwaysav) && !empty($pavfrom) && !empty($pavto) ? VikBooking::getDateTimestamp($pavfrom, 0, 0, 0).';'.VikBooking::getDateTimestamp($pavto, 23, 59, 59) : '';
9713 + if ($pforcesel == 1) {
9714 + $strforceval = intval($pforceval)."-".($pforcevalperday == "1" ? "1" : "0")."-".($pforcevalperchild == "1" ? "1" : "0")."-".($pforcesummary == "1" ? "1" : "0");
9715 + } else {
9716 + $strforceval = "";
9717 + }
9718 + $minguestsnum = VikRequest::getInt('minguestsnum', 0, 'request');
9719 + $mingueststype = VikRequest::getString('mingueststype', 'guests', 'request');
9720 + $minguestsnum = $minguestsnum < 0 ? 0 : $minguestsnum;
9721 + $mingueststype = !empty($mingueststype) && !in_array($mingueststype, array('adults', 'guests')) ? 'guests' : $mingueststype;
9722 + $maxguestsnum = VikRequest::getInt('maxguestsnum', 0, 'request');
9723 + $maxgueststype = VikRequest::getString('maxgueststype', 'guests', 'request');
9724 + $maxguestsnum = $maxguestsnum < 0 ? 0 : $maxguestsnum;
9725 + $maxgueststype = !empty($maxgueststype) && !in_array($maxgueststype, array('adults', 'guests')) ? 'guests' : $maxgueststype;
9726 + $minguests = VikRequest::getInt('minguests', 0, 'request');
9727 + $minguests_conflict = false;
9728 + if ($minguests > 0 && $minguestsnum > 0 && $maxguestsnum > 0) {
9729 + if ($minguestsnum >= $maxguestsnum) {
9730 + $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL1');
9731 + } elseif (($maxguestsnum - $minguestsnum) < 2) {
9732 + $minguests_conflict = JText::translate('VBOMINMAXGUESTSOPTCONFL2');
9733 + }
9734 + }
9735 + if (!$minguests || $minguests_conflict !== false) {
9736 + $minguestsnum = 0;
9737 + $maxguestsnum = 0;
9738 + if ($minguests_conflict !== false) {
9739 + // raise warning, but do not stop the process
9740 + VikError::raiseWarning('', $minguests_conflict);
9741 + }
9742 + }
9743 + $damagedep = VikRequest::getInt('damagedep', 0, 'request');
9744 + $pet_fee = VikRequest::getInt('pet_fee', 0, 'request');
9745 + $custom_checkinout = VikRequest::getInt('custom_checkinout', 0, 'request');
9746 + $set_checkin = VikRequest::getInt('set_checkin', 0, 'request');
9747 + $set_checkout = VikRequest::getInt('set_checkout', 0, 'request');
9748 + if (!$custom_checkinout) {
9749 + $set_checkin = 0;
9750 + $set_checkout = 0;
9751 + }
9752 + if ((!$set_checkin && !$set_checkout) || $set_checkin == $set_checkout) {
9753 + // check-in and check-out times should not be equal or both empty
9754 + $custom_checkinout = 0;
9755 + }
9756 + $damagedep_settings = $damagedep ? ((array) $app->input->get('damagedep_settings', [], 'array')) : [];
9757 + $oparams = [
9758 + 'minguestsnum' => $minguestsnum,
9759 + 'mingueststype' => $mingueststype,
9760 + 'maxguestsnum' => $maxguestsnum,
9761 + 'maxgueststype' => $maxgueststype,
9762 + 'damagedep' => $damagedep,
9763 + 'damagedep_settings' => $damagedep_settings,
9764 + 'pet_fee' => $pet_fee,
9765 + 'custom_checkinout' => $custom_checkinout,
9766 + 'set_checkin' => $set_checkin,
9767 + 'set_checkout' => $set_checkout,
9768 + ];
9769 +
9770 + /**
9771 + * Ensure options of type city tax never get a tax rate.
9772 + *
9773 + * @since 1.18.3 (J) - 1.8.3 (WP)
9774 + */
9775 + if ($pis_citytax) {
9776 + $poptaliq = 0;
9777 + }
9778 +
9779 + if (!empty($poptname)) {
9780 + if (intval($_FILES['optimg']['error']) == 0 && VikBooking::caniWrite(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR) && trim($_FILES['optimg']['name'])!="") {
9781 + jimport('joomla.filesystem.file');
9782 + $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
9783 + if (@is_uploaded_file($_FILES['optimg']['tmp_name'])) {
9784 + $safename=JFile::makeSafe(str_replace(" ", "_", strtolower($_FILES['optimg']['name'])));
9785 + if (file_exists($updpath.$safename)) {
9786 + $j = 1;
9787 + while (file_exists($updpath.$j.$safename)) {
9788 + $j++;
9789 + }
9790 + $pwhere = $updpath.$j.$safename;
9791 + } else {
9792 + $j = "";
9793 + $pwhere = $updpath.$safename;
9794 + }
9795 + if (!getimagesize($_FILES['optimg']['tmp_name']) || !preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $safename)) {
9796 + @unlink($pwhere);
9797 + $picon = "";
9798 + } else {
9799 + VikBooking::uploadFile($_FILES['optimg']['tmp_name'], $pwhere);
9800 + @chmod($pwhere, 0644);
9801 + $picon = $j.$safename;
9802 + if ($pautoresize == "1" && !empty($presizeto)) {
9803 + $eforj = new vikResizer();
9804 + $origmod = $eforj->proportionalImage($pwhere, $updpath.'r_'.$j.$safename, $presizeto, $presizeto);
9805 + if ($origmod) {
9806 + @unlink($pwhere);
9807 + $picon = 'r_'.$j.$safename;
9808 + }
9809 + }
9810 + }
9811 + } else {
9812 + $picon = "";
9813 + }
9814 + } else {
9815 + $picon = "";
9816 + }
9817 + $poptperday = ($poptperday == "each" ? "1" : "0");
9818 + $poptperperson = ($poptperperson == "each" ? "1" : "0");
9819 + ($popthmany == "yes" ? $popthmany = "1" : $popthmany = "0");
9820 + $ageintervalstr = '';
9821 + if ($pifchildren == 1 && count($pagefrom) > 0 && count($pagecost) > 0 && count($pagefrom) == count($pagecost)) {
9822 + foreach ($pagefrom as $kage => $vage) {
9823 + $afrom = intval($vage);
9824 + $ato = intval($pageto[$kage]);
9825 + $acost = floatval($pagecost[$kage]);
9826 + if (strlen($vage) > 0 && strlen($pagecost[$kage]) > 0) {
9827 + if ($ato < $afrom) $ato = $afrom;
9828 + $ageintervalstr .= $afrom.'_'.$ato.'_'.$acost.(array_key_exists($kage, $pagectype) && strpos($pagectype[$kage], '%') !== false ? '_%'.(strpos($pagectype[$kage], '%b') !== false ? 'b' : '') : '').';;';
9829 + }
9830 + }
9831 + $ageintervalstr = rtrim($ageintervalstr, ';;');
9832 + if (!empty($ageintervalstr)) {
9833 + $pforcesel = 1;
9834 + }
9835 + }
9836 + $q = "SELECT `ordering` FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` DESC LIMIT 1;";
9837 + $dbo->setQuery($q);
9838 + $dbo->execute();
9839 + if ($dbo->getNumRows() == 1) {
9840 + $getlast = $dbo->loadResult();
9841 + $newsortnum = $getlast + 1;
9842 + } else {
9843 + $newsortnum = 1;
9844 + }
9845 + $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)) . ");";
9846 + $dbo->setQuery($q);
9847 + $dbo->execute();
9848 + $newoptid = $dbo->insertid();
9849 +
9850 + if (!empty($newoptid)) {
9851 + // assign/unset option-rooms relations
9852 + $rooms_with_opt = array();
9853 + if (count($pidrooms)) {
9854 + // assign this new option to the requested rooms
9855 + foreach ($pidrooms as $idroom) {
9856 + if (empty($idroom)) {
9857 + continue;
9858 + }
9859 + $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id`=" . (int)$idroom . ";";
9860 + $dbo->setQuery($q);
9861 + $dbo->execute();
9862 + if (!$dbo->getNumRows()) {
9863 + continue;
9864 + }
9865 + $room_data = $dbo->loadAssoc();
9866 + array_push($rooms_with_opt, $room_data['id']);
9867 + $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9868 + if (in_array((string)$newoptid, $current_opts)) {
9869 + continue;
9870 + }
9871 + if (count($current_opts) === 1 && (string)$current_opts[0] == '0') {
9872 + // make sure we do not concatenate a real ID to 0
9873 + $current_opts = array();
9874 + }
9875 + array_push($current_opts, $newoptid);
9876 + $new_opts = implode(';', $current_opts) . ';';
9877 + $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9878 + $dbo->setQuery($q);
9879 + $dbo->execute();
9880 + }
9881 + }
9882 + if (!count($rooms_with_opt)) {
9883 + // get all rooms to unset this option (if previously set)
9884 + array_push($rooms_with_opt, '0');
9885 + }
9886 + // unset the option from the other rooms that may have it
9887 + $q = "SELECT `id`, `idopt` FROM `#__vikbooking_rooms` WHERE `id` NOT IN (" . implode(', ', $rooms_with_opt) . ");";
9888 + $dbo->setQuery($q);
9889 + $dbo->execute();
9890 + if ($dbo->getNumRows()) {
9891 + $unset_rooms_opt = $dbo->loadAssocList();
9892 + foreach ($unset_rooms_opt as $room_data) {
9893 + $current_opts = empty($room_data['idopt']) ? array() : explode(';', rtrim($room_data['idopt'], ';'));
9894 + if (!in_array((string)$newoptid, $current_opts)) {
9895 + // this room is not using this option
9896 + continue;
9897 + }
9898 + $optkey = array_search((string)$newoptid, $current_opts);
9899 + if ($optkey === false) {
9900 + // key not found
9901 + continue;
9902 + }
9903 + // unset this option ID from the string
9904 + unset($current_opts[$optkey]);
9905 + if (!count($current_opts)) {
9906 + // a room with no options assigned will be listed as "0;"
9907 + $current_opts = array(0);
9908 + }
9909 + $new_opts = implode(';', $current_opts) . ';';
9910 + $q = "UPDATE `#__vikbooking_rooms` SET `idopt`=" . $dbo->quote($new_opts) . " WHERE `id`={$room_data['id']};";
9911 + $dbo->setQuery($q);
9912 + $dbo->execute();
9913 + }
9914 + }
9915 + //
9916 + }
9917 +
9918 + }
9919 + $mainframe = JFactory::getApplication();
9920 + $mainframe->redirect("index.php?option=com_vikbooking&task=" . ($stay && isset($newoptid) && !empty($newoptid) ? 'editoptional&cid[]=' . $newoptid : 'optionals'));
9921 + }
9922 +
9923 + public function removeoptionals()
9924 + {
9925 + if (!JSession::checkToken()) {
9926 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
9927 + }
9928 +
9929 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
9930 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
9931 + }
9932 +
9933 + $ids = VikRequest::getVar('cid', array(0));
9934 + if ($ids) {
9935 + $dbo = JFactory::getDbo();
9936 + foreach ($ids as $d) {
9937 + $q = "SELECT `img` FROM `#__vikbooking_optionals` WHERE `id`=".$dbo->quote($d).";";
9938 + $dbo->setQuery($q);
9939 + $dbo->execute();
9940 + if ($dbo->getNumRows() == 1) {
9941 + $rows = $dbo->loadAssocList();
9942 + if (!empty($rows[0]['img']) && file_exists(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['img'])) {
9943 + @unlink(VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR.$rows[0]['img']);
9944 + }
9945 + }
9946 + $q = "DELETE FROM `#__vikbooking_optionals` WHERE `id`=".$dbo->quote($d).";";
9947 + $dbo->setQuery($q);
9948 + $dbo->execute();
9949 + }
9950 + }
9951 + $mainframe = JFactory::getApplication();
9952 + $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
9953 + }
9954 +
9955 + public function sendcustomsms() {
9956 + $mainframe = JFactory::getApplication();
9957 + $pphone = VikRequest::getString('phone', '', 'request');
9958 + $psmscont = VikRequest::getString('smscont', '', 'request');
9959 + $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
9960 + $pgoto = !empty($pgoto) ? urldecode($pgoto) : 'index.php?option=com_vikbooking';
9961 + if (!empty($pphone) && !empty($psmscont)) {
9962 + $sms_api = VikBooking::getSMSAPIClass();
9963 + $sms_api_params = VikBooking::getSMSParams();
9964 + if (!empty($sms_api) && file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api) && !empty($sms_api_params)) {
9965 + require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
9966 + $sms_obj = new VikSmsApi(array(), $sms_api_params);
9967 + $response_obj = $sms_obj->sendMessage($pphone, $psmscont);
9968 + if ( !$sms_obj->validateResponse($response_obj) ) {
9969 + VikError::raiseWarning('', $sms_obj->getLog());
9970 + } else {
9971 + $mainframe->enqueueMessage(JText::translate('VBSENDSMSOK'));
9972 + }
9973 + } else {
9974 + VikError::raiseWarning('', JText::translate('VBSENDSMSERRMISSAPI'));
9975 + }
9976 + } else {
9977 + VikError::raiseWarning('', JText::translate('VBSENDSMSERRMISSDATA'));
9978 + }
9979 + $mainframe->redirect($pgoto);
9980 + }
9981 +
9982 + public function sendcustomemail() {
9983 + $dbo = JFactory::getDbo();
9984 + $mainframe = JFactory::getApplication();
9985 + $vbo_tn = VikBooking::getTranslator();
9986 + $pbid = VikRequest::getInt('bid', '', 'request');
9987 + $pemailsubj = VikRequest::getString('emailsubj', '', 'request');
9988 + $pemail = VikRequest::getString('email', '', 'request');
9989 + $pemailcont = VikRequest::getString('emailcont', '', 'request', VIKREQUEST_ALLOWRAW);
9990 + $pemailfrom = VikRequest::getString('emailfrom', '', 'request');
9991 + $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
9992 + $pgoto = !empty($pgoto) ? urldecode($pgoto) : 'index.php?option=com_vikbooking';
9993 + if (!empty($pemail) && !empty($pemailcont)) {
9994 + $email_attach = null;
9995 + jimport('joomla.filesystem.file');
9996 + $pemailattch = VikRequest::getVar('emailattch', null, 'files', 'array');
9997 + if (isset($pemailattch) && strlen(trim($pemailattch['name']))) {
9998 + $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($pemailattch['name'])));
9999 + $src = $pemailattch['tmp_name'];
10000 + $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
10001 + $j = "";
10002 + if (file_exists($dest.$filename)) {
10003 + $j = rand(171, 1717);
10004 + while (file_exists($dest.$j.$filename)) {
10005 + $j++;
10006 + }
10007 + }
10008 + $finaldest = $dest.$j.$filename;
10009 + if (VikBooking::uploadFile($src, $finaldest)) {
10010 + $email_attach = $finaldest;
10011 + } else {
10012 + VikError::raiseWarning('', 'Error uploading the attachment. Email not sent.');
10013 + $mainframe->redirect($pgoto);
10014 + exit;
10015 + }
10016 + }
10017 + //VBO 1.10 - special tags for the custom email template files and messages
10018 + $orig_mail_cont = $pemailcont;
10019 + if (strpos($pemailcont, '{') !== false && strpos($pemailcont, '}') !== false) {
10020 + // replace any possible placeholder for special tags
10021 + $pemailcont = preg_replace_callback("/(<strong class=\"vbo-editor-hl-specialtag\">)([^<]+)(<\/strong>)/", function($match) {
10022 + return $match[2];
10023 + }, $pemailcont);
10024 +
10025 + $booking = array();
10026 + $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.";";
10027 + $dbo->setQuery($q);
10028 + $dbo->execute();
10029 + if ($dbo->getNumRows() > 0) {
10030 + $booking = $dbo->loadAssoc();
10031 + }
10032 + $booking_rooms = array();
10033 + $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.";";
10034 + $dbo->setQuery($q);
10035 + $dbo->execute();
10036 + if ($dbo->getNumRows() > 0) {
10037 + $booking_rooms = $dbo->loadAssocList();
10038 + if (!empty($booking['lang'])) {
10039 + $vbo_tn->translateContents($booking_rooms, '#__vikbooking_rooms', array('id' => 'idroom', 'room_name' => 'name'), array(), $booking['lang']);
10040 + }
10041 + }
10042 + //we use the same parsing function as the one for the Customer SMS Template
10043 + $pemailcont = VikBooking::parseCustomerSMSTemplate($booking, $booking_rooms, null, $pemailcont);
10044 + }
10045 + //
10046 + // allow the use of token {booking_id} in subject
10047 + $pemailsubj = str_replace('{booking_id}', $pbid, $pemailsubj);
10048 + //
10049 + $is_html = (strpos($pemailcont, '<') !== false && strpos($pemailcont, '>') !== false);
10050 + $pemailcont = !$is_html ? nl2br($pemailcont) : $pemailcont;
10051 + $vbo_app = VikBooking::getVboApplication();
10052 + $vbo_app->sendMail($pemailfrom, $pemailfrom, $pemail, $pemailfrom, $pemailsubj, $pemailcont, $is_html, 'base64', $email_attach);
10053 + $mainframe->enqueueMessage(JText::translate('VBSENDEMAILOK'));
10054 + if ($email_attach !== null) {
10055 + @unlink($email_attach);
10056 + }
10057 + //Booking History
10058 + VikBooking::getBookingHistoryInstance()->setBid($pbid)->store('CE', nl2br($pemailsubj . "\n\n" . $pemailcont));
10059 + //
10060 + //Save email template for future sending
10061 + $config_rec_exists = false;
10062 + $emtpl = array(
10063 + 'emailsubj' => $pemailsubj,
10064 + 'emailcont' => $orig_mail_cont,
10065 + 'emailfrom' => $pemailfrom
10066 + );
10067 + $cur_emtpl = array();
10068 + $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='customemailtpls';";
10069 + $dbo->setQuery($q);
10070 + $dbo->execute();
10071 + if ($dbo->getNumRows() > 0) {
10072 + $config_rec_exists = true;
10073 + $cur_emtpl = $dbo->loadResult();
10074 + $cur_emtpl = empty($cur_emtpl) ? array() : json_decode($cur_emtpl, true);
10075 + $cur_emtpl = is_array($cur_emtpl) ? $cur_emtpl : array();
10076 + }
10077 + if (count($cur_emtpl) > 0) {
10078 + $existing_subj = false;
10079 + foreach ($cur_emtpl as $emk => $emv) {
10080 + if (array_key_exists('emailsubj', $emv) && $emv['emailsubj'] == $emtpl['emailsubj']) {
10081 + $cur_emtpl[$emk] = $emtpl;
10082 + $existing_subj = true;
10083 + break;
10084 + }
10085 + }
10086 + if ($existing_subj === false) {
10087 + $cur_emtpl[] = $emtpl;
10088 + }
10089 + } else {
10090 + $cur_emtpl[] = $emtpl;
10091 + }
10092 + if (count($cur_emtpl) > 10) {
10093 + //Max 10 templates to avoid problems with the size of the field and truncated json strings
10094 + $exceed = count($cur_emtpl) - 10;
10095 + for ($tl=0; $tl < $exceed; $tl++) {
10096 + unset($cur_emtpl[$tl]);
10097 + }
10098 + $cur_emtpl = array_values($cur_emtpl);
10099 + }
10100 + if ($config_rec_exists === true) {
10101 + $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($cur_emtpl))." WHERE `param`='customemailtpls';";
10102 + $dbo->setQuery($q);
10103 + $dbo->execute();
10104 + } else {
10105 + $q = "INSERT INTO `#__vikbooking_config` (`param`,`setting`) VALUES ('customemailtpls', ".$dbo->quote(json_encode($cur_emtpl)).");";
10106 + $dbo->setQuery($q);
10107 + $dbo->execute();
10108 + }
10109 + //
10110 + } else {
10111 + VikError::raiseWarning('', JText::translate('VBSENDEMAILERRMISSDATA'));
10112 + }
10113 + $mainframe->redirect($pgoto);
10114 + }
10115 +
10116 + public function rmcustomemailtpl() {
10117 + $cid = VikRequest::getVar('cid', array(0));
10118 + $oid = $cid[0];
10119 + $dbo = JFactory::getDBO();
10120 + $mainframe = JFactory::getApplication();
10121 + $tplind = VikRequest::getInt('tplind', '', 'request');
10122 + if (empty($oid) || !(strlen($tplind) > 0)) {
10123 + VikError::raiseWarning('', 'Missing Data.');
10124 + $mainframe->redirect('index.php?option=com_vikbooking');
10125 + exit;
10126 + }
10127 + $cur_emtpl = array();
10128 + $q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='customemailtpls';";
10129 + $dbo->setQuery($q);
10130 + $dbo->execute();
10131 + if ($dbo->getNumRows() > 0) {
10132 + $cur_emtpl = $dbo->loadResult();
10133 + $cur_emtpl = empty($cur_emtpl) ? array() : json_decode($cur_emtpl, true);
10134 + $cur_emtpl = is_array($cur_emtpl) ? $cur_emtpl : array();
10135 + } else {
10136 + VikError::raiseWarning('', 'Missing Templates Record.');
10137 + $mainframe->redirect('index.php?option=com_vikbooking');
10138 + exit;
10139 + }
10140 + if (array_key_exists($tplind, $cur_emtpl)) {
10141 + unset($cur_emtpl[$tplind]);
10142 + $cur_emtpl = count($cur_emtpl) > 0 ? array_values($cur_emtpl) : array();
10143 + $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($cur_emtpl))." WHERE `param`='customemailtpls';";
10144 + $dbo->setQuery($q);
10145 + $dbo->execute();
10146 + }
10147 + $mainframe->redirect('index.php?option=com_vikbooking&task=editorder&cid[]='.$oid.'&customemail=1');
10148 + exit;
10149 + }
10150 +
10151 + public function exportcustomers() {
10152 + //we do not set the menu for this view
10153 +
10154 + VikRequest::setVar('view', VikRequest::getCmd('view', 'exportcustomers'));
10155 +
10156 + parent::display();
10157 +
10158 + if (VikBooking::showFooter()) {
10159 + VikBookingHelper::printFooter();
10160 + }
10161 + }
10162 +
10163 + public function csvexportprepare() {
10164 + //modal box, so we do not set menu or footer
10165 +
10166 + VikRequest::setVar('view', VikRequest::getCmd('view', 'csvexportprepare'));
10167 +
10168 + parent::display();
10169 + }
10170 +
10171 + public function icsexportprepare() {
10172 + //modal box, so we do not set menu or footer
10173 +
10174 + VikRequest::setVar('view', VikRequest::getCmd('view', 'icsexportprepare'));
10175 +
10176 + parent::display();
10177 + }
10178 +
10179 + public function bookingcheckin() {
10180 + //modal box, so we do not set menu or footer
10181 +
10182 + VikRequest::setVar('view', VikRequest::getCmd('view', 'bookingcheckin'));
10183 +
10184 + parent::display();
10185 + }
10186 +
10187 + public function gencheckindoc() {
10188 + //modal box, so we do not set menu or footer
10189 +
10190 + VikRequest::setVar('view', VikRequest::getCmd('view', 'gencheckindoc'));
10191 +
10192 + parent::display();
10193 + }
10194 +
10195 + public function checkversion() {
10196 + //to be called via ajax
10197 + $params = new stdClass;
10198 + $params->version = VIKBOOKING_SOFTWARE_VERSION;
10199 + $params->alias = 'com_vikbooking';
10200 +
10201 + $result = array();
10202 +
10203 + if (!count($result)) {
10204 + $result = new stdClass;
10205 + $result->status = 0;
10206 + } else {
10207 + $result = $result[0];
10208 + }
10209 +
10210 + echo json_encode($result);
10211 + exit;
10212 + }
10213 +
10214 + public function updateprogram() {
10215 + $params = new stdClass;
10216 + $params->version = VIKBOOKING_SOFTWARE_VERSION;
10217 + $params->alias = 'com_vikbooking';
10218 +
10219 + $result = array();
10220 +
10221 + if (!count($result) || !$result[0]) {
10222 + if (class_exists('JEventDispatcher')) {
10223 + $dispatcher = JEventDispatcher::getInstance();
10224 + $result = $dispatcher->trigger('checkVersion', array(&$params));
10225 + } else {
10226 + $app = JFactory::getApplication();
10227 + if (method_exists($app, 'triggerEvent')) {
10228 + $result = $app->triggerEvent('checkVersion', array(&$params));
10229 + }
10230 + }
10231 + }
10232 +
10233 + if (!count($result) || !$result[0]->status || !$result[0]->response->status) {
10234 + exit('Error, plugin disabled');
10235 + }
10236 +
10237 + JToolbarHelper::title(JText::translate('VBMAINTITLEUPDATEPROGRAM'));
10238 +
10239 + VikBookingHelper::pUpdateProgram($result[0]->response);
10240 + }
10241 +
10242 + public function updateprogramlaunch() {
10243 + $params = new stdClass;
10244 + $params->version = VIKBOOKING_SOFTWARE_VERSION;
10245 + $params->alias = 'com_vikbooking';
10246 +
10247 + $json = new stdClass;
10248 + $json->status = false;
10249 +
10250 + echo json_encode($json);
10251 + exit;
10252 + }
10253 +
10254 + public function invoke_vcm()
10255 + {
10256 + $app = JFactory::getApplication();
10257 +
10258 + $oids = VikRequest::getVar('cid', []);
10259 + $sync_type = VikRequest::getString('stype', 'new', 'request');
10260 + $sync_type = !in_array($sync_type, ['new', 'modify', 'cancel']) ? 'new' : $sync_type;
10261 + $original_booking_js = VikRequest::getString('origb', '', 'request', VIKREQUEST_ALLOWRAW);
10262 + $return_url = VikRequest::getString('returl', '', 'request', VIKREQUEST_ALLOWRAW);
10263 + $return_url = !empty($return_url) ? urldecode($return_url) : $return_url;
10264 +
10265 + if (!$oids || !is_file(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
10266 + $app->redirect("index.php?option=com_vikbooking&task=orders");
10267 + $app->close();
10268 + }
10269 +
10270 + $result = VikBooking::getVcmInvoker()
10271 + ->setOids($oids)
10272 + ->setSyncType($sync_type)
10273 + ->setOriginalBooking($original_booking_js, true)
10274 + ->doSync();
10275 +
10276 + if ($result === true) {
10277 + $app->enqueueMessage(JText::translate('VBCHANNELMANAGERRESULTOK'));
10278 + } else {
10279 + VikError::raiseWarning('', JText::translate('VBCHANNELMANAGERRESULTKO').' <a href="index.php?option=com_vikchannelmanager" target="_blank">'.JText::translate('VBCHANNELMANAGEROPEN').'</a>');
10280 + }
10281 +
10282 + if (!empty($return_url)) {
10283 + $app->redirect($return_url);
10284 + } else {
10285 + $app->redirect("index.php?option=com_vikbooking&task=orders");
10286 + }
10287 +
10288 + $app->close();
10289 + }
10290 +
10291 + public function multiphotosupload() {
10292 + jimport('joomla.filesystem.file');
10293 +
10294 + $dbo = JFactory::getDBO();
10295 + $proomid = VikRequest::getInt('roomid', '', 'request');
10296 +
10297 + $resp = array('files' => array());
10298 + $error_messages = array(
10299 + 1 => 'The uploaded file exceeds the upload_max_filesize directive in php.ini',
10300 + 2 => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
10301 + 3 => 'The uploaded file was only partially uploaded',
10302 + 4 => 'No file was uploaded',
10303 + 6 => 'Missing a temporary folder',
10304 + 7 => 'Failed to write file to disk',
10305 + 8 => 'A PHP extension stopped the file upload',
10306 + 'post_max_size' => 'The uploaded file exceeds the post_max_size directive in php.ini',
10307 + 'max_file_size' => 'File is too big',
10308 + 'min_file_size' => 'File is too small',
10309 + 'accept_file_types' => 'Filetype not allowed',
10310 + 'max_number_of_files' => 'Maximum number of files exceeded',
10311 + 'max_width' => 'Image exceeds maximum width',
10312 + 'min_width' => 'Image requires a minimum width',
10313 + 'max_height' => 'Image exceeds maximum height',
10314 + 'min_height' => 'Image requires a minimum height',
10315 + 'abort' => 'File upload aborted',
10316 + 'image_resize' => 'Failed to resize image',
10317 + 'vbo_type' => 'The file type cannot be accepted',
10318 + 'vbo_jupload' => 'The upload has failed. Check your CMS settings and permissions',
10319 + 'vbo_perm' => 'Error moving the uploaded files. Check your permissions'
10320 + );
10321 +
10322 + $creativik = new vikResizer();
10323 + $updpath = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
10324 + $bigsdest = $updpath;
10325 + $thumbsdest = $updpath;
10326 + $dest = $updpath;
10327 + $moreimagestr = '';
10328 + $cur_captions = json_encode(array());
10329 +
10330 + $q = "SELECT `moreimgs`,`imgcaptions` FROM `#__vikbooking_rooms` WHERE `id`=".$proomid.";";
10331 + $dbo->setQuery($q);
10332 + $dbo->execute();
10333 + if ($dbo->getNumRows() == 1) {
10334 + $photo_data = $dbo->loadAssocList();
10335 + $cur_captions = $photo_data[0]['imgcaptions'];
10336 + $cur_photos = $photo_data[0]['moreimgs'];
10337 + if (!empty($cur_photos)) {
10338 + $moreimagestr .= $cur_photos;
10339 + }
10340 + }
10341 +
10342 + $bulkphotos = VikRequest::getVar('bulkphotos', null, 'files', 'array');
10343 +
10344 + if (is_array($bulkphotos) && count($bulkphotos) > 0 && array_key_exists('name', $bulkphotos) && count($bulkphotos['name']) > 0) {
10345 + foreach ($bulkphotos['name'] as $updk => $photoname) {
10346 + $uploaded_image = array();
10347 + $filename = JFile::makeSafe(str_replace(" ", "_", strtolower($photoname)));
10348 + $src = $bulkphotos['tmp_name'][$updk];
10349 + $j = "";
10350 + if (file_exists($dest.$filename)) {
10351 + $j = rand(171, 1717);
10352 + while (file_exists($dest.$j.$filename)) {
10353 + $j++;
10354 + }
10355 + }
10356 + $finaldest=$dest.$j.$filename;
10357 + $is_error = false;
10358 + $err_key = '';
10359 + if (array_key_exists('error', $bulkphotos) && array_key_exists($updk, $bulkphotos['error']) && !empty($bulkphotos['error'][$updk])) {
10360 + if (array_key_exists($bulkphotos['error'][$updk], $error_messages)) {
10361 + $is_error = true;
10362 + $err_key = $bulkphotos['error'][$updk];
10363 + }
10364 + }
10365 + if (!$is_error) {
10366 + $check = getimagesize($bulkphotos['tmp_name'][$updk]);
10367 + if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
10368 + if (VikBooking::uploadFile($src, $finaldest)) {
10369 + $gimg = $j.$filename;
10370 + //orig img
10371 + $origmod = true;
10372 + VikBooking::uploadFile($finaldest, $bigsdest.'big_'.$j.$filename, true);
10373 + //thumb
10374 + $thumbsize = VikBooking::getThumbSize();
10375 + $thumb = $creativik->proportionalImage($finaldest, $thumbsdest.'thumb_'.$j.$filename, $thumbsize, $thumbsize);
10376 + if (!$thumb || !$origmod) {
10377 + if (file_exists($bigsdest.'big_'.$j.$filename)) @unlink($bigsdest.'big_'.$j.$filename);
10378 + if (file_exists($thumbsdest.'thumb_'.$j.$filename)) @unlink($thumbsdest.'thumb_'.$j.$filename);
10379 + $is_error = true;
10380 + $err_key = 'vbo_perm';
10381 + } else {
10382 + $moreimagestr.=$j.$filename.";;";
10383 + }
10384 + @unlink($finaldest);
10385 + } else {
10386 + $is_error = true;
10387 + $err_key = 'vbo_jupload';
10388 + }
10389 + } else {
10390 + $is_error = true;
10391 + $err_key = 'vbo_type';
10392 + }
10393 + }
10394 + $img = new stdClass();
10395 + if ($is_error) {
10396 + $img->name = '';
10397 + $img->size = '';
10398 + $img->type = '';
10399 + $img->url = '';
10400 + $img->error = array_key_exists($err_key, $error_messages) ? $error_messages[$err_key] : 'Generic Error for Upload';
10401 + } else {
10402 + $img->name = $photoname;
10403 + $img->size = $bulkphotos['size'][$updk];
10404 + $img->type = $bulkphotos['type'][$updk];
10405 + $img->url = VBO_SITE_URI.'resources/uploads/big_'.$j.$filename;
10406 + }
10407 + $resp['files'][] = $img;
10408 + }
10409 + } else {
10410 + $res = new stdClass();
10411 + $res->name = '';
10412 + $res->size = '';
10413 + $res->type = '';
10414 + $res->url = '';
10415 + $res->error = 'No images received for upload';
10416 + $resp['files'][] = $res;
10417 + }
10418 + //Update current extra images string
10419 + $q = "UPDATE `#__vikbooking_rooms` SET `moreimgs`=".$dbo->quote($moreimagestr)." WHERE `id`=".$proomid.";";
10420 + $dbo->setQuery($q);
10421 + $dbo->execute();
10422 + $resp['actmoreimgs'] = $moreimagestr;
10423 + //Update current extra images uploaded
10424 + $cur_thumbs = '';
10425 + $morei=explode(';;', $moreimagestr);
10426 + if (@count($morei) > 0) {
10427 + $imgcaptions = json_decode($cur_captions, true);
10428 + $usecaptions = empty($imgcaptions) || is_null($imgcaptions) || !is_array($imgcaptions) || !(count($imgcaptions) > 0) ? false : true;
10429 + $cur_thumbs .= '<ul class="vbo-sortable">';
10430 + foreach ($morei as $ki => $mi) {
10431 + if (!empty($mi)) {
10432 + $cur_thumbs .= '<li class="vbo-editroom-currentphoto">';
10433 + $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>';
10434 + $cur_thumbs .= '<a class="vbo-toggle-imgcaption" href="javascript: void(0);" onclick="vbOpenImgDetails(\''.$ki.'\', this)"><i class="'.VikBookingIcons::i('cog').'"></i></a>';
10435 + $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>';
10436 + $cur_thumbs .= '</li>';
10437 + }
10438 + }
10439 + $cur_thumbs .= '</ul>';
10440 + $cur_thumbs .= '<br clear="all"/>';
10441 + }
10442 + $resp['currentthumbs'] = $cur_thumbs;
10443 +
10444 + echo json_encode($resp);
10445 + exit;
10446 + }
10447 +
10448 + public function loadsmsbalance() {
10449 + //to be called via ajax
10450 + $html = 'Error1 [N/A]';
10451 + $sms_api = VikBooking::getSMSAPIClass();
10452 + if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api)) {
10453 + require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
10454 + $sms_obj = new VikSmsApi(array(), VikBooking::getSMSParams());
10455 + if (method_exists('VikSmsApi', 'estimate')) {
10456 + $array_result = $sms_obj->estimate("+393711271611", "estimate credit");
10457 + if ( $array_result->errorCode != 0 ) {
10458 + $html = 'Error3 ['.$array_result->errorMsg.']';
10459 + } else {
10460 + $html = VikBooking::getCurrencySymb().' '.$array_result->userCredit;
10461 + }
10462 + } else {
10463 + $html = 'Error2 [N/A]';
10464 + }
10465 + }
10466 + echo $html;
10467 + exit;
10468 + }
10469 +
10470 + public function loadsmsparams() {
10471 + //to be called via ajax
10472 + $html = '---------';
10473 + $phpfile = VikRequest::getString('phpfile', '', 'request');
10474 + if (!empty($phpfile)) {
10475 + $sms_api = VikBooking::getSMSAPIClass();
10476 + $sms_params = $sms_api == $phpfile ? VikBooking::getSMSParams(false) : '';
10477 + $html = VikBooking::displaySMSParameters($phpfile, $sms_params);
10478 + }
10479 + echo $html;
10480 + exit;
10481 + }
10482 +
10483 + public function loadcronparams() {
10484 + //to be called via ajax
10485 + $html = '---------';
10486 + $phpfile = VikRequest::getString('phpfile', '', 'request');
10487 + if (!empty($phpfile)) {
10488 + $html = VikBooking::displayCronParameters($phpfile);
10489 + }
10490 + echo $html;
10491 + exit;
10492 + }
10493 +
10494 + public function loadpaymentparams() {
10495 + //to be called via ajax
10496 + $html = '<p>---------</p>';
10497 + $phpfile = VikRequest::getString('phpfile', '', 'request');
10498 + if (!empty($phpfile)) {
10499 + $html = VikBooking::displayPaymentParameters($phpfile);
10500 + }
10501 + echo $html;
10502 + exit;
10503 + }
10504 +
10505 + public function setbookingtag() {
10506 + //to be called via ajax
10507 + $dbo = JFactory::getDBO();
10508 + $pidorder = VikRequest::getInt('idorder', '', 'request');
10509 + $ptagkey = VikRequest::getInt('tagkey', '', 'request');
10510 + if (!empty($pidorder) && $ptagkey >= 0) {
10511 + $all_tags = VikBooking::loadBookingsColorTags();
10512 + if (array_key_exists($ptagkey, $all_tags)) {
10513 + $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `id`=".(int)$pidorder.";";
10514 + $dbo->setQuery($q);
10515 + $dbo->execute();
10516 + if ($dbo->getNumRows() > 0) {
10517 + $newcolortag = json_encode($all_tags[$ptagkey]);
10518 + $q = "UPDATE `#__vikbooking_orders` SET `colortag`=".$dbo->quote($newcolortag)." WHERE `id`=".(int)$pidorder.";";
10519 + $dbo->setQuery($q);
10520 + $dbo->execute();
10521 + $newcolortag = $all_tags[$ptagkey];
10522 + $newcolortag['name'] = JText::translate($newcolortag['name']);
10523 + $newcolortag['fontcolor'] = VikBooking::getBestColorContrast($newcolortag['color']);
10524 + echo json_encode($newcolortag);
10525 + } else {
10526 + echo 'e4j.error.Booking ('.$pidorder.') not found';
10527 + }
10528 + } else {
10529 + echo 'e4j.error.Color Tag ('.$ptagkey.') not found';
10530 + }
10531 + } else {
10532 + echo 'e4j.error.Missing Data';
10533 + }
10534 + exit;
10535 + }
10536 +
10537 + public function updatereceiptnum() {
10538 + //to be called via ajax
10539 + $pnewnum = VikRequest::getInt('newnum', '', 'request');
10540 + $pnewnotes = VikRequest::getString('newnotes', '', 'request', VIKREQUEST_ALLOWRAW);
10541 + $poid = VikRequest::getInt('oid', '', 'request');
10542 + if ($pnewnum > 0) {
10543 + VikBooking::getNextReceiptNumber($poid, $pnewnum);
10544 + VikBooking::getReceiptNotes($pnewnotes);
10545 + //Booking History
10546 + VikBooking::getBookingHistoryInstance()->setBid($poid)->store('BR', JText::translate('VBOFISCRECEIPTNUM').': '.$pnewnum);
10547 + //
10548 + echo 'e4j.ok';
10549 + exit;
10550 + }
10551 + echo 'e4j.error';
10552 + exit;
10553 + }
10554 +
10555 + /**
10556 + * AJAX endpoint to check if a room ID is available on specific dates.
10557 + */
10558 + public function isroombookable()
10559 + {
10560 + $app = JFactory::getApplication();
10561 + $dbo = JFactory::getDbo();
10562 +
10563 + $prid = $app->input->getUInt('rid', 0);
10564 + $pfdate = $app->input->getString('fdate', '');
10565 + $ptdate = $app->input->getString('tdate', '');
10566 +
10567 + if (empty($prid) || empty($pfdate) || empty($ptdate)) {
10568 + VBOHttpDocument::getInstance($app)->close(400, 'Missing request values.');
10569 + }
10570 +
10571 + $res = [
10572 + 'status' => 0,
10573 + 'err' => '',
10574 + ];
10575 +
10576 + $room_info = VikBooking::getRoomInfo($prid);
10577 + if (!$room_info) {
10578 + VBOHttpDocument::getInstance($app)->close(404, 'Room not found.');
10579 + }
10580 +
10581 + $pcheckinh = 0;
10582 + $pcheckinm = 0;
10583 + $pcheckouth = 0;
10584 + $pcheckoutm = 0;
10585 + $timeopst = VikBooking::getTimeOpenStore();
10586 + if (is_array($timeopst)) {
10587 + $opent = VikBooking::getHoursMinutes($timeopst[0]);
10588 + $closet = VikBooking::getHoursMinutes($timeopst[1]);
10589 + $pcheckinh = $opent[0];
10590 + $pcheckinm = $opent[1];
10591 + $pcheckouth = $closet[0];
10592 + $pcheckoutm = $closet[1];
10593 + }
10594 +
10595 + $from_ts = VikBooking::getDateTimestamp($pfdate, $pcheckinh, $pcheckinm);
10596 + $to_ts = VikBooking::getDateTimestamp($ptdate, $pcheckouth, $pcheckoutm);
10597 +
10598 + if (!empty($from_ts) && !empty($to_ts) && VikBooking::roomBookable($room_info['id'], $room_info['units'], $from_ts, $to_ts)) {
10599 + $res['status'] = 1;
10600 + } else {
10601 + if (empty($from_ts) || empty($to_ts)) {
10602 + $res['err'] = 'Invalid dates';
10603 + } else {
10604 + // not available
10605 + $res['err'] = JText::sprintf('VBOBOOKADDROOMERR', $room_info['name'], $pfdate, $ptdate);
10606 + }
10607 + }
10608 +
10609 + // send response to output
10610 + VBOHttpDocument::getInstance($app)->json($res);
10611 + }
10612 +
10613 + public function uploadsnapshot() {
10614 + $snap_base_path = VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'idscans';
10615 + /**
10616 + * We no longer access the uploaded file from php://input, we now retrieve it as a regular file upload.
10617 + * The old snapshot collection script with Flash no longer works in 2021.
10618 + *
10619 + * @since 1.14 (J) - 1.4.0 (WP)
10620 + */
10621 + $result = null;
10622 + try {
10623 + $result = VikBooking::uploadFileFromRequest(VikRequest::getVar('snapshot', null, 'files', 'array'), $snap_base_path, 'png,jpg,jpeg');
10624 + } catch (RuntimeException $e) {
10625 + echo "e4j.error.Error " . $e->getMessage();
10626 + exit;
10627 + }
10628 +
10629 + if (!is_object($result)) {
10630 + echo "e4j.error.Invalid upload response";
10631 + exit;
10632 + }
10633 +
10634 + echo $result->filename;
10635 + exit;
10636 + }
10637 +
10638 + public function checkvcmrateschanges() {
10639 + //to be called via ajax
10640 + $session = JFactory::getSession();
10641 + $ret = array('changesCount' => 0, 'changesData' => '');
10642 + $updforvcm = $session->get('vbVcmRatesUpd', '');
10643 + if (!empty($updforvcm) && is_array($updforvcm) && count($updforvcm) > 0) {
10644 + $ret['changesCount'] = $updforvcm['count'];
10645 + $ret['changesData'] = $updforvcm;
10646 + }
10647 +
10648 + echo json_encode($ret);
10649 + exit;
10650 + }
10651 +
10652 + /**
10653 + * AJAX endpoint to load the details of one or more bookings.
10654 + *
10655 + * @return void
10656 + *
10657 + * @since 1.16.0 (J) - 1.6.0 (WP) the method was refactored.
10658 + */
10659 + public function getbookingsinfo()
10660 + {
10661 + //to be called via ajax
10662 + $dbo = JFactory::getDbo();
10663 +
10664 + $booking_infos = [];
10665 + $bookings = [];
10666 +
10667 + $pidorders = VikRequest::getString('idorders', '', 'request');
10668 + $psubroom = VikRequest::getString('subroom', '', 'request');
10669 + $pstatus = VikRequest::getString('status', '', 'request');
10670 + $pstay_date = VikRequest::getString('stay_date', '', 'request');
10671 + $pidroom = VikRequest::getInt('idroom', 0, 'request');
10672 + $psharedcal = VikRequest::getInt('sharedcal', 0, 'request');
10673 +
10674 + if (!empty($pidorders)) {
10675 + $bookings = explode(',', $pidorders);
10676 + foreach ($bookings as $k => $v) {
10677 + $v = intval(str_replace('-', '', $v));
10678 + if (empty($v)) {
10679 + unset($bookings[$k]);
10680 + continue;
10681 + }
10682 + $bookings[$k] = $v;
10683 + }
10684 + }
10685 + $bookings = array_values($bookings);
10686 +
10687 + if (!$bookings) {
10688 + /**
10689 + * AJAX requests made by the page availability overview may contain empty booking IDs
10690 + * due to SQL errors that only occupied the room, but could not save the booking record.
10691 + * Clean up busy records where the busy relations contain empty booking IDs.
10692 + *
10693 + * @since 1.14 (J) - 1.4.0 (WP)
10694 + */
10695 + $hanging_busy_ids = [];
10696 +
10697 + $q = "SELECT `idbusy` FROM `#__vikbooking_ordersbusy` WHERE `idorder` = 0 OR `idorder` IS NULL;";
10698 + $dbo->setQuery($q);
10699 + $removelist = $dbo->loadAssocList();
10700 + if ($removelist) {
10701 + foreach ($removelist as $hanging_busy) {
10702 + $hanging_busy_id = (int)$hanging_busy['idbusy'];
10703 + if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
10704 + array_push($hanging_busy_ids, $hanging_busy_id);
10705 + }
10706 + }
10707 + }
10708 +
10709 + // let's check also for ghost records that only occupy the room
10710 + $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);";
10711 + $dbo->setQuery($q);
10712 + $removelist = $dbo->loadAssocList();
10713 + if ($removelist) {
10714 + foreach ($removelist as $hanging_busy) {
10715 + $hanging_busy_id = (int)$hanging_busy['id'];
10716 + if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
10717 + array_push($hanging_busy_ids, $hanging_busy_id);
10718 + }
10719 + }
10720 + }
10721 +
10722 + if ($hanging_busy_ids) {
10723 + $q = "DELETE FROM `#__vikbooking_busy` WHERE `id` IN (" . implode(', ', $hanging_busy_ids) . ");";
10724 + $dbo->setQuery($q);
10725 + $dbo->execute();
10726 + }
10727 + //
10728 +
10729 + // output the error
10730 + VBOHttpDocument::getInstance()->close(500, '1 - ' . JText::translate('VBOVWGETBKERRMISSDATA'));
10731 + }
10732 +
10733 + $nowdf = VikBooking::getDateFormat(true);
10734 + if ($nowdf == "%d/%m/%Y") {
10735 + $df = 'd/m/Y';
10736 + } elseif ($nowdf == "%m/%d/%Y") {
10737 + $df = 'm/d/Y';
10738 + } else {
10739 + $df = 'Y/m/d';
10740 + }
10741 + $datesep = VikBooking::getDateSeparator(true);
10742 + $currencysymb = VikBooking::getCurrencySymb();
10743 + $current_y = date('Y');
10744 + $current_ts = time();
10745 + $short_meal_enums = VBOMealplanManager::getInstance()->getShortMealPlans();
10746 +
10747 + $query = $dbo->getQuery(true);
10748 + $query->select('o.*');
10749 + $query->from($dbo->qn('#__vikbooking_orders', 'o'));
10750 + if (!empty($pstay_date) && !empty($pidroom) && $pstatus == 'any') {
10751 + // include the requested booking IDs and the cancelled reservations for this stay date
10752 + $stay_date_info = getdate(strtotime($pstay_date));
10753 + $lim_ts_to = mktime(23, 59, 59, $stay_date_info['mon'], $stay_date_info['mday'], $stay_date_info['year']);
10754 + $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) . '))');
10755 + // exclude the pending reservations
10756 + $query->where($dbo->qn('o.status') . ' IN (' . $dbo->q('confirmed') . ', ' . $dbo->q('cancelled') . ')');
10757 + } else {
10758 + // include only the requested booking IDs
10759 + $query->where($dbo->qn('o.id') . ' IN (' . implode(', ', $bookings) . ')');
10760 + }
10761 + if ($pstatus != 'any') {
10762 + $query->where($dbo->qn('o.status') . ' != ' . $dbo->q('cancelled'));
10763 + }
10764 + if (!empty($pstay_date) && $pstatus == 'any') {
10765 + // sort by confirmed status before cancelled status
10766 + $query->order('CASE WHEN ' . $dbo->qn('o.status') . ' = ' . $dbo->q('confirmed') . ' THEN 1 ELSE 0 END DESC');
10767 + $query->order($dbo->qn('o.id') . ' ASC');
10768 + }
10769 + $dbo->setQuery($query);
10770 + $booking_infos = $dbo->loadAssocList();
10771 +
10772 + foreach ($booking_infos as $k => $row) {
10773 + // rooms, amounts and guests information
10774 + $rooms = VikBooking::loadOrdersRoomsData($row['id']);
10775 + $rids_involved = [];
10776 + $room_names = [];
10777 + $totadults = 0;
10778 + $totchildren = 0;
10779 + foreach ($rooms as $rr) {
10780 + $rids_involved[] = $rr['idroom'];
10781 + $totadults += $rr['adults'];
10782 + $totchildren += $rr['children'];
10783 + $room_names[] = $rr['room_name'];
10784 + if ($row['split_stay']) {
10785 + // do not sum guests in case of split stay booking
10786 + $totadults = $rr['adults'];
10787 + $totchildren = $rr['children'];
10788 + }
10789 + }
10790 +
10791 + if (!empty($pstay_date) && !empty($pidroom) && $pstatus == 'any') {
10792 + // make sure we have fetched a reservation for the correct room (in case of cancellations included)
10793 + if (!in_array($pidroom, $rids_involved)) {
10794 + $is_out_of_scope = true;
10795 + if ($psharedcal && count($bookings) === 1) {
10796 + $is_out_of_scope = ($row['id'] != $bookings[0]);
10797 + }
10798 + if ($is_out_of_scope) {
10799 + // out of scope reservation, unset it and go to the next one
10800 + unset($booking_infos[$k]);
10801 + continue;
10802 + }
10803 + }
10804 + }
10805 +
10806 + // included meal plans to be displayed in case of single-room booking
10807 + $included_meals = [];
10808 + $rplan_name = '';
10809 + if (count($rooms) === 1) {
10810 + // rate plan name and ID, if any
10811 + $active_rplan_id = 0;
10812 + if (!empty($rooms[0]['otarplan'])) {
10813 + $rplan_name = $rooms[0]['otarplan'];
10814 + } else {
10815 + list($rplan_name, $active_rplan_id) = VBOMealplanManager::getInstance()->getPriceData($rooms[0]['idtar']);
10816 + }
10817 +
10818 + // find the included meals
10819 + if (!empty($rooms[0]['meals'])) {
10820 + // display included meals defined at room-reservation record
10821 + $included_meals = VBOMealplanManager::getInstance()->roomRateIncludedMeals($rooms[0]);
10822 + } else {
10823 + // fetch default included meals in the selected rate plan
10824 + $included_meals = $active_rplan_id ? VBOMealplanManager::getInstance()->ratePlanIncludedMeals($active_rplan_id) : [];
10825 + }
10826 + if (!$included_meals && empty($row['meals']) && !empty($row['idorderota']) && !empty($row['channel']) && !empty($row['custdata'])) {
10827 + // attempt to fetch the included meal plans from the raw customer data or OTA reservation and room
10828 + $included_meals = VBOMealplanManager::getInstance()->otaDataIncludedMeals($row, $rooms[0]);
10829 + }
10830 + }
10831 +
10832 + if ($included_meals) {
10833 + $short_incl_meals = [];
10834 + foreach ($included_meals as $meal_enum => $meal_name) {
10835 + $short_incl_meals[] = $short_meal_enums[$meal_enum];
10836 + }
10837 + $booking_infos[$k]['meals_included'] = $short_incl_meals;
10838 + }
10839 +
10840 + $booking_infos[$k]['rateplan_name'] = $rplan_name;
10841 + $booking_infos[$k]['currency_symb'] = $currencysymb;
10842 + if ($row['status'] == 'confirmed') {
10843 + $booking_infos[$k]['status_lbl'] = JText::translate('VBCONFIRMED');
10844 + if ($row['checkout'] < $current_ts) {
10845 + $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSOUT');
10846 + } elseif ($row['checkin'] < $current_ts && $row['checkout'] > $current_ts) {
10847 + if ($row['checked'] == 1) {
10848 + $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSIN');
10849 + } elseif ($row['checked'] == -1) {
10850 + $booking_infos[$k]['status_lbl'] = JText::translate('VBOCHECKEDSTATUSNOS');
10851 + }
10852 + }
10853 + } elseif ($row['status'] == 'standby') {
10854 + $booking_infos[$k]['status_lbl'] = JText::translate('VBSTANDBY');
10855 + } elseif ($row['status'] == 'cancelled') {
10856 + $booking_infos[$k]['status_lbl'] = JText::translate('VBCANCELLED');
10857 + } else {
10858 + $booking_infos[$k]['status_lbl'] = $row['status'];
10859 + }
10860 + $booking_infos[$k]['colortag'] = VikBooking::applyBookingColorTag($row);
10861 + if ($booking_infos[$k]['colortag']) {
10862 + $booking_infos[$k]['colortag']['name'] = JText::translate($booking_infos[$k]['colortag']['name']);
10863 + }
10864 + $booking_infos[$k]['room_names'] = implode(', ', $room_names);
10865 + $booking_infos[$k]['tot_adults'] = $totadults;
10866 + $booking_infos[$k]['tot_children'] = $totchildren;
10867 + $booking_infos[$k]['format_tot'] = VikBooking::numberFormat($row['total']);
10868 + $booking_infos[$k]['format_totpaid'] = VikBooking::numberFormat($row['totpaid']);
10869 +
10870 + // room indexes
10871 + $rindexes = [];
10872 + $av_room_indexes = [];
10873 + $used_indexes_map = [];
10874 + $sub_units_data = [];
10875 + $optindexes = [];
10876 + $subroomdata = !empty($psubroom) ? explode('-', $psubroom) : array();
10877 + $missing_index = false;
10878 + foreach ($rooms as $kor => $or) {
10879 + if ($row['status'] != "confirmed" || $row['closure'] || empty($or['params'])) {
10880 + // cannot build room indexes data
10881 + continue;
10882 + }
10883 +
10884 + $room_params = json_decode($or['params'], true);
10885 + if (!is_array($room_params) || empty($room_params['features']) || !is_array($room_params['features'])) {
10886 + // no distinctive features information
10887 + continue;
10888 + }
10889 +
10890 + if (!strlen($or['roomindex'])) {
10891 + // turn flag on for missing index when room does support them
10892 + $missing_index = true;
10893 + // build array with available room indexes
10894 + $av_indexes = [];
10895 + $unavailable_indexes = VikBooking::getRoomUnitNumsUnavailable($row, $or['idroom']);
10896 + foreach ($room_params['features'] as $rind => $rfeatures) {
10897 + if (in_array($rind, $unavailable_indexes) || (isset($used_indexes_map[$or['idroom']]) && in_array($rind, $used_indexes_map[$or['idroom']]))) {
10898 + continue;
10899 + }
10900 + foreach ($rfeatures as $fname => $fval) {
10901 + if ($fval) {
10902 + $av_indexes[$rind] = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
10903 + break;
10904 + }
10905 + }
10906 + }
10907 + if ($av_indexes) {
10908 + // push available indexes for this room
10909 + $av_room_indexes[$kor] = [
10910 + 'rid' => $or['idroom'],
10911 + 'name' => $or['room_name'],
10912 + 'list' => $av_indexes,
10913 + ];
10914 + }
10915 + // do not proceed any further
10916 + continue;
10917 + }
10918 +
10919 + // parse distinctive features
10920 + foreach ($room_params['features'] as $rind => $rfeatures) {
10921 + if ($rind != $or['roomindex']) {
10922 + continue;
10923 + }
10924 + $ind_str = '';
10925 + $ind_str_short = '';
10926 + foreach ($rfeatures as $fname => $fval) {
10927 + if (strlen($fval)) {
10928 + $ind_str = '#' . $rind . ' - ' . JText::translate($fname) . ': ' . $fval;
10929 + $ind_str_short = $fval;
10930 + break;
10931 + }
10932 + }
10933 + if (!isset($rindexes[$or['room_name']])) {
10934 + $rindexes[$or['room_name']] = $ind_str;
10935 + $sub_units_data[$or['room_name']] = $ind_str_short;
10936 + } else {
10937 + $rindexes[$or['room_name']] .= ', ' . $ind_str;
10938 + $sub_units_data[$or['room_name']] .= ', ' . $ind_str_short;
10939 + }
10940 + break;
10941 + }
10942 +
10943 + // build options to switch sub-unit index
10944 + if (count($subroomdata) && !count($optindexes) && $or['idroom'] == (int)$subroomdata[0]) {
10945 + // build the options for switching the room index for this room
10946 + foreach ($room_params['features'] as $rind => $rfeatures) {
10947 + foreach ($rfeatures as $fname => $fval) {
10948 + if (strlen((string)$fval)) {
10949 + $optindexes[] = '<option value="'.$rind.'"'.($rind == (int)$subroomdata[1] ? ' selected="selected"' : '').'>#'.$rind.' - '.JText::translate($fname).': '.$fval.'</option>';
10950 + break;
10951 + }
10952 + }
10953 + }
10954 + }
10955 + }
10956 +
10957 + if ($rindexes) {
10958 + $booking_infos[$k]['rindexes'] = $rindexes;
10959 + $booking_infos[$k]['sub_units_data'] = $sub_units_data;
10960 + }
10961 +
10962 + if ($optindexes) {
10963 + $booking_infos[$k]['optindexes'] = $optindexes;
10964 + }
10965 +
10966 + if ($missing_index && $av_room_indexes) {
10967 + $booking_infos[$k]['av_room_indexes'] = $av_room_indexes;
10968 + }
10969 +
10970 + // include flag for missing room index
10971 + $booking_infos[$k]['missing_index'] = $missing_index;
10972 +
10973 + // channel provenience and small logo URL
10974 + $ota_logo_img = JText::translate('VBORDFROMSITE');
10975 + $booking_avatar_src = null;
10976 + $booking_avatar_alt = null;
10977 + if (!empty($row['channel'])) {
10978 + $channelparts = explode('_', $row['channel']);
10979 + $otachannel = array_key_exists(1, $channelparts) && strlen($channelparts[1]) > 0 ? $channelparts[1] : ucwords($channelparts[0]);
10980 + $ota_logo_img = VikBooking::getVcmChannelsLogo($row['channel']);
10981 + if ($ota_logo_img === false) {
10982 + $ota_logo_img = $otachannel;
10983 + } else {
10984 + $ota_logo_img = '<img src="'.$ota_logo_img.'" class="vbo-channelimg-small"/>';
10985 + }
10986 + $logo_helper = VikBooking::getVcmChannelsLogo($row['channel'], $get_istance = true);
10987 + if ($logo_helper !== false) {
10988 + $booking_avatar_src = $logo_helper->getSmallLogoURL();
10989 + $booking_avatar_alt = $logo_helper->provenience;
10990 + }
10991 + }
10992 + $booking_infos[$k]['channelimg'] = $ota_logo_img;
10993 + $booking_infos[$k]['avatar_src'] = $booking_avatar_src;
10994 + $booking_infos[$k]['avatar_alt'] = $booking_avatar_alt;
10995 +
10996 + // Customer Details
10997 + $custdata = $row['custdata'];
10998 + $custdata_parts = explode("\n", $row['custdata']);
10999 + if (count($custdata_parts) > 2 && strpos($custdata_parts[0], ':') !== false && strpos($custdata_parts[1], ':') !== false) {
11000 + //get the first two fields
11001 + $custvalues = [];
11002 + foreach ($custdata_parts as $custdet) {
11003 + if (strlen($custdet) < 1) {
11004 + continue;
11005 + }
11006 + $custdet_parts = explode(':', $custdet);
11007 + if (count($custdet_parts) >= 2) {
11008 + unset($custdet_parts[0]);
11009 + array_push($custvalues, trim(implode(':', $custdet_parts)));
11010 + }
11011 + if (count($custvalues) > 1) {
11012 + break;
11013 + }
11014 + }
11015 + if (count($custvalues) > 1) {
11016 + $custdata = implode(' ', $custvalues);
11017 + }
11018 + }
11019 + if (strlen($custdata) > 45) {
11020 + $custdata = (function_exists('mb_substr') ? mb_substr($custdata, 0, 45, 'UTF-8') : substr($custdata, 0, 45)) . " ...";
11021 + }
11022 +
11023 + // customer record details
11024 + $customer = [];
11025 + $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'];
11026 + $dbo->setQuery($q, 0, 1);
11027 + $dbo->execute();
11028 + if ($dbo->getNumRows()) {
11029 + $customer = $dbo->loadAssoc();
11030 + if (!empty($customer['first_name'])) {
11031 + $custdata = $customer['first_name'].' '.$customer['last_name'];
11032 + if (!empty($customer['country'])) {
11033 + if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$customer['country'].'.png')) {
11034 + $custdata .= '<img src="'.VBO_ADMIN_URI.'resources/countries/'.$customer['country'].'.png'.'" title="'.htmlspecialchars($customer['country']).'" class="vbo-country-flag vbo-country-flag-left"/>';
11035 + }
11036 + }
11037 + }
11038 + }
11039 + $booking_infos[$k]['customer'] = $customer;
11040 +
11041 + // check if a profile picture is available for the customer
11042 + if (!empty($customer['pic'])) {
11043 + $booking_avatar_src = strpos($customer['pic'], 'http') === 0 ? $customer['pic'] : VBO_SITE_URI . 'resources/uploads/' . $customer['pic'];
11044 + $booking_avatar_alt = basename($booking_avatar_src);
11045 + $booking_infos[$k]['avatar_src'] = $booking_avatar_src;
11046 + $booking_infos[$k]['avatar_alt'] = $booking_avatar_alt;
11047 + }
11048 +
11049 + // whether this is a closure
11050 + $booking_infos[$k]['closure'] = (int)$row['closure'];
11051 + $booking_infos[$k]['closure_txt'] = $row['closure'] ? JText::translate('VBDBTEXTROOMCLOSED') : null;
11052 +
11053 + // short customer information
11054 + $custdata = JText::translate('VBDBTEXTROOMCLOSED') == $row['custdata'] ? '<span class="vbordersroomclosed">'.JText::translate('VBDBTEXTROOMCLOSED').'</span>' : $custdata;
11055 + $booking_infos[$k]['cinfo'] = $custdata;
11056 +
11057 + // formatted dates
11058 + $booking_infos[$k]['ts'] = date(str_replace("/", $datesep, $df).' H:i', $row['ts']);
11059 + $booking_infos[$k]['checkin'] = date(str_replace("/", $datesep, $df).' H:i', $row['checkin']);
11060 + $booking_infos[$k]['checkout'] = date(str_replace("/", $datesep, $df).' H:i', $row['checkout']);
11061 +
11062 + // short booking date, check-in, check-out date format
11063 + $stay_info_in = getdate($row['checkin']);
11064 + $stay_info_out = getdate($row['checkout']);
11065 + $str_checkin = date('d', $row['checkin']);
11066 + $str_checkin .= $stay_info_in['mon'] != $stay_info_out['mon'] ? ' ' . VikBooking::sayMonth($stay_info_in['mon'], $short = true) : '';
11067 + $str_checkout = date('d', $row['checkout']) . ' ' . VikBooking::sayMonth($stay_info_out['mon'], $short = true);
11068 + if ($stay_info_in['year'] != $stay_info_out['year'] || $stay_info_in['year'] != $current_y || $stay_info_out['year'] != $current_y) {
11069 + $str_checkout .= ' ' . $stay_info_out['year'];
11070 + }
11071 + $booking_infos[$k]['checkin_short'] = $str_checkin;
11072 + $booking_infos[$k]['checkout_short'] = $str_checkout;
11073 + $booking_infos[$k]['book_date'] = date(str_replace("/", $datesep, $df), $row['ts']);
11074 + $booking_infos[$k]['book_time'] = date('H:i', $row['ts']);
11075 + }
11076 +
11077 + if (!$booking_infos) {
11078 + if (!empty($pidroom) && $psharedcal) {
11079 + // when this flag is enabled, we are excluding bookings not made directly
11080 + // for the given room type ID, hence we may get an empty list due to shared calendars
11081 + VBOHttpDocument::getInstance()->close(500, 'No bookings made directly for this room-type.');
11082 + }
11083 + // output the error
11084 + VBOHttpDocument::getInstance()->close(500, '2 - ' . JText::translate('VBOVWGETBKERRMISSDATA'));
11085 + }
11086 +
11087 + // output the JSON encoded response and exit
11088 + VBOHttpDocument::getInstance()->json($booking_infos);
11089 + }
11090 +
11091 + /**
11092 + * AJAX endpoint to switch a booking room index.
11093 + *
11094 + * @return void
11095 + *
11096 + * @since 1.18.2 (J) - 1.8.2 (WP) method refactored.
11097 + * @since 1.18.7 (J) - 1.8.7 (WP) introduced history update.
11098 + */
11099 + public function switchRoomIndex()
11100 + {
11101 + if (!JSession::checkToken()) {
11102 + VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
11103 + }
11104 +
11105 + $app = JFactory::getApplication();
11106 + $dbo = JFactory::getDbo();
11107 +
11108 + $bid = $app->input->getInt('bid', 0);
11109 + $rid = $app->input->getInt('rid', 0);
11110 + $old_rindex = $app->input->getInt('old_rindex', 0);
11111 + $new_rindex = $app->input->getInt('new_rindex', 0);
11112 + $is_tmp_row = $app->input->getBool('is_tmp_row', false);
11113 + $is_from_tmp_row = $app->input->getBool('is_from_tmp_row', false);
11114 +
11115 + if (empty($bid) || empty($rid) || (empty($old_rindex) && !$is_from_tmp_row) || (empty($new_rindex) && !$is_tmp_row) || $new_rindex < 0) {
11116 + // abort for missing or invalid room indexes
11117 + VBOHttpDocument::getInstance($app)->close(200, 'e4j.error.#1 Missing Data');
11118 + }
11119 +
11120 + // fetch the current booking room record
11121 + $dbo->setQuery(
11122 + $dbo->getQuery(true)
11123 + ->select('*')
11124 + ->from($dbo->qn('#__vikbooking_ordersrooms'))
11125 + ->where($dbo->qn('idorder') . ' = ' . $bid)
11126 + ->where($dbo->qn('idroom') . ' = ' . $rid)
11127 + ->where($dbo->qn('roomindex') . ((empty($old_rindex) || $old_rindex == -1) && $is_from_tmp_row ? ' IS NULL' : ' = ' . $old_rindex))
11128 + ->order($dbo->qn('id') . ' ASC')
11129 + );
11130 + $roomRow = $dbo->loadAssoc();
11131 +
11132 + if (!$roomRow) {
11133 + // abort for record not found
11134 + VBOHttpDocument::getInstance($app)->close(200, 'e4j.error.#2 Record not found');
11135 + }
11136 +
11137 + // load booking and booking rooms data for the history before updating
11138 + $booking = VikBooking::getBookingInfoFromID($bid);
11139 + $prev_booking_rooms = VikBooking::loadOrdersRoomsData($bid);
11140 + $current_booking_rooms = $prev_booking_rooms;
11141 + // update new room index for current booking rooms
11142 + foreach ($current_booking_rooms as $k => $booking_room) {
11143 + if ($booking_room['id'] == $roomRow['id']) {
11144 + // update new room index
11145 + $current_booking_rooms[$k]['roomindex'] = empty($new_rindex) && $is_tmp_row ? null : $new_rindex;
11146 + break;
11147 + }
11148 + }
11149 +
11150 + // update booking room record by switching sub-unit
11151 + $dbo->setQuery(
11152 + $dbo->getQuery(true)
11153 + ->update($dbo->qn('#__vikbooking_ordersrooms'))
11154 + ->set($dbo->qn('roomindex') . ' = ' . (empty($new_rindex) && $is_tmp_row ? 'NULL' : $new_rindex))
11155 + ->where($dbo->qn('id') . ' = ' . (int) $roomRow['id'])
11156 + );
11157 + $dbo->execute();
11158 +
11159 + // update history record by setting the proper bookings data
11160 + $user = JFactory::getUser();
11161 + VikBooking::getBookingHistoryInstance($bid)
11162 + ->setPrevBooking(array_merge($booking, ['rooms_info' => $prev_booking_rooms]))
11163 + ->setBookingData($booking, $current_booking_rooms)
11164 + ->store(
11165 + 'MB',
11166 + sprintf(
11167 + '%s [%d → %d]',
11168 + JText::translate('VBODEFAULTDISTFEATUREONE'),
11169 + (int) $roomRow['roomindex'],
11170 + (int) (empty($new_rindex) && $is_tmp_row ? 0 : $new_rindex)
11171 + ) . " ({$user->name})"
11172 + );
11173 +
11174 + // process completed
11175 + VBOHttpDocument::getInstance($app)->close(200, 'e4j.ok');
11176 + }
11177 +
11178 + public function searchcustomer()
11179 + {
11180 + // to be called via ajax
11181 + $dbo = JFactory::getDbo();
11182 +
11183 + $kw = VikRequest::getString('kw', '', 'request');
11184 + $nopin = VikRequest::getInt('nopin', '', 'request');
11185 + $email = VikRequest::getInt('email', 0, 'request');
11186 + $selector = VikRequest::getString('selector', 'vbo-custsearchres-entry', 'request');
11187 + $no_script = VikRequest::getInt('no_script', 0, 'request');
11188 +
11189 + if (!strlen($kw)) {
11190 + VBOHttpDocument::getInstance()->close(200, '');
11191 + }
11192 +
11193 + if ($nopin > 0) {
11194 + //page all bookings
11195 + $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;";
11196 + } elseif ($email > 0) {
11197 + // page calendar for checking if an email exists
11198 + $q = "SELECT `first_name`, `last_name`, `email` FROM `#__vikbooking_customers` WHERE `email`=".$dbo->quote($kw).";";
11199 + } else {
11200 + //page calendar
11201 + $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;";
11202 + }
11203 + $dbo->setQuery($q);
11204 + $customers = $dbo->loadAssocList();
11205 +
11206 + if (!$customers) {
11207 + VBOHttpDocument::getInstance()->close(200, '');
11208 + }
11209 +
11210 + if ($email > 0) {
11211 + VBOHttpDocument::getInstance()->json($customers[0]);
11212 + }
11213 +
11214 + $cust_old_fields = array();
11215 + $cstring_search = '<div class="vbo-custsearchres-inner">' . "\n";
11216 + foreach ($customers as $k => $v) {
11217 + $cstring_search .= '<div class="' . $selector . '" data-custid="' . (int) $v['id'] . '" data-email="' . htmlspecialchars($v['email']) . '" data-phone="' . htmlspecialchars($v['phone']) . '" data-country="' . htmlspecialchars($v['country']) . '" data-pin="' . htmlspecialchars($v['pin']) . '" data-firstname="' . htmlspecialchars($v['first_name']) . '" data-lastname="' . htmlspecialchars($v['last_name']) . '">'."\n";
11218 + $cstring_search .= '<span class="vbo-custsearchres-cflag">';
11219 + if (!empty($v['pic'])) {
11220 + $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";
11221 + } elseif (is_file(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$v['country'].'.png')) {
11222 + $cstring_search .= '<img src="'.VBO_ADMIN_URI.'resources/countries/'.$v['country'].'.png'.'" title="'.htmlspecialchars($v['country']).'" class="vbo-country-flag"/>'."\n";
11223 + } else {
11224 + $cstring_search .= '<i class="' . VikBookingIcons::i('globe') . '"></i>';
11225 + }
11226 + $cstring_search .= '</span>';
11227 + $cstring_search .= '<span class="vbo-custsearchres-name" title="'.htmlspecialchars($v['email']).'">'.$v['first_name'].' '.$v['last_name'].'</span>'."\n";
11228 + if (!($nopin > 0)) {
11229 + $cstring_search .= '<span class="vbo-custsearchres-pin">'.$v['pin'].'</span>'."\n";
11230 + }
11231 + $cstring_search .= '</div>'."\n";
11232 + if (!empty($v['cfields'])) {
11233 + $oldfields = json_decode($v['cfields'], true);
11234 + if (is_array($oldfields) && count($oldfields)) {
11235 + $cust_old_fields[$v['id']] = $oldfields;
11236 + }
11237 + }
11238 + }
11239 + $cstring_search .= '</div>'."\n";
11240 +
11241 + /**
11242 + * Add the necessary JS code for the arrow navigation.
11243 + */
11244 + $cstring_search_js = '<script type="text/javascript">';
11245 + $cstring_search_js .= '
11246 +var vboCust = jQuery(".' . $selector . '");
11247 +var vboCustSelected = null;
11248 +var vboCustomerNavigationFn = (e) => {
11249 + if (e.which === 40) {
11250 + if (vboCustSelected) {
11251 + vboCustSelected.removeClass("' . $selector . '-highligthed");
11252 + next = vboCustSelected.next();
11253 + if (next.length > 0) {
11254 + vboCustSelected = next.addClass("' . $selector . '-highligthed");
11255 + } else {
11256 + vboCustSelected = vboCust.eq(0).addClass("' . $selector . '-highligthed");
11257 + }
11258 + } else {
11259 + vboCustSelected = vboCust.eq(0).addClass("' . $selector . '-highligthed");
11260 + }
11261 + } else if (e.which === 38) {
11262 + if (vboCustSelected) {
11263 + vboCustSelected.removeClass("' . $selector . '-highligthed");
11264 + next = vboCustSelected.prev();
11265 + if (next.length > 0) {
11266 + vboCustSelected = next.addClass("' . $selector . '-highligthed");
11267 + } else {
11268 + vboCustSelected = vboCust.last().addClass("' . $selector . '-highligthed");
11269 + }
11270 + } else {
11271 + vboCustSelected = vboCust.last().addClass("' . $selector . '-highligthed");
11272 + }
11273 + } else if (e.which === 13) {
11274 + if (vboCustSelected) {
11275 + vboCustSelected.trigger("click");
11276 + }
11277 + }
11278 +};
11279 +jQuery(window).off("keydown", vboCustomerNavigationFn);
11280 +jQuery(window).keydown(vboCustomerNavigationFn);
11281 +document.addEventListener("vbo-search-customers-navigation-dismissed", (e) => {
11282 + jQuery(window).off("keydown", vboCustomerNavigationFn);
11283 +})
11284 +jQuery(".' . $selector . '").off("hover");
11285 +jQuery(".' . $selector . '").hover(function() {
11286 + if (vboCustSelected) {
11287 + vboCustSelected.removeClass("' . $selector . '-highligthed");
11288 + vboCustSelected = null;
11289 + }
11290 + vboCustSelected = jQuery(this).addClass("' . $selector . '-highligthed");
11291 +}, function() {
11292 + if (vboCustSelected) {
11293 + vboCustSelected.removeClass("' . $selector . '-highligthed");
11294 + vboCustSelected = null;
11295 + }
11296 + jQuery(this).removeClass("' . $selector . '-highligthed");
11297 +});';
11298 + $cstring_search_js .= '</script>';
11299 +
11300 + if (!$no_script) {
11301 + // append JS
11302 + $cstring_search .= $cstring_search_js;
11303 + }
11304 +
11305 + VBOHttpDocument::getInstance()->json([($nopin > 0 ? '' : $cust_old_fields), $cstring_search]);
11306 + }
11307 +
11308 + public function sharesignaturelink() {
11309 + //to be called via ajax
11310 + $dbo = JFactory::getDBO();
11311 + $response = array(
11312 + 'status' => 0,
11313 + 'error' => 'Generic Error'
11314 + );
11315 + $pbid = VikRequest::getInt('bid', '', 'request');
11316 + $phow = VikRequest::getString('how', '', 'request');
11317 + $pto = VikRequest::getString('to', '', 'request');
11318 + $pcustomer = VikRequest::getInt('customer', '', 'request');
11319 + $cpin = VikBooking::getCPinIstance();
11320 + $customer_info = $cpin->getCustomerByID($pcustomer);
11321 + if (!empty($pbid) && !empty($phow) && !empty($pto) && count($customer_info) > 0) {
11322 + $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$pbid." AND `status`='confirmed' AND `checked` > 0;";
11323 + $dbo->setQuery($q);
11324 + $dbo->execute();
11325 + if ($dbo->getNumRows() > 0) {
11326 + $row = $dbo->loadAssoc();
11327 +
11328 + $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'];
11329 + if (VBOPlatformDetection::isWordPress()) {
11330 + /**
11331 + * @wponly Rewrite URI for front-end signature
11332 + */
11333 + $share_link = str_replace(JUri::root(), '', $share_link);
11334 + $model = JModel::getInstance('vikbooking', 'shortcodes');
11335 + $itemid = $model->all('post_id', $full = true);
11336 + if (count($itemid)) {
11337 + $share_link = JRoute::rewrite($share_link . "&Itemid={$itemid[0]->post_id}", false);
11338 + }
11339 + } else {
11340 + /**
11341 + * @joomlaonly
11342 + */
11343 + $best_menuitem_id = VikBooking::findProperItemIdType(['vikbooking', 'booking'], $row['lang']);
11344 + if ($best_menuitem_id) {
11345 + $share_base = str_replace(JUri::root(), '', $share_link);
11346 + $share_link = VikBooking::externalroute($share_base, $xhtml = false, $best_menuitem_id);
11347 + }
11348 + }
11349 +
11350 + $share_message = JText::sprintf('VBOSIGNSHAREMESSAGE', ltrim($customer_info['first_name'].' '.$customer_info['last_name']), $share_link, VikBooking::getFrontTitle());
11351 + if ($phow == 'email') {
11352 + $sender = VikBooking::getSenderMail();
11353 + $vbo_app = VikBooking::getVboApplication();
11354 + $vbo_app->sendMail($sender, $sender, $pto, $sender, JText::translate('VBOSIGNSHARESUBJECT'), $share_message, false);
11355 + $response['status'] = 1;
11356 + } elseif ($phow == 'sms') {
11357 + $share_message = JText::sprintf('VBOSIGNSHAREMESSAGESMS', ltrim($customer_info['first_name'].' '.$customer_info['last_name']), $share_link, VikBooking::getFrontTitle());
11358 + $sms_api = VikBooking::getSMSAPIClass();
11359 + $sms_api_params = VikBooking::getSMSParams();
11360 + if (!empty($sms_api) && file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api) && !empty($sms_api_params)) {
11361 + require_once(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'smsapi'.DIRECTORY_SEPARATOR.$sms_api);
11362 + $sms_obj = new VikSmsApi(array(), $sms_api_params);
11363 + $response_obj = $sms_obj->sendMessage($pto, $share_message);
11364 + if ($sms_obj->validateResponse($response_obj)) {
11365 + $response['status'] = 1;
11366 + } else {
11367 + $response['error'] = $sms_obj->getLog();
11368 + }
11369 + } else {
11370 + $response['error'] = 'No SMS Provider Configured';
11371 + }
11372 + } else {
11373 + $response['error'] = 'Invalid Sending Method';
11374 + }
11375 + } else {
11376 + $response['error'] = 'Invalid Booking ID';
11377 + }
11378 + } else {
11379 + $response['error'] = 'Empty values';
11380 + }
11381 +
11382 + echo json_encode($response);
11383 + exit;
11384 + }
11385 +
11386 + public function dayselectioncount() {
11387 + //to be called via ajax
11388 + $tsinit = VikRequest::getString('dinit', '', 'request');
11389 + $tsend = VikRequest::getString('dend', '', 'request');
11390 + if (strlen($tsinit) > 0 && strlen($tsend) > 0) {
11391 + $ptsinit=VikBooking::getDateTimestamp($tsinit, '0', '0');
11392 + $ptsend=VikBooking::getDateTimestamp($tsend, '23', '59');
11393 + $diff = $ptsend - $ptsinit;
11394 + if ($diff >= 172800) {
11395 + $datef = VikBooking::getDateFormat(true);
11396 + if ($datef=="%d/%m/%Y") {
11397 + $df = 'd-m-Y';
11398 + } else {
11399 + $df = 'Y-m-d';
11400 + }
11401 + //minimum 2 days for excluding some days
11402 + $daysdiff = floor($diff / 86400);
11403 + $infoinit = getdate($ptsinit);
11404 + $select = '';
11405 + $select .= '<div style="display: inline-block;"><select name="excludeday[]" multiple="multiple" size="'.($daysdiff > 8 ? 8 : $daysdiff).'" id="vboexclusion">';
11406 + for($i = 0; $i <= $daysdiff; $i++) {
11407 + $ts = $i > 0 ? mktime(0, 0, 0, $infoinit['mon'], ((int)$infoinit['mday'] + $i), $infoinit['year']) : $ptsinit;
11408 + $infots = getdate($ts);
11409 + $optval = $infots['mon'].'-'.$infots['mday'].'-'.$infots['year'];
11410 + $select .= '<option value="'.$optval.'">'.date($df, $ts).'</option>';
11411 + }
11412 + $select .= '</select></div>';
11413 + //excluded days of the week
11414 + if ($daysdiff >= 14) {
11415 + $select .= '<div style="display: inline-block; margin-left: 40px;"><select name="excludewdays[]" multiple="multiple" size="8" id="excludewdays" onchange="vboExcludeWDays();">';
11416 + $select .= '<optgroup label="'.JText::translate('VBOEXCLWEEKD').'">';
11417 + $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>';
11418 + $select .= '</optgroup>';
11419 + $select .= '</select></div>';
11420 + }
11421 + //
11422 + echo $select;
11423 + } else {
11424 + echo '';
11425 + }
11426 + } else {
11427 + echo '';
11428 + }
11429 + exit;
11430 + }
11431 +
11432 + public function createcheckindoc()
11433 + {
11434 + if (!JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking')) {
11435 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
11436 + }
11437 +
11438 + $cid = VikRequest::getVar('cid', array(0));
11439 + $id = $cid[0];
11440 +
11441 + $dbo = JFactory::getDBO();
11442 + $mainframe = JFactory::getApplication();
11443 + $vbo_tn = VikBooking::getTranslator();
11444 + $lang = JFactory::getLanguage();
11445 + $ptmpl = VikRequest::getString('tmpl', '', 'request');
11446 + $psignature = VikRequest::getString('signature', '', 'request', VIKREQUEST_ALLOWRAW);
11447 + $ppad_width = VikRequest::getInt('pad_width', '', 'request');
11448 + $ppad_ratio = VikRequest::getInt('pad_ratio', '', 'request');
11449 + $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$id." AND `status`='confirmed' AND `checked` > 0;";
11450 + $dbo->setQuery($q);
11451 + $row = $dbo->loadAssoc();
11452 + if (!$row) {
11453 + $mainframe->redirect('index.php');
11454 + exit;
11455 + }
11456 + if (!empty($row['lang'])) {
11457 + if ($lang->getTag() != $row['lang']) {
11458 + if (VBOPlatformDetection::isWordPress()) {
11459 + $lang->load('com_vikbooking', VIKBOOKING_LANG, $row['lang'], true);
11460 + } else {
11461 + $lang->load('com_vikbooking', JPATH_SITE, $row['lang'], true);
11462 + $lang->load('com_vikbooking', JPATH_ADMINISTRATOR, $row['lang'], true);
11463 + $lang->load('joomla', JPATH_SITE, $row['lang'], true);
11464 + $lang->load('joomla', JPATH_ADMINISTRATOR, $row['lang'], true);
11465 + }
11466 + }
11467 + if ($vbo_tn->getDefaultLang() != $row['lang']) {
11468 + // force the translation to start because contents should be translated
11469 + $vbo_tn::$force_tolang = $row['lang'];
11470 + }
11471 + }
11472 + $customer = array();
11473 + $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'].";";
11474 + $dbo->setQuery($q);
11475 + $dbo->execute();
11476 + if ($dbo->getNumRows() > 0) {
11477 + $customer = $dbo->loadAssoc();
11478 + if (!empty($customer['country'])) {
11479 + if (file_exists(VBO_ADMIN_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'countries'.DIRECTORY_SEPARATOR.$customer['country'].'.png')) {
11480 + $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"/>';
11481 + }
11482 + }
11483 + }
11484 + if (!(count($customer) > 0)) {
11485 + VikError::raiseWarning('', JText::translate('VBOCHECKINERRNOCUSTOMER'));
11486 + $mainframe->redirect('index.php?option=com_vikbooking&task=newcustomer&checkin=1&bid='.$row['id'].($ptmpl == 'component' ? '&tmpl=component' : ''));
11487 + exit;
11488 + }
11489 + $customer['pax_data'] = !empty($customer['pax_data']) ? json_decode($customer['pax_data'], true) : array();
11490 + //check if the signature has been submitted
11491 + $signature_data = '';
11492 + $cont_type = '';
11493 + if (!empty($psignature)) {
11494 + //check whether the format is accepted
11495 + if (strpos($psignature, 'image/png') !== false || strpos($psignature, 'image/jpeg') !== false || strpos($psignature, 'image/svg') !== false) {
11496 + $parts = explode(';base64,', $psignature);
11497 + $cont_type_parts = explode('image/', $parts[0]);
11498 + $cont_type = $cont_type_parts[1];
11499 + if (!empty($parts[1])) {
11500 + $signature_data = base64_decode($parts[1]);
11501 + }
11502 + }
11503 + }
11504 + if (!empty($signature_data)) {
11505 + //write file
11506 + $sign_fname = $row['id'].'_'.$row['sid'].'_'.$customer['id'].'.'.$cont_type;
11507 + $filepath = VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'resources' . DIRECTORY_SEPARATOR . 'idscans' . DIRECTORY_SEPARATOR . $sign_fname;
11508 + $fp = fopen($filepath, 'w+');
11509 + $bytes = fwrite($fp, $signature_data);
11510 + fclose($fp);
11511 + if ($bytes !== false && $bytes > 0) {
11512 + //update the signature in the DB
11513 + $q = "UPDATE `#__vikbooking_customers_orders` SET `signature`=".$dbo->quote($sign_fname)." WHERE `idorder`=".(int)$row['id'].";";
11514 + $dbo->setQuery($q);
11515 + $dbo->execute();
11516 + $customer['signature'] = $sign_fname;
11517 + //resize image for screens with high resolution
11518 + if ($ppad_ratio > 1) {
11519 + $new_width = floor(($ppad_width / 2));
11520 + $creativik = new vikResizer();
11521 + $creativik->proportionalImage($filepath, $filepath, $new_width, $new_width);
11522 + } else {
11523 + /**
11524 + * @wponly - trigger files mirroring
11525 + */
11526 + VikBookingLoader::import('update.manager');
11527 + VikBookingUpdateManager::triggerUploadBackup($filepath);
11528 + //
11529 + }
11530 + //
11531 + } else {
11532 + VikError::raiseWarning('', JText::translate('VBOERRSTORESIGNFILE'));
11533 + }
11534 + }
11535 + //
11536 + //generate PDF for check-in document by parsing the apposite template file
11537 + $booking_rooms = array();
11538 + $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'].";";
11539 + $dbo->setQuery($q);
11540 + $dbo->execute();
11541 + if ($dbo->getNumRows() > 0) {
11542 + $booking_rooms = $dbo->loadAssocList();
11543 + if (!empty($row['lang'])) {
11544 + $vbo_tn->translateContents($booking_rooms, '#__vikbooking_rooms', array('id' => 'idroom', 'room_name' => 'name'), array(), $row['lang']);
11545 + }
11546 + }
11547 + if (!class_exists('TCPDF')) {
11548 + require_once(VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "tcpdf" . DIRECTORY_SEPARATOR . 'tcpdf.php');
11549 + }
11550 + $usepdffont = is_file(VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "tcpdf" . DIRECTORY_SEPARATOR . "fonts" . DIRECTORY_SEPARATOR . "dejavusans.php") ? 'dejavusans' : 'helvetica';
11551 +
11552 + /**
11553 + * Trigger event to allow third party plugins to return a specific font name.
11554 + *
11555 + * @since 1.16.0 (J) - 1.6.0 (WP)
11556 + */
11557 + $custom_pdf_font = VBOFactory::getPlatform()->getDispatcher()->filter('onGetPdfFontNameVikBooking', [$usepdffont]);
11558 + if (is_array($custom_pdf_font) && !empty($custom_pdf_font[0])) {
11559 + $usepdffont = $custom_pdf_font[0];
11560 + }
11561 +
11562 + list($checkintpl, $pdfparams) = VikBooking::loadCheckinDocTmpl($row, $booking_rooms, $customer);
11563 + $checkin_body = VikBooking::parseCheckinDocTemplate($checkintpl, $row, $booking_rooms, $customer);
11564 +
11565 + // build the proper document SID for bc
11566 + $doc_sid = $row['sid'] ?: $row['idorderota'] ?: '';
11567 + $pdffname = $row['id'] . '_' . $doc_sid . '.pdf';
11568 +
11569 + $pathpdf = VBO_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "checkins" . DIRECTORY_SEPARATOR . "generated" . DIRECTORY_SEPARATOR . $pdffname;
11570 + if (file_exists($pathpdf)) @unlink($pathpdf);
11571 + $pdf_page_format = is_array($pdfparams['pdf_page_format']) ? $pdfparams['pdf_page_format'] : constant($pdfparams['pdf_page_format']);
11572 + $pdf = new TCPDF(constant($pdfparams['pdf_page_orientation']), constant($pdfparams['pdf_unit']), $pdf_page_format, true, 'UTF-8', false);
11573 + $pdf->SetTitle(JText::translate('VBOCHECKINDOCTITLE'));
11574 + //Header for each page of the pdf
11575 + if ($pdfparams['show_header'] == 1 && count($pdfparams['header_data']) > 0) {
11576 + $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]);
11577 + }
11578 + //header and footer fonts
11579 + $pdf->setHeaderFont(array($usepdffont, '', $pdfparams['header_font_size']));
11580 + $pdf->setFooterFont(array($usepdffont, '', $pdfparams['footer_font_size']));
11581 + //margins
11582 + $pdf->SetMargins(constant($pdfparams['pdf_margin_left']), constant($pdfparams['pdf_margin_top']), constant($pdfparams['pdf_margin_right']));
11583 + $pdf->SetHeaderMargin(constant($pdfparams['pdf_margin_header']));
11584 + $pdf->SetFooterMargin(constant($pdfparams['pdf_margin_footer']));
11585 + //
11586 + $pdf->SetAutoPageBreak(true, constant($pdfparams['pdf_margin_bottom']));
11587 + $pdf->setImageScale(constant($pdfparams['pdf_image_scale_ratio']));
11588 + $pdf->SetFont($usepdffont, '', (int)$pdfparams['body_font_size']);
11589 + if ($pdfparams['show_header'] == 0 || !(count($pdfparams['header_data']) > 0)) {
11590 + $pdf->SetPrintHeader(false);
11591 + }
11592 + if ($pdfparams['show_footer'] == 0) {
11593 + $pdf->SetPrintFooter(false);
11594 + }
11595 + $pdf->AddPage();
11596 + $pdf->writeHTML($checkin_body, true, false, true, false, '');
11597 + $pdf->lastPage();
11598 + $pdf->Output($pathpdf, 'F');
11599 + if (!file_exists($pathpdf)) {
11600 + VikError::raiseWarning('', JText::translate('VBOERRGENCHECKINDOC'));
11601 + } else {
11602 + $q = "UPDATE `#__vikbooking_customers_orders` SET `checkindoc`=".$dbo->quote($pdffname)." WHERE `idorder`=".(int)$row['id'].";";
11603 + $dbo->setQuery($q);
11604 + $dbo->execute();
11605 + $mainframe->enqueueMessage(JText::translate('VBOGENCHECKINDOCSUCCESS'));
11606 + /**
11607 + * @wponly - trigger files mirroring
11608 + */
11609 + VikBookingLoader::import('update.manager');
11610 + VikBookingUpdateManager::triggerUploadBackup($pathpdf);
11611 + //
11612 + }
11613 + //
11614 + /**
11615 + * @wponly - this task is executed via Ajax for the Modal forms listener. We cannot redirect to tmpl=component
11616 + */
11617 + $mainframe->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id']);
11618 + exit;
11619 + }
11620 +
11621 + public function updatebookingcheckin()
11622 + {
11623 + if (!JFactory::getUser()->authorise('core.vbo.bookings', 'com_vikbooking')) {
11624 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
11625 + }
11626 +
11627 + $cid = VikRequest::getVar('cid', array(0));
11628 + $id = $cid[0];
11629 +
11630 + $dbo = JFactory::getDbo();
11631 + $app = JFactory::getApplication();
11632 +
11633 + $ptmpl = $app->input->getString('tmpl', '');
11634 + $pnewtotpaid = $app->input->getFloat('newtotpaid', 0);
11635 + $pguests = $app->input->get('guests', [], 'array');
11636 + $pcomments = JComponentHelper::filterText($app->input->get('comments', '', 'raw'));
11637 + $pcheckin_action = $app->input->getInt('checkin_action', 0);
11638 + $valid_actions = array(-1, 0, 1, 2);
11639 + if (!in_array($pcheckin_action, $valid_actions)) {
11640 + $app->redirect('index.php');
11641 + exit;
11642 + }
11643 + $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=".(int)$id." AND `status`='confirmed';";
11644 + $dbo->setQuery($q);
11645 + $dbo->execute();
11646 + if ($dbo->getNumRows() < 1) {
11647 + $app->redirect('index.php');
11648 + exit;
11649 + }
11650 + $row = $dbo->loadAssoc();
11651 + $q = "SELECT * FROM `#__vikbooking_customers_orders` WHERE `idorder`=".$row['id'].";";
11652 + $dbo->setQuery($q);
11653 + $dbo->execute();
11654 + if ($dbo->getNumRows() < 1) {
11655 + VikError::raiseWarning('', JText::translate('VBOCHECKINERRNOCUSTOMER'));
11656 + $app->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id'].($ptmpl == 'component' ? '&tmpl=component' : ''));
11657 + exit;
11658 + }
11659 + $custorder = $dbo->loadAssoc();
11660 + //update checked status and new total paid
11661 + $q = "UPDATE `#__vikbooking_orders` SET `checked`=".$pcheckin_action."".($pnewtotpaid > 0 ? ', `totpaid`='.$pnewtotpaid : '')." WHERE `id`=".$row['id'].";";
11662 + $dbo->setQuery($q);
11663 + $dbo->execute();
11664 + // Booking History log for new amount paid (payment update)
11665 + if ($pnewtotpaid > 0 && $pnewtotpaid > (float)$row['totpaid']) {
11666 + $extra_data = new stdClass;
11667 + $extra_data->amount_paid = ($pnewtotpaid - (float)$row['totpaid']);
11668 + VikBooking::getBookingHistoryInstance()->setBid($row['id'])->setExtraData($extra_data)->store('PU', JText::sprintf('VBOPREVAMOUNTPAID', VikBooking::numberFormat((float)$row['totpaid'])));
11669 + }
11670 + //
11671 + //Booking History
11672 + $hist_type = 'A';
11673 + if ($pcheckin_action < 0) {
11674 + $hist_type = 'Z';
11675 + } elseif ($pcheckin_action == 1) {
11676 + $hist_type = 'B';
11677 + } elseif ($pcheckin_action == 2) {
11678 + $hist_type = 'C';
11679 + }
11680 + $user = JFactory::getUser();
11681 + VikBooking::getBookingHistoryInstance()->setBid($row['id'])->store('R' . $hist_type, "({$user->name})");
11682 + //
11683 + //Guests Details
11684 + $guests_details = array();
11685 + list($pax_fields, $pax_fields_attributes) = VikBooking::getPaxFields();
11686 + // grab also the fields for front-end pre check-in
11687 + list($pre_pax_fields, $pre_pax_fields_attributes) = VikBooking::getPaxFields(true);
11688 + //
11689 + foreach ($pguests as $ind => $adults) {
11690 + foreach ($adults as $aduind => $details) {
11691 + foreach ($pax_fields as $key => $v) {
11692 + if (isset($details[$key]) && ((is_scalar($details[$key]) && strlen($details[$key])) || !empty($details[$key]))) {
11693 + if (!isset($guests_details[$ind])) {
11694 + $guests_details[$ind] = array();
11695 + }
11696 + if (!isset($guests_details[$ind][$aduind])) {
11697 + $guests_details[$ind][$aduind] = array();
11698 + }
11699 + $guests_details[$ind][$aduind][$key] = $details[$key];
11700 + }
11701 + }
11702 + foreach ($pre_pax_fields as $key => $v) {
11703 + if (isset($pax_fields[$key])) {
11704 + // we must have parsed this back-end field already
11705 + continue;
11706 + }
11707 + if (isset($details[$key]) && ((is_scalar($details[$key]) && strlen($details[$key])) || !empty($details[$key]))) {
11708 + if (!isset($guests_details[$ind])) {
11709 + $guests_details[$ind] = array();
11710 + }
11711 + if (!isset($guests_details[$ind][$aduind])) {
11712 + $guests_details[$ind][$aduind] = array();
11713 + }
11714 + if (!isset($guests_details[$ind][$aduind][$key])) {
11715 + $guests_details[$ind][$aduind][$key] = $details[$key];
11716 + }
11717 + }
11718 + }
11719 + }
11720 + }
11721 +
11722 + if ($guests_details) {
11723 + // current pax data may contain some extra information collected via front-end pre-checkin so we need to merge them
11724 + $curpaxdata = json_decode($custorder['pax_data'], true);
11725 + if (is_array($curpaxdata) && $curpaxdata) {
11726 + // scan new guest registration details
11727 + foreach ($guests_details as $ind => $groom) {
11728 + foreach ($groom as $aduind => $aduinfo) {
11729 + if (isset($curpaxdata[$ind][$aduind])) {
11730 + $guests_details[$ind][$aduind] = array_merge($curpaxdata[$ind][$aduind], $guests_details[$ind][$aduind]);
11731 + // unset some default pax fields that were not specified now, or data cannot be deleted for guests
11732 + foreach ($guests_details[$ind][$aduind] as $key => $det) {
11733 + if (isset($pguests[$ind][$aduind][$key]) && empty($pguests[$ind][$aduind][$key])) {
11734 + // this default pax field was specified as empty now, so we cannot merge it
11735 + unset($guests_details[$ind][$aduind][$key]);
11736 + }
11737 + }
11738 + }
11739 + }
11740 + }
11741 +
11742 + /**
11743 + * In order to not lose any custom registration data added through PMS reports,
11744 + * we scan the previous registration data to ensure we keep them in the update.
11745 + *
11746 + * @since 1.16.10 (J) - 1.6.10 (WP)
11747 + */
11748 + foreach ($curpaxdata as $ind => $groom) {
11749 + if (!isset($guests_details[$ind])) {
11750 + // ignore deleted room registration
11751 + continue;
11752 + }
11753 + foreach ($groom as $aduind => $aduinfo) {
11754 + if (!isset($guests_details[$ind][$aduind]) || !is_array($aduinfo)) {
11755 + // ignore deleted room-guest registration
11756 + continue;
11757 + }
11758 + foreach ($aduinfo as $field_key => $field_val) {
11759 + if (!isset($guests_details[$ind][$aduind][$field_key]) && !empty($field_val)) {
11760 + // merge previous room-guest registration data
11761 + $guests_details[$ind][$aduind][$field_key] = $field_val;
11762 + }
11763 + }
11764 + }
11765 + }
11766 + }
11767 +
11768 + $q = "UPDATE `#__vikbooking_customers_orders` SET `pax_data`=" . $dbo->q(json_encode($guests_details)) . " WHERE `id`=" . (int) $custorder['id'] . ";";
11769 + $dbo->setQuery($q);
11770 + $dbo->execute();
11771 + }
11772 +
11773 + //'checked' status comments
11774 + $q = "UPDATE `#__vikbooking_customers_orders` SET `comments`=".$dbo->quote($pcomments)." WHERE `id`=".$custorder['id'].";";
11775 + $dbo->setQuery($q);
11776 + $dbo->execute();
11777 +
11778 + $app->enqueueMessage(JText::translate('VBOCHECKINSTATUSUPDATED'));
11779 + $app->redirect('index.php?option=com_vikbooking&task=bookingcheckin&cid[]='.$row['id'].($pcheckin_action != $row['checked'] ? '&changed=1' : '').($ptmpl == 'component' ? '&tmpl=component' : ''));
11780 + exit;
11781 + }
11782 +
11783 + public function alterbooking()
11784 + {
11785 + $dbo = JFactory::getDbo();
11786 + $app = JFactory::getApplication();
11787 + $user = JFactory::getUser();
11788 +
11789 + $response = array(
11790 + 'esit' => 1,
11791 + 'message' => '',
11792 + 'vcm' => '',
11793 + );
11794 +
11795 + // must be a string as it may contain a dash
11796 + $pidorder = VikRequest::getString('idorder', '', 'request');
11797 + $pidorder = intval(str_replace('-', '', $pidorder));
11798 +
11799 + $poldidroom = VikRequest::getInt('oldidroom', '', 'request');
11800 + $pidroom = VikRequest::getInt('idroom', 0, 'request');
11801 + $pfromdate = VikRequest::getString('fromdate', '', 'request');
11802 + $ptodate = VikRequest::getString('todate', '', 'request');
11803 + $pdebug = VikRequest::getInt('e4j_debug', 0, 'request');
11804 + if ($pdebug == 1) {
11805 + echo 'e4j.error.'.print_r($app->input->post->getArray(), true);
11806 + exit;
11807 + }
11808 +
11809 + $nowdf = VikBooking::getDateFormat(true);
11810 + if ($nowdf == "%d/%m/%Y") {
11811 + $df = 'd/m/Y';
11812 + } elseif ($nowdf == "%m/%d/%Y") {
11813 + $df = 'm/d/Y';
11814 + } else {
11815 + $df = 'Y/m/d';
11816 + }
11817 + $pcheckinh = 0;
11818 + $pcheckinm = 0;
11819 + $pcheckouth = 0;
11820 + $pcheckoutm = 0;
11821 + $timeopst = VikBooking::getTimeOpenStore();
11822 + if (is_array($timeopst)) {
11823 + $opent = VikBooking::getHoursMinutes($timeopst[0]);
11824 + $closet = VikBooking::getHoursMinutes($timeopst[1]);
11825 + $pcheckinh = $opent[0];
11826 + $pcheckinm = $opent[1];
11827 + $pcheckouth = $closet[0];
11828 + $pcheckoutm = $closet[1];
11829 + }
11830 + $info_tsto = getdate(strtotime($ptodate));
11831 + $actualtsto = mktime(0, 0, 0, $info_tsto['mon'], ($info_tsto['mday'] + 1), $info_tsto['year']);
11832 + $first = VikBooking::getDateTimestamp(date($df, strtotime($pfromdate)), $pcheckinh, $pcheckinm);
11833 + $second = VikBooking::getDateTimestamp(date($df, $actualtsto), $pcheckouth, $pcheckoutm);
11834 + $ptodate = date('Y-m-d', $second);
11835 + if (!($second > $first)) {
11836 + echo 'e4j.error.1 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11837 + exit;
11838 + }
11839 + if (!($pidorder > 0) || !($pidroom > 0) || empty($pfromdate) || empty($ptodate)) {
11840 + echo 'e4j.error.2 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11841 + exit;
11842 + }
11843 +
11844 + $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $pidorder . " AND `status`='confirmed'";
11845 + $dbo->setQuery($q, 0, 1);
11846 + $dbo->execute();
11847 + if (!$dbo->getNumRows()) {
11848 + echo 'e4j.error.3 '.addslashes(JText::translate('VBOVWALTBKERRMISSDATA'));
11849 + exit;
11850 + }
11851 + $ord = $dbo->loadAssoc();
11852 +
11853 + $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;";
11854 + $dbo->setQuery($q);
11855 + $dbo->execute();
11856 + $ordersrooms = $dbo->loadAssocList();
11857 +
11858 + // store for VCM the current rooms before the modification
11859 + $ord['rooms_info'] = $ordersrooms;
11860 +
11861 + // package or custom rate
11862 + $is_package = !empty($ord['pkg']) ? true : false;
11863 + $is_cust_cost = false;
11864 + foreach ($ordersrooms as $kor => $or) {
11865 + if ($is_package !== true && !empty($or['cust_cost']) && $or['cust_cost'] > 0.00) {
11866 + $is_cust_cost = true;
11867 + break;
11868 + }
11869 + }
11870 +
11871 + // availability helper
11872 + $av_helper = VikBooking::getAvailabilityInstance();
11873 +
11874 + // room stay dates in case of split stay
11875 + $room_stay_dates = [];
11876 + if ($ord['split_stay']) {
11877 + // no need to get the transient based on booking status, as the booking must be confirmed
11878 + $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
11879 + // immediately count the number of nights of stay for each split room
11880 + foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
11881 + $room_stay_dates[$sps_r_k]['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
11882 + }
11883 + }
11884 +
11885 + // determine if dates have changed
11886 + $dates_changed = false;
11887 + if (date('Y-m-d', $ord['checkin']) != $pfromdate || date('Y-m-d', $ord['checkout']) != $ptodate) {
11888 + $dates_changed = true;
11889 + }
11890 +
11891 + $toswitch = array();
11892 + $idbooked = array();
11893 + $rooms_units = array();
11894 + $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms`;";
11895 + $dbo->setQuery($q);
11896 + $dbo->execute();
11897 + $all_rooms = $dbo->loadAssocList();
11898 + foreach ($all_rooms as $rr) {
11899 + $rooms_units[$rr['id']]['name'] = $rr['name'];
11900 + $rooms_units[$rr['id']]['units'] = $rr['units'];
11901 + }
11902 +
11903 + // switch room
11904 + if ($poldidroom != $pidroom) {
11905 + foreach ($ordersrooms as $ind => $or) {
11906 + if ($poldidroom == $or['idroom'] && array_key_exists($pidroom, $rooms_units)) {
11907 + if (!isset($idbooked[$or['idroom']])) {
11908 + $idbooked[$or['idroom']] = 0;
11909 + }
11910 + // $idbooked is not really needed as switch is never made for the same room id
11911 + $idbooked[$or['idroom']]++;
11912 + //
11913 + $orkey = count($toswitch);
11914 + $toswitch[$orkey]['from'] = $or['idroom'];
11915 + $toswitch[$orkey]['to'] = $pidroom;
11916 + $toswitch[$orkey]['record'] = $or;
11917 + $toswitch[$orkey]['record_ind'] = $ind;
11918 + break;
11919 + }
11920 + }
11921 + }
11922 + if (count($toswitch)) {
11923 + foreach ($toswitch as $ksw => $rsw) {
11924 + $plusunit = array_key_exists($rsw['to'], $idbooked) ? $idbooked[$rsw['to']] : 0;
11925 + $room_checkin = $ord['checkin'];
11926 + $room_checkout = $ord['checkout'];
11927 + if ($ord['split_stay'] && count($room_stay_dates) && isset($room_stay_dates[$rsw['record_ind']]) && $room_stay_dates[$rsw['record_ind']]['idroom'] == $rsw['from']) {
11928 + $room_checkin = $room_stay_dates[$rsw['record_ind']]['checkin'];
11929 + $room_checkout = $room_stay_dates[$rsw['record_ind']]['checkout'];
11930 + }
11931 + if (!VikBooking::roomBookable($rsw['to'], ($rooms_units[$rsw['to']]['units'] + $plusunit), $room_checkin, $room_checkout)) {
11932 + // the room is not available
11933 + unset($toswitch[$ksw]);
11934 + echo 'e4j.error.'.JText::sprintf('VBSWITCHRERR', $rsw['record']['name'], $rooms_units[$rsw['to']]['name']);
11935 + exit;
11936 + }
11937 + }
11938 + if (count($toswitch)) {
11939 + //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)
11940 + reset($ordersrooms);
11941 + $q = "UPDATE `#__vikbooking_ordersrooms` SET `idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=".$ordersrooms[0]['id'].";";
11942 + $dbo->setQuery($q);
11943 + $dbo->execute();
11944 + //
11945 + foreach ($toswitch as $ksw => $rsw) {
11946 + // update room reservation record
11947 + $q = "UPDATE `#__vikbooking_ordersrooms` SET `idroom`=" . $rsw['to'] . ",`idtar`=NULL,`roomindex`=NULL,`room_cost`=NULL WHERE `id`=" . $rsw['record']['id'] . ";";
11948 + $dbo->setQuery($q);
11949 + $dbo->execute();
11950 + $response['message'] .= JText::sprintf('VBOVWALTBKSWITCHROK', $rsw['record']['name'], $rooms_units[$rsw['to']]['name'])."\n";
11951 +
11952 + // update Notes field for this booking to keep track of the previous room that was assigned
11953 + $prev_room_name = array_key_exists($rsw['from'], $rooms_units) ? $rooms_units[$rsw['from']]['name'] : '';
11954 + if (!empty($prev_room_name)) {
11955 + $new_notes = JText::sprintf('VBOPREVROOMMOVED', $prev_room_name, date($df.' H:i:s'))."\n".$ord['adminnotes'];
11956 + $q = "UPDATE `#__vikbooking_orders` SET `adminnotes`=".$dbo->quote($new_notes)." WHERE `id`=".(int)$ord['id'].";";
11957 + $dbo->setQuery($q);
11958 + $dbo->execute();
11959 + }
11960 +
11961 + if ($ord['status'] == 'confirmed') {
11962 + // update room record in _busy
11963 + 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'])) {
11964 + // in case of a split stay it is fundamental to update the exact busy record ID
11965 + $q = "UPDATE `#__vikbooking_busy` SET `idroom`=" . $rsw['to'] . " WHERE `id`=" . (int)$room_stay_dates[$rsw['record_ind']]['id'];
11966 + $dbo->setQuery($q);
11967 + $dbo->execute();
11968 + } else {
11969 + // regular processing of a room ID for a reservation, no matter which one, we switch it
11970 + $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;";
11971 + $dbo->setQuery($q);
11972 + $dbo->execute();
11973 + if ($dbo->getNumRows() == 1) {
11974 + $cur_busy = $dbo->loadAssocList();
11975 + $q = "UPDATE `#__vikbooking_busy` SET `idroom`=".$rsw['to']." WHERE `id`=".$cur_busy[0]['id']." AND `idroom`=".$cur_busy[0]['idroom']." LIMIT 1;";
11976 + $dbo->setQuery($q);
11977 + $dbo->execute();
11978 + }
11979 + }
11980 +
11981 + // if automated updates enabled, keep $response['vcm'] empty
11982 + // Invoke Channel Manager
11983 + if (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
11984 + $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>';
11985 + }
11986 + } elseif ($ord['status'] == 'standby') {
11987 + // remove record in _tmplock
11988 + $q = "DELETE FROM `#__vikbooking_tmplock` WHERE `idorder`=" . intval($ord['id']) . ";";
11989 + $dbo->setQuery($q);
11990 + $dbo->execute();
11991 + }
11992 + }
11993 +
11994 + // check if sub-units should be assigned again when switching room
11995 + if (!$dates_changed && !$ord['split_stay'] && VikBooking::autoRoomUnit()) {
11996 + $new_order_rooms = VikBooking::loadOrdersRoomsData($ord['id']);
11997 + $room_indexes_usemap = [];
11998 + foreach ($new_order_rooms as $kor => $or) {
11999 + $num = $kor + 1;
12000 + // assign room specific unit
12001 + $room_indexes = VikBooking::getRoomUnitNumsAvailable($ord, $or['idroom']);
12002 + $use_ind_key = 0;
12003 + if ($room_indexes) {
12004 + if (!array_key_exists($or['idroom'], $room_indexes_usemap)) {
12005 + $room_indexes_usemap[$or['idroom']] = $use_ind_key;
12006 + } else {
12007 + $use_ind_key = $room_indexes_usemap[$or['idroom']];
12008 + }
12009 + $q = "UPDATE `#__vikbooking_ordersrooms` SET `roomindex`=".(int)$room_indexes[$use_ind_key]." WHERE `id`=".(int)$or['id'].";";
12010 + $dbo->setQuery($q);
12011 + $dbo->execute();
12012 + $room_indexes_usemap[$or['idroom']]++;
12013 + }
12014 + }
12015 + }
12016 +
12017 + // do not terminate the process when there is a switch, proceed to check the dates.
12018 + }
12019 + }
12020 +
12021 + // change dates
12022 + if ($dates_changed) {
12023 + if ($ord['split_stay']) {
12024 + // we do not allow to drag and change dates for rooms in a split stay reservation
12025 + echo 'e4j.error.' . JText::sprintf('VBO_BOOK_SPLIT_STAY_CANNOTDRAG', $ord['id']);
12026 + exit;
12027 + }
12028 +
12029 + // total nights of stay
12030 + $daysdiff = $ord['days'];
12031 +
12032 + // re-read ordersrooms (as rooms may have been switched)
12033 + $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;";
12034 + $dbo->setQuery($q);
12035 + $dbo->execute();
12036 + $ordersrooms = $dbo->loadAssocList();
12037 +
12038 + $groupdays = VikBooking::getGroupDays($first, $second, $daysdiff);
12039 + $opertwounits = true;
12040 + $units_counter = array();
12041 + foreach ($ordersrooms as $ind => $or) {
12042 + if (!isset($units_counter[$or['idroom']])) {
12043 + $units_counter[$or['idroom']] = -1;
12044 + }
12045 + $units_counter[$or['idroom']]++;
12046 + }
12047 +
12048 + foreach ($ordersrooms as $ind => $or) {
12049 + $num = $ind + 1;
12050 + $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'] . ";";
12051 + $dbo->setQuery($check);
12052 + $dbo->execute();
12053 + if ($dbo->getNumRows() > 0) {
12054 + $busy = $dbo->loadAssocList();
12055 + foreach ($groupdays as $gday) {
12056 + $bfound = 0;
12057 + foreach ($busy as $bu) {
12058 + if ($gday >= $bu['checkin'] && $gday <= $bu['realback']) {
12059 + $bfound++;
12060 + }
12061 + }
12062 + if ($bfound >= ($or['units'] - $units_counter[$or['idroom']]) || !VikBooking::roomNotLocked($or['idroom'], $or['units'], $first, $second)) {
12063 + $opertwounits = false;
12064 + break 2;
12065 + }
12066 + }
12067 + }
12068 + }
12069 + if ($opertwounits !== true) {
12070 + $response['esit'] = 0;
12071 + $response['message'] = JText::translate('VBROOMNOTRIT')." ".date($df.' H:i', $first)." ".JText::translate('VBROOMNOTCONSTO')." ".date($df.' H:i', $second);
12072 + echo json_encode($response);
12073 + exit;
12074 + }
12075 +
12076 + // update dates and busy records
12077 + $realback = VikBooking::getHoursRoomAvail() * 3600;
12078 + $realback += $second;
12079 + $q = "UPDATE `#__vikbooking_orders` SET `checkin`='".$first."', `checkout`='".$second."' WHERE `id`=".$ord['id'].";";
12080 + $dbo->setQuery($q);
12081 + $dbo->execute();
12082 + if ($ord['status'] == 'confirmed') {
12083 + $q = "SELECT `b`.`id` FROM `#__vikbooking_busy` AS `b`,`#__vikbooking_ordersbusy` AS `ob` WHERE `b`.`id`=`ob`.`idbusy` AND `ob`.`idorder`=".$ord['id'].";";
12084 + $dbo->setQuery($q);
12085 + $dbo->execute();
12086 + $allbusy = $dbo->loadAssocList();
12087 + foreach ($allbusy as $bb) {
12088 + $q = "UPDATE `#__vikbooking_busy` SET `checkin`='".$first."', `checkout`='".$second."', `realback`='".$realback."' WHERE `id`='".$bb['id']."';";
12089 + $dbo->setQuery($q);
12090 + $dbo->execute();
12091 + }
12092 + // if automated updates enabled, keep $response['vcm'] empty
12093 + // Invoke Channel Manager
12094 + if (file_exists(VCM_SITE_PATH . DIRECTORY_SEPARATOR . "helpers" . DIRECTORY_SEPARATOR . "synch.vikbooking.php")) {
12095 + $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>';
12096 + }
12097 + }
12098 + $response['message'] .= JText::translate('RESUPDATED')."\n";
12099 + }
12100 +
12101 + if (count($toswitch)) {
12102 + /**
12103 + * Rooms have changed so the new rates must be re-calculated.
12104 + * Maybe they should be calculated in any case, even if just
12105 + * the dates have changed. For the moment the rates are reset.
12106 + */
12107 + }
12108 +
12109 + // unset any previously booked room due to calendar sharing
12110 + VikBooking::cleanSharedCalendarsBusy($ord['id']);
12111 + // check if some of the rooms booked have shared calendars
12112 + VikBooking::updateSharedCalendars($ord['id']);
12113 + //
12114 +
12115 + //Booking History
12116 + VikBooking::getBookingHistoryInstance($ord['id'])->setPrevBooking($ord)->store('MB', "({$user->name}) " . VikBooking::getLogBookingModification($ord));
12117 + //
12118 +
12119 + $vcm_autosync = VikBooking::vcmAutoUpdate();
12120 + if ($vcm_autosync > 0 && !empty($response['vcm'])) {
12121 + //unset the vcm property as no buttons should be displayed when in auto-sync
12122 + $response['vcm'] = '';
12123 + $vcm_obj = VikBooking::getVcmInvoker();
12124 + $vcm_obj->setOids(array($ord['id']))->setSyncType('modify')->setOriginalBooking($ord);
12125 + $sync_result = $vcm_obj->doSync();
12126 + if ($sync_result === false) {
12127 + $response['message'] .= JText::translate('VBCHANNELMANAGERRESULTKO')." (".$vcm_obj->getError().")\n";
12128 + }
12129 + }
12130 +
12131 + // in case of error but not empty VCM message, set an error that will be displayed after the mustReload
12132 + if ($response['esit'] < 1 && !empty($response['vcm'])) {
12133 + VikError::raiseNotice('', $response['vcm']);
12134 + }
12135 +
12136 + $response['message'] = nl2br($response['message']);
12137 + echo json_encode($response);
12138 + exit;
12139 + }
12140 +
12141 + public function modroomrateplans()
12142 + {
12143 + $dbo = JFactory::getDbo();
12144 + $session = JFactory::getSession();
12145 +
12146 + $updforvcm = $session->get('vbVcmRatesUpd', '');
12147 + $updforvcm = empty($updforvcm) || !is_array($updforvcm) ? array() : $updforvcm;
12148 +
12149 + $pid_room = VikRequest::getInt('id_room', '', 'request');
12150 + $pid_price = VikRequest::getInt('id_price', '', 'request');
12151 + $ptype = VikRequest::getString('type', '', 'request');
12152 + $pfromdate = VikRequest::getString('fromdate', '', 'request');
12153 + $ptodate = VikRequest::getString('todate', '', 'request');
12154 +
12155 + if (empty($pid_room) || empty($pid_price) || empty($ptype) || empty($pfromdate) || empty($ptodate) || !(strtotime($pfromdate) > 0) || !(strtotime($ptodate) > 0)) {
12156 + echo 'e4j.error.'.addslashes(JText::translate('VBRATESOVWERRMODRPLANS'));
12157 + exit;
12158 + }
12159 +
12160 + $q = "SELECT * FROM `#__vikbooking_prices` WHERE `id`=".$pid_price.";";
12161 + $dbo->setQuery($q);
12162 + $price_record = $dbo->loadAssoc();
12163 +
12164 + if (!$price_record) {
12165 + echo 'e4j.error.'.addslashes(JText::translate('VBRATESOVWERRMODRPLANS')).'.';
12166 + exit;
12167 + }
12168 +
12169 + $current_closed = array();
12170 + if (!empty($price_record['closingd'])) {
12171 + $current_closed = json_decode($price_record['closingd'], true);
12172 + }
12173 + $current_closed = !is_array($current_closed) ? array() : $current_closed;
12174 +
12175 + $start_ts = strtotime($pfromdate);
12176 + $end_ts = strtotime($ptodate);
12177 + $infostart = getdate($start_ts);
12178 + $all_days = array();
12179 + $output = array();
12180 + while ($infostart[0] > 0 && $infostart[0] <= $end_ts) {
12181 + $all_days[] = date('Y-m-d', $infostart[0]);
12182 + $indkey = $infostart['mday'].'-'.$infostart['mon'].'-'.$infostart['year'].'-'.$pid_price;
12183 + $output[$indkey] = array();
12184 + $infostart = getdate(mktime(0, 0, 0, $infostart['mon'], ($infostart['mday'] + 1), $infostart['year']));
12185 + }
12186 +
12187 + if ($ptype == 'close') {
12188 + // close
12189 + if (!array_key_exists($pid_room, $current_closed)) {
12190 + $current_closed[$pid_room] = array();
12191 + }
12192 + foreach ($all_days as $daymod) {
12193 + if (!in_array($daymod, $current_closed[$pid_room])) {
12194 + $current_closed[$pid_room][] = $daymod;
12195 + }
12196 + }
12197 + } else {
12198 + // open
12199 + if (array_key_exists($pid_room, $current_closed)) {
12200 + foreach ($all_days as $daymod) {
12201 + if (in_array($daymod, $current_closed[$pid_room])) {
12202 + foreach ($current_closed[$pid_room] as $ck => $cv) {
12203 + if ($daymod == $cv) {
12204 + unset($current_closed[$pid_room][$ck]);
12205 + }
12206 + }
12207 + }
12208 + }
12209 + } else {
12210 + $current_closed[$pid_room] = array();
12211 + }
12212 + }
12213 +
12214 + if (!$current_closed[$pid_room]) {
12215 + unset($current_closed[$pid_room]);
12216 + }
12217 +
12218 + $q = "UPDATE `#__vikbooking_prices` SET `closingd`=".(count($current_closed) > 0 ? $dbo->quote(json_encode($current_closed)) : "NULL")." WHERE `id`=".(int)$pid_price.";";
12219 + $dbo->setQuery($q);
12220 + $dbo->execute();
12221 +
12222 + $oldcsscls = $ptype == 'close' ? 'vbo-roverw-rplan-on' : 'vbo-roverw-rplan-off';
12223 + $newcsscls = $ptype == 'close' ? 'vbo-roverw-rplan-off' : 'vbo-roverw-rplan-on';
12224 +
12225 + foreach ($output as $ok => $ov) {
12226 + $output[$ok] = array('oldcls' => $oldcsscls, 'newcls' => $newcsscls);
12227 + }
12228 +
12229 + // build new session values
12230 + $updforvcm['count'] = array_key_exists('count', $updforvcm) && !empty($updforvcm['count']) ? ($updforvcm['count'] + 1) : 1;
12231 +
12232 + if (array_key_exists('dfrom', $updforvcm) && !empty($updforvcm['dfrom'])) {
12233 + $updforvcm['dfrom'] = $updforvcm['dfrom'] > $start_ts ? $start_ts : $updforvcm['dfrom'];
12234 + } else {
12235 + $updforvcm['dfrom'] = $start_ts;
12236 + }
12237 +
12238 + if (array_key_exists('dto', $updforvcm) && !empty($updforvcm['dto'])) {
12239 + $updforvcm['dto'] = $updforvcm['dto'] < $end_ts ? $end_ts : $updforvcm['dto'];
12240 + } else {
12241 + $updforvcm['dto'] = $end_ts;
12242 + }
12243 +
12244 + if (array_key_exists('rooms', $updforvcm) && is_array($updforvcm['rooms'])) {
12245 + if (!in_array($pid_room, $updforvcm['rooms'])) {
12246 + $updforvcm['rooms'][] = $pid_room;
12247 + }
12248 + } else {
12249 + $updforvcm['rooms'] = array($pid_room);
12250 + }
12251 +
12252 + if (array_key_exists('rplans', $updforvcm) && is_array($updforvcm['rplans'])) {
12253 + if (array_key_exists($pid_room, $updforvcm['rplans'])) {
12254 + if (!in_array($pid_price, $updforvcm['rplans'][$pid_room])) {
12255 + $updforvcm['rplans'][$pid_room][] = $pid_price;
12256 + }
12257 + } else {
12258 + $updforvcm['rplans'][$pid_room] = array($pid_price);
12259 + }
12260 + } else {
12261 + $updforvcm['rplans'] = array($pid_room => array($pid_price));
12262 + }
12263 +
12264 + /**
12265 + * Rather than suggesting the administrator to manually invoke VCM to launch a Bulk Action,
12266 + * we try to silently trigger an automatic bulk action before updating the session values.
12267 + *
12268 + * @since 1.17.1 (J) - 1.7.1 (WP)
12269 + * @since 1.17.5 (J) - 1.7.5 (WP) the "rate_id" property is passed along the auto-bulk data.
12270 + */
12271 + $rates_aligned = false;
12272 + try {
12273 + if (class_exists('VikChannelManager')) {
12274 + $rates_aligned = VikChannelManager::autoBulkActions([
12275 + 'from_date' => $pfromdate,
12276 + 'to_date' => $ptodate,
12277 + 'forced_rooms' => [$pid_room],
12278 + 'rate_id' => $pid_price,
12279 + 'update' => 'rates',
12280 + ]);
12281 + }
12282 + } catch (Throwable $e) {
12283 + // do nothing
12284 + $rates_aligned = false;
12285 + }
12286 +
12287 + if (!$rates_aligned) {
12288 + // update session values
12289 + $session->set('vbVcmRatesUpd', $updforvcm);
12290 + }
12291 +
12292 + echo json_encode($output);
12293 + exit;
12294 + }
12295 +
12296 + public function icsexportlaunch() {
12297 + $dbo = JFactory::getDBO();
12298 + $pcheckindate = VikRequest::getString('checkindate', '', 'request');
12299 + $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
12300 + $pstatus = VikRequest::getString('status', '', 'request');
12301 + $validstatus = array('confirmed', 'standby', 'cancelled');
12302 + $filterstatus = '';
12303 + $filterfirst = 0;
12304 + $filtersecond = 0;
12305 + $nowdf = VikBooking::getDateFormat(true);
12306 + if ($nowdf == "%d/%m/%Y") {
12307 + $df = 'd/m/Y';
12308 + } elseif ($nowdf == "%m/%d/%Y") {
12309 + $df = 'm/d/Y';
12310 + } else {
12311 + $df = 'Y/m/d';
12312 + }
12313 + $currencyname = VikBooking::getCurrencyName();
12314 + if (!empty($pstatus) && in_array($pstatus, $validstatus)) {
12315 + $filterstatus = $pstatus;
12316 + }
12317 + if (!empty($pcheckindate)) {
12318 + if (VikBooking::dateIsValid($pcheckindate)) {
12319 + $first=VikBooking::getDateTimestamp($pcheckindate, '0', '0');
12320 + $filterfirst = $first;
12321 + }
12322 + }
12323 + if (!empty($pcheckoutdate)) {
12324 + if (VikBooking::dateIsValid($pcheckoutdate)) {
12325 + $second=VikBooking::getDateTimestamp($pcheckoutdate, '23', '59');
12326 + if ($second > $first) {
12327 + $filtersecond = $second;
12328 + }
12329 + }
12330 + }
12331 + $clause = array();
12332 + if ($filterfirst > 0) {
12333 + $clause[] = "`o`.`checkin` >= ".$filterfirst;
12334 + }
12335 + if ($filtersecond > 0) {
12336 + $clause[] = "`o`.`checkout` <= ".$filtersecond;
12337 + }
12338 + if (!empty($filterstatus)) {
12339 + $clause[] = "`o`.`status` = '".$filterstatus."'";
12340 + }
12341 + $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;";
12342 + $dbo->setQuery($q);
12343 + $dbo->execute();
12344 + if ($dbo->getNumRows() > 0) {
12345 + $orders = $dbo->loadAssocList();
12346 + $icscontent = "BEGIN:VCALENDAR\n";
12347 + $icscontent .= "VERSION:2.0\n";
12348 + $icscontent .= "PRODID:-//e4j//VikBooking//EN\n";
12349 + $icscontent .= "CALSCALE:GREGORIAN\n";
12350 + $str = "";
12351 + foreach ($orders as $kord => $ord) {
12352 + if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12353 + continue;
12354 + }
12355 + $usecurrencyname = $currencyname;
12356 + $usecurrencyname = !empty($ord['idorderota']) && !empty($ord['chcurrency']) ? $ord['chcurrency'] : $usecurrencyname;
12357 + $statusstr = '';
12358 + if ($ord['status'] == 'confirmed') {
12359 + $statusstr = JText::translate('VBCSVSTATUSCONFIRMED');
12360 + } elseif ($ord['status'] == 'standby') {
12361 + $statusstr = JText::translate('VBCSVSTATUSSTANDBY');
12362 + } elseif ($ord['status'] == 'cancelled') {
12363 + $statusstr = JText::translate('VBCSVSTATUSCANCELLED');
12364 + }
12365 + $uri = JURI::root().'index.php?option=com_vikbooking&view=booking&sid='.$ord['sid'].'&ts='.$ord['ts'];
12366 + /**
12367 + * @wponly Rewrite URI for front-end
12368 + */
12369 + $uri = str_replace(JUri::root(), '', $uri);
12370 + $model = JModel::getInstance('vikbooking', 'shortcodes');
12371 + $itemid = $model->best('booking');
12372 + if ($itemid) {
12373 + $uri = JRoute::rewrite($uri . "&Itemid={$itemid}", false);
12374 + }
12375 + //
12376 + $ordnumbstr = $ord['id'].(!empty($ord['confirmnumber']) ? ' - '.$ord['confirmnumber'] : '').(!empty($ord['idorderota']) ? ' ('.ucwords($ord['channel']).')' : '').' - '.$statusstr;
12377 + $peoplestr = ($ord['adults'] + $ord['children']).($ord['children'] > 0 ? ' ('.JText::translate('VBCSVCHILDREN').': '.$ord['children'].')' : '');
12378 + $totalstring = ($ord['total'] > 0 ? ($usecurrencyname.' '.VikBooking::numberFormat($ord['total'])) : '');
12379 + $totalpaidstring = ($ord['totpaid'] > 0 ? (' ('.VikBooking::numberFormat($ord['totpaid']).')') : '');
12380 + $description = JText::sprintf('VBICSEXPDESCRIPTION', $ordnumbstr."\\n", $peoplestr."\\n", $ord['days']."\\n", $totalstring.$totalpaidstring."\\n", "\\n".str_replace("\n", "\\n", trim($ord['custdata'])));
12381 + $str .= "BEGIN:VEVENT\n";
12382 + $str .= "DTEND:" . JFactory::getDate(date('Y-m-d H:i:s', $ord['checkout']), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12383 + $str .= "UID:" . uniqid() . "\n";
12384 + $str .= "DTSTAMP:" . JFactory::getDate(date('Y-m-d H:i:s'), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12385 + $str .= ((strlen($description) > 0 ) ? "DESCRIPTION:".preg_replace('/([\,;])/','\\\$1', $description)."\n" : "");
12386 + $str .= "URL;VALUE=URI:" . preg_replace('/([\,;])/','\\\$1', $uri) . "\n";
12387 + $str .= "SUMMARY:" . JText::sprintf('VBICSEXPSUMMARY', date($df, $ord['checkin'])) . "\n";
12388 + $str .= "DTSTART:" . JFactory::getDate(date('Y-m-d H:i:s', $ord['checkin']), date_default_timezone_get())->format('Ymd\THis\Z') . "\n";
12389 + $str .= "END:VEVENT\n";
12390 + }
12391 + $icscontent .= $str;
12392 + $icscontent .= "END:VCALENDAR\n";
12393 + //download file from buffer
12394 + header("Content-Type: application/octet-stream; ");
12395 + header("Cache-Control: no-store, no-cache");
12396 + header('Content-Disposition: attachment; filename="bookings_export.ics"');
12397 + $f = fopen('php://output', "w");
12398 + fwrite($f, $icscontent);
12399 + fclose($f);
12400 + exit;
12401 + } else {
12402 + VikError::raiseWarning('', JText::translate('VBICSEXPNORECORDS'));
12403 + $mainframe = JFactory::getApplication();
12404 + $mainframe->redirect("index.php?option=com_vikbooking&task=icsexportprepare&checkindate=".$pcheckindate."&checkoutdate=".$pcheckoutdate."&status=".$pstatus."&tmpl=component");
12405 + }
12406 + }
12407 +
12408 + public function csvexportlaunch()
12409 + {
12410 + $dbo = JFactory::getDbo();
12411 + $app = JFactory::getApplication();
12412 +
12413 + $pdatefilt = VikRequest::getString('datefilt', '', 'request');
12414 + $proomfilt = VikRequest::getString('roomfilt', '', 'request');
12415 + $pchfilt = VikRequest::getString('chfilt', '', 'request');
12416 + $ppayfilt = VikRequest::getString('payfilt', '', 'request');
12417 + $pcheckindate = VikRequest::getString('checkindate', '', 'request');
12418 + $pcheckoutdate = VikRequest::getString('checkoutdate', '', 'request');
12419 + $pstatus = VikRequest::getString('status', '', 'request');
12420 + $pcatfilt = VikRequest::getInt('catfilt', 0, 'request');
12421 + $pformat = VikRequest::getString('format', 'csv', 'request');
12422 +
12423 + // let the report class (a generic one) generate the CSV file in the proper format
12424 + $report_obj = VikBooking::getReportInstance('revenue')->setExportCSVFormat($pformat);
12425 +
12426 + $validstatus = array('confirmed', 'standby', 'cancelled');
12427 + $validdates = array('ts', 'checkin', 'checkout');
12428 +
12429 + $filterdate = '';
12430 + $filterstatus = '';
12431 + $first = 0;
12432 + $filterfirst = 0;
12433 + $filtersecond = 0;
12434 + $nowdf = VikBooking::getDateFormat(true);
12435 + if ($nowdf == "%d/%m/%Y") {
12436 + $df = 'd/m/Y';
12437 + } elseif ($nowdf == "%m/%d/%Y") {
12438 + $df = 'm/d/Y';
12439 + } else {
12440 + $df = 'Y/m/d';
12441 + }
12442 + $datesep = VikBooking::getDateSeparator(true);
12443 + $currencyname = VikBooking::getCurrencyName();
12444 +
12445 + if (!empty($pstatus) && in_array($pstatus, $validstatus)) {
12446 + $filterstatus = $pstatus;
12447 + }
12448 + if (!empty($pdatefilt) && in_array($pdatefilt, $validdates)) {
12449 + $filterdate = $pdatefilt;
12450 + }
12451 + if (!empty($pcheckindate) && !empty($filterdate)) {
12452 + if (VikBooking::dateIsValid($pcheckindate)) {
12453 + $first = VikBooking::getDateTimestamp($pcheckindate, '0', '0');
12454 + $filterfirst = $first;
12455 + }
12456 + }
12457 + if (!empty($pcheckoutdate) && !empty($filterdate)) {
12458 + if (VikBooking::dateIsValid($pcheckoutdate)) {
12459 + $second = VikBooking::getDateTimestamp($pcheckoutdate, '23', '59');
12460 + if ($second > $first) {
12461 + $filtersecond = $second;
12462 + }
12463 + }
12464 + }
12465 + $clause = array();
12466 + if ($filterfirst > 0) {
12467 + $clause[] = "`o`.`".$filterdate."` >= ".$filterfirst;
12468 + }
12469 + if ($filtersecond > 0) {
12470 + $clause[] = "`o`.`".$filterdate."` <= ".$filtersecond;
12471 + }
12472 + if (!empty($filterstatus)) {
12473 + $clause[] = "`o`.`status` = '".$filterstatus."'";
12474 + }
12475 + if (!empty($pchfilt)) {
12476 + $clause[] = "`o`.`channel` LIKE ".$dbo->quote("%".$pchfilt."%");
12477 + }
12478 + if (!empty($ppayfilt)) {
12479 + $clause[] = "`o`.`idpayment` LIKE '".$ppayfilt."=%'";
12480 + }
12481 + if (!empty($proomfilt)) {
12482 + $clause[] = "`or`.`idroom` = '".(int)$proomfilt."'";
12483 + }
12484 +
12485 + if (!empty($pcatfilt)) {
12486 + $room_cat_ids = array();
12487 + $q = "SELECT `id`,`idcat` FROM `#__vikbooking_rooms` WHERE `idcat` LIKE " . $dbo->quote("%$pcatfilt%");
12488 + $dbo->setQuery($q);
12489 + $dbo->execute();
12490 + if ($dbo->getNumRows()) {
12491 + $records = $dbo->loadAssocList();
12492 + foreach ($records as $rcat) {
12493 + $parts = explode(';', $rcat['idcat']);
12494 + if (in_array($pcatfilt, $parts)) {
12495 + $room_cat_ids[] = $rcat['id'];
12496 + }
12497 + }
12498 + }
12499 + if (count($room_cat_ids)) {
12500 + $clause[] = "`or`.`idroom` IN (" . implode(', ', $room_cat_ids) . ")";
12501 + }
12502 + }
12503 +
12504 + $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;";
12505 + $dbo->setQuery($q);
12506 + $orders = $dbo->loadAssocList();
12507 + if (!$orders) {
12508 + $app->enqueueMessage(JText::translate('VBCSVEXPNORECORDS'), 'error');
12509 + $app->redirect("index.php?option=com_vikbooking&task=csvexportprepare&checkindate=".$pcheckindate."&checkoutdate=".$pcheckoutdate."&status=".$pstatus."&tmpl=component");
12510 + $app->close();
12511 + }
12512 +
12513 + // options
12514 + $all_options = array();
12515 + $q = "SELECT * FROM `#__vikbooking_optionals` ORDER BY `#__vikbooking_optionals`.`ordering` ASC;";
12516 + $dbo->setQuery($q);
12517 + $options = $dbo->loadAssocList();
12518 + if ($options) {
12519 + foreach ($options as $ok => $ov) {
12520 + $all_options[$ov['id']] = $ov;
12521 + }
12522 + }
12523 +
12524 + // build columns
12525 + $columns = [
12526 + [
12527 + 'label' => JText::translate('VBDASHBOOKINGID'),
12528 + ],
12529 + [
12530 + 'label' => JText::translate('VBPVIEWORDERSONE'),
12531 + ],
12532 + [
12533 + 'label' => JText::translate('VBCSVCHECKIN'),
12534 + ],
12535 + [
12536 + 'label' => JText::translate('VBCSVCHECKOUT'),
12537 + ],
12538 + [
12539 + 'label' => JText::translate('VBCSVNIGHTS'),
12540 + ],
12541 + [
12542 + 'label' => JText::translate('VBCSVROOM'),
12543 + ],
12544 + [
12545 + 'label' => JText::translate('VBCSVPEOPLE'),
12546 + ],
12547 + [
12548 + 'label' => JText::translate('VBCSVCUSTINFO'),
12549 + ],
12550 + [
12551 + 'label' => JText::translate('ORDER_SPREQUESTS'),
12552 + ],
12553 + [
12554 + 'label' => JText::translate('ORDER_NOTES'),
12555 + ],
12556 + [
12557 + 'label' => JText::translate('VBCSVCREATEDBY'),
12558 + ],
12559 + [
12560 + 'label' => JText::translate('VBCSVCUSTMAIL'),
12561 + ],
12562 + [
12563 + 'label' => JText::translate('ORDER_PHONE'),
12564 + ],
12565 + [
12566 + 'label' => JText::translate('VBCSVOPTIONS'),
12567 + ],
12568 + [
12569 + 'label' => JText::translate('VBCSVPAYMENTMETHOD'),
12570 + ],
12571 + [
12572 + 'label' => JText::translate('VBCSVORDIDCONFNUMB'),
12573 + ],
12574 + [
12575 + 'label' => JText::translate('VBOCHANNEL'),
12576 + ],
12577 + [
12578 + 'label' => JText::translate('VBCSVEXPFILTBSTATUS'),
12579 + ],
12580 + [
12581 + 'label' => JText::translate('VBCSVTOTAL'),
12582 + ],
12583 + [
12584 + 'label' => JText::translate('VBCSVTOTPAID'),
12585 + ],
12586 + [
12587 + 'label' => JText::translate('VBCSVTOTTAXES'),
12588 + ],
12589 + ];
12590 +
12591 + // booking cancellation details
12592 + $cancellation_timestamps = [];
12593 +
12594 + if (empty($filterstatus) || $filterstatus === 'cancelled') {
12595 + // insert column for cancellation date at index 2
12596 + array_splice($columns, 2, 0, [['label' => JText::translate('VBO_CANC_DATE')]]);
12597 + // gather all cancelled bookings, if any
12598 + $cancellation_ids = [];
12599 + foreach ($orders as $order) {
12600 + if ($order['status'] === 'cancelled' && !in_array($order['id'], $cancellation_ids)) {
12601 + $cancellation_ids[] = $order['id'];
12602 + }
12603 + }
12604 + if ($cancellation_ids && $cancHistoryEvents = VikBooking::getBookingHistoryInstance(0)->getBookingEventsType('cancelled')) {
12605 + // list of booking IDs with cancellation events processed
12606 + $cancBidsProcessed = [];
12607 +
12608 + // query the database to fetch the needed history records
12609 + $dbo->setQuery(
12610 + $dbo->getQuery(true)
12611 + ->select([
12612 + $dbo->qn('idorder'),
12613 + $dbo->qn('dt'),
12614 + ])
12615 + ->from($dbo->qn('#__vikbooking_orderhistory'))
12616 + ->where($dbo->qn('idorder') . ' IN (' . implode(', ', array_map('intval', $cancellation_ids)) . ')')
12617 + ->where($dbo->qn('type') . ' IN (' . implode(', ', array_map([$dbo, 'q'], $cancHistoryEvents)) . ')')
12618 + ->order($dbo->qn('idorder') . ' ASC')
12619 + ->order($dbo->qn('dt') . ' ASC')
12620 + );
12621 +
12622 + // scan all booking cancellation records
12623 + foreach ($dbo->loadAssocList() as $cancRecord) {
12624 + if (!($cancBidsProcessed[$cancRecord['idorder']] ?? 0)) {
12625 + // turn flag on to process this booking only once and get the earliest (first) cancellation
12626 + $cancBidsProcessed[$cancRecord['idorder']] = 1;
12627 +
12628 + // convert the cancellation date from UTC to local timezone and set booking cancellation timestamp
12629 + $cancellation_timestamps[$cancRecord['idorder']] = JHtml::fetch('date', $cancRecord['dt'], 'U');
12630 + }
12631 + }
12632 + }
12633 + }
12634 +
12635 + // set CSV columns
12636 + $report_obj->setReportCols($columns);
12637 +
12638 + // prepare the container for the CSV rows
12639 + $orderscsv = [];
12640 +
12641 + // availability helper
12642 + $av_helper = VikBooking::getAvailabilityInstance();
12643 +
12644 + $room_inds = [];
12645 + $room_stay_dates = [];
12646 + foreach ($orders as $kord => $ord) {
12647 + // room index in this booking
12648 + if (!isset($room_inds[$ord['id']])) {
12649 + $room_inds[$ord['id']] = -1;
12650 + }
12651 + $room_inds[$ord['id']]++;
12652 +
12653 + /**
12654 + * Split stay reservation.
12655 + *
12656 + * @since 1.16.0 (J) - 1.6.0 (WP)
12657 + */
12658 + $room_stay_dates = $room_inds[$ord['id']] > 0 ? $room_stay_dates : [];
12659 + if ($ord['split_stay']) {
12660 + if ($ord['status'] == 'confirmed') {
12661 + $room_stay_dates = $av_helper->loadSplitStayBusyRecords($ord['id']);
12662 + } else {
12663 + $room_stay_dates = VBOFactory::getConfig()->getArray('split_stay_' . $ord['id'], []);
12664 + }
12665 + // immediately count the number of nights of stay for each split room
12666 + foreach ($room_stay_dates as $sps_r_k => $sps_r_v) {
12667 + if (!empty($sps_r_v['checkin_ts']) && !empty($sps_r_v['checkout_ts'])) {
12668 + // overwrite values for compatibility with non-confirmed bookings
12669 + $sps_r_v['checkin'] = $sps_r_v['checkin_ts'];
12670 + $sps_r_v['checkout'] = $sps_r_v['checkout_ts'];
12671 + }
12672 + $sps_r_v['nights'] = $av_helper->countNightsOfStay($sps_r_v['checkin'], $sps_r_v['checkout']);
12673 + // overwrite the whole array
12674 + $room_stay_dates[$sps_r_k] = $sps_r_v;
12675 + }
12676 + }
12677 +
12678 + // determine nights and dates for this room booking
12679 + $booking_nights = $ord['days'];
12680 + $booking_checkin = $ord['checkin'];
12681 + $booking_checkout = $ord['checkout'];
12682 + 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']) {
12683 + $booking_nights = $room_stay_dates[$room_inds[$ord['id']]]['nights'];
12684 + $booking_checkin = $room_stay_dates[$room_inds[$ord['id']]]['checkin'];
12685 + $booking_checkout = $room_stay_dates[$room_inds[$ord['id']]]['checkout'];
12686 + }
12687 +
12688 + $usecurrencyname = $currencyname;
12689 + $usecurrencyname = !empty($ord['idorderota']) && !empty($ord['chcurrency']) ? $ord['chcurrency'] : $usecurrencyname;
12690 + $peoplestr = ($ord['adults'] + $ord['children']).($ord['children'] > 0 ? ' ('.JText::translate('VBCSVCHILDREN').': '.$ord['children'].')' : '');
12691 + $custinfostr = str_replace(",", " ", $ord['custdata']);
12692 + $customer = VikBooking::getCPinIstance()->getCustomerFromBooking($ord['id']);
12693 + if (count($customer)) {
12694 + $custinfostr = $customer['first_name'] . ' ' . $customer['last_name'];
12695 + }
12696 + $special_requests = '';
12697 + if (preg_match("/(?:special requests:\s*)(.*?)$/is", $ord['custdata'], $match)) {
12698 + $special_requests = $match[1];
12699 + } elseif (preg_match("/(?:special request:\s*)(.*?)$/is", $ord['custdata'], $match)) {
12700 + $special_requests = $match[1];
12701 + } elseif (preg_match("/(?:special request\s*)(.*?)$/is", $ord['custdata'], $match)) {
12702 + $special_requests = $match[1];
12703 + } elseif (preg_match("/(?:" . JText::translate('ORDER_SPREQUESTS') . ":\s*)(.*?)$/is", $ord['custdata'], $match)) {
12704 + $special_requests = $match[1];
12705 + }
12706 + $paystr = '';
12707 + if (!empty($ord['idpayment'])) {
12708 + $payparts = explode('=', $ord['idpayment']);
12709 + $paystr = $payparts[1];
12710 + }
12711 + $ordnumbstr = $ord['id'] . ' - ' . $ord['confirmnumber'] . (!empty($ord['idorderota']) ? ' (' . $ord['idorderota'] . ')' : '');
12712 + $bookingSource = JText::translate('VBORDFROMSITE');
12713 + if (!empty($ord['channel']) && !empty($ord['idorderota'])) {
12714 + $chparts = explode('_', $ord['channel']);
12715 + $bookingSource = ($chparts[1] ?? '') ?: $chparts[0];
12716 + }
12717 + $statusstr = '';
12718 + if ($ord['status'] == 'confirmed') {
12719 + $statusstr = JText::translate('VBCSVSTATUSCONFIRMED');
12720 + } elseif ($ord['status'] == 'standby') {
12721 + $statusstr = JText::translate('VBCSVSTATUSSTANDBY');
12722 + } elseif ($ord['status'] == 'cancelled') {
12723 + $statusstr = JText::translate('VBCSVSTATUSCANCELLED');
12724 + }
12725 + $totalstring = $usecurrencyname . ' ' . VikBooking::numberFormat($ord['total']);
12726 + if ($ord['roomsnum'] > 1) {
12727 + // take the cost for the individual room
12728 + $totalstring = !empty($ord['cust_cost']) && $ord['cust_cost'] > 0 ? ($usecurrencyname . ' ' . VikBooking::numberFormat($ord['cust_cost'])) : ($usecurrencyname . ' ' . VikBooking::numberFormat($ord['room_cost']));
12729 + }
12730 + $totalpaidstring = $usecurrencyname . ' ' . VikBooking::numberFormat($ord['totpaid']);
12731 + if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12732 + // total paid will be printed only for the last room booked
12733 + $totalpaidstring = '';
12734 + }
12735 + $options_str = '';
12736 + if (!empty($ord['optionals'])) {
12737 + $stepo = explode(";", $ord['optionals']);
12738 + foreach ($stepo as $roptkey => $oo) {
12739 + if (!empty($oo)) {
12740 + $stept = explode(":", $oo);
12741 + if (array_key_exists($stept[0], $all_options)) {
12742 + $actopt = $all_options[$stept[0]];
12743 + $optpcent = false;
12744 + if (!empty($actopt['ageintervals']) && $ord['children'] > 0 && strstr($stept[1], '-') != false) {
12745 + $optagenames = VikBooking::getOptionIntervalsAges($actopt['ageintervals']);
12746 + $optagepcent = VikBooking::getOptionIntervalsPercentage($actopt['ageintervals']);
12747 + $optageovrct = VikBooking::getOptionIntervalChildOverrides($actopt, $ord['adults'], $ord['children']);
12748 + $child_num = VikBooking::getRoomOptionChildNumber($ord['optionals'], $actopt['id'], $roptkey, $ord['children']);
12749 + $optagecosts = VikBooking::getOptionIntervalsCosts(isset($optageovrct['ageintervals_child' . ($child_num + 1)]) ? $optageovrct['ageintervals_child' . ($child_num + 1)] : $actopt['ageintervals']);
12750 + $agestept = explode('-', $stept[1]);
12751 + $stept[1] = $agestept[0];
12752 + $chvar = $agestept[1];
12753 + if (array_key_exists(($chvar - 1), $optagepcent) && $optagepcent[($chvar - 1)] > 0) {
12754 + $optpcent = true;
12755 + }
12756 + $actopt['chageintv'] = $chvar;
12757 + if (isset($optagenames[($chvar - 1)])) {
12758 + $actopt['name'] .= ' ('.$optagenames[($chvar - 1)].')';
12759 + }
12760 + if (isset($optagecosts[($chvar - 1)])) {
12761 + $realcost = (intval($actopt['perday']) == 1 ? (floatval($optagecosts[($chvar - 1)]) * $booking_nights * $stept[1]) : (floatval($optagecosts[($chvar - 1)]) * $stept[1]));
12762 + } else {
12763 + $realcost = 0;
12764 + }
12765 + } else {
12766 + // VBO 1.11 - options percentage cost of the room total fee
12767 + $optpcent = (int)$actopt['pcentroom'] ? true : $optpcent;
12768 + //
12769 + $realcost = (intval($actopt['perday']) == 1 ? ($actopt['cost'] * $booking_nights * $stept[1]) : ($actopt['cost'] * $stept[1]));
12770 + }
12771 + if ($actopt['maxprice'] > 0 && $realcost > $actopt['maxprice']) {
12772 + $realcost=$actopt['maxprice'];
12773 + if (intval($actopt['hmany']) == 1 && intval($stept[1]) > 1) {
12774 + $realcost = $actopt['maxprice'] * $stept[1];
12775 + }
12776 + }
12777 + $realcost = $actopt['perperson'] == 1 ? ($realcost * $ord['adults']) : $realcost;
12778 +
12779 + /**
12780 + * Trigger event to allow third party plugins to apply a custom calculation for the option/extra fee or tax.
12781 + *
12782 + * @since 1.17.7 (J) - 1.7.7 (WP)
12783 + */
12784 + $custom_calculation = VBOFactory::getPlatform()->getDispatcher()->filter('onCalculateBookingOptionFeeCost', [$realcost, &$actopt, $ord, $ord]);
12785 + if ($custom_calculation) {
12786 + $realcost = (float) $custom_calculation[0];
12787 + }
12788 +
12789 + $tmpopr = VikBooking::sayOptionalsPlusIva($realcost, $actopt['idiva']);
12790 + $options_str .= ($stept[1] > 1 ? $stept[1]." " : "").$actopt['name'].": ".(!$optpcent ? $currencyname : '')." ".VikBooking::numberFormat($tmpopr).($optpcent ? ' %' : '')." \r\n";
12791 + }
12792 + }
12793 + }
12794 + }
12795 +
12796 + // custom extra costs
12797 + if (!empty($ord['extracosts'])) {
12798 + $cur_extra_costs = json_decode($ord['extracosts'], true);
12799 + foreach ($cur_extra_costs as $eck => $ecv) {
12800 + $ecplustax = !empty($ecv['idtax']) ? VikBooking::sayOptionalsPlusIva($ecv['cost'], $ecv['idtax']) : $ecv['cost'];
12801 + $options_str .= $ecv['name'].": ".$currencyname." ".VikBooking::numberFormat($ecplustax)." \r\n";
12802 + }
12803 + }
12804 +
12805 + // taxes
12806 + $taxes_str = '';
12807 + if ($ord['tot_taxes'] > 0.00) {
12808 + $taxes_str .= $usecurrencyname.' '.VikBooking::numberFormat($ord['tot_taxes']);
12809 + if (!empty($ord['aliq']) && !empty($ord['breakdown'])) {
12810 + $tax_breakdown = json_decode($ord['breakdown'], true);
12811 + $tax_breakdown = is_array($tax_breakdown) && count($tax_breakdown) > 0 ? $tax_breakdown : array();
12812 + if (count($tax_breakdown)) {
12813 + foreach ($tax_breakdown as $tbkk => $tbkv) {
12814 + $tax_break_cost = $ord['tot_taxes'] * floatval($tbkv['aliq']) / $ord['aliq'];
12815 + $taxes_str .= "\r\n".$tbkv['name'].": ".$usecurrencyname.' '.VikBooking::numberFormat($tax_break_cost);
12816 + }
12817 + }
12818 + }
12819 + }
12820 + if (isset($orders[($kord + 1)]) && $orders[($kord + 1)]['id'] == $ord['id']) {
12821 + // total taxes will be printed only for the last room booked
12822 + $taxes_str = '';
12823 + }
12824 +
12825 + // created by
12826 + $created_by = '';
12827 + if (!empty($ord['ujid'])) {
12828 + $creator = new JUser($ord['ujid']);
12829 + if (property_exists($creator, 'name')) {
12830 + $created_by = $creator->name.' ('.$creator->username.')';
12831 + }
12832 + }
12833 + if (empty($created_by) && !empty($ord['t_first_name'])) {
12834 + $created_by = $ord['t_first_name'].' '.$ord['t_last_name'];
12835 + }
12836 +
12837 + // build CSV line data
12838 + $line_data = [
12839 + [
12840 + 'value' => $ord['id'],
12841 + ],
12842 + [
12843 + 'value' => date(str_replace("/", $datesep, $df), $ord['ts']),
12844 + ],
12845 + [
12846 + 'value' => date(str_replace("/", $datesep, $df), $booking_checkin),
12847 + ],
12848 + [
12849 + 'value' => date(str_replace("/", $datesep, $df), $booking_checkout),
12850 + ],
12851 + [
12852 + 'value' => $booking_nights,
12853 + ],
12854 + [
12855 + 'value' => $ord['name'],
12856 + ],
12857 + [
12858 + 'value' => $peoplestr,
12859 + ],
12860 + [
12861 + 'value' => $custinfostr,
12862 + ],
12863 + [
12864 + 'value' => $special_requests,
12865 + ],
12866 + [
12867 + 'value' => $ord['adminnotes'],
12868 + ],
12869 + [
12870 + 'value' => $created_by,
12871 + ],
12872 + [
12873 + 'value' => $ord['custmail'],
12874 + ],
12875 + [
12876 + 'value' => $ord['phone'],
12877 + ],
12878 + [
12879 + 'value' => $options_str,
12880 + ],
12881 + [
12882 + 'value' => $paystr,
12883 + ],
12884 + [
12885 + 'value' => $ordnumbstr,
12886 + ],
12887 + [
12888 + 'value' => $bookingSource,
12889 + ],
12890 + [
12891 + 'value' => $statusstr,
12892 + ],
12893 + [
12894 + 'value' => $totalstring,
12895 + ],
12896 + [
12897 + 'value' => $totalpaidstring,
12898 + ],
12899 + [
12900 + 'value' => $taxes_str,
12901 + ],
12902 + ];
12903 +
12904 + if (empty($filterstatus) || $filterstatus === 'cancelled') {
12905 + // obtain cancellation date for this booking
12906 + $booking_canc_date = $cancellation_timestamps[$ord['id']] ?? '';
12907 + if ($booking_canc_date) {
12908 + $booking_canc_date = date(str_replace("/", $datesep, $df), $booking_canc_date);
12909 + }
12910 + // insert column for cancellation date at index 2
12911 + array_splice($line_data, 2, 0, [['value' => $booking_canc_date]]);
12912 + }
12913 +
12914 + // push line for export
12915 + $orderscsv[] = $line_data;
12916 + }
12917 +
12918 + // set CSV rows
12919 + $report_obj->setReportRows($orderscsv);
12920 +
12921 + // build lines to export
12922 + $csvlines = $report_obj->getExportCSVLines($no_data = true);
12923 +
12924 + // set export file name
12925 + $report_obj->setExportCSVFileName('bookings_export_' . date('Y-m-d') . '.csv');
12926 +
12927 + // force the download of the CSV file
12928 + $report_obj->outputHeaders();
12929 +
12930 + // send lines to output
12931 + $report_obj->outputCSV($csvlines);
12932 +
12933 + exit;
12934 + }
12935 +
12936 + public function exportcustomerslaunch() {
12937 + $cid = VikRequest::getVar('cid', array(0));
12938 + $dbo = JFactory::getDBO();
12939 + $pnotes = VikRequest::getInt('notes', '', 'request');
12940 + $pscanimg = VikRequest::getInt('scanimg', '', 'request');
12941 + $ppin = VikRequest::getInt('pin', '', 'request');
12942 + $pcountry = VikRequest::getString('country', '', 'request');
12943 + $pfromdate = VikRequest::getString('fromdate', '', 'request');
12944 + $ptodate = VikRequest::getString('todate', '', 'request');
12945 + $pdatefilt = VikRequest::getInt('datefilt', '', 'request');
12946 + $clauses = array();
12947 + if (count($cid) > 0 && !empty($cid[0])) {
12948 + $clauses[] = "`c`.`id` IN (".implode(', ', $cid).")";
12949 + }
12950 + if (!empty($pcountry)) {
12951 + $clauses[] = "`c`.`country`=".$dbo->quote($pcountry);
12952 + }
12953 + $datescol = '`bk`.`ts`';
12954 + if ($pdatefilt > 0) {
12955 + if ($pdatefilt == 1) {
12956 + $datescol = '`bk`.`ts`';
12957 + } elseif ($pdatefilt == 2) {
12958 + $datescol = '`bk`.`checkin`';
12959 + } elseif ($pdatefilt == 3) {
12960 + $datescol = '`bk`.`checkout`';
12961 + }
12962 + }
12963 + if (!empty($pfromdate)) {
12964 + $from_ts = VikBooking::getDateTimestamp($pfromdate, 0, 0);
12965 + $clauses[] = $datescol.">=".$from_ts;
12966 + }
12967 + if (!empty($ptodate)) {
12968 + $to_ts = VikBooking::getDateTimestamp($ptodate, 23, 59);
12969 + $clauses[] = $datescol."<=".$to_ts;
12970 + }
12971 + //this query below is safe with the error #1055 when sql_mode=only_full_group_by
12972 + $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`,".
12973 + "(SELECT COUNT(*) FROM `#__vikbooking_customers_orders` AS `co` WHERE `co`.`idcustomer`=`c`.`id`) AS `tot_bookings`,".
12974 + "`cy`.`country_3_code`,`cy`.`country_name` ".
12975 + "FROM `#__vikbooking_customers` AS `c` LEFT JOIN `#__vikbooking_countries` `cy` ON `cy`.`country_3_code`=`c`.`country` ".
12976 + "LEFT JOIN `#__vikbooking_customers_orders` `co` ON `co`.`idcustomer`=`c`.`id` ".
12977 + "LEFT JOIN `#__vikbooking_orders` `bk` ON `bk`.`id`=`co`.`idorder`".
12978 + (count($clauses) > 0 ? " WHERE ".implode(' AND ', $clauses) : "")."
12979 + 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` ".
12980 + "ORDER BY `c`.`last_name` ASC;";
12981 + $dbo->setQuery($q);
12982 + $customers = $dbo->loadAssocList();
12983 + if (!$customers) {
12984 + VikError::raiseWarning('', JText::translate('VBONORECORDSCSVCUSTOMERS'));
12985 + $mainframe = JFactory::getApplication();
12986 + $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
12987 + exit;
12988 + }
12989 + $csvlines = [];
12990 + $csvheadline = [
12991 + 'ID',
12992 + JText::translate('VBCUSTOMERLASTNAME'),
12993 + JText::translate('VBCUSTOMERFIRSTNAME'),
12994 + JText::translate('VBCUSTOMEREMAIL'),
12995 + JText::translate('VBCUSTOMERPHONE'),
12996 + JText::translate('VBCUSTOMERADDRESS'),
12997 + JText::translate('VBCUSTOMERCITY'),
12998 + JText::translate('VBCUSTOMERZIP'),
12999 + JText::translate('VBCUSTOMERCOUNTRY'),
13000 + JText::translate('VBCUSTOMERGENDER'),
13001 + JText::translate('ORDER_DBIRTH'),
13002 + JText::translate('VBCUSTOMERTOTBOOKINGS'),
13003 + ];
13004 + if ($ppin > 0) {
13005 + $csvheadline[] = JText::translate('VBCUSTOMERPIN');
13006 + }
13007 + if ($pscanimg > 0) {
13008 + $csvheadline[] = JText::translate('VBCUSTOMERDOCTYPE');
13009 + $csvheadline[] = JText::translate('VBCUSTOMERDOCNUM');
13010 + $csvheadline[] = JText::translate('VBCUSTOMERDOCIMG');
13011 + }
13012 + if ($pnotes > 0) {
13013 + $csvheadline[] = JText::translate('VBCUSTOMERNOTES');
13014 + }
13015 + $csvlines[] = $csvheadline;
13016 + foreach ($customers as $customer) {
13017 + $csvcustomerline = [
13018 + $customer['id'],
13019 + $customer['last_name'],
13020 + $customer['first_name'],
13021 + $customer['email'],
13022 + $customer['phone'],
13023 + $customer['address'],
13024 + $customer['city'],
13025 + $customer['zip'],
13026 + $customer['country_name'],
13027 + $customer['gender'],
13028 + $customer['bdate'],
13029 + $customer['tot_bookings'],
13030 + ];
13031 + if ($ppin > 0) {
13032 + $csvcustomerline[] = $customer['pin'];
13033 + }
13034 + if ($pscanimg > 0) {
13035 + $csvcustomerline[] = $customer['doctype'];
13036 + $csvcustomerline[] = $customer['docnum'];
13037 + $csvcustomerline[] = (!empty($customer['docimg']) ? VBO_ADMIN_URI.'resources/idscans/'.$customer['docimg'] : '');
13038 + }
13039 + if ($pnotes > 0) {
13040 + $csvcustomerline[] = $customer['notes'];
13041 + }
13042 + $csvlines[] = $csvcustomerline;
13043 + }
13044 + header("Content-type: text/csv");
13045 + header("Cache-Control: no-store, no-cache");
13046 + header('Content-Disposition: attachment; filename="customers_export_'.(!empty($pcountry) ? strtolower($pcountry).'_' : '').date('Y-m-d').'.csv"');
13047 + $outstream = fopen("php://output", 'w');
13048 + foreach ($csvlines as $csvline) {
13049 + fputcsv($outstream, $csvline, $separator = ',', $enclosure = '"', $escape = '');
13050 + }
13051 + fclose($outstream);
13052 + exit;
13053 + }
13054 +
13055 + public function renewsession() {
13056 + /*
13057 + * @wponly
13058 + * We just destroy the session
13059 + */
13060 + JSessionHandler::destroy();
13061 + $mainframe = JFactory::getApplication();
13062 + $mainframe->redirect("index.php?option=com_vikbooking&task=config");
13063 + }
13064 +
13065 + public function trackings() {
13066 + VikBookingHelper::printHeader("trackings");
13067 +
13068 + VikRequest::setVar('view', VikRequest::getCmd('view', 'trackings'));
13069 +
13070 + parent::display();
13071 +
13072 + if (VikBooking::showFooter()) {
13073 + VikBookingHelper::printFooter();
13074 + }
13075 + }
13076 +
13077 + public function trkconfig() {
13078 + VikBookingHelper::printHeader("trackings");
13079 +
13080 + VikRequest::setVar('view', VikRequest::getCmd('view', 'trkconfig'));
13081 +
13082 + parent::display();
13083 +
13084 + if (VikBooking::showFooter()) {
13085 + VikBookingHelper::printFooter();
13086 + }
13087 + }
13088 +
13089 + public function savetrkconfigstay() {
13090 + if (!JSession::checkToken()) {
13091 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13092 + }
13093 + $this->do_savetrkconfig(true);
13094 + }
13095 +
13096 + public function savetrkconfig() {
13097 + if (!JSession::checkToken()) {
13098 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13099 + }
13100 + $this->do_savetrkconfig();
13101 + }
13102 +
13103 + private function do_savetrkconfig($stay = false) {
13104 + $dbo = JFactory::getDBO();
13105 + $trkenabled = VikRequest::getInt('trkenabled', 0, 'request');
13106 + $trkenabled = $trkenabled == 1 ? 1 : 0;
13107 + $trkcookierfrdur = VikRequest::getFloat('trkcookierfrdur', 1, 'request');
13108 + $trkcookierfrdur = $trkcookierfrdur < 0.1 ? 1 : $trkcookierfrdur;
13109 + $trkcampname = VikRequest::getVar('trkcampname', array());
13110 + $trkcampkey = VikRequest::getVar('trkcampkey', array());
13111 + $trkcampval = VikRequest::getVar('trkcampval', array());
13112 + $trkcampaigns = array();
13113 + foreach ($trkcampname as $k => $v) {
13114 + if (empty($trkcampkey[$k])) {
13115 + continue;
13116 + }
13117 + $trkcampkey[$k] = str_replace(' ', '', trim($trkcampkey[$k]));
13118 + $name = !empty($v) ? $v : date('Y-m-d').' '.(count($trkcampaigns) + 1);
13119 + $trkcampaigns[$trkcampkey[$k]] = array(
13120 + 'key' => $trkcampkey[$k],
13121 + 'value' => $trkcampval[$k],
13122 + 'name' => $name,
13123 + );
13124 + }
13125 +
13126 + $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($trkenabled)." WHERE `param`='trkenabled';";
13127 + $dbo->setQuery($q);
13128 + $dbo->execute();
13129 + $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote($trkcookierfrdur)." WHERE `param`='trkcookierfrdur';";
13130 + $dbo->setQuery($q);
13131 + $dbo->execute();
13132 + $q = "UPDATE `#__vikbooking_config` SET `setting`=".$dbo->quote(json_encode($trkcampaigns))." WHERE `param`='trkcampaigns';";
13133 + $dbo->setQuery($q);
13134 + $dbo->execute();
13135 +
13136 + $mainframe = JFactory::getApplication();
13137 + $mainframe->redirect("index.php?option=com_vikbooking&task=".($stay ? 'trkconfig' : 'trackings'));
13138 + }
13139 +
13140 + public function modtracking() {
13141 + $dbo = JFactory::getDbo();
13142 + $cid = VikRequest::getVar('cid', array());
13143 + foreach ($cid as $id) {
13144 + if (!empty($id)) {
13145 + $q = "SELECT `id`,`published` FROM `#__vikbooking_trackings` WHERE `id`=".(int)$id.";";
13146 + $dbo->setQuery($q);
13147 + $dbo->execute();
13148 + if ($dbo->getNumRows()) {
13149 + $data = $dbo->loadAssoc();
13150 + $q = "UPDATE `#__vikbooking_trackings` SET `published`=".($data['published'] ? '0' : '1')." WHERE `id`=".(int)$data['id'].";";
13151 + $dbo->setQuery($q);
13152 + $dbo->execute();
13153 + }
13154 + }
13155 + }
13156 + $mainframe = JFactory::getApplication();
13157 + $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13158 + }
13159 +
13160 + public function removetrackings()
13161 + {
13162 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
13163 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13164 + }
13165 +
13166 + $ids = VikRequest::getVar('cid', array());
13167 + $dbo = JFactory::getDbo();
13168 +
13169 + foreach ($ids as $d) {
13170 + $q = "DELETE FROM `#__vikbooking_trackings` WHERE `id`=".(int)$d.";";
13171 + $dbo->setQuery($q);
13172 + $dbo->execute();
13173 + $q = "DELETE FROM `#__vikbooking_tracking_infos` WHERE `idtracking`=".(int)$d.";";
13174 + $dbo->setQuery($q);
13175 + $dbo->execute();
13176 + }
13177 +
13178 + $mainframe = JFactory::getApplication();
13179 + $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13180 + }
13181 +
13182 + /**
13183 + * Invokes the Tracker class to obtain
13184 + * geo information about the IP addresses.
13185 + * This task is called via ajax.
13186 + *
13187 + * @since 1.11
13188 + */
13189 + public function getgeoinfo() {
13190 + $ips = VikRequest::getVar('ips', array());
13191 + if (!count($ips)) {
13192 + echo 'e4j.error.empty IPs';
13193 + exit;
13194 + }
13195 +
13196 + // require the Tracker class without instantiating the object
13197 + VikBooking::getTracker(true);
13198 + $geo_info = VikBookingTracker::getIpGeoInfo($ips);
13199 +
13200 + if ($geo_info === false) {
13201 + echo 'e4j.error.Tracker error, could not get geo info from IPs';
13202 + exit;
13203 + }
13204 +
13205 + // update db values and compose response
13206 + $dbo = JFactory::getDbo();
13207 + $resp = array();
13208 + foreach ($geo_info as $id => $geo) {
13209 + if (is_null($geo) || $geo === false) {
13210 + continue;
13211 + }
13212 + // compose geo info string
13213 + $geovals = array();
13214 + if (!empty($geo['city'])) {
13215 + array_push($geovals, $geo['city']);
13216 + }
13217 + if (!empty($geo['region'])) {
13218 + array_push($geovals, $geo['region']);
13219 + }
13220 + $threecode = '';
13221 + $cname = '';
13222 + if (!empty($geo['country'])) {
13223 + // returned country is a 2-char code, get the 3-char country code
13224 + $q = "SELECT `country_3_code`,`country_name` FROM `#__vikbooking_countries` WHERE `country_2_code`=".$dbo->quote($geo['country']).";";
13225 + $dbo->setQuery($q);
13226 + $dbo->execute();
13227 + if ($dbo->getNumRows()) {
13228 + $cinfo = $dbo->loadAssoc();
13229 + $threecode = $cinfo['country_3_code'];
13230 + $cname = $cinfo['country_name'];
13231 + }
13232 + array_push($geovals, (empty($cname) ? $geo['country'] : $cname));
13233 + }
13234 +
13235 + // full geo information string
13236 + $geoinfostr = implode(', ', $geovals);
13237 +
13238 + // push data to the response pool
13239 + $resp[$id] = array();
13240 + $resp[$id]['geo'] = $geoinfostr;
13241 + if (!empty($cname)) {
13242 + $resp[$id]['country'] = $cname;
13243 + }
13244 + if (!empty($threecode)) {
13245 + $resp[$id]['country3'] = $threecode;
13246 + }
13247 +
13248 + // update main tracking record
13249 + $q = "UPDATE `#__vikbooking_trackings` SET `geo`=".$dbo->quote($geoinfostr).(!empty($threecode) ? ', `country`='.$dbo->quote($threecode) : '')." WHERE `id`=".(int)$id.";";
13250 + $dbo->setQuery($q);
13251 + $dbo->execute();
13252 + }
13253 +
13254 + // output the JSON response
13255 + echo json_encode($resp);
13256 + exit;
13257 + }
13258 +
13259 + /**
13260 + * Counts the orphan dates for all published rooms
13261 + * depending on their restrictions and booked dates.
13262 + * By default, the task takes up to 3 months ahead.
13263 + * It is possible to filter the request by rooms and months.
13264 + * This task should be called via ajax.
13265 + *
13266 + * @since 1.11
13267 + */
13268 + public function orphanscount()
13269 + {
13270 + $dbo = JFactory::getDbo();
13271 + $orphans = array();
13272 +
13273 + $nowdf = VikBooking::getDateFormat();
13274 + if ($nowdf == "%d/%m/%Y") {
13275 + $df = 'd/m/Y';
13276 + } elseif ($nowdf == "%m/%d/%Y") {
13277 + $df = 'm/d/Y';
13278 + } else {
13279 + $df = 'Y/m/d';
13280 + }
13281 +
13282 + // global min los
13283 + $glob_minlos = VikBooking::getDefaultNightsCalendar();
13284 + $glob_minlos = $glob_minlos < 1 ? 1 : $glob_minlos;
13285 +
13286 + // rooms and dates
13287 + $roomids = VikRequest::getVar('roomids', array(), 'request', 'int');
13288 + $months = VikRequest::getInt('months', 3, 'request');
13289 + $from = VikRequest::getString('from', '', 'request');
13290 + $today = strtotime(date('Y').'-'.date('m').'-'.date('d'));
13291 + if (!empty($from)) {
13292 + $fromts = VikBooking::getDateTimestamp($from, 0, 0);
13293 + if (!empty($fromts)) {
13294 + // custom starting date
13295 + $today = $fromts;
13296 + }
13297 + }
13298 + $until = strtotime("+{$months} months", $today);
13299 +
13300 + // load all rooms
13301 + $rooms = array();
13302 + $q = "SELECT `id`,`name`,`units` FROM `#__vikbooking_rooms` WHERE `avail`=1".(count($roomids) ? ' AND `id` IN ('.implode(', ', $roomids).')' : '').";";
13303 + $dbo->setQuery($q);
13304 + $dbo->execute();
13305 + if ($dbo->getNumRows()) {
13306 + $allrooms = $dbo->loadAssocList();
13307 + foreach ($allrooms as $r) {
13308 + $rooms[$r['id']] = $r;
13309 + }
13310 + }
13311 + if (!count($rooms)) {
13312 + // no rooms found, exit
13313 + echo json_encode($orphans);
13314 + exit;
13315 + }
13316 +
13317 + // load availabilities
13318 + $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.");";
13319 + $dbo->setQuery($q);
13320 + $dbo->execute();
13321 + if (!$dbo->getNumRows()) {
13322 + // no booked dates found, exit
13323 + echo json_encode($orphans);
13324 + exit;
13325 + }
13326 + $busy = $dbo->loadAssocList();
13327 +
13328 + // sort booked dates by room id
13329 + $rooms_busy = array();
13330 + foreach ($busy as $b) {
13331 + if (!isset($rooms_busy[$b['idroom']])) {
13332 + $rooms_busy[$b['idroom']] = array();
13333 + }
13334 + array_push($rooms_busy[$b['idroom']], $b);
13335 + }
13336 +
13337 + // load restrictions
13338 + $rooms_restr = array();
13339 + foreach ($rooms as $rid => $r) {
13340 + $restrictions = VikBooking::loadRestrictions(true, array($rid));
13341 + if (count($restrictions)) {
13342 + $rooms_restr[$rid] = $restrictions;
13343 + }
13344 + }
13345 + if (!count($rooms_restr) && $glob_minlos < 2) {
13346 + // no restrictions found and minlos=1, exit
13347 + echo json_encode($orphans);
13348 + exit;
13349 + }
13350 +
13351 + // count availability and minlos per day
13352 + $rooms_data = array();
13353 + foreach ($rooms as $rid => $r) {
13354 + $rooms_data[$rid] = array(
13355 + 'avail' => array(),
13356 + 'restr' => array()
13357 + );
13358 + $nowts = getdate($today);
13359 + while ($nowts[0] <= $until) {
13360 + $dateind = date('Y-m-d', $nowts[0]);
13361 +
13362 + // remaining availability
13363 + if (!isset($rooms_busy[$rid])) {
13364 + // no bookings for this room, set full availability for this day
13365 + $rooms_data[$rid]['avail'][] = array(
13366 + 'dt' => $dateind,
13367 + 'units' => $r['units']
13368 + );
13369 + } else {
13370 + // check remaining availability for this day
13371 + $totfound = 0;
13372 + foreach ($rooms_busy[$rid] as $b) {
13373 + $tmpone = getdate($b['checkin']);
13374 + $rit = ($tmpone['mon'] < 10 ? "0".$tmpone['mon'] : $tmpone['mon'])."/".($tmpone['mday'] < 10 ? "0".$tmpone['mday'] : $tmpone['mday'])."/".$tmpone['year'];
13375 + $ritts = strtotime($rit);
13376 + $tmptwo = getdate($b['checkout']);
13377 + $con = ($tmptwo['mon'] < 10 ? "0".$tmptwo['mon'] : $tmptwo['mon'])."/".($tmptwo['mday'] < 10 ? "0".$tmptwo['mday'] : $tmptwo['mday'])."/".$tmptwo['year'];
13378 + $conts = strtotime($con);
13379 + if ($nowts[0] >= $ritts && $nowts[0] < $conts) {
13380 + $totfound++;
13381 + }
13382 + }
13383 + $totfound = $totfound > $r['units'] ? $r['units'] : $totfound;
13384 + $rooms_data[$rid]['avail'][] = array(
13385 + 'dt' => $dateind,
13386 + 'units' => ($r['units'] - $totfound)
13387 + );
13388 + }
13389 +
13390 + // restrictions
13391 + if (!isset($rooms_restr[$rid])) {
13392 + // no restrictions for this room, set global minlos for this day
13393 + $rooms_data[$rid]['restr'][] = array(
13394 + 'dt' => $dateind,
13395 + 'minlos' => $glob_minlos
13396 + );
13397 + } else {
13398 + // get restriction for this day
13399 + $today_tsin = mktime(0, 0, 0, $nowts['mon'], $nowts['mday'], $nowts['year']);
13400 + $today_tsout = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
13401 +
13402 + $restr = VikBooking::parseSeasonRestrictions($today_tsin, $today_tsout, 1, $rooms_restr[$rid]);
13403 + $minlos = count($restr) ? $restr['minlos'] : $glob_minlos;
13404 +
13405 + $rooms_data[$rid]['restr'][] = array(
13406 + 'dt' => $dateind,
13407 + 'minlos' => $minlos
13408 + );
13409 + }
13410 +
13411 + // next loop
13412 + $dayts = mktime(0, 0, 0, $nowts['mon'], ($nowts['mday'] + 1), $nowts['year']);
13413 + $nowts = getdate($dayts);
13414 + }
13415 + }
13416 +
13417 + // week days and months labels
13418 + $days_labels = array(
13419 + JText::translate('VBSUNDAY'),
13420 + JText::translate('VBMONDAY'),
13421 + JText::translate('VBTUESDAY'),
13422 + JText::translate('VBWEDNESDAY'),
13423 + JText::translate('VBTHURSDAY'),
13424 + JText::translate('VBFRIDAY'),
13425 + JText::translate('VBSATURDAY')
13426 + );
13427 + $months_labels = array(
13428 + JText::translate('VBMONTHONE'),
13429 + JText::translate('VBMONTHTWO'),
13430 + JText::translate('VBMONTHTHREE'),
13431 + JText::translate('VBMONTHFOUR'),
13432 + JText::translate('VBMONTHFIVE'),
13433 + JText::translate('VBMONTHSIX'),
13434 + JText::translate('VBMONTHSEVEN'),
13435 + JText::translate('VBMONTHEIGHT'),
13436 + JText::translate('VBMONTHNINE'),
13437 + JText::translate('VBMONTHTEN'),
13438 + JText::translate('VBMONTHELEVEN'),
13439 + JText::translate('VBMONTHTWELVE')
13440 + );
13441 +
13442 + // orphan dates calculation method
13443 + $calc_method = VikBooking::orphansCalculation();
13444 +
13445 + // parse data and build orphans if any
13446 + foreach ($rooms_data as $rid => $data) {
13447 + foreach ($data['avail'] as $ind => $av) {
13448 + if (!isset($data['restr'][$ind]) || $av['units'] < 1) {
13449 + // continue, no restriction set or no availability for this day
13450 + continue;
13451 + }
13452 + if ($data['restr'][$ind]['minlos'] < 2) {
13453 + // continue, no min los > 1 set for this day
13454 + continue;
13455 + }
13456 + // check if any night after today, until min los, is fully booked
13457 + $hasorphans = false;
13458 + $forward_count = 0;
13459 + for ($i = 1; $i < $data['restr'][$ind]['minlos']; $i++) {
13460 + if (!isset($data['avail'][($ind + $i)])) {
13461 + // break loop, no info for this day after
13462 + break;
13463 + }
13464 + if ($data['avail'][($ind + $i)]['units'] > 0) {
13465 + // continue, availability found for tomorrow, we need a non available next-day
13466 + continue;
13467 + }
13468 + // orphan found
13469 + $hasorphans = true;
13470 + $forward_count = $i;
13471 + break;
13472 + }
13473 +
13474 + /**
13475 + * Backward calculation method only if "prevnext".
13476 + *
13477 + * @since 1.3.0
13478 + */
13479 + $backward_count = 0;
13480 + for ($i = 1; $i <= $data['restr'][$ind]['minlos']; $i++) {
13481 + if (!isset($data['avail'][($ind - $i)])) {
13482 + // break loop, no info for this prev day
13483 + break;
13484 + }
13485 + if ($data['avail'][($ind - $i)]['units'] > 0) {
13486 + // increase free nights going backward
13487 + $backward_count++;
13488 + }
13489 + }
13490 + if ($calc_method == 'prevnext' && $hasorphans && $backward_count > 0 && ($backward_count >= $data['restr'][$ind]['minlos'] || ($backward_count + $forward_count) >= $data['restr'][$ind]['minlos'])) {
13491 + // this should not be an orphan date because of enough free days back, or enough free days in between
13492 + $hasorphans = false;
13493 + }
13494 + //
13495 +
13496 + if ($hasorphans) {
13497 + // 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
13498 + if (!isset($orphans[$rid])) {
13499 + $orphans[$rid] = array(
13500 + 'name' => $rooms[$rid]['name'],
13501 + 'dates' => array(),
13502 + 'rdates' => array(),
13503 + 'linkd' => date($df, strtotime($av['dt']))
13504 + );
13505 + }
13506 + array_push($orphans[$rid]['dates'], $av['dt']);
13507 + // build the value for the readable date
13508 + $dtinfo = getdate(strtotime($av['dt']));
13509 + $rdate = $days_labels[$dtinfo['wday']] . ', ' . $months_labels[($dtinfo['mon'] - 1)] . ' ' . $dtinfo['mday'] . ' ' . $dtinfo['year'];
13510 + array_push($orphans[$rid]['rdates'], $rdate);
13511 + }
13512 + }
13513 + }
13514 +
13515 + // output response
13516 + echo json_encode($orphans);
13517 + exit;
13518 + }
13519 +
13520 + public function tableaux() {
13521 + VikBookingHelper::printHeader("tableaux");
13522 +
13523 + VikRequest::setVar('view', VikRequest::getCmd('view', 'tableaux'));
13524 +
13525 + parent::display();
13526 +
13527 + if (VikBooking::showFooter()) {
13528 + VikBookingHelper::printFooter();
13529 + }
13530 + }
13531 +
13532 + public function operators() {
13533 + VikBookingHelper::printHeader("operators");
13534 +
13535 + VikRequest::setVar('view', VikRequest::getCmd('view', 'operators'));
13536 +
13537 + parent::display();
13538 +
13539 + if (VikBooking::showFooter()) {
13540 + VikBookingHelper::printFooter();
13541 + }
13542 + }
13543 +
13544 + public function newoperator() {
13545 + VikBookingHelper::printHeader("operators");
13546 +
13547 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoperator'));
13548 +
13549 + parent::display();
13550 +
13551 + if (VikBooking::showFooter()) {
13552 + VikBookingHelper::printFooter();
13553 + }
13554 + }
13555 +
13556 + public function editoperator() {
13557 + VikBookingHelper::printHeader("operators");
13558 +
13559 + VikRequest::setVar('view', VikRequest::getCmd('view', 'manageoperator'));
13560 +
13561 + parent::display();
13562 +
13563 + if (VikBooking::showFooter()) {
13564 + VikBookingHelper::printFooter();
13565 + }
13566 + }
13567 +
13568 + public function updateoperator()
13569 + {
13570 + if (!JSession::checkToken()) {
13571 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13572 + }
13573 +
13574 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
13575 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13576 + }
13577 +
13578 + $this->do_updateoperator();
13579 + }
13580 +
13581 + public function updateoperatorstay()
13582 + {
13583 + if (!JSession::checkToken()) {
13584 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13585 + }
13586 +
13587 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
13588 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13589 + }
13590 +
13591 + $this->do_updateoperator(true);
13592 + }
13593 +
13594 + private function do_updateoperator($stay = false)
13595 + {
13596 + $dbo = JFactory::getDbo();
13597 + $app = JFactory::getApplication();
13598 + $pfirst_name = VikRequest::getString('first_name', '', 'request');
13599 + $plast_name = VikRequest::getString('last_name', '', 'request');
13600 + $pemail = VikRequest::getString('email', '', 'request');
13601 + $pphone = VikRequest::getString('phone', '', 'request');
13602 + $pcode = VikRequest::getString('code', '', 'request');
13603 + $pujid = VikRequest::getInt('ujid', '', 'request');
13604 + $pwhere = VikRequest::getInt('where', '', 'request');
13605 +
13606 + $work_days_week = (array) $app->input->get('work_days_week', [], 'array');
13607 + $work_days_exceptions = (array) $app->input->get('work_days_exceptions', [], 'array');
13608 +
13609 + // normalize to linear arrays
13610 + $work_days_week_schedule = array_combine(array_keys($work_days_week), array_values($work_days_week));
13611 + $work_days_week = [];
13612 + foreach ($work_days_week_schedule as $wday => $whours) {
13613 + $work_days_week[] = [
13614 + 'wday' => $wday,
13615 + 'hours' => $whours,
13616 + ];
13617 + }
13618 + foreach ($work_days_exceptions as &$wexceptions) {
13619 + if (is_scalar($wexceptions)) {
13620 + $wexceptions = json_decode($wexceptions, true);
13621 + }
13622 + }
13623 + unset($wexceptions);
13624 +
13625 + if (!empty($pfirst_name) && !empty($pemail) && !empty($pcode)) {
13626 + $q = "SELECT * FROM `#__vikbooking_operators` WHERE `id`=".(int)$pwhere." LIMIT 1;";
13627 + $dbo->setQuery($q);
13628 + $customer = $dbo->loadAssoc();
13629 + if (!$customer) {
13630 + $app->redirect("index.php?option=com_vikbooking&task=operators");
13631 + exit;
13632 + }
13633 +
13634 + $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;";
13635 + $dbo->setQuery($q);
13636 + $ex_operator = $dbo->loadAssoc();
13637 + if (!$ex_operator) {
13638 + // update fingerprint for the operator
13639 + $fingpt = md5($pwhere . $pemail);
13640 +
13641 + /**
13642 + * Operator profile picture (URL or uploaded file).
13643 + *
13644 + * @since 1.16.9 (J) - 1.6.9 (WP)
13645 + */
13646 + $operator_pic = VikRequest::getString('pic', '', 'request');
13647 + $operator_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
13648 + if (is_array($operator_pic_img) && !empty($operator_pic_img['name'])) {
13649 + $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($operator_pic_img['name'])));
13650 + $src = $operator_pic_img['tmp_name'];
13651 + $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
13652 + $j = "";
13653 + if (is_file($dest.$filename)) {
13654 + $j = rand(1, 99999);
13655 + while (is_file($dest . $j .$filename)) {
13656 + $j++;
13657 + }
13658 + }
13659 + $finaldest = $dest . $j . $filename;
13660 + $check = getimagesize($operator_pic_img['tmp_name']);
13661 + if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
13662 + if (VikBooking::uploadFile($src, $finaldest)) {
13663 + $operator_pic = $j . $filename;
13664 + } else {
13665 + VikError::raiseWarning('', 'Error while uploading image');
13666 + }
13667 + } else {
13668 + VikError::raiseWarning('', 'Uploaded file is not an Image');
13669 + }
13670 + }
13671 +
13672 + // update record
13673 + $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;
13674 + $dbo->setQuery($q);
13675 + $dbo->execute();
13676 + $app->enqueueMessage(JText::translate('VBOPERATORSAVED'));
13677 + } else {
13678 + //email already exists
13679 + 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>');
13680 + $app->redirect("index.php?option=com_vikbooking&task=editoperator&cid[]=".$pwhere);
13681 + exit;
13682 + }
13683 + } else {
13684 + VikError::raiseWarning('', JText::translate('VBERROPERATORDATA'));
13685 + }
13686 +
13687 + if ($stay) {
13688 + $app->redirect("index.php?option=com_vikbooking&task=editoperator&cid[]=".$pwhere);
13689 + } else {
13690 + $app->redirect("index.php?option=com_vikbooking&task=operators");
13691 + }
13692 +
13693 + $app->close();
13694 + }
13695 +
13696 + public function saveoperator()
13697 + {
13698 + if (!JSession::checkToken()) {
13699 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13700 + }
13701 +
13702 + $dbo = JFactory::getDbo();
13703 + $app = JFactory::getApplication();
13704 + $pfirst_name = VikRequest::getString('first_name', '', 'request');
13705 + $plast_name = VikRequest::getString('last_name', '', 'request');
13706 + $pemail = VikRequest::getString('email', '', 'request');
13707 + $pphone = VikRequest::getString('phone', '', 'request');
13708 + $pcode = VikRequest::getString('code', '', 'request');
13709 + $pujid = VikRequest::getInt('ujid', '', 'request');
13710 +
13711 + $work_days_week = (array) $app->input->get('work_days_week', [], 'array');
13712 + $work_days_exceptions = (array) $app->input->get('work_days_exceptions', [], 'array');
13713 +
13714 + // normalize to linear arrays
13715 + $work_days_week_schedule = array_combine(array_keys($work_days_week), array_values($work_days_week));
13716 + $work_days_week = [];
13717 + foreach ($work_days_week_schedule as $wday => $whours) {
13718 + $work_days_week[] = [
13719 + 'wday' => $wday,
13720 + 'hours' => $whours,
13721 + ];
13722 + }
13723 + foreach ($work_days_exceptions as &$wexceptions) {
13724 + if (is_scalar($wexceptions)) {
13725 + $wexceptions = json_decode($wexceptions, true);
13726 + }
13727 + }
13728 + unset($wexceptions);
13729 +
13730 + if (!empty($pfirst_name) && !empty($pemail) && !empty($pcode)) {
13731 + $q = "SELECT * FROM `#__vikbooking_operators` WHERE `email`=".$dbo->quote($pemail)." OR ".(!empty($pcode) ? "`code`=".$dbo->quote($pcode) : "`ujid`=".$dbo->quote($pujid))." LIMIT 1;";
13732 + $dbo->setQuery($q);
13733 + $ex_operator = $dbo->loadAssoc();
13734 + if (!$ex_operator) {
13735 + /**
13736 + * Operator profile picture (URL or uploaded file).
13737 + *
13738 + * @since 1.16.9 (J) - 1.6.9 (WP)
13739 + */
13740 + $operator_pic = VikRequest::getString('pic', '', 'request');
13741 + $operator_pic_img = VikRequest::getVar('picimg', null, 'files', 'array');
13742 + if (is_array($operator_pic_img) && !empty($operator_pic_img['name'])) {
13743 + $filename = JFile::makeSafe(rand(100, 9999).str_replace(" ", "_", strtolower($operator_pic_img['name'])));
13744 + $src = $operator_pic_img['tmp_name'];
13745 + $dest = VBO_SITE_PATH.DIRECTORY_SEPARATOR.'resources'.DIRECTORY_SEPARATOR.'uploads'.DIRECTORY_SEPARATOR;
13746 + $j = "";
13747 + if (is_file($dest.$filename)) {
13748 + $j = rand(1, 99999);
13749 + while (is_file($dest . $j .$filename)) {
13750 + $j++;
13751 + }
13752 + }
13753 + $finaldest = $dest . $j . $filename;
13754 + $check = getimagesize($operator_pic_img['tmp_name']);
13755 + if (($check[2] & imagetypes()) && preg_match("/\.(a?png|jpe?g|bmp|gif|ico|webp)\z/i", $filename)) {
13756 + if (VikBooking::uploadFile($src, $finaldest)) {
13757 + $operator_pic = $j . $filename;
13758 + } else {
13759 + VikError::raiseWarning('', 'Error while uploading image');
13760 + }
13761 + } else {
13762 + VikError::raiseWarning('', 'Uploaded file is not an Image');
13763 + }
13764 + }
13765 +
13766 + $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') . ");";
13767 + $dbo->setQuery($q);
13768 + $dbo->execute();
13769 + $lid = $dbo->insertid();
13770 + if (!empty($lid)) {
13771 + $app->enqueueMessage(JText::translate('VBOPERATORSAVED'));
13772 + // generate fingerprint for the operator
13773 + $q = "UPDATE `#__vikbooking_operators` SET `fingpt`=".$dbo->q(md5($lid.$pemail))." WHERE `id`=".(int)$lid.";";
13774 + $dbo->setQuery($q);
13775 + $dbo->execute();
13776 + }
13777 + } else {
13778 + // email already exists
13779 + 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>');
13780 + }
13781 + } else {
13782 + VikError::raiseWarning('', JText::translate('VBERROPERATORDATA'));
13783 + }
13784 +
13785 + $app->redirect("index.php?option=com_vikbooking&task=operators");
13786 + $app->close();
13787 + }
13788 +
13789 + public function removeoperators()
13790 + {
13791 + if (!JSession::checkToken()) {
13792 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
13793 + }
13794 +
13795 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
13796 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
13797 + }
13798 +
13799 + $ids = VikRequest::getVar('cid', array(0));
13800 + if ($ids) {
13801 + $dbo = JFactory::getDBO();
13802 + foreach ($ids as $d) {
13803 + $q = "DELETE FROM `#__vikbooking_operators` WHERE `id`=".(int)$d.";";
13804 + $dbo->setQuery($q);
13805 + $dbo->execute();
13806 + }
13807 + }
13808 + $mainframe = JFactory::getApplication();
13809 + $mainframe->redirect("index.php?option=com_vikbooking&task=operators");
13810 + }
13811 +
13812 + public function canceloperator() {
13813 + $mainframe = JFactory::getApplication();
13814 + $mainframe->redirect("index.php?option=com_vikbooking&task=operators");
13815 + }
13816 +
13817 + public function cancelcrons() {
13818 + $mainframe = JFactory::getApplication();
13819 + $mainframe->redirect("index.php?option=com_vikbooking&task=crons");
13820 + }
13821 +
13822 + public function cancelpackages() {
13823 + $mainframe = JFactory::getApplication();
13824 + $mainframe->redirect("index.php?option=com_vikbooking&task=packages");
13825 + }
13826 +
13827 + public function cancelcustomer() {
13828 + $mainframe = JFactory::getApplication();
13829 + $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
13830 + if (!empty($pgoto)) {
13831 + $mainframe->redirect(base64_decode($pgoto));
13832 + exit;
13833 + }
13834 + $mainframe->redirect("index.php?option=com_vikbooking&task=customers");
13835 + }
13836 +
13837 + public function cancelbusyvcm() {
13838 + $mainframe = JFactory::getApplication();
13839 + $mainframe->redirect("index.php?option=com_vikchannelmanager&task=oversight");
13840 + }
13841 +
13842 + public function cancelrestriction() {
13843 + $mainframe = JFactory::getApplication();
13844 + $mainframe->redirect("index.php?option=com_vikbooking&task=restrictions");
13845 + }
13846 +
13847 + public function cancelcoupon() {
13848 + $mainframe = JFactory::getApplication();
13849 + $mainframe->redirect("index.php?option=com_vikbooking&task=coupons");
13850 + }
13851 +
13852 + public function cancelcustomf() {
13853 + $mainframe = JFactory::getApplication();
13854 + $mainframe->redirect("index.php?option=com_vikbooking&task=customf");
13855 + }
13856 +
13857 + public function cancelpayment() {
13858 + $mainframe = JFactory::getApplication();
13859 + $mainframe->redirect("index.php?option=com_vikbooking&task=payments");
13860 + }
13861 +
13862 + public function cancelseason() {
13863 + $mainframe = JFactory::getApplication();
13864 + $mainframe->redirect("index.php?option=com_vikbooking&task=seasons");
13865 + }
13866 +
13867 + public function goconfig() {
13868 + $mainframe = JFactory::getApplication();
13869 + $mainframe->redirect("index.php?option=com_vikbooking&task=config");
13870 + }
13871 +
13872 + public function canceledorder() {
13873 + $pgoto = VikRequest::getString('goto', 'orders', 'request');
13874 + $mainframe = JFactory::getApplication();
13875 + $mainframe->redirect("index.php?option=com_vikbooking&task=" . $pgoto);
13876 + }
13877 +
13878 + public function cancelbusy() {
13879 + $pidorder = VikRequest::getString('idorder', '', 'request');
13880 + $pgoto = VikRequest::getString('goto', '', 'request');
13881 + $mainframe = JFactory::getApplication();
13882 + $mainframe->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=".$pidorder.($pgoto == 'overv' ? '&goto=overv' : ''));
13883 + }
13884 +
13885 + public function canceloverv() {
13886 + $mainframe = JFactory::getApplication();
13887 + $mainframe->redirect("index.php?option=com_vikbooking&task=overv");
13888 + }
13889 +
13890 + public function canceltableaux() {
13891 + $mainframe = JFactory::getApplication();
13892 + $mainframe->redirect("index.php?option=com_vikbooking&task=tableaux");
13893 + }
13894 +
13895 + public function cancelcalendar() {
13896 + $pidroom = VikRequest::getString('idroom', '', 'request');
13897 + $mainframe = JFactory::getApplication();
13898 + $mainframe->redirect("index.php?option=com_vikbooking&task=calendar&cid[]=".$pidroom);
13899 + }
13900 +
13901 + public function canceloptionals() {
13902 + $mainframe = JFactory::getApplication();
13903 + $mainframe->redirect("index.php?option=com_vikbooking&task=optionals");
13904 + }
13905 +
13906 + public function cancel() {
13907 + $mainframe = JFactory::getApplication();
13908 + $mainframe->redirect("index.php?option=com_vikbooking&task=rooms");
13909 + }
13910 +
13911 + public function cancelcarat() {
13912 + $mainframe = JFactory::getApplication();
13913 + $mainframe->redirect("index.php?option=com_vikbooking&task=carat");
13914 + }
13915 +
13916 + public function cancelcat() {
13917 + $mainframe = JFactory::getApplication();
13918 + $mainframe->redirect("index.php?option=com_vikbooking&task=categories");
13919 + }
13920 +
13921 + public function cancelprice() {
13922 + $mainframe = JFactory::getApplication();
13923 + $mainframe->redirect("index.php?option=com_vikbooking&task=prices");
13924 + }
13925 +
13926 + public function canceliva() {
13927 + $mainframe = JFactory::getApplication();
13928 + $mainframe->redirect("index.php?option=com_vikbooking&task=iva");
13929 + }
13930 +
13931 + public function canceltrk() {
13932 + $mainframe = JFactory::getApplication();
13933 + $mainframe->redirect("index.php?option=com_vikbooking&task=trackings");
13934 + }
13935 +
13936 + public function canceldash() {
13937 + $mainframe = JFactory::getApplication();
13938 + $mainframe->redirect("index.php?option=com_vikbooking");
13939 + }
13940 +
13941 + public function cancelinvoice() {
13942 + $mainframe = JFactory::getApplication();
13943 + $pgoto = VikRequest::getString('goto', '', 'request', VIKREQUEST_ALLOWRAW);
13944 + if (!empty($pgoto)) {
13945 + $mainframe->redirect(base64_decode($pgoto));
13946 + exit;
13947 + }
13948 + $mainframe->redirect("index.php?option=com_vikbooking&task=invoices");
13949 + }
13950 +
13951 + /**
13952 + * AJAX upload the customer documents.
13953 + *
13954 + * @return void
13955 + *
13956 + * @throws Exception
13957 + */
13958 + public function upload_customer_document()
13959 + {
13960 + $app = JFactory::getApplication();
13961 +
13962 + if (!JSession::checkToken()) {
13963 + // missing CSRF-proof token
13964 + VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
13965 + }
13966 +
13967 + $dbo = JFactory::getDbo();
13968 + $input = $app->input;
13969 +
13970 + $customer_id = $input->getUint('customer', 0);
13971 +
13972 + $result = new stdClass;
13973 + $result->status = 0;
13974 +
13975 + try
13976 + {
13977 + $q = $dbo->getQuery(true)
13978 + ->select($dbo->qn(array(
13979 + 'id',
13980 + 'first_name',
13981 + 'last_name',
13982 + 'email',
13983 + 'docsfolder',
13984 + )))
13985 + ->from($dbo->qn('#__vikbooking_customers'))
13986 + ->where($dbo->qn('id') . ' = ' . $customer_id);
13987 +
13988 + $dbo->setQuery($q, 0, 1);
13989 + $customer = $dbo->loadObject();
13990 +
13991 + if (!$customer)
13992 + {
13993 + throw new Exception(sprintf('Customer [%d] not found', $customer_id), 404);
13994 + }
13995 +
13996 + // fetch documents folder path
13997 + $dirpath = VBO_CUSTOMERS_PATH . DIRECTORY_SEPARATOR;
13998 +
13999 + // check if we have a valid directory
14000 + if (empty($customer->docsfolder) || !is_dir($dirpath . $customer->docsfolder))
14001 + {
14002 + // randomize string
14003 + $customer->seed = uniqid();
14004 +
14005 + // create blocks for hashed folder
14006 + $parts = [
14007 + $customer->first_name,
14008 + $customer->last_name,
14009 + md5(serialize($customer)),
14010 + ];
14011 +
14012 + // join fetched parts
14013 + $customer->docsfolder = JFilterOutput::stringURLSafe(implode('-', array_filter($parts)));
14014 +
14015 + if (strlen($customer->docsfolder) < 16)
14016 + {
14017 + throw new Exception('Possible security breach. Please specify the most details as possible.', 400);
14018 + }
14019 +
14020 + jimport('joomla.filesystem.folder');
14021 +
14022 + // create a folder for this customer
14023 + $created = JFolder::create($dirpath . $customer->docsfolder);
14024 +
14025 + if (!$created)
14026 + {
14027 + throw new Exception(sprintf('Unable to create the folder [%s]', $dirpath . $customer->docsfolder), 403);
14028 + }
14029 +
14030 + unset($customer->seed);
14031 +
14032 + // update docs folder
14033 + $dbo->updateObject('#__vikbooking_customers', $customer, 'id');
14034 + }
14035 +
14036 + // get file from request
14037 + $file = $input->files->get('file', array(), 'array');
14038 +
14039 + // try to upload the file
14040 + $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');
14041 + $result->status = 1;
14042 +
14043 + $result->size = JHtml::fetch('number.bytes', filesize($result->path), 'auto', 0);
14044 + $result->url = str_replace(DIRECTORY_SEPARATOR, '/', str_replace(VBO_CUSTOMERS_PATH . DIRECTORY_SEPARATOR, VBO_CUSTOMERS_URI, $result->path));
14045 + }
14046 + catch (Exception $e)
14047 + {
14048 + $result->error = $e->getMessage();
14049 + $result->code = $e->getCode();
14050 + }
14051 +
14052 + VBOHttpDocument::getInstance($app)->json($result);
14053 + }
14054 +
14055 + /**
14056 + * AJAX delete the customer documents.
14057 + *
14058 + * @return void
14059 + *
14060 + * @throws Exception
14061 + */
14062 + public function delete_customer_document()
14063 + {
14064 + $app = JFactory::getApplication();
14065 +
14066 + if (!JSession::checkToken()) {
14067 + // missing CSRF-proof token
14068 + VBOHttpDocument::getInstance($app)->close(403, JText::translate('JINVALID_TOKEN'));
14069 + }
14070 +
14071 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
14072 + VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14073 + }
14074 +
14075 + $dbo = JFactory::getDbo();
14076 + $input = $app->input;
14077 +
14078 + $customer_id = $input->getUint('customer', 0);
14079 +
14080 + $result = new stdClass;
14081 + $result->status = 0;
14082 +
14083 + $q = $dbo->getQuery(true)
14084 + ->select($dbo->qn('docsfolder'))
14085 + ->from($dbo->qn('#__vikbooking_customers'))
14086 + ->where($dbo->qn('id') . ' = ' . $customer_id);
14087 +
14088 + $dbo->setQuery($q, 0, 1);
14089 + $dbo->execute();
14090 +
14091 + if (!$dbo->getNumRows())
14092 + {
14093 + VBOHttpDocument::getInstance($app)->close(404, sprintf('Customer [%d] not found', $customer_id));
14094 + }
14095 +
14096 + $folder = $dbo->loadResult();
14097 +
14098 + if (!$folder)
14099 + {
14100 + VBOHttpDocument::getInstance($app)->close(500, 'The customer does not have any documents');
14101 + }
14102 +
14103 + $file = $input->getString('file');
14104 +
14105 + if (!$file)
14106 + {
14107 + VBOHttpDocument::getInstance($app)->close(400, 'File to remove not specified');
14108 + }
14109 +
14110 + $path = implode(DIRECTORY_SEPARATOR, array(VBO_CUSTOMERS_PATH, $folder, $file));
14111 +
14112 + if (!is_file($path))
14113 + {
14114 + VBOHttpDocument::getInstance($app)->close(404, sprintf('File [%s] not found', $path));
14115 + }
14116 +
14117 + /**
14118 + * Accept only non-traversal paths under the customer docs folder.
14119 + *
14120 + * @since 1.18.13 (J) - 1.8.13 (WP)
14121 + */
14122 + $path = realpath($path);
14123 +
14124 + if (!$path || strpos($path, VBO_CUSTOMERS_PATH) !== 0)
14125 + {
14126 + VBOHttpDocument::getInstance($app)->close(403, 'Path not allowed for file deletion.');
14127 + }
14128 +
14129 + $removed = JFile::delete($path);
14130 +
14131 + VBOHttpDocument::getInstance($app)->json(array('status' => (int) $removed));
14132 + }
14133 +
14134 + /**
14135 + * AJAX task to invoke a specific report and obtain information.
14136 + *
14137 + * @since 1.3.0
14138 + */
14139 + public function get_report_data()
14140 + {
14141 + $report_name = VikRequest::getString('report_name', '', 'request');
14142 + $current_fest = VikRequest::getString('current_fest', '', 'request');
14143 + $current_fromdate = VikRequest::getString('current_fromdate', '', 'request');
14144 + $current_todate = VikRequest::getString('current_todate', '', 'request');
14145 + $step = VikRequest::getString('step', 'weekend', 'request');
14146 + $direction = VikRequest::getString('direction', 'load', 'request');
14147 + $period = VikRequest::getString('period', 'full', 'request');
14148 + $krsort = VikRequest::getString('krsort', 'occupancy', 'request');
14149 + $krorder = VikRequest::getString('krorder', 'DESC', 'request');
14150 + $chart_datatype = VikRequest::getVar('chart_datatype', array(), 'request');
14151 + $chart_meta_data = VikRequest::getString('chart_meta_data', '', 'request', VIKREQUEST_ALLOWRAW);
14152 + $chart_meta_data = !empty($chart_meta_data) ? json_decode($chart_meta_data, true) : array();
14153 + // idroom can be an array of IDs or just one ID as int/string
14154 + $idroom = VikRequest::getVar('idroom', null, 'request');
14155 + //
14156 +
14157 + if (empty($report_name) || empty($current_fromdate) || empty($current_todate)) {
14158 + throw new Exception("Missing request data", 400);
14159 + }
14160 +
14161 + // get requested report instance
14162 + $report = VikBooking::getReportInstance($report_name);
14163 + if (!$report) {
14164 + throw new Exception("Report not found", 404);
14165 + }
14166 +
14167 + // chart data
14168 + if (empty($chart_datatype)) {
14169 + $chart_datatype = array(
14170 + 'type' => 'doughnut',
14171 + 'depth' => 1,
14172 + 'keys' => array($krsort),
14173 + );
14174 + }
14175 +
14176 + // website date format
14177 + $df = $report->getDateFormat();
14178 +
14179 + // prepare request params for the report
14180 + $rparams = array(
14181 + 'fromdate' => $current_fromdate,
14182 + 'todate' => $current_todate,
14183 + 'period' => $period,
14184 + 'krsort' => $krsort,
14185 + 'krorder' => $krorder,
14186 + 'idroom' => $idroom,
14187 + );
14188 +
14189 + // starting dates info and timestamps
14190 + $from_ts = VikBooking::getDateTimestamp($current_fromdate, 0, 0, 0);
14191 + $to_ts = VikBooking::getDateTimestamp($current_todate, 23, 59, 59);
14192 + $from_info = getdate($from_ts);
14193 + $to_info = getdate($to_ts);
14194 +
14195 + // the name of the period requested and whether it's a fest
14196 + $period_name = '';
14197 + $is_fest = null;
14198 +
14199 + if ($direction == 'prev' || $direction == 'next') {
14200 + // calculate prev or next dates
14201 + if ($step == 'weekend') {
14202 + $period_name = JText::translate('VBOWEEKND');
14203 + if ($direction == 'next') {
14204 + // next weekend from current end date
14205 + $next_ts = strtotime("next friday", $to_ts);
14206 + } else {
14207 + // prev weekend from current start date
14208 + $next_ts = strtotime("previous friday", $from_ts);
14209 + }
14210 + $next_info = getdate($next_ts);
14211 + $new_from_ts = $next_ts;
14212 + $new_to_ts = mktime(23, 59, 59, $next_info['mon'], ($next_info['mday'] + 1), $next_info['year']);
14213 + $rparams['fromdate'] = date($df, $new_from_ts);
14214 + $rparams['todate'] = date($df, $new_to_ts);
14215 + } elseif ($step == 'week') {
14216 + $period_name = JText::translate('VBOWEEK');
14217 + if ($direction == 'next') {
14218 + // start next week from the current end date
14219 + $new_from_ts = $to_ts;
14220 + $new_to_ts = mktime(23, 59, 59, $to_info['mon'], ($to_info['mday'] + 7), $to_info['year']);
14221 + $rparams['fromdate'] = $rparams['todate'];
14222 + $rparams['todate'] = date($df, $new_to_ts);
14223 + } else {
14224 + // end prev week from the current from date
14225 + $new_from_ts = mktime(0, 0, 0, $from_info['mon'], ($from_info['mday'] - 7), $from_info['year']);
14226 + $new_to_ts = $from_ts;
14227 + $rparams['todate'] = $rparams['fromdate'];
14228 + $rparams['fromdate'] = date($df, $new_from_ts);
14229 + }
14230 + } else {
14231 + // month
14232 + $period_name = JText::translate('VBPVIEWRESTRICTIONSTWO');
14233 + if ($direction == 'next') {
14234 + // next month from the current from date
14235 + $nextmonts = mktime(0, 0, 0, ($from_info['mon'] + 1), 1, $from_info['year']);
14236 + $new_from_ts = $nextmonts;
14237 + $new_to_ts = mktime(23, 59, 59, ($from_info['mon'] + 1), date('t', $nextmonts), $from_info['year']);
14238 + $rparams['fromdate'] = date($df, $new_from_ts);
14239 + $rparams['todate'] = date($df, $new_to_ts);
14240 + } else {
14241 + // prev month from the current from date
14242 + $nextmonts = mktime(0, 0, 0, ($from_info['mon'] - 1), 1, $from_info['year']);
14243 + $new_from_ts = $nextmonts;
14244 + $new_to_ts = mktime(23, 59, 59, ($from_info['mon'] - 1), date('t', $nextmonts), $from_info['year']);
14245 + $rparams['fromdate'] = date($df, $new_from_ts);
14246 + $rparams['todate'] = date($df, $new_to_ts);
14247 + }
14248 + }
14249 +
14250 + // get the next festivities
14251 + $fests = VikBooking::getFestivitiesInstance();
14252 + $next_fests = $fests->loadFestDates();
14253 + if (count($next_fests)) {
14254 + // check whether a festivity should be displayed rather than the calculated period of dates
14255 + foreach ($next_fests as $fest) {
14256 + $fest_found = false;
14257 + if ($direction == 'next' && $fest['festinfo'][0]->from_ts > $from_ts && $fest['festinfo'][0]->from_ts <= $new_to_ts) {
14258 + $fest_found = true;
14259 + } elseif ($direction == 'prev' && $fest['festinfo'][0]->from_ts < $to_ts && $fest['festinfo'][0]->from_ts >= $new_from_ts) {
14260 + $fest_found = true;
14261 + }
14262 + if ($fest_found && (string)$fest['festinfo'][0]->next_ts != $current_fest) {
14263 + // festivity found before next calculated period
14264 + $is_fest = $fest['festinfo'][0]->next_ts;
14265 + $period_name = $fest['festinfo'][0]->trans_name;
14266 + $new_from_ts = $fest['festinfo'][0]->from_ts;
14267 + $new_to_ts = $fest['festinfo'][0]->to_ts;
14268 + $rparams['fromdate'] = date($df, $new_from_ts);
14269 + $rparams['todate'] = date($df, $new_to_ts);
14270 + break;
14271 + }
14272 + }
14273 + }
14274 + } else {
14275 + // load requested dates by skipping the festivities
14276 + $new_from_ts = $from_ts;
14277 + $new_to_ts = $to_ts;
14278 + }
14279 +
14280 + // invoke report
14281 + $report->injectParams($rparams);
14282 + $report_values = $report->getReportValues(1);
14283 + $report_cols = $report->getColumnsValues();
14284 + $report_chart = null;
14285 + $report_chart_metas = array();
14286 + $chart_meta_data = array(
14287 + 'keys' => array(
14288 + 'occupancy',
14289 + 'tot_bookings',
14290 + 'nights_booked',
14291 + ),
14292 + );
14293 + $error = null;
14294 +
14295 + if (!count($report_values)) {
14296 + $error = strlen($report->getError()) ? $report->getError() : JText::translate('VBNOTRACKINGS');
14297 + } else {
14298 + // get doughnut Chart for the requested key
14299 + $report_chart = $report->getChart((array) $chart_datatype);
14300 +
14301 + // get Chart meta data
14302 + $all_chart_metas = $report->getChartMetaData(null, $chart_meta_data);
14303 + if (count($all_chart_metas)) {
14304 + // merge all positions into one array
14305 + foreach ($all_chart_metas as $pos_metas) {
14306 + $report_chart_metas = array_merge($report_chart_metas, $pos_metas);
14307 + }
14308 + }
14309 +
14310 + if (empty($period_name)) {
14311 + $period_name = $report->getProperty('chartTitle');
14312 + }
14313 + }
14314 +
14315 + // build response
14316 + $response = new stdClass;
14317 + $response->error = $error;
14318 + $response->fromdate = $rparams['fromdate'];
14319 + $response->todate = $rparams['todate'];
14320 + $response->in_days = $report->countDaysTo($new_from_ts);
14321 + $response->in_days_to = $report->countDaysTo($new_to_ts);
14322 + $response->in_days_avg = $report->countAverageDays($response->in_days, $response->in_days_to);
14323 + $response->period_name = $period_name;
14324 + $response->period_date = count($report_values) && isset($report_values['day']) ? $report_values['day']['display_value'] : '';
14325 + $response->is_fest = $is_fest;
14326 + $response->report_chart = $report_chart;
14327 + $response->report_cols = $report_cols;
14328 + $response->report_values = $report_values;
14329 + $response->report_script = $report->getScript();
14330 + $response->chart_labels = $report->getProperty('chartJsLabels');
14331 + $response->dataset_label = $report->getProperty('chartJsDataSetLabel');
14332 + $response->chart_colors = $report->getProperty('chartJsColors');
14333 + $response->chart_data = $report->getProperty('chartJsData');
14334 + $response->report_chart_metas = $report_chart_metas;
14335 +
14336 + echo json_encode($response);
14337 + exit;
14338 + }
14339 +
14340 + /**
14341 + * Go to the previous booking.
14342 + *
14343 + * @uses navigateToBooking()
14344 + *
14345 + * @since 1.3.0
14346 + */
14347 + public function prev_booking()
14348 + {
14349 + $this->navigateToBooking('prev');
14350 + }
14351 +
14352 + /**
14353 + * Go to the next booking.
14354 + *
14355 + * @uses navigateToBooking()
14356 + *
14357 + * @since 1.3.0
14358 + */
14359 + public function next_booking()
14360 + {
14361 + $this->navigateToBooking('next');
14362 + }
14363 +
14364 + /**
14365 + * Given the current booking ID in the request, we navigate
14366 + * either to the next or to the previous reservation (if any).
14367 + *
14368 + * @param string $direction either next or prev.
14369 + *
14370 + * @return void
14371 + *
14372 + * @since 1.3.0
14373 + */
14374 + private function navigateToBooking($direction = 'next')
14375 + {
14376 + $bid = VikRequest::getInt('whereup', 0, 'request');
14377 + if (empty($bid) || $bid < 1 || !in_array($direction, array('prev', 'next'))) {
14378 + throw new Exception("Invalid request", 400);
14379 + }
14380 +
14381 + $dbo = JFactory::getDbo();
14382 + $app = JFactory::getApplication();
14383 +
14384 + $q = "SELECT `id` FROM `#__vikbooking_orders` WHERE `id`" . ($direction == 'next' ? '>' : '<') . "{$bid} ORDER BY `id` " . ($direction == 'next' ? 'ASC' : 'DESC');
14385 + $dbo->setQuery($q, 0, 1);
14386 + $dbo->execute();
14387 + if (!$dbo->getNumRows()) {
14388 + VikError::raiseWarning('', JText::translate('VBPEDITBUSYONE'));
14389 + $app->redirect("index.php?option=com_vikbooking&task=orders");
14390 + exit;
14391 + }
14392 +
14393 + $app->redirect("index.php?option=com_vikbooking&task=editorder&cid[]=" . $dbo->loadResult());
14394 + exit;
14395 + }
14396 +
14397 + /**
14398 + * AJAX request: from a list of reservation IDs, we return the ones
14399 + * that have a review with the related review ID on VCM.
14400 + *
14401 + * @since 1.13
14402 + */
14403 + public function bookings_have_reviews()
14404 + {
14405 + if (!JSession::checkToken()) {
14406 + // missing CSRF-proof token
14407 + VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14408 + }
14409 +
14410 + $dbo = JFactory::getDbo();
14411 +
14412 + $bids = VikRequest::getVar('bids', [], 'request', 'array');
14413 + $vcm_installed = class_exists('VikChannelManager');
14414 + $withreviews = [];
14415 +
14416 + if ($vcm_installed && $bids) {
14417 + $bids = array_filter(array_map('intval', (array) $bids));
14418 + $bids = $bids ?: [0];
14419 +
14420 + try {
14421 + $q = "SELECT `id`, `idorder` FROM `#__vikchannelmanager_otareviews` WHERE `idorder` IN (" . implode(', ', $bids) . ");";
14422 + $dbo->setQuery($q);
14423 + $reviews = $dbo->loadAssocList();
14424 +
14425 + foreach ($reviews as $r) {
14426 + $withreviews[$r['idorder']] = $r['id'];
14427 + }
14428 + } catch (Exception $e) {
14429 + // do nothing, outdated version
14430 + }
14431 + }
14432 +
14433 + // output list of booking IDs found, if any
14434 + VBOHttpDocument::getInstance()->json($withreviews);
14435 + }
14436 +
14437 + /**
14438 + * AJAX request for adding a new room-day note.
14439 + *
14440 + * @return void
14441 + *
14442 + * @since 1.13.5
14443 + */
14444 + public function add_roomdaynote()
14445 + {
14446 + $dt = VikRequest::getString('dt', '', 'request');
14447 + $idroom = VikRequest::getInt('idroom', 0, 'request');
14448 + $subunit = VikRequest::getInt('subunit', 0, 'request');
14449 + $type = VikRequest::getString('type', '', 'request');
14450 + $type = empty($type) ? 'custom' : $type;
14451 + $name = VikRequest::getString('name', '', 'request');
14452 + $descr = VikRequest::getString('descr', '', 'request');
14453 + $cdays = VikRequest::getInt('cdays', 0, 'request');
14454 + $cdays = $cdays < 0 ? 0 : $cdays;
14455 + $cdays = $cdays > 365 ? 365 : $cdays;
14456 + if (empty($idroom) || empty($dt) || !strtotime($dt)) {
14457 + echo 'e4j.error.1';
14458 + exit;
14459 + }
14460 +
14461 + // reload end date
14462 + $end_date = $dt;
14463 +
14464 + // build critical date object
14465 + $new_note = array(
14466 + 'name' => $name,
14467 + 'type' => $type,
14468 + 'descr' => $descr,
14469 + );
14470 +
14471 + // get object
14472 + $notes = VikBooking::getCriticalDatesInstance();
14473 +
14474 + // store the notes for all consecutive dates
14475 + for ($i = 0; $i <= $cdays; $i++) {
14476 + $store_dt = $dt;
14477 + if ($i > 0) {
14478 + $dt_info = getdate(strtotime($store_dt));
14479 + $store_dt = date('Y-m-d', mktime(0, 0, 0, $dt_info['mon'], ($dt_info['mday'] + $i), $dt_info['year']));
14480 + $end_date = $store_dt;
14481 + }
14482 + $result = $notes->storeDayNote($new_note, $store_dt, $idroom, $subunit);
14483 + if (!$result) {
14484 + echo 'e4j.error.2';
14485 + exit;
14486 + }
14487 + }
14488 +
14489 + // reload all room day notes for this day for the AJAX response
14490 + $all_notes = $notes->loadRoomDayNotes($dt, $end_date, $idroom, $subunit);
14491 +
14492 + if (!$all_notes || !count($all_notes)) {
14493 + // no notes found even after storing it
14494 + echo 'e4j.error.3';
14495 + exit;
14496 + }
14497 +
14498 + echo json_encode($all_notes);
14499 + exit;
14500 + }
14501 +
14502 + /**
14503 + * AJAX request for removing a room day note.
14504 + *
14505 + * @return void
14506 + *
14507 + * @since 1.13.5
14508 + */
14509 + public function remove_roomdaynote()
14510 + {
14511 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
14512 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14513 + }
14514 +
14515 + $dt = VikRequest::getString('dt', '', 'request');
14516 + $idroom = VikRequest::getInt('idroom', 0, 'request');
14517 + $subunit = VikRequest::getInt('subunit', 0, 'request');
14518 + $type = VikRequest::getString('type', '', 'request');
14519 + $type = empty($type) ? 'custom' : $type;
14520 + $ind = VikRequest::getInt('ind', 0, 'request');
14521 + if (empty($dt) || !strtotime($dt)) {
14522 + echo 'e4j.error.1';
14523 + exit;
14524 + }
14525 +
14526 + $notes = VikBooking::getCriticalDatesInstance();
14527 + $result = $notes->deleteDayNote($ind, $dt, $idroom, $subunit, $type);
14528 + if (!$result) {
14529 + echo 'e4j.error.2';
14530 + exit;
14531 + }
14532 +
14533 + echo 'e4j.ok';
14534 + exit;
14535 + }
14536 +
14537 + /**
14538 + * AJAX request for storing an event for a booking.
14539 + * Firstly developed for the VCM Reporting API - Guest Misconduct,
14540 + * but it can be used for any other purpose.
14541 + *
14542 + * @return void
14543 + *
14544 + * @since 1.13.5
14545 + */
14546 + public function store_booking_history_event()
14547 + {
14548 + $bid = VikRequest::getInt('bid', 0, 'request');
14549 + $event = VikRequest::getString('event', '', 'request');
14550 + $descr = VikRequest::getString('descr', '', 'request');
14551 +
14552 + if (empty($bid) || empty($event)) {
14553 + throw new Exception("Missing required information", 500);
14554 + }
14555 +
14556 + // Booking History
14557 + VikBooking::getBookingHistoryInstance()->setBid($bid)->store($event, $descr);
14558 + //
14559 +
14560 + echo 'e4j.ok';
14561 + exit;
14562 + }
14563 +
14564 + /**
14565 + * AJAX request for updating an option/extra service.
14566 + * Firstly developed for the VCM Vacation Rentals Essentials API - Damage Deposit,
14567 + * but it can be used for any other purpose.
14568 + *
14569 + * @return void
14570 + *
14571 + * @since 1.13.5
14572 + */
14573 + public function update_option_params()
14574 + {
14575 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
14576 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14577 + }
14578 +
14579 + $optid = VikRequest::getInt('optid', 0, 'request');
14580 + $oparams = VikRequest::getVar('oparams', array(), 'request', 'array');
14581 +
14582 + if (empty($optid) || !is_array($oparams) || empty($oparams)) {
14583 + throw new Exception("Missing required information", 500);
14584 + }
14585 +
14586 + $dbo = JFactory::getDbo();
14587 + $q = "SELECT `oparams` FROM `#__vikbooking_optionals` WHERE `id`=" . (int)$optid . ";";
14588 + $dbo->setQuery($q);
14589 + $dbo->execute();
14590 + if (!$dbo->getNumRows()) {
14591 + throw new Exception("Option not found", 404);
14592 + }
14593 + $cur_params = $dbo->loadResult();
14594 + $cur_params = !empty($cur_params) ? json_decode($cur_params, true) : array();
14595 + $cur_params = !is_array($cur_params) ? array() : $cur_params;
14596 +
14597 + foreach ($oparams as $k => $v) {
14598 + if (empty($k)) {
14599 + continue;
14600 + }
14601 + $cur_params[$k] = $v;
14602 + }
14603 +
14604 + $q = "UPDATE `#__vikbooking_optionals` SET `oparams`=" . $dbo->quote(json_encode($cur_params)) ." WHERE `id`=" . (int)$optid . ";";
14605 + $dbo->setQuery($q);
14606 + $dbo->execute();
14607 +
14608 + echo 'e4j.ok';
14609 + exit;
14610 + }
14611 +
14612 + /**
14613 + * Hidden task to clean up duplicate records in certain database tables
14614 + * due to a double execution of the installation queries. Ghost records,
14615 + * if any, are also removed to clean up issues with hanging records.
14616 + *
14617 + * @since November 4th 2020
14618 + * @since 1.16.3 (J) - 1.6.3 (WP)
14619 + */
14620 + public function clean_duplicate_records()
14621 + {
14622 + if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14623 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14624 + }
14625 +
14626 + $dbo = JFactory::getDbo();
14627 +
14628 + $tables_with_duplicates = [
14629 + '#__vikbooking_config' => [
14630 + 'id_key' => 'id',
14631 + 'compare_key' => 'param',
14632 + ],
14633 + '#__vikbooking_countries' => [
14634 + 'id_key' => 'id',
14635 + 'compare_key' => 'country_3_code',
14636 + ],
14637 + '#__vikbooking_custfields' => [
14638 + 'id_key' => 'id',
14639 + 'compare_key' => 'name',
14640 + ],
14641 + '#__vikbooking_texts' => [
14642 + 'id_key' => 'id',
14643 + 'compare_key' => 'param',
14644 + ],
14645 + ];
14646 +
14647 + foreach ($tables_with_duplicates as $tblname => $data) {
14648 + $doubles = [];
14649 + $storage = [];
14650 + $rmlist = [];
14651 +
14652 + $q = "SELECT * FROM `{$tblname}` ORDER BY `{$data['id_key']}` DESC;";
14653 + $dbo->setQuery($q);
14654 + $rows = $dbo->loadAssocList();
14655 + if (!$rows) {
14656 + echo "<p>No records found in table {$tblname}</p>";
14657 + continue;
14658 + }
14659 +
14660 + foreach ($rows as $row) {
14661 + if (!isset($doubles[$row[$data['compare_key']]])) {
14662 + $doubles[$row[$data['compare_key']]] = 0;
14663 + }
14664 + $doubles[$row[$data['compare_key']]]++;
14665 + if (!isset($storage[$row[$data['compare_key']]])) {
14666 + $storage[$row[$data['compare_key']]] = [];
14667 + }
14668 + array_push($storage[$row[$data['compare_key']]], $row[$data['id_key']]);
14669 + }
14670 +
14671 + foreach ($doubles as $paramkey => $paramcount) {
14672 + if ($paramcount < 2 || !isset($storage[$paramkey]) || count($storage[$paramkey]) < 2 || $paramcount != count($storage[$paramkey])) {
14673 + continue;
14674 + }
14675 + $exceeding = $paramcount - 1;
14676 + for ($x = 0; $x < $exceeding; $x++) {
14677 + array_push($rmlist, $storage[$paramkey][$x]);
14678 + }
14679 + }
14680 +
14681 + echo "<p>Total records found in table {$tblname}: " . count($rows) . "</p>";
14682 + echo '<p>Total records to remove: ' . count($rmlist) . '</p>';
14683 + echo '<pre style="display: none;">'.print_r($rmlist, true).'</pre><br/>';
14684 +
14685 + if (count($rmlist)) {
14686 + $q = "DELETE FROM `{$tblname}` WHERE `{$data['id_key']}` IN (" . implode(', ', $rmlist) . ");";
14687 + $dbo->setQuery($q);
14688 + $dbo->execute();
14689 + }
14690 + }
14691 +
14692 + /**
14693 + * Clean up busy records where the busy relations contain empty booking IDs.
14694 + */
14695 + $hanging_busy_ids = [];
14696 +
14697 + $q = "SELECT `idbusy` FROM `#__vikbooking_ordersbusy` WHERE `idorder` = 0 OR `idorder` IS NULL;";
14698 + $dbo->setQuery($q);
14699 + $removelist = $dbo->loadAssocList();
14700 + if ($removelist) {
14701 + foreach ($removelist as $hanging_busy) {
14702 + $hanging_busy_id = (int)$hanging_busy['idbusy'];
14703 + if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
14704 + array_push($hanging_busy_ids, $hanging_busy_id);
14705 + }
14706 + }
14707 + }
14708 +
14709 + // let's check also for ghost records that only occupy the room
14710 + $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);";
14711 + $dbo->setQuery($q);
14712 + $removelist = $dbo->loadAssocList();
14713 + if ($removelist) {
14714 + foreach ($removelist as $hanging_busy) {
14715 + $hanging_busy_id = (int)$hanging_busy['id'];
14716 + if (!in_array($hanging_busy_id, $hanging_busy_ids)) {
14717 + array_push($hanging_busy_ids, $hanging_busy_id);
14718 + }
14719 + }
14720 + }
14721 +
14722 + if ($hanging_busy_ids) {
14723 + $q = "DELETE FROM `#__vikbooking_busy` WHERE `id` IN (" . implode(', ', $hanging_busy_ids) . ");";
14724 + $dbo->setQuery($q);
14725 + $dbo->execute();
14726 + }
14727 +
14728 + echo "<p>Total ghost records removed: " . count($hanging_busy_ids) . "</p>";
14729 +
14730 + return;
14731 + }
14732 +
14733 + /**
14734 + * Hidden task to scan all database tables of VikBooking and Vik Channel Manager
14735 + * to ensure the column `id` is defined as a primary key and got an auto-increment
14736 + * extra flag properly defined and set. We've noticed that some third-party plugins
14737 + * used to migrate WP sites may break the primary keys, and so new records won't get an ID.
14738 + *
14739 + * @since 1.16.8 (J) - 1.6.8 (WP)
14740 + */
14741 + public function fix_autoincrement_tables()
14742 + {
14743 + if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14744 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14745 + }
14746 +
14747 + $dbo = JFactory::getDbo();
14748 +
14749 + // load all the installed database tables
14750 + $tables = $dbo->getTableList();
14751 +
14752 + // get current database prefix
14753 + $prefix = $dbo->getPrefix();
14754 +
14755 + // replace prefix with placeholder
14756 + $tables = array_map(function($table) use ($prefix)
14757 + {
14758 + return preg_replace("/^{$prefix}/", '#__', $table);
14759 + }, $tables);
14760 +
14761 + // remove all the tables that do not belong to VikBooking/VCM
14762 + $tables = array_values(array_filter($tables, function($table)
14763 + {
14764 + if (preg_match("/^#__vik(?:booking|channelmanager)_config$/", $table))
14765 + {
14766 + // exclude the configuration table, which will be handled in a different way
14767 + return false;
14768 + }
14769 +
14770 + return preg_match("/^#__vik(?:booking|channelmanager)_/", $table);
14771 + }));
14772 +
14773 + foreach ($tables as $table) {
14774 + $columns = $dbo->getTableColumns($table, false);
14775 + if (!isset($columns['id']) || empty($columns['id']->Type) || !empty($columns['id']->Extra)) {
14776 + continue;
14777 + }
14778 +
14779 + echo 'Fixing ' . $table. ' for missing auto-increment<br/><pre>' . print_r($columns['id'], true) . '</pre><br/>';
14780 +
14781 + // set auto-increment and primary key
14782 + $dbo->setQuery("ALTER TABLE `{$table}` MODIFY `id` " . $columns['id']->Type . " NOT NULL AUTO_INCREMENT PRIMARY KEY;");
14783 + $dbo->execute();
14784 +
14785 + // count next auto-increment
14786 + $dbo->setQuery("SELECT MAX(`id`) FROM `{$table}`");
14787 + $next_ai = (int) $dbo->loadResult() + 1;
14788 +
14789 + // update next auto-increment value
14790 + $dbo->setQuery("ALTER TABLE `{$table}` AUTO_INCREMENT = {$next_ai}");
14791 + $dbo->execute();
14792 + }
14793 + }
14794 +
14795 + /**
14796 + * Hidden task to (re-)run the update queries from a given plugin version.
14797 + * Useful to ensure the database structure is up-to-date and no update queries went lost.
14798 + *
14799 + * @since 1.17.6 (J) - 1.7.6 (WP)
14800 + */
14801 + public function run_update_queries()
14802 + {
14803 + $app = JFactory::getApplication();
14804 + $dbo = JFactory::getDbo();
14805 +
14806 + if (!JFactory::getUser()->authorise('core.admin', 'com_vikbooking')) {
14807 + VBOHttpDocument::getInstance($app)->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
14808 + }
14809 +
14810 + $from_version = $app->input->getString('from_version');
14811 +
14812 + if (empty($from_version)) {
14813 + VBOHttpDocument::getInstance()->close(400, 'Missing from version value.');
14814 + }
14815 +
14816 + // determine the SQL updates directory path
14817 + $sql_updates_path = '';
14818 + if (VBOPlatformDetection::isWordPress()) {
14819 + $sql_updates_path = implode(DIRECTORY_SEPARATOR, [VIKBOOKING_BASE, 'sql', 'update', 'mysql']);
14820 + } else {
14821 + $sql_updates_path = implode(DIRECTORY_SEPARATOR, [VBO_ADMIN_PATH, 'sql', 'updates', 'mysql']);
14822 + }
14823 +
14824 + if (!$sql_updates_path || !is_dir($sql_updates_path)) {
14825 + VBOHttpDocument::getInstance()->close(500, 'Could not find SQL updates path.');
14826 + }
14827 +
14828 + // read all SQL update files
14829 + $sql_update_files = JFolder::files($sql_updates_path, '\.sql', $recurse = false, $full = true);
14830 +
14831 + // filter SQL files with just the valid ones
14832 + $sql_update_files = array_filter($sql_update_files, function($sql_update_file) use ($from_version) {
14833 + $file_version = basename($sql_update_file, '.sql');
14834 + return version_compare($file_version, $from_version, '>=');
14835 + });
14836 +
14837 + // sort files by version ascending
14838 + usort($sql_update_files, function($a, $b) {
14839 + return version_compare(basename($a, '.sql'), basename($b, '.sql'));
14840 + });
14841 +
14842 + if (!$sql_update_files) {
14843 + VBOHttpDocument::getInstance()->close(500, sprintf('Could not find any suitable SQL update file from version %s.', $from_version));
14844 + }
14845 +
14846 + $success_queries = 0;
14847 +
14848 + foreach ($sql_update_files as $file) {
14849 + $handle = fopen($file, 'r');
14850 +
14851 + $bytes = '';
14852 + while (!feof($handle)) {
14853 + $bytes .= fread($handle, 8192);
14854 + }
14855 +
14856 + fclose($handle);
14857 +
14858 + if (VBOPlatformDetection::isWordPress()) {
14859 + $queries_list = JDatabaseHelper::splitSql($bytes);
14860 + } else {
14861 + try {
14862 + if (class_exists('JDatabaseDriver')) {
14863 + $queries_list = JDatabaseDriver::splitSql($bytes);
14864 + } else {
14865 + $queries_list = Joomla\Database\DatabaseDriver::splitSql($bytes);
14866 + }
14867 + } catch(Throwable $e) {
14868 + $app->enqueueMessage(sprintf('Error splitting queries: %s', $e->getMessage()), 'error');
14869 + $queries_list = [];
14870 + }
14871 + }
14872 +
14873 + foreach ($queries_list as $q) {
14874 + try {
14875 + $dbo->setQuery($q);
14876 + $result = $dbo->execute();
14877 + } catch (Exception $e) {
14878 + $result = false;
14879 + $app->enqueueMessage(sprintf('Error executing query: %s', $e->getMessage()), 'warning');
14880 + }
14881 +
14882 + if ($result) {
14883 + $success_queries++;
14884 + }
14885 + }
14886 + }
14887 +
14888 + if ($success_queries) {
14889 + $app->enqueueMessage(sprintf('Successful queries: %d', $success_queries), 'success');
14890 + }
14891 +
14892 + // send response to output
14893 + echo '<pre>'.print_r($sql_update_files, true).'</pre><br/>';
14894 + }
14895 +
14896 + /**
14897 + * Loads a specific admin widget ID and executes the requested method.
14898 + * Useful for loading a newly added widget, or to execute custom methods.
14899 + *
14900 + * @see this is an AJAX endpoint.
14901 + *
14902 + * @since 1.14 (J) - 1.4.0 (WP)
14903 + * @since 1.15 (J) - 1.5.0 (WP) widget callback can return values rather than just echoing.
14904 + * @since 1.16.5 (J) - 1.6.5 (WP) widgets are rendered within a try-catch statement.
14905 + */
14906 + public function exec_admin_widget()
14907 + {
14908 + if (!JSession::checkToken()) {
14909 + // missing CSRF-proof token
14910 + VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14911 + }
14912 +
14913 + $widget_id = VikRequest::getString('widget_id', '', 'request');
14914 + $call = VikRequest::getString('call', '', 'request');
14915 + $return = VikRequest::getInt('return', 0, 'request');
14916 + $vbo_page = VikRequest::getString('vbo_page', '', 'request');
14917 + $vbo_uri = VikRequest::getString('vbo_uri', '', 'request');
14918 + $multitask = VikRequest::getInt('multitask', 0, 'request');
14919 +
14920 + if (empty($widget_id)) {
14921 + VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget ID');
14922 + }
14923 +
14924 + if (empty($call) || !is_string($call)) {
14925 + VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget Callback');
14926 + }
14927 +
14928 + // invoke admin widgets helper
14929 + $widgets_helper = VikBooking::getAdminWidgetsInstance();
14930 + $widget = $widgets_helper->getWidget($widget_id);
14931 +
14932 + if ($widget === false) {
14933 + VBOHttpDocument::getInstance()->close(404, 'Requested Admin Widget not found');
14934 + }
14935 +
14936 + if (!method_exists($widget, $call) || !is_callable(array($widget, $call))) {
14937 + VBOHttpDocument::getInstance()->close(403, 'Admin Widget Callback not found or not callable');
14938 + }
14939 +
14940 + // get the multitask parser object
14941 + $parser = VBOMultitaskParser::getInstance($vbo_page, $vbo_uri);
14942 +
14943 + // check if arguments should be passed
14944 + $call_args = [];
14945 + if ($multitask && $call === 'render') {
14946 + // build the multitask data object and inject it to the args as the first index
14947 + $call_args[] = $parser->getData();
14948 +
14949 + // bind options within the widget, if any
14950 + $widget->bindOptions($call_args[0]);
14951 + } else {
14952 + // always bind multitask options, if any
14953 + $widget->bindOptions($parser->getOptions());
14954 + }
14955 +
14956 + try {
14957 + if ($return) {
14958 + // invoke the widget's method and get the value returned
14959 + $widget_response = $call_args ? call_user_func_array([$widget, $call], $call_args) : $widget->{$call}();
14960 + } else {
14961 + // invoke the widget's method within a buffer
14962 + ob_start();
14963 + if ($call_args) {
14964 + $res = call_user_func_array([$widget, $call], $call_args);
14965 + } else {
14966 + $widget->{$call}();
14967 + }
14968 + $widget_response = ob_get_contents();
14969 + ob_end_clean();
14970 + }
14971 + } catch (Throwable $e) {
14972 + VBOHttpDocument::getInstance()->close($e->getCode() ?: 500, sprintf("%s\n%s at line %d", $e->getMessage(), $e->getFile(), $e->getLine()));
14973 + } catch (Exception $e) {
14974 + VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
14975 + }
14976 +
14977 + // prepare response object with a property equal to the called method
14978 + $response = new stdClass;
14979 + $response->{$call} = $widget_response;
14980 +
14981 + // output the JSON encoded response and exit
14982 + VBOHttpDocument::getInstance()->json($response);
14983 + }
14984 +
14985 + /**
14986 + * Updates the map of admin widgets.
14987 + *
14988 + * @throws Exception this is an AJAX endpoint.
14989 + *
14990 + * @since 1.4.0
14991 + */
14992 + public function save_admin_widgets()
14993 + {
14994 + if (!JSession::checkToken()) {
14995 + // missing CSRF-proof token
14996 + VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
14997 + }
14998 +
14999 + // make sure permissions are sufficient
15000 + if (!JFactory::getUser()->authorise('core.vbo.global', 'com_vikbooking')) {
15001 + VBOHttpDocument::getInstance()->close(403, 'You are not authorized to modify the widgets.');
15002 + }
15003 +
15004 + $psections = VikRequest::getVar('sections', array(), 'request', 'array');
15005 + if (!is_array($psections) || !count($psections)) {
15006 + VBOHttpDocument::getInstance()->close(500, 'No sections found in map');
15007 + }
15008 +
15009 + // request values are all converted to arrays, so restore the object styling
15010 + $psections = json_decode(json_encode($psections));
15011 +
15012 + // update map
15013 + $result = VikBooking::getAdminWidgetsInstance()->updateWidgetsMap($psections);
15014 +
15015 + $response = new stdClass;
15016 + $response->status = (int)$result;
15017 +
15018 + // output the JSON encoded response and exit
15019 + VBOHttpDocument::getInstance()->json($response);
15020 + }
15021 +
15022 + /**
15023 + * Restores the default admin widgets map.
15024 + *
15025 + * @since 1.4.0
15026 + */
15027 + public function reset_admin_widgets()
15028 + {
15029 + // reset map and redirect to dashboard
15030 + VikBooking::getAdminWidgetsInstance()->restoreDefaultWidgetsMap();
15031 +
15032 + JFactory::getApplication()->redirect('index.php?option=com_vikbooking');
15033 + exit;
15034 + }
15035 +
15036 + /**
15037 + * Updates the welcome message status for the widget's customizer via AJAX.
15038 + *
15039 + * @since 1.4.0
15040 + */
15041 + public function admin_widgets_welcome()
15042 + {
15043 + if (!JSession::checkToken()) {
15044 + // missing CSRF-proof token
15045 + VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15046 + }
15047 +
15048 + $hide_welcome = VikRequest::getInt('hide_welcome', 0, 'request');
15049 + // update configuration value
15050 + VikBooking::getAdminWidgetsInstance()->updateWelcome($hide_welcome);
15051 +
15052 + $response = new stdClass;
15053 + $response->status = $hide_welcome;
15054 +
15055 + // output the JSON encoded response and exit
15056 + VBOHttpDocument::getInstance()->json($response);
15057 + }
15058 +
15059 + public function newcondtext()
15060 + {
15061 + VikBookingHelper::printHeader("11");
15062 +
15063 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecondtext'));
15064 +
15065 + parent::display();
15066 +
15067 + if (VikBooking::showFooter()) {
15068 + VikBookingHelper::printFooter();
15069 + }
15070 + }
15071 +
15072 + public function editcondtext()
15073 + {
15074 + VikBookingHelper::printHeader("11");
15075 +
15076 + VikRequest::setVar('view', VikRequest::getCmd('view', 'managecondtext'));
15077 +
15078 + parent::display();
15079 +
15080 + if (VikBooking::showFooter()) {
15081 + VikBookingHelper::printFooter();
15082 + }
15083 + }
15084 +
15085 + public function cancelcondtext()
15086 + {
15087 + JFactory::getApplication()->redirect('index.php?option=com_vikbooking&task=config&tab=7');
15088 + }
15089 +
15090 + public function createcondtext()
15091 + {
15092 + if (!JSession::checkToken()) {
15093 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15094 + }
15095 +
15096 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
15097 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15098 + }
15099 +
15100 + $this->_doCreateCondText();
15101 + }
15102 +
15103 + public function createcondtextstay()
15104 + {
15105 + if (!JSession::checkToken()) {
15106 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15107 + }
15108 +
15109 + if (!JFactory::getUser()->authorise('core.create', 'com_vikbooking')) {
15110 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15111 + }
15112 +
15113 + $this->_doCreateCondText(true);
15114 + }
15115 +
15116 + private function _doCreateCondText($stay = false)
15117 + {
15118 + $dbo = JFactory::getDbo();
15119 + $app = JFactory::getApplication();
15120 + $rules_helper = VikBooking::getConditionalRulesInstance();
15121 + $rules_list = $rules_helper->composeRulesParamsFromRequest();
15122 +
15123 + $condtextname = VikRequest::getString('condtextname', '', 'request');
15124 + $condtexttkn = VikRequest::getString('condtexttkn', '', 'request');
15125 + $msg = VikRequest::getString('msg', '', 'request', VIKREQUEST_ALLOWRAW);
15126 + $debug = VikRequest::getInt('debug', 0, 'request');
15127 + if (empty($condtextname)) {
15128 + $condtextname = date('Y-m-dHis');
15129 + $condtexttkn = '{condition: ' . date('YmdHis') . '}';
15130 + }
15131 +
15132 + $existing_tokens = $rules_helper->getSpecialTags();
15133 + if (count($existing_tokens) && isset($existing_tokens[$condtexttkn])) {
15134 + VikError::raiseWarning('', 'Another conditional text with the same special tag already exists');
15135 + $app->redirect('index.php?option=com_vikbooking&task=newcondtext');
15136 + exit;
15137 + }
15138 +
15139 + $data = new stdClass;
15140 + $data->name = $condtextname;
15141 + $data->token = $condtexttkn;
15142 + $data->rules = json_encode($rules_list);
15143 + $data->msg = $msg;
15144 + $data->lastupd = JDate::getInstance()->toSql();
15145 + $data->debug = $debug;
15146 +
15147 + $dbo->insertObject('#__vikbooking_condtexts', $data, 'id');
15148 +
15149 + if (isset($data->id)) {
15150 + $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
15151 + }
15152 +
15153 + if (!$stay || !isset($data->id)) {
15154 + $this->cancelcondtext();
15155 + exit;
15156 + }
15157 +
15158 + $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $data->id);
15159 + }
15160 +
15161 + public function updatecondtext()
15162 + {
15163 + if (!JSession::checkToken()) {
15164 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15165 + }
15166 +
15167 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
15168 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15169 + }
15170 +
15171 + $this->_doUpdateCondText();
15172 + }
15173 +
15174 + public function updatecondtextstay()
15175 + {
15176 + if (!JSession::checkToken()) {
15177 + throw new Exception(JText::translate('JINVALID_TOKEN'), 403);
15178 + }
15179 +
15180 + if (!JFactory::getUser()->authorise('core.edit', 'com_vikbooking')) {
15181 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15182 + }
15183 +
15184 + $this->_doUpdateCondText(true);
15185 + }
15186 +
15187 + private function _doUpdateCondText($stay = false)
15188 + {
15189 + $dbo = JFactory::getDbo();
15190 + $app = JFactory::getApplication();
15191 + $rules_helper = VikBooking::getConditionalRulesInstance();
15192 + $rules_list = $rules_helper->composeRulesParamsFromRequest();
15193 +
15194 + $pwhere = VikRequest::getInt('where', '', 'request');
15195 + $condtextname = VikRequest::getString('condtextname', '', 'request');
15196 + $condtexttkn = VikRequest::getString('condtexttkn', '', 'request');
15197 + $msg = VikRequest::getString('msg', '', 'request', VIKREQUEST_ALLOWRAW);
15198 + $debug = VikRequest::getInt('debug', 0, 'request');
15199 + if (empty($condtextname)) {
15200 + $condtextname = date('Y-m-dHis');
15201 + $condtexttkn = '{condition: ' . date('YmdHis') . '}';
15202 + }
15203 +
15204 + $existing_tokens = $rules_helper->getSpecialTags();
15205 + if (count($existing_tokens) && isset($existing_tokens[$condtexttkn]) && ($existing_tokens[$condtexttkn]['id'] != $pwhere)) {
15206 + VikError::raiseWarning('', 'Another conditional text with the same special tag already exists (' . $existing_tokens[$condtexttkn]['name'] . ')');
15207 + $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $pwhere);
15208 + exit;
15209 + }
15210 +
15211 + $data = new stdClass;
15212 + $data->id = $pwhere;
15213 + $data->name = $condtextname;
15214 + $data->token = $condtexttkn;
15215 + $data->rules = json_encode($rules_list);
15216 + $data->msg = $msg;
15217 + $data->lastupd = JDate::getInstance()->toSql();
15218 + $data->debug = $debug;
15219 +
15220 + $dbo->updateObject('#__vikbooking_condtexts', $data, 'id');
15221 +
15222 + $app->enqueueMessage(JText::translate('VBSEASONUPDATED'));
15223 +
15224 + if (!$stay) {
15225 + $this->cancelcondtext();
15226 + exit;
15227 + }
15228 +
15229 + $app->redirect('index.php?option=com_vikbooking&task=editcondtext&cid[]=' . $data->id);
15230 + }
15231 +
15232 + public function removecondtext()
15233 + {
15234 + if (!JFactory::getUser()->authorise('core.delete', 'com_vikbooking')) {
15235 + VBOHttpDocument::getInstance()->close(403, JText::translate('JERROR_ALERTNOAUTHOR'));
15236 + }
15237 +
15238 + $dbo = JFactory::getDbo();
15239 + $ids = VikRequest::getVar('cid', array());
15240 +
15241 + VikBooking::getConditionalRulesInstance(true);
15242 + $templates = VikBookingHelperConditionalRules::getTemplateFilesPaths();
15243 +
15244 + foreach ($ids as $d) {
15245 + $q = "SELECT `token` FROM `#__vikbooking_condtexts` WHERE `id`=" . (int)$d . ";";
15246 + $dbo->setQuery($q);
15247 + $dbo->execute();
15248 + if (!$dbo->getNumRows()) {
15249 + continue;
15250 + }
15251 + $special_tag = $dbo->loadResult();
15252 +
15253 + // remove the token from each template file if it was used before
15254 + if (!empty($special_tag)) {
15255 + // remove token from all template files
15256 + foreach ($templates as $tkey => $tpath) {
15257 + // get requested file content
15258 + $fcontent = VikBookingHelperConditionalRules::getTemplateFileCode($tkey);
15259 + if (empty($fcontent) || !is_string($fcontent)) {
15260 + break;
15261 + }
15262 + // remove tag from code content
15263 + $fcontent = str_replace($special_tag, '', $fcontent);
15264 + // update the file code
15265 + VikBookingHelperConditionalRules::writeTemplateFileCode($tkey, $fcontent);
15266 + }
15267 + }
15268 +
15269 + // delete the record
15270 + $q = "DELETE FROM `#__vikbooking_condtexts` WHERE `id`=" . (int)$d . ";";
15271 + $dbo->setQuery($q);
15272 + $dbo->execute();
15273 + }
15274 +
15275 + $this->cancelcondtext();
15276 + }
15277 +
15278 + /**
15279 + * AJAX endpoint to update one template file with the given tag or styles.
15280 + * A JSON response will be echoed by exiting the process.
15281 + */
15282 + public function condtext_update_tmpl()
15283 + {
15284 + VikBooking::getConditionalRulesInstance(true);
15285 +
15286 + $tagaction = VikRequest::getString('tagaction', '', 'request');
15287 + $tag = VikRequest::getString('tag', '', 'request');
15288 + $file = VikRequest::getString('file', '', 'request', VIKREQUEST_ALLOWRAW);
15289 + $newcontent = VikRequest::getString('newcontent', '', 'request', VIKREQUEST_ALLOWRAW);
15290 + $custom_classes = VikRequest::getVar('custom_classes', array(), 'request', 'array');
15291 +
15292 + $allowed_actions = array(
15293 + 'add',
15294 + 'remove',
15295 + 'styles',
15296 + 'restore',
15297 + );
15298 +
15299 + if (empty($tagaction) || empty($file) || !in_array($tagaction, $allowed_actions)) {
15300 + throw new Exception("Invalid request submitted", 500);
15301 + }
15302 +
15303 + if (in_array($tagaction, array('add', 'remove')) && empty($tag)) {
15304 + throw new Exception("Invalid request submitted - missing tag", 500);
15305 + }
15306 +
15307 + if (in_array($tagaction, array('add', 'styles')) && empty($newcontent)) {
15308 + throw new Exception("Invalid request submitted - missing new HTML content", 500);
15309 + }
15310 +
15311 + if ($tagaction == 'styles' && (!is_array($custom_classes) || !count($custom_classes))) {
15312 + throw new Exception("No custom CSS classes to parse", 500);
15313 + }
15314 +
15315 + if ($tagaction == 'restore') {
15316 + // immediately restore the requested file to avoid script interruptions
15317 + VikBookingHelperConditionalRules::restoreTemplateFileCode($file);
15318 + }
15319 +
15320 + // get requested file content
15321 + $fcontent = VikBookingHelperConditionalRules::getTemplateFileCode($file);
15322 + if (empty($fcontent) || !is_string($fcontent)) {
15323 + throw new Exception("File not found or its code is unreadable", 404);
15324 + }
15325 +
15326 + if ($tagaction == 'remove') {
15327 + // remove tag from code content
15328 + $fcontent = str_replace($tag, '', $fcontent);
15329 + } elseif ($tagaction == 'add') {
15330 + // add tag to code content in the same exact position
15331 + $fcontent = VikBookingHelperConditionalRules::addTagByComparingSources($tag, $file, $newcontent, $fcontent);
15332 + } elseif ($tagaction == 'styles') {
15333 + // apply the same styling rules
15334 + $fcontent = VikBookingHelperConditionalRules::addStylesByComparingSources($custom_classes, $file, $newcontent, $fcontent);
15335 + }
15336 +
15337 + // update the file code
15338 + $res = VikBookingHelperConditionalRules::writeTemplateFileCode($file, $fcontent);
15339 +
15340 + if (!$res) {
15341 + throw new Exception("Could not update the source code of the template file", 500);
15342 + }
15343 +
15344 + // parse new HTML content
15345 + $newhtmls = VikBookingHelperConditionalRules::getTemplateFilesContents($file);
15346 + if (!is_array($newhtmls) || !isset($newhtmls[$file])) {
15347 + throw new Exception("Could not parse new template file content", 404);
15348 + }
15349 +
15350 + // trigger backup/mirroring, if available
15351 + if (VBOPlatformDetection::isWordPress()) {
15352 + VikBookingUpdateManager::storeTemplateContent($file, $newhtmls[$file]);
15353 + }
15354 +
15355 + // build output
15356 + $output = new stdClass;
15357 + $output->newhtml = $newhtmls[$file];
15358 + $output->log = VikBookingHelperConditionalRules::getEditingLog();
15359 +
15360 + echo json_encode($output);
15361 + exit;
15362 + }
15363 +
15364 + /**
15365 + * AJAX endpoint to invoke methods of the geocoding helper.
15366 + */
15367 + public function geocoding_endpoint()
15368 + {
15369 + $geo = VikBooking::getGeocodingInstance();
15370 + $callback = VikRequest::getString('callback', '', 'request');
15371 +
15372 + if (empty($callback) || !method_exists($geo, $callback) || !is_callable(array($geo, $callback))) {
15373 + throw new Exception("Callback not available", 403);
15374 + }
15375 +
15376 + // invoke requested method
15377 + $res = $geo->{$callback}();
15378 +
15379 + // prepare response
15380 + $response = new stdClass;
15381 + $response->{$callback} = $res;
15382 +
15383 + echo json_encode($response);
15384 + exit;
15385 + }
15386 +
15387 + public function refundtn()
15388 + {
15389 + //modal box, so we do not set menu or footer
15390 +
15391 + VikRequest::setVar('view', VikRequest::getCmd('view', 'refundtn'));
15392 +
15393 + parent::display();
15394 + }
15395 +
15396 + public function do_refundtn()
15397 + {
15398 + $dbo = JFactory::getDbo();
15399 + $app = JFactory::getApplication();
15400 +
15401 + $bid = VikRequest::getInt('bid', 0, 'request');
15402 + $amount = VikRequest::getFloat('amount', 0, 'request');
15403 + $refund_reason = VikRequest::getString('refund_reason', '', 'request');
15404 + $tmpl = VikRequest::getString('tmpl', '', 'request');
15405 + $nav_suffix = $tmpl == 'component' ? '&tmpl=component' : '';
15406 +
15407 + $currencysymb = VikBooking::getCurrencySymb();
15408 +
15409 + if (empty($bid) || $amount <= 0) {
15410 + VikError::raiseWarning('', JText::translate('VBO_PLEASE_FILL_FIELDS'));
15411 + $app->redirect('index.php?option=com_vikbooking');
15412 + exit;
15413 + }
15414 +
15415 + $q = "SELECT * FROM `#__vikbooking_orders` WHERE `id`=" . $bid . " AND `status`!='standby';";
15416 + $dbo->setQuery($q);
15417 + $row = $dbo->loadAssoc();
15418 + if (!$row) {
15419 + VikError::raiseWarning('', 'Booking not found');
15420 + $app->redirect('index.php?option=com_vikbooking');
15421 + exit;
15422 + }
15423 +
15424 + // get booking history instance
15425 + $history_obj = VikBooking::getBookingHistoryInstance();
15426 + $history_obj->setBid($row['id']);
15427 +
15428 + // get payment information
15429 + $payment = VikBooking::getPayment($row['idpayment']);
15430 + $tn_driver = is_array($payment) ? $payment['file'] : null;
15431 +
15432 + // transaction data validation callback
15433 + $tn_data_callback = function($data) use ($tn_driver) {
15434 + return (is_object($data) && isset($data->driver) && basename($data->driver, '.php') == basename($tn_driver, '.php'));
15435 + };
15436 + // get previous transactions
15437 + $prev_tn_data = $history_obj->getEventsWithData(array('P0', 'PN'), $tn_data_callback);
15438 +
15439 + if (!is_array($prev_tn_data) || !count($prev_tn_data)) {
15440 + // no previous transactions found
15441 + VikError::raiseWarning('', 'No previous transactions found, unable to issue the refund');
15442 + $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15443 + exit;
15444 + }
15445 +
15446 + // push refund information for the payment gateway
15447 + $row['total_to_refund'] = $amount;
15448 + $row['transaction'] = $prev_tn_data;
15449 + $row['refund_reason'] = $refund_reason;
15450 +
15451 + // push the transaction currency information
15452 + $row['transaction_currency'] = VikBooking::getCurrencyCodePp();
15453 +
15454 + /**
15455 + * Trigger event to allow third-party plugins to manipulate the transaction data.
15456 + *
15457 + * @since 1.18.5 (J) - 1.8.5 (WP)
15458 + */
15459 + VBOFactory::getPlatform()->getDispatcher()->trigger('onInitRefundTransaction', [&$row, &$payment['params']]);
15460 +
15461 + if (VBOPlatformDetection::isWordPress()) {
15462 + /**
15463 + * @wponly The payment gateway is loaded
15464 + * through the apposite dispatcher.
15465 + */
15466 + JLoader::import('adapter.payment.dispatcher');
15467 + $obj = JPaymentDispatcher::getInstance('vikbooking', $payment['file'], $row, $payment['params']);
15468 + } else {
15469 + /**
15470 + * @joomlaonly The Payment Factory library will invoke the gateway.
15471 + *
15472 + * @since 1.14.3
15473 + */
15474 + require_once VBO_ADMIN_PATH . DIRECTORY_SEPARATOR . 'payments' . DIRECTORY_SEPARATOR . 'libraries' . DIRECTORY_SEPARATOR . 'factory.php';
15475 + $obj = VBOPaymentFactory::getPaymentInstance($payment['file'], $row, $payment['params']);
15476 + }
15477 +
15478 + if (!method_exists($obj, 'isRefundSupported') || !$obj->isRefundSupported()) {
15479 + // refund not supported
15480 + VikError::raiseWarning('', 'The selected payment method does not support refunds');
15481 + $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15482 + exit;
15483 + }
15484 +
15485 + // perform the refund transaction
15486 + $array_result = $obj->refund();
15487 +
15488 + if ($array_result['verified'] != 1) {
15489 + // raise warning by getting the message
15490 + if (!empty($array_result['log']) && is_string($array_result['log'])) {
15491 + VikError::raiseWarning('', $array_result['log']);
15492 + } else {
15493 + VikError::raiseWarning('', 'Operation failed');
15494 + }
15495 + $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . $nav_suffix);
15496 + exit;
15497 + }
15498 +
15499 + /**
15500 + * New payment plugins can return the total amount refunded ('tot_paid').
15501 + *
15502 + * @since 1.15.4 (J) - 1.5.10 (WP)
15503 + */
15504 + if (!empty($array_result['tot_paid'])) {
15505 + // overwrite the requested amount with the returned one
15506 + $amount = (float)$array_result['tot_paid'];
15507 + }
15508 +
15509 + /**
15510 + * The history event extra data will contain the "amount_paid" (refunded).
15511 + *
15512 + * @since 1.16.9 (J) - 1.6.9 (WP)
15513 + */
15514 + $history_obj->setExtraData([
15515 + 'amount_paid' => $amount,
15516 + ]);
15517 +
15518 + // update total paid, total and refund columns for the booking
15519 + $booking = new stdClass;
15520 + $booking->id = $row['id'];
15521 + if ($row['totpaid'] > 0) {
15522 + $booking->totpaid = $row['totpaid'] - $amount;
15523 + }
15524 + if ($row['total'] > 0) {
15525 + $booking->total = $row['total'] - $amount;
15526 + }
15527 + $booking->refund = (float)$row['refund'] + $amount;
15528 + // update record in db
15529 + $dbo->updateObject('#__vikbooking_orders', $booking, 'id');
15530 +
15531 + // store the refund event
15532 + $event_descr = [
15533 + '(' . $payment['name'] . ')',
15534 + $refund_reason,
15535 + $currencysymb . ' ' . VikBooking::numberFormat($amount),
15536 + ];
15537 + $history_obj->store('RF', implode("\n", $event_descr));
15538 +
15539 + // display success message and redirect
15540 + $app->enqueueMessage(JText::translate('VBO_REFUND_SUCCESS'));
15541 + $app->redirect('index.php?option=com_vikbooking&task=refundtn&cid[]=' . $row['id'] . '&success=1' . $nav_suffix);
15542 + exit;
15543 + }
15544 +
15545 + /**
15546 + * AJAX upload endpoint for media files.
15547 + *
15548 + * @return void
15549 + *
15550 + * @throws Exception
15551 + *
15552 + * @since 1.15.0 (J) - 1.5.0 (WP)
15553 + */
15554 + public function upload_media_file()
15555 + {
15556 + $input = JFactory::getApplication()->input;
15557 +
15558 + // allowed types
15559 + $type = $input->getString('type', '');
15560 + $mask = 'png,apng,jpg,jpeg,bmp,heic,webp,gif,ico,svg';
15561 +
15562 + if ($type != 'image') {
15563 + $mask .= ',zip,rar,pdf,doc,docx,rtf,odt,pages,xls,xlsx,csv,ods,numbers,txt,md';
15564 + }
15565 +
15566 + // response object
15567 + $result = new stdClass;
15568 + $result->status = 0;
15569 +
15570 + try
15571 + {
15572 + // get file from request
15573 + $file = $input->files->get('file', array(), 'array');
15574 +
15575 + // try to upload the file
15576 + $result = VikBooking::uploadFileFromRequest($file, VBO_MEDIA_PATH, $mask);
15577 + $result->status = 1;
15578 +
15579 + $result->size = JHtml::fetch('number.bytes', filesize($result->path), 'auto', 0);
15580 + $result->url = str_replace(DIRECTORY_SEPARATOR, '/', str_replace(VBO_MEDIA_PATH . DIRECTORY_SEPARATOR, VBO_MEDIA_URI, $result->path));
15581 + }
15582 + catch (Exception $e)
15583 + {
15584 + $result->error = $e->getMessage();
15585 + $result->code = $e->getCode();
15586 + }
15587 +
15588 + echo json_encode($result);
15589 + exit;
15590 + }
15591 +
15592 + /**
15593 + * AJAX endpoint to invoke a report object's method.
15594 + *
15595 + * @return void
15596 + *
15597 + * @since 1.15.0 (J) - 1.5.0 (WP)
15598 + * @since 1.18.6 (J) - 1.8.6 (WP) added support for "call_args".
15599 + */
15600 + public function invoke_report()
15601 + {
15602 + $app = JFactory::getApplication();
15603 +
15604 + $report_name = $app->input->getString('report', '');
15605 + $report_call = $app->input->getString('call', '');
15606 + $call_args = $app->input->get('call_args', [], 'array');
15607 + $params = $app->input->get('params', [], 'array');
15608 +
15609 + if (empty($report_name)) {
15610 + VBOHttpDocument::getInstance($app)->close(400, 'Missing report name');
15611 + }
15612 +
15613 + if (empty($report_call)) {
15614 + VBOHttpDocument::getInstance($app)->close(400, 'Missing report call');
15615 + }
15616 +
15617 + // get requested report instance
15618 + $report = VikBooking::getReportInstance($report_name);
15619 + if (!$report) {
15620 + VBOHttpDocument::getInstance($app)->close(404, 'Report not found');
15621 + }
15622 +
15623 + if (!method_exists($report, $report_call) || !is_callable(array($report, $report_call))) {
15624 + VBOHttpDocument::getInstance($app)->close(403, sprintf('Cannot call [%s] on report', $report_call));
15625 + }
15626 +
15627 + try {
15628 + // call on report's method
15629 + if ($call_args) {
15630 + $result = call_user_func_array([$report, $report_call], $call_args);
15631 + } else {
15632 + $result = $report->{$report_call}($params);
15633 + }
15634 + } catch (Exception $e) {
15635 + VBOHttpDocument::getInstance($app)->close($e->getCode() ?: 500, $e->getMessage());
15636 + }
15637 +
15638 + if (is_null($result)) {
15639 + VBOHttpDocument::getInstance($app)->close(400, 'Null response');
15640 + }
15641 +
15642 + if (is_scalar($result)) {
15643 + // wrap result within an array for a JSON encoded response
15644 + VBOHttpDocument::getInstance($app)->json([$result]);
15645 + }
15646 +
15647 + // output the JSON encoded array/object returned
15648 + VBOHttpDocument::getInstance($app)->json($result);
15649 + }
15650 +
15651 + /**
15652 + * Handles requests for the multitask widgets panel.
15653 + *
15654 + * @see this is an AJAX endpoint.
15655 + *
15656 + * @since 1.15.0 (J) - 1.5.0 (WP)
15657 + * @since 1.16.5 (J) - 1.6.5 (WP) widgets are rendered within a try-catch statement.
15658 + */
15659 + public function exec_multitask_widgets()
15660 + {
15661 + if (!JSession::checkToken()) {
15662 + // missing CSRF-proof token
15663 + VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15664 + }
15665 +
15666 + $call = VikRequest::getString('call', '', 'request');
15667 + $call_args = VikRequest::getVar('call_args', array(), 'request', 'array');
15668 +
15669 + if (empty($call)) {
15670 + VBOHttpDocument::getInstance()->close(500, 'Empty Admin Widget Callback');
15671 + }
15672 +
15673 + // invoke admin widgets helper
15674 + $widgets_helper = VikBooking::getAdminWidgetsInstance();
15675 +
15676 + if (!method_exists($widgets_helper, $call) || !is_callable(array($widgets_helper, $call))) {
15677 + VBOHttpDocument::getInstance()->close(403, 'Admin Widgets Callback not found or not callable');
15678 + }
15679 +
15680 + try {
15681 + // invoke the helper's method and get the value returned
15682 + if (is_array($call_args) && count($call_args)) {
15683 + $result = call_user_func_array(array($widgets_helper, $call), $call_args);
15684 + } else {
15685 + $result = $widgets_helper->{$call}();
15686 + }
15687 + } catch (Throwable $e) {
15688 + VBOHttpDocument::getInstance()->close($e->getCode() ?: 500, sprintf("%s\n%s at line %d", $e->getMessage(), $e->getFile(), $e->getLine()));
15689 + } catch (Exception $e) {
15690 + VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
15691 + }
15692 +
15693 + // prepare response object with the result property
15694 + $response = new stdClass;
15695 + $response->result = $result;
15696 +
15697 + // output the JSON response and exit
15698 + VBOHttpDocument::getInstance()->json($response);
15699 + }
15700 +
15701 + /**
15702 + * Handles requests for displaying a browser notification being dispatched.
15703 + *
15704 + * @see this is an AJAX endpoint.
15705 + *
15706 + * @since 1.15.0 (J) - 1.5.0 (WP)
15707 + */
15708 + public function notification_displayer()
15709 + {
15710 + $payload_str = VikRequest::getString('payload', '', 'request', VIKREQUEST_ALLOWRAW);
15711 +
15712 + if (empty($payload_str)) {
15713 + VBOHttpDocument::getInstance()->close(500, 'Empty notification payload');
15714 + }
15715 +
15716 + // attempt to decode the notification payload
15717 + $payload = json_decode($payload_str);
15718 +
15719 + if (!is_object($payload)) {
15720 + VBOHttpDocument::getInstance()->close(500, 'Could not decode notification payload: ' . $payload_str);
15721 + }
15722 +
15723 + // get notification displayer for this type of notification
15724 + $displayer = VBONotificationBuilder::getInstance($payload)->getDisplayer();
15725 + if (!$displayer) {
15726 + VBOHttpDocument::getInstance()->close(500, 'Could not build notification display data from payload: ' . $payload_str);
15727 + }
15728 +
15729 + // compose the notification display data object
15730 + try {
15731 + $notif_data = $displayer->getData();
15732 + if (!$notif_data) {
15733 + throw new Exception('Error building the notification display data', 500);
15734 + }
15735 + } catch (Exception $e) {
15736 + VBOHttpDocument::getInstance()->close($e->getCode(), $e->getMessage());
15737 + }
15738 +
15739 + // output the JSON response and exit
15740 + VBOHttpDocument::getInstance()->json($notif_data);
15741 + }
15742 +
15743 + /**
15744 + * Handles requests for watching widgets data and getting
15745 + * new events to trigger browser notifications.
15746 + *
15747 + * @see this is an AJAX endpoint.
15748 + *
15749 + * @since 1.15.0 (J) - 1.5.0 (WP)
15750 + * @since 1.16.8 (J) - 1.6.8 (WP) introduced notification events.
15751 + */
15752 + public function widgets_watch_data()
15753 + {
15754 + if (!JSession::checkToken()) {
15755 + // missing CSRF-proof token
15756 + VBOHttpDocument::getInstance()->close(403, JText::translate('JINVALID_TOKEN'));
15757 + }
15758 +
15759 + $app = JFactory::getApplication();
15760 +
15761 + $watch_data_str = $app->input->get('watch_data', '', 'raw');
15762 + $pushed_data_str = $app->input->get('pushed_data', '[]', 'raw');
15763 +
15764 + if (empty($watch_data_str)) {
15765 + VBOHttpDocument::getInstance()->close(500, 'Empty watch-data payload');
15766 + }
15767 +
15768 + // attempt to decode the watch-data payload
15769 + $watch_data = json_decode($watch_data_str, true);
15770 +
15771 + if (!$watch_data) {
15772 + VBOHttpDocument::getInstance()->close(500, 'Could not decode watch-data payload: ' . $watch_data_str);
15773 + }
15774 +
15775 + // check if any pushed data was set
15776 + $pushed_data = (array)json_decode($pushed_data_str, true);
15777 +
15778 + // container for new notifications
15779 + $notifs_pool = [];
15780 +
15781 + // container for the events data to dispatch
15782 + $events_pool = [];
15783 +
15784 + // get admin widgets helper
15785 + $widgets_helper = VikBooking::getAdminWidgetsInstance();
15786 +
15787 + foreach ($watch_data as $widget_id => $data) {
15788 + // invoke admin widget (with no pre-loading)
15789 + $widget_instance = $widgets_helper->getWidget($widget_id);
15790 + if (!$widget_instance) {
15791 + continue;
15792 + }
15793 +
15794 + // build the widget watch data object
15795 + $widget_watch_data = VBONotificationWatchdata::getInstance($data)->setPushedData($pushed_data);
15796 +
15797 + // check if the widget needs to emit browser notifications
15798 + list($watch_next, $notifications) = $widget_instance->getNotifications($widget_watch_data);
15799 +
15800 + // check if the widget needs to emit JavaScript events
15801 + $events = $widget_instance->getNotificationEvents($widget_watch_data);
15802 +
15803 + if ($watch_next) {
15804 + // update next watch-data object for this widget
15805 + $watch_data[$widget_id] = $watch_next;
15806 + }
15807 +
15808 + if (is_array($notifications) && $notifications) {
15809 + // merge notifications
15810 + $notifs_pool = array_merge($notifs_pool, $notifications);
15811 + }
15812 +
15813 + if (is_array($events) && $events) {
15814 + // push notification events for this widget
15815 + $events_pool[] = $events;
15816 + }
15817 + }
15818 +
15819 + // build the response object
15820 + $response = new stdClass;
15821 + $response->watch_data = $watch_data;
15822 + $response->notifications = $notifs_pool;
15823 + $response->events = $events_pool;
15824 +
15825 + // output the JSON response and exit
15826 + VBOHttpDocument::getInstance()->json($response);
15827 + }
15828 +
15829 + /**
15830 + * Outputs a list of CSS assets required to render the admin widgets
15831 + * externally from Vik Booking. Useful i.e. to Vik Channel Manager.
15832 + *
15833 + * @see this is an AJAX endpoint.
15834 + *
15835 + * @since 1.16.0 (J) - 1.6.0 (WP)
15836 + */
15837 + public function widgets_get_assets()
15838 + {
15839 + // list of needed CSS asset details
15840 + $assets_pool = [];
15841 +
15842 + // appearance preference assets (one or none)
15843 + $app_pref_asset = VikBooking::loadAppearancePreferenceAssets($get_info = true);
15844 +
15845 + if (VBOPlatformDetection::isWordPress()) {
15846 + // WordPress (main CSS)
15847 + $assets_pool[] = [
15848 + 'rel' => 'stylesheet',
15849 + 'id' => 'vbo-style-css',
15850 + 'href' => VIKBOOKING_ADMIN_ASSETS_URI . 'vikbooking.css?ver=' . VIKBOOKING_SOFTWARE_VERSION,
15851 + 'media' => 'all',
15852 + ];
15853 +
15854 + if (is_array($app_pref_asset) && !empty($app_pref_asset['href'])) {
15855 + // appearance preference CSS
15856 + $assets_pool[] = [
15857 + 'rel' => 'stylesheet',
15858 + 'id' => (!empty($app_pref_asset['id']) ? $app_pref_asset['id'] : rand()),
15859 + 'href' => $app_pref_asset['href'] . '?ver=' . VIKBOOKING_SOFTWARE_VERSION,
15860 + 'media' => 'all',
15861 + ];
15862 + }
15863 + } else {
15864 + // Joomla (main CSS)
15865 + $assets_pool[] = [
15866 + 'rel' => 'stylesheet',
15867 + 'id' => 'vbo-style-css',
15868 + 'href' => VBO_ADMIN_URI . 'vikbooking.css?' . VIKBOOKING_SOFTWARE_VERSION,
15869 + 'media' => 'all',
15870 + ];
15871 +
15872 + if (is_array($app_pref_asset) && !empty($app_pref_asset['href'])) {
15873 + // appearance preference CSS
15874 + $assets_pool[] = [
15875 + 'rel' => 'stylesheet',
15876 + 'id' => (!empty($app_pref_asset['id']) ? $app_pref_asset['id'] : rand()),
15877 + 'href' => $app_pref_asset['href'] . '?' . VIKBOOKING_SOFTWARE_VERSION,
15878 + 'media' => 'all',
15879 + ];
15880 + }
15881 + }
15882 +
15883 + // output the JSON response and exit
15884 + VBOHttpDocument::getInstance()->json($assets_pool);
15885 + }
15886 +}