| @@ -6,12 +6,9 @@ | ||
| 6 | 6 | */ |
| 7 | 7 | |
| 8 | 8 | namespace SRFM\Inc; |
| 9 | 9 | |
| 10 | -use Spec_Gb_Helper; | |
| 11 | 10 | use SRFM\Inc\Traits\Get_Instance; |
| 12 | -use SRFM\Inc\Smart_Tags; | |
| 13 | -use SRFM\Inc\Helper; | |
| 14 | 11 | |
| 15 | 12 | if ( ! defined( 'ABSPATH' ) ) { |
| 16 | 13 | exit; // Exit if accessed directly. |
| 17 | 14 | } |
| @@ -21,9 +18,9 @@ | ||
| 21 | 18 | * |
| 22 | 19 | * @since 0.0.1 |
| 23 | 20 | */ |
| 24 | 21 | class Gutenberg_Hooks { |
| 25 | - | |
| 22 | + use Get_Instance; | |
| 26 | 23 | /** |
| 27 | 24 | * Block patterns to register. |
| 28 | 25 | * |
| 29 | 26 | * @var array<mixed> |
| @@ -29,10 +26,8 @@ | ||
| 29 | 26 | * @var array<mixed> |
| 30 | 27 | */ |
| 31 | 28 | protected $patterns = []; |
| 32 | 29 | |
| 33 | - use Get_Instance; | |
| 34 | - | |
| 35 | 30 | /** |
| 36 | 31 | * Class constructor. |
| 37 | 32 | * |
| 38 | 33 | * @return void |
| @@ -52,20 +47,20 @@ | ||
| 52 | 47 | |
| 53 | 48 | // Initializing hooks. |
| 54 | 49 | add_action( 'enqueue_block_editor_assets', [ $this, 'form_editor_screen_assets' ] ); |
| 55 | 50 | add_action( 'enqueue_block_editor_assets', [ $this, 'block_editor_assets' ] ); |
| 56 | - add_filter( 'block_categories_all', [ $this, 'register_block_categories' ], 10, 1 ); | |
| 57 | - add_action( 'init', [ $this, 'register_block_patterns' ], 9 ); | |
| 51 | + add_filter( 'block_categories_all', [ $this, 'register_block_categories' ], 10, 2 ); | |
| 58 | 52 | add_filter( 'allowed_block_types_all', [ $this, 'disable_forms_wrapper_block' ], 10, 2 ); |
| 59 | 53 | add_action( 'save_post_sureforms_form', [ $this, 'update_field_slug' ], 10, 2 ); |
| 54 | + add_action( 'load-post.php', [ $this, 'maybe_migrate_form_stylings' ] ); | |
| 60 | 55 | } |
| 61 | 56 | |
| 62 | 57 | /** |
| 63 | 58 | * Disable Sureforms_Form Block and allowed only sureforms block inside Sureform CPT editor. |
| 64 | 59 | * |
| 65 | - * @param bool|string[] $allowed_block_types Array of block types. | |
| 60 | + * @param bool|array<string> $allowed_block_types Array of block types. | |
| 66 | 61 | * @param \WP_Block_Editor_Context $editor_context The current block editor context. |
| 67 | - * @return array<mixed>|void | |
| 62 | + * @return array<mixed>|bool | |
| 68 | 63 | * @since 0.0.1 |
| 69 | 64 | */ |
| 70 | 65 | public function disable_forms_wrapper_block( $allowed_block_types, $editor_context ) { |
| 71 | 66 | if ( ! empty( $editor_context->post->post_type ) && 'sureforms_form' === $editor_context->post->post_type ) { |
| @@ -85,26 +80,31 @@ | ||
| 85 | 80 | 'srfm/icon', |
| 86 | 81 | 'srfm/image', |
| 87 | 82 | 'srfm/advanced-heading', |
| 88 | 83 | 'srfm/inline-button', |
| 84 | + 'srfm/payment', | |
| 89 | 85 | ]; |
| 90 | 86 | // Apply a filter to the $allow_block_types types array. |
| 91 | - $allow_block_types = apply_filters( 'srfm_allowed_block_types', $allow_block_types, $editor_context ); | |
| 92 | - return $allow_block_types; | |
| 87 | + return apply_filters( 'srfm_allowed_block_types', $allow_block_types, $editor_context ); | |
| 93 | 88 | } |
| 89 | + | |
| 90 | + // Return the default $allowed_block_types value. | |
| 91 | + return $allowed_block_types; | |
| 94 | 92 | } |
| 95 | 93 | |
| 96 | 94 | /** |
| 97 | 95 | * Register our custom block category. |
| 98 | 96 | * |
| 99 | - * @param array<mixed> $categories Array of categories. | |
| 97 | + * @param array<mixed> $categories Array of categories. | |
| 98 | + * @param \WP_Block_Editor_Context $block_editor_context The current block editor context. | |
| 100 | 99 | * @return array<mixed> |
| 101 | 100 | * @since 0.0.1 |
| 102 | 101 | */ |
| 103 | - public function register_block_categories( $categories ) { | |
| 104 | - $screen = get_current_screen(); | |
| 102 | + public function register_block_categories( $categories, $block_editor_context ) { | |
| 105 | 103 | |
| 106 | - if ( $screen && SRFM_FORMS_POST_TYPE === $screen->post_type ) { | |
| 104 | + $post_type = $block_editor_context->post->post_type ?? ''; | |
| 105 | + | |
| 106 | + if ( $post_type && SRFM_FORMS_POST_TYPE === $post_type ) { | |
| 107 | 107 | $title = esc_html__( 'General Fields', 'sureforms' ); |
| 108 | 108 | } else { |
| 109 | 109 | $title = esc_html__( 'SureForms', 'sureforms' ); |
| 110 | 110 | } |
| @@ -122,52 +122,9 @@ | ||
| 122 | 122 | |
| 123 | 123 | return array_merge( $custom_categories, $categories ); |
| 124 | 124 | } |
| 125 | 125 | |
| 126 | - | |
| 127 | 126 | /** |
| 128 | - * Register our block patterns. | |
| 129 | - * | |
| 130 | - * @return void | |
| 131 | - * @since 0.0.1 | |
| 132 | - */ | |
| 133 | - public function register_block_patterns() { | |
| 134 | - // Apply filters to the patterns. | |
| 135 | - $this->patterns = apply_filters( 'srfm_block_patterns', $this->patterns ); | |
| 136 | - | |
| 137 | - // Iterate over each block pattern. | |
| 138 | - foreach ( $this->patterns as $block_pattern ) { | |
| 139 | - // Attempt to register block pattern from the main directory. | |
| 140 | - if ( ! $this->register_block_pattern_from_directory( $block_pattern, plugin_dir_path( SRFM_FILE ) . 'templates/forms/' ) ) { | |
| 141 | - // If unsuccessful, attempt to register block pattern from the pro directory. | |
| 142 | - if ( defined( 'SRFM_PRO_VER' ) && defined( 'SRFM_PRO_DIR' ) ) { | |
| 143 | - $this->register_block_pattern_from_directory( $block_pattern, SRFM_PRO_DIR . 'templates/forms/' ); | |
| 144 | - } | |
| 145 | - } | |
| 146 | - } | |
| 147 | - } | |
| 148 | - | |
| 149 | - /** | |
| 150 | - * Register block pattern from the specified directory. | |
| 151 | - * | |
| 152 | - * @param string|mixed $block_pattern The block pattern name. | |
| 153 | - * @param string $directory The directory path. | |
| 154 | - * @since 0.0.2 | |
| 155 | - * @return bool True if the block pattern was registered, false otherwise. | |
| 156 | - */ | |
| 157 | - private function register_block_pattern_from_directory( $block_pattern, $directory ) { | |
| 158 | - $pattern_file = $directory . $block_pattern . '.php'; | |
| 159 | - | |
| 160 | - if ( is_readable( $pattern_file ) ) { | |
| 161 | - register_block_pattern( 'srfm/' . $block_pattern, require $pattern_file ); | |
| 162 | - return true; | |
| 163 | - } | |
| 164 | - | |
| 165 | - return false; | |
| 166 | - } | |
| 167 | - | |
| 168 | - | |
| 169 | - /** | |
| 170 | 127 | * Add Form Editor Scripts. |
| 171 | 128 | * |
| 172 | 129 | * @return void |
| 173 | 130 | * @since 0.0.1 |
| @@ -190,9 +147,21 @@ | ||
| 190 | 147 | 'version' => SRFM_VER, |
| 191 | 148 | ]; |
| 192 | 149 | |
| 193 | 150 | wp_enqueue_script( SRFM_SLUG . $form_editor_script, SRFM_URL . 'assets/build/formEditor.js', $script_info['dependencies'], $script_info['version'], true ); |
| 151 | + wp_localize_script( SRFM_SLUG . $form_editor_script, 'scIcons', [ 'path' => SRFM_URL . 'assets/build/icon-assets' ] ); | |
| 194 | 152 | |
| 153 | + // Deep-link (#3030): the "Finish setting up" Thank You notice CTA opens this | |
| 154 | + // editor with ?srfm_focus=… to auto-open a settings tab. Gutenberg strips | |
| 155 | + // unrecognised query args client-side before the editor bundle can read them, | |
| 156 | + // so surface the value from the server — where it is never stripped — as a | |
| 157 | + // global the bundle reads at evaluation time. Validated to a known allowlist. | |
| 158 | + // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- Read-only deep-link hint on an authenticated editor screen; no state change. | |
| 159 | + $srfm_focus = isset( $_GET['srfm_focus'] ) ? sanitize_key( wp_unslash( $_GET['srfm_focus'] ) ) : ''; | |
| 160 | + if ( in_array( $srfm_focus, [ 'thankyou', 'notifications' ], true ) ) { | |
| 161 | + wp_add_inline_script( SRFM_SLUG . $form_editor_script, 'window.srfmDeepLinkFocus = ' . wp_json_encode( $srfm_focus ) . ';', 'before' ); | |
| 162 | + } | |
| 163 | + | |
| 195 | 164 | // Enqueue the code editor for the Custom CSS Editor in SureForms. |
| 196 | 165 | wp_enqueue_code_editor( [ 'type' => 'text/css' ] ); |
| 197 | 166 | wp_enqueue_script( 'wp-theme-plugin-editor' ); |
| 198 | 167 | wp_enqueue_style( 'wp-codemirror' ); |
| @@ -200,12 +169,15 @@ | ||
| 200 | 169 | wp_localize_script( |
| 201 | 170 | SRFM_SLUG . $form_editor_script, |
| 202 | 171 | SRFM_SLUG . '_block_data', |
| 203 | 172 | [ |
| 204 | - 'plugin_url' => SRFM_URL, | |
| 205 | - 'admin_email' => get_option( 'admin_email' ), | |
| 173 | + 'plugin_url' => SRFM_URL, | |
| 174 | + 'admin_email' => get_option( 'admin_email' ), | |
| 175 | + 'pro_plugin_name' => defined( 'SRFM_PRO_VER' ) && defined( 'SRFM_PRO_PRODUCT' ) ? SRFM_PRO_PRODUCT : 'free', | |
| 206 | 176 | ] |
| 207 | 177 | ); |
| 178 | + | |
| 179 | + Helper::register_script_translations( SRFM_SLUG . $form_editor_script ); | |
| 208 | 180 | } |
| 209 | 181 | |
| 210 | 182 | /** |
| 211 | 183 | * Register all editor scripts. |
| @@ -225,27 +197,43 @@ | ||
| 225 | 197 | 'version' => SRFM_VER, |
| 226 | 198 | ]; |
| 227 | 199 | wp_enqueue_script( SRFM_SLUG . $all_screen_blocks, SRFM_URL . 'assets/build/blocks.js', $blocks_info['dependencies'], SRFM_VER, true ); |
| 228 | 200 | |
| 229 | - $plugin_path = 'sureforms-pro/sureforms-pro.php'; | |
| 201 | + Helper::register_script_translations( SRFM_SLUG . $all_screen_blocks ); | |
| 230 | 202 | |
| 203 | + $site_url = Helper::get_string_value( wp_parse_url( esc_url( get_site_url() ), PHP_URL_HOST ) ); | |
| 204 | + $site_url = preg_replace( '/^www\./', '', $site_url ); | |
| 205 | + | |
| 231 | 206 | wp_localize_script( |
| 232 | 207 | SRFM_SLUG . $all_screen_blocks, |
| 233 | 208 | SRFM_SLUG . '_block_data', |
| 234 | 209 | [ |
| 235 | - 'template_picker_url' => admin_url( '/admin.php?page=add-new-form' ), | |
| 236 | - 'plugin_url' => SRFM_URL, | |
| 237 | - 'admin_email' => get_option( 'admin_email' ), | |
| 238 | - 'post_url' => admin_url( 'post.php' ), | |
| 239 | - 'current_screen' => $screen, | |
| 240 | - 'smart_tags_array' => Smart_Tags::smart_tag_list(), | |
| 241 | - 'smart_tags_array_email' => Smart_Tags::email_smart_tag_list(), | |
| 242 | - 'srfm_form_markup_nonce' => wp_create_nonce( 'srfm_form_markup' ), | |
| 243 | - 'get_form_markup_url' => 'sureforms/v1/generate-form-markup', | |
| 244 | - 'is_pro_active' => defined( 'SRFM_PRO_VER' ), | |
| 245 | - 'get_default_dynamic_block_option' => get_option( 'get_default_dynamic_block_option', Helper::default_dynamic_block_option() ), | |
| 246 | - 'form_selector_nonce' => current_user_can( 'edit_posts' ) ? wp_create_nonce( 'wp_rest' ) : '', | |
| 247 | - 'is_admin_user' => current_user_can( 'manage_options' ), | |
| 210 | + 'template_picker_url' => admin_url( '/admin.php?page=add-new-form' ), | |
| 211 | + 'plugin_url' => SRFM_URL, | |
| 212 | + 'admin_email' => get_option( 'admin_email' ), | |
| 213 | + 'post_url' => admin_url( 'post.php' ), | |
| 214 | + 'current_screen' => $screen, | |
| 215 | + 'smart_tags_array' => Smart_Tags::smart_tag_list(), | |
| 216 | + 'smart_tags_array_email' => Smart_Tags::email_smart_tag_list(), | |
| 217 | + // No srfm_form_markup nonce: the generate-form-markup route is gated by a | |
| 218 | + // capability check, not by holding a nonce. It used to be minted here for | |
| 219 | + // every editor user and read from the query string, which is precisely how | |
| 220 | + // that route ended up with no real authorization (#2995). | |
| 221 | + 'get_form_markup_url' => 'sureforms/v1/generate-form-markup', | |
| 222 | + 'is_pro_active' => Helper::has_pro(), | |
| 223 | + 'srfm_default_dynamic_block_option' => wp_parse_args( Helper::get_array_value( get_option( 'srfm_default_dynamic_block_option', [] ) ), Helper::default_dynamic_block_option() ), | |
| 224 | + 'form_selector_nonce' => Helper::current_user_can( 'edit_posts' ) ? wp_create_nonce( 'wp_rest' ) : '', | |
| 225 | + 'is_admin_user' => Helper::current_user_can(), | |
| 226 | + 'site_url' => $site_url, | |
| 227 | + 'is_suremails_active' => is_plugin_active( 'suremails/suremails.php' ), | |
| 228 | + 'upgrade_url' => add_query_arg( | |
| 229 | + [ 'utm_medium' => 'embed_styling_upgrade' ], | |
| 230 | + Helper::get_sureforms_website_url( 'pricing' ) | |
| 231 | + ), | |
| 232 | + 'default_translations' => [ | |
| 233 | + 'gdpr_label' => __( 'I consent to have this website store my submitted information so they can respond to my inquiry.', 'sureforms' ), | |
| 234 | + 'dropdown_placeholder' => __( 'Select an option', 'sureforms' ), | |
| 235 | + ], | |
| 248 | 236 | ] |
| 249 | 237 | ); |
| 250 | 238 | |
| 251 | 239 | // Localizing the field preview image links. |
| @@ -265,8 +253,9 @@ | ||
| 265 | 253 | 'phone_preview' => SRFM_URL . 'images/field-previews/phone.svg', |
| 266 | 254 | 'dropdown_preview' => SRFM_URL . 'images/field-previews/dropdown.svg', |
| 267 | 255 | 'address_preview' => SRFM_URL . 'images/field-previews/address.svg', |
| 268 | 256 | 'sureforms_preview' => SRFM_URL . 'images/field-previews/sureforms.svg', |
| 257 | + 'payment_preview' => SRFM_URL . 'images/field-previews/payment.svg', | |
| 269 | 258 | ] |
| 270 | 259 | ) |
| 271 | 260 | ); |
| 272 | 261 | |
| @@ -305,21 +294,67 @@ | ||
| 305 | 294 | * used to maintain uniqueness of slugs. |
| 306 | 295 | */ |
| 307 | 296 | $slugs = []; |
| 308 | 297 | |
| 309 | - list( $blocks, $slugs, $updated ) = Helper::process_blocks( $blocks, $slugs, $updated ); | |
| 298 | + [ $blocks, $slugs, $updated ] = Helper::process_blocks( $blocks, $slugs, $updated ); | |
| 310 | 299 | |
| 300 | + // Process and store block configurations for form fields. | |
| 301 | + Field_Validation::add_block_config( $blocks, $post_id ); | |
| 302 | + | |
| 311 | 303 | if ( ! $updated ) { |
| 312 | 304 | return; |
| 313 | 305 | } |
| 314 | 306 | |
| 315 | - $post_content = addslashes( serialize_blocks( $blocks ) ); | |
| 307 | + $post_content = serialize_blocks( $blocks ); | |
| 316 | 308 | |
| 309 | + // Use wp_slash() to preserve unicode escapes (like \u003c for <) in block attributes. | |
| 310 | + // Without this, wp_update_post() calls wp_unslash() which corrupts these escapes. | |
| 317 | 311 | wp_update_post( |
| 318 | 312 | [ |
| 319 | 313 | 'ID' => $post_id, |
| 320 | - 'post_content' => $post_content, | |
| 314 | + 'post_content' => wp_slash( $post_content ), | |
| 321 | 315 | ] |
| 322 | 316 | ); |
| 323 | 317 | } |
| 324 | 318 | |
| 319 | + /** | |
| 320 | + * Migrate the background type and associated values from | |
| 321 | + * instant form settings to form styling meta. | |
| 322 | + * | |
| 323 | + * @since 1.4.4 | |
| 324 | + * @return void | |
| 325 | + */ | |
| 326 | + public function maybe_migrate_form_stylings() { | |
| 327 | + $post_id = isset( $_GET['post'] ) ? Helper::get_integer_value( sanitize_text_field( wp_unslash( $_GET['post'] ) ) ) : ''; // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- $_GET['post'] does not provide nonce. | |
| 328 | + if ( empty( $post_id ) || ! Helper::current_user_can() ) { | |
| 329 | + return; | |
| 330 | + } | |
| 331 | + | |
| 332 | + $post_type = get_post_type( $post_id ); | |
| 333 | + if ( SRFM_FORMS_POST_TYPE !== $post_type ) { | |
| 334 | + return; | |
| 335 | + } | |
| 336 | + | |
| 337 | + $instant_form_settings = get_post_meta( $post_id, '_srfm_instant_form_settings', true ); | |
| 338 | + if ( ! is_array( $instant_form_settings ) ) { | |
| 339 | + return; | |
| 340 | + } | |
| 341 | + | |
| 342 | + $form_styling = get_post_meta( $post_id, '_srfm_forms_styling', true ); | |
| 343 | + if ( ! is_array( $form_styling ) ) { | |
| 344 | + $form_styling = []; | |
| 345 | + } | |
| 346 | + | |
| 347 | + $migrated = false; | |
| 348 | + $keys = [ 'bg_type', 'bg_color', 'bg_image', 'bg_image_id' ]; | |
| 349 | + | |
| 350 | + foreach ( $keys as $key ) { | |
| 351 | + if ( ! isset( $form_styling[ $key ] ) && isset( $instant_form_settings[ $key ] ) ) { | |
| 352 | + $form_styling[ $key ] = $instant_form_settings[ $key ]; | |
| 353 | + $migrated = true; | |
| 354 | + } | |
| 355 | + } | |
| 356 | + if ( $migrated ) { | |
| 357 | + update_post_meta( $post_id, '_srfm_forms_styling', $form_styling ); | |
| 358 | + } | |
| 359 | + } | |
| 325 | 360 | } |