PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.9.2
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.9.2
4.0.7 4.0.6 4.0.5 4.0.4 4.0.3 3.0.5 3.0.6 3.0.7 3.0.8 3.0.9 3.1.0 3.1.1 3.1.2 3.1.3 3.10.0 3.10.1 3.10.10 3.10.11 3.10.12 3.10.13 3.10.14 3.10.15 3.10.2 3.10.3 3.10.4 All 148 releases
← All changes | classes/Visualizer/Module/Admin.php +90 -571 4.0.43.9.2 View file →
@@ -29,10 +29,8 @@
29 29 class Visualizer_Module_Admin extends Visualizer_Module {
30 30
31 31 const NAME = __CLASS__;
32 32
33 - const OPTION_GLOBAL_SETTINGS = 'visualizer_global_settings';
34 -
35 33 /**
36 34 * Library page suffix.
37 35 *
38 36 * @since 1.0.0
@@ -42,24 +40,8 @@
42 40 */
43 41 private $_libraryPage;
44 42
45 43 /**
46 - * Support page suffix.
47 - *
48 - * @access private
49 - * @var string
50 - */
51 - private $_supportPage;
52 -
53 - /**
54 - * Settings page suffix.
55 - *
56 - * @access private
57 - * @var string
58 - */
59 - private $_settingsPage;
60 -
61 - /**
62 44 * Constructor.
63 45 *
64 46 * @since 1.0.0
65 47 *
@@ -70,14 +52,10 @@
70 52 public function __construct( Visualizer_Plugin $plugin ) {
71 53 parent::__construct( $plugin );
72 54 $this->_addAction( 'load-post.php', 'enqueueMediaScripts' );
73 55 $this->_addAction( 'load-post-new.php', 'enqueueMediaScripts' );
74 - $this->_addAction( 'enqueue_block_editor_assets', 'enqueueMediaScripts' );
75 56 $this->_addAction( 'admin_footer', 'renderTemplates' );
76 57 $this->_addAction( 'admin_enqueue_scripts', 'enqueueLibraryScripts', null, 0 );
77 - $this->_addAction( 'admin_enqueue_scripts', 'enqueue_support_page' );
78 - $this->_addAction( 'admin_enqueue_scripts', 'enqueueSettingsScripts' );
79 - $this->_addAction( 'admin_post_visualizer_save_global_settings', 'saveGlobalSettings' );
80 58 $this->_addAction( 'admin_menu', 'registerAdminMenu' );
81 59 $this->_addFilter( 'media_view_strings', 'setupMediaViewStrings' );
82 60 $this->_addFilter( 'plugin_action_links', 'getPluginActionLinks', 10, 2 );
83 61 $this->_addFilter( 'plugin_row_meta', 'getPluginMetaLinks', 10, 2 );
@@ -82,9 +60,8 @@
82 60 $this->_addFilter( 'plugin_action_links', 'getPluginActionLinks', 10, 2 );
83 61 $this->_addFilter( 'plugin_row_meta', 'getPluginMetaLinks', 10, 2 );
84 62 $this->_addFilter( 'visualizer_logger_data', 'getLoggerData' );
85 63 $this->_addFilter( 'visualizer_feedback_review_trigger', 'feedbackReviewTrigger' );
86 - $this->_addFilter( 'themeisle_sdk_blackfriday_data', 'add_black_friday_data' );
87 64
88 65 // screen pagination
89 66 $this->_addFilter( 'set-screen-option', 'setScreenOptions', 10, 3 );
90 67
@@ -98,45 +75,14 @@
98 75 $this->_addAction( 'admin_init', 'init' );
99 76
100 77 $this->_addAction( 'visualizer_chart_languages', 'addMultilingualSupport' );
101 78
102 - $this->_addFilter( 'admin_footer_text', 'render_review_notice' );
103 -
104 - if ( ! defined( 'TI_E2E_TESTING' ) ) {
105 - $this->_addFilter( 'themeisle-sdk/survey/' . VISUALIZER_DIRNAME, 'get_survey_metadata', 10, 2 );
106 - }
107 -
108 - if ( defined( 'TI_E2E_TESTING' ) ) {
79 + if ( defined( 'TI_CYPRESS_TESTING' ) ) {
109 80 $this->load_cypress_hooks();
110 81 }
111 - }
112 - /**
113 - * Display review notice.
114 - */
115 - public function render_review_notice( $footer_text ) {
116 - $current_screen = get_current_screen();
117 82
118 - $visualizer_page_ids = array( 'toplevel_page_visualizer', 'visualizer_page_viz-support', 'visualizer_page_ti-about-visualizer' );
119 -
120 - if ( ! empty( $current_screen ) && isset( $current_screen->id ) ) {
121 - foreach ( $visualizer_page_ids as $page_to_check ) {
122 - if ( strpos( $current_screen->id, $page_to_check ) !== false ) {
123 - $footer_text = sprintf(
124 - // translators: %1$s - the name of the plugin (Visualizer), %2$s - message (You can help us by leaving a), %3$s - HTML entity code.
125 - __( 'Enjoying %1$s? %2$s %3$s rating. Thank you for being so supportive!', 'visualizer' ),
126 - '<b>Visualizer</b>',
127 - esc_html__( 'You can help us by leaving a', 'visualizer' ),
128 - '<a href="https://wordpress.org/support/plugin/visualizer/reviews/#new-post" target="_blank">&#9733;&#9733;&#9733;&#9733;&#9733;</a>'
129 - );
130 - break;
131 - }
132 - }
133 - }
134 -
135 - return $footer_text;
136 83 }
137 84
138 -
139 85 /**
140 86 * Define the hooks that are needed for cypress.
141 87 *
142 88 * @since 3.4.0
@@ -144,9 +90,9 @@
144 90 */
145 91 private function load_cypress_hooks() {
146 92 // all charts should load on the same page without pagination.
147 93 add_filter(
148 - 'visualizer_query_args', function ( $args ) {
94 + 'visualizer_query_args', function( $args ) {
149 95 $args['posts_per_page'] = 20;
150 96 return $args;
151 97 }, 10, 1
152 98 );
@@ -167,16 +113,23 @@
167 113 if ( apply_filters( 'visualizer_is_business', false ) ) {
168 114 return;
169 115 }
170 116
171 - $is_new_personal = apply_filters( 'visualizer_is_new_personal', false );
117 + $license = __( 'PRO', 'visualizer' );
118 + if ( Visualizer_Module::is_pro() ) {
119 + switch ( $plan ) {
120 + case 1:
121 + $license = __( 'Developer', 'visualizer' );
122 + break;
123 + }
124 + }
172 125
173 126 $hours = array(
174 - '0.16' => __( '10 minutes', 'visualizer' ),
175 - '1' => __( 'Each hour', 'visualizer' ),
176 - '12' => __( 'Each 12 hours', 'visualizer' ),
177 - '24' => __( 'Each day', 'visualizer' ),
178 - '72' => __( 'Each 3 days', 'visualizer' ),
127 + '0' => __( 'Live', 'visualizer' ),
128 + '1' => __( 'Each hour', 'visualizer' ),
129 + '12' => __( 'Each 12 hours', 'visualizer' ),
130 + '24' => __( 'Each day', 'visualizer' ),
131 + '72' => __( 'Each 3 days', 'visualizer' ),
179 132 );
180 133
181 134 switch ( $feature ) {
182 135 case 'json':
@@ -181,9 +134,9 @@
181 134 switch ( $feature ) {
182 135 case 'json':
183 136 case 'csv':
184 137 // no more schedules if pro is already active.
185 - if ( Visualizer_Module::is_pro() && ! $is_new_personal ) {
138 + if ( Visualizer_Module::is_pro() ) {
186 139 return;
187 140 }
188 141 break;
189 142 case 'wp':
@@ -193,9 +146,9 @@
193 146 default:
194 147 return;
195 148 }
196 149
197 - echo '<optgroup disabled label="' . __( 'Upgrade required', 'visualizer' ) . '">';
150 + echo '<optgroup disabled label="' . sprintf( __( 'More in the %s version', 'visualizer' ), $license ) . '">';
198 151 foreach ( $hours as $hour => $desc ) {
199 152 echo '<option disabled>' . $desc . '</option>';
200 153 }
201 154 echo '</optgroup>';
@@ -263,9 +216,10 @@
263 216 *
264 217 * @access public
265 218 */
266 219 public function init() {
267 - if ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) && 'true' === get_user_option( 'rich_editing' ) ) {
220 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
221 + if ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) && 'true' == get_user_option( 'rich_editing' ) ) {
268 222 $this->_addFilter( 'mce_external_languages', 'add_tinymce_lang', 10, 1 );
269 223 $this->_addFilter( 'mce_external_plugins', 'tinymce_plugin', 10, 1 );
270 224 $this->_addFilter( 'mce_buttons', 'register_mce_button', 10, 1 );
271 225 $this->_addFilter( 'tiny_mce_before_init', 'get_strings_for_block', 10, 1 );
@@ -277,9 +231,9 @@
277 231 *
278 232 * @since ?
279 233 * @access friendly
280 234 */
281 - public function get_strings_for_block( $settings ) {
235 + function get_strings_for_block( $settings ) {
282 236 $class = new Visualizer_Module_Language();
283 237 $strings = $class->get_strings();
284 238 $array = array( 'visualizer_tinymce_plugin' => json_encode( $strings ) );
285 239 return array_merge( $settings, $array );
@@ -355,36 +309,11 @@
355 309 * @access public
356 310 */
357 311 public function enqueueMediaScripts() {
358 312 global $typenow;
359 - global $current_screen;
360 -
361 - if ( post_type_supports( $typenow, 'editor' ) || $current_screen->id === 'widgets' || $current_screen->id === 'customize' ) {
313 + if ( post_type_supports( $typenow, 'editor' ) ) {
362 314 wp_enqueue_style( 'visualizer-media', VISUALIZER_ABSURL . 'css/media.css', array( 'media-views' ), Visualizer_Plugin::VERSION );
363 315
364 - $d3_renderer_asset = VISUALIZER_ABSPATH . '/classes/Visualizer/D3Renderer/build/index.asset.php';
365 - if ( file_exists( $d3_renderer_asset ) && ! wp_script_is( 'visualizer-d3-renderer', 'registered' ) ) {
366 - // @phpstan-ignore-next-line
367 - $d3_asset = include $d3_renderer_asset;
368 - wp_register_script(
369 - 'visualizer-d3-renderer',
370 - VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/index.js',
371 - array_merge( $d3_asset['dependencies'], array( 'jquery' ) ),
372 - $d3_asset['version'],
373 - true
374 - );
375 - }
376 - if ( wp_script_is( 'visualizer-d3-renderer', 'registered' ) ) {
377 - wp_enqueue_script( 'visualizer-d3-renderer' );
378 - wp_localize_script(
379 - 'visualizer-d3-renderer',
380 - 'vizD3Renderer',
381 - array(
382 - 'iframeJsUrl' => VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/iframe.js',
383 - )
384 - );
385 - }
386 -
387 316 // Load all the assets for the different libraries we support.
388 317 $deps = array(
389 318 Visualizer_Render_Sidebar_Google::enqueue_assets( array( 'media-editor' ) ),
390 319 Visualizer_Render_Sidebar_Type_DataTable_Tabular::enqueue_assets( array( 'media-editor' ) ),
@@ -398,9 +327,9 @@
398 327 'visualizer-media-view',
399 328 'visualizer',
400 329 array(
401 330 'i10n' => array(
402 - 'insert' => __( 'Insert Chart', 'visualizer' ),
331 + 'insert' => __( 'Insert', 'visualizer' ),
403 332 ),
404 333 )
405 334 );
406 335 wp_enqueue_script( 'visualizer-media-toolbar', VISUALIZER_ABSURL . 'js/media/toolbar.js', array( 'visualizer-media-view' ), Visualizer_Plugin::VERSION, true );
@@ -429,9 +358,9 @@
429 358 'controller' => array(
430 359 'title' => esc_html__( 'Visualizations', 'visualizer' ),
431 360 ),
432 361 'routers' => array(
433 - 'library' => esc_html__( 'Insert from Library', 'visualizer' ),
362 + 'library' => esc_html__( 'From Library', 'visualizer' ),
434 363 'create' => esc_html__( 'Create New', 'visualizer' ),
435 364 ),
436 365 'library' => array(
437 366 'filters' => $chart_types,
@@ -461,9 +390,9 @@
461 390 'enabled' => true,
462 391 );
463 392 }
464 393
465 - $enabled = self::proFeaturesEnabled();
394 + $enabled = self::proFeaturesLocked();
466 395
467 396 $types = array_merge(
468 397 $additional,
469 398 array(
@@ -474,14 +403,14 @@
474 403 ),
475 404 'pie' => array(
476 405 'name' => esc_html__( 'Pie/Donut', 'visualizer' ),
477 406 'enabled' => true,
478 - 'supports' => array( 'Google Charts', 'ChartJS' ),
407 + 'supports' => $enabled ? array( 'Google Charts', 'ChartJS' ) : array( 'Google Charts' ),
479 408 ),
480 409 'line' => array(
481 410 'name' => esc_html__( 'Line', 'visualizer' ),
482 411 'enabled' => true,
483 - 'supports' => array( 'Google Charts', 'ChartJS' ),
412 + 'supports' => $enabled ? array( 'Google Charts', 'ChartJS' ) : array( 'Google Charts' ),
484 413 ),
485 414 'bar' => array(
486 415 'name' => esc_html__( 'Bar', 'visualizer' ),
487 416 'enabled' => true,
@@ -491,21 +420,18 @@
491 420 'name' => esc_html__( 'Area', 'visualizer' ),
492 421 'enabled' => $enabled,
493 422 // in ChartJS, the fill option is used to make Line chart an area: https://www.chartjs.org/docs/latest/charts/area.html
494 423 'supports' => array( 'Google Charts' ),
495 - 'demo_url' => 'https://demo.themeisle.com/visualizer/area-chart/',
496 424 ),
497 425 'geo' => array(
498 426 'name' => esc_html__( 'Geo', 'visualizer' ),
499 427 'enabled' => $enabled,
500 428 'supports' => array( 'Google Charts' ),
501 - 'demo_url' => 'https://demo.themeisle.com/visualizer/geo-chart/',
502 429 ),
503 430 'column' => array(
504 431 'name' => esc_html__( 'Column', 'visualizer' ),
505 432 'enabled' => $enabled,
506 433 'supports' => array( 'Google Charts', 'ChartJS' ),
507 - 'demo_url' => 'https://demo.themeisle.com/visualizer/column-chart/',
508 434 ),
509 435 'bubble' => array(
510 436 'name' => esc_html__( 'Bubble', 'visualizer' ),
511 437 'enabled' => $enabled,
@@ -510,27 +436,23 @@
510 436 'name' => esc_html__( 'Bubble', 'visualizer' ),
511 437 'enabled' => $enabled,
512 438 // chartjs' bubble is ugly looking (and it won't work off the default bubble.csv) so it is being excluded for the time being.
513 439 'supports' => array( 'Google Charts' ),
514 - 'demo_url' => 'https://demo.themeisle.com/visualizer/bubble-chart/',
515 440 ),
516 441 'scatter' => array(
517 442 'name' => esc_html__( 'Scatter', 'visualizer' ),
518 443 'enabled' => $enabled,
519 444 'supports' => array( 'Google Charts' ),
520 - 'demo_url' => 'https://demo.themeisle.com/visualizer/scatter-chart/',
521 445 ),
522 446 'gauge' => array(
523 447 'name' => esc_html__( 'Gauge', 'visualizer' ),
524 448 'enabled' => $enabled,
525 449 'supports' => array( 'Google Charts' ),
526 - 'demo_url' => 'https://demo.themeisle.com/visualizer/gauge-chart/',
527 450 ),
528 451 'candlestick' => array(
529 452 'name' => esc_html__( 'Candlestick', 'visualizer' ),
530 453 'enabled' => $enabled,
531 454 'supports' => array( 'Google Charts' ),
532 - 'demo_url' => 'https://demo.themeisle.com/visualizer/candlestick-chart/',
533 455 ),
534 456 // pro types
535 457 'timeline' => array(
536 458 'name' => esc_html__( 'Timeline', 'visualizer' ),
@@ -535,27 +457,23 @@
535 457 'timeline' => array(
536 458 'name' => esc_html__( 'Timeline', 'visualizer' ),
537 459 'enabled' => false,
538 460 'supports' => array( 'Google Charts', 'ChartJS' ),
539 - 'demo_url' => 'https://demo.themeisle.com/visualizer/timeline-chart/',
540 461 ),
541 462 'combo' => array(
542 463 'name' => esc_html__( 'Combo', 'visualizer' ),
543 464 'enabled' => false,
544 465 'supports' => array( 'Google Charts' ),
545 - 'demo_url' => 'https://demo.themeisle.com/visualizer/combo-chart/',
546 466 ),
547 467 'polarArea' => array(
548 468 'name' => esc_html__( 'Polar Area', 'visualizer' ),
549 469 'enabled' => false,
550 470 'supports' => array( 'ChartJS' ),
551 - 'demo_url' => 'https://demo.themeisle.com/visualizer/polar-area-chart/',
552 471 ),
553 472 'radar' => array(
554 473 'name' => esc_html__( 'Radar/Spider', 'visualizer' ),
555 474 'enabled' => false,
556 475 'supports' => array( 'ChartJS' ),
557 - 'demo_url' => 'https://demo.themeisle.com/visualizer/radar-spider-chart/',
558 476 ),
559 477 )
560 478 );
561 479 $types = apply_filters( 'visualizer_pro_chart_types', $types );
@@ -682,159 +600,45 @@
682 600 *
683 601 * @param string $suffix The current page suffix.
684 602 */
685 603 public function enqueueLibraryScripts( $suffix ) {
686 - if ( $suffix !== $this->_libraryPage ) {
687 - return;
688 - }
689 -
690 - wp_enqueue_style( 'visualizer-library', VISUALIZER_ABSURL . 'css/library.css', array(), Visualizer_Plugin::VERSION );
691 - $this->_addFilter( 'media_upload_tabs', 'setupVisualizerTab' );
692 - wp_enqueue_media();
693 - wp_enqueue_script(
694 - 'visualizer-library',
695 - VISUALIZER_ABSURL . 'js/library.js',
696 - array(
697 - 'jquery',
698 - 'media-views',
699 - 'clipboard',
700 - ),
701 - Visualizer_Plugin::VERSION,
702 - true
703 - );
704 -
705 - wp_enqueue_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
706 -
707 - $has_d3 = false;
708 - $query = $this->getQuery();
709 - while ( $query->have_posts() ) {
710 - $chart = $query->next_post();
711 - $library = $this->load_chart_type( $chart->ID );
712 - if ( is_null( $library ) ) {
713 - continue;
714 - }
604 + if ( $suffix === $this->_libraryPage ) {
605 + wp_enqueue_style( 'visualizer-library', VISUALIZER_ABSURL . 'css/library.css', array(), Visualizer_Plugin::VERSION );
606 + $this->_addFilter( 'media_upload_tabs', 'setupVisualizerTab' );
607 + wp_enqueue_media();
715 608 wp_enqueue_script(
716 - "visualizer-render-$library",
717 - VISUALIZER_ABSURL . 'js/render-facade.js',
718 - apply_filters( 'visualizer_assets_render', array( 'visualizer-library', 'visualizer-customization' ), true ),
609 + 'visualizer-library',
610 + VISUALIZER_ABSURL . 'js/library.js',
611 + array(
612 + 'jquery',
613 + 'media-views',
614 + 'clipboard',
615 + ),
719 616 Visualizer_Plugin::VERSION,
720 617 true
721 618 );
722 - if ( 'd3' === $library ) {
723 - $has_d3 = true;
724 - }
725 - }
726 619
727 - if ( $has_d3 ) {
728 - $d3_renderer_asset = VISUALIZER_ABSPATH . '/classes/Visualizer/D3Renderer/build/index.asset.php';
729 - if ( file_exists( $d3_renderer_asset ) && ! wp_script_is( 'visualizer-d3-renderer', 'registered' ) ) {
730 - /**
731 - * Ignore missing build asset in source checkout.
732 - *
733 - * @phpstan-ignore-next-line
734 - */
735 - $d3_asset = include $d3_renderer_asset;
736 - wp_register_script(
737 - 'visualizer-d3-renderer',
738 - VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/index.js',
739 - array_merge( $d3_asset['dependencies'], array( 'jquery' ) ),
740 - $d3_asset['version'],
741 - true
742 - );
743 - wp_enqueue_script( 'visualizer-d3-renderer' );
744 - }
745 - wp_localize_script(
746 - 'visualizer-d3-renderer',
747 - 'vizD3Renderer',
748 - array(
749 - 'iframeJsUrl' => VISUALIZER_ABSURL . 'classes/Visualizer/D3Renderer/build/iframe.js',
750 - )
751 - );
752 - }
620 + wp_enqueue_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
753 621
754 - $chart_builder_asset = VISUALIZER_ABSPATH . '/classes/Visualizer/ChartBuilder/build/index.asset.php';
755 - if ( file_exists( $chart_builder_asset ) ) {
756 - /**
757 - * Ignore missing build asset in source checkout.
758 - *
759 - * @phpstan-ignore-next-line
760 - */
761 - $asset = include $chart_builder_asset;
762 - $chart_builder_deps = $asset['dependencies'];
763 - if ( ! wp_script_is( 'visualizer-handsontable', 'registered' ) && defined( 'Visualizer_Pro_ABSURL' ) && defined( 'VISUALIZER_PRO_VERSION' ) ) {
764 - wp_register_script(
765 - 'visualizer-handsontable',
766 - Visualizer_Pro_ABSURL . 'vendor/handsontable/dist/handsontable.full.min.js',
767 - array(),
768 - VISUALIZER_PRO_VERSION,
622 + $query = $this->getQuery();
623 + while ( $query->have_posts() ) {
624 + $chart = $query->next_post();
625 + $library = $this->load_chart_type( $chart->ID );
626 + if ( is_null( $library ) ) {
627 + continue;
628 + }
629 + wp_enqueue_script(
630 + "visualizer-render-$library",
631 + VISUALIZER_ABSURL . 'js/render-facade.js',
632 + apply_filters( 'visualizer_assets_render', array( 'visualizer-library', 'visualizer-customization' ), true ),
633 + Visualizer_Plugin::VERSION,
769 634 true
770 635 );
771 636 }
772 - if ( ! wp_style_is( 'visualizer-handsontable', 'registered' ) && defined( 'Visualizer_Pro_ABSURL' ) && defined( 'VISUALIZER_PRO_VERSION' ) ) {
773 - wp_register_style(
774 - 'visualizer-handsontable',
775 - Visualizer_Pro_ABSURL . 'vendor/handsontable/dist/handsontable.full.min.css',
776 - array(),
777 - VISUALIZER_PRO_VERSION
778 - );
779 - }
780 - if ( wp_script_is( 'visualizer-handsontable', 'registered' ) ) {
781 - wp_enqueue_script( 'visualizer-handsontable' );
782 - $chart_builder_deps = array_unique( array_merge( $chart_builder_deps, array( 'visualizer-handsontable' ) ) );
783 - }
784 - if ( wp_style_is( 'visualizer-handsontable', 'registered' ) ) {
785 - wp_enqueue_style( 'visualizer-handsontable' );
786 - }
787 - wp_enqueue_script(
788 - 'visualizer-chart-builder',
789 - VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/index.js',
790 - $chart_builder_deps,
791 - $asset['version'],
792 - true
793 - );
794 - wp_enqueue_style(
795 - 'visualizer-chart-builder',
796 - VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/style-index.css',
797 - array(),
798 - $asset['version']
799 - );
800 - $chart_builder_css = VISUALIZER_ABSPATH . '/classes/Visualizer/ChartBuilder/build/index.css';
801 - if ( file_exists( $chart_builder_css ) ) {
802 - wp_enqueue_style(
803 - 'visualizer-chart-builder-runtime',
804 - VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/index.css',
805 - array( 'visualizer-chart-builder' ),
806 - $asset['version']
807 - );
808 - }
809 - wp_localize_script(
810 - 'visualizer-chart-builder',
811 - 'vizAIBuilder',
812 - array(
813 - 'ajaxUrl' => admin_url( 'admin-ajax.php' ),
814 - 'nonce' => wp_create_nonce( 'visualizer-ai-builder' ),
815 - 'isPro' => Visualizer_Module::is_pro(),
816 - 'upgradeUrl' => tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'aiBuilderUpgrade', 'dataSource' ),
817 - )
818 - );
819 637 }
820 -
821 - do_action( 'themeisle_internal_page', VISUALIZER_DIRNAME, 'library' );
822 638 }
823 639
824 640 /**
825 - * Enqueue script for support page.
826 - *
827 - * @param string $hook_suffix Current hook.
828 - */
829 - public function enqueue_support_page( $hook_suffix ) {
830 - if ( $this->_supportPage !== $hook_suffix ) {
831 - return;
832 - }
833 - do_action( 'themeisle_internal_page', VISUALIZER_DIRNAME, 'support' );
834 - }
835 -
836 - /**
837 641 * Adds visualizer tab for media upload tabs array.
838 642 *
839 643 * @since 1.0.0
840 644 *
@@ -866,9 +670,9 @@
866 670 Visualizer_Plugin::NAME,
867 671 __( 'Chart Library', 'visualizer' ),
868 672 __( 'Chart Library', 'visualizer' ),
869 673 'edit_posts',
870 - 'admin.php?page=' . Visualizer_Plugin::NAME
674 + admin_url( 'admin.php?page=' . Visualizer_Plugin::NAME )
871 675 );
872 676 add_submenu_page(
873 677 Visualizer_Plugin::NAME,
874 678 __( 'Add New Chart', 'visualizer' ),
@@ -873,22 +677,12 @@
873 677 Visualizer_Plugin::NAME,
874 678 __( 'Add New Chart', 'visualizer' ),
875 679 __( 'Add New Chart', 'visualizer' ),
876 680 'edit_posts',
877 - 'admin.php?page=' . Visualizer_Plugin::NAME . '&vaction=addnew'
681 + admin_url( 'admin.php?page=' . Visualizer_Plugin::NAME . '&vaction=addnew' )
878 682 );
879 -
880 - $this->_settingsPage = add_submenu_page(
683 + add_submenu_page(
881 684 Visualizer_Plugin::NAME,
882 - __( 'Settings', 'visualizer' ),
883 - __( 'Settings', 'visualizer' ),
884 - 'manage_options',
885 - 'viz-settings',
886 - array( $this, 'renderSettingsPage' )
887 - );
888 -
889 - $this->_supportPage = add_submenu_page(
890 - Visualizer_Plugin::NAME,
891 685 __( 'Support', 'visualizer' ),
892 686 __( 'Support', 'visualizer' ) . '<span class="dashicons dashicons-editor-help more-features-icon" style="width: 17px; height: 17px; margin-left: 4px; color: #ffca54; font-size: 17px; vertical-align: -3px;"></span>',
893 687 'edit_posts',
894 688 'viz-support',
@@ -894,12 +688,22 @@
894 688 'viz-support',
895 689 array( $this, 'renderSupportPage' )
896 690 );
897 691
692 + if ( ! Visualizer_Module::is_pro() ) {
693 + add_submenu_page(
694 + Visualizer_Plugin::NAME,
695 + __( 'Get Visualizer Pro', 'visualizer' ),
696 + __( 'Get Visualizer Pro', 'visualizer' ),
697 + 'edit_posts',
698 + 'viz-get-pro',
699 + '__return_null'
700 + );
701 + add_action( 'admin_footer', array( $this, 'handleGetProSubMenu' ) );
702 + }
898 703 remove_submenu_page( Visualizer_Plugin::NAME, Visualizer_Plugin::NAME );
899 704
900 705 add_action( "load-{$this->_libraryPage}", array( $this, 'addScreenOptions' ) );
901 - add_action( 'admin_footer', array( $this, 'handleGetProSubMenu' ) );
902 706 }
903 707
904 708 /**
905 709 * Handle get pro plugin submenu.
@@ -904,41 +708,38 @@
904 708 /**
905 709 * Handle get pro plugin submenu.
906 710 */
907 711 public function handleGetProSubMenu() {
908 - if ( ! Visualizer_Module::is_pro() ) {
909 - ?>
712 + ?>
910 713 <style type="text/css">
911 - #toplevel_page_visualizer ul.wp-submenu li:last-child {
714 + #toplevel_page_visualizer ul.wp-submenu li.wp-first-item + li + li + li {
912 715 background: #FF7E65;
913 716 font-size: 14px;
914 717 font-weight: 600;
915 718 color: #fff;
916 719 }
917 - #toplevel_page_visualizer ul.wp-submenu li:last-child > a > span {
720 + #toplevel_page_visualizer ul.wp-submenu li.wp-first-item + li + li + li > a {
918 721 color: #fff !important;
919 722 }
920 - #toplevel_page_visualizer ul.wp-submenu li:last-child > a:hover {
723 + #toplevel_page_visualizer ul.wp-submenu li.wp-first-item + li + li + li > a:hover {
921 724 box-shadow: inherit;
922 725 }
923 726 </style>
924 - <?php
925 - if ( get_option( 'visualizer_fresh_install', false ) ) {
926 - ?>
927 - <style type="text/css">
928 - #toplevel_page_visualizer ul.wp-submenu li.wp-first-item + li + li + li {
929 - display: none;
930 - }
931 - </style>
932 - <?php
933 - }
934 - }
727 + <script type="text/javascript">
728 + jQuery( document ).ready( function() {
729 + jQuery( '#toplevel_page_visualizer' ).on( 'click', 'li:not(.wp-submenu-head, .wp-first-item):eq(2)', function( e ) {
730 + e.preventDefault();
731 + window.open( '<?php echo tsdk_utmify( 'https://themeisle.com/plugins/visualizer-charts-and-graphs/upgrade/', 'toplevel' ); ?>', '_blank' );
732 + } );
733 + } );
734 + </script>
735 + <?php
935 736 }
936 737
937 738 /**
938 739 * Adds the screen options for pagination.
939 740 */
940 - public function addScreenOptions() {
741 + function addScreenOptions() {
941 742 $screen = get_current_screen();
942 743
943 744 // bail if it's some other page.
944 745 if ( ! is_object( $screen ) || $screen->id !== $this->_libraryPage ) {
@@ -955,9 +756,9 @@
955 756
956 757 /**
957 758 * Returns the screen option for pagination.
958 759 */
959 - public function setScreenOptions( $status, $option, $value ) {
760 + function setScreenOptions( $status, $option, $value ) {
960 761 if ( 'visualizer_library_per_page' === $option ) {
961 762 return $value;
962 763 }
963 764 }
@@ -1081,9 +882,9 @@
1081 882
1082 883 $this->getDisplayFilters( $query_args );
1083 884
1084 885 // Added by Ash/Upwork
1085 - $filterByMeta = ! empty( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
886 + $filterByMeta = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
1086 887 if ( $filterByMeta ) {
1087 888 $query = array(
1088 889 'key' => Visualizer_Plugin::CF_SETTINGS,
1089 890 'value' => $filterByMeta,
@@ -1109,91 +910,8 @@
1109 910 include_once VISUALIZER_ABSPATH . '/templates/support.php';
1110 911 }
1111 912
1112 913 /**
1113 - * Enqueues scripts/styles for the Settings page.
1114 - *
1115 - * @access public
1116 - */
1117 - public function enqueueSettingsScripts( string $hook_suffix ): void {
1118 - if ( $this->_settingsPage !== $hook_suffix ) {
1119 - return;
1120 - }
1121 - wp_enqueue_style( 'wp-color-picker' );
1122 - wp_enqueue_script( 'wp-color-picker' );
1123 - }
1124 -
1125 - /**
1126 - * Renders the global style settings page.
1127 - *
1128 - * @access public
1129 - */
1130 - public function renderSettingsPage(): void {
1131 - if ( ! current_user_can( 'manage_options' ) ) {
1132 - wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'visualizer' ) );
1133 - }
1134 - $settings = self::getGlobalSettings();
1135 - include_once VISUALIZER_ABSPATH . '/templates/global-settings.php';
1136 - }
1137 -
1138 - /**
1139 - * Returns the global style settings option.
1140 - *
1141 - * @return array<string, string>
1142 - * @access public
1143 - * @static
1144 - */
1145 - public static function getGlobalSettings(): array {
1146 - $defaults = array(
1147 - 'color_primary' => '',
1148 - 'color_secondary' => '',
1149 - 'apply_existing' => '0',
1150 - );
1151 - $saved = get_option( self::OPTION_GLOBAL_SETTINGS, array() );
1152 - return wp_parse_args( $saved, $defaults );
1153 - }
1154 -
1155 - /**
1156 - * Handles saving of the global style settings.
1157 - *
1158 - * @access public
1159 - */
1160 - public function saveGlobalSettings(): void {
1161 - if ( ! current_user_can( 'manage_options' ) ) {
1162 - wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'visualizer' ) );
1163 - }
1164 -
1165 - check_admin_referer( 'visualizer_save_global_settings' );
1166 -
1167 - $color_primary = sanitize_hex_color( wp_unslash( $_POST['visualizer_color_primary'] ?? '' ) );
1168 - $color_secondary = sanitize_hex_color( wp_unslash( $_POST['visualizer_color_secondary'] ?? '' ) );
1169 - $apply_existing = ! empty( $_POST['visualizer_apply_existing'] ) ? '1' : '0';
1170 - $clear = ! empty( $_POST['visualizer_clear_settings'] );
1171 -
1172 - if ( $clear ) {
1173 - delete_option( self::OPTION_GLOBAL_SETTINGS );
1174 - } else {
1175 - $settings = array(
1176 - 'color_primary' => $color_primary ? $color_primary : '',
1177 - 'color_secondary' => $color_secondary ? $color_secondary : '',
1178 - 'apply_existing' => $apply_existing,
1179 - );
1180 - update_option( self::OPTION_GLOBAL_SETTINGS, $settings );
1181 - }
1182 -
1183 - wp_safe_redirect(
1184 - add_query_arg(
1185 - array(
1186 - 'page' => 'viz-settings',
1187 - 'updated' => $clear ? 'cleared' : 'true',
1188 - ),
1189 - admin_url( 'admin.php' )
1190 - )
1191 - );
1192 - exit;
1193 - }
1194 -
1195 - /**
1196 914 * Renders visualizer library page.
1197 915 *
1198 916 * @since 1.0.0
1199 917 *
@@ -1277,9 +995,8 @@
1277 995
1278 996 // add chart to the array
1279 997 $charts[ $id ] = array(
1280 998 'id' => $chart->ID,
1281 - 'title' => $chart->post_title,
1282 999 'type' => $type,
1283 1000 'series' => $series,
1284 1001 'settings' => $settings,
1285 1002 'data' => $data,
@@ -1284,15 +1001,11 @@
1284 1001 'settings' => $settings,
1285 1002 'data' => $data,
1286 1003 'library' => $library,
1287 1004 );
1288 - if ( 'd3' === $library ) {
1289 - $charts[ $id ]['code'] = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
1290 - }
1291 1005 }
1292 1006 // enqueue charts array
1293 1007 $ajaxurl = admin_url( 'admin-ajax.php' );
1294 -
1295 1008 wp_localize_script(
1296 1009 'visualizer-library',
1297 1010 'visualizer',
1298 1011 array(
@@ -1329,9 +1042,8 @@
1329 1042 'i10n' => array(
1330 1043 'copied' => __( 'The shortcode has been copied to your clipboard. Hit Ctrl-V/Cmd-V to paste it.', 'visualizer' ),
1331 1044 'conflict' => __( 'We have detected a potential conflict with another component that prevents Visualizer from functioning properly. Please disable any of the following components if they are activated on your instance: Modern Events Calendar plugin, Acronix plugin. In case the aforementioned components are not activated or you continue to see this error message, please disable all other plugins and enable them one by one to find out the component that is causing the conflict.', 'visualizer' ),
1332 1045 ),
1333 - 'is_pro_user' => Visualizer_Module::is_pro(),
1334 1046 )
1335 1047 );
1336 1048 // render library page
1337 1049 $render = new Visualizer_Render_Library();
@@ -1347,9 +1059,8 @@
1347 1059 'total' => $query->max_num_pages,
1348 1060 'type' => 'array',
1349 1061 )
1350 1062 );
1351 -
1352 1063 $render->render();
1353 1064 }
1354 1065
1355 1066 /**
@@ -1391,36 +1102,19 @@
1391 1102 *
1392 1103 * @return array Updated array of plugin meta links.
1393 1104 */
1394 1105 public function getPluginMetaLinks( $plugin_meta, $plugin_file ) {
1395 - if ( Visualizer_Module::is_pro() ) {
1396 - return $plugin_meta;
1397 - }
1398 -
1399 - // Also suppress the upsell when Pro is installed but not currently active.
1400 - if ( ! function_exists( 'get_plugins' ) ) {
1401 - require_once ABSPATH . 'wp-admin/includes/plugin.php';
1402 - }
1403 - foreach ( array_keys( get_plugins() ) as $installed_plugin ) {
1404 - if ( false !== strpos( $installed_plugin, 'visualizer-pro' ) ) {
1405 - return $plugin_meta;
1406 - }
1407 - }
1408 -
1409 1106 if ( $plugin_file === plugin_basename( VISUALIZER_BASEFILE ) ) {
1410 - $is_black_friday = apply_filters( 'themeisle_sdk_is_black_friday_sale', false );
1411 1107 // knowledge base link
1412 1108 $plugin_meta[] = sprintf(
1413 1109 '<a href="' . VISUALIZER_MAIN_DOC . '" target="_blank">%s</a>',
1414 1110 esc_html__( 'Docs', 'visualizer' )
1415 1111 );
1416 - if ( ! $is_black_friday ) {
1417 - // flattr link
1418 - $plugin_meta[] = sprintf(
1419 - '<a style="color:red" href="' . tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'pluginrow' ) . '" target="_blank">%s</a>',
1420 - esc_html__( 'Get Visualizer Pro', 'visualizer' )
1421 - );
1422 - }
1112 + // flattr link
1113 + $plugin_meta[] = sprintf(
1114 + '<a style="color:red" href="' . tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'pluginrow' ) . '" target="_blank">%s</a>',
1115 + esc_html__( 'Pro Addon', 'visualizer' )
1116 + );
1423 1117 }
1424 1118
1425 1119 return $plugin_meta;
1426 1120 }
@@ -1425,39 +1119,17 @@
1425 1119 return $plugin_meta;
1426 1120 }
1427 1121
1428 1122 /**
1429 - * Returns true when premium chart types should be enabled for the current user.
1123 + * If check is existing user.
1430 1124 *
1431 - * Pro 1.9.0+ hooks the 'visualizer_is_pro' filter and returns true only when
1432 - * the license is valid. Pre-1.9.0 Pro defined a Visualizer_Pro class instead;
1433 - * we detect that as a fallback so those legacy installs still work.
1434 - *
1435 - * Passing false as the filter default ensures the constant VISUALIZER_PRO
1436 - * (which is set to true whenever the Pro class exists, regardless of license
1437 - * state) cannot bypass the license check.
1438 - *
1439 - * @return bool
1125 + * @return bool Default false
1440 1126 */
1441 - public static function proFeaturesEnabled() {
1442 - $is_pro_filter = apply_filters( 'visualizer_is_pro', false );
1443 -
1444 - // Pro 1.9.0+: filter is hooked and returns true only with a valid license.
1445 - if ( $is_pro_filter ) {
1127 + public static function proFeaturesLocked() {
1128 + if ( Visualizer_Module::is_pro() ) {
1446 1129 return true;
1447 1130 }
1448 -
1449 - // Pro is installed (active) but the license check above did not pass.
1450 - // Do NOT fall through to the legacy "old user" path — that path exists
1451 - // only for sites that never had Pro installed. If Pro is present but
1452 - // the license is inactive we should lock, regardless of chart history.
1453 - if ( class_exists( 'Visualizer_Pro', false ) ) {
1454 - return false;
1455 - }
1456 -
1457 - // No Pro installed at all: grant legacy access to existing users so
1458 - // their charts are not suddenly broken when they upgrade the free plugin.
1459 - return 'yes' === get_option( 'visualizer-new-user' ) ? false : true;
1131 + return 'yes' === get_option( 'visualizer-new-user', 'yes' ) ? false : true;
1460 1132 }
1461 1133
1462 1134 /**
1463 1135 * Multilingual Support.
@@ -1500,159 +1172,6 @@
1500 1172 <a href="<?php echo tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'wpml-support', 'visualizer_render_char' ); ?>"target="_blank"><?php esc_html_e( 'Upgrade to PRO to active this translation for charts', 'visualizer' ); ?></a>
1501 1173 </div>
1502 1174 <?php
1503 1175 }
1504 - }
1505 -
1506 - /**
1507 - * Check chart is enabled or not.
1508 - *
1509 - * @param string $type Chart ID.
1510 - *
1511 - * @return bool Default false
1512 - */
1513 - public static function checkChartStatus( $type ) {
1514 - $types = self::_getChartTypesLocalized( false, false, false, true );
1515 - if ( isset( $types[ $type ] ) ) {
1516 - return ! empty( $types[ $type ]['enabled'] );
1517 - }
1518 - return false;
1519 - }
1520 -
1521 - /**
1522 - * Get the survey metadata.
1523 - *
1524 - * @param array $data The data for survey in Formbricks format.
1525 - * @param string $page_slug The slug of the loaded page.
1526 - *
1527 - * @return array The survey metadata.
1528 - */
1529 - public function get_survey_metadata( $data, $page_slug ) {
1530 - $install_date = get_option( 'visualizer_install', time() );
1531 - $install_days_number = intval( ( time() - $install_date ) / DAY_IN_SECONDS );
1532 -
1533 - $license_status = apply_filters( 'product_visualizer_license_status', 'invalid' );
1534 - $license_plan = apply_filters( 'product_visualizer_license_plan', false );
1535 - $license_key = apply_filters( 'product_visualizer_license_key', false );
1536 -
1537 - $plugin_data = get_plugin_data( VISUALIZER_BASEFILE, false, false );
1538 - $plugin_version = '';
1539 -
1540 - if ( ! empty( $plugin_data['Version'] ) ) {
1541 - $plugin_version = $plugin_data['Version'];
1542 - }
1543 -
1544 - $count_charts_cache_key = 'visualizer_count_charts';
1545 - $charts_number = get_transient( $count_charts_cache_key );
1546 -
1547 - if ( false === $charts_number ) {
1548 - $charts_number = $this->count_charts( 100 );
1549 - set_transient( $count_charts_cache_key, $charts_number, 100 === $charts_number ? WEEK_IN_SECONDS : 6 * HOUR_IN_SECONDS );
1550 - } else {
1551 - $charts_number = strval( $charts_number );
1552 - }
1553 -
1554 - $data = array(
1555 - 'environmentId' => 'cltef8cut1s7wyyfxy3rlxzs5',
1556 - 'attributes' => array(
1557 - 'free_version' => $plugin_version,
1558 - 'pro_version' => defined( 'VISUALIZER_PRO_VERSION' ) ? VISUALIZER_PRO_VERSION : '',
1559 - 'license_status' => $license_status,
1560 - 'install_days_number' => $install_days_number,
1561 - 'charts_number' => $charts_number,
1562 - ),
1563 - );
1564 -
1565 - if ( ! empty( $license_plan ) ) {
1566 - $data['attributes']['plan'] = $license_plan;
1567 - }
1568 -
1569 - if ( ! empty( $license_key ) ) {
1570 - $data['attributes']['license_key'] = apply_filters( 'themeisle_sdk_secret_masking', $license_key );
1571 - }
1572 -
1573 - return $data;
1574 - }
1575 -
1576 - /**
1577 - * Count the charts.
1578 - *
1579 - * @param int $limit The count limit (optional).
1580 - *
1581 - * @return int The number of charts.
1582 - */
1583 - public function count_charts( $limit = -1 ) {
1584 - $args = array(
1585 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
1586 - 'post_status' => 'publish',
1587 - 'posts_per_page' => $limit,
1588 - 'fields' => 'ids',
1589 - );
1590 -
1591 - $query = new WP_Query( $args);
1592 - return $query->post_count;
1593 - }
1594 -
1595 - /**
1596 - * Set the black friday data.
1597 - *
1598 - * @param array $configs The configuration array for the loaded products.
1599 - * @return array
1600 - */
1601 - public function add_black_friday_data( $configs ) {
1602 - $config = $configs['default'];
1603 -
1604 - $message = __( 'Database queries, private charts, auto-sync. Go beyond basic charts. Exclusively for existing Visualizer users.', 'visualizer' );
1605 - $cta_label = __( 'Get Visualizer Pro', 'visualizer' );
1606 -
1607 - $plan = apply_filters( 'product_visualizer_license_plan', 0 );
1608 - $license = apply_filters( 'product_visualizer_license_key', false );
1609 - $status = apply_filters( 'product_visualizer_license_status', false );
1610 - $pro_product_slug = defined( 'VISUALIZER_PRO_BASEFILE' ) ? basename( dirname( VISUALIZER_PRO_BASEFILE ) ) : '';
1611 -
1612 - $is_pro = 'valid' === $status;
1613 - $is_expired = 'expired' === $status || 'active-expired' === $status;
1614 -
1615 - if ( $is_pro ) {
1616 - // translators: %s is the discount percentage.
1617 - $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - up to %s off', 'visualizer' ), '30%' );
1618 - // translators: %1$s - discount, %2$s - discount.
1619 - $message = sprintf( __( 'Upgrade your Visualizer Pro plan: %1$s off this week. Already on the plan you need? Renew early and save up to %2$s.', 'visualizer' ), '30%', '20%' );
1620 - $cta_label = __( 'See your options', 'visualizer' );
1621 - } elseif ( $is_expired ) {
1622 - // translators: %s is the discount percentage.
1623 - $config['upgrade_menu_text'] = sprintf( __( 'BF Sale - %s off', 'visualizer' ), '50%' );
1624 - // translators: %s is the discount percentage.
1625 - $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - %s off', 'visualizer' ), '50%' );
1626 - $message = __( 'Your Visualizer Pro features are still here, just locked. Renew at a reduced rate this week.', 'visualizer' );
1627 - $cta_label = __( 'Reactivate now', 'visualizer' );
1628 - } else {
1629 - // translators: %s is the discount percentage.
1630 - $config['plugin_meta_message'] = sprintf( __( 'Black Friday Sale - %s off', 'visualizer' ), '60%' );
1631 - $config['title'] = __( 'Visualizer Pro: 60% off this week', 'visualizer' );
1632 - // translators: %s is the discount percentage.
1633 - $config['upgrade_menu_text'] = sprintf( __( 'BF Sale - %s off', 'visualizer' ), '60%' );
1634 - }
1635 -
1636 - $url_params = array(
1637 - 'utm_term' => $is_pro ? 'plan-' . $plan : 'free',
1638 - 'lkey' => ! empty( $license ) ? $license : false,
1639 - 'expired' => $is_expired ? '1' : false,
1640 - );
1641 -
1642 - if ( ( $is_pro || $is_expired ) && ! empty( $pro_product_slug ) ) {
1643 - $config['plugin_meta_targets'] = array( $pro_product_slug );
1644 - }
1645 -
1646 - $config['message'] = $message;
1647 - $config['cta_label'] = $cta_label;
1648 -
1649 - $config['sale_url'] = add_query_arg(
1650 - $url_params,
1651 - tsdk_translate_link( tsdk_utmify( 'https://themeisle.link/vizualizer-bf', 'bfcm', 'visualizer' ) )
1652 - );
1653 -
1654 - $configs[ VISUALIZER_DIRNAME ] = $config;
1655 -
1656 - return $configs;
1657 1176 }
1658 1177 }