PluginProbe
WPZOOM Forms – Drag & Drop Contact Form Builder for WordPress / 2.0.5
WPZOOM Forms – Drag & Drop Contact Form Builder for WordPress v2.0.5
2.0.9 2.0.8 2.0.7 2.0.6 2.0.5 2.0.4 2.0.3 2.0.2 2.0.1 2.0.0 trunk 1.0.0 1.0.1 1.0.2 1.0.3 1.0.4 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.2.0 1.2.1 1.2.10 All 43 releases
wpzoom-forms / wpzoom-forms.php

wpzoom-forms.php in WPZOOM Forms – Drag & Drop Contact Form Builder for WordPress 2.0.5, at wpzoom-forms.php

3,539 lines 151.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * WPZOOM Forms - Custom forms for WordPress, by WPZOOM.
4 *
5 * @package WPZOOM_Forms
6 * @author WPZOOM
7 * @copyright 2023 WPZOOM
8 * @license GPL-2.0-or-later
9 *
10 * @wordpress-plugin
11 * Plugin Name: WPZOOM Forms
12 * Plugin URI: https://www.wpzoom.com/plugins/wpzoom-forms
13 * Description: Simple, user-friendly contact form plugin for WordPress with a dedicated drag-and-drop builder.
14 * Author: WPZOOM
15 * Author URI: https://www.wpzoom.com
16 * Version: 2.0.5
17 * License: GPL2+
18 * License URI: http://www.gnu.org/licenses/gpl-2.0.txt
19 */
20
21 // Exit if accessed directly
22 defined( 'ABSPATH' ) || exit;
23
24 // settings page url attribute
25 define( 'WPZOOM_FORMS_SETTINGS_PAGE', 'wpzf-settings' );
26
27 if ( ! defined( 'WPZOOM_FORMS_VERSION' ) ) {
28 define( 'WPZOOM_FORMS_VERSION', get_file_data( __FILE__, [ 'Version' ] )[0] ); // phpcs:ignore
29 }
30
31 define( 'WPZOOM_FORMS__FILE__', __FILE__ );
32 define( 'WPZOOM_FORMS_PLUGIN_BASE', plugin_basename( WPZOOM_FORMS__FILE__ ) );
33 define( 'WPZOOM_FORMS_PLUGIN_DIR', dirname( WPZOOM_FORMS_PLUGIN_BASE ) );
34
35 define( 'WPZOOM_FORMS_PATH', plugin_dir_path( WPZOOM_FORMS__FILE__ ) );
36 define( 'WPZOOM_FORMS_URL', plugin_dir_url( WPZOOM_FORMS__FILE__ ) );
37
38
39 // WPZOOM Notice Center (submodule at classes/notice-center).
40 $wpz_notice_center_path = WPZOOM_FORMS_PATH . 'classes/notice-center/';
41 $wpz_notice_center_url = WPZOOM_FORMS_URL . 'classes/notice-center/';
42 if ( is_admin() && ! class_exists( 'WPZOOM_Notice_Center' ) && file_exists( $wpz_notice_center_path . 'notice-center.php' ) ) {
43 require_once $wpz_notice_center_path . 'notice-center.php';
44 WPZOOM_Notice_Center::get_instance()->set_assets( array(
45 'css_url' => $wpz_notice_center_url . 'assets/notice-center.css',
46 'js_url' => $wpz_notice_center_url . 'assets/notice-center.js',
47 ) );
48 }
49
50 // Instance the plugin
51 $wpzoom_forms = new WPZOOM_Forms();
52
53 // Register plugin activation hook
54 register_activation_hook( __FILE__, array( $wpzoom_forms, 'activate' ) );
55
56 // Hook the plugin into WordPress
57 add_action( 'init', array( $wpzoom_forms, 'init' ), 9 );
58
59
60
61 /**
62 * Class WPZOOM_Forms
63 *
64 * Main container class of the ZOOM Forms WordPress plugin.
65 *
66 * @since 1.0.0
67 */
68 class WPZOOM_Forms {
69
70 /**
71 * Whether the plugin has been initialized.
72 *
73 * @var boolean
74 * @access public
75 * @since 1.0.0
76 */
77 public $initialized = false;
78
79 /**
80 * The path to this plugin's root directory.
81 *
82 * @var string
83 * @access public
84 * @since 1.0.0
85 */
86 public $plugin_dir_path;
87
88 /**
89 * The URL to this plugin's root directory.
90 *
91 * @var string
92 * @access public
93 * @since 1.0.0
94 */
95 public $plugin_dir_url;
96
97 /**
98 * The path to this plugin's "main" directory.
99 *
100 * @var string
101 * @access public
102 * @since 1.0.0
103 */
104 public $main_dir_path;
105
106 /**
107 * The URL to this plugin's "main" directory.
108 *
109 * @var string
110 * @access public
111 * @since 1.0.0
112 */
113 public $main_dir_url;
114
115 /**
116 * The URL to this plugin's "dist" directory.
117 *
118 * @var string
119 * @access public
120 * @since 1.0.0
121 */
122 public $dist_dir_url;
123
124 /**
125 * UTM source for header and footer links.
126 *
127 * @var string
128 */
129 public $utm_source = '?utm_source=wpadmin&utm_medium=wpzoom-forms-free&utm_campaign=header-footer-links';
130
131 /**
132 * Initializes the plugin and sets up needed hooks and features.
133 *
134 * @access public
135 * @return void
136 * @since 1.0.0
137 * @see ZOOM_Forms::block_setup()
138 */
139 public function init() {
140 if ( false === $this->initialized ) {
141 $this->plugin_dir_path = plugin_dir_path( __FILE__ );
142 $this->plugin_dir_url = plugin_dir_url( __FILE__ );
143 $this->main_dir_path = trailingslashit( $this->plugin_dir_path . 'build' );
144 $this->main_dir_url = trailingslashit( $this->plugin_dir_url . 'build' );
145 $this->dist_dir_url = trailingslashit( $this->plugin_dir_url . 'dist' );
146
147 load_plugin_textdomain( 'wpzoom-forms', false, dirname( plugin_basename( __FILE__ ) ) . '/languages' );
148
149 // Register welcome guide setting
150 register_setting(
151 'general',
152 'wpzoom_forms_welcome_guide_shown',
153 array(
154 'type' => 'boolean',
155 'default' => false,
156 'show_in_rest' => true,
157 'sanitize_callback' => 'rest_sanitize_boolean',
158 )
159 );
160
161 add_filter( 'allowed_block_types_all', array( $this, 'filter_allowed_block_types' ), 10, 2 );
162 add_filter( 'block_categories_all', array( $this, 'filter_block_categories' ), 10, 2 );
163 add_filter( 'post_row_actions', array( $this, 'modify_row_actions' ), 10, 2 );
164 add_filter( 'bulk_actions-edit-wpzf-form', array( $this, 'remove_bulk_actions' ), 10 );
165 add_filter( 'bulk_actions-edit-wpzf-submission', array( $this, 'remove_bulk_actions' ), 10 );
166 add_filter( 'manage_edit-wpzf-form_columns', array( $this, 'post_list_columns_form' ), 10 );
167 add_filter( 'manage_edit-wpzf-submission_columns', array( $this, 'post_list_columns_submit' ), 10 );
168 add_filter( 'manage_edit-wpzf-submission_sortable_columns', array( $this, 'post_list_sortable_columns_submit' ), 10 );
169 add_filter( 'screen_options_show_screen', array( $this, 'remove_screen_options' ), 10, 2 );
170 add_filter( 'views_edit-wpzf-form', array( $this, 'post_list_views' ), 10 );
171 add_filter( 'list_table_primary_column', array( $this, 'post_list_primary_column' ), 10, 2 );
172 add_action( 'admin_menu', array( $this, 'admin_menu' ), 10 );
173 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ), 100 );
174 add_action( 'enqueue_block_editor_assets', array( $this, 'register_backend_assets' ), 10 );
175 add_action( 'enqueue_block_assets', array( $this, 'register_frontend_assets' ), 10 );
176 add_action( 'enqueue_block_assets', array( $this, 'block_frontend_assets' ), 10 );
177 add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_frontend_scripts' ), 10 );
178 add_action( 'all_admin_notices', array( $this, 'admin_page_header' ), 1 );
179 add_action( 'in_admin_footer', array( $this, 'admin_page_footer' ), 10 );
180 add_filter( 'admin_body_class', array( $this, 'admin_body_class_filter' ), 10 );
181 add_action( 'manage_wpzf-form_posts_custom_column', array( $this, 'post_list_custom_columns_form' ), 10, 2 );
182 add_action( 'manage_wpzf-submission_posts_custom_column', array( $this, 'post_list_custom_columns_submit' ), 10, 2 );
183 add_action( 'pre_get_posts', array( $this, 'sort_custom_column' ), 10 );
184 add_action( 'in_admin_header', array( $this, 'remove_meta_boxes' ), 100 );
185 add_action( 'add_meta_boxes_wpzf-submission', array( $this, 'add_meta_boxes' ), 10 );
186 add_action( 'admin_post_wpzf_submit', array( $this, 'action_form_post' ), 10 );
187 add_action( 'admin_post_nopriv_wpzf_submit', array( $this, 'action_form_post' ), 10 );
188
189 add_action( 'restrict_manage_posts', array( $this, 'custom_filter_by_form' ), 10 );
190 add_action( 'parse_query', array( $this, 'filter_posts_by_form' ), 10 );
191
192 // Upsell notice for PRO version (via WPZOOM Notice Center)
193 add_filter( 'wpzoom_notice_center_notices', array( $this, 'register_upsell_notice' ) );
194
195 register_post_type(
196 'wpzf-form',
197 array(
198 'label' => __( 'WPZOOM Forms', 'wpzoom-forms' ),
199 'labels' => array(
200 'name' => _x( 'WPZOOM Forms', 'post type general name', 'wpzoom-forms' ),
201 'singular_name' => _x( 'Form', 'post type singular name', 'wpzoom-forms' ),
202 'add_new' => _x( 'Add New', 'post', 'wpzoom-forms' ),
203 'add_new_item' => __( 'Add New Form', 'wpzoom-forms' ),
204 'edit_item' => __( 'Edit Form', 'wpzoom-forms' ),
205 'new_item' => __( 'New Form', 'wpzoom-forms' ),
206 'view_item' => __( 'View Form', 'wpzoom-forms' ),
207 'view_items' => __( 'View Forms', 'wpzoom-forms' ),
208 'search_items' => __( 'Search Forms', 'wpzoom-forms' ),
209 'not_found' => __( 'No forms found.', 'wpzoom-forms' ),
210 'not_found_in_trash' => __( 'No forms found in Trash.', 'wpzoom-forms' ),
211 'all_items' => __( 'All Forms', 'wpzoom-forms' ),
212 'archives' => __( 'Form Archives', 'wpzoom-forms' ),
213 'attributes' => __( 'Form Attributes', 'wpzoom-forms' ),
214 'insert_into_item' => __( 'Insert into form', 'wpzoom-forms' ),
215 'uploaded_to_this_item' => __( 'Uploaded to this form', 'wpzoom-forms' ),
216 'featured_image' => _x( 'Featured image', 'post', 'wpzoom-forms' ),
217 'set_featured_image' => _x( 'Set featured image', 'post', 'wpzoom-forms' ),
218 'remove_featured_image' => _x( 'Remove featured image', 'post', 'wpzoom-forms' ),
219 'use_featured_image' => _x( 'Use as featured image', 'post', 'wpzoom-forms' ),
220 'filter_items_list' => __( 'Filter forms list', 'wpzoom-forms' ),
221 'items_list_navigation' => __( 'Forms list navigation', 'wpzoom-forms' ),
222 'items_list' => __( 'Forms list', 'wpzoom-forms' ),
223 'item_published' => __( 'Form saved.', 'wpzoom-forms' ),
224 'item_published_privately' => __( 'Form saved privately.', 'wpzoom-forms' ),
225 'item_reverted_to_draft' => __( 'Form reverted to draft.', 'wpzoom-forms' ),
226 'item_scheduled' => __( 'Form scheduled.', 'wpzoom-forms' ),
227 'item_updated' => __( 'Form updated.', 'wpzoom-forms' )
228 ),
229 'public' => true,
230 'exclude_from_search' => true,
231 'publicly_queryable' => false,
232 'show_in_rest' => true,
233 'menu_position' => 30,
234 'menu_icon' => 'data:image/svg+xml;base64,' . base64_encode('<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path fillRule="evenodd" clipRule="evenodd" d="M7.33333 2H20.6667V22H4V5.33333H7.33333V2ZM17.3333 5.33333H10.6667V8.66667H7.33333V18.6667H10.6667V15.3333H14V12H10.6667V8.66667H17.3333V5.33333Z" fill="#a7aaad"/></svg>'),
235 'supports' => array( 'title', 'editor', 'custom-fields', 'revisions' )
236 )
237 );
238
239 register_post_type(
240 'wpzf-submission',
241 array(
242 'label' => __( 'WPZOOM Form Submissions', 'wpzoom-forms' ),
243 'labels' => array(
244 'name' => _x( 'WPZOOM Form Submissions', 'post type general name', 'wpzoom-forms' ),
245 'singular_name' => _x( 'WPZOOM Form Submission', 'post type singular name', 'wpzoom-forms' ),
246 'add_new' => _x( 'Add New', 'post', 'wpzoom-forms' ),
247 'add_new_item' => __( 'Add New Submission', 'wpzoom-forms' ),
248 'edit_item' => __( 'WPZOOM Form Submission', 'wpzoom-forms' ),
249 'new_item' => __( 'New Submission', 'wpzoom-forms' ),
250 'view_item' => __( 'View Submission', 'wpzoom-forms' ),
251 'view_items' => __( 'View Submissions', 'wpzoom-forms' ),
252 'search_items' => __( 'Search Submissions', 'wpzoom-forms' ),
253 'not_found' => __( 'No submissions found.', 'wpzoom-forms' ),
254 'not_found_in_trash' => __( 'No submissions found in Trash.', 'wpzoom-forms' ),
255 'all_items' => __( 'Submissions', 'wpzoom-forms' ),
256 'archives' => __( 'Submission Archives', 'wpzoom-forms' ),
257 'attributes' => __( 'Submission Attributes', 'wpzoom-forms' ),
258 'insert_into_item' => __( 'Insert into submission', 'wpzoom-forms' ),
259 'uploaded_to_this_item' => __( 'Uploaded to this submission', 'wpzoom-forms' ),
260 'featured_image' => _x( 'Featured image', 'post', 'wpzoom-forms' ),
261 'set_featured_image' => _x( 'Set featured image', 'post', 'wpzoom-forms' ),
262 'remove_featured_image' => _x( 'Remove featured image', 'post', 'wpzoom-forms' ),
263 'use_featured_image' => _x( 'Use as featured image', 'post', 'wpzoom-forms' ),
264 'filter_items_list' => __( 'Filter form submission list', 'wpzoom-forms' ),
265 'items_list_navigation' => __( 'Form Submissions list navigation', 'wpzoom-forms' ),
266 'items_list' => __( 'Form Submissions list', 'wpzoom-forms' ),
267 'item_published' => __( 'Form Submission saved.', 'wpzoom-forms' ),
268 'item_published_privately' => __( 'Form Submission saved privately.', 'wpzoom-forms' ),
269 'item_reverted_to_draft' => __( 'Form Submission reverted to draft.', 'wpzoom-forms' ),
270 'item_scheduled' => __( 'Form Submission scheduled.', 'wpzoom-forms' ),
271 'item_updated' => __( 'Form Submission updated.', 'wpzoom-forms' )
272 ),
273 'public' => true,
274 'exclude_from_search' => true,
275 'publicly_queryable' => false,
276 'show_in_nav_menus' => false,
277 'show_in_admin_bar' => false,
278 'show_in_rest' => false,
279 'show_in_menu' => 'edit.php?post_type=wpzf-form',
280 'menu_position' => 31,
281 'menu_icon' => 'dashicons-email-alt2',
282 'supports' => array( '' ),
283 'capabilities' => array(
284 'create_posts' => 'do_not_allow',
285 'publish_posts' => 'do_not_allow',
286 'edit_posts' => 'edit_posts',
287 'delete_posts' => 'delete_posts'
288 ),
289 'map_meta_cap' => true
290 )
291 );
292
293 register_post_status(
294 'spam',
295 array(
296 'label' => esc_html_x( 'Spam', 'post', 'wpzoom-forms' ),
297 'label_count' => _n_noop( 'Spam <span class="count">(%s)</span>', 'Spam <span class="count">(%s)</span>', 'wpzoom-forms' ),
298 'public' => false
299 )
300 );
301
302 register_meta(
303 'post',
304 '_form_method',
305 array(
306 'object_subtype' => 'wpzf-form',
307 'show_in_rest' => true,
308 'single' => true,
309 'type' => 'string',
310 'default' => 'email',
311 'sanitize_callback' => 'sanitize_text_field',
312 'auth_callback' => function() { return current_user_can( 'edit_posts' ); }
313 )
314 );
315
316 register_meta(
317 'post',
318 '_form_email',
319 array(
320 'object_subtype' => 'wpzf-form',
321 'show_in_rest' => true,
322 'single' => true,
323 'type' => 'string',
324 'default' => '',
325 'sanitize_callback' => 'sanitize_text_field',
326 'auth_callback' => function() { return current_user_can( 'edit_posts' ); }
327 )
328 );
329
330 register_meta(
331 'post',
332 '_form_subject',
333 array(
334 'object_subtype' => 'wpzf-form',
335 'show_in_rest' => true,
336 'single' => true,
337 'type' => 'string',
338 'default' => esc_html__( 'New Form Submission', 'wpzoom-forms' ),
339 'sanitize_callback' => 'sanitize_text_field',
340 'auth_callback' => function() { return current_user_can( 'edit_posts' ); }
341 )
342 );
343
344 register_meta(
345 'post',
346 '_form_success_message',
347 array(
348 'object_subtype' => 'wpzf-form',
349 'show_in_rest' => true,
350 'single' => true,
351 'type' => 'string',
352 'default' => __( 'Thanks! We\'ve received your submission!', 'wpzoom-forms' ),
353 'sanitize_callback' => function( $value ) {
354 return wp_kses( $value, array() );
355 },
356 'auth_callback' => function() { return current_user_can( 'edit_posts' ); }
357 )
358 );
359
360 register_meta(
361 'post',
362 '_form_failure_message',
363 array(
364 'object_subtype' => 'wpzf-form',
365 'show_in_rest' => true,
366 'single' => true,
367 'type' => 'string',
368 'default' => __( 'Submission failed!', 'wpzoom-forms' ),
369 'sanitize_callback' => function( $value ) {
370 return wp_kses( $value, array() );
371 },
372 'auth_callback' => function() { return current_user_can( 'edit_posts' ); }
373 )
374 );
375
376 add_shortcode( 'wpzf_form', array( $this, 'shortcode_output' ) );
377
378 //$form_pto = get_post_type_object( 'wpzf-form' );
379 //$form_pto->template = array( array( 'wpzoom-forms/form' ) );
380
381 if ( $this->is_post_type( 'wpzf-form' ) ) {
382 $this->forms_display();
383 $this->register_blocks();
384 } elseif ( $this->is_post_type( 'wpzf-submission' ) ) {
385 $this->submissions_display();
386 } else {
387 $this->register_form_block();
388 }
389
390 $this->initialized = true;
391 }
392 }
393
394 /**
395 * Runs once during the activation of the plugin to run some one-time setup functions.
396 *
397 * @access public
398 * @return void
399 * @since 1.0.0
400 * @see ZOOM_Forms::init()
401 */
402 public function activate() {
403 $this->init();
404
405 flush_rewrite_rules();
406
407 // If this is the first time activating the plugin...
408 if ( ! get_option( 'wpzf-form_first-activate' ) ) {
409 // Insert an initial example form post
410 wp_insert_post( array(
411 'post_type' => 'wpzf-form',
412 'post_status' => 'publish',
413 'post_title' => __( 'Example Form', 'wpzoom-forms' ),
414 'post_content' => '<!-- wp:wpzoom-forms/form -->
415 <div class="wp-block-wpzoom-forms-form"><!-- wp:wpzoom-forms/text-name-field {"id":"input_ae87379b","name":"Name"} -->
416 <label for="input_ae87379b"><span>Name</span><sup class="wp-block-wpzoom-forms-required">*</sup></label><input type="text" name="input_ae87379b" id="input_ae87379b" placeholder="" required class="wp-block-wpzoom-forms-text-name-field"/>
417 <!-- /wp:wpzoom-forms/text-name-field -->
418
419 <!-- wp:wpzoom-forms/text-email-field {"id":"input_8468ae36","name":"Email"} -->
420 <label for="input_8468ae36"><span>Email</span><sup class="wp-block-wpzoom-forms-required">*</sup></label><input type="email" name="input_8468ae36" id="input_8468ae36" placeholder="" required data-replyto="false" class="wp-block-wpzoom-forms-text-email-field"/>
421 <!-- /wp:wpzoom-forms/text-email-field -->
422
423 <!-- wp:wpzoom-forms/text-plain-field {"id":"input_85d61063","name":"Subject","label":"Subject","subject":true} -->
424 <label for="input_85d61063"><span>Subject</span><sup class="wp-block-wpzoom-forms-required">*</sup></label><input type="text" name="input_85d61063" id="input_85d61063" placeholder="" required data-subject="true" class="wp-block-wpzoom-forms-text-plain-field"/>
425 <!-- /wp:wpzoom-forms/text-plain-field -->
426
427 <!-- wp:wpzoom-forms/textarea-field {"id":"input_7f6bf203","name":"Message"} -->
428 <label for="input_7f6bf203"><span>Message</span></label><textarea name="input_7f6bf203" id="input_7f6bf203" cols="55" rows="10" placeholder="" class="wp-block-wpzoom-forms-textarea-field"></textarea>
429 <!-- /wp:wpzoom-forms/textarea-field -->
430
431 <!-- wp:columns {"verticalAlignment":"center"} -->
432 <div class="wp-block-columns are-vertically-aligned-center"><!-- wp:column {"verticalAlignment":"center","width":"30%"} -->
433 <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:30%"><!-- wp:wpzoom-forms/submit-field {"id":"input_submit"} -->
434 <input type="submit" id="input_submit" value="Submit" class="wp-block-wpzoom-forms-submit-field"/>
435 <!-- /wp:wpzoom-forms/submit-field --></div>
436 <!-- /wp:column -->
437
438 <!-- wp:column {"verticalAlignment":"center","width":"70%"} -->
439 <div class="wp-block-column is-vertically-aligned-center" style="flex-basis:70%"><!-- wp:paragraph {"align":"right","style":{"typography":{"fontSize":16}}} -->
440 <p class="has-text-align-right" style="font-size:16px">Fields marked with <strong class="has-accent-color has-text-color">*</strong> are required.</p>
441 <!-- /wp:paragraph --></div>
442 <!-- /wp:column --></div>
443 <!-- /wp:columns --></div>
444 <!-- /wp:wpzoom-forms/form -->',
445 'meta_input' => array(
446 '_form_method' => 'email',
447 '_form_email' => trim( get_option( 'admin_email' ) ),
448 '_form_subject' => esc_html__( 'New Form Submission', 'wpzoom-forms' ),
449 '_form_success_message' => __( 'Thanks! We\'ve received your submission!', 'wpzoom-forms' ),
450 '_form_failure_message' => __( 'Submission failed!', 'wpzoom-forms' ),
451
452 )
453 ) );
454
455 // Make sure we don't insert an example form on every activation
456 update_option( 'wpzf-form_first-activate', true );
457 }
458 }
459
460 /**
461 * Modifies the way the backend forms list is displayed.
462 *
463 * @access public
464 * @return void
465 * @since 1.0.0
466 */
467 public function forms_display() {
468 global $wp_post_statuses;
469
470 $publish = $wp_post_statuses['publish'];
471 $publish->label = __( 'Saved', 'wpzoom-forms' );
472 $publish->label_count[0] = __( 'Saved <span class="count">(%s)</span>', 'wpzoom-forms' );
473 $publish->label_count[1] = __( 'Saved <span class="count">(%s)</span>', 'wpzoom-forms' );
474 $publish->label_count['singular'] = __( 'Saved <span class="count">(%s)</span>', 'wpzoom-forms' );
475 $publish->label_count['plural'] = __( 'Saved <span class="count">(%s)</span>', 'wpzoom-forms' );
476
477 $wp_post_statuses['publish'] = $publish;
478
479 $wp_post_statuses = array_diff_key(
480 $wp_post_statuses,
481 array_flip( array(
482 'future',
483 'pending',
484 'private',
485 'request-pending',
486 'request-confirmed',
487 'request-failed',
488 'request-completed'
489 ) )
490 );
491
492 add_filter( 'post_date_column_status', function() { return __( 'Last Modified', 'wpzoom-forms' ); } );
493 add_filter( 'post_date_column_time', function( $time, $post ) {
494 return sprintf(
495 __( '%1$s at %2$s', 'wpzoom-forms' ),
496 get_the_modified_time( __( 'Y/m/d', 'wpzoom-forms' ), $post ),
497 get_the_modified_time( __( 'g:i a', 'wpzoom-forms' ), $post )
498 );
499 }, 10, 2 );
500 }
501
502 /**
503 * Modifies the way the backend submissions list is displayed.
504 *
505 * @access public
506 * @return void
507 * @since 1.0.0
508 */
509 public function submissions_display() {
510 global $wp_post_statuses;
511
512 $wp_post_statuses = array_diff_key(
513 $wp_post_statuses,
514 array_flip( [
515 'future',
516 'pending',
517 'private',
518 'request-pending',
519 'request-confirmed',
520 'request-failed',
521 'request-completed'
522 ] )
523 );
524
525 add_filter( 'post_date_column_status', function() { return __( 'Submitted', 'wpzoom-forms' ); } );
526 }
527
528 /**
529 * Registers all the custom blocks for this plugin.
530 *
531 * @access public
532 * @return void
533 * @since 1.0.0
534 */
535 public function register_blocks() {
536 register_block_type(
537 'wpzoom-forms/form',
538 array(
539 'script' => 'wpzoom-forms-js-frontend-formblock',
540 'style' => 'wpzoom-forms-css-frontend-formblock',
541 'editor_script' => 'wpzoom-forms-js-backend-main',
542 'editor_style' => 'wpzoom-forms-css-backend-main'
543 )
544 );
545
546 foreach ( array( 'multi-checkbox', 'checkbox', 'email', 'label', 'name', 'phone', 'plain', 'radio', 'select', 'submit', 'textarea', 'website', 'datepicker' ) as $block ) {
547 register_block_type( $this->main_dir_path . 'fields/' . $block . '/block.json' );
548 }
549 }
550
551 /**
552 * Registers the main form block used for inserting a form into a regular post/page.
553 *
554 * @access public
555 * @return void
556 * @since 1.0.0
557 */
558 public function register_form_block() {
559 register_block_type(
560 'wpzoom-forms/form-block',
561 array(
562 'attributes' => array(
563 'formId' => array(
564 'type' => 'string',
565 'default' => '-1'
566 ),
567 'align' => array(
568 'type' => 'string',
569 'default' => 'none'
570 ),
571 'formBgColor' => array(
572 'type' => 'string',
573 'default' => ''
574 ),
575 'formBrd' => array(
576 'type' => 'string',
577 'default' => ''
578 ),
579
580 'fieldBrdStyle' => array(
581 'type' => 'string',
582 'default' => 'default'
583 ),
584 'fieldBrdWidth' => array(
585 'type' => 'number',
586 'default' => 0
587 ),
588 'fieldBrdRadius' => array(
589 'type' => 'number',
590 'default' => 0
591 ),
592
593 'fieldBrdColor' => array(
594 'type' => 'string',
595 'default' => ''
596 ),
597 'fieldTextColor' => array(
598 'type' => 'string',
599 'default' => ''
600 ),
601 'fieldBgColor' => array(
602 'type' => 'string',
603 'default' => ''
604 ),
605 'labelTextColor' => array(
606 'type' => 'string',
607 'default' => ''
608 ),
609 'btnBgColor' => array(
610 'type' => 'string',
611 'default' => ''
612 ),
613 'btnTextColor' => array(
614 'type' => 'string',
615 'default' => ''
616 ),
617 'btnBrdWidth' => array(
618 'type' => 'number',
619 'default' => 0
620 ),
621 'btnBrdStyle' => array(
622 'type' => 'string',
623 'default' => 'default'
624 ),
625 'btnBrdRadius' => array(
626 'type' => 'number',
627 'default' => 0
628 ),
629 'btnBrdColor' => array(
630 'type' => 'string',
631 'default' => ''
632 ),
633 'btnHoverBgColor' => array(
634 'type' => 'string',
635 'default' => ''
636 ),
637 'btnHoverTextColor' => array(
638 'type' => 'string',
639 'default' => ''
640 ),
641 'btnHoverBrdColor' => array(
642 'type' => 'string',
643 'default' => ''
644 ),
645
646 ),
647 'script' => 'wpzoom-forms-js-frontend-formblock',
648 'style' => 'wpzoom-forms-css-frontend-formblock',
649 'editor_script' => 'wpzoom-forms-js-backend-formblock',
650 'editor_style' => 'wpzoom-forms-css-backend-formblock',
651 'render_callback' => array( $this, 'form_block_render' )
652 )
653 );
654 }
655
656 /**
657 * Filters the allowed Gutenberg block types for a given post.
658 *
659 * @access public
660 * @param array $allowed_block_types An array of all the allowed block types.
661 * @param WP_Block_Editor_Context $block_editor_context The current block editor context.
662 * @return array
663 * @since 1.0.0
664 */
665 public function filter_allowed_block_types( $allowed_block_types, $block_editor_context ) {
666 if ( null !== $block_editor_context->post && 'wpzf-form' == $block_editor_context->post->post_type ) {
667 $allowed_block_types = array(
668 'wpzoom-forms/form',
669 'wpzoom-forms/datepicker-field',
670 'wpzoom-forms/text-plain-field',
671 'wpzoom-forms/text-name-field',
672 'wpzoom-forms/text-email-field',
673 'wpzoom-forms/text-website-field',
674 'wpzoom-forms/text-phone-field',
675 'wpzoom-forms/textarea-field',
676 'wpzoom-forms/select-field',
677 'wpzoom-forms/multi-checkbox-field',
678 'wpzoom-forms/checkbox-field',
679 'wpzoom-forms/radio-field',
680 'wpzoom-forms/label-field',
681 'wpzoom-forms/submit-field',
682 'core/paragraph',
683 'core/heading',
684 'core/list',
685 'core/quote',
686 'core/code',
687 'core/preformatted',
688 'core/pullquote',
689 'core/table',
690 'core/verse',
691 'core/image',
692 'core/gallery',
693 'core/audio',
694 'core/cover',
695 'core/file',
696 'core/media-text',
697 'core/video',
698 'core/buttons',
699 'core/columns',
700 'core/group',
701 'core/more',
702 'core/nextpage',
703 'core/separator',
704 'core/spacer'
705 );
706 }
707
708 return $allowed_block_types;
709 }
710
711 /**
712 * Adds needed categories to the Gutenberg block categories, if not already present.
713 *
714 * @access public
715 * @param array $categories Array containing all registered Gutenberg block categories.
716 * @param WP_Block_Editor_Context $block_editor_context The current block editor context.
717 * @return array
718 * @since 1.0.0
719 */
720 public function filter_block_categories( $categories, $block_editor_context ) {
721 if ( null !== $block_editor_context->post && 'wpzf-form' == $block_editor_context->post->post_type ) {
722 $category_slugs = wp_list_pluck( $categories, 'slug' );
723
724 if ( ! in_array( 'wpzoom-forms', $category_slugs, true ) ) {
725 array_unshift(
726 $categories,
727 array(
728 'slug' => 'wpzoom-forms',
729 'title' => __( 'WPZOOM Forms', 'wpzoom-forms' ),
730 'icon' => 'wordpress'
731 )
732 );
733 }
734 } else {
735 $category_slugs = wp_list_pluck( $categories, 'slug' );
736
737 if ( ! in_array( 'wpzoom-blocks', $category_slugs, true ) ) {
738 $categories = array_merge(
739 $categories,
740 array(
741 array(
742 'slug' => 'wpzoom-blocks',
743 'title' => __( 'WPZOOM Blocks', 'wpzoom-forms' ),
744 'icon' => 'wordpress'
745 )
746 )
747 );
748 }
749 }
750
751 return $categories;
752 }
753
754 /**
755 * Add some extra admin menu items.
756 *
757 * @access public
758 * @return void
759 * @since 1.0.0
760 */
761 public function admin_menu() {
762
763 global $submenu;
764
765 $page_title = esc_html__( 'WPZOOM Forms Settings Page', 'wpzoom-forms' );
766
767 add_submenu_page(
768 'edit.php?post_type=wpzf-form',
769 $page_title,
770 esc_html__( 'Settings', 'wpzoom-forms' ),
771 'manage_options',
772 'wpzf-settings',
773 array( $this, 'render_settings_page' )
774 );
775
776 add_submenu_page(
777 'edit.php?post_type=wpzf-form',
778 $page_title,
779 '<span style="color:#3496fe; font-weight: 600;">' . esc_html__( 'UPGRADE', 'wpzoom-forms' ) . ' &rarr; <span class="wpz-premium-badge" style="background-color: #3496fe; color: #fff; margin-left: 3px; font-size: 9px; min-height: 14px; border-radius: 3px; display: inline-block; font-weight: 600; line-height: 1.4; padding: 0 5px">PRO</span></span>',
780 'manage_options',
781 'wpzf-pro-page',
782 array( $this, 'render_upsell_page' )
783 );
784
785 add_submenu_page(
786 'edit.php?post_type=wpzf-form',
787 esc_html__( 'Import & Export', 'wpzoom-forms' ),
788 esc_html__( 'Import & Export', 'wpzoom-forms' ) . ' <span class="wpz-premium-badge" style="background-color: #3496fe; color: #fff; margin-left: 3px; font-size: 9px; min-height: 14px; border-radius: 3px; display: inline-block; font-weight: 600; line-height: 1.4; padding: 0 5px">PRO</span>',
789 'edit_posts',
790 'wpzf-import',
791 array( $this, 'render_import_export_upsell_page' )
792 );
793
794 }
795
796 /**
797 * Registers needed scripts for use on the admin backend.
798 *
799 * @access public
800 * @return void
801 * @since 1.0.0
802 */
803 public function admin_enqueue_scripts() {
804 if ( 'wpzf-submission' == get_post_type() ) {
805 wp_dequeue_script( 'autosave' );
806
807 wp_enqueue_style(
808 'wpzoom-forms-css-backend-submissions',
809 trailingslashit( $this->main_dir_url ) . 'submissions/backend/style.css',
810 array(),
811 WPZOOM_FORMS_VERSION
812 );
813 }
814
815 $current_page = get_current_screen()->id;
816
817 if ( 'edit-wpzf-form' == $current_page || 'wpzf-form' == $current_page || 'edit-wpzf-submission' == $current_page || 'wpzf-submission' == $current_page || 'wpzf-form_page_wpzf-settings' == $current_page || 'wpzf-form_page_wpzoom-forms-pro-license' == $current_page || 'wpzf-form_page_wpzf-pro-page' == $current_page || 'wpzf-form_page_wpzf-import' == $current_page ) {
818 wp_enqueue_style(
819 'wpzoom-forms-css-backend-main',
820 trailingslashit( $this->main_dir_url ) . 'main/backend/style.css',
821 array(),
822 WPZOOM_FORMS_VERSION
823 );
824 }
825
826 if ( 'edit-wpzf-form' == $current_page || 'wpzf-form' == $current_page ) {
827 wp_enqueue_script(
828 'wpzoom-forms-cpt',
829 WPZOOM_FORMS_URL . 'dist/assets/admin/js/forms-cpt.js',
830 array( 'wp-data' ),
831 WPZOOM_FORMS_VERSION,
832 true
833 );
834 }
835
836 }
837
838 /**
839 * Registers needed scripts and styles for use on the backend.
840 *
841 * @access public
842 * @return void
843 * @since 1.0.0
844 */
845 public function register_backend_assets() {
846 if ( 'wpzf-form' == get_post_type() ) {
847 wp_register_script(
848 'wpzoom-forms-js-backend-main',
849 trailingslashit( $this->main_dir_url ) . 'main/backend/script.js',
850 array( 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill' ),
851 WPZOOM_FORMS_VERSION,
852 true
853 );
854
855 wp_localize_script(
856 'wpzoom-forms-js-backend-main',
857 'wpzf_formblock',
858 array(
859 'admin_url' => trailingslashit( admin_url() ),
860 'admin_email' => '' . get_site_option( 'admin_email', '' )
861 )
862 );
863
864 wp_set_script_translations( 'wpzoom-forms-js-backend-main', 'wpzoom-forms' );
865
866 wp_register_style(
867 'wpzoom-forms-css-backend-main',
868 trailingslashit( $this->main_dir_url ) . 'main/backend/style.css',
869 array(),
870 WPZOOM_FORMS_VERSION
871 );
872 } else {
873 wp_register_script(
874 'wpzoom-forms-js-backend-formblock',
875 trailingslashit( $this->main_dir_url ) . 'form-block/backend/script.js',
876 array( 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill' ),
877 WPZOOM_FORMS_VERSION,
878 true
879 );
880
881 wp_localize_script(
882 'wpzoom-forms-js-backend-formblock',
883 'wpzf_formblock',
884 array(
885 'admin_url' => trailingslashit( admin_url() ),
886 'admin_email' => '' . get_site_option( 'admin_email', '' )
887 )
888 );
889
890 wp_set_script_translations( 'wpzoom-forms-js-backend-formblock', 'wpzoom-forms' );
891
892 wp_register_style(
893 'wpzoom-forms-css-backend-formblock',
894 trailingslashit( $this->main_dir_url ) . 'form-block/backend/style.css',
895 array(),
896 WPZOOM_FORMS_VERSION
897 );
898 }
899 }
900
901 /**
902 * Get normalized spam protection settings and whether the selected method is fully configured.
903 *
904 * @access private
905 * @return array
906 * @since 1.3.7
907 */
908 public function get_spam_protection_config() {
909 $service = sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_service' ) );
910 $type = sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_type' ) );
911
912 if ( ! in_array( $service, array( 'none', 'recaptcha', 'turnstile' ), true ) ) {
913 $service = 'none';
914 }
915
916 if ( ! in_array( $type, array( 'v2', 'v3' ), true ) ) {
917 $type = 'v2';
918 }
919
920 $recaptcha_v2_site_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_site_key' ) ) );
921 $recaptcha_v2_secret_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_secret_key' ) ) );
922 $recaptcha_v3_site_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_site_key_v3' ) ) );
923 $recaptcha_v3_secret_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_secret_key_v3' ) ) );
924 $turnstile_site_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_turnstile_site_key' ) ) );
925 $turnstile_secret_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_turnstile_secret_key' ) ) );
926
927 $active_service = 'none';
928
929 if ( 'recaptcha' === $service ) {
930 if ( 'v3' === $type && ! empty( $recaptcha_v3_site_key ) && ! empty( $recaptcha_v3_secret_key ) ) {
931 $active_service = 'recaptcha';
932 } elseif ( 'v2' === $type && ! empty( $recaptcha_v2_site_key ) && ! empty( $recaptcha_v2_secret_key ) ) {
933 $active_service = 'recaptcha';
934 }
935 } elseif ( 'turnstile' === $service && ! empty( $turnstile_site_key ) && ! empty( $turnstile_secret_key ) ) {
936 $active_service = 'turnstile';
937 }
938
939 return array(
940 'service' => $service,
941 'type' => $type,
942 'active_service' => $active_service,
943 'recaptcha_v2_site_key' => $recaptcha_v2_site_key,
944 'recaptcha_v2_secret_key' => $recaptcha_v2_secret_key,
945 'recaptcha_v3_site_key' => $recaptcha_v3_site_key,
946 'recaptcha_v3_secret_key' => $recaptcha_v3_secret_key,
947 'turnstile_site_key' => $turnstile_site_key,
948 'turnstile_secret_key' => $turnstile_secret_key,
949 );
950 }
951
952 /**
953 * Registers needed scripts and styles for use on the frontend.
954 *
955 * @access public
956 * @return void
957 * @since 1.0.0
958 */
959 public function register_frontend_assets() {
960
961 $depends = array( 'jquery' );
962 $captcha = $this->get_spam_protection_config();
963 $captcha_type = $captcha['type'];
964 $active_method = $captcha['active_service'];
965
966 if ( ! is_admin() ) {
967 if ( 'recaptcha' === $active_method ) {
968 $site_key = ( 'v3' === $captcha_type ) ? $captcha['recaptcha_v3_site_key'] : $captcha['recaptcha_v2_site_key'];
969
970 if ( 'v2' === $captcha_type ) {
971 wp_register_script(
972 'google-recaptcha',
973 'https://www.google.com/recaptcha/api.js',
974 array(),
975 WPZOOM_FORMS_VERSION,
976 true
977 );
978 }
979 elseif ( 'v3' === $captcha_type ) {
980 wp_register_script(
981 'google-recaptcha',
982 "https://www.google.com/recaptcha/api.js?render={$site_key}",
983 array(),
984 WPZOOM_FORMS_VERSION,
985 true
986 );
987 }
988
989 $depends[] = 'google-recaptcha';
990 } elseif ( 'turnstile' === $active_method ) {
991 wp_register_script(
992 'turnstile-recaptcha',
993 'https://challenges.cloudflare.com/turnstile/v0/api.js',
994 array(),
995 null,
996 array( 'strategy' => 'defer' ),
997 );
998
999 $depends[] = 'turnstile-recaptcha';
1000 }
1001 }
1002
1003 wp_register_script(
1004 'wpzoom-forms-js-frontend-formblock',
1005 trailingslashit( $this->dist_dir_url ) . 'assets/frontend/js/script.js',
1006 $depends,
1007 WPZOOM_FORMS_VERSION,
1008 array(
1009 'in_footer' => true,
1010 'strategy' => 'defer',
1011 )
1012 );
1013
1014 $use_theme_style = boolval( WPZOOM_Forms_Settings::get( 'wpzf_use_theme_styles' ) );
1015
1016 wp_register_style(
1017 'wpzoom-forms-css-frontend-formblock',
1018 ( $use_theme_style ? trailingslashit( $this->main_dir_url ) . 'form-block/frontend/style.css' : false ),
1019 array(),
1020 WPZOOM_FORMS_VERSION
1021 );
1022
1023 // Register style for datepicker field
1024 wp_register_style(
1025 'wpzoom-forms-css-frontend-flatpickr',
1026 trailingslashit( $this->dist_dir_url ) . 'assets/frontend/flatpickr/css/flatpickr.min.css',
1027 array(),
1028 WPZOOM_FORMS_VERSION
1029 );
1030
1031 //Register script for datepicker field
1032 wp_register_script(
1033 'wpzoom-forms-js-frontend-flatpickr',
1034 trailingslashit( $this->dist_dir_url ) . 'assets/frontend/flatpickr/js/flatpickr.js',
1035 array( 'jquery' ),
1036 WPZOOM_FORMS_VERSION,
1037 array(
1038 'in_footer' => true,
1039 'strategy' => 'defer',
1040 )
1041 );
1042
1043 wp_register_script(
1044 'wpzoom-forms-js-frontend-datepicker',
1045 trailingslashit( $this->dist_dir_url ) . 'assets/frontend/js/datepicker.js',
1046 array( 'wpzoom-forms-js-frontend-flatpickr' ),
1047 WPZOOM_FORMS_VERSION,
1048 array(
1049 'in_footer' => true,
1050 'strategy' => 'defer',
1051 )
1052 );
1053 }
1054
1055 /**
1056 * Enqueues needed scripts and styles for use on the frontend.
1057 *
1058 * @access public
1059 * @return void
1060 * @since 1.0.0
1061 */
1062 public function enqueue_frontend_scripts() {
1063
1064 $global_load_assets = boolval( WPZOOM_Forms_Settings::get( 'wpzf_global_assets_load' ) );
1065
1066 if ( $global_load_assets ) {
1067 wp_enqueue_style( 'wpzoom-forms-css-frontend-formblock' );
1068 wp_enqueue_script( 'wpzoom-forms-js-frontend-formblock' );
1069 }
1070
1071 }
1072
1073 /**
1074 * Enqueues needed scripts and styles for use on the frontend.
1075 *
1076 * @access public
1077 * @return void
1078 * @since 1.0.0
1079 */
1080 public function block_frontend_assets() {
1081
1082 if( self::has_block( 'wpzoom-forms/datepicker-field' ) || self::has_elementor_widget() || self::has_cpt_form_shortcode() ) {
1083 wp_enqueue_style( 'wpzoom-forms-css-frontend-flatpickr' );
1084 wp_enqueue_script( 'wpzoom-forms-js-frontend-flatpickr' );
1085 wp_enqueue_script( 'wpzoom-forms-js-frontend-datepicker' );
1086 }
1087
1088 }
1089
1090 // Check if a block exists in the post content
1091 static function has_block( $block_name ) {
1092
1093 if( ! has_block( 'wpzoom-forms/form-block' ) ) {
1094 return false;
1095 }
1096
1097 global $post;
1098
1099 $gutenberg_matches = array();
1100 $gutenberg_patern = '/<!--\s+wp:(wpzoom\-forms\/form\-block)(\s+(\{.*?\}))?\s+(\/)?-->/';
1101 preg_match_all( $gutenberg_patern, $post->post_content, $matches );
1102
1103 // Check if the block exists in the post content
1104 if ( isset( $matches[3] ) ) {
1105 foreach ( $matches[3] as $block_attributes_json ) {
1106 if ( ! empty( $block_attributes_json ) ) {
1107 $atts = json_decode( $block_attributes_json, true );
1108 }
1109 }
1110 }
1111
1112 $form_ID = isset( $atts['formId'] ) ? $atts['formId'] : '';
1113
1114 if( $form_ID ) {
1115 $form_post = get_post( $form_ID );
1116 if( has_block( $block_name, $form_post ) ) {
1117 return true;
1118 }
1119 }
1120
1121 return false;
1122
1123 }
1124
1125 // Check the form exists in the elementor posts content
1126 public static function has_elementor_widget( $post_id = 0 ) {
1127
1128 if ( ! defined( 'ELEMENTOR_VERSION' ) && ! is_callable( 'Elementor\Plugin::instance' ) ) {
1129 return false;
1130 }
1131
1132 $post_id = $post_id > 0 ? $post_id : get_the_ID();
1133
1134 $elementor_data = get_post_meta( $post_id, '_elementor_data' );
1135
1136 if ( isset( $elementor_data[0] ) && is_string( $elementor_data[0] ) ) {
1137
1138 $regExp = '/"widgetType":"([^"]*)/i';
1139 $outputArray = array();
1140
1141 // Define the pattern to match the shortcode
1142 $scPattern = '/\[wpzf_form([^\]]*)\]/';
1143 preg_match_all( $scPattern, $elementor_data[0], $matches );
1144
1145 // Check if any matches are found
1146 if ( ! empty( $matches[0] ) ) {
1147 return true;
1148 }
1149
1150 if ( preg_match_all( $regExp, $elementor_data[0], $outputArray, PREG_SET_ORDER) ) {}
1151
1152 foreach( $outputArray as $found ) {
1153 if( in_array( 'wpzoom-forms-widget-cpt', $found ) ) {
1154 return true;
1155 }
1156 }
1157
1158 }
1159
1160 return false;
1161 }
1162
1163 /**
1164 * Check the post content has cpt form shortcode
1165 *
1166 * @since 1.2.0
1167 * @param int $post_id The post ID.
1168 * @param boolean|int $content The post content.
1169 * @return boolean Return true if post content has cpt form shortcode, else return false.
1170 */
1171 public static function has_cpt_form_shortcode( $post_id = 0, $content = '' ) {
1172
1173 $post_id = $post_id > 0 ? $post_id : get_the_ID();
1174
1175 if ( empty( $content ) ) {
1176 $content = get_post_field( 'post_content', $post_id );
1177 }
1178
1179 if ( $content ) {
1180 if ( has_shortcode( $content, 'wpzf_form' ) ) {
1181 return true;
1182 }
1183 }
1184 return false;
1185 }
1186
1187 /**
1188 * Changes the row actions in post lists for certain post types.
1189 *
1190 * @access public
1191 * @param array $actions An array of all the possible post row actions.
1192 * @param WP_Post $post The post object for the post that is being displayed.
1193 * @return array
1194 * @since 1.0.0
1195 */
1196 public function modify_row_actions( $actions, $post ) {
1197 if ( 'wpzf-form' == $post->post_type || 'wpzf-submission' == $post->post_type ) {
1198 if ( 'wpzf-submission' == $post->post_type && isset( $actions['edit'] ) ) {
1199 $actions['edit'] = preg_replace( '/\<a([^>]+)\>(.+)\<\/a\>/i', '<a$1>' . __( 'View', 'wpzoom-forms' ) . '</a>', $actions['edit'] );
1200 }
1201
1202 if ( isset( $actions['inline hide-if-no-js'] ) ) {
1203 unset( $actions['inline hide-if-no-js'] );
1204 }
1205 }
1206
1207 return $actions;
1208 }
1209
1210 /**
1211 * Removes the Inline Edit option from post types it doesn't make sense for.
1212 *
1213 * @access public
1214 * @param array $actions An array of all the possible bulk actions.
1215 * @return array
1216 * @since 1.0.0
1217 */
1218 public function remove_bulk_actions( $actions ) {
1219 if ( isset( $actions['edit'] ) ) {
1220 unset( $actions['edit'] );
1221 }
1222
1223 return $actions;
1224 }
1225
1226 /**
1227 * Changes the columns displayed in the post list for the form custom post type.
1228 *
1229 * @access public
1230 * @param array $columns An array of all the columns.
1231 * @return array
1232 * @since 1.0.0
1233 */
1234 public function post_list_columns_form( $columns ) {
1235 return array(
1236 'cb' => $columns['cb'],
1237 'title' => __( 'Title', 'wpzoom-forms' ),
1238 'responses' => __( 'Responses', 'wpzoom-forms' ),
1239 'shortcode' => __( 'Shortcode', 'wpzoom-forms' ),
1240 'date' => $columns['date']
1241 );
1242 }
1243
1244 /**
1245 * Changes the columns displayed in the post list for the submissions custom post type.
1246 *
1247 * @access public
1248 * @param array $columns An array of all the columns.
1249 * @return array
1250 * @since 1.0.0
1251 */
1252 public function post_list_columns_submit( $columns ) {
1253 return array(
1254 'cb' => $columns['cb'],
1255 'submission_id' => __( 'ID', 'wpzoom-forms' ),
1256 'name' => __( 'Name', 'wpzoom-forms' ),
1257 'email' => __( 'Email', 'wpzoom-forms' ),
1258 'subject' => __( 'Subject', 'wpzoom-forms' ),
1259 'form' => __( 'Form', 'wpzoom-forms' ),
1260 'date' => $columns['date']
1261 );
1262 }
1263
1264 /**
1265 * Changes the sortable columns displayed in the post list for the submissions custom post type.
1266 *
1267 * @access public
1268 * @param array $columns An array of all the sortable columns.
1269 * @return array
1270 * @since 1.0.0
1271 */
1272 public function post_list_sortable_columns_submit( $columns ) {
1273 $columns['submission_id'] = 'ID';
1274 $columns['form'] = 'wpzf_form';
1275
1276 return $columns;
1277 }
1278
1279 // Add the filter dropdown to the posts list
1280 public function custom_filter_by_form() {
1281 global $wpdb;
1282
1283 // Check if we are on the posts list page
1284 if ( isset( $_GET['post_type'] ) && $_GET['post_type'] == 'wpzf-submission' ) {
1285
1286 $form_id_filter_selected = isset( $_GET['form_id_filter'] ) ? $_GET['form_id_filter'] : '';
1287
1288 // Fetch unique form IDs and their corresponding titles from your database
1289 $form_ids_query = $wpdb->get_results("SELECT ID AS form_id, post_title
1290 FROM $wpdb->posts
1291 WHERE post_type = 'wpzf-form'
1292 AND post_status = 'publish'");
1293
1294
1295 // If we have forms, display the dropdown
1296 if ( ! empty( $form_ids_query ) ) {
1297 // Initialize an array to store unique form IDs
1298 $form_ids = array();
1299 ?>
1300 <select name="form_id_filter" id="form_id_filter">
1301 <option value=""><?php echo esc_html__( 'All Forms', 'wpzoom-forms' ); ?></option>
1302 <?php
1303 foreach ( $form_ids_query as $result ) {
1304
1305 // Check if the form ID already exists in the array
1306 if ( ! in_array( $result->form_id, $form_ids ) ) {
1307
1308 // Add form ID to the array to prevent duplication
1309 $form_ids[] = $result->form_id;
1310
1311 // Output the option with form ID as value and post title as option name
1312 echo '<option '. selected( $result->form_id, $form_id_filter_selected, true ) .' value="' . $result->form_id . '">' . $result->post_title . '</option>';
1313 }
1314 }
1315 ?>
1316 </select>
1317 <?php
1318 }
1319
1320
1321 }
1322 }
1323
1324 // Modify the query based on the selected form ID
1325 public function filter_posts_by_form ( $query ) {
1326
1327 global $pagenow;
1328
1329 // Check if we are on the posts list page and a form ID filter is set
1330 if ( is_admin() && $pagenow == 'edit.php' && isset( $_GET['form_id_filter'] ) && $_GET['form_id_filter'] != '' ) {
1331
1332 $query->query_vars['meta_key'] = '_wpzf_form_id';
1333 $query->query_vars['meta_value'] = $_GET['form_id_filter'];
1334
1335 }
1336 }
1337
1338 /**
1339 * Changes the column content displayed in the post list for the form custom post type.
1340 *
1341 * @access public
1342 * @param string $column The name of the column to display.
1343 * @param int $post_id The ID of the current post.
1344 * @return array
1345 * @since 1.0.0
1346 */
1347 public function post_list_custom_columns_form( $column, $post_id ) {
1348
1349 if ( 'shortcode' == $column ) {
1350 printf( '<input type="text" value="[wpzf_form id=&quot;%d&quot;]" readonly />', absint( $post_id ) );
1351 }
1352
1353 if( 'responses' == $column ) {
1354
1355 $args = array(
1356 'post_type' => 'wpzf-submission',
1357 'post_status' => 'publish',
1358 'posts_per_page' => -1,
1359 'meta_query' => array(
1360 array(
1361 'key' => '_wpzf_form_id',
1362 'value' => $post_id,
1363 'compare' => '='
1364 )
1365 )
1366 );
1367
1368 $forms_count = 0;
1369
1370 $responses = new WP_Query( $args );
1371
1372 if( $responses && $responses->have_posts() ) {
1373 $forms_count = $responses->post_count;
1374 }
1375
1376 if( 0 < $forms_count ) {
1377 printf( '<a href="%s">%s</a>', admin_url( 'edit.php?post_type=wpzf-submission&&form_id_filter=' . $post_id ), $forms_count );
1378 } else {
1379 echo $forms_count;
1380 }
1381
1382 }
1383 }
1384
1385 /**
1386 * Changes the column content displayed in the post list for the submissions custom post type.
1387 *
1388 * @access public
1389 * @param string $column The name of the column to display.
1390 * @param int $post_id The ID of the current post.
1391 * @return array
1392 * @since 1.0.0
1393 */
1394 public function post_list_custom_columns_submit( $column, $post_id ) {
1395 $fields = get_post_meta( $post_id, '_wpzf_fields', true );
1396
1397 switch ( $column ) {
1398 case 'submission_id':
1399 printf(
1400 '<strong><a href="%s" class="row-title">%s #%s</a></strong>',
1401 esc_url( get_edit_post_link( $post_id ) ),
1402 esc_html__( 'Submission', 'wpzoom-forms' ),
1403 esc_html( $post_id )
1404 );
1405 break;
1406
1407 case 'name':
1408 $name_found = false;
1409
1410 if ( ! empty( $fields ) && is_array( $fields ) ) {
1411 // Common name field patterns
1412 $name_patterns = array( 'name', 'full_name', 'first_name', 'last_name', 'fullname', 'your_name' );
1413
1414 foreach ( $fields as $name => $value ) {
1415 $field_name = strtolower( $name );
1416 foreach ( $name_patterns as $pattern ) {
1417 if ( strpos( $field_name, $pattern ) !== false && ! empty( $value ) ) {
1418 echo '<strong>' . esc_html( $value ) . '</strong>';
1419 $name_found = true;
1420 break 2;
1421 }
1422 }
1423 }
1424 }
1425
1426 if ( ! $name_found ) {
1427 echo '&mdash;';
1428 }
1429 break;
1430
1431 case 'email':
1432 $email_found = false;
1433
1434 if ( ! empty( $fields ) && is_array( $fields ) ) {
1435 // Common email field patterns
1436 $email_patterns = array( 'email', 'e-mail', 'mail', 'email_address', 'your_email' );
1437
1438 foreach ( $fields as $name => $value ) {
1439 $field_name = strtolower( $name );
1440 foreach ( $email_patterns as $pattern ) {
1441 if ( strpos( $field_name, $pattern ) !== false && ! empty( $value ) ) {
1442 echo '<a href="mailto:' . esc_attr( $value ) . '">' . esc_html( $value ) . '</a>';
1443 $email_found = true;
1444 break 2;
1445 }
1446 }
1447 }
1448 }
1449
1450 if ( ! $email_found ) {
1451 echo '&mdash;';
1452 }
1453 break;
1454
1455 case 'subject':
1456 $subject_found = false;
1457
1458 if ( ! empty( $fields ) && is_array( $fields ) ) {
1459 // First try to find a subject field
1460 foreach ( $fields as $name => $value ) {
1461 if ( stripos( $name, 'subject' ) !== false || stripos( $name, 'title' ) !== false || stripos( $name, 'topic' ) !== false ) {
1462 echo esc_html( $value );
1463 $subject_found = true;
1464 break;
1465 }
1466 }
1467
1468 // If no subject field found, show first non-empty field (likely message)
1469 if ( ! $subject_found ) {
1470 foreach ( $fields as $name => $value ) {
1471 // Skip name and email fields
1472 $field_name = strtolower( $name );
1473 if ( strpos( $field_name, 'name' ) !== false || strpos( $field_name, 'email' ) !== false ) {
1474 continue;
1475 }
1476 if ( ! empty( $value ) ) {
1477 echo esc_html( wp_trim_words( $value, 10, '...' ) );
1478 $subject_found = true;
1479 break;
1480 }
1481 }
1482 }
1483 }
1484
1485 if ( ! $subject_found ) {
1486 echo '&mdash;';
1487 }
1488 break;
1489
1490 case 'form':
1491 $form_id = intval( get_post_meta( $post_id, '_wpzf_form_id', true ) );
1492 $form_name = __( '[Unknown]', 'wpzoom-forms' );
1493
1494 if ( $form_id > 0 ) {
1495 $form_post = get_post( $form_id );
1496
1497 if ( ! is_null( $form_post ) && 'wpzf-form' === $form_post->post_type ) {
1498 $form_title = get_the_title( $form_id );
1499
1500 if ( ! empty( $form_title ) && 'trash' !== $form_post->post_status ) {
1501 $form_name = '<a href="' . esc_url( get_edit_post_link( $form_id ) ) . '">' . esc_html( $form_title ) . '</a>';
1502 } elseif ( 'trash' === $form_post->post_status ) {
1503 $form_name = sprintf(
1504 '<span style="color: #d63638;">%s</span> <small>(%s)</small>',
1505 esc_html( $form_title ?: __( 'Untitled Form', 'wpzoom-forms' ) ),
1506 esc_html__( 'Trashed', 'wpzoom-forms' )
1507 );
1508 }
1509 }
1510 }
1511
1512 echo wp_kses( $form_name, array( 'a' => array( 'href' => array() ), 'span' => array( 'style' => array() ), 'small' => array() ) );
1513 break;
1514 }
1515 }
1516
1517 /**
1518 * Sets what the primary column is in the post list for certain post types.
1519 *
1520 * @access public
1521 * @param string $default The default/primary column.
1522 * @param string $screen Which screen is currently being rendered.
1523 * @return array
1524 * @since 1.0.0
1525 */
1526 public function post_list_primary_column( $default, $screen ) {
1527 if ( 'edit-wpzf-submission' == $screen ) {
1528 $default = 'submission_id';
1529 }
1530
1531 return $default;
1532 }
1533
1534 /**
1535 * Handles sorting of custom columns in the post list for certain post types.
1536 *
1537 * @access public
1538 * @param WP_Query $query The current query.
1539 * @return array
1540 * @since 1.0.0
1541 */
1542 public function sort_custom_column( $query ) {
1543 $orderby = $query->get( 'orderby' );
1544
1545 if ( 'wpzf_desc' == $orderby || 'wpzf_form' == $orderby ) {
1546 $key = 'wpzf_desc' == $orderby ? '_wpzf_fields' : '_wpzf_form_id';
1547
1548 $query->set( 'meta_query', array(
1549 'relation' => 'OR',
1550 array(
1551 'key' => $key,
1552 'compare' => 'NOT EXISTS'
1553 ),
1554 array(
1555 'key' => $key
1556 )
1557 ) );
1558
1559 $query->set( 'orderby', 'meta_value' );
1560 }
1561 }
1562
1563 /**
1564 * Changes things on the edit screen of certain post types.
1565 *
1566 * @access public
1567 * @return void
1568 * @since 1.0.0
1569 */
1570 public function remove_meta_boxes() {
1571 global $current_screen;
1572
1573 if ( 'wpzf-submission' == $current_screen->post_type && 'wpzf-submission' == $current_screen->id ) {
1574 global $wp_meta_boxes;
1575
1576 $wp_meta_boxes = array(
1577 'wpzf-submission' => array(
1578 'advanced' => array(),
1579 'side' => array(
1580 'high' => array(
1581 'wpzf-submission-details-mb' => $wp_meta_boxes['wpzf-submission']['side']['high']['wpzf-submission-details-mb']
1582 )
1583 ),
1584 'normal' => array(
1585 'high' => array(
1586 'wpzf-submission-mb' => $wp_meta_boxes['wpzf-submission']['normal']['high']['wpzf-submission-mb']
1587 )
1588 )
1589 )
1590 );
1591
1592 add_screen_option( 'layout_columns', array( 'max' => 2, 'default' => 2 ) );
1593 }
1594 }
1595
1596 /**
1597 * Removes screen options where not needed.
1598 *
1599 * @access public
1600 * @param bool $show_screen Whether to show Screen Options tab.
1601 * @param WP_Screen $screen Current WP_Screen instance.
1602 * @return bool
1603 * @since 1.0.0
1604 */
1605 public function remove_screen_options( $show_screen, $screen ) {
1606 return 'wpzf-submission' == $screen->post_type && 'post' == $screen->base ? false : $show_screen;
1607 }
1608
1609 /**
1610 * Changes the views items for certain post types.
1611 *
1612 * @access public
1613 * @param array $views All the possible views.
1614 * @return bool
1615 * @since 1.0.0
1616 */
1617 public function post_list_views( $views ) {
1618 if ( isset( $views['publish'] ) ) {
1619 unset( $views['publish'] );
1620 }
1621
1622 $current_page = get_current_screen()->id;
1623
1624 ?>
1625 <div class="wpzf_wrap wpzf_settings-add-new">
1626 <?php
1627 if ( 'edit-wpzf-form' == $current_page ) {
1628 echo '<a href="' . esc_url( admin_url( 'post-new.php?post_type=wpzf-form' ) ) . '" class="button-primary">' . __( 'Add new form', 'wpzoom-forms' ) . '</a>';
1629 }
1630 ?>
1631 </div>
1632 <?php
1633
1634 return $views;
1635 }
1636
1637 /**
1638 * Adds custom meta boxes to certain post types.
1639 *
1640 * @access public
1641 * @return void
1642 * @since 1.0.0
1643 */
1644 public function add_meta_boxes() {
1645 add_meta_box(
1646 'wpzf-submission-details-mb',
1647 __( 'Submission Details', 'wpzoom-forms' ),
1648 array( $this, 'submission_meta_box_details' ),
1649 'wpzf-submission',
1650 'side',
1651 'high'
1652 );
1653
1654 add_meta_box(
1655 'wpzf-submission-mb',
1656 __( 'Submission', 'wpzoom-forms' ),
1657 array( $this, 'submission_meta_box' ),
1658 'wpzf-submission',
1659 'normal',
1660 'high'
1661 );
1662 }
1663
1664 /**
1665 * Outputs the content for the submission details sidebar meta box.
1666 *
1667 * @access public
1668 * @return void
1669 * @since 1.3.5
1670 */
1671 public function submission_meta_box_details() {
1672 $post_id = get_the_ID();
1673 $post = get_post( $post_id );
1674
1675 // Get form info
1676 $form_id = get_post_meta( $post_id, '_wpzf_form_id', true );
1677 $form_name = __( '[Unknown]', 'wpzoom-forms' );
1678 $show_form_link = false;
1679
1680 if ( $form_id > 0 ) {
1681 $form_post = get_post( $form_id );
1682
1683 if ( ! is_null( $form_post ) && 'wpzf-form' === $form_post->post_type ) {
1684 $form_title = get_the_title( $form_id );
1685
1686 if ( ! empty( $form_title ) && 'trash' !== $form_post->post_status ) {
1687 $form_name = $form_title;
1688 $show_form_link = true;
1689 } elseif ( 'trash' === $form_post->post_status ) {
1690 $form_name = sprintf(
1691 '<span style="color: #d63638;">%s</span> <small>(%s)</small>',
1692 $form_title ?: __( 'Untitled Form', 'wpzoom-forms' ),
1693 __( 'Trashed', 'wpzoom-forms' )
1694 );
1695 }
1696 }
1697 }
1698
1699 // Get submission date
1700 $date = sprintf(
1701 __( 'Submitted on %1$s at %2$s', 'wpzoom-forms' ),
1702 get_the_date(),
1703 get_the_time()
1704 );
1705
1706 // Get trash/delete link
1707 $delete_link = '';
1708 $action_text = '';
1709 if ( current_user_can( 'delete_post', $post_id ) ) {
1710 if ( 'trash' === $post->post_status ) {
1711 $delete_link = get_delete_post_link( $post_id, '', true );
1712 $action_text = __( 'Delete Permanently', 'wpzoom-forms' );
1713 } else {
1714 $delete_link = get_delete_post_link( $post_id );
1715 $action_text = _x( 'Move to Trash', 'verb', 'wpzoom-forms' );
1716 }
1717 }
1718
1719 // Output the details
1720 ?>
1721 <style>
1722 #wpzf-submission-details-mb {
1723 display: block !important;
1724 }
1725 #wpzf-submission-details-mb .inside {
1726 margin: 0;
1727 padding: 0;
1728 }
1729 .wpzf-submission-details {
1730 margin: 0;
1731 padding: 0;
1732 list-style: none;
1733 display: block !important;
1734 }
1735 .wpzf-submission-details li {
1736 margin: 0;
1737 padding: 8px 12px;
1738 border-bottom: 1px solid #eee;
1739 display: block;
1740 line-height: 1.5;
1741 }
1742 .wpzf-submission-details li:last-child {
1743 border-bottom: none;
1744 }
1745 .wpzf-submission-details li strong {
1746 display: block;
1747 margin-bottom: 4px;
1748 color: #1d2327;
1749 }
1750 .wpzf-submission-details .wpzf-submission-actions {
1751 padding: 12px;
1752 }
1753 .wpzf-submission-details .submitdelete {
1754 color: #b32d2e;
1755 text-decoration: none;
1756 }
1757 .wpzf-submission-details .submitdelete:hover {
1758 color: #a00;
1759 }
1760 </style>
1761 <ul class="wpzf-submission-details">
1762 <li>
1763 <strong><?php esc_html_e( 'Submission ID:', 'wpzoom-forms' ); ?></strong>
1764 #<?php echo esc_html( $post_id ); ?>
1765 </li>
1766 <li>
1767 <strong><?php esc_html_e( 'Form:', 'wpzoom-forms' ); ?></strong>
1768 <?php if ( $show_form_link ) : ?>
1769 <a href="<?php echo esc_url( get_edit_post_link( $form_id ) ); ?>"><?php echo esc_html( $form_name ); ?></a>
1770 <?php else : ?>
1771 <?php echo wp_kses_post( $form_name ); ?>
1772 <?php endif; ?>
1773 </li>
1774 <li>
1775 <strong><?php esc_html_e( 'Date:', 'wpzoom-forms' ); ?></strong>
1776 <?php echo esc_html( $date ); ?>
1777 </li>
1778 <?php if ( ! empty( $delete_link ) ) : ?>
1779 <li class="wpzf-submission-actions">
1780 <a href="<?php echo esc_url( $delete_link ); ?>" class="submitdelete deletion">
1781 <?php echo esc_html( $action_text ); ?>
1782 </a>
1783 </li>
1784 <?php endif; ?>
1785 </ul>
1786 <?php
1787 }
1788
1789 /**
1790 * Outputs the content for the submission meta box.
1791 *
1792 * @access public
1793 * @return void
1794 * @since 1.0.0
1795 */
1796 public function submission_meta_box() {
1797 $fields = get_post_meta( get_the_ID(), '_wpzf_fields', true );
1798
1799 if ( ! is_null( $fields ) && false !== $fields && is_array( $fields ) && ! empty( $fields ) ) {
1800 echo '<ul class="wpzf-submission-view">';
1801
1802 $form_id = intval( get_post_meta( get_the_ID(), '_wpzf_form_id', true ) );
1803 $form_name = __( '[Unknown]', 'wpzoom-forms' );
1804
1805 if ( $form_id > 0 ) {
1806 $form_name = $form_id;
1807
1808 if ( ! is_null( get_post( $form_id ) ) ) {
1809 $form_name = '<a href="' . esc_url( get_edit_post_link( $form_id ) ) . '">' . get_the_title( $form_id ) . '</a>';
1810 }
1811 }
1812
1813 $form_name = wp_kses( $form_name, array( 'a' => array( 'href' => array() ) ) );
1814
1815 echo '<li class="top"><h3>' . sprintf( __( 'Form: %s', 'wpzoom-forms' ), $form_name ) . '</h3></li>';
1816
1817 foreach ( $fields as $name => $value ) {
1818 echo '<li><h3>' . esc_html( $name ) . '</h3><div>' . make_clickable( nl2br( esc_html( $value ) ) ) . '</div></li>';
1819 }
1820
1821 echo '</ul>';
1822
1823 return;
1824 }
1825
1826 printf( '<p class="empty">%s</p>', __( 'Submission is empty&hellip;', 'wpzoom-forms' ) );
1827 }
1828
1829 /**
1830 * Returns whether the current page is related to the given post type.
1831 *
1832 * @access public
1833 * @param string $type The type to check for.
1834 * @return bool
1835 * @since 1.0.0
1836 */
1837 public function is_post_type( $type ) {
1838 $typenow = '';
1839
1840 if ( is_admin() && current_user_can( 'edit_posts' ) ) {
1841 if ( isset( $_REQUEST['post_type'] ) && post_type_exists( sanitize_key( $_REQUEST['post_type'] ) ) ) {
1842 $typenow = sanitize_key( $_REQUEST['post_type'] );
1843 } else {
1844 $post_id = -1;
1845
1846 if ( isset( $_GET['post'] ) && isset( $_POST['post_ID'] ) && (int) $_GET['post'] !== (int) $_POST['post_ID'] ) {
1847 // Do nothing
1848 } elseif ( isset( $_GET['post'] ) ) {
1849 $post_id = (int) $_GET['post'];
1850 } elseif ( isset( $_POST['post_ID'] ) ) {
1851 $post_id = (int) $_POST['post_ID'];
1852 }
1853
1854 if ( $post_id > -1 ) {
1855 $post = get_post( $post_id );
1856
1857 if ( ! is_null( $post ) && $post instanceof WP_Post ) {
1858 $typenow = $post->post_type;
1859 }
1860 }
1861 }
1862 }
1863
1864 return $type == $typenow;
1865 }
1866
1867 /**
1868 * Called to render the form block on the frontend.
1869 *
1870 * @access public
1871 * @param array $attributes An array containing the attributes for the block.
1872 * @param string $content A string containing the content of the block.
1873 * @param WP_Block $block The WP_Block representation of the block.
1874 * @return string
1875 * @since 1.0.0
1876 */
1877 public function form_block_render( $attributes, $content = '', $block = null ) {
1878 global $current_screen;
1879
1880 $current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
1881
1882 // Allow rendering during REST API requests (ServerSideRender / block preview)
1883 // and during Elementor's AJAX-driven widget preview in the editor.
1884 $is_rest = defined( 'REST_REQUEST' ) && REST_REQUEST;
1885 $is_elementor_ajax = defined( 'DOING_AJAX' ) && DOING_AJAX && isset( $_REQUEST['action'] ) && strpos( sanitize_key( wp_unslash( $_REQUEST['action'] ) ), 'elementor' ) === 0; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1886 if ( ( is_admin() && ! $is_rest && ! $is_elementor_ajax ) || ( ! is_null( $current_screen ) && $current_screen->is_block_editor() ) ) return '';
1887
1888 // Get form ID and validate form exists and is published
1889 $form_id = isset( $attributes['formId'] ) ? intval( $attributes['formId'] ) : 0;
1890 $form_post = get_post( $form_id );
1891
1892 // Check if form exists and is published
1893 if ( ! $form_post || 'wpzf-form' !== $form_post->post_type || 'publish' !== $form_post->post_status ) {
1894 // Show message only to administrators
1895 if ( current_user_can( 'manage_options' ) ) {
1896 $message = '';
1897 if ( ! $form_post ) {
1898 $message = sprintf(
1899 __( 'Contact form not found (ID: %d). Please select a different form or create a new one.', 'wpzoom-forms' ),
1900 $form_id
1901 );
1902 } elseif ( 'wpzf-form' !== $form_post->post_type ) {
1903 $message = sprintf(
1904 __( 'Invalid form type (ID: %d). Please select a valid WPZOOM form.', 'wpzoom-forms' ),
1905 $form_id
1906 );
1907 } elseif ( 'trash' === $form_post->post_status ) {
1908 $message = sprintf(
1909 __( 'Contact form "%s" is in trash (ID: %d). Please restore it or select a different form.', 'wpzoom-forms' ),
1910 $form_post->post_title,
1911 $form_id
1912 );
1913 } else {
1914 $message = sprintf(
1915 __( 'Contact form "%s" is not published (ID: %d). Please publish it or select a different form.', 'wpzoom-forms' ),
1916 $form_post->post_title,
1917 $form_id
1918 );
1919 }
1920
1921 return sprintf(
1922 '<div class="wpzoom-forms-admin-notice" style="background: #fff; border: 1px solid #c3c4c7; border-left: 4px solid #d63638; box-shadow: 0 1px 1px rgba(0,0,0,0.04); padding: 1em 12px; margin: 1em 0;">
1923 <p style="margin: 0; font-size: 14px; color: #d63638;">
1924 <strong>%s:</strong> %s
1925 </p>
1926 <p style="margin: 8px 0 0 0; font-size: 13px; color: #646970;">
1927 <a href="%s" target="_blank">%s</a>
1928 </p>
1929 </div>',
1930 esc_html__( 'WPZOOM Forms Admin Notice', 'wpzoom-forms' ),
1931 esc_html( $message ),
1932 esc_url( admin_url( 'edit.php?post_type=wpzf-form' ) ),
1933 esc_html__( 'Manage Forms', 'wpzoom-forms' )
1934 );
1935 }
1936
1937 // Don't show anything to non-administrators
1938 return '';
1939 }
1940
1941 $align = isset( $attributes['align'] ) && ! empty( $attributes['align'] ) ? $attributes['align'] : 'none';
1942
1943 //Get styles from the block
1944
1945 $fieldBgColor = isset( $attributes['fieldBgColor'] ) ? $attributes['fieldBgColor'] : '';
1946 $fieldBrdStyle = isset( $attributes['fieldBrdStyle'] ) ? $attributes['fieldBrdStyle'] : '';
1947 $fieldBrdWidth = isset( $attributes['fieldBrdWidth'] ) ? $attributes['fieldBrdWidth'] . 'px' : '';
1948 $fieldBrdRadius = isset( $attributes['fieldBrdRadius'] ) ? $attributes['fieldBrdRadius'] . 'px' : '';
1949 $fieldBrdColor = isset( $attributes['fieldBrdColor'] ) ? $attributes['fieldBrdColor'] : '';
1950 $fieldTextColor = isset( $attributes['fieldTextColor'] ) ? $attributes['fieldTextColor'] : '';
1951 $labelTextColor = isset( $attributes['labelTextColor'] ) ? $attributes['labelTextColor'] : '';
1952 $btnBrdRadius = isset( $attributes['btnBrdRadius'] ) ? $attributes['btnBrdRadius'] . 'px' : '';
1953 $btnBrdStyle = isset( $attributes['btnBrdStyle'] ) ? $attributes['btnBrdStyle'] : '';
1954 $btnTextColor = isset( $attributes['btnTextColor'] ) ? $attributes['btnTextColor'] : '';
1955 $btnBrdWidth = isset( $attributes['btnBrdWidth'] ) ? $attributes['btnBrdWidth']. 'px' : '';
1956 $btnBrdColor = isset( $attributes['btnBrdColor'] ) ? $attributes['btnBrdColor'] : '';
1957 $btnBgColor = isset( $attributes['btnBgColor'] ) ? $attributes['btnBgColor'] : '';
1958
1959 $form_ID = 'wpzf-' . intval( $attributes['formId'] );
1960 $form_notice_id = $form_ID . '-notice';
1961 $form_success_message = get_post_meta( intval( $attributes['formId'] ), '_form_success_message', true );
1962 $form_failure_message = get_post_meta( intval( $attributes['formId'] ), '_form_failure_message', true );
1963
1964 // Use default messages if custom ones are not set
1965 if ( empty( $form_success_message ) ) {
1966 $form_success_message = __( 'Thanks! We\'ve received your submission!', 'wpzoom-forms' );
1967 }
1968 if ( empty( $form_failure_message ) ) {
1969 $form_failure_message = __( 'Submission failed!', 'wpzoom-forms' );
1970 }
1971
1972 // Enqueue core block library styles for inner blocks (columns, group, spacer, etc.)
1973 wp_enqueue_style( 'wp-block-library' );
1974
1975 // Process form content through do_blocks() to render inner blocks and enqueue their styles
1976 $form_content = get_post_field( 'post_content', intval( $attributes['formId'] ), 'raw' );
1977 $form_content = do_blocks( $form_content );
1978 $form_content = preg_replace(
1979 array( '/<!--(.*)-->/Uis', '/<(input|textarea|select)(.*)name="([^"]+)"/Uis' ),
1980 array( '', '<$1$2name="wpzf_$3"' ),
1981 $form_content
1982 );
1983
1984 // If this (legacy) form actually contains a date field, make sure the
1985 // datepicker assets load. We enqueue here — at render time — rather than
1986 // relying solely on the page-scan in block_frontend_assets(), which only
1987 // inspects a single form-block per page and misses multi-form pages,
1988 // shortcode and Elementor embeds. The scripts are registered in_footer so
1989 // this late enqueue still prints correctly.
1990 if ( false !== strpos( $form_content, 'data-datepicker' ) ) {
1991 wp_enqueue_style( 'wpzoom-forms-css-frontend-flatpickr' );
1992 wp_enqueue_script( 'wpzoom-forms-js-frontend-flatpickr' );
1993 wp_enqueue_script( 'wpzoom-forms-js-frontend-datepicker' );
1994 }
1995
1996 $submitted_form_id = isset( $_GET['wpzf_submitted_form'] ) ? intval( $_GET['wpzf_submitted_form'] ) : -1;
1997 $show_notice = isset( $_GET['success'] ) && ( -1 === $submitted_form_id || $submitted_form_id === intval( $attributes['formId'] ) );
1998
1999 $content = sprintf(
2000 '<!-- ZOOM Forms Start -->
2001 <form id="wpzf-%2$s" method="post" action="%1$s" class="wpzoom-forms_form%6$s">
2002 <input type="hidden" name="action" value="wpzf_submit" />
2003 <input type="hidden" name="form_id" value="%2$s" />
2004 %3$s
2005 %5$s
2006 </form>
2007 %4$s
2008 <!-- ZOOM Forms End -->',
2009 admin_url( 'admin-post.php' ),
2010 intval( $attributes['formId'] ),
2011 wp_nonce_field( 'wpzf_submit', '_wpnonce', true, false ),
2012 ( $show_notice
2013 ? '<div id="' . esc_attr( $form_notice_id ) . '" class="notice ' . ( '1' == $_GET['success'] ? 'success' : 'error' ) . '" tabindex="-1"><p>' .
2014 ( '1' == $_GET['success'] ? wp_kses_post($form_success_message) : wp_kses_post($form_failure_message) ) .
2015 '</p></div>'
2016 : ''
2017 ),
2018 $form_content,
2019 ( 'none' !== $align ? ' align' . $align : '' )
2020 );
2021
2022 preg_match( '/<input(?:.*)name="([^"]+)"(?:.*)data-replyto="true"/is', $content, $match1 );
2023 preg_match( '/<input(?:.*)name="([^"]+)"(?:.*)data-subject="true"/is', $content, $match2 );
2024
2025 if ( ! empty( $match1 ) && is_array( $match1 ) && isset( $match1[1] ) ) {
2026 $content = preg_replace( '/<\/form>/is', '<input type="hidden" name="wpzf_replyto" value="' . $match1[1] . '" /></form>', $content );
2027 }
2028
2029 if ( ! empty( $match2 ) && is_array( $match2 ) && isset( $match2[1] ) ) {
2030 $content = preg_replace( '/<\/form>/is', '<input type="hidden" name="wpzf_subject" value="' . $match2[1] . '" /></form>', $content );
2031 }
2032
2033 $captcha_config = $this->get_spam_protection_config();
2034 $captcha_method = $captcha_config['active_service'];
2035 $recaptcha_type = $captcha_config['type'];
2036 $recaptcha_badge_location = esc_attr( sanitize_text_field( WPZOOM_Forms_Settings::get( 'wpzf_global_recaptcha_badge_location' ) ) );
2037 $turnstile_site_key = esc_attr( $captcha_config['turnstile_site_key'] );
2038 $turnstile_widget_theme = esc_attr( sanitize_text_field( WPZOOM_Forms_Settings::get( 'wpzf_global_turnstile_widget_theme' ) ) );
2039
2040 if ( 'recaptcha' === $captcha_method ) {
2041 $recaptcha_site_key = ( 'v3' === $recaptcha_type ) ? $captcha_config['recaptcha_v3_site_key'] : $captcha_config['recaptcha_v2_site_key'];
2042 $data_badge_location = '';
2043 if ( ! empty( $recaptcha_badge_location ) ) {
2044 $data_badge_location = 'data-badge="' . $recaptcha_badge_location . '"';
2045 }
2046
2047 $content = preg_replace( '/<input([^>]*)type="submit"([^>]*)class="([^"]+)"/i', '<input $1 type="submit" data-sitekey="' . $recaptcha_site_key . '" data-callback="wpzf_submit" data-action="submit" ' . $data_badge_location . ' $2 class="$3 g-recaptcha"', $content );
2048 } elseif ( 'turnstile' === $captcha_method ) {
2049 $turnstile_widget = '<div class="cf-turnstile" data-theme="' . $turnstile_widget_theme . '" data-sitekey="' . $turnstile_site_key . '"></div>';
2050 $content = preg_replace( '/<input([^>]*)type="submit"([^>]*)class="([^"]+)"([^>]*)>/i', '<input $1 type="submit" data-callback="wpzf_submit" data-action="submit" $2 class="$3 cf-captcha" $4>' . $turnstile_widget, $content );
2051 }
2052
2053 $style = $styleOutput = '';
2054
2055 // Add custom styles to the form
2056 $field_sel = '#' . $form_ID . ' input:not([type="submit"]), #' . $form_ID . ' textarea, #' . $form_ID . ' select';
2057 if( ! empty( $fieldBgColor ) ) {
2058 $styleOutput .= sprintf( $field_sel . ' { background-color: %s; }', $fieldBgColor );
2059 }
2060 if( ! empty( $fieldBrdStyle ) && 'default' !== $fieldBrdStyle ) {
2061 $styleOutput .= sprintf( $field_sel . ' { border-style: %s; }', $fieldBrdStyle );
2062 if( ! empty( $fieldBrdWidth ) ) {
2063 $styleOutput .= sprintf( $field_sel . ' { border-width: %s; }', $fieldBrdWidth );
2064 }
2065 if( ! empty( $fieldBrdRadius ) ) {
2066 $styleOutput .= sprintf( $field_sel . ' { border-radius: %s; }', $fieldBrdRadius );
2067 }
2068 }
2069
2070 if( ! empty( $fieldBrdColor ) ) {
2071 $styleOutput .= sprintf( $field_sel . ' { border-color: %s; }', $fieldBrdColor );
2072 }
2073 if( ! empty( $fieldTextColor ) ) {
2074 $styleOutput .= sprintf( $field_sel . ' { color: %s; }', $fieldTextColor );
2075 }
2076
2077 //Label styles
2078 if( ! empty( $labelTextColor ) ) {
2079 $styleOutput .= sprintf( '#' . $form_ID . ' label { color: %s; }', $labelTextColor );
2080 }
2081
2082 //Button styles — target both old <input type="submit"> and new builder's <button class="wpzf-submit__btn">
2083 $btn_sel = '#' . $form_ID . ' input[type="submit"], #' . $form_ID . ' button.wpzf-submit__btn';
2084 if( ! empty( $btnBrdStyle ) && 'default' !== $btnBrdStyle ) {
2085 $styleOutput .= sprintf( $btn_sel . ' { border-style: %s; }', $btnBrdStyle );
2086 if( ! empty( $btnBrdWidth ) ) {
2087 $styleOutput .= sprintf( $btn_sel . ' { border-width: %s; }', $btnBrdWidth );
2088 }
2089 if( ! empty( $btnBrdRadius ) ) {
2090 $styleOutput .= sprintf( $btn_sel . ' { border-radius: %s; }', $btnBrdRadius );
2091 }
2092 }
2093 if( ! empty( $btnTextColor ) ) {
2094 $styleOutput .= sprintf( $btn_sel . ' { color: %s; }', $btnTextColor );
2095 }
2096 if( ! empty( $btnBrdColor ) ) {
2097 $styleOutput .= sprintf( $btn_sel . ' { border-color: %s; }', $btnBrdColor );
2098 }
2099 if( ! empty( $btnBgColor ) ) {
2100 $styleOutput .= sprintf( $btn_sel . ' { background-color: %s; }', $btnBgColor );
2101 }
2102
2103 // Add styles for the notice
2104 $styleOutput .= sprintf( '#%s + .notice { margin-top: 20px; padding: 15px; border-radius: 4px; }', $form_ID );
2105 $styleOutput .= sprintf( '#%s + .notice.success { background-color: #e7f7ed; color: #227045; border-left: 4px solid #46b450; }', $form_ID );
2106 $styleOutput .= sprintf( '#%s + .notice.error { background-color: #fde8e8; color: #8a1f11; border-left: 4px solid #cc0000; }', $form_ID );
2107 $styleOutput .= sprintf( '#%s + .notice p { margin: 0; }', $form_ID );
2108 $styleOutput .= sprintf( '#%s { scroll-margin-top: 24px; }', $form_notice_id );
2109
2110 $style = sprintf( '<style>%s</style>',
2111 $styleOutput
2112 );
2113
2114 $content = $content . $style;
2115
2116 return $content;
2117 }
2118
2119 /**
2120 * Render the contents of the settings page in the admin.
2121 *
2122 * @access public
2123 * @return void
2124 * @since 1.0.0
2125 */
2126 public function render_settings_page() {
2127 do_action( 'wpzoom_forms_admin_page' );
2128 }
2129
2130 /**
2131 * Render the contents of the upsell page in the admin.
2132 *
2133 * @access public
2134 * @return void
2135 * @since 1.0.0
2136 */
2137 public function render_upsell_page() {
2138 do_action( 'wpzoom_forms_admin_page_upsell' );
2139 }
2140
2141 /**
2142 * Render the Import & Export upsell page.
2143 *
2144 * @access public
2145 * @return void
2146 * @since 1.3.4
2147 */
2148 public function render_import_export_upsell_page() {
2149 if ( ! current_user_can( 'edit_posts' ) ) {
2150 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wpzoom-forms' ) );
2151 }
2152
2153 $pro_url = 'https://www.wpzoom.com/plugins/wpzoom-forms/?utm_source=wpadmin&utm_medium=wpzoom-forms-free&utm_campaign=import-export-upsell';
2154 ?>
2155 <div class="wrap wpzoom-forms-import-export-upsell-page" style="position: relative;">
2156 <h1><?php esc_html_e( 'Import & Export Forms', 'wpzoom-forms' ); ?></h1>
2157 <div class="wpzoom-forms-import-export-upsell-container">
2158 <!-- Blurred placeholder content -->
2159 <div class="wpzoom-forms-import-export-blurred-content" style="filter: blur(4px); pointer-events: none; opacity: 0.3; margin-bottom: 40px;">
2160 <div style="background: #fff; border: 1px solid #c3c4c7; padding: 20px; margin: 20px 0; border-radius: 4px;">
2161 <div style="display: flex; gap: 20px; margin-bottom: 30px;">
2162 <div style="flex: 1;">
2163 <label style="display: block; margin-bottom: 8px; font-weight: 600; color: #1d2327;"><?php esc_html_e( 'Export Forms', 'wpzoom-forms' ); ?></label>
2164 <select style="width: 100%; padding: 8px; border: 1px solid #c3c4c7; border-radius: 4px;">
2165 <option><?php esc_html_e( 'Select forms to export...', 'wpzoom-forms' ); ?></option>
2166 <option><?php esc_html_e( 'Contact Form', 'wpzoom-forms' ); ?></option>
2167 <option><?php esc_html_e( 'Newsletter Form', 'wpzoom-forms' ); ?></option>
2168 </select>
2169 </div>
2170 <div style="flex: 1;">
2171 <label style="display: block; margin-bottom: 8px; font-weight: 600; color: #1d2327;"><?php esc_html_e( 'Export Format', 'wpzoom-forms' ); ?></label>
2172 <select style="width: 100%; padding: 8px; border: 1px solid #c3c4c7; border-radius: 4px;">
2173 <option>JSON</option>
2174 <option>CSV</option>
2175 </select>
2176 </div>
2177 </div>
2178 <div style="display: flex; gap: 20px; margin-bottom: 30px;">
2179 <div style="flex: 1;">
2180 <label style="display: block; margin-bottom: 8px; font-weight: 600; color: #1d2327;"><?php esc_html_e( 'Import Forms', 'wpzoom-forms' ); ?></label>
2181 <input type="file" style="width: 100%; padding: 8px; border: 1px solid #c3c4c7; border-radius: 4px;" />
2182 </div>
2183 <div style="flex: 1;">
2184 <label style="display: block; margin-bottom: 8px; font-weight: 600; color: #1d2327;"><?php esc_html_e( 'Export Submissions', 'wpzoom-forms' ); ?></label>
2185 <select style="width: 100%; padding: 8px; border: 1px solid #c3c4c7; border-radius: 4px;">
2186 <option><?php esc_html_e( 'Select form...', 'wpzoom-forms' ); ?></option>
2187 </select>
2188 </div>
2189 </div>
2190 <div style="display: flex; gap: 10px;">
2191 <button style="padding: 10px 20px; background: #2271b1; color: #fff; border: none; border-radius: 4px; cursor: pointer;"><?php esc_html_e( 'Export', 'wpzoom-forms' ); ?></button>
2192 <button style="padding: 10px 20px; background: #2271b1; color: #fff; border: none; border-radius: 4px; cursor: pointer;"><?php esc_html_e( 'Import', 'wpzoom-forms' ); ?></button>
2193 <button style="padding: 10px 20px; background: #2271b1; color: #fff; border: none; border-radius: 4px; cursor: pointer;"><?php esc_html_e( 'Export CSV', 'wpzoom-forms' ); ?></button>
2194 </div>
2195 </div>
2196 </div>
2197
2198 <!-- Overlay with Popover Modal -->
2199 <div class="import-export-upsell-overlay">
2200 <div class="wpzoom-forms-import-export-popover" style="background: #fff; border-radius: 8px; box-shadow: 0 4px 20px rgba(0,0,0,0.15); padding: 40px; max-width: 500px; width: 90%;">
2201 <!-- Icon -->
2202 <div style="text-align: center; margin-bottom: 20px;">
2203 <div style="width: 60px; height: 60px; margin: 0 auto; background-color: #3496FF; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 4px 12px rgba(52, 150, 255, 0.3);">
2204 <svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2205 <path d="M12 2L15.09 8.26L22 9.27L17 14.14L18.18 21.02L12 17.77L5.82 21.02L7 14.14L2 9.27L8.91 8.26L12 2Z" fill="#fff"/>
2206 </svg>
2207 </div>
2208 </div>
2209
2210 <!-- Title -->
2211 <h2 style="text-align: center; margin: 0 0 15px 0; font-size: 24px; font-weight: 600; color: #1d2327;">
2212 <?php esc_html_e( 'Unlock Import & Export', 'wpzoom-forms' ); ?>
2213 </h2>
2214
2215 <!-- Description -->
2216 <p style="text-align: center; margin: 0 0 25px 0; color: #50575e; font-size: 14px; line-height: 1.6;">
2217 <?php esc_html_e( 'Transfer your forms between sites, back up your work, and export submissions for analysis. Manage your forms with powerful import and export tools.', 'wpzoom-forms' ); ?>
2218 </p>
2219
2220 <!-- Features List -->
2221 <ul style="list-style: none; padding: 0; margin: 0 0 30px 0;">
2222 <li style="display: flex; align-items: flex-start; margin-bottom: 12px; color: #1d2327; font-size: 14px;">
2223 <span style="display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; background: #3496FF; border-radius: 50%; margin-right: 12px; flex-shrink: 0; margin-top: 2px;">
2224 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2225 <path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2226 </svg>
2227 </span>
2228 <span><?php esc_html_e( 'Export forms to JSON files for backup or migration', 'wpzoom-forms' ); ?></span>
2229 </li>
2230 <li style="display: flex; align-items: flex-start; margin-bottom: 12px; color: #1d2327; font-size: 14px;">
2231 <span style="display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; background: #3496FF; border-radius: 50%; margin-right: 12px; flex-shrink: 0; margin-top: 2px;">
2232 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2233 <path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2234 </svg>
2235 </span>
2236 <span><?php esc_html_e( 'Import forms from JSON files to quickly set up new sites', 'wpzoom-forms' ); ?></span>
2237 </li>
2238 <li style="display: flex; align-items: flex-start; margin-bottom: 12px; color: #1d2327; font-size: 14px;">
2239 <span style="display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; background: #3496FF; border-radius: 50%; margin-right: 12px; flex-shrink: 0; margin-top: 2px;">
2240 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2241 <path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2242 </svg>
2243 </span>
2244 <span><?php esc_html_e( 'Export form submissions to CSV for data analysis', 'wpzoom-forms' ); ?></span>
2245 </li>
2246 <li style="display: flex; align-items: flex-start; margin-bottom: 12px; color: #1d2327; font-size: 14px;">
2247 <span style="display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; background: #3496FF; border-radius: 50%; margin-right: 12px; flex-shrink: 0; margin-top: 2px;">
2248 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2249 <path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2250 </svg>
2251 </span>
2252 <span><?php esc_html_e( 'Transfer forms between multiple WordPress sites', 'wpzoom-forms' ); ?></span>
2253 </li>
2254 <li style="display: flex; align-items: flex-start; margin-bottom: 12px; color: #1d2327; font-size: 14px;">
2255 <span style="display: inline-flex; align-items: center; justify-content: center; width: 20px; height: 20px; background: #3496FF; border-radius: 50%; margin-right: 12px; flex-shrink: 0; margin-top: 2px;">
2256 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2257 <path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2258 </svg>
2259 </span>
2260 <span><?php esc_html_e( 'Backup your forms before making major changes', 'wpzoom-forms' ); ?></span>
2261 </li>
2262 </ul>
2263
2264 <!-- CTA Button -->
2265 <div style="text-align: center; margin-bottom: 15px;">
2266 <a href="<?php echo esc_url( $pro_url ); ?>" class="button button-primary button-large" style="background-color: #3496FF; border: none; border-radius: 6px; padding: 16px 20px; line-height: 1; font-size: 16px; font-weight: 600; color: #fff; text-decoration: none; display: inline-block; box-shadow: 0 4px 12px rgba(52, 150, 255, 0.3); transition: transform 0.2s, box-shadow 0.2s;" target="_blank" onmouseover="this.style.transform='translateY(-2px)'; this.style.boxShadow='0 6px 16px rgba(52, 150, 255, 0.4)';" onmouseout="this.style.transform='translateY(0)'; this.style.boxShadow='0 4px 12px rgba(52, 150, 255, 0.3)';">
2267 <?php esc_html_e( 'Upgrade to PRO', 'wpzoom-forms' ); ?>
2268 </a>
2269 </div>
2270
2271 <!-- Plugin Attribution -->
2272 <p style="text-align: center; margin: 0; color: #8c8f94; font-size: 12px;">
2273 <?php esc_html_e( 'Part of the WPZOOM Forms PRO plugin', 'wpzoom-forms' ); ?>
2274 </p>
2275 </div>
2276 </div>
2277 </di>
2278 </div>
2279
2280 <style>
2281 .wpzoom-forms-import-export-upsell-container {
2282 position: relative;
2283 min-height: 680px;
2284 }
2285 .wpzoom-forms-import-export-blurred-content {
2286 z-index: 1;
2287 }
2288 .import-export-upsell-overlay {
2289 position: absolute;
2290 top: 60px;
2291 left: 0;
2292 right: 0;
2293 bottom: 0;
2294 display: flex;
2295 align-items: flex-start;
2296 justify-content: center;
2297 padding-top: 80px;
2298 z-index: 100;
2299 background: hsla(0, 0%, 100%, .1);
2300 }
2301 .wpzoom-forms-import-export-popover {
2302 z-index: 10000;
2303 }
2304 @media screen and (max-width: 782px) {
2305 .wpzoom-forms-import-export-popover {
2306 padding: 30px 20px;
2307 max-width: 90%;
2308 }
2309 }
2310 </style>
2311 <?php
2312 }
2313
2314 /**
2315 * Page header used on all admin pages.
2316 *
2317 * @access public
2318 * @return void
2319 * @since 1.0.0
2320 */
2321 public function admin_page_header() {
2322
2323 $current_page = get_current_screen()->id;
2324
2325 if ( 'edit-wpzf-form' == $current_page || 'edit-wpzf-submission' == $current_page || 'wpzf-submission' == $current_page || 'wpzf-form_page_wpzf-settings' == $current_page || 'wpzf-form_page_wpzoom-forms-pro-license' == $current_page || 'wpzf-form_page_wpzf-pro-page' == $current_page ) {
2326 ?>
2327 <header class="wpzoom-new-admin-wrap wpzoom-new-admin_settings-header">
2328 <h1 class="wpzoom-new-admin_settings-main-title wp-heading">
2329 <?php
2330 echo apply_filters(
2331 'wpzf_admin-header-title',
2332 sprintf(
2333 __( 'WPZOOM Forms <small>Lite</small>', 'wpzoom-forms' )
2334 )
2335 );
2336 ?>
2337
2338 <span class="wpzoom-new-admin_settings-main-title-version">
2339 <?php
2340 echo apply_filters(
2341 'wpzf_admin-header-title-version',
2342 sprintf(
2343 esc_html__( 'v %s', 'wpzoom-forms' ),
2344 WPZOOM_FORMS_VERSION
2345 )
2346 );
2347 ?>
2348 </span>
2349 </h1>
2350
2351 <nav class="wpzoom-new-admin_settings-main-nav">
2352 <ul>
2353 <?php
2354 $pages = apply_filters(
2355 'wpzoom_forms_settings_menu_items',
2356 array(
2357 'edit-wpzf-form' => array(
2358 'name' => __( 'Forms', 'wpzoom-forms' ),
2359 'url' => admin_url( 'edit.php?post_type=wpzf-form' ),
2360 ),
2361 'edit-wpzf-submission' => array(
2362 'name' => __( 'Submissions', 'wpzoom-forms' ),
2363 'url' => admin_url( 'edit.php?post_type=wpzf-submission' ),
2364 'altid' => 'wpzf-submission'
2365 ),
2366 'wpzf-form_page_wpzf-settings' => array(
2367 'name' => esc_html__( 'Settings', 'wpzoom-forms' ),
2368 'url' => admin_url( 'edit.php?post_type=wpzf-form&page=wpzf-settings' ),
2369 ),
2370 'wpzf-form_page_wpzf-pro-page' => array(
2371 'name' => esc_html__( 'Upgrade to PRO', 'wpzoom-forms' ),
2372 'url' => admin_url( 'edit.php?post_type=wpzf-form&page=wpzf-pro-page' ),
2373 ),
2374 'wpzf-form_page_wpzf-import' => array(
2375 'name' => esc_html__( 'Import & Export', 'wpzoom-forms' ),
2376 'url' => admin_url( 'edit.php?post_type=wpzf-form&page=wpzf-import' ),
2377 ),
2378 )
2379 );
2380
2381 foreach ( $pages as $id => $atts ) {
2382 printf(
2383 // translators: %1$s = possible class attribute, %2$s = page url, %3$s = page name.
2384 _x( '<li%1$s><a href="%2$s">%3$s</a></li>', 'Main menu page item', 'wpzoom-forms' ),
2385 ( $current_page === $id || ( isset( $atts['altid'] ) && $current_page == $atts['altid'] ) ? ' class="active"' : '' ),
2386 esc_url( $atts['url'] ),
2387 esc_html( $atts['name'] )
2388 );
2389 }
2390 ?>
2391 </ul>
2392 </nav>
2393 </header>
2394 <?php
2395 }
2396
2397 if ( 'edit-wpzf-form' == $current_page || 'wpzf-form' == $current_page ) {
2398 $forms_count = wp_count_posts( 'wpzf-form' );
2399
2400 if ( $forms_count->publish < 1 && $forms_count->draft < 1 && $forms_count->trash < 1 ) {
2401 ?>
2402 <div class="wpzf_no-forms">
2403 <div class="left-column">
2404 <h3>
2405 <?php esc_html_e( 'Hi there!', 'wpzoom-forms' ); ?>
2406 </h3>
2407
2408 <h2>
2409 <?php esc_html_e( 'It appears that you haven&rsquo;t made any forms yet.', 'wpzoom-forms' ); ?>
2410 </h2>
2411
2412 <p>
2413 <?php esc_html_e( 'With WPZOOM Forms, you have the ability to create contact forms, surveys, and various other types of forms effortlessly and swiftly.', 'wpzoom-forms' ); ?>
2414 </p>
2415
2416 <a href="<?php echo esc_url( admin_url( 'post-new.php?post_type=wpzf-form' ) ); ?>" class="button-primary wpzf-add-form-btn">
2417 <?php esc_html_e( 'Add new form', 'wpzoom-forms' ); ?>
2418 </a>
2419 </div>
2420
2421 <div class="right-column">
2422 <svg width="360" height="360" fill="none" xmlns="http://www.w3.org/2000/svg">
2423 <mask id="a" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="25" y="140" width="304" height="175">
2424 <path d="M305.985 314.458H47.535a24.293 24.293 0 0 1-3.753-.29c-3.966-.621-7.575-2.21-10.528-4.493a21.616 21.616 0 0 1-2.11-1.862.535.535 0 0 0 .502-.168l94.603-106.18 50.511 30.403 50.49-30.425 94.623 106.202a.531.531 0 0 0 .504.168 21.988 21.988 0 0 1-4.388 3.406 22.998 22.998 0 0 1-6.489 2.603 24 24 0 0 1-5.515.636zm16.809-7.078a.517.517 0 0 0-.126-.438l-37.526-42.119c.465.15.961.263 1.488.328 4.522.558 10.117-.941 13.717 7.925 1.072 2.64 2.755 3.646 4.524 3.646 4.168 0 8.8-5.601 6.971-8.59-2.605-4.255 2.223-7.364 4.674-12.255 1.591-3.176-.007-5.899-3.066-7.309-2.354-1.081-6.664-.35-7.987-3.181-.961-2.057-.308-4.881-.825-7.094-.735-3.151-2.695-4.059-4.592-4.059-1.46 0-2.883.538-3.683 1.003-1.056.616-2.896 1.835-4.431 1.835-1.137 0-2.108-.671-2.466-2.751-.628-3.647-4.487-5.975-8.17-5.975-1.26 0-2.499.273-3.581.859-.469.23-.835.496-1.121.781-2.489 2.275 0 5.251 0 5.251-4.981 4.511.69 11.772 2.376 13.454 1.684 1.682 3.294 4.052 2.067 8.025-.341 1.111-.408 2.215-.23 3.241l-52.631-59.072 24.464-14.743 75.566-45.435V294.001c0 4.5-1.578 8.66-4.253 12.039-.366.461-.753.909-1.159 1.34zm-292.07 0c-.024-.025-.048-.053-.073-.078-3.327-3.577-5.337-8.222-5.337-13.301V140.707l75.566 45.486 24.443 14.714-94.471 106.035a.522.522 0 0 0-.128.438z" fill="#fff" />
2425 </mask>
2426 <g mask="url(#a)">
2427 <path d="M305.983 314.458H47.534a24.564 24.564 0 0 1-3.752-.29c-3.966-.621-7.575-2.21-10.528-4.493a21.583 21.583 0 0 1-2.111-1.862.537.537 0 0 0 .503-.168l94.603-106.18 50.509 30.404 50.491-30.426 94.622 106.202a.538.538 0 0 0 .504.168 21.982 21.982 0 0 1-4.387 3.406 23.044 23.044 0 0 1-6.49 2.604c-1.762.415-3.61.635-5.515.635zm16.809-7.078a.513.513 0 0 0-.125-.438l-37.525-42.118c.465.15.961.262 1.488.327 4.52.559 10.117-.941 13.717 7.925 1.071 2.641 2.754 3.647 4.523 3.647 4.169 0 8.799-5.602 6.971-8.591-2.604-4.255 2.222-7.364 4.674-12.255 1.59-3.176-.009-5.899-3.067-7.308-2.352-1.082-6.663-.351-7.986-3.181-.961-2.058-.309-4.882-.825-7.094-.734-3.152-2.695-4.06-4.591-4.06-1.462 0-2.885.538-3.684 1.004-1.057.615-2.896 1.834-4.43 1.834-1.136 0-2.108-.67-2.468-2.751-.626-3.646-4.486-5.974-8.168-5.974-1.261 0-2.499.273-3.582.858a4.227 4.227 0 0 0-1.122.781c-2.488 2.276 0 5.252 0 5.252-4.98 4.51.691 11.771 2.378 13.453 1.683 1.683 3.293 4.053 2.066 8.025-.341 1.111-.409 2.215-.23 3.241l-52.631-59.071 24.464-14.743 75.566-45.436V294.001c0 4.501-1.579 8.661-4.254 12.04-.365.46-.751.908-1.159 1.339zm-292.067 0-.074-.078c-3.327-3.577-5.337-8.222-5.337-13.301V140.708l75.565 45.485 24.444 14.715-94.471 106.034a.522.522 0 0 0-.127.438z" fill="#083EA7" />
2428 </g>
2429 <mask id="b" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="25" y="52" width="304" height="180">
2430 <path d="m176.76 231.868-50.511-30.403.038-.04a.533.533 0 0 0-.399-.883.53.53 0 0 0-.396.178l-.169.187-24.443-14.714-75.565-45.486-.315-.19 17.42-10.512 58.46-35.278 65.693-39.64c3.197-1.93 6.689-2.894 10.184-2.894 3.496 0 6.993.966 10.19 2.894L252.64 94.74l60.632 36.579 15.248 9.198-.314.19-75.566 45.435-24.464 14.743-.148-.165a.53.53 0 0 0-.75-.044.533.533 0 0 0-.045.749l.017.018-50.49 30.425z" fill="#fff" />
2431 </mask>
2432 <g mask="url(#b)">
2433 <path d="m176.76 231.868-50.511-30.404.038-.039a.533.533 0 0 0-.399-.883.53.53 0 0 0-.396.178l-.169.187-24.443-14.714-75.565-45.486-.315-.19 17.42-10.512 58.46-35.277 65.693-39.643c3.196-1.929 6.69-2.89 10.184-2.89 3.497 0 6.993.964 10.189 2.89l65.695 39.655 60.63 36.579 15.249 9.198-.315.189-75.564 45.436-24.466 14.743-.147-.165a.529.529 0 0 0-.749-.044.533.533 0 0 0-.045.749l.017.018-50.491 30.425z" fill="#242628" />
2434 </g>
2435 <mask id="c" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="30" y="200" width="97" height="108">
2436 <path d="M31.25 307.823a.536.536 0 0 1-.107-.01 26.971 26.971 0 0 1-.418-.433.522.522 0 0 1 .127-.438l94.472-106.035.926.558-94.604 106.18a.53.53 0 0 1-.397.178z" fill="#fff" />
2437 </mask>
2438 <g mask="url(#c)">
2439 <path d="M31.25 307.823a.536.536 0 0 1-.107-.01 26.971 26.971 0 0 1-.418-.433.522.522 0 0 1 .127-.438l94.472-106.035.926.558-94.604 106.18a.53.53 0 0 1-.397.178z" fill="#fff" />
2440 </g>
2441 <mask id="d" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="227" y="200" width="96" height="108">
2442 <path d="M322.272 307.823a.531.531 0 0 1-.399-.178L227.25 201.443l.926-.558 52.631 59.072c.386 2.223 1.927 4.085 4.335 4.866l37.526 42.119a.517.517 0 0 1 .126.438c-.136.147-.276.29-.417.433a.533.533 0 0 1-.105.01z" fill="#fff" />
2443 </mask>
2444 <g mask="url(#d)">
2445 <path d="M322.27 307.822a.532.532 0 0 1-.397-.177l-94.622-106.202.924-.558 52.63 59.072c.387 2.223 1.927 4.085 4.336 4.866l37.527 42.118a.523.523 0 0 1 .126.439c-.137.147-.277.29-.417.433a.533.533 0 0 1-.107.009z" fill="#fff" />
2446 </g>
2447 <mask id="e" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="274" y="228" width="44" height="49">
2448 <path d="M304.871 276.722c-1.769 0-3.452-1.006-4.524-3.646-3.6-8.866-9.196-7.367-13.717-7.925a8.029 8.029 0 0 1-1.488-.328c-2.409-.781-3.949-2.643-4.336-4.866-.178-1.026-.11-2.13.231-3.241 1.227-3.973-.384-6.343-2.067-8.025-1.686-1.682-7.357-8.943-2.376-13.454 0 0-2.489-2.976 0-5.251-1.877 1.87-.259 4.62-.259 4.62l1.38-.961c5.861.078 3.5 6.673 3.5 6.673l.369.188s.638-.686 1.207-.686c.236 0 .462.118.625.451.552 1.136-.808 3.259-.808 3.259s.497 1.239 1.636 1.239c.502 0 1.129-.24 1.892-.936 1.882-1.707 2.845-3.868 2.567-6.854.072.398.24.894.619 1.269.364.366.861.549 1.471.549.055 0 .113-.003.17-.005.901-.048 2.186-.446 3.548-.869 1.769-.548 3.691-1.144 5.219-1.144.793 0 1.48.163 1.982.576.743.608 1.014 1.699.836 3.341l.205.02c.189-1.712-.11-2.866-.908-3.521-.544-.446-1.277-.619-2.115-.619-1.563 0-3.5.599-5.282 1.152-1.347.418-2.622.811-3.497.858a2.709 2.709 0 0 1-.156.005c-.554 0-1.001-.162-1.327-.485-.665-.664-.617-1.79-.615-1.8l-.208-.01c0 .01-.003.088.002.205a16.274 16.274 0 0 0-.634-2.743c-1.102-3.484-4.236-5.246-7.219-5.246-1.081 0-2.14.23-3.079.693 1.082-.586 2.321-.859 3.581-.859 3.683 0 7.542 2.328 8.169 5.975.359 2.08 1.33 2.751 2.467 2.751 1.535 0 3.375-1.219 4.431-1.835.8-.465 2.223-1.003 3.683-1.003 1.897 0 3.857.908 4.592 4.059.517 2.213-.136 5.037.825 7.094 1.323 2.831 5.633 2.1 7.987 3.181 3.058 1.41 4.657 4.133 3.066 7.309-2.452 4.891-7.279 8-4.675 12.255 1.83 2.989-2.802 8.59-6.97 8.59zm3.297-27.44-.028.205c.048.005 4.725.673 6.133 2.966.451.736.519 1.569.203 2.478-.66 1.902-1.894 3.487-2.983 4.886-1.516 1.942-2.823 3.619-2.339 5.604l.201-.05c-.459-1.887.818-3.529 2.298-5.429 1.102-1.409 2.346-3.008 3.019-4.943.338-.969.263-1.863-.224-2.654-1.46-2.375-6.084-3.036-6.28-3.063z" fill="#fff" />
2449 </mask>
2450 <g mask="url(#e)">
2451 <path d="M304.871 276.723c-1.77 0-3.455-1.006-4.526-3.647-3.601-8.866-9.194-7.367-13.717-7.925a8.002 8.002 0 0 1-1.488-.328c-2.409-.781-3.947-2.643-4.335-4.865-.179-1.027-.11-2.13.23-3.242 1.228-3.972-.382-6.343-2.066-8.024-1.688-1.683-7.358-8.944-2.376-13.454 0 0-2.49-2.976 0-5.252-1.879 1.87-.26 4.621-.26 4.621l1.38-.961c5.861.077 3.502 6.673 3.502 6.673l.368.188s.636-.686 1.206-.686c.238 0 .462.118.625.45.553 1.137-.808 3.259-.808 3.259s.498 1.239 1.638 1.239c.5 0 1.128-.24 1.889-.936 1.884-1.707 2.846-3.867 2.567-6.853.075.398.241.893.62 1.269.365.365.863.548 1.471.548.056 0 .114-.003.172-.005.899-.048 2.185-.445 3.546-.869 1.77-.548 3.693-1.144 5.22-1.144.793 0 1.479.163 1.983.576.741.609 1.012 1.7.835 3.342l.205.02c.188-1.712-.111-2.866-.907-3.522-.545-.446-1.278-.618-2.116-.618-1.563 0-3.5.598-5.281 1.151-1.35.418-2.622.811-3.499.859-.053.002-.102.005-.155.005-.556 0-1.001-.163-1.328-.486-.664-.663-.617-1.79-.614-1.8l-.207-.009c0 .009-.003.087 0 .205-.111-.849-.324-1.76-.634-2.744-1.101-3.484-4.235-5.246-7.219-5.246a6.967 6.967 0 0 0-3.079.694c1.081-.586 2.321-.859 3.582-.859 3.682 0 7.541 2.328 8.168 5.975.36 2.08 1.331 2.75 2.468 2.75 1.535 0 3.375-1.219 4.431-1.834.8-.466 2.221-1.004 3.682-1.004 1.898 0 3.856.909 4.592 4.06.517 2.213-.136 5.036.827 7.093 1.322 2.831 5.632 2.101 7.986 3.182 3.059 1.409 4.655 4.132 3.065 7.308-2.451 4.892-7.278 8-4.675 12.256 1.831 2.988-2.802 8.59-6.968 8.59zm3.294-27.441-.027.205c.049.005 4.724.674 6.132 2.967.454.735.52 1.569.205 2.478-.661 1.902-1.895 3.486-2.985 4.885-1.516 1.943-2.821 3.62-2.337 5.605l.199-.05c-.459-1.888.819-3.53 2.299-5.43 1.103-1.409 2.345-3.008 3.02-4.943.338-.969.263-1.862-.224-2.653-1.46-2.375-6.085-3.036-6.282-3.064z" fill="#242628" />
2452 </g>
2453 <mask id="f" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="261" y="282" width="18" height="42">
2454 <path d="M270.028 282.812s-3.402.526-7.229 3.079a1.904 1.904 0 0 0-.843 1.82c.459 3.619 2.344 15.338 8.556 26.785.326.603.422 1.306.276 1.977l-1.079 5.011-2.002 1.872h5.156l3.433-9.266-1.468-.463a2.772 2.772 0 0 1-1.94-2.596l-.173-9.939c.148-4.43-2.722-7.487-2.722-7.487l8.235-.398-8.2-10.395z" fill="#fff" />
2455 </mask>
2456 <g mask="url(#f)">
2457 <path d="M270.028 282.812s-3.402.526-7.229 3.079a1.898 1.898 0 0 0-.844 1.82c.46 3.619 2.343 15.338 8.556 26.785.326.603.422 1.306.275 1.977l-1.077 5.011-2.004 1.872h5.156l3.435-9.266-1.469-.463a2.774 2.774 0 0 1-1.941-2.596l-.171-9.939c.146-4.43-2.723-7.486-2.723-7.486l8.235-.399-8.199-10.395z" fill="url(#g)" />
2458 </g>
2459 <mask id="h" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="265" y="313" width="13" height="13">
2460 <path d="M265.694 324.26c-.469.613 5.053 1.422 7.844.083 0 0 3.869-6.716 3.869-10.173 0 0-.309-.703-2.58-.543 0 0-1.3 7.947-2.669 8.838-1.373.891-3.837.09-3.837.09s-1.934.801-2.627 1.705z" fill="#fff" />
2461 </mask>
2462 <g mask="url(#h)">
2463 <path d="M265.693 324.26c-.469.614 5.052 1.422 7.842.083 0 0 3.869-6.716 3.869-10.172 0 0-.307-.704-2.58-.543 0 0-1.299 7.946-2.669 8.837-1.372.892-3.836.091-3.836.091s-1.935.801-2.626 1.704z" fill="#242628" />
2464 </g>
2465 <mask id="i" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="242" y="284" width="36" height="41">
2466 <path d="M277.186 293.753c-.045-.078-8.864-1.059-8.864-1.059s.288 2.676-.615 6.358c-.901 3.684-13.853 19.641-13.853 19.641l1.358 4.295c-4.198 1.46-8.328 1.692-12.393.744l8.416-5.039s7.876-26.775 12.701-31.553l8.842-3.051 4.408 9.664z" fill="#fff" />
2467 </mask>
2468 <g mask="url(#i)">
2469 <path d="M277.185 293.754c-.046-.078-8.864-1.059-8.864-1.059s.287 2.675-.615 6.357c-.9 3.685-13.853 19.641-13.853 19.641l1.356 4.295c-4.197 1.46-8.327 1.693-12.391.744l8.415-5.039s7.876-26.774 12.701-31.553l8.842-3.051 4.409 9.665z" fill="url(#j)" />
2470 </g>
2471 <mask id="k" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="264" y="278" width="39" height="17">
2472 <path d="M296.968 278.172s5.083 7.541 5.5 10.98c.416 3.44-1.393 4.839-6.725 5.069-5.334.23-19.568.225-19.568.225l-12.152-10.357s8.855-3.151 11.158-4.043c2.301-.891 21.787-1.874 21.787-1.874z" fill="#fff" />
2473 </mask>
2474 <g mask="url(#k)">
2475 <path d="M296.969 278.172s5.082 7.541 5.5 10.98c.415 3.439-1.392 4.839-6.724 5.069-5.334.23-19.571.225-19.571.225l-12.149-10.357s8.854-3.152 11.156-4.043c2.302-.891 21.788-1.874 21.788-1.874z" fill="#001C37" />
2476 </g>
2477 <mask id="l" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="240" y="321" width="16" height="5">
2478 <path d="M254.7 321.542s1.46 1.411.474 2.983c0 0-11.582 2.048-14.726 1.119 0 0 .653-1.487 4.193-3.179l-.261.201c-.331.252-.136.781.278.758 2.334-.133 7.164-.561 10.042-1.882z" fill="#fff" />
2479 </mask>
2480 <g mask="url(#l)">
2481 <path d="M254.7 321.542s1.46 1.411.474 2.983c0 0-11.582 2.048-14.726 1.119 0 0 .653-1.487 4.193-3.179l-.261.201c-.331.252-.136.781.278.758 2.334-.133 7.164-.561 10.042-1.882z" fill="#242628" />
2482 </g>
2483 <mask id="m" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="272" y="249" width="31" height="29">
2484 <path d="M283.223 277.173s-5.916-5.622-8.829-9.344a7.887 7.887 0 0 1-1.343-7.111c1.33-4.47 3.694-9.098 3.91-9.819.331-1.107 6.967-1.059 6.967-1.059s13.058 8.455 18.189 22.775l-5.764 1.79s-3.409-5.707-6.777-9.334c0 0 5.829 7.682 6.035 10.825l-12.388 1.277z" fill="#fff" />
2485 </mask>
2486 <g mask="url(#m)">
2487 <path d="M283.222 277.173s-5.915-5.622-8.829-9.344a7.89 7.89 0 0 1-1.343-7.111c1.332-4.47 3.696-9.098 3.91-9.819.331-1.106 6.968-1.059 6.968-1.059s13.057 8.455 18.188 22.775l-5.762 1.79s-3.412-5.707-6.779-9.334c0 0 5.83 7.682 6.035 10.825l-12.388 1.277z" fill="#1FDE91" />
2488 </g>
2489 <mask id="n" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="276" y="243" width="8" height="8">
2490 <path d="M276.157 244.726c.07.228 1.802 5.957 1.937 6.075.136.12 3.799.315 5.523-.858l-2.926-6.11-4.534.893z" fill="#fff" />
2491 </mask>
2492 <g mask="url(#n)">
2493 <path d="M276.158 244.726c.069.228 1.8 5.957 1.936 6.075.136.12 3.8.315 5.523-.859l-2.927-6.11-4.532.894z" fill="url(#o)" />
2494 </g>
2495 <mask id="p" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="272" y="232" width="13" height="14">
2496 <path d="M279.003 232.694s-3.127 1.317-5.252 5.825c-2.128 4.508-.539 6.588 1.455 6.873 1.995.288 4.976.243 8.132-2.668 3.154-2.911-.271-11.592-4.335-10.03z" fill="#fff" />
2497 </mask>
2498 <g mask="url(#p)">
2499 <path d="M279.003 232.694s-3.128 1.317-5.253 5.825c-2.128 4.508-.539 6.588 1.455 6.873 1.997.288 4.976.243 8.133-2.668 3.154-2.911-.272-11.591-4.335-10.03z" fill="url(#q)" />
2500 </g>
2501 <mask id="r" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="275" y="228" width="14" height="17">
2502 <path d="M284.245 244.769c-1.14 0-1.636-1.239-1.636-1.239s1.36-2.123.808-3.259c-.164-.333-.389-.451-.625-.451-.57 0-1.207.686-1.207.686l-.369-.188s2.361-6.595-3.5-6.673l-1.38.961s-1.618-2.75.258-4.62a4.172 4.172 0 0 1 1.122-.781 6.96 6.96 0 0 1 3.079-.693c2.983 0 6.117 1.762 7.218 5.246.311.984.522 1.895.635 2.743.005.123.02.291.055.478.279 2.986-.685 5.147-2.567 6.854-.762.695-1.39.936-1.891.936z" fill="#fff" />
2503 </mask>
2504 <g mask="url(#r)">
2505 <path d="M284.246 244.768c-1.14 0-1.635-1.239-1.635-1.239s1.358-2.122.806-3.259c-.163-.332-.389-.45-.625-.45-.57 0-1.205.686-1.205.686l-.371-.188s2.362-6.596-3.499-6.673l-1.379.961s-1.62-2.751.257-4.621c.287-.285.653-.55 1.122-.781a6.956 6.956 0 0 1 3.079-.693c2.984 0 6.118 1.762 7.219 5.247.311.983.521 1.894.635 2.743.003.122.019.29.055.478.277 2.986-.684 5.146-2.566 6.853-.765.696-1.392.936-1.893.936z" fill="#242628" />
2506 </g>
2507 <mask id="s" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="280" y="239" width="4" height="5">
2508 <path d="M280.594 241.37s1.192-3.397 2.782-2.045c1.591 1.354-1.292 3.138-2.421 3.717l-.361-1.672z" fill="#fff" />
2509 </mask>
2510 <g mask="url(#s)">
2511 <path d="M280.593 241.37s1.191-3.397 2.782-2.045c1.591 1.354-1.294 3.139-2.421 3.717l-.361-1.672z" fill="url(#t)" />
2512 </g>
2513 <mask id="u" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="271" y="272" width="33" height="8">
2514 <path d="m271.167 277.591 4.348-4.18 6.015 2.536s5.989.923 9.414 0l3.427-.927 7.746-2.405 1.214 3.429c.309.871 0 1.837-.755 2.371-.67.475-1.455.998-1.957 1.216-1.019.446-25.139-.308-25.139-.308l-4.313-1.732z" fill="#fff" />
2515 </mask>
2516 <g mask="url(#u)">
2517 <path d="m271.167 277.591 4.348-4.18 6.016 2.535s5.987.924 9.412 0l3.428-.926 7.747-2.405 1.215 3.429c.308.871 0 1.837-.757 2.37-.67.476-1.456.999-1.958 1.217-1.016.445-25.138-.308-25.138-.308l-4.313-1.732z" fill="url(#v)" />
2518 </g>
2519 <mask id="w" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="288" y="236" width="15" height="5">
2520 <path d="m302.754 240.716-.206-.02c.178-1.642-.093-2.733-.835-3.341-.502-.413-1.19-.576-1.983-.576-1.528 0-3.45.596-5.219 1.144-1.362.423-2.647.821-3.547.869-.058.002-.116.005-.171.005-.61 0-1.107-.183-1.47-.549-.379-.375-.547-.871-.62-1.269-.015-.157-.035-.315-.055-.478-.005-.117-.003-.195-.003-.205l.208.01c-.002.01-.05 1.136.615 1.8.326.323.773.485 1.327.485.053 0 .103-.002.156-.005.876-.047 2.15-.44 3.498-.858 1.781-.553 3.718-1.152 5.281-1.152.838 0 1.571.173 2.116.619.797.655 1.096 1.809.908 3.521z" fill="#fff" />
2521 </mask>
2522 <g mask="url(#w)">
2523 <path d="m302.753 240.717-.206-.02c.177-1.642-.094-2.734-.836-3.342-.503-.413-1.189-.575-1.981-.575-1.53 0-3.45.595-5.22 1.143-1.364.424-2.647.821-3.549.869-.056.002-.115.005-.169.005-.61 0-1.106-.183-1.47-.548-.38-.376-.549-.871-.622-1.269-.013-.158-.034-.316-.053-.478-.005-.118-.003-.196-.003-.206l.209.01c-.003.01-.051 1.137.613 1.8.327.323.774.486 1.329.486.051 0 .102-.003.155-.005.876-.048 2.151-.441 3.498-.859 1.781-.553 3.717-1.151 5.282-1.151.838 0 1.569.172 2.113.618.798.656 1.098 1.81.91 3.522z" fill="#424242" />
2524 </g>
2525 <mask id="x" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="288" y="236" width="1" height="1">
2526 <path d="M288.703 236.979a3.421 3.421 0 0 1-.056-.478c.021.163.041.321.056.478z" fill="#fff" />
2527 </mask>
2528 <g mask="url(#x)">
2529 <path d="M288.701 236.98a3.415 3.415 0 0 1-.055-.478c.019.163.039.32.055.478z" fill="url(#y)" />
2530 </g>
2531 <mask id="z" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="308" y="249" width="7" height="17">
2532 <path d="M309.154 265.421c-.484-1.985.823-3.662 2.339-5.604 1.089-1.399 2.323-2.984 2.983-4.886.316-.909.248-1.742-.203-2.478-1.408-2.293-6.085-2.961-6.132-2.966l.027-.205c.196.027 4.82.688 6.28 3.063.487.791.563 1.685.224 2.654-.673 1.934-1.917 3.534-3.019 4.943-1.48 1.9-2.757 3.542-2.298 5.429l-.201.05z" fill="#fff" />
2533 </mask>
2534 <g mask="url(#z)">
2535 <path d="M309.153 265.422c-.484-1.985.823-3.662 2.337-5.605 1.09-1.399 2.326-2.983 2.984-4.885.316-.909.25-1.743-.201-2.479-1.409-2.292-6.086-2.961-6.132-2.966l.027-.205c.196.028 4.819.688 6.278 3.064.487.791.564 1.684.226 2.653-.674 1.935-1.919 3.534-3.019 4.943-1.481 1.9-2.758 3.542-2.299 5.43l-.201.05z" fill="#424242" />
2536 </g>
2537 <mask id="A" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="285" y="299" width="4" height="25">
2538 <path d="M288.083 324h-2.291v-24.71h2.291V324z" fill="#fff" />
2539 </mask>
2540 <g mask="url(#A)">
2541 <path d="M288.083 324h-2.29v-24.71h2.29V324z" fill="#242628" />
2542 </g>
2543 <mask id="B" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="276" y="322" width="21" height="3">
2544 <path d="M296.967 324.475h-20.06a1.49 1.49 0 0 1 1.491-1.487h17.079c.823 0 1.49.666 1.49 1.487z" fill="#fff" />
2545 </mask>
2546 <g mask="url(#B)">
2547 <path d="M296.966 324.475h-20.061a1.49 1.49 0 0 1 1.491-1.486h17.08c.822 0 1.49.665 1.49 1.486z" fill="#242628" />
2548 </g>
2549 <mask id="C" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="274" y="293" width="26" height="8">
2550 <path d="M299.947 298.431a2.468 2.468 0 0 1-2.464 2.458h-20.296a2.468 2.468 0 0 1-2.464-2.458v-2.222a2.467 2.467 0 0 1 2.464-2.456h20.296a2.467 2.467 0 0 1 2.464 2.456v2.222z" fill="#fff" />
2551 </mask>
2552 <g mask="url(#C)">
2553 <path d="M299.949 298.431a2.47 2.47 0 0 1-2.464 2.458h-20.296a2.467 2.467 0 0 1-2.463-2.458v-2.223a2.464 2.464 0 0 1 2.463-2.455h20.296a2.466 2.466 0 0 1 2.464 2.455v2.223z" fill="#242628" />
2554 </g>
2555 <mask id="D" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="261" y="277" width="28" height="3">
2556 <path d="M287.621 279.563h-.058l-25.224-.027a1.127 1.127 0 1 1 .003-2.253l25.224.028c.625 0 1.129.505 1.129 1.129a1.132 1.132 0 0 1-1.074 1.123z" fill="#fff" />
2557 </mask>
2558 <g mask="url(#D)">
2559 <path d="M287.624 279.563h-.06l-25.222-.028a1.126 1.126 0 0 1-1.127-1.126c0-.623.508-1.136 1.127-1.126l25.225.027c.624 0 1.13.506 1.13 1.129a1.132 1.132 0 0 1-1.073 1.124z" fill="#242628" />
2560 </g>
2561 <mask id="E" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="237" y="256" width="38" height="24">
2562 <path d="M239.665 256.716h21.006c1.551 0 2.961.893 3.623 2.29l9.71 20.542H247.9a2.431 2.431 0 0 1-2.263-1.534l-7.53-19.008a1.674 1.674 0 0 1 1.558-2.29z" fill="#fff" />
2563 </mask>
2564 <g mask="url(#E)">
2565 <path d="M239.665 256.716h21.006c1.551 0 2.961.893 3.623 2.29l9.71 20.542H247.9a2.431 2.431 0 0 1-2.263-1.534l-7.53-19.008a1.674 1.674 0 0 1 1.558-2.29z" fill="#242628" />
2566 </g>
2567 <mask id="F" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="215" y="279" width="108" height="6">
2568 <path d="M322.877 280.482a.938.938 0 0 0-.221-.806 1.07 1.07 0 0 0-.803-.353H216.599c-.409 0-.79.195-1.011.518a1.062 1.062 0 0 0-.078 1.071l1.142 2.311c.261.528.828.866 1.45.866h103.031c.579 0 1.076-.383 1.187-.916l.557-2.691z" fill="#fff" />
2569 </mask>
2570 <g mask="url(#F)">
2571 <path d="M322.876 280.482a.94.94 0 0 0-.222-.806 1.077 1.077 0 0 0-.804-.353H216.596c-.409 0-.79.196-1.008.519-.219.32-.25.726-.079 1.071l1.14 2.31c.261.528.83.866 1.452.866H321.13c.58 0 1.076-.383 1.188-.916l.558-2.691z" fill="#1FDE91" />
2572 </g>
2573 <mask id="G" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="221" y="281" width="22" height="49">
2574 <path d="M222.498 329.136a.883.883 0 0 1-.364-.078.95.95 0 0 1-.484-1.239l19.373-45.963a.915.915 0 0 1 1.212-.495c.469.207.685.76.484 1.239l-19.373 45.965a.92.92 0 0 1-.848.571z" fill="#fff" />
2575 </mask>
2576 <g mask="url(#G)">
2577 <path d="M222.498 329.136a.883.883 0 0 1-.364-.078.95.95 0 0 1-.484-1.239l19.373-45.963a.915.915 0 0 1 1.212-.495c.469.207.685.76.484 1.239l-19.373 45.965a.92.92 0 0 1-.848.571z" fill="#1FDE91" />
2578 </g>
2579 <mask id="H" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="224" y="281" width="22" height="49">
2580 <path d="M244.322 329.136a.922.922 0 0 1-.85-.571l-19.37-45.965a.95.95 0 0 1 .481-1.239.918.918 0 0 1 1.215.495l19.372 45.963a.95.95 0 0 1-.484 1.239.89.89 0 0 1-.364.078z" fill="#fff" />
2581 </mask>
2582 <g mask="url(#H)">
2583 <path d="M244.322 329.136a.922.922 0 0 1-.85-.571l-19.37-45.965a.95.95 0 0 1 .481-1.239.918.918 0 0 1 1.215.495l19.372 45.963a.95.95 0 0 1-.484 1.239.89.89 0 0 1-.364.078z" fill="#1FDE91" />
2584 </g>
2585 <mask id="I" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="297" y="281" width="23" height="49">
2586 <path d="M298.785 329.136a.907.907 0 0 1-.367-.078.95.95 0 0 1-.482-1.239l19.371-45.963a.918.918 0 0 1 1.214-.495c.469.207.685.76.484 1.239l-19.372 45.965a.924.924 0 0 1-.848.571z" fill="#fff" />
2587 </mask>
2588 <g mask="url(#I)">
2589 <path d="M298.785 329.136a.914.914 0 0 1-.367-.078.95.95 0 0 1-.481-1.239l19.37-45.963a.918.918 0 0 1 1.214-.495c.469.207.686.76.486 1.238l-19.375 45.966a.922.922 0 0 1-.847.571z" fill="#1FDE91" />
2590 </g>
2591 <mask id="J" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="300" y="281" width="22" height="49">
2592 <path d="M320.606 329.136a.924.924 0 0 1-.848-.571L300.386 282.6a.951.951 0 0 1 .484-1.239.917.917 0 0 1 1.214.495l19.37 45.963a.949.949 0 0 1-.481 1.239.907.907 0 0 1-.367.078z" fill="#fff" />
2593 </mask>
2594 <g mask="url(#J)">
2595 <path d="M320.607 329.136a.923.923 0 0 1-.849-.571L300.385 282.6a.951.951 0 0 1 .483-1.239.918.918 0 0 1 1.215.495l19.37 45.963a.948.948 0 0 1-.48 1.239.9.9 0 0 1-.366.078z" fill="#1FDE91" />
2596 </g>
2597 <mask id="K" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="37" y="31" width="272" height="201">
2598 <path d="m37.887 148.035 139.064 83.741 131.13-78.792V31H48.806l-10.92 117.035z" fill="#fff" />
2599 </mask>
2600 <g mask="url(#K)">
2601 <path d="M270.976 66.156H58.964c-3.739 0-6.835 3.235-6.835 6.966v171.639h249.263V96.978l-30.416-30.822z" fill="#fff" />
2602 <path d="m301.274 96.979-27.82.24c-.742.007-1.316-.283-1.841-.8-.524-.519-.577-1.224-.577-1.959V66.37l30.238 30.609z" fill="#81909C" />
2603 </g>
2604 <path d="M335.988 197.782v21.17c0 1.725-1.055 3.124-2.355 3.124h-77.309c-1.078 0-2.02.976-2.282 2.365l-1.389 7.372c-.182.976-1.208 1.059-1.479.115l-1.786-6.225c-.349-1.211-1.225-2.013-2.199-2.013h-3.323c-1.3 0-2.354-1.399-2.354-3.123v-21.762c0-1.715 1.043-3.11 2.334-3.124l89.767-1.022c1.311-.015 2.375 1.387 2.375 3.123z" fill="#fff" />
2605 <path d="M243.993 208.501c0 5.36 4.246 9.708 9.482 9.708 5.237 0 9.482-4.348 9.482-9.708 0-5.362-4.245-9.709-9.482-9.709-5.236 0-9.482 4.347-9.482 9.709z" fill="#1FDE91" />
2606 <mask id="L" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="250" y="201" width="7" height="8">
2607 <path d="M256.911 204.565c0 1.744-1.418 3.627-3.164 3.627-1.749 0-3.167-1.883-3.167-3.627 0-1.744 1.418-2.981 3.167-2.981 1.746 0 3.164 1.237 3.164 2.981z" fill="#fff" />
2608 </mask>
2609 <g mask="url(#L)">
2610 <path d="M256.911 204.565c0 1.744-1.418 3.627-3.164 3.627-1.749 0-3.167-1.883-3.167-3.627 0-1.744 1.418-2.981 3.167-2.981 1.746 0 3.164 1.237 3.164 2.981z" fill="#242628" />
2611 </g>
2612 <mask id="M" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="247" y="209" width="13" height="6">
2613 <path d="M249.817 214.908h7.728c.813 0 1.533-.526 1.782-1.299l.414-1.297a1.86 1.86 0 0 0-1.127-2.318c-3.146-1.148-6.398-1.001-9.728.105a1.859 1.859 0 0 0-1.194 2.253l.321 1.176c.221.814.959 1.38 1.804 1.38z" fill="#fff" />
2614 </mask>
2615 <g mask="url(#M)">
2616 <path d="M249.817 214.908h7.728c.813 0 1.533-.526 1.782-1.299l.414-1.297a1.86 1.86 0 0 0-1.127-2.318c-3.146-1.148-6.398-1.001-9.728.105a1.859 1.859 0 0 0-1.194 2.253l.321 1.176c.221.814.959 1.38 1.804 1.38z" fill="#242628" />
2617 </g>
2618 <mask id="N" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="268" y="209" width="61" height="4">
2619 <path d="M328.603 211.033c0 1.014-.858 1.837-1.917 1.837h-56.427c-1.059 0-1.917-.823-1.917-1.837s.858-1.834 1.917-1.834h56.427c1.059 0 1.917.82 1.917 1.834z" fill="#fff" />
2620 </mask>
2621 <g mask="url(#N)">
2622 <path d="M328.601 211.033c0 1.014-.859 1.837-1.917 1.837h-56.426c-1.061 0-1.917-.823-1.917-1.837 0-1.013.856-1.833 1.917-1.833h56.426c1.058 0 1.917.82 1.917 1.833z" fill="#242628" />
2623 </g>
2624 <mask id="O" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="268" y="215" width="34" height="4">
2625 <path d="M301.329 216.787c0 .922-.748 1.667-1.671 1.667h-29.583a1.668 1.668 0 1 1 0-3.336h29.583a1.67 1.67 0 0 1 1.671 1.669z" fill="#fff" />
2626 </mask>
2627 <g mask="url(#O)">
2628 <path d="M301.328 216.788c0 .921-.748 1.667-1.67 1.667h-29.582a1.667 1.667 0 1 1 0-3.337h29.582c.922 0 1.67.746 1.67 1.67z" fill="#242628" />
2629 </g>
2630 <mask id="P" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="303" y="215" width="26" height="4">
2631 <path d="M328.665 216.787a1.67 1.67 0 0 1-1.673 1.667h-21.35a1.667 1.667 0 1 1 0-3.336h21.35a1.67 1.67 0 0 1 1.673 1.669z" fill="#fff" />
2632 </mask>
2633 <g mask="url(#P)">
2634 <path d="M328.666 216.787a1.67 1.67 0 0 1-1.674 1.667h-21.349a1.667 1.667 0 1 1 0-3.336h21.349a1.67 1.67 0 0 1 1.674 1.669z" fill="#242628" />
2635 </g>
2636 <path d="M301.368 202.131a2.345 2.345 0 0 1-2.347 2.342h-28.487a2.345 2.345 0 0 1-2.349-2.342v-.191a2.345 2.345 0 0 1 2.349-2.342h28.487a2.346 2.346 0 0 1 2.347 2.342v.191zM82.198 81.84h9.38v.938h-9.38zM82.198 90.282h9.38v.938h-9.38zM82.198 82.778h.938v.938h-.938zM82.198 89.344h.938v.938h-.938zM90.64 89.344h.938v.938h-.938z" fill="#1FDE91" />
2637 <path fill="#1FDE91" d="M81.26 82.778h.938v7.504h-.938zM91.578 82.778h.938v7.504h-.938zM83.136 83.716h.938v.938h-.938zM89.702 83.716h.938v.938h-.938zM84.074 84.654h.938v.938h-.938zM88.764 84.654h.938v.938h-.938zM85.012 85.592h.938v.938h-.938zM87.826 85.592h.938v.938h-.938z" />
2638 <path fill="#1FDE91" d="M85.95 86.529h1.876v.938H85.95zM90.64 82.778h.938v.938h-.938z" />
2639 <rect x="81.26" y="151.468" width="182.938" height="14.146" rx="1.286" fill="#fff" />
2640 <rect x="86.26" y="157.058" width="33" height="2" rx="1" fill="#8B8D8F" />
2641 <rect x="80.869" y="151.077" width="183.72" height="14.928" rx="1.677" stroke="#242628" stroke-opacity=".5" stroke-width=".782" />
2642 <rect x="81.26" y="127.644" width="182.938" height="14.146" rx="1.286" fill="#fff" />
2643 <rect x="86.26" y="134.058" width="33" height="2" rx="1" fill="#8B8D8F" />
2644 <rect x="80.869" y="127.253" width="183.72" height="14.928" rx="1.677" stroke="#242628" stroke-opacity=".5" stroke-width=".782" />
2645 <rect x="81.26" y="103.726" width="182.938" height="14.146" rx="1.286" fill="#fff" />
2646 <rect x="86.26" y="110.058" width="33" height="2" rx="1" fill="#8B8D8F" />
2647 <rect x="80.869" y="103.335" width="183.72" height="14.928" rx="1.677" stroke="#242628" stroke-opacity=".5" stroke-width=".782" />
2648 <path d="M104.812 83.524c-1.74 0-2.736 1.26-2.736 3.156 0 1.956 1.128 3.024 2.748 3.024 1.524 0 2.448-.84 2.448-2.244v-.036h-2.652v-1.356h4.08V91h-1.296l-.096-1.008c-.48.672-1.476 1.14-2.664 1.14-2.46 0-4.176-1.788-4.176-4.488 0-2.664 1.74-4.56 4.38-4.56 2.004 0 3.552 1.164 3.804 2.952h-1.62c-.276-1.032-1.164-1.512-2.22-1.512zm7.983 7.632c-1.764 0-3-1.284-3-3.12 0-1.86 1.212-3.144 2.952-3.144 1.776 0 2.904 1.188 2.904 3.036v.444l-4.464.012c.108 1.044.66 1.572 1.632 1.572.804 0 1.332-.312 1.5-.876h1.356c-.252 1.296-1.332 2.076-2.88 2.076zm-.036-5.064c-.864 0-1.392.468-1.536 1.356h2.976c0-.816-.564-1.356-1.44-1.356zM118.78 91h-1.464v-4.704h-1.14v-1.224h1.14v-1.848h1.464v1.848h1.152v1.224h-1.152V91zm6.028-7.116a.889.889 0 0 1-.9-.888c0-.492.396-.876.9-.876.48 0 .876.384.876.876a.884.884 0 0 1-.876.888zM124.076 91v-5.928h1.464V91h-1.464zm4.441 0h-1.464v-5.928h1.356l.12.768c.372-.6 1.092-.948 1.896-.948 1.488 0 2.256.924 2.256 2.46V91h-1.464v-3.3c0-.996-.492-1.476-1.248-1.476-.9 0-1.452.624-1.452 1.584V91zm10.853 0h-1.464v-4.704h-1.14v-1.224h1.14v-1.848h1.464v1.848h1.152v1.224h-1.152V91zm1.655-2.976c0-1.848 1.332-3.12 3.168-3.12s3.168 1.272 3.168 3.12-1.332 3.12-3.168 3.12-3.168-1.272-3.168-3.12zm1.464 0c0 1.08.696 1.812 1.704 1.812s1.704-.732 1.704-1.812-.696-1.812-1.704-1.812-1.704.732-1.704 1.812zm9.912-2.952h1.464V91h-1.356l-.108-.792c-.36.564-1.128.948-1.92.948-1.368 0-2.172-.924-2.172-2.376v-3.708h1.464v3.192c0 1.128.444 1.584 1.26 1.584.924 0 1.368-.54 1.368-1.668v-3.108zm2.511 2.952c0-1.836 1.212-3.132 2.964-3.132 1.62 0 2.724.9 2.88 2.328h-1.464c-.168-.672-.66-1.02-1.356-1.02-.936 0-1.56.708-1.56 1.824s.576 1.812 1.512 1.812c.732 0 1.248-.36 1.404-1.008h1.476c-.18 1.38-1.332 2.328-2.88 2.328-1.8 0-2.976-1.248-2.976-3.132zm8.41 2.976h-1.464v-8.928h1.476v3.768c.372-.576 1.068-.948 1.92-.948 1.464 0 2.232.924 2.232 2.46V91h-1.464v-3.3c0-.996-.492-1.476-1.236-1.476-.924 0-1.464.648-1.464 1.536V91z" fill="#242628" />
2649 <path fill="#1FDE91" d="M158.26 148.058h2.736v54.717h-2.736z" />
2650 <path fill="#1FDE91" d="M160.995 150.794h2.736v2.736h-2.736zM171.939 161.737h2.736v2.736h-2.736zM182.883 172.681h2.736v2.736h-2.736zM166.468 156.265h2.736v2.736h-2.736zM177.411 167.209h2.736v2.736h-2.736zM188.354 178.152h2.736v2.736h-2.736zM169.203 189.096h2.736v2.736h-2.736z" />
2651 <path fill="#1FDE91" d="M166.468 191.831h2.736v2.736h-2.736zM163.731 194.567h2.736v2.736h-2.736zM160.995 197.303h2.736v2.736h-2.736zM180.146 183.624h2.736v8.208h-2.736zM182.883 191.831h2.736v5.472h-2.736zM174.675 191.831h2.736v5.472h-2.736zM171.939 186.36h2.736v5.472h-2.736zM185.618 197.303h2.736v5.472h-2.736zM177.411 197.303h2.736v5.472h-2.736zM188.354 202.775h2.736v5.472h-2.736zM180.146 202.775h2.736v5.472h-2.736zM163.731 153.53h2.736v2.736h-2.736zM174.675 164.473h2.736v2.736h-2.736zM185.618 175.416h2.736v2.736h-2.736zM169.203 159.001h2.736v2.736h-2.736zM180.146 169.945h2.736v2.736h-2.736zM180.146 180.888h13.679v2.736h-13.679zM182.883 208.247h5.472v2.736h-5.472z" />
2652 <defs>
2653 <linearGradient id="g" x1="279.23" y1="322.708" x2="271.012" y2="285.385" gradientUnits="userSpaceOnUse">
2654 <stop stop-color="#FF928E" />
2655 <stop offset="1" stop-color="#FEB3B1" />
2656 </linearGradient>
2657 <linearGradient id="j" x1="292.659" y1="233.667" x2="273.411" y2="280.645" gradientUnits="userSpaceOnUse">
2658 <stop stop-color="#FF928E" />
2659 <stop offset="1" stop-color="#FEB3B1" />
2660 </linearGradient>
2661 <linearGradient id="o" x1="285.033" y1="237.355" x2="293.362" y2="257.803" gradientUnits="userSpaceOnUse">
2662 <stop stop-color="#FF928E" />
2663 <stop offset="1" stop-color="#FEB3B1" />
2664 </linearGradient>
2665 <linearGradient id="q" x1="-716.313" y1="340.056" x2="-703.14" y2="338.684" gradientUnits="userSpaceOnUse">
2666 <stop stop-color="#FF928E" />
2667 <stop offset="1" stop-color="#FEB3B1" />
2668 </linearGradient>
2669 <linearGradient id="t" x1="-715.046" y1="342.488" x2="-701.896" y2="341.119" gradientUnits="userSpaceOnUse">
2670 <stop stop-color="#FF928E" />
2671 <stop offset="1" stop-color="#FEB3B1" />
2672 </linearGradient>
2673 <linearGradient id="v" x1="352.794" y1="272.43" x2="310.594" y2="268.666" gradientUnits="userSpaceOnUse">
2674 <stop stop-color="#FF928E" />
2675 <stop offset="1" stop-color="#FEB3B1" />
2676 </linearGradient>
2677 <linearGradient id="y" x1="222.273" y1="156.367" x2="354.285" y2="289.941" gradientUnits="userSpaceOnUse">
2678 <stop stop-color="#B05B34" />
2679 <stop offset="1" stop-color="#3225BE" />
2680 </linearGradient>
2681 </defs>
2682 </svg>
2683 </div>
2684 </div>
2685 <?php
2686 }
2687 }
2688 }
2689
2690 /**
2691 * Page footer used on all admin pages.
2692 *
2693 * @access public
2694 * @return void
2695 * @since 1.0.0
2696 */
2697 public function admin_page_footer() {
2698 $current_page = get_current_screen()->id;
2699
2700 if ( 'edit-wpzf-form' == $current_page || 'wpzf-form' == $current_page || 'edit-wpzf-submission' == $current_page || 'wpzf-submission' == $current_page || 'wpzf-form_page_wpzf-settings' == $current_page || 'wpzf-form_page_wpzoom-forms-pro-license' == $current_page || 'wpzf-form_page_wpzf-pro-page' == $current_page ) {
2701 ?>
2702 <footer class="wpzoom-new-admin_settings-footer">
2703 <div class="wpzoom-new-admin_settings-footer-wrap">
2704 <h3 class="wpzoom-new-admin_settings-footer-logo">
2705 <a href="https://www.wpzoom.com/<?php echo $this->utm_source; ?>" target="_blank" title="<?php esc_html_e( 'WPZOOM - WordPress themes with modern features and professional support', 'wpzoom-forms' ); ?>">
2706 <?php _e( 'WPZOOM', 'wpzoom-forms' ); ?>
2707 </a>
2708 </h3>
2709
2710 <ul class="wpzoom-new-admin_settings-footer-links">
2711 <li class="wpzoom-new-admin_settings-footer-links-themes">
2712 <a href="https://www.wpzoom.com/themes/<?php echo $this->utm_source; ?>" target="_blank" title="<?php _e( 'Check out our themes', 'wpzoom-forms' ); ?>">
2713 <?php _e( 'Our Themes', 'wpzoom-forms' ); ?>
2714 </a>
2715 </li>
2716
2717 <li class="wpzoom-new-admin_settings-footer-links-themes">
2718 <a href="https://www.wpzoom.com/plugins/<?php echo $this->utm_source; ?>" target="_blank" title="<?php _e( 'Check out our plugins', 'wpzoom-forms' ); ?>">
2719 <?php _e( 'Our Plugins', 'wpzoom-forms' ); ?>
2720 </a>
2721 </li>
2722
2723 <li class="wpzoom-new-admin_settings-footer-links-blog">
2724 <a href="https://www.wpzoom.com/blog/" target="_blank" title="<?php _e( 'See the latest updates on our blog', 'wpzoom-forms' ); ?>">
2725 <?php _e( 'Blog', 'wpzoom-forms' ); ?>
2726 </a>
2727 </li>
2728
2729 <li class="wpzoom-new-admin_settings-footer-links-themes">
2730 <a href="https://www.wpzoom.com/documentation/wpzoom-forms/" target="_blank" title="<?php _e( 'Documentation', 'wpzoom-forms' ); ?>">
2731 <?php _e( 'Documentation', 'wpzoom-forms' ); ?>
2732 </a>
2733 </li>
2734
2735 <li class="wpzoom-new-admin_settings-footer-links-support">
2736 <a href="https://www.wpzoom.com/support/" target="_blank" title="<?php _e( 'Get support', 'wpzoom-forms' ); ?>">
2737 <?php _e( 'Support', 'wpzoom-forms' ); ?>
2738 </a>
2739 </li>
2740 </ul>
2741 </div>
2742 </footer>
2743 <?php
2744 }
2745 }
2746
2747 public function admin_body_class_filter( $classes ) {
2748 $current_page = get_current_screen()->id;
2749
2750 if ( 'edit-wpzf-form' == $current_page || 'wpzf-form' == $current_page || 'edit-wpzf-submission' == $current_page || 'wpzf-submission' == $current_page || 'wpzf-form_page_wpzf-settings' == $current_page || 'wpzf-form_page_wpzoom-forms-pro-license' == $current_page || 'wpzf-form_page_wpzf-pro-page' == $current_page || 'wpzf-form_page_wpzf-import' == $current_page ) {
2751 $classes .= ' wpzoom-new-admin';
2752 }
2753
2754 $forms_count = wp_count_posts( 'wpzf-form' );
2755
2756 if ( 'edit-wpzf-form' == $current_page && $forms_count->publish < 1 && $forms_count->draft < 1 && $forms_count->trash < 1 ) {
2757 $classes .= ' wpzf-no-forms';
2758 }
2759
2760 return $classes;
2761 }
2762
2763 /**
2764 * Returns whether the given input is considered spam by checking it with Akismet.
2765 *
2766 * @since 1.0.4
2767 * @access public
2768 * @param array $input The input to check for spam.
2769 * @return bool Whether it is spam.
2770 */
2771 public function not_spam( $input ) {
2772
2773 // Check if Akismet class exists and has required methods
2774 if( class_exists( 'Akismet' ) && is_callable( array( 'Akismet', 'get_api_key' ) ) && is_callable( array( 'Akismet', 'http_post' ) ) ) {
2775
2776 $request = array(
2777 'comment_type' => 'contact-form',
2778 'comment_author' => isset( $input['name'] ) ? $input['name'] : '',
2779 'comment_author_email' => isset( $input['from'] ) ? $input['from'] : '',
2780 'comment_author_url' => isset( $input['url'] ) ? $input['url'] : '',
2781 'comment_content' => isset( $input['message'] ) ? $input['message'] : '',
2782 'blog' => get_option( 'home' ),
2783 'user_ip' => $this->get_remote_address(),
2784 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2785 'user_agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) : null,
2786 'referrer' => wp_get_referer() ? wp_get_referer() : null,
2787 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2788 'permalink' => get_permalink(),
2789 'blog_lang' => get_locale(),
2790 'blog_charset' => get_bloginfo( 'charset' ),
2791 'user_role' => Akismet::get_user_roles( get_current_user_id() ),
2792 'is_test' => false,
2793
2794 );
2795
2796 $response = Akismet::http_post( build_query( $request ), 'comment-check' );
2797
2798 if( ! empty( $response ) && isset( $response[1] ) && 'true' === trim( $response[1] ) ) {
2799 return false;
2800 }
2801
2802 }
2803
2804 return true;
2805 }
2806
2807 /**
2808 * Returns the remote address of the connected peer.
2809 *
2810 * @since 1.0.4
2811 * @access public
2812 * @return string The remote address, or empty string on failure.
2813 */
2814 public function get_remote_address() {
2815 $server_variable_keys = array(
2816 'HTTP_CLIENT_IP',
2817 'HTTP_X_FORWARDED_FOR',
2818 'HTTP_X_FORWARDED',
2819 'HTTP_X_CLUSTER_CLIENT_IP',
2820 'HTTP_FORWARDED_FOR',
2821 'HTTP_FORWARDED',
2822 'REMOTE_ADDR'
2823 );
2824
2825 foreach ( $server_variable_keys as $key ) {
2826 if ( array_key_exists( $key, $_SERVER ) === true ) {
2827 foreach ( array_map( 'trim', explode( ',', $_SERVER[ $key ] ) ) as $ip ) {
2828 if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
2829 return $ip;
2830 }
2831 }
2832 }
2833 }
2834
2835 return '';
2836 }
2837
2838 /**
2839 * Callback that is triggered when a form is submitted on the frontend.
2840 *
2841 * @access public
2842 * @return void
2843 * @since 1.0.0
2844 */
2845 public function action_form_post() {
2846 $success = false;
2847 $url = isset( $_POST['_wp_http_referer'] ) ? sanitize_text_field( wp_unslash( $_POST['_wp_http_referer'] ) ) : home_url();
2848 $form_id = -1;
2849 $captcha_check_passed = true;
2850
2851 if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'wpzf_submit' ) ) {
2852 $form_id = isset( $_POST['form_id'] ) ? intval( $_POST['form_id'] ) : -1;
2853 $blocks = parse_blocks( $form_id > -1 ? get_post_field( 'post_content', $form_id, 'raw' ) : '' );
2854
2855 // Check if spam protection is fully configured and validate only then.
2856 $captcha_config = $this->get_spam_protection_config();
2857 if ( 'recaptcha' === $captcha_config['active_service'] ) {
2858 $captcha_check_passed = false;
2859 $captcha = false;
2860 $recaptchaType = $captcha_config['type'];
2861
2862 if ( isset( $_POST['g-recaptcha-response'] ) ) {
2863 $captcha = trim( sanitize_text_field( $_POST['g-recaptcha-response'] ) );
2864 }
2865
2866 if( 'v3' == $recaptchaType && isset( $_POST['recaptcha_token'] ) ) {
2867 $captcha = trim( sanitize_text_field( $_POST['recaptcha_token'] ) );
2868 }
2869
2870
2871 if ( ! empty( $captcha ) ) {
2872 $secret = false;
2873 if ( 'v3' === $recaptchaType && ! empty( $captcha_config['recaptcha_v3_secret_key'] ) ) {
2874 $secret = trim( $captcha_config['recaptcha_v3_secret_key'] );
2875 } else {
2876 $secret = trim( $captcha_config['recaptcha_v2_secret_key'] );
2877 }
2878
2879 if ( ! empty( $secret ) ) {
2880 $response = file_get_contents(
2881 sprintf(
2882 'https://www.google.com/recaptcha/api/siteverify?secret=%1$s&response=%2$s&remoteip=%3$s',
2883 $secret,
2884 $captcha,
2885 $_SERVER['REMOTE_ADDR']
2886 )
2887 );
2888
2889 if ( false !== $response && ! empty( $response ) ) {
2890 $json = json_decode( $response );
2891
2892 if( 'v3' == $recaptchaType ) {
2893 if ( null !== $json && is_object( $json ) && true === $json->success && $json->score >= 0.5 ) {
2894 $captcha_check_passed = true;
2895 }
2896 }
2897 else {
2898 if ( null !== $json && is_object( $json ) && true === $json->success ) {
2899 $captcha_check_passed = true;
2900 }
2901 }
2902
2903 }
2904 }
2905 }
2906 } elseif ( 'turnstile' === $captcha_config['active_service'] ) {
2907 $captcha_check_passed = false;
2908
2909 $captcha = isset( $_POST['cf-turnstile-response'] ) ? sanitize_text_field( wp_unslash( $_POST['cf-turnstile-response'] ) ) : '';
2910 if ( ! empty( $captcha ) ) {
2911 $secret = trim( $captcha_config['turnstile_secret_key'] );
2912 $ip = $_SERVER['REMOTE_ADDR'];
2913
2914 $url_path = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
2915 $data = array(
2916 'secret' => $secret,
2917 'response' => $captcha,
2918 'remoteip' => $ip
2919 );
2920
2921 $options = array(
2922 'http' => array(
2923 'method' => 'POST',
2924 'content' => http_build_query($data)
2925 )
2926 );
2927 $stream = stream_context_create($options);
2928 $result = file_get_contents($url_path, false, $stream);
2929 $result = json_decode($result, true);
2930
2931 if ( intval( $result['success'] ) !== 1 ) {
2932 $captcha_check_passed = false;
2933 } else {
2934 $captcha_check_passed = true;
2935 }
2936 }
2937 }
2938
2939 if ( $captcha_check_passed && count( $blocks ) > 0 ) {
2940 $clean_site_name = sanitize_text_field( get_bloginfo( 'name' ) );
2941 $input_blocks = $this->get_input_blocks( $blocks );
2942 $form_method = get_post_meta( $form_id, '_form_method', true ) ?: 'email';
2943 $form_email = get_post_meta( $form_id, '_form_email', true );
2944 $form_subject = get_post_meta( $form_id, '_form_subject', true );
2945 $fallback_email = trim( get_option( 'admin_email' ) );
2946 $sendto = sanitize_email( false !== $form_email && ! empty( $form_email ) && filter_var( $form_email, FILTER_VALIDATE_EMAIL ) ? $form_email : $fallback_email );
2947
2948 if ( 'email' == $form_method || 'combined' == $form_method ) {
2949 $email_body = '<html>
2950 <head>
2951 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
2952 <meta http-equiv="X-UA-Compatible" content="IE=edge">
2953 <meta name="viewport" content="width=device-width">
2954
2955 <style type="text/css">
2956 body {
2957 -ms-text-size-adjust: 100%;height: 100%; line-height: 1.6;
2958 font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;
2959 }
2960 a { color: #4477bd; }
2961 a:hover {
2962 color: #e2911a !important;
2963 }
2964 a:active {
2965 color: #0d3d62 !important;
2966 }
2967 p{
2968 margin:10px 0;
2969 padding:0;
2970 font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;
2971 }
2972 table{
2973 border-collapse:collapse;
2974 }
2975 h1,h2,h3,h4,h5,h6{
2976 display:block;
2977 margin:0;
2978 padding:0;
2979 }
2980 img,a img{
2981 border:0;
2982 height:auto;
2983 outline:none;
2984 text-decoration:none;
2985 }
2986 body,#bodyTable,#bodyCell{
2987 height:100%;
2988 margin:0;
2989 padding:0;
2990 }
2991 #outlook a{
2992 padding:0;
2993 }
2994 img{
2995 -ms-interpolation-mode:bicubic;
2996 }
2997 table{
2998 mso-table-lspace:0pt;
2999 mso-table-rspace:0pt;
3000 }
3001 p,a,li,td,blockquote{
3002 mso-line-height-rule:exactly;
3003 }
3004 a[href^=tel],a[href^=sms]{
3005 color:inherit;
3006 cursor:default;
3007 text-decoration:none;
3008 }
3009 p,a,li,td,body,table,blockquote{
3010 -ms-text-size-adjust:100%;
3011 -webkit-text-size-adjust:100%;
3012 }
3013 a[x-apple-data-detectors]{
3014 color:inherit !important;
3015 text-decoration:none !important;
3016 font-size:inherit !important;
3017 font-family:inherit !important;
3018 font-weight:inherit !important;
3019 line-height:inherit !important;
3020 }
3021 @media only screen and (max-width: 480px){
3022 body,table,td,p,a,li,blockquote{
3023 -webkit-text-size-adjust:none !important;
3024 }
3025 }
3026 @media only screen and (max-width: 480px){
3027 body{
3028 width:100% !important;
3029 min-width:100% !important;
3030 }
3031 }
3032 </style>
3033 </head>
3034 <body style="height: 100%;margin: 0;padding: 0;width: 100%;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
3035 <div style="font-family:-apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;box-sizing:border-box;font-size:14px;line-height:1.5;max-width:600px;overflow:visible;display:block;margin:0">
3036 <table width="100%" cellpadding="0" cellspacing="0" style="font-family:-apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;box-sizing:border-box;font-size:14px;line-height:1.5">
3037 <tbody>
3038 <tr style="font-family:-apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;box-sizing:border-box;font-size:14px;line-height:1.5">
3039 <td style="font-family:-apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;box-sizing:border-box;font-size:14px;line-height:1.5;vertical-align:top;color:#222222;padding:25px" valign="top">';
3040 $replyto = '';
3041 $sbj = '';
3042 $raw_content = array(
3043 '_wpzf_fields' => array()
3044 );
3045
3046 foreach ( $_REQUEST as $key => $value ) {
3047 if ( strpos( $key, 'wpzf_' ) === 0 ) {
3048 $id = substr( $key, 5 );
3049 $name = isset( $input_blocks[ $id ] ) ? $input_blocks[ $id ] : __( 'Unnamed Input', 'wpzoom-forms' );
3050 $value = is_array( $value ) ? implode( ', ', $value ) : $value;
3051
3052 if ( 'wpzf_replyto' == $key ) {
3053 $replyto = sanitize_text_field( $value );
3054 continue;
3055 } elseif ( 'wpzf_subject' == $key ) {
3056 $sbj = sanitize_text_field( $value );
3057 continue;
3058 }
3059
3060 $email_body .= '<strong>' . wp_kses_post( wp_unslash( $name ) ) . ':</strong><br/>' . nl2br( wp_kses_post( wp_unslash( $value ) ) ) . '<br/><br/>';
3061 $raw_content['_wpzf_fields'][ $name ] = sanitize_textarea_field( $value );
3062 }
3063 }
3064
3065 $fromaddr = ! empty( $replyto ) && isset( $_REQUEST[ $replyto ] ) ? sanitize_email( $_REQUEST[ $replyto ] ) : $sendto;
3066 $cleanname = sanitize_text_field( get_bloginfo( 'name' ) );
3067 $subjectline = ! empty( $sbj ) && isset( $_REQUEST[ $sbj ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $sbj ] ) ) : $form_subject;
3068 $subjectline .= sprintf( __( ' - %s', 'wpzoom-forms' ), $cleanname );
3069
3070 $email_body = '<html style="background-color:#f4f3f3;"><body style="background-color:#f4f3f3;padding:2em;"><div style="background-color:#ffffff;width:70%;padding:2em;border-radius:10px;">' . preg_replace( '/<br\/><br\/><hr\/><br\/>$/is', '', $email_body ) . '</div></body></html>';
3071
3072 $headers = sprintf(
3073 "Content-Type: text/html; charset=UTF-8\r\nFrom: %s <%s>\r\nReply-To: %s",
3074 $cleanname,
3075 $fromaddr,
3076 $fromaddr
3077 );
3078
3079 $email_body .= '</td>
3080 </tr>
3081 </tbody></table>
3082 </div>
3083
3084 <div style="font-family:-apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;box-sizing:border-box;font-size:14px;line-height:1.5;max-width:600px;overflow:visible;display:block;margin:0">
3085 <table width="100%" cellpadding="0" cellspacing="0" style="font-family:&quot;Helvetica Neue&quot;,&quot;Helvetica&quot;,Helvetica,Arial,sans-serif;box-sizing:border-box;font-size:14px;line-height:1.5">
3086 <tbody><tr style="font-family:-apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;box-sizing:border-box;font-size:14px;line-height:1.5">
3087 <td style="font-family:-apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;box-sizing:border-box;font-size:14px;line-height:1.5;vertical-align:top;width:100%;clear:both;color:#777;border-top-width:1px;border-top-color:#d0d0d0;border-top-style:solid;padding:25px" valign="top">
3088 <p>Sent from <a href="' . esc_attr( get_bloginfo( 'url' ) ) . '">' . $cleanname . '</a> using the <strong>WPZOOM Forms</strong> plugin.</p>
3089 <br style="font-family:&quot;Helvetica Neue&quot;,&quot;Helvetica&quot;,Helvetica,Arial,sans-serif;box-sizing:border-box;font-size:14px;line-height:1.5">
3090 </td>
3091 </tr>
3092 </tbody></table>
3093 </div>
3094 </body>
3095 </html>';
3096
3097 $details = array(
3098 'from' => $fromaddr,
3099 'message' => $raw_content
3100 );
3101
3102 if ( $this->not_spam( $details ) ) {
3103 $success = wp_mail( $sendto, $subjectline, $email_body, $headers );
3104 }
3105 }
3106 if ( 'db' == $form_method || 'combined' == $form_method ) {
3107 $content = array(
3108 '_wpzf_form_id' => $form_id,
3109 '_wpzf_fields' => array()
3110 );
3111
3112 $replyto = '';
3113 $sbj = '';
3114
3115 foreach ( $_REQUEST as $key => $value ) {
3116 if ( strpos( $key, 'wpzf_' ) === 0 ) {
3117 $id = substr( $key, 5 );
3118 $name = isset( $input_blocks[ $id ] ) ? $input_blocks[ $id ] : __( 'Unnamed Input', 'wpzoom-forms' );
3119 $value = is_array( $value ) ? implode( ', ', $value ) : $value;
3120
3121 if ( 'wpzf_replyto' == $key || 'wpzf_subject' == $key ) {
3122 if ( 'wpzf_replyto' == $key ) {
3123 $replyto = sanitize_text_field( $value );
3124 } elseif ( 'wpzf_subject' == $key ) {
3125 $sbj = sanitize_text_field( $value );
3126 }
3127
3128 continue;
3129 }
3130
3131 $content['_wpzf_fields'][ $name ] = sanitize_textarea_field( $value );
3132 }
3133 }
3134
3135 $details = array(
3136 'from' => '',
3137 'message' => $content
3138 );
3139
3140 if ( ! empty( $replyto ) ) {
3141 $fromaddr = isset( $_REQUEST[ $replyto ] ) ? sanitize_email( $_REQUEST[ $replyto ] ) : $sendto;
3142 $details['from'] = $fromaddr;
3143 }
3144
3145 if ( $this->not_spam( $details ) ) {
3146 $success = false !== $content && 0 < wp_insert_post( array(
3147 'post_type' => 'wpzf-submission',
3148 'post_status' => 'publish',
3149 'comment_status' => 'closed',
3150 'ping_status' => 'closed',
3151 'post_title' => __( 'Submission', 'wpzoom-forms' ),
3152 'post_author' => 1,
3153 'post_category' => array( 1 ),
3154 'post_content' => __( 'Submission', 'wpzoom-forms' ),
3155 'meta_input' => $content
3156 ) );
3157 }
3158 }
3159 }
3160 }
3161
3162 $redirect_url = urldecode(
3163 add_query_arg(
3164 array(
3165 'success' => ( $success ? '1' : '0' ),
3166 'wpzf_submitted_form' => $form_id,
3167 ),
3168 $url
3169 )
3170 );
3171
3172 wp_safe_redirect(
3173 $redirect_url . ( $form_id > -1 ? '#wpzf-' . $form_id . '-notice' : '' )
3174 );
3175
3176 exit;
3177 }
3178
3179 /**
3180 * Filters a hierarchical array of Gutenberg blocks to return just the blocks added by this plugin.
3181 *
3182 * @access public
3183 * @param array $blocks A hierarchical array of Gutenberg blocks.
3184 * @return array
3185 * @since 1.0.0
3186 */
3187 public function get_input_blocks( $blocks ) {
3188 $found = array();
3189
3190 if ( is_array( $blocks ) && count( $blocks ) > 0 ) {
3191 for ( $i = 0; $i < count( $blocks ); $i++ ) {
3192 $block = $blocks[ $i ];
3193
3194 if ( isset( $block['blockName'] ) &&
3195 preg_match( '/^wpzoom\-forms\//i', $block['blockName'] ) &&
3196 ! preg_match( '/(label|submit)\-field$/i', $block['blockName'] ) &&
3197 isset( $block['attrs'] ) ) {
3198 $attrs = $block['attrs'];
3199
3200 if ( array_key_exists( 'id', $attrs ) && array_key_exists( 'name', $attrs ) ) {
3201 $found[ $attrs['id'] ] = $attrs['name'];
3202 }
3203 }
3204
3205 if ( isset( $block['innerBlocks'] ) ) {
3206 $found = array_merge( $found, $this->get_input_blocks( $block['innerBlocks'] ) );
3207 }
3208 }
3209 }
3210
3211 return $found;
3212 }
3213
3214 /**
3215 * Returns the output for the form shortcode.
3216 *
3217 * @access public
3218 * @param array|string $atts Shortcode attributes array or empty string.
3219 * @param string $content The shortcode content, or null if not set.
3220 * @param string $tag The shortcode name.
3221 * @return string The shortcode output.
3222 * @since 1.0.0
3223 */
3224 public function shortcode_output( $atts, $content, $tag ) {
3225 $id = is_array( $atts ) && array_key_exists( 'id', $atts ) ? intval( $atts['id'] ) : -1;
3226 $output = '';
3227
3228 if ( $id > 0 ) {
3229 wp_enqueue_script( 'wpzoom-forms-js-frontend-formblock' );
3230 wp_enqueue_style( 'wpzoom-forms-css-frontend-formblock' );
3231
3232 $output = $this->form_block_render( array( 'formId' => $id ) );
3233 }
3234
3235 return $output;
3236 }
3237
3238 /**
3239 * Register the PRO upsell notice with WPZOOM Notice Center.
3240 *
3241 * @since 1.3.4
3242 * @param array $notices Existing notices from the filter.
3243 * @return array Notices with upsell notice added when applicable.
3244 */
3245 public function register_upsell_notice( $notices ) {
3246 if ( ! is_array( $notices ) ) {
3247 $notices = array();
3248 }
3249
3250 // Don't add if PRO version is active.
3251 if ( defined( 'WPZOOM_FORMS_PRO_VERSION' ) ) {
3252 return $notices;
3253 }
3254
3255 $upsell_url = admin_url( 'edit.php?post_type=wpzf-form&page=wpzf-pro-page' );
3256 $pro_url = 'https://www.wpzoom.com/plugins/wpzoom-forms/?utm_source=wpadmin&utm_medium=wpzoom-forms-free&utm_campaign=upsell-notice';
3257
3258 $content = '<p>' . esc_html__( 'Unlock powerful features to build smarter, more flexible forms:', 'wpzoom-forms' ) . '</p>';
3259 $content .= '<ul>';
3260 $content .= '<li><strong>' . esc_html__( 'AI Form Generator', 'wpzoom-forms' ) . '</strong> &mdash; ' . esc_html__( 'Describe your form in simple words and let AI create it for you!', 'wpzoom-forms' ) . '</li>';
3261 $content .= '<li><strong>' . esc_html__( '30+ Pre-built Templates', 'wpzoom-forms' ) . '</strong> &mdash; ' . esc_html__( 'Healthcare, education, real estate, restaurant, HR, and more categories.', 'wpzoom-forms' ) . '</li>';
3262 $content .= '<li><strong>' . esc_html__( 'Conditional Logic', 'wpzoom-forms' ) . '</strong> &mdash; ' . esc_html__( 'Show or hide fields dynamically based on user selections.', 'wpzoom-forms' ) . '</li>';
3263 $content .= '<li><strong>' . esc_html__( 'Mailchimp Integration', 'wpzoom-forms' ) . '</strong> &mdash; ' . esc_html__( 'Automatically add subscribers to your Mailchimp audiences.', 'wpzoom-forms' ) . '</li>';
3264 $content .= '<li><strong>' . esc_html__( 'Import/Export Forms', 'wpzoom-forms' ) . '</strong> &mdash; ' . esc_html__( 'Easily transfer forms between sites or back up your work.', 'wpzoom-forms' ) . '</li>';
3265 $content .= '<li><strong>' . esc_html__( 'Export Submissions to CSV', 'wpzoom-forms' ) . '</strong> &mdash; ' . esc_html__( 'Download form submissions as CSV files for reporting.', 'wpzoom-forms' ) . '</li>';
3266 $content .= '</ul>';
3267
3268 $notices[] = array(
3269 'id' => 'wpzf_upsell_pro',
3270 'priority' => 10,
3271 'heading' => __( 'Upgrade to WPZOOM Forms PRO', 'wpzoom-forms' ),
3272 'content' => $content,
3273 'icon' => array(
3274 'type' => 'image',
3275 'src' => plugins_url( 'dist/assets/admin/images/icon-forms.svg', __FILE__ ),
3276 ),
3277 'primary_button' => array(
3278 'label' => __( 'Upgrade to PRO', 'wpzoom-forms' ),
3279 'url' => $pro_url,
3280 'new_tab' => true,
3281 ),
3282 'secondary_button' => array(
3283 'label' => __( 'Learn More', 'wpzoom-forms' ),
3284 'url' => $upsell_url,
3285 'new_tab' => false,
3286 ),
3287 'capability' => 'manage_options',
3288 'screens' => array( 'dashboard', 'edit-wpzf-form', 'wpzf-form', 'edit-wpzf-submission', 'wpzf-submission' ),
3289 'source' => 'WPZOOM Forms',
3290 'conditions' => function () {
3291 return ! get_option( 'wpzf_upsell_notice_dismissed' );
3292 },
3293 );
3294
3295 return $notices;
3296 }
3297 }
3298
3299 if( ! function_exists ( 'wpzoom_forms_load_files' ) ) {
3300 function wpzoom_forms_load_files() {
3301
3302 //Load Settings Panel
3303 require_once 'classes/class-wpzoom-forms-settings-fields.php';
3304 require_once 'classes/class-wpzoom-forms-settings-page.php';
3305 require_once 'classes/class-wpzoom-forms-template-manager.php';
3306 require_once 'classes/class-wpzoom-forms-settings-upsell.php';
3307
3308 }
3309 add_action( 'plugin_loaded', 'wpzoom_forms_load_files' );
3310 }
3311
3312 if ( ! function_exists( 'wpzoom_forms_plugin_action_links' ) ) {
3313 /**
3314 * Plugin action links.
3315 *
3316 * Adds action links to the plugin list table.
3317 *
3318 * Fired by `plugin_action_links` filter.
3319 *
3320 * @since 1.3.5
3321 *
3322 * @param array $links An array of plugin action links.
3323 *
3324 * @return array An array of plugin action links.
3325 */
3326 function wpzoom_forms_plugin_action_links( $links ) {
3327 $settings_link = sprintf(
3328 '<a href="%1$s">%2$s</a>',
3329 admin_url( 'edit.php?post_type=wpzf-form&page=' . WPZOOM_FORMS_SETTINGS_PAGE ),
3330 esc_html__( 'Settings', 'wpzoom-forms' )
3331 );
3332
3333 array_unshift( $links, $settings_link );
3334
3335 $links['go_pro'] = sprintf(
3336 '<a href="%1$s" target="_blank" class="wpzoom-forms-gopro" style="color:#2271b1;font-weight:bold;">%2$s &rarr; <span class="wpzoom-premium-badge" style="background-color: #2271b1; color: #fff; margin-left: 5px; font-size: 11px; min-height: 16px; border-radius: 8px; display: inline-block; font-weight: 600; line-height: 1.6; padding: 0 8px;">%3$s</span></a>',
3337 'https://www.wpzoom.com/plugins/wpzoom-forms/?utm_source=wpadmin&utm_medium=plugin&utm_campaign=wpzoom-forms-free&utm_content=plugins-page',
3338 esc_html__( 'UPGRADE', 'wpzoom-forms' ),
3339 esc_html__( 'PRO', 'wpzoom-forms' )
3340 );
3341
3342 return $links;
3343 }
3344 add_filter( 'plugin_action_links_' . WPZOOM_FORMS_PLUGIN_BASE, 'wpzoom_forms_plugin_action_links' );
3345 }
3346
3347 /**
3348 * Check if the Elementor Page Builder is enabled load the widget
3349 */
3350 if ( defined( 'ELEMENTOR_VERSION' ) && is_callable( 'Elementor\Plugin::instance' ) ) {
3351 require_once 'elementor/wpzoom-forms-elementor.php';
3352 }
3353
3354 /* -------------------------------------------------------------------------- */
3355 /* WPZOOM Forms v2 — schema-driven builder, REST API, renderer. */
3356 /* Coexists with the legacy block system: schema is the source of truth, the */
3357 /* legacy renderer remains a fallback during/after migration. */
3358 /* -------------------------------------------------------------------------- */
3359 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-schema.php';
3360 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-migration.php';
3361 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-templates.php';
3362 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-option-lists.php';
3363 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-rest.php';
3364 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-renderer.php';
3365 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-submission-handler.php';
3366 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-builder-page.php';
3367 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-submission-view.php';
3368
3369 /**
3370 * Shared embed helper used by the shortcode and the Elementor widget.
3371 *
3372 * Routes to the v2 renderer for forms saved through the new builder
3373 * (_wpzf_schema postmeta present) and to the legacy block renderer for
3374 * old block-based forms that have no schema yet.
3375 *
3376 * @param int $id Form post ID.
3377 * @return string Rendered HTML.
3378 */
3379 function wpzoom_forms_render_embed( $id ) {
3380 global $wpzoom_forms;
3381 $id = (int) $id;
3382 if ( $id < 1 ) {
3383 return '';
3384 }
3385
3386 if ( get_post_meta( $id, WPZOOM_Forms_Schema::META_KEY, true ) ) {
3387 return WPZOOM_Forms_Renderer::render( $id );
3388 }
3389
3390 // Legacy form — fall back to the block-based renderer.
3391 if ( $wpzoom_forms ) {
3392 wp_enqueue_script( 'wpzoom-forms-js-frontend-formblock' );
3393 wp_enqueue_style( 'wpzoom-forms-css-frontend-formblock' );
3394 return $wpzoom_forms->form_block_render( array( 'formId' => $id ) );
3395 }
3396
3397 return '';
3398 }
3399
3400 add_action( 'init', function() {
3401 global $wpzoom_forms;
3402
3403 // Take over form submission from the legacy handler.
3404 if ( $wpzoom_forms ) {
3405 remove_action( 'admin_post_wpzf_submit', array( $wpzoom_forms, 'action_form_post' ), 10 );
3406 remove_action( 'admin_post_nopriv_wpzf_submit', array( $wpzoom_forms, 'action_form_post' ), 10 );
3407 // The legacy class whitelist-resets $wp_meta_boxes to keep ONLY its own boxes —
3408 // drop that hook so our submission-detail boxes survive.
3409 remove_action( 'in_admin_header', array( $wpzoom_forms, 'remove_meta_boxes' ), 100 );
3410 // The legacy meta-box registration adds boxes we replace with our own.
3411 remove_action( 'add_meta_boxes_wpzf-submission', array( $wpzoom_forms, 'add_meta_boxes' ), 10 );
3412 }
3413
3414 // Disable the legacy template-picker modal — our React builder owns templates now.
3415 if ( class_exists( 'WPZOOM_Forms_Template_Manager' ) ) {
3416 $legacy_tm = WPZOOM_Forms_Template_Manager::instance();
3417 remove_action( 'admin_enqueue_scripts', array( $legacy_tm, 'scripts' ) );
3418 remove_action( 'admin_footer', array( $legacy_tm, 'modal_window' ) );
3419 remove_filter( 'default_content', array( $legacy_tm, 'default_form_content' ), 10 );
3420 }
3421
3422 // Register meta so REST/admin can expose it.
3423 register_post_meta( 'wpzf-form', WPZOOM_Forms_Schema::META_KEY, array(
3424 'type' => 'string',
3425 'single' => true,
3426 'show_in_rest' => true,
3427 'auth_callback' => function() { return current_user_can( 'edit_posts' ); },
3428 ) );
3429
3430 ( new WPZOOM_Forms_REST() )->register();
3431 ( new WPZOOM_Forms_Submission_Handler() )->register();
3432 ( new WPZOOM_Forms_Builder_Page() )->register();
3433 ( new WPZOOM_Forms_Submission_View() )->register();
3434 }, 11 ); // After main class init (priority 9).
3435
3436 /**
3437 * Override the legacy shortcode to route through the shared embed helper,
3438 * which selects v2 or legacy renderer based on whether _wpzf_schema exists.
3439 */
3440 add_action( 'init', function() {
3441 remove_shortcode( 'wpzf_form' );
3442 add_shortcode( 'wpzf_form', function( $atts ) {
3443 $atts = shortcode_atts( array( 'id' => 0 ), $atts, 'wpzf_form' );
3444 return wpzoom_forms_render_embed( (int) $atts['id'] );
3445 } );
3446 }, 12 );
3447
3448 /**
3449 * For the form-block (Gutenberg block that embeds a form by id), substitute
3450 * the render callback to use the new renderer. The block remains registered
3451 * so existing content keeps working. We hook `render_block` (rather than
3452 * patching the block type at registration time) because the main class
3453 * registers the block at init:9, before our filters get a chance to attach.
3454 */
3455 add_filter( 'render_block', function( $block_content, $block ) {
3456 if ( ! isset( $block['blockName'] ) || $block['blockName'] !== 'wpzoom-forms/form-block' ) return $block_content;
3457 if ( is_admin() ) return $block_content;
3458 $id = isset( $block['attrs']['formId'] ) ? (int) $block['attrs']['formId'] : 0;
3459 if ( $id < 1 ) return $block_content;
3460
3461 // Only switch to the v2 renderer when the form has been saved through the new
3462 // builder (_wpzf_schema exists). Otherwise return the original block output
3463 // unchanged so old forms keep all their existing styling and behaviour.
3464 if ( ! get_post_meta( $id, WPZOOM_Forms_Schema::META_KEY, true ) ) {
3465 return $block_content;
3466 }
3467
3468 // The render_callback (form_block_render) already built a <style> tag containing
3469 // per-form color/border overrides from the block attributes (fieldBgColor, btnBgColor, …).
3470 // Preserve it so those settings actually apply to the new renderer's output.
3471 $block_styles = '';
3472 if ( preg_match( '/(<style\b[^>]*>.*?<\/style>)/is', $block_content, $m ) ) {
3473 $block_styles = $m[1];
3474 }
3475
3476 // Preserve the block's alignment (wide/full) on the rendered wrapper. The
3477 // render callback no longer produces this output, so the align class set in
3478 // the editor must be re-applied here or the frontend ignores it.
3479 $align = isset( $block['attrs']['align'] ) ? $block['attrs']['align'] : '';
3480 $root_classes = ( $align && 'none' !== $align ) ? 'align' . $align : '';
3481
3482 return $block_styles . WPZOOM_Forms_Renderer::render( $id, $root_classes );
3483 }, 9, 2 );
3484
3485
3486 /**
3487 * Customize the "All Forms" admin list a bit:
3488 * - rename "Add New" wording, replace target link with our builder.
3489 */
3490 add_action( 'admin_head-edit.php', function() {
3491 $screen = get_current_screen();
3492 if ( ! $screen || $screen->post_type !== 'wpzf-form' ) return;
3493 ?>
3494 <style>
3495 .wp-heading-inline + .page-title-action { background: #2271b1; color: #fff; border-color: #2271b1; }
3496 .wp-heading-inline + .page-title-action:hover { background: #135e96; }
3497 </style>
3498 <script>
3499 jQuery(function($){
3500 var btn = $('.page-title-action').first();
3501 if (btn.length) {
3502 btn.attr('href', '<?php echo esc_js( admin_url( 'admin.php?page=wpzf-form-builder' ) ); ?>');
3503 btn.text(<?php echo wp_json_encode( __( 'Add New Form', 'wpzoom-forms' ) ); ?>);
3504 }
3505 });
3506 </script>
3507 <?php
3508 } );
3509
3510 /**
3511 * Render the legacy "Upgrade to PRO" sidebar banner on the forms list page.
3512 * The legacy template-manager modal used to ship this; we disabled that modal
3513 * when replacing the form editor, so re-emit the banner here so the upsell
3514 * isn't lost.
3515 */
3516 add_action( 'admin_footer-edit.php', function() {
3517 $screen = get_current_screen();
3518 if ( ! $screen || $screen->post_type !== 'wpzf-form' ) return;
3519 if ( ! class_exists( 'WPZOOM_Forms_Settings' ) ) return;
3520
3521 $settings_class = new WPZOOM_Forms_Settings();
3522 $settings_class->upsell_banner(); // echoes the .wpzoom-forms-settings-upsell-container markup
3523 ?>
3524 <script>
3525 jQuery(function($){
3526 var $banner = $('.wpzoom-forms-settings-upsell-container').last();
3527 if ( ! $banner.length ) return;
3528 // Drop the upsell into the right rail next to the posts table.
3529 var $filter = $('#posts-filter');
3530 if ( $filter.length ) {
3531 $banner.css({ 'float':'right', 'margin-left':'20px', 'max-width':'340px' });
3532 $filter.before( $banner );
3533 }
3534 });
3535 </script>
3536 <?php
3537 } );
3538
3539