Below is a differential between the unpatched vulnerable code and the patched update, for reference.
--- a/motopress-hotel-booking-lite/functions.php
+++ b/motopress-hotel-booking-lite/functions.php
@@ -894,6 +894,7 @@
function mphb_get_polyfill_for( $function ) {
switch ( $function ) {
case 'mb_convert_encoding':
+ case 'mb_encode_numericentity':
require_once MPHB()->getPluginPath( 'includes/polyfills/mbstring.php' );
break;
}
@@ -1234,16 +1235,16 @@
* @since 3.8
*/
function mphb_get_available_rooms( $from, $to, $atts = array() ) {
- $roomTypeId = isset( $atts['room_type_id'] ) ? $atts['room_type_id'] : 0;
- $searchAtts = array();
+ $roomTypeId = isset( $atts['room_type_id'] ) ? $atts['room_type_id'] : 0;
+ $searchAtts = array();
if ( isset( $atts['exclude_bookings'] ) ) {
- $searchAtts['exclude_bookings'] = $atts['exclude_bookings'];
+ $searchAtts['exclude_bookings'] = $atts['exclude_bookings'];
}
- $searchAtts['skip_buffer_rules'] = false;
+ $searchAtts['skip_buffer_rules'] = false;
- return MPHB()->getRoomRepository()->getAvailableRooms( $from, $to, $roomTypeId, $searchAtts );
+ return MPHB()->getRoomRepository()->getAvailableRooms( $from, $to, $roomTypeId, $searchAtts );
}
/**
@@ -1525,11 +1526,13 @@
*
* @param string $tip Help tip text.
* @param bool $allowHtml Allow sanitized HTML if true or escape.
+ * @param string $cssClass Custom CSS class.
+ *
* @return string
*
* @since 3.9.8
*/
-function mphb_help_tip( $tip, $allowHtml = false ) {
+function mphb_help_tip( $tip, $allowHtml = false, $cssClass = '' ) {
if ( $allowHtml ) {
$tip = htmlspecialchars(
wp_kses(
@@ -1551,7 +1554,7 @@
$tip = esc_attr( $tip );
}
- return '<span class="mphb-help-tip" data-tip="' . $tip . '"></span>';
+ return '<span class="mphb-help-tip ' . esc_attr( $cssClass ) . '" data-tip="' . $tip . '"></span>';
}
/**
--- a/motopress-hotel-booking-lite/includes/admin/edit-cpt-pages/booking-edit-cpt-page.php
+++ b/motopress-hotel-booking-lite/includes/admin/edit-cpt-pages/booking-edit-cpt-page.php
@@ -161,14 +161,7 @@
$booking = MPHB()->getBookingRepository()->findById( $post->ID );
foreach ( array_reverse( $booking->getLogs() ) as $log ) {
- ?>
- <strong> <?php esc_html_e( 'Date:', 'motopress-hotel-booking' ); ?></strong>
- <span>
- <?php comment_date( MPHB()->settings()->dateTime()->getDateTimeFormatWP( ' @ ' ), $log->comment_ID ); ?>
- </span>
- <br/>
- <strong><?php esc_html_e( 'Author:', 'motopress-hotel-booking' ); ?></strong>
- <?php
+
if ( ! empty( $log->user_id ) ) {
$userInfo = get_userdata( $log->user_id );
$userName = $userInfo ? $userInfo->display_name : ( $log->comment_author ?: 'DELETED' );
@@ -185,21 +178,24 @@
$authorName = '<i>' . __( 'Auto', 'motopress-hotel-booking' ) . '</i>';
}
?>
- <span>
- <?php
- // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- echo $authorName;
- ?>
- </span>
- <br/>
- <strong><?php esc_html_e( 'Message:', 'motopress-hotel-booking' ); ?></strong>
- <span>
- <?php
- // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- echo $log->comment_content;
- ?>
+ <div class="mphb-log-wrapper">
+ <div class="mphb-log-message">
+ <?php
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo $log->comment_content;
+ ?>
+ </div>
+ <span class="mphb-log-meta">
+ <?php
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ printf(
+ '%s / %s',
+ comment_date( MPHB()->settings()->dateTime()->getDateTimeFormatWP( ' @ ' ), $log->comment_ID ),
+ $authorName
+ );
+ ?>
</span>
- <hr/>
+ </div>
<?php
}
}
--- a/motopress-hotel-booking-lite/includes/admin/edit-cpt-pages/payment-edit-cpt-page.php
+++ b/motopress-hotel-booking-lite/includes/admin/edit-cpt-pages/payment-edit-cpt-page.php
@@ -91,24 +91,24 @@
public function renderLogMetaBox( $post, $metabox ) {
$payment = MPHB()->getPaymentRepository()->findById( $post->ID );
- echo '<textarea rows="3" name="_mphb_add_log" style="width:100%"></textarea><br/>';
-
foreach ( array_reverse( $payment->getLogs() ) as $log ) {
?>
- <hr/>
- <strong> <?php esc_html_e( 'Date:', 'motopress-hotel-booking' ); ?></strong>
- <span>
+ <div class="mphb-log-wrapper">
+ <div class="mphb-log-message">
+ <?php
+ // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
+ echo $log['message'];
+ ?>
+ </div>
+ <span class="mphb-log-meta">
<?php echo esc_html( mysql2date( MPHB()->settings()->dateTime()->getDateTimeFormatWP( ' @ ' ), $log['date'] ) ); ?>
- </span><br/>
- <strong><?php esc_html_e( 'Message:', 'motopress-hotel-booking' ); ?></strong>
- <span>
- <?php
- // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
- echo $log['message'];
- ?>
</span>
+ </div>
<?php
}
+
+ echo '<br/><textarea rows="2" name="_mphb_add_log" style="width:100%" placeholder="' .
+ esc_html( 'Add new', 'motopress-hotel-booking' ) . '"></textarea>';
}
public function saveMetaBoxes( $postId, $post, $update ) {
--- a/motopress-hotel-booking-lite/includes/admin/fields/price-breakdown-field.php
+++ b/motopress-hotel-booking-lite/includes/admin/fields/price-breakdown-field.php
@@ -2,6 +2,12 @@
namespace MPHBAdminFields;
+use MPHBViewsBookingView;
+
+if ( ! defined( 'ABSPATH' ) ) {
+ exit;
+}
+
class PriceBreakdownField extends InputField {
const TYPE = 'price-breakdown';
@@ -19,7 +25,10 @@
$priceBreakdown = json_decode( mphb_strip_price_breakdown_json( $this->value ), true );
if ( is_array( $priceBreakdown ) ) {
- $result .= MPHBViewsBookingView::generatePriceBreakdownArray( $priceBreakdown );
+ $result .= BookingView::generatePriceBreakdownArray(
+ $priceBreakdown,
+ array( 'coupon_removable' => false )
+ );
}
$result .= '</div>';
--- a/motopress-hotel-booking-lite/includes/admin/fields/total-price-field.php
+++ b/motopress-hotel-booking-lite/includes/admin/fields/total-price-field.php
@@ -11,11 +11,14 @@
protected $inputType = 'number';
public function renderInput() {
+
// [MB-684] Prevent excess number of digits
$this->value = round( $this->value, MPHB()->settings()->currency()->getPriceDecimalsCount() );
- $result = parent::renderInput();
- $result .= '<span class="description">' . MPHB()->settings()->currency()->getCurrencySymbol() . '</span>';
+
+ $result = sprintf( MPHB()->settings()->currency()->getPriceFormat(), parent::renderInput() );
$result .= ' <button type="button" id="mphb-recalculate-total-price" class="button button-secondary">' . __( 'Recalculate Total Price', 'motopress-hotel-booking' ) . '</button>';
+ $result .= ' ' . mphb_help_tip(
+ __( 'Recalculate the total price and price breakdown using current rates, fees, taxes, and coupons. No changes are saved until the booking is updated.', 'motopress-hotel-booking' ), false, 'mphb-help-tip__size-medium' );
$result .= '<span class="mphb-preloader mphb-hide"></span>';
$result .= '<div class="mphb-errors-wrapper mphb-hide"></div>';
return $result;
--- a/motopress-hotel-booking-lite/includes/admin/fields/variable-pricing-field.php
+++ b/motopress-hotel-booking-lite/includes/admin/fields/variable-pricing-field.php
@@ -106,7 +106,8 @@
$result .= '</tr>';
$result .= '<tr class="mphb-pricing-headers">';
- $result .= '<th colspan="2">' . esc_html__( 'Base Occupancy', 'motopress-hotel-booking' ) . '</th>';
+ $result .= '<th colspan="2">' . esc_html__( 'Base Occupancy', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
+ __( 'Number of guests included in the base nightly price.', 'motopress-hotel-booking' ) ) . '</th>';
$result .= '<th class="mphb-pricing-price-per-night" colspan="' . count( $periods ) . '">' . esc_html__( 'Price per night', 'motopress-hotel-booking' ) . '</th>';
$result .= '<th> </th>';
$result .= '</tr>';
--- a/motopress-hotel-booking-lite/includes/admin/manage-cpt-pages/coupon-manage-cpt-page.php
+++ b/motopress-hotel-booking-lite/includes/admin/manage-cpt-pages/coupon-manage-cpt-page.php
@@ -37,70 +37,80 @@
switch ( $column ) {
case 'amount':
+
// Accommodation discount
- esc_html_e( 'Accommodation:', 'motopress-hotel-booking' );
+ if ( $coupon->getRoomDiscountType() !== CouponCPT::TYPE_ACCOMMODATION_NONE ) {
- echo ' ';
+ esc_html_e( 'Accommodation:', 'motopress-hotel-booking' );
- switch ( $coupon->getRoomDiscountType() ) {
- case CouponCPT::TYPE_ACCOMMODATION_NONE:
- echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
- break;
-
- case CouponCPT::TYPE_ACCOMMODATION_PERCENTAGE:
- printf( '%d%%', $coupon->getRoomAmount() );
- break;
-
- case CouponCPT::TYPE_ACCOMMODATION_FIXED:
- echo mphb_format_price( $coupon->getRoomAmount() );
- break;
-
- case CouponCPT::TYPE_ACCOMMODATION_FIXED_PER_DAY:
- // translators: %s is a coupon amount per day
- printf( esc_html__( '%s per day', 'motopress-hotel-booking' ), mphb_format_price( $coupon->getRoomAmount() ) );
- break;
- }
+ echo ' ';
- echo '<br>';
+ switch ( $coupon->getRoomDiscountType() ) {
+ case CouponCPT::TYPE_ACCOMMODATION_NONE:
+ echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
+ break;
- // Service discount
- esc_html_e( 'Service:', 'motopress-hotel-booking' );
+ case CouponCPT::TYPE_ACCOMMODATION_PERCENTAGE:
+ printf( '%d%%', $coupon->getRoomAmount() );
+ break;
+
+ case CouponCPT::TYPE_ACCOMMODATION_FIXED:
+ echo mphb_format_price( $coupon->getRoomAmount() );
+ break;
- echo ' ';
+ case CouponCPT::TYPE_ACCOMMODATION_FIXED_PER_DAY:
+ // translators: %s is a coupon amount per day
+ printf( esc_html__( '%s per day', 'motopress-hotel-booking' ), mphb_format_price( $coupon->getRoomAmount() ) );
+ break;
+ }
- switch ( $coupon->getServiceDiscountType() ) {
- case CouponCPT::TYPE_SERVICE_NONE:
- echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
- break;
-
- case CouponCPT::TYPE_SERVICE_PERCENTAGE:
- printf( '%d%%', $coupon->getServiceAmount() );
- break;
-
- case CouponCPT::TYPE_SERVICE_FIXED:
- echo mphb_format_price( $coupon->getServiceAmount() );
- break;
+ echo '<br>';
}
- echo '<br>';
+ // Service discount
+ if ( $coupon->getServiceDiscountType() !== CouponCPT::TYPE_SERVICE_NONE ) {
+
+ esc_html_e( 'Service:', 'motopress-hotel-booking' );
+
+ echo ' ';
+
+ switch ( $coupon->getServiceDiscountType() ) {
+ case CouponCPT::TYPE_SERVICE_NONE:
+ echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
+ break;
+
+ case CouponCPT::TYPE_SERVICE_PERCENTAGE:
+ printf( '%d%%', $coupon->getServiceAmount() );
+ break;
+
+ case CouponCPT::TYPE_SERVICE_FIXED:
+ echo mphb_format_price( $coupon->getServiceAmount() );
+ break;
+ }
+
+ echo '<br>';
+ }
// Fee discount
- esc_html_e( 'Fee:', 'motopress-hotel-booking' );
+ if ( $coupon->getFeeDiscountType() !== CouponCPT::TYPE_FEE_NONE ) {
+
+ esc_html_e( 'Fee:', 'motopress-hotel-booking' );
+
+ echo ' ';
+
+ switch ( $coupon->getFeeDiscountType() ) {
+ case CouponCPT::TYPE_FEE_NONE:
+ echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
+ break;
- echo ' ';
+ case CouponCPT::TYPE_FEE_PERCENTAGE:
+ printf( '%d%%', $coupon->getFeeAmount() );
+ break;
- switch ( $coupon->getFeeDiscountType() ) {
- case CouponCPT::TYPE_FEE_NONE:
- echo esc_html( self::EMPTY_VALUE_PLACEHOLDER );
- break;
-
- case CouponCPT::TYPE_FEE_PERCENTAGE:
- printf( '%d%%', $coupon->getFeeAmount() );
- break;
-
- case CouponCPT::TYPE_FEE_FIXED:
- echo mphb_format_price( $coupon->getFeeAmount() );
- break;
+ case CouponCPT::TYPE_FEE_FIXED:
+ echo mphb_format_price( $coupon->getFeeAmount() );
+ break;
+ }
}
break;
@@ -108,7 +118,7 @@
case 'usage_count':
if ( $coupon->getUsageLimit() ) {
- printf( '%d/%d', $coupon->getUsageCount(), $coupon->getUsageLimit() );
+ printf( '%d / %d', $coupon->getUsageCount(), $coupon->getUsageLimit() );
} else {
--- a/motopress-hotel-booking-lite/includes/admin/menu-pages/settings-menu-page.php
+++ b/motopress-hotel-booking-lite/includes/admin/menu-pages/settings-menu-page.php
@@ -49,7 +49,7 @@
$generateTabs[] = self::TAB_ADVANCED;
}
- if ( MPHB()->settings()->license()->isEnabled() ) {
+ if ( MPHB()->settings()->license()->isVisible() ) {
$generateTabs[] = self::TAB_LICENSE;
}
--- a/motopress-hotel-booking-lite/includes/admin/menu-pages/taxes-and-fees-menu-page.php
+++ b/motopress-hotel-booking-lite/includes/admin/menu-pages/taxes-and-fees-menu-page.php
@@ -179,7 +179,7 @@
'limit',
array(
'type' => 'number',
- 'label' => __( 'Limit', 'motopress-hotel-booking' ) . mphb_help_tip(
+ 'label' => __( 'Limit', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
__( 'How often this fee is charged. Set 0 to charge each day of the stay period. Set 1 to charge once.', 'motopress-hotel-booking' )
),
'inner_label' => __( 'days', 'motopress-hotel-booking' ),
@@ -194,7 +194,7 @@
'included',
array(
'type' => 'single-checkbox',
- 'label' => __( 'Include', 'motopress-hotel-booking' ) . mphb_help_tip(
+ 'label' => __( 'Include', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
__( 'Show accommodation rate with this charge included', 'motopress-hotel-booking' )
),
'inner_label' => __( 'Include', 'motopress-hotel-booking' ),
@@ -275,7 +275,7 @@
'limit',
array(
'type' => 'number',
- 'label' => __( 'Limit', 'motopress-hotel-booking' ) . mphb_help_tip(
+ 'label' => __( 'Limit', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
__( 'Limit of days the fee is charged. Set 0 to charge each day of stay period. Set 1 to charge once.', 'motopress-hotel-booking' )
),
'inner_label' => __( 'days', 'motopress-hotel-booking' ),
@@ -290,7 +290,7 @@
'included',
array(
'type' => 'single-checkbox',
- 'label' => __( 'Include', 'motopress-hotel-booking' ) . mphb_help_tip(
+ 'label' => __( 'Include', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
__( 'Show accommodation rate with this charge included', 'motopress-hotel-booking' )
),
'inner_label' => __( 'Include', 'motopress-hotel-booking' ),
@@ -361,7 +361,7 @@
'limit',
array(
'type' => 'number',
- 'label' => __( 'Limit', 'motopress-hotel-booking' ) . mphb_help_tip(
+ 'label' => __( 'Limit', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
__( 'Limit of days the fee is charged. Set 0 to charge each day of stay period. Set 1 to charge once.', 'motopress-hotel-booking' )
),
'inner_label' => __( 'days', 'motopress-hotel-booking' ),
@@ -432,7 +432,7 @@
'limit',
array(
'type' => 'number',
- 'label' => __( 'Limit', 'motopress-hotel-booking' ) . mphb_help_tip(
+ 'label' => __( 'Limit', 'motopress-hotel-booking' ) . ' ' . mphb_help_tip(
__( 'Limit of days the fee is charged. Set 0 to charge each day of stay period. Set 1 to charge once.', 'motopress-hotel-booking' )
),
'inner_label' => __( 'days', 'motopress-hotel-booking' ),
--- a/motopress-hotel-booking-lite/includes/advanced/api/controllers/v1/get-blocks-controller.php
+++ b/motopress-hotel-booking-lite/includes/advanced/api/controllers/v1/get-blocks-controller.php
@@ -68,20 +68,19 @@
'sanitize_callback' => 'rest_sanitize_request_arg',
),
'per_page' => array(
- 'description' => 'Maximum number of items to be returned in result set.',
- 'oneOf' => array(
+ 'description' => 'Maximum number of items to be returned in result set.',
+ 'oneOf' => array(
array(
- 'type' => 'integer',
- 'minimum' => 1,
- 'sanitize_callback' => 'rest_sanitize_request_arg',
+ 'type' => 'integer',
+ 'minimum' => 1,
),
array(
- 'type' => 'integer',
- 'minimum' => -1,
- 'maximum' => -1,
- 'sanitize_callback' => 'rest_sanitize_request_arg',
+ 'type' => 'integer',
+ 'minimum' => -1,
+ 'maximum' => -1,
),
),
+ 'sanitize_callback' => 'rest_sanitize_request_arg',
),
'restrictions' => array(
'type' => 'array',
--- a/motopress-hotel-booking-lite/includes/advanced/api/controllers/v1/submit-checkout-controller.php
+++ b/motopress-hotel-booking-lite/includes/advanced/api/controllers/v1/submit-checkout-controller.php
@@ -89,11 +89,6 @@
'required' => true,
'sanitize_callback' => 'rest_sanitize_request_arg',
),
- 'note' => array(
- 'type' => 'string',
- 'default' => '',
- 'sanitize_callback' => 'rest_sanitize_request_arg',
- ),
'payment_details' => array(
'type' => 'object',
'properties' => array(
@@ -274,7 +269,7 @@
'check_out_date' => $checkOutDate,
'checkout_id' => $requestArgs['checkout_id'],
'customer' => $customer,
- 'note' => $requestArgs['note'],
+ 'note' => $customerData['note'] ?? '',
'reserved_rooms' => self::parseRooms( $requestArgs, $checkInDate, $checkOutDate ),
'status' => MPHB()->postTypes()->booking()->statuses()->getDefaultNewBookingStatus(),
);
@@ -309,7 +304,10 @@
$isDoingPayment = apply_filters( 'mphb_checkout_doing_payment', $isDoingPayment );
if ( $isDoingPayment ) {
- $paymentDetails = $requestArgs['payment_details'] + array(
+ // There is no "payment_details" in $requestArgs if the booking is free
+ $paymentDetails = $requestArgs['payment_details'] ?? array();
+
+ $paymentDetails += array(
'currency' => MPHB()->settings()->currency()->getCurrencyCode(),
'gateway_id' => 'manual',
'payment_fields' => array(),
--- a/motopress-hotel-booking-lite/includes/ajax-api/ajax-actions/update-booking-notes.php
+++ b/motopress-hotel-booking-lite/includes/ajax-api/ajax-actions/update-booking-notes.php
@@ -2,6 +2,7 @@
namespace MPHBAjaxApi;
+use MPHBUsersAndRolesCapabilitiesAndRoles;
use MPHBUtilsValidateUtils;
if ( ! defined( 'ABSPATH' ) ) {
@@ -16,6 +17,10 @@
const REQUEST_DATA_BOOKING_ID = 'booking_id';
const REQUEST_DATA_NOTES = 'notes';
+ public static function isActionForGuestUser() {
+ return false;
+ }
+
public static function getAjaxActionNameWithouPrefix() {
return 'update_booking_notes';
}
@@ -27,7 +32,14 @@
protected static function getValidatedRequestData() {
$requestData = parent::getValidatedRequestData();
- $requestData[ static::REQUEST_DATA_BOOKING_ID ] = static::getIntegerFromRequest( static::REQUEST_DATA_BOOKING_ID, true );
+ $bookingId = static::getIntegerFromRequest( static::REQUEST_DATA_BOOKING_ID, $isRequired = true );
+ $booking = mphb_bookings_facade()->findBookingById( $bookingId );
+
+ if ( is_null( $booking ) ) {
+ throw new Exception( esc_html__( 'The booking not found.', 'motopress-hotel-booking' ) );
+ }
+
+ $requestData[ static::REQUEST_DATA_BOOKING_ID ] = $bookingId;
// $isRequired = false to save [] when the list is empty, since jQuery.ajax() skips empty objects
$requestData[ static::REQUEST_DATA_NOTES ] = static::getNotesFromRequest( static::REQUEST_DATA_NOTES );
@@ -77,6 +89,10 @@
}
protected static function doAction( array $requestData ) {
+ if ( ! current_user_can( CapabilitiesAndRoles::EDIT_BOOKINGS ) ) {
+ throw new Exception( esc_html__( 'Request does not pass security verification. Please refresh the page and try one more time.', 'motopress-hotel-booking' ) );
+ }
+
$bookingId = $requestData[ static::REQUEST_DATA_BOOKING_ID ];
$notes = $requestData[ static::REQUEST_DATA_NOTES ];
--- a/motopress-hotel-booking-lite/includes/ajax.php
+++ b/motopress-hotel-booking-lite/includes/ajax.php
@@ -290,7 +290,10 @@
// [MB-684] Prevent excess number of digits
'total' => round( $booking->calcPrice(), MPHB()->settings()->currency()->getPriceDecimalsCount() ),
'price_breakdown' => json_encode( $priceBreakdown ),
- 'price_breakdown_html' => BookingView::generatePriceBreakdownArray( $priceBreakdown ),
+ 'price_breakdown_html' => BookingView::generatePriceBreakdownArray(
+ $priceBreakdown,
+ array( 'coupon_removable' => false )
+ ),
)
);
}
--- a/motopress-hotel-booking-lite/includes/core/data/booking-rules-data.php
+++ b/motopress-hotel-booking-lite/includes/core/data/booking-rules-data.php
@@ -104,7 +104,7 @@
private $cachedRulesByDates = array();
/**
- * See <code>loadBlocksForMonth()</code>.
+ * See <code>getBlocksForMonth()</code>.
*
* @var array <code>[ Block ID => [ room_type_id, room_id, ... ] ]</code>
*/
@@ -112,11 +112,13 @@
/**
* New items are added with each new month's load. See
- * <code>loadBlocksForMonth()</code>.
+ * <code>getBlocksByDate()</code>.
*
* @var array <code>[
- * Date string ("Y-m-d") => [
- * Block ID => [ room_type_id, room_id, ... ]
+ * Room type ID (int) => [
+ * Date string ("Y-m-d") => [
+ * Block ID => [ room_type_id, room_id, ... ]
+ * ]
* ]
* ]</code>
*/
@@ -478,17 +480,16 @@
}
$allRoomsCount = 0;
+
if ( 0 === $roomTypeOriginalId ) {
$allRoomsCount = MPHB()->getRoomPersistence()->getCount();
- }
-
- if ( 0 < $roomTypeOriginalId ) {
+ } elseif ( 0 < $roomTypeOriginalId ) {
$allRoomsCount = count( MPHB()->getRoomPersistence()->findAllIdsByType( $roomTypeOriginalId ) );
}
- $result['not_check_in'] = $result['not_check_in'] ?? $allRoomsCount <= $notCheckInRoomsCount;
+ $result['not_check_in'] = $result['not_check_in'] ?? $allRoomsCount <= $notCheckInRoomsCount;
$result['not_check_out'] = $result['not_check_out'] ?? $allRoomsCount <= $notCheckOutRoomsCount;
- $result['not_stay_in'] = $result['not_stay_in'] ?? $allRoomsCount <= $notStayInRoomsCount;
+ $result['not_stay_in'] = $result['not_stay_in'] ?? $allRoomsCount <= $notStayInRoomsCount;
$result = array_merge(
array(
@@ -1163,27 +1164,70 @@
}
/**
+ * @param int $roomTypeId Room type ID or 0 (all room types).
* @return array <code>[ Block ID => [ room_type_id, room_id, ... ] ]</code>
*/
private function getBlocksByDate( int $roomTypeId, DateTime $date ): array {
$dateStr = DateUtils::formatDateDB( $date );
if ( ! isset( $this->blocksByDate[ $roomTypeId ][ $dateStr ] ) ) {
- $this->loadBlocksForMonth( $roomTypeId, $date );
+ $blocksByDate = $this->getBlocksForMonth( $roomTypeId, $date );
+
+ // Treat trashed rooms as deleted, especially since "trash" is not
+ // included in the "all" list in RoomPersistence
+
+ // Filter trashed rooms
+ $trashedRooms = mphb_rooms_facade()->getTrashedRoomIds( $roomTypeId );
+
+ if ( ! empty( $trashedRooms ) ) {
+ foreach ( array_keys( $blocksByDate ) as $dateStr ) {
+ $blocksByDate[ $dateStr ] = array_filter(
+ $blocksByDate[ $dateStr ],
+ fn( $block ) => ! in_array( $block['room_id'], $trashedRooms )
+ );
+ }
+ }
+
+ // Filter trashed room types
+ if ( $roomTypeId === 0 ) {
+ $trashedRoomTypes = mphb_rooms_facade()->getTrashedRoomTypeIds();
+
+ if ( ! empty( $trashedRoomTypes ) ) {
+ foreach ( array_keys( $blocksByDate ) as $dateStr ) {
+ $blocksByDate[ $dateStr ] = array_filter(
+ $blocksByDate[ $dateStr ],
+ fn( $block ) => ! in_array( $block['room_type_id'], $trashedRoomTypes )
+ );
+ }
+ }
+ }
+
+ if ( ! isset( $this->blocksByDate[ $roomTypeId ] ) ) {
+ $this->blocksByDate[ $roomTypeId ] = array();
+ }
+
+ $this->blocksByDate[ $roomTypeId ] += $blocksByDate;
}
return $this->blocksByDate[ $roomTypeId ][ $dateStr ];
}
- private function loadBlocksForMonth( int $roomTypeId, DateTime $dateOfMonth ): void {
- $monthStartStr = date( 'Y-m-01', $dateOfMonth->getTimestamp() );
- $monthEndStr = date( 'Y-m-t', $dateOfMonth->getTimestamp() );
+ /**
+ * @return array <code>[
+ * Date string ("Y-m-d") => [
+ * Block ID => [ room_type_id, room_id, ... ]
+ * ]
+ * ]
+ */
+ private function getBlocksForMonth( int $roomTypeId, DateTime $dateOfMonth ): array {
+ $monthStartStr = $dateOfMonth->format( 'Y-m-01' );
+ $monthEndStr = $dateOfMonth->format( 'Y-m-t' );
$monthStart = DateUtils::createDate( $monthStartStr );
$monthEnd = DateUtils::createDate( $monthEndStr );
if ( $monthStart === null || $monthEnd === null ) {
- return;
+ return array();
}
$blocksForMonth = MPHB()->getBlocksRepository()->getItemsForPeriod( $roomTypeId, $monthStartStr, $monthEndStr );
@@ -1208,10 +1252,6 @@
}
}
- if ( ! isset( $this->blocksByDate[ $roomTypeId ] ) ) {
- $this->blocksByDate[ $roomTypeId ] = [];
- }
-
- $this->blocksByDate[ $roomTypeId ] += $blocksByDate;
+ return $blocksByDate;
}
}
--- a/motopress-hotel-booking-lite/includes/core/rooms-core-api-facade.php
+++ b/motopress-hotel-booking-lite/includes/core/rooms-core-api-facade.php
@@ -162,4 +162,41 @@
public function updateRoomType( RoomType $roomType ): void {
MPHB()->getRoomTypeRepository()->save( $roomType );
}
+
+ /**
+ * @return int[]
+ */
+ public function getTrashedRoomIds( int $roomTypeId = 0 ): array {
+ if ( ! $roomTypeId ) {
+ return MPHB()->getRoomPersistence()->getPosts(
+ array(
+ 'fields' => 'ids',
+ 'post_status' => 'trash',
+ 'posts_per_page' => -1,
+ )
+ );
+ } else {
+ return MPHB()->getRoomPersistence()->getPosts(
+ array(
+ 'fields' => 'ids',
+ 'post_status' => 'trash',
+ 'posts_per_page' => -1,
+ 'room_type_id' => $roomTypeId,
+ )
+ );
+ }
+ }
+
+ /**
+ * @return int[]
+ */
+ public function getTrashedRoomTypeIds(): array {
+ return MPHB()->getRoomTypePersistence()->getPosts(
+ array(
+ 'fields' => 'ids',
+ 'post_status' => 'trash',
+ 'posts_per_page' => -1,
+ )
+ );
+ }
}
--- a/motopress-hotel-booking-lite/includes/emails/abstract-email.php
+++ b/motopress-hotel-booking-lite/includes/emails/abstract-email.php
@@ -286,10 +286,10 @@
// apply CSS styles inline for picky email clients
$emogrifier = new EmogrifierEmogrifier( $html, $styles );
- // Load polyfill for function mb_convert_encoding() if it not exists.
- // Emogrifier is bad in converting non-ASCII characters. See MB-1023
- if ( ! function_exists( 'mb_convert_encoding' ) ) {
- mphb_get_polyfill_for( 'mb_convert_encoding' );
+ // Load polyfill for function mb_encode_numericentity() if it not exists.
+ // Emogrifier is bad in converting non-ASCII characters. See MPI-5089
+ if ( ! function_exists( 'mb_encode_numericentity' ) ) {
+ mphb_get_polyfill_for( 'mb_encode_numericentity' );
}
$html = $emogrifier->emogrify();
--- a/motopress-hotel-booking-lite/includes/libraries/emogrifier/emogrifier.php
+++ b/motopress-hotel-booking-lite/includes/libraries/emogrifier/emogrifier.php
@@ -558,8 +558,16 @@
$bodyWithoutUnprocessableTags = $this->html;
}
- if ( function_exists( 'mb_convert_encoding' ) ) {
- return mb_convert_encoding( $bodyWithoutUnprocessableTags, 'HTML-ENTITIES', self::ENCODING );
+ // Fork notice: mb_convert_encoding() replaced with mb_encode_numericentity() for MPI-13456
+ if ( function_exists( 'mb_encode_numericentity' ) ) {
+ return mb_encode_numericentity(
+ htmlspecialchars_decode(
+ htmlentities( $bodyWithoutUnprocessableTags, ENT_NOQUOTES, self::ENCODING, false ),
+ ENT_NOQUOTES
+ ),
+ array( 0x80, 0x10FFFF, 0, ~0 ),
+ self::ENCODING
+ );
} else {
return htmlspecialchars_decode( utf8_decode( htmlentities( $bodyWithoutUnprocessableTags, ENT_COMPAT, self::ENCODING, false ) ) );
}
--- a/motopress-hotel-booking-lite/includes/polyfills/mbstring.php
+++ b/motopress-hotel-booking-lite/includes/polyfills/mbstring.php
@@ -121,6 +121,116 @@
}
}
+if ( ! function_exists( 'mb_encode_numericentity' ) ) {
+ /**
+ * @link https://github.com/symfony/polyfill-mbstring
+ *
+ * @since 6.x.x
+ */
+ function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false)
+ {
+ if ( $s !== null && ! is_scalar( $s ) && ! ( is_object( $s ) && method_exists( $s, '__toString' ) ) ) {
+ trigger_error( 'mb_encode_numericentity() expects parameter 1 to be string, ' . gettype( $s ) . ' given', E_USER_WARNING );
+
+ return null;
+ }
+
+ if ( ! is_array( $convmap ) || ( PHP_VERSION_ID < 80000 && ! $convmap ) ) {
+ return false;
+ }
+
+ if ( $encoding !== null && ! is_scalar( $encoding ) ) {
+ trigger_error( 'mb_encode_numericentity() expects parameter 3 to be string, ' . gettype( $s ) . ' given', E_USER_WARNING );
+
+ return null; // Instead of '' (cf. mb_decode_numericentity)
+ }
+
+ if ( $is_hex !== null && ! is_scalar( $is_hex ) ) {
+ trigger_error( 'mb_encode_numericentity() expects parameter 4 to be boolean, ' . gettype( $s ) . ' given', E_USER_WARNING );
+
+ return null;
+ }
+
+ $s = (string) $s;
+
+ if ( $s === '' ) {
+ return '';
+ }
+
+ $encoding = mb_validate_encoding( $encoding );
+
+ if ( $encoding === 'UTF-8' ) {
+ $encoding = null;
+ if ( ! preg_match( '//u', $s ) ) {
+ $s = @iconv( 'UTF-8', 'UTF-8//IGNORE', $s );
+ }
+ } else {
+ $s = iconv( $encoding, 'UTF-8//IGNORE', $s );
+ }
+
+ static $ulenMask = array( "xC0" => 2, "xD0" => 2, "xE0" => 3, "xF0" => 4 );
+
+ $cnt = floor( count( $convmap ) / 4 ) * 4;
+ $i = 0;
+ $len = strlen( $s );
+ $result = '';
+
+ while ( $i < $len ) {
+ $ulen = $s[ $i ] < "x80" ? 1 : $ulenMask[ $s[ $i ] & "xF0" ];
+ $uchr = substr( $s, $i, $ulen );
+ $i += $ulen;
+ $c = mb_ord( $uchr );
+
+ for ( $j = 0; $j < $cnt; $j += 4 ) {
+ if ( $c >= $convmap[ $j ] && $c <= $convmap[ $j + 1 ] ) {
+ $cOffset = ( $c + $convmap[ $j + 2 ] ) & $convmap[ $j + 3 ];
+ $result .= $is_hex ? sprintf( '&#x%X;', $cOffset ) : '&#' . $cOffset . ';';
+ continue 2;
+ }
+ }
+
+ $result .= $uchr;
+ }
+
+ if ( $encoding === null ) {
+ return $result;
+ }
+
+ return iconv( 'UTF-8', $encoding . '//IGNORE', $result );
+ }
+}
+
+if ( ! function_exists( 'mb_ord' ) ) {
+ /**
+ * @link https://github.com/symfony/polyfill-mbstring
+ *
+ * @since 6.x.x
+ */
+ function mb_ord( $s, $encoding = null ) {
+ $encoding = mb_validate_encoding( $encoding );
+
+ if ( $encoding !== 'UTF-8' ) {
+ $s = mb_convert_encoding( $s, 'UTF-8', $encoding );
+ }
+
+ if ( strlen( $s ) === 1 ) {
+ return ord( $s );
+ }
+
+ $code = ( $s = unpack( 'C*', substr( $s, 0, 4 ) ) ) ? $s[1] : 0;
+
+ if ( $code >= 0xF0 ) {
+ return ( ( $code - 0xF0 ) << 18 ) + ( ( $s[2] - 0x80 ) << 12 ) + ( ( $s[3] - 0x80 ) << 6 ) + $s[4] - 0x80;
+ } elseif ( $code >= 0xE0 ) {
+ return ( ( $code - 0xE0 ) << 12 ) + ( ( $s[2] - 0x80 ) << 6 ) + $s[3] - 0x80;
+ } elseif ( $code >= 0xC0 ) {
+ return ( ( $code - 0xC0 ) << 6 ) + $s[2] - 0x80;
+ }
+
+ return $code;
+ }
+}
+
if ( ! function_exists( 'mb_validate_encoding' ) ) {
/**
* @link https://github.com/symfony/polyfill-mbstring
--- a/motopress-hotel-booking-lite/includes/post-types/booking-cpt.php
+++ b/motopress-hotel-booking-lite/includes/post-types/booking-cpt.php
@@ -160,7 +160,7 @@
'mphb_note',
array(
'type' => 'textarea',
- 'rows' => 8,
+ 'rows' => 3,
'label' => __( 'Customer Note', 'motopress-hotel-booking' ),
)
),
--- a/motopress-hotel-booking-lite/includes/post-types/service-cpt.php
+++ b/motopress-hotel-booking-lite/includes/post-types/service-cpt.php
@@ -214,6 +214,16 @@
$priceGroup->addField( $priceQuantityField );
+ $priceGroup->addField(
+ FieldsFieldFactory::create(
+ 'mphb_help_notice',
+ array(
+ 'type' => 'placeholder',
+ 'description' => '<i>' . __('Note: this service will appear at checkout only if it is selected in the accommodation type settings.', 'motopress-hotel-booking') . '</i>',
+ )
+ )
+ );
+
return array( $priceGroup );
}
--- a/motopress-hotel-booking-lite/includes/repositories/blocks-repository.php
+++ b/motopress-hotel-booking-lite/includes/repositories/blocks-repository.php
@@ -463,7 +463,7 @@
$queryArgs += array(
'orderby' => array( 'block_id' => 'ASC' ),
'page' => 1,
- 'per_page' => self::ITEMS_PER_PAGE,
+ 'per_page' => $this->getDefaultItemsPerPage(),
);
// countItems() changes this part to "SELECT COUNT(*)"
@@ -521,7 +521,7 @@
}
// Build LIMIT SQL
- if ( $queryArgs['per_page'] !== -1 ) {
+ if ( $queryArgs['per_page'] > 0 ) {
$itemsOffset = ( $queryArgs['page'] - 1 ) * $queryArgs['per_page'];
$preparedSql .= $wpdb->prepare( " LIMIT %d, %d", $itemsOffset, $queryArgs['per_page'] );
--- a/motopress-hotel-booking-lite/includes/settings/license-settings.php
+++ b/motopress-hotel-booking-lite/includes/settings/license-settings.php
@@ -136,13 +136,17 @@
}
/**
- *
* @return bool
*/
public function isEnabled() {
return false;
}
+ public function isVisible(): bool {
+ return false;
+
+ }
+
/**
*
* @return stdClass|null
--- a/motopress-hotel-booking-lite/includes/users-and-roles/capabilities-and-roles.php
+++ b/motopress-hotel-booking-lite/includes/users-and-roles/capabilities-and-roles.php
@@ -19,6 +19,7 @@
const VIEW_CUSTOMERS = 'mphb_view_customers';
const EDIT_CUSTOMER = 'mphb_edit_customer';
const DELETE_CUSTOMER = 'mphb_delete_customer';
+ const EDIT_BOOKINGS = 'edit_mphb_bookings';
/**
* @var array
--- a/motopress-hotel-booking-lite/includes/users-and-roles/customers.php
+++ b/motopress-hotel-booking-lite/includes/users-and-roles/customers.php
@@ -284,8 +284,6 @@
* @return WP_Error|true
*/
public static function validateCustomer( $customer ) {
- global $wpdb;
-
if ( ! $customer->getEmail() ) {
return new WP_Error( 'empty_email', __( 'Please, provide a valid email.', 'motopress-hotel-booking' ) );
}
--- a/motopress-hotel-booking-lite/motopress-hotel-booking.php
+++ b/motopress-hotel-booking-lite/motopress-hotel-booking.php
@@ -4,7 +4,7 @@
* Plugin Name: Hotel Booking Lite
* Plugin URI: https://motopress.com/products/hotel-booking/
* Description: Manage your hotel booking services. Perfect for hotels, villas, guest houses, hostels, and apartments of all sizes.
- * Version: 6.0.1
+ * Version: 6.0.2
* Requires at least: 5.2
* Requires PHP: 7.4
* Author: MotoPress
--- a/motopress-hotel-booking-lite/plugin.php
+++ b/motopress-hotel-booking-lite/plugin.php
@@ -1732,7 +1732,6 @@
public function initAutoUpdater() {
if ( $this->settings->license()->isEnabled() ) {
-
$pluginData = $this->getPluginData();
$apiData = array(
@@ -1743,6 +1742,9 @@
);
new MPHBLibrariesEDD_Plugin_UpdaterEDD_Plugin_Updater( MPHB()->settings()->license()->getStoreUrl(), self::$_pluginFile, $apiData );
+ }
+
+ if ( $this->settings->license()->isVisible() ) {
new MPHBLicenseNotice( MPHB_PLUGIN_FILE );
}
}