← All changes
|
includes/page-form-builder/field-packs/custom-shortcode/field-custom-shortcode.php
+116
-9
11.8.2
→
11.9
View file →
| @@ -1,12 +1,12 @@ | ||
| 1 | 1 | <?php |
| 2 | 2 | /** |
| 3 | 3 | * WPBC BFB Pack: Custom Shortcode. |
| 4 | 4 | * |
| 5 | - * Adds one literal, bare shortcode token to the Advanced Booking Form. This is | |
| 6 | - * intentionally domain-neutral: paid editions may later resolve a token such | |
| 7 | - * as `[airport_transfer_hint]`, while Form Builder only stores and exports the | |
| 8 | - * authorized form author's exact token. | |
| 5 | + * Adds one validated shortcode token to the Advanced Booking Form. The token | |
| 6 | + * may include Booking Calendar options and quoted values, such as | |
| 7 | + * `[coupon discount ""]`, while the field pack remains independent of the | |
| 8 | + * runtime service that interprets the selected shortcode. | |
| 9 | 9 | * |
| 10 | 10 | * @package Booking Calendar |
| 11 | 11 | * @since 11.8.2 |
| 12 | 12 | */ |
| @@ -15,12 +15,119 @@ | ||
| 15 | 15 | exit; |
| 16 | 16 | } |
| 17 | 17 | |
| 18 | 18 | /** |
| 19 | + * Normalize one Custom Shortcode value for persistence. | |
| 20 | + * | |
| 21 | + * This mirrors the browser-side syntax boundary so a direct or modified save | |
| 22 | + * request cannot persist nested tokens, HTML delimiters, control characters, | |
| 23 | + * unbalanced quotes, or an excessively long shortcode through this field pack. | |
| 24 | + * Runtime shortcode support remains the responsibility of the public form | |
| 25 | + * parser and edition-specific token producers. | |
| 26 | + * | |
| 27 | + * @since 11.8.4 | |
| 28 | + * | |
| 29 | + * @param mixed $shortcode_value Candidate Custom Shortcode value. | |
| 30 | + * | |
| 31 | + * @return string Normalized shortcode, or an empty string when invalid. | |
| 32 | + */ | |
| 33 | +function wpbc_bfb_normalize_custom_shortcode( $shortcode_value ) { | |
| 34 | + $shortcode = trim( (string) $shortcode_value ); | |
| 35 | + | |
| 36 | + if ( strlen( $shortcode ) > 4000 ) { | |
| 37 | + return ''; | |
| 38 | + } | |
| 39 | + | |
| 40 | + $shortcode_characters = array(); | |
| 41 | + $shortcode_length = preg_match_all( '/./us', $shortcode, $shortcode_characters ); | |
| 42 | + | |
| 43 | + if ( | |
| 44 | + false === $shortcode_length || | |
| 45 | + $shortcode_length < 3 || | |
| 46 | + $shortcode_length > 1000 || | |
| 47 | + '[' !== substr( $shortcode, 0, 1 ) || | |
| 48 | + ']' !== substr( $shortcode, -1 ) | |
| 49 | + ) { | |
| 50 | + return ''; | |
| 51 | + } | |
| 52 | + | |
| 53 | + $shortcode_body = trim( substr( $shortcode, 1, -1 ) ); | |
| 54 | + | |
| 55 | + if ( '' === $shortcode_body || preg_match( '/[\x00-\x1F\x7F<>\[\]]/', $shortcode_body ) ) { | |
| 56 | + return ''; | |
| 57 | + } | |
| 58 | + | |
| 59 | + $shortcode_name_end = strpos( $shortcode_body, ' ' ); | |
| 60 | + $shortcode_name = false === $shortcode_name_end | |
| 61 | + ? $shortcode_body | |
| 62 | + : substr( $shortcode_body, 0, $shortcode_name_end ); | |
| 63 | + | |
| 64 | + if ( ! preg_match( '/^[A-Za-z0-9_.*-]+$/D', $shortcode_name ) ) { | |
| 65 | + return ''; | |
| 66 | + } | |
| 67 | + | |
| 68 | + $active_quote = ''; | |
| 69 | + $body_length = strlen( $shortcode_body ); | |
| 70 | + | |
| 71 | + for ( $index = strlen( $shortcode_name ); $index < $body_length; $index++ ) { | |
| 72 | + $character = $shortcode_body[ $index ]; | |
| 73 | + | |
| 74 | + if ( '' !== $active_quote ) { | |
| 75 | + if ( $character === $active_quote ) { | |
| 76 | + $active_quote = ''; | |
| 77 | + } | |
| 78 | + continue; | |
| 79 | + } | |
| 80 | + | |
| 81 | + if ( '"' === $character || "'" === $character ) { | |
| 82 | + $active_quote = $character; | |
| 83 | + } | |
| 84 | + } | |
| 85 | + | |
| 86 | + return '' === $active_quote ? '[' . $shortcode_body . ']' : ''; | |
| 87 | +} | |
| 88 | + | |
| 89 | +/** | |
| 90 | + * Sanitize Custom Shortcode values inside a Form Builder structure. | |
| 91 | + * | |
| 92 | + * The recursive traversal understands only the standard field-node envelope | |
| 93 | + * and leaves every unrelated field, section, page, and unknown extension value | |
| 94 | + * untouched. | |
| 95 | + * | |
| 96 | + * @since 11.8.4 | |
| 97 | + * | |
| 98 | + * @param array $structure Form Builder structure about to be persisted. | |
| 99 | + * | |
| 100 | + * @return array Structure with Custom Shortcode values normalized. | |
| 101 | + */ | |
| 102 | +function wpbc_bfb_sanitize_structure__custom_shortcode( $structure ) { | |
| 103 | + if ( | |
| 104 | + 'field' === ( isset( $structure['type'] ) ? $structure['type'] : '' ) && | |
| 105 | + isset( $structure['data'] ) && | |
| 106 | + is_array( $structure['data'] ) && | |
| 107 | + 'custom_shortcode' === ( isset( $structure['data']['type'] ) ? $structure['data']['type'] : '' ) | |
| 108 | + ) { | |
| 109 | + $structure['data']['shortcode'] = wpbc_bfb_normalize_custom_shortcode( | |
| 110 | + isset( $structure['data']['shortcode'] ) ? $structure['data']['shortcode'] : '' | |
| 111 | + ); | |
| 112 | + } | |
| 113 | + | |
| 114 | + foreach ( $structure as $structure_key => $structure_value ) { | |
| 115 | + if ( is_array( $structure_value ) ) { | |
| 116 | + $structure[ $structure_key ] = wpbc_bfb_sanitize_structure__custom_shortcode( $structure_value ); | |
| 117 | + } | |
| 118 | + } | |
| 119 | + | |
| 120 | + return $structure; | |
| 121 | +} | |
| 122 | +add_filter( 'wpbc_bfb_sanitize_structure_before_save', 'wpbc_bfb_sanitize_structure__custom_shortcode', 10, 1 ); | |
| 123 | + | |
| 124 | +/** | |
| 19 | 125 | * Register the Custom Shortcode field schema and Inspector control. |
| 20 | 126 | * |
| 21 | - * The field stores one executable-free string. The browser pack validates that | |
| 22 | - * it is one bare bracketed token before exporting it to the Advanced form. | |
| 127 | + * The field stores one string. The browser pack applies a bounded allow-list | |
| 128 | + * before exporting it, and the existing save endpoint applies WordPress KSES | |
| 129 | + * to the complete generated Advanced Booking Form. | |
| 23 | 130 | * |
| 24 | 131 | * @param array $packs Registered Builder field packs. |
| 25 | 132 | * |
| 26 | 133 | * @return array Updated field packs. |
| @@ -41,9 +148,9 @@ | ||
| 41 | 148 | ), |
| 42 | 149 | ), |
| 43 | 150 | 'inspector_ui' => array( |
| 44 | 151 | 'title' => __( 'Custom Shortcode', 'booking' ), |
| 45 | - 'description' => __( 'Add one supported shortcode to the booking form. For a Form Options Cost hint, use its form field name followed by _hint.', 'booking' ), | |
| 152 | + 'description' => __( 'Add one supported Booking Calendar shortcode. Options and quoted values are allowed, for example [coupon discount ""]. For a Form Options Cost hint, use its field name followed by _hint.', 'booking' ), | |
| 46 | 153 | 'header_variant' => 'toolbar', |
| 47 | 154 | 'header_actions' => array( 'deselect', 'scrollto', 'move-up', 'move-down', 'duplicate', 'delete' ), |
| 48 | 155 | 'groups' => array( |
| 49 | 156 | array( |
| @@ -90,9 +197,9 @@ | ||
| 90 | 197 | 'wpbc-bfb_field_custom_shortcode', |
| 91 | 198 | 'WPBC_BFB_Custom_Shortcode_Boot', |
| 92 | 199 | array( |
| 93 | 200 | 'example_shortcode' => '[field_name_hint]', |
| 94 | - 'invalid_message' => __( 'Enter one shortcode in square brackets, for example [field_name_hint].', 'booking' ), | |
| 201 | + 'invalid_message' => __( 'Enter one complete shortcode, for example [field_name_hint] or [coupon discount ""].', 'booking' ), | |
| 95 | 202 | ) |
| 96 | 203 | ); |
| 97 | 204 | } |
| 98 | 205 | add_action( 'wpbc_enqueue_js_field_pack', 'wpbc_bfb_enqueue__custom_shortcode_js', 10, 1 ); |
| @@ -100,9 +207,9 @@ | ||
| 100 | 207 | /** |
| 101 | 208 | * Add Custom Shortcode to the Cost Hints palette group. |
| 102 | 209 | * |
| 103 | 210 | * The palette placement reflects the primary Form Options Costs use case, but |
| 104 | - * the field itself remains a generic literal-token exporter. | |
| 211 | + * the field itself remains a generic single-token exporter. | |
| 105 | 212 | * |
| 106 | 213 | * @param string $group Palette group. |
| 107 | 214 | * @param string $position Position inside the group. |
| 108 | 215 | * |