PluginProbe
Smart Grid-Layout Design for Contact Form 7 / 4.10
Smart Grid-Layout Design for Contact Form 7 v4.10
4.18.0 4.17.0 3.2.0 3.2.1 3.3.0 3.3.1 3.3.2 3.3.3 3.3.4 3.3.5 3.3.6 3.3.7 3.3.8 4.0.0 4.0.1 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 4.10 4.11 4.12 4.13 4.14 All 119 releases
cf7-grid-layout / public / class-cf7-grid-layout-public.php

class-cf7-grid-layout-public.php in Smart Grid-Layout Design for Contact Form 7 4.10, at public/class-cf7-grid-layout-public.php

1,557 lines 61.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 use voku\helper\HtmlDomParser;
3
4 /**
5 * The public-facing functionality of the plugin.
6 *
7 * @link http://syllogic.in
8 * @since 1.0.0
9 *
10 * @package Cf7_Grid_Layout
11 * @subpackage Cf7_Grid_Layout/public
12 */
13
14 /**
15 * The public-facing functionality of the plugin.
16 *
17 * Defines the plugin name, version, and two examples hooks for how to
18 * enqueue the admin-specific stylesheet and JavaScript.
19 *
20 * @package Cf7_Grid_Layout
21 * @subpackage Cf7_Grid_Layout/public
22 * @author Aurovrata V. <vrata@syllogic.in>
23 */
24 class Cf7_Grid_Layout_Public {
25
26 /**
27 * The ID of this plugin.
28 *
29 * @since 1.0.0
30 * @access private
31 * @var string $plugin_name The ID of this plugin.
32 */
33 private $plugin_name;
34 private $registered = false;
35
36 /**
37 * The version of this plugin.
38 *
39 * @since 1.0.0
40 * @access private
41 * @var string $version The current version of this plugin.
42 */
43 private $version;
44
45 /**
46 * The version of this plugin.
47 *
48 * @since 2.6.0
49 * @access private
50 * @var string $form_id The current form id.
51 */
52 private $form_id='';
53
54 /**
55 * The cf7 array fields.
56 *
57 * @since 1.0.0
58 * @access private
59 * @var Array $array_grid_fields The form fields which were converted to arrays by the plugin.
60 */
61 static private $array_grid_fields = array();
62
63 /**
64 * The cf7 array fields.
65 *
66 * @since 2.5.0
67 * @access private
68 * @var Array $array_toggle_fields The form fields which are within toggle sections.
69 */
70 static private $array_toggle_fields = array();
71
72 /**
73 * The cf7 array fields.
74 *
75 * @since 4.0.0
76 * @access private
77 * @var Array $array_tabbed_toggles The form toggles within tabbed sections.
78 */
79 static private $array_tabbed_toggles = array();
80 /**
81 * The cf7 array fields.
82 *
83 * @since 2.5.0
84 * @access private
85 * @var Array $array_toggled_panels The form toggled sections used.
86 */
87 static private $array_toggled_panels = array();
88 /**
89 * The cf7 array fields.
90 *
91 * @since 2.6.0
92 * @access private
93 * @var Array $localised_data Localised data parameters.
94 */
95 private $localised_data = array();
96 /**
97 * The wp_errors returned by the CF7 v5.4 wpcf7_unship_uploaded_file() function.
98 *
99 * @since 4.10.1
100 * @access private
101 * @var Array $file_wp_errors track file errors.
102 */
103 private $file_wp_errors = array();
104 /**
105 * Initialize the class and set its properties.
106 *
107 * @since 1.0.0
108 * @param string $plugin_name The name of the plugin.
109 * @param string $version The version of this plugin.
110 */
111 public function __construct( $plugin_name, $version ) {
112 $this->plugin_name = $plugin_name;
113 $this->version = $version;
114 }
115 /**
116 *
117 *
118 * @since 1.0.0
119 * @param string $field field name to check
120 * @param string $form_id form id for which to check the
121 * @return string 'singular' for non-grid fields, 'tab' for tabbed fields, 'table' for tablled fields, 'both' for fields in tables in tabs.
122 */
123 static public function field_type($field, $form_id){
124 if(!isset(self::$array_grid_fields[$form_id])){
125 //try to load the fields.
126 self::get_grid_fields($form_id);
127 }
128 $type = 'singular';
129 if(isset(self::$array_grid_fields[$form_id][$field])){
130 $type = self::$array_grid_fields[$form_id][$field][1]; //array(origin_id,type);
131 }
132 return $type;
133 }
134
135 /**
136 * Check if a field is in a toggled section.
137 *
138 *@since 2.5.0
139 *@param string $field field nav_menu_link_attributes.
140 *@return array null or array of panel ids.
141 */
142 protected function get_toggle($field){
143 return isset(self::$array_toggle_fields[$this->form_id][$field]) ? self::$array_toggle_fields[$this->form_id][$field]: null;
144 }
145 /**
146 * Check if a toggled section was used and its fields submitted.
147 *
148 *@since 2.5.0
149 *@param string $toggle toogle id
150 *@return boolean was the toggle section submitted.
151 */
152 protected function is_submitted($toggle){
153 return !empty( array_intersect( array_keys(self::$array_toggled_panels[$this->form_id]), $toggle) );
154 }
155 /**
156 * Check if a toggled section was used and its fields submitted.
157 *
158 *@since 4.0.0
159 *@param string $toggle toogle id
160 *@return boolean was the toggle section inside a tabbed section.
161 */
162 protected function is_tabbed($toggle){
163 /* if the array is not set for this form_id,
164 * then it is because the form is an older version and
165 * should return true for backward compatibility */
166 if(isset(self::$array_tabbed_toggles[$this->form_id])){
167 return isset(self::$array_tabbed_toggles[$this->form_id][$toggle]);
168 }else return true;
169 }
170 /**
171 * Register the stylesheets for the public-facing side of the site.
172 *
173 * @since 1.0.0
174 */
175 public function register_styles() {
176 $airplane=false;
177 if( class_exists( 'Airplane_Mode_Core' ) && Airplane_Mode_Core::getInstance()->enabled()){
178 $airplane=true;
179 }
180 $plugin_dir = plugin_dir_url( __DIR__ );
181 //default style for cf7 grid forms (row buttons and tables mainly).
182 //others
183 // get registered script object for jquery-ui
184 global $wp_scripts;
185 $ui = $wp_scripts->query('jquery-ui-core');
186
187 // tell WordPress to load the Smoothness theme from Google CDN
188 if( !$airplane ){
189 $protocol = is_ssl() ? 'https' : 'http';
190 $url_path = "$protocol://cdnjs.cloudflare.com/ajax/libs/jqueryui/{$ui->ver}/";
191 wp_register_style('cf7-jquery-ui', $url_path . 'themes/smoothness/jquery-ui.min.css', array(), $ui->ver , 'all');
192 wp_register_style( 'cf7-jquery-ui-theme', $url_path . 'jquery-ui.theme.min.css', array(), $ui->ver, 'all');
193 wp_register_style( 'cf7-jquery-ui-structure', $url_path . 'jquery-ui.structure.min.css', array(), $ui->ver, 'all');
194 }
195
196
197 /** @since 3.1,0 improve live loading of resources */
198 $ff = '';
199 $pf='';
200 if(!defined('WP_DEBUG') || !WP_DEBUG){
201 $ff = '.min';
202 $pf = '/min';
203 }
204 wp_register_style( 'cf7-benchmark-css', $plugin_dir . "public/css{$pf}/cf7-benchmark.css", array(), $this->version, 'all' );
205 wp_register_style( $this->plugin_name, $plugin_dir . "public/css{$pf}/cf7-grid-layout-public.css", array(), $this->version, 'all' );
206 wp_register_style( 'smart-grid', $plugin_dir . "assets/css.gs/smart-grid{$ff}.css", array(), $this->version, 'all' );
207 wp_register_style('jquery-toggles-css', $plugin_dir . "assets/jquery-toggles/css/toggles{$ff}.css", array(), $this->version, 'all' );
208 wp_register_style('jquery-toggles-light-css', $plugin_dir . "assets/jquery-toggles/css/themes/toggles-light{$ff}.css", array('jquery-toggles-css'), $this->version, 'all' );
209
210 /** @since 4.2.0 enable Gliderliders for slider sections */
211 wp_register_style('glider-style', $plugin_dir . 'assets/glider-js/glider.min.css', array(), '1.7.4','all');
212
213 //allow custom script registration
214 do_action('smart_grid_register_styles');
215 /** @since 4.10.0 abstract out dynamic lists */
216 do_action('cf7sg_register_dynamic_lists');
217 $lists = cf7sg_get_dynamic_lists();
218 foreach($lists as $l) $l->register_scripts($airplane);
219 }
220
221 /**
222 * Register the JavaScript for the public-facing side of the site.
223 *
224 * @since 1.0.0
225 */
226 public function register_scripts() {
227 $airplane=false;
228 if( class_exists( 'Airplane_Mode_Core' ) && Airplane_Mode_Core::getInstance()->enabled()){
229 $airplane=true;
230 }
231 /** @since 3.1,0 improve live loading of resources */
232 $pf='';
233 if(!defined('WP_DEBUG') || !WP_DEBUG){
234 $pf = '/min';
235 }
236 $plugin_dir = plugin_dir_url( __DIR__ );
237 wp_register_script( $this->plugin_name, $plugin_dir . "public/js{$pf}/cf7-grid-layout-public.js", array( 'jquery','contact-form-7' ), $this->version, true );
238
239 wp_register_script('jquery-toggles', $plugin_dir . 'assets/jquery-toggles/toggles.min.js', array( 'jquery' ), $this->version, true );
240 wp_register_script('js-cf7sg-benchmarking', $plugin_dir . "public/js{$pf}/cf7-benchmark.js", array( 'jquery' ), $this->version, true );
241 /** @since 4.2.0 enable Glider sliders for slider sections */
242 wp_register_script('glider-js', $plugin_dir . 'assets/glider-js/glider.js', null, '1.7.4',true);
243 //allow custom script registration
244 do_action('smart_grid_register_scripts');
245 /** @since 4.10.0 abstract out dynamic lists */
246 do_action('cf7sg_register_dynamic_lists');
247 $lists = cf7sg_get_dynamic_lists();
248 foreach($lists as $l) $l->register_styles($airplane);
249 }
250 /**
251 * Dequeue script 'contact-form-7'
252 * hooked on 'wp_print_scripts', this canbe re-enqeued on specific cf7 shortcode calls
253 * @since 1.0.0
254 **/
255 public function dequeue_cf7_scripts(){
256 wp_dequeue_script('contact-form-7');
257 }
258 /**
259 * Dequeue script 'contact-form-7'
260 * hooked on 'wp_print_style', this canbe re-enqeued on specific cf7 shortcode calls
261 * @since 1.0.0
262 **/
263 public function dequeue_cf7_styles(){
264 wp_dequeue_style('contact-form-7');
265 }
266 /**
267 * Add the cf7 key to the hidden fields so as not to have to load it after each submission.
268 * Hooked to 'wpcf7_form_hidden_fields'
269 * @since 1.0.0
270 * @param Array $hidden hidden fields to add to cf7 form.
271 * @return Array hidden fields to add to cf7 form.
272 **/
273 public function add_hidden_fields($hidden){
274 $form = wpcf7_get_current_contact_form();
275 $post = get_post($form->id());
276 $hidden['_wpcf7_key'] = $post->post_name;
277 $hidden['_cf7sg_toggles'] = '';
278 $hidden['_cf7sg_version'] = $this->version;
279 /** @since 4.4.0 enable rest authentication for logged in users. */
280 $hidden['_wpnonce'] = wp_create_nonce('wp_rest');
281 return $hidden;
282 }
283 /**
284 * Filter autop off for grid forms.
285 * hooked to 'wpcf7_autop_or_not'
286 *@since 4.0.0
287 *@param boolean $autop true or false.
288 *@return boolean true or false
289 */
290 public function disable_autop_for_grid($autop){
291 $form = WPCF7_ContactForm::get_current();
292 if(isset($form) && $form->id()>0){
293 $is_grid_form = get_post_meta($form->id(), '_cf7sg_managed_form', true);
294 $autop = ( !$is_grid_form || ''==$is_grid_form );
295 }
296 return $autop;
297 }
298 /**
299 * Enqueue scripts requried for cf7 shortcode
300 * hooked on 'do_shortcode_tag',
301 * @since 1.0.0
302 **/
303 public function cf7_shortcode_request($output, $tag, $attr){
304 //if not a cf7 shortcode, then exit.
305 if('contact-form-7' !== $tag){
306 return $output;
307 }
308 //wp_enqueue_script('contact-form-7'); //default cf7 plugin script.
309 wp_enqueue_script('contact-form-7'); //default cf7 plugin script.
310 $cf7_id = $attr['id'];
311 //validate version number.
312 $form_version = get_post_meta($cf7_id, '_cf7sg_version', true);
313 if(empty($form_version) || version_compare($form_version, CF7SG_VERSION_FORM_UPDATE, '<')){
314 return '<p><em>'.__('Form is deprecated, please cotact the webmaster to <a href="https://wordpress.org/support/topic/upgrade-your-form-message-instead-of-form-being-displayed/">upgrade</a> this form.', 'cf7-grid-layout').'</em></p>';
315 }
316 //get the key
317 $cf7post = get_post($cf7_id);
318 if(empty($cf7post) || WPCF7_ContactForm::post_type != $cf7post->post_type){ //not a form.
319 return $output;
320 }
321 $cf7_key = $cf7post->post_name;
322
323 /** @since 3.0.0 load scripts only for required classes */
324 $class = get_post_meta($cf7_id, '_cf7sg_script_classes', true);
325 if(empty($class)){
326 $class = array();
327 }
328 //check classes required for sub-forms.
329 $sub_forms = array();
330 $use_grid_js = false;
331 $sub_form_keys = get_post_meta($cf7_id, '_cf7sg_sub_forms', true);
332 if(!empty($sub_form_keys)){
333 $args = array(
334 'post_type' => 'wpcf7_contact_form',
335 'post_name__in' => $sub_form_keys
336 );
337 $sub_forms = get_posts($args);
338 foreach($sub_forms as $form){
339 $sub_class = get_post_meta($form->ID, '_cf7sg_script_classes', true);
340 if(!empty($sub_class)) $class = array_unique(array_merge($class, $sub_class));
341 }
342 }
343 //select2
344 if(array_search('has-select2',$class, true)!==false){
345 wp_enqueue_script('jquery-select2');
346 wp_enqueue_style('select2-style');
347 $use_grid_js=true;
348 }
349 //nice-select
350 if(array_search('has-nice-select',$class, true)!==false){
351 wp_enqueue_script('jquery-nice-select');
352 wp_enqueue_style('jquery-nice-select-css');
353 $use_grid_js=true;
354 }
355 //benchmark
356 if(array_search('has-benchmark',$class, true)!==false){
357 wp_enqueue_script('js-cf7sg-benchmarking');
358 $use_grid_js=true;
359 }
360 if(array_search('has-date',$class, true)!==false){
361 wp_enqueue_script('jquery-ui-datepicker');
362 wp_enqueue_style('cf7-jquery-ui');
363 $use_grid_js=true;
364 }
365 /**
366 * @since 1.2.3 disable cf7sg styling/js for non-cf7sg forms.
367 */
368 $is_form = get_post_meta($cf7_id, '_cf7sg_managed_form', true);
369
370 $use_grid_js = ($use_grid_js or $is_form);
371
372 //cf7 plugin styles.
373 wp_enqueue_style('contact-form-7');
374
375 /** @since 2.6.0 disabled button message*/
376 $form = WPCF7_ContactForm::get_instance($cf7_id);
377 $messages = array();
378 if( !empty($form) ) $messages = $form->prop('messages');
379 else debug_msg("CF7SG FROM ERROR: unable to retrieve cf7 form $cf7_id");
380 //setup classes and id for wrapper.
381 $css_id ='';
382 apply_filters_deprecated('cf7_smart_grid_form_id', array($css_id, $attr), '4.6.0','', __('this filter is no longer available', 'cf7-grid-layout'));
383 $css_id = $this->form_css_id($cf7_key);
384 /** @since 4.6.0 allow redirect on submit */
385 $redirect = get_post_meta($cf7_id, '_cf7sg_page_redirect',true);
386 if(!empty($redirect)){
387 $cache = get_post_meta($cf7_id, '_cf7sg_cache_redirect_data',true);
388 if(!empty($cache)){
389 $cache = wp_create_nonce($css_id);
390 }
391 $redirect = get_permalink($redirect).( empty($cache) ? '':"?cf7sg=$cache");
392 }else $redirect='';
393 /** @since 4.4.0 enable prefilling of form fields*/
394 $prefill = apply_filters('cf7sg_prefill_form_fields', array(), $cf7_key);
395 if( !empty($redirect) or !empty($prefill) ) $use_grid_js = true;
396 if($use_grid_js){
397 $this->localise_script( array(
398 'url' => admin_url( 'admin-ajax.php' ),
399 'debug'=>( defined('WP_DEBUG') && WP_DEBUG ),
400 $css_id => array(
401 'prefill'=>$prefill,
402 'submit_disabled'=> isset($messages['submit_disabled']) ? $messages['submit_disabled']: __( "Disabled! To enable, check the acceptance field.", 'cf7-grid-layout' ),
403 'max_table_rows' => isset($messages['max_table_rows']) ? $messages['max_table_rows']: __( "You have reached the maximum number of rows.", 'cf7-grid-layout' ),
404 'table_labels' => apply_filters('cf7sg_remove_table_row_labels',true,$cf7_key),
405 'redirect'=>$redirect
406 )
407 ));
408 //cf7sg script & style.
409 wp_enqueue_script($this->plugin_name);
410 wp_localize_script( $this->plugin_name, 'cf7sg', $this->localise_script() );
411 }
412 //load custom css/js script from theme css folder.
413 $themepath = get_stylesheet_directory();
414 $themeuri = get_stylesheet_directory_uri();
415 if( file_exists($themepath.'/css/'.$cf7_key.'.css') ){
416 $dep = array();
417 if($is_form) $dep =array($this->plugin_name);
418 wp_enqueue_style( $cf7_key.'-css' , $themeuri.'/css/'.$cf7_key.'.css', $dep, null, 'all');
419 }
420 if( file_exists($themepath.'/js/'.$cf7_key.'.js') ){
421 $dep = array();
422 if($use_grid_js) $dep =array($this->plugin_name);
423 wp_enqueue_script( $cf7_key.'-js' , $themeuri.'/js/'.$cf7_key.'.js', $dep , null, true);
424 do_action_deprecated('smart_grid_register_custom_script', array($cf7_key), '4.6.0', 'cf7sg_enqueue_custom_script-$form_key', __('deprecated action hook','cf7-grid-layout'));
425 do_action("cf7sg_enqueue_custom_script-{$cf7_key}",$cf7_key.'-js');
426 }
427
428 if(empty($is_form) or !$is_form){
429 do_action('smart_grid_enqueue_scripts', $cf7_key, $attr, $class);
430 $classes = implode(' ', $class) .' key_'.$cf7_key;
431 $output = '<div class="cf7sg-container cf7sg-not-grid"><div id="' . $css_id . '" class="'.($use_grid_js?'cf7-smart-grid ':''). $classes . '">' . $output . '</div></div>';
432 return $output;
433 }
434 //grid styling.
435 wp_enqueue_style($this->plugin_name);
436 //load required dependencies for grid form.
437 wp_enqueue_style('smart-grid');
438 /** @since 4.4.0 set max-width of forms dynamically */
439 $max_width = apply_filters('cf7sg_max_form_width', 940, $cf7_key);
440 $mobile_cutoff = apply_filters('cf7sg_responsive_width', 480, $cf7_key);
441 wp_add_inline_style('smart-grid', ":root {--max-cf7sg-form-width: {$max_width}px } @media (max-width: {$mobile_cutoff}px) {.cf7sg-container .cf7-smart-grid.has-grid form .container .row .columns {float: none;margin: 0;width: 100%;}}");
442 wp_enqueue_style('dashicons');
443 //slider introduced in 4.0.
444 if(array_search('has-slider',$class, true)!==false){
445 wp_enqueue_script('glider-js');
446 wp_enqueue_style('glider-style');
447 }
448 //jquery accordion for collapsible rows.
449 $has_section = array_search('has-accordion',$class, true) !==false;
450 if($has_section){
451 wp_enqueue_script('jquery-ui-accordion');
452 }
453 $has_tabs = array_search('has-tabs',$class, true) !==false;
454 $has_tables = array_search('has-table',$class, true) !==false;
455 $has_toggles = array_search('has-toggles',$class, true) !==false;
456
457 $form_time = strtotime($cf7post->post_modified);
458 if(!empty($sub_forms)){
459 $cf7_form = $form_raw = '';
460 foreach($sub_forms as $post_obj){
461 //check form saved date, if sub-form is newer, we need to udpate it.
462 if(strtotime($post_obj->post_modified ) > $form_time){
463 if(empty($cf7_form)){
464 $cf7_form = WPCF7_ContactForm::get_instance($cf7_id);
465 $form_raw = '';
466 if( !empty($cf7_form) ) $form_raw = $cf7_form->prop( 'form' );
467 }
468 $form_raw = $this->update_sub_form($form_raw, $post_obj);
469 }
470 }
471 if(!empty($cf7_form)){ //redraw the form.
472 $cf7_form = wpcf7_save_contact_form(array('id'=>$cf7_id, 'form'=>$form_raw));
473 //reload the form
474 $output = $cf7_form->form_html($attr);
475 $class[]= 'has-update';
476 //actino for other plugin notification.
477 do_action('cf7sg_subform_uddate',$cf7_id, $cf7_form, $attr );
478 }
479 }
480 //required for tables/tabs/accordion
481 if($has_tabs || $has_tables || $has_toggles || $has_section){
482 wp_enqueue_script('jquery-effects-core');
483 }
484
485 if($has_tabs || $has_toggles || $has_section){
486 wp_enqueue_style('cf7-jquery-ui-theme');
487 wp_enqueue_style('cf7-jquery-ui-structure');
488 wp_enqueue_style('cf7-jquery-ui');
489 }
490 if($has_tabs) wp_enqueue_script('jquery-ui-tabs');
491 if($has_toggles){
492 wp_enqueue_script('jquery-toggles');
493 wp_enqueue_style('jquery-toggles-css');
494 wp_enqueue_style('jquery-toggles-light-css');
495 }
496 //$cf7_key = get_post_meta($cf7_id, '_smart_grid_cf7_form_key', true);
497 //allow custom script print
498 do_action('smart_grid_enqueue_scripts', $cf7_key, $attr, $class);
499
500 $classes = implode(' ', $class) .' key_'.$cf7_key;
501 $output = '<div class="cf7sg-container"><div id="' . $css_id . '" class="cf7-smart-grid ' . $classes . '">' . $output . '</div></div>';
502 return $output;
503 }
504 /**
505 *
506 *
507 *@since 4.6.0
508 *@param string $param text_description
509 *@return string text_description
510 */
511 public function form_css_id($cf7_key){
512 return 'cf7sg-form-'.$cf7_key;
513 }
514 /**
515 * Function hooked on 'cf7_2_post_form_values' to load toggle status for saved submissions
516 *
517 *@since 1.1.0
518 *@param string $post_id saved submission post ID
519 */
520 public function load_saved_toggled_status($field_values){
521 if(!isset($field_values['map_post_id']) || empty($field_values['map_post_id'])){
522 return $field_values;
523 }
524 $post_id = $field_values['map_post_id'];
525 $toggles = get_post_meta($post_id, 'cf7sg_toggles_status', true);
526 if(!empty($toggles)){
527 $cf7post = get_post($post_id);
528 $cf7_key = $cf7post->post_name;
529 wp_enqueue_script($this->plugin_name);
530 wp_localize_script( $this->plugin_name, 'cf7sg', $this->localise_script( array('toggles_status' => $toggles)), $this->form_css_id($cf7_key));
531 }
532 return $field_values;
533 }
534 /**
535 * Single source for localised script.
536 *
537 *@since 2.6.0
538 *@param array $params additional parameter to send.
539 *@return array data array to localise
540 */
541 private function localise_script($params=array(), $css_id=null){
542 if( empty($css_id) ) $this->localised_data += $params;
543 else{
544 if( !isset($this->localised_data[$css_id]) ) $this->localised_data[$css_id] = array();
545 $this->localised_data[$css_id] += $params;
546 }
547 return $this->localised_data;
548 }
549 /**
550 * Update sub-forms in cf7 forms
551 * Hooked to 'do_shortcode'
552 * @since 1.0.0
553 * @param String $form_raw HTML form.
554 * @param WP_Post $sub_form_post CF7 sub-form post object.
555 * @return String HTML for new form.
556 **/
557 public function update_sub_form($form_raw, $sub_form_post){
558 //Create a new DOM document
559 $cf7_key = $sub_form_post->post_name;
560 $sub_form_raw = get_post_meta($sub_form_post->ID, '_form', true);
561 //PHP DOM plugin.
562 /** @since 3.2.0 use Simple HTML Dom library */
563 require_once plugin_dir_path( __DIR__ ) . 'assets/simple-html-dom/autoload.php';
564
565 $dom = HtmlDomParser::str_get_html($form_raw);
566 //reset the inner form.
567 $element = $dom->find('#cf7sg-form-'.$cf7_key);
568
569 $element[0]->innertext = $sub_form_raw;
570
571 return $dom->outertext;
572 }
573 /**
574 * Function to load custom js script for Post My CF7 Form loading of form field values
575 * Hooked to 'cf7_2_post_echo_field_mapping_script'
576 * @since 1.0.0
577 * @param boolean $default_script whether to use the default script or not, default is true.
578 * @param string $field cf7 form field name
579 * @param string $type field type (number, text, select...)
580 * @param string $json_value the json value loaded for this field in the form.
581 * @param string $$js_form the javascript variable in which the form is loaded.
582 * @return boolean false to print a custom script from the called function, true for the default script printed by this plugin.
583 **/
584 public function load_tabs_table_field($default_script, $post_id, $field, $type, $json_value, $js_form){
585 $grid = self::field_type($field, $post_id);
586 switch($grid){
587 case 'tab':
588 case 'table':
589 case 'both':
590 include( plugin_dir_path( __FILE__ ) . '/partials/cf7sg-field-load-script.php');
591 $default_script = false;
592 break;
593 default:
594 break;
595 }
596 return $default_script;
597 }
598 /**
599 * Register a [save] shortcode with CF7.
600 * Hooked on 'wpcf7_init'
601 * This function registers a callback function to expand the shortcode for the save button field.
602 * @since 2.0.0
603 */
604 public function register_cf7_shortcode() {
605 if( function_exists('wpcf7_add_form_tag') ) {
606 //benchmark
607 wpcf7_add_form_tag(
608 array( 'benchmark', 'benchmark*' ),
609 array($this,'cf7_benchmark_shortcode'),
610 true //has name
611 );
612 /** @since 4.10.0 abstract out dynamic lists */
613 do_action('cf7sg_register_dynamic_lists');
614 $lists = cf7sg_get_dynamic_lists();
615 // debug_msg($lists, 'dynamic lists ');
616 foreach($lists as $l) $l->register_cf7_shortcode();
617 }
618 }
619 /**
620 * Register a [benchmark] shortcode with CF7.
621 * called by funciton above
622 * This function registers a callback function to expand the shortcode for the googleMap form fields.
623 * @since 1.0.0
624 * @param strng $tag the tag name designated in the tag help screen
625 * @return string a set of html fields to capture the googleMap information
626 */
627
628 public function cf7_benchmark_shortcode($tag){
629 $tag = new WPCF7_FormTag( $tag );
630 wp_enqueue_script('js-cf7sg-benchmarking');
631 wp_enqueue_style( 'cf7-benchmark-css' );
632 ob_start();
633 include( plugin_dir_path( __FILE__ ) . '/partials/cf7-benchmark-tag.php');
634 $html = ob_get_contents ();
635 ob_end_clean();
636 return $html;
637 }
638
639 /**
640 * Function to save ajax submitted grid fields, grid fields are any input/select fields used in the table/tabs constructs
641 * hooked on wp_ajax_nopriv_save_grid_fields and wp_ajax_save_grid_fields. The ajax is only fired in case a sub-form has been updated.
642 * @since 1.0.0
643 **/
644 public function save_grid_fields(){
645 if( !isset($_POST['nonce']) ){
646 wp_send_json_error(array('message'=>'nonce failed'));
647 wp_die();
648 }
649 $cf7_id = sanitize_text_field($_POST['id']);
650
651 if(!wpcf7_verify_nonce($_POST['nonce'], $cf7_id)){
652 wp_send_json_error(array('message'=>'nonce failed'));
653 wp_die();
654 }
655
656 if(isset($_POST['tabs_fields']) && isset($_POST['table_fields'])){
657 $tabs_fields = json_decode(stripslashes($_POST['tabs_fields']));
658 if(empty($tabs_fields)) $tabs_fields = array();
659 if(!is_array($tabs_fields)) $tabs_fields = array($tabs_fields);
660 //validate each field name as text, sanitize.
661 $sanitised_tab_fields = array();
662 foreach($tabs_fields as $field){
663 $sanitised_tab_fields[] = sanitize_text_field($field);
664 }
665 $table_fields = json_decode(stripslashes($_POST['table_fields']));
666 if(empty($table_fields)) $table_fields = array();
667 if(!is_array($table_fields)) $table_fields = array($table_fields);
668 $sanitised_table_fields = array();
669 foreach($table_fields as $field){
670 $sanitised_table_fields[] = sanitize_text_field($field);
671 }
672 //debug_msg($grid_fields, $cf7_id);
673 update_post_meta($cf7_id, '_cf7sg_grid_tabs_names', $sanitised_tab_fields);
674 update_post_meta($cf7_id, '_cf7sg_grid_table_names', $sanitised_table_fields);
675 wp_send_json_success(array('message'=>'saved fields'));
676 }else{
677 wp_send_json_error(array('message'=>'no data received'));
678 }
679 wp_die();
680 }
681 /**
682 * This function filters submitted data and consolidates grid field values into arrays.
683 * Function hooked on 'wpcf7_posted_data'
684 * @since 1.0.0
685 * @param Array $data unvalidated submitted data.
686 * @return Array filtered submitted data.
687 **/
688 public function setup_grid_values($data){
689 // debug_msg($data, 'CF7 processed data ');
690 $cf7form = WPCF7_ContactForm::get_current();
691 // debug_msg($_POST);
692 if(empty($cf7form) ){
693 if(isset($_POST['_wpcf7']) ){
694 $cf7_id = $_POST['_wpcf7'];
695 $cf7form = WPCF7_ContactForm::get_instance($cf7_id);
696 if(empty($cf7form) ){
697 debug_msg("CF7SG ERROR: fn setup_grid_values() is unable to load submitted form");
698 return $data;
699 }
700 }
701 }
702 $cf7_id = $cf7form->id();
703 $this->form_id = $cf7_id;
704 //debug_msg($grid_fields, 'grid fields...');
705
706 foreach($cf7form->scan_form_tags() as $tag){
707 if(empty($tag->name)) continue;
708 $field_name = $tag['name'];
709 $field_type = self::field_type($field_name, $cf7_id);
710 switch($field_type){
711 case 'tab':
712 case 'table':
713 case 'both':
714 // the $data array is passed by reference and will be consolidated.
715 /** @since 2.5.0 */
716 $this->consolidate_grid_submissions($tag, $field_type, $data);
717 break;
718 default:
719 $toggle = $this->get_toggle($field_name);
720 /** @since 4.8.1 preserve cf7 data schema for some plugins */
721 if( apply_filters('cf7sg_preserve_cf7_data_schema', false) ){
722 if(!empty($toggle) && !$this->is_submitted($toggle)) $data[$field_name] = null;
723 }else{
724 if(!empty($toggle)){
725 $data[$field_name] = $this->is_submitted($toggle) ? $this->get_field_value($field_name,$field_type, $tag) : null;
726 }else $data[$field_name] = $this->get_field_value($field_name, $tag['basetype'], $tag);
727 }
728 break;
729 }
730 }
731 //add toggled sections as submitted values.
732 $groups = get_post_meta($this->form_id, '_cf7sg_grid_grouped_toggles', true);
733 if(!empty($groups)){
734 foreach($groups as $group=>$grp_toggles){
735 foreach($grp_toggles as $toggle){
736 if( isset(self::$array_toggled_panels[$this->form_id][$toggle]) ){
737 self::$array_toggled_panels[$this->form_id][$group]=self::$array_toggled_panels[$this->form_id][$toggle];
738 break;
739 }
740 }
741 }
742 $data += self::$array_toggled_panels[$this->form_id];
743 }
744 // debug_msg($data, 'consolidated + submitted: ');
745 // debug_msg($_POST);
746 return $data;
747 }
748 /**
749 * new function to consolidate submitted data for improved handling of optional toggled fields as well as special fields such as files and checkboxes in tabbed sectoins.
750 *
751 *@since 2.5.0
752 *@param array $field_tag a cf7 form field tag which is part of tabs or table grid section.
753 *@param array $type field type, should be 'tab'/'table'/'both'.
754 *@param array $data cf7 form submissed data passed by reference as consolidated grid values will be removed and the original field value replaced with an array.
755 *@return array a filtered array of $index_suffix=>$value pairs for tabs or rows fields, The index suffix is '.row-<index>' for tables and '.tab-<index>' for tabs. This method returns a 2 dimensional array for fields which are both within rowns and tabs. The 2 dimensional array will list [<tab_suffix>][<row_suffix>]=>$value. The original field name that was submitted can be reconstructed as $field_name.$index_suffix. The first field will have an empty string as its $index_suffix.
756 */
757 private function consolidate_grid_submissions($field_tag, $type, &$data){
758 //get_post_meta($post_id, '_cf7sg_has_tables', true);
759 if(isset($_POST['_wpcf7']) ) $cf7_id = $_POST['_wpcf7'];
760 else{
761 debug_msg("CF7SG ERROR: fn consolidate_grid_submissions() is unable to load submitted form");
762 }
763 $is_used = false;
764 $field_name = $field_tag['name'];
765 $field_type = $field_tag['basetype'];
766 $origin = self::$array_grid_fields[$cf7_id][$field_name][0];//array(origin,type);
767 // debug_msg($origin, "$field_name origin ");
768 $values = array();
769 $regex = '';
770 $submitted_fields=array();
771 $max_fields =0;
772 $purge_fields=array();
773 $toggle = $this->get_toggle($field_name);
774 switch($type){
775 case 'tab':
776 case 'table':
777 $index_suffix = ('table'==$type)?'_row-':'_tab-';
778
779 //find the number of rows submitted.
780 $max_fields = isset($_POST[$origin]) ? $_POST[$origin]:0;
781
782 if(!empty($toggle) && !$this->is_submitted($toggle)){ //check if submitted.
783 $values[''] = null;
784 }else{
785 $values['']= $this->get_field_value($field_name,$field_type, $field_tag);
786 $is_used = true;
787 }
788
789 for($idx=1;$idx<$max_fields;$idx++){
790 $fid=$index_suffix.$idx;
791 $toggle_id = $this->is_tabbed($toggle[0]) ? preg_filter('/$/',$fid, $toggle) : $toggle;
792 if(!empty($toggle) && !$this->is_submitted($toggle_id)){ //check if submitted.
793 $values[$fid] = null;
794 }else{
795 $values[$fid] = $this->get_field_value($field_name.$fid,$field_type, $field_tag);
796 $is_used = true;
797 }
798 $purge_fields[$field_name.$fid] = true;
799 }
800 break;
801 case 'both':
802 $origins = explode(':', $origin);
803 $table_origin = $origins[0];
804 $tab_origin = $origins[1];
805 $max_tabs = isset($_POST[$tab_origin])?$_POST[$tab_origin]:0;
806 $values[''] = array(); //init multi-dimensional array
807 // not required since it is set in the for loop below.
808 // if(!empty($toggle) && !$this->is_submitted($toggle)){ //check if submitted, tab<-toggle<-table.
809 // $values[''][''] = null;
810 // }else{
811 // $values[''][''] = $this->get_field_value($field_name,$field_type,$field_tag);
812 // }
813 for($idx=0;$idx<$max_tabs;$idx++){ //tables in other tabs.
814 $max_fields = 0;
815 if(0==$idx && isset($_POST[$table_origin])){
816 $max_fields = $_POST[$table_origin];
817 }else if($idx>0 && isset($_POST[$table_origin.'_tab-'.$idx])){
818 $max_fields = $_POST[$table_origin.'_tab-'.$idx];
819 }
820 $tab_suffix= ($idx>0)? '_tab-'.$idx:'';
821 for($jdx=0;$jdx<$max_fields;$jdx++){
822 $row_suffix= ($jdx>0)?'_row-'.$jdx:'';
823 $fid = $tab_suffix.$row_suffix;
824 $toggle_id = $this->is_tabbed($toggle[0]) ? preg_filter('/$/',$tab_suffix,$toggle) : $toggle;
825
826 if(!empty($toggle) && !$this->is_submitted($toggle_id)){ //check if submitted, tab<-toggle<-table.
827 $values[$tab_suffix][$row_suffix] = null;
828 }else{
829 $values[$tab_suffix][$row_suffix] = $this->get_field_value($field_name.$fid,$field_type, $field_tag);
830 $is_used = true;
831 }
832 $purge_fields[$field_name.$fid] = true; //remove from the original $data.
833 }
834 }
835 break;
836 }
837 $purge_fields[$field_name] = true;
838 //debug_msg($purge_fields, 'purging ');
839 $data = array_udiff_uassoc($data, $purge_fields, function($a, $b){return 0;}, function($a, $b){
840 if ($a === $b) return 0;
841 return ($a > $b)? 1:-1;
842 }); //this will remove all the surplus fields
843 if( $is_used ) $data[$field_name] = $values;
844 }
845 /**
846 * Extract either a file name or a field value
847 *
848 *@since 2.5.0
849 *@param array $data submitted data.
850 *@return string text_description
851 */
852 private function get_field_value($field_name, $field_type, $field_tag){
853 $value = '';
854 if('file' == $field_type) { /** @since 4.9.0 handle files in posted data due to CF7 5.4 changes */
855 if(empty($_FILES[$field_name])) return;
856 $file = $_FILES[$field_name];
857
858 $args = array(
859 'tag' => $field_tag,
860 'name' => $field_name,
861 'required' => $field_tag->is_required(),
862 'filetypes' => $field_tag->get_option( 'filetypes' ),
863 'limit' => $field_tag->get_limit_option(),
864 );
865 $value = wpcf7_unship_uploaded_file( $file, $args );
866 if(is_wp_error($value)) {
867 $this->file_wp_errors[$field_name]=$value;
868 $value='';
869 }
870 }else{
871 $pipes = $field_tag->pipes;
872
873 $value = isset($_POST[$field_name]) ? $_POST[$field_name]:'';
874
875 if ( WPCF7_USE_PIPE and $pipes instanceof WPCF7_Pipes and ! $pipes->zero() and !in_array( $field_type, array('map','dynamic_select')) ){
876 if(is_array($value)){
877 $piped = array();
878 foreach($value as $v){
879 $piped[] = $pipes->do_pipe($v);
880 }
881 $value = $piped;
882 }else $value = $pipes->do_pipe($value);
883 }
884 }
885 return $value;
886 }
887 /**
888 * function returns an array of fields as keys and value which are eitehr 'tab' or 'table', or 'both'.
889 *
890 * @since 1.0.0
891 * @param string $form_id form id for which to return fields.
892 * @return array empty array if no fields found..
893 **/
894 static public function get_grid_fields($form_id){
895 if(isset(self::$array_grid_fields[$form_id])){
896 return self::$array_grid_fields[$form_id];
897 }
898 $grid_fields = array();
899 //tables
900 $fields = get_post_meta($form_id , '_cf7sg_grid_table_names', true);
901 if(!empty($fields)){
902 if( is_array( $fields[key($fields)] ) ){
903 foreach($fields as $table=>$table_fields) $grid_fields += array_fill_keys($table_fields, array($table,'table'));
904 }else $grid_fields += array_fill_keys($fields, array('cf7-sg-table-000','table'));
905 }
906 //tabs
907 $fields = get_post_meta($form_id , '_cf7sg_grid_tabs_names', true);
908 if(!empty($fields)){
909 if(is_array( $fields[key( $fields)] ) ){ /** @since 2.4.2 */
910 foreach($fields as $tab=>$tab_fields){
911 foreach($tab_fields as $field){
912 if(isset($grid_fields[$field])){
913 $grid_fields[$field] = array($grid_fields[$field][0].':'.$tab,'both');
914 }else $grid_fields[$field] = array($tab,'tab');
915 }
916 }
917 }else{
918 foreach($fields as $field){
919 if(isset($grid_fields[$field])) $grid_fields[$field] = array($grid_fields[$field][0].':cf7-sg-tab-000','both');
920 else $grid_fields[$field] = array('cf7-sg-tab-000','tab');
921 }
922 }
923 }
924 $subform_keys = get_post_meta($form_id, '_cf7sg_sub_forms', true);
925 if(!empty($subform_keys)){
926 foreach($subform_keys as $cf7Key){
927 $post_id = get_cf7form_id($cf7Key);
928 $grid_fields += self::get_grid_fields($post_id);
929 }
930 }
931 self::$array_grid_fields[$form_id]=$grid_fields;
932 //load panel fields.
933 self::$array_toggled_panels[$form_id]=array();
934 $toggled_panels = array();
935 if(isset($_POST['_cf7sg_toggles'])){
936 $toggled_panels = json_decode( stripslashes($_POST['_cf7sg_toggles']));
937 if(!empty($toggled_panels)) $toggled_panels = get_object_vars($toggled_panels);
938 else $toggled_panels = array();
939 }
940 self::$array_toggled_panels[$form_id]=$toggled_panels;
941
942 $fields = get_post_meta($form_id, '_cf7sg_grid_toggled_names', true);
943 self::$array_toggle_fields[$form_id]=array();
944 if(!empty($fields)){
945 foreach($fields as $panel=>$pfields){
946 foreach($pfields as $field){
947 if( isset(self::$array_toggle_fields[$form_id][$field]) ){
948 self::$array_toggle_fields[$form_id][$field][] = $panel;
949 }else{
950 self::$array_toggle_fields[$form_id][$field]= array($panel);
951 }
952 }
953 }
954 }
955 $tabtgls = get_post_meta($form_id, '_cf7sg_grid_tabbed_toggles', true);
956 if(!empty($tabtgls)) $tabtgls = array_fill_keys($tabtgls,true);
957 else $tabtgls = array();
958 self::$array_tabbed_toggles[$form_id]=$tabtgls;
959 return $grid_fields;
960 }
961
962 /**
963 * Validates required benchmark and dynamic_select tags
964 *
965 * @since 1.0.0
966 * @param WPCF7_Validation $result validation object
967 * @param Array $tag an array of cf7 tag attributes and values
968 * @return WPCF7_Validation validation result
969 **/
970 public function validate_required($result, $tag){
971 if(!isset( $_POST[$tag->name] ) ){
972 return $result; //not submitted hence disabled.
973 }
974 $tag = new WPCF7_FormTag( $tag );
975
976 $name = $tag->name;
977 $value = isset( $_POST[$name] )
978 ? trim( strtr( (string) sanitize_text_field($_POST[$name]), "\n", " " ) ): '';
979
980 if ( $tag->is_required() && '' == $value ) {
981 $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
982 }
983 return $result;
984 }
985
986 /**
987 * Final validation with all values submitted for inter dependent validation
988 * Hooked to filter 'wpcf7_validate', sets up the final $result object
989 * @since 1.0.0
990 * @param WPCF7_Validation $result validation object
991 * @param Array $tags an array of cf7 tag used in this form
992 * @return WPCF7_Validation validation result
993 **/
994 public function filter_wpcf7_validate($result){
995 /** @since 3.3.3 fix for captch field validation*/
996 $invalids = $result->get_invalid_fields();
997 /**
998 *@since 1.1.0
999 *reset the validation result.
1000 * this is required to dynamically disable required form fields & stop their validation.
1001 * Disabled fields are not submitted but CF7 forces their values to empty and therefore flags requried fields as invalid at submission even if they are disabled.
1002 * this bug was reported in the cf7 support forum:
1003 * https://wordpress.org/support/topic/bug-javascript-disabled-fields-flagged-as-invalid/
1004 **/
1005 $result = new WPCF7_Validation();
1006
1007 //rebuild the default validation result.
1008 $cf7form = WPCF7_ContactForm::get_current();
1009 $submitted = WPCF7_Submission::get_instance();
1010 $data = $submitted->get_posted_data();
1011 $tag_types = array(); //store all form tags, including cloned tags for array fields.
1012
1013 /** @since 3.1.3 allow validation of unvalidated data in custom validation filter */
1014 $validation = array(); //holds all the validation messages.
1015 $validated = array();
1016 $submission = array();
1017 /**
1018 *@since 2.1.0 fix issue with Conditional Field plugin.
1019 */
1020 $data = $this->remove_hidden_fields_from_conditional_plugin($data);
1021 $toggle_status = '';
1022 if(isset($_POST['_cf7sg_toggles'])){
1023 $toggle_status = json_decode( stripslashes($_POST['_cf7sg_toggles']));
1024 }
1025
1026 foreach ( $cf7form->scan_form_tags() as $tag ) {
1027 //debug_msg($data);
1028 /**
1029 *@since 1.9.0 fix issue with Conditional Field plugin.
1030 */
1031 //validation for non-deprecated plugins (v2.5.0+).
1032 if(!isset($data[$tag['name']])) continue; //it was removed by some plugin.
1033 $type = $tag['type'];
1034 //check to see if this field is an array (table or tab or both).
1035 $tag_types[$tag['name']] = $tag['type'];
1036 $field_type = self::field_type($tag['name'], $cf7form->id());
1037 switch($field_type){
1038 case 'tab':
1039 case 'table':
1040 case 'both':
1041 // the $data array is passed by reference and will be consolidated.
1042 $values = $data[$tag['name']];
1043 /** @since 3.1.3 track all validations */
1044 $validation[$tag['name']] = array();
1045 $validated[$tag['name']] = array();
1046 $submission[$tag['name']] = array();
1047 $idx = 0;
1048 foreach($values as $index=>$value){
1049 if(is_array($value) && 'both'==$field_type){
1050 $validation[$tag['name']][$idx]=array();
1051 $validated[$tag['name']][$idx] = array();
1052 $submission[$tag['name']][$idx] = array();
1053 $rdx = 0;
1054 foreach($value as $row=>$row_value){
1055 if(!isset($row_value)) continue;
1056 $sg_field_tag = clone $tag;
1057 $sg_field_tag['name'] = $tag['name'].$index.$row;
1058 if($tag['basetype'] === 'file' ){
1059 $result = apply_filters("wpcf7_validate_{$sg_field_tag->type}", $result, $sg_field_tag,
1060 array(
1061 'uploaded_files' => isset($this->file_wp_errors[$sg_field_tag['name']]) ? $this->file_wp_errors[$sg_field_tag['name']]: $row_value,
1062 )
1063 );
1064 }else{
1065 $result = apply_filters("wpcf7_validate_{$tag['type']}", $result, $sg_field_tag);
1066 }
1067 /** @since 3.1.3 */
1068 $validation[$tag['name']][$idx][$rdx] = $this->strip_cf7_validation($result, $sg_field_tag->name);
1069 $validated[$tag['name']][$idx][$rdx] = $sg_field_tag;
1070 $submission[$tag['name']][$idx][$rdx] = $row_value;
1071 $rdx++;
1072 }
1073 }else{
1074 if(!isset($value)) continue;
1075 $sg_field_tag = clone $tag;
1076 $sg_field_tag['name'] = $tag['name'].$index;
1077 if($tag['basetype'] === 'file' ){
1078 $result = apply_filters("wpcf7_validate_{$sg_field_tag->type}", $result, $sg_field_tag,
1079 array(
1080 'uploaded_files' => isset($this->file_wp_errors[$sg_field_tag['name']]) ? $this->file_wp_errors[$sg_field_tag['name']]: $value,
1081 )
1082 );
1083 }else{
1084 $result= apply_filters("wpcf7_validate_{$tag['type']}", $result, $sg_field_tag);
1085 }
1086 /** @since 3.1.3 */
1087 $validation[$tag['name']][$idx] = $this->strip_cf7_validation($result, $sg_field_tag->name);
1088 $validated[$tag['name']][$idx] = $sg_field_tag;
1089 $submission[$tag['name']][$idx] = $value;
1090 }
1091 $idx++;
1092 }
1093 // debug_msg($values, 'values '.$tag['name'].'....');
1094 // debug_msg($result, 'validation '.$tag['name'].'....');
1095 break;
1096 default:
1097 switch($type) {
1098 case 'captchar': //cannot be called twice, hence see if already invalidated.
1099 /** @since 3.3.3 check if invalidated previously */
1100 if( isset($invalids[$tag['name']]) ){
1101 $result->invalidate( $tag, $invalids[$tag['name']]['reason']);
1102 $validation[$tag['name']] = $invalids[$tag['name']]['reason'];
1103 $validated[$tag['name']] = $tag;
1104 }
1105 break;
1106 default:
1107 if($tag['basetype'] === 'file' ){
1108 $result = apply_filters("wpcf7_validate_{$tag->type}", $result, $tag,
1109 array(
1110 'uploaded_files' => isset($this->file_wp_errors[$tag['name']]) ? $this->file_wp_errors[$tag['name']]: $data[$tag['name']],
1111 )
1112 );
1113 }else{
1114 $result= apply_filters( "wpcf7_validate_{$type}", $result, $tag );
1115 }
1116 /** @since 3.1.3 */
1117 $validation[$tag['name']] = $this->strip_cf7_validation($result, $tag['name']);
1118 $validated[$tag['name']] = $tag;
1119 $submission[$tag['name']] = $data[$tag['name']];
1120 break;
1121 }
1122 break;
1123 }
1124 }
1125 $form_key = '';
1126 if(isset($_POST['_wpcf7_key'])){
1127 $form_key = $_POST['_wpcf7_key'];
1128 }
1129 // debug_msg($data, 'data ');
1130 //allow for more complex validation.
1131 if(has_filter('cf7sg_validate_submission')){
1132 /**
1133 * filter to validate the entire submission and check interdependency of fields
1134 * @since 1.0.0
1135 * @param Array $validation an array of $field_name=>$validaton_message.
1136 * @param Array $submission submitted data, $field_name => $value pairs ($value can be an array especially for fields in tables and tabs) null values are fields which have not been disabled and not submitted such as those in toggled sections.
1137 * @param String $form_key unique form key to identify current form.
1138 * @param int $form_id cf7 form post ID.
1139 * @return Array an array of errors messages, $field_name => $error_msg for single values, and $field_name => [0=>$error_msg, 1=>$error_msg,...] for array values
1140 */
1141 $validation = apply_filters('cf7sg_validate_submission', $validation, $submission, $form_key, $_POST['_wpcf7']);
1142
1143 //now that the user has filtered the validatoin, reset the in cf7 inalids.
1144 $result = new WPCF7_Validation();
1145
1146 if(!empty($validation)){
1147 foreach($validation as $name=>$msg){
1148 switch(true){
1149 case is_array($msg):
1150 foreach($msg as $idx=>$value){
1151 switch(true){
1152 case is_array($value):
1153 foreach($value as $rdx=>$message){
1154 if(empty($message)) continue;
1155 $result->invalidate($validated[$name][$idx][$rdx], $message);
1156 }
1157 break;
1158 case is_array($validated[$name][$idx]): //error, expecting array..
1159 debug_msg('Filtered cf7sg_validate_submission validation ERROR, expecting array for table field within tab: '.$name.'['.$idx.']');
1160 break;
1161 case empty($value): //no message, just continue.
1162 break;
1163 default:
1164 $result->invalidate($validated[$name][$idx], $value);
1165 break;
1166 }
1167 }
1168 break;
1169 case is_array($validated[$name]): //error, we should have an array.
1170 debug_msg('Filtered cf7sg_validate_submission validation ERROR, expecting array for field '.$name);
1171 break;
1172 case empty($msg): //no message, just continue.
1173 break;
1174 default:
1175 $result->invalidate($validated[$name], $msg);
1176 break;
1177 }
1178 }
1179 }
1180 }
1181 if($result->is_valid()){
1182 /**
1183 * Once data is valid fire action to expose final submitted data.
1184 * @since 4.9.0
1185 * @param Array $submission submitted data, $field_name => $value pairs ($value can be an array especially for fields in tables and tabs) null values are fields which have not been disabled and not submitted such as those in toggled sections.
1186 * @param String $form_key unique form key to identify current form.
1187 * @param int $form_id cf7 form post ID.
1188 */
1189 do_action('cf7sg_valid_form_submission',$submission, $form_key, $_POST['_wpcf7']);
1190 }
1191 return $result;
1192 }
1193
1194 /**
1195 * Function to strip the submitted data for a given field.
1196 * Handles base type files whose data is stored in the $_FILES global array.
1197 *@since 3.1.3
1198 *@param string $name field name.
1199 *@param string $type base type.
1200 *@param mixed $data $_POST data.
1201 *@return mixed value actually submitted.
1202 */
1203 public function get_submitted_data($name, $type, $data){
1204 $value = $data;
1205 switch($type){
1206 case 'file':
1207 case 'file*':
1208 $value = array();
1209 if(isset($_FILES[$name])) $value = $_FILES[$name];
1210 break;
1211 }
1212 return $value;
1213 }
1214 /**
1215 * This function strips the validation message from cf7 WPCF7_Validation object for storage and custom modification using the hook 'cf7sg_validate_submission'.
1216 *
1217 *@since 3.1.3
1218 *@param WPCF7_Validation $result cf7 validation object for the field being validated.
1219 *@param WPCF7_FormTag $tag cf7 tag object for the field being validated.
1220 *@return string text_description
1221 */
1222 protected function strip_cf7_validation($result, $name){
1223 $invalid = $result->get_invalid_fields();
1224 $msg = '';
1225 if( !empty($invalid) && isset($invalid[$name]['reason']) ){
1226 $msg = $invalid[$name]['reason'];
1227 }
1228 return $msg;
1229 }
1230 /**
1231 * Function to save toggled collapsible sections status to open them up again for draft forms.
1232 * Hooks action 'cf7_2_post_form_posted'
1233 * @since 1.0.0
1234 * @param string $key form unique key.
1235 * @param Array $submitted_data array of field-name=>value pairs submitted in form
1236 *@since 1.1.0
1237 *@param string $param text_description
1238 *@return string text_description
1239 */
1240 public function save_toggle_status($post_id, $key, $post_fields, $post_meta_fields, $submitted_data){
1241 if(isset($submitted_data['_cf7sg_toggles'])){
1242 $toggles = json_decode( stripslashes( $submitted_data['_cf7sg_toggles'] ) );
1243 $toggles_array = array();
1244 if(!empty($toggles)){
1245 foreach($toggles as $key=>$value){
1246 $toggles_array[$key] = sanitize_text_field($value);
1247 }
1248 }
1249 //debug_msg($toggles_array, 'toggles status saved, ');
1250 update_post_meta($post_id, 'cf7sg_toggles_status', $toggles_array);
1251 }
1252 }
1253 /**
1254 * Function to save custom options values added to tagged select2 fields.
1255 * Hooks action 'cf7_2_post_form_posted'
1256 * @since 1.0.0
1257 * @param string $key form unique key.
1258 * @param Array $submitted_data array of field-name=>value pairs submitted in form
1259 **/
1260 public function save_select2_custom_options($post_id, $cf7_key, $post_fields, $post_meta_fields, $submitted_data){
1261 $form_id = get_cf7form_id($cf7_key);
1262 /** @since 4.10.0 abstract out dynamic lists */
1263 $lists = cf7sg_get_dynamic_lists();
1264 foreach($lists as $l){
1265 $l->save_form_2_post($post_id, $form_id, $cf7_key, $post_fields, $post_meta_fields, $submitted_data);
1266 }
1267 }
1268
1269 /**
1270 * Function to temporarily fix the conditional fields plugin issue.
1271 * this is is called by the vlidation function 'filter_wpcf7_validate' above.
1272 *@since 2.1
1273 *@param array $posted_data submitted data
1274 *@return array submitted data without fields that remained hidden.
1275 */
1276 private function remove_hidden_fields_from_conditional_plugin($posted_data){
1277 /*code taken from cf7cf.php file in cf7-conditional-fields*/
1278 if(!isset($_POST['_wpcf7cf_hidden_group_fields'])){
1279 return $posted_data;
1280 }
1281
1282 $hidden_fields = json_decode(stripslashes($_POST['_wpcf7cf_hidden_group_fields']));
1283
1284 if (is_array($hidden_fields) && count($hidden_fields) > 0) {
1285 foreach ($hidden_fields as $field) {
1286 $field = str_replace('[]', '', $field);
1287 // if (wpcf7cf_endswith($field, '[]')) {
1288 // $field = substr($field,0,strlen($field)-2);
1289 // }
1290 unset( $posted_data[$field] );
1291 }
1292 }
1293 return $posted_data;
1294 }
1295 /**
1296 * Filter mail tags which are table or tab fields.
1297 * Hooked on 'wpcf7_mail_tag_replaced'.
1298 *@since 2.1
1299 *@param string $replaced mail tag string to replace.
1300 *@param mixed $submitted value of submitted field from cf7 posted data..
1301 *@param boolea $html mail if mail body is using html.
1302 *@param WPCF7_MailTag $mail_tag mail tag object of field being replaced.
1303 *@return string replacement string.
1304 */
1305 public function filter_table_tab_mail_tag($replaced, $submitted, $html=false, $mail_tag=null ){
1306 $cf7form = WPCF7_ContactForm::get_current();
1307 if(empty($cf7form)){
1308 debug_msg(mail_tag, 'SMART GRID (ERR): unable to retrieve current form while filtering mail tag: ');
1309 return $replaced; //no form object.
1310 }
1311 $cf7form_key = get_cf7form_key($cf7form->id());
1312 $submitted_cf7 = WPCF7_Submission::get_instance();
1313 $submitted_data = array();
1314 if(!empty($submitted_cf7)) $submitted_data = $submitted_cf7->get_posted_data();
1315
1316 switch(true){
1317 case 0===strpos($mail_tag->field_name(), 'cf7sg-toggle-'):
1318 $toggle = str_replace('cf7sg-toggle-', '', $mail_tag->field_name());
1319 if(isset($submitted_data[$toggle])) $replaced = $submitted_data[$toggle];
1320 break;
1321 case 0===strpos($mail_tag->field_name(), 'cf7sg-form-'):
1322 $replaced ="";
1323 break;
1324 }
1325
1326 if(empty($mail_tag)) return $replaced;
1327
1328 $field_type = self::field_type($mail_tag->field_name(), $cf7form->id());
1329 $label = '';
1330 $build = false;
1331
1332 switch($field_type){
1333 case 'tab':
1334 case 'table':
1335 if($html){
1336 $filtered = apply_filters_deprecated('cf7sg_mailtag_grid_fields', array('', $mail_tag->field_name(), $submitted, $cf7form_key, false), '4.6.2','cf7sg_mailtag_{$field_name}', __('this filter is no longer available', 'cf7-grid-layout'));
1337 $filtered = apply_filters('cf7sg_mailtag_'.$mail_tag->field_name(), $filtered, $submitted_data, $cf7form_key);
1338 if(!empty($filtered)){
1339 $replaced = $filtered;
1340 break;
1341 }
1342 }
1343 $label=('table'==$field_type)?'row':$field_type;
1344 if($html && is_array($submitted)) {
1345 $idx=0;
1346 $replaced='';
1347 foreach($submitted as $index=>$value){
1348 $idx++;
1349 if(is_array($value)) $value = implode(' | ', $value);
1350 $replaced .= '<div><label>'.$label.'('.$idx.'):</label><span>'.$value.'</span></div>';
1351 }
1352 }
1353 break;
1354 case 'both':
1355 if($html){
1356 $filtered = apply_filters_deprecated('cf7sg_mailtag_grid_fields', array('', $mail_tag->field_name(), $submitted, $cf7form_key, false), '4.6.2','cf7sg_mailtag_{$field_name}', __('this filter is no longer available', 'cf7-grid-layout'));
1357 $filtered = apply_filters('cf7sg_mailtag_'.$mail_tag->field_name(), $filtered, $submitted_data, $cf7form_key);
1358 if(!empty($filtered)){
1359 $replaced = $filtered;
1360 break;
1361 }
1362 }
1363 // build table.
1364 $replaced = '';
1365 if( is_array($submitted)) {
1366 $tab = 0;
1367 foreach($submitted as $index=>$value){
1368 $tab++;
1369 $idx=0;
1370 if($html) $replaced .= '<div><span>tab('.$tab.')</span>&nbsp;';
1371 else $replaced .= 'tab('.$tab.') = ';
1372 if(is_array($value)){
1373 foreach($value as $row=>$row_value){
1374 $idx++;
1375 if(is_array($row_value)) $row_value = implode('|', $row_value);
1376 if($html) $replaced .='<div><label>row('.$idx.'):</label><span>' . $row_value . '</span></div>';
1377 else $replaced .= $row_value.',';
1378 }
1379 }
1380 if($html) $replaced .='</div>';
1381 else $replaced .= PHP_EOL;
1382 }
1383 }
1384 break;
1385 default: //general fix for cf7 mail tags.
1386 $tag = $mail_tag->corresponding_form_tag();
1387
1388 /**
1389 * Filter the value inserted in the mail tag.
1390 * @since 2.9.0.
1391 * @param string $replaced value to filter.
1392 * @param string $field_name name of the field being inserted in the mail.
1393 * @param array $submitted submitted form data.
1394 * @param string $form_key unique form key identifier.
1395 * @return string a value to replace.
1396 */
1397 if($tag instanceof WPCF7_FormTag){
1398 $replaced = apply_filters('cf7sg_mailtag_'.$tag->basetype, $replaced, $mail_tag->field_name(), $submitted_data, $cf7form_key);
1399 }
1400 /**
1401 * Filter the value inserted in the mail tag.
1402 * @since 3.1.6.
1403 * @param string $replaced value to filter.
1404 * @param string $field_name name of the field being inserted in the mail.
1405 * @param array $submitted submitted form data.
1406 * @param string $form_key unique form key identifier.
1407 * @return string a value to replace.
1408 */
1409 $replaced = apply_filters('cf7sg_mailtag_'.$mail_tag->field_name(), $replaced, $submitted_data, $cf7form_key);
1410 break;
1411 }
1412
1413 return $replaced;
1414 }
1415 /**
1416 * We need to add the missing attachments just before the mail is sent.
1417 * Hooked to filter 'wpcf7_mail_components', sets up the final $components object
1418 * @since 2.4.1
1419 * @param Array $components an array of mail parts
1420 * @param WPCF7_ContactForm $cf7form cf7 form object
1421 * @param WPCF7_Mail $cf7mail cf7 mail object
1422 * @return Array an array of mail parts
1423 */
1424 public function wpcf7_mail_components($components, $cf7form, $cf7mail) {
1425 $submission = WPCF7_Submission::get_instance();
1426 /** @since 4.9 file validation changes in CF7 v5.4 */
1427 // $uploaded_files = $submission->uploaded_files(); //get file path from data subission itself.
1428 //get the tags that are file uploads.
1429 $tags = $cf7form->scan_form_tags(array(
1430 'feature' => 'file-uploading',
1431 ) );
1432 /** @since 2.5.6 fix issue with non-attached files */
1433 $template = $cf7mail->get( 'attachments' );
1434 $row = $tab = null;
1435 /** @since 2.8.1 fix for send pdf */
1436 $attachments = $components['attachments'];
1437 foreach ( $tags as $tag ) {
1438 $name = $tag['name'];
1439 if ( false === strpos( $template, "[${name}]" ) ) continue; //not attached.
1440 $field_type = self::field_type($name, $cf7form->id());
1441 $data = $submission->get_posted_data($name);
1442 switch($field_type){
1443 case 'tab':
1444 case 'table':
1445 foreach($data as $field_idx=>$file_path){
1446 if(empty($file_path)) continue 1;
1447 foreach($file_path as $path){
1448 $row = ('table'==$field_type)? str_replace('_row-', '', $field_idx):null;
1449 $row = (isset($row) && empty($row))? 1:1+(int)$row;
1450 $tab = ('tab'==$field_type)? str_replace('_tab-', '', $field_idx):null;
1451 $tab = (isset($tab) && empty($tab))? 1:1+(int)$tab;
1452 // $file = $uploaded_files[$name.$field_idx];
1453
1454 if(!in_array($path,$attachments)) $attachments[] = $path;
1455 $file_name = explode('/',$path);
1456 $file_name = $file_name[count($file_name)-1];
1457 $components['body'].= apply_filters('cf7sg_annotate_mail_attach_grid_files','', $name, $file_name, $tab, $row, $_POST['_wpcf7_key']);
1458 }
1459 }
1460 break;
1461 case 'both':
1462 $row = $tab = 1;
1463 foreach($data as $field_tab=>$tab_files){
1464 if(empty($tab_files)) continue 1;
1465 $tab = str_replace('_tab-', '', $field_tab);
1466 $tab = empty($tab)?1:1+(int) $tab;
1467 foreach($tab_files as $field_row=>$file_path){
1468 if(empty($file_path)) continue;
1469 foreach($file_path as $path){
1470 $row = str_replace('_row-', '', $field_row);
1471 // $file = $uploaded_files[$name.$field_tab.$field_row];
1472 if(!in_array($path,$attachments)) $attachments[] = $path;
1473 $file_name = explode('/',$path);
1474 $file_name = $file_name[count($file_name)-1];
1475 $components['body'].= apply_filters('cf7sg_annotate_mail_attach_grid_files','', $name, $file_name, $tab, $row, $_POST['_wpcf7_key']);
1476 }
1477 }
1478 }
1479 break;
1480 default: //singular fields.
1481 if(empty($data)) continue 2;
1482 foreach($data as $path){
1483 if(!in_array($path, $attachments)) $attachments[] = $path;
1484 $file_name = explode('/',$path);
1485 $file_name = $file_name[count($file_name)-1];
1486 $components['body'].= apply_filters('cf7sg_annotate_mail_attach_grid_files','', $name, $file_name, null, null, $_POST['_wpcf7_key']);
1487 }
1488 break;
1489 }
1490 }
1491 $components['attachments'] = $attachments;
1492 return $components;
1493 }
1494
1495 /**
1496 * Track form field value submissions for preview forms.
1497 * Hooked to action 'wpcf7_before_send_mail'
1498 *@since 4.4.0
1499 *@param WPCF7_Contact_Form $form cf7 form object.
1500 */
1501 public function on_submit_success($form){
1502 if(empty($form)) return;
1503 /** @since 4.6.0 check if the form is being redirected and data cached */
1504 $redirect = get_post_meta($form->id(), '_cf7sg_page_redirect',true);
1505 if(!empty($redirect)){
1506 $cache = get_post_meta($form->id(), '_cf7sg_cache_redirect_data',true);
1507 if(is_array($cache)){
1508 $submission = WPCF7_Submission::get_instance();
1509 $data = array();
1510 if(!empty($submission)){
1511 $data['fields'] = $submission->get_posted_data();
1512 $files = $submission->uploaded_files();
1513 if(!empty($files)) $data['files'] = $files;
1514 }
1515 $data = apply_filters('cf7sg_form_redirect_cached_data',$data, $_POST['_wpcf7_key'], $form->id());
1516 $transient = '_cf7sg_'.wp_create_nonce( $this->form_css_id($_POST['_wpcf7_key']) );
1517 set_transient( $transient, $data, $cache[0]*$cache[1]);
1518 // debug_msg($data, "setting transient $transient, expiring in ".$cache[0]*$cache[1]);
1519 }
1520 }
1521 //for preview forms...
1522 if( !isset($_POST['_cf7sg_preview']) ) return;
1523 $prefill = array();
1524 foreach( $form->scan_form_tags() as $tag){
1525 if( isset($_POST[$tag->name]) and !empty($_POST[$tag->name]) ){
1526 $prefill[$tag->name] = $_POST[$tag->name];
1527 }
1528 $prefill['_cf7sg_toggles'] = self::$array_toggled_panels[$form->id()];
1529 }
1530
1531 if(!empty($prefill)) setcookie('_cf7sg_'. sanitize_text_field( $_POST['_wpcf7_key'] ), json_encode($prefill),0,'/');
1532 }
1533
1534 }
1535
1536 if(!function_exists('cf7sg_extract_submitted_files')){
1537 /**
1538 * Extract submitted files. Used by other plugins to extract files.
1539 *
1540 *@since 4.9.0
1541 *@param Array $files array of files.
1542 *@return Array $file_name=>$file_path.
1543 */
1544 function cf7sg_extract_submitted_files(Array $files){
1545 $results=array();
1546 foreach($files as $file){
1547 if(is_array($file)) $results = array_merge($results, cf7sg_extract_submitted_files($file));
1548 else{
1549 $filename = explode('/',$file);
1550 $filename = $filename[count($filename)-1];
1551 $results[$filename] = $file;
1552 }
1553 }
1554 return $results;
1555 }
1556 }
1557