PluginProbe
WPZOOM Forms – Drag & Drop Contact Form Builder for WordPress / 2.0.2
WPZOOM Forms – Drag & Drop Contact Form Builder for WordPress v2.0.2
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.2, at wpzoom-forms.php

3,526 lines 151.3 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.2
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_register_style(
865 'wpzoom-forms-css-backend-main',
866 trailingslashit( $this->main_dir_url ) . 'main/backend/style.css',
867 array(),
868 WPZOOM_FORMS_VERSION
869 );
870 } else {
871 wp_register_script(
872 'wpzoom-forms-js-backend-formblock',
873 trailingslashit( $this->main_dir_url ) . 'form-block/backend/script.js',
874 array( 'wp-blocks', 'wp-components', 'wp-core-data', 'wp-data', 'wp-element', 'wp-i18n', 'wp-polyfill' ),
875 WPZOOM_FORMS_VERSION,
876 true
877 );
878
879 wp_localize_script(
880 'wpzoom-forms-js-backend-formblock',
881 'wpzf_formblock',
882 array(
883 'admin_url' => trailingslashit( admin_url() ),
884 'admin_email' => '' . get_site_option( 'admin_email', '' )
885 )
886 );
887
888 wp_register_style(
889 'wpzoom-forms-css-backend-formblock',
890 trailingslashit( $this->main_dir_url ) . 'form-block/backend/style.css',
891 array(),
892 WPZOOM_FORMS_VERSION
893 );
894 }
895 }
896
897 /**
898 * Get normalized spam protection settings and whether the selected method is fully configured.
899 *
900 * @access private
901 * @return array
902 * @since 1.3.7
903 */
904 public function get_spam_protection_config() {
905 $service = sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_service' ) );
906 $type = sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_type' ) );
907
908 if ( ! in_array( $service, array( 'none', 'recaptcha', 'turnstile' ), true ) ) {
909 $service = 'none';
910 }
911
912 if ( ! in_array( $type, array( 'v2', 'v3' ), true ) ) {
913 $type = 'v2';
914 }
915
916 $recaptcha_v2_site_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_site_key' ) ) );
917 $recaptcha_v2_secret_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_secret_key' ) ) );
918 $recaptcha_v3_site_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_site_key_v3' ) ) );
919 $recaptcha_v3_secret_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_captcha_secret_key_v3' ) ) );
920 $turnstile_site_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_turnstile_site_key' ) ) );
921 $turnstile_secret_key = trim( sanitize_text_field( (string) WPZOOM_Forms_Settings::get( 'wpzf_global_turnstile_secret_key' ) ) );
922
923 $active_service = 'none';
924
925 if ( 'recaptcha' === $service ) {
926 if ( 'v3' === $type && ! empty( $recaptcha_v3_site_key ) && ! empty( $recaptcha_v3_secret_key ) ) {
927 $active_service = 'recaptcha';
928 } elseif ( 'v2' === $type && ! empty( $recaptcha_v2_site_key ) && ! empty( $recaptcha_v2_secret_key ) ) {
929 $active_service = 'recaptcha';
930 }
931 } elseif ( 'turnstile' === $service && ! empty( $turnstile_site_key ) && ! empty( $turnstile_secret_key ) ) {
932 $active_service = 'turnstile';
933 }
934
935 return array(
936 'service' => $service,
937 'type' => $type,
938 'active_service' => $active_service,
939 'recaptcha_v2_site_key' => $recaptcha_v2_site_key,
940 'recaptcha_v2_secret_key' => $recaptcha_v2_secret_key,
941 'recaptcha_v3_site_key' => $recaptcha_v3_site_key,
942 'recaptcha_v3_secret_key' => $recaptcha_v3_secret_key,
943 'turnstile_site_key' => $turnstile_site_key,
944 'turnstile_secret_key' => $turnstile_secret_key,
945 );
946 }
947
948 /**
949 * Registers needed scripts and styles for use on the frontend.
950 *
951 * @access public
952 * @return void
953 * @since 1.0.0
954 */
955 public function register_frontend_assets() {
956
957 $depends = array( 'jquery' );
958 $captcha = $this->get_spam_protection_config();
959 $captcha_type = $captcha['type'];
960 $active_method = $captcha['active_service'];
961
962 if ( ! is_admin() ) {
963 if ( 'recaptcha' === $active_method ) {
964 $site_key = ( 'v3' === $captcha_type ) ? $captcha['recaptcha_v3_site_key'] : $captcha['recaptcha_v2_site_key'];
965
966 if ( 'v2' === $captcha_type ) {
967 wp_register_script(
968 'google-recaptcha',
969 'https://www.google.com/recaptcha/api.js',
970 array(),
971 WPZOOM_FORMS_VERSION,
972 true
973 );
974 }
975 elseif ( 'v3' === $captcha_type ) {
976 wp_register_script(
977 'google-recaptcha',
978 "https://www.google.com/recaptcha/api.js?render={$site_key}",
979 array(),
980 WPZOOM_FORMS_VERSION,
981 true
982 );
983 }
984
985 $depends[] = 'google-recaptcha';
986 } elseif ( 'turnstile' === $active_method ) {
987 wp_register_script(
988 'turnstile-recaptcha',
989 'https://challenges.cloudflare.com/turnstile/v0/api.js',
990 array(),
991 null,
992 array( 'strategy' => 'defer' ),
993 );
994
995 $depends[] = 'turnstile-recaptcha';
996 }
997 }
998
999 wp_register_script(
1000 'wpzoom-forms-js-frontend-formblock',
1001 trailingslashit( $this->dist_dir_url ) . 'assets/frontend/js/script.js',
1002 $depends,
1003 WPZOOM_FORMS_VERSION,
1004 true
1005 );
1006
1007 $use_theme_style = boolval( WPZOOM_Forms_Settings::get( 'wpzf_use_theme_styles' ) );
1008
1009 wp_register_style(
1010 'wpzoom-forms-css-frontend-formblock',
1011 ( $use_theme_style ? trailingslashit( $this->main_dir_url ) . 'form-block/frontend/style.css' : false ),
1012 array(),
1013 WPZOOM_FORMS_VERSION
1014 );
1015
1016 // Register style for datepicker field
1017 wp_register_style(
1018 'wpzoom-forms-css-frontend-flatpickr',
1019 trailingslashit( $this->dist_dir_url ) . 'assets/frontend/flatpickr/css/flatpickr.min.css',
1020 array(),
1021 WPZOOM_FORMS_VERSION
1022 );
1023
1024 //Register script for datepicker field
1025 wp_register_script(
1026 'wpzoom-forms-js-frontend-flatpickr',
1027 trailingslashit( $this->dist_dir_url ) . 'assets/frontend/flatpickr/js/flatpickr.js',
1028 array( 'jquery' ),
1029 WPZOOM_FORMS_VERSION,
1030 true
1031 );
1032
1033 wp_register_script(
1034 'wpzoom-forms-js-frontend-datepicker',
1035 trailingslashit( $this->dist_dir_url ) . 'assets/frontend/js/datepicker.js',
1036 array( 'wpzoom-forms-js-frontend-flatpickr' ),
1037 WPZOOM_FORMS_VERSION,
1038 true
1039 );
1040 }
1041
1042 /**
1043 * Enqueues needed scripts and styles for use on the frontend.
1044 *
1045 * @access public
1046 * @return void
1047 * @since 1.0.0
1048 */
1049 public function enqueue_frontend_scripts() {
1050
1051 $global_load_assets = boolval( WPZOOM_Forms_Settings::get( 'wpzf_global_assets_load' ) );
1052
1053 if ( $global_load_assets ) {
1054 wp_enqueue_style( 'wpzoom-forms-css-frontend-formblock' );
1055 wp_enqueue_script( 'wpzoom-forms-js-frontend-formblock' );
1056 }
1057
1058 }
1059
1060 /**
1061 * Enqueues needed scripts and styles for use on the frontend.
1062 *
1063 * @access public
1064 * @return void
1065 * @since 1.0.0
1066 */
1067 public function block_frontend_assets() {
1068
1069 if( self::has_block( 'wpzoom-forms/datepicker-field' ) || self::has_elementor_widget() || self::has_cpt_form_shortcode() ) {
1070 wp_enqueue_style( 'wpzoom-forms-css-frontend-flatpickr' );
1071 wp_enqueue_script( 'wpzoom-forms-js-frontend-flatpickr' );
1072 wp_enqueue_script( 'wpzoom-forms-js-frontend-datepicker' );
1073 }
1074
1075 }
1076
1077 // Check if a block exists in the post content
1078 static function has_block( $block_name ) {
1079
1080 if( ! has_block( 'wpzoom-forms/form-block' ) ) {
1081 return false;
1082 }
1083
1084 global $post;
1085
1086 $gutenberg_matches = array();
1087 $gutenberg_patern = '/<!--\s+wp:(wpzoom\-forms\/form\-block)(\s+(\{.*?\}))?\s+(\/)?-->/';
1088 preg_match_all( $gutenberg_patern, $post->post_content, $matches );
1089
1090 // Check if the block exists in the post content
1091 if ( isset( $matches[3] ) ) {
1092 foreach ( $matches[3] as $block_attributes_json ) {
1093 if ( ! empty( $block_attributes_json ) ) {
1094 $atts = json_decode( $block_attributes_json, true );
1095 }
1096 }
1097 }
1098
1099 $form_ID = isset( $atts['formId'] ) ? $atts['formId'] : '';
1100
1101 if( $form_ID ) {
1102 $form_post = get_post( $form_ID );
1103 if( has_block( $block_name, $form_post ) ) {
1104 return true;
1105 }
1106 }
1107
1108 return false;
1109
1110 }
1111
1112 // Check the form exists in the elementor posts content
1113 public static function has_elementor_widget( $post_id = 0 ) {
1114
1115 if ( ! defined( 'ELEMENTOR_VERSION' ) && ! is_callable( 'Elementor\Plugin::instance' ) ) {
1116 return false;
1117 }
1118
1119 $post_id = $post_id > 0 ? $post_id : get_the_ID();
1120
1121 $elementor_data = get_post_meta( $post_id, '_elementor_data' );
1122
1123 if ( isset( $elementor_data[0] ) && is_string( $elementor_data[0] ) ) {
1124
1125 $regExp = '/"widgetType":"([^"]*)/i';
1126 $outputArray = array();
1127
1128 // Define the pattern to match the shortcode
1129 $scPattern = '/\[wpzf_form([^\]]*)\]/';
1130 preg_match_all( $scPattern, $elementor_data[0], $matches );
1131
1132 // Check if any matches are found
1133 if ( ! empty( $matches[0] ) ) {
1134 return true;
1135 }
1136
1137 if ( preg_match_all( $regExp, $elementor_data[0], $outputArray, PREG_SET_ORDER) ) {}
1138
1139 foreach( $outputArray as $found ) {
1140 if( in_array( 'wpzoom-forms-widget-cpt', $found ) ) {
1141 return true;
1142 }
1143 }
1144
1145 }
1146
1147 return false;
1148 }
1149
1150 /**
1151 * Check the post content has cpt form shortcode
1152 *
1153 * @since 1.2.0
1154 * @param int $post_id The post ID.
1155 * @param boolean|int $content The post content.
1156 * @return boolean Return true if post content has cpt form shortcode, else return false.
1157 */
1158 public static function has_cpt_form_shortcode( $post_id = 0, $content = '' ) {
1159
1160 $post_id = $post_id > 0 ? $post_id : get_the_ID();
1161
1162 if ( empty( $content ) ) {
1163 $content = get_post_field( 'post_content', $post_id );
1164 }
1165
1166 if ( $content ) {
1167 if ( has_shortcode( $content, 'wpzf_form' ) ) {
1168 return true;
1169 }
1170 }
1171 return false;
1172 }
1173
1174 /**
1175 * Changes the row actions in post lists for certain post types.
1176 *
1177 * @access public
1178 * @param array $actions An array of all the possible post row actions.
1179 * @param WP_Post $post The post object for the post that is being displayed.
1180 * @return array
1181 * @since 1.0.0
1182 */
1183 public function modify_row_actions( $actions, $post ) {
1184 if ( 'wpzf-form' == $post->post_type || 'wpzf-submission' == $post->post_type ) {
1185 if ( 'wpzf-submission' == $post->post_type && isset( $actions['edit'] ) ) {
1186 $actions['edit'] = preg_replace( '/\<a([^>]+)\>(.+)\<\/a\>/i', '<a$1>' . __( 'View', 'wpzoom-forms' ) . '</a>', $actions['edit'] );
1187 }
1188
1189 if ( isset( $actions['inline hide-if-no-js'] ) ) {
1190 unset( $actions['inline hide-if-no-js'] );
1191 }
1192 }
1193
1194 return $actions;
1195 }
1196
1197 /**
1198 * Removes the Inline Edit option from post types it doesn't make sense for.
1199 *
1200 * @access public
1201 * @param array $actions An array of all the possible bulk actions.
1202 * @return array
1203 * @since 1.0.0
1204 */
1205 public function remove_bulk_actions( $actions ) {
1206 if ( isset( $actions['edit'] ) ) {
1207 unset( $actions['edit'] );
1208 }
1209
1210 return $actions;
1211 }
1212
1213 /**
1214 * Changes the columns displayed in the post list for the form custom post type.
1215 *
1216 * @access public
1217 * @param array $columns An array of all the columns.
1218 * @return array
1219 * @since 1.0.0
1220 */
1221 public function post_list_columns_form( $columns ) {
1222 return array(
1223 'cb' => $columns['cb'],
1224 'title' => __( 'Title', 'wpzoom-forms' ),
1225 'responses' => __( 'Responses', 'wpzoom-forms' ),
1226 'shortcode' => __( 'Shortcode', 'wpzoom-forms' ),
1227 'date' => $columns['date']
1228 );
1229 }
1230
1231 /**
1232 * Changes the columns displayed in the post list for the submissions custom post type.
1233 *
1234 * @access public
1235 * @param array $columns An array of all the columns.
1236 * @return array
1237 * @since 1.0.0
1238 */
1239 public function post_list_columns_submit( $columns ) {
1240 return array(
1241 'cb' => $columns['cb'],
1242 'submission_id' => __( 'ID', 'wpzoom-forms' ),
1243 'name' => __( 'Name', 'wpzoom-forms' ),
1244 'email' => __( 'Email', 'wpzoom-forms' ),
1245 'subject' => __( 'Subject', 'wpzoom-forms' ),
1246 'form' => __( 'Form', 'wpzoom-forms' ),
1247 'date' => $columns['date']
1248 );
1249 }
1250
1251 /**
1252 * Changes the sortable columns displayed in the post list for the submissions custom post type.
1253 *
1254 * @access public
1255 * @param array $columns An array of all the sortable columns.
1256 * @return array
1257 * @since 1.0.0
1258 */
1259 public function post_list_sortable_columns_submit( $columns ) {
1260 $columns['submission_id'] = 'ID';
1261 $columns['form'] = 'wpzf_form';
1262
1263 return $columns;
1264 }
1265
1266 // Add the filter dropdown to the posts list
1267 public function custom_filter_by_form() {
1268 global $wpdb;
1269
1270 // Check if we are on the posts list page
1271 if ( isset( $_GET['post_type'] ) && $_GET['post_type'] == 'wpzf-submission' ) {
1272
1273 $form_id_filter_selected = isset( $_GET['form_id_filter'] ) ? $_GET['form_id_filter'] : '';
1274
1275 // Fetch unique form IDs and their corresponding titles from your database
1276 $form_ids_query = $wpdb->get_results("SELECT ID AS form_id, post_title
1277 FROM $wpdb->posts
1278 WHERE post_type = 'wpzf-form'
1279 AND post_status = 'publish'");
1280
1281
1282 // If we have forms, display the dropdown
1283 if ( ! empty( $form_ids_query ) ) {
1284 // Initialize an array to store unique form IDs
1285 $form_ids = array();
1286 ?>
1287 <select name="form_id_filter" id="form_id_filter">
1288 <option value=""><?php echo esc_html__( 'All Forms', 'wpzoom-forms' ); ?></option>
1289 <?php
1290 foreach ( $form_ids_query as $result ) {
1291
1292 // Check if the form ID already exists in the array
1293 if ( ! in_array( $result->form_id, $form_ids ) ) {
1294
1295 // Add form ID to the array to prevent duplication
1296 $form_ids[] = $result->form_id;
1297
1298 // Output the option with form ID as value and post title as option name
1299 echo '<option '. selected( $result->form_id, $form_id_filter_selected, true ) .' value="' . $result->form_id . '">' . $result->post_title . '</option>';
1300 }
1301 }
1302 ?>
1303 </select>
1304 <?php
1305 }
1306
1307
1308 }
1309 }
1310
1311 // Modify the query based on the selected form ID
1312 public function filter_posts_by_form ( $query ) {
1313
1314 global $pagenow;
1315
1316 // Check if we are on the posts list page and a form ID filter is set
1317 if ( is_admin() && $pagenow == 'edit.php' && isset( $_GET['form_id_filter'] ) && $_GET['form_id_filter'] != '' ) {
1318
1319 $query->query_vars['meta_key'] = '_wpzf_form_id';
1320 $query->query_vars['meta_value'] = $_GET['form_id_filter'];
1321
1322 }
1323 }
1324
1325 /**
1326 * Changes the column content displayed in the post list for the form custom post type.
1327 *
1328 * @access public
1329 * @param string $column The name of the column to display.
1330 * @param int $post_id The ID of the current post.
1331 * @return array
1332 * @since 1.0.0
1333 */
1334 public function post_list_custom_columns_form( $column, $post_id ) {
1335
1336 if ( 'shortcode' == $column ) {
1337 printf( '<input type="text" value="[wpzf_form id=&quot;%s&quot;]" readonly />', $post_id );
1338 }
1339
1340 if( 'responses' == $column ) {
1341
1342 $args = array(
1343 'post_type' => 'wpzf-submission',
1344 'post_status' => 'publish',
1345 'posts_per_page' => -1,
1346 'meta_query' => array(
1347 array(
1348 'key' => '_wpzf_form_id',
1349 'value' => $post_id,
1350 'compare' => '='
1351 )
1352 )
1353 );
1354
1355 $forms_count = 0;
1356
1357 $responses = new WP_Query( $args );
1358
1359 if( $responses && $responses->have_posts() ) {
1360 $forms_count = $responses->post_count;
1361 }
1362
1363 if( 0 < $forms_count ) {
1364 printf( '<a href="%s">%s</a>', admin_url( 'edit.php?post_type=wpzf-submission&&form_id_filter=' . $post_id ), $forms_count );
1365 } else {
1366 echo $forms_count;
1367 }
1368
1369 }
1370 }
1371
1372 /**
1373 * Changes the column content displayed in the post list for the submissions custom post type.
1374 *
1375 * @access public
1376 * @param string $column The name of the column to display.
1377 * @param int $post_id The ID of the current post.
1378 * @return array
1379 * @since 1.0.0
1380 */
1381 public function post_list_custom_columns_submit( $column, $post_id ) {
1382 $fields = get_post_meta( $post_id, '_wpzf_fields', true );
1383
1384 switch ( $column ) {
1385 case 'submission_id':
1386 printf(
1387 '<strong><a href="%s" class="row-title">%s #%s</a></strong>',
1388 esc_url( get_edit_post_link( $post_id ) ),
1389 esc_html__( 'Submission', 'wpzoom-forms' ),
1390 esc_html( $post_id )
1391 );
1392 break;
1393
1394 case 'name':
1395 $name_found = false;
1396
1397 if ( ! empty( $fields ) && is_array( $fields ) ) {
1398 // Common name field patterns
1399 $name_patterns = array( 'name', 'full_name', 'first_name', 'last_name', 'fullname', 'your_name' );
1400
1401 foreach ( $fields as $name => $value ) {
1402 $field_name = strtolower( $name );
1403 foreach ( $name_patterns as $pattern ) {
1404 if ( strpos( $field_name, $pattern ) !== false && ! empty( $value ) ) {
1405 echo '<strong>' . esc_html( $value ) . '</strong>';
1406 $name_found = true;
1407 break 2;
1408 }
1409 }
1410 }
1411 }
1412
1413 if ( ! $name_found ) {
1414 echo '&mdash;';
1415 }
1416 break;
1417
1418 case 'email':
1419 $email_found = false;
1420
1421 if ( ! empty( $fields ) && is_array( $fields ) ) {
1422 // Common email field patterns
1423 $email_patterns = array( 'email', 'e-mail', 'mail', 'email_address', 'your_email' );
1424
1425 foreach ( $fields as $name => $value ) {
1426 $field_name = strtolower( $name );
1427 foreach ( $email_patterns as $pattern ) {
1428 if ( strpos( $field_name, $pattern ) !== false && ! empty( $value ) ) {
1429 echo '<a href="mailto:' . esc_attr( $value ) . '">' . esc_html( $value ) . '</a>';
1430 $email_found = true;
1431 break 2;
1432 }
1433 }
1434 }
1435 }
1436
1437 if ( ! $email_found ) {
1438 echo '&mdash;';
1439 }
1440 break;
1441
1442 case 'subject':
1443 $subject_found = false;
1444
1445 if ( ! empty( $fields ) && is_array( $fields ) ) {
1446 // First try to find a subject field
1447 foreach ( $fields as $name => $value ) {
1448 if ( stripos( $name, 'subject' ) !== false || stripos( $name, 'title' ) !== false || stripos( $name, 'topic' ) !== false ) {
1449 echo esc_html( $value );
1450 $subject_found = true;
1451 break;
1452 }
1453 }
1454
1455 // If no subject field found, show first non-empty field (likely message)
1456 if ( ! $subject_found ) {
1457 foreach ( $fields as $name => $value ) {
1458 // Skip name and email fields
1459 $field_name = strtolower( $name );
1460 if ( strpos( $field_name, 'name' ) !== false || strpos( $field_name, 'email' ) !== false ) {
1461 continue;
1462 }
1463 if ( ! empty( $value ) ) {
1464 echo esc_html( wp_trim_words( $value, 10, '...' ) );
1465 $subject_found = true;
1466 break;
1467 }
1468 }
1469 }
1470 }
1471
1472 if ( ! $subject_found ) {
1473 echo '&mdash;';
1474 }
1475 break;
1476
1477 case 'form':
1478 $form_id = intval( get_post_meta( $post_id, '_wpzf_form_id', true ) );
1479 $form_name = __( '[Unknown]', 'wpzoom-forms' );
1480
1481 if ( $form_id > 0 ) {
1482 $form_post = get_post( $form_id );
1483
1484 if ( ! is_null( $form_post ) && 'wpzf-form' === $form_post->post_type ) {
1485 $form_title = get_the_title( $form_id );
1486
1487 if ( ! empty( $form_title ) && 'trash' !== $form_post->post_status ) {
1488 $form_name = '<a href="' . esc_url( get_edit_post_link( $form_id ) ) . '">' . esc_html( $form_title ) . '</a>';
1489 } elseif ( 'trash' === $form_post->post_status ) {
1490 $form_name = sprintf(
1491 '<span style="color: #d63638;">%s</span> <small>(%s)</small>',
1492 esc_html( $form_title ?: __( 'Untitled Form', 'wpzoom-forms' ) ),
1493 esc_html__( 'Trashed', 'wpzoom-forms' )
1494 );
1495 }
1496 }
1497 }
1498
1499 echo wp_kses( $form_name, array( 'a' => array( 'href' => array() ), 'span' => array( 'style' => array() ), 'small' => array() ) );
1500 break;
1501 }
1502 }
1503
1504 /**
1505 * Sets what the primary column is in the post list for certain post types.
1506 *
1507 * @access public
1508 * @param string $default The default/primary column.
1509 * @param string $screen Which screen is currently being rendered.
1510 * @return array
1511 * @since 1.0.0
1512 */
1513 public function post_list_primary_column( $default, $screen ) {
1514 if ( 'edit-wpzf-submission' == $screen ) {
1515 $default = 'submission_id';
1516 }
1517
1518 return $default;
1519 }
1520
1521 /**
1522 * Handles sorting of custom columns in the post list for certain post types.
1523 *
1524 * @access public
1525 * @param WP_Query $query The current query.
1526 * @return array
1527 * @since 1.0.0
1528 */
1529 public function sort_custom_column( $query ) {
1530 $orderby = $query->get( 'orderby' );
1531
1532 if ( 'wpzf_desc' == $orderby || 'wpzf_form' == $orderby ) {
1533 $key = 'wpzf_desc' == $orderby ? '_wpzf_fields' : '_wpzf_form_id';
1534
1535 $query->set( 'meta_query', array(
1536 'relation' => 'OR',
1537 array(
1538 'key' => $key,
1539 'compare' => 'NOT EXISTS'
1540 ),
1541 array(
1542 'key' => $key
1543 )
1544 ) );
1545
1546 $query->set( 'orderby', 'meta_value' );
1547 }
1548 }
1549
1550 /**
1551 * Changes things on the edit screen of certain post types.
1552 *
1553 * @access public
1554 * @return void
1555 * @since 1.0.0
1556 */
1557 public function remove_meta_boxes() {
1558 global $current_screen;
1559
1560 if ( 'wpzf-submission' == $current_screen->post_type && 'wpzf-submission' == $current_screen->id ) {
1561 global $wp_meta_boxes;
1562
1563 $wp_meta_boxes = array(
1564 'wpzf-submission' => array(
1565 'advanced' => array(),
1566 'side' => array(
1567 'high' => array(
1568 'wpzf-submission-details-mb' => $wp_meta_boxes['wpzf-submission']['side']['high']['wpzf-submission-details-mb']
1569 )
1570 ),
1571 'normal' => array(
1572 'high' => array(
1573 'wpzf-submission-mb' => $wp_meta_boxes['wpzf-submission']['normal']['high']['wpzf-submission-mb']
1574 )
1575 )
1576 )
1577 );
1578
1579 add_screen_option( 'layout_columns', array( 'max' => 2, 'default' => 2 ) );
1580 }
1581 }
1582
1583 /**
1584 * Removes screen options where not needed.
1585 *
1586 * @access public
1587 * @param bool $show_screen Whether to show Screen Options tab.
1588 * @param WP_Screen $screen Current WP_Screen instance.
1589 * @return bool
1590 * @since 1.0.0
1591 */
1592 public function remove_screen_options( $show_screen, $screen ) {
1593 return 'wpzf-submission' == $screen->post_type && 'post' == $screen->base ? false : $show_screen;
1594 }
1595
1596 /**
1597 * Changes the views items for certain post types.
1598 *
1599 * @access public
1600 * @param array $views All the possible views.
1601 * @return bool
1602 * @since 1.0.0
1603 */
1604 public function post_list_views( $views ) {
1605 if ( isset( $views['publish'] ) ) {
1606 unset( $views['publish'] );
1607 }
1608
1609 $current_page = get_current_screen()->id;
1610
1611 ?>
1612 <div class="wpzf_wrap wpzf_settings-add-new">
1613 <?php
1614 if ( 'edit-wpzf-form' == $current_page ) {
1615 echo '<a href="' . esc_url( admin_url( 'post-new.php?post_type=wpzf-form' ) ) . '" class="button-primary">' . __( 'Add new form', 'wpzoom-forms' ) . '</a>';
1616 }
1617 ?>
1618 </div>
1619 <?php
1620
1621 return $views;
1622 }
1623
1624 /**
1625 * Adds custom meta boxes to certain post types.
1626 *
1627 * @access public
1628 * @return void
1629 * @since 1.0.0
1630 */
1631 public function add_meta_boxes() {
1632 add_meta_box(
1633 'wpzf-submission-details-mb',
1634 __( 'Submission Details', 'wpzoom-forms' ),
1635 array( $this, 'submission_meta_box_details' ),
1636 'wpzf-submission',
1637 'side',
1638 'high'
1639 );
1640
1641 add_meta_box(
1642 'wpzf-submission-mb',
1643 __( 'Submission', 'wpzoom-forms' ),
1644 array( $this, 'submission_meta_box' ),
1645 'wpzf-submission',
1646 'normal',
1647 'high'
1648 );
1649 }
1650
1651 /**
1652 * Outputs the content for the submission details sidebar meta box.
1653 *
1654 * @access public
1655 * @return void
1656 * @since 1.3.5
1657 */
1658 public function submission_meta_box_details() {
1659 $post_id = get_the_ID();
1660 $post = get_post( $post_id );
1661
1662 // Get form info
1663 $form_id = get_post_meta( $post_id, '_wpzf_form_id', true );
1664 $form_name = __( '[Unknown]', 'wpzoom-forms' );
1665 $show_form_link = false;
1666
1667 if ( $form_id > 0 ) {
1668 $form_post = get_post( $form_id );
1669
1670 if ( ! is_null( $form_post ) && 'wpzf-form' === $form_post->post_type ) {
1671 $form_title = get_the_title( $form_id );
1672
1673 if ( ! empty( $form_title ) && 'trash' !== $form_post->post_status ) {
1674 $form_name = $form_title;
1675 $show_form_link = true;
1676 } elseif ( 'trash' === $form_post->post_status ) {
1677 $form_name = sprintf(
1678 '<span style="color: #d63638;">%s</span> <small>(%s)</small>',
1679 $form_title ?: __( 'Untitled Form', 'wpzoom-forms' ),
1680 __( 'Trashed', 'wpzoom-forms' )
1681 );
1682 }
1683 }
1684 }
1685
1686 // Get submission date
1687 $date = sprintf(
1688 __( 'Submitted on %1$s at %2$s', 'wpzoom-forms' ),
1689 get_the_date(),
1690 get_the_time()
1691 );
1692
1693 // Get trash/delete link
1694 $delete_link = '';
1695 $action_text = '';
1696 if ( current_user_can( 'delete_post', $post_id ) ) {
1697 if ( 'trash' === $post->post_status ) {
1698 $delete_link = get_delete_post_link( $post_id, '', true );
1699 $action_text = __( 'Delete Permanently', 'wpzoom-forms' );
1700 } else {
1701 $delete_link = get_delete_post_link( $post_id );
1702 $action_text = _x( 'Move to Trash', 'verb', 'wpzoom-forms' );
1703 }
1704 }
1705
1706 // Output the details
1707 ?>
1708 <style>
1709 #wpzf-submission-details-mb {
1710 display: block !important;
1711 }
1712 #wpzf-submission-details-mb .inside {
1713 margin: 0;
1714 padding: 0;
1715 }
1716 .wpzf-submission-details {
1717 margin: 0;
1718 padding: 0;
1719 list-style: none;
1720 display: block !important;
1721 }
1722 .wpzf-submission-details li {
1723 margin: 0;
1724 padding: 8px 12px;
1725 border-bottom: 1px solid #eee;
1726 display: block;
1727 line-height: 1.5;
1728 }
1729 .wpzf-submission-details li:last-child {
1730 border-bottom: none;
1731 }
1732 .wpzf-submission-details li strong {
1733 display: block;
1734 margin-bottom: 4px;
1735 color: #1d2327;
1736 }
1737 .wpzf-submission-details .wpzf-submission-actions {
1738 padding: 12px;
1739 }
1740 .wpzf-submission-details .submitdelete {
1741 color: #b32d2e;
1742 text-decoration: none;
1743 }
1744 .wpzf-submission-details .submitdelete:hover {
1745 color: #a00;
1746 }
1747 </style>
1748 <ul class="wpzf-submission-details">
1749 <li>
1750 <strong><?php esc_html_e( 'Submission ID:', 'wpzoom-forms' ); ?></strong>
1751 #<?php echo esc_html( $post_id ); ?>
1752 </li>
1753 <li>
1754 <strong><?php esc_html_e( 'Form:', 'wpzoom-forms' ); ?></strong>
1755 <?php if ( $show_form_link ) : ?>
1756 <a href="<?php echo esc_url( get_edit_post_link( $form_id ) ); ?>"><?php echo esc_html( $form_name ); ?></a>
1757 <?php else : ?>
1758 <?php echo wp_kses_post( $form_name ); ?>
1759 <?php endif; ?>
1760 </li>
1761 <li>
1762 <strong><?php esc_html_e( 'Date:', 'wpzoom-forms' ); ?></strong>
1763 <?php echo esc_html( $date ); ?>
1764 </li>
1765 <?php if ( ! empty( $delete_link ) ) : ?>
1766 <li class="wpzf-submission-actions">
1767 <a href="<?php echo esc_url( $delete_link ); ?>" class="submitdelete deletion">
1768 <?php echo esc_html( $action_text ); ?>
1769 </a>
1770 </li>
1771 <?php endif; ?>
1772 </ul>
1773 <?php
1774 }
1775
1776 /**
1777 * Outputs the content for the submission meta box.
1778 *
1779 * @access public
1780 * @return void
1781 * @since 1.0.0
1782 */
1783 public function submission_meta_box() {
1784 $fields = get_post_meta( get_the_ID(), '_wpzf_fields', true );
1785
1786 if ( ! is_null( $fields ) && false !== $fields && is_array( $fields ) && ! empty( $fields ) ) {
1787 echo '<ul class="wpzf-submission-view">';
1788
1789 $form_id = intval( get_post_meta( get_the_ID(), '_wpzf_form_id', true ) );
1790 $form_name = __( '[Unknown]', 'wpzoom-forms' );
1791
1792 if ( $form_id > 0 ) {
1793 $form_name = $form_id;
1794
1795 if ( ! is_null( get_post( $form_id ) ) ) {
1796 $form_name = '<a href="' . esc_url( get_edit_post_link( $form_id ) ) . '">' . get_the_title( $form_id ) . '</a>';
1797 }
1798 }
1799
1800 $form_name = wp_kses( $form_name, array( 'a' => array( 'href' => array() ) ) );
1801
1802 echo '<li class="top"><h3>' . sprintf( __( 'Form: %s', 'wpzoom-forms' ), $form_name ) . '</h3></li>';
1803
1804 foreach ( $fields as $name => $value ) {
1805 echo '<li><h3>' . esc_html( $name ) . '</h3><div>' . make_clickable( nl2br( esc_html( $value ) ) ) . '</div></li>';
1806 }
1807
1808 echo '</ul>';
1809
1810 return;
1811 }
1812
1813 printf( '<p class="empty">%s</p>', __( 'Submission is empty&hellip;', 'wpzoom-forms' ) );
1814 }
1815
1816 /**
1817 * Returns whether the current page is related to the given post type.
1818 *
1819 * @access public
1820 * @param string $type The type to check for.
1821 * @return bool
1822 * @since 1.0.0
1823 */
1824 public function is_post_type( $type ) {
1825 $typenow = '';
1826
1827 if ( is_admin() && current_user_can( 'edit_posts' ) ) {
1828 if ( isset( $_REQUEST['post_type'] ) && post_type_exists( sanitize_key( $_REQUEST['post_type'] ) ) ) {
1829 $typenow = sanitize_key( $_REQUEST['post_type'] );
1830 } else {
1831 $post_id = -1;
1832
1833 if ( isset( $_GET['post'] ) && isset( $_POST['post_ID'] ) && (int) $_GET['post'] !== (int) $_POST['post_ID'] ) {
1834 // Do nothing
1835 } elseif ( isset( $_GET['post'] ) ) {
1836 $post_id = (int) $_GET['post'];
1837 } elseif ( isset( $_POST['post_ID'] ) ) {
1838 $post_id = (int) $_POST['post_ID'];
1839 }
1840
1841 if ( $post_id > -1 ) {
1842 $post = get_post( $post_id );
1843
1844 if ( ! is_null( $post ) && $post instanceof WP_Post ) {
1845 $typenow = $post->post_type;
1846 }
1847 }
1848 }
1849 }
1850
1851 return $type == $typenow;
1852 }
1853
1854 /**
1855 * Called to render the form block on the frontend.
1856 *
1857 * @access public
1858 * @param array $attributes An array containing the attributes for the block.
1859 * @param string $content A string containing the content of the block.
1860 * @param WP_Block $block The WP_Block representation of the block.
1861 * @return string
1862 * @since 1.0.0
1863 */
1864 public function form_block_render( $attributes, $content = '', $block = null ) {
1865 global $current_screen;
1866
1867 $current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null;
1868
1869 // Allow rendering during REST API requests (ServerSideRender / block preview)
1870 // and during Elementor's AJAX-driven widget preview in the editor.
1871 $is_rest = defined( 'REST_REQUEST' ) && REST_REQUEST;
1872 $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
1873 if ( ( is_admin() && ! $is_rest && ! $is_elementor_ajax ) || ( ! is_null( $current_screen ) && $current_screen->is_block_editor() ) ) return '';
1874
1875 // Get form ID and validate form exists and is published
1876 $form_id = isset( $attributes['formId'] ) ? intval( $attributes['formId'] ) : 0;
1877 $form_post = get_post( $form_id );
1878
1879 // Check if form exists and is published
1880 if ( ! $form_post || 'wpzf-form' !== $form_post->post_type || 'publish' !== $form_post->post_status ) {
1881 // Show message only to administrators
1882 if ( current_user_can( 'manage_options' ) ) {
1883 $message = '';
1884 if ( ! $form_post ) {
1885 $message = sprintf(
1886 __( 'Contact form not found (ID: %d). Please select a different form or create a new one.', 'wpzoom-forms' ),
1887 $form_id
1888 );
1889 } elseif ( 'wpzf-form' !== $form_post->post_type ) {
1890 $message = sprintf(
1891 __( 'Invalid form type (ID: %d). Please select a valid WPZOOM form.', 'wpzoom-forms' ),
1892 $form_id
1893 );
1894 } elseif ( 'trash' === $form_post->post_status ) {
1895 $message = sprintf(
1896 __( 'Contact form "%s" is in trash (ID: %d). Please restore it or select a different form.', 'wpzoom-forms' ),
1897 $form_post->post_title,
1898 $form_id
1899 );
1900 } else {
1901 $message = sprintf(
1902 __( 'Contact form "%s" is not published (ID: %d). Please publish it or select a different form.', 'wpzoom-forms' ),
1903 $form_post->post_title,
1904 $form_id
1905 );
1906 }
1907
1908 return sprintf(
1909 '<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;">
1910 <p style="margin: 0; font-size: 14px; color: #d63638;">
1911 <strong>%s:</strong> %s
1912 </p>
1913 <p style="margin: 8px 0 0 0; font-size: 13px; color: #646970;">
1914 <a href="%s" target="_blank">%s</a>
1915 </p>
1916 </div>',
1917 esc_html__( 'WPZOOM Forms Admin Notice', 'wpzoom-forms' ),
1918 esc_html( $message ),
1919 esc_url( admin_url( 'edit.php?post_type=wpzf-form' ) ),
1920 esc_html__( 'Manage Forms', 'wpzoom-forms' )
1921 );
1922 }
1923
1924 // Don't show anything to non-administrators
1925 return '';
1926 }
1927
1928 $align = isset( $attributes['align'] ) && ! empty( $attributes['align'] ) ? $attributes['align'] : 'none';
1929
1930 //Get styles from the block
1931
1932 $fieldBgColor = isset( $attributes['fieldBgColor'] ) ? $attributes['fieldBgColor'] : '';
1933 $fieldBrdStyle = isset( $attributes['fieldBrdStyle'] ) ? $attributes['fieldBrdStyle'] : '';
1934 $fieldBrdWidth = isset( $attributes['fieldBrdWidth'] ) ? $attributes['fieldBrdWidth'] . 'px' : '';
1935 $fieldBrdRadius = isset( $attributes['fieldBrdRadius'] ) ? $attributes['fieldBrdRadius'] . 'px' : '';
1936 $fieldBrdColor = isset( $attributes['fieldBrdColor'] ) ? $attributes['fieldBrdColor'] : '';
1937 $fieldTextColor = isset( $attributes['fieldTextColor'] ) ? $attributes['fieldTextColor'] : '';
1938 $labelTextColor = isset( $attributes['labelTextColor'] ) ? $attributes['labelTextColor'] : '';
1939 $btnBrdRadius = isset( $attributes['btnBrdRadius'] ) ? $attributes['btnBrdRadius'] . 'px' : '';
1940 $btnBrdStyle = isset( $attributes['btnBrdStyle'] ) ? $attributes['btnBrdStyle'] : '';
1941 $btnTextColor = isset( $attributes['btnTextColor'] ) ? $attributes['btnTextColor'] : '';
1942 $btnBrdWidth = isset( $attributes['btnBrdWidth'] ) ? $attributes['btnBrdWidth']. 'px' : '';
1943 $btnBrdColor = isset( $attributes['btnBrdColor'] ) ? $attributes['btnBrdColor'] : '';
1944 $btnBgColor = isset( $attributes['btnBgColor'] ) ? $attributes['btnBgColor'] : '';
1945
1946 $form_ID = 'wpzf-' . intval( $attributes['formId'] );
1947 $form_notice_id = $form_ID . '-notice';
1948 $form_success_message = get_post_meta( intval( $attributes['formId'] ), '_form_success_message', true );
1949 $form_failure_message = get_post_meta( intval( $attributes['formId'] ), '_form_failure_message', true );
1950
1951 // Use default messages if custom ones are not set
1952 if ( empty( $form_success_message ) ) {
1953 $form_success_message = __( 'Thanks! We\'ve received your submission!', 'wpzoom-forms' );
1954 }
1955 if ( empty( $form_failure_message ) ) {
1956 $form_failure_message = __( 'Submission failed!', 'wpzoom-forms' );
1957 }
1958
1959 // Enqueue core block library styles for inner blocks (columns, group, spacer, etc.)
1960 wp_enqueue_style( 'wp-block-library' );
1961
1962 // Process form content through do_blocks() to render inner blocks and enqueue their styles
1963 $form_content = get_post_field( 'post_content', intval( $attributes['formId'] ), 'raw' );
1964 $form_content = do_blocks( $form_content );
1965 $form_content = preg_replace(
1966 array( '/<!--(.*)-->/Uis', '/<(input|textarea|select)(.*)name="([^"]+)"/Uis' ),
1967 array( '', '<$1$2name="wpzf_$3"' ),
1968 $form_content
1969 );
1970
1971 // If this (legacy) form actually contains a date field, make sure the
1972 // datepicker assets load. We enqueue here — at render time — rather than
1973 // relying solely on the page-scan in block_frontend_assets(), which only
1974 // inspects a single form-block per page and misses multi-form pages,
1975 // shortcode and Elementor embeds. The scripts are registered in_footer so
1976 // this late enqueue still prints correctly.
1977 if ( false !== strpos( $form_content, 'data-datepicker' ) ) {
1978 wp_enqueue_style( 'wpzoom-forms-css-frontend-flatpickr' );
1979 wp_enqueue_script( 'wpzoom-forms-js-frontend-flatpickr' );
1980 wp_enqueue_script( 'wpzoom-forms-js-frontend-datepicker' );
1981 }
1982
1983 $submitted_form_id = isset( $_GET['wpzf_submitted_form'] ) ? intval( $_GET['wpzf_submitted_form'] ) : -1;
1984 $show_notice = isset( $_GET['success'] ) && ( -1 === $submitted_form_id || $submitted_form_id === intval( $attributes['formId'] ) );
1985
1986 $content = sprintf(
1987 '<!-- ZOOM Forms Start -->
1988 <form id="wpzf-%2$s" method="post" action="%1$s" class="wpzoom-forms_form%6$s">
1989 <input type="hidden" name="action" value="wpzf_submit" />
1990 <input type="hidden" name="form_id" value="%2$s" />
1991 %3$s
1992 %5$s
1993 </form>
1994 %4$s
1995 <!-- ZOOM Forms End -->',
1996 admin_url( 'admin-post.php' ),
1997 intval( $attributes['formId'] ),
1998 wp_nonce_field( 'wpzf_submit', '_wpnonce', true, false ),
1999 ( $show_notice
2000 ? '<div id="' . esc_attr( $form_notice_id ) . '" class="notice ' . ( '1' == $_GET['success'] ? 'success' : 'error' ) . '" tabindex="-1"><p>' .
2001 ( '1' == $_GET['success'] ? wp_kses_post($form_success_message) : wp_kses_post($form_failure_message) ) .
2002 '</p></div>'
2003 : ''
2004 ),
2005 $form_content,
2006 ( 'none' !== $align ? ' align' . $align : '' )
2007 );
2008
2009 preg_match( '/<input(?:.*)name="([^"]+)"(?:.*)data-replyto="true"/is', $content, $match1 );
2010 preg_match( '/<input(?:.*)name="([^"]+)"(?:.*)data-subject="true"/is', $content, $match2 );
2011
2012 if ( ! empty( $match1 ) && is_array( $match1 ) && isset( $match1[1] ) ) {
2013 $content = preg_replace( '/<\/form>/is', '<input type="hidden" name="wpzf_replyto" value="' . $match1[1] . '" /></form>', $content );
2014 }
2015
2016 if ( ! empty( $match2 ) && is_array( $match2 ) && isset( $match2[1] ) ) {
2017 $content = preg_replace( '/<\/form>/is', '<input type="hidden" name="wpzf_subject" value="' . $match2[1] . '" /></form>', $content );
2018 }
2019
2020 $captcha_config = $this->get_spam_protection_config();
2021 $captcha_method = $captcha_config['active_service'];
2022 $recaptcha_type = $captcha_config['type'];
2023 $recaptcha_badge_location = esc_attr( sanitize_text_field( WPZOOM_Forms_Settings::get( 'wpzf_global_recaptcha_badge_location' ) ) );
2024 $turnstile_site_key = esc_attr( $captcha_config['turnstile_site_key'] );
2025 $turnstile_widget_theme = esc_attr( sanitize_text_field( WPZOOM_Forms_Settings::get( 'wpzf_global_turnstile_widget_theme' ) ) );
2026
2027 if ( 'recaptcha' === $captcha_method ) {
2028 $recaptcha_site_key = ( 'v3' === $recaptcha_type ) ? $captcha_config['recaptcha_v3_site_key'] : $captcha_config['recaptcha_v2_site_key'];
2029 $data_badge_location = '';
2030 if ( ! empty( $recaptcha_badge_location ) ) {
2031 $data_badge_location = 'data-badge="' . $recaptcha_badge_location . '"';
2032 }
2033
2034 $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 );
2035 } elseif ( 'turnstile' === $captcha_method ) {
2036 $turnstile_widget = '<div class="cf-turnstile" data-theme="' . $turnstile_widget_theme . '" data-sitekey="' . $turnstile_site_key . '"></div>';
2037 $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 );
2038 }
2039
2040 $style = $styleOutput = '';
2041
2042 // Add custom styles to the form
2043 $field_sel = '#' . $form_ID . ' input:not([type="submit"]), #' . $form_ID . ' textarea, #' . $form_ID . ' select';
2044 if( ! empty( $fieldBgColor ) ) {
2045 $styleOutput .= sprintf( $field_sel . ' { background-color: %s; }', $fieldBgColor );
2046 }
2047 if( ! empty( $fieldBrdStyle ) && 'default' !== $fieldBrdStyle ) {
2048 $styleOutput .= sprintf( $field_sel . ' { border-style: %s; }', $fieldBrdStyle );
2049 if( ! empty( $fieldBrdWidth ) ) {
2050 $styleOutput .= sprintf( $field_sel . ' { border-width: %s; }', $fieldBrdWidth );
2051 }
2052 if( ! empty( $fieldBrdRadius ) ) {
2053 $styleOutput .= sprintf( $field_sel . ' { border-radius: %s; }', $fieldBrdRadius );
2054 }
2055 }
2056
2057 if( ! empty( $fieldBrdColor ) ) {
2058 $styleOutput .= sprintf( $field_sel . ' { border-color: %s; }', $fieldBrdColor );
2059 }
2060 if( ! empty( $fieldTextColor ) ) {
2061 $styleOutput .= sprintf( $field_sel . ' { color: %s; }', $fieldTextColor );
2062 }
2063
2064 //Label styles
2065 if( ! empty( $labelTextColor ) ) {
2066 $styleOutput .= sprintf( '#' . $form_ID . ' label { color: %s; }', $labelTextColor );
2067 }
2068
2069 //Button styles — target both old <input type="submit"> and new builder's <button class="wpzf-submit__btn">
2070 $btn_sel = '#' . $form_ID . ' input[type="submit"], #' . $form_ID . ' button.wpzf-submit__btn';
2071 if( ! empty( $btnBrdStyle ) && 'default' !== $btnBrdStyle ) {
2072 $styleOutput .= sprintf( $btn_sel . ' { border-style: %s; }', $btnBrdStyle );
2073 if( ! empty( $btnBrdWidth ) ) {
2074 $styleOutput .= sprintf( $btn_sel . ' { border-width: %s; }', $btnBrdWidth );
2075 }
2076 if( ! empty( $btnBrdRadius ) ) {
2077 $styleOutput .= sprintf( $btn_sel . ' { border-radius: %s; }', $btnBrdRadius );
2078 }
2079 }
2080 if( ! empty( $btnTextColor ) ) {
2081 $styleOutput .= sprintf( $btn_sel . ' { color: %s; }', $btnTextColor );
2082 }
2083 if( ! empty( $btnBrdColor ) ) {
2084 $styleOutput .= sprintf( $btn_sel . ' { border-color: %s; }', $btnBrdColor );
2085 }
2086 if( ! empty( $btnBgColor ) ) {
2087 $styleOutput .= sprintf( $btn_sel . ' { background-color: %s; }', $btnBgColor );
2088 }
2089
2090 // Add styles for the notice
2091 $styleOutput .= sprintf( '#%s + .notice { margin-top: 20px; padding: 15px; border-radius: 4px; }', $form_ID );
2092 $styleOutput .= sprintf( '#%s + .notice.success { background-color: #e7f7ed; color: #227045; border-left: 4px solid #46b450; }', $form_ID );
2093 $styleOutput .= sprintf( '#%s + .notice.error { background-color: #fde8e8; color: #8a1f11; border-left: 4px solid #cc0000; }', $form_ID );
2094 $styleOutput .= sprintf( '#%s + .notice p { margin: 0; }', $form_ID );
2095 $styleOutput .= sprintf( '#%s { scroll-margin-top: 24px; }', $form_notice_id );
2096
2097 $style = sprintf( '<style>%s</style>',
2098 $styleOutput
2099 );
2100
2101 $content = $content . $style;
2102
2103 return $content;
2104 }
2105
2106 /**
2107 * Render the contents of the settings page in the admin.
2108 *
2109 * @access public
2110 * @return void
2111 * @since 1.0.0
2112 */
2113 public function render_settings_page() {
2114 do_action( 'wpzoom_forms_admin_page' );
2115 }
2116
2117 /**
2118 * Render the contents of the upsell page in the admin.
2119 *
2120 * @access public
2121 * @return void
2122 * @since 1.0.0
2123 */
2124 public function render_upsell_page() {
2125 do_action( 'wpzoom_forms_admin_page_upsell' );
2126 }
2127
2128 /**
2129 * Render the Import & Export upsell page.
2130 *
2131 * @access public
2132 * @return void
2133 * @since 1.3.4
2134 */
2135 public function render_import_export_upsell_page() {
2136 if ( ! current_user_can( 'edit_posts' ) ) {
2137 wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'wpzoom-forms' ) );
2138 }
2139
2140 $pro_url = 'https://www.wpzoom.com/plugins/wpzoom-forms/?utm_source=wpadmin&utm_medium=wpzoom-forms-free&utm_campaign=import-export-upsell';
2141 ?>
2142 <div class="wrap wpzoom-forms-import-export-upsell-page" style="position: relative;">
2143 <h1><?php esc_html_e( 'Import & Export Forms', 'wpzoom-forms' ); ?></h1>
2144 <div class="wpzoom-forms-import-export-upsell-container">
2145 <!-- Blurred placeholder content -->
2146 <div class="wpzoom-forms-import-export-blurred-content" style="filter: blur(4px); pointer-events: none; opacity: 0.3; margin-bottom: 40px;">
2147 <div style="background: #fff; border: 1px solid #c3c4c7; padding: 20px; margin: 20px 0; border-radius: 4px;">
2148 <div style="display: flex; gap: 20px; margin-bottom: 30px;">
2149 <div style="flex: 1;">
2150 <label style="display: block; margin-bottom: 8px; font-weight: 600; color: #1d2327;"><?php esc_html_e( 'Export Forms', 'wpzoom-forms' ); ?></label>
2151 <select style="width: 100%; padding: 8px; border: 1px solid #c3c4c7; border-radius: 4px;">
2152 <option><?php esc_html_e( 'Select forms to export...', 'wpzoom-forms' ); ?></option>
2153 <option><?php esc_html_e( 'Contact Form', 'wpzoom-forms' ); ?></option>
2154 <option><?php esc_html_e( 'Newsletter Form', 'wpzoom-forms' ); ?></option>
2155 </select>
2156 </div>
2157 <div style="flex: 1;">
2158 <label style="display: block; margin-bottom: 8px; font-weight: 600; color: #1d2327;"><?php esc_html_e( 'Export Format', 'wpzoom-forms' ); ?></label>
2159 <select style="width: 100%; padding: 8px; border: 1px solid #c3c4c7; border-radius: 4px;">
2160 <option>JSON</option>
2161 <option>CSV</option>
2162 </select>
2163 </div>
2164 </div>
2165 <div style="display: flex; gap: 20px; margin-bottom: 30px;">
2166 <div style="flex: 1;">
2167 <label style="display: block; margin-bottom: 8px; font-weight: 600; color: #1d2327;"><?php esc_html_e( 'Import Forms', 'wpzoom-forms' ); ?></label>
2168 <input type="file" style="width: 100%; padding: 8px; border: 1px solid #c3c4c7; border-radius: 4px;" />
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 Submissions', 'wpzoom-forms' ); ?></label>
2172 <select style="width: 100%; padding: 8px; border: 1px solid #c3c4c7; border-radius: 4px;">
2173 <option><?php esc_html_e( 'Select form...', 'wpzoom-forms' ); ?></option>
2174 </select>
2175 </div>
2176 </div>
2177 <div style="display: flex; gap: 10px;">
2178 <button style="padding: 10px 20px; background: #2271b1; color: #fff; border: none; border-radius: 4px; cursor: pointer;"><?php esc_html_e( 'Export', 'wpzoom-forms' ); ?></button>
2179 <button style="padding: 10px 20px; background: #2271b1; color: #fff; border: none; border-radius: 4px; cursor: pointer;"><?php esc_html_e( 'Import', 'wpzoom-forms' ); ?></button>
2180 <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>
2181 </div>
2182 </div>
2183 </div>
2184
2185 <!-- Overlay with Popover Modal -->
2186 <div class="import-export-upsell-overlay">
2187 <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%;">
2188 <!-- Icon -->
2189 <div style="text-align: center; margin-bottom: 20px;">
2190 <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);">
2191 <svg width="40" height="40" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
2192 <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"/>
2193 </svg>
2194 </div>
2195 </div>
2196
2197 <!-- Title -->
2198 <h2 style="text-align: center; margin: 0 0 15px 0; font-size: 24px; font-weight: 600; color: #1d2327;">
2199 <?php esc_html_e( 'Unlock Import & Export', 'wpzoom-forms' ); ?>
2200 </h2>
2201
2202 <!-- Description -->
2203 <p style="text-align: center; margin: 0 0 25px 0; color: #50575e; font-size: 14px; line-height: 1.6;">
2204 <?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' ); ?>
2205 </p>
2206
2207 <!-- Features List -->
2208 <ul style="list-style: none; padding: 0; margin: 0 0 30px 0;">
2209 <li style="display: flex; align-items: flex-start; margin-bottom: 12px; color: #1d2327; font-size: 14px;">
2210 <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;">
2211 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2212 <path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2213 </svg>
2214 </span>
2215 <span><?php esc_html_e( 'Export forms to JSON files for backup or migration', 'wpzoom-forms' ); ?></span>
2216 </li>
2217 <li style="display: flex; align-items: flex-start; margin-bottom: 12px; color: #1d2327; font-size: 14px;">
2218 <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;">
2219 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2220 <path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2221 </svg>
2222 </span>
2223 <span><?php esc_html_e( 'Import forms from JSON files to quickly set up new sites', 'wpzoom-forms' ); ?></span>
2224 </li>
2225 <li style="display: flex; align-items: flex-start; margin-bottom: 12px; color: #1d2327; font-size: 14px;">
2226 <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;">
2227 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2228 <path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2229 </svg>
2230 </span>
2231 <span><?php esc_html_e( 'Export form submissions to CSV for data analysis', 'wpzoom-forms' ); ?></span>
2232 </li>
2233 <li style="display: flex; align-items: flex-start; margin-bottom: 12px; color: #1d2327; font-size: 14px;">
2234 <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;">
2235 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2236 <path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2237 </svg>
2238 </span>
2239 <span><?php esc_html_e( 'Transfer forms between multiple WordPress sites', 'wpzoom-forms' ); ?></span>
2240 </li>
2241 <li style="display: flex; align-items: flex-start; margin-bottom: 12px; color: #1d2327; font-size: 14px;">
2242 <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;">
2243 <svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
2244 <path d="M10 3L4.5 8.5L2 6" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
2245 </svg>
2246 </span>
2247 <span><?php esc_html_e( 'Backup your forms before making major changes', 'wpzoom-forms' ); ?></span>
2248 </li>
2249 </ul>
2250
2251 <!-- CTA Button -->
2252 <div style="text-align: center; margin-bottom: 15px;">
2253 <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)';">
2254 <?php esc_html_e( 'Upgrade to PRO', 'wpzoom-forms' ); ?>
2255 </a>
2256 </div>
2257
2258 <!-- Plugin Attribution -->
2259 <p style="text-align: center; margin: 0; color: #8c8f94; font-size: 12px;">
2260 <?php esc_html_e( 'Part of the WPZOOM Forms PRO plugin', 'wpzoom-forms' ); ?>
2261 </p>
2262 </div>
2263 </div>
2264 </di>
2265 </div>
2266
2267 <style>
2268 .wpzoom-forms-import-export-upsell-container {
2269 position: relative;
2270 min-height: 680px;
2271 }
2272 .wpzoom-forms-import-export-blurred-content {
2273 z-index: 1;
2274 }
2275 .import-export-upsell-overlay {
2276 position: absolute;
2277 top: 60px;
2278 left: 0;
2279 right: 0;
2280 bottom: 0;
2281 display: flex;
2282 align-items: flex-start;
2283 justify-content: center;
2284 padding-top: 80px;
2285 z-index: 100;
2286 background: hsla(0, 0%, 100%, .1);
2287 }
2288 .wpzoom-forms-import-export-popover {
2289 z-index: 10000;
2290 }
2291 @media screen and (max-width: 782px) {
2292 .wpzoom-forms-import-export-popover {
2293 padding: 30px 20px;
2294 max-width: 90%;
2295 }
2296 }
2297 </style>
2298 <?php
2299 }
2300
2301 /**
2302 * Page header used on all admin pages.
2303 *
2304 * @access public
2305 * @return void
2306 * @since 1.0.0
2307 */
2308 public function admin_page_header() {
2309
2310 $current_page = get_current_screen()->id;
2311
2312 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 ) {
2313 ?>
2314 <header class="wpzoom-new-admin-wrap wpzoom-new-admin_settings-header">
2315 <h1 class="wpzoom-new-admin_settings-main-title wp-heading">
2316 <?php
2317 echo apply_filters(
2318 'wpzf_admin-header-title',
2319 sprintf(
2320 __( 'WPZOOM Forms <small>Lite</small>', 'wpzoom-forms' )
2321 )
2322 );
2323 ?>
2324
2325 <span class="wpzoom-new-admin_settings-main-title-version">
2326 <?php
2327 echo apply_filters(
2328 'wpzf_admin-header-title-version',
2329 sprintf(
2330 esc_html__( 'v %s', 'wpzoom-forms' ),
2331 WPZOOM_FORMS_VERSION
2332 )
2333 );
2334 ?>
2335 </span>
2336 </h1>
2337
2338 <nav class="wpzoom-new-admin_settings-main-nav">
2339 <ul>
2340 <?php
2341 $pages = apply_filters(
2342 'wpzoom_forms_settings_menu_items',
2343 array(
2344 'edit-wpzf-form' => array(
2345 'name' => __( 'Forms', 'wpzoom-forms' ),
2346 'url' => admin_url( 'edit.php?post_type=wpzf-form' ),
2347 ),
2348 'edit-wpzf-submission' => array(
2349 'name' => __( 'Submissions', 'wpzoom-forms' ),
2350 'url' => admin_url( 'edit.php?post_type=wpzf-submission' ),
2351 'altid' => 'wpzf-submission'
2352 ),
2353 'wpzf-form_page_wpzf-settings' => array(
2354 'name' => esc_html__( 'Settings', 'wpzoom-forms' ),
2355 'url' => admin_url( 'edit.php?post_type=wpzf-form&page=wpzf-settings' ),
2356 ),
2357 'wpzf-form_page_wpzf-pro-page' => array(
2358 'name' => esc_html__( 'Upgrade to PRO', 'wpzoom-forms' ),
2359 'url' => admin_url( 'edit.php?post_type=wpzf-form&page=wpzf-pro-page' ),
2360 ),
2361 'wpzf-form_page_wpzf-import' => array(
2362 'name' => esc_html__( 'Import & Export', 'wpzoom-forms' ),
2363 'url' => admin_url( 'edit.php?post_type=wpzf-form&page=wpzf-import' ),
2364 ),
2365 )
2366 );
2367
2368 foreach ( $pages as $id => $atts ) {
2369 printf(
2370 // translators: %1$s = possible class attribute, %2$s = page url, %3$s = page name.
2371 _x( '<li%1$s><a href="%2$s">%3$s</a></li>', 'Main menu page item', 'wpzoom-forms' ),
2372 ( $current_page === $id || ( isset( $atts['altid'] ) && $current_page == $atts['altid'] ) ? ' class="active"' : '' ),
2373 esc_url( $atts['url'] ),
2374 esc_html( $atts['name'] )
2375 );
2376 }
2377 ?>
2378 </ul>
2379 </nav>
2380 </header>
2381 <?php
2382 }
2383
2384 if ( 'edit-wpzf-form' == $current_page || 'wpzf-form' == $current_page ) {
2385 $forms_count = wp_count_posts( 'wpzf-form' );
2386
2387 if ( $forms_count->publish < 1 && $forms_count->draft < 1 && $forms_count->trash < 1 ) {
2388 ?>
2389 <div class="wpzf_no-forms">
2390 <div class="left-column">
2391 <h3>
2392 <?php esc_html_e( 'Hi there!', 'wpzoom-forms' ); ?>
2393 </h3>
2394
2395 <h2>
2396 <?php esc_html_e( 'It appears that you haven&rsquo;t made any forms yet.', 'wpzoom-forms' ); ?>
2397 </h2>
2398
2399 <p>
2400 <?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' ); ?>
2401 </p>
2402
2403 <a href="<?php echo esc_url( admin_url( 'post-new.php?post_type=wpzf-form' ) ); ?>" class="button-primary wpzf-add-form-btn">
2404 <?php esc_html_e( 'Add new form', 'wpzoom-forms' ); ?>
2405 </a>
2406 </div>
2407
2408 <div class="right-column">
2409 <svg width="360" height="360" fill="none" xmlns="http://www.w3.org/2000/svg">
2410 <mask id="a" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="25" y="140" width="304" height="175">
2411 <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" />
2412 </mask>
2413 <g mask="url(#a)">
2414 <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" />
2415 </g>
2416 <mask id="b" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="25" y="52" width="304" height="180">
2417 <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" />
2418 </mask>
2419 <g mask="url(#b)">
2420 <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" />
2421 </g>
2422 <mask id="c" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="30" y="200" width="97" height="108">
2423 <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" />
2424 </mask>
2425 <g mask="url(#c)">
2426 <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" />
2427 </g>
2428 <mask id="d" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="227" y="200" width="96" height="108">
2429 <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" />
2430 </mask>
2431 <g mask="url(#d)">
2432 <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" />
2433 </g>
2434 <mask id="e" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="274" y="228" width="44" height="49">
2435 <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" />
2436 </mask>
2437 <g mask="url(#e)">
2438 <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" />
2439 </g>
2440 <mask id="f" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="261" y="282" width="18" height="42">
2441 <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" />
2442 </mask>
2443 <g mask="url(#f)">
2444 <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)" />
2445 </g>
2446 <mask id="h" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="265" y="313" width="13" height="13">
2447 <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" />
2448 </mask>
2449 <g mask="url(#h)">
2450 <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" />
2451 </g>
2452 <mask id="i" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="242" y="284" width="36" height="41">
2453 <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" />
2454 </mask>
2455 <g mask="url(#i)">
2456 <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)" />
2457 </g>
2458 <mask id="k" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="264" y="278" width="39" height="17">
2459 <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" />
2460 </mask>
2461 <g mask="url(#k)">
2462 <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" />
2463 </g>
2464 <mask id="l" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="240" y="321" width="16" height="5">
2465 <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" />
2466 </mask>
2467 <g mask="url(#l)">
2468 <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" />
2469 </g>
2470 <mask id="m" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="272" y="249" width="31" height="29">
2471 <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" />
2472 </mask>
2473 <g mask="url(#m)">
2474 <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" />
2475 </g>
2476 <mask id="n" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="276" y="243" width="8" height="8">
2477 <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" />
2478 </mask>
2479 <g mask="url(#n)">
2480 <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)" />
2481 </g>
2482 <mask id="p" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="272" y="232" width="13" height="14">
2483 <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" />
2484 </mask>
2485 <g mask="url(#p)">
2486 <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)" />
2487 </g>
2488 <mask id="r" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="275" y="228" width="14" height="17">
2489 <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" />
2490 </mask>
2491 <g mask="url(#r)">
2492 <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" />
2493 </g>
2494 <mask id="s" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="280" y="239" width="4" height="5">
2495 <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" />
2496 </mask>
2497 <g mask="url(#s)">
2498 <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)" />
2499 </g>
2500 <mask id="u" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="271" y="272" width="33" height="8">
2501 <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" />
2502 </mask>
2503 <g mask="url(#u)">
2504 <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)" />
2505 </g>
2506 <mask id="w" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="288" y="236" width="15" height="5">
2507 <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" />
2508 </mask>
2509 <g mask="url(#w)">
2510 <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" />
2511 </g>
2512 <mask id="x" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="288" y="236" width="1" height="1">
2513 <path d="M288.703 236.979a3.421 3.421 0 0 1-.056-.478c.021.163.041.321.056.478z" fill="#fff" />
2514 </mask>
2515 <g mask="url(#x)">
2516 <path d="M288.701 236.98a3.415 3.415 0 0 1-.055-.478c.019.163.039.32.055.478z" fill="url(#y)" />
2517 </g>
2518 <mask id="z" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="308" y="249" width="7" height="17">
2519 <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" />
2520 </mask>
2521 <g mask="url(#z)">
2522 <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" />
2523 </g>
2524 <mask id="A" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="285" y="299" width="4" height="25">
2525 <path d="M288.083 324h-2.291v-24.71h2.291V324z" fill="#fff" />
2526 </mask>
2527 <g mask="url(#A)">
2528 <path d="M288.083 324h-2.29v-24.71h2.29V324z" fill="#242628" />
2529 </g>
2530 <mask id="B" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="276" y="322" width="21" height="3">
2531 <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" />
2532 </mask>
2533 <g mask="url(#B)">
2534 <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" />
2535 </g>
2536 <mask id="C" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="274" y="293" width="26" height="8">
2537 <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" />
2538 </mask>
2539 <g mask="url(#C)">
2540 <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" />
2541 </g>
2542 <mask id="D" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="261" y="277" width="28" height="3">
2543 <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" />
2544 </mask>
2545 <g mask="url(#D)">
2546 <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" />
2547 </g>
2548 <mask id="E" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="237" y="256" width="38" height="24">
2549 <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" />
2550 </mask>
2551 <g mask="url(#E)">
2552 <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" />
2553 </g>
2554 <mask id="F" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="215" y="279" width="108" height="6">
2555 <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" />
2556 </mask>
2557 <g mask="url(#F)">
2558 <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" />
2559 </g>
2560 <mask id="G" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="221" y="281" width="22" height="49">
2561 <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" />
2562 </mask>
2563 <g mask="url(#G)">
2564 <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" />
2565 </g>
2566 <mask id="H" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="224" y="281" width="22" height="49">
2567 <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" />
2568 </mask>
2569 <g mask="url(#H)">
2570 <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" />
2571 </g>
2572 <mask id="I" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="297" y="281" width="23" height="49">
2573 <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" />
2574 </mask>
2575 <g mask="url(#I)">
2576 <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" />
2577 </g>
2578 <mask id="J" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="300" y="281" width="22" height="49">
2579 <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" />
2580 </mask>
2581 <g mask="url(#J)">
2582 <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" />
2583 </g>
2584 <mask id="K" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="37" y="31" width="272" height="201">
2585 <path d="m37.887 148.035 139.064 83.741 131.13-78.792V31H48.806l-10.92 117.035z" fill="#fff" />
2586 </mask>
2587 <g mask="url(#K)">
2588 <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" />
2589 <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" />
2590 </g>
2591 <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" />
2592 <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" />
2593 <mask id="L" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="250" y="201" width="7" height="8">
2594 <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" />
2595 </mask>
2596 <g mask="url(#L)">
2597 <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" />
2598 </g>
2599 <mask id="M" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="247" y="209" width="13" height="6">
2600 <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" />
2601 </mask>
2602 <g mask="url(#M)">
2603 <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" />
2604 </g>
2605 <mask id="N" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="268" y="209" width="61" height="4">
2606 <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" />
2607 </mask>
2608 <g mask="url(#N)">
2609 <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" />
2610 </g>
2611 <mask id="O" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="268" y="215" width="34" height="4">
2612 <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" />
2613 </mask>
2614 <g mask="url(#O)">
2615 <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" />
2616 </g>
2617 <mask id="P" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="303" y="215" width="26" height="4">
2618 <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" />
2619 </mask>
2620 <g mask="url(#P)">
2621 <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" />
2622 </g>
2623 <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" />
2624 <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" />
2625 <path fill="#1FDE91" d="M85.95 86.529h1.876v.938H85.95zM90.64 82.778h.938v.938h-.938z" />
2626 <rect x="81.26" y="151.468" width="182.938" height="14.146" rx="1.286" fill="#fff" />
2627 <rect x="86.26" y="157.058" width="33" height="2" rx="1" fill="#8B8D8F" />
2628 <rect x="80.869" y="151.077" width="183.72" height="14.928" rx="1.677" stroke="#242628" stroke-opacity=".5" stroke-width=".782" />
2629 <rect x="81.26" y="127.644" width="182.938" height="14.146" rx="1.286" fill="#fff" />
2630 <rect x="86.26" y="134.058" width="33" height="2" rx="1" fill="#8B8D8F" />
2631 <rect x="80.869" y="127.253" width="183.72" height="14.928" rx="1.677" stroke="#242628" stroke-opacity=".5" stroke-width=".782" />
2632 <rect x="81.26" y="103.726" width="182.938" height="14.146" rx="1.286" fill="#fff" />
2633 <rect x="86.26" y="110.058" width="33" height="2" rx="1" fill="#8B8D8F" />
2634 <rect x="80.869" y="103.335" width="183.72" height="14.928" rx="1.677" stroke="#242628" stroke-opacity=".5" stroke-width=".782" />
2635 <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" />
2636 <path fill="#1FDE91" d="M158.26 148.058h2.736v54.717h-2.736z" />
2637 <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" />
2638 <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" />
2639 <defs>
2640 <linearGradient id="g" x1="279.23" y1="322.708" x2="271.012" y2="285.385" gradientUnits="userSpaceOnUse">
2641 <stop stop-color="#FF928E" />
2642 <stop offset="1" stop-color="#FEB3B1" />
2643 </linearGradient>
2644 <linearGradient id="j" x1="292.659" y1="233.667" x2="273.411" y2="280.645" gradientUnits="userSpaceOnUse">
2645 <stop stop-color="#FF928E" />
2646 <stop offset="1" stop-color="#FEB3B1" />
2647 </linearGradient>
2648 <linearGradient id="o" x1="285.033" y1="237.355" x2="293.362" y2="257.803" gradientUnits="userSpaceOnUse">
2649 <stop stop-color="#FF928E" />
2650 <stop offset="1" stop-color="#FEB3B1" />
2651 </linearGradient>
2652 <linearGradient id="q" x1="-716.313" y1="340.056" x2="-703.14" y2="338.684" gradientUnits="userSpaceOnUse">
2653 <stop stop-color="#FF928E" />
2654 <stop offset="1" stop-color="#FEB3B1" />
2655 </linearGradient>
2656 <linearGradient id="t" x1="-715.046" y1="342.488" x2="-701.896" y2="341.119" gradientUnits="userSpaceOnUse">
2657 <stop stop-color="#FF928E" />
2658 <stop offset="1" stop-color="#FEB3B1" />
2659 </linearGradient>
2660 <linearGradient id="v" x1="352.794" y1="272.43" x2="310.594" y2="268.666" gradientUnits="userSpaceOnUse">
2661 <stop stop-color="#FF928E" />
2662 <stop offset="1" stop-color="#FEB3B1" />
2663 </linearGradient>
2664 <linearGradient id="y" x1="222.273" y1="156.367" x2="354.285" y2="289.941" gradientUnits="userSpaceOnUse">
2665 <stop stop-color="#B05B34" />
2666 <stop offset="1" stop-color="#3225BE" />
2667 </linearGradient>
2668 </defs>
2669 </svg>
2670 </div>
2671 </div>
2672 <?php
2673 }
2674 }
2675 }
2676
2677 /**
2678 * Page footer used on all admin pages.
2679 *
2680 * @access public
2681 * @return void
2682 * @since 1.0.0
2683 */
2684 public function admin_page_footer() {
2685 $current_page = get_current_screen()->id;
2686
2687 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 ) {
2688 ?>
2689 <footer class="wpzoom-new-admin_settings-footer">
2690 <div class="wpzoom-new-admin_settings-footer-wrap">
2691 <h3 class="wpzoom-new-admin_settings-footer-logo">
2692 <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' ); ?>">
2693 <?php _e( 'WPZOOM', 'wpzoom-forms' ); ?>
2694 </a>
2695 </h3>
2696
2697 <ul class="wpzoom-new-admin_settings-footer-links">
2698 <li class="wpzoom-new-admin_settings-footer-links-themes">
2699 <a href="https://www.wpzoom.com/themes/<?php echo $this->utm_source; ?>" target="_blank" title="<?php _e( 'Check out our themes', 'wpzoom-forms' ); ?>">
2700 <?php _e( 'Our Themes', 'wpzoom-forms' ); ?>
2701 </a>
2702 </li>
2703
2704 <li class="wpzoom-new-admin_settings-footer-links-themes">
2705 <a href="https://www.wpzoom.com/plugins/<?php echo $this->utm_source; ?>" target="_blank" title="<?php _e( 'Check out our plugins', 'wpzoom-forms' ); ?>">
2706 <?php _e( 'Our Plugins', 'wpzoom-forms' ); ?>
2707 </a>
2708 </li>
2709
2710 <li class="wpzoom-new-admin_settings-footer-links-blog">
2711 <a href="https://www.wpzoom.com/blog/" target="_blank" title="<?php _e( 'See the latest updates on our blog', 'wpzoom-forms' ); ?>">
2712 <?php _e( 'Blog', 'wpzoom-forms' ); ?>
2713 </a>
2714 </li>
2715
2716 <li class="wpzoom-new-admin_settings-footer-links-themes">
2717 <a href="https://www.wpzoom.com/documentation/wpzoom-forms/" target="_blank" title="<?php _e( 'Documentation', 'wpzoom-forms' ); ?>">
2718 <?php _e( 'Documentation', 'wpzoom-forms' ); ?>
2719 </a>
2720 </li>
2721
2722 <li class="wpzoom-new-admin_settings-footer-links-support">
2723 <a href="https://www.wpzoom.com/support/" target="_blank" title="<?php _e( 'Get support', 'wpzoom-forms' ); ?>">
2724 <?php _e( 'Support', 'wpzoom-forms' ); ?>
2725 </a>
2726 </li>
2727 </ul>
2728 </div>
2729 </footer>
2730 <?php
2731 }
2732 }
2733
2734 public function admin_body_class_filter( $classes ) {
2735 $current_page = get_current_screen()->id;
2736
2737 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 ) {
2738 $classes .= ' wpzoom-new-admin';
2739 }
2740
2741 $forms_count = wp_count_posts( 'wpzf-form' );
2742
2743 if ( 'edit-wpzf-form' == $current_page && $forms_count->publish < 1 && $forms_count->draft < 1 && $forms_count->trash < 1 ) {
2744 $classes .= ' wpzf-no-forms';
2745 }
2746
2747 return $classes;
2748 }
2749
2750 /**
2751 * Returns whether the given input is considered spam by checking it with Akismet.
2752 *
2753 * @since 1.0.4
2754 * @access public
2755 * @param array $input The input to check for spam.
2756 * @return bool Whether it is spam.
2757 */
2758 public function not_spam( $input ) {
2759
2760 // Check if Akismet class exists and has required methods
2761 if( class_exists( 'Akismet' ) && is_callable( array( 'Akismet', 'get_api_key' ) ) && is_callable( array( 'Akismet', 'http_post' ) ) ) {
2762
2763 $request = array(
2764 'comment_type' => 'contact-form',
2765 'comment_author' => isset( $input['name'] ) ? $input['name'] : '',
2766 'comment_author_email' => isset( $input['from'] ) ? $input['from'] : '',
2767 'comment_author_url' => isset( $input['url'] ) ? $input['url'] : '',
2768 'comment_content' => isset( $input['message'] ) ? $input['message'] : '',
2769 'blog' => get_option( 'home' ),
2770 'user_ip' => $this->get_remote_address(),
2771 // phpcs:disable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2772 'user_agent' => isset( $_SERVER['HTTP_USER_AGENT'] ) ? wp_unslash( $_SERVER['HTTP_USER_AGENT'] ) : null,
2773 'referrer' => wp_get_referer() ? wp_get_referer() : null,
2774 // phpcs:enable WordPress.Security.ValidatedSanitizedInput.InputNotSanitized
2775 'permalink' => get_permalink(),
2776 'blog_lang' => get_locale(),
2777 'blog_charset' => get_bloginfo( 'charset' ),
2778 'user_role' => Akismet::get_user_roles( get_current_user_id() ),
2779 'is_test' => false,
2780
2781 );
2782
2783 $response = Akismet::http_post( build_query( $request ), 'comment-check' );
2784
2785 if( ! empty( $response ) && isset( $response[1] ) && 'true' === trim( $response[1] ) ) {
2786 return false;
2787 }
2788
2789 }
2790
2791 return true;
2792 }
2793
2794 /**
2795 * Returns the remote address of the connected peer.
2796 *
2797 * @since 1.0.4
2798 * @access public
2799 * @return string The remote address, or empty string on failure.
2800 */
2801 public function get_remote_address() {
2802 $server_variable_keys = array(
2803 'HTTP_CLIENT_IP',
2804 'HTTP_X_FORWARDED_FOR',
2805 'HTTP_X_FORWARDED',
2806 'HTTP_X_CLUSTER_CLIENT_IP',
2807 'HTTP_FORWARDED_FOR',
2808 'HTTP_FORWARDED',
2809 'REMOTE_ADDR'
2810 );
2811
2812 foreach ( $server_variable_keys as $key ) {
2813 if ( array_key_exists( $key, $_SERVER ) === true ) {
2814 foreach ( array_map( 'trim', explode( ',', $_SERVER[ $key ] ) ) as $ip ) {
2815 if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) !== false ) {
2816 return $ip;
2817 }
2818 }
2819 }
2820 }
2821
2822 return '';
2823 }
2824
2825 /**
2826 * Callback that is triggered when a form is submitted on the frontend.
2827 *
2828 * @access public
2829 * @return void
2830 * @since 1.0.0
2831 */
2832 public function action_form_post() {
2833 $success = false;
2834 $url = isset( $_POST['_wp_http_referer'] ) ? sanitize_text_field( wp_unslash( $_POST['_wp_http_referer'] ) ) : home_url();
2835 $form_id = -1;
2836 $captcha_check_passed = true;
2837
2838 if ( isset( $_POST['_wpnonce'] ) && wp_verify_nonce( $_POST['_wpnonce'], 'wpzf_submit' ) ) {
2839 $form_id = isset( $_POST['form_id'] ) ? intval( $_POST['form_id'] ) : -1;
2840 $blocks = parse_blocks( $form_id > -1 ? get_post_field( 'post_content', $form_id, 'raw' ) : '' );
2841
2842 // Check if spam protection is fully configured and validate only then.
2843 $captcha_config = $this->get_spam_protection_config();
2844 if ( 'recaptcha' === $captcha_config['active_service'] ) {
2845 $captcha_check_passed = false;
2846 $captcha = false;
2847 $recaptchaType = $captcha_config['type'];
2848
2849 if ( isset( $_POST['g-recaptcha-response'] ) ) {
2850 $captcha = trim( sanitize_text_field( $_POST['g-recaptcha-response'] ) );
2851 }
2852
2853 if( 'v3' == $recaptchaType && isset( $_POST['recaptcha_token'] ) ) {
2854 $captcha = trim( sanitize_text_field( $_POST['recaptcha_token'] ) );
2855 }
2856
2857
2858 if ( ! empty( $captcha ) ) {
2859 $secret = false;
2860 if ( 'v3' === $recaptchaType && ! empty( $captcha_config['recaptcha_v3_secret_key'] ) ) {
2861 $secret = trim( $captcha_config['recaptcha_v3_secret_key'] );
2862 } else {
2863 $secret = trim( $captcha_config['recaptcha_v2_secret_key'] );
2864 }
2865
2866 if ( ! empty( $secret ) ) {
2867 $response = file_get_contents(
2868 sprintf(
2869 'https://www.google.com/recaptcha/api/siteverify?secret=%1$s&response=%2$s&remoteip=%3$s',
2870 $secret,
2871 $captcha,
2872 $_SERVER['REMOTE_ADDR']
2873 )
2874 );
2875
2876 if ( false !== $response && ! empty( $response ) ) {
2877 $json = json_decode( $response );
2878
2879 if( 'v3' == $recaptchaType ) {
2880 if ( null !== $json && is_object( $json ) && true === $json->success && $json->score >= 0.5 ) {
2881 $captcha_check_passed = true;
2882 }
2883 }
2884 else {
2885 if ( null !== $json && is_object( $json ) && true === $json->success ) {
2886 $captcha_check_passed = true;
2887 }
2888 }
2889
2890 }
2891 }
2892 }
2893 } elseif ( 'turnstile' === $captcha_config['active_service'] ) {
2894 $captcha_check_passed = false;
2895
2896 $captcha = isset( $_POST['cf-turnstile-response'] ) ? sanitize_text_field( wp_unslash( $_POST['cf-turnstile-response'] ) ) : '';
2897 if ( ! empty( $captcha ) ) {
2898 $secret = trim( $captcha_config['turnstile_secret_key'] );
2899 $ip = $_SERVER['REMOTE_ADDR'];
2900
2901 $url_path = 'https://challenges.cloudflare.com/turnstile/v0/siteverify';
2902 $data = array(
2903 'secret' => $secret,
2904 'response' => $captcha,
2905 'remoteip' => $ip
2906 );
2907
2908 $options = array(
2909 'http' => array(
2910 'method' => 'POST',
2911 'content' => http_build_query($data)
2912 )
2913 );
2914 $stream = stream_context_create($options);
2915 $result = file_get_contents($url_path, false, $stream);
2916 $result = json_decode($result, true);
2917
2918 if ( intval( $result['success'] ) !== 1 ) {
2919 $captcha_check_passed = false;
2920 } else {
2921 $captcha_check_passed = true;
2922 }
2923 }
2924 }
2925
2926 if ( $captcha_check_passed && count( $blocks ) > 0 ) {
2927 $clean_site_name = sanitize_text_field( get_bloginfo( 'name' ) );
2928 $input_blocks = $this->get_input_blocks( $blocks );
2929 $form_method = get_post_meta( $form_id, '_form_method', true ) ?: 'email';
2930 $form_email = get_post_meta( $form_id, '_form_email', true );
2931 $form_subject = get_post_meta( $form_id, '_form_subject', true );
2932 $fallback_email = trim( get_option( 'admin_email' ) );
2933 $sendto = sanitize_email( false !== $form_email && ! empty( $form_email ) && filter_var( $form_email, FILTER_VALIDATE_EMAIL ) ? $form_email : $fallback_email );
2934
2935 if ( 'email' == $form_method || 'combined' == $form_method ) {
2936 $email_body = '<html>
2937 <head>
2938 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
2939 <meta http-equiv="X-UA-Compatible" content="IE=edge">
2940 <meta name="viewport" content="width=device-width">
2941
2942 <style type="text/css">
2943 body {
2944 -ms-text-size-adjust: 100%;height: 100%; line-height: 1.6;
2945 font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;
2946 }
2947 a { color: #4477bd; }
2948 a:hover {
2949 color: #e2911a !important;
2950 }
2951 a:active {
2952 color: #0d3d62 !important;
2953 }
2954 p{
2955 margin:10px 0;
2956 padding:0;
2957 font-family: -apple-system, BlinkMacSystemFont, avenir next, avenir, segoe ui, helvetica neue, helvetica, Cantarell, Ubuntu, roboto, noto, arial, sans-serif;
2958 }
2959 table{
2960 border-collapse:collapse;
2961 }
2962 h1,h2,h3,h4,h5,h6{
2963 display:block;
2964 margin:0;
2965 padding:0;
2966 }
2967 img,a img{
2968 border:0;
2969 height:auto;
2970 outline:none;
2971 text-decoration:none;
2972 }
2973 body,#bodyTable,#bodyCell{
2974 height:100%;
2975 margin:0;
2976 padding:0;
2977 }
2978 #outlook a{
2979 padding:0;
2980 }
2981 img{
2982 -ms-interpolation-mode:bicubic;
2983 }
2984 table{
2985 mso-table-lspace:0pt;
2986 mso-table-rspace:0pt;
2987 }
2988 p,a,li,td,blockquote{
2989 mso-line-height-rule:exactly;
2990 }
2991 a[href^=tel],a[href^=sms]{
2992 color:inherit;
2993 cursor:default;
2994 text-decoration:none;
2995 }
2996 p,a,li,td,body,table,blockquote{
2997 -ms-text-size-adjust:100%;
2998 -webkit-text-size-adjust:100%;
2999 }
3000 a[x-apple-data-detectors]{
3001 color:inherit !important;
3002 text-decoration:none !important;
3003 font-size:inherit !important;
3004 font-family:inherit !important;
3005 font-weight:inherit !important;
3006 line-height:inherit !important;
3007 }
3008 @media only screen and (max-width: 480px){
3009 body,table,td,p,a,li,blockquote{
3010 -webkit-text-size-adjust:none !important;
3011 }
3012 }
3013 @media only screen and (max-width: 480px){
3014 body{
3015 width:100% !important;
3016 min-width:100% !important;
3017 }
3018 }
3019 </style>
3020 </head>
3021 <body style="height: 100%;margin: 0;padding: 0;width: 100%;-ms-text-size-adjust: 100%;-webkit-text-size-adjust: 100%;">
3022 <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">
3023 <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">
3024 <tbody>
3025 <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">
3026 <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">';
3027 $replyto = '';
3028 $sbj = '';
3029 $raw_content = array(
3030 '_wpzf_fields' => array()
3031 );
3032
3033 foreach ( $_REQUEST as $key => $value ) {
3034 if ( strpos( $key, 'wpzf_' ) === 0 ) {
3035 $id = substr( $key, 5 );
3036 $name = isset( $input_blocks[ $id ] ) ? $input_blocks[ $id ] : __( 'Unnamed Input', 'wpzoom-forms' );
3037 $value = is_array( $value ) ? implode( ', ', $value ) : $value;
3038
3039 if ( 'wpzf_replyto' == $key ) {
3040 $replyto = sanitize_text_field( $value );
3041 continue;
3042 } elseif ( 'wpzf_subject' == $key ) {
3043 $sbj = sanitize_text_field( $value );
3044 continue;
3045 }
3046
3047 $email_body .= '<strong>' . wp_kses_post( wp_unslash( $name ) ) . ':</strong><br/>' . nl2br( wp_kses_post( wp_unslash( $value ) ) ) . '<br/><br/>';
3048 $raw_content['_wpzf_fields'][ $name ] = sanitize_textarea_field( $value );
3049 }
3050 }
3051
3052 $fromaddr = ! empty( $replyto ) && isset( $_REQUEST[ $replyto ] ) ? sanitize_email( $_REQUEST[ $replyto ] ) : $sendto;
3053 $cleanname = sanitize_text_field( get_bloginfo( 'name' ) );
3054 $subjectline = ! empty( $sbj ) && isset( $_REQUEST[ $sbj ] ) ? sanitize_text_field( wp_unslash( $_REQUEST[ $sbj ] ) ) : $form_subject;
3055 $subjectline .= sprintf( __( ' - %s', 'wpzoom-forms' ), $cleanname );
3056
3057 $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>';
3058
3059 $headers = sprintf(
3060 "Content-Type: text/html; charset=UTF-8\r\nFrom: %s <%s>\r\nReply-To: %s",
3061 $cleanname,
3062 $fromaddr,
3063 $fromaddr
3064 );
3065
3066 $email_body .= '</td>
3067 </tr>
3068 </tbody></table>
3069 </div>
3070
3071 <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">
3072 <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">
3073 <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">
3074 <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">
3075 <p>Sent from <a href="' . esc_attr( get_bloginfo( 'url' ) ) . '">' . $cleanname . '</a> using the <strong>WPZOOM Forms</strong> plugin.</p>
3076 <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">
3077 </td>
3078 </tr>
3079 </tbody></table>
3080 </div>
3081 </body>
3082 </html>';
3083
3084 $details = array(
3085 'from' => $fromaddr,
3086 'message' => $raw_content
3087 );
3088
3089 if ( $this->not_spam( $details ) ) {
3090 $success = wp_mail( $sendto, $subjectline, $email_body, $headers );
3091 }
3092 }
3093 if ( 'db' == $form_method || 'combined' == $form_method ) {
3094 $content = array(
3095 '_wpzf_form_id' => $form_id,
3096 '_wpzf_fields' => array()
3097 );
3098
3099 $replyto = '';
3100 $sbj = '';
3101
3102 foreach ( $_REQUEST as $key => $value ) {
3103 if ( strpos( $key, 'wpzf_' ) === 0 ) {
3104 $id = substr( $key, 5 );
3105 $name = isset( $input_blocks[ $id ] ) ? $input_blocks[ $id ] : __( 'Unnamed Input', 'wpzoom-forms' );
3106 $value = is_array( $value ) ? implode( ', ', $value ) : $value;
3107
3108 if ( 'wpzf_replyto' == $key || 'wpzf_subject' == $key ) {
3109 if ( 'wpzf_replyto' == $key ) {
3110 $replyto = sanitize_text_field( $value );
3111 } elseif ( 'wpzf_subject' == $key ) {
3112 $sbj = sanitize_text_field( $value );
3113 }
3114
3115 continue;
3116 }
3117
3118 $content['_wpzf_fields'][ $name ] = sanitize_textarea_field( $value );
3119 }
3120 }
3121
3122 $details = array(
3123 'from' => '',
3124 'message' => $content
3125 );
3126
3127 if ( ! empty( $replyto ) ) {
3128 $fromaddr = isset( $_REQUEST[ $replyto ] ) ? sanitize_email( $_REQUEST[ $replyto ] ) : $sendto;
3129 $details['from'] = $fromaddr;
3130 }
3131
3132 if ( $this->not_spam( $details ) ) {
3133 $success = false !== $content && 0 < wp_insert_post( array(
3134 'post_type' => 'wpzf-submission',
3135 'post_status' => 'publish',
3136 'comment_status' => 'closed',
3137 'ping_status' => 'closed',
3138 'post_title' => __( 'Submission', 'wpzoom-forms' ),
3139 'post_author' => 1,
3140 'post_category' => array( 1 ),
3141 'post_content' => __( 'Submission', 'wpzoom-forms' ),
3142 'meta_input' => $content
3143 ) );
3144 }
3145 }
3146 }
3147 }
3148
3149 $redirect_url = urldecode(
3150 add_query_arg(
3151 array(
3152 'success' => ( $success ? '1' : '0' ),
3153 'wpzf_submitted_form' => $form_id,
3154 ),
3155 $url
3156 )
3157 );
3158
3159 wp_safe_redirect(
3160 $redirect_url . ( $form_id > -1 ? '#wpzf-' . $form_id . '-notice' : '' )
3161 );
3162
3163 exit;
3164 }
3165
3166 /**
3167 * Filters a hierarchical array of Gutenberg blocks to return just the blocks added by this plugin.
3168 *
3169 * @access public
3170 * @param array $blocks A hierarchical array of Gutenberg blocks.
3171 * @return array
3172 * @since 1.0.0
3173 */
3174 public function get_input_blocks( $blocks ) {
3175 $found = array();
3176
3177 if ( is_array( $blocks ) && count( $blocks ) > 0 ) {
3178 for ( $i = 0; $i < count( $blocks ); $i++ ) {
3179 $block = $blocks[ $i ];
3180
3181 if ( isset( $block['blockName'] ) &&
3182 preg_match( '/^wpzoom\-forms\//i', $block['blockName'] ) &&
3183 ! preg_match( '/(label|submit)\-field$/i', $block['blockName'] ) &&
3184 isset( $block['attrs'] ) ) {
3185 $attrs = $block['attrs'];
3186
3187 if ( array_key_exists( 'id', $attrs ) && array_key_exists( 'name', $attrs ) ) {
3188 $found[ $attrs['id'] ] = $attrs['name'];
3189 }
3190 }
3191
3192 if ( isset( $block['innerBlocks'] ) ) {
3193 $found = array_merge( $found, $this->get_input_blocks( $block['innerBlocks'] ) );
3194 }
3195 }
3196 }
3197
3198 return $found;
3199 }
3200
3201 /**
3202 * Returns the output for the form shortcode.
3203 *
3204 * @access public
3205 * @param array|string $atts Shortcode attributes array or empty string.
3206 * @param string $content The shortcode content, or null if not set.
3207 * @param string $tag The shortcode name.
3208 * @return string The shortcode output.
3209 * @since 1.0.0
3210 */
3211 public function shortcode_output( $atts, $content, $tag ) {
3212 $id = is_array( $atts ) && array_key_exists( 'id', $atts ) ? intval( $atts['id'] ) : -1;
3213 $output = '';
3214
3215 if ( $id > 0 ) {
3216 wp_enqueue_script( 'wpzoom-forms-js-frontend-formblock' );
3217 wp_enqueue_style( 'wpzoom-forms-css-frontend-formblock' );
3218
3219 $output = $this->form_block_render( array( 'formId' => $id ) );
3220 }
3221
3222 return $output;
3223 }
3224
3225 /**
3226 * Register the PRO upsell notice with WPZOOM Notice Center.
3227 *
3228 * @since 1.3.4
3229 * @param array $notices Existing notices from the filter.
3230 * @return array Notices with upsell notice added when applicable.
3231 */
3232 public function register_upsell_notice( $notices ) {
3233 if ( ! is_array( $notices ) ) {
3234 $notices = array();
3235 }
3236
3237 // Don't add if PRO version is active.
3238 if ( defined( 'WPZOOM_FORMS_PRO_VERSION' ) ) {
3239 return $notices;
3240 }
3241
3242 $upsell_url = admin_url( 'edit.php?post_type=wpzf-form&page=wpzf-pro-page' );
3243 $pro_url = 'https://www.wpzoom.com/plugins/wpzoom-forms/?utm_source=wpadmin&utm_medium=wpzoom-forms-free&utm_campaign=upsell-notice';
3244
3245 $content = '<p>' . esc_html__( 'Unlock powerful features to build smarter, more flexible forms:', 'wpzoom-forms' ) . '</p>';
3246 $content .= '<ul>';
3247 $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>';
3248 $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>';
3249 $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>';
3250 $content .= '<li><strong>' . esc_html__( 'Mailchimp Integration', 'wpzoom-forms' ) . '</strong> &mdash; ' . esc_html__( 'Automatically add subscribers to your Mailchimp audiences.', 'wpzoom-forms' ) . '</li>';
3251 $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>';
3252 $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>';
3253 $content .= '</ul>';
3254
3255 $notices[] = array(
3256 'id' => 'wpzf_upsell_pro',
3257 'priority' => 10,
3258 'heading' => __( 'Upgrade to WPZOOM Forms PRO', 'wpzoom-forms' ),
3259 'content' => $content,
3260 'icon' => array(
3261 'type' => 'image',
3262 'src' => plugins_url( 'dist/assets/admin/images/icon-forms.svg', __FILE__ ),
3263 ),
3264 'primary_button' => array(
3265 'label' => __( 'Upgrade to PRO', 'wpzoom-forms' ),
3266 'url' => $pro_url,
3267 'new_tab' => true,
3268 ),
3269 'secondary_button' => array(
3270 'label' => __( 'Learn More', 'wpzoom-forms' ),
3271 'url' => $upsell_url,
3272 'new_tab' => false,
3273 ),
3274 'capability' => 'manage_options',
3275 'screens' => array( 'dashboard', 'edit-wpzf-form', 'wpzf-form', 'edit-wpzf-submission', 'wpzf-submission' ),
3276 'source' => 'WPZOOM Forms',
3277 'conditions' => function () {
3278 return ! get_option( 'wpzf_upsell_notice_dismissed' );
3279 },
3280 );
3281
3282 return $notices;
3283 }
3284 }
3285
3286 if( ! function_exists ( 'wpzoom_forms_load_files' ) ) {
3287 function wpzoom_forms_load_files() {
3288
3289 //Load Settings Panel
3290 require_once 'classes/class-wpzoom-forms-settings-fields.php';
3291 require_once 'classes/class-wpzoom-forms-settings-page.php';
3292 require_once 'classes/class-wpzoom-forms-template-manager.php';
3293 require_once 'classes/class-wpzoom-forms-settings-upsell.php';
3294
3295 }
3296 add_action( 'plugin_loaded', 'wpzoom_forms_load_files' );
3297 }
3298
3299 if ( ! function_exists( 'wpzoom_forms_plugin_action_links' ) ) {
3300 /**
3301 * Plugin action links.
3302 *
3303 * Adds action links to the plugin list table.
3304 *
3305 * Fired by `plugin_action_links` filter.
3306 *
3307 * @since 1.3.5
3308 *
3309 * @param array $links An array of plugin action links.
3310 *
3311 * @return array An array of plugin action links.
3312 */
3313 function wpzoom_forms_plugin_action_links( $links ) {
3314 $settings_link = sprintf(
3315 '<a href="%1$s">%2$s</a>',
3316 admin_url( 'edit.php?post_type=wpzf-form&page=' . WPZOOM_FORMS_SETTINGS_PAGE ),
3317 esc_html__( 'Settings', 'wpzoom-forms' )
3318 );
3319
3320 array_unshift( $links, $settings_link );
3321
3322 $links['go_pro'] = sprintf(
3323 '<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>',
3324 'https://www.wpzoom.com/plugins/wpzoom-forms/?utm_source=wpadmin&utm_medium=plugin&utm_campaign=wpzoom-forms-free&utm_content=plugins-page',
3325 esc_html__( 'UPGRADE', 'wpzoom-forms' ),
3326 esc_html__( 'PRO', 'wpzoom-forms' )
3327 );
3328
3329 return $links;
3330 }
3331 add_filter( 'plugin_action_links_' . WPZOOM_FORMS_PLUGIN_BASE, 'wpzoom_forms_plugin_action_links' );
3332 }
3333
3334 /**
3335 * Check if the Elementor Page Builder is enabled load the widget
3336 */
3337 if ( defined( 'ELEMENTOR_VERSION' ) && is_callable( 'Elementor\Plugin::instance' ) ) {
3338 require_once 'elementor/wpzoom-forms-elementor.php';
3339 }
3340
3341 /* -------------------------------------------------------------------------- */
3342 /* WPZOOM Forms v2 — schema-driven builder, REST API, renderer. */
3343 /* Coexists with the legacy block system: schema is the source of truth, the */
3344 /* legacy renderer remains a fallback during/after migration. */
3345 /* -------------------------------------------------------------------------- */
3346 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-schema.php';
3347 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-migration.php';
3348 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-templates.php';
3349 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-option-lists.php';
3350 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-rest.php';
3351 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-renderer.php';
3352 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-submission-handler.php';
3353 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-builder-page.php';
3354 require_once WPZOOM_FORMS_PATH . 'classes/class-wpzoom-forms-submission-view.php';
3355
3356 /**
3357 * Shared embed helper used by the shortcode and the Elementor widget.
3358 *
3359 * Routes to the v2 renderer for forms saved through the new builder
3360 * (_wpzf_schema postmeta present) and to the legacy block renderer for
3361 * old block-based forms that have no schema yet.
3362 *
3363 * @param int $id Form post ID.
3364 * @return string Rendered HTML.
3365 */
3366 function wpzoom_forms_render_embed( $id ) {
3367 global $wpzoom_forms;
3368 $id = (int) $id;
3369 if ( $id < 1 ) {
3370 return '';
3371 }
3372
3373 if ( get_post_meta( $id, WPZOOM_Forms_Schema::META_KEY, true ) ) {
3374 return WPZOOM_Forms_Renderer::render( $id );
3375 }
3376
3377 // Legacy form — fall back to the block-based renderer.
3378 if ( $wpzoom_forms ) {
3379 wp_enqueue_script( 'wpzoom-forms-js-frontend-formblock' );
3380 wp_enqueue_style( 'wpzoom-forms-css-frontend-formblock' );
3381 return $wpzoom_forms->form_block_render( array( 'formId' => $id ) );
3382 }
3383
3384 return '';
3385 }
3386
3387 add_action( 'init', function() {
3388 global $wpzoom_forms;
3389
3390 // Take over form submission from the legacy handler.
3391 if ( $wpzoom_forms ) {
3392 remove_action( 'admin_post_wpzf_submit', array( $wpzoom_forms, 'action_form_post' ), 10 );
3393 remove_action( 'admin_post_nopriv_wpzf_submit', array( $wpzoom_forms, 'action_form_post' ), 10 );
3394 // The legacy class whitelist-resets $wp_meta_boxes to keep ONLY its own boxes —
3395 // drop that hook so our submission-detail boxes survive.
3396 remove_action( 'in_admin_header', array( $wpzoom_forms, 'remove_meta_boxes' ), 100 );
3397 // The legacy meta-box registration adds boxes we replace with our own.
3398 remove_action( 'add_meta_boxes_wpzf-submission', array( $wpzoom_forms, 'add_meta_boxes' ), 10 );
3399 }
3400
3401 // Disable the legacy template-picker modal — our React builder owns templates now.
3402 if ( class_exists( 'WPZOOM_Forms_Template_Manager' ) ) {
3403 $legacy_tm = WPZOOM_Forms_Template_Manager::instance();
3404 remove_action( 'admin_enqueue_scripts', array( $legacy_tm, 'scripts' ) );
3405 remove_action( 'admin_footer', array( $legacy_tm, 'modal_window' ) );
3406 remove_filter( 'default_content', array( $legacy_tm, 'default_form_content' ), 10 );
3407 }
3408
3409 // Register meta so REST/admin can expose it.
3410 register_post_meta( 'wpzf-form', WPZOOM_Forms_Schema::META_KEY, array(
3411 'type' => 'string',
3412 'single' => true,
3413 'show_in_rest' => true,
3414 'auth_callback' => function() { return current_user_can( 'edit_posts' ); },
3415 ) );
3416
3417 ( new WPZOOM_Forms_REST() )->register();
3418 ( new WPZOOM_Forms_Submission_Handler() )->register();
3419 ( new WPZOOM_Forms_Builder_Page() )->register();
3420 ( new WPZOOM_Forms_Submission_View() )->register();
3421 }, 11 ); // After main class init (priority 9).
3422
3423 /**
3424 * Override the legacy shortcode to route through the shared embed helper,
3425 * which selects v2 or legacy renderer based on whether _wpzf_schema exists.
3426 */
3427 add_action( 'init', function() {
3428 remove_shortcode( 'wpzf_form' );
3429 add_shortcode( 'wpzf_form', function( $atts ) {
3430 $atts = shortcode_atts( array( 'id' => 0 ), $atts, 'wpzf_form' );
3431 return wpzoom_forms_render_embed( (int) $atts['id'] );
3432 } );
3433 }, 12 );
3434
3435 /**
3436 * For the form-block (Gutenberg block that embeds a form by id), substitute
3437 * the render callback to use the new renderer. The block remains registered
3438 * so existing content keeps working. We hook `render_block` (rather than
3439 * patching the block type at registration time) because the main class
3440 * registers the block at init:9, before our filters get a chance to attach.
3441 */
3442 add_filter( 'render_block', function( $block_content, $block ) {
3443 if ( ! isset( $block['blockName'] ) || $block['blockName'] !== 'wpzoom-forms/form-block' ) return $block_content;
3444 if ( is_admin() ) return $block_content;
3445 $id = isset( $block['attrs']['formId'] ) ? (int) $block['attrs']['formId'] : 0;
3446 if ( $id < 1 ) return $block_content;
3447
3448 // Only switch to the v2 renderer when the form has been saved through the new
3449 // builder (_wpzf_schema exists). Otherwise return the original block output
3450 // unchanged so old forms keep all their existing styling and behaviour.
3451 if ( ! get_post_meta( $id, WPZOOM_Forms_Schema::META_KEY, true ) ) {
3452 return $block_content;
3453 }
3454
3455 // The render_callback (form_block_render) already built a <style> tag containing
3456 // per-form color/border overrides from the block attributes (fieldBgColor, btnBgColor, …).
3457 // Preserve it so those settings actually apply to the new renderer's output.
3458 $block_styles = '';
3459 if ( preg_match( '/(<style\b[^>]*>.*?<\/style>)/is', $block_content, $m ) ) {
3460 $block_styles = $m[1];
3461 }
3462
3463 // Preserve the block's alignment (wide/full) on the rendered wrapper. The
3464 // render callback no longer produces this output, so the align class set in
3465 // the editor must be re-applied here or the frontend ignores it.
3466 $align = isset( $block['attrs']['align'] ) ? $block['attrs']['align'] : '';
3467 $root_classes = ( $align && 'none' !== $align ) ? 'align' . $align : '';
3468
3469 return $block_styles . WPZOOM_Forms_Renderer::render( $id, $root_classes );
3470 }, 9, 2 );
3471
3472
3473 /**
3474 * Customize the "All Forms" admin list a bit:
3475 * - rename "Add New" wording, replace target link with our builder.
3476 */
3477 add_action( 'admin_head-edit.php', function() {
3478 $screen = get_current_screen();
3479 if ( ! $screen || $screen->post_type !== 'wpzf-form' ) return;
3480 ?>
3481 <style>
3482 .wp-heading-inline + .page-title-action { background: #2271b1; color: #fff; border-color: #2271b1; }
3483 .wp-heading-inline + .page-title-action:hover { background: #135e96; }
3484 </style>
3485 <script>
3486 jQuery(function($){
3487 var btn = $('.page-title-action').first();
3488 if (btn.length) {
3489 btn.attr('href', '<?php echo esc_js( admin_url( 'admin.php?page=wpzf-form-builder' ) ); ?>');
3490 btn.text(<?php echo wp_json_encode( __( 'Add New Form', 'wpzoom-forms' ) ); ?>);
3491 }
3492 });
3493 </script>
3494 <?php
3495 } );
3496
3497 /**
3498 * Render the legacy "Upgrade to PRO" sidebar banner on the forms list page.
3499 * The legacy template-manager modal used to ship this; we disabled that modal
3500 * when replacing the form editor, so re-emit the banner here so the upsell
3501 * isn't lost.
3502 */
3503 add_action( 'admin_footer-edit.php', function() {
3504 $screen = get_current_screen();
3505 if ( ! $screen || $screen->post_type !== 'wpzf-form' ) return;
3506 if ( ! class_exists( 'WPZOOM_Forms_Settings' ) ) return;
3507
3508 $settings_class = new WPZOOM_Forms_Settings();
3509 $settings_class->upsell_banner(); // echoes the .wpzoom-forms-settings-upsell-container markup
3510 ?>
3511 <script>
3512 jQuery(function($){
3513 var $banner = $('.wpzoom-forms-settings-upsell-container').last();
3514 if ( ! $banner.length ) return;
3515 // Drop the upsell into the right rail next to the posts table.
3516 var $filter = $('#posts-filter');
3517 if ( $filter.length ) {
3518 $banner.css({ 'float':'right', 'margin-left':'20px', 'max-width':'340px' });
3519 $filter.before( $banner );
3520 }
3521 });
3522 </script>
3523 <?php
3524 } );
3525
3526