PluginProbe
Smart Grid-Layout Design for Contact Form 7 / 4.5
Smart Grid-Layout Design for Contact Form 7 v4.5
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.5, at public/class-cf7-grid-layout-public.php

1,837 lines 74.4 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 /**
98 * Initialize the class and set its properties.
99 *
100 * @since 1.0.0
101 * @param string $plugin_name The name of the plugin.
102 * @param string $version The version of this plugin.
103 */
104 public function __construct( $plugin_name, $version ) {
105
106 $this->plugin_name = $plugin_name;
107 $this->version = $version;
108 }
109 /**
110 *
111 *
112 * @since 1.0.0
113 * @param string $field field name to check
114 * @param string $form_id form id for which to check the
115 * @return string 'singular' for non-grid fields, 'tab' for tabbed fields, 'table' for tablled fields, 'both' for fields in tables in tabs.
116 */
117 static public function field_type($field, $form_id){
118 if(!isset(self::$array_grid_fields[$form_id])){
119 //try to load the fields.
120 self::get_grid_fields($form_id);
121 }
122 $type = 'singular';
123 if(isset(self::$array_grid_fields[$form_id][$field])){
124 $type = self::$array_grid_fields[$form_id][$field][1]; //array(origin_id,type);
125 }
126 return $type;
127 }
128
129 /**
130 * Check if a field is in a toggled section.
131 *
132 *@since 2.5.0
133 *@param string $field field nav_menu_link_attributes.
134 *@return array null or array of panel ids.
135 */
136 protected function get_toggle($field){
137 return isset(self::$array_toggle_fields[$this->form_id][$field]) ? self::$array_toggle_fields[$this->form_id][$field]: null;
138 }
139 /**
140 * Check if a toggled section was used and its fields submitted.
141 *
142 *@since 2.5.0
143 *@param string $toggle toogle id
144 *@return boolean was the toggle section submitted.
145 */
146 protected function is_submitted($toggle){
147 return !empty( array_intersect( array_keys(self::$array_toggled_panels[$this->form_id]), $toggle) );
148 }
149 /**
150 * Check if a toggled section was used and its fields submitted.
151 *
152 *@since 4.0.0
153 *@param string $toggle toogle id
154 *@return boolean was the toggle section inside a tabbed section.
155 */
156 protected function is_tabbed($toggle){
157 /* if the array is not set for this form_id,
158 * then it is because the form is an older version and
159 * should return true for backward compatibility */
160 if(isset(self::$array_tabbed_toggles[$this->form_id])){
161 return isset(self::$array_tabbed_toggles[$this->form_id][$toggle]);
162 }else return true;
163 }
164 /**
165 * Register the stylesheets for the public-facing side of the site.
166 *
167 * @since 1.0.0
168 */
169 public function register_styles() {
170 $airplane=false;
171 if( class_exists( 'Airplane_Mode_Core' ) && Airplane_Mode_Core::getInstance()->enabled()){
172 $airplane=true;
173 }
174 $plugin_dir = plugin_dir_url( __DIR__ );
175 //default style for cf7 grid forms (row buttons and tables mainly).
176 //others
177 // get registered script object for jquery-ui
178 global $wp_scripts;
179 $ui = $wp_scripts->query('jquery-ui-core');
180
181 // tell WordPress to load the Smoothness theme from Google CDN
182 if( !$airplane ){
183 $protocol = is_ssl() ? 'https' : 'http';
184 $url_path = "$protocol://cdnjs.cloudflare.com/ajax/libs/jqueryui/{$ui->ver}/";
185 wp_register_style('cf7-jquery-ui', $url_path . 'themes/smoothness/jquery-ui.min.css', array(), $ui->ver , 'all');
186 wp_register_style( 'cf7-jquery-ui-theme', $url_path . 'jquery-ui.theme.min.css', array(), $ui->ver, 'all');
187 wp_register_style( 'cf7-jquery-ui-structure', $url_path . 'jquery-ui.structure.min.css', array(), $ui->ver, 'all');
188 }
189
190
191 /** @since 3.1,0 improve live loading of resources */
192 $ff = '';
193 $pf='';
194 if(!defined('WP_DEBUG') || !WP_DEBUG){
195 $ff = '.min';
196 $pf = '/min';
197 }
198 wp_register_style( 'cf7-benchmark-css', $plugin_dir . "public/css{$pf}/cf7-benchmark.css", array(), $this->version, 'all' );
199 wp_register_style( $this->plugin_name, $plugin_dir . "public/css{$pf}/cf7-grid-layout-public.css", array(), $this->version, 'all' );
200 wp_register_style( 'smart-grid', $plugin_dir . "assets/css.gs/smart-grid{$ff}.css", array(), $this->version, 'all' );
201 wp_register_style('jquery-nice-select-css', $plugin_dir . "assets/jquery-nice-select/css/nice-select{$ff}.css", array(), $this->version, 'all' );
202 wp_register_style('jquery-toggles-css', $plugin_dir . "assets/jquery-toggles/css/toggles{$ff}.css", array(), $this->version, 'all' );
203 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' );
204 /** @since 3.2.1 use cloudflare for live sites */
205 if( $airplane || (defined('WP_DEBUG') && WP_DEBUG) ){
206 wp_register_style('select2-style', $plugin_dir . 'assets/select2/css/select2.min.css', array(), '4.0.13', 'all' );
207 }else{
208 wp_register_style('select2-style', '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/css/select2.min.css', array(), '4.0.13','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 }
216
217 /**
218 * Register the JavaScript for the public-facing side of the site.
219 *
220 * @since 1.0.0
221 */
222 public function register_scripts() {
223 $airplane=false;
224 if( class_exists( 'Airplane_Mode_Core' ) && Airplane_Mode_Core::getInstance()->enabled()){
225 $airplane=true;
226 }
227 /** @since 3.1,0 improve live loading of resources */
228 $pf='';
229 if(!defined('WP_DEBUG') || !WP_DEBUG){
230 $pf = '/min';
231 }
232 $plugin_dir = plugin_dir_url( __DIR__ );
233 wp_register_script( $this->plugin_name, $plugin_dir . "public/js{$pf}/cf7-grid-layout-public.js", array( 'jquery','contact-form-7' ), $this->version, true );
234 /** @since 3.2.1 use cloudflare for live sites */
235 if( $airplane || (defined('WP_DEBUG') && WP_DEBUG) ){
236 wp_register_script('jquery-select2', $plugin_dir . 'assets/select2/js/select2.min.js', array( 'jquery' ), '4.0.13', true );
237 }else{
238 wp_register_script('jquery-select2', '//cdnjs.cloudflare.com/ajax/libs/select2/4.0.13/js/select2.min.js', array( 'jquery' ), '4.0.13', true );
239 }
240 wp_register_script('jquery-nice-select', $plugin_dir . 'assets/jquery-nice-select/js/jquery.nice-select.min.js', array( 'jquery' ), $this->version, true );
241 wp_register_script('jquery-toggles', $plugin_dir . 'assets/jquery-toggles/toggles.min.js', array( 'jquery' ), $this->version, true );
242 wp_register_script('js-cf7sg-benchmarking', $plugin_dir . "public/js{$pf}/cf7-benchmark.js", array( 'jquery' ), $this->version, true );
243 /** @since 4.2.0 enable Glider sliders for slider sections */
244 wp_register_script('glider-js', $plugin_dir . 'assets/glider-js/glider.js', null, '1.7.4',true);
245 //allow custom script registration
246 do_action('smart_grid_register_scripts');
247 }
248 /**
249 * Dequeue script 'contact-form-7'
250 * hooked on 'wp_print_scripts', this canbe re-enqeued on specific cf7 shortcode calls
251 * @since 1.0.0
252 **/
253 public function dequeue_cf7_scripts(){
254 wp_dequeue_script('contact-form-7');
255 }
256 /**
257 * Dequeue script 'contact-form-7'
258 * hooked on 'wp_print_style', this canbe re-enqeued on specific cf7 shortcode calls
259 * @since 1.0.0
260 **/
261 public function dequeue_cf7_styles(){
262 wp_dequeue_style('contact-form-7');
263 }
264 /**
265 * Add the cf7 key to the hidden fields so as not to have to load it after each submission.
266 * Hooked to 'wpcf7_form_hidden_fields'
267 * @since 1.0.0
268 * @param Array $hidden hidden fields to add to cf7 form.
269 * @return Array hidden fields to add to cf7 form.
270 **/
271 public function add_hidden_fields($hidden){
272 $form = wpcf7_get_current_contact_form();
273 $post = get_post($form->id());
274 $hidden['_wpcf7_key'] = $post->post_name;
275 $hidden['_cf7sg_toggles'] = '';
276 $hidden['_cf7sg_version'] = $this->version;
277 /** @since 4.4.0 enable rest authentication for logged in users. */
278 $hidden['_wpnonce'] = wp_create_nonce('wp_rest');
279 return $hidden;
280 }
281 /**
282 * Filter autop off for grid forms.
283 * hooked to 'wpcf7_autop_or_not'
284 *@since 4.0.0
285 *@param boolean $autop true or false.
286 *@return boolean true or false
287 */
288 public function disable_autop_for_grid($autop){
289 $form = WPCF7_ContactForm::get_current();
290 if(isset($form) && $form->id()>0){
291 $is_grid_form = get_post_meta($form->id(), '_cf7sg_managed_form', true);
292 $autop = ( !$is_grid_form || ''==$is_grid_form );
293 }
294 return $autop;
295 }
296 /**
297 * Enqueue scripts requried for cf7 shortcode
298 * hooked on 'do_shortcode_tag',
299 * @since 1.0.0
300 **/
301 public function cf7_shortcode_request($output, $tag, $attr){
302 //if not a cf7 shortcode, then exit.
303 if('contact-form-7' !== $tag){
304 return $output;
305 }
306 //wp_enqueue_script('contact-form-7'); //default cf7 plugin script.
307 wp_enqueue_script('contact-form-7'); //default cf7 plugin script.
308 $cf7_id = $attr['id'];
309 //get the key
310 $cf7post = get_post($cf7_id);
311 if(empty($cf7post) || WPCF7_ContactForm::post_type != $cf7post->post_type){ //not a form.
312 return $output;
313 }
314 $cf7_key = $cf7post->post_name;
315
316 /** @since 3.0.0 load scripts only for required classes */
317 $class = get_post_meta($cf7_id, '_cf7sg_script_classes', true);
318 if(empty($class)){
319 $class = array();
320 }
321 //check classes required for sub-forms.
322 $sub_forms = array();
323 $use_grid_js = false;
324 $sub_form_keys = get_post_meta($cf7_id, '_cf7sg_sub_forms', true);
325 if(!empty($sub_form_keys)){
326 $args = array(
327 'post_type' => 'wpcf7_contact_form',
328 'post_name__in' => $sub_form_keys
329 );
330 $sub_forms = get_posts($args);
331 foreach($sub_forms as $form){
332 $sub_class = get_post_meta($form->ID, '_cf7sg_script_classes', true);
333 if(!empty($sub_class)) $class = array_unique(array_merge($class, $sub_class));
334 }
335 }
336 //select2
337 if(array_search('has-select2',$class, true)!==false){
338 wp_enqueue_script('jquery-select2');
339 wp_enqueue_style('select2-style');
340 $use_grid_js=true;
341 }
342 //nice-select
343 if(array_search('has-nice-select',$class, true)!==false){
344 wp_enqueue_script('jquery-nice-select');
345 wp_enqueue_style('jquery-nice-select-css');
346 $use_grid_js=true;
347 }
348 //benchmark
349 if(array_search('has-benchmark',$class, true)!==false){
350 wp_enqueue_script('js-cf7sg-benchmarking');
351 $use_grid_js=true;
352 }
353 if(array_search('has-date',$class, true)!==false){
354 wp_enqueue_script('jquery-ui-datepicker');
355 wp_enqueue_style('cf7-jquery-ui');
356 $use_grid_js=true;
357 }
358 /**
359 * @since 1.2.3 disable cf7sg styling/js for non-cf7sg forms.
360 */
361 $is_form = get_post_meta($cf7_id, '_cf7sg_managed_form', true);
362
363 $use_grid_js = ($use_grid_js or $is_form);
364
365 //cf7 plugin styles.
366 wp_enqueue_style('contact-form-7');
367
368 /** @since 2.6.0 disabled button message*/
369 $form = WPCF7_ContactForm::get_instance($cf7_id);
370 $messages = array();
371 if( !empty($form) ) $messages = $form->prop('messages');
372 else debug_msg("CF7SG FROM ERROR: unable to retrieve cf7 form $cf7_id");
373 //setup classes and id for wrapper.
374 $css_id = apply_filters('cf7_smart_grid_form_id', 'cf7sg-form-'.$cf7_key, $attr);
375
376 /** @since 4.4.0 enable prefilling of form fields*/
377 $prefill = apply_filters('cf7sg_prefill_form_fields', array(), $cf7_key);
378 if( !empty($prefill) and is_array($prefill) ) $use_grid_js = true;
379 if($use_grid_js){
380 $this->localised_data = array(
381 'url' => admin_url( 'admin-ajax.php' ),
382 'submit_disabled'=> isset($messages['submit_disabled']) ? $messages['submit_disabled']: __( "Disabled! To enable, check the acceptance field.", 'cf7-grid-layout' ),
383 'max_table_rows' => isset($messages['max_table_rows']) ? $messages['max_table_rows']: __( "You have reached the maximum number of rows.", 'cf7-grid-layout' ),
384 'table_labels' => apply_filters('cf7sg_remove_table_row_labels',true,$cf7_key),
385 'debug'=>( defined('WP_DEBUG') && WP_DEBUG ),
386 $css_id => $prefill,
387 );
388 //cf7sg script & style.
389 wp_enqueue_script($this->plugin_name);
390 wp_localize_script( $this->plugin_name, 'cf7sg', $this->localise_script() );
391 }
392 //load custom css/js script from theme css folder.
393 $themepath = get_stylesheet_directory();
394 $themeuri = get_stylesheet_directory_uri();
395 if( file_exists($themepath.'/css/'.$cf7_key.'.css') ){
396 $dep = array();
397 if($is_form) $dep =array($this->plugin_name);
398 wp_enqueue_style( $cf7_key.'-css' , $themeuri.'/css/'.$cf7_key.'.css', $dep, null, 'all');
399 }
400 if( file_exists($themepath.'/js/'.$cf7_key.'.js') ){
401 $dep = array();
402 if($use_grid_js) $dep =array($this->plugin_name);
403 wp_enqueue_script( $cf7_key.'-js' , $themeuri.'/js/'.$cf7_key.'.js', $dep , null, true);
404 do_action('smart_grid_register_custom_script', $cf7_key);
405 }
406
407 if(empty($is_form) or !$is_form){
408 do_action('smart_grid_enqueue_scripts', $cf7_key, $attr);
409 $classes = implode(' ', $class) .' key_'.$cf7_key;
410 $output = '<div class="cf7sg-container cf7sg-not-grid"><div id="' . $css_id . '" class="cf7-smart-grid ' . $classes . '">' . $output . '</div></div>';
411 return $output;
412 }
413 //grid styling.
414 wp_enqueue_style($this->plugin_name);
415 //load required dependencies for grid form.
416 wp_enqueue_style('smart-grid');
417 /** @since 4.4.0 set max-width of forms dynamically */
418 $max_width = apply_filters('cf7sg_max_form_width', 940, $cf7_key);
419 $mobile_cutoff = apply_filters('cf7sg_responsive_width', 480, $cf7_key);
420 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%;}}");
421 wp_enqueue_style('dashicons');
422 //slider introduced in 4.0.
423 if(array_search('has-slider',$class, true)!==false){
424 wp_enqueue_script('glider-js');
425 wp_enqueue_style('glider-style');
426 }
427 //jquery accordion for collapsible rows.
428 $has_section = array_search('has-accordion',$class, true) !==false;
429 if($has_section){
430 wp_enqueue_script('jquery-ui-accordion');
431 }
432 $has_tabs = array_search('has-tabs',$class, true) !==false;
433 $has_tables = array_search('has-table',$class, true) !==false;
434 $has_toggles = array_search('has-toggles',$class, true) !==false;
435
436 $form_time = strtotime($cf7post->post_modified);
437 if(!empty($sub_forms)){
438 $cf7_form = $form_raw = '';
439 foreach($sub_forms as $post_obj){
440 //check form saved date, if sub-form is newer, we need to udpate it.
441 if(strtotime($post_obj->post_modified ) > $form_time){
442 if(empty($cf7_form)){
443 $cf7_form = WPCF7_ContactForm::get_instance($cf7_id);
444 $form_raw = '';
445 if( !empty($cf7_form) ) $form_raw = $cf7_form->prop( 'form' );
446 }
447 $form_raw = $this->update_sub_form($form_raw, $post_obj);
448 }
449 }
450 if(!empty($cf7_form)){ //redraw the form.
451 $cf7_form = wpcf7_save_contact_form(array('id'=>$cf7_id, 'form'=>$form_raw));
452 //reload the form
453 $output = $cf7_form->form_html($attr);
454 $class[]= 'has-update';
455 //actino for other plugin notification.
456 do_action('cf7sg_subform_uddate',$cf7_id, $cf7_form, $attr );
457 }
458 }
459 //required for tables/tabs/accordion
460 if($has_tabs || $has_tables || $has_toggles || $has_section){
461 wp_enqueue_script('jquery-effects-core');
462 }
463
464 if($has_tabs || $has_toggles || $has_section){
465 wp_enqueue_style('cf7-jquery-ui-theme');
466 wp_enqueue_style('cf7-jquery-ui-structure');
467 wp_enqueue_style('cf7-jquery-ui');
468 }
469 if($has_tabs) wp_enqueue_script('jquery-ui-tabs');
470 if($has_toggles){
471 wp_enqueue_script('jquery-toggles');
472 wp_enqueue_style('jquery-toggles-css');
473 wp_enqueue_style('jquery-toggles-light-css');
474 }
475 //$cf7_key = get_post_meta($cf7_id, '_smart_grid_cf7_form_key', true);
476 //allow custom script print
477 do_action('smart_grid_enqueue_scripts', $cf7_key, $attr);
478
479 $classes = implode(' ', $class) .' key_'.$cf7_key;
480 $output = '<div class="cf7sg-container"><div id="' . $css_id . '" class="cf7-smart-grid ' . $classes . '">' . $output . '</div></div>';
481 return $output;
482 }
483 /**
484 * Function hooked on 'cf7_2_post_form_values' to load toggle status for saed submissions
485 *
486 *@since 1.1.0
487 *@param string $post_id saved submission post ID
488 */
489 public function load_saved_toggled_status($field_values){
490 if(!isset($field_values['map_post_id']) || empty($field_values['map_post_id'])){
491 return $field_values;
492 }
493 $post_id = $field_values['map_post_id'];
494 $toggles = get_post_meta($post_id, 'cf7sg_toggles_status', true);
495 //debug_msg($toggles, $post_id.' status ');
496 if(!empty($toggles)){
497 wp_enqueue_script($this->plugin_name);
498 wp_localize_script( $this->plugin_name, 'cf7sg', $this->localise_script( array('toggles_status' => $toggles)) );
499 }
500 return $field_values;
501 }
502 /**
503 * Single source for localised script.
504 *
505 *@since 2.6.0
506 *@param array $params additional parameter to send.
507 *@return array data array to localise
508 */
509 private function localise_script($params=array()){
510 $this->localised_data += $params;
511 return $this->localised_data;
512 }
513 /**
514 * Update sub-forms in cf7 forms
515 * Hooked to 'do_shortcode'
516 * @since 1.0.0
517 * @param String $form_raw HTML form.
518 * @param WP_Post $sub_form_post CF7 sub-form post object.
519 * @return String HTML for new form.
520 **/
521 public function update_sub_form($form_raw, $sub_form_post){
522 //Create a new DOM document
523 $cf7_key = $sub_form_post->post_name;
524 $sub_form_raw = get_post_meta($sub_form_post->ID, '_form', true);
525 //PHP DOM plugin.
526 /** @since 3.2.0 use Simple HTML Dom library */
527 require_once plugin_dir_path( __DIR__ ) . 'assets/simple-html-dom/autoload.php';
528
529 $dom = HtmlDomParser::str_get_html($form_raw);
530 //reset the inner form.
531 $element = $dom->find('#cf7sg-form-'.$cf7_key);
532
533 $element[0]->innertext = $sub_form_raw;
534
535 return $dom->outertext;
536 }
537 /**
538 * Function to load custom js script for Post My CF7 Form loading of form field values
539 * Hooked to 'cf7_2_post_echo_field_mapping_script'
540 * @since 1.0.0
541 * @param boolean $default_script whether to use the default script or not, default is true.
542 * @param string $field cf7 form field name
543 * @param string $type field type (number, text, select...)
544 * @param string $json_value the json value loaded for this field in the form.
545 * @param string $$js_form the javascript variable in which the form is loaded.
546 * @return boolean false to print a custom script from the called function, true for the default script printed by this plugin.
547 **/
548 public function load_tabs_table_field($default_script, $post_id, $field, $type, $json_value, $js_form){
549 $grid = self::field_type($field, $post_id);
550 switch($grid){
551 case 'tab':
552 case 'table':
553 case 'both':
554 include( plugin_dir_path( __FILE__ ) . '/partials/cf7sg-field-load-script.php');
555 $default_script = false;
556 break;
557 default:
558 break;
559 }
560 return $default_script;
561 }
562 /**
563 * Register a [save] shortcode with CF7.
564 * Hooked on 'wpcf7_init'
565 * This function registers a callback function to expand the shortcode for the save button field.
566 * @since 2.0.0
567 */
568 public function register_cf7_shortcode() {
569 if( function_exists('wpcf7_add_form_tag') ) {
570 //dynamic select
571 wpcf7_add_form_tag(
572 array( 'dynamic_select', 'dynamic_select*' ),
573 array($this,'cf7_taxonomy_shortcode'),
574 true //has name
575 );
576 //benchmark
577 wpcf7_add_form_tag(
578 array( 'benchmark', 'benchmark*' ),
579 array($this,'cf7_benchmark_shortcode'),
580 true //has name
581 );
582 }
583 }
584 /**
585 * Register a [benchmark] shortcode with CF7.
586 * called by funciton above
587 * This function registers a callback function to expand the shortcode for the googleMap form fields.
588 * @since 1.0.0
589 * @param strng $tag the tag name designated in the tag help screen
590 * @return string a set of html fields to capture the googleMap information
591 */
592
593 public function cf7_benchmark_shortcode($tag){
594 $tag = new WPCF7_FormTag( $tag );
595 wp_enqueue_script('js-cf7sg-benchmarking');
596 wp_enqueue_style( 'cf7-benchmark-css' );
597 ob_start();
598 include( plugin_dir_path( __FILE__ ) . '/partials/cf7-benchmark-tag.php');
599 $html = ob_get_contents ();
600 ob_end_clean();
601 $this->update_form_classes('has-benchmark');
602 return $html;
603 }
604 /**
605 * Register a [dynamic_display] shortcode with CF7.
606 * called by funciton above
607 * This function registers a callback function to expand the shortcode for the googleMap form fields.
608 * @since 1.0.0
609 * @param strng $tag the tag name designated in the tag help screen
610 * @return string a set of html fields to capture the googleMap information
611 */
612
613 public function cf7_taxonomy_shortcode($tag){
614 $tag = new WPCF7_FormTag( $tag );
615 $source = self::get_dynamic_drodown_attributes($tag);
616 ob_start();
617 include( plugin_dir_path( __FILE__ ) . '/partials/cf7-taxonomy-tag-display.php');
618 $html = ob_get_contents ();
619 ob_end_clean();
620 return $html;
621 }
622 /**
623 *
624 *
625 * @since 1.0.0
626 * @param mixed $class array of classes or string to set class for form and dependency loading .
627 * @param Object $cf7_form cf7 post id against which set the c$lass .
628 **/
629 public function update_form_classes($class, $cf7_id=null){
630 if(empty($cf7_id)){
631 //get latest form
632 if(function_exists('wpcf7_get_current_contact_form')){
633 $cf7_form = wpcf7_get_current_contact_form();
634 if(!empty($cf7_form)) $cf7_id = $cf7_form->id();
635 }
636 }
637 if(empty($cf7_id)){
638 debug_msg('Unable to get Contact Form 7 ID to set class: '.$class);
639 return;
640 }
641 $classes = get_post_meta($cf7_id, '_cf7sg_classes', true);
642 if(empty($classes)){
643 $classes = array();
644 }
645 if(is_array($class)){
646 foreach($class as $class_name){
647 $classes[$class_name] = true;
648 }
649 }else{
650 $classes[$class] = true;
651 }
652 update_post_meta($cf7_id, '_cf7sg_classes', $classes);
653 return;
654 }
655 /**
656 * Function to save ajax submitted grid fields, grid fields are any input/select fields used in the table/tabs constructs
657 * 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.
658 * @since 1.0.0
659 **/
660 public function save_grid_fields(){
661 if( !isset($_POST['nonce']) ){
662 wp_send_json_error(array('message'=>'nonce failed'));
663 wp_die();
664 }
665 $cf7_id = sanitize_text_field($_POST['id']);
666
667 if(!wpcf7_verify_nonce($_POST['nonce'], $cf7_id)){
668 wp_send_json_error(array('message'=>'nonce failed'));
669 wp_die();
670 }
671
672 if(isset($_POST['tabs_fields']) && isset($_POST['table_fields'])){
673 $tabs_fields = json_decode(stripslashes($_POST['tabs_fields']));
674 if(empty($tabs_fields)) $tabs_fields = array();
675 if(!is_array($tabs_fields)) $tabs_fields = array($tabs_fields);
676 //validate each field name as text, sanitize.
677 $sanitised_tab_fields = array();
678 foreach($tabs_fields as $field){
679 $sanitised_tab_fields[] = sanitize_text_field($field);
680 }
681 $table_fields = json_decode(stripslashes($_POST['table_fields']));
682 if(empty($table_fields)) $table_fields = array();
683 if(!is_array($table_fields)) $table_fields = array($table_fields);
684 $sanitised_table_fields = array();
685 foreach($table_fields as $field){
686 $sanitised_table_fields[] = sanitize_text_field($field);
687 }
688 //debug_msg($grid_fields, $cf7_id);
689 update_post_meta($cf7_id, '_cf7sg_grid_tabs_names', $sanitised_tab_fields);
690 update_post_meta($cf7_id, '_cf7sg_grid_table_names', $sanitised_table_fields);
691 wp_send_json_success(array('message'=>'saved fields'));
692 }else{
693 wp_send_json_error(array('message'=>'no data received'));
694 }
695 wp_die();
696 }
697 /**
698 * This function filters submitted data and consolidates grid field values into arrays.
699 * Function hooked on 'wpcf7_posted_data'
700 * @since 1.0.0
701 * @param Array $data unvalidated submitted data.
702 * @return Array filtered submitted data.
703 **/
704 public function setup_grid_values($data){
705 $cf7form = WPCF7_ContactForm::get_current();
706 // debug_msg($_POST);
707 if(empty($cf7form) ){
708 if(isset($_POST['_wpcf7']) ){
709 $cf7_id = $_POST['_wpcf7'];
710 $cf7form = WPCF7_ContactForm::get_instance($cf7_id);
711 if(empty($cf7form) ){
712 debug_msg("CF7SG ERROR: fn setup_grid_values() is unable to load submitted form");
713 return $data;
714 }
715 }
716 }
717 $cf7_id = $cf7form->id();
718 $this->form_id = $cf7_id;
719 //debug_msg($grid_fields, 'grid fields...');
720 $tags = $cf7form->scan_form_tags();
721 foreach($tags as $tag){
722 if(empty($tag->name)) continue;
723 $field_name = $tag['name'];
724 $field_type = self::field_type($field_name, $cf7_id);
725 switch($field_type){
726 case 'tab':
727 case 'table':
728 case 'both':
729 // the $data array is passed by reference and will be consolidated.
730 /** @since 2.5.0 */
731 if( isset($_POST['_cf7sg_version']) && version_compare($_POST['_cf7sg_version'],'2.5.0','>=') ){
732 $this->consolidate_grid_submissions_v2($tag, $field_type, $data);
733 }else{
734 $this->consolidate_grid_submissions($field_name, $field_type, $data);
735 }
736 break;
737 default:
738 $toggle = $this->get_toggle($field_name);
739 if(!empty($toggle)){
740 $data[$field_name] = $this->is_submitted($toggle) ? $this->get_field_value($field_name,$field_type, $tag) : null;
741 }else $data[$field_name]= $this->get_field_value($field_name, $tag['basetype'], $tag);
742
743 break;
744 }
745 }
746 //add toggled sections as submitted values.
747 $groups = get_post_meta($this->form_id, '_cf7sg_grid_grouped_toggles', true);
748
749 foreach($groups as $group=>$grp_toggles){
750 foreach($grp_toggles as $toggle){
751 if( isset(self::$array_toggled_panels[$this->form_id][$toggle]) ){
752 self::$array_toggled_panels[$this->form_id][$group]=self::$array_toggled_panels[$this->form_id][$toggle];
753 break;
754 }
755 }
756 }
757 $data += self::$array_toggled_panels[$this->form_id];
758 // debug_msg($data, 'consolidated + submitted: ');
759 // $this->submitted_data[$this->form_id] = $data;
760 return $data;
761 }
762 /**
763 * 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.
764 *
765 *@since 2.5.0
766 *@param array $field_tag a cf7 form field tag which is part of tabs or table grid section.
767 *@param array $type field type, should be 'tab'/'table'/'both'.
768 *@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.
769 *@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.
770 */
771 private function consolidate_grid_submissions_v2($field_tag, $type, &$data){
772 //get_post_meta($post_id, '_cf7sg_has_tables', true);
773 if(isset($_POST['_wpcf7']) ) $cf7_id = $_POST['_wpcf7'];
774 else{
775 debug_msg("CF7SG ERROR: fn consolidate_grid_submissions_v2() is unable to load submitted form");
776 }
777 $is_used = false;
778 $field_name = $field_tag['name'];
779 $field_type = $field_tag['basetype'];
780 $origin = self::$array_grid_fields[$cf7_id][$field_name][0];//array(origin,type);
781 // debug_msg($origin, "$field_name origin ");
782 $values = array();
783 $regex = '';
784 $submitted_fields=array();
785 $max_fields =0;
786 $purge_fields=array();
787 $toggle = $this->get_toggle($field_name);
788 switch($type){
789 case 'tab':
790 case 'table':
791 $index_suffix = ('table'==$type)?'_row-':'_tab-';
792
793 //find the number of rows submitted.
794 $max_fields = isset($_POST[$origin]) ? $_POST[$origin]:0;
795
796 if(!empty($toggle) && !$this->is_submitted($toggle)){ //check if submitted.
797 $values[''] = null;
798 }else{
799 $values['']= $this->get_field_value($field_name,$field_type, $field_tag);
800 $is_used = true;
801 }
802
803 for($idx=1;$idx<$max_fields;$idx++){
804 $fid=$index_suffix.$idx;
805 $toggle_id = $this->is_tabbed($toggle[0]) ? preg_filter('/$/',$fid, $toggle) : $toggle;
806 if(!empty($toggle) && !$this->is_submitted($toggle_id)){ //check if submitted.
807 $values[$fid] = null;
808 }else{
809 $values[$fid] = $this->get_field_value($field_name.$fid,$field_type, $field_tag);
810 $is_used = true;
811 }
812 $purge_fields[$field_name.$fid] = true;
813 }
814 break;
815 case 'both':
816 $origins = explode(':', $origin);
817 $table_origin = $origins[0];
818 $tab_origin = $origins[1];
819 $max_tabs = isset($_POST[$tab_origin])?$_POST[$tab_origin]:0;
820 $values[''] = array(); //init multi-dimensional array
821 // not required since it is set in the for loop below.
822 // if(!empty($toggle) && !$this->is_submitted($toggle)){ //check if submitted, tab<-toggle<-table.
823 // $values[''][''] = null;
824 // }else{
825 // $values[''][''] = $this->get_field_value($field_name,$field_type,$field_tag);
826 // }
827 for($idx=0;$idx<$max_tabs;$idx++){ //tables in other tabs.
828 $max_fields = 0;
829 if(0==$idx && isset($_POST[$table_origin])){
830 $max_fields = $_POST[$table_origin];
831 }else if($idx>0 && isset($_POST[$table_origin.'_tab-'.$idx])){
832 $max_fields = $_POST[$table_origin.'_tab-'.$idx];
833 }
834 $tab_suffix= ($idx>0)? '_tab-'.$idx:'';
835 for($jdx=0;$jdx<$max_fields;$jdx++){
836 $row_suffix= ($jdx>0)?'_row-'.$jdx:'';
837 $fid = $tab_suffix.$row_suffix;
838 $toggle_id = $this->is_tabbed($toggle[0]) ? preg_filter('/$/',$tab_suffix,$toggle) : $toggle;
839
840 if(!empty($toggle) && !$this->is_submitted($toggle_id)){ //check if submitted, tab<-toggle<-table.
841 $values[$tab_suffix][$row_suffix] = null;
842 }else{
843 $values[$tab_suffix][$row_suffix] = $this->get_field_value($field_name.$fid,$field_type, $field_tag);
844 $is_used = true;
845 }
846 $purge_fields[$field_name.$fid] = true; //remove from the original $data.
847 }
848 }
849 break;
850 }
851 $purge_fields[$field_name] = true;
852 //debug_msg($purge_fields, 'purging ');
853 $data = array_udiff_uassoc($data, $purge_fields, function($a, $b){return 0;}, function($a, $b){
854 if ($a === $b) return 0;
855 return ($a > $b)? 1:-1;
856 }); //this will remove all the surplus fields
857 if( $is_used ) $data[$field_name] = $values;
858 else
859 return;
860 }
861 /**
862 * Extract either a file name or a field value
863 *
864 *@since 2.5.0
865 *@param array $data submitted data.
866 *@return string text_description
867 */
868 private function get_field_value($field_name, $field_type, $field_tag){
869 $value = '';
870 if('file' == $field_type) {
871 $value = isset($_FILES[$field_name]) ? $_FILES[$field_name]['name']:'';
872 }else{
873 $pipes = $field_tag->pipes;
874 $value = isset($_POST[$field_name]) ? $_POST[$field_name]:'';
875
876 if ( WPCF7_USE_PIPE and $pipes instanceof WPCF7_Pipes and ! $pipes->zero() and !in_array( $field_type, array('map','dynamic_select')) ){
877 if(is_array($value)){
878 $piped = array();
879 foreach($value as $v){
880 $piped[] = $pipes->do_pipe($v);
881 }
882 $value = $piped;
883 }else $value = $pipes->do_pipe($value);
884 }
885 }
886 return $value;
887 }
888 /**
889 * function returns an array of fields as keys and value which are eitehr 'tab' or 'table', or 'both'.
890 *
891 * @since 1.0.0
892 * @param string $form_id form id for which to return fields.
893 * @return array empty array if no fields found..
894 **/
895 static public function get_grid_fields($form_id){
896 if(isset(self::$array_grid_fields[$form_id])){
897 return self::$array_grid_fields[$form_id];
898 }
899 $grid_fields = array();
900 //tables
901 $fields = get_post_meta($form_id , '_cf7sg_grid_table_names', true);
902 if(!empty($fields)){
903 if( is_array( $fields[key($fields)] ) ){
904 foreach($fields as $table=>$table_fields) $grid_fields += array_fill_keys($table_fields, array($table,'table'));
905 }else $grid_fields += array_fill_keys($fields, array('cf7-sg-table-000','table'));
906 }
907 //tabs
908 $fields = get_post_meta($form_id , '_cf7sg_grid_tabs_names', true);
909 if(!empty($fields)){
910 if(is_array( $fields[key( $fields)] ) ){ /** @since 2.4.2 */
911 foreach($fields as $tab=>$tab_fields){
912 foreach($tab_fields as $field){
913 if(isset($grid_fields[$field])){
914 $grid_fields[$field] = array($grid_fields[$field][0].':'.$tab,'both');
915 }else $grid_fields[$field] = array($tab,'tab');
916 }
917 }
918 }else{
919 foreach($fields as $field){
920 if(isset($grid_fields[$field])) $grid_fields[$field] = array($grid_fields[$field][0].':cf7-sg-tab-000','both');
921 else $grid_fields[$field] = array('cf7-sg-tab-000','tab');
922 }
923 }
924 }
925 $subform_keys = get_post_meta($form_id, '_cf7sg_sub_forms', true);
926 if(!empty($subform_keys)){
927 foreach($subform_keys as $cf7Key){
928 $post_id = get_cf7form_id($cf7Key);
929 $grid_fields += self::get_grid_fields($post_id);
930 }
931 }
932 self::$array_grid_fields[$form_id]=$grid_fields;
933 //load panel fields.
934 if( isset($_POST['_cf7sg_version']) && version_compare($_POST['_cf7sg_version'],'2.5.0','>=') ){
935 self::$array_toggled_panels[$form_id]=array();
936 $toggled_panels = array();
937 if(isset($_POST['_cf7sg_toggles'])){
938 $toggled_panels = json_decode( stripslashes($_POST['_cf7sg_toggles']));
939 if(!empty($toggled_panels)) $toggled_panels = get_object_vars($toggled_panels);
940 else $toggled_panels = array();
941 }
942 self::$array_toggled_panels[$form_id]=$toggled_panels;
943
944 $fields = get_post_meta($form_id, '_cf7sg_grid_toggled_names', true);
945 self::$array_toggle_fields[$form_id]=array();
946 if(!empty($fields)){
947 foreach($fields as $panel=>$pfields){
948 foreach($pfields as $field){
949 if( isset(self::$array_toggle_fields[$form_id][$field]) ){
950 self::$array_toggle_fields[$form_id][$field][] = $panel;
951 }else{
952 self::$array_toggle_fields[$form_id][$field]= array($panel);
953 }
954 }
955 }
956 }
957 if(version_compare($_POST['_cf7sg_version'],'4.0.0','>=')){ //track tabbed toggles.
958 $tabtgls = get_post_meta($form_id, '_cf7sg_grid_tabbed_toggles', true);
959 if(!empty($tabtgls)) $tabtgls = array_fill_keys($tabtgls,true);
960 else $tabtgls = array();
961 self::$array_tabbed_toggles[$form_id]=$tabtgls;
962 }
963 }
964 return $grid_fields;
965 }
966 /**
967 * Consolidates submitted data from table and tab fields into arrays.
968 *
969 *@since 1.0.0
970 *@param string $field_name a cf7 form field name which is part of tabs or table grid section.
971 *@param array $type field type, should be 'tab'/'table'/'both'.
972 *@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.
973 *@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.
974 */
975 private function consolidate_grid_submissions($field_name, $type, &$data){
976 $cf7_key = '';
977 if(isset($_POST['_wpcf7_key'])) $cf7_key = $_POST['_wpcf7_key'];
978 $cf7_id = 0;
979 if(isset($_POST['_wpcf7'])) $cf7_id = $_POST['_wpcf7'];
980
981 $values = array();
982 $regex = '';
983 $submitted_fields=array();
984 $max_fields =0;
985 $purge_fields=array();
986 switch($type){
987 case 'table':
988 $index_suffix = '_row-';
989 $regex = '/^'.preg_quote($field_name).'_row-[0-9]+$/';
990 $values['']=$data[$field_name];
991 //extract all relevant fields
992 $submitted_fields = preg_grep($regex, array_keys($data));
993 $max_fields = sizeof($submitted_fields);
994 break;
995 case 'tab':
996 $index_suffix = '_tab-';
997 $regex = '/^'.preg_quote($field_name).'_tab-[0-9]+$/';
998 $values['']=$data[$field_name];
999 //extract all relevant fields
1000 $submitted_fields = preg_grep($regex, array_keys($data));
1001 foreach($submitted_fields as $field){
1002 $idx = 1.0 * str_replace($field_name.'_tab-', '', $field);
1003 if($max_fields < $idx) $max_fields = $idx;
1004 }
1005 break;
1006 case 'both':
1007 $regex = '/^'.preg_quote($field_name);
1008 $values[''] = array(); //init multi-dimensional array
1009 if(isset($data[$field_name])){
1010 $values[''][''] = $data[$field_name];
1011 }else{
1012 $values[''][''] = null;
1013 }
1014 //extract all relevant fields
1015 $submitted_fields = preg_grep($regex.'_tab-[0-9]+_row-[0-9]+$/', array_keys($data));
1016 //we also need the rows with tab index=0
1017 $submitted_fields += preg_grep($regex.'_row-[0-9]+$/', array_keys($data));
1018 //.. and tabs with row index=0
1019 $submitted_fields += preg_grep($regex.'_tab-[0-9]+$/', array_keys($data));
1020 //if('other-rm-qty'==$field_name){
1021 // debug_msg($submitted_fields, 'Found fields '.$field_name);
1022 //}
1023 $max_fields = sizeof($submitted_fields);
1024 break;
1025 }
1026
1027 $row_idx=1; //[0][0] already set above is this is tab table field
1028 $tab_idx=0;
1029 $error_loop=false;
1030 $loop_counter_limit = apply_filters('cf7sg_set_max_tabs_limit', 10, $cf7_key, $cf7_id);
1031 for($idx=1; ($idx <= $max_fields && !$error_loop); $idx++){
1032 switch($type){
1033 case 'table':
1034 case 'tab':
1035 if(!isset($data[$field_name.$index_suffix.$idx])){
1036 /**
1037 *assuming this field was not submitted as it was disabled, hence set to null.
1038 * @since 1.0.0
1039 */
1040 $values[$index_suffix.$idx] = null;
1041 }else{
1042 $values[$index_suffix.$idx] = $data[$field_name.$index_suffix.$idx];
1043 $purge_fields[$field_name.$index_suffix.$idx] = true;
1044 }
1045 break;
1046 case 'both':
1047 //first attempt to look for the frist tab rows
1048 $row_suffix = ($row_idx>0)?'_row-'.$row_idx:'';
1049 $tab_suffix = ($tab_idx>0)?'_tab-'.$tab_idx:'';
1050 //if('other-rm-qty'==$field_name){
1051 // debug_msg($idx.'Looking for: '.$field_name.$tab_suffix.$row_suffix);
1052 //}
1053 if(isset($data[$field_name.$tab_suffix.$row_suffix])){
1054 $values[$tab_suffix][$row_suffix] = $data[$field_name.$tab_suffix.$row_suffix];
1055 $purge_fields[$field_name.$tab_suffix.$row_suffix] = true;
1056 //next loop, look for the next row within the same tab,
1057 $row_idx +=1;
1058 }else{
1059
1060 $loop_counter=$tab_idx;
1061 $loop = true;
1062 do{//move to next tab, and reset the row counter
1063 // debug_msg('loop:'.$loop_counter.', max: '.$loop_counter_limit);
1064 $loop_counter +=1;
1065 if($loop_counter > $loop_counter_limit) $error_loop=true;
1066 $tab_idx +=1;
1067 $row_idx = 0;
1068 $row_suffix = '';
1069 $tab_suffix = '_tab-'.$tab_idx;
1070 //init new tab row values array
1071 $values[$tab_suffix] = array();
1072 //keep looking for next tab if we don't find a value;
1073 /**
1074 *if the frist row is not submitted then this may be in a toggled section which has been disabled.
1075 * but we still need to keep looking for values in other tabs.
1076 *@since 1.1.0
1077 */
1078 $values[$tab_suffix][$row_suffix] = null;
1079 $loop = !isset($data[$field_name.$tab_suffix.$row_suffix]) && !$error_loop;
1080 }while($loop);
1081 if(!$error_loop) {
1082 $values[$tab_suffix][$row_suffix] = $data[$field_name.$tab_suffix.$row_suffix];
1083 $purge_fields[$field_name.$tab_suffix.$row_suffix] = true;
1084 $row_idx +=1; //next loop, keep searching in thsi tab.
1085 }else{
1086 debug_msg($submitted_fields, 'CF7SG ERROR: Reached max tabs search loop for table field '.$field_name.' (cannot find any new rows above tab '.$tab_idx.' therefor abandoning search for remaining '.(sizeof($submitted_fields)-$idx).' regex match in preg_grep result array listed below)');
1087 //for loop will end here
1088 }
1089 }
1090 break;
1091 }//end switch.
1092 }//end for loop preg_grep array.
1093 if(!$error_loop){
1094 //debug_msg($purge_fields, 'purging ');
1095 //debug_msg($data);
1096 $data = array_udiff_uassoc($data, $purge_fields, function($a, $b){return 0;}, function($a, $b){
1097 if ($a === $b) return 0;
1098 return ($a > $b)? 1:-1;
1099 }); //this will remove all the surplus fields
1100 $data[$field_name] = $values;
1101 }
1102 return $values;
1103 }
1104 /**
1105 * Validates required benchmark and dynamic_select tags
1106 *
1107 * @since 1.0.0
1108 * @param WPCF7_Validation $result validation object
1109 * @param Array $tag an array of cf7 tag attributes and values
1110 * @return WPCF7_Validation validation result
1111 **/
1112 public function validate_required($result, $tag){
1113 if(!isset( $_POST[$tag->name] ) ){
1114 return $result; //not submitted hence disabled.
1115 }
1116 $tag = new WPCF7_FormTag( $tag );
1117
1118 $name = $tag->name;
1119 $value = isset( $_POST[$name] )
1120 ? trim( strtr( (string) sanitize_text_field($_POST[$name]), "\n", " " ) ): '';
1121
1122 if ( $tag->is_required() && '' == $value ) {
1123 $result->invalidate( $tag, wpcf7_get_message( 'invalid_required' ) );
1124 }
1125 return $result;
1126 }
1127
1128 /**
1129 * Final validation with all values submitted for inter dependent validation
1130 * Hooked to filter 'wpcf7_validate', sets up the final $result object
1131 * @since 1.0.0
1132 * @param WPCF7_Validation $result validation object
1133 * @param Array $tags an array of cf7 tag used in this form
1134 * @return WPCF7_Validation validation result
1135 **/
1136 public function filter_wpcf7_validate($result, $tags){
1137 /** @since 3.3.3 fix for captch field validation*/
1138 $invalids = $result->get_invalid_fields();
1139 /**
1140 *@since 1.1.0
1141 *reset the validation result.
1142 * this is required to dynamically disable required form fields & stop their validation.
1143 * 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.
1144 * this bug was reported in the cf7 support forum:
1145 * https://wordpress.org/support/topic/bug-javascript-disabled-fields-flagged-as-invalid/
1146 **/
1147 $result = new WPCF7_Validation();
1148
1149 //rebuild the default validation result.
1150 $cf7form = WPCF7_ContactForm::get_current();
1151 $tags = $cf7form->scan_form_tags();
1152 $submitted = WPCF7_Submission::get_instance();
1153 $data = $submitted->get_posted_data();
1154 $tag_types = array(); //store all form tags, including cloned tags for array fields.
1155
1156 /** @since 3.1.3 allow validation of unvalidated data in custom validation filter */
1157 $validation = array(); //holds all the validation messages.
1158 $validated = array();
1159 $submission = array();
1160 /**
1161 *@since 2.1.0 fix issue with Conditional Field plugin.
1162 */
1163 $data = $this->remove_hidden_fields_from_conditional_plugin($data);
1164 $toggle_status = '';
1165 if(isset($_POST['_cf7sg_toggles'])){
1166 $toggle_status = json_decode( stripslashes($_POST['_cf7sg_toggles']));
1167 }
1168 /** @since 2.5.0 new data consolidation enables simpler validation */
1169 $deprecated = true; //check if the form being submitted was created with an older version of the plugin.
1170 if( isset($_POST['_cf7sg_version']) && version_compare($_POST['_cf7sg_version'],'2.5.0','>=') ){
1171 $deprecated = false;
1172 }
1173 foreach ( $tags as $tag ) {
1174 //debug_msg($data);
1175 if($deprecated){
1176 /**
1177 * @since 2.1.5 fix validation of non-toggled checkox/radio. Toggled fields are now tracked in the tag itself as a class.
1178 */
1179 if(!isset($_POST[$tag['name']])){
1180 $isRequired = false;
1181 switch($tag['type']){
1182 case 'checkbox*':
1183 case 'radio':
1184 case 'file*': /** @since 2.3.1 fix as file field has no values in $_POST.*/
1185 $isRequired = true;
1186 $tag_options = $tag->options;
1187 if(empty($tag_options)) break; //break from switch, not toggled, we need to validate.
1188 $toggle='';
1189 foreach($tag_options as $option){
1190 $match = array();
1191 preg_match('/class:cf7sg-toggle-(.[^\s]+)/i',$option, $match);
1192 if(!empty($match)){
1193 $toggle=$match[1];
1194 break; //break fromeach loop.
1195 }
1196 }
1197 if(empty($toggle)) break; //break from switch, not toggled, we need to validate.
1198 //check if the toggle is open. only open toggles are registered.
1199 if(!empty($toggle_status) && property_exists($toggle_status, $toggle)) break; //break from switch, is toggled and in use, we need to validate.
1200 //if we reached here, then the checkbox/radio is toggled and not in use, so do not validate.
1201 $isRequired = false;
1202 break;
1203 }
1204 if(!$isRequired) continue;//not submitted==disabled, or not used.
1205 }
1206 }
1207 /**
1208 *@since 1.9.0 fix issue with Conditional Field plugin.
1209 */
1210 //validation for non-deprecated plugins (v2.5.0+).
1211 if(!isset($data[$tag['name']])) continue; //it was removed by some plugin.
1212 $type = $tag['type'];
1213 //check to see if this field is an array (table or tab or both).
1214 $tag_types[$tag['name']] = $tag['type'];
1215 $field_type = self::field_type($tag['name'], $cf7form->id());
1216 switch($field_type){
1217 case 'tab':
1218 case 'table':
1219 case 'both':
1220 // the $data array is passed by reference and will be consolidated.
1221 $values = $data[$tag['name']];
1222 /** @since 3.1.3 track all validations */
1223 $validation[$tag['name']] = array();
1224 $validated[$tag['name']] = array();
1225 $submission[$tag['name']] = array();
1226 $idx = 0;
1227 foreach($values as $index=>$value){
1228 if(is_array($value) && 'both'==$field_type){
1229 $validation[$tag['name']][$idx]=array();
1230 $validated[$tag['name']][$idx] = array();
1231 $submission[$tag['name']][$idx] = array();
1232 $rdx = 0;
1233 foreach($value as $row=>$row_value){
1234 if($deprecated && !isset($_POST[$tag['name'].$index.$row])) continue;
1235 elseif(!isset($row_value)) continue;
1236 $sg_field_tag = clone $tag;
1237 $sg_field_tag['name'] = $tag['name'].$index.$row;
1238 $result = apply_filters("wpcf7_validate_{$tag['type']}", $result, $sg_field_tag);
1239 /** @since 3.1.3 */
1240 $validation[$tag['name']][$idx][$rdx] = $this->strip_cf7_validation($result, $sg_field_tag->name);
1241 $validated[$tag['name']][$idx][$rdx] = $sg_field_tag;
1242 $submission[$tag['name']][$idx][$rdx] = $this->get_submitted_data($tag['name'], $type, $row_value);
1243 $rdx++;
1244 }
1245 }else{
1246 if($deprecated && !isset($_POST[$tag['name'].$index])) continue;
1247 elseif(!isset($value)) continue;
1248 $sg_field_tag = clone $tag;
1249 $sg_field_tag['name'] = $tag['name'].$index;
1250 $result= apply_filters("wpcf7_validate_{$tag['type']}", $result, $sg_field_tag);
1251 /** @since 3.1.3 */
1252 $validation[$tag['name']][$idx] = $this->strip_cf7_validation($result, $sg_field_tag->name);
1253 $validated[$tag['name']][$idx] = $sg_field_tag;
1254 $submission[$tag['name']][$idx] = $this->get_submitted_data($tag['name'], $type, $value);
1255 }
1256 $idx++;
1257 }
1258 // debug_msg($values, 'values '.$tag['name'].'....');
1259 // debug_msg($result, 'validation '.$tag['name'].'....');
1260 if($deprecated){ //if deprecated no need to bother with validation/validated arrays.
1261 /** @since 2.3.1 fix the files tag validation for files in tabs/tables*/
1262 switch($type){
1263 case 'file':
1264 case 'file*':
1265 // Search for $_FILES where name match a tabbed file field
1266 $regex = '/^'.preg_quote($tag['name']);
1267 $submitted_fields = preg_grep($regex.'$/', array_keys($_FILES));
1268 //extract all relevant fields
1269 $submitted_fields += preg_grep($regex.'_tab-[0-9]+_row-[0-9]+$/', array_keys($_FILES));
1270 //we also need the rows with tab index=0
1271 $row_fields = preg_grep($regex.'_row-[0-9]+$/', array_keys($_FILES));
1272 //.. and tabs with row index=0
1273 $submitted_fields += preg_grep($regex.'_tab-[0-9]+$/', array_keys($_FILES));
1274 foreach($submitted_fields as $index => $field){
1275 // For each tabbed file, call the filter validate to add uploaded_files
1276 $sg_field_tag = clone $tag;
1277 $sg_field_tag['name'] = $field;
1278 $result = apply_filters("wpcf7_validate_{$sg_field_tag['type']}", $result, $sg_field_tag);
1279 }
1280 }
1281 }
1282 break;
1283 default:
1284 switch($type) {
1285 case 'captchar': //cannot be called twice, hence see if already invalidated.
1286 /** @since 3.3.3 check if invalidated previously */
1287 if( isset($invalids[$tag['name']]) ){
1288 $result->invalidate( $tag, $invalids[$tag['name']]['reason']);
1289 $validation[$tag['name']] = $invalids[$tag['name']]['reason'];
1290 $validated[$tag['name']] = $tag;
1291 }
1292 break;
1293 default:
1294 //if(!isset($data[$tag['name']])) continue 2; /*likely toggled and unused*/
1295 $result= apply_filters( "wpcf7_validate_{$type}", $result, $tag );
1296 /** @since 3.1.3 */
1297 $validation[$tag['name']] = $this->strip_cf7_validation($result, $tag['name']);
1298 $validated[$tag['name']] = $tag;
1299 $submission[$tag['name']] = $this->get_submitted_data($tag['name'], $type, $data[$tag['name']]);
1300 break;
1301 }
1302 break;
1303 }
1304 }
1305 $form_key = '';
1306 if(isset($_POST['_wpcf7_key'])){
1307 $form_key = $_POST['_wpcf7_key'];
1308 }
1309
1310 //allow for more complex validation.
1311 if(has_filter('cf7sg_validate_submission')){
1312 /**
1313 * filter to validate the entire submission and check interdependency of fields
1314 * @since 1.0.0
1315 * @param Array $validation an array of $field_name=>$validaton_message.
1316 * @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.
1317 * @param String $form_key unique form key to identify current form.
1318 * @param int $form_id cf7 form post ID.
1319 * @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
1320 */
1321 $validation = apply_filters('cf7sg_validate_submission', $validation, $submission, $form_key, $_POST['_wpcf7']);
1322
1323 //now that the user has filtered the validatoin, reset the in cf7 inalids.
1324 $result = new WPCF7_Validation();
1325
1326 if(!empty($validation)){
1327 foreach($validation as $name=>$msg){
1328 switch(true){
1329 case is_array($msg):
1330 foreach($msg as $idx=>$value){
1331 switch(true){
1332 case is_array($value):
1333 foreach($value as $rdx=>$message){
1334 if(empty($message)) continue;
1335 $result->invalidate($validated[$name][$idx][$rdx], $message);
1336 }
1337 break;
1338 case is_array($validated[$name][$idx]): //error, expecting array..
1339 debug_msg('Filtered cf7sg_validate_submission validation ERROR, expecting array for table field within tab: '.$name.'['.$idx.']');
1340 break;
1341 case empty($value): //no message, just continue.
1342 break;
1343 default:
1344 $result->invalidate($validated[$name][$idx], $value);
1345 break;
1346 }
1347 }
1348 break;
1349 case is_array($validated[$name]): //error, we should have an array.
1350 debug_msg('Filtered cf7sg_validate_submission validation ERROR, expecting array for field '.$name);
1351 break;
1352 case empty($msg): //no message, just continue.
1353 break;
1354 default:
1355 $result->invalidate($validated[$name], $msg);
1356 break;
1357 }
1358 }
1359 }
1360 }
1361 return $result;
1362 }
1363 /**
1364 * Function to strip the submitted data for a given field.
1365 * Handles base type files whose data is stored in the $_FILES global array.
1366 *@since 3.1.3
1367 *@param string $name field name.
1368 *@param string $type base type.
1369 *@param mixed $data $_POST data.
1370 *@return mixed value actually submitted.
1371 */
1372 public function get_submitted_data($name, $type, $data){
1373 $value = $data;
1374 switch($type){
1375 case 'file':
1376 case 'file*':
1377 $value = array();
1378 if(isset($_FILES[$name])) $value = $_FILES[$name];
1379 break;
1380 }
1381 return $value;
1382 }
1383 /**
1384 * This function strips the validation message from cf7 WPCF7_Validation object for storage and custom modification using the hook 'cf7sg_validate_submission'.
1385 *
1386 *@since 3.1.3
1387 *@param WPCF7_Validation $result cf7 validation object for the field being validated.
1388 *@param WPCF7_FormTag $tag cf7 tag object for the field being validated.
1389 *@return string text_description
1390 */
1391 protected function strip_cf7_validation($result, $name){
1392 $invalid = $result->get_invalid_fields();
1393 $msg = '';
1394 if( !empty($invalid) && isset($invalid[$name]['reason']) ){
1395 $msg = $invalid[$name]['reason'];
1396 }
1397 return $msg;
1398 }
1399 /**
1400 * Function to save toggled collapsible sections status to open them up again for draft forms.
1401 * Hooks action 'cf7_2_post_form_posted'
1402 * @since 1.0.0
1403 * @param string $key form unique key.
1404 * @param Array $submitted_data array of field-name=>value pairs submitted in form
1405 *@since 1.1.0
1406 *@param string $param text_description
1407 *@return string text_description
1408 */
1409 public function save_toggle_status($post_id, $key, $post_fields, $post_meta_fields, $submitted_data){
1410 if(isset($submitted_data['_cf7sg_toggles'])){
1411 $toggles = json_decode( stripslashes( $submitted_data['_cf7sg_toggles'] ) );
1412 $toggles_array = array();
1413 if(!empty($toggles)){
1414 foreach($toggles as $key=>$value){
1415 $toggles_array[$key] = sanitize_text_field($value);
1416 }
1417 }
1418 //debug_msg($toggles_array, 'toggles status saved, ');
1419 update_post_meta($post_id, 'cf7sg_toggles_status', $toggles_array);
1420 }
1421 }
1422 /**
1423 * Function to save custom options values added to tagged select2 fields.
1424 * Hooks action 'cf7_2_post_form_posted'
1425 * @since 1.0.0
1426 * @param string $key form unique key.
1427 * @param Array $submitted_data array of field-name=>value pairs submitted in form
1428 **/
1429 public function save_select2_custom_options($post_id, $key, $post_fields, $post_meta_fields, $submitted_data){
1430 $form_id = get_cf7form_id($key);
1431 $tagged_fields = get_post_meta($form_id, '_cf7sg_select2_tagged_fields', true);
1432 if(empty($tagged_fields)){
1433 return;
1434 }
1435
1436 foreach($tagged_fields as $field_name=>$source){
1437 $value = $submitted_data[$field_name];
1438 $is_array = true;
1439 if(!is_array($value)){
1440 $value = array($value);
1441 $is_array = false;
1442 }
1443 switch($source['source']){
1444 case 'taxonomy':
1445 $taxonomy = $source['taxonomy'];
1446 $idx=0;
1447 foreach($value as $term){
1448 if(!term_exists($term, $taxonomy)){
1449 /**
1450 * Filter custom options from tag enabled select2 dynamic-dropdown fields
1451 * where the source of options come from taxonomy terms. Filter is fired when a new value is submitted. The pluign inserts a new term by default as per submitted value.
1452 * @param string $term the new term submitted by the user.
1453 * @param string $field_name the name of the form field.
1454 * @param string $taxonomy the taxonomy to which this is added.
1455 * @param array $submitted_data array of other submitted $field=>$value pairs.
1456 * @param string $key the form unique key.
1457 * @return string the new term name to insert.
1458 * @since 2.0.0
1459 */
1460 $term = apply_filters('cf7sg_dynamic_dropdown_new_term', $term, $field_name, $taxonomy, $submitted_data, $key);
1461 $new_term = wp_insert_term($term, $taxonomy);
1462 if(!is_wp_error($new_term)){
1463 $new_term = get_term($new_term['term_id'], $taxonomy);
1464 $value[$idx] = $new_term->slug;
1465 }
1466 }
1467 $idx++;
1468 }
1469 break;
1470 case 'post':
1471 $args = array(
1472 'post_type' => $source['post'],
1473 'post_status' => 'publish',
1474 'posts_per_page' => -1
1475 );
1476 if(!empty($source['taxonomy'])){
1477 $tax = array();
1478 if(sizeof($source['taxonomy']) > 1){
1479 $tax['relation'] = 'AND';
1480 }
1481 foreach($source['taxonomy'] as $term => $taxonomy){
1482 $tax[]=array(
1483 'taxonomy' => $taxonomy,
1484 'field' => 'slug',
1485 'terms' => $term
1486 );
1487 }
1488 $args['tax_query'] = $tax;
1489 }
1490 $args = apply_filters('cf7sg_dynamic_dropdown_post_query', $args, $tag->name, $key);
1491 $posts = get_posts($args);
1492 $options = array();
1493 if(!empty($posts)){
1494 foreach($posts as $post){
1495 $options[$post->post_name] = $post->post_title;
1496 }
1497 }
1498 $idx=0;
1499 foreach($value as $slug){
1500 if(!isset($options[$slug])){
1501 $args = $source;
1502 $post_type = $source['post'];
1503 $title = $slug;
1504 $post_name = '';
1505 /**
1506 * Filter custom options from tag enabled select2 dynamic-dropdown fields
1507 * where the source of options come from post titles. Filter is fired when a new value is submitted.
1508 * This plugin does not take any further action, ie no post of $post_type will be created. It is upto you to do so and return the slug of the newly created post.
1509 * @param string $post_name the new post slug.
1510 * @param string $field_name the name of the form field.
1511 * @param string $title new value being submitted for a new post title.
1512 * @param string $post_type the post type from which this dropdown was built
1513 * @param array $args an array of additional parameters that was set in the tag, for example the taxonomy and terms from which to filter the posts for the dynamic list.
1514 * @param array $submitted_data array of other submitted $field=>$value pairs.
1515 * @param string $key the form unique key.
1516 */
1517 $value[$idx] = apply_filters('cf7sg_dynamic_dropdown_new_post', $post_name, $field_name, $title, $post_type, $args, $submitted_data, $key);
1518 }
1519 $idx++;
1520 }
1521 break;
1522 case 'filter':
1523 $values = $value;
1524 /**
1525 * Filter custom otions from tag enabled select2 dynamic-dropdown fields
1526 * where the source of options come from a filter. Filter is fired when values are submitted.
1527 * Return updated values if any are custom values so that saved/draft submissions will reflect the correct value saved in the DB,
1528 * @param array $values an array submitted values (several values can be submitted in the case of a tabbed/table input field).
1529 * @param string $field_name the name of the form field.
1530 * @param array $submitted_data array of other submitted $field=>$value pairs.
1531 * @param string $key the form unique key.
1532 */
1533 $value = apply_filters('cf7sg_dynamic_dropdown_filter_select2_submission', $values, $field_name, $submitted_data, $key);
1534 break;
1535 }
1536 //Save the modified value, find which post field the field is mapped to
1537 if(!$is_array){
1538 $value = $value[0];
1539 }
1540 if( isset($post_fields[$field_name]) ){
1541 $post_key = $post_fields[$field_name];
1542 switch($post_key){
1543 case 'title':
1544 case 'author':
1545 case 'excerpt':
1546 $post_key = 'post_'.$post_key;
1547 break;
1548 case 'editor':
1549 $post_key ='post_content';
1550 break;
1551 case 'slug':
1552 $post_key ='post_name';
1553 break;
1554 }
1555 wp_update_post(array(
1556 'ID' => $post_id,
1557 $post_key => $value
1558 ));
1559 }else if( isset($post_meta_fields[$field_name]) ){
1560 update_post_meta($post_id, $post_meta_fields[$field_name], $value);
1561 }
1562 }
1563 }
1564 /**
1565 * Function to retrieve dynamic-dropdown attributes
1566 *
1567 * @since 1.0.0
1568 * @param WPCF7_FormTag $tag cf7 tag object of basetype dynamic_select.
1569 * @return array an array of attributes with 'source'=>[post|taxonomy|filter], .
1570 **/
1571 static public function get_dynamic_drodown_attributes($tag){
1572 if(is_array($tag) && isset($tag['basetype']) && 'dynamic_select' === $tag['basetype']){
1573 $tag = new WPCF7_FormTag($tag);
1574 }
1575 if( !($tag instanceof WPCF7_FormTag) || 'dynamic_select' !== $tag['basetype']){
1576 return false;
1577 }
1578 $source = array();
1579 foreach($tag->values as $values){
1580 if(0 === strpos($values, 'slug:') ){
1581 $source['source'] = "taxonomy";
1582 $source['taxonomy'] = str_replace('slug:', '', $values);
1583 }
1584 if(0 === strpos($values, 'source:post')){
1585 $source['source'] = "post";
1586 $source['post'] = str_replace('source:post:', '', $values);
1587 }
1588 if(0 === strpos($values, 'taxonomy:')){
1589 if(empty($source['taxonomy'])){
1590 $source['taxonomy'] = array();
1591 }
1592 $values = str_replace('taxonomy:', '', $values);
1593 $exp = explode(":", $values);
1594 if(!empty($exp) && is_array($exp)){
1595 $source['taxonomy'][$exp[1]] = $exp[0];
1596 }
1597 }
1598 if(0 === strpos($values, 'source:filter')){
1599 $source['source'] = "filter";
1600 }
1601 }
1602 return $source;
1603 }
1604 /**
1605 * Function to temporarily fix the conditional fields plugin issue.
1606 * this is is called by the vlidation function 'filter_wpcf7_validate' above.
1607 *@since 2.1
1608 *@param array $posted_data submitted data
1609 *@return array submitted data without fields that remained hidden.
1610 */
1611 private function remove_hidden_fields_from_conditional_plugin($posted_data){
1612 /*code taken from cf7cf.php file in cf7-conditional-fields*/
1613 if(!isset($_POST['_wpcf7cf_hidden_group_fields'])){
1614 return $posted_data;
1615 }
1616
1617 $hidden_fields = json_decode(stripslashes($_POST['_wpcf7cf_hidden_group_fields']));
1618
1619 if (is_array($hidden_fields) && count($hidden_fields) > 0) {
1620 foreach ($hidden_fields as $field) {
1621 $field = str_replace('[]', '', $field);
1622 // if (wpcf7cf_endswith($field, '[]')) {
1623 // $field = substr($field,0,strlen($field)-2);
1624 // }
1625 unset( $posted_data[$field] );
1626 }
1627 }
1628 return $posted_data;
1629 }
1630 /**
1631 * Filter mail tags which are table or tab fields.
1632 * Hooked on 'wpcf7_mail_tag_replaced'.
1633 *@since 2.1
1634 *@param string $replaced mail tag string to replace.
1635 *@param mixed $submitted value of submitted field from cf7 posted data..
1636 *@param boolea $html mail if mail body is using html.
1637 *@param WPCF7_MailTag $mail_tag mail tag object of field being replaced.
1638 *@return string replacement string.
1639 */
1640 public function filter_table_tab_mail_tag($replaced, $submitted, $html=false, $mail_tag=null ){
1641 $cf7form = WPCF7_ContactForm::get_current();
1642 $cf7form_key = get_cf7form_key($cf7form->id());
1643 $submitted_cf7 = WPCF7_Submission::get_instance();
1644 $submitted_data = array();
1645 if(!empty($submitted_cf7)) $submitted_data = $submitted_cf7->get_posted_data();
1646
1647 switch(true){
1648 case 0===strpos($mail_tag->field_name(), 'cf7sg-toggle-'):
1649 $toggle = str_replace('cf7sg-toggle-', '', $mail_tag->field_name());
1650 if(isset($submitted_data[$toggle])) $replaced = $submitted_data[$toggle];
1651 break;
1652 case 0===strpos($mail_tag->field_name(), 'cf7sg-form-'):
1653 $replaced ="";
1654 break;
1655 }
1656
1657 if(empty($mail_tag)) return $replaced;
1658
1659 $field_type = self::field_type($mail_tag->field_name(), $cf7form->id());
1660 $label = '';
1661 $build = false;
1662
1663 switch($field_type){
1664 case 'tab':
1665 case 'table':
1666 if($html){
1667 $filtered = apply_filters('cf7sg_mailtag_grid_fields', '', $mail_tag->field_name(), $submitted, $cf7form_key, false);
1668 if(!empty($filtered)){
1669 $replaced = $filtered;
1670 break;
1671 }
1672 }
1673 $label=('table'==$field_type)?'row':$field_type;
1674 if($html && is_array($submitted)) {
1675 $idx=0;
1676 $replaced='';
1677 foreach($submitted as $index=>$value){
1678 $idx++;
1679 if(is_array($value)) $value = implode(' | ', $value);
1680 $replaced .= '<div><label>'.$label.'('.$idx.'):</label><span>'.$value.'</span></div>';
1681 }
1682 }
1683 break;
1684 case 'both':
1685 if($html){
1686 $filtered = apply_filters('cf7sg_mailtag_grid_fields', '', $mail_tag->field_name(), $submitted, $cf7form_key, true);
1687 if(!empty($filtered)){
1688 $replaced = $filtered;
1689 break;
1690 }
1691 }
1692 // build table.
1693 $replaced = '';
1694 if( is_array($submitted)) {
1695 $tab = 0;
1696 foreach($submitted as $index=>$value){
1697 $tab++;
1698 $idx=0;
1699 if($html) $replaced .= '<div><span>tab('.$tab.')</span>&nbsp;';
1700 else $replaced .= 'tab('.$tab.') = ';
1701 if(is_array($value)){
1702 foreach($value as $row=>$row_value){
1703 $idx++;
1704 if(is_array($row_value)) $row_value = implode('|', $row_value);
1705 if($html) $replaced .='<div><label>row('.$idx.'):</label><span>' . $row_value . '</span></div>';
1706 else $replaced .= $row_value.',';
1707 }
1708 }
1709 if($html) $replaced .='</div>';
1710 else $replaced .= PHP_EOL;
1711 }
1712 }
1713 break;
1714 default: //general fix for cf7 mail tags.
1715 $tag = $mail_tag->corresponding_form_tag();
1716
1717 /**
1718 * Filter the value inserted in the mail tag.
1719 * @since 2.9.0.
1720 * @param string $replaced value to filter.
1721 * @param string $field_name name of the field being inserted in the mail.
1722 * @param array $submitted submitted form data.
1723 * @param string $form_key unique form key identifier.
1724 * @return string a value to replace.
1725 */
1726 if($tag instanceof WPCF7_FormTag){
1727 $replaced = apply_filters('cf7sg_mailtag_'.$tag->basetype, $replaced, $mail_tag->field_name(), $submitted_data, $cf7form_key);
1728 }
1729 /**
1730 * Filter the value inserted in the mail tag.
1731 * @since 3.1.6.
1732 * @param string $replaced value to filter.
1733 * @param string $field_name name of the field being inserted in the mail.
1734 * @param array $submitted submitted form data.
1735 * @param string $form_key unique form key identifier.
1736 * @return string a value to replace.
1737 */
1738 $replaced = apply_filters('cf7sg_mailtag_'.$mail_tag->field_name(), $replaced, $submitted_data, $cf7form_key);
1739 break;
1740 }
1741
1742 return $replaced;
1743 }
1744 /**
1745 * We need to add the missing attachments just before the mail is sent.
1746 * Hooked to filter 'wpcf7_mail_components', sets up the final $components object
1747 * @since 2.4.1
1748 * @param Array $components an array of mail parts
1749 * @param WPCF7_ContactForm $cf7form cf7 form object
1750 * @param WPCF7_Mail $cf7mail cf7 mail object
1751 * @return Array an array of mail parts
1752 */
1753 public function wpcf7_mail_components($components, $cf7form, $cf7mail) {
1754 //$tags = $cf7form->scan_form_tags();
1755 $submission = WPCF7_Submission::get_instance();
1756 $uploaded_files = $submission->uploaded_files();
1757 $cf7_mail = WPCF7_Mail::get_current();
1758 /** @since 2.5.6 fix issue with non-attached files */
1759 $template = $cf7_mail->get( 'attachments' );
1760 $row = $tab = null;
1761 $idx = 0;
1762 /** @since 2.8.1 fix for send pdf */
1763 $attachments = $components['attachments'];
1764 foreach ( (array) $uploaded_files as $name => $path ) {
1765 if ( false === strpos( $template, "[${name}]" ) ) continue; //not attached.
1766 $field_type = self::field_type($name, $cf7form->id());
1767 $data = $submission->get_posted_data($name);
1768 switch($field_type){
1769 case 'tab':
1770 case 'table':
1771 foreach($data as $field_idx=>$file_name){
1772 if(empty($file_name)) continue;
1773 $idx++;
1774 $row = ('table'==$field_type)? str_replace('_row-', '', $field_idx):null;
1775 $row = (isset($row) && empty($row))? 1:1+(int)$row;
1776 $tab = ('tab'==$field_type)? str_replace('_tab-', '', $field_idx):null;
1777 $tab = (isset($tab) && empty($tab))? 1:1+(int)$tab;
1778 $file = $uploaded_files[$name.$field_idx];
1779 if(!in_array($attachments,$file)) $attachments[] = $file;
1780 $file = explode('/',$file);
1781 $filename = $file[count($file)-1];
1782 $components['body'].= apply_filters('cf7sg_annotate_mail_attach_grid_files', '', $name, $row, $tab, $idx,$filename, $_POST['_wpcf7_key']);
1783 }
1784 break;
1785 case 'both':
1786 $row = $tab = 1;
1787 foreach($data as $field_tab=>$tab_files){
1788 $tab = str_replace('_tab-', '', $field_tab);
1789 $tab = empty($tab)?1:1+(int) $tab;
1790 foreach($tab_files as $field_row=>$file_name){
1791 if(empty($file_name)) continue;
1792 $idx++;
1793 $row = str_replace('_row-', '', $field_row);
1794 $file = $uploaded_files[$name.$field_tab.$field_row];
1795 if(!in_array($attachments,$file)) $attachments[] = $file;
1796 $file = explode('/',$file);
1797 $filename = $file[count($file)-1];
1798 $components['body'].= apply_filters('cf7sg_annotate_mail_attach_grid_files','', $name, $row, $tab, $idx,$filename, $_POST['_wpcf7_key']);
1799 }
1800 }
1801 break;
1802 default: //singular fields.
1803 if(empty($path)) continue 2;
1804 $idx++;
1805 if(!in_array($path, $attachments)) $attachments[] = $path;
1806 $path = explode('/',$path);
1807 $filename = $path[count($path)-1];
1808 $components['body'].= apply_filters('cf7sg_annotate_mail_attach_grid_files','', $name, null, null, $idx,$filename, $_POST['_wpcf7_key']);
1809 break;
1810 }
1811 }
1812 $components['attachments'] = $attachments;
1813 return $components;
1814 }
1815 /**
1816 * Track form field value submissions for preview forms.
1817 * Hooked to action 'wpcf7_before_send_mail'
1818 *@since 4.4.0
1819 *@param string $param text_description
1820 *@return string text_description
1821 */
1822 public function prefill_preview_forms($form){
1823 if(empty($form)) return;
1824 if( !isset($_POST['_cf7sg_preview']) ) return;
1825 $prefill = array();
1826 foreach( $form->scan_form_tags() as $tag){
1827 if( isset($_POST[$tag->name]) and !empty($_POST[$tag->name]) ){
1828 $prefill[$tag->name] = $_POST[$tag->name];
1829 }
1830 $prefill['_cf7sg_toggles'] = self::$array_toggled_panels[$form->id()];
1831 }
1832
1833 if(!empty($prefill)) setcookie('_cf7sg_'.$_POST['_wpcf7_key'], json_encode($prefill),0,'/');
1834 }
1835
1836 }
1837