PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 4.0.2
Visualizer – Tables & Charts Manager with Built-in AI Generator v4.0.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
visualizer / classes / Visualizer / Module / Admin.php

Admin.php in Visualizer – Tables & Charts Manager with Built-in AI Generator 4.0.2, at classes/Visualizer/Module/Admin.php

1,659 lines 54.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 // +----------------------------------------------------------------------+
3 // | Copyright 2013 Madpixels (email : visualizer@madpixels.net) |
4 // +----------------------------------------------------------------------+
5 // | This program is free software; you can redistribute it and/or modify |
6 // | it under the terms of the GNU General Public License, version 2, as |
7 // | published by the Free Software Foundation. |
8 // | |
9 // | This program is distributed in the hope that it will be useful, |
10 // | but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 // | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 // | GNU General Public License for more details. |
13 // | |
14 // | You should have received a copy of the GNU General Public License |
15 // | along with this program; if not, write to the Free Software |
16 // | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, |
17 // | MA 02110-1301 USA |
18 // +----------------------------------------------------------------------+
19 // | Author: Eugene Manuilov <eugene@manuilov.org> |
20 // +----------------------------------------------------------------------+
21 /**
22 * The module for all admin stuff.
23 *
24 * @category Visualizer
25 * @package Module
26 *
27 * @since 1.0.0
28 */
29 class Visualizer_Module_Admin extends Visualizer_Module {
30
31 const NAME = __CLASS__;
32
33 const OPTION_GLOBAL_SETTINGS = 'visualizer_global_settings';
34
35 /**
36 * Library page suffix.
37 *
38 * @since 1.0.0
39 *
40 * @access private
41 * @var string
42 */
43 private $_libraryPage;
44
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 /**
62 * Constructor.
63 *
64 * @since 1.0.0
65 *
66 * @access public
67 *
68 * @param Visualizer_Plugin $plugin The instance of the plugin.
69 */
70 public function __construct( Visualizer_Plugin $plugin ) {
71 parent::__construct( $plugin );
72 $this->_addAction( 'load-post.php', 'enqueueMediaScripts' );
73 $this->_addAction( 'load-post-new.php', 'enqueueMediaScripts' );
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' );
80 $this->_addAction( 'admin_menu', 'registerAdminMenu' );
81 $this->_addFilter( 'media_view_strings', 'setupMediaViewStrings' );
82 $this->_addFilter( 'plugin_action_links', 'getPluginActionLinks', 10, 2 );
83 $this->_addFilter( 'plugin_row_meta', 'getPluginMetaLinks', 10, 2 );
84 $this->_addFilter( 'visualizer_logger_data', 'getLoggerData' );
85 $this->_addFilter( 'visualizer_feedback_review_trigger', 'feedbackReviewTrigger' );
86 $this->_addFilter( 'themeisle_sdk_blackfriday_data', 'add_black_friday_data' );
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
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 }
111 }
112 /**
113 * Display review notice.
114 */
115 public function render_review_notice( $footer_text ) {
116 $current_screen = get_current_screen();
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
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 /**
262 * Admin init.
263 *
264 * @access public
265 */
266 public function init() {
267 if ( current_user_can( 'edit_posts' ) && current_user_can( 'edit_pages' ) && 'true' === get_user_option( 'rich_editing' ) ) {
268 $this->_addFilter( 'mce_external_languages', 'add_tinymce_lang', 10, 1 );
269 $this->_addFilter( 'mce_external_plugins', 'tinymce_plugin', 10, 1 );
270 $this->_addFilter( 'mce_buttons', 'register_mce_button', 10, 1 );
271 $this->_addFilter( 'tiny_mce_before_init', 'get_strings_for_block', 10, 1 );
272 }
273 }
274
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 /**
289 * Load plugin translation for - TinyMCE API
290 *
291 * @access public
292 * @param array $arr The tinymce_lang array.
293 * @return array
294 */
295 public function add_tinymce_lang( $arr ) {
296 $ui_lang = VISUALIZER_ABSPATH . '/classes/Visualizer/Module/Language.php';
297 $ui_lang = apply_filters( 'visualizer_ui_lang_filter', $ui_lang );
298 $arr[] = $ui_lang;
299 return $arr;
300 }
301
302 /**
303 * Load custom js options - TinyMCE API
304 *
305 * @access public
306 * @param array $plugin_array The tinymce plugin array.
307 * @return array
308 */
309 public function tinymce_plugin( $plugin_array ) {
310 $plugin_array['visualizer_mce_button'] = VISUALIZER_ABSURL . 'js/mce.js';
311 return $plugin_array;
312 }
313
314 /**
315 * Register new button in the editor
316 *
317 * @access public
318 * @param array $buttons The tinymce buttons array.
319 * @return array
320 */
321 public function register_mce_button( $buttons ) {
322 array_push( $buttons, 'visualizer_mce_button' );
323 return $buttons;
324 }
325
326 /**
327 * Whether to show the feedback review or not.
328 *
329 * @access public
330 */
331 public function feedbackReviewTrigger( $dumb ) {
332 $query = new WP_Query(
333 array(
334 'posts_per_page' => 50,
335 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
336 'fields' => 'ids',
337 'update_post_meta_cache' => false,
338 'update_post_term_cache' => false,
339 )
340 );
341
342 if ( $query->have_posts() && $query->post_count > 0 ) {
343 return true;
344 }
345 return false;
346 }
347
348 /**
349 * Enqueues media scripts and styles.
350 *
351 * @since 1.0.0
352 * @uses wp_enqueue_style To enqueue style file.
353 * @uses wp_enqueue_script To enqueue script file.
354 *
355 * @access public
356 */
357 public function enqueueMediaScripts() {
358 global $typenow;
359 global $current_screen;
360
361 if ( post_type_supports( $typenow, 'editor' ) || $current_screen->id === 'widgets' || $current_screen->id === 'customize' ) {
362 wp_enqueue_style( 'visualizer-media', VISUALIZER_ABSURL . 'css/media.css', array( 'media-views' ), Visualizer_Plugin::VERSION );
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 );
394 wp_enqueue_script( 'visualizer-media-collection', VISUALIZER_ABSURL . 'js/media/collection.js', array( 'visualizer-media-model' ), Visualizer_Plugin::VERSION, true );
395 wp_enqueue_script( 'visualizer-media-controller', VISUALIZER_ABSURL . 'js/media/controller.js', array( 'visualizer-media-collection' ), Visualizer_Plugin::VERSION, true );
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 );
406 wp_enqueue_script( 'visualizer-media-toolbar', VISUALIZER_ABSURL . 'js/media/toolbar.js', array( 'visualizer-media-view' ), Visualizer_Plugin::VERSION, true );
407 wp_enqueue_script( 'visualizer-media', VISUALIZER_ABSURL . 'js/media.js', array( 'visualizer-media-toolbar' ), Visualizer_Plugin::VERSION, true );
408 }
409 }
410
411 /**
412 * Extends media view strings with visualizer strings.
413 *
414 * @since 1.0.0
415 *
416 * @access public
417 *
418 * @param array $strings The array of media view strings.
419 *
420 * @return array The extended array of media view strings.
421 */
422 public function setupMediaViewStrings( $strings ) {
423 $chart_types = self::_getChartTypesLocalized( true, true, true );
424 $strings['visualizer'] = array(
425 'actions' => array(
426 'get_charts' => Visualizer_Plugin::ACTION_GET_CHARTS,
427 'delete_chart' => Visualizer_Plugin::ACTION_DELETE_CHART,
428 ),
429 'controller' => array(
430 'title' => esc_html__( 'Visualizations', 'visualizer' ),
431 ),
432 'routers' => array(
433 'library' => esc_html__( 'Insert from Library', 'visualizer' ),
434 'create' => esc_html__( 'Create New', 'visualizer' ),
435 ),
436 'library' => array(
437 'filters' => $chart_types,
438 'types' => array_keys( $chart_types ),
439 ),
440 'nonce' => wp_create_nonce(),
441 'buildurl' => esc_url( add_query_arg( 'action', Visualizer_Plugin::ACTION_CREATE_CHART, admin_url( 'admin-ajax.php' ) ) ),
442 );
443
444 return $strings;
445 }
446
447 /**
448 * Returns associated array of chart types and localized names.
449 *
450 * @since 1.0.0
451 *
452 * @static
453 * @access private
454 * @return array The associated array of chart types with localized names.
455 */
456 public static function _getChartTypesLocalized( $enabledOnly = false, $get2Darray = false, $add_select = false, $where = null ) {
457 $additional = array();
458 if ( $add_select ) {
459 $additional['select'] = array(
460 'name' => esc_html__( 'All', 'visualizer' ),
461 'enabled' => true,
462 );
463 }
464
465 $enabled = self::proFeaturesEnabled();
466
467 $types = array_merge(
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 ),
475 'pie' => array(
476 'name' => esc_html__( 'Pie/Donut', 'visualizer' ),
477 'enabled' => true,
478 'supports' => array( 'Google Charts', 'ChartJS' ),
479 ),
480 'line' => array(
481 'name' => esc_html__( 'Line', 'visualizer' ),
482 'enabled' => true,
483 'supports' => array( 'Google Charts', 'ChartJS' ),
484 ),
485 'bar' => array(
486 'name' => esc_html__( 'Bar', 'visualizer' ),
487 'enabled' => true,
488 'supports' => $enabled ? array( 'Google Charts', 'ChartJS' ) : array( 'Google Charts' ),
489 ),
490 'area' => array(
491 'name' => esc_html__( 'Area', 'visualizer' ),
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/',
496 ),
497 'geo' => array(
498 'name' => esc_html__( 'Geo', 'visualizer' ),
499 'enabled' => $enabled,
500 'supports' => array( 'Google Charts' ),
501 'demo_url' => 'https://demo.themeisle.com/visualizer/geo-chart/',
502 ),
503 'column' => array(
504 'name' => esc_html__( 'Column', 'visualizer' ),
505 'enabled' => $enabled,
506 'supports' => array( 'Google Charts', 'ChartJS' ),
507 'demo_url' => 'https://demo.themeisle.com/visualizer/column-chart/',
508 ),
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/',
515 ),
516 'scatter' => array(
517 'name' => esc_html__( 'Scatter', 'visualizer' ),
518 'enabled' => $enabled,
519 'supports' => array( 'Google Charts' ),
520 'demo_url' => 'https://demo.themeisle.com/visualizer/scatter-chart/',
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 ),
528 'candlestick' => array(
529 'name' => esc_html__( 'Candlestick', 'visualizer' ),
530 'enabled' => $enabled,
531 'supports' => array( 'Google Charts' ),
532 'demo_url' => 'https://demo.themeisle.com/visualizer/candlestick-chart/',
533 ),
534 // pro types
535 'timeline' => array(
536 'name' => esc_html__( 'Timeline', 'visualizer' ),
537 'enabled' => false,
538 'supports' => array( 'Google Charts', 'ChartJS' ),
539 'demo_url' => 'https://demo.themeisle.com/visualizer/timeline-chart/',
540 ),
541 'combo' => array(
542 'name' => esc_html__( 'Combo', 'visualizer' ),
543 'enabled' => false,
544 'supports' => array( 'Google Charts' ),
545 'demo_url' => 'https://demo.themeisle.com/visualizer/combo-chart/',
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 ),
559 )
560 );
561 $types = apply_filters( 'visualizer_pro_chart_types', $types );
562 if ( $enabledOnly ) {
563 $filtered = array();
564 foreach ( $types as $type => $array ) {
565 if ( ! is_array( $array ) ) {
566 // support for old pro
567 $array = array( 'enabled' => true, 'name' => $array );
568 }
569 // backward compatibility for PRO before v1.9.0
570 if ( ! array_key_exists( 'supports', $array ) ) {
571 $array['supports'] = array( 'Google Charts' );
572 }
573 if ( ! $array['enabled'] ) {
574 continue;
575 }
576 $filtered[ $type ] = $array;
577 }
578 $types = $filtered;
579 }
580 if ( $get2Darray ) {
581 $doubleD = array();
582 foreach ( $types as $type => $array ) {
583 if ( ! is_array( $array ) ) {
584 // support for old pro
585 $array = array( 'enabled' => true, 'name' => $array );
586 }
587 // backward compatibility for PRO before v1.9.0
588 if ( ! array_key_exists( 'supports', $array ) ) {
589 $array['supports'] = array( 'Google Charts' );
590 }
591 $doubleD[ $type ] = $array['name'];
592 }
593 $types = $doubleD;
594 }
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
653 return $types;
654 }
655
656 /**
657 * Renders templates to use in media popup.
658 *
659 * @since 1.0.0
660 * @global string $pagenow The name of the current page.
661 *
662 * @access public
663 */
664 public function renderTemplates() {
665 global $pagenow;
666 if ( 'post.php' !== $pagenow && 'post-new.php' !== $pagenow ) {
667 return;
668 }
669 $render = new Visualizer_Render_Templates();
670 $render->render();
671 }
672
673 /**
674 * Enqueues library scripts and styles.
675 *
676 * @since 1.0.0
677 * @uses wp_enqueue_style() To enqueue library stylesheet.
678 * @uses wp_enqueue_script() To enqueue javascript file.
679 * @uses wp_enqueue_media() To enqueue media stuff.
680 *
681 * @access public
682 *
683 * @param string $suffix The current page suffix.
684 */
685 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 }
715 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 ),
719 Visualizer_Plugin::VERSION,
720 true
721 );
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 }
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 }
820
821 do_action( 'themeisle_internal_page', VISUALIZER_DIRNAME, 'library' );
822 }
823
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 /**
837 * Adds visualizer tab for media upload tabs array.
838 *
839 * @since 1.0.0
840 *
841 * @access public
842 *
843 * @param array $tabs The array of media upload tabs.
844 *
845 * @return array Extended array of media upload tabs.
846 */
847 public function setupVisualizerTab( $tabs ) {
848 $tabs['visualizer'] = 'Visualizer';
849
850 return $tabs;
851 }
852
853 /**
854 * Registers admin menu for visualizer library.
855 *
856 * @since 1.0.0
857 *
858 * @access public
859 */
860 public function registerAdminMenu() {
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' ) );
902 }
903
904 /**
905 * Handle get pro plugin submenu.
906 */
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
1048 // get current page
1049 $page = filter_input(
1050 INPUT_GET,
1051 'vpage',
1052 FILTER_VALIDATE_INT,
1053 array(
1054 'options' => array(
1055 'min_range' => 1,
1056 'default' => 1,
1057 ),
1058 )
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
1075 // the initial query arguments to fetch charts
1076 $query_args = array(
1077 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
1078 'posts_per_page' => $per_page,
1079 'paged' => $page,
1080 );
1081
1082 $this->getDisplayFilters( $query_args );
1083
1084 // Added by Ash/Upwork
1085 $filterByMeta = ! empty( $_GET['s'] ) ? sanitize_text_field( wp_unslash( $_GET['s'] ) ) : null; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
1086 if ( $filterByMeta ) {
1087 $query = array(
1088 'key' => Visualizer_Plugin::CF_SETTINGS,
1089 'value' => $filterByMeta,
1090 'compare' => 'LIKE',
1091 );
1092 $meta = isset( $query_args['meta_query'] ) ? $query_args['meta_query'] : array();
1093 $meta[] = $query;
1094 $query_args['meta_query'] = $meta;
1095 }
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() {
1203 $charts = array();
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 = '';
1225 while ( $query->have_posts() ) {
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
1235 // fetch and update settings
1236 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, 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
1247 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $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
1278 // add chart to the array
1279 $charts[ $id ] = array(
1280 'id' => $chart->ID,
1281 'title' => $chart->post_title,
1282 'type' => $type,
1283 'series' => $series,
1284 'settings' => $settings,
1285 'data' => $data,
1286 'library' => $library,
1287 );
1288 if ( 'd3' === $library ) {
1289 $charts[ $id ]['code'] = get_post_meta( $chart->ID, Visualizer_Module_AIBuilder::CF_D3_CODE, true );
1290 }
1291 }
1292 // enqueue charts array
1293 $ajaxurl = admin_url( 'admin-ajax.php' );
1294
1295 wp_localize_script(
1296 'visualizer-library',
1297 'visualizer',
1298 array(
1299 'language' => $this->get_language(),
1300 'map_api_key' => get_option( 'visualizer-map-api-key' ),
1301 'charts' => $charts,
1302 'urls' => array(
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 )
1315 ),
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 )
1325 ),
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(),
1334 )
1335 );
1336 // render library page
1337 $render = new Visualizer_Render_Library();
1338 $render->charts = $charts;
1339 $render->type = $filter;
1340 $render->types = self::_getChartTypesLocalized( false, false, false, true );
1341 $render->custom_css = $css;
1342 $render->pagination = paginate_links(
1343 array(
1344 'base' => add_query_arg( array( 'vpage' => '%#%', 'vaction' => false ) ),
1345 'format' => '',
1346 'current' => $page,
1347 'total' => $query->max_num_pages,
1348 'type' => 'array',
1349 )
1350 );
1351
1352 $render->render();
1353 }
1354
1355 /**
1356 * Updates the plugin's action links, which will be rendered at the plugins table.
1357 *
1358 * @since 1.0.0
1359 *
1360 * @access public
1361 *
1362 * @param array $links The array of original action links.
1363 * @param string $file The plugin basename.
1364 *
1365 * @return array Updated array of action links.
1366 */
1367 public function getPluginActionLinks( $links, $file ) {
1368 if ( $file === plugin_basename( VISUALIZER_BASEFILE ) ) {
1369 array_unshift(
1370 $links,
1371 sprintf(
1372 '<a href="%s">%s</a>',
1373 admin_url( 'admin.php?page=' . Visualizer_Plugin::NAME ),
1374 esc_html__( 'Library', 'visualizer' )
1375 )
1376 );
1377 }
1378
1379 return $links;
1380 }
1381
1382 /**
1383 * Updates the plugin's meta links, which will be rendered at the plugins table.
1384 *
1385 * @since 1.0.0
1386 *
1387 * @access public
1388 *
1389 * @param array $plugin_meta The array of a plugin meta links.
1390 * @param string $plugin_file The plugin's basename.
1391 *
1392 * @return array Updated array of plugin meta links.
1393 */
1394 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 if ( $plugin_file === plugin_basename( VISUALIZER_BASEFILE ) ) {
1410 $is_black_friday = apply_filters( 'themeisle_sdk_is_black_friday_sale', false );
1411 // knowledge base link
1412 $plugin_meta[] = sprintf(
1413 '<a href="' . VISUALIZER_MAIN_DOC . '" target="_blank">%s</a>',
1414 esc_html__( 'Docs', 'visualizer' )
1415 );
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 }
1423 }
1424
1425 return $plugin_meta;
1426 }
1427
1428 /**
1429 * Returns true when premium chart types should be enabled for the current user.
1430 *
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
1440 */
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 ) {
1446 return true;
1447 }
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;
1460 }
1461
1462 /**
1463 * Multilingual Support.
1464 *
1465 * @param int $chart_id Chart ID.
1466 * @return bool Default false
1467 */
1468 public function addMultilingualSupport( $chart_id ) {
1469 global $sitepress;
1470 if ( Visualizer_Module::is_pro() ) {
1471 return;
1472 }
1473 if ( function_exists( 'icl_get_languages' ) && $sitepress instanceof \SitePress ) {
1474 $language = icl_get_languages();
1475 $current_lang = icl_get_current_language();
1476 $default_lang = icl_get_default_language();
1477 $post_info = wpml_get_language_information( null, $chart_id );
1478 $translations = array();
1479 if ( ! empty( $post_info ) && ( $default_lang === $post_info['language_code'] ) ) {
1480 $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
1481 $translations = $sitepress->get_element_translations( $trid );
1482 }
1483 if ( empty( $translations ) ) {
1484 return;
1485 }
1486 ?>
1487 <hr><div class="visualizer-languages-list only-pro">
1488 <?php
1489 foreach ( $language as $lang ) {
1490 $lang_code = $lang['code'];
1491 if ( $current_lang !== $lang_code ) {
1492 ?>
1493 <a href="javascript:;">
1494 <img src="<?php echo esc_url( $lang['country_flag_url'] ); ?>" alt="<?php echo esc_attr( $lang['translated_name'] ); ?>">
1495 </a>
1496 <?php
1497 }
1498 }
1499 ?>
1500 <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 </div>
1502 <?php
1503 }
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 }
1658 }
1659