Published : July 7, 2026

CVE-2026-6818: VikBooking Hotel Booking Engine & PMS <= 1.8.8 Unauthenticated Stored Cross-Site Scripting via 'special_requests' Parameter PoC, Patch Analysis & Rule

CVE ID CVE-2026-6818
Plugin vikbooking
Severity High (CVSS 7.2)
CWE 79
Vulnerable Version 1.8.8
Patched Version 1.8.9
Disclosed July 6, 2026

Analysis Overview

Atomic Edge analysis of CVE-2026-6818:

This vulnerability is an unauthenticated Stored Cross-Site Scripting (XSS) vulnerability in the VikBooking Hotel Booking Engine & PMS plugin for WordPress, affecting all versions up to and including 1.8.8. The flaw exists in the ‘special_requests’ parameter during the booking process, where unauthenticated attackers can inject arbitrary web scripts that execute when administrators view affected pages. The CVSS score is 7.2 (High).

The root cause lies in insufficient input sanitization and output escaping for the ‘special_requests’ parameter. The vulnerable code is located in the front-end controller at `vikbooking/site/controller.php`. Key changes show that the `$inquiry` parameter (which includes ‘special_requests’) was fetched using `$input->get(…, ‘raw’)` and assigned to `$info_val` without sanitization. The patch changes this to `’array’` filter and adds `strip_tags($info_val)` at lines ~4629-4631 to strip HTML tags from the submitted value. This allows an attacker to inject script tags (e.g., `alert(‘XSS’)`) that are stored in the database and later rendered without escaping in admin views like the booking details widget.

Exploitation requires an unauthenticated attacker to submit a booking request with a malicious payload in the ‘special_requests’ field. The attacker sends a POST request to the endpoint that handles the booking confirmation (likely `index.php?option=com_vikbooking&task=oconfirm` or similar), including parameters such as `inquiry[special_requests]=alert(document.cookie)`. No authentication is required. The payload is stored and executed when an administrator views the booking details, typically via the admin panel or email notifications.

The patch addresses the vulnerability by changing the input filter from ‘raw’ (which allows unfiltered HTML) to ‘array’, and then applying `strip_tags()` to the value before storing it. This removes any HTML tags, including script elements, from the ‘special_requests’ input. The patch is applied in `vikbooking/site/controller.php` at lines ~4580-4631. Additional hardening includes proper output escaping in other areas like `vikbooking/admin/helpers/widgets/booking_details.php` (line 684) where `htmlspecialchars()` is applied to `$details[‘adminnotes’]`, and in `vikbooking/admin/views/orders/tmpl/default.php` (line 769) where `htmlspecialchars()` is applied to `$row[‘adminnotes’]`. The version number is also updated from 1.8.8 to 1.8.9.

Successful exploitation allows an attacker to execute arbitrary JavaScript in the context of an administrator’s browser session. This can lead to session hijacking, theft of cookies or authentication tokens, defacement of the admin interface, creation of rogue admin accounts (if combined with CSRF), or theft of sensitive booking data. Since the attack is unauthenticated and stored, any administrator accessing the compromised booking record will trigger the payload, maximizing the impact.

Differential between vulnerable and patched code

Below is a differential between the unpatched vulnerable code and the patched update, for reference.

Code Diff
--- a/vikbooking/admin/controller.php
+++ b/vikbooking/admin/controller.php
@@ -2519,11 +2519,46 @@
 					$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'] . ";";
 					$dbo->setQuery($q);
 					$allbusy = $dbo->loadAssocList();
+
 					foreach ($allbusy as $bb) {
 						$q = "UPDATE `#__vikbooking_busy` SET `checkin`=" . $first . ", `checkout`=" . $second . ", `realback`=" . $realback . " WHERE `id`=" . $bb['id'] . ";";
 						$dbo->setQuery($q);
 						$dbo->execute();
 					}
+
+					if (!$allbusy) {
+						/**
+						 * If no existing busy records were fetched, it means we have some missing or broken
+						 * records in the database. Proceed with restoring them to occupy the room(s).
+						 *
+						 * @since 	1.8.19 (J) - 1.8.9 (WP)
+						 */
+						$dbo->setQuery(
+							$dbo->getQuery(true)
+								->delete($dbo->qn('#__vikbooking_ordersbusy'))
+								->where($dbo->qn('idorder') . ' = ' . (int) $ord['id'])
+						);
+						$dbo->execute();
+						foreach ($ordersrooms as $or) {
+							if ($room_removed !== false && $or['id'] == $prm_room_oid) {
+								continue;
+							}
+							$restoreBusyRecord = (object) [
+								'idroom'   => $or['idroom'],
+								'checkin'  => $first,
+								'checkout' => $second,
+								'realback' => $realback,
+							];
+							$dbo->insertObject('#__vikbooking_busy', $restoreBusyRecord, 'id');
+							if (!empty($restoreBusyRecord->id)) {
+								$restoreBusyRelation = (object) [
+									'idorder' => $ord['id'],
+									'idbusy'  => $restoreBusyRecord->id,
+								];
+								$dbo->insertObject('#__vikbooking_ordersbusy', $restoreBusyRelation, 'id');
+							}
+						}
+					}
 				}

 				/**
@@ -11170,7 +11205,7 @@
 		$cust_old_fields = array();
 		$cstring_search = '<div class="vbo-custsearchres-inner">' . "n";
 		foreach ($customers as $k => $v) {
-			$cstring_search .= '<div class="' . $selector . '" data-custid="'.$v['id'].'" data-email="'.$v['email'].'" data-phone="'.htmlspecialchars($v['phone']).'" data-country="'.$v['country'].'" data-pin="'.$v['pin'].'" data-firstname="'.htmlspecialchars($v['first_name']).'" data-lastname="'.htmlspecialchars($v['last_name']).'">'."n";
+			$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";
 			$cstring_search .= '<span class="vbo-custsearchres-cflag">';
 			if (!empty($v['pic'])) {
 				$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";
--- a/vikbooking/admin/controllers/payschedules.php
+++ b/vikbooking/admin/controllers/payschedules.php
@@ -46,7 +46,7 @@
 		}

 		// current date object
-		$now_dt = JFactory::getdate('now');
+		$now_dt = JFactory::getDate('now');

 		// access the time with hours and minutes
 		$time_parts = explode(':', $time);
--- a/vikbooking/admin/controllers/quote.php
+++ b/vikbooking/admin/controllers/quote.php
@@ -135,6 +135,7 @@
                 'children'   => array_sum(array_column($solution['rooms'], 'children')),
                 'id_payment' => $quote['id_payment'] ?? null,
                 'lock_until' => $quote['validity'] ?? null,
+                'dont_lock'  => empty($quote['validity']),
                 'idquote'    => $quoteId,
             ], true);

--- a/vikbooking/admin/helpers/src/model/payschedules.php
+++ b/vikbooking/admin/helpers/src/model/payschedules.php
@@ -156,6 +156,54 @@
     }

     /**
+     * Method to delete one or more records.
+     *
+     * @param   mixed    $pks  An array of record primary keys, or a single one.
+     *
+     * @return  bool     True if successful, false if an error occurs.
+     *
+     * @since   1.8.19 (J) - 1.8.9 (WP)
+     */
+    public function delete($pks)
+    {
+        $dbo = JFactory::getDbo();
+
+        if (!$pks) {
+            // nothing to delete
+            return false;
+        }
+
+        if (!is_array($pks)) {
+            // wrap into an array
+            $pks = [$pks];
+        }
+
+        $deleted = 0;
+
+        foreach ($pks as $pk) {
+            // ensure the item exists
+            $item = $this->getItem($pk);
+
+            if (!$item) {
+                continue;
+            }
+
+            $dbo->setQuery(
+                $dbo->getQuery(true)
+                    ->delete($dbo->qn('#__vikbooking_payschedules'))
+                    ->where($dbo->qn('id') . ' = ' . (int) $item->id)
+            );
+            $dbo->execute();
+
+            if ((bool) $dbo->getAffectedRows()) {
+                $deleted++;
+            }
+        }
+
+        return (bool) $deleted;
+    }
+
+    /**
      * Watches and eventually processes the automatic payment collections scheduled.
      *
      * @param   int     $lim    the limit of payments to process, defaults to 5 per execution.
--- a/vikbooking/admin/helpers/src/model/quote.php
+++ b/vikbooking/admin/helpers/src/model/quote.php
@@ -137,6 +137,7 @@
                         $dbo->qn('o.idpayment'),
                         $dbo->qn('o.roomsnum'),
                         $dbo->qn('o.total'),
+                        $dbo->qn('o.lang'),
                         $dbo->qn('o.idquote'),
                         $dbo->qn('or.idroom'),
                         $dbo->qn('or.adults'),
@@ -214,6 +215,7 @@
                         'nights'    => $bookingRoomsData[0]->days,
                         'status'    => $bookingRoomsData[0]->status,
                         'total'     => $bookingRoomsData[0]->total,
+                        'lang'      => $bookingRoomsData[0]->lang,
                         'idpayment' => $bookingRoomsData[0]->idpayment,
                         'sid'       => $bookingRoomsData[0]->sid,
                         'ts'        => $bookingRoomsData[0]->ts,
@@ -390,6 +392,13 @@
         // access booking registry for the first related booking (if any)
         $bookingRegistry = $bookingIds ? VBOBookingRegistry::getInstance(['id' => $bookingIds[0] ?? 0]) : null;

+        // route quote URI
+        $quoteUri = VikBooking::externalroute(
+            "index.php?option=com_vikbooking&view=quote&ref={$quote->uuid}",
+            false,
+            (VikBooking::findProperItemIdType(['quote'], $bookingRegistry->getProperty('lang') ?: null) ?: null)
+        );
+
         // build the list of known tag parameters
         $tagParameters = [
             '{first_name}'    => $quote->first_name ?? '',
@@ -400,7 +409,7 @@
             '{tot_adults}'    => $bookingRegistry ? $bookingRegistry->countTotalAdults() : 0,
             '{tot_children}'  => $bookingRegistry ? $bookingRegistry->countTotalChildren() : 0,
             '{tot_guests}'    => $bookingRegistry ? $bookingRegistry->countTotalGuests() : 0,
-            '{quote_link}'    => VikBooking::externalroute("index.php?option=com_vikbooking&view=quote&ref={$quote->uuid}", false),
+            '{quote_link}'    => $quoteUri,
         ];

         /**
--- a/vikbooking/admin/helpers/src/model/reservation.php
+++ b/vikbooking/admin/helpers/src/model/reservation.php
@@ -2597,6 +2597,7 @@
         $num_rooms        = $this->isMultiRoom() ? count($rooms_pool) : $this->get('num_rooms', 1);
         $default_checkin  = $this->get('checkin', 0);
         $default_checkout = $this->get('checkout', 0);
+        $check_locked     = (bool) $this->get('check_locked', 0);

         // determine availability
         $rooms_available = true;
@@ -2624,8 +2625,13 @@
                     // check the remaining availability for the number of room units booked
                     $unitsBooked = $roomUnitsUse[$roomStay['idroom']];
                     $check_units = $roomStay['units'] - $unitsBooked + 1;
-                    // check if the room is available
-                    $rooms_available = VikBooking::roomBookable($roomStay['idroom'], $check_units, $roomStay['checkin'], $roomStay['checkout']);
+                    if ($check_locked) {
+                        // check if the room is available and not temporarily locked
+                        $rooms_available = VikBooking::roomNotLocked($roomStay['idroom'], $check_units, $roomStay['checkin'], $roomStay['checkout'], true);
+                    } else {
+                        // check if the room is available
+                        $rooms_available = VikBooking::roomBookable($roomStay['idroom'], $check_units, $roomStay['checkin'], $roomStay['checkout']);
+                    }
                     if (!$rooms_available) {
                         // set an error and abort
                         $this->setError(sprintf(
@@ -2646,8 +2652,13 @@
                     // only when non closing the room we check the availability for the units requested for booking
                     $check_units = $check_units - $num_rooms + 1;
                 }
-                // check if the room is available
-                $rooms_available = VikBooking::roomBookable($rooms_pool[0]['id'], $check_units, $default_checkin, $default_checkout);
+                if ($check_locked) {
+                    // check if the room is available and not temporarily locked
+                    $rooms_available = VikBooking::roomNotLocked($rooms_pool[0]['id'], $check_units, $default_checkin, $default_checkout, true);
+                } else {
+                    // check if the room is available
+                    $rooms_available = VikBooking::roomBookable($rooms_pool[0]['id'], $check_units, $default_checkin, $default_checkout);
+                }
             }
         } else {
             $all_rooms = $this->loadAllRoomsData();
@@ -3490,21 +3501,24 @@
                 } elseif ($status == 'standby' && empty($split_stay_data)) {
                     // lock room for pending status when NO split-stay data by supporting room-level dates
                     $lockUntil = VikBooking::getMinutesLock(true);
+
                     if ($this->get('lock_until')) {
                         // convert expected date-time string into a timestamp for custom lock until date
                         $lockUntil = strtotime((string) $this->get('lock_until')) ?: $lockUntil;
                     }

-                    // store room lock record
-                    $tmplock_record = new stdClass;
-                    $tmplock_record->idroom   = (int) $nowroom['id'];
-                    $tmplock_record->checkin  = $room_stay_checkin;
-                    $tmplock_record->checkout = $room_stay_checkout;
-                    $tmplock_record->until    = $lockUntil;
-                    $tmplock_record->realback = $room_stay_realback;
-                    $tmplock_record->idorder  = (int) $newoid;
+                    if ($lockUntil && $this->get('dont_lock') !== true) {
+                        // store room lock record
+                        $tmplock_record = new stdClass;
+                        $tmplock_record->idroom   = (int) $nowroom['id'];
+                        $tmplock_record->checkin  = $room_stay_checkin;
+                        $tmplock_record->checkout = $room_stay_checkout;
+                        $tmplock_record->until    = $lockUntil;
+                        $tmplock_record->realback = $room_stay_realback;
+                        $tmplock_record->idorder  = (int) $newoid;

-                    $dbo->insertObject('#__vikbooking_tmplock', $tmplock_record, 'id');
+                        $dbo->insertObject('#__vikbooking_tmplock', $tmplock_record, 'id');
+                    }
                 }
             }
         }
--- a/vikbooking/admin/helpers/src/taxonomy/summary.php
+++ b/vikbooking/admin/helpers/src/taxonomy/summary.php
@@ -201,13 +201,17 @@
 			return $tax_map[$tax_id];
 		}

+		if (empty($tax_id)) {
+			return [];
+		}
+
 		$dbo = JFactory::getDbo();

 		$q = "SELECT * FROM `#__vikbooking_iva` WHERE `id`=" . $tax_id;
 		$dbo->setQuery($q, 0, 1);

 		// cache value and return it
-		$tax_map[$tax_id] = $dbo->loadAssoc();
+		$tax_map[$tax_id] = (array) $dbo->loadAssoc();

 		return $tax_map[$tax_id];
 	}
--- a/vikbooking/admin/helpers/widgets/booking_details.php
+++ b/vikbooking/admin/helpers/widgets/booking_details.php
@@ -681,7 +681,7 @@
 						<div class="vbo-params-block">
 							<div class="vbo-param-container">
 								<div class="vbo-param-setting">
-									<blockquote class="vbo-booking-admin-notes"><?php echo nl2br($details['adminnotes']); ?></blockquote>
+									<blockquote class="vbo-booking-admin-notes"><?php echo nl2br(htmlspecialchars($details['adminnotes'])); ?></blockquote>
 								</div>
 							</div>
 						</div>
--- a/vikbooking/admin/helpers/widgets/guest_messages.php
+++ b/vikbooking/admin/helpers/widgets/guest_messages.php
@@ -405,7 +405,7 @@
 					<div class="vbo-dashboard-guest-activity-content-head">
 						<div class="vbo-dashboard-guest-activity-content-info-details">
 							<h4 class="vbo-w-guestmessages-message-gtitle"><span><?php
-							if (!$gmessage->first_name && !$gmessage->last_name) {
+							if (empty($gmessage->first_name) && empty($gmessage->last_name)) {
 								echo JText::translate('VBO_GUEST');
 							} else {
 								echo $gmessage->first_name . (!empty($gmessage->last_name) ? ' ' . $gmessage->last_name : '');
--- a/vikbooking/admin/layouts/quote/manage/html.php
+++ b/vikbooking/admin/layouts/quote/manage/html.php
@@ -84,6 +84,12 @@

     <?php
     if ($quote) {
+        // route quote URI
+        $quoteUri = VikBooking::externalroute(
+            "index.php?option=com_vikbooking&view=quote&ref={$quote->uuid}",
+            false,
+            (VikBooking::findProperItemIdType(['quote'], $quote->solutions[0]->lang ?? null) ?: null)
+        );
         ?>
         <div class="vbo-quote-section vbo-quote-section-current" data-quote-id="<?php echo $quote->id; ?>">
             <div class="vbo-quote-section-head vbo-quote-section-head-sb">
@@ -92,7 +98,7 @@
                     <span class="vbo-quote-section-name"><?php echo sprintf('%s #%d', JText::translate('VBO_BTYPE_QUOTE'), $quote->id); ?></span>
                 </div>
                 <div class="vbo-quote-edit-link">
-                    <a class="btn btn-small vbo-config-btn" target="_blank" href="<?php echo VikBooking::externalroute('index.php?option=com_vikbooking&view=quote&ref=' . $quote->uuid); ?>"><?php VikBookingIcons::e('external-link'); ?> <?php echo JText::translate('VBVIEWORDFRONT'); ?></a>
+                    <a class="btn btn-small vbo-config-btn" target="_blank" href="<?php echo $quoteUri; ?>"><?php VikBookingIcons::e('external-link'); ?> <?php echo JText::translate('VBVIEWORDFRONT'); ?></a>
                 </div>
             </div>
             <div class="vbo-quote-section-body">
@@ -402,7 +408,7 @@
             </div>
             <div class="vbo-quote-section-body">
                 <div class="vbo-quote-validity-date">
-                    <label for="vbo-quote-valid-until"><?php echo JText::translate('VBO_QUOTE_VALIDITY'); ?></label>
+                    <label for="vbo-quote-valid-until"><?php echo JText::translate('VBO_QUOTE_VALIDITY'); ?> <span class="vbo-quote-validity-date-help"><?php VikBookingIcons::e('circle-question', 'icn-nomargin'); ?></span></label>
                     <?php
                     $validUntilVal = JFactory::getDate('+2 days 23:59')->format('Y-m-d H:i');
                     if ($quote) {
--- a/vikbooking/admin/layouts/quote/manage/script.php
+++ b/vikbooking/admin/layouts/quote/manage/script.php
@@ -517,6 +517,18 @@
         });

         /**
+         * Register event listener for the valid until help icon.
+         */
+        document.querySelector('.vbo-quote-validity-date-help')?.addEventListener('click', (e) => {
+            VBOCore.displayModal({
+                extra_class: 'vbo-modal-rounded vbo-modal-tooltip',
+                body:        <?php echo json_encode(JText::translate('VBO_LOCK_UNTIL_HELP')); ?>,
+                lock_scroll: true,
+                draggable:   false,
+            });
+        });
+
+        /**
          * Register event listener for the quote submit (save new) button.
          */
         document.querySelector('.vbo-quote-submit-btn')?.addEventListener('click', (e) => {
--- a/vikbooking/admin/views/editorder/tmpl/default.php
+++ b/vikbooking/admin/views/editorder/tmpl/default.php
@@ -2714,7 +2714,7 @@
 					</div>
 					<div class="vbo-bookingdet-noteslogs-cont">
 						<div id="vbadminnotesdiv" class="vbo-extra-panel" style="display: block;">
-							<textarea name="adminnotes" class="vbadminnotestarea"><?php echo strip_tags((string)$row['adminnotes']); ?></textarea>
+							<textarea name="adminnotes" class="vbadminnotestarea"><?php echo JHtml::fetch('esc_textarea', (string) $row['adminnotes']); ?></textarea>
 							<input type="submit" name="updadmnotes" value="<?php echo JText::translate('VBADMINNOTESUPD'); ?>" class="btn btn-success" />
 						</div>
 					<?php
--- a/vikbooking/admin/views/orders/tmpl/default.php
+++ b/vikbooking/admin/views/orders/tmpl/default.php
@@ -766,7 +766,7 @@
 				if (!empty($row['adminnotes'])) {
 					?>
 				<span class="vbo-admin-tipsicon vbo-admin-notes-icn" data-bid="<?php echo $row['id']; ?>"><?php VikBookingIcons::e('sticky-note'); ?></span> 
-				<div class="vbo-order-admin-notes-cnt" data-bid="<?php echo $row['id']; ?>" style="display: none;"><?php echo nl2br($row['adminnotes']); ?></div>
+				<div class="vbo-order-admin-notes-cnt" data-bid="<?php echo $row['id']; ?>" style="display: none;"><?php echo nl2br(htmlspecialchars($row['adminnotes'])); ?></div>
 					<?php
 				}

--- a/vikbooking/defines.php
+++ b/vikbooking/defines.php
@@ -12,7 +12,7 @@
 defined('ABSPATH') or die('No script kiddies please!');

 // Software version
-define('VIKBOOKING_SOFTWARE_VERSION', '1.8.8');
+define('VIKBOOKING_SOFTWARE_VERSION', '1.8.9');

 // Base path
 define('VIKBOOKING_BASE', dirname(__FILE__));
--- a/vikbooking/libraries/language/admin.php
+++ b/vikbooking/libraries/language/admin.php
@@ -8672,6 +8672,9 @@
 			case 'VBO_QUOTE_NOTES_HELP':
 				$result = __('Notes will be displayed to customer in front-end.', 'vikbooking');
 				break;
+			case 'VBO_LOCK_UNTIL_HELP':
+				$result = __('Set a date to temporarily lock the selected rooms. Leave blank to keep availability open.', 'vikbooking');
+				break;
 		}

 		return $result;
--- a/vikbooking/site/controller.php
+++ b/vikbooking/site/controller.php
@@ -304,6 +304,10 @@
 				$user_inp_val = VikRequest::getString('vbf' . $cf['id'], '', 'request');
 				if (intval($cf['isemail']) == 1 && $emailwasfound == false) {
 					$useremail = trim($user_inp_val);
+					if (!preg_match('/^(([^<>()[]\.,;:s@"]+(.[^<>()[]\.,;:s@"]+)*)|(".+"))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9]+.)+[a-zA-Z]{2,}))$/', $useremail)) {
+						showSelectVb('Invalid email address provided.');
+						return;
+					}
 					$emailwasfound = true;
 				}
 				if ($cf['isnominative'] == 1) {
@@ -4576,9 +4580,9 @@
 		$checkout_m  = $input->getInt('checkoutm', 0);
 		$categories  = $input->getString('categories', '');
 		$roomsnum  	 = $input->getInt('roomsnum', 1);
-		$adults 	 = $input->get('adults', array(), 'int');
-		$children 	 = $input->get('children', array(), 'int');
-		$inquiry 	 = $input->get('inquiry', array(), 'raw');
+		$adults 	 = $input->get('adults', [], 'int');
+		$children 	 = $input->get('children', [], 'int');
+		$inquiry 	 = $input->get('inquiry', [], 'array');
 		$ulang 		 = $input->getString('ulang', '');

 		$timeopst = VikBooking::getTimeOpenStore();
@@ -4625,6 +4629,8 @@
 					// empty or invalid field
 					continue;
 				}
+				// accept only text from submitted value
+				$info_val = strip_tags($info_val);
 				if ($info_type == 'nominative') {
 					if (empty($t_first_name)) {
 						$t_first_name = $info_val;
--- a/vikbooking/site/helpers/icons.php
+++ b/vikbooking/site/helpers/icons.php
@@ -100,6 +100,7 @@
         'cog'                  => 'fas fa-gear',
         'cogs'                 => 'fas fa-gears',
         'user-cog'             => 'fas fa-user-gear',
+        'circle-question'      => 'far fa-circle-question',

         /**
          * Resolving map used until the older FA v5.12.1.
--- a/vikbooking/site/helpers/lib.vikbooking.php
+++ b/vikbooking/site/helpers/lib.vikbooking.php
@@ -2038,21 +2038,16 @@
 	 * 			This was made to return the proper number of days in advance
 	 * 			in case the property is currently closed.
 	 */
-	public static function getMinDaysAdvance($no_closing_dates = false) {
+	public static function getMinDaysAdvance($no_closing_dates = false)
+	{
 		// cache value in static var
 		static $getMinDaysAdvance = null;

 		if ($getMinDaysAdvance) {
 			return $getMinDaysAdvance;
 		}
-		//

-		$dbo = JFactory::getDbo();
-
-		$q = "SELECT `setting` FROM `#__vikbooking_config` WHERE `param`='mindaysadvance';";
-		$dbo->setQuery($q);
-		$dbo->execute();
-		$mind = $dbo->getNumRows() ? (int)$dbo->loadResult() : 0;
+		$mind = VBOFactory::getConfig()->getInt('mindaysadvance', 0);

 		// update cached var
 		$getMinDaysAdvance = $mind;
@@ -5185,7 +5180,7 @@
 			return $cost;
 		}

-		$tax_rate = VBOTaxonomySummary::getTaxRateRecord($idiva);
+		$tax_rate = VBOTaxonomySummary::getTaxRateRecord((int) $idiva);
 		if (!$tax_rate) {
 			return $cost;
 		}
@@ -5232,7 +5227,7 @@
 			return $cost;
 		}

-		$tax_rate = VBOTaxonomySummary::getTaxRateRecord($idiva);
+		$tax_rate = VBOTaxonomySummary::getTaxRateRecord((int) $idiva);
 		if (!$tax_rate) {
 			return $cost;
 		}
@@ -5278,7 +5273,7 @@
 			return $cost;
 		}

-		$tax_rate = VBOTaxonomySummary::getTaxRateRecord($idiva);
+		$tax_rate = VBOTaxonomySummary::getTaxRateRecord((int) $idiva);
 		if (!$tax_rate) {
 			return $cost;
 		}
@@ -5322,7 +5317,7 @@
 			return $cost;
 		}

-		$tax_rate = VBOTaxonomySummary::getTaxRateRecord($idiva);
+		$tax_rate = VBOTaxonomySummary::getTaxRateRecord((int) $idiva);
 		if (!$tax_rate) {
 			return $cost;
 		}
--- a/vikbooking/site/views/oconfirm/tmpl/default.php
+++ b/vikbooking/site/views/oconfirm/tmpl/default.php
@@ -1339,7 +1339,7 @@
 	$dep_amount = VikBooking::calcDepositOverride($dep_amount, $days);
 	$dep_type = VikBooking::getTypeDeposit();
 	$dep_nonrefund_allowed = VikBooking::allowDepositFromRates($tars);
-	if (!(count($this->mod_booking) > 0) && !VikBooking::payTotal() && VikBooking::depositAllowedDaysAdv($second) && VikBooking::depositCustomerChoice() && $dep_amount > 0 && $dep_nonrefund_allowed && ($dep_type == "fixed" || ($dep_type != "fixed" && $dep_amount < 100))) {
+	if (!(count($this->mod_booking) > 0) && !VikBooking::payTotal() && VikBooking::depositAllowedDaysAdv($first) && VikBooking::depositCustomerChoice() && $dep_amount > 0 && $dep_nonrefund_allowed && ($dep_type == "fixed" || ($dep_type != "fixed" && $dep_amount < 100))) {
 		$dep_amount = ($dep_amount - abs($dep_amount)) > 0.00 ? VikBooking::numberFormat($dep_amount) : $dep_amount;
 		$dep_string = $dep_type == "fixed" ? VikBooking::formatCurrencyNumber($dep_amount, $currencysymb) : $dep_amount . '%';
 		?>
--- a/vikbooking/site/views/quote/tmpl/default.php
+++ b/vikbooking/site/views/quote/tmpl/default.php
@@ -150,6 +150,11 @@
             $totalRooms    = count($quoteSolution->rooms);
             // tell if we should "hide" this quote solution by default
             $isHid = $isConfirmed && $quoteSolution->status != 'confirmed';
+            // rate components collector
+            $rateComponents = [
+                'applied'  => 0,
+                'original' => 0,
+            ];
             ?>
             <div class="vbo-quote-solution">

@@ -248,6 +253,9 @@
                                 // fallback onto the last room rate obtained
                                 $origRoomRateData = end($roomRates[$bookingRoom->id]) ?: null;
                             }
+                            // increase rate components
+                            $rateComponents['applied']  += $bookingRoom->cust_cost;
+                            $rateComponents['original'] += $origRoomRateData['cost'];
                         }
                     }
                     ?>
@@ -417,7 +425,23 @@
                 <div class="vbo-quote-sol-footer" style="<?php echo $isHid ? 'display: none;' : ''; ?>">
                     <div class="vbo-quote-sol-total">
                         <div class="vbo-quote-sol-total-label"><?php echo JText::translate('VBTOTAL'); ?></div>
-                        <div class="vbo-quote-sol-total-price"><?php echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($quoteSolution->total), $currencysymb, ['<span class="vbo_currency">%s</span>', '<span class="vbo_price">%s</span>']); ?></div>
+                        <div class="vbo-quote-sol-total-price">
+                        <?php
+                        if ($rateComponents['applied'] && $rateComponents['original'] > $rateComponents['applied']) {
+                            // calculate the total cost without the custom discounted rate
+                            $originalTotal = $quoteSolution->total - $rateComponents['applied'] + $rateComponents['original'];
+                            // display striked-through amount
+                            ?>
+                            <span class="vbo-quote-sol-total-price-disc"><?php
+                            echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($originalTotal), $currencysymb, ['<span class="vbo_currency">%s</span>', '<span class="vbo_price">%s</span>']);
+                            ?></span>
+                            <?php
+                        }
+
+                        // display quote solution total amount
+                        echo VikBooking::formatCurrencyNumber(VikBooking::numberFormat($quoteSolution->total), $currencysymb, ['<span class="vbo_currency">%s</span>', '<span class="vbo_price">%s</span>']);
+                        ?>
+                        </div>
                         <div class="vbo-quote-sol-total-subtext"><?php
                         echo implode(', ', [
                             sprintf('%d %s', $totalRooms, JText::translate($totalRooms === 1 ? 'VBSEARCHRESROOM' : 'VBSEARCHRESROOMS')),
--- a/vikbooking/vikbooking.php
+++ b/vikbooking/vikbooking.php
@@ -3,7 +3,7 @@
 Plugin Name:  VikBooking
 Plugin URI:   https://vikwp.com/plugin/vikbooking
 Description:  Certified Booking Engine for Hotels and Accommodations.
-Version:      1.8.8
+Version:      1.8.9
 Author:       E4J s.r.l.
 Author URI:   https://vikwp.com
 License:      GPL2

Frequently Asked Questions

Atomic Edge WAF security layer inspecting website traffic.

How Atomic Edge Works

Simple Setup. Powerful Security.

Atomic Edge acts as a security layer between your website & the internet. Our AI inspection and analysis engine auto blocks threats before traditional firewall services can inspect, research and build archaic regex filters.

Get Started

Trusted by Developers & Organizations

Trusted by Developers
Black & McDonald logo representing Enterprise tier security and support for Atomic Edge WAF.Covenant House Toronto logo featuring a dove and text for Atomic Edge Enterprise planAlzheimer Society Canada logo representing trusted organizations and security partners.University of Toronto logo representing trusted organizations using Atomic Edge WAFSpecsavvers logo, trusted developers and organizations using Atomic Edge securityHarvard Medical School logo representing trusted organizations using Atomic Edge WAF.