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

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