PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.0
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.0
4.0.8 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 All 149 releases
← All changes | classes/Visualizer/Module/Admin.php +1184 -98 3.0.64.0.0 View file →
@@ -29,8 +29,10 @@
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 +
33 35 /**
34 36 * Library page suffix.
35 37 *
36 38 * @since 1.0.0
@@ -40,8 +42,24 @@
40 42 */
41 43 private $_libraryPage;
42 44
43 45 /**
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 + /**
44 62 * Constructor.
45 63 *
46 64 * @since 1.0.0
47 65 *
@@ -52,34 +70,223 @@
52 70 public function __construct( Visualizer_Plugin $plugin ) {
53 71 parent::__construct( $plugin );
54 72 $this->_addAction( 'load-post.php', 'enqueueMediaScripts' );
55 73 $this->_addAction( 'load-post-new.php', 'enqueueMediaScripts' );
56 - $this->_addAction( 'admin_footer', 'renderTempaltes' );
57 - $this->_addAction( 'admin_enqueue_scripts', 'enqueueLibraryScripts', null, 8 );
74 + $this->_addAction( 'enqueue_block_editor_assets', 'enqueueMediaScripts' );
75 + $this->_addAction( 'admin_footer', 'renderTemplates' );
76 + $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' );
58 80 $this->_addAction( 'admin_menu', 'registerAdminMenu' );
59 81 $this->_addFilter( 'media_view_strings', 'setupMediaViewStrings' );
60 82 $this->_addFilter( 'plugin_action_links', 'getPluginActionLinks', 10, 2 );
83 + $this->_addFilter( 'plugin_row_meta', 'getPluginMetaLinks', 10, 2 );
61 84 $this->_addFilter( 'visualizer_logger_data', 'getLoggerData' );
62 - $this->_addFilter( 'visualizer_get_chart_counts', 'getChartCountsByTypeAndMeta' );
63 85 $this->_addFilter( 'visualizer_feedback_review_trigger', 'feedbackReviewTrigger' );
86 + $this->_addFilter( 'themeisle_sdk_blackfriday_data', 'add_black_friday_data' );
64 87
88 + // screen pagination
89 + $this->_addFilter( 'set-screen-option', 'setScreenOptions', 10, 3 );
90 +
91 + // revision support.
92 + $this->_addFilter( 'wp_revisions_to_keep', 'limitRevisions', null, 10, 2 );
93 + $this->_addAction( '_wp_put_post_revision', 'addRevision', null, 10, 1 );
94 + $this->_addAction( 'wp_restore_post_revision', 'restoreRevision', null, 10, 2 );
95 +
96 + $this->_addAction( 'visualizer_chart_schedules_spl', 'addSplChartSchedules', null, 10, 3 );
97 +
65 98 $this->_addAction( 'admin_init', 'init' );
99 +
100 + $this->_addAction( 'visualizer_chart_languages', 'addMultilingualSupport' );
101 +
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' ) ) {
109 + $this->load_cypress_hooks();
110 + }
66 111 }
112 + /**
113 + * Display review notice.
114 + */
115 + public function render_review_notice( $footer_text ) {
116 + $current_screen = get_current_screen();
67 117
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 + }
137 +
138 +
68 139 /**
140 + * Define the hooks that are needed for cypress.
141 + *
142 + * @since 3.4.0
143 + * @access private
144 + */
145 + private function load_cypress_hooks() {
146 + // all charts should load on the same page without pagination.
147 + add_filter(
148 + 'visualizer_query_args', function ( $args ) {
149 + $args['posts_per_page'] = 20;
150 + return $args;
151 + }, 10, 1
152 + );
153 + }
154 +
155 + /**
156 + * Add disabled `optgroup` schedules to the drop downs.
157 + *
158 + * @since ?
159 + *
160 + * @access public
161 + *
162 + * @param string $feature The feature for which to add schedules.
163 + * @param int $chart_id The chart ID.
164 + * @param int $plan The plan number.
165 + */
166 + public function addSplChartSchedules( $feature, $chart_id, $plan ) {
167 + if ( apply_filters( 'visualizer_is_business', false ) ) {
168 + return;
169 + }
170 +
171 + $is_new_personal = apply_filters( 'visualizer_is_new_personal', false );
172 +
173 + $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' ),
179 + );
180 +
181 + switch ( $feature ) {
182 + case 'json':
183 + case 'csv':
184 + // no more schedules if pro is already active.
185 + if ( Visualizer_Module::is_pro() && ! $is_new_personal ) {
186 + return;
187 + }
188 + break;
189 + case 'wp':
190 + // fall-through.
191 + case 'db':
192 + break;
193 + default:
194 + return;
195 + }
196 +
197 + echo '<optgroup disabled label="' . __( 'Upgrade required', 'visualizer' ) . '">';
198 + foreach ( $hours as $hour => $desc ) {
199 + echo '<option disabled>' . $desc . '</option>';
200 + }
201 + echo '</optgroup>';
202 + }
203 +
204 + /**
205 + * No limits on revisions.
206 + */
207 + public function limitRevisions( $num, $post ) {
208 + if ( Visualizer_Plugin::CPT_VISUALIZER === $post->post_type ) {
209 + return -1;
210 + }
211 + return $num;
212 + }
213 +
214 + /**
215 + * Add a revision.
216 + */
217 + public function addRevision( $revision_id ) {
218 + $parent_id = wp_is_post_revision( $revision_id );
219 + if ( Visualizer_Plugin::CPT_VISUALIZER === get_post_type( $parent_id ) ) {
220 + // add the meta data to this revision.
221 + $meta = get_post_meta( $parent_id, '', true );
222 +
223 + if ( $meta ) {
224 + foreach ( $meta as $key => $value ) {
225 + if ( 0 === strpos( $key, 'visualizer' ) ) {
226 + add_metadata( 'post', $revision_id, $key, maybe_unserialize( $value[0] ) );
227 + }
228 + }
229 + }
230 + }
231 + }
232 +
233 + /**
234 + * Restore a revision.
235 + */
236 + public function restoreRevision( $post_id, $revision_id ) {
237 + if ( Visualizer_Plugin::CPT_VISUALIZER === get_post_type( $post_id ) ) {
238 + // get the meta information from the revision.
239 + $meta = get_metadata( 'post', $revision_id, '', true );
240 +
241 + // delete all meta information from the post before adding.
242 + $post_meta = get_post_meta( $post_id, '', true );
243 + if ( $post_meta ) {
244 + foreach ( $meta as $key => $value ) {
245 + if ( 0 === strpos( $key, 'visualizer' ) ) {
246 + delete_post_meta( $post_id, $key );
247 + }
248 + }
249 + }
250 +
251 + if ( $meta ) {
252 + foreach ( $meta as $key => $value ) {
253 + if ( 0 === strpos( $key, 'visualizer' ) ) {
254 + add_post_meta( $post_id, $key, maybe_unserialize( $value[0] ) );
255 + }
256 + }
257 + }
258 + }
259 + }
260 +
261 + /**
69 262 * Admin init.
70 263 *
71 264 * @access public
72 265 */
73 266 public function init() {
74 - if ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) && 'true' == get_user_option( 'rich_editing' ) ) {
267 + if ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) && 'true' === get_user_option( 'rich_editing' ) ) {
75 268 $this->_addFilter( 'mce_external_languages', 'add_tinymce_lang', 10, 1 );
76 269 $this->_addFilter( 'mce_external_plugins', 'tinymce_plugin', 10, 1 );
77 270 $this->_addFilter( 'mce_buttons', 'register_mce_button', 10, 1 );
271 + $this->_addFilter( 'tiny_mce_before_init', 'get_strings_for_block', 10, 1 );
78 272 }
79 273 }
80 274
81 275 /**
276 + * Add the strings required for the TinyMCE buttons for the classic block (not the classic editor).
277 + *
278 + * @since ?
279 + * @access friendly
280 + */
281 + public function get_strings_for_block( $settings ) {
282 + $class = new Visualizer_Module_Language();
283 + $strings = $class->get_strings();
284 + $array = array( 'visualizer_tinymce_plugin' => json_encode( $strings ) );
285 + return array_merge( $settings, $array );
286 + }
287 +
288 + /**
82 289 * Load plugin translation for - TinyMCE API
83 290 *
84 291 * @access public
85 292 * @param array $arr The tinymce_lang array.
@@ -148,16 +355,55 @@
148 355 * @access public
149 356 */
150 357 public function enqueueMediaScripts() {
151 358 global $typenow;
152 - if ( post_type_supports( $typenow, 'editor' ) ) {
359 + global $current_screen;
360 +
361 + if ( post_type_supports( $typenow, 'editor' ) || $current_screen->id === 'widgets' || $current_screen->id === 'customize' ) {
153 362 wp_enqueue_style( 'visualizer-media', VISUALIZER_ABSURL . 'css/media.css', array( 'media-views' ), Visualizer_Plugin::VERSION );
154 - wp_enqueue_script( 'visualizer-google-jsapi-new', '//www.gstatic.com/charts/loader.js', array( 'media-editor' ), null, true );
155 - wp_enqueue_script( 'visualizer-google-jsapi-old', '//www.google.com/jsapi', array( 'visualizer-google-jsapi-new' ), null, true );
156 - wp_enqueue_script( 'visualizer-media-model', VISUALIZER_ABSURL . 'js/media/model.js', array( 'visualizer-google-jsapi-old' ), Visualizer_Plugin::VERSION, true );
363 +
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 + // Load all the assets for the different libraries we support.
388 + $deps = array(
389 + Visualizer_Render_Sidebar_Google::enqueue_assets( array( 'media-editor' ) ),
390 + Visualizer_Render_Sidebar_Type_DataTable_Tabular::enqueue_assets( array( 'media-editor' ) ),
391 + );
392 +
393 + wp_enqueue_script( 'visualizer-media-model', VISUALIZER_ABSURL . 'js/media/model.js', $deps, Visualizer_Plugin::VERSION, true );
157 394 wp_enqueue_script( 'visualizer-media-collection', VISUALIZER_ABSURL . 'js/media/collection.js', array( 'visualizer-media-model' ), Visualizer_Plugin::VERSION, true );
158 395 wp_enqueue_script( 'visualizer-media-controller', VISUALIZER_ABSURL . 'js/media/controller.js', array( 'visualizer-media-collection' ), Visualizer_Plugin::VERSION, true );
159 396 wp_enqueue_script( 'visualizer-media-view', VISUALIZER_ABSURL . 'js/media/view.js', array( 'visualizer-media-controller' ), Visualizer_Plugin::VERSION, true );
397 + wp_localize_script(
398 + 'visualizer-media-view',
399 + 'visualizer',
400 + array(
401 + 'i10n' => array(
402 + 'insert' => __( 'Insert Chart', 'visualizer' ),
403 + ),
404 + )
405 + );
160 406 wp_enqueue_script( 'visualizer-media-toolbar', VISUALIZER_ABSURL . 'js/media/toolbar.js', array( 'visualizer-media-view' ), Visualizer_Plugin::VERSION, true );
161 407 wp_enqueue_script( 'visualizer-media', VISUALIZER_ABSURL . 'js/media.js', array( 'visualizer-media-toolbar' ), Visualizer_Plugin::VERSION, true );
162 408 }
163 409 }
@@ -183,9 +429,9 @@
183 429 'controller' => array(
184 430 'title' => esc_html__( 'Visualizations', 'visualizer' ),
185 431 ),
186 432 'routers' => array(
187 - 'library' => esc_html__( 'From Library', 'visualizer' ),
433 + 'library' => esc_html__( 'Insert from Library', 'visualizer' ),
188 434 'create' => esc_html__( 'Create New', 'visualizer' ),
189 435 ),
190 436 'library' => array(
191 437 'filters' => $chart_types,
@@ -191,9 +437,9 @@
191 437 'filters' => $chart_types,
192 438 'types' => array_keys( $chart_types ),
193 439 ),
194 440 'nonce' => wp_create_nonce(),
195 - 'buildurl' => add_query_arg( 'action', Visualizer_Plugin::ACTION_CREATE_CHART, admin_url( 'admin-ajax.php' ) ),
441 + 'buildurl' => esc_url( add_query_arg( 'action', Visualizer_Plugin::ACTION_CREATE_CHART, admin_url( 'admin-ajax.php' ) ) ),
196 442 );
197 443
198 444 return $strings;
199 445 }
@@ -206,9 +452,9 @@
206 452 * @static
207 453 * @access private
208 454 * @return array The associated array of chart types with localized names.
209 455 */
210 - public static function _getChartTypesLocalized( $enabledOnly = false, $get2Darray = false, $add_select = false ) {
456 + public static function _getChartTypesLocalized( $enabledOnly = false, $get2Darray = false, $add_select = false, $where = null ) {
211 457 $additional = array();
212 458 if ( $add_select ) {
213 459 $additional['select'] = array(
214 460 'name' => esc_html__( 'All', 'visualizer' ),
@@ -215,59 +461,102 @@
215 461 'enabled' => true,
216 462 );
217 463 }
218 464
465 + $enabled = self::proFeaturesEnabled();
466 +
219 467 $types = array_merge(
220 - $additional, array(
468 + $additional,
469 + array(
470 + 'tabular' => array(
471 + 'name' => esc_html__( 'Table', 'visualizer' ),
472 + 'enabled' => true,
473 + 'supports' => $enabled ? array( 'Google Charts', 'DataTable' ) : array( 'DataTable' ),
474 + ),
221 475 'pie' => array(
222 - 'name' => esc_html__( 'Pie', 'visualizer' ),
476 + 'name' => esc_html__( 'Pie/Donut', 'visualizer' ),
223 477 'enabled' => true,
478 + 'supports' => array( 'Google Charts', 'ChartJS' ),
224 479 ),
225 480 'line' => array(
226 481 'name' => esc_html__( 'Line', 'visualizer' ),
227 482 'enabled' => true,
483 + 'supports' => array( 'Google Charts', 'ChartJS' ),
228 484 ),
485 + 'bar' => array(
486 + 'name' => esc_html__( 'Bar', 'visualizer' ),
487 + 'enabled' => true,
488 + 'supports' => $enabled ? array( 'Google Charts', 'ChartJS' ) : array( 'Google Charts' ),
489 + ),
229 490 'area' => array(
230 491 'name' => esc_html__( 'Area', 'visualizer' ),
231 - 'enabled' => true,
492 + 'enabled' => $enabled,
493 + // in ChartJS, the fill option is used to make Line chart an area: https://www.chartjs.org/docs/latest/charts/area.html
494 + 'supports' => array( 'Google Charts' ),
495 + 'demo_url' => 'https://demo.themeisle.com/visualizer/area-chart/',
232 496 ),
233 497 'geo' => array(
234 498 'name' => esc_html__( 'Geo', 'visualizer' ),
235 - 'enabled' => true,
499 + 'enabled' => $enabled,
500 + 'supports' => array( 'Google Charts' ),
501 + 'demo_url' => 'https://demo.themeisle.com/visualizer/geo-chart/',
236 502 ),
237 - 'bar' => array(
238 - 'name' => esc_html__( 'Bar', 'visualizer' ),
239 - 'enabled' => true,
240 - ),
241 503 'column' => array(
242 504 'name' => esc_html__( 'Column', 'visualizer' ),
243 - 'enabled' => true,
505 + 'enabled' => $enabled,
506 + 'supports' => array( 'Google Charts', 'ChartJS' ),
507 + 'demo_url' => 'https://demo.themeisle.com/visualizer/column-chart/',
244 508 ),
245 - 'gauge' => array(
246 - 'name' => esc_html__( 'Gauge', 'visualizer' ),
247 - 'enabled' => true,
509 + 'bubble' => array(
510 + 'name' => esc_html__( 'Bubble', 'visualizer' ),
511 + 'enabled' => $enabled,
512 + // 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 + 'supports' => array( 'Google Charts' ),
514 + 'demo_url' => 'https://demo.themeisle.com/visualizer/bubble-chart/',
248 515 ),
249 516 'scatter' => array(
250 517 'name' => esc_html__( 'Scatter', 'visualizer' ),
251 - 'enabled' => true,
518 + 'enabled' => $enabled,
519 + 'supports' => array( 'Google Charts' ),
520 + 'demo_url' => 'https://demo.themeisle.com/visualizer/scatter-chart/',
252 521 ),
522 + 'gauge' => array(
523 + 'name' => esc_html__( 'Gauge', 'visualizer' ),
524 + 'enabled' => $enabled,
525 + 'supports' => array( 'Google Charts' ),
526 + 'demo_url' => 'https://demo.themeisle.com/visualizer/gauge-chart/',
527 + ),
253 528 'candlestick' => array(
254 529 'name' => esc_html__( 'Candlestick', 'visualizer' ),
255 - 'enabled' => true,
530 + 'enabled' => $enabled,
531 + 'supports' => array( 'Google Charts' ),
532 + 'demo_url' => 'https://demo.themeisle.com/visualizer/candlestick-chart/',
256 533 ),
257 534 // pro types
258 - 'table' => array(
259 - 'name' => esc_html__( 'Table', 'visualizer' ),
260 - 'enabled' => false,
261 - ),
262 535 'timeline' => array(
263 536 'name' => esc_html__( 'Timeline', 'visualizer' ),
264 537 'enabled' => false,
538 + 'supports' => array( 'Google Charts', 'ChartJS' ),
539 + 'demo_url' => 'https://demo.themeisle.com/visualizer/timeline-chart/',
265 540 ),
266 541 'combo' => array(
267 542 'name' => esc_html__( 'Combo', 'visualizer' ),
268 543 'enabled' => false,
544 + 'supports' => array( 'Google Charts' ),
545 + 'demo_url' => 'https://demo.themeisle.com/visualizer/combo-chart/',
269 546 ),
547 + 'polarArea' => array(
548 + 'name' => esc_html__( 'Polar Area', 'visualizer' ),
549 + 'enabled' => false,
550 + 'supports' => array( 'ChartJS' ),
551 + 'demo_url' => 'https://demo.themeisle.com/visualizer/polar-area-chart/',
552 + ),
553 + 'radar' => array(
554 + 'name' => esc_html__( 'Radar/Spider', 'visualizer' ),
555 + 'enabled' => false,
556 + 'supports' => array( 'ChartJS' ),
557 + 'demo_url' => 'https://demo.themeisle.com/visualizer/radar-spider-chart/',
558 + ),
270 559 )
271 560 );
272 561 $types = apply_filters( 'visualizer_pro_chart_types', $types );
273 562 if ( $enabledOnly ) {
@@ -276,8 +565,12 @@
276 565 if ( ! is_array( $array ) ) {
277 566 // support for old pro
278 567 $array = array( 'enabled' => true, 'name' => $array );
279 568 }
569 + // backward compatibility for PRO before v1.9.0
570 + if ( ! array_key_exists( 'supports', $array ) ) {
571 + $array['supports'] = array( 'Google Charts' );
572 + }
280 573 if ( ! $array['enabled'] ) {
281 574 continue;
282 575 }
283 576 $filtered[ $type ] = $array;
@@ -290,13 +583,74 @@
290 583 if ( ! is_array( $array ) ) {
291 584 // support for old pro
292 585 $array = array( 'enabled' => true, 'name' => $array );
293 586 }
587 + // backward compatibility for PRO before v1.9.0
588 + if ( ! array_key_exists( 'supports', $array ) ) {
589 + $array['supports'] = array( 'Google Charts' );
590 + }
294 591 $doubleD[ $type ] = $array['name'];
295 592 }
296 593 $types = $doubleD;
297 594 }
298 595
596 + return self::handleDeprecatedCharts( $types, $enabledOnly, $get2Darray, $where );
597 + }
598 +
599 + /**
600 + * Handle (soon-to-be) deprecated charts.
601 + */
602 + private static function handleDeprecatedCharts( $types, $enabledOnly, $get2Darray, $where ) {
603 + $deprecated = array();
604 +
605 + switch ( $where ) {
606 + case 'types':
607 + // fall-through
608 + case 'library':
609 + // if a user has a Gauge/Candlestick chart, then let them keep using it.
610 + if ( ! Visualizer_Module::is_pro() ) {
611 + if ( ! self::hasChartType( 'gauge' ) ) {
612 + if ( $get2Darray ) {
613 + $deprecated[] = 'gauge';
614 + } else {
615 + $types['gauge']['enabled'] = false;
616 + }
617 + }
618 + if ( ! self::hasChartType( 'candlestick' ) ) {
619 + if ( $get2Darray ) {
620 + $deprecated[] = 'candlestick';
621 + } else {
622 + $types['candlestick']['enabled'] = false;
623 + }
624 + }
625 + }
626 + break;
627 + default:
628 + // if a user has a Gauge/Candlestick chart, then let them keep using it.
629 + if ( ! Visualizer_Module::is_pro() ) {
630 + if ( ! self::hasChartType( 'gauge' ) ) {
631 + if ( $get2Darray ) {
632 + $deprecated[] = 'gauge';
633 + } else {
634 + $types['gauge']['enabled'] = false;
635 + }
636 + }
637 + if ( ! self::hasChartType( 'candlestick' ) ) {
638 + if ( $get2Darray ) {
639 + $deprecated[] = 'candlestick';
640 + } else {
641 + $types['candlestick']['enabled'] = false;
642 + }
643 + }
644 + }
645 + }
646 +
647 + if ( $deprecated ) {
648 + foreach ( $deprecated as $type ) {
649 + unset( $types[ $type ] );
650 + }
651 + }
652 +
299 653 return $types;
300 654 }
301 655
302 656 /**
@@ -306,11 +660,11 @@
306 660 * @global string $pagenow The name of the current page.
307 661 *
308 662 * @access public
309 663 */
310 - public function renderTempaltes() {
664 + public function renderTemplates() {
311 665 global $pagenow;
312 - if ( 'post.php' != $pagenow && 'post-new.php' != $pagenow ) {
666 + if ( 'post.php' !== $pagenow && 'post-new.php' !== $pagenow ) {
313 667 return;
314 668 }
315 669 $render = new Visualizer_Render_Templates();
316 670 $render->render();
@@ -328,30 +682,159 @@
328 682 *
329 683 * @param string $suffix The current page suffix.
330 684 */
331 685 public function enqueueLibraryScripts( $suffix ) {
332 - if ( $suffix == $this->_libraryPage ) {
333 - wp_enqueue_style( 'visualizer-library', VISUALIZER_ABSURL . 'css/library.css', array(), Visualizer_Plugin::VERSION );
334 - $this->_addFilter( 'media_upload_tabs', 'setupVisualizerTab' );
335 - wp_enqueue_media();
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 + }
336 715 wp_enqueue_script(
337 - 'visualizer-library', VISUALIZER_ABSURL . 'js/library.js', array(
338 - 'jquery',
339 - 'media-views',
340 - ), Visualizer_Plugin::VERSION, true
716 + "visualizer-render-$library",
717 + VISUALIZER_ABSURL . 'js/render-facade.js',
718 + apply_filters( 'visualizer_assets_render', array( 'visualizer-library', 'visualizer-customization' ), true ),
719 + Visualizer_Plugin::VERSION,
720 + true
341 721 );
342 - wp_enqueue_script( 'google-jsapi-new', '//www.gstatic.com/charts/loader.js', array(), null, true );
343 - wp_enqueue_script( 'google-jsapi-old', '//www.google.com/jsapi', array( 'google-jsapi-new' ), null, true );
722 + if ( 'd3' === $library ) {
723 + $has_d3 = true;
724 + }
725 + }
726 +
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 + }
753 +
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,
769 + true
770 + );
771 + }
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 + }
344 787 wp_enqueue_script(
345 - 'visualizer-render', VISUALIZER_ABSURL . 'js/render.js', array(
346 - 'google-jsapi-old',
347 - 'visualizer-library',
348 - ), Visualizer_Plugin::VERSION, true
788 + 'visualizer-chart-builder',
789 + VISUALIZER_ABSURL . 'classes/Visualizer/ChartBuilder/build/index.js',
790 + $chart_builder_deps,
791 + $asset['version'],
792 + true
349 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 + );
350 819 }
820 +
821 + do_action( 'themeisle_internal_page', VISUALIZER_DIRNAME, 'library' );
351 822 }
352 823
353 824 /**
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 + /**
354 837 * Adds visualizer tab for media upload tabs array.
355 838 *
356 839 * @since 1.0.0
357 840 *
@@ -374,24 +857,201 @@
374 857 *
375 858 * @access public
376 859 */
377 860 public function registerAdminMenu() {
378 - $title = esc_html__( 'Visualizer Library', 'visualizer' );
379 - $callback = array( $this, 'renderLibraryPage' );
380 - $this->_libraryPage = add_submenu_page( 'upload.php', $title, $title, 'edit_posts', Visualizer_Plugin::NAME, $callback );
861 + $svg_base64_icon = 'data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyB3aWR0aD0iNzdweCIgaGVpZ2h0PSI3N3B4IiB2aWV3Qm94PSIwIDAgNzcgNzciIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+CiAgICA8IS0tIEdlbmVyYXRvcjogU2tldGNoIDUyLjYgKDY3NDkxKSAtIGh0dHA6Ly93d3cuYm9oZW1pYW5jb2RpbmcuY29tL3NrZXRjaCAtLT4KICAgIDx0aXRsZT5Db21iaW5lZCBTaGFwZTwvdGl0bGU+CiAgICA8ZGVzYz5DcmVhdGVkIHdpdGggU2tldGNoLjwvZGVzYz4KICAgIDxnIGlkPSJQcm9kdWN0LVBhZ2UiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPgogICAgICAgIDxnIGlkPSJXb3JkUHJlc3MtcGx1Z2lucyIgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoLTE5MC4wMDAwMDAsIC00MzUuMDAwMDAwKSIgZmlsbD0iIzM5QzNEMiI+CiAgICAgICAgICAgIDxwYXRoIGQ9Ik0yMjguNSw1MTIgQzIwNy4yMzcwMzcsNTEyIDE5MCw0OTQuNzYyOTYzIDE5MCw0NzMuNSBDMTkwLDQ1Mi4yMzcwMzcgMjA3LjIzNzAzNyw0MzUgMjI4LjUsNDM1IEMyNDkuNzYyOTYzLDQzNSAyNjcsNDUyLjIzNzAzNyAyNjcsNDczLjUgQzI2Nyw0OTQuNzYyOTYzIDI0OS43NjI5NjMsNTEyIDIyOC41LDUxMiBaIE0yNDYuODQzNzUsNDgzLjU5MjA1OSBMMjE1LjYyNSw0ODMuNTkyMDU5IEwyMTUuNjI1LDQ2MC44MjY1NDQgQzIxNS42MjUsNDYwLjE2NDU0NyAyMTUuMTA3NTc4LDQ1OS42MjgzNTkgMjE0LjQ2ODc1LDQ1OS42MjgzNTkgTDIxMi4xNTYyNSw0NTkuNjI4MzU5IEMyMTEuNTE3NDIyLDQ1OS42MjgzNTkgMjExLDQ2MC4xNjQ1NDcgMjExLDQ2MC44MjY1NDQgTDIxMSw0ODUuOTg4NDI5IEMyMTEsNDg3LjMxMTY3NCAyMTIuMDM1NTY2LDQ4OC4zODQ3OTggMjEzLjMxMjUsNDg4LjM4NDc5OCBMMjQ2Ljg0Mzc1LDQ4OC4zODQ3OTggQzI0Ny40ODI1NzgsNDg4LjM4NDc5OCAyNDgsNDg3Ljg0ODYxMSAyNDgsNDg3LjE4NjYxMyBMMjQ4LDQ4NC43OTAyNDQgQzI0OCw0ODQuMTI4MjQ2IDI0Ny40ODI1NzgsNDgzLjU5MjA1OSAyNDYuODQzNzUsNDgzLjU5MjA1OSBaIE0yNDQuNTMxMjUsNDYyLjAyNDcyOSBMMjM1Ljk5OTU3LDQ2Mi4wMjQ3MjkgQzIzNC40NTQ1MzEsNDYyLjAyNDcyOSAyMzMuNjgwNTY2LDQ2My45NjA1NDcgMjM0Ljc3MzIyMyw0NjUuMDkyODMyIEwyMzcuMTE0NjI5LDQ2Ny41MTkxNTYgTDIzMS44MTI1LDQ3My4wMTQzMzIgTDIyNi41MTAzNzEsNDY3LjUxOTkwNSBDMjI1LjYwNzA1MSw0NjYuNTgzODIzIDIyNC4xNDI5NDksNDY2LjU4MzgyMyAyMjMuMjQwMzUyLDQ2Ny41MTk5MDUgTDIxOC4yNzY0MjYsNDcyLjY2Mzg2MyBDMjE3LjgyNDc2Niw0NzMuMTMxOTA0IDIxNy44MjQ3NjYsNDczLjg5MDUwNSAyMTguMjc2NDI2LDQ3NC4zNTg1NDYgTDIxOS45MTEwNzQsNDc2LjA1MjQ4IEMyMjAuMzYyNzM0LDQ3Ni41MjA1MjEgMjIxLjA5NDc4NSw0NzYuNTIwNTIxIDIyMS41NDY0NDUsNDc2LjA1MjQ4IEwyMjQuODc1LDQ3Mi42MDI0NTYgTDIzMC4xNzcxMjksNDc4LjA5Njg4MyBDMjMxLjA4MDQ0OSw0NzkuMDMyOTY1IDIzMi41NDQ1NTEsNDc5LjAzMjk2NSAyMzMuNDQ3MTQ4LDQ3OC4wOTY4ODMgTDI0MC4zODQ2NDgsNDcwLjkwNzc3MyBMMjQyLjcyNjA1NSw0NzMuMzM0MDk4IEMyNDMuODE4NzExLDQ3NC40NjYzODIgMjQ1LjY4Njc3Nyw0NzMuNjY0MzQ3IDI0NS42ODY3NzcsNDcyLjA2MzI3MyBMMjQ1LjY4Njc3Nyw0NjMuMjIyOTE0IEMyNDUuNjg3NSw0NjIuNTYwOTE3IDI0NS4xNzAwNzgsNDYyLjAyNDcyOSAyNDQuNTMxMjUsNDYyLjAyNDcyOSBaIiBpZD0iQ29tYmluZWQtU2hhcGUiPjwvcGF0aD4KICAgICAgICA8L2c+CiAgICA8L2c+Cjwvc3ZnPg==';
862 +
863 + $this->_libraryPage = add_menu_page( __( 'Visualizer', 'visualizer' ), __( 'Visualizer', 'visualizer' ), 'edit_posts', Visualizer_Plugin::NAME, array( $this, 'renderLibraryPage' ), $svg_base64_icon, 99.7666 );
864 +
865 + add_submenu_page(
866 + Visualizer_Plugin::NAME,
867 + __( 'Chart Library', 'visualizer' ),
868 + __( 'Chart Library', 'visualizer' ),
869 + 'edit_posts',
870 + 'admin.php?page=' . Visualizer_Plugin::NAME
871 + );
872 + add_submenu_page(
873 + Visualizer_Plugin::NAME,
874 + __( 'Add New Chart', 'visualizer' ),
875 + __( 'Add New Chart', 'visualizer' ),
876 + 'edit_posts',
877 + 'admin.php?page=' . Visualizer_Plugin::NAME . '&vaction=addnew'
878 + );
879 +
880 + $this->_settingsPage = add_submenu_page(
881 + 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 + __( 'Support', 'visualizer' ),
892 + __( '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 + 'edit_posts',
894 + 'viz-support',
895 + array( $this, 'renderSupportPage' )
896 + );
897 +
898 + remove_submenu_page( Visualizer_Plugin::NAME, Visualizer_Plugin::NAME );
899 +
900 + add_action( "load-{$this->_libraryPage}", array( $this, 'addScreenOptions' ) );
901 + add_action( 'admin_footer', array( $this, 'handleGetProSubMenu' ) );
381 902 }
382 903
383 904 /**
384 - * Renders visualizer library page.
385 - *
386 - * @since 1.0.0
387 - *
388 - * @access public
905 + * Handle get pro plugin submenu.
389 906 */
390 - public function renderLibraryPage() {
907 + public function handleGetProSubMenu() {
908 + if ( ! Visualizer_Module::is_pro() ) {
909 + ?>
910 + <style type="text/css">
911 + #toplevel_page_visualizer ul.wp-submenu li:last-child {
912 + background: #FF7E65;
913 + font-size: 14px;
914 + font-weight: 600;
915 + color: #fff;
916 + }
917 + #toplevel_page_visualizer ul.wp-submenu li:last-child > a > span {
918 + color: #fff !important;
919 + }
920 + #toplevel_page_visualizer ul.wp-submenu li:last-child > a:hover {
921 + box-shadow: inherit;
922 + }
923 + </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 + }
935 + }
936 +
937 + /**
938 + * Adds the screen options for pagination.
939 + */
940 + public function addScreenOptions() {
941 + $screen = get_current_screen();
942 +
943 + // bail if it's some other page.
944 + if ( ! is_object( $screen ) || $screen->id !== $this->_libraryPage ) {
945 + return;
946 + }
947 +
948 + $args = array(
949 + 'label' => __( 'Number of charts per page:', 'visualizer' ),
950 + 'default' => 6,
951 + 'option' => 'visualizer_library_per_page',
952 + );
953 + add_screen_option( 'per_page', $args );
954 + }
955 +
956 + /**
957 + * Returns the screen option for pagination.
958 + */
959 + public function setScreenOptions( $status, $option, $value ) {
960 + if ( 'visualizer_library_per_page' === $option ) {
961 + return $value;
962 + }
963 + }
964 +
965 + /**
966 + * Adds the display filters to be used in the meta_query while displaying the charts.
967 + */
968 + private function getDisplayFilters( &$query_args ) {
969 + $query = array();
970 + global $sitepress;
971 + if ( Visualizer_Module::is_pro() && ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) ) {
972 + $current_lang = icl_get_current_language();
973 + if ( in_array( $current_lang, array( 'all', icl_get_default_language() ), true ) ) {
974 + $query[] = array(
975 + 'key' => 'chart_lang',
976 + 'compare' => 'NOT EXISTS',
977 + );
978 + } else {
979 + $query[] = array(
980 + 'key' => 'chart_lang',
981 + 'value' => $current_lang,
982 + 'compare' => '=',
983 + );
984 + }
985 + }
986 +
987 + // add chart type filter to the query arguments
988 + $type = filter_input( INPUT_GET, 'type' );
989 + if ( $type && in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
990 + $query[] = array(
991 + 'key' => Visualizer_Plugin::CF_CHART_TYPE,
992 + 'value' => $type,
993 + 'compare' => '=',
994 + );
995 + }
996 +
997 + // add chart library filter to the query arguments
998 + $library = filter_input( INPUT_GET, 'library' );
999 + if ( $library ) {
1000 + $query[] = array(
1001 + 'key' => Visualizer_Plugin::CF_CHART_LIBRARY,
1002 + 'value' => $library,
1003 + 'compare' => '=',
1004 + );
1005 + }
1006 +
1007 + // add date filter to the query arguments
1008 + $date = filter_input( INPUT_GET, 'date' );
1009 + $possible = array_keys( Visualizer_Plugin::getSupportedDateFilter() );
1010 + if ( $date && in_array( $date, $possible, true ) ) {
1011 + $query_args['date_query'] = array(
1012 + 'after' => "$date -1 day",
1013 + 'inclusive' => true,
1014 + );
1015 + }
1016 +
1017 + // add source filter to the query arguments
1018 + $source = filter_input( INPUT_GET, 'source' );
1019 + if ( $source ) {
1020 + $source = ucwords( $source );
1021 + $source = "Visualizer_Source_{$source}";
1022 + $query[] = array(
1023 + 'key' => Visualizer_Plugin::CF_SOURCE,
1024 + 'value' => $source,
1025 + 'compare' => '=',
1026 + );
1027 + }
1028 +
1029 + $query_args['meta_query'] = $query;
1030 +
1031 + $orderby = filter_input( INPUT_GET, 'orderby' );
1032 + $order = filter_input( INPUT_GET, 'order' );
1033 + if ( $orderby ) {
1034 + $query_args['order_by'] = $orderby;
1035 + }
1036 + $query_args['order'] = empty( $order ) ? 'desc' : $order;
1037 + }
1038 +
1039 + /**
1040 + * Get the instance of WP_Query that fetches the charts as per the given criteria.
1041 + */
1042 + private function getQuery() {
1043 + static $q;
1044 + if ( ! is_null( $q ) ) {
1045 + return $q;
1046 + }
1047 +
391 1048 // get current page
392 1049 $page = filter_input(
393 - INPUT_GET, 'vpage', FILTER_VALIDATE_INT, array(
1050 + INPUT_GET,
1051 + 'vpage',
1052 + FILTER_VALIDATE_INT,
1053 + array(
394 1054 'options' => array(
395 1055 'min_range' => 1,
396 1056 'default' => 1,
397 1057 ),
@@ -396,29 +1056,34 @@
396 1056 'default' => 1,
397 1057 ),
398 1058 )
399 1059 );
1060 +
1061 + $per_page = 6;
1062 + $screen = get_current_screen();
1063 + if ( $screen ) {
1064 + // retrieve the per_page option
1065 + $screen_option = $screen->get_option( 'per_page', 'option' );
1066 + // retrieve the value stored for the current user
1067 + $user = get_current_user_id();
1068 + $per_page = get_user_meta( $user, $screen_option, true );
1069 + if ( empty( $per_page ) || $per_page < 1 ) {
1070 + // nothing set, get the default value
1071 + $per_page = $screen->get_option( 'per_page', 'default' );
1072 + }
1073 + }
1074 +
400 1075 // the initial query arguments to fetch charts
401 1076 $query_args = array(
402 1077 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
403 - 'posts_per_page' => 6,
1078 + 'posts_per_page' => $per_page,
404 1079 'paged' => $page,
405 1080 );
406 - // add chart type filter to the query arguments
407 - $filter = filter_input( INPUT_GET, 'type' );
408 - if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) {
409 - $query_args['meta_query'] = array(
410 - array(
411 - 'key' => Visualizer_Plugin::CF_CHART_TYPE,
412 - 'value' => $filter,
413 - 'compare' => '=',
414 - ),
415 - );
416 - } else {
417 - $filter = 'all';
418 - }
1081 +
1082 + $this->getDisplayFilters( $query_args );
1083 +
419 1084 // Added by Ash/Upwork
420 - $filterByMeta = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );
1085 + $filterByMeta = ! empty( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
421 1086 if ( $filterByMeta ) {
422 1087 $query = array(
423 1088 'key' => Visualizer_Plugin::CF_SETTINGS,
424 1089 'value' => $filterByMeta,
@@ -427,49 +1092,246 @@
427 1092 $meta = isset( $query_args['meta_query'] ) ? $query_args['meta_query'] : array();
428 1093 $meta[] = $query;
429 1094 $query_args['meta_query'] = $meta;
430 1095 }
431 - // Added by Ash/Upwork
432 - // fetch charts
1096 + $q = new WP_Query( apply_filters( 'visualizer_query_args', $query_args ) );
1097 + return $q;
1098 + }
1099 +
1100 + /**
1101 + * Renders support page.
1102 + *
1103 + * @since 3.3.0
1104 + *
1105 + * @access public
1106 + */
1107 + public function renderSupportPage() {
1108 + wp_enqueue_style( 'visualizer-upsell', VISUALIZER_ABSURL . 'css/upsell.css', array(), Visualizer_Plugin::VERSION );
1109 + include_once VISUALIZER_ABSPATH . '/templates/support.php';
1110 + }
1111 +
1112 + /**
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 + * Renders visualizer library page.
1197 + *
1198 + * @since 1.0.0
1199 + *
1200 + * @access public
1201 + */
1202 + public function renderLibraryPage() {
433 1203 $charts = array();
434 - $query = new WP_Query( $query_args );
1204 + $query = $this->getQuery();
1205 +
1206 + // get current page
1207 + $page = filter_input(
1208 + INPUT_GET,
1209 + 'vpage',
1210 + FILTER_VALIDATE_INT,
1211 + array(
1212 + 'options' => array(
1213 + 'min_range' => 1,
1214 + 'default' => 1,
1215 + ),
1216 + )
1217 + );
1218 + // add chart type filter to the query arguments
1219 + $filter = filter_input( INPUT_GET, 'type' );
1220 + if ( ! ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) ) {
1221 + $filter = 'all';
1222 + }
1223 +
1224 + $css = '';
435 1225 while ( $query->have_posts() ) {
436 1226 $chart = $query->next_post();
1227 +
1228 + // if the user has updated a chart and instead of saving it, has closed the modal. If the user refreshes, they should get the original chart.
1229 + $chart = $this->handleExistingRevisions( $chart->ID, $chart );
1230 + // refresh a "live" db query chart.
1231 + $chart = apply_filters( 'visualizer_schedule_refresh_chart', $chart, $chart->ID, false );
1232 +
1233 + $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
1234 +
437 1235 // fetch and update settings
438 1236 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
439 - unset( $settings['height'], $settings['width'] );
440 - $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
1237 +
1238 + $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
1239 + if ( ! empty( $atts['settings'] ) ) {
1240 + $settings = apply_filters( $atts['settings'], $settings, $chart->ID, $type );
1241 + }
1242 +
1243 + if ( $settings ) {
1244 + unset( $settings['height'], $settings['width'], $settings['chartArea'] );
1245 + }
1246 +
441 1247 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
442 - $data = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, unserialize( html_entity_decode( $chart->post_content ) ), $chart->ID, $type );
1248 + $data = self::get_chart_data( $chart, $type );
1249 +
1250 + // Set default setting series when add manual chart data - Only during first creation.
1251 + if ( isset( $settings['series'] ) && ! empty( $series ) ) {
1252 + $count_series = count( $series ) - count( $settings['series'] );
1253 + if ( $count_series > 1 ) {
1254 + $clone_last_series = end( $settings['series'] );
1255 + $clone_last_series = array_fill_keys( array_keys( $clone_last_series ), '' );
1256 + foreach ( range( 1, $count_series ) as $item ) {
1257 + $settings['series'][] = $clone_last_series;
1258 + }
1259 + }
1260 + }
1261 +
1262 + $library = $this->load_chart_type( $chart->ID );
1263 +
1264 + $id = 'visualizer-' . $chart->ID;
1265 + $arguments = $this->get_inline_custom_css( $id, $settings );
1266 + if ( ! empty( $arguments ) ) {
1267 + $css .= $arguments[0];
1268 + $settings = $arguments[1];
1269 + }
1270 +
1271 + if ( isset( $settings['controls']['ui']['labelStacking'] ) ) {
1272 + unset( $settings['controls']['ui']['labelStacking'] );
1273 + }
1274 + if ( isset( $settings['controls']['ui']['orientation'] ) ) {
1275 + unset( $settings['controls']['ui']['orientation'] );
1276 + }
1277 +
443 1278 // add chart to the array
444 - $charts[ 'visualizer-' . $chart->ID ] = array(
1279 + $charts[ $id ] = array(
445 1280 'id' => $chart->ID,
1281 + 'title' => $chart->post_title,
446 1282 'type' => $type,
447 1283 'series' => $series,
448 1284 'settings' => $settings,
449 1285 'data' => $data,
1286 + 'library' => $library,
450 1287 );
1288 + if ( 'd3' === $library ) {
1289 + $charts[ $id ]['code'] = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
1290 + }
451 1291 }
452 1292 // enqueue charts array
453 1293 $ajaxurl = admin_url( 'admin-ajax.php' );
1294 +
454 1295 wp_localize_script(
455 - 'visualizer-library', 'visualizer', array(
1296 + 'visualizer-library',
1297 + 'visualizer',
1298 + array(
1299 + 'language' => $this->get_language(),
1300 + 'map_api_key' => get_option( 'visualizer-map-api-key' ),
456 1301 'charts' => $charts,
457 1302 'urls' => array(
458 - 'base' => add_query_arg( 'vpage', false ),
459 - 'create' => add_query_arg(
460 - array(
461 - 'action' => Visualizer_Plugin::ACTION_CREATE_CHART,
462 - 'library' => 'yes',
463 - ), $ajaxurl
1303 + 'base' => esc_url( add_query_arg( array( 'vpage' => false, 'vaction' => false ) ) ),
1304 + 'create' => esc_url(
1305 + add_query_arg(
1306 + array(
1307 + 'action' => Visualizer_Plugin::ACTION_CREATE_CHART,
1308 + 'library' => 'yes',
1309 + 'type' => isset( $_GET['type'] ) ? $_GET['type'] : '',
1310 + 'chart-library' => isset( $_GET['chart-library'] ) ? $_GET['chart-library'] : '',
1311 + 'vaction' => false,
1312 + ),
1313 + $ajaxurl
1314 + )
464 1315 ),
465 - 'edit' => add_query_arg(
466 - array(
467 - 'action' => Visualizer_Plugin::ACTION_EDIT_CHART,
468 - 'library' => 'yes',
469 - ), $ajaxurl
1316 + 'edit' => esc_url(
1317 + add_query_arg(
1318 + array(
1319 + 'action' => Visualizer_Plugin::ACTION_EDIT_CHART,
1320 + 'library' => 'yes',
1321 + 'vaction' => false,
1322 + ),
1323 + $ajaxurl
1324 + )
470 1325 ),
471 1326 ),
1327 + 'page_type' => 'library',
1328 + 'is_front' => false,
1329 + 'i10n' => array(
1330 + 'copied' => __( 'The shortcode has been copied to your clipboard. Hit Ctrl-V/Cmd-V to paste it.', 'visualizer' ),
1331 + '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 + ),
1333 + 'is_pro_user' => Visualizer_Module::is_pro(),
472 1334 )
473 1335 );
474 1336 // render library page
475 1337 $render = new Visualizer_Render_Library();
@@ -474,12 +1336,13 @@
474 1336 // render library page
475 1337 $render = new Visualizer_Render_Library();
476 1338 $render->charts = $charts;
477 1339 $render->type = $filter;
478 - $render->types = self::_getChartTypesLocalized();
1340 + $render->types = self::_getChartTypesLocalized( false, false, false, true );
1341 + $render->custom_css = $css;
479 1342 $render->pagination = paginate_links(
480 1343 array(
481 - 'base' => add_query_arg( 'vpage', '%#%' ),
1344 + 'base' => add_query_arg( array( 'vpage' => '%#%', 'vaction' => false ) ),
482 1345 'format' => '',
483 1346 'current' => $page,
484 1347 'total' => $query->max_num_pages,
485 1348 'type' => 'array',
@@ -484,8 +1347,9 @@
484 1347 'total' => $query->max_num_pages,
485 1348 'type' => 'array',
486 1349 )
487 1350 );
1351 +
488 1352 $render->render();
489 1353 }
490 1354
491 1355 /**
@@ -500,14 +1364,14 @@
500 1364 *
501 1365 * @return array Updated array of action links.
502 1366 */
503 1367 public function getPluginActionLinks( $links, $file ) {
504 - if ( $file == plugin_basename( VISUALIZER_BASEFILE ) ) {
1368 + if ( $file === plugin_basename( VISUALIZER_BASEFILE ) ) {
505 1369 array_unshift(
506 1370 $links,
507 1371 sprintf(
508 1372 '<a href="%s">%s</a>',
509 - admin_url( 'upload.php?page=' . Visualizer_Plugin::NAME ),
1373 + admin_url( 'admin.php?page=' . Visualizer_Plugin::NAME ),
510 1374 esc_html__( 'Library', 'visualizer' )
511 1375 )
512 1376 );
513 1377 }
@@ -527,18 +1391,32 @@
527 1391 *
528 1392 * @return array Updated array of plugin meta links.
529 1393 */
530 1394 public function getPluginMetaLinks( $plugin_meta, $plugin_file ) {
531 - if ( $plugin_file == plugin_basename( VISUALIZER_BASEFILE ) ) {
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 + if ( $plugin_file === plugin_basename( VISUALIZER_BASEFILE ) ) {
532 1410 // knowledge base link
533 1411 $plugin_meta[] = sprintf(
534 - '<a href="https://github.com/codeinwp/visualizer/wiki" target="_blank">%s</a>',
535 - esc_html__( 'Knowledge Base', 'visualizer' )
1412 + '<a href="' . VISUALIZER_MAIN_DOC . '" target="_blank">%s</a>',
1413 + esc_html__( 'Docs', 'visualizer' )
536 1414 );
537 1415 // flattr link
538 1416 $plugin_meta[] = sprintf(
539 - '<a style="color:red" href="https://themeisle.com/plugins/visualizer-charts-and-graphs-pro-addon/" target="_blank">%s</a>',
540 - esc_html__( 'Pro Addon', 'visualizer' )
1417 + '<a style="color:red" href="' . tsdk_utmify( Visualizer_Plugin::PRO_TEASER_URL, 'pluginrow' ) . '" target="_blank">%s</a>',
1418 + esc_html__( 'Get Visualizer Pro', 'visualizer' )
541 1419 );
542 1420 }
543 1421
544 1422 return $plugin_meta;
@@ -543,5 +1421,213 @@
543 1421
544 1422 return $plugin_meta;
545 1423 }
546 1424
1425 + /**
1426 + * Returns true when premium chart types should be enabled for the current user.
1427 + *
1428 + * Pro 1.9.0+ hooks the 'visualizer_is_pro' filter and returns true only when
1429 + * the license is valid. Pre-1.9.0 Pro defined a Visualizer_Pro class instead;
1430 + * we detect that as a fallback so those legacy installs still work.
1431 + *
1432 + * Passing false as the filter default ensures the constant VISUALIZER_PRO
1433 + * (which is set to true whenever the Pro class exists, regardless of license
1434 + * state) cannot bypass the license check.
1435 + *
1436 + * @return bool
1437 + */
1438 + public static function proFeaturesEnabled() {
1439 + $is_pro_filter = apply_filters( 'visualizer_is_pro', false );
1440 +
1441 + // Pro 1.9.0+: filter is hooked and returns true only with a valid license.
1442 + if ( $is_pro_filter ) {
1443 + return true;
1444 + }
1445 +
1446 + // Pro is installed (active) but the license check above did not pass.
1447 + // Do NOT fall through to the legacy "old user" path — that path exists
1448 + // only for sites that never had Pro installed. If Pro is present but
1449 + // the license is inactive we should lock, regardless of chart history.
1450 + if ( class_exists( 'Visualizer_Pro', false ) ) {
1451 + return false;
1452 + }
1453 +
1454 + // No Pro installed at all: grant legacy access to existing users so
1455 + // their charts are not suddenly broken when they upgrade the free plugin.
1456 + return 'yes' === get_option( 'visualizer-new-user' ) ? false : true;
1457 + }
1458 +
1459 + /**
1460 + * Multilingual Support.
1461 + *
1462 + * @param int $chart_id Chart ID.
1463 + * @return bool Default false
1464 + */
1465 + public function addMultilingualSupport( $chart_id ) {
1466 + global $sitepress;
1467 + if ( Visualizer_Module::is_pro() ) {
1468 + return;
1469 + }
1470 + if ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) {
1471 + $language = icl_get_languages();
1472 + $current_lang = icl_get_current_language();
1473 + $default_lang = icl_get_default_language();
1474 + $post_info = wpml_get_language_information( null, $chart_id );
1475 + $translations = array();
1476 + if ( ! empty( $post_info ) && ( $default_lang === $post_info['language_code'] ) ) {
1477 + $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
1478 + $translations = $sitepress->get_element_translations( $trid );
1479 + }
1480 + if ( empty( $translations ) ) {
1481 + return;
1482 + }
1483 + ?>
1484 + <hr><div class="visualizer-languages-list only-pro">
1485 + <?php
1486 + foreach ( $language as $lang ) {
1487 + $lang_code = $lang['code'];
1488 + if ( $current_lang !== $lang_code ) {
1489 + ?>
1490 + <a href="javascript:;">
1491 + <img src="<?php echo esc_url( $lang['country_flag_url'] ); ?>" alt="<?php echo esc_attr( $lang['translated_name'] ); ?>">
1492 + </a>
1493 + <?php
1494 + }
1495 + }
1496 + ?>
1497 + <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>
1498 + </div>
1499 + <?php
1500 + }
1501 + }
1502 +
1503 + /**
1504 + * Check chart is enabled or not.
1505 + *
1506 + * @param string $type Chart ID.
1507 + *
1508 + * @return bool Default false
1509 + */
1510 + public static function checkChartStatus( $type ) {
1511 + $types = self::_getChartTypesLocalized( false, false, false, true );
1512 + if ( isset( $types[ $type ] ) ) {
1513 + return ! empty( $types[ $type ]['enabled'] );
1514 + }
1515 + return false;
1516 + }
1517 +
1518 + /**
1519 + * Get the survey metadata.
1520 + *
1521 + * @param array $data The data for survey in Formbricks format.
1522 + * @param string $page_slug The slug of the loaded page.
1523 + *
1524 + * @return array The survey metadata.
1525 + */
1526 + public function get_survey_metadata( $data, $page_slug ) {
1527 + $install_date = get_option( 'visualizer_install', time() );
1528 + $install_days_number = intval( ( time() - $install_date ) / DAY_IN_SECONDS );
1529 +
1530 + $license_status = apply_filters( 'product_visualizer_license_status', 'invalid' );
1531 + $license_plan = apply_filters( 'product_visualizer_license_plan', false );
1532 + $license_key = apply_filters( 'product_visualizer_license_key', false );
1533 +
1534 + $plugin_data = get_plugin_data( VISUALIZER_BASEFILE, false, false );
1535 + $plugin_version = '';
1536 +
1537 + if ( ! empty( $plugin_data['Version'] ) ) {
1538 + $plugin_version = $plugin_data['Version'];
1539 + }
1540 +
1541 + $count_charts_cache_key = 'visualizer_count_charts';
1542 + $charts_number = get_transient( $count_charts_cache_key );
1543 +
1544 + if ( false === $charts_number ) {
1545 + $charts_number = $this->count_charts( 100 );
1546 + set_transient( $count_charts_cache_key, $charts_number, 100 === $charts_number ? WEEK_IN_SECONDS : 6 * HOUR_IN_SECONDS );
1547 + } else {
1548 + $charts_number = strval( $charts_number );
1549 + }
1550 +
1551 + $data = array(
1552 + 'environmentId' => 'cltef8cut1s7wyyfxy3rlxzs5',
1553 + 'attributes' => array(
1554 + 'free_version' => $plugin_version,
1555 + 'pro_version' => defined( 'VISUALIZER_PRO_VERSION' ) ? VISUALIZER_PRO_VERSION : '',
1556 + 'license_status' => $license_status,
1557 + 'install_days_number' => $install_days_number,
1558 + 'charts_number' => $charts_number,
1559 + ),
1560 + );
1561 +
1562 + if ( ! empty( $license_plan ) ) {
1563 + $data['attributes']['plan'] = $license_plan;
1564 + }
1565 +
1566 + if ( ! empty( $license_key ) ) {
1567 + $data['attributes']['license_key'] = apply_filters( 'themeisle_sdk_secret_masking', $license_key );
1568 + }
1569 +
1570 + return $data;
1571 + }
1572 +
1573 + /**
1574 + * Count the charts.
1575 + *
1576 + * @param int $limit The count limit (optional).
1577 + *
1578 + * @return int The number of charts.
1579 + */
1580 + public function count_charts( $limit = -1 ) {
1581 + $args = array(
1582 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
1583 + 'post_status' => 'publish',
1584 + 'posts_per_page' => $limit,
1585 + 'fields' => 'ids',
1586 + );
1587 +
1588 + $query = new WP_Query( $args);
1589 + return $query->post_count;
1590 + }
1591 +
1592 + /**
1593 + * Set the black friday data.
1594 + *
1595 + * @param array $configs The configuration array for the loaded products.
1596 + * @return array
1597 + */
1598 + public function add_black_friday_data( $configs ) {
1599 + $config = $configs['default'];
1600 +
1601 + // translators: %1$s - HTML tag, %2$s - discount, %3$s - HTML tag, %4$s - product name.
1602 + $message_template = __( 'Our biggest sale of the year: %1$sup to %2$s OFF%3$s on %4$s. Don\'t miss this limited-time offer.', 'visualizer' );
1603 + $product_label = 'Visualizer';
1604 + $discount = '70%';
1605 +
1606 + $plan = apply_filters( 'product_visualizer_license_plan', 0 );
1607 + $license = apply_filters( 'product_visualizer_license_key', false );
1608 + $is_pro = 0 < $plan;
1609 +
1610 + if ( $is_pro ) {
1611 + // translators: %1$s - HTML tag, %2$s - discount, %3$s - HTML tag, %4$s - product name.
1612 + $message_template = __( 'Get %1$sup to %2$s off%3$s when you upgrade your %4$s plan or renew early.', 'visualizer' );
1613 + $product_label = 'Visualizer Pro';
1614 + $discount = '30%';
1615 + }
1616 +
1617 + $product_label = sprintf( '<strong>%s</strong>', $product_label );
1618 + $url_params = array(
1619 + 'utm_term' => $is_pro ? 'plan-' . $plan : 'free',
1620 + 'lkey' => ! empty( $license ) ? $license : false,
1621 + );
1622 +
1623 + $config['message'] = sprintf( $message_template, '<strong>', $discount, '</strong>', $product_label );
1624 + $config['sale_url'] = add_query_arg(
1625 + $url_params,
1626 + tsdk_translate_link( tsdk_utmify( 'https://themeisle.link/vizualizer-bf', 'bfcm', 'visualizer' ) )
1627 + );
1628 +
1629 + $configs[ VISUALIZER_DIRNAME ] = $config;
1630 +
1631 + return $configs;
1632 + }
547 1633 }