PluginProbe
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder / 2.10.1
Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder v2.10.1
V-3.3.0 3.2.2 3.2.1 3.2.0 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 V3.0.3 V3.0.2 -3.0.1 V_3.0.0 1.1.1 1.1.8 1.2 1.3 1.4 1.4.18 1.5.2 1.9 2.0 2.10.0 2.10.1 2.10.2 All 137 releases
bit-form / v1 / includes / Plugin.php

Plugin.php in Bit Form – Contact Form, Payment Forms, Multi Step Forms, Calculator & Custom Form Builder 2.10.1, at v1/includes/Plugin.php

726 lines 21.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace BitCode\BitForm;
4
5 /**
6 * Main class for the plugin.
7 *
8 * @since 1.0.0-alpha
9 */
10
11 use BitCode\BitForm\Admin\Admin_Bar;
12 use BitCode\BitForm\Admin\Form\AdminFormHandler;
13 use BitCode\BitForm\Core\Ajax\AjaxService;
14 use BitCode\BitForm\Core\Capability\Request;
15 use BitCode\BitForm\Core\Database\DB;
16 use BitCode\BitForm\Core\Database\FormModel;
17 use BitCode\BitForm\Core\Database\ReportsModel;
18 use BitCode\BitForm\Core\Fallback\FormEntryMetaChange;
19 use BitCode\BitForm\Core\Form\FormHandler;
20 use BitCode\BitForm\Core\Integration\Integrations;
21 use BitCode\BitForm\Core\Util\Activation;
22 use BitCode\BitForm\Core\Util\Deactivation;
23 use BitCode\BitForm\Core\Util\FileDownloadProvider;
24 use BitCode\BitForm\Core\Util\GutenBlockProvider;
25 use BitCode\BitForm\Core\Util\IpTool;
26 use BitCode\BitForm\Core\Util\Uninstallation;
27
28 final class Plugin
29 {
30 /**
31 * Main instance of the plugin.
32 *
33 * @since 1.0.0-alpha
34 * @var Plugin|null
35 */
36 private static $instance = null;
37
38 /**
39 * Registers the plugin with WordPress.
40 *
41 * @since 1.0.0-alpha
42 */
43 public function register()
44 {
45 add_action('plugins_loaded', [$this, 'init_plugin']);
46 (new Activation())->activate();
47 (new Deactivation())->register();
48 (new Uninstallation())->register();
49 }
50
51 /**
52 * Initialize the hooks
53 *
54 * @return void
55 */
56 public function init_hooks()
57 {
58 add_action('init', [$this, 'registerBitformsPostType']);
59 add_action('init', [$this, 'registerCustomPostType']);
60 add_action('bitforms_exec_integrations', [Integrations::class, 'integrationExecutionHelper'], 1, 5);
61 // Localize our plugin
62 add_action('init', [$this, 'localization_setup']);
63
64 // initialize the classes
65 add_action('init', [$this, 'init_classes']);
66
67 add_action('init', [$this, 'versionUpdateFallbacks']);
68
69 add_filter('plugin_action_links_' . plugin_basename(BITFORMS_PLUGIN_MAIN_FILE), [$this, 'plugin_action_links']);
70 }
71
72 public function versionUpdateFallbacks()
73 {
74 $installed = get_option('bitforms_installed');
75 $oldversion = null;
76 if ($installed) {
77 $oldversion = get_option('bitforms_version');
78 }
79 if ($oldversion && version_compare($oldversion, BITFORMS_VERSION, '!=')) {
80 update_option('bitforms_version', BITFORMS_VERSION);
81 if (version_compare('1.0.4', $oldversion, '>=')) {
82 // if version is lower or equal than 1.0.4
83 $formEntryMetaChange = new FormEntryMetaChange();
84 $formEntryMetaChange->changeMeta();
85 }
86
87 if (version_compare('1.4.1', $oldversion, '>=')) {
88 // if version is lower or equal than 1.4.1
89 $this->addButtonToFieds();
90 }
91
92 if (version_compare('1.4.7', $oldversion, '>=')) {
93 // if version is equal or lower than 1.4.7
94 $this->validationErrMsgFallback();
95 }
96
97 if (version_compare('1.4.11', $oldversion, '>=')) {
98 // if version is equal or lower than 1.4.11
99 $this->buttonValidsObjectFallback();
100 }
101
102 if (version_compare('1.4.12.2', $oldversion, '>=')) {
103 // if version is equal or lower than 1.4.12.2
104 $this->validationEmailFallback();
105 }
106 if (version_compare('1.5.2', $oldversion, '>=')) {
107 $this->reportFallback();
108 }
109 if (version_compare('1.7.2', $oldversion, '>=')) {
110 $this->optionFallBack();
111 }
112 }
113 }
114
115 public function reportFallback()
116 {
117 $reportMdl = new ReportsModel();
118 global $wpdb;
119 $tablename = $wpdb->prefix . 'bitforms_reports';
120
121 // $latestIds = $wpdb->get_results("SELECT MAX(`id`) as latest_id FROM $tablename GROUP BY `context`");
122 // $sql = $wpdb->prepare("SELECT MAX(`id`) as latest_id FROM {$tablename} GROUP BY `context`");
123 $wpdb->query(
124 $wpdb->prepare(
125 "SELECT MAX(`id`) as latest_id FROM $tablename GROUP BY `context`"
126 )
127 );
128 $latestIds = $wpdb->last_result;
129
130 $notDeletedIds = array_column($latestIds, 'latest_id');
131
132 if (!is_wp_error($latestIds) && count($notDeletedIds) > 0) {
133 $reportMdl->bulkDelete(['id' => $notDeletedIds], 'NOT IN');
134 }
135
136 $ipTool = new IpTool();
137 $user_details = $ipTool->getUserDetail();
138
139 $formModel = new FormModel();
140
141 $forms = $formModel->get(
142 ['id']
143 );
144
145 $details = [
146 'report_name' => 'All Entries',
147 'hiddenColumns' => [],
148 'pageSize' => 10,
149 'sortBy' => [],
150 'filters' => [],
151 'globalFilter' => '',
152 'order' => [],
153 ];
154
155 foreach ($forms as $form) {
156 $existReportId = $reportMdl->get(
157 [
158 'id',
159 ],
160 [
161 'context' => $form->id,
162 ]
163 );
164 if (is_wp_error($existReportId)) {
165 $defaultReport = [
166 'type' => 'table',
167 'category' => 'form',
168 'context' => $form->id,
169 'details' => wp_json_encode($details),
170 'isDefault' => (bool) 1,
171 'user_id' => $user_details['id'],
172 'user_ip' => $user_details['ip'],
173 'user_device' => $user_details['device'],
174 'created_at' => $user_details['time'],
175 'updated_at' => $user_details['time'],
176 ];
177
178 $reportMdl->insert($defaultReport);
179 }
180 }
181 }
182
183 public function validationErrMsgFallback()
184 {
185 $formModel = new FormModel();
186 $forms = $formModel->get(
187 ['id', 'form_content']
188 );
189
190 if (isset($forms) && !is_wp_error($forms)) {
191 foreach ($forms as $form) {
192 $formID = $form->id;
193 $formContent = json_decode($form->form_content);
194 $fields = $formContent->fields;
195
196 foreach ($fields as $fldKey => $fldData) {
197 if (isset($fldData->valid->req)) {
198 if (!isset($fldData->err)) {
199 $fldData->err = (object) [];
200 }
201 $fldData->err->req = (object) [
202 'show' => true,
203 'dflt' => 'This field is required',
204 ];
205 }
206
207 if ('check' === $fldData->typ) {
208 $reqOpts = array_map(function ($op) {
209 return $op->lbl;
210 }, array_filter($fldData->opt, function ($ot) {
211 return isset($ot->req);
212 }));
213 if (!empty($reqOpts)) {
214 if (!isset($fldData->err)) {
215 $fldData->err = (object) [];
216 }
217 $fldData->err->req = (object) [
218 'show' => true,
219 'dflt' => implode(', ', $reqOpts) . ' is required',
220 ];
221 }
222 }
223
224 if ('number' === $fldData->typ) {
225 if (!isset($fldData->err)) {
226 $fldData->err = (object) [];
227 }
228 $fldData->err->invalid = (object) [
229 'show' => true,
230 'dflt' => 'Number is invalid',
231 ];
232
233 if (isset($fldData->mn)) {
234 $fldData->err->mn = (object) [
235 'show' => true,
236 'dflt' => 'Minimum number is ' . $fldData->mn,
237 ];
238 }
239
240 if (isset($fldData->mx)) {
241 $fldData->err->mx = (object) [
242 'show' => true,
243 'dflt' => 'Maximum number is ' . $fldData->mx,
244 ];
245 }
246 }
247
248 if ('email' === $fldData->typ) {
249 $fldData->pattern = '^$_bf_$w+([.-]?$_bf_$w+)*@$_bf_$w+([.-]?$_bf_$w+)*($_bf_$.$_bf_$w{2,3})+$';
250 if (!isset($fldData->err)) {
251 $fldData->err = (object) [];
252 }
253 $fldData->err->invalid = (object) [
254 'show' => true,
255 'dflt' => 'Email is invalid',
256 ];
257 }
258
259 if ('url' === $fldData->typ) {
260 $fldData->attr->pattern = '^(?:(?:https?|ftp):$_bf_$/$_bf_$/)?(?:(?!(?:10|127)(?:$_bf_$.$_bf_$d{1,3}){3})(?!(?:169$_bf_$.254|192$_bf_$.168)(?:$_bf_$.$_bf_$d{1,3}){2})(?!172$_bf_$.(?:1[6-9]|2$_bf_$d|3[0-1])(?:$_bf_$.$_bf_$d{1,3}){2})(?:[1-9]$_bf_$d?|1$_bf_$d$_bf_$d|2[01]$_bf_$d|22[0-3])(?:$_bf_$.(?:1?$_bf_$d{1,2}|2[0-4]$_bf_$d|25[0-5])){2}(?:$_bf_$.(?:[1-9]$_bf_$d?|1$_bf_$d$_bf_$d|2[0-4]$_bf_$d|25[0-4]))|(?:(?:[a-z$_bf_$u00a1-$_bf_$uffff0-9]-*)*[a-z$_bf_$u00a1-$_bf_$uffff0-9]+)(?:$_bf_$.(?:[a-z$_bf_$u00a1-$_bf_$uffff0-9]-*)*[a-z$_bf_$u00a1-$_bf_$uffff0-9]+)*(?:$_bf_$.(?:[a-z$_bf_$u00a1-$_bf_$uffff]{2,})))(?::$_bf_$d{2,5})?(?:$_bf_$/$_bf_$S*)?$';
261 if (!isset($fldData->err)) {
262 $fldData->err = (object) [];
263 }
264 $fldData->err->invalid = [
265 'show' => true,
266 'dflt' => 'URL is invalid',
267 ];
268 }
269 $fields->{$fldKey} = $fldData;
270 }
271
272 $formContent->fields = $fields;
273
274 $formModel->update(
275 [
276 'form_content' => wp_json_encode($formContent),
277 ],
278 [
279 'id' => $formID,
280 ]
281 );
282 }
283 }
284 }
285
286 public function validationEmailFallback()
287 {
288 $formModel = new FormModel();
289 $forms = $formModel->get(
290 ['id', 'form_content']
291 );
292
293 if (!is_wp_error($forms)) {
294 foreach ($forms as $form) {
295 $formID = $form->id;
296 $formContent = json_decode($form->form_content);
297 $fields = $formContent->fields;
298
299 foreach ($fields as $fldKey => $fldData) {
300 if ('email' === $fldData->typ) {
301 $fldData->pattern = '^$_bf_$w+([.-]?$_bf_$w+)*@$_bf_$w+([.-]?$_bf_$w+)*($_bf_$.$_bf_$w{1,24})+$';
302 }
303 $fields->{$fldKey} = $fldData;
304 }
305
306 $formContent->fields = $fields;
307
308 $formModel->update(
309 [
310 'form_content' => wp_json_encode($formContent),
311 ],
312 [
313 'id' => $formID,
314 ]
315 );
316 }
317 }
318 }
319
320 public function buttonValidsObjectFallback()
321 {
322 $formModel = new FormModel();
323 $forms = $formModel->get(
324 ['id', 'form_content']
325 );
326
327 if (!is_wp_error($forms)) {
328 foreach ($forms as $form) {
329 $formID = $form->id;
330 $formContent = json_decode($form->form_content);
331 $fields = $formContent->fields;
332
333 foreach ($fields as $fldKey => $fldData) {
334 if ('button' === $fldData->typ && !isset($fldData->valid)) {
335 $fldData->valid = (object) [];
336 }
337 $fields->{$fldKey} = $fldData;
338 }
339
340 $formContent->fields = $fields;
341
342 $formModel->update(
343 [
344 'form_content' => wp_json_encode($formContent),
345 ],
346 [
347 'id' => $formID,
348 ]
349 );
350 }
351 }
352 }
353
354 private function commaReplace($options, $lbl, $val)
355 {
356 foreach ($options as $key => $option) {
357 if (!empty($option[$lbl]) && !empty($option[$val])) {
358 $options[$key][$val] = str_replace(',', '_', $option[$val]);
359 }
360 if (!empty($option[$lbl]) && empty($option[$val])) {
361 $options[$key][$val] = str_replace(',', '_', $option[$lbl]);
362 }
363 }
364 return $options;
365 }
366
367 private function replaceOptionSeparator($formContent)
368 {
369 $updated = false;
370 foreach ($formContent['fields'] as $key => $field) {
371 if ('check' === $field['typ']) {
372 $formContent['fields'][$key]['opt'] = $this->commaReplace($field['opt'], 'lbl', 'val');
373 $updated = true;
374 }
375 if ('select' === $field['typ']) {
376 $formContent['fields'][$key]['opt'] = $this->commaReplace($field['opt'], 'label', 'value');
377 $updated = true;
378 }
379 }
380 return [$formContent, $updated];
381 }
382
383 public function optionFallBack()
384 {
385 $formModel = new FormModel();
386 $forms = $formModel->get(
387 ['id', 'form_content']
388 );
389
390 foreach ($forms as $form) {
391 $formContent = json_decode($form->form_content, true);
392 $optReplaceComma = $this->replaceOptionSeparator($formContent);
393 if ($optReplaceComma[1]) {
394 $formModel->update(
395 [
396 'form_content' => wp_json_encode($optReplaceComma[0]),
397 ],
398 [
399 'id' => $form->id,
400 ]
401 );
402 }
403 }
404 }
405
406 /**
407 * Updates bit form content tables on wp db
408 *
409 * @return void
410 */
411 public function update_tables()
412 {
413 if (!current_user_can('manage_options')) {
414 return;
415 }
416
417 global $bitforms_db_version;
418 $installed_db_version = get_site_option('bitforms_db_version');
419 if ($installed_db_version !== $bitforms_db_version) {
420 DB::migrate();
421 }
422 }
423
424 /**
425 * Register post type for bitforms
426 *
427 * @return void
428 */
429 public function registerBitformsPostType()
430 {
431 $args = [
432 'label' => __('Bitform Pages', 'bit-form'),
433 'public' => true,
434 'show_ui' => true,
435 'show_in_menu' => false,
436 'capability_type' => 'page',
437 'hierarchical' => false,
438 'query_var' => false,
439 'supports' => ['title'],
440 'show_in_rest' => false,
441 ];
442 register_post_type('bitforms', $args);
443 }
444
445 public function registerCustomPostType()
446 {
447 flush_rewrite_rules(false);
448 $cpts = get_option('bitform_custom_post_types');
449 if (!empty($cpts)) {
450 foreach ($cpts as $cpt) {
451 $labels = [
452 'name' => _x($cpt->name, 'Post type general name', 'bit-form'),
453 'singular_name' => _x($cpt->singular_label, 'Post type singular name', 'bit-form'),
454 'menu_name' => _x($cpt->menu_name, 'Admin Menu text', 'bit-form'),
455 ];
456 $args = [
457 'labels' => $labels,
458 'public' => 1 === $cpt->public ? true : false,
459 'publicly_queryable' => 1 === $cpt->public_queryable ? true : false,
460 'show_ui' => 1 === $cpt->show_ui ? true : false,
461 'show_in_menu' => 1 === $cpt->show_in_menu ? true : false,
462 'query_var' => true,
463 'rewrite' => ['slug' => $cpt->name],
464 'capability_type' => 'post',
465 'has_archive' => true,
466 'hierarchical' => false,
467 'show_in_rest' => 1 === isset($cpt->show_in_rest) ? true : false,
468 'menu_icon' => $cpt->menu_icon,
469 'supports' => ['title', 'editor', 'author', 'excerpt', 'comments'],
470 ];
471 register_post_type($cpt->name, $args);
472 }
473 }
474 }
475
476 /**
477 * Initialize plugin for localization
478 *
479 * @uses load_plugin_textdomain()
480 */
481 public function localization_setup()
482 {
483 load_plugin_textdomain('bitform', false, dirname(BITFORMS_PLUGIN_BASENAME) . '/languages');
484 }
485
486 /**
487 * Instantiate the required classes
488 *
489 * @return void
490 */
491 public function init_classes()
492 {
493 if (Request::Check('admin')) {
494 (new Admin_Bar())->register();
495 }
496 if (Request::Check('ajax')) {
497 new AjaxService();
498 }
499 if (Request::Check('frontend')) {
500 $formHandler = new FormHandler();
501 $formHandler->frontend;
502 }
503 if (Request::isPluginPage()) {
504 (new FileDownloadProvider())->register();
505 }
506 if (current_user_can('edit_posts')) {
507 (new GutenBlockProvider())->register();
508 }
509 }
510
511 /**
512 * Plugin action links
513 *
514 * @param array $links
515 *
516 * @return array
517 */
518 public function plugin_action_links($links)
519 {
520 $links[] = '<a href="https://docs.form.bitapps.pro" target="_blank">' . __('Docs', 'bit-form') . '</a>';
521
522 return $links;
523 }
524
525 public function init_plugin()
526 {
527 $this->init_hooks();
528 $this->update_tables();
529 do_action('bitform_loaded');
530 }
531
532 /**
533 * Retrieves the main instance of the plugin.
534 *
535 * @since 1.0.0-alpha
536 *
537 * @return BITFORM Plugin main instance.
538 */
539 public static function instance()
540 {
541 return static::$instance;
542 }
543
544 /**
545 * Loads the plugin main instance and initializes it.
546 *
547 * @param string $main_file Absolute path to the plugin main file.
548 *
549 * @return bool True if the plugin main instance could be loaded, false otherwise.
550 */
551 public static function load($main_file)
552 {
553 if (null !== static::$instance) {
554 return false;
555 }
556
557 static::$instance = new static($main_file);
558 static::$instance->register();
559 return true;
560 }
561
562 public function addButtonToFieds()
563 {
564 // if version is lower or equal than 1.3.13
565 $formModel = new FormModel();
566 $forms = $formModel->get(
567 ['id', 'form_content'],
568 [
569 'form_content' => ['operator' => 'LIKE', 'value' => '%\"buttons\":%'],
570 ]
571 );
572 if (!is_wp_error($forms)) {
573 $adminFormHandler = new AdminFormHandler();
574 foreach ($forms as $form) {
575 $formID = $form->id;
576 $formContent = json_decode($form->form_content);
577 $fields = wp_json_encode($formContent->fields);
578 if (isset($formContent->buttons)) {
579 if (false === strpos($fields, '"btnTyp":"submit"')) {
580 $align = isset($formContent->buttons->align) ? $formContent->buttons->align : 'right';
581 $btnSiz = isset($formContent->buttons->btnSiz) ? $formContent->buttons->btnSiz : 'md';
582 $fulW = isset($formContent->buttons->fulW) ? $formContent->buttons->fulW : false;
583 $btnData = [
584 'typ' => 'button',
585 'btnSiz' => $btnSiz,
586 'fulW' => $fulW,
587 ];
588 $newID = 0;
589 foreach ($formContent->fields as $key => $value) {
590 $oldID = \preg_replace('/bf?\d+-(\d+)-?/', '$1', $key);
591 if ($oldID > $newID) {
592 $newID = $oldID;
593 }
594 }
595 $newID = $newID + 1;
596 $sBtnID = "bf${formID}-${newID}";
597 $lY = 0;
598 $mY = 0;
599 $sY = 0;
600 foreach ($formContent->layout->lg as $lg) {
601 if ($lg->y > $lY) {
602 $lY = $lg->y;
603 }
604 }
605 $lY = $lY + $lg->h + 1;
606 foreach ($formContent->layout->md as $md) {
607 if ($md->y > $mY) {
608 $mY = $md->y;
609 }
610 }
611 $mY = $mY + $md->h + 1;
612 foreach ($formContent->layout->sm as $sm) {
613 if ($sm->y > $sY) {
614 $sY = $sm->y;
615 }
616 }
617 $sY = $sY + $sm->h + 1;
618 if (isset($formContent->buttons->rstBtnTxt)) {
619 $newID += 1;
620 $rBtnID = "bf${formID}-${newID}";
621
622 if ($fulW) {
623 $btnData['btnTyp'] = 'submit';
624 $btnData['align'] = $align;
625 $btnData['txt'] = $formContent->buttons->subBtnTxt;
626 $formContent->fields->{$sBtnID} = (object) $btnData;
627 $rBtnData = $btnData;
628 $rBtnData['btnTyp'] = 'reset';
629 $rBtnData['align'] = $align;
630 $rBtnData['txt'] = $formContent->buttons->rstBtnTxt;
631 $formContent->fields->{$rBtnID} = (object) $rBtnData;
632 } else {
633 $btnData['btnTyp'] = 'submit';
634 $btnData['align'] = 'right';
635 $btnData['txt'] = $formContent->buttons->subBtnTxt;
636 $formContent->fields->{$sBtnID} = (object) $btnData;
637 $rBtnData = $btnData;
638 $rBtnData['btnTyp'] = 'reset';
639 $rBtnData['align'] = 'left';
640 $rBtnData['txt'] = $formContent->buttons->rstBtnTxt;
641 $formContent->fields->{$rBtnID} = (object) $rBtnData;
642 }
643 $subBtnLay = [
644 'h' => 2,
645 'i' => $sBtnID,
646 'minH' => 2,
647 'x' => 0,
648 'y' => 1000,
649 ];
650
651 $subBtnLay['w'] = 3;
652 $subBtnLay['y'] = $lY;
653
654 $formContent->layout->lg[] = (object) $subBtnLay;
655
656 $subBtnLay['w'] = 2;
657 $subBtnLay['y'] = $mY;
658
659 $formContent->layout->md[] = (object) $subBtnLay;
660
661 $subBtnLay['w'] = 1;
662 $subBtnLay['y'] = $sY;
663
664 $formContent->layout->sm[] = (object) $subBtnLay;
665
666 $subBtnLay['i'] = $rBtnID;
667 $subBtnLay['x'] = 3;
668 $subBtnLay['w'] = 3;
669 $subBtnLay['y'] = $lY;
670
671 $formContent->layout->lg[] = (object) $subBtnLay;
672
673 $subBtnLay['x'] = 2;
674 $subBtnLay['w'] = 2;
675 $subBtnLay['y'] = $mY;
676
677 $formContent->layout->md[] = (object) $subBtnLay;
678
679 $subBtnLay['w'] = 1;
680 $subBtnLay['x'] = 1;
681 $subBtnLay['y'] = $sY;
682
683 $formContent->layout->sm[] = (object) $subBtnLay;
684 } else {
685 $btnData['btnTyp'] = 'submit';
686 $btnData['txt'] = $formContent->buttons->subBtnTxt;
687 $btnData['align'] = $align;
688 $formContent->fields->{$sBtnID} = (object) $btnData;
689
690 $subBtnLay = [
691 'h' => 2,
692 'i' => $sBtnID,
693 'minH' => 2,
694 'x' => 0,
695 'w' => 6,
696 ];
697
698 $subBtnLay['y'] = $lY;
699
700 $formContent->layout->lg[] = (object) $subBtnLay;
701
702 $subBtnLay['y'] = $mY;
703
704 $formContent->layout->md[] = (object) $subBtnLay;
705
706 $subBtnLay['y'] = $sY;
707
708 $formContent->layout->sm[] = (object) $subBtnLay;
709 }
710 }
711 $adminFormHandler->saveLayoutStyleSheet($formContent->layout, 'bitform-layout-' . $formID . '.css');
712 unset($formContent->buttons);
713 $formModel->update(
714 [
715 'form_content' => wp_json_encode($formContent),
716 ],
717 [
718 'id' => $formID,
719 ]
720 );
721 }
722 }
723 }
724 }
725 }
726