PluginProbe
Booking for Appointments and Events Calendar – Amelia / 1.2.10
Booking for Appointments and Events Calendar – Amelia v1.2.10
2.4.9 2.4.8 2.4.7 2.4.6 2.4.5 2.4.4 2.4.3 2.4.2 2.4.1 2.4 trunk 1.2.1 1.2.10 1.2.11 1.2.12 1.2.13 1.2.14 1.2.15 1.2.16 1.2.17 1.2.18 1.2.19 1.2.2 1.2.20 1.2.21 All 59 releases
ameliabooking / ameliabooking.php

ameliabooking.php in Booking for Appointments and Events Calendar – Amelia 1.2.10, at ameliabooking.php

592 lines 20.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /*
3 Plugin Name: Amelia
4 Plugin URI: https://wpamelia.com/
5 Description: Amelia is a simple yet powerful automated booking specialist, working 24/7 to make sure your customers can make appointments and events even while you sleep!
6 Version: 1.2.10
7 Author: TMS
8 Author URI: https://tmsproducts.io/
9 Text Domain: wpamelia
10 Domain Path: /languages
11 License: GPLv2 or later
12 License URI: http://www.gnu.org/licenses/gpl-2.0.html
13 */
14
15 namespace AmeliaBooking;
16
17 use AmeliaBooking\Domain\Services\Settings\SettingsService;
18 use AmeliaBooking\Infrastructure\Common\Container;
19 use AmeliaBooking\Infrastructure\Routes\Routes;
20 use AmeliaBooking\Infrastructure\Services\Payment\SquareService;
21 use AmeliaBooking\Infrastructure\WP\ButtonService\ButtonService;
22 use AmeliaBooking\Infrastructure\WP\config\Menu;
23 use AmeliaBooking\Infrastructure\WP\Elementor\ElementorBlock;
24 use AmeliaBooking\Infrastructure\WP\ErrorService\ErrorService;
25 use AmeliaBooking\Infrastructure\WP\GutenbergBlock\AmeliaBookingGutenbergBlock;
26 use AmeliaBooking\Infrastructure\WP\GutenbergBlock\AmeliaStepBookingGutenbergBlock;
27 use AmeliaBooking\Infrastructure\WP\GutenbergBlock\AmeliaCatalogBookingGutenbergBlock;
28 use AmeliaBooking\Infrastructure\WP\GutenbergBlock\AmeliaCatalogGutenbergBlock;
29 use AmeliaBooking\Infrastructure\WP\GutenbergBlock\AmeliaEventsGutenbergBlock;
30 use AmeliaBooking\Infrastructure\WP\GutenbergBlock\AmeliaEventsListBookingGutenbergBlock;
31 use AmeliaBooking\Infrastructure\WP\Integrations\WooCommerce\StarterWooCommerceService;
32 use AmeliaBooking\Infrastructure\WP\SettingsService\SettingsStorage;
33 use AmeliaBooking\Infrastructure\WP\Translations\BackendStrings;
34 use AmeliaBooking\Infrastructure\WP\UserRoles\UserRoles;
35 use AmeliaBooking\Infrastructure\WP\WPMenu\Submenu;
36 use AmeliaBooking\Infrastructure\WP\WPMenu\SubmenuPageHandler;
37 use Exception;
38 use Interop\Container\Exception\ContainerException;
39 use Slim\App;
40
41 // No direct access
42 defined('ABSPATH') or die('No script kiddies please!');
43
44 // Const for path root
45 if (!defined('AMELIA_PATH')) {
46 define('AMELIA_PATH', __DIR__);
47 }
48
49 // Const for uploads path
50 if (!defined('AMELIA_UPLOADS_PATH')) {
51 $uploadDir = wp_upload_dir();
52 define('AMELIA_UPLOADS_PATH', $uploadDir['basedir']);
53 }
54
55 // Const for uploads url
56 if (!defined('AMELIA_UPLOADS_URL')) {
57 $uploadUrl = wp_upload_dir();
58 define('AMELIA_UPLOADS_URL', set_url_scheme($uploadUrl['baseurl']));
59 }
60
61 // Const for uploads url
62 if (!defined('AMELIA_UPLOADS_FILES_URL')) {
63 define('AMELIA_UPLOADS_FILES_URL', AMELIA_UPLOADS_URL . '/amelia/files/');
64 }
65
66 // Const for uploads files path
67 if (!defined('AMELIA_UPLOADS_FILES_PATH')) {
68 define('AMELIA_UPLOADS_FILES_PATH', AMELIA_UPLOADS_PATH . '/amelia/files/');
69 }
70
71 // Const for uploads files path
72 if (!defined('AMELIA_UPLOADS_FILES_PATH_USE')) {
73 define('AMELIA_UPLOADS_FILES_PATH_USE', true);
74 }
75
76 // Const for URL root
77 if (!defined('AMELIA_URL')) {
78 define('AMELIA_URL', plugin_dir_url(__FILE__));
79 }
80
81 if (!defined('AMELIA_HOME_URL')) {
82 define('AMELIA_HOME_URL', get_home_url());
83 }
84
85 // Const for URL Actions identifier
86 if (!defined('AMELIA_ACTION_SLUG')) {
87 define('AMELIA_ACTION_SLUG', 'action=wpamelia_api&call=');
88 }
89
90 // Const for URL Actions identifier
91 if (!defined('AMELIA_ACTION_URL')) {
92 define('AMELIA_ACTION_URL', admin_url('admin-ajax.php', '') . '?' . AMELIA_ACTION_SLUG);
93 }
94
95 // Const for URL Actions identifier
96 if (!defined('AMELIA_PAGE_URL')) {
97 define('AMELIA_PAGE_URL', get_site_url() . '/wp-admin/admin.php?page=');
98 }
99
100 // Const for URL Actions identifier
101 if (!defined('AMELIA_LOGIN_URL')) {
102 define('AMELIA_LOGIN_URL', get_site_url() . '/wp-login.php?redirect_to=');
103 }
104
105 // Const for Amelia version
106 if (!defined('AMELIA_VERSION')) {
107 define('AMELIA_VERSION', '1.2.10');
108 }
109
110 // Const for site URL
111 if (!defined('AMELIA_SITE_URL')) {
112 define('AMELIA_SITE_URL', get_site_url());
113 }
114
115 // Const for plugin basename
116 if (!defined('AMELIA_PLUGIN_SLUG')) {
117 define('AMELIA_PLUGIN_SLUG', plugin_basename(__FILE__));
118 }
119
120 // Const for Amelia SMS API
121 if (!defined('AMELIA_SMS_API_URL')) {
122 define('AMELIA_SMS_API_URL', 'https://smsapi.wpamelia.com/');
123 define('AMELIA_SMS_VENDOR_ID', 36082);
124 define('AMELIA_SMS_PRODUCT_ID_10', 595657);
125 define('AMELIA_SMS_PRODUCT_ID_20', 595658);
126 define('AMELIA_SMS_PRODUCT_ID_50', 595659);
127 define('AMELIA_SMS_PRODUCT_ID_100', 595660);
128 define('AMELIA_SMS_PRODUCT_ID_200', 595661);
129 define('AMELIA_SMS_PRODUCT_ID_500', 595662);
130 }
131
132 if (!defined('AMELIA_STORE_API_URL')) {
133 define('AMELIA_STORE_API_URL', 'https://store.tms-plugins.com/api/');
134 }
135
136 if (!defined('AMELIA_MIDDLEWARE_IS_SANDBOX')) {
137 define('AMELIA_MIDDLEWARE_IS_SANDBOX', false);
138 }
139
140 if (!defined('AMELIA_MIDDLEWARE_API_URL')) {
141 define('AMELIA_MIDDLEWARE_API_URL', 'https://middleware.wpamelia.com/');
142 }
143
144 if (!defined('AMELIA_DEV')) {
145 define('AMELIA_DEV', false);
146 }
147
148 if (!defined('AMELIA_NGROK_URL')) {
149 define('AMELIA_NGROK_URL', '97619f3954de.ngrok.app');
150 }
151
152 require_once AMELIA_PATH . '/vendor/autoload.php';
153
154 /**
155 * @noinspection AutoloadingIssuesInspection
156 *
157 * Class Plugin
158 *
159 * @package AmeliaBooking
160 *
161 * @phpcs:ignoreFile
162 * @SuppressWarnings(PHPMD)
163 */
164 class Plugin
165 {
166
167 /**
168 * API Call
169 *
170 * @throws \InvalidArgumentException
171 */
172 public static function wpAmeliaApiCall()
173 {
174 try {
175 /** @var Container $container */
176 $container = require AMELIA_PATH . '/src/Infrastructure/ContainerConfig/container.php';
177
178 $app = new App($container);
179
180 // Initialize all API routes
181 Routes::routes($app, $container);
182
183 $app->run();
184
185 exit();
186 } catch (Exception $e) {
187 echo 'ERROR: ' . esc_html($e->getMessage());
188 }
189 }
190
191 static function square_weekly_token_refresh( $schedules ) {
192 $schedules['weekly'] = array(
193 'interval' => 604800,
194 'display' => __('Add weekly cron to refresh square access token every 7 days')
195 );
196 return $schedules;
197 }
198
199 /**
200 * Initialize the plugin
201 */
202 public static function init()
203 {
204 $settingsService = new SettingsService(new SettingsStorage());
205
206 self::weglotConflict($settingsService, true);
207
208 // Const for path root
209 if (!defined('AMELIA_LOCALE')) {
210 define('AMELIA_LOCALE', get_user_locale());
211 }
212
213 load_plugin_textdomain('wpamelia', false, plugin_basename(__DIR__) . '/languages/' . AMELIA_LOCALE . '/');
214
215 self::weglotConflict($settingsService, false);
216
217 if (StarterWooCommerceService::isEnabled()) {
218 if (!empty($settingsService->getCategorySettings('payments')['wc']['dashboard'])) {
219 add_filter('woocommerce_prevent_admin_access', '__return_false');
220 }
221
222 if (!empty($settingsService->getCategorySettings('payments')['wc']['enabled'])) {
223 try {
224 StarterWooCommerceService::init($settingsService);
225 } catch (ContainerException $e) {
226 }
227 } else {
228 StarterWooCommerceService::setContainer(require AMELIA_PATH . '/src/Infrastructure/ContainerConfig/container.php');
229 StarterWooCommerceService::$settingsService = $settingsService;
230
231 add_filter('woocommerce_after_order_itemmeta', [StarterWooCommerceService::class, 'orderItemMeta'], 10, 3);
232 }
233 }
234
235 if (!empty($settingsService->getCategorySettings('payments')['square']['enabled']) &&
236 !empty($settingsService->getCategorySettings('payments')['square']['accessToken'])) {
237 add_filter( 'cron_schedules', [self::class, 'square_weekly_token_refresh'] );
238
239 if ( ! wp_next_scheduled( 'amelia_square_access_token_refresh' ) ) {
240 wp_schedule_event( time(), 'weekly', 'amelia_square_access_token_refresh' );
241 }
242
243 /** @var Container $container */
244 $container = require AMELIA_PATH . '/src/Infrastructure/ContainerConfig/container.php';
245
246 /** @var SquareService $squareService */
247 $squareService = $container->get('infrastructure.payment.square.service');
248
249 add_action( 'amelia_square_access_token_refresh', [$squareService, 'refreshAccessToken'] );
250 }
251
252 $ameliaRole = UserRoles::getUserAmeliaRole(wp_get_current_user());
253
254 // Init menu if user is logged in with amelia role
255 if (in_array($ameliaRole, ['admin', 'manager', 'provider', 'customer'])) {
256 if ($ameliaRole === 'admin') {
257 ErrorService::setNotices();
258 }
259
260 $menuItems = new Menu($settingsService);
261
262 // Init admin menu
263 $wpMenu = new Submenu(
264 new SubmenuPageHandler($settingsService),
265 $menuItems()
266 );
267 $wpMenu->init();
268
269 // Add TinyMCE button for shortcode generator
270 ButtonService::renderButton();
271
272 // Add Gutenberg Block for shortcode generator
273 AmeliaStepBookingGutenbergBlock::init();
274 AmeliaCatalogBookingGutenbergBlock::init();
275 AmeliaBookingGutenbergBlock::init();
276 AmeliaCatalogGutenbergBlock::init();
277 AmeliaEventsGutenbergBlock::init();
278 AmeliaEventsListBookingGutenbergBlock::init();
279
280
281 add_filter('block_categories_all', array('AmeliaBooking\Plugin', 'addAmeliaBlockCategory'), 10, 2);
282 add_filter('learn-press/frontend-default-scripts', array('AmeliaBooking\Plugin', 'learnPressConflict'));
283 }
284
285 if (!is_admin()) {
286 add_filter('learn-press/frontend-default-scripts', array('AmeliaBooking\Plugin', 'learnPressConflict'));
287 add_shortcode('ameliabooking', array('AmeliaBooking\Infrastructure\WP\ShortcodeService\BookingShortcodeService', 'shortcodeHandler'));
288 add_shortcode('ameliacatalog', array('AmeliaBooking\Infrastructure\WP\ShortcodeService\CatalogShortcodeService', 'shortcodeHandler'));
289 add_shortcode('ameliaevents', array('AmeliaBooking\Infrastructure\WP\ShortcodeService\EventsShortcodeService', 'shortcodeHandler'));
290 add_shortcode('ameliaeventslistbooking', array('AmeliaBooking\Infrastructure\WP\ShortcodeService\EventsListBookingShortcodeService', 'shortcodeHandler'));
291 add_shortcode('ameliastepbooking', array('AmeliaBooking\Infrastructure\WP\ShortcodeService\StepBookingShortcodeService', 'shortcodeHandler'));
292 add_shortcode('ameliacatalogbooking', array('AmeliaBooking\Infrastructure\WP\ShortcodeService\CatalogBookingShortcodeService', 'shortcodeHandler'));
293 }
294
295 if (defined('ELEMENTOR_VERSION')) {
296 ElementorBlock::get_instance();
297 }
298
299 require_once AMELIA_PATH . '/extensions/divi_amelia/divi_amelia.php';
300
301
302 }
303
304 /**
305 * Creating Amelia block category in Gutenberg
306 */
307 public static function addAmeliaBlockCategory($categories, $post)
308 {
309 return array_merge(
310 array(
311 array(
312 'slug' => 'amelia-blocks',
313 'title' => 'Amelia',
314 ),
315 ),
316 $categories
317 );
318 }
319
320 /**
321 * Fix for conflict with Weglot plugin
322 * @param $settingsService
323 * @param $init
324 */
325 public static function weglotConflict($settingsService, $init)
326 {
327 if (defined('AMELIA_LOCALE_FORCED') &&
328 AMELIA_LOCALE_FORCED &&
329 function_exists('weglot_get_current_language')
330 ) {
331 try {
332 if ($init && !defined('AMELIA_LOCALE')) {
333 $weglotCurrentLanguage = weglot_get_current_language();
334
335 $ameliaUsedLanguages = array_flip($settingsService->getSetting('general', 'usedLanguages'));
336
337 require_once ABSPATH . 'wp-admin/includes/translation-install.php';
338
339 global $locale;
340
341 $potentialLanguages = [];
342
343 foreach (wp_get_available_translations() as $key => $value) {
344 if (substr($key, 0, 2) === substr($weglotCurrentLanguage, 0, 2)) {
345 $potentialLanguages[] = $key;
346 }
347 }
348
349 foreach ($potentialLanguages as $potentialLanguage) {
350 if (array_key_exists($potentialLanguage, $ameliaUsedLanguages)) {
351 $locale = $potentialLanguage;
352 break;
353 }
354 }
355 } else {
356 global $locale;
357
358 $locale = AMELIA_LOCALE_FORCED;
359 }
360 } catch (\Exception $e) {
361
362 }
363 }
364 }
365
366 /**
367 * Fix for conflict with LearnPress plugin
368 */
369 public static function learnPressConflict($data)
370 {
371
372 if (has_shortcode(get_post(get_the_ID())->post_content, 'ameliabooking') ||
373 has_shortcode(get_post(get_the_ID())->post_content, 'ameliacatalog') ||
374 has_shortcode(get_post(get_the_ID())->post_content, 'ameliaevents') ||
375 has_shortcode(get_post(get_the_ID())->post_content, 'ameliaeventslistbooking') ||
376 has_shortcode(get_post(get_the_ID())->post_content, 'ameliastepbooking')
377 ) {
378 return array();
379 } else {
380 return $data;
381 }
382
383 }
384
385 public static function adminInit()
386 {
387 $settingsService = new SettingsService(new SettingsStorage());
388
389 if (AMELIA_VERSION !== $settingsService->getSetting('activation', 'version')) {
390 $settingsService->setSetting('activation', 'version', AMELIA_VERSION);
391
392 require_once ABSPATH . 'wp-admin/includes/plugin.php';
393
394 deactivate_plugins(AMELIA_PLUGIN_SLUG);
395 activate_plugin(AMELIA_PLUGIN_SLUG);
396 }
397 }
398
399 /**
400 * @param $networkWide
401 */
402 public static function activation($networkWide)
403 {
404 load_plugin_textdomain('wpamelia', false, plugin_basename(__DIR__) . '/languages/' . get_locale() . '/');
405
406 // Check PHP version
407 if (!defined('PHP_VERSION_ID') || PHP_VERSION_ID < 50500) {
408 deactivate_plugins(AMELIA_PLUGIN_SLUG);
409 wp_die(
410 BackendStrings::getCommonStrings()['php_version_message'],
411 BackendStrings::getCommonStrings()['php_version_title'],
412 array('response' => 200, 'back_link' => TRUE)
413 );
414 }
415 //Network activation
416 if ($networkWide && function_exists('is_multisite') && is_multisite()) {
417 Infrastructure\WP\InstallActions\ActivationMultisite::init();
418 }
419
420 Infrastructure\WP\InstallActions\ActivationDatabaseHook::init();
421 }
422
423 /**
424 * @param $dirPath
425 */
426 public static function deleteFolderContent($dirPath)
427 {
428 if (substr($dirPath, strlen($dirPath) - 1, 1) != '/') {
429 $dirPath .= '/';
430 }
431
432 $files = glob($dirPath . '*', GLOB_MARK);
433
434 foreach ($files as $file) {
435 if (is_dir($file)) {
436 self::deleteFolderContent($file);
437 } else {
438 unlink($file);
439 }
440 }
441 }
442
443 /**
444 * @throws Domain\Common\Exceptions\InvalidArgumentException
445 */
446 public static function deletion()
447 {
448 $settingsService = new SettingsService(new SettingsStorage());
449
450 if ($settingsService->getSetting('activation', 'deleteTables')) {
451 //Network deletion
452 if (function_exists('is_multisite') &&
453 is_multisite()
454 ) {
455 Infrastructure\WP\InstallActions\DeletionMultisite::delete();
456 }
457
458 Infrastructure\WP\InstallActions\DeleteDatabaseHook::delete();
459
460
461 // Delete Roles
462 global $wp_roles;
463
464 $wp_roles->remove_role('wpamelia-customer');
465 $wp_roles->remove_role('wpamelia-provider');
466 $wp_roles->remove_role('wpamelia-manager');
467
468
469 // Delete Settings
470 delete_option('amelia_settings');
471 delete_option('amelia_stash');
472 delete_option('amelia_show_wpdt_promo');
473
474 // Delete Files
475 foreach (['/amelia/css', '/amelia/files/tmp', '/amelia/files', '/amelia'] as $path) {
476 if (is_dir(AMELIA_UPLOADS_PATH . $path)) {
477 self::deleteFolderContent(AMELIA_UPLOADS_PATH . $path);
478 rmdir(AMELIA_UPLOADS_PATH . $path);
479 }
480 }
481 }
482 }
483
484
485 public static function elementor_popup_notice(){
486 global $pagenow;
487 if ($pagenow == 'edit.php' &&
488 !empty($_REQUEST['post_type']) &&
489 $_REQUEST['post_type'] === 'elementor_library' &&
490 !empty($_REQUEST['tabs_group']) &&
491 $_REQUEST['tabs_group'] === 'popup'
492 ) {
493 echo "<div class='notice notice-warning'>
494 <p>" . esc_html__(BackendStrings::getCommonStrings()['elementor_popup_notice']) . "</p>
495 </div>";
496 }
497 }
498
499 /**
500 * Show WPDT promo notice
501 **/
502 public static function wpdt_dashboard_promo()
503 {
504 $wpAmeliaPage = isset($_GET['page']) ? $_GET['page'] : '';
505
506 require_once AMELIA_PATH . '/extensions/wpdt/functions.php';
507
508 if( is_admin() && (strpos($wpAmeliaPage,'wpamelia-dashboard') !== false) &&
509 amelia_installed_plugins_wpdt_promotion() &&
510 get_option( 'amelia_show_wpdt_promo' ) == 'yes'
511 ) {
512 include AMELIA_PATH . '/extensions/wpdt/promote_wpdt.php';
513 wp_enqueue_style('wdt-promo-css', AMELIA_URL . 'public/css/backend/promote_wpdt.css');
514 }
515 }
516
517 /**
518 * Remove WPDT promo notice
519 **/
520 public static function amelia_remove_wpdt_promo_notice()
521 {
522 update_option( 'amelia_show_wpdt_promo', 'no' );
523 echo json_encode( array("success") );
524 exit;
525 }
526
527 }
528
529 add_action('wp_ajax_amelia_remove_wpdt_promo_notice', array('AmeliaBooking\Plugin', 'amelia_remove_wpdt_promo_notice'));
530
531 add_action('admin_notices', array('AmeliaBooking\Plugin', 'elementor_popup_notice'));
532 add_action('admin_notices', array('AmeliaBooking\Plugin', 'wpdt_dashboard_promo'));
533
534 /** Redirect For Outlook Calendar */
535 if (is_admin()) {
536 add_action('wp_loaded', array('AmeliaBooking\Infrastructure\Services\Outlook\StarterOutlookCalendarService', 'handleCallback'));
537 }
538
539 /** Isolate API calls */
540 add_action('wp_ajax_wpamelia_api', array('AmeliaBooking\Plugin', 'wpAmeliaApiCall'));
541 add_action('wp_ajax_nopriv_wpamelia_api', array('AmeliaBooking\Plugin', 'wpAmeliaApiCall'));
542
543 /** Init the plugin */
544 add_action('plugins_loaded', array('AmeliaBooking\Plugin', 'init'));
545
546 add_action('admin_init', array('AmeliaBooking\Plugin', 'adminInit'));
547
548 /** Activation hooks */
549 register_activation_hook(__FILE__, array('AmeliaBooking\Plugin', 'activation'));
550 register_activation_hook(__FILE__, array('AmeliaBooking\Infrastructure\WP\InstallActions\ActivationRolesHook', 'init'));
551 register_activation_hook(__FILE__, array('AmeliaBooking\Infrastructure\WP\InstallActions\ActivationSettingsHook', 'init'));
552 register_uninstall_hook(__FILE__, array('AmeliaBooking\Plugin', 'deletion'));
553
554 /** Activation hook for new site on multisite setup */
555 add_action('wpmu_new_blog', array('AmeliaBooking\Infrastructure\WP\InstallActions\ActivationNewSiteMultisite', 'init'));
556
557 /** Define the API for updating checking */
558
559 /** Define the alternative response for information checking */
560
561 /** Add a message for unavailable auto update if plugin is not activated */
562
563 /** Add error message on plugin update if plugin is not activated */
564
565 add_filter('script_loader_tag', array('AmeliaBooking\Infrastructure\WP\ShortcodeService\StepBookingShortcodeService', 'prepareScripts') , 10, 3);
566 add_filter('style_loader_tag', array('AmeliaBooking\Infrastructure\WP\ShortcodeService\StepBookingShortcodeService', 'prepareStyles') , 10, 3);
567
568 add_filter('script_loader_tag', array('AmeliaBooking\Infrastructure\WP\ShortcodeService\EventsListBookingShortcodeService', 'prepareScripts') , 10, 3);
569 add_filter('style_loader_tag', array('AmeliaBooking\Infrastructure\WP\ShortcodeService\EventsListBookingShortcodeService', 'prepareStyles') , 10, 3);
570
571 add_filter('submenu_file', function($submenu_file) {
572 global $submenu;
573
574 if (!empty($submenu['amelia'])) {
575 foreach ($submenu['amelia'] as $index => $item) {
576 foreach ($item as $key => $value) {
577 if ($value === 'wpamelia-customize-new') {
578 unset($submenu['amelia'][$index]);
579
580 break 2;
581 }
582 }
583 }
584 }
585
586 return $submenu_file;
587 });
588
589
590 add_action( 'wp_logout', array('AmeliaBooking\Infrastructure\WP\UserService\UserService', 'logoutAmeliaUser'));
591 add_action( 'profile_update', array('AmeliaBooking\Infrastructure\WP\UserService\UserService', 'updateAmeliaUser'), 10, 3);
592