PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 3.06.06
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v3.06.06
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 3.06.06, at classes/models/FrmFormAction.php

755 lines 22.2 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 'plugin' => $this->id_base,
89 'tooltip' => $name,
90 );
91
92 $action_options = apply_filters( 'frm_' . $id_base . '_action_options', $action_options );
93 $this->action_options = wp_parse_args( $action_options, $default_options );
94 $this->control_options = wp_parse_args( $control_options, array( 'id_base' => $this->id_base ) );
95 }
96
97 /**
98 * @param string $id_base
99 */
100 public function FrmFormAction( $id_base, $name, $action_options = array(), $control_options = array() ) {
101 self::__construct( $id_base, $name, $action_options, $control_options );
102 }
103
104 /**
105 * Constructs name attributes for use in form() fields
106 *
107 * This function should be used in form() methods to create name attributes for fields to be saved by update()
108 *
109 * @param string $field_name Field name
110 * @return string Name attribute for $field_name
111 */
112 public function get_field_name( $field_name, $post_field = 'post_content' ) {
113 $name = $this->option_name . '[' . $this->number . ']';
114 $name .= ( empty( $post_field ) ? '' : '[' . $post_field . ']' );
115 $name .= '[' . $field_name . ']';
116 return $name;
117 }
118
119 /**
120 * Constructs id attributes for use in form() fields
121 *
122 * This function should be used in form() methods to create id attributes for fields to be saved by update()
123 *
124 * @param string $field_name Field name
125 * @return string ID attribute for $field_name
126 */
127 public function get_field_id( $field_name ) {
128 return $field_name . '_' . $this->number;
129 }
130
131 // Private Function. Don't worry about this.
132
133 public function _set( $number ) {
134 $this->number = $number;
135 $this->id = $this->id_base . '-' . $number;
136 }
137
138 public function prepare_new( $form_id = false ) {
139 if ( $form_id ) {
140 $this->form_id = $form_id;
141 }
142
143 $post_content = array();
144 $default_values = $this->get_global_defaults();
145
146 // fill default values
147 $post_content = wp_parse_args( $post_content, $default_values );
148
149 if ( ! isset( $post_content['event'] ) && ! $this->action_options['force_event'] ) {
150 $post_content['event'] = array( reset( $this->action_options['event'] ) );
151 }
152
153 $form_action = array(
154 'post_title' => $this->name,
155 'post_content' => $post_content,
156 'post_excerpt' => $this->id_base,
157 'ID' => '',
158 'post_status' => 'publish',
159 'post_type' => FrmFormActionsController::$action_post_type,
160 'post_name' => $this->form_id . '_' . $this->id_base . '_' . $this->number,
161 'menu_order' => $this->form_id,
162 );
163 unset( $post_content );
164
165 return (object) $form_action;
166 }
167
168 public function create( $form_id ) {
169 $this->form_id = $form_id;
170
171 $action = $this->prepare_new();
172
173 return $this->save_settings( $action );
174 }
175
176 public function duplicate_form_actions( $form_id, $old_id ) {
177 if ( $form_id == $old_id ) {
178 // don't duplicate the actions if this is a template getting updated
179 return;
180 }
181
182 $this->form_id = $old_id;
183 $actions = $this->get_all( $old_id );
184
185 $this->form_id = $form_id;
186 foreach ( $actions as $action ) {
187 $this->duplicate_one( $action, $form_id );
188 unset( $action );
189 }
190 }
191
192 /* Check if imported action should be created or updated
193 *
194 * Since 2.0
195 *
196 * @param array $action
197 * @return integer $post_id
198 */
199 public function maybe_create_action( $action, $forms ) {
200 if ( isset( $action['ID'] ) && is_numeric( $action['ID'] ) && isset( $forms[ $action['menu_order'] ] ) && $forms[ $action['menu_order'] ] == 'updated' ) {
201 // Update action only
202 $action['post_content'] = FrmAppHelper::maybe_json_decode( $action['post_content'] );
203 $post_id = $this->save_settings( $action );
204 } else {
205 // Create action
206 $action['post_content'] = FrmAppHelper::maybe_json_decode( $action['post_content'] );
207 $post_id = $this->duplicate_one( (object) $action, $action['menu_order'] );
208 }
209 return $post_id;
210 }
211
212 public function duplicate_one( $action, $form_id ) {
213 global $frm_duplicate_ids;
214
215 $action->menu_order = $form_id;
216 $switch = $this->get_global_switch_fields();
217 foreach ( (array) $action->post_content as $key => $val ) {
218 if ( is_numeric( $val ) && isset( $frm_duplicate_ids[ $val ] ) ) {
219 $action->post_content[ $key ] = $frm_duplicate_ids[ $val ];
220 } else if ( ! is_array( $val ) ) {
221 $action->post_content[ $key ] = FrmFieldsHelper::switch_field_ids( $val );
222 } else if ( isset( $switch[ $key ] ) && is_array( $switch[ $key ] ) ) {
223 // loop through each value if empty
224 if ( empty( $switch[ $key ] ) ) {
225 $switch[ $key ] = array_keys( $val );
226 }
227
228 foreach ( $switch[ $key ] as $subkey ) {
229 $action->post_content[ $key ] = $this->duplicate_array_walk( $action->post_content[ $key ], $subkey, $val );
230 }
231 }
232
233 unset( $key, $val );
234 }
235 unset( $action->ID );
236
237 return $this->save_settings( $action );
238 }
239
240 private function duplicate_array_walk( $action, $subkey, $val ) {
241 global $frm_duplicate_ids;
242
243 if ( is_array( $subkey ) ) {
244 foreach ( $subkey as $subkey2 ) {
245 foreach ( (array) $val as $ck => $cv ) {
246 if ( is_array( $cv ) ) {
247 $action[ $ck ] = $this->duplicate_array_walk( $action[ $ck ], $subkey2, $cv );
248 } else if ( isset( $cv[ $subkey ] ) && is_numeric( $cv[ $subkey ] ) && isset( $frm_duplicate_ids[ $cv[ $subkey ] ] ) ) {
249 $action[ $ck ][ $subkey ] = $frm_duplicate_ids[ $cv[ $subkey ] ];
250 }
251 }
252 }
253 } else {
254 foreach ( (array) $val as $ck => $cv ) {
255 if ( is_array( $cv ) ) {
256 $action[ $ck ] = $this->duplicate_array_walk( $action[ $ck ], $subkey, $cv );
257 } else if ( $ck == $subkey && isset( $frm_duplicate_ids[ $cv ] ) ) {
258 $action[ $ck ] = $frm_duplicate_ids[ $cv ];
259 }
260 }
261 }
262
263 return $action;
264 }
265
266 /**
267 * Deal with changed settings.
268 *
269 * Do NOT over-ride this function
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 $old_instance = isset( $all_instances[ $number ] ) ? $all_instances[ $number ] : array();
293
294 if ( ! isset( $new_instance['post_status'] ) ) {
295 $new_instance['post_status'] = 'draft';
296 }
297
298 // settings were never opened, so don't update
299 if ( ! isset( $new_instance['post_title'] ) ) {
300 $this->maybe_update_status( $new_instance, $old_instance );
301 $action_ids[] = $new_instance['ID'];
302 $this->updated = true;
303 continue;
304 }
305
306 $new_instance['post_type'] = FrmFormActionsController::$action_post_type;
307 $new_instance['post_name'] = $this->form_id . '_' . $this->id_base . '_' . $this->number;
308 $new_instance['menu_order'] = $this->form_id;
309 $new_instance['post_date'] = isset( $old_instance->post_date ) ? $old_instance->post_date : '';
310
311 $instance = $this->update( $new_instance, $old_instance );
312
313 /**
314 * Filter an action's settings before saving.
315 *
316 * Returning false will effectively short-circuit the widget's ability
317 * to update settings.
318 *
319 * @since 2.0
320 *
321 * @param array $instance The current widget instance's settings.
322 * @param array $new_instance Array of new widget settings.
323 * @param array $old_instance Array of old widget settings.
324 * @param WP_Widget $this The current widget instance.
325 */
326 $instance = apply_filters( 'frm_action_update_callback', $instance, $new_instance, $old_instance, $this );
327
328 $instance['post_content'] = apply_filters( 'frm_before_save_action', $instance['post_content'], $instance, $new_instance, $old_instance, $this );
329 $instance['post_content'] = apply_filters( 'frm_before_save_' . $this->id_base . '_action', $new_instance['post_content'], $instance, $new_instance, $old_instance, $this );
330
331 if ( false !== $instance ) {
332 $all_instances[ $number ] = $instance;
333 }
334
335 $action_ids[] = $this->save_settings( $instance );
336
337 $this->updated = true;
338 }
339
340 return $action_ids;
341 }
342
343 /**
344 * If the status of the action has changed, update it
345 *
346 * @since 3.04
347 */
348 protected function maybe_update_status( $new_instance, $old_instance ) {
349 if ( $new_instance['post_status'] !== $old_instance->post_status ) {
350 self::clear_cache();
351 wp_update_post(
352 array(
353 'ID' => $new_instance['ID'],
354 'post_status' => $new_instance['post_status'],
355 )
356 );
357 }
358 }
359
360 public function save_settings( $settings ) {
361 self::clear_cache();
362 return FrmDb::save_settings( $settings, 'frm_actions' );
363 }
364
365 public function get_single_action( $id ) {
366 $action = get_post( $id );
367 if ( $action ) {
368 $action = $this->prepare_action( $action );
369 $this->_set( $id );
370 }
371 return $action;
372 }
373
374 public function get_one( $form_id ) {
375 return $this->get_all( $form_id, 1 );
376 }
377
378 public static function get_action_for_form( $form_id, $type = 'all', $atts = array() ) {
379 $action_controls = FrmFormActionsController::get_form_actions( $type );
380 if ( empty( $action_controls ) ) {
381 // don't continue if there are no available actions
382 return array();
383 }
384
385 if ( 'all' != $type ) {
386 return $action_controls->get_all( $form_id, $atts );
387 }
388
389 self::prepare_get_action( $atts );
390
391 $limit = apply_filters( 'frm_form_action_limit', $atts['limit'], compact( 'type', 'form_id' ) );
392
393 $args = self::action_args( $form_id, $limit );
394 $args['post_status'] = $atts['post_status'];
395 $actions = FrmDb::check_cache( serialize( $args ), 'frm_actions', $args, 'get_posts' );
396
397 if ( ! $actions ) {
398 return array();
399 }
400
401 $settings = array();
402 foreach ( $actions as $action ) {
403 // some plugins/themes are formatting the post_excerpt
404 $action->post_excerpt = sanitize_title( $action->post_excerpt );
405
406 if ( ! isset( $action_controls[ $action->post_excerpt ] ) ) {
407 continue;
408 }
409
410 $action = $action_controls[ $action->post_excerpt ]->prepare_action( $action );
411 $settings[ $action->ID ] = $action;
412
413 if ( count( $settings ) >= $limit ) {
414 break;
415 }
416 }
417
418 if ( 1 === $limit ) {
419 $settings = reset( $settings );
420 }
421
422 return $settings;
423 }
424
425 /**
426 * @since 3.04
427 * @param array $args
428 * @param string $default_status
429 */
430 protected static function prepare_get_action( &$args, $default_status = 'publish' ) {
431 if ( is_numeric( $args ) ) {
432 // for reverse compatibility. $limit was changed to $args
433 $args = array(
434 'limit' => $args,
435 );
436 }
437 $defaults = array(
438 'limit' => 99,
439 'post_status' => $default_status,
440 );
441 $args = wp_parse_args( $args, $defaults );
442 }
443
444 /**
445 * @param int $action_id
446 */
447 public static function get_single_action_type( $action_id, $type ) {
448 if ( ! $type ) {
449 return false;
450 }
451 $action_control = FrmFormActionsController::get_form_actions( $type );
452 return $action_control->get_single_action( $action_id );
453 }
454
455 /**
456 * @param int $form_id
457 * @return bool
458 */
459 public static function form_has_action_type( $form_id, $type ) {
460 $payment_actions = self::get_action_for_form( $form_id, $type );
461 return ! empty( $payment_actions );
462 }
463
464 public function get_all( $form_id = false, $atts = array() ) {
465 self::prepare_get_action( $atts, 'any' );
466 $limit = $atts['limit'];
467
468 if ( $form_id ) {
469 $this->form_id = $form_id;
470 }
471
472 $type = $this->id_base;
473
474 global $frm_vars;
475 $frm_vars['action_type'] = $type;
476
477 add_filter( 'posts_where', 'FrmFormActionsController::limit_by_type' );
478 $query = self::action_args( $form_id, $limit );
479 $query['post_status'] = $atts['post_status'];
480 $query['suppress_filters'] = false;
481
482 $actions = FrmDb::check_cache( serialize( $query ) . '_type_' . $type, 'frm_actions', $query, 'get_posts' );
483 unset( $query );
484
485 remove_filter( 'posts_where', 'FrmFormActionsController::limit_by_type' );
486
487 if ( empty( $actions ) ) {
488 return array();
489 }
490
491 $settings = array();
492 foreach ( $actions as $action ) {
493 if ( count( $settings ) >= $limit ) {
494 continue;
495 }
496
497 $action = $this->prepare_action( $action );
498
499 $settings[ $action->ID ] = $action;
500 }
501
502 if ( 1 === $limit ) {
503 $settings = reset( $settings );
504 }
505
506 return $settings;
507 }
508
509 public static function action_args( $form_id = 0, $limit = 99 ) {
510 $args = array(
511 'post_type' => FrmFormActionsController::$action_post_type,
512 'post_status' => 'publish',
513 'numberposts' => $limit,
514 'orderby' => 'title',
515 'order' => 'ASC',
516 );
517
518 if ( $form_id && $form_id != 'all' ) {
519 $args['menu_order'] = $form_id;
520 }
521
522 return $args;
523 }
524
525 public function prepare_action( $action ) {
526 $action->post_content = (array) FrmAppHelper::maybe_json_decode( $action->post_content );
527 $action->post_excerpt = sanitize_title( $action->post_excerpt );
528
529 $default_values = $this->get_global_defaults();
530
531 // fill default values
532 $action->post_content += $default_values;
533
534 foreach ( $default_values as $k => $vals ) {
535 if ( is_array( $vals ) && ! empty( $vals ) ) {
536 if ( 'event' == $k && ! $this->action_options['force_event'] && ! empty( $action->post_content[ $k ] ) ) {
537 continue;
538 }
539 $action->post_content[ $k ] = wp_parse_args( $action->post_content[ $k ], $vals );
540 }
541 }
542
543 if ( ! is_array( $action->post_content['event'] ) ) {
544 $action->post_content['event'] = explode( ',', $action->post_content['event'] );
545 }
546
547 return $action;
548 }
549
550 public function destroy( $form_id = false, $type = 'default' ) {
551 global $wpdb;
552
553 $this->form_id = $form_id;
554
555 $query = array( 'post_type' => FrmFormActionsController::$action_post_type );
556 if ( $form_id ) {
557 $query['menu_order'] = $form_id;
558 }
559 if ( 'all' != $type ) {
560 $query['post_excerpt'] = $this->id_base;
561 }
562
563 $post_ids = FrmDb::get_col( $wpdb->posts, $query, 'ID' );
564
565 foreach ( $post_ids as $id ) {
566 wp_delete_post( $id );
567 }
568 self::clear_cache();
569 }
570
571 /**
572 * Delete the action cache when a form action is created, deleted, or updated
573 *
574 * @since 2.0.5
575 */
576 public static function clear_cache() {
577 FrmDb::cache_delete_group( 'frm_actions' );
578 }
579
580 public function get_settings() {
581 return self::get_action_for_form( $this->form_id, $this->id_base );
582 }
583
584 public function get_global_defaults() {
585 $defaults = $this->get_defaults();
586
587 if ( ! isset( $defaults['event'] ) ) {
588 $defaults['event'] = array( 'create' );
589 }
590
591 if ( ! isset( $defaults['conditions'] ) ) {
592 $defaults['conditions'] = array(
593 'send_stop' => '',
594 'any_all' => '',
595 );
596 }
597
598 return $defaults;
599 }
600
601 public function get_global_switch_fields() {
602 $switch = $this->get_switch_fields();
603 $switch['conditions'] = array( 'hide_field' );
604 return $switch;
605 }
606
607 /**
608 * Migrate settings from form->options into new action.
609 */
610 public function migrate_to_2( $form, $update = 'update' ) {
611 $action = $this->prepare_new( $form->id );
612 $form->options = maybe_unserialize( $form->options );
613
614 // fill with existing options
615 foreach ( $action->post_content as $name => $val ) {
616 if ( isset( $form->options[ $name ] ) ) {
617 $action->post_content[ $name ] = $form->options[ $name ];
618 unset( $form->options[ $name ] );
619 }
620 }
621
622 $action = $this->migrate_values( $action, $form );
623
624 // check if action already exists
625 $post_id = get_posts(
626 array(
627 'name' => $action->post_name,
628 'post_type' => FrmFormActionsController::$action_post_type,
629 'post_status' => $action->post_status,
630 'numberposts' => 1,
631 )
632 );
633
634 if ( empty( $post_id ) ) {
635 // create action now
636 $post_id = $this->save_settings( $action );
637 }
638
639 if ( $post_id && 'update' == $update ) {
640 global $wpdb;
641 $form->options = maybe_serialize( $form->options );
642
643 // update form options
644 $wpdb->update( $wpdb->prefix . 'frm_forms', array( 'options' => $form->options ), array( 'id' => $form->id ) );
645 FrmForm::clear_form_cache();
646 }
647
648 return $post_id;
649 }
650
651 public static function action_conditions_met( $action, $entry ) {
652 $notification = $action->post_content;
653 $stop = false;
654 $met = array();
655
656 if ( ! isset( $notification['conditions'] ) || empty( $notification['conditions'] ) ) {
657 return $stop;
658 }
659
660 foreach ( $notification['conditions'] as $k => $condition ) {
661 if ( ! is_numeric( $k ) ) {
662 continue;
663 }
664
665 if ( $stop && 'any' == $notification['conditions']['any_all'] && 'stop' == $notification['conditions']['send_stop'] ) {
666 continue;
667 }
668
669 self::prepare_logic_value( $condition['hide_opt'] );
670
671 $observed_value = self::get_value_from_entry( $entry, $condition['hide_field'] );
672
673 $stop = FrmFieldsHelper::value_meets_condition( $observed_value, $condition['hide_field_cond'], $condition['hide_opt'] );
674
675 if ( $notification['conditions']['send_stop'] == 'send' ) {
676 $stop = $stop ? false : true;
677 }
678
679 $met[ $stop ] = $stop;
680 }
681
682 if ( $notification['conditions']['any_all'] == 'all' && ! empty( $met ) && isset( $met[0] ) && isset( $met[1] ) ) {
683 $stop = ( $notification['conditions']['send_stop'] == 'send' );
684 } elseif ( $notification['conditions']['any_all'] == 'any' && $notification['conditions']['send_stop'] == 'send' && isset( $met[0] ) ) {
685 $stop = false;
686 }
687
688 return $stop;
689 }
690
691 /**
692 * Prepare the logic value for comparison against the entered value
693 *
694 * @since 2.01.02
695 * @param array|string $logic_value
696 */
697 private static function prepare_logic_value( &$logic_value ) {
698 if ( is_array( $logic_value ) ) {
699 $logic_value = reset( $logic_value );
700 }
701
702 if ( $logic_value == 'current_user' ) {
703 $logic_value = get_current_user_id();
704 }
705 }
706
707
708 /**
709 * Get the value from a specific field and entry
710 *
711 * @since 2.01.02
712 * @param object $entry
713 * @param int $field_id
714 * @return array|bool|mixed|string
715 */
716 private static function get_value_from_entry( $entry, $field_id ) {
717 $observed_value = '';
718
719 if ( isset( $entry->metas[ $field_id ] ) ) {
720 $observed_value = $entry->metas[ $field_id ];
721 } else if ( $entry->post_id && FrmAppHelper::pro_is_installed() ) {
722 $field = FrmField::getOne( $field_id );
723 $observed_value = FrmProEntryMetaHelper::get_post_or_meta_value(
724 $entry,
725 $field,
726 array(
727 'links' => false,
728 'truncate' => false,
729 )
730 );
731 }
732
733 return $observed_value;
734 }
735
736 public static function default_action_opts( $class = '' ) {
737 return array(
738 'classes' => 'frm_icon_font ' . $class,
739 'active' => false,
740 'limit' => 0,
741 );
742 }
743
744 public static function trigger_labels() {
745 $triggers = array(
746 'draft' => __( 'Save Draft', 'formidable' ),
747 'create' => __( 'Create', 'formidable' ),
748 'update' => __( 'Update', 'formidable' ),
749 'delete' => __( 'Delete', 'formidable' ),
750 'import' => __( 'Import', 'formidable' ),
751 );
752 return apply_filters( 'frm_action_triggers', $triggers );
753 }
754 }
755