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

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