PluginProbe
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz / 2.12.7
SureForms – Contact Form Builder, AI Forms, Payment Form, Survey & Quiz v2.12.7
2.12.7 2.12.6 2.12.5 2.12.4 2.12.3 2.12.2 2.12.1 2.12.0 2.11.1 2.11.0 2.10.1 2.10.0 2.9.1 2.9.0 2.8.2 2.8.1 2.7.0 2.7.1 2.8.0 trunk 0.0.10 0.0.11 0.0.12 0.0.13 0.0.2 All 97 releases
← All changes | inc/post-types.php +166 -38 2.7.12.12.7 View file →
@@ -35,9 +35,8 @@
35 35 add_action( 'init', [ $this, 'register_post_types' ] );
36 36 add_action( 'init', [ $this, 'register_post_metas' ] );
37 37 add_shortcode( 'sureforms', [ $this, 'forms_shortcode' ] );
38 38 add_action( 'manage_posts_extra_tablenav', [ $this, 'maybe_render_blank_form_state' ] );
39 - add_action( 'admin_bar_menu', [ $this, 'remove_admin_bar_menu_item' ], 80, 1 );
40 39 add_action( 'template_redirect', [ $this, 'srfm_instant_form_redirect' ] );
41 40 add_action( 'template_redirect', [ $this, 'disable_sureforms_archive_page' ], 9 );
42 41 add_action( 'load-edit.php', [ $this, 'redirect_forms_listing_page' ] );
43 42
@@ -42,8 +41,9 @@
42 41 add_action( 'load-edit.php', [ $this, 'redirect_forms_listing_page' ] );
43 42
44 43 add_filter( 'rest_prepare_sureforms_form', [ $this, 'sureforms_normalize_meta_for_rest' ], 10, 2 );
45 44 add_action( 'admin_bar_menu', [ $this, 'add_edit_form_to_admin_bar_menu' ], 100 );
45 + add_action( 'admin_bar_menu', [ $this, 'add_new_form_to_admin_bar_menu' ], 100 );
46 46 }
47 47
48 48 /**
49 49 * Redirect the forms listing page to the updated forms page.
@@ -103,8 +103,47 @@
103 103 );
104 104 }
105 105
106 106 /**
107 + * Add a "Form" shortcut to the admin bar "+ New" menu (#3026).
108 + *
109 + * Mirrors how core post types appear under "+ New", but added manually rather
110 + * than via `show_in_admin_bar` so it does not also register a second front-end
111 + * "Edit" node alongside the custom one in add_edit_form_to_admin_bar_menu().
112 + * Gated on the form CPT's own create capability (manage_options for this CPT),
113 + * so it only shows for users who can actually create a form.
114 + *
115 + * @param WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance.
116 + * @since 2.12.4
117 + * @return void
118 + */
119 + public function add_new_form_to_admin_bar_menu( $wp_admin_bar ) {
120 + if ( ! is_admin_bar_showing() || ! $wp_admin_bar instanceof WP_Admin_Bar ) {
121 + return;
122 + }
123 +
124 + $post_type = get_post_type_object( SRFM_FORMS_POST_TYPE );
125 +
126 + if ( ! $post_type || empty( $post_type->cap->create_posts ) || ! current_user_can( $post_type->cap->create_posts ) ) {
127 + return;
128 + }
129 +
130 + // Core registers the "+ New" (new-content) group at priority 70, so running at
131 + // 100 places this node inside it. If core skipped the group (the user can create
132 + // nothing else), WP_Admin_Bar::_bind() drops this orphan node silently.
133 + // name_admin_bar is the label core uses for "+ New" children; escaped because
134 + // WP_Admin_Bar echoes node titles unescaped.
135 + $wp_admin_bar->add_node(
136 + [
137 + 'id' => 'new-' . SRFM_FORMS_POST_TYPE,
138 + 'parent' => 'new-content',
139 + 'title' => esc_html( $post_type->labels->name_admin_bar ),
140 + 'href' => esc_url( admin_url( 'post-new.php?post_type=' . SRFM_FORMS_POST_TYPE ) ),
141 + ]
142 + );
143 + }
144 +
145 + /**
107 146 * Remove this method in the future once _srfm_form_confirmation meta is updated.
108 147 * Normalize the _srfm_form_confirmation meta before it's sent to the REST API.
109 148 * Ensures the meta data is type-safe and includes necessary defaults like `hide_copy`.
110 149 *
@@ -114,22 +153,43 @@
114 153 * @return WP_REST_Response Modified REST response with normalized meta.
115 154 * @since 1.7.3
116 155 */
117 156 public function sureforms_normalize_meta_for_rest( $response, $post ) {
118 - $meta_raw = get_post_meta( $post->ID, '_srfm_form_confirmation', true );
119 - $form_confirmation = maybe_unserialize( is_string( $meta_raw ) ? $meta_raw : '' );
157 + $meta_raw = get_post_meta( $post->ID, '_srfm_form_confirmation', true );
158 + // Meta may be a PHP array (new forms stored via update_post_meta with an array)
159 + // or a serialized/JSON string (legacy forms). Handle both.
160 + $form_confirmation = is_array( $meta_raw ) ? $meta_raw : maybe_unserialize( is_string( $meta_raw ) ? $meta_raw : '' );
120 161
121 162 if ( ! is_array( $form_confirmation ) ) {
122 163 return $response;
123 164 }
124 165
166 + // Only normalize keys that extensions (e.g. SureForms Pro) have actually
167 + // declared in the REST schema; otherwise REST PUT validation rejects them
168 + // with "<key> is not a valid property of Object.".
169 + $registered = get_registered_meta_keys( 'post', SRFM_FORMS_POST_TYPE );
170 + $item_properties = $registered['_srfm_form_confirmation']['show_in_rest']['schema']['items']['properties'] ?? [];
171 +
125 172 foreach ( $form_confirmation as $index => $item ) {
126 173 if ( ! is_array( $item ) ) {
127 174 continue;
128 175 }
129 176
130 - $form_confirmation[ $index ]['hide_copy'] = ! empty( $item['hide_copy'] );
131 - $form_confirmation[ $index ]['hide_download_all'] = ! empty( $item['hide_download_all'] );
177 + if ( isset( $item_properties['hide_copy'] ) ) {
178 + $form_confirmation[ $index ]['hide_copy'] = ! empty( $item['hide_copy'] );
179 + }
180 + if ( isset( $item_properties['hide_download_all'] ) ) {
181 + $form_confirmation[ $index ]['hide_download_all'] = ! empty( $item['hide_download_all'] );
182 + }
183 +
184 + // DOMDocument::saveHTML() strips the "data:" prefix from data URIs in src attributes.
185 + // Restore it so the editor displays SVG images correctly.
186 + if ( isset( $item['message'] ) && is_string( $item['message'] ) && false !== strpos( $item['message'], 'src="image/svg+xml;base64' ) ) {
187 + $normalized = preg_replace( '/src="image\/svg\+xml;base64/', 'src="data:image/svg+xml;base64', $item['message'] );
188 + if ( is_string( $normalized ) ) {
189 + $form_confirmation[ $index ]['message'] = $normalized;
190 + }
191 + }
132 192 }
133 193
134 194 $response_data = $response->get_data();
135 195 if ( is_array( $response_data ) ) {
@@ -271,20 +331,8 @@
271 331 }
272 332 }
273 333
274 334 /**
275 - * Remove add new form menu item.
276 - *
277 - * @param WP_Admin_Bar $wp_admin_bar WP_Admin_Bar instance.
278 - *
279 - * @return void
280 - * @since 0.0.1
281 - */
282 - public function remove_admin_bar_menu_item( $wp_admin_bar ) {
283 - $wp_admin_bar->remove_node( 'new-sureforms_form' );
284 - }
285 -
286 - /**
287 335 * Show blank slate styles.
288 336 *
289 337 * @return void
290 338 * @since 0.0.1
@@ -399,8 +447,13 @@
399 447 '_srfm_is_ai_generated' => 'boolean',
400 448 ]
401 449 );
402 450
451 + // NOTE: `_srfm_form_views` is intentionally NOT registered here. It is a
452 + // server-side counter written only by Form_Views (get/add_post_meta + atomic
453 + // SQL). Exposing it to the block editor via show_in_rest let a form save /
454 + // autosave round-trip a stale value and clobber the live count back to 0.
455 +
403 456 // Form Custom CSS meta.
404 457 register_post_meta(
405 458 'sureforms_form',
406 459 '_srfm_form_custom_css',
@@ -460,14 +513,35 @@
460 513 register_post_meta(
461 514 SRFM_FORMS_POST_TYPE,
462 515 '_srfm_instant_form_settings',
463 516 [
464 - 'single' => true,
465 - 'type' => 'object',
466 - 'auth_callback' => static function() {
517 + 'single' => true,
518 + 'type' => 'object',
519 + 'auth_callback' => static function() {
467 520 return Helper::current_user_can();
468 521 },
469 - 'show_in_rest' => [
522 + 'sanitize_callback' => static function( $meta_value ) {
523 + if ( ! is_array( $meta_value ) ) {
524 + return [];
525 + }
526 + return [
527 + 'site_logo' => isset( $meta_value['site_logo'] ) ? esc_url_raw( $meta_value['site_logo'] ) : '',
528 + 'site_logo_id' => isset( $meta_value['site_logo_id'] ) ? absint( $meta_value['site_logo_id'] ) : 0,
529 + 'cover_type' => isset( $meta_value['cover_type'] ) ? sanitize_text_field( $meta_value['cover_type'] ) : '',
530 + 'cover_color' => isset( $meta_value['cover_color'] ) ? sanitize_text_field( $meta_value['cover_color'] ) : '',
531 + 'cover_image' => isset( $meta_value['cover_image'] ) ? esc_url_raw( $meta_value['cover_image'] ) : '',
532 + 'cover_image_id' => isset( $meta_value['cover_image_id'] ) ? absint( $meta_value['cover_image_id'] ) : 0,
533 + 'bg_type' => isset( $meta_value['bg_type'] ) ? sanitize_text_field( $meta_value['bg_type'] ) : '',
534 + 'bg_color' => isset( $meta_value['bg_color'] ) ? sanitize_text_field( $meta_value['bg_color'] ) : '',
535 + 'bg_image' => isset( $meta_value['bg_image'] ) ? esc_url_raw( $meta_value['bg_image'] ) : '',
536 + 'bg_image_id' => isset( $meta_value['bg_image_id'] ) ? absint( $meta_value['bg_image_id'] ) : 0,
537 + 'enable_instant_form' => isset( $meta_value['enable_instant_form'] ) ? filter_var( $meta_value['enable_instant_form'], FILTER_VALIDATE_BOOLEAN ) : false,
538 + 'form_container_width' => isset( $meta_value['form_container_width'] ) ? absint( $meta_value['form_container_width'] ) : 620,
539 + 'single_page_form_title' => isset( $meta_value['single_page_form_title'] ) ? filter_var( $meta_value['single_page_form_title'], FILTER_VALIDATE_BOOLEAN ) : true,
540 + 'use_banner_as_page_background' => isset( $meta_value['use_banner_as_page_background'] ) ? filter_var( $meta_value['use_banner_as_page_background'], FILTER_VALIDATE_BOOLEAN ) : false,
541 + ];
542 + },
543 + 'show_in_rest' => [
470 544 'schema' => [
471 545 'type' => 'object',
472 546 'context' => [ 'edit' ],
473 547 'properties' => [
@@ -517,9 +591,9 @@
517 591 ],
518 592 ],
519 593 ],
520 594 ],
521 - 'default' => [
595 + 'default' => [
522 596 'bg_type' => 'color',
523 597 'bg_color' => '#ffffff',
524 598 'bg_image' => '',
525 599 'site_logo' => '',
@@ -537,14 +611,17 @@
537 611 register_post_meta(
538 612 SRFM_FORMS_POST_TYPE,
539 613 '_srfm_forms_styling',
540 614 [
541 - 'single' => true,
542 - 'type' => 'object',
543 - 'auth_callback' => static function() {
615 + 'single' => true,
616 + 'type' => 'object',
617 + 'auth_callback' => static function() {
544 618 return Helper::current_user_can();
545 619 },
546 - 'show_in_rest' => [
620 + 'sanitize_callback' => static function( $meta_value ) {
621 + return Helper::sanitize_by_type( $meta_value );
622 + },
623 + 'show_in_rest' => [
547 624 'schema' => [
548 625 'type' => 'object',
549 626 'context' => [ 'edit' ],
550 627 'properties' => [
@@ -777,12 +854,16 @@
777 854 ],
778 855 'instant_form_border_radius_link' => [
779 856 'type' => 'boolean',
780 857 ],
858 + // Disable default SureForms styling.
859 + 'disable_default_styles' => [
860 + 'type' => 'boolean',
861 + ],
781 862 ],
782 863 ],
783 864 ],
784 - 'default' => [
865 + 'default' => [
785 866 'primary_color' => '#111C44',
786 867 'text_color' => '#1E1E1E',
787 868 'text_color_on_primary' => '#FFFFFF',
788 869 'field_spacing' => 'medium',
@@ -857,8 +938,10 @@
857 938 'instant_form_border_radius_bottom' => 12,
858 939 'instant_form_border_radius_left' => 12,
859 940 'instant_form_border_radius_unit' => 'px',
860 941 'instant_form_border_radius_link' => true,
942 + // Disable default SureForms styling.
943 + 'disable_default_styles' => false,
861 944 ],
862 945 ]
863 946 );
864 947
@@ -866,14 +949,40 @@
866 949 register_post_meta(
867 950 'sureforms_form',
868 951 '_srfm_email_notification',
869 952 [
870 - 'single' => true,
871 - 'type' => 'array',
872 - 'auth_callback' => static function() {
953 + 'single' => true,
954 + 'type' => 'array',
955 + 'auth_callback' => static function() {
873 956 return Helper::current_user_can();
874 957 },
875 - 'show_in_rest' => [
958 + 'sanitize_callback' => static function( $meta_value ) {
959 + if ( ! is_array( $meta_value ) ) {
960 + return [];
961 + }
962 + $sanitized = [];
963 + foreach ( $meta_value as $item ) {
964 + if ( ! is_array( $item ) ) {
965 + continue;
966 + }
967 + $sanitized[] = [
968 + 'id' => isset( $item['id'] ) ? intval( $item['id'] ) : 0,
969 + 'status' => isset( $item['status'] ) ? filter_var( $item['status'], FILTER_VALIDATE_BOOLEAN ) : false,
970 + 'is_raw_format' => isset( $item['is_raw_format'] ) ? filter_var( $item['is_raw_format'], FILTER_VALIDATE_BOOLEAN ) : false,
971 + 'name' => isset( $item['name'] ) ? sanitize_text_field( $item['name'] ) : '',
972 + 'email_to' => isset( $item['email_to'] ) ? sanitize_text_field( $item['email_to'] ) : '',
973 + 'email_reply_to' => isset( $item['email_reply_to'] ) ? sanitize_text_field( $item['email_reply_to'] ) : '',
974 + 'from_name' => isset( $item['from_name'] ) ? sanitize_text_field( $item['from_name'] ) : '',
975 + 'from_email' => isset( $item['from_email'] ) ? sanitize_text_field( $item['from_email'] ) : '',
976 + 'email_cc' => isset( $item['email_cc'] ) ? sanitize_text_field( $item['email_cc'] ) : '',
977 + 'email_bcc' => isset( $item['email_bcc'] ) ? sanitize_text_field( $item['email_bcc'] ) : '',
978 + 'subject' => isset( $item['subject'] ) ? sanitize_text_field( $item['subject'] ) : '',
979 + 'email_body' => isset( $item['email_body'] ) ? wp_kses_post( $item['email_body'] ) : '',
980 + ];
981 + }
982 + return $sanitized;
983 + },
984 + 'show_in_rest' => [
876 985 'schema' => [
877 986 'type' => 'array',
878 987 'context' => [ 'edit' ],
879 988 'items' => [
@@ -918,9 +1027,9 @@
918 1027 ],
919 1028 ],
920 1029 ],
921 1030 ],
922 - 'default' => [
1031 + 'default' => [
923 1032 [
924 1033 'id' => 1,
925 1034 'status' => true,
926 1035 'is_raw_format' => false,
@@ -942,14 +1051,33 @@
942 1051 register_post_meta(
943 1052 'sureforms_form',
944 1053 '_srfm_compliance',
945 1054 [
946 - 'single' => true,
947 - 'type' => 'array',
948 - 'auth_callback' => static function() {
1055 + 'single' => true,
1056 + 'type' => 'array',
1057 + 'auth_callback' => static function() {
949 1058 return Helper::current_user_can();
950 1059 },
951 - 'show_in_rest' => [
1060 + 'sanitize_callback' => static function( $meta_value ) {
1061 + if ( ! is_array( $meta_value ) ) {
1062 + return [];
1063 + }
1064 + $sanitized = [];
1065 + foreach ( $meta_value as $item ) {
1066 + if ( ! is_array( $item ) ) {
1067 + continue;
1068 + }
1069 + $sanitized[] = [
1070 + 'id' => isset( $item['id'] ) ? sanitize_text_field( $item['id'] ) : '',
1071 + 'gdpr' => isset( $item['gdpr'] ) ? filter_var( $item['gdpr'], FILTER_VALIDATE_BOOLEAN ) : false,
1072 + 'do_not_store_entries' => isset( $item['do_not_store_entries'] ) ? filter_var( $item['do_not_store_entries'], FILTER_VALIDATE_BOOLEAN ) : false,
1073 + 'auto_delete_entries' => isset( $item['auto_delete_entries'] ) ? filter_var( $item['auto_delete_entries'], FILTER_VALIDATE_BOOLEAN ) : false,
1074 + 'auto_delete_days' => isset( $item['auto_delete_days'] ) ? sanitize_text_field( $item['auto_delete_days'] ) : '',
1075 + ];
1076 + }
1077 + return $sanitized;
1078 + },
1079 + 'show_in_rest' => [
952 1080 'schema' => [
953 1081 'type' => 'array',
954 1082 'context' => [ 'edit' ],
955 1083 'items' => [
@@ -973,9 +1101,9 @@
973 1101 ],
974 1102 ],
975 1103 ],
976 1104 ],
977 - 'default' => [
1105 + 'default' => [
978 1106 [
979 1107 'id' => 'gdpr',
980 1108 'gdpr' => false,
981 1109 'do_not_store_entries' => false,
@@ -1015,9 +1143,9 @@
1015 1143 'id' => isset( $item['id'] ) ? intval( $item['id'] ) : 0,
1016 1144 'confirmation_type' => isset( $item['confirmation_type'] ) ? sanitize_text_field( $item['confirmation_type'] ) : '',
1017 1145 'page_url' => isset( $item['page_url'] ) ? esc_url_raw( $item['page_url'] ) : '',
1018 1146 'custom_url' => isset( $item['custom_url'] ) ? esc_url_raw( $item['custom_url'] ) : '',
1019 - 'message' => isset( $item['message'] ) ? Helper::strip_js_attributes( $item['message'] ) : '',
1147 + 'message' => isset( $item['message'] ) ? wp_kses_post( $item['message'] ) : '',
1020 1148 'submission_action' => isset( $item['submission_action'] ) ? sanitize_text_field( $item['submission_action'] ) : '',
1021 1149 'enable_query_params' => isset( $item['enable_query_params'] ) ? filter_var( $item['enable_query_params'], FILTER_VALIDATE_BOOLEAN ) : false,
1022 1150 'query_params' => isset( $item['query_params'] ) && is_array( $item['query_params'] )
1023 1151 ? array_map(