PluginProbe
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More / 6.17
Formidable Forms – WordPress Form Builder for Contact Forms, Calculators, Quizzes & More v6.17
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 / FrmMigrate.php

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

695 lines 19.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 if ( ! defined( 'ABSPATH' ) ) {
3 die( 'You are not allowed to call this page directly.' );
4 }
5
6 class FrmMigrate {
7 public $fields;
8 public $forms;
9 public $entries;
10 public $entry_metas;
11
12 public function __construct() {
13 global $wpdb;
14 $this->fields = $wpdb->prefix . 'frm_fields';
15 $this->forms = $wpdb->prefix . 'frm_forms';
16 $this->entries = $wpdb->prefix . 'frm_items';
17 $this->entry_metas = $wpdb->prefix . 'frm_item_metas';
18 }
19
20 public function upgrade() {
21 do_action( 'frm_before_install' );
22
23 global $wpdb, $frm_vars;
24
25 $frm_vars['doing_upgrade'] = true;
26
27 $needs_upgrade = FrmAppController::compare_for_update(
28 array(
29 'option' => 'frm_db_version',
30 'new_db_version' => FrmAppHelper::$db_version,
31 'new_plugin_version' => FrmAppHelper::plugin_version(),
32 )
33 );
34
35 if ( $needs_upgrade ) {
36 // update rewrite rules for views and other custom post types
37 flush_rewrite_rules();
38
39 require_once ABSPATH . 'wp-admin/includes/upgrade.php';
40
41 $old_db_version = get_option( 'frm_db_version' );
42
43 $this->create_tables();
44 $this->migrate_data( $old_db_version );
45
46 // SAVE DB VERSION.
47 update_option( 'frm_db_version', FrmAppHelper::plugin_version() . '-' . FrmAppHelper::$db_version );
48
49 if ( ! $old_db_version ) {
50 $this->maybe_create_contact_form();
51
52 if ( false === get_option( 'frm_first_activation' ) ) {
53 update_option( 'frm_first_activation', time(), false );
54 }
55 }
56 }//end if
57
58 do_action( 'frm_after_install' );
59
60 $frm_vars['doing_upgrade'] = false;
61
62 FrmAppHelper::save_combined_js();
63
64 // update the styling settings
65 if ( function_exists( 'get_filesystem_method' ) ) {
66 $frm_style = new FrmStyle();
67 $frm_style->update( 'default' );
68 }
69 }
70
71 public function collation() {
72 global $wpdb;
73 if ( ! $wpdb->has_cap( 'collation' ) ) {
74 return '';
75 }
76
77 return $wpdb->get_charset_collate();
78 }
79
80 private function create_tables() {
81 $charset_collate = $this->collation();
82 $sql = array();
83
84 /* Create/Upgrade Fields Table */
85 $sql[] = 'CREATE TABLE ' . $this->fields . ' (
86 id BIGINT(20) NOT NULL auto_increment,
87 field_key varchar(100) default NULL,
88 name text default NULL,
89 description longtext default NULL,
90 type text default NULL,
91 default_value longtext default NULL,
92 options longtext default NULL,
93 field_order int(11) default 0,
94 required int(1) default NULL,
95 field_options longtext default NULL,
96 form_id int(11) default NULL,
97 created_at datetime NOT NULL,
98 PRIMARY KEY (id),
99 KEY form_id (form_id),
100 UNIQUE KEY field_key (field_key)
101 )';
102
103 /* Create/Upgrade Forms Table */
104 $sql[] = 'CREATE TABLE ' . $this->forms . ' (
105 id int(11) NOT NULL auto_increment,
106 form_key varchar(100) default NULL,
107 name varchar(255) default NULL,
108 description text default NULL,
109 parent_form_id int(11) default 0,
110 logged_in tinyint(1) default NULL,
111 editable tinyint(1) default NULL,
112 is_template tinyint(1) default 0,
113 default_template tinyint(1) default 0,
114 status varchar(255) default NULL,
115 options longtext default NULL,
116 created_at datetime NOT NULL,
117 PRIMARY KEY (id),
118 UNIQUE KEY form_key (form_key)
119 )';
120
121 /* Create/Upgrade Items Table */
122 $sql[] = 'CREATE TABLE ' . $this->entries . ' (
123 id BIGINT(20) NOT NULL auto_increment,
124 item_key varchar(100) default NULL,
125 name varchar(255) default NULL,
126 description text default NULL,
127 ip text default NULL,
128 form_id BIGINT(20) default NULL,
129 post_id BIGINT(20) default NULL,
130 user_id BIGINT(20) default NULL,
131 parent_item_id BIGINT(20) default 0,
132 is_draft tinyint(1) default 0,
133 updated_by BIGINT(20) default NULL,
134 created_at datetime NOT NULL,
135 updated_at datetime NOT NULL,
136 PRIMARY KEY (id),
137 KEY form_id (form_id),
138 KEY post_id (post_id),
139 KEY user_id (user_id),
140 KEY parent_item_id (parent_item_id),
141 UNIQUE KEY item_key (item_key)
142 )';
143
144 /* Create/Upgrade Meta Table */
145 $sql[] = 'CREATE TABLE ' . $this->entry_metas . ' (
146 id BIGINT(20) NOT NULL auto_increment,
147 meta_value longtext default NULL,
148 field_id BIGINT(20) NOT NULL,
149 item_id BIGINT(20) NOT NULL,
150 created_at datetime NOT NULL,
151 PRIMARY KEY (id),
152 KEY field_id (field_id),
153 KEY item_id (item_id)
154 )';
155
156 foreach ( $sql as $q ) {
157 if ( function_exists( 'dbDelta' ) ) {
158 dbDelta( $q . $charset_collate . ';' );
159 } else {
160 global $wpdb;
161 $wpdb->query( $q . $charset_collate ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
162 }
163 unset( $q );
164 }
165
166 $this->add_composite_indexes_for_entries();
167 }
168
169 /**
170 * These indexes help optimize database queries for entries.
171 *
172 * @since 6.6
173 * @since 6.16.3 idx_form_id_is_draft was also added to frm_items.
174 * @since 6.17 idx_form_id_type was also added to frm_fields.
175 *
176 * @return void
177 */
178 private function add_composite_indexes_for_entries() {
179 global $wpdb;
180
181 $table_name = "{$wpdb->prefix}frm_items";
182 $index_name = 'idx_is_draft_created_at';
183
184 if ( ! self::index_exists( $table_name, $index_name ) ) {
185 $wpdb->query( "CREATE INDEX idx_is_draft_created_at ON `{$wpdb->prefix}frm_items` (is_draft, created_at)" );
186 }
187
188 $table_name = "{$wpdb->prefix}frm_item_metas";
189 $index_name = 'idx_field_id_item_id';
190
191 if ( ! self::index_exists( $table_name, $index_name ) ) {
192 $wpdb->query( "CREATE INDEX idx_field_id_item_id ON `{$wpdb->prefix}frm_item_metas` (field_id, item_id)" );
193 }
194
195 $table_name = "{$wpdb->prefix}frm_items";
196 $index_name = 'idx_form_id_is_draft';
197
198 if ( ! self::index_exists( $table_name, $index_name ) ) {
199 $wpdb->query( "CREATE INDEX idx_form_id_is_draft ON `{$wpdb->prefix}frm_items` (form_id, is_draft)" );
200 }
201
202 $table_name = "{$wpdb->prefix}frm_fields";
203 $index_name = 'idx_form_id_type';
204
205 if ( ! self::index_exists( $table_name, $index_name ) ) {
206 $wpdb->query( "CREATE INDEX idx_form_id_type ON `{$wpdb->prefix}frm_fields` (form_id, type(30))" );
207 }
208 }
209
210 /**
211 * Check that an index exists in a database table before trying to add it (which results in an error).
212 *
213 * @since 6.6
214 *
215 * @param string $table_name
216 * @param string $index_name
217 * @return bool
218 */
219 private static function index_exists( $table_name, $index_name ) {
220 global $wpdb;
221 $row = $wpdb->get_row(
222 $wpdb->prepare(
223 'SELECT 1 FROM information_schema.statistics
224 WHERE table_schema = database()
225 AND table_name = %s
226 AND index_name = %s
227 LIMIT 1',
228 array( $table_name, $index_name )
229 )
230 );
231 return (bool) $row;
232 }
233
234 private function maybe_create_contact_form() {
235 $form_exists = FrmForm::get_id_by_key( 'contact-form' );
236 if ( ! $form_exists ) {
237 $this->add_default_template();
238 }
239 }
240
241 /**
242 * Create the default contact form
243 *
244 * @since 3.06
245 */
246 private function add_default_template() {
247 if ( FrmXMLHelper::check_if_libxml_disable_entity_loader_exists() ) {
248 // XML import is not enabled on your server.
249 return;
250 }
251
252 $set_err = libxml_use_internal_errors( true );
253 $loader = FrmXMLHelper::maybe_libxml_disable_entity_loader( true );
254
255 $file = FrmAppHelper::plugin_path() . '/classes/views/xml/default-templates.xml';
256 FrmXMLHelper::import_xml( $file );
257
258 libxml_use_internal_errors( $set_err );
259 FrmXMLHelper::maybe_libxml_disable_entity_loader( $loader );
260 }
261
262 /**
263 * @param int|string $old_db_version
264 */
265 private function migrate_data( $old_db_version ) {
266 if ( ! $old_db_version ) {
267 $old_db_version = get_option( 'frm_db_version' );
268 }
269 if ( strpos( $old_db_version, '-' ) ) {
270 $last_upgrade = explode( '-', $old_db_version );
271 $old_db_version = (int) $last_upgrade[1];
272 }
273
274 if ( ! is_numeric( $old_db_version ) ) {
275 // bail if we don't know the previous version
276 return;
277 }
278
279 $migrations = array( 16, 11, 16, 17, 23, 25, 86, 90, 97, 98, 101 );
280 foreach ( $migrations as $migration ) {
281 if ( FrmAppHelper::$db_version >= $migration && $old_db_version < $migration ) {
282 $function_name = 'migrate_to_' . $migration;
283 $this->$function_name();
284 }
285 }
286 }
287
288 public function uninstall() {
289 if ( ! current_user_can( 'administrator' ) ) {
290 $frm_settings = FrmAppHelper::get_settings();
291 wp_die( esc_html( $frm_settings->admin_permission ) );
292 }
293
294 global $wpdb, $wp_roles;
295
296 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->fields ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
297 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->forms ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
298 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->entries ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
299 $wpdb->query( 'DROP TABLE IF EXISTS ' . $this->entry_metas ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
300
301 delete_option( 'frm_options' );
302 delete_option( 'frm_db_version' );
303 delete_option( 'frm_install_running' );
304 delete_option( 'frm_lite_settings_upgrade' );
305 delete_option( 'frm-usage-uuid' );
306 delete_option( 'frm_inbox' );
307 delete_option( 'frmpro_css' );
308 delete_option( FrmOnboardingWizardController::REDIRECT_STATUS_OPTION );
309 delete_option( FrmEmailSummaryHelper::$option_name );
310
311 // Delete roles.
312 $frm_roles = FrmAppHelper::frm_capabilities();
313 $roles = get_editable_roles();
314 foreach ( $frm_roles as $frm_role => $frm_role_description ) {
315 foreach ( $roles as $role => $details ) {
316 $wp_roles->remove_cap( $role, $frm_role );
317 unset( $role, $details );
318 }
319 unset( $frm_role, $frm_role_description );
320 }
321 unset( $roles, $frm_roles );
322
323 // delete actions, views, and styles
324
325 // prevent the post deletion from triggering entries to be deleted
326 remove_action( 'before_delete_post', 'FrmProDisplaysController::before_delete_post' );
327 remove_action( 'deleted_post', 'FrmProEntriesController::delete_entry' );
328
329 $post_ids = $wpdb->get_col( $wpdb->prepare( 'SELECT ID FROM ' . $wpdb->posts . ' WHERE post_type in (%s, %s, %s)', FrmFormActionsController::$action_post_type, FrmStylesController::$post_type, 'frm_display' ) );
330 foreach ( $post_ids as $post_id ) {
331 // Delete's each post.
332 wp_delete_post( $post_id, true );
333 }
334 unset( $post_ids );
335
336 // delete transients
337 delete_transient( 'frmpro_css' );
338 delete_transient( 'frm_options' );
339 delete_transient( 'frmpro_options' );
340 delete_transient( FrmOnboardingWizardController::TRANSIENT_NAME );
341
342 $wpdb->query( $wpdb->prepare( 'DELETE FROM ' . $wpdb->options . ' WHERE option_name LIKE %s OR option_name LIKE %s', '_transient_timeout_frm_form_fields_%', '_transient_frm_form_fields_%' ) );
343
344 do_action( 'frm_after_uninstall' );
345
346 return true;
347 }
348
349 /**
350 * Disables summary email for multisite (not the main site) if recipient setting isn't changed.
351 *
352 * @since 6.8
353 */
354 private function migrate_to_101() {
355 if ( ! is_multisite() || get_main_site_id() === get_current_blog_id() ) {
356 return;
357 }
358
359 $frm_settings = FrmAppHelper::get_settings();
360 if ( empty( $frm_settings->summary_emails ) || '[admin_email]' !== $frm_settings->summary_emails_recipients ) {
361 // User changed it.
362 return;
363 }
364
365 $frm_settings->summary_emails = 0;
366 $frm_settings->store();
367 }
368
369 /**
370 * Clear frmpro_css transient.
371 *
372 * @since 4.10.02
373 */
374 private function migrate_to_98() {
375 delete_transient( 'frmpro_css' );
376 }
377
378 /**
379 * Move default_blank and clear_on_focus to placeholder.
380 *
381 * @since 4.0
382 */
383 private function migrate_to_97() {
384 $this->migrate_to_placeholder( 'clear_on_focus' );
385 $this->migrate_to_placeholder( 'default_blank' );
386 }
387
388 /**
389 * Move clear_on_focus or default_blank to placeholder.
390 *
391 * @since 4.0
392 */
393 private function migrate_to_placeholder( $type = 'clear_on_focus' ) {
394 $query = array(
395 'field_options like' => '"' . $type . '";s:1:"1";',
396 );
397
398 $fields = FrmDb::get_results( $this->fields, $query, 'id, default_value, field_options, options' );
399
400 foreach ( $fields as $field ) {
401 FrmAppHelper::unserialize_or_decode( $field->field_options );
402 FrmAppHelper::unserialize_or_decode( $field->options );
403 $update_values = FrmXMLHelper::migrate_field_placeholder( $field, $type );
404 if ( empty( $update_values ) ) {
405 continue;
406 }
407
408 FrmField::update( $field->id, $update_values );
409 unset( $field );
410 }
411 }
412
413 /**
414 * Delete unneeded default templates
415 *
416 * @since 3.06
417 */
418 private function migrate_to_90() {
419 $form = FrmForm::getOne( 'contact' );
420 if ( $form && $form->default_template == 1 ) {
421 FrmForm::destroy( 'contact' );
422 }
423 }
424
425 /**
426 * Reverse migration 17 -- Divide by 9
427 *
428 * @since 3.0.05
429 */
430 private function migrate_to_86() {
431
432 $fields = $this->get_fields_with_size();
433
434 foreach ( (array) $fields as $f ) {
435 FrmAppHelper::unserialize_or_decode( $f->field_options );
436 $size = $f->field_options['size'];
437 $this->maybe_convert_migrated_size( $size );
438
439 if ( $size === $f->field_options['size'] ) {
440 continue;
441 }
442
443 $f->field_options['size'] = $size;
444 FrmField::update( $f->id, array( 'field_options' => $f->field_options ) );
445 unset( $f );
446 }
447
448 // reverse the extra size changes in widgets
449 $widgets = get_option( 'widget_frm_show_form' );
450 if ( empty( $widgets ) ) {
451 return;
452 }
453
454 $this->revert_widget_field_size();
455 }
456
457 private function get_fields_with_size() {
458 $field_types = array(
459 'textarea',
460 'text',
461 'number',
462 'email',
463 'url',
464 'rte',
465 'date',
466 'phone',
467 'password',
468 'image',
469 'tag',
470 'file',
471 );
472
473 $query = array(
474 'type' => $field_types,
475 'field_options like' => 's:4:"size";',
476 'field_options not like' => 's:4:"size";s:0:',
477 );
478
479 return FrmDb::get_results( $this->fields, $query, 'id, field_options' );
480 }
481
482 /**
483 * Reverse the extra size changes in widgets
484 *
485 * @since 3.0.05
486 */
487 private function revert_widget_field_size() {
488 $widgets = get_option( 'widget_frm_show_form' );
489 if ( empty( $widgets ) ) {
490 return;
491 }
492
493 FrmAppHelper::unserialize_or_decode( $widgets );
494 foreach ( $widgets as $k => $widget ) {
495 if ( ! is_array( $widget ) || ! isset( $widget['size'] ) ) {
496 continue;
497 }
498
499 $this->maybe_convert_migrated_size( $widgets[ $k ]['size'] );
500 }
501 update_option( 'widget_frm_show_form', $widgets );
502 }
503
504 /**
505 * Divide by 9 to reverse the multiplication
506 *
507 * @since 3.0.05
508 */
509 private function maybe_convert_migrated_size( &$size ) {
510 $has_px_size = ! empty( $size ) && strpos( $size, 'px' );
511 if ( ! $has_px_size ) {
512 return;
513 }
514
515 $int_size = str_replace( 'px', '', $size );
516 if ( ! is_numeric( $int_size ) || (int) $int_size < 900 ) {
517 return;
518 }
519
520 $pixel_conversion = 9;
521
522 $size = round( (int) $int_size / $pixel_conversion );
523 }
524
525 /**
526 * Migrate old styling settings. If sites are using the old
527 * default 400px field width, switch it to 100%
528 *
529 * @since 2.0.4
530 */
531 private function migrate_to_25() {
532 // get the style that was created with the style migration
533 $frm_style = new FrmStyle();
534 $styles = $frm_style->get_all( 'post_date', 'ASC', 1 );
535 if ( empty( $styles ) ) {
536 return;
537 }
538
539 foreach ( $styles as $style ) {
540 if ( $style->post_content['field_width'] === '400px' ) {
541 $style->post_content['field_width'] = '100%';
542 $frm_style->save( (array) $style );
543
544 return;
545 }
546 }
547 }
548
549 /**
550 * Check if the parent_form_id columns exists.
551 * If not, try and add it again
552 *
553 * @since 2.0.2
554 */
555 private function migrate_to_23() {
556 global $wpdb;
557 $exists = $wpdb->get_row( 'SHOW COLUMNS FROM ' . $this->forms . ' LIKE "parent_form_id"' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
558 if ( empty( $exists ) ) {
559 $wpdb->query( 'ALTER TABLE ' . $this->forms . ' ADD parent_form_id int(11) default 0' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
560 }
561 }
562
563 /**
564 * Change field size from character to pixel -- Multiply by 9
565 */
566 private function migrate_to_17() {
567 $fields = $this->get_fields_with_size();
568
569 foreach ( $fields as $f ) {
570 FrmAppHelper::unserialize_or_decode( $f->field_options );
571 if ( empty( $f->field_options['size'] ) || ! is_numeric( $f->field_options['size'] ) ) {
572 continue;
573 }
574
575 $this->convert_character_to_px( $f->field_options['size'] );
576
577 FrmField::update( $f->id, array( 'field_options' => $f->field_options ) );
578 unset( $f );
579 }
580
581 $this->adjust_widget_size();
582 }
583
584 /**
585 * Change the characters in widgets to pixels
586 */
587 private function adjust_widget_size() {
588 $widgets = get_option( 'widget_frm_show_form' );
589 if ( empty( $widgets ) ) {
590 return;
591 }
592
593 FrmAppHelper::unserialize_or_decode( $widgets );
594 foreach ( $widgets as $k => $widget ) {
595 if ( ! is_array( $widget ) || ! isset( $widget['size'] ) ) {
596 continue;
597 }
598 $this->convert_character_to_px( $widgets[ $k ]['size'] );
599 }
600 update_option( 'widget_frm_show_form', $widgets );
601 }
602
603 private function convert_character_to_px( &$size ) {
604 $pixel_conversion = 9;
605
606 $size = round( $pixel_conversion * (int) $size );
607 $size .= 'px';
608 }
609
610 /**
611 * Migrate post and email notification settings into actions
612 */
613 private function migrate_to_16() {
614 $forms = FrmDb::get_results( $this->forms, array(), 'id, options, is_template, default_template' );
615
616 /**
617 * Old email settings format:
618 * email_to: Email or field id
619 * also_email_to: array of fields ids
620 * reply_to: Email, field id, 'custom'
621 * cust_reply_to: string
622 * reply_to_name: field id, 'custom'
623 * cust_reply_to_name: string
624 * plain_text: 0|1
625 * email_message: string or ''
626 * email_subject: string or ''
627 * inc_user_info: 0|1
628 * update_email: 0, 1, 2
629 *
630 * Old autoresponder settings format:
631 * auto_responder: 0|1
632 * ar_email_message: string or ''
633 * ar_email_to: field id
634 * ar_plain_text: 0|1
635 * ar_reply_to_name: string
636 * ar_reply_to: string
637 * ar_email_subject: string
638 * ar_update_email: 0, 1, 2
639 *
640 * New email settings:
641 * post_content: json settings
642 * post_title: form id
643 * post_excerpt: message
644 */
645 foreach ( $forms as $form ) {
646 if ( $form->is_template && $form->default_template ) {
647 // don't migrate the default templates since the email will be added anyway
648 continue;
649 }
650
651 // Format form options
652 $form_options = $form->options;
653 FrmAppHelper::unserialize_or_decode( $form_options );
654
655 // Migrate settings to actions
656 FrmXMLHelper::migrate_form_settings_to_actions( $form_options, $form->id );
657 }
658 }
659
660 private function migrate_to_11() {
661 global $wpdb;
662
663 $forms = FrmDb::get_results( $this->forms, array(), 'id, options' );
664
665 $sending = __( 'Sending', 'formidable' );
666 $img = FrmAppHelper::plugin_url() . '/images/ajax_loader.gif';
667 $old_default_html = <<<DEFAULT_HTML
668 <div class="frm_submit">
669 [if back_button]<input type="submit" value="[back_label]" name="frm_prev_page" formnovalidate="formnovalidate" [back_hook] />[/if back_button]
670 <input type="submit" value="[button_label]" [button_action] />
671 <img class="frm_ajax_loading" src="$img" alt="$sending" style="visibility:hidden;" />
672 </div>
673 DEFAULT_HTML;
674 unset( $sending, $img );
675
676 $new_default_html = FrmFormsHelper::get_default_html( 'submit' );
677 $draft_link = FrmFormsHelper::get_draft_link();
678 foreach ( $forms as $form ) {
679 FrmAppHelper::unserialize_or_decode( $form->options );
680 if ( empty( $form->options['submit_html'] ) ) {
681 continue;
682 }
683
684 if ( $form->options['submit_html'] != $new_default_html && $form->options['submit_html'] == $old_default_html ) {
685 $form->options['submit_html'] = $new_default_html;
686 $wpdb->update( $this->forms, array( 'options' => serialize( $form->options ) ), array( 'id' => $form->id ) );
687 } elseif ( ! strpos( $form->options['submit_html'], 'save_draft' ) ) {
688 $form->options['submit_html'] = preg_replace( '~\<\/div\>(?!.*\<\/div\>)~', $draft_link . "\r\n</div>", $form->options['submit_html'] );
689 $wpdb->update( $this->forms, array( 'options' => serialize( $form->options ) ), array( 'id' => $form->id ) );
690 }
691 unset( $form );
692 }
693 }
694 }
695