| @@ -1,4 +1,4 @@ | ||
| 1 | 1 | <?php /** |
| 2 | 2 | * @version 1.1 |
| 3 | 3 | * @package Booking Calendar |
| 4 | 4 | * @category Timeline for Admin Panel |
| @@ -33,13 +33,21 @@ | ||
| 33 | 33 | |
| 34 | 34 | private $html_client_id; // ID of border element at client side. |
| 35 | 35 | public $options; // FixIn: 7.0.1.50. |
| 36 | 36 | |
| 37 | - private $data_in_previous_cell; // FixIn: 8.5.2.6. | |
| 37 | + private $data_in_previous_cell; // FixIn: 8.5.2.6. | |
| 38 | + | |
| 39 | + /** | |
| 40 | + * Exact booking scope resolved from a public booking hash. | |
| 41 | + * | |
| 42 | + * @var array|false | |
| 43 | + */ | |
| 44 | + private $booking_hash_scope; | |
| 38 | 45 | |
| 39 | - public function __construct(){// $bookings, $booking_types ) { | |
| 46 | + public function __construct(){// $bookings, $booking_types ) { | |
| 40 | 47 | |
| 41 | - $this->reset_data_in_previous_cell(); | |
| 48 | + $this->reset_data_in_previous_cell(); | |
| 49 | + $this->booking_hash_scope = false; | |
| 42 | 50 | |
| 43 | 51 | $this->options = array(); // FixIn: 7.0.1.50. |
| 44 | 52 | |
| 45 | 53 | $this->html_client_id = false; |
| @@ -114,13 +122,191 @@ | ||
| 114 | 122 | ) |
| 115 | 123 | ); |
| 116 | 124 | |
| 117 | 125 | |
| 118 | - } | |
| 119 | - | |
| 120 | - | |
| 121 | - /** | |
| 122 | - * Rezet data in previos cell | |
| 126 | + } | |
| 127 | + | |
| 128 | + /** | |
| 129 | + * Validate the server-generated DOM identifier used by Timeline navigation. | |
| 130 | + * | |
| 131 | + * Public AJAX requests may return this value in JavaScript and inline event | |
| 132 | + * attributes. Accepting only the established prefix and decimal suffix keeps | |
| 133 | + * it an identifier rather than caller-controlled markup or selector syntax. | |
| 134 | + * | |
| 135 | + * @param mixed $html_client_id Candidate Timeline DOM identifier. | |
| 136 | + * @return string Valid identifier, or an empty string. | |
| 137 | + */ | |
| 138 | + public static function normalize_html_client_id( $html_client_id ) { | |
| 139 | + if ( ! is_scalar( $html_client_id ) ) { | |
| 140 | + return ''; | |
| 141 | + } | |
| 142 | + | |
| 143 | + $html_client_id = (string) $html_client_id; | |
| 144 | + if ( 64 < strlen( $html_client_id ) ) { | |
| 145 | + return ''; | |
| 146 | + } | |
| 147 | + | |
| 148 | + return preg_match( '/\Awpbc_timeline_[0-9]+\z/D', $html_client_id ) | |
| 149 | + ? $html_client_id | |
| 150 | + : ''; | |
| 151 | + } | |
| 152 | + | |
| 153 | + /** | |
| 154 | + * Normalize the public timeline options contract. | |
| 155 | + * | |
| 156 | + * Timeline navigation round-trips options through the browser. Only Resource | |
| 157 | + * links are consumed by the renderer, so every other key is discarded rather | |
| 158 | + * than retained as attacker-controlled state for a later response. | |
| 159 | + * | |
| 160 | + * @param mixed $options Candidate timeline options. | |
| 161 | + * @return array<string,array<int,string>> Valid Resource links keyed by Resource ID. | |
| 162 | + */ | |
| 163 | + public static function normalize_options( $options ) { | |
| 164 | + if ( | |
| 165 | + ! is_array( $options ) | |
| 166 | + || empty( $options['resource_link'] ) | |
| 167 | + || ! is_array( $options['resource_link'] ) | |
| 168 | + ) { | |
| 169 | + return array(); | |
| 170 | + } | |
| 171 | + | |
| 172 | + $resource_links = array(); | |
| 173 | + foreach ( $options['resource_link'] as $resource_key => $resource_url ) { | |
| 174 | + if ( ! is_scalar( $resource_key ) || ! is_scalar( $resource_url ) ) { | |
| 175 | + continue; | |
| 176 | + } | |
| 177 | + | |
| 178 | + $resource_id = absint( $resource_key ); | |
| 179 | + $resource_url = esc_url_raw( (string) $resource_url ); | |
| 180 | + if ( empty( $resource_id ) || '' === $resource_url ) { | |
| 181 | + continue; | |
| 182 | + } | |
| 183 | + | |
| 184 | + $resource_links[ $resource_id ] = $resource_url; | |
| 185 | + } | |
| 186 | + | |
| 187 | + return empty( $resource_links ) | |
| 188 | + ? array() | |
| 189 | + : array( 'resource_link' => $resource_links ); | |
| 190 | + } | |
| 191 | + | |
| 192 | + /** | |
| 193 | + * Decode and normalize browser-submitted timeline options. | |
| 194 | + * | |
| 195 | + * @param mixed $encoded_options JSON text received from the timeline client. | |
| 196 | + * @return array<string,array<int,string>> Valid Resource links, or an empty array. | |
| 197 | + */ | |
| 198 | + public static function decode_options( $encoded_options ) { | |
| 199 | + if ( ! is_scalar( $encoded_options ) ) { | |
| 200 | + return array(); | |
| 201 | + } | |
| 202 | + | |
| 203 | + $decoded_options = json_decode( (string) $encoded_options, true, 32 ); | |
| 204 | + if ( JSON_ERROR_NONE !== json_last_error() ) { | |
| 205 | + return array(); | |
| 206 | + } | |
| 207 | + | |
| 208 | + return self::normalize_options( $decoded_options ); | |
| 209 | + } | |
| 210 | + | |
| 211 | + /** | |
| 212 | + * Encode timeline options as one complete JavaScript string literal. | |
| 213 | + * | |
| 214 | + * The timeline browser contract stores JSON text, rather than an object, in | |
| 215 | + * `timeline_obj.options`. Encoding the normalized options twice preserves that | |
| 216 | + * contract while the hexadecimal flags prevent quotes or HTML delimiters in a | |
| 217 | + * URL from terminating the inline script context. | |
| 218 | + * | |
| 219 | + * @param mixed $options Candidate timeline options. | |
| 220 | + * @return string JavaScript-safe JSON string literal, including its delimiters. | |
| 221 | + */ | |
| 222 | + public static function encode_options_for_inline_script( $options ) { | |
| 223 | + $options_json = wp_json_encode( self::normalize_options( $options ) ); | |
| 224 | + if ( false === $options_json ) { | |
| 225 | + $options_json = '{}'; | |
| 226 | + } | |
| 227 | + | |
| 228 | + $javascript_literal = wp_json_encode( | |
| 229 | + $options_json, | |
| 230 | + JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | |
| 231 | + ); | |
| 232 | + | |
| 233 | + return false === $javascript_literal ? '"{}"' : $javascript_literal; | |
| 234 | + } | |
| 235 | + | |
| 236 | + /** | |
| 237 | + * Apply an exact booking and resource authorization scope to timeline SQL arguments. | |
| 238 | + * | |
| 239 | + * A booking hash is a bearer credential for one booking. It must never be | |
| 240 | + * converted into a customer-data keyword or used to broaden the query. Invalid | |
| 241 | + * hashes deliberately select an impossible booking ID so processing fails closed. | |
| 242 | + * | |
| 243 | + * @param array $query_args Clean timeline query arguments. | |
| 244 | + * @param string $booking_hash Public booking hash supplied by the timeline request. | |
| 245 | + * @return array Query arguments restricted to the authorized booking, when applicable. | |
| 246 | + */ | |
| 247 | + private function wpbc_apply_booking_hash_scope_to_query_args( $query_args, $booking_hash ) { | |
| 248 | + $this->booking_hash_scope = false; | |
| 249 | + $booking_hash = sanitize_text_field( (string) $booking_hash ); | |
| 250 | + | |
| 251 | + if ( empty( $booking_hash ) ) { | |
| 252 | + return $query_args; | |
| 253 | + } | |
| 254 | + | |
| 255 | + $this->request_args['only_booked_resources'] = 1; | |
| 256 | + $booking_scope = wpbc_hash__get_booking_id__resource_id( $booking_hash ); | |
| 257 | + | |
| 258 | + if ( empty( $booking_scope ) || count( $booking_scope ) < 2 ) { | |
| 259 | + $query_args['wh_booking_id'] = '-1'; | |
| 260 | + return $query_args; | |
| 261 | + } | |
| 262 | + | |
| 263 | + $booking_id = absint( $booking_scope[0] ); | |
| 264 | + $resource_id = absint( $booking_scope[1] ); | |
| 265 | + $booking_row = wpbc_db_get_booking_details( $booking_id ); | |
| 266 | + | |
| 267 | + if ( empty( $booking_id ) || empty( $resource_id ) || empty( $booking_row ) || $resource_id !== absint( $booking_row->booking_type ) ) { | |
| 268 | + $query_args['wh_booking_id'] = '-1'; | |
| 269 | + return $query_args; | |
| 270 | + } | |
| 271 | + | |
| 272 | + $this->booking_hash_scope = array( | |
| 273 | + 'booking_id' => $booking_id, | |
| 274 | + 'resource_id' => $resource_id, | |
| 275 | + ); | |
| 276 | + $query_args['wh_booking_id'] = (string) $booking_id; | |
| 277 | + $query_args['wh_booking_type'] = (string) $resource_id; | |
| 278 | + | |
| 279 | + return $query_args; | |
| 280 | + } | |
| 281 | + | |
| 282 | + /** | |
| 283 | + * Verify that a returned booking remains inside the resolved hash scope. | |
| 284 | + * | |
| 285 | + * This rendering-layer check is intentionally independent of the SQL restriction | |
| 286 | + * so a future query regression cannot expose another booking's popover data. | |
| 287 | + * Timelines without a booking hash retain their configured public presentation. | |
| 288 | + * | |
| 289 | + * @param int $booking_id Booking ID about to be rendered. | |
| 290 | + * @param array $bookings Booking objects keyed by booking ID. | |
| 291 | + * @return bool True when popover rendering is authorized for this row. | |
| 292 | + */ | |
| 293 | + private function wpbc_is_booking_authorized_for_hash( $booking_id, $bookings ) { | |
| 294 | + if ( empty( $this->request_args['booking_hash'] ) ) { | |
| 295 | + return true; | |
| 296 | + } | |
| 297 | + | |
| 298 | + $booking_id = absint( $booking_id ); | |
| 299 | + if ( empty( $this->booking_hash_scope ) || $booking_id !== $this->booking_hash_scope['booking_id'] || empty( $bookings[ $booking_id ] ) ) { | |
| 300 | + return false; | |
| 301 | + } | |
| 302 | + | |
| 303 | + return $this->booking_hash_scope['resource_id'] === absint( $bookings[ $booking_id ]->booking_type ); | |
| 304 | + } | |
| 305 | + | |
| 306 | + | |
| 307 | + /** | |
| 308 | + * Rezet data in previos cell | |
| 123 | 309 | */ |
| 124 | 310 | private function reset_data_in_previous_cell(){ |
| 125 | 311 | |
| 126 | 312 | $this->data_in_previous_cell = array( |
| @@ -147,17 +333,17 @@ | ||
| 147 | 333 | |
| 148 | 334 | $this->is_frontend = true; |
| 149 | 335 | |
| 150 | 336 | // FixIn: 7.0.1.50. |
| 151 | - if ( isset( $attr['options'] ) ) { | |
| 337 | + if ( isset( $attr['options'] ) ) { | |
| 152 | 338 | |
| 153 | - $bk_otions = $attr['options']; | |
| 154 | - $bk_otions = html_entity_decode( $bk_otions ); // FixIn: 9.8.15.6. | |
| 339 | + $shortcode_param__options = $attr['options']; | |
| 340 | + $shortcode_param__options = html_entity_decode( $shortcode_param__options ); // FixIn: 9.8.15.6. | |
| 155 | 341 | $custom_params = array(); |
| 156 | - if (! empty($bk_otions)) { | |
| 342 | + if (! empty($shortcode_param__options)) { | |
| 157 | 343 | $param ='\s*([^\s]+)=[\'"]{1}([^\'"]+)[\'"]{1}\s*'; // Find all possible options |
| 158 | 344 | $pattern_to_search='%\s*{([^\s]+)' . $param .'}\s*[,]?\s*%'; |
| 159 | - preg_match_all($pattern_to_search, $bk_otions, $matches, PREG_SET_ORDER); | |
| 345 | + preg_match_all($pattern_to_search, $shortcode_param__options, $matches, PREG_SET_ORDER); | |
| 160 | 346 | //debuge($matches); |
| 161 | 347 | /** |
| 162 | 348 | * [bookingtimeline ... options='{resource_link 3="http://beta/resource-apartment3-id3/"},{resource_link 4="http://beta/resource-3-id4/"}' ... ] |
| 163 | 349 | [0] => {resource_link 3="http://beta/resource-apartment3-id3/"}, |
| @@ -173,11 +359,12 @@ | ||
| 173 | 359 | $this->options[ $matche_value[1] ][ $matche_value[2] ] = $matche_value[3]; |
| 174 | 360 | } |
| 175 | 361 | } |
| 176 | 362 | |
| 177 | -//debuge($this->options); | |
| 178 | - } | |
| 179 | - // FixIn: 7.0.1.50. | |
| 363 | +//debuge($this->options); | |
| 364 | + } | |
| 365 | + $this->options = self::normalize_options( $this->options ); | |
| 366 | + // FixIn: 7.0.1.50. | |
| 180 | 367 | |
| 181 | 368 | |
| 182 | 369 | //Ovverride some parameters |
| 183 | 370 | //if ( isset( $attr['resource_id'] ) ) { $attr['type'] = $attr['resource_id']; } |
| @@ -197,36 +384,12 @@ | ||
| 197 | 384 | // Get clean parameters to request booking data |
| 198 | 385 | $args = $this->wpbc_get_clean_paramas_from_request_for_timeline(); |
| 199 | 386 | |
| 200 | 387 | |
| 201 | - // FixIn: 8.1.3.5. | |
| 202 | - /** Client - Page first load | |
| 203 | - * | |
| 204 | - * If provided valid request_args['booking_hash'] | |
| 205 | - * - Firstly defined in constructor in $_REQUEST['booking_hash'] | |
| 206 | - * - or overwrited in define_request_view_params_from_params from parameters in shortcode 'booking_hash' | |
| 207 | - * then check, if exist booking for this hash. | |
| 208 | - * If exist, get Email of this booking, and | |
| 209 | - * filter getting all other bookings by email keyword. | |
| 210 | - * Addtionly set param ['only_booked_resources'] for showing only booking resources with exist bookings. | |
| 211 | - */ | |
| 212 | - if ( isset( $this->request_args['booking_hash'] ) ) { | |
| 388 | + // A public booking hash authorizes only its exact booking and resource. | |
| 389 | + $args = $this->wpbc_apply_booking_hash_scope_to_query_args( $args, $this->request_args['booking_hash'] ); | |
| 213 | 390 | |
| 214 | - // Get booking details by HASH, and then return Email (or other data of booking, or false if error | |
| 215 | - $booking_details_email = wpbc_get__booking_data_field__by_booking_hash( $this->request_args['booking_hash'] , 'email' ); | |
| 216 | 391 | |
| 217 | - if ( ! empty( $booking_details_email ) ) { | |
| 218 | - | |
| 219 | - // Do not show booking resources with no bookings | |
| 220 | - $this->request_args['only_booked_resources'] = 1; | |
| 221 | - | |
| 222 | - //Set keyword for showing bookings ony relative to this email | |
| 223 | - $args['wh_keyword'] = $booking_details_email; // '[email protected]'; | |
| 224 | - } | |
| 225 | - } | |
| 226 | - //FixIn: 8.1.3.5 - End | |
| 227 | - | |
| 228 | - | |
| 229 | 392 | // Get booking data |
| 230 | 393 | $bk_listing = wpbc_get_bookings_objects( $args ); |
| 231 | 394 | $this->bookings = $bk_listing['bookings']; |
| 232 | 395 | $this->booking_types = $bk_listing['resources']; |
| @@ -257,10 +420,41 @@ | ||
| 257 | 420 | public function ajax_init( $attr ) { |
| 258 | 421 | |
| 259 | 422 | if ( ! defined( 'WPBC_TIMELINE_AJAX' ) ) { define( 'WPBC_TIMELINE_AJAX', true ); } // FixIn: 8.4.7.13. |
| 260 | 423 | |
| 261 | - $this->is_frontend = (bool) $attr['is_frontend'];; | |
| 424 | + // FixIn: 10.14.14.2. | |
| 425 | + $is_frontend = (bool) $attr['is_frontend']; | |
| 262 | 426 | |
| 427 | + // Not front-end (or possibly changed parameter by attacker to see the bookings). | |
| 428 | + if ( ! $is_frontend ) { | |
| 429 | + | |
| 430 | + // If this is not front-end -- admin side, then allow do this only for logged in users, with minimum user role, defined in the settings. | |
| 431 | + // Get minimum user role to access the Timeline in admin panel (Booking Listing and Timeline Overview (Calendar Overview). | |
| 432 | + $curr_user_role = get_bk_option( 'booking_user_role_booking' ); | |
| 433 | + | |
| 434 | + // Get current user. | |
| 435 | + $current_user = wpbc_get_current_user(); | |
| 436 | + $user_role_map = array( | |
| 437 | + 'administrator' => 10, | |
| 438 | + 'editor' => 7, | |
| 439 | + 'author' => 2, | |
| 440 | + 'contributor' => 1, | |
| 441 | + 'subscriber' => 0, | |
| 442 | + ); | |
| 443 | + | |
| 444 | + $level = 0; | |
| 445 | + if ( isset( $user_role_map[ $curr_user_role ] ) ) { | |
| 446 | + $level = $user_role_map[ $curr_user_role ]; | |
| 447 | + } | |
| 448 | + | |
| 449 | + if ( empty( $current_user ) || empty( $current_user->user_level ) || ( $current_user->user_level < $level ) ) { | |
| 450 | + // Security Fix: Enforce frontend mode for non-admins. | |
| 451 | + $attr['is_frontend'] = 1; | |
| 452 | + } | |
| 453 | + } | |
| 454 | + | |
| 455 | + $this->is_frontend = (bool) $attr['is_frontend']; | |
| 456 | + | |
| 263 | 457 | //Ovverride some parameters |
| 264 | 458 | //if ( isset( $attr['resource_id'] ) ) { $attr['type'] = $attr['resource_id']; } |
| 265 | 459 | if ( isset( $attr['type'] ) ) { |
| 266 | 460 | $attr['wh_booking_type'] = $attr['type']; //Instead of 'wh_booking_type' paramter in shortcode is used 'type' parameter |
| @@ -429,37 +623,13 @@ | ||
| 429 | 623 | // Get clean parameters to request booking data |
| 430 | 624 | $args = $this->wpbc_get_clean_paramas_from_request_for_timeline(); |
| 431 | 625 | |
| 432 | 626 | |
| 433 | - // FixIn: 8.1.3.5. | |
| 434 | - /** | |
| 435 | - * If provided valid ['booking_hash'] in timeline_obj in JavaScript param during Ajax request, | |
| 436 | - * then check, if exist booking for this hash. If exist, get Email of this booking, and | |
| 437 | - * filter getting all other bookings by email keyword. | |
| 438 | - * Addtionly set param ['only_booked_resources'] for showing only booking resources with exist bookings | |
| 439 | - */ | |
| 440 | - if ( isset( $attr['booking_hash'] ) ) { | |
| 627 | + // Apply the same exact-booking scope to every unauthenticated navigation request. | |
| 628 | + $booking_hash = isset( $attr['booking_hash'] ) ? $attr['booking_hash'] : ''; | |
| 629 | + $args = $this->wpbc_apply_booking_hash_scope_to_query_args( $args, $booking_hash ); | |
| 441 | 630 | |
| 442 | - // Get booking details by HASH, and then return Email (or other data of booking, or false if error | |
| 443 | - $booking_details_email = wpbc_get__booking_data_field__by_booking_hash( $attr['booking_hash'] , 'email' ); | |
| 444 | -//debuge($attr, $booking_details_email); | |
| 445 | - if ( ! empty( $booking_details_email ) ) { | |
| 446 | 631 | |
| 447 | - // Do not show booking resources with no bookings | |
| 448 | - $this->request_args['only_booked_resources'] = 1; | |
| 449 | - | |
| 450 | - //Set keyword for showing bookings ony relative to this email | |
| 451 | - $args['wh_keyword'] = $booking_details_email; // '[email protected]'; | |
| 452 | - } | |
| 453 | - if ( ( empty( $booking_details_email ) ) && ( ! empty( $attr['booking_hash'] ) ) ) { // FixIn: 8.4.6.1. | |
| 454 | - // FixIn: 8.4.5.13. | |
| 455 | - $this->request_args['only_booked_resources'] = 1; | |
| 456 | - $args['wh_keyword'] = '``^`````^^````^`````````'; | |
| 457 | - } | |
| 458 | - } | |
| 459 | - //FixIn: 8.1.3.5 - End | |
| 460 | - | |
| 461 | - | |
| 462 | 632 | // Get booking data |
| 463 | 633 | $bk_listing = wpbc_get_bookings_objects( $args ); |
| 464 | 634 | |
| 465 | 635 | $this->bookings = $bk_listing['bookings']; |
| @@ -470,9 +640,9 @@ | ||
| 470 | 640 | $this->dates_array = $bookings_date_time[0]; |
| 471 | 641 | $this->time_array_new = $bookings_date_time[1]; |
| 472 | 642 | |
| 473 | 643 | |
| 474 | - $this->html_client_id = $attr['html_client_id']; | |
| 644 | + $this->html_client_id = self::normalize_html_client_id( $attr['html_client_id'] ); | |
| 475 | 645 | |
| 476 | 646 | return $this->html_client_id; |
| 477 | 647 | } |
| 478 | 648 | |
| @@ -528,9 +698,12 @@ | ||
| 528 | 698 | 'header_title' : "<?php echo esc_js( $this->timeline_titles['header_title'] ); ?>", |
| 529 | 699 | 'wh_trash' : "<?php echo esc_js( $this->request_args['wh_trash'] ); ?>", |
| 530 | 700 | 'limit_hours' : "<?php echo esc_js( $this->request_args['limit_hours'] ); ?>", |
| 531 | 701 | 'only_booked_resources': "<?php echo esc_js( $this->request_args['only_booked_resources'] ); ?>", |
| 532 | - 'options' : '<?php echo wp_json_encode( $this->options ); ?>', | |
| 702 | + 'options' : <?php | |
| 703 | + // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- Returns a complete JSON-encoded JavaScript string literal. | |
| 704 | + echo self::encode_options_for_inline_script( $this->options ); | |
| 705 | + ?>, | |
| 533 | 706 | 'booking_hash' : "<?php echo esc_js( $this->request_args['booking_hash'] ); ?>" |
| 534 | 707 | }; |
| 535 | 708 | </script> |
| 536 | 709 | <div class="flex_tl_nav"> |
| @@ -853,11 +1026,11 @@ | ||
| 853 | 1026 | } // FixIn: 7.0.1.14. |
| 854 | 1027 | if ( isset( $param['booking_hash'] ) ) { |
| 855 | 1028 | $this->request_args['booking_hash'] = $param['booking_hash']; |
| 856 | 1029 | } // FixIn: 8.1.3.5. |
| 857 | - if ( ( empty( $this->options ) ) && ( isset( $param['options'] ) ) ) { | |
| 858 | - $this->options = json_decode( wp_unslash( $param['options'] ), true ); // FixIn: 9.2.1.8. | |
| 859 | - } | |
| 1030 | + if ( ( empty( $this->options ) ) && ( isset( $param['options'] ) ) ) { | |
| 1031 | + $this->options = self::decode_options( $param['options'] ); // FixIn: 9.2.1.8. | |
| 1032 | + } | |
| 860 | 1033 | |
| 861 | 1034 | } |
| 862 | 1035 | |
| 863 | 1036 | |
| @@ -982,9 +1155,9 @@ | ||
| 982 | 1155 | $booking_id__to_del = $bookings_to_del [ $bk_i ]; |
| 983 | 1156 | |
| 984 | 1157 | ?><div style="margin:10px;font-weight:600;font-size:1.05em;"><?php |
| 985 | 1158 | |
| 986 | - echo wp_kses_post( sprintf( 'Delete booking with %s', htmlspecialchars_decode( '<strong></strong><a href="' . esc_url( wpbc_get_bookings_url() . '&view_mode=vm_listing&tab=actions&wh_booking_id=' . $booking_id__to_del ) . '">' . 'ID = ' . $booking_id__to_del . '</a></strong>' ) ) ); | |
| 1159 | + echo wp_kses_post( sprintf( 'Delete booking with %s', htmlspecialchars_decode( '<strong></strong><a href="' . esc_url( wpbc_get_bookings_url() . '&tab=vm_booking_listing&wh_booking_id=' . $booking_id__to_del ) . '">' . 'ID = ' . $booking_id__to_del . '</a></strong>' ) ) ); | |
| 987 | 1160 | |
| 988 | 1161 | ?></div><?php |
| 989 | 1162 | } |
| 990 | 1163 | |
| @@ -1864,9 +2037,11 @@ | ||
| 1864 | 2037 | } |
| 1865 | 2038 | |
| 1866 | 2039 | $bk_title .= " \n" . $this->get_booking_title_for_timeline( $booking_id, $row_settings['bookings'] ); |
| 1867 | 2040 | |
| 1868 | - $bk_title .= " \n" . wp_strip_all_tags( wpbc_get_short_dates_formated_to_show( $row_settings['bookings'][ $booking_id ]->dates_short ) ) ; | |
| 2041 | + $bk_title .= " \n" . wp_strip_all_tags( wpbc_get_short_dates_formated_to_show( $row_settings['bookings'][ $booking_id ]->dates_short ) ) ; | |
| 2042 | + | |
| 2043 | + $bk_title = apply_filters( 'wpbc_timeline_booking_pipeline_title', $bk_title, $booking_id, $row_settings['bookings'] ); | |
| 1869 | 2044 | |
| 1870 | 2045 | ?><a href="javascript:void(0)" |
| 1871 | 2046 | class="in_cell_date_booking_pipeline_a" |
| 1872 | 2047 | title="<?php echo esc_attr( $bk_title ); ?>" |
| @@ -1897,9 +2072,10 @@ | ||
| 1897 | 2072 | $bk_a_title_arr[] = $bk_a_title; |
| 1898 | 2073 | |
| 1899 | 2074 | $title_in_day = $title = $title_hint = ''; |
| 1900 | 2075 | |
| 1901 | - if ( $is_show_popover_in_timeline ) { | |
| 2076 | + $can_show_booking_popover = $is_show_popover_in_timeline && $this->wpbc_is_booking_authorized_for_hash( $booking_id, $row_settings['bookings'] ); | |
| 2077 | + if ( $can_show_booking_popover ) { | |
| 1902 | 2078 | $popup_content = $this->wpbc_get_booking_info_4_popover( $booking_id, $row_settings['bookings'], $row_settings['booking_types'] ); |
| 1903 | 2079 | |
| 1904 | 2080 | |
| 1905 | 2081 | $popup_title_arr[] = $popup_content['title']; |
| @@ -1936,15 +2112,15 @@ | ||
| 1936 | 2112 | // Booking CELL Title |
| 1937 | 2113 | ?><a href="javascript:void(0)" |
| 1938 | 2114 | class="<?php echo esc_attr( implode(' ', array( |
| 1939 | 2115 | 'in_cell_date_booking_title', |
| 1940 | - ( $is_show_popover_in_timeline ) ? 'popover_bottom' : '', | |
| 1941 | - ( $is_show_popover_in_timeline ) ? 'popover_click' : '', | |
| 2116 | + ( ! empty( $popup_content_arr ) ) ? 'popover_bottom' : '', | |
| 2117 | + ( ! empty( $popup_content_arr ) ) ? 'popover_click' : '', | |
| 1942 | 2118 | ( count( $bookings_in_cell ) > 1 ) ? 'several_bookings_in_cell' : '' |
| 1943 | 2119 | ))); ?>" |
| 1944 | 2120 | <?php |
| 1945 | 2121 | // FixIn: 8.9.3.3. |
| 1946 | - if ( $is_show_popover_in_timeline ) { ?> | |
| 2122 | + if ( ! empty( $popup_content_arr ) ) { ?> | |
| 1947 | 2123 | data-content="<?php echo esc_html( str_replace( '"', "", $popup_content_arr ) ); ?>" |
| 1948 | 2124 | data-original-title="<?php echo esc_html( str_replace( '"', "", $popup_title_arr ) ); ?>" |
| 1949 | 2125 | <?php } ?> |
| 1950 | 2126 | ><?php |
| @@ -2549,9 +2725,9 @@ | ||
| 2549 | 2725 | $this->show_timeline_header_start__admin_panel(); |
| 2550 | 2726 | } |
| 2551 | 2727 | // New Spinner Loader // FixIn: 10.0.0.25. |
| 2552 | 2728 | ?><div class="flex_tl_table_loading wpbc_spins_loading_container"> |
| 2553 | - <div class="wpbc_booking_form_spin_loader"><div class="wpbc_spins_loader_wrapper"><div class="wpbc_spins_loader_mini"></div></div></div> | |
| 2729 | + <div class="wpbc_booking_form_spin_loader"><div class="wpbc_spins_loader_wrapper"><div class="wpbc_spin_loader_one_new"></div></div></div> | |
| 2554 | 2730 | <?php // echo '<span class="0glyphicon 0glyphicon-refresh wpbc_icn_autorenew wpbc_spin"></span>' ?> |
| 2555 | 2731 | <span><?php esc_html_e('Loading','booking'); ?>...</span> |
| 2556 | 2732 | </div><?php |
| 2557 | 2733 | |
| @@ -2968,11 +3144,18 @@ | ||
| 2968 | 3144 | * @param string $content_text |
| 2969 | 3145 | * |
| 2970 | 3146 | * @return array |
| 2971 | 3147 | */ |
| 2972 | - public function wpbc_get_booking_info_4_popover( $bk_id, $bookings, $booking_types ){ | |
| 2973 | - | |
| 2974 | - if ( isset( $bookings[ $bk_id ] ) ) { | |
| 3148 | + public function wpbc_get_booking_info_4_popover( $bk_id, $bookings, $booking_types ){ | |
| 3149 | + | |
| 3150 | + if ( ! $this->wpbc_is_booking_authorized_for_hash( $bk_id, $bookings ) ) { | |
| 3151 | + return array( | |
| 3152 | + 'title' => '', | |
| 3153 | + 'content' => '', | |
| 3154 | + ); | |
| 3155 | + } | |
| 3156 | + | |
| 3157 | + if ( isset( $bookings[ $bk_id ] ) ) { | |
| 2975 | 3158 | //$bookings[ $bk_id ]->form_show = str_replace( "&", '&', $bookings[ $bk_id ]->form_show ); // FixIn: 7.1.2.12. |
| 2976 | 3159 | // We escaping at other place: wpbc__legacy__get_form_content_arr() |
| 2977 | 3160 | } |
| 2978 | 3161 | |
| @@ -3002,26 +3185,39 @@ | ||
| 3002 | 3185 | if ( ( ! $this->is_frontend ) && ( $is_can ) ) { |
| 3003 | 3186 | // Link |
| 3004 | 3187 | $header_title .= '<a class=\'button button-secondary\' |
| 3005 | 3188 | title=\'' . esc_attr( str_replace( "'", '', __( 'Booking Listing', 'booking' ) ) ) . '\' |
| 3006 | - href=\''.wpbc_get_bookings_url( true, false ).'&wh_booking_id='.$bk_id.'&view_mode=vm_listing&tab=actions\' ><i class=\'wpbc_icn_gps_fixed\'></i></a>'; | |
| 3007 | - //Edit | |
| 3008 | - if ( class_exists( 'wpdev_bk_personal' ) ) { | |
| 3009 | - $bk_url_add = wpbc_get_new_booking_url( true, false ); | |
| 3010 | - $bk_hash = (isset( $bookings[$bk_id]->hash )) ? $bookings[$bk_id]->hash : ''; | |
| 3011 | - $bk_booking_type = $bookings[$bk_id]->booking_type; | |
| 3012 | - $edit_booking_url = $bk_url_add . '&booking_type=' . $bk_booking_type . '&booking_hash=' . $bk_hash . '&parent_res=1'; | |
| 3013 | - // FixIn: 10.10.1.2 $edit_booking_url .= ( 'Off' !== get_bk_option( 'booking_is_resource_no_update__during_editing' ) ) ? '&resource_no_update=1' : ''; // FixIn: 9.4.2.3. | |
| 3189 | + href=\''.wpbc_get_bookings_url( true, false ).'&wh_booking_id='.$bk_id.'&tab=vm_booking_listing\' ><i class=\'wpbc_icn_gps_fixed\'></i></a>'; | |
| 3190 | + //Edit | |
| 3191 | + if ( class_exists( 'wpdev_bk_personal' ) ) { | |
| 3192 | + $bk_hash = (isset( $bookings[$bk_id]->hash )) ? $bookings[$bk_id]->hash : ''; | |
| 3193 | + $bk_booking_type = $bookings[$bk_id]->booking_type; | |
| 3194 | + // FixIn: 10.10.1.2 $edit_booking_url .= ( 'Off' !== get_bk_option( 'booking_is_resource_no_update__during_editing' ) ) ? '&resource_no_update=1' : ''; // FixIn: 9.4.2.3. | |
| 3195 | + | |
| 3196 | + $custom_booking_form = ''; | |
| 3197 | + if ( ! empty( $bookings[ $bk_id ]->form_data['_all_fields_']['wpbc_custom_booking_form'] ) ) { | |
| 3198 | + $custom_booking_form = $bookings[ $bk_id ]->form_data['_all_fields_']['wpbc_custom_booking_form']; | |
| 3199 | + } | |
| 3200 | + $edit_booking_url = wpbc_get_booking_admin_edit_url( $bk_booking_type, $bk_hash, $custom_booking_form ); | |
| 3201 | + | |
| 3202 | + $edit_booking_onclick = ''; | |
| 3203 | + if ( ! wpbc_is_booking_admin_edit_page_enabled() ) { | |
| 3204 | + $edit_booking_onclick = "if ( 'function' === typeof wpbc_boo_listing__click__add_booking_modal_from_row ) {" | |
| 3205 | + . ' wpbc_boo_listing__click__add_booking_modal_from_row(' | |
| 3206 | + . absint( $bk_id ) . ',' | |
| 3207 | + . absint( $bk_booking_type ) . ',' | |
| 3208 | + . "'" . esc_js( $bk_hash ) . "'," | |
| 3209 | + . "'" . esc_js( $custom_booking_form ) . "'" | |
| 3210 | + . ' ); return false; }'; | |
| 3211 | + } | |
| 3212 | + | |
| 3213 | + $header_title .= '<a class=\'button button-secondary\' | |
| 3214 | + title=\'' . esc_attr( str_replace( "'", '', __( 'Edit', 'booking' ) ) ) . '\' | |
| 3215 | + href=\'' . esc_url( $edit_booking_url ) . '\'' | |
| 3216 | + . ( '' !== $edit_booking_onclick ? ' onclick=\'' . esc_attr( $edit_booking_onclick ) . '\'' : '' ) | |
| 3217 | + . ' ><i class=\'wpbc_icn_draw\'></i></a>'; | |
| 3014 | 3218 | |
| 3015 | - if ( ! empty( $bookings[ $bk_id ]->form_data['_all_fields_']['wpbc_custom_booking_form'] ) ) { | |
| 3016 | - $edit_booking_url .= '&booking_form=' . $bookings[ $bk_id ]->form_data['_all_fields_']['wpbc_custom_booking_form']; // FixIn: 9.4.3.12. | |
| 3017 | - } | |
| 3018 | 3219 | |
| 3019 | - $header_title .= '<a class=\'button button-secondary\' | |
| 3020 | - title=\'' . esc_attr( str_replace( "'", '', __( 'Edit', 'booking' ) ) ) . '\' | |
| 3021 | - href=\'' . esc_url($edit_booking_url) . '\' onclick=\'\' ><i class=\'wpbc_icn_draw\'></i></a>'; | |
| 3022 | - | |
| 3023 | - | |
| 3024 | 3220 | $header_title .= '<span class=\'wpbc-buttons-separator\'></span>'; |
| 3025 | 3221 | } |
| 3026 | 3222 | // Trash |
| 3027 | 3223 | //$header_title .= '<a class=\'button button-secondary\' href=\'javascript:;\' onclick=\'javascript:delete_booking(' . $bk_id . ', ' . $this->current_user_id . ', "' . wpbc_get_maybe_reloaded_booking_locale() . '" , 1 );\' ><i class=\'wpbc_icn_delete_outline\'></i></a>'; |
| @@ -3058,10 +3254,10 @@ | ||
| 3058 | 3254 | |
| 3059 | 3255 | //Edit |
| 3060 | 3256 | if ( class_exists( 'wpdev_bk_personal' ) ) { |
| 3061 | 3257 | |
| 3062 | - // $edit_booking_url_admin = wpbc_get_bookings_url( true, false ).'&wh_booking_id='.$bk_id.'&view_mode=vm_listing&tab=actions'; | |
| 3063 | - // $trash_booking_url_admin = wpbc_get_bookings_url( true, false ).'&wh_booking_id='.$bk_id.'&view_mode=vm_listing&tab=actions'; | |
| 3258 | + // $edit_booking_url_admin = wpbc_get_bookings_url( true, false ).'&wh_booking_id='.$bk_id.'&tab=vm_booking_listing'; | |
| 3259 | + // $trash_booking_url_admin = wpbc_get_bookings_url( true, false ).'&wh_booking_id='.$bk_id.'&tab=vm_booking_listing'; | |
| 3064 | 3260 | |
| 3065 | 3261 | $is_change_hash_after_approvement = get_bk_option( 'booking_is_change_hash_after_approvement' ); // FixIn: 8.6.1.6. |
| 3066 | 3262 | if ( ( ! $is_approved ) || ( 'On' != $is_change_hash_after_approvement ) ) { // FixIn: 8.2.1.14. |
| 3067 | 3263 | $visitorbookingediturl = apply_bk_filter( 'wpdev_booking_set_booking_edit_link_at_email', '[visitorbookingediturl]', $bk_id ); |
| @@ -3076,17 +3272,17 @@ | ||
| 3076 | 3272 | } |
| 3077 | 3273 | |
| 3078 | 3274 | $header_title .= '</div>'; |
| 3079 | 3275 | } |
| 3080 | - | |
| 3276 | + | |
| 3081 | 3277 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 3082 | - // Content | |
| 3278 | + // Content | |
| 3083 | 3279 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 3084 | 3280 | |
| 3085 | 3281 | // Container |
| 3086 | 3282 | $content_text = '<div id=\'wpbc-booking-id-'.$bk_id.'\' class=\'flex-popover-content-data\' >'; |
| 3087 | 3283 | |
| 3088 | - | |
| 3284 | + | |
| 3089 | 3285 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 3090 | 3286 | // Labels |
| 3091 | 3287 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 3092 | 3288 | $content_text .= '<div class=\'flex-popover-bars\' >'; |
| @@ -3191,12 +3387,12 @@ | ||
| 3191 | 3387 | // Notes |
| 3192 | 3388 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 3193 | 3389 | |
| 3194 | 3390 | // Notes |
| 3195 | - if ( ! empty( $bookings[$bk_id]->remark ) ) { | |
| 3391 | + if ( ! empty( $bookings[$bk_id]->remark ) ) { | |
| 3196 | 3392 | $content_text .= '<div class=\'wpbc-popover-booking-notes\'>' . '<strong>' . esc_js( __('Note', 'booking') ). ':</strong> ' . esc_textarea( $bookings[$bk_id]->remark ) . '</div>'; //FixIn: 7.1.1.2 // FixIn: 7.1.1.3. |
| 3197 | 3393 | } |
| 3198 | - | |
| 3394 | + | |
| 3199 | 3395 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 3200 | 3396 | // Dates |
| 3201 | 3397 | //////////////////////////////////////////////////////////////////////////////////////////////////////////////// |
| 3202 | 3398 | |
| @@ -3202,9 +3398,9 @@ | ||
| 3202 | 3398 | |
| 3203 | 3399 | $bk_dates_short_id = array(); //BL |
| 3204 | 3400 | if ( count( $bookings[$bk_id]->dates ) > 0 ) |
| 3205 | 3401 | $bk_dates_short_id = (isset( $bookings[$bk_id]->dates_short_id )) ? $bookings[$bk_id]->dates_short_id : array(); // Array ([0] => [1] => .... [4] => 6... [11] => [12] => 8 ) |
| 3206 | - | |
| 3402 | + | |
| 3207 | 3403 | $short_dates_content = wpbc_get_short_dates_formated_to_show( $bookings[$bk_id]->dates_short, $is_approved, $bk_dates_short_id, $booking_types ); |
| 3208 | 3404 | $short_dates_content = str_replace( '"', "'", $short_dates_content ); |
| 3209 | 3405 | |
| 3210 | 3406 | $content_text .= '<div class=\'flex-label-dates \'>'; |
| @@ -3227,20 +3423,24 @@ | ||
| 3227 | 3423 | |
| 3228 | 3424 | |
| 3229 | 3425 | $content_text .= '</div>'; // Main Container: 'flex-popover-content-data' |
| 3230 | 3426 | |
| 3231 | - return array( | |
| 3232 | - 'title' => $header_title, | |
| 3233 | - 'content' => $content_text | |
| 3234 | - ); | |
| 3235 | - } | |
| 3236 | - | |
| 3427 | + $popover = array( | |
| 3428 | + 'title' => $header_title, | |
| 3429 | + 'content' => $content_text | |
| 3430 | + ); | |
| 3431 | + | |
| 3432 | + $popover = apply_filters( 'wpbc_timeline_booking_popover', $popover, $bk_id, $bookings, $this->is_frontend ); | |
| 3433 | + | |
| 3434 | + return $popover; | |
| 3435 | + } | |
| 3436 | + | |
| 3237 | 3437 | } |
| 3238 | 3438 | |
| 3239 | 3439 | |
| 3240 | 3440 | |
| 3241 | 3441 | /** Navigation of Timeline in Ajax request */ |
| 3242 | -function wpbc_ajax_flex_timeline() { | |
| 3442 | +function wpbc_ajax_flex_timeline() { | |
| 3243 | 3443 | /* |
| 3244 | 3444 | [timeline_obj] => Array |
| 3245 | 3445 | ( |
| 3246 | 3446 | [is_frontend] => 1 |
| @@ -3247,22 +3447,83 @@ | ||
| 3247 | 3447 | [html_client_id] => wpbc_timeline_1454680376080 |
| 3248 | 3448 | [wh_booking_type] => 3,4,1,5,6,7,8,9,2,10,11,12,14 |
| 3249 | 3449 | [is_matrix] => 1 |
| 3250 | 3450 | [view_days_num] => 30 |
| 3251 | - [scroll_start_date] => | |
| 3451 | + [scroll_start_date] => | |
| 3252 | 3452 | [scroll_day] => 0 |
| 3253 | 3453 | [scroll_month] => 0 |
| 3254 | 3454 | ) |
| 3255 | 3455 | */ |
| 3256 | 3456 | |
| 3257 | - // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 3258 | - foreach ( $_POST['timeline_obj'] as $tl_key => $tl_value ) { | |
| 3259 | - $_POST['timeline_obj'][ $tl_key ] = wpbc_clean_text_value( $tl_value ); // phpcs:ignore WordPress.Security.ValidatedSanitizedInput.InputNotValidated, WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized | |
| 3260 | - } | |
| 3457 | + // Public timeline navigation accepts only one flat scalar attribute map. | |
| 3458 | + // phpcs:ignore WordPress.Security.NonceVerification.Missing | |
| 3459 | + if ( ! isset( $_POST['timeline_obj'] ) || ! is_array( $_POST['timeline_obj'] ) ) { | |
| 3460 | + status_header( 400 ); | |
| 3461 | + wp_die( '' ); | |
| 3462 | + } | |
| 3463 | + | |
| 3464 | + $attr = array(); | |
| 3465 | + $allowed_timeline_keys = array_fill_keys( | |
| 3466 | + array( | |
| 3467 | + 'is_frontend', | |
| 3468 | + 'html_client_id', | |
| 3469 | + 'wh_booking_type', | |
| 3470 | + 'is_matrix', | |
| 3471 | + 'view_days_num', | |
| 3472 | + 'scroll_start_date', | |
| 3473 | + 'scroll_day', | |
| 3474 | + 'scroll_month', | |
| 3475 | + 'header_column1', | |
| 3476 | + 'header_column2', | |
| 3477 | + 'header_title', | |
| 3478 | + 'wh_trash', | |
| 3479 | + 'limit_hours', | |
| 3480 | + 'only_booked_resources', | |
| 3481 | + 'options', | |
| 3482 | + 'booking_hash', | |
| 3483 | + ), | |
| 3484 | + true | |
| 3485 | + ); | |
| 3486 | + // phpcs:ignore WordPress.Security.NonceVerification.Missing | |
| 3487 | + foreach ( $_POST['timeline_obj'] as $tl_key => $tl_value ) { | |
| 3488 | + if ( ! is_scalar( $tl_key ) || ! is_scalar( $tl_value ) ) { | |
| 3489 | + status_header( 400 ); | |
| 3490 | + wp_die( '' ); | |
| 3491 | + } | |
| 3492 | + | |
| 3493 | + $clean_key = sanitize_key( wp_unslash( (string) $tl_key ) ); | |
| 3494 | + if ( '' === $clean_key || ! isset( $allowed_timeline_keys[ $clean_key ] ) ) { | |
| 3495 | + continue; | |
| 3496 | + } | |
| 3497 | + $clean_value = wp_unslash( (string) $tl_value ); | |
| 3498 | + if ( 'options' === $clean_key ) { | |
| 3499 | + $normalized_options = WPBC_TimelineFlex::decode_options( $clean_value ); | |
| 3500 | + $encoded_options = wp_json_encode( $normalized_options ); | |
| 3501 | + $attr[ $clean_key ] = false === $encoded_options ? '{}' : $encoded_options; | |
| 3502 | + continue; | |
| 3503 | + } | |
| 3504 | + | |
| 3505 | + $attr[ $clean_key ] = wpbc_clean_text_value( $clean_value ); | |
| 3506 | + } | |
| 3507 | + | |
| 3508 | + // phpcs:ignore WordPress.Security.NonceVerification.Missing | |
| 3509 | + if ( isset( $_POST['nav_step'] ) && ! is_scalar( $_POST['nav_step'] ) ) { | |
| 3510 | + status_header( 400 ); | |
| 3511 | + wp_die( '' ); | |
| 3512 | + } | |
| 3513 | + | |
| 3514 | + $attr['nav_step'] = isset( $_POST['nav_step'] ) | |
| 3515 | + ? wpbc_clean_text_value( wp_unslash( (string) $_POST['nav_step'] ) ) // phpcs:ignore WordPress.Security.NonceVerification.Missing | |
| 3516 | + : '0'; | |
| 3517 | + $attr['is_frontend'] = isset( $attr['is_frontend'] ) ? $attr['is_frontend'] : '1'; | |
| 3518 | + $attr['html_client_id'] = isset( $attr['html_client_id'] ) | |
| 3519 | + ? WPBC_TimelineFlex::normalize_html_client_id( $attr['html_client_id'] ) | |
| 3520 | + : ''; | |
| 3521 | + if ( '' === $attr['html_client_id'] ) { | |
| 3522 | + status_header( 400 ); | |
| 3523 | + wp_die( '' ); | |
| 3524 | + } | |
| 3261 | 3525 | |
| 3262 | - $attr = $_POST['timeline_obj']; // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.InputNotValidated | |
| 3263 | - $attr['nav_step'] = wpbc_clean_text_value( $_POST['nav_step'] ); // phpcs:ignore WordPress.Security.NonceVerification.Missing, WordPress.Security.ValidatedSanitizedInput.MissingUnslash, WordPress.Security.ValidatedSanitizedInput.InputNotSanitized, WordPress.Security.ValidatedSanitizedInput.InputNotValidated | |
| 3264 | - | |
| 3265 | 3526 | // FixIn: 9.9.0.18. |
| 3266 | 3527 | $server_zone = date_default_timezone_get(); // If in 'Theme' or 'other plugin' set default timezone other than UTC. Save it. |
| 3267 | 3528 | if ( 'UTC' !== $server_zone ) { // Needed for WP date functions - set timezone to UTC. |
| 3268 | 3529 | // phpcs:ignore WordPress.DateTime.RestrictedFunctions.timezone_change_date_default_timezone_set |
| @@ -3273,10 +3534,10 @@ | ||
| 3273 | 3534 | |
| 3274 | 3535 | $timeline = new WPBC_TimelineFlex(); |
| 3275 | 3536 | |
| 3276 | 3537 | $html_client_id = $timeline->ajax_init( $attr ); // Define arameters and get bookings |
| 3277 | -//debuge($timeline->options); | |
| 3278 | - | |
| 3538 | +//debuge($timeline->options); | |
| 3539 | + | |
| 3279 | 3540 | //echo '<div class="wpbc_timeline_ajax_replace">'; // Replace content of this container |
| 3280 | 3541 | $timeline->show_timeline(); |
| 3281 | 3542 | |
| 3282 | 3543 | |
| @@ -3292,11 +3553,11 @@ | ||
| 3292 | 3553 | echo $html; |
| 3293 | 3554 | |
| 3294 | 3555 | /* ?><script type="text/javascript"> wpbc_define_tippy_popover(); </script><?php */ |
| 3295 | 3556 | } |
| 3296 | - //echo '</div>'; | |
| 3557 | + //echo '</div>'; | |
| 3297 | 3558 | |
| 3298 | - | |
| 3559 | + | |
| 3299 | 3560 | $timeline_results = ob_get_contents(); |
| 3300 | 3561 | |
| 3301 | 3562 | ob_end_clean(); |
| 3302 | 3563 | |
| @@ -3306,9 +3567,9 @@ | ||
| 3306 | 3567 | @date_default_timezone_set( $server_zone ); |
| 3307 | 3568 | } |
| 3308 | 3569 | |
| 3309 | 3570 | // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped |
| 3310 | - echo $timeline_results ; | |
| 3571 | + echo $timeline_results ; | |
| 3311 | 3572 | } |
| 3312 | 3573 | add_bk_action('wpbc_ajax_flex_timeline', 'wpbc_ajax_flex_timeline'); |
| 3313 | 3574 | |
| 3314 | 3575 | |
| @@ -3322,22 +3583,32 @@ | ||
| 3322 | 3583 | * @param $booking_hash |
| 3323 | 3584 | * |
| 3324 | 3585 | * @return bool |
| 3325 | 3586 | */ |
| 3326 | -function wpbc_is_show_popover_in_flex_timeline( $is_frontend, $booking_hash ){ | |
| 3587 | +function wpbc_is_show_popover_in_flex_timeline( $is_frontend, $booking_hash ) { | |
| 3327 | 3588 | |
| 3328 | - // Default for admin | |
| 3589 | + // Default for admin. | |
| 3329 | 3590 | $is_show_popover_in_timeline = true; |
| 3330 | 3591 | |
| 3331 | - // For client Timeline | |
| 3332 | - if ( $is_frontend ) | |
| 3333 | - $is_show_popover_in_timeline = ( get_bk_option( 'booking_is_show_popover_in_timeline_front_end' ) == 'On' ) ? true : false ; | |
| 3592 | + // For client Timeline. | |
| 3593 | + if ( $is_frontend ) { | |
| 3334 | 3594 | |
| 3335 | - // For customer booking listing with ability to edit | |
| 3595 | + // FixIn: 10.14.11.1. | |
| 3596 | + if ( WPBC_DISABLE_POPOVER_IN_TIMELINE ) { | |
| 3597 | + $is_show_popover_in_timeline = false; | |
| 3598 | + } else { | |
| 3599 | + $is_show_popover_in_timeline = ( get_bk_option( 'booking_is_show_popover_in_timeline_front_end' ) == 'On' ) ? true : false; | |
| 3600 | + if ( ! class_exists( 'wpdev_bk_personal' ) ) { | |
| 3601 | + $is_show_popover_in_timeline = false; // FixIn: 10.14.9.2. | |
| 3602 | + } | |
| 3603 | + } | |
| 3604 | + } | |
| 3605 | + | |
| 3606 | + // For customer booking listing with ability to edit. | |
| 3336 | 3607 | // FixIn: 8.1.3.5. |
| 3337 | 3608 | if ( ( $is_frontend ) && ( ! empty( $booking_hash ) ) ) { |
| 3338 | 3609 | |
| 3339 | - //In case if we have valid valid hash then show booking details | |
| 3610 | + // In case if we have valid valid hash then show booking details. | |
| 3340 | 3611 | $my_booking_id_type = wpbc_hash__get_booking_id__resource_id( $booking_hash ); |
| 3341 | 3612 | |
| 3342 | 3613 | if ( ! empty( $my_booking_id_type ) ) { |
| 3343 | 3614 | $is_show_popover_in_timeline = true; |
| @@ -3378,9 +3649,9 @@ | ||
| 3378 | 3649 | [bookingtimeline type="4" view_days_num=365 scroll_start_date="" scroll_month=-3] |
| 3379 | 3650 | |
| 3380 | 3651 | |
| 3381 | 3652 | */ |
| 3382 | -function bookingflextimeline_shortcode($attr) { | |
| 3653 | +function bookingflextimeline_shortcode($attr) { // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedFunctionFound | |
| 3383 | 3654 | |
| 3384 | 3655 | if ( wpbc_is_on_edit_page() ) { |
| 3385 | 3656 | return wpbc_get_preview_for_shortcode( 'bookingflextimeline', $attr ); // FixIn: 9.9.0.39. |
| 3386 | 3657 | } |
| @@ -3523,9 +3794,9 @@ | ||
| 3523 | 3794 | |
| 3524 | 3795 | if ( in_array( $where_to_load, array( 'admin', 'both', 'client' ) ) ) { |
| 3525 | 3796 | |
| 3526 | 3797 | // phpcs:ignore WordPress.Security.NonceVerification.Recommended, WordPress.Security.NonceVerification.Missing |
| 3527 | - if ( ( ( isset($_REQUEST['view_mode']) ) && ( $_REQUEST['view_mode']== 'vm_calendar' ) ) || ( 'client' == $where_to_load ) ){ | |
| 3798 | + if ( ( ( isset($_REQUEST['tab']) ) && ( $_REQUEST['tab']== 'vm_calendar' ) ) || ( 'client' == $where_to_load ) ){ | |
| 3528 | 3799 | |
| 3529 | 3800 | wp_enqueue_style( 'wpbc-flex-timeline' |
| 3530 | 3801 | , trailingslashit( plugins_url( '', __FILE__ ) ) . '_out/timeline_v2.1.css' /* wpbc_plugin_url( '/src/css/code_mirror.css' ) */ |
| 3531 | 3802 | , array() |
| @@ -3532,5 +3803,5 @@ | ||
| 3532 | 3803 | , WP_BK_VERSION_NUM ); |
| 3533 | 3804 | } |
| 3534 | 3805 | } |
| 3535 | 3806 | } |
| 3536 | -add_action( 'wpbc_enqueue_css_files', 'wpbc_timeline_enqueue_css_files', 50 ); | |
| 3807 | +add_action( 'wpbc_enqueue_css_files', 'wpbc_timeline_enqueue_css_files', 50 ); | |