PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 11.8.4 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 All 205 releases
booking / includes / page-form-builder / assets / template-records / bfb-activate-templates.php

bfb-activate-templates.php in Booking Calendar 11.9, at includes/page-form-builder/assets/template-records/bfb-activate-templates.php

577 lines 19.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Booking Form Builder - Bundled templates activation/update helpers.
4 *
5 * Responsibilities:
6 * - Register bundled BFB templates.
7 * - Seed missing template records into wp_booking_form_structures.
8 * - Track per-template seed versions.
9 * - Optionally update bundled template rows in future releases.
10 *
11 * @package Booking Calendar
12 * @author wpdevelop, oplugins
13 * @web-site https://wpbookingcalendar.com/
14 * @email [email protected]
15 *
16 * @since 11.0.x
17 * @file ../includes/page-form-builder/assets/template-records/bfb-activate-templates.php
18 * @modified 2026-03-09
19 */
20
21 if ( ! defined( 'ABSPATH' ) ) {
22 exit; // Exit if accessed directly.
23 }
24
25 /**
26 * Return option key storing bundled templates seed state.
27 *
28 * Stored value example:
29 * array(
30 * 'service_appointments' => '11.0.0',
31 * 'contact_inquiry' => '11.1.0',
32 * )
33 *
34 * @return string
35 */
36 function wpbc_bfb_activation__get_templates_seed_state_option_key() {
37 return 'wpbc_bfb_templates_seed_state';
38 }
39
40 /**
41 * Get stored bundled templates seed state.
42 *
43 * @return array
44 */
45 function wpbc_bfb_activation__get_templates_seed_state() {
46
47 $state = get_option( wpbc_bfb_activation__get_templates_seed_state_option_key(), array() );
48
49 return ( is_array( $state ) ) ? $state : array();
50 }
51
52 /**
53 * Save bundled templates seed state.
54 *
55 * @param array $state Seed state.
56 *
57 * @return void
58 */
59 function wpbc_bfb_activation__save_templates_seed_state( $state ) {
60
61 if ( ! is_array( $state ) ) {
62 $state = array();
63 }
64
65 update_option( wpbc_bfb_activation__get_templates_seed_state_option_key(), $state );
66 }
67
68 /**
69 * Delete bundled templates seed state.
70 *
71 * @return void
72 */
73 function wpbc_bfb_activation__delete_templates_seed_state() {
74 delete_bk_option( wpbc_bfb_activation__get_templates_seed_state_option_key() );
75 }
76
77 /**
78 * Return absolute path to bundled template records directory.
79 *
80 * @return string
81 */
82 function wpbc_bfb_activation__get_templates_dir_path() {
83
84 // return dirname( __FILE__ ); // . '/../assets/template-records'; //.
85
86 return WPBC_PLUGIN_DIR . '/includes/page-form-builder/assets/template-records';
87 }
88
89 /**
90 * Return bundled template registry.
91 *
92 * Each item must return:
93 * array(
94 * 'template_key' => 'service_appointments',
95 * 'seed_version' => '11.0.0',
96 * 'sync_mode' => 'insert_only', // or 'upsert'
97 * 'record' => array( ... DB columns ... ),
98 * )
99 *
100 * Notes:
101 * - template_key must stay stable across releases.
102 * - seed_version is compared against stored seed state for this template.
103 * - sync_mode:
104 * - insert_only = insert once if missing, never overwrite existing row.
105 * - upsert = insert if missing, update existing row when seed_version increases.
106 *
107 * @return array
108 */
109 function wpbc_bfb_activation__get_templates_registry() {
110
111 $templates = array();
112 $template_files = array(
113
114 // Contact Forms.
115 wpbc_bfb_activation__get_templates_dir_path() . '/contact_form_simple.php',
116 wpbc_bfb_activation__get_templates_dir_path() . '/technical_support_form.php',
117 wpbc_bfb_activation__get_templates_dir_path() . '/inquiry_form_simple.php',
118
119 // Advanced Not Synced Forms.
120 wpbc_bfb_activation__get_templates_dir_path() . '/dates_advanced_2_steps_wizard_with_hints.php',
121 wpbc_bfb_activation__get_templates_dir_path() . '/dates_advanced_form_with_hints.php',
122
123 // Time Slots.
124 wpbc_bfb_activation__get_templates_dir_path() . '/time_slots_start_end_times_1_hour_selection.php',
125 wpbc_bfb_activation__get_templates_dir_path() . '/time_slots_start_duration_times_selection.php',
126 wpbc_bfb_activation__get_templates_dir_path() . '/time_slots_30_min_2_steps_wizard.php',
127 wpbc_bfb_activation__get_templates_dir_path() . '/time_slots_20_min_2_steps_wizard.php',
128
129 // Time - Service Appointments.
130 wpbc_bfb_activation__get_templates_dir_path() . '/time_appointments_3_steps_wizard.php',
131 wpbc_bfb_activation__get_templates_dir_path() . '/time_appointments_2_steps_vertical_layout.php',
132 wpbc_bfb_activation__get_templates_dir_path() . '/time_appointments_2_steps_wizard.php',
133
134 // Full Day - Booking Forms.
135 wpbc_bfb_activation__get_templates_dir_path() . '/dates_form_with_3_columns_layout.php',
136 wpbc_bfb_activation__get_templates_dir_path() . '/dates_form_with_calendar_next_to_form.php',
137 wpbc_bfb_activation__get_templates_dir_path() . '/dates_form_with_vertical_layout.php',
138 wpbc_bfb_activation__get_templates_dir_path() . '/dates_form_wizard_2_steps.php',
139
140
141 // Hints
142 wpbc_bfb_activation__get_templates_dir_path() . '/time_slots_20_min_3_steps_review_with_hints.php',
143 wpbc_bfb_activation__get_templates_dir_path() . '/time_appointments_3_steps_review_with_hints.php',
144 wpbc_bfb_activation__get_templates_dir_path() . '/dates_advanced_3_steps_review_with_hints.php',
145 wpbc_bfb_activation__get_templates_dir_path() . '/dates_2_columns_sidebar_hints.php',
146
147 // Example future records:
148 // wpbc_bfb_activation__get_templates_dir_path() . '/template-contact-inquiry.php',
149 // wpbc_bfb_activation__get_templates_dir_path() . '/template-event-registration.php',
150 );
151
152 // Keep private live-demo fixtures out of customer template libraries.
153 if ( function_exists( 'wpbc_is_this_demo' ) && wpbc_is_this_demo() ) {
154 $template_files = array_merge(
155 $template_files,
156 array(
157 wpbc_bfb_activation__get_templates_dir_path() . '/demo_personal.php',
158 wpbc_bfb_activation__get_templates_dir_path() . '/demo_business_small.php',
159 wpbc_bfb_activation__get_templates_dir_path() . '/demo_business_medium.php',
160 wpbc_bfb_activation__get_templates_dir_path() . '/demo_business_large.php',
161 wpbc_bfb_activation__get_templates_dir_path() . '/demo_multiuser.php',
162 wpbc_bfb_activation__get_templates_dir_path() . '/demo_multiuser_owner_1.php',
163 wpbc_bfb_activation__get_templates_dir_path() . '/demo_multiuser_owner_2.php',
164 wpbc_bfb_activation__get_templates_dir_path() . '/demo_multiuser_owner_3.php',
165 )
166 );
167 }
168
169 /*
170 * Template cards are listed newest-first, with the row ID as the final
171 * tie-breaker. Append preferred templates in increasing display priority.
172 * The last bundled template seeded is therefore the first library card after
173 * activation. The insert-only records below never overwrite installed rows.
174 */
175 $template_files[] = wpbc_bfb_activation__get_templates_dir_path() . '/appointments_services_flow.php';
176 $template_files[] = wpbc_bfb_activation__get_templates_dir_path() . '/appointments_services_selection_summary.php';
177 $template_files[] = wpbc_bfb_activation__get_templates_dir_path() . '/dates_form_with_inline_hints.php';
178 $template_files[] = wpbc_bfb_activation__get_templates_dir_path() . '/dates_vertical_hints_full_days.php';
179 $template_files[] = wpbc_bfb_activation__get_templates_dir_path() . '/dates_2_columns_hints_full_days.php';
180 $template_files[] = wpbc_bfb_activation__get_templates_dir_path() . '/time_slots_2_columns_hints.php';
181
182 foreach ( $template_files as $template_file ) {
183 if ( file_exists( $template_file ) ) {
184 $template_config = require $template_file;
185
186 if ( is_array( $template_config ) ) {
187 $templates[] = $template_config;
188 }
189 }
190 }
191
192 /**
193 * Allow third-party code or future modules to register bundled templates.
194 */
195 $templates = apply_filters( 'wpbc_bfb_activation__templates_registry', $templates );
196
197 return $templates;
198 }
199
200 /**
201 * Keep the replacement Appointment template adjacent to its legacy predecessor.
202 *
203 * The shared list endpoint owns the neutral ordering mechanic. This domain
204 * registration supplies only the two stable template slugs and does not change
205 * either insert-only row or its saved timestamp.
206 *
207 * @param array $adjacencies Registered template-library adjacency pairs.
208 *
209 * @return array Filtered adjacency pairs.
210 */
211 function wpbc_bfb_activation__register_template_library_adjacencies( $adjacencies ) {
212
213 if ( ! is_array( $adjacencies ) ) {
214 $adjacencies = array();
215 }
216
217 $adjacencies[] = array(
218 'before' => 'appointments_services_selection_summary',
219 'after' => 'appointments_services_flow',
220 );
221
222 return $adjacencies;
223 }
224 add_filter( 'wpbc_bfb_template_library_adjacencies', 'wpbc_bfb_activation__register_template_library_adjacencies' );
225
226 /**
227 * Add default form appearance settings to bundled template settings_json.
228 *
229 * Individual templates can override any key in their own settings_json; this only fills missing values.
230 *
231 * @param string $settings_json Template settings JSON.
232 *
233 * @return string
234 */
235 function wpbc_bfb_activation__add_default_form_appearance_settings( $settings_json ) {
236
237 $settings = array();
238 if ( is_string( $settings_json ) && '' !== trim( $settings_json ) ) {
239 $decoded = json_decode( (string) $settings_json, true );
240 if ( is_array( $decoded ) ) {
241 $settings = $decoded;
242 }
243 }
244
245 if ( empty( $settings['options'] ) || ! is_array( $settings['options'] ) ) {
246 $settings['options'] = array();
247 }
248
249 if ( function_exists( 'wpbc_bfb_settings__strip_form_style_options_from_form_settings' ) ) {
250 $settings = wpbc_bfb_settings__strip_form_style_options_from_form_settings( $settings );
251 }
252
253 if ( empty( $settings['css_vars'] ) || ! is_array( $settings['css_vars'] ) ) {
254 $settings['css_vars'] = array();
255 }
256
257 if ( empty( $settings['bfb_options'] ) || ! is_array( $settings['bfb_options'] ) ) {
258 $settings['bfb_options'] = array( 'advanced_mode_source' => 'builder' );
259 }
260
261 return function_exists( 'wpbc_form_config__encode_json' )
262 ? wpbc_form_config__encode_json( $settings )
263 : wp_json_encode( $settings );
264 }
265
266 /**
267 * Normalize bundled template config.
268 *
269 * @param array $template_config Raw template config.
270 *
271 * @return array
272 */
273 function wpbc_bfb_activation__normalize_template_config( $template_config ) {
274
275 $template_config = wp_parse_args(
276 $template_config,
277 array(
278 'template_key' => '',
279 'seed_version' => '1.0.0',
280 'sync_mode' => 'insert_only',
281 'record' => array(),
282 )
283 );
284
285 $template_config['template_key'] = sanitize_key( $template_config['template_key'] );
286 $template_config['seed_version'] = (string) $template_config['seed_version'];
287 $template_config['sync_mode'] = ( 'upsert' === $template_config['sync_mode'] ) ? 'upsert' : 'insert_only';
288
289 $template_config['record'] = wp_parse_args(
290 $template_config['record'],
291 array(
292 'form_slug' => '',
293 'status' => 'template',
294 'scope' => 'global',
295 'version' => 1,
296 'booking_resource_id' => null,
297 'owner_user_id' => 0,
298 'engine' => 'bfb',
299 'engine_version' => '1.0',
300 'structure_json' => '',
301 'settings_json' => '',
302 'advanced_form' => '',
303 'content_form' => '',
304 'is_default' => 0,
305 'title' => '',
306 'description' => '',
307 'picture_url' => '',
308 )
309 );
310
311 $template_config['record']['form_slug'] = sanitize_title( $template_config['record']['form_slug'] );
312 $template_config['record']['status'] = sanitize_key( $template_config['record']['status'] );
313 $template_config['record']['scope'] = sanitize_key( $template_config['record']['scope'] );
314 $template_config['record']['version'] = (int) $template_config['record']['version'];
315 $template_config['record']['owner_user_id'] = (int) $template_config['record']['owner_user_id'];
316 $template_config['record']['is_default'] = (int) $template_config['record']['is_default'];
317 $template_config['record']['engine'] = (string) $template_config['record']['engine'];
318 $template_config['record']['engine_version'] = (string) $template_config['record']['engine_version'];
319 $template_config['record']['title'] = (string) $template_config['record']['title'];
320 $template_config['record']['description'] = (string) $template_config['record']['description'];
321 $template_config['record']['picture_url'] = (string) $template_config['record']['picture_url'];
322 $template_config['record']['settings_json'] = wpbc_bfb_activation__add_default_form_appearance_settings( (string) $template_config['record']['settings_json'] );
323
324 if ( null !== $template_config['record']['booking_resource_id'] ) {
325 $template_config['record']['booking_resource_id'] = (int) $template_config['record']['booking_resource_id'];
326 }
327
328 return $template_config;
329 }
330
331 /**
332 * Find existing bundled template row by unique fields.
333 *
334 * @param array $record Template DB record.
335 *
336 * @return int
337 */
338 function wpbc_bfb_activation__find_existing_template_id( $record ) {
339
340 global $wpdb;
341
342 // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared, WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
343 $existing_id = (int) $wpdb->get_var( $wpdb->prepare(
344 "SELECT booking_form_id
345 FROM {$wpdb->prefix}booking_form_structures
346 WHERE form_slug = %s
347 AND status = %s
348 AND owner_user_id = %d
349 LIMIT 1",
350 $record['form_slug'],
351 $record['status'],
352 (int) $record['owner_user_id']
353 ) );
354
355 return $existing_id;
356 }
357
358 /**
359 * Build DB row for insert/update.
360 *
361 * @param array $record Template DB record.
362 *
363 * @return array
364 */
365 function wpbc_bfb_activation__build_template_db_row( $record ) {
366
367 $now = current_time( 'mysql' );
368
369 return array(
370 'form_slug' => $record['form_slug'],
371 'status' => $record['status'],
372 'scope' => $record['scope'],
373 'version' => (int) $record['version'],
374 'booking_resource_id' => $record['booking_resource_id'],
375 'owner_user_id' => (int) $record['owner_user_id'],
376 'engine' => $record['engine'],
377 'engine_version' => $record['engine_version'],
378 'structure_json' => $record['structure_json'],
379 'settings_json' => $record['settings_json'],
380 'advanced_form' => $record['advanced_form'],
381 'content_form' => $record['content_form'],
382 'is_default' => (int) $record['is_default'],
383 'title' => $record['title'],
384 'description' => $record['description'],
385 'picture_url' => $record['picture_url'],
386 'updated_at' => $now,
387 'updated_by' => 0,
388 );
389 }
390
391 /**
392 * Insert bundled template record.
393 *
394 * @param array $record Template DB record.
395 *
396 * @return bool
397 */
398 function wpbc_bfb_activation__insert_template_record( $record ) {
399
400 global $wpdb;
401
402 $table_name = $wpdb->prefix . 'booking_form_structures';
403 $data = wpbc_bfb_activation__build_template_db_row( $record );
404
405 $data['created_at'] = $data['updated_at'];
406 $data['created_by'] = 0;
407 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
408 $inserted = $wpdb->insert( $table_name, $data );
409
410 return ( false !== $inserted );
411 }
412
413 /**
414 * Update bundled template record by row ID.
415 *
416 * @param int $booking_form_id Existing row ID.
417 * @param array $record Template DB record.
418 *
419 * @return bool
420 */
421 function wpbc_bfb_activation__update_template_record( $booking_form_id, $record ) {
422
423 global $wpdb;
424
425 $table_name = $wpdb->prefix . 'booking_form_structures';
426 $booking_form_id = (int) $booking_form_id;
427 $data = wpbc_bfb_activation__build_template_db_row( $record );
428
429 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
430 $updated = $wpdb->update(
431 $table_name,
432 $data,
433 array(
434 'booking_form_id' => $booking_form_id,
435 )
436 );
437
438 return ( false !== $updated );
439 }
440
441 /**
442 * Sync one bundled template.
443 *
444 * @param array $template_config Template config.
445 * @param array $seed_state Current stored seed state.
446 *
447 * @return array
448 */
449 /**
450 * Sync one bundled template.
451 *
452 * @param array $template_config Template config.
453 * @param array $seed_state Current stored seed state.
454 *
455 * @return array
456 */
457 function wpbc_bfb_activation__sync_one_template( $template_config, $seed_state ) {
458
459 $result = array(
460 'ok' => false,
461 'seed_state' => $seed_state,
462 );
463
464 $template_config = wpbc_bfb_activation__normalize_template_config( $template_config );
465
466 if ( empty( $template_config['template_key'] ) ) {
467 return $result;
468 }
469
470 if ( empty( $template_config['record']['form_slug'] ) ) {
471 return $result;
472 }
473
474 $template_key = $template_config['template_key'];
475 $seed_version = $template_config['seed_version'];
476 $sync_mode = $template_config['sync_mode'];
477 $record = $template_config['record'];
478 $stored_version = isset( $seed_state[ $template_key ] ) ? (string) $seed_state[ $template_key ] : '';
479 $existing_row_id = wpbc_bfb_activation__find_existing_template_id( $record );
480
481 // Already synced for this template version and DB row still exists.
482 if (
483 ( $existing_row_id > 0 ) &&
484 ( ! empty( $stored_version ) ) &&
485 version_compare( $stored_version, $seed_version, '>=' )
486 ) {
487 $result['ok'] = true;
488 return $result;
489 }
490
491 // Insert if missing.
492 if ( $existing_row_id <= 0 ) {
493 $result['ok'] = wpbc_bfb_activation__insert_template_record( $record );
494
495 if ( $result['ok'] ) {
496 $result['seed_state'][ $template_key ] = $seed_version;
497 }
498
499 return $result;
500 }
501
502 // Existing row found.
503 if ( 'upsert' === $sync_mode ) {
504 $result['ok'] = wpbc_bfb_activation__update_template_record( $existing_row_id, $record );
505 } else {
506 // insert_only mode: mark as synced, do not overwrite existing row.
507 $result['ok'] = true;
508 }
509
510 if ( $result['ok'] ) {
511 $result['seed_state'][ $template_key ] = $seed_version;
512 }
513
514 return $result;
515 }
516
517 /**
518 * Seed bundled BFB templates.
519 *
520 * Runs both:
521 * - after table creation,
522 * - when table already exists.
523 *
524 * @return void
525 */
526 /**
527 * Seed bundled BFB templates.
528 *
529 * Runs both:
530 * - after table creation,
531 * - when table already exists.
532 *
533 * @return void
534 */
535 function wpbc_bfb_activation__seed_templates() {
536
537 if ( ! wpbc_is_table_exists( 'booking_form_structures' ) ) {
538 return;
539 }
540
541 $templates = wpbc_bfb_activation__get_templates_registry();
542 if ( empty( $templates ) || ! is_array( $templates ) ) {
543 return;
544 }
545
546 $seed_state = wpbc_bfb_activation__get_templates_seed_state();
547 $is_changed = false;
548
549 foreach ( $templates as $template_config ) {
550
551 $seed_state_before = $seed_state;
552
553 $sync_result = wpbc_bfb_activation__sync_one_template( $template_config, $seed_state );
554 $seed_state = $sync_result['seed_state'];
555
556 if ( $seed_state !== $seed_state_before ) {
557 $is_changed = true;
558 }
559 }
560
561 if ( $is_changed ) {
562 wpbc_bfb_activation__save_templates_seed_state( $seed_state );
563 }
564 }
565 add_action( 'wpbc_bfb_activation__form_structures_table__after_create', 'wpbc_bfb_activation__seed_templates' );
566 add_action( 'wpbc_bfb_activation__form_structures_table__table_already_exists', 'wpbc_bfb_activation__seed_templates' );
567
568 /**
569 * Cleanup bundled templates seed state on deactivation.
570 *
571 * @return void
572 */
573 function wpbc_bfb_activation__cleanup_templates_seed_state_on_deactivation() {
574 wpbc_bfb_activation__delete_templates_seed_state();
575 }
576 add_bk_action( 'wpbc_free_version_deactivation', 'wpbc_bfb_activation__cleanup_templates_seed_state_on_deactivation' );
577