PluginProbe
Booking Calendar / 11.0
Booking Calendar v11.0
11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 10.11 10.11.2 All 203 releases
booking / includes / _front_end / hooks / class-fe-bfb-settings-hooks.php

class-fe-bfb-settings-hooks.php in Booking Calendar 11.0, at includes/_front_end/hooks/class-fe-bfb-settings-hooks.php

244 lines 9.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * BFB Settings -> Front-End Booking Form Post-Processing Hooks
4 *
5 * This file registers hook callbacks that apply **BFB form settings** (decoded JSON settings array) to the final booking form HTML that is rendered on the front-end.
6 *
7 * These hooks are executed inside `WPBC_FE_Form_Body_Renderer::render()` in this order:
8 *
9 * 1) `wpbc_booking_form__body_html__before_postprocess` --> raw **form body HTML only** (no injected calendar/captcha/extra calendars yet).
10 *
11 * 2) `wpbc_booking_form__html__before_wrapper` --> composed **form HTML** where calendar/captcha/extra calendars already injected,
12 *
13 * 3) `wpbc_booking_form__wrapped_html__before_inline_scripts --> full **wrapped HTML** including the wrapper `<div class="wpbc_container wpbc_form ...">` and the `<form>` markup
14 *
15 * 4) `wpbc_booking_form__wrapped_html__after_inline_scripts` --> the final HTML after inline scripts are appended.
16 *
17 * Notes:
18 * - `$bfb_settings` is expected to be an array like:
19 * [
20 * 'options' => [ 'booking_form_theme' => 'wpbc_theme_dark_1', ... ]
21 * ]
22 *
23 * @package Booking Calendar
24 * @since 11.0.x
25 * @file ../includes/_front_end/hooks/class-fe-bfb-settings-hooks.php
26 */
27
28 if ( ! defined( 'ABSPATH' ) ) {
29 exit;
30 }
31
32 /**
33 * ['options']['booking_form_layout_width'] - > Inject per-form CSS variables: into the BFB form root wrapper. On Step 1.
34 *
35 * Purpose:
36 * - If the form body contains the BFB root element (class `wpbc_bfb_form`),
37 * inject CSS Custom Properties (variables) into its `style` attribute.
38 *
39 * This hook must run **before** legacy post-processing inserts calendars/captcha, because it targets the BFB body root and should not depend on wrapper existence.
40 *
41 * Expected `$bfb_settings` format: - `$bfb_settings['css_vars']` is an associative array: `--var-name` => `value`.
42 *
43 * @since 11.0.x
44 *
45 * @param string $form_html Raw booking form body HTML (no calendar/captcha injected yet).
46 * @param array $bfb_settings Decoded BFB settings array (may be empty).
47 * @param int $resource_id Booking resource ID.
48 * @param string $custom_booking_form_name Booking form slug/name (legacy: "standard").
49 *
50 * @return string Filtered body HTML.
51 */
52 function wpbc_booking_form__body_html__before_postprocess__apply_bfb_vars( $form_html, $bfb_settings, $resource_id, $custom_booking_form_name ) {
53
54 $form_html = (string) $form_html;
55 $bfb_settings = is_array( $bfb_settings ) ? $bfb_settings : array();
56
57 // If body has no BFB root, do nothing (fast path).
58 if ( false === strpos( $form_html, 'wpbc_bfb_form' ) ) {
59 return $form_html;
60 }
61
62 // Local helper (no global function redeclare risk).
63 $sanitize_css_length = function( $v ) {
64 $v = trim( (string) $v );
65 if ( '' === $v ) {
66 return '';
67 }
68 // Allow only: number + unit.
69 return preg_match( '/^-?\d+(?:\.\d+)?(?:%|px|rem|em|vw|vh)$/', $v ) ? $v : '';
70 };
71
72 $width = '';
73 if ( ! empty( $bfb_settings['options'] ) && is_array( $bfb_settings['options'] ) ) {
74 $width = isset( $bfb_settings['options']['booking_form_layout_width'] )
75 ? $sanitize_css_length( $bfb_settings['options']['booking_form_layout_width'] )
76 : '';
77 }
78
79 // If no valid width -> do nothing (keeps HTML unchanged).
80 if ( '' === $width ) {
81 return $form_html;
82 }
83
84 $css_vars = ( ! empty( $bfb_settings['css_vars'] ) && is_array( $bfb_settings['css_vars'] ) )
85 ? $bfb_settings['css_vars']
86 : array();
87
88 // Use a dedicated CSS var name.
89 $css_vars['--wpbc-bfb-booking_form_layout_width'] = $width;
90
91 return WPBC_FE_Form_Style_Injector::inject_css_vars_into_bfb_root( $form_html, $css_vars );
92 }
93 add_filter( 'wpbc_booking_form__body_html__before_postprocess', 'wpbc_booking_form__body_html__before_postprocess__apply_bfb_vars', 10, 4 );
94
95
96 /**
97 * ['options'][booking_form_theme] -> Apply a "theme" CSS class to the main booking form wrapper container. On Step 3.
98 *
99 * Purpose:
100 * - Add `$bfb_settings['options']['booking_form_theme']` as an extra class to the FIRST wrapper container:
101 * `<div class="... wpbc_container wpbc_form ...">`
102 *
103 * Why here:
104 * - The wrapper container does not exist until `WPBC_FE_Form_Wrapper::wrap()` runs, therefore this must be executed on the wrapped HTML stage.
105 *
106 * Potential limitations:
107 * - This implementation supports a **single** class value. If you plan to support multiple
108 * classes in one setting (space-separated), you should split and sanitize each class.
109 *
110 * @since 11.0.x
111 *
112 * @param string $wrapped_html Fully wrapped booking form HTML (before inline scripts appended).
113 * @param array $bfb_settings Decoded BFB settings array (may be empty).
114 * @param int $resource_id Booking resource ID.
115 * @param string $custom_booking_form_name Booking form slug/name (legacy: "standard").
116 *
117 * @return string Filtered wrapped HTML.
118 */
119 function wpbc_booking_form__wrapped_html__before_inline_scripts__apply_theme_class( $wrapped_html, $bfb_settings, $resource_id, $custom_booking_form_name ) {
120
121 $wrapped_html = (string) $wrapped_html;
122 $bfb_settings = is_array( $bfb_settings ) ? $bfb_settings : array();
123
124 if ( empty( $bfb_settings['options']['booking_form_theme'] ) ) {
125 return $wrapped_html;
126 }
127
128 // Get theme class(es).
129 $raw_theme = trim( (string) $bfb_settings['options']['booking_form_theme'] );
130 $theme_class_arr = array(); // token => true .
131 $parts = preg_split( '/\s+/', $raw_theme, - 1, PREG_SPLIT_NO_EMPTY );
132 foreach ( $parts as $part ) {
133 $tok = sanitize_html_class( (string) $part );
134 if ( '' === $tok ) { continue; }
135 $theme_class_arr[ $tok ] = true;
136 }
137 $theme_class_arr = array_keys( $theme_class_arr );
138 if ( empty( $theme_class_arr ) ) {
139 return $wrapped_html;
140 }
141
142
143 /**
144 * Find the first wrapper container:
145 * <div class="... wpbc_container wpbc_form ...">
146 *
147 * BUG CHECK:
148 * - The pattern is "tempered" to find the class attribute containing both tokens.
149 * - It does not require order (wpbc_container can appear before/after wpbc_form).
150 * - It matches the opening <div ...> tag only.
151 */
152 $pattern = '/<div\b[^>]*\bclass\s*=\s*(["\'])(?:(?!\1).)*\bwpbc_container\b(?:(?!\1).)*\bwpbc_form\b(?:(?!\1).)*\1[^>]*>/i';
153
154 return preg_replace_callback( $pattern, function ( $m ) use ( $theme_class_arr ) {
155
156 $tag = $m[0];
157
158 if ( preg_match( '/\bclass\s*=\s*(["\'])(.*?)\1/i', $tag, $cm ) ) {
159
160 $quote = $cm[1];
161 $classes = (string) $cm[2];
162
163 // Optional but recommended: remove any existing wpbc_theme_* to avoid conflicts.
164 $classes = preg_replace( '/\bwpbc_theme_[A-Za-z0-9_-]+\b/', '', $classes );
165 $classes = trim( preg_replace( '/\s+/', ' ', $classes ) );
166
167 // Add missing theme tokens.
168 foreach ( $theme_class_arr as $tok ) {
169 if ( false === strpos( ' ' . $classes . ' ', ' ' . $tok . ' ' ) ) {
170 $classes = trim( $classes . ' ' . $tok );
171 }
172 }
173
174 $tag = preg_replace( '/\bclass\s*=\s*(["\'])(.*?)\1/i', 'class=' . $quote . esc_attr( $classes ) . $quote, $tag, 1 );
175 }
176
177 return $tag;
178 }, $wrapped_html, 1 );
179
180 }
181 add_filter( 'wpbc_booking_form__wrapped_html__before_inline_scripts', 'wpbc_booking_form__wrapped_html__before_inline_scripts__apply_theme_class', 10, 4 );
182
183
184 /**
185 * ['options']['booking_type_of_day_selections'] -> Set per-form day selection mode for calendar init.
186 *
187 * Uses legacy JS global: bk_days_selection_mode = 'single'|'multiple'
188 *
189 * @param string $wrapped_html
190 * @param array $bfb_settings
191 * @param int $resource_id
192 * @param string $custom_booking_form_name
193 *
194 * @return string
195 */
196 function wpbc_booking_form__wrapped_html__before_inline_scripts__apply_day_selection_mode( $wrapped_html, $bfb_settings, $resource_id, $custom_booking_form_name ) {
197
198 $wrapped_html = (string) $wrapped_html;
199 $bfb_settings = is_array( $bfb_settings ) ? $bfb_settings : array();
200
201 $mode = '';
202 if ( ! empty( $bfb_settings['options'] ) && is_array( $bfb_settings['options'] ) ) {
203 if ( isset( $bfb_settings['options']['booking_type_of_day_selections'] ) ) {
204 $mode = (string) $bfb_settings['options']['booking_type_of_day_selections'];
205 }
206 }
207
208 if ( ( 'single' !== $mode ) && ( 'multiple' !== $mode ) ) {
209 return $wrapped_html;
210 }
211
212 $rid = (int) $resource_id;
213 $fn = ( 'single' === $mode ) ? 'wpbc_cal_ready_days_select__single' : 'wpbc_cal_ready_days_select__multiple';
214
215 $js_body = '(function(){';
216 $js_body .= 'var rid=' . $rid . ';';
217 $js_body .= 'var tries=0;';
218 $js_body .= 'function run(){';
219 $js_body .= ' if (typeof window.' . $fn . ' === "function") { window.' . $fn . '(rid); return; }';
220 $js_body .= ' if (tries++ < 40) { setTimeout(run,50); }';
221 $js_body .= '}';
222 $js_body .= 'run();';
223 $js_body .= '}());';
224
225 $can_use_wp_inline = ( ! wp_doing_ajax() );
226
227 // Elementor safe adding inline scripts after loaded of 'wpbc_all' script.
228 if ( $can_use_wp_inline && class_exists( 'WPBC_FE_Assets' ) && function_exists( 'wp_script_is' ) ) {
229
230 $assets_key = 'wpbc:day-select:' . $rid . ':' . md5( (string) $custom_booking_form_name . ':' . $mode );
231
232 $added = WPBC_FE_Assets::add_jq_ready_js_to_wp_script( 'wpbc_all', $js_body, $assets_key );
233
234 if ( $added ) {
235 return $wrapped_html; // no <script> printed in content.
236 }
237 }
238
239 // Fallback only if WP inline could not be attached.
240 $wrapped_html .= "\n" . '<script type="text/javascript">' . "\n" . $js_body . "\n" . '</script>' . "\n";
241 return $wrapped_html;
242 }
243 add_filter( 'wpbc_booking_form__wrapped_html__before_inline_scripts', 'wpbc_booking_form__wrapped_html__before_inline_scripts__apply_day_selection_mode', 9, 4 );
244