PluginProbe
Visualizer – Tables & Charts Manager with Built-in AI Generator / 3.9.0
Visualizer – Tables & Charts Manager with Built-in AI Generator v3.9.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/Chart.php +802 -115 3.1.33.9.0 View file →
@@ -60,12 +60,249 @@
60 60 $this->_addAjaxAction( Visualizer_Plugin::ACTION_EXPORT_DATA, 'exportData' );
61 61
62 62 $this->_addAjaxAction( Visualizer_Plugin::ACTION_FETCH_DB_DATA, 'getQueryData' );
63 63 $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_DB_QUERY, 'saveQuery' );
64 +
65 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_GET_ROOTS, 'getJsonRoots' );
66 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_GET_DATA, 'getJsonData' );
67 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_SET_DATA, 'setJsonData' );
68 + $this->_addAjaxAction( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE, 'setJsonSchedule' );
69 +
64 70 $this->_addAjaxAction( Visualizer_Plugin::ACTION_SAVE_FILTER_QUERY, 'saveFilter' );
71 +
72 + $this->_addFilter( 'visualizer_get_sidebar', 'getSidebar', 10, 2 );
73 +
65 74 }
66 75
67 76 /**
77 + * Generates the HTML of the sidebar for the chart.
78 + *
79 + * @since ?
80 + *
81 + * @access public
82 + */
83 + public function getSidebar( $sidebar, $chart_id ) {
84 + $chart = get_post( $chart_id );
85 + $data = $this->_getChartArray( $chart );
86 + $sidebar = '';
87 + $sidebar_class = $this->load_chart_class_name( $chart_id );
88 + if ( class_exists( $sidebar_class, true ) ) {
89 + $sidebar = new $sidebar_class( $data['settings'] );
90 + $sidebar->__series = $data['series'];
91 + $sidebar->__data = $data['data'];
92 + } else {
93 + $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
94 + if ( $sidebar !== '' ) {
95 + $sidebar->__series = $data['series'];
96 + $sidebar->__data = $data['data'];
97 + }
98 + }
99 + return str_replace( "'", '"', $sidebar->__toString() );
100 + }
101 +
102 + /**
103 + * Sets the schedule for how JSON-endpoint charts should be updated.
104 + *
105 + * @since ?
106 + *
107 + * @access public
108 + */
109 + public function setJsonSchedule() {
110 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION, 'security' );
111 +
112 + $chart_id = filter_input(
113 + INPUT_POST,
114 + 'chart',
115 + FILTER_VALIDATE_INT,
116 + array(
117 + 'options' => array(
118 + 'min_range' => 1,
119 + ),
120 + )
121 + );
122 +
123 + if ( ! $chart_id ) {
124 + wp_send_json_error();
125 + }
126 +
127 + $time = filter_input(
128 + INPUT_POST,
129 + 'time',
130 + FILTER_VALIDATE_INT,
131 + array(
132 + 'options' => array(
133 + 'min_range' => -1,
134 + ),
135 + )
136 + );
137 +
138 + if ( Visualizer_Module::is_pro() ) {
139 + $is_woocommerce_report = filter_input(
140 + INPUT_POST,
141 + 'is_woocommerce_report',
142 + FILTER_VALIDATE_BOOLEAN
143 + );
144 +
145 + if ( $is_woocommerce_report ) {
146 + update_post_meta( $chart_id, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE, true );
147 + } else {
148 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_IS_WOOCOMMERCE_SOURCE );
149 + }
150 + }
151 +
152 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE );
153 +
154 + if ( -1 < $time ) {
155 + add_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, $time );
156 + // Update schedules.
157 + $schedules = get_option( Visualizer_Plugin::CF_JSON_SCHEDULE, array() );
158 + $schedules[ $chart_id ] = time() + $time * HOUR_IN_SECONDS;
159 + update_option( Visualizer_Plugin::CF_JSON_SCHEDULE, $schedules );
160 + }
161 + wp_send_json_success();
162 + }
163 +
164 + /**
165 + * Get the root elements for JSON-endpoint.
166 + *
167 + * @since ?
168 + *
169 + * @access public
170 + */
171 + public function getJsonRoots() {
172 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION, 'security' );
173 +
174 + $params = wp_parse_args( $_POST['params'] );
175 +
176 + $source = new Visualizer_Source_Json( $params );
177 +
178 + $roots = $source->fetchRoots();
179 + if ( empty( $roots ) ) {
180 + wp_send_json_error( array( 'msg' => $source->get_error() ) );
181 + }
182 +
183 + wp_send_json_success( array( 'url' => $params['url'], 'roots' => $roots ) );
184 + }
185 +
186 + /**
187 + * Get the data for the JSON-endpoint corresponding to the chosen root.
188 + *
189 + * @since ?
190 + *
191 + * @access public
192 + */
193 + public function getJsonData() {
194 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION, 'security' );
195 +
196 + $params = wp_parse_args( $_POST['params'] );
197 +
198 + $chart_id = $params['chart'];
199 +
200 + if ( empty( $chart_id ) ) {
201 + wp_die();
202 + }
203 +
204 + $source = new Visualizer_Source_Json( $params );
205 + $source->fetch();
206 + $data = $source->getRawData();
207 +
208 + if ( empty( $data ) ) {
209 + wp_send_json_error( array( 'msg' => esc_html__( 'Unable to fetch data from the endpoint. Please try again.', 'visualizer' ) ) );
210 + }
211 +
212 + $data = Visualizer_Render_Layout::show( 'editor-table', $data, $chart_id, 'viz-json-table', false, false );
213 + wp_send_json_success( array( 'table' => $data, 'root' => $params['root'], 'url' => $params['url'], 'paging' => $source->getPaginationElements() ) );
214 + }
215 +
216 + /**
217 + * Updates the database with the correct post parameters for JSON-endpoint charts.
218 + *
219 + * @since ?
220 + *
221 + * @access public
222 + */
223 + public function setJsonData() {
224 + check_ajax_referer( Visualizer_Plugin::ACTION_JSON_SET_DATA . Visualizer_Plugin::VERSION, 'security' );
225 +
226 + $params = $_POST;
227 + $chart_id = $_GET['chart'];
228 +
229 + if ( empty( $chart_id ) ) {
230 + wp_die();
231 + }
232 +
233 + $chart = get_post( $chart_id );
234 +
235 + $source = new Visualizer_Source_Json( $params );
236 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true );
237 + $source->fetchFromEditableTable();
238 +
239 + $content = $source->getData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
240 + $chart->post_content = $content;
241 + wp_update_post( $chart->to_array() );
242 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
243 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
244 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
245 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_URL, $params['url'] );
246 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_ROOT, $params['root'] );
247 +
248 + delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_HEADERS );
249 + $headers = array( 'method' => $params['method'] );
250 + if ( ! empty( $params['auth'] ) ) {
251 + $headers['auth'] = $params['auth'];
252 + } elseif ( ! empty( $params['username'] ) && ! empty( $params['password'] ) ) {
253 + $headers['auth'] = array( 'username' => $params['username'], 'password' => $params['password'] );
254 + }
255 +
256 + add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_HEADERS, $headers );
257 +
258 + delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING );
259 + if ( ! empty( $params['paging'] ) ) {
260 + add_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_PAGING, $params['paging'] );
261 + }
262 +
263 + if ( Visualizer_Module::is_pro() ) {
264 + if ( ! empty( $params['vz_woo_source'] ) ) {
265 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE, $params['vz_woo_source'] );
266 + } else {
267 + delete_post_meta( $chart->ID, Visualizer_Plugin::CF_JSON_WOOCOMMERCE_SOURCE );
268 + }
269 + }
270 +
271 + $time = filter_input(
272 + INPUT_POST,
273 + 'time',
274 + FILTER_VALIDATE_INT,
275 + array(
276 + 'options' => array(
277 + 'min_range' => -1,
278 + ),
279 + )
280 + );
281 +
282 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE );
283 +
284 + if ( -1 < $time ) {
285 + add_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_SCHEDULE, $time );
286 + }
287 +
288 + // delete other source specific parameters.
289 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
290 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
291 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_URL );
292 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_SCHEDULE );
293 +
294 + $render = new Visualizer_Render_Page_Update();
295 + $render->id = $chart->ID;
296 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
297 + $render->series = json_encode( $source->getSeries() );
298 + $render->render();
299 +
300 + defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
301 + }
302 +
303 +
304 + /**
68 305 * Fetches charts from database.
69 306 *
70 307 * This method is also called from the media pop-up (classic editor: create a post and add chart from insert content).
71 308 *
@@ -94,9 +331,9 @@
94 331 // 'filter' is from the modal from the add media button.
95 332 $filter = filter_input( INPUT_GET, 'filter', FILTER_SANITIZE_STRING );
96 333 }
97 334
98 - if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes() ) ) {
335 + if ( $filter && in_array( $filter, Visualizer_Plugin::getChartTypes(), true ) ) {
99 336 $query_args['meta_query'] = array(
100 337 array(
101 338 'key' => Visualizer_Plugin::CF_CHART_TYPE,
102 339 'value' => $filter,
@@ -147,9 +384,9 @@
147 384 $chart = $this->_chart;
148 385 }
149 386 $type = get_post_meta( $chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
150 387 $series = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SERIES, get_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, true ), $chart->ID, $type );
151 - $data = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_DATA, unserialize( $chart->post_content ), $chart->ID, $type );
388 + $data = self::get_chart_data( $chart, $type );
152 389 $library = $this->load_chart_type( $chart->ID );
153 390
154 391 $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
155 392 $settings = apply_filters( Visualizer_Plugin::FILTER_GET_CHART_SETTINGS, $settings, $chart->ID, $type );
@@ -201,9 +438,9 @@
201 438 *
202 439 * @access public
203 440 */
204 441 public function deleteChart() {
205 - $is_post = $_SERVER['REQUEST_METHOD'] == 'POST';
442 + $is_post = $_SERVER['REQUEST_METHOD'] === 'POST';
206 443 $input_method = $is_post ? INPUT_POST : INPUT_GET;
207 444 $chart_id = $success = false;
208 445 $nonce = wp_verify_nonce( filter_input( $input_method, 'nonce' ) );
209 446 $capable = current_user_can( 'delete_posts' );
@@ -219,13 +456,26 @@
219 456 )
220 457 );
221 458 if ( $chart_id ) {
222 459 $chart = get_post( $chart_id );
223 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
460 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
224 461 }
225 462 }
226 463 if ( $success ) {
227 - wp_delete_post( $chart_id, true );
464 + if ( Visualizer_Module::is_pro() && function_exists( 'icl_get_languages' ) ) {
465 + global $sitepress;
466 + $trid = $sitepress->get_element_trid( $chart_id, 'post_' . Visualizer_Plugin::CPT_VISUALIZER );
467 + $translations = $sitepress->get_element_translations( $trid );
468 + if ( ! empty( $translations ) ) {
469 + foreach ( $translations as $translated_post ) {
470 + wp_delete_post( $translated_post->element_id, true );
471 + }
472 + } else {
473 + wp_delete_post( $chart_id, true );
474 + }
475 + } else {
476 + wp_delete_post( $chart_id, true );
477 + }
228 478 }
229 479 if ( $is_post ) {
230 480 self::_sendResponse(
231 481 array(
@@ -232,9 +482,9 @@
232 482 'success' => $success,
233 483 )
234 484 );
235 485 }
236 - wp_redirect( wp_get_referer() );
486 + wp_redirect( remove_query_arg( 'vaction', wp_get_referer() ) );
237 487 exit;
238 488 }
239 489
240 490 /**
@@ -274,50 +524,112 @@
274 524 * @access public
275 525 */
276 526 public function renderChartPages() {
277 527 defined( 'IFRAME_REQUEST' ) || define( 'IFRAME_REQUEST', 1 );
528 + if ( ! defined( 'ET_BUILDER_PRODUCT_VERSION' ) && function_exists( 'et_get_theme_version' ) ) {
529 + define( 'ET_BUILDER_PRODUCT_VERSION', et_get_theme_version() );
530 + }
531 + // Set current screen for the render chart.
532 + set_current_screen( 'visualizer_render_chart' );
278 533 // check chart, if chart not exists, will create new one and redirects to the same page with proper chart id
279 534 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
280 - if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
281 - $this->deleteOldCharts();
282 - $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
283 - $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
284 - $source->fetch();
285 - $chart_id = wp_insert_post(
286 - array(
287 - 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
288 - 'post_title' => 'Visualization',
289 - 'post_author' => get_current_user_id(),
290 - 'post_status' => 'auto-draft',
291 - 'post_content' => $source->getData(),
292 - )
293 - );
294 - if ( $chart_id && ! is_wp_error( $chart_id ) ) {
295 - add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
296 - add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
297 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
298 - add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
299 - add_post_meta(
300 - $chart_id,
301 - Visualizer_Plugin::CF_SETTINGS,
535 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
536 + if ( empty( $_GET['lang'] ) || empty( $_GET['parent_chart_id'] ) ) {
537 + $this->deleteOldCharts();
538 + $default_type = isset( $_GET['type'] ) && ! empty( $_GET['type'] ) ? $_GET['type'] : 'line';
539 + $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $default_type . '.csv' );
540 + $source->fetch();
541 + $chart_id = wp_insert_post(
302 542 array(
303 - 'focusTarget' => 'datum',
543 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
544 + 'post_title' => 'Visualization',
545 + 'post_author' => get_current_user_id(),
546 + 'post_status' => 'auto-draft',
547 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
304 548 )
305 549 );
550 + if ( $chart_id && ! is_wp_error( $chart_id ) ) {
551 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, $default_type );
552 + add_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 1 );
553 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
554 + add_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
555 + add_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_LIBRARY, '' );
556 + add_post_meta(
557 + $chart_id,
558 + Visualizer_Plugin::CF_SETTINGS,
559 + array(
560 + 'focusTarget' => 'datum',
561 + )
562 + );
563 +
564 + do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
565 + }
566 + } else {
567 + if ( current_user_can( 'edit_posts' ) ) {
568 + $parent_chart_id = isset( $_GET['parent_chart_id'] ) ? filter_var( $_GET['parent_chart_id'], FILTER_VALIDATE_INT ) : '';
569 + $success = false;
570 + if ( $parent_chart_id ) {
571 + $parent_chart = get_post( $parent_chart_id );
572 + $success = $parent_chart && $parent_chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
573 + }
574 + if ( $success ) {
575 + $new_chart_id = wp_insert_post(
576 + array(
577 + 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
578 + 'post_title' => 'Visualization',
579 + 'post_author' => get_current_user_id(),
580 + 'post_status' => $parent_chart->post_status,
581 + 'post_content' => $parent_chart->post_content,
582 + )
583 + );
584 +
585 + if ( is_wp_error( $new_chart_id ) ) {
586 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Error while cloning chart %d = %s', $parent_chart_id, print_r( $new_chart_id, true ) ), 'error', __FILE__, __LINE__ );
587 + } else {
588 + $post_meta = get_post_meta( $parent_chart_id );
589 + $chart_id = $new_chart_id;
590 + foreach ( $post_meta as $key => $value ) {
591 + if ( strpos( $key, 'visualizer-' ) !== false ) {
592 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
593 + }
594 + }
595 + }
596 + }
597 + }
306 598 do_action( 'visualizer_pro_new_chart_defaults', $chart_id );
307 599 }
308 - wp_redirect( add_query_arg( 'chart', (int) $chart_id ) );
309 - defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
600 + wp_redirect( esc_url_raw( add_query_arg( 'chart', (int) $chart_id ) ) );
601 +
602 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
603 + wp_die();
604 + }
605 + exit();
310 606 }
311 607
312 - $this->load_chart_type( $chart_id );
608 + $_POST['save_chart_image'] = isset( $_POST['save_chart_image'] ) && 'yes' === $_POST['save_chart_image'] ? true : false;
609 + $_POST['lazy_load_chart'] = isset( $_POST['lazy_load_chart'] ) && 'yes' === $_POST['lazy_load_chart'] ? true : false;
313 610
611 + if ( isset( $_POST['chart-img'] ) && ! empty( $_POST['chart-img'] ) ) {
612 + $attachment_id = $this->save_chart_image( $_POST['chart-img'], $chart_id, $_POST['save_chart_image'] );
613 + if ( $attachment_id ) {
614 + update_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, $attachment_id );
615 + }
616 + }
617 + $lib = $this->load_chart_type( $chart_id );
618 +
619 + // the alpha color picker (RGBA) is not supported by google.
620 + $color_picker_dep = 'wp-color-picker';
621 + if ( in_array( $lib, array( 'chartjs', 'datatables' ), true ) && ! wp_script_is( 'wp-color-picker-alpha', 'registered' ) ) {
622 + wp_register_script( 'wp-color-picker-alpha', VISUALIZER_ABSURL . 'js/lib/wp-color-picker-alpha.min.js', array( 'wp-color-picker' ), Visualizer_Plugin::VERSION );
623 + $color_picker_dep = 'wp-color-picker-alpha';
624 + }
625 +
314 626 // enqueue and register scripts and styles
315 627 wp_register_script( 'visualizer-chosen', VISUALIZER_ABSURL . 'js/lib/chosen.jquery.min.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
316 628 wp_register_style( 'visualizer-chosen', VISUALIZER_ABSURL . 'css/lib/chosen.min.css', array(), Visualizer_Plugin::VERSION );
317 629
318 630 wp_register_style( 'visualizer-frame', VISUALIZER_ABSURL . 'css/frame.css', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION );
319 - wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen' ), Visualizer_Plugin::VERSION, true );
631 + wp_register_script( 'visualizer-frame', VISUALIZER_ABSURL . 'js/frame.js', array( 'visualizer-chosen', 'jquery-ui-accordion', 'jquery-ui-tabs' ), Visualizer_Plugin::VERSION, true );
320 632 wp_register_script( 'visualizer-customization', $this->get_user_customization_js(), array(), null, true );
321 633 wp_register_script(
322 634 'visualizer-render',
323 635 VISUALIZER_ABSURL . 'js/render-facade.js',
@@ -328,19 +640,23 @@
328 640 wp_register_script(
329 641 'visualizer-preview',
330 642 VISUALIZER_ABSURL . 'js/preview.js',
331 643 array(
332 - 'wp-color-picker',
644 + $color_picker_dep,
333 645 'visualizer-render',
334 646 ),
335 647 Visualizer_Plugin::VERSION,
336 648 true
337 649 );
338 - // added by Ash/Upwork
339 - if ( VISUALIZER_PRO ) {
650 + wp_register_script( 'visualizer-editor-simple', VISUALIZER_ABSURL . 'js/simple-editor.js', array( 'jquery' ), Visualizer_Plugin::VERSION, true );
651 +
652 + do_action( 'visualizer_add_scripts' );
653 +
654 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
340 655 global $Visualizer_Pro;
341 656 $Visualizer_Pro->_addScriptsAndStyles();
342 657 }
658 +
343 659 // dispatch pages
344 660 $this->_chart = get_post( $chart_id );
345 661 $tab = isset( $_GET['tab'] ) && ! empty( $_GET['tab'] ) ? $_GET['tab'] : 'visualizer';
346 662
@@ -352,8 +668,9 @@
352 668 if ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) {
353 669 // if the cancel button is clicked.
354 670 $this->undoRevisions( $chart_id, true );
355 671 } elseif ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
672 + $this->handlePermissions();
356 673 // if the save button is clicked.
357 674 $this->undoRevisions( $chart_id, false );
358 675 } else {
359 676 // if the edit button is clicked.
@@ -359,8 +676,16 @@
359 676 // if the edit button is clicked.
360 677 $this->_chart = $this->handleExistingRevisions( $chart_id, $this->_chart );
361 678 }
362 679
680 + // Clear existing chart cache.
681 + if ( isset( $_POST['save'] ) && 1 === intval( $_POST['save'] ) ) {
682 + $cache_key = Visualizer_Plugin::CF_CHART_CACHE . '_' . $chart_id;
683 + if ( get_transient( $cache_key ) ) {
684 + delete_transient( $cache_key );
685 + }
686 + }
687 +
363 688 switch ( $tab ) {
364 689 case 'settings':
365 690 $this->_handleDataAndSettingsPage();
366 691 break;
@@ -368,9 +693,9 @@
368 693 case 'visualizer': // fall through.
369 694 $this->_handleTypesPage();
370 695 break;
371 696 default:
372 - do_action( 'visualizer_pro_handle_tab', $tab, $this->_chart );
697 + // this should never happen.
373 698 break;
374 699 }
375 700 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
376 701 }
@@ -377,25 +702,26 @@
377 702
378 703 /**
379 704 * Load code editor assets.
380 705 */
381 - private function loadCodeEditorAssets() {
706 + private function loadCodeEditorAssets( $chart_id ) {
382 707 global $wp_version;
383 708
384 - if ( ! VISUALIZER_PRO ) {
709 + $wp_scripts = wp_scripts();
710 +
711 + // data tables assets.
712 + wp_register_script( 'visualizer-datatables', VISUALIZER_ABSURL . 'js/lib/datatables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
713 + wp_register_style( 'visualizer-datatables', VISUALIZER_ABSURL . 'css/lib/datatables.min.css', array(), Visualizer_Plugin::VERSION );
714 + wp_register_style( 'visualizer-jquery-ui', sprintf( '//code.jquery.com/ui/%s/themes/smoothness/jquery-ui.css', $wp_scripts->registered['jquery-ui-core']->ver ), array( 'visualizer-datatables' ), Visualizer_Plugin::VERSION );
715 + wp_enqueue_script( 'visualizer-datatables' );
716 + wp_enqueue_style( 'visualizer-jquery-ui' );
717 +
718 + if ( ! Visualizer_Module::is_pro() ) {
385 719 return;
386 720 }
387 721
388 - $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping();
722 + $table_col_mapping = Visualizer_Source_Query_Params::get_all_db_tables_column_mapping( $chart_id );
389 723
390 - // data tables assets.
391 - wp_register_script( 'visualizer-datatables', '//cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js', array( 'jquery-ui-core' ), Visualizer_Plugin::VERSION );
392 - wp_register_style( 'visualizer-datatables', '//cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css', array(), Visualizer_Plugin::VERSION );
393 - wp_register_style( 'visualizer-datatables-ui', '//code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css', array( 'visualizer-datatables' ), Visualizer_Plugin::VERSION );
394 -
395 - wp_enqueue_script( 'visualizer-datatables' );
396 - wp_enqueue_style( 'visualizer-datatables-ui' );
397 -
398 724 if ( version_compare( $wp_version, '4.9.0', '<' ) ) {
399 725 // code mirror assets.
400 726 wp_register_script( 'visualizer-codemirror-core', '//codemirror.net/lib/codemirror.js', array( 'jquery' ), Visualizer_Plugin::VERSION );
401 727 wp_register_script( 'visualizer-codemirror-placeholder', '//codemirror.net/addon/display/placeholder.js', array( 'visualizer-codemirror-core' ), Visualizer_Plugin::VERSION );
@@ -429,8 +755,25 @@
429 755 return $table_col_mapping;
430 756 }
431 757
432 758 /**
759 + * Handle permissions from the new conslidated settings sidebar.
760 + */
761 + private function handlePermissions() {
762 + if ( ! Visualizer_Module::is_pro() ) {
763 + return;
764 + }
765 +
766 + // we will not support old free and new pro.
767 + // handling new free and old pro.
768 + if ( has_action( 'visualizer_pro_handle_tab' ) ) {
769 + do_action( 'visualizer_pro_handle_tab', 'permissions', $this->_chart );
770 + } else {
771 + do_action( 'visualizer_handle_permissions', $this->_chart );
772 + }
773 + }
774 +
775 + /**
433 776 * Handle data and settings page
434 777 */
435 778 private function _handleDataAndSettingsPage() {
436 779 if ( isset( $_POST['map_api_key'] ) ) {
@@ -435,30 +778,53 @@
435 778 private function _handleDataAndSettingsPage() {
436 779 if ( isset( $_POST['map_api_key'] ) ) {
437 780 update_option( 'visualizer-map-api-key', $_POST['map_api_key'] );
438 781 }
439 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
440 - if ( $this->_chart->post_status == 'auto-draft' ) {
782 +
783 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'] ) ) {
784 + if ( $this->_chart->post_status === 'auto-draft' ) {
441 785 $this->_chart->post_status = 'publish';
442 786
443 787 // ensure that a revision is not created. If a revision is created it will have the proper data and the parent of the revision will have default data.
444 788 // we do not want any difference in data so disable revisions temporarily.
445 - add_filter( 'wp_revisions_to_keep', '__return_false' );
789 + $this->disableRevisionsTemporarily();
790 +
446 791 wp_update_post( $this->_chart->to_array() );
447 792 }
448 793 // save meta data only when it is NOT being canceled.
449 794 if ( ! ( isset( $_POST['cancel'] ) && 1 === intval( $_POST['cancel'] ) ) ) {
450 795 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $_POST );
796 +
797 + // we will keep a parameter called 'internal_title' that will be set to the given title or, if empty, the chart ID
798 + // this will help in searching with the chart id.
799 + $settings = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
800 + $title = null;
801 + if ( isset( $settings['title'] ) && ! empty( $settings['title'] ) ) {
802 + $title = $settings['title'];
803 + if ( is_array( $title ) ) {
804 + $title = $settings['title']['text'];
805 + }
806 + }
807 + if ( empty( $title ) ) {
808 + $title = $this->_chart->ID;
809 + }
810 + $settings['internal_title'] = $title;
811 + $settings_label = isset( $settings['pieResidueSliceLabel'] ) ? $settings['pieResidueSliceLabel'] : '';
812 + if ( empty( $settings_label ) ) {
813 + $settings['pieResidueSliceLabel'] = esc_html__( 'Other', 'visualizer' );
814 + } else {
815 + $settings['pieResidueSliceLabel'] = $settings_label;
816 + }
817 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
451 818 }
452 819 $render = new Visualizer_Render_Page_Send();
453 820 $render->text = sprintf( '[visualizer id="%d"]', $this->_chart->ID );
454 821 wp_iframe( array( $render, 'render' ) );
455 -
456 822 return;
457 823 }
458 824 $data = $this->_getChartArray();
459 825 $sidebar = '';
460 - $sidebar_class = 'Visualizer_Render_Sidebar_Type_' . ucfirst( $data['type'] );
826 + $sidebar_class = $this->load_chart_class_name( $this->_chart->ID );
461 827 if ( class_exists( $sidebar_class, true ) ) {
462 828 $sidebar = new $sidebar_class( $data['settings'] );
463 829 $sidebar->__series = $data['series'];
464 830 $sidebar->__data = $data['data'];
@@ -463,14 +829,14 @@
463 829 $sidebar->__series = $data['series'];
464 830 $sidebar->__data = $data['data'];
465 831 } else {
466 832 $sidebar = apply_filters( 'visualizer_pro_chart_type_sidebar', '', $data );
467 - if ( $sidebar != '' ) {
833 + if ( $sidebar !== '' ) {
468 834 $sidebar->__series = $data['series'];
469 835 $sidebar->__data = $data['data'];
470 836 }
471 837 }
472 - unset( $data['settings']['width'], $data['settings']['height'] );
838 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
473 839 wp_enqueue_style( 'wp-color-picker' );
474 840 wp_enqueue_style( 'visualizer-frame' );
475 841 wp_enqueue_script( 'visualizer-preview' );
476 842 wp_enqueue_script( 'visualizer-chosen' );
@@ -475,17 +841,37 @@
475 841 wp_enqueue_script( 'visualizer-preview' );
476 842 wp_enqueue_script( 'visualizer-chosen' );
477 843 wp_enqueue_script( 'visualizer-render' );
478 844
479 - $table_col_mapping = $this->loadCodeEditorAssets();
845 + if ( Visualizer_Module::can_show_feature( 'simple-editor' ) ) {
846 + wp_enqueue_script( 'visualizer-editor-simple' );
847 + wp_localize_script(
848 + 'visualizer-editor-simple',
849 + 'visualizer1',
850 + array(
851 + 'ajax' => array(
852 + 'url' => admin_url( 'admin-ajax.php' ),
853 + 'nonces' => array(
854 + ),
855 + 'actions' => array(
856 + ),
857 + ),
858 + )
859 + );
860 + }
480 861
862 + $table_col_mapping = $this->loadCodeEditorAssets( $this->_chart->ID );
863 +
481 864 wp_localize_script(
482 865 'visualizer-render',
483 866 'visualizer',
484 867 array(
485 868 'l10n' => array(
486 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
869 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
487 870 'loading' => esc_html__( 'Loading...', 'visualizer' ),
871 + 'json_error' => esc_html__( 'An error occured in fetching data.', 'visualizer' ),
872 + 'select_columns' => esc_html__( 'Please select a few columns to include in the chart.', 'visualizer' ),
873 + 'save_settings' => __( 'You have modified the chart\'s settings. To modify the source/data again, you must save this chart and reopen it for editing. If you continue without saving the chart, you may lose your changes.', 'visualizer' ),
488 874 ),
489 875 'charts' => array(
490 876 'canvas' => $data,
491 877 'id' => $this->_chart->ID,
@@ -496,19 +882,29 @@
496 882 'url' => admin_url( 'admin-ajax.php' ),
497 883 'nonces' => array(
498 884 'permissions' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA ),
499 885 'db_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION ),
886 + 'json_get_roots' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_ROOTS . Visualizer_Plugin::VERSION ),
887 + 'json_get_data' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_GET_DATA . Visualizer_Plugin::VERSION ),
888 + 'json_set_schedule' => wp_create_nonce( Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE . Visualizer_Plugin::VERSION ),
500 889 ),
501 890 'actions' => array(
502 891 'permissions' => Visualizer_Plugin::ACTION_FETCH_PERMISSIONS_DATA,
503 892 'db_get_data' => Visualizer_Plugin::ACTION_FETCH_DB_DATA,
893 + 'json_get_roots' => Visualizer_Plugin::ACTION_JSON_GET_ROOTS,
894 + 'json_get_data' => Visualizer_Plugin::ACTION_JSON_GET_DATA,
895 + 'json_set_schedule' => Visualizer_Plugin::ACTION_JSON_SET_SCHEDULE,
504 896 ),
505 897 ),
506 898 'db_query' => array(
507 899 'tables' => $table_col_mapping,
508 900 ),
509 - 'is_pro' => VISUALIZER_PRO,
901 + 'is_pro' => Visualizer_Module::is_pro(),
510 902 'page_type' => 'chart',
903 + 'json_tag_separator' => Visualizer_Source_Json::TAG_SEPARATOR,
904 + 'json_tag_separator_view' => Visualizer_Source_Json::TAG_SEPARATOR_VIEW,
905 + 'is_front' => false,
906 + 'rest_base' => get_rest_url( null, 'wc/v3/reports/' ),
511 907 )
512 908 );
513 909
514 910 $render = new Visualizer_Render_Page_Data();
@@ -516,21 +912,25 @@
516 912 $render->type = $data['type'];
517 913 $render->custom_css = $data['css'];
518 914 $render->sidebar = $sidebar;
519 915 if ( filter_input( INPUT_GET, 'library', FILTER_VALIDATE_BOOLEAN ) ) {
520 - $render->button = filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART
916 + $render->button = filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART
521 917 ? esc_html__( 'Save Chart', 'visualizer' )
522 918 : esc_html__( 'Create Chart', 'visualizer' );
523 - if ( filter_input( INPUT_GET, 'action' ) == Visualizer_Plugin::ACTION_EDIT_CHART ) {
919 + if ( filter_input( INPUT_GET, 'action' ) === Visualizer_Plugin::ACTION_EDIT_CHART ) {
524 920 $render->cancel_button = esc_html__( 'Cancel', 'visualizer' );
525 921 }
526 922 } else {
527 923 $render->button = esc_attr__( 'Insert Chart', 'visualizer' );
528 924 }
529 - if ( VISUALIZER_PRO ) {
925 +
926 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
927 +
928 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
530 929 global $Visualizer_Pro;
531 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
930 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
532 931 }
932 +
533 933 $this->_addAction( 'admin_head', 'renderFlattrScript' );
534 934 wp_iframe( array( $render, 'render' ) );
535 935 }
536 936
@@ -542,24 +942,35 @@
542 942 * @access private
543 943 */
544 944 private function _handleTypesPage() {
545 945 // process post request
546 - if ( $_SERVER['REQUEST_METHOD'] == 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
946 + if ( $_SERVER['REQUEST_METHOD'] === 'POST' && wp_verify_nonce( filter_input( INPUT_POST, 'nonce' ) ) ) {
547 947 $type = filter_input( INPUT_POST, 'type' );
548 - if ( in_array( $type, Visualizer_Plugin::getChartTypes() ) ) {
948 + $library = filter_input( INPUT_POST, 'chart-library' );
949 + if ( in_array( $type, Visualizer_Plugin::getChartTypes(), true ) ) {
950 + if ( empty( $library ) ) {
951 + // library cannot be empty.
952 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, 'Chart library empty while creating the chart! Aborting...', 'error', __FILE__, __LINE__ );
953 + return;
954 + }
955 +
549 956 // save new chart type
550 957 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, $type );
958 + update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_LIBRARY, $library );
551 959 // if the chart has default data, update it with appropriate default data for new type
552 960 if ( filter_var( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, true ), FILTER_VALIDATE_BOOLEAN ) ) {
553 961 $source = new Visualizer_Source_Csv( VISUALIZER_ABSPATH . DIRECTORY_SEPARATOR . 'samples' . DIRECTORY_SEPARATOR . $type . '.csv' );
554 962 $source->fetch();
555 - $this->_chart->post_content = $source->getData();
963 + $this->_chart->post_content = $source->getData( get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
556 964 wp_update_post( $this->_chart->to_array() );
557 965 update_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
558 966 }
967 +
968 + Visualizer_Module_Utility::set_defaults( $this->_chart );
969 +
559 970 // redirect to next tab
560 971 // changed by Ash/Upwork
561 - wp_redirect( add_query_arg( 'tab', 'settings' ) );
972 + wp_redirect( esc_url_raw( add_query_arg( 'tab', 'settings' ) ) );
562 973
563 974 return;
564 975 }
565 976 }
@@ -564,9 +975,9 @@
564 975 }
565 976 }
566 977 $render = new Visualizer_Render_Page_Types();
567 978 $render->type = get_post_meta( $this->_chart->ID, Visualizer_Plugin::CF_CHART_TYPE, true );
568 - $render->types = Visualizer_Module_Admin::_getChartTypesLocalized();
979 + $render->types = Visualizer_Module_Admin::_getChartTypesLocalized( false, false, false, 'types' );
569 980 $render->chart = $this->_chart;
570 981 wp_enqueue_style( 'visualizer-frame' );
571 982 wp_enqueue_script( 'visualizer-frame' );
572 983 wp_iframe( array( $render, 'render' ) );
@@ -582,10 +993,131 @@
582 993 */
583 994 public function renderFlattrScript() {
584 995 echo '';
585 996 }
586 - // changed by Ash/Upwork
997 +
587 998 /**
999 + * Processes the CSV that is sent in the request as a string.
1000 + *
1001 + * @since 3.2.0
1002 + */
1003 + private function handleCSVasString( $data, $editor_type ) {
1004 + $source = null;
1005 +
1006 + switch ( $editor_type ) {
1007 + case 'text':
1008 + // data coming in from the text editor.
1009 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1010 + $handle = fopen( $tmpfile, 'w' );
1011 + $values = preg_split( '/[\n\r]+/', stripslashes( trim( $data ) ) );
1012 + if ( $values ) {
1013 + foreach ( $values as $row ) {
1014 + if ( empty( $row ) ) {
1015 + continue;
1016 + }
1017 + $row = explode( ',', $row );
1018 + $row = array_map(
1019 + function( $r ) {
1020 + return '' === $r ? ' ' : $r;
1021 + },
1022 + $row
1023 + );
1024 + $row = implode( ',', $row );
1025 + // don't use fpucsv here because we need to just dump the data
1026 + // minus the empty rows
1027 + // because fputcsv needs to tokenize
1028 + // we can standardize the CSV enclosure here and replace all ' with "
1029 + // we can assume that if there are an even number of ' they should be changed to "
1030 + // but that will screw up words like fo'c'sle
1031 + // so here let's just assume ' will NOT be used for enclosures
1032 + fwrite( $handle, $row );
1033 + fwrite( $handle, PHP_EOL );
1034 + }
1035 + }
1036 + $source = new Visualizer_Source_Csv( $tmpfile );
1037 + fclose( $handle );
1038 + break;
1039 + case 'table':
1040 + // Import from Chart
1041 + // fall-through.
1042 + case 'excel':
1043 + // data coming in from the excel editor.
1044 + $source = apply_filters( 'visualizer_pro_handle_chart_data', $data, '' );
1045 + break;
1046 + }
1047 + return $source;
1048 + }
1049 +
1050 + /**
1051 + * Parses the data uploaded as an HTML table.
1052 + *
1053 + * @since 3.2.0
1054 + *
1055 + * @access private
1056 + */
1057 + private function handleTabularData() {
1058 + $csv = array();
1059 + // the datatable mentions the headers twice, so lets remove the duplicates.
1060 + $headers = array_unique( array_filter( $_POST['header'] ) );
1061 + $types = $_POST['type'];
1062 +
1063 + // capture all the indexes that correspond to excluded columns.
1064 + $exclude = array();
1065 + $index = 0;
1066 + foreach ( $types as $type ) {
1067 + if ( empty( $type ) ) {
1068 + $exclude[] = $index;
1069 + }
1070 + $index++;
1071 + }
1072 +
1073 + // when N headers are being renamed, the number of headers increases by N
1074 + // because of the way datatable duplicates header information
1075 + // so unset the headers that have been renamed.
1076 + if ( count( $headers ) !== count( $types ) ) {
1077 + $to = count( $headers );
1078 + for ( $i = count( $types ); $i < $to; $i++ ) {
1079 + unset( $headers[ $i + 1 ] );
1080 + }
1081 + }
1082 +
1083 + $columns = array();
1084 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1085 + if ( ! isset( $_POST[ 'data' . $i ] ) ) {
1086 + continue;
1087 + }
1088 + $columns[ $i ] = $_POST[ 'data' . $i ];
1089 + }
1090 +
1091 + $csv[] = $headers;
1092 + $csv[] = $types;
1093 + for ( $j = 0; $j < count( $columns[0] ); $j++ ) {
1094 + $row = array();
1095 + for ( $i = 0; $i < count( $headers ); $i++ ) {
1096 + $row[] = sanitize_text_field( $columns[ $i ][ $j ] );
1097 + }
1098 + $csv[] = $row;
1099 + }
1100 +
1101 + $tmpfile = tempnam( get_temp_dir(), Visualizer_Plugin::NAME );
1102 + $handle = fopen( $tmpfile, 'w' );
1103 +
1104 + if ( $csv ) {
1105 + $index = 0;
1106 + foreach ( $csv as $row ) {
1107 + // remove all the cells corresponding to the excluded headers.
1108 + foreach ( $exclude as $j ) {
1109 + unset( $row[ $j ] );
1110 + }
1111 + fputcsv( $handle, $row );
1112 + }
1113 + }
1114 + $source = new Visualizer_Source_Csv( $tmpfile );
1115 + fclose( $handle );
1116 + return $source;
1117 + }
1118 +
1119 + /**
588 1120 * Parses uploaded CSV file and saves new data for the chart.
589 1121 *
590 1122 * @since 1.0.0
591 1123 *
@@ -607,9 +1139,9 @@
607 1139
608 1140 // check chart, if chart exists
609 1141 // do not use filter_input as it does not work for phpunit test cases, use filter_var instead
610 1142 $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
611 - if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type != Visualizer_Plugin::CPT_VISUALIZER ) {
1143 + if ( ! $chart_id || ! ( $chart = get_post( $chart_id ) ) || $chart->post_type !== Visualizer_Plugin::CPT_VISUALIZER ) {
612 1144 if ( ! $can_die ) {
613 1145 return;
614 1146 }
615 1147 status_header( 400 );
@@ -615,8 +1147,10 @@
615 1147 status_header( 400 );
616 1148 exit;
617 1149 }
618 1150
1151 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Uploading data for chart %d with POST = %s and GET = %s', $chart_id, print_r( $_POST, true ), print_r( $_GET, true ) ), 'debug', __FILE__, __LINE__ );
1152 +
619 1153 if ( ! isset( $_POST['vz-import-time'] ) ) {
620 1154 apply_filters( 'visualizer_pro_remove_schedule', $chart_id );
621 1155 }
622 1156
@@ -628,27 +1162,58 @@
628 1162
629 1163 // delete "import from db" specific parameters.
630 1164 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY );
631 1165 delete_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE );
1166 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS );
632 1167 }
633 1168
1169 + // delete json related data.
1170 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_URL );
1171 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_ROOT );
1172 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_PAGING );
1173 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_JSON_HEADERS );
1174 +
1175 + // delete last error
1176 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR );
1177 +
1178 + // delete editor related data.
1179 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR );
1180 +
1181 + // delete this so that a JSON import can be later edited manually without a problem.
1182 + delete_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE );
1183 +
634 1184 $source = null;
635 1185 $render = new Visualizer_Render_Page_Update();
636 - if ( isset( $_POST['remote_data'] ) && filter_var( $_POST['remote_data'], FILTER_VALIDATE_URL ) ) {
637 - $source = new Visualizer_Source_Csv_Remote( $_POST['remote_data'] );
1186 +
1187 + $remote_data = false;
1188 + if ( isset( $_POST['remote_data'] ) && function_exists( 'wp_http_validate_url' ) ) {
1189 + $remote_data = wp_http_validate_url( $_POST['remote_data'] );
1190 + }
1191 + if ( false !== $remote_data ) {
1192 + $source = new Visualizer_Source_Csv_Remote( $remote_data );
638 1193 if ( isset( $_POST['vz-import-time'] ) ) {
639 - apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $_POST['remote_data'], $_POST['vz-import-time'] );
1194 + apply_filters( 'visualizer_pro_chart_schedule', $chart_id, $remote_data, $_POST['vz-import-time'] );
640 1195 }
1196 + // phpcs:ignore WordPress.PHP.StrictComparisons.LooseComparison
641 1197 } elseif ( isset( $_FILES['local_data'] ) && $_FILES['local_data']['error'] == 0 ) {
642 1198 $source = new Visualizer_Source_Csv( $_FILES['local_data']['tmp_name'] );
643 1199 } elseif ( isset( $_POST['chart_data'] ) && strlen( $_POST['chart_data'] ) > 0 ) {
644 - $source = apply_filters( 'visualizer_pro_handle_chart_data', $_POST['chart_data'], '', $chart_id, $_POST );
1200 + $source = $this->handleCSVasString( $_POST['chart_data'], $_POST['editor-type'] );
1201 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
1202 + } elseif ( isset( $_POST['table_data'] ) && 'yes' === $_POST['table_data'] ) {
1203 + $source = $this->handleTabularData();
1204 + update_post_meta( $chart_id, Visualizer_Plugin::CF_EDITOR, $_POST['editor-type'] );
645 1205 } else {
646 - $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please, try again.', 'visualizer' );
1206 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'CSV file with chart data was not uploaded for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
1207 + $render->message = esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' );
1208 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, esc_html__( 'CSV file with chart data was not uploaded. Please try again.', 'visualizer' ) );
647 1209 }
1210 +
1211 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Uploaded data for chart %d with source %s', $chart_id, print_r( $source, true ) ), 'debug', __FILE__, __LINE__ );
1212 +
648 1213 if ( $source ) {
649 1214 if ( $source->fetch() ) {
650 - $content = $source->getData();
1215 + $content = $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) );
651 1216 $populate = true;
652 1217 if ( is_string( $content ) && is_array( unserialize( $content ) ) ) {
653 1218 $json = unserialize( $content );
654 1219 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
@@ -654,8 +1219,9 @@
654 1219 // if source exists, so should data. if source exists but data is blank, do not populate the chart.
655 1220 // if we populate the data even if it is empty, the chart will show "Table has no columns".
656 1221 if ( array_key_exists( 'source', $json ) && ! empty( $json['source'] ) && ( ! array_key_exists( 'data', $json ) || empty( $json['data'] ) ) ) {
657 1222 do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ), 'warn', __FILE__, __LINE__ );
1223 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, sprintf( 'Not populating chart data as source exists (%s) but data is empty!', $json['source'] ) );
658 1224 $populate = false;
659 1225 }
660 1226 }
661 1227 if ( $populate ) {
@@ -661,17 +1227,43 @@
661 1227 if ( $populate ) {
662 1228 $chart->post_content = $content;
663 1229 }
664 1230 wp_update_post( $chart->to_array() );
1231 +
665 1232 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
666 1233 update_post_meta( $chart->ID, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
667 1234 update_post_meta( $chart->ID, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1235 +
1236 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Updated post for chart %d', $chart_id ), 'debug', __FILE__, __LINE__ );
1237 +
1238 + Visualizer_Module_Utility::set_defaults( $chart, null );
1239 +
1240 + $settings = get_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, true );
1241 + if ( isset( $settings['series'] ) && ! ( count( $settings['series'] ) - count( $source->getSeries() ) > 1 ) ) {
1242 + $diff_total_series = abs( count( $settings['series'] ) - count( $source->getSeries() ) );
1243 + if ( $diff_total_series ) {
1244 + foreach ( range( 1, $diff_total_series ) as $k => $diff_series ) {
1245 + $settings['series'][] = end( $settings['series'] );
1246 + }
1247 + update_post_meta( $chart->ID, Visualizer_Plugin::CF_SETTINGS, $settings );
1248 + }
1249 + }
1250 +
668 1251 $render->id = $chart->ID;
669 - $render->data = json_encode( $source->getRawData() );
1252 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart->ID, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
670 1253 $render->series = json_encode( $source->getSeries() );
1254 + $render->settings = json_encode( $settings );
671 1255 } else {
672 - $render->message = esc_html__( 'CSV file is broken or invalid. Please, try again.', 'visualizer' );
1256 + $error = $source->get_error();
1257 + if ( empty( $error ) ) {
1258 + $error = esc_html__( 'CSV file is broken or invalid. Please try again.', 'visualizer' );
1259 + }
1260 + $render->message = $error;
1261 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( '%s for chart %d.', $error, $chart_id ), 'error', __FILE__, __LINE__ );
1262 + update_post_meta( $chart_id, Visualizer_Plugin::CF_ERROR, $error );
673 1263 }
1264 + } else {
1265 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Unknown internal error for chart %d.', $chart_id ), 'error', __FILE__, __LINE__ );
674 1266 }
675 1267 $render->render();
676 1268 if ( ! $can_die ) {
677 1269 return;
@@ -687,27 +1279,18 @@
687 1279 * @access public
688 1280 */
689 1281 public function cloneChart() {
690 1282 $chart_id = $success = false;
691 - $nonce = wp_verify_nonce( filter_input( INPUT_GET, 'nonce' ), Visualizer_Plugin::ACTION_CLONE_CHART );
1283 + $nonce = isset( $_GET['nonce'] ) && wp_verify_nonce( $_GET['nonce'], Visualizer_Plugin::ACTION_CLONE_CHART );
692 1284 $capable = current_user_can( 'edit_posts' );
693 1285 if ( $nonce && $capable ) {
694 - $chart_id = filter_input(
695 - INPUT_GET,
696 - 'chart',
697 - FILTER_VALIDATE_INT,
698 - array(
699 - 'options' => array(
700 - 'min_range' => 1,
701 - ),
702 - )
703 - );
1286 + $chart_id = isset( $_GET['chart'] ) ? filter_var( $_GET['chart'], FILTER_VALIDATE_INT ) : '';
704 1287 if ( $chart_id ) {
705 1288 $chart = get_post( $chart_id );
706 - $success = $chart && $chart->post_type == Visualizer_Plugin::CPT_VISUALIZER;
1289 + $success = $chart && $chart->post_type === Visualizer_Plugin::CPT_VISUALIZER;
707 1290 }
708 1291 }
709 - $redirect = wp_get_referer();
1292 + $redirect = remove_query_arg( 'vaction', wp_get_referer() );
710 1293 if ( $success ) {
711 1294 $new_chart_id = wp_insert_post(
712 1295 array(
713 1296 'post_type' => Visualizer_Plugin::CPT_VISUALIZER,
@@ -716,23 +1299,35 @@
716 1299 'post_status' => $chart->post_status,
717 1300 'post_content' => $chart->post_content,
718 1301 )
719 1302 );
720 - if ( $new_chart_id && ! is_wp_error( $new_chart_id ) ) {
721 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_CHART_TYPE, get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_TYPE, true ) );
722 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, get_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, true ) );
723 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SOURCE, get_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, true ) );
724 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SERIES, get_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, true ) );
725 - add_post_meta( $new_chart_id, Visualizer_Plugin::CF_SETTINGS, get_post_meta( $chart_id, Visualizer_Plugin::CF_SETTINGS, true ) );
726 - $redirect = add_query_arg(
727 - array(
728 - 'page' => 'visualizer',
729 - 'type' => filter_input( INPUT_GET, 'type' ),
1303 + if ( is_wp_error( $new_chart_id ) ) {
1304 + do_action( 'themeisle_log_event', Visualizer_Plugin::NAME, sprintf( 'Error while cloning chart %d = %s', $chart_id, print_r( $new_chart_id, true ) ), 'error', __FILE__, __LINE__ );
1305 + } else {
1306 + $post_meta = get_post_meta( $chart_id );
1307 + foreach ( $post_meta as $key => $value ) {
1308 + if ( strpos( $key, 'visualizer-' ) !== false ) {
1309 + add_post_meta( $new_chart_id, $key, maybe_unserialize( $value[0] ) );
1310 + }
1311 + }
1312 + $redirect = esc_url(
1313 + add_query_arg(
1314 + array(
1315 + 'page' => 'visualizer',
1316 + 'type' => filter_input( INPUT_GET, 'type' ),
1317 + 'vaction' => false,
1318 + ),
1319 + admin_url( 'admin.php' )
730 1320 ),
731 - admin_url( 'upload.php' )
1321 + null,
1322 + 'db'
732 1323 );
733 1324 }
734 1325 }
1326 +
1327 + if ( defined( 'WP_TESTS_DOMAIN' ) ) {
1328 + wp_die();
1329 + }
735 1330 wp_redirect( $redirect );
736 1331 exit;
737 1332 }
738 1333
@@ -778,9 +1373,12 @@
778 1373 $data = $this->_getChartArray();
779 1374 $render = new Visualizer_Render_Page_Data();
780 1375 $render->chart = $this->_chart;
781 1376 $render->type = $data['type'];
782 - unset( $data['settings']['width'], $data['settings']['height'] );
1377 +
1378 + if ( $data && $data['settings'] ) {
1379 + unset( $data['settings']['width'], $data['settings']['height'], $data['settings']['chartArea'] );
1380 + }
783 1381 wp_enqueue_style( 'visualizer-frame' );
784 1382 wp_enqueue_script( 'visualizer-render' );
785 1383 wp_localize_script(
786 1384 'visualizer-render',
@@ -786,9 +1384,9 @@
786 1384 'visualizer-render',
787 1385 'visualizer',
788 1386 array(
789 1387 'l10n' => array(
790 - 'invalid_source' => esc_html__( 'You have entered invalid URL. Please, insert proper URL.', 'visualizer' ),
1388 + 'invalid_source' => esc_html__( 'You have entered an invalid URL. Please provide a valid URL.', 'visualizer' ),
791 1389 'loading' => esc_html__( 'Loading...', 'visualizer' ),
792 1390 ),
793 1391 'charts' => array(
794 1392 'canvas' => $data,
@@ -794,13 +1392,16 @@
794 1392 'canvas' => $data,
795 1393 ),
796 1394 )
797 1395 );
798 - // Added by Ash/Upwork
799 - if ( VISUALIZER_PRO ) {
1396 +
1397 + do_action( 'visualizer_enqueue_scripts_and_styles', $data, $this->_chart->ID );
1398 +
1399 + if ( Visualizer_Module::is_pro() && Visualizer_Module::is_pro_older_than( '1.9.0' ) ) {
800 1400 global $Visualizer_Pro;
801 - $Visualizer_Pro->_enqueueScriptsAndStyles( $data );
1401 + $Visualizer_Pro->_enqueueScriptsAndStyles( $data, $this->_chart->ID );
802 1402 }
1403 +
803 1404 // Added by Ash/Upwork
804 1405 $this->_addAction( 'admin_head', 'renderFlattrScript' );
805 1406 wp_iframe( array( $render, 'render' ) );
806 1407 }
@@ -813,13 +1414,14 @@
813 1414 public function getQueryData() {
814 1415 check_ajax_referer( Visualizer_Plugin::ACTION_FETCH_DB_DATA . Visualizer_Plugin::VERSION, 'security' );
815 1416
816 1417 $params = wp_parse_args( $_POST['params'] );
817 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1418 + $chart_id = filter_var( $params['chart_id'], FILTER_VALIDATE_INT );
1419 +
1420 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
818 1421 $html = $source->fetch( true );
819 - $error = '';
820 - if ( empty( $html ) ) {
821 - $error = $source->get_error();
1422 + $error = $source->get_error();
1423 + if ( ! empty( $error ) ) {
822 1424 wp_send_json_error( array( 'msg' => $error ) );
823 1425 }
824 1426 wp_send_json_success( array( 'table' => $html ) );
825 1427 }
@@ -842,21 +1444,42 @@
842 1444 ),
843 1445 )
844 1446 );
845 1447
1448 + $hours = filter_input(
1449 + INPUT_POST,
1450 + 'refresh',
1451 + FILTER_VALIDATE_FLOAT,
1452 + array(
1453 + 'options' => array(
1454 + 'min_range' => -1,
1455 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1456 + ),
1457 + )
1458 + );
1459 +
1460 + if ( ! is_numeric( $hours ) ) {
1461 + $hours = -1;
1462 + }
1463 +
846 1464 $render = new Visualizer_Render_Page_Update();
847 1465 if ( $chart_id ) {
848 1466 $params = wp_parse_args( $_POST['params'] );
849 - $source = new Visualizer_Source_Query( stripslashes( $params['query'] ) );
1467 + $source = new Visualizer_Source_Query( stripslashes( $params['query'] ), $chart_id, $params );
850 1468 $source->fetch( false );
851 1469 $error = $source->get_error();
852 1470 if ( empty( $error ) ) {
853 - $hours = $_POST['refresh'];
854 1471 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_QUERY, stripslashes( $params['query'] ) );
855 1472 update_post_meta( $chart_id, Visualizer_Plugin::CF_SOURCE, $source->getSourceName() );
856 1473 update_post_meta( $chart_id, Visualizer_Plugin::CF_SERIES, $source->getSeries() );
857 1474 update_post_meta( $chart_id, Visualizer_Plugin::CF_DB_SCHEDULE, $hours );
858 1475 update_post_meta( $chart_id, Visualizer_Plugin::CF_DEFAULT_DATA, 0 );
1476 + if ( isset( $params['db_type'] ) && $params['db_type'] !== Visualizer_Plugin::WP_DB_NAME ) {
1477 + $remote_db_params = $params;
1478 + unset( $remote_db_params['query'] );
1479 + unset( $remote_db_params['chart_id'] );
1480 + update_post_meta( $chart_id, Visualizer_Plugin::CF_REMOTE_DB_PARAMS, $remote_db_params );
1481 + }
859 1482
860 1483 $schedules = get_option( Visualizer_Plugin::CF_DB_SCHEDULE, array() );
861 1484 $schedules[ $chart_id ] = time() + $hours * HOUR_IN_SECONDS;
862 1485 update_option( Visualizer_Plugin::CF_DB_SCHEDULE, $schedules );
@@ -863,13 +1486,14 @@
863 1486
864 1487 wp_update_post(
865 1488 array(
866 1489 'ID' => $chart_id,
867 - 'post_content' => $source->getData(),
1490 + 'post_content' => $source->getData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ),
868 1491 )
869 1492 );
870 - $render->data = json_encode( $source->getRawData() );
1493 + $render->data = json_encode( $source->getRawData( get_post_meta( $chart_id, Visualizer_Plugin::CF_EDITABLE_TABLE, true ) ) );
871 1494 $render->series = json_encode( $source->getSeries() );
1495 + $render->id = $chart_id;
872 1496 } else {
873 1497 $render->message = $error;
874 1498 }
875 1499 }
@@ -898,13 +1522,76 @@
898 1522 ),
899 1523 )
900 1524 );
901 1525
902 - $hours = $_POST['refresh'];
1526 + $hours = filter_input(
1527 + INPUT_POST,
1528 + 'refresh',
1529 + FILTER_VALIDATE_INT,
1530 + array(
1531 + 'options' => array(
1532 + 'min_range' => -1,
1533 + 'max_range' => apply_filters( 'visualizer_is_business', false ) ? PHP_INT_MAX : -1,
1534 + ),
1535 + )
1536 + );
903 1537
1538 + if ( 0 !== $hours && empty( $hours ) ) {
1539 + $hours = -1;
1540 + }
1541 +
904 1542 do_action( 'visualizer_save_filter', $chart_id, $hours );
905 1543
906 1544 if ( ! ( defined( 'VISUALIZER_DO_NOT_DIE' ) && VISUALIZER_DO_NOT_DIE ) ) {
907 1545 defined( 'WP_TESTS_DOMAIN' ) ? wp_die() : exit();
908 1546 }
1547 + }
1548 +
1549 + /**
1550 + * Save chart image.
1551 + *
1552 + * @param string $base64_img Chart image.
1553 + * @param int $chart_id Chart ID.
1554 + * @param bool $save_attachment Save attachment.
1555 + * @return attachment ID
1556 + */
1557 + public function save_chart_image( $base64_img, $chart_id, $save_attachment = true ) {
1558 + // Delete old chart image.
1559 + $old_attachment_id = get_post_meta( $chart_id, Visualizer_Plugin::CF_CHART_IMAGE, true );
1560 + if ( $old_attachment_id ) {
1561 + wp_delete_attachment( $old_attachment_id, true );
1562 + }
1563 +
1564 + if ( ! $save_attachment ) {
1565 + return 0;
1566 + }
1567 +
1568 + // Upload dir.
1569 + $upload_dir = wp_upload_dir();
1570 + $upload_path = str_replace( '/', DIRECTORY_SEPARATOR, $upload_dir['path'] ) . DIRECTORY_SEPARATOR;
1571 +
1572 + $img = str_replace( 'data:image/png;base64,', '', $base64_img );
1573 + $img = str_replace( ' ', '+', $img );
1574 + $decoded = base64_decode( $img );
1575 + $filename = 'visualization-' . $chart_id . '.png';
1576 + $file_type = 'image/png';
1577 + $hashed_filename = $filename;
1578 +
1579 + // Save the image in the uploads directory.
1580 + require_once ABSPATH . '/wp-admin/includes/file.php';
1581 + \WP_Filesystem();
1582 + global $wp_filesystem;
1583 + $upload_file = $wp_filesystem->put_contents( $upload_path . $hashed_filename, $decoded );
1584 +
1585 + // Insert new chart image.
1586 + $attachment = array(
1587 + 'post_mime_type' => $file_type,
1588 + 'post_title' => preg_replace( '/\.[^.]+$/', '', basename( $hashed_filename ) ),
1589 + 'post_content' => '',
1590 + 'post_status' => 'inherit',
1591 + 'guid' => $upload_dir['url'] . '/' . basename( $hashed_filename ),
1592 + );
1593 +
1594 + $attach_id = wp_insert_attachment( $attachment, $upload_dir['path'] . '/' . $hashed_filename );
1595 + return $attach_id;
909 1596 }
910 1597 }