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

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