PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 2.05.09
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v2.05.09
6.35 6.34 6.33.1 6.33 6.32.1 6.32 6.31 6.25 6.25.1 6.26 6.26.1 6.27 6.28 6.29 6.3 6.3.1 6.3.2 6.30 6.4 6.4.1 6.4.2 6.5 6.5.1 6.5.2 6.5.3 All 141 releases
formidable / classes / models / FrmFormAction.php

FrmFormAction.php in Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More 2.05.09, at classes/models/FrmFormAction.php

702 lines 21.4 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class FrmFormAction {
4
5 public $id_base; // Root id for all actions of this type.
6 public $name; // Name for this action type.
7 public $option_name;
8 public $action_options; // Option array passed to wp_register_sidebar_widget()
9 public $control_options; // Option array passed to wp_register_widget_control()
10
11 public $form_id; // The ID of the form to evaluate
12 public $number = false; // Unique ID number of the current instance.
13 public $id = ''; // Unique ID string of the current instance (id_base-number)
14 public $updated = false; // Set true when we update the data after a POST submit - makes sure we don't do it twice.
15
16 // Member functions that you must over-ride.
17
18 /**
19 * This function should check that $new_instance is set correctly.
20 * The newly calculated value of $instance should be returned.
21 * If "false" is returned, the instance won't be saved/updated.
22 *
23 * @param array $new_instance New settings for this instance as input by the user via form()
24 * @param array $old_instance Old settings for this instance
25 * @return array Settings to save or bool false to cancel saving
26 */
27 public function update( $new_instance, $old_instance ) {
28 return $new_instance;
29 }
30
31 /**
32 * Echo the settings update form
33 *
34 * @param array $instance Current settings
35 */
36 public function form( $instance, $args = array() ) {
37 echo '<p class="no-options-widget">' . esc_html__( 'There are no options for this action.', 'formidable' ) . '</p>';
38 return 'noform';
39 }
40
41 /**
42 * @return array of the default options
43 */
44 public function get_defaults() {
45 return array();
46 }
47
48 public function get_switch_fields() {
49 return array();
50 }
51
52 public function migrate_values( $action, $form ) {
53 return $action;
54 }
55
56 // Functions you'll need to call.
57
58 /**
59 * PHP5 constructor
60 *
61 * @param string $id_base Optional Base ID for the widget, lower case,
62 * if left empty a portion of the widget's class name will be used. Has to be unique.
63 * @param string $name Name for the widget displayed on the configuration page.
64 * @param array $action_options Optional Passed to wp_register_sidebar_widget()
65 * - description: shown on the configuration page
66 * - classname
67 * @param array $control_options Optional Passed to wp_register_widget_control()
68 * - width: required if more than 250px
69 * - height: currently not used but may be needed in the future
70 */
71 public function __construct( $id_base, $name, $action_options = array(), $control_options = array() ) {
72 if ( ! defined('ABSPATH') ) {
73 die('You are not allowed to call this page directly.');
74 }
75
76 $this->id_base = strtolower($id_base);
77 $this->name = $name;
78 $this->option_name = 'frm_' . $this->id_base . '_action';
79
80 $default_options = array(
81 'classes' => '',
82 'active' => true,
83 'event' => array( 'create' ),
84 'limit' => 1,
85 'force_event' => false,
86 'priority' => 20,
87 'ajax_load' => true,
88 'tooltip' => $name,
89 );
90
91 $action_options = apply_filters( 'frm_' . $id_base . '_action_options', $action_options );
92 $this->action_options = wp_parse_args( $action_options, $default_options );
93 $this->control_options = wp_parse_args( $control_options, array( 'id_base' => $this->id_base ) );
94 }
95
96 /**
97 * @param string $id_base
98 */
99 public function FrmFormAction( $id_base, $name, $action_options = array(), $control_options = array() ) {
100 FrmFormAction::__construct( $id_base, $name, $action_options, $control_options );
101 }
102
103 /**
104 * Constructs name attributes for use in form() fields
105 *
106 * This function should be used in form() methods to create name attributes for fields to be saved by update()
107 *
108 * @param string $field_name Field name
109 * @return string Name attribute for $field_name
110 */
111 public function get_field_name( $field_name, $post_field = 'post_content' ) {
112 $name = $this->option_name . '[' . $this->number . ']';
113 $name .= ( empty( $post_field ) ? '' : '[' . $post_field . ']' );
114 $name .= '[' . $field_name . ']';
115 return $name;
116 }
117
118 /**
119 * Constructs id attributes for use in form() fields
120 *
121 * This function should be used in form() methods to create id attributes for fields to be saved by update()
122 *
123 * @param string $field_name Field name
124 * @return string ID attribute for $field_name
125 */
126 public function get_field_id( $field_name ) {
127 return $field_name . '_' . $this->number;
128 }
129
130 // Private Function. Don't worry about this.
131
132 public function _set( $number ) {
133 $this->number = $number;
134 $this->id = $this->id_base . '-' . $number;
135 }
136
137 public function prepare_new( $form_id = false ) {
138 if ( $form_id ) {
139 $this->form_id = $form_id;
140 }
141
142 $post_content = array();
143 $default_values = $this->get_global_defaults();
144
145 // fill default values
146 $post_content = wp_parse_args( $post_content, $default_values);
147
148 if ( ! isset($post_content['event']) && ! $this->action_options['force_event'] ) {
149 $post_content['event'] = array( reset($this->action_options['event']) );
150 }
151
152 $form_action = array(
153 'post_title' => $this->name,
154 'post_content' => $post_content,
155 'post_excerpt' => $this->id_base,
156 'ID' => '',
157 'post_status' => 'publish',
158 'post_type' => FrmFormActionsController::$action_post_type,
159 'post_name' => $this->form_id . '_' . $this->id_base . '_' . $this->number,
160 'menu_order' => $this->form_id,
161 );
162 unset($post_content);
163
164 return (object) $form_action;
165 }
166
167 public function create( $form_id ) {
168 $this->form_id = $form_id;
169
170 $action = $this->prepare_new();
171
172 return $this->save_settings($action);
173 }
174
175 public function duplicate_form_actions( $form_id, $old_id ) {
176 if ( $form_id == $old_id ) {
177 // don't duplicate the actions if this is a template getting updated
178 return;
179 }
180
181 $this->form_id = $old_id;
182 $actions = $this->get_all( $old_id );
183
184 $this->form_id = $form_id;
185 foreach ( $actions as $action ) {
186 $this->duplicate_one($action, $form_id);
187 unset($action);
188 }
189 }
190
191 /* Check if imported action should be created or updated
192 *
193 * Since 2.0
194 *
195 * @param array $action
196 * @return integer $post_id
197 */
198 public function maybe_create_action( $action, $forms ) {
199 if ( isset( $action['ID'] ) && is_numeric( $action['ID'] ) && $forms[ $action['menu_order'] ] == 'updated' ) {
200 // Update action only
201 $action['post_content'] = FrmAppHelper::maybe_json_decode( $action['post_content'] );
202 $post_id = $this->save_settings( $action );
203 } else {
204 // Create action
205 $action['post_content'] = FrmAppHelper::maybe_json_decode($action['post_content']);
206 $post_id = $this->duplicate_one( (object) $action, $action['menu_order']);
207 }
208 return $post_id;
209 }
210
211 public function duplicate_one( $action, $form_id ) {
212 global $frm_duplicate_ids;
213
214 $action->menu_order = $form_id;
215 $switch = $this->get_global_switch_fields();
216 foreach ( (array) $action->post_content as $key => $val ) {
217 if ( is_numeric( $val ) && isset( $frm_duplicate_ids[ $val ] ) ) {
218 $action->post_content[ $key ] = $frm_duplicate_ids[ $val ];
219 } else if ( ! is_array( $val ) ) {
220 $action->post_content[ $key ] = FrmFieldsHelper::switch_field_ids( $val );
221 } else if ( isset( $switch[ $key ] ) && is_array( $switch[ $key ] ) ) {
222 // loop through each value if empty
223 if ( empty( $switch[ $key ] ) ) {
224 $switch[ $key ] = array_keys( $val );
225 }
226
227 foreach ( $switch[ $key ] as $subkey ) {
228 $action->post_content[ $key ] = $this->duplicate_array_walk( $action->post_content[ $key ], $subkey, $val );
229 }
230 }
231
232 unset($key, $val);
233 }
234 unset($action->ID);
235
236 return $this->save_settings($action);
237 }
238
239 private function duplicate_array_walk( $action, $subkey, $val ) {
240 global $frm_duplicate_ids;
241
242 if ( is_array($subkey) ) {
243 foreach ( $subkey as $subkey2 ) {
244 foreach ( (array) $val as $ck => $cv ) {
245 if ( is_array($cv) ) {
246 $action[ $ck ] = $this->duplicate_array_walk( $action[ $ck ], $subkey2, $cv );
247 } else if ( isset( $cv[ $subkey ] ) && is_numeric( $cv[ $subkey ] ) && isset( $frm_duplicate_ids[ $cv[ $subkey ] ] ) ) {
248 $action[ $ck ][ $subkey ] = $frm_duplicate_ids[ $cv[ $subkey ] ];
249 }
250 }
251 }
252 } else {
253 foreach ( (array) $val as $ck => $cv ) {
254 if ( is_array($cv) ) {
255 $action[ $ck ] = $this->duplicate_array_walk( $action[ $ck ], $subkey, $cv );
256 } else if ( $ck == $subkey && isset( $frm_duplicate_ids[ $cv ] ) ) {
257 $action[ $ck ] = $frm_duplicate_ids[ $cv ];
258 }
259 }
260 }
261
262 return $action;
263 }
264
265 /**
266 * Deal with changed settings.
267 *
268 * Do NOT over-ride this function
269 *
270 */
271 public function update_callback( $form_id ) {
272 $this->form_id = $form_id;
273
274 $all_instances = $this->get_settings();
275
276 // We need to update the data
277 if ( $this->updated ) {
278 return;
279 }
280
281 if ( isset( $_POST[ $this->option_name ] ) && is_array( $_POST[ $this->option_name ] ) ) {
282 $settings = $_POST[ $this->option_name ];
283 } else {
284 return;
285 }
286
287 $action_ids = array();
288
289 foreach ( $settings as $number => $new_instance ) {
290 $this->_set($number);
291
292 if ( ! isset($new_instance['post_title']) ) {
293 // settings were never opened, so don't update
294 $action_ids[] = $new_instance['ID'];
295 $this->updated = true;
296 continue;
297 }
298
299 $old_instance = isset( $all_instances[ $number ] ) ? $all_instances[ $number ] : array();
300
301 $new_instance['post_type'] = FrmFormActionsController::$action_post_type;
302 $new_instance['post_name'] = $this->form_id . '_' . $this->id_base . '_' . $this->number;
303 $new_instance['menu_order'] = $this->form_id;
304 $new_instance['post_status'] = 'publish';
305 $new_instance['post_date'] = isset( $old_instance->post_date ) ? $old_instance->post_date : '';
306
307 $instance = $this->update( $new_instance, $old_instance );
308
309 /**
310 * Filter an action's settings before saving.
311 *
312 * Returning false will effectively short-circuit the widget's ability
313 * to update settings.
314 *
315 * @since 2.0
316 *
317 * @param array $instance The current widget instance's settings.
318 * @param array $new_instance Array of new widget settings.
319 * @param array $old_instance Array of old widget settings.
320 * @param WP_Widget $this The current widget instance.
321 */
322 $instance = apply_filters( 'frm_action_update_callback', $instance, $new_instance, $old_instance, $this );
323
324 $instance['post_content'] = apply_filters('frm_before_save_action', $instance['post_content'], $instance, $new_instance, $old_instance, $this);
325 $instance['post_content'] = apply_filters( 'frm_before_save_' . $this->id_base . '_action', $new_instance['post_content'], $instance, $new_instance, $old_instance, $this );
326
327 if ( false !== $instance ) {
328 $all_instances[ $number ] = $instance;
329 }
330
331 $action_ids[] = $this->save_settings($instance);
332
333 $this->updated = true;
334 }
335
336 return $action_ids;
337 }
338
339 public function save_settings( $settings ) {
340 self::clear_cache();
341 return FrmDb::save_settings( $settings, 'frm_actions' );
342 }
343
344 public function get_single_action( $id ) {
345 $action = get_post($id);
346 if ( $action ) {
347 $action = $this->prepare_action( $action );
348 $this->_set( $id );
349 }
350 return $action;
351 }
352
353 public function get_one( $form_id ) {
354 return $this->get_all($form_id, 1);
355 }
356
357 public static function get_action_for_form( $form_id, $type = 'all', $limit = 99 ) {
358 $action_controls = FrmFormActionsController::get_form_actions( $type );
359 if ( empty($action_controls) ) {
360 // don't continue if there are no available actions
361 return array();
362 }
363
364 $limit = apply_filters( 'frm_form_action_limit', $limit, compact( 'type', 'form_id' ) );
365
366 if ( 'all' != $type ) {
367 return $action_controls->get_all( $form_id, $limit );
368 }
369
370 $args = self::action_args( $form_id, $limit );
371 $actions = FrmDb::check_cache( serialize( $args ), 'frm_actions', $args, 'get_posts' );
372
373 if ( ! $actions ) {
374 return array();
375 }
376
377 $settings = array();
378 foreach ( $actions as $action ) {
379 // some plugins/themes are formatting the post_excerpt
380 $action->post_excerpt = sanitize_title( $action->post_excerpt );
381
382 if ( ! isset( $action_controls[ $action->post_excerpt ] ) ) {
383 continue;
384 }
385
386 $action = $action_controls[ $action->post_excerpt ]->prepare_action( $action );
387 $settings[ $action->ID ] = $action;
388
389 if ( count( $settings ) >= $limit ) {
390 break;
391 }
392 }
393
394 if ( 1 === $limit ) {
395 $settings = reset($settings);
396 }
397
398 return $settings;
399 }
400
401 /**
402 * @param int $action_id
403 */
404 public static function get_single_action_type( $action_id, $type ) {
405 if ( ! $type ) {
406 return false;
407 }
408 $action_control = FrmFormActionsController::get_form_actions( $type );
409 return $action_control->get_single_action( $action_id );
410 }
411
412 /**
413 * @param int $form_id
414 * @return bool
415 */
416 public static function form_has_action_type( $form_id, $type ) {
417 $payment_actions = self::get_action_for_form( $form_id, $type );
418 return ! empty( $payment_actions );
419 }
420
421 public function get_all( $form_id = false, $limit = 99 ) {
422 if ( $form_id ) {
423 $this->form_id = $form_id;
424 }
425
426 $type = $this->id_base;
427
428 global $frm_vars;
429 $frm_vars['action_type'] = $type;
430
431 add_filter( 'posts_where', 'FrmFormActionsController::limit_by_type' );
432 $query = self::action_args( $form_id, $limit );
433 $query['post_status'] = 'any';
434 $query['suppress_filters'] = false;
435
436 $actions = FrmDb::check_cache( serialize( $query ) . '_type_' . $type, 'frm_actions', $query, 'get_posts' );
437 unset($query);
438
439 remove_filter( 'posts_where', 'FrmFormActionsController::limit_by_type' );
440
441 if ( empty($actions) ) {
442 return array();
443 }
444
445 $settings = array();
446 foreach ( $actions as $action ) {
447 if ( count($settings) >= $limit ) {
448 continue;
449 }
450
451 $action = $this->prepare_action($action);
452
453 $settings[ $action->ID ] = $action;
454 }
455
456 if ( 1 === $limit ) {
457 $settings = reset($settings);
458 }
459
460 return $settings;
461 }
462
463 public static function action_args( $form_id = 0, $limit = 99 ) {
464 $args = array(
465 'post_type' => FrmFormActionsController::$action_post_type,
466 'post_status' => 'publish',
467 'numberposts' => $limit,
468 'orderby' => 'title',
469 'order' => 'ASC',
470 );
471
472 if ( $form_id && $form_id != 'all' ) {
473 $args['menu_order'] = $form_id;
474 }
475
476 return $args;
477 }
478
479 public function prepare_action( $action ) {
480 $action->post_content = (array) FrmAppHelper::maybe_json_decode($action->post_content);
481 $action->post_excerpt = sanitize_title( $action->post_excerpt );
482
483 $default_values = $this->get_global_defaults();
484
485 // fill default values
486 $action->post_content += $default_values;
487
488 foreach ( $default_values as $k => $vals ) {
489 if ( is_array($vals) && ! empty($vals) ) {
490 if ( 'event' == $k && ! $this->action_options['force_event'] && ! empty( $action->post_content[ $k ] ) ) {
491 continue;
492 }
493 $action->post_content[ $k ] = wp_parse_args( $action->post_content[ $k ], $vals );
494 }
495 }
496
497 if ( ! is_array($action->post_content['event']) ) {
498 $action->post_content['event'] = explode(',', $action->post_content['event']);
499 }
500
501 return $action;
502 }
503
504 public function destroy( $form_id = false, $type = 'default' ) {
505 global $wpdb;
506
507 $this->form_id = $form_id;
508
509 $query = array( 'post_type' => FrmFormActionsController::$action_post_type );
510 if ( $form_id ) {
511 $query['menu_order'] = $form_id;
512 }
513 if ( 'all' != $type ) {
514 $query['post_excerpt'] = $this->id_base;
515 }
516
517 $post_ids = FrmDb::get_col( $wpdb->posts, $query, 'ID' );
518
519 foreach ( $post_ids as $id ) {
520 wp_delete_post($id);
521 }
522 self::clear_cache();
523 }
524
525 /**
526 * Delete the action cache when a form action is created, deleted, or updated
527 *
528 * @since 2.0.5
529 */
530 public static function clear_cache() {
531 FrmDb::cache_delete_group( 'frm_actions' );
532 }
533
534 public function get_settings() {
535 return self::get_action_for_form( $this->form_id, $this->id_base );
536 }
537
538 public function get_global_defaults() {
539 $defaults = $this->get_defaults();
540
541 if ( ! isset($defaults['event']) ) {
542 $defaults['event'] = array( 'create' );
543 }
544
545 if ( ! isset($defaults['conditions']) ) {
546 $defaults['conditions'] = array(
547 'send_stop' => '',
548 'any_all' => '',
549 );
550 }
551
552 return $defaults;
553 }
554
555 public function get_global_switch_fields() {
556 $switch = $this->get_switch_fields();
557 $switch['conditions'] = array( 'hide_field' );
558 return $switch;
559 }
560
561 /**
562 * Migrate settings from form->options into new action.
563 */
564 public function migrate_to_2( $form, $update = 'update' ) {
565 $action = $this->prepare_new($form->id);
566 $form->options = maybe_unserialize($form->options);
567
568 // fill with existing options
569 foreach ( $action->post_content as $name => $val ) {
570 if ( isset( $form->options[ $name ] ) ) {
571 $action->post_content[ $name ] = $form->options[ $name ];
572 unset( $form->options[ $name ] );
573 }
574 }
575
576 $action = $this->migrate_values($action, $form);
577
578 // check if action already exists
579 $post_id = get_posts( array(
580 'name' => $action->post_name,
581 'post_type' => FrmFormActionsController::$action_post_type,
582 'post_status' => $action->post_status,
583 'numberposts' => 1,
584 ) );
585
586 if ( empty($post_id) ) {
587 // create action now
588 $post_id = $this->save_settings($action);
589 }
590
591 if ( $post_id && 'update' == $update ) {
592 global $wpdb;
593 $form->options = maybe_serialize($form->options);
594
595 // update form options
596 $wpdb->update( $wpdb->prefix . 'frm_forms', array( 'options' => $form->options ), array( 'id' => $form->id ) );
597 FrmForm::clear_form_cache();
598 }
599
600 return $post_id;
601 }
602
603 public static function action_conditions_met( $action, $entry ) {
604 $notification = $action->post_content;
605 $stop = false;
606 $met = array();
607
608 if ( ! isset( $notification['conditions'] ) || empty( $notification['conditions'] ) ) {
609 return $stop;
610 }
611
612 foreach ( $notification['conditions'] as $k => $condition ) {
613 if ( ! is_numeric( $k ) ) {
614 continue;
615 }
616
617 if ( $stop && 'any' == $notification['conditions']['any_all'] && 'stop' == $notification['conditions']['send_stop'] ) {
618 continue;
619 }
620
621 self::prepare_logic_value( $condition['hide_opt'] );
622
623 $observed_value = self::get_value_from_entry( $entry, $condition['hide_field'] );
624
625 $stop = FrmFieldsHelper::value_meets_condition($observed_value, $condition['hide_field_cond'], $condition['hide_opt']);
626
627 if ( $notification['conditions']['send_stop'] == 'send' ) {
628 $stop = $stop ? false : true;
629 }
630
631 $met[ $stop ] = $stop;
632 }
633
634 if ( $notification['conditions']['any_all'] == 'all' && ! empty( $met ) && isset( $met[0] ) && isset( $met[1] ) ) {
635 $stop = ( $notification['conditions']['send_stop'] == 'send' );
636 } else if ( $notification['conditions']['any_all'] == 'any' && $notification['conditions']['send_stop'] == 'send' && isset($met[0]) ) {
637 $stop = false;
638 }
639
640 return $stop;
641 }
642
643 /**
644 * Prepare the logic value for comparison against the entered value
645 *
646 * @since 2.01.02
647 * @param array|string $logic_value
648 */
649 private static function prepare_logic_value( &$logic_value ) {
650 if ( is_array( $logic_value ) ) {
651 $logic_value = reset( $logic_value );
652 }
653
654 if ( $logic_value == 'current_user' ) {
655 $logic_value = get_current_user_id();
656 }
657 }
658
659
660 /**
661 * Get the value from a specific field and entry
662 *
663 * @since 2.01.02
664 * @param object $entry
665 * @param int $field_id
666 * @return array|bool|mixed|string
667 */
668 private static function get_value_from_entry( $entry, $field_id ) {
669 $observed_value = '';
670
671 if ( isset( $entry->metas[ $field_id ] ) ) {
672 $observed_value = $entry->metas[ $field_id ];
673 } else if ( $entry->post_id && FrmAppHelper::pro_is_installed() ) {
674 $field = FrmField::getOne( $field_id );
675 $observed_value = FrmProEntryMetaHelper::get_post_or_meta_value( $entry, $field, array(
676 'links' => false,
677 'truncate' => false,
678 ) );
679 }
680
681 return $observed_value;
682 }
683
684 public static function default_action_opts( $class = '' ) {
685 return array(
686 'classes' => 'frm_icon_font ' . $class,
687 'active' => false,
688 'limit' => 0,
689 );
690 }
691
692 public static function trigger_labels() {
693 return apply_filters( 'frm_action_triggers', array(
694 'draft' => __( 'Save Draft', 'formidable' ),
695 'create' => __( 'Create', 'formidable' ),
696 'update' => __( 'Update', 'formidable' ),
697 'delete' => __( 'Delete', 'formidable' ),
698 'import' => __( 'Import', 'formidable' ),
699 ) );
700 }
701 }
702