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

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