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

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